Try   HackMD

Rev - DCTF final

Okay lại là một task nhỏ về rev và mình đánh giá bài lần này không quá dễ cũng không quá khó. Tuy nhiên mình rút ra được 1 bài học xương máu:

  • Hãy nên tìm hiểu kĩ trước khi làm

Bắt đầu từ vấn đề thời gian, khi mà thực tế 1 bài CTF chỉ có vài ba tiếng để làm, đối với mình bài này 5 tiếng là khá nhiều tuy nhiên do mình bị gấp nên mới bị như này.

Bài có 2 file, 1 là byte code, 2 là 1 runner. Vì là 1 bài VM đơn giản nên mình cũng không nghĩ gì nhiều, mình mất vài chục phút và có thể hiểu được 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 →

Trước giờ VM thì mình thường viết disassembler cho dễ reverse và trace, tuy
vấn đề chỉ bắt đầu khi mà mình nghĩ bài này nó dễ, nên ban đầu mình chỉ debug, xem cách nó biến đổi input và rev lại. Tuy nhiên mình đã nhầm và mình mất 2~3 tiếng vào việc này một cách khá là vô nghĩa.

Do đó khoảng thời gian này mình không có note lại gì nhiều ngoại trừ 1 vài thứ linh tinh như:

flag_offset = 0x7FFFB91885F8 - idaapi.get_imagebase()

# 0x6dc: try debugging me
# 0x6c9: YAY!!!

Trong khoảng thời gian này mình cũng hiểu được check cơ bản:

  • lấy input và ghi vào 1 buffer (biết được offset của buffer này)
  • compare with format flag: "flag{" -> print "LGTM" (Look good to me =)))
  • compare length: length flag == 56? -> print "LGTM"

Vì ban đầu test input của mình < 56 nên lúc debug mình chẳng thấy gì nhiều, đặt biệt lúc đặt HW breakpoint cũng không có gì, cho tới khi mình mới nhận ra nó check length =))) thế là mất thêm 1 khoảng thời gian.

Ở 1 tiếng cuối cùng trong 5 tiếng, mình mới thực sự làm bài này:

Vấn đề ở chổ mình bắt đầu viết dissasembler (vì opcode trên bit nên mình cũng ngại viết ở lúc đầu, lúc sau nhận ra thì đã hơi muộn)

import struct
with open('bytecode','rb') as f:
    bytecode = f.read()
# assert len(bytecode)//4==1774
bytecode = [struct.unpack('<I',bytecode[i:i+4])[0] for i in range(0,len(bytecode),4)]
assert len(bytecode)==1774
pc = 0
def ins_print(a):
    print(f'L_{hex(pc)}: {hex(bytecode[pc])}    '.ljust(25),end = "")
    print(a)
b = 1
while pc < 1618:
    b = bytecode[pc]
    # byte 0x2
    if b==0xffffffff:
        pc+=1
        continue
    if (b & 0x40000):
        ins_print(f'add value, reg1, reg2')
    if (b&0x80000):
        ins_print(f'mul value, reg1, reg2')
    if (b&0x20000):
        ins_print(f'shrl value, reg1, reg2')
    if (b&0x100000):
        ins_print(f'xor value, reg1, reg2')
    if (b&0x200000):
        ins_print(f'cmp reg1, reg2      ; value = reg1==reg2')
    

    #byte 0x0
    if ( (b & 4) != 0 ):
        ins_print(f'mov idx, tmp2')
    if ( (b & 0x20) != 0 ):
        ins_print(f'mov idx, tmp3')
    if ( (b & 0x100) != 0 ):
        ins_print(f'mov idx, tmp4')
    
    # byte0
    # if ( (b & 0x40) != 0 ):
    #     ins_print(f'mov value, tmp2')
    # if ( (b & 0x20) != 0 ):
    #     ins_print(f'mov value, tmp3') 
    # if ( (b & 8) != 0 ):
    #     ins_print(f'mov value, tmp4')
    # if ( (b & 4) != 0 ):
    #     ins_print(f'mov value, tmp5')
    # if ( (b & 2) != 0 ):
    #     ins_print(f'mov value, tmp6')
    # if ( (b & 1) != 0 ):
    #     ins_print(f'mov value, mem[idx]     ; get_mem_idx')

    #byte 0x3
    if   ( (b & 0x4000000) != 0 ):
        ins_print(f'mov value, bytecode[pc+1]')
    if ( (b & 0x8000000) != 0 ):
        ins_print(f'mov value, bytecode[idx+1]')
    if ( (b & 0x2000000) != 0 ):
        ins_print(f'read value         ;read char ')
    if ( (b & 0x1000000) != 0 ):
        ins_print(f'write value        ;write char ')
    if ((b & 0x10000000) != 0):
        # ins_print(f'nop')
        pc +=1
    elif ((b & 0x20000000) != 0):
        ins_print(f'         ; {hex(bytecode[pc+1])}')
        # print()
        pc +=2
    else:
        pc+=1
    if ((b & 0x40000000) != 0):
        ins_print(f'jmp value')

    # elif
    # pc += 1

# ins_print(bytecode.index(78))

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 →

Tới đây thì mình vừa debug vừa xem code thì nó dễ hơn hẳn, đây là một trong những lí do mà nên viết disassembler trước khi làm bất kì bài vm nào.

Từ lúc này thì mình tìm được crit, ban đầu mình đoán nó chỉ xor cơ bản hoặc RC4, nhưng do debug mãi và cái opcode trên bit khá lạ, nên mình không tìm dc:

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ả bài chỉ có mỗi đoạn này là dùng xor, mình debug và tìm buffer bytecode, và đặt hardware breakpoint ngay tại đúng address của ins này. sau đó xem ngay lúc xor 2 instruction:

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 →

Input:

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 →

Xoring:

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 →

Okay tới đây mình thử làm tay vài lần thì thấy là ok, chúng nó chỉ xor với nhau thôi, chỉ cần xor ngược lại là ra flag.

Encrypted flag nằm ở offset gần cuối, mở bằng HXD hoặc xem trong heap sẽ thấy.
Sau đó chỉ cần 1 script nhỏ để extract hết mấy cái keystream mà nó đã xor ra.

x = []
end_address = 0x561760873DC8 #
while True:
    idaapi.continue_process()
    idaapi.wait_for_next_event(WFNE_SUSP, -1)
    if get_reg_value('rip') == end_address: break
    x.append(get_reg_value('rdx'))
    print(x)

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 →

Script solve:

from pwn import *
from arc4 import ARC4

x = bytes.fromhex('4E 00 00 00 80 00 00 00 05 00 00 00 F5 00 00 00 16 00 00 00 14 00 00 00 21 00 00 00 4B 00 00 00 D9 00 00 00 FA 00 00 00 FA 00 00 00 55 00 00 00 CE 00 00 00 EE 00 00 00 CC 00 00 00 50 00 00 00 26 00 00 00 81 00 00 00 D2 00 00 00 56 00 00 00 FB 00 00 00 84 00 00 00 F4 00 00 00 40 00 00 00 3E 00 00 00 D9 00 00 00 BE 00 00 00 3D 00 00 00 FE 00 00 00 A0 00 00 00 74 00 00 00 F8 00 00 00 8F 00 00 00 64 00 00 00 39 00 00 00 95 00 00 00 B9 00 00 00 36 00 00 00 9B 00 00 00 DC 00 00 00 DA 00 00 00 DC 00 00 00 DB 00 00 00 AB 00 00 00 89 00 00 00 82 00 00 00 32 00 00 00 6F 00 00 00 A5 00 00 00 8C 00 00 00 44 00 00 00 75 00 00 00 43 00 00 00 A7 00 00 00 F8 00 00 00 59 00 00 00 1B 00 00 00 5B 00 00 00 33 00 00 00 31 00 00 00 6D 00 00 00 00 00 00 00 1B 00 00 00 5B 00 00 00 33 00 00 00 32 00 00 00 6D 00 00 00 00 00 00 00 1B 00 00 00 5B 00 00 00 30 00 00 00 6D 00 00 00 00 00 00 00 6D 00 00 00 00 00 00 00')
new = [x[i] for i in range(0,len(x),4)][:56]

print(bytes(new))
# 0x6dc: try debugging me
# 0x6c9: YAY!!!
# print(bytes(new2))
# for i in range(256):
#     print(xor(i,new))
print(new)
# flag = b'flag{'
# for i in range(1,len(flag)):
#     print((flag[i]-flag[i-1])&0xff,end = ',')



sus = [0x28,0xec,0x64] + [146, 109, 96, 73, 34, 170, 165, 141, 52, 189, 177, 173, 15, 112, 204, 141, 48, 148, 232, 159, 51, 97, 171, 221, 9, 161, 144, 6, 167, 220, 13, 93, 166, 230, 117, 243, 232, 180, 178, 190, 199, 214, 251, 2, 26, 250, 232, 33, 22, 42, 195, 157, 36]

print(xor(sus,new))


# arc = ARC4(b'I need t')
# print(arc.decrypt(bytes(new)))

P/s:
Sau đó mình debug mình mới thấy 1 vài chổ có 0x100 (256) và khá chắc là RC4, mình cũng nhận ra bản thân từng code RC4 bằng VM chỉ là VM lần này hơi khác 1 tí

Bài này tuy cũng ngắn nhưng lại có nhiều bài học, đặc biệt là trong việc quản lí thời gian + mindset khi làm các bài tương tự. Cảm ơn a Mochi.

THE END