<h1 id="KCSC"> KCSC_CTF_2023</h1>
Do bận ôn thi học kì nên hôm nay mình mới có thời gian để làm lại các bài của giải KCSC. Cảm ơn ban tổ chức đã tạo nên sân chơi này để mọi người học hỏi. Và giờ chúng ta bắt đầu thôi !!!!!!
Challenge: tổng cộng có 10. <a href="https://github.com/0xMikiko/CTF-Challenge-Storage">Hãy tải đề tại đây.</a> Flag format: KCSC{XXXXXXXXXX}.
<ul>
<li><a href="#Real Warmup">1. Real Warmup</a></li>
<li><a href="#Hide and seek">2. Hide and seek</a></li>
<li><a href="#Awg Mah Back">3. Awg Mah Back</a></li>
<li><a href="#Images">4. Images</a></li>
<li><a href="#Dont Call Me Kamui">5. Dont Call Me Kamui</a></li>
<li><a href="#Dynamic Function">6. Dynamic Function</a></li>
<li><a href="#Password Checker ">7. Password Checker </a></li>
<li><a href="#Mission: Impossible">8. Mission: Impossible</a></li>
<li><a href="#Two-faces">9. Two-faces</a></li>
<li><a href="#The Ultimate Xor">10. The Ultimate Xor</a></li>
</ul>

<h2>Difficulty: Easy </h2>
<div id="Real Warmup"></div>
<h3>1. Real Warmup</h3>
Mở chương trình trong DIE để biết thông tin tổng quan.

Mở chương trình trong IDA

Ta có thể nhận ra chuỗi đó được mã hóa base64.
:::info
```python
import base64
cipher_flag = "S0NTQ3tDaDQwYF9NfF98bjlgX0QzTidfVjAxJ183N1wvX0tDU0N9"
flag = base64.b64decode(cipher_flag)
print(flag)
```
:::
:::success
```
Flag: KCSC{Ch40`_M|_|n9`_D3N'_V01'_77\/_KCSC}
```
:::
<div id="Hide and seek"></div>
<h3>2. Hide and seek</h3>
Mở chương trình trong DIE để biết thông tin tổng quan.

Chạy thử chương trình ta nhận được thông báo như sau:

Ta nhận thấy đường dẫn đã được che đi bới thuộc tính khi ghi ra console. Để biết thêm chi tiết, <a href="https://stackoverflow.com/questions/30645675/how-to-change-colour-of-a-specific-output-on-console-in-c">hãy đọc tại đây.</a>
Dùng Everything để tìm file cho nhanh (don't blame me :<). Ta tìm được "temp_html_file_x.html" và nhận được flag.

:::success
```
Flag: KCSC{~(^._.)=^._.^=(._.^)~}
```
:::
<div id="Awg Mah Back"></div>
<h3>3. Awg Mah Back</h3>
Đề bài cho chúng ta file src.py. Chương trình đọc dữ liệu từ flag.txt, thực hiện xor nhiều lần, cộng chuỗi lại và viết ra output.txt

Ta mô tả hoạt động của chương trình như sau:
:::info
```python
a0 = flag[0:len/3]
b0 = flag[len/3:2*len/3]
c0 = flag[2*len/3:]
a1 = a0 ^ ( len[0] + len[1])
b1 = a1 ^ b0
c1 = b1 ^ c0 = a1 ^ b0 ^ c0
a2 = c1 ^ a1 = a1 ^ b0 ^ c0 ^ a1 = b0 ^ c0
b2 = a2 ^ b1 = b0 ^ c0 ^ a1 ^ b0 = a1 ^ c0
c2 = b2 ^ c1 = a1 ^ c0 ^ a1 ^ b0 ^ c0 = b0
****
c3 = c2 ^ ( len[0] * len[1]) = b0 ^ ( len[0] * len[1])
enc = a2+b2+c3
```
:::
Ta viết chương trình giải mã như sau. Flag được mã hóa base64 tận ba lần nhá.
:::info
```python
from pwn import *
with open("output.txt", "rb") as f:
cipher = f.read()
length = len(cipher)
a = cipher[0:length // 3]
b = cipher[length // 3:2 * length // 3]
c = cipher[2 * length // 3:]
plain_b = xor(c,2)
plain_c = xor(plain_b,a)
plain_a = xor(xor(b,3),plain_c)
flag = (plain_a+plain_b+plain_c)
for i in range(3):
flag = base64.b64decode(flag)
with open('int.txt', 'wb') as (f):
f.write(flag)
#
```
:::
:::success
```
KCSC{84cK_t0_BaCK_To_B4ck_X0r`_4nD_864_oM3g4LuL}
```
:::
<h2>Difficulty: Medium </h2>
<div id="Images"></div>
<h3>4. Images</h3>
Bác nào cho quả đề ác ghê :<<. Mình nhập tay ra flag, bạn nào convert từ ảnh sang dạng khác rồi code cũng được. Ta chú ý ở ảnh thứ 2


Thực hiện nhập dữ liệu vào Buffer lấy độ dài của Buffer lưu vào <b>var_10C</b>. Kiểm tra dữ liệu nhập có dài 63 kí tự không. Nội dung của flag sẽ có pattern chung là

Kiểm tra từng kí tự có bằng với <b>operands</b> của opcode <b>cmp</b> không. Tôi không hiểu tại sao tôi lại dành gần 1 tiếng để dò từng kí tự :<
:::info
```python
index = [0, 0x1A, 0X14, 0X18, 34, 23, 18, 21, 22, 9, 5, 8, 10, 14, 12, 16, 3, 30, 48, 41, 44, 39, 7, 28, 29, 15, 38, 42, 46, 37, 33, 32, 36, 31, 25, 27, 35, 45, 19, 40, 43, 47, 17, 49, 50, 4, 13, 11, 6, 2, 1]
char = [75, 110, 101, 104, 101, 110, 107, 110, 95, 111, 67, 95, 110, 95, 118, 97, 67, 105, 121, 95, 104, 110, 109, 110, 104, 100, 111, 97, 110, 100, 104, 95, 95, 110, 97, 95, 116, 95, 105, 103, 110, 97, 95, 96, 125, 123, 105, 95, 97, 83, 67]
flag = [''] * len(char)
for i in range(len(char)):
flag[index[i]] = chr(char[i])
print("".join(flag))
```
:::
:::success
```
Flag: KCSC{Cam_on_vi_da_kien_nhan_nhin_het_dong_anh_nay`}
```
:::
<div id="Dont Call Me Kamui"></div>
<h3>5. Dont Call Me Kamui</h3>
Mở chương trình trong DIE để biết thông tin tổng quan.

Mở chương trình trong IDA

Ta có thể nhận ra hàm <u>sub_B811E0</u> là mã hóa SHA-256 thông qua hằng số được sử dụng và khối dữ liệu 64 bit. <a href="https://www.simplilearn.com/tutorials/cyber-security-tutorial/sha-256-algorithm">Hãy đọc thêm tại đây</a>

Nhìn sơ qua ta thấy họ yêu cầu nhập dữ liệu đoạn chuỗi 64 bytes vào so sánh. Cụ thể như sau:
<ul>
<li>v4 là con trỏ giữ địa chỉ của byte đầu tiên, mục đích để lưu mã hash (cụ thể là v15) <u>1cf18a243c25a56a993c8207d1161a9c2de5f34b952d382704b94dc5e888b108</u></li>
<li>v17 là mảng lưu mã băm của giá trị nhập vào và so sánh với v4</li>
<li>Nếu đúng, giá trị nhập vào sẽ XOR với v12 để tạo flag.</li>
</ul>
Ta sử dụng <a href="https://10015.io/tools/sha256-encrypt-decrypt"> SHA-256 Online</a> để tìm ra kết quả

Và thế là ta có được flag như sau:

:::success
```
Flag: KCSC{het_y_tuong_roi_nen_di_an_trom_idea_KMACTF_hjhj}
```
::::
<h2>Difficulty: Intermediate </h2>
<div id="Dynamic Function"></div>
<h3>6. Dynamic Function</h3>
Mở chương trình trong DIE để biết thông tin tổng quan.

Mở chương trình trong IDA

Chương trình yêu cầu ta nhập vào Buffer 30 kí tự và mở đầu là "KCSC". Khi phân tích tĩnh, "byte_417c50" là các byte rác. Khi thực thi chương trình, nó sẽ XOR 0x41 để tạo hàm. Ta viết đoạn script như sau và tạo hàm.
:::info
```python
import ida_bytes
start = 0x00417C50
end = 0x00417CF4
for i in range(start, end):
tmp = ida_bytes.get_byte(i)
ida_bytes.patch_byte(i, tmp^0x41)
```
:::

:::info
```python
key = "reversing_is_pretty_cool"
flag = ""
cipher = [ 0x44, 0x93, 0x51, 0x42, 0x24, 0x45, 0x2E, 0x9B, 0x01, 0x99,
0x7F, 0x05, 0x4D, 0x47, 0x25, 0x43, 0xA2, 0xE2, 0x3E, 0xAA,
0x85, 0x99, 0x18, 0x7E]
for i in range(24):
num = (cipher[i] ^ ord(key[i]))
mod,div = num%16,num//16
mod,div=div,mod
flag += chr(div*16 + mod)
print(flag)
```
:::
:::success
```
Flag: KCSC{correct_flag!submit_now!}
```
:::
<div id="Password Checker"></div>
<h3>7. Password Checker</h3>
Mở chương trình trong DIE để biết thông tin tổng quan.

Mở chương trình trong IDA

Chương trình sử dụng hàm băm "hash" trong thư viện "hash.so" để mã hóa dữ liệu đầu vào và so sánh. Nếu đúng sẽ giải mã mật khẩu thông qua dữ liệu đầu vào.

Thời gian đầu mình đọc lướt qua các tham số, mình đã khẳng định rằng đây là MD4,MD5. Tuy nhiên, sau nhiều lần debug mà mã băm vẫn không trùng khớp, mình đưa ra dự đoán => đây là custom MD5, nó đã bị thay tham số. Vì vậy chỉ còn cách gọi hàm trong thư viện hash.so thôi.
Bác nào tay to thì thay password từ từ rồi cũng ra, nhưng tác giả lại xóa thư viện sau mỗi lần thử nên hơi bị lâu nhá.
Tại đây, ta tạo danh sách các mã băm của các mật khẩu trong wordlist.txt và so sánh với giá trị cần tìm.
:::info
```python
import ctypes
HASH_LIB_PATH = './hash.so'
EXPECTED_HASH = "b99aff88d8e71fba4bce610f4d3cbc8d"
BUFFER_SIZE = 32
def load_hash_library():
try:
return ctypes.CDLL(HASH_LIB_PATH)
except OSError as e:
print(f"Error loading the hash library: {e}")
raise
def hash_string(hash_lib, input_string):
buffer = ctypes.create_string_buffer(BUFFER_SIZE)
input_string = input_string.encode('utf-8')
hash_lib.hash(buffer, input_string)
address = int.from_bytes(buffer[:8], byteorder='little')
return ctypes.string_at(ctypes.cast(address, ctypes.c_char_p)).decode('utf-8')
def main():
hash_lib = load_hash_library()
with open('wordlist.txt', 'r', encoding='utf-8', errors='ignore') as file:
passwords = [line.strip() for line in file]
for password in passwords:
if hash_string(hash_lib, password) == EXPECTED_HASH:
print(f"Password found: {password}")
break
if __name__ == "__main__":
main()
```
:::

:::success
```
Flag: KCSC{tuyet_voi!!!qua_dep_trai_roi<3<3}
```
:::
<div id="Mission: Impossible"></div>
<h3>8. Mission: Impossible</h3>
Mở chương trình trong DIE để biết thông tin tổng quan.

Để có được flag, ta cần tiêu diệt toàn bộ kẻ địch và hoàn thành game. Nếu không thay đổi thông số ta và địch, điều này là bất khả thi. Vì vậy, cần xác định nơi và cách thay đổi thông số.
Mở "GameStart.exe" bằng IDA, sau một lúc lâu đọc chương trình, ta được các đoạn quan trọng sau:

Thực hiện kiểm tra có đường dẫn `C:\Program Files\WinRAR\WinRAR.exe` , nếu không sẽ thông báo không tìm thấy và kết thức chương trình.

Tại hàm sub_140012A96, kiểm tra trong path hiện tại có tệp "resource", đọc 4 byte đầu từ tệp và so sánh:
- Nếu a1 = 0 => signature = "sonx"
- Nếu a1 = 1 và signature == "sonx" => signature = "rar!"
- Nếu a1 = 1 và signature != "sonx" => in ra "Unkown resource format" và "Restore resource signature failed!"


Giải nén bằng WinRAR bằng lệnh "C:\\Program Files\\WinRAR\\WinRAR.exe\" x -ierr ./resources" với các tham số:
- x: chế độ giải nén
- -ierr: bỏ qua tất cả các lỗi
- ./resources: tên tệp giải nén
Bắt đầu trò chơi, ta nhận được 3 hidden file bao gồm:
- audio: tệp âm thanh
- graphics: tệp hình ảnh(vũ khí, đạn, địch, ta,...)
- config: thông số về âm thanh, màn hinh, và <b>quan trọng nhất là địch và ta</b>

Thay đổi thông số: <b>buff mình khỏe lên và neff địch như cách riot nerf con akali. </b>Bây giờ, sửa 4 byte đầu thành "sonx" của "./resource" để game chạy lại được.
Vừa mở game xong win luôn, và đây là flag

:::success
```
Flag: KCSC{y0u_w0n_4nd_g3t_th3_fl4g}
```
:::
<h2>Difficulty: Hard </h2>
<div id="Two-faces"></div>
<h3>9. Two-faces</h3>
Mở chương trình trong DIE để biết thông tin tổng quan.

Mở chương trình trong IDA

Chương trình kiểm tra dữ liệu nhập vào có mở đầu là "KCSC{" và có độ dài là 22, dữ liệu bên trong { } có độ dài là 16 => thêm vào Block.
Vòng lặp thực hiện 100 lần 4 hàm như sau:
- Hàm sub_4F1FF0: shift rows - dịch trái các hàng theo thứ tự 0,1,2,3.

- Hàm sub_4F1CF0: shift columns - dịch lên các cột theo thứ tự 0,1,2,3.

- Hàm sub_4F2110: swap bit - đảo vị trí 4 bit đầu và 4 bit cuối của tất cả giá trị trong khối.

- Hàm sub_4F2200: xor count - (count + 85) XOR từng giá trị trong khối.

- Kết quả được trả về dùng để tạo chuỗi hex và so sánh

Ta có thể viết chương trình để tìm flag như sau
:::info
```python
def shift_rows(block):
return [row[-i:] + row[:-i] for i, row in enumerate(block)]
def shift_columns(block):
shifted_block = [
[block[(i - j) % 4][j] for j in range(4)]
for i in range(4)
]
return shifted_block
def swap_hex(block):
return [[(elem >> 4) | ((elem & 0xf) << 4) for elem in row] for row in block]
def xor(block, key):
return [[elem ^ key for elem in row] for row in block]
def main():
cipher_hex = "FDA6FF91ADA0FDB7ABA9FB91EFAFFAA2"
block = [[int(cipher_hex[i * 8 + j * 2: i * 8 + j * 2 + 2],16) for j in range(4)] for i in range(4)]
for i in range(100):
block = xor(block,99 + 85 - i)
block = swap_hex(block)
block = shift_columns(block)
block = shift_rows(block)
flag = ''.join([chr(element) for row in block for element in row])
print(flag)
if __name__ == "__main__":
main()
# 3a5y_ch41leng3_!
```
:::

Tuy nhiên khi nhập flag thì nhận được thông báo là sai!! Vậy ta đã sai ở đâu? Khi quan sát danh sách các hàm, ta nhận thấy có <b><u>TlsCallback_0</u></b>. <a href="https://medium.com/@aragornSec/thread-local-storage-197f9a3f4fe3">Hãy đọc thêm về TlsCallback tại đây.</a> Nói ngắn gọi thì hàm TLSCallback sẽ thực thi trước đến entry point.

Mình đã patch để việc kiểm tra debug không gây ảnh hưởng luồng thực thi. Có một số opcode chúng ta cần ghi nhớ, trong trường hợp này là <u>push</u> và <u>return</u>.

Đồng thời, sử dụng VirtualProtect thay đổi quyền truy cập của vùng nhớ. <a href="https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect">Đọc thêm VirtualProtect tại đây.</a> Vậy tại sao lại có 2 opcode đó ở đây. Mục đích để ghi đè được hàm <u>j_memcpy()</u> bằng câu lệnh sau:
push 0xBE133E
ret
Vì vậy, giá trị hex ta tính được và đề cho là tham số của sub_BE133E(), không phải là của strcmp(). Nhớ pass phần Anti-Debug nhé, nếu không nó vẫn sai đấy.
Quan sát tại sub_BE133E, ta nhận thấy chương trình đang cố thực hiện phép XOR với các byte, mục đích nhằm thay đổi block mà ta cần so sánh.

Chương trình để tìm flag có sự thay đổi như sau:
:::info
```python
def shift_rows(block):
return [row[-i:] + row[:-i] for i, row in enumerate(block)]
def shift_columns(block):
shifted_block = [[block[(i - j) % 4][j] for j in range(4)] for i in range(4)]
return shifted_block
def swap_hex(block):
return [[(elem >> 4) | ((elem & 0xf) << 4) for elem in row] for row in block]
def xor(block, key):
return [[elem ^ key for elem in row] for row in block]
def main():
xor_value = [7,124,0,7,127,119,120,1,0,115,7,117,0,2,3,115,7,7,0,12,7,114,123,112,4,127,3,4,7,113,0,4]
fake_cipher = "FDA6FF91ADA0FDB7ABA9FB91EFAFFAA2"
real_cipher = ''.join(chr(ord(c) ^ xor_value[i]) for i, c in enumerate(fake_cipher))
block = [[int(real_cipher[i * 8 + j * 2: i * 8 + j * 2 + 2],16) for j in range(4)] for i in range(4)]
for i in range(100):
block = xor(block,99 + 85 - i)
block = swap_hex(block)
block = shift_columns(block)
block = shift_rows(block)
flag = ''.join([chr(element) for row in block for element in row])
print(flag)
if __name__ == "__main__":
main()
```
:::
:::success
```
Flag: KCSC{function_h00k1ng}
```
:::
<div id="The Ultimate Xor"></div>
<h3>10. The Ultimate Xor</h3>
Đề bài cho ta đoạn asm của một chương trình, thông thường nếu thao tác với file, ta sẽ dùng capstone để disassembly xử lý dữ liệu. Còn nếu cho file .txt thì ta đọc file bình thường.
Ta có thể mô hình hóa thành sơ đồ như sau:

Chương trình có rất nhiều phép kiểm tra điều kiện, nên cho dù là mở trong IDA thì chúng ta cũng không thể giả mã hoặc xem bằng đồ thị. May mắn thay, thuật toán kiểm tra chỉ là phép XOR.
:::info
```python
cipher = []
xor_value = []
# Mở file để đọc
i = 0
with open('asm.txt', 'r') as file:
# Đọc từng dòng trong file
count = 0
for line in file:
if count > 20:
xor_value.append(0)
count = 0
# Kiểm tra xem chuỗi "mov dword ptr [rbp - 0x10]," có xuất hiện trong dòng không
if (b"\tmov\tdword ptr [rbp - 0x10]").decode('utf-8') in line:
# In ra dòng thỏa mãn điều kiện
words = line.split()
value = words[-1]
cipher.append((int(value,16)))
elif ("xor") in line:
words = line.split()
value = words[-1]
xor_value.append(int(value,16))
count = 0
count = count + 1
message = ''.join(chr(c ^ xor) for c, xor in zip(cipher, xor_value))
print(message)
```
:::
:::success
```
Flag: "I_reverse_all_this_and_all_I_got_is_this_flag"
```
:::