KCSC CTF 2023

Gues

Các bài rev ở đây thường không có description :D

Chạy file:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Code trong IDA (đã được rename function và biến):

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Như ta có thể thấy, chương trình nhận vào input 4 kí tự nếu lớn hơn thì xuất ra "too long" và exit.

Còn đây là bên trong susfunction:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Function này khá là rối, mình không chắc cái này có thể dịch ngược hoàn toàn, tuy nhiên dựa vào length của input khá ít nên mình có thể dễ dàng solve bằng bruteforce:

from subprocess import *
import struct
import string
import requests
from concurrent.futures import ThreadPoolExecutor
n = 0
m = string.printable
m = string.ascii_letters
x = []
for i in m:
    for j in m:
        for k in m:
            for l in m:
                x.append((i+j+k+l).encode())
def func(user_input):
    p = Popen([r'gues.exe', 'f'], stdout = PIPE, stdin = PIPE, stderr = STDOUT)
    p.stdin.write(user_input)
    testresult = p.communicate()[0]
    return testresult
def check(x):
    for i in x:
        if chr(i) not in string.printable:
            return False
    return True
n = 0
for ui in x:
    res = func(ui)
    if b'C{' in res:
        print(res,ui)
    n+=1

Vì mình code bài này khá vội nên là như mì ăn liền vậy, code không clean mọi người thông cảm:V

Ban đầu có thể nói là brute khá lâu, nhma thay vì dùng string.printable thì mình chuyển sang string.ascii_letters và đây là kết quả:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Flag: "KCSC{brut6_n6rv6r_die}

Mix

Như cái tên bài thì bài này mix giữa C# và C++.

Đầu tiên mình dùng Die để detect:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Do là file là C# obfuscated nên mình đã dùng tool để deobfuscate trước, cụ thể là de4dot:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Có được file Mix-cleaned.exe, load nó bằng dnspy và đây là hàm main:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Đa số các hàm đều là built-in function ví dụ như smethod_17() chỉ đơn giản là compare 2 buffer,

Ref Flagenc, mình có được flag_encrypted:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Về input của mình chỉ đơn giản là chuyển thành bytes, sau đó encrypt bởi hàm enc() và đem so sánh với flagenc.

Mình thử debug và stepinto enc tuy nhiên không có kết quả là mấy.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Tuy nhiên, tên bài là Mix mình nghĩ vẫn còn code đâu đó, cụ thể là C++, thử load vào IDA bằng IDA:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Sau khi xem string thì mình thấy có 1 thứ rất khả nghi, đó là "expand 64-byte k"

Mình nhận ra đây rất có thể là Salsa20 hoặc là ChaCha20, còn đây là hàm enc mà mình tìm được:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Như đã thấy, ta có 32 bytes của key nằm ở v9 và nonce là 8 bytes nằm ở v9[4]. Sau khi xem hàm sub_140001F10 thì mình khá chắc chắn là đây là chacha20.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Sau đó mình thử viết script (có salsa 20 bởi vì lúc viết script mình chạy không ra, nghi ngờ là do mình đoán sai):

from pwn import *
from Crypto.Cipher import Salsa20,ChaCha20,ChaCha20_Poly1305
flag_enc = bytes([143,124,41,217,251,185,171,26,74,83,173,236,169,239,22,115,128,248,92,93])
print(flag_enc,flag_enc.hex())
def ls2bytes(ls):
    return b"".join([bytes.fromhex(x)[::-1] for x in ls])
secret = [0]*7
secret[0] = "43604259"
secret[1] = "45724033"
secret[2] = "1763427E"
secret[3] = "5B725F70"
secret[4] = "507D527F"
secret[5] = "193D1976"
secret[6] = "3713371337133713"
nonce = [0]*2
nonce[0] = "03010200"
nonce[1] = "11FF0005"

secret = ls2bytes(secret)
nonce = ls2bytes(nonce)
print(secret.hex(),nonce.hex())

new_secret = []
for i in range(0,32,2):
    new_secret.append(secret[i]^0x13)
    new_secret.append(secret[i+1]^0x37)
new_secret = bytes(new_secret)[:32] #+b'\x00'*32
print(new_secret,nonce)

cipher = cipher = Salsa20.new(key=new_secret, nonce=nonce)
plaintext = cipher.decrypt(flag_enc)
print("Salsa:",plaintext)
cipher = cipher = ChaCha20.new(key=new_secret, nonce=nonce)
plaintext = cipher.decrypt(flag_enc)
print("Chacha:",plaintext)

Tuy nhiên cả 2 plaintext đều không phải flag. Mình cũng không hiểu tại sao, có thể là do đoạn "expand 64 byte k" (thường là 32).

Do đó vi tính chất đối xứng, nên mình quyết định debug và patch input của mình thành flagenc => enc(flagenc) = flag.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Đoạn này đổi string lại thành bytes của flagenc.

Sau đó dựa theo địa chỉ cùa ptr để tìm ra vị trí của nó trên memory:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Lúc đầu mình làm không ra, tuy nhiên mình phát hiện patch sai 1 bytes,

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Đoạn 73 3F F8 nếu đúng như flagenc thì phải là 73 80 F8

Ta chỉ cần sửa đoạn này lại, hoặc từ đầu có thể patch bytes thẳng vào memory(cách này hay hơn :V), đặt breakpoint ngay chổ so sánh và tìm flag trên memory:

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Flag: "KCSC{h3Ll0_fR0M_c##}"