[Plaid CTF 2016] quick (Re175)

Tôi cảm thấy mình cần phải thức tỉnh, khi mà hàng ngày vẫn có biết bao lượt truy cập vào blog này, trong khi last post của nó đã cách đây hơn 1 năm. Tôi thật tệ.

quick

Reversing (175 pts)

Why be slow when you can be quick?

File dự phòng: quick

Người lười như tôi thường sau khi down đề về sẽ mở nó bằng notepad, xem mấy byte đầu thấy ELF thì tôi sẽ kéo nó vào IDA 32 bit, nếu sai tôi lại kéo nó sang IDA x64. Dạo trước có đợt tôi quyết tâm thoát lười, sau khi xem vài byte đầu bằng notepad tôi vẫn ráng xem thêm vài dòng bên dưới, nếu thấy x86-64 thì chỉ phải kéo một phát vào IDA là đúng luôn. Lâu không tập, tôi mất cmn luôn kỹ năng ấy.

Ok, bài này là ELF x64. Xem danh sách hàm trong IDA bạn sẽ thấy nó dùng Swift, để chạy nó chúng ta cần file bị thiếu bên dưới (hãy dùng Ubuntu 14.04 nếu muốn thành người văn minh):

[sh]
➜ Desktop ldd ./quick
linux-vdso.so.1 => (0x00007fff4f5a3000)
libswiftCore.so => not found
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fa36792c000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa367626000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa367410000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa36704b000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa367c30000)

[/sh]
Hàm main khá dài, tôi không biết nó làm trò con bò gì, nhưng hàm check key nằm ở 0x403660. Nó cũng không ngắn gì cho cam. Chúng ta có 2 mảng 33 phần tử, kiểu thế này (tôi không paste code IDA ở đây vì tôi không thích):

[python]
arr1 = [0x81, 0x74, 0x87, 0x79, 0xB0, 0x6A, 0xAA, 0xA7, 0x68, 0x94, 0xA4, 0x7B, 0xAF, 0x89, 0xD4, 0xD1, 0x92, 0xBE, 0x94, 0xD8, 0x92, 0xCC, 0xDA, 0xD1, 0xD3, 0xA9, 0xD3, 0xAA, 0xEC, 0xA8, 0xDD, 0xEF, 0xFA]
arr2 = [0x50, 0x43, 0x8A, 0x64, 0xED, 0xA6, 0xAB, 0x93, 0xCC, 0xEB, 0xC2, 0x9A, 0xFA, 0x6A, 0xAB, 0x93, 0xCC, 0xEB, 0x6A, 0xBB, 0x62, 0x33, 0xD1, 0xF5, 0xC2, 0x9A, 0xFA, 0x6A, 0xBB, 0x62, 0x33, 0xD1, 0xD7]
[/python]

Hàm check này cần trả về 1, và chúng ta thấy nó trả về 0 ở đây:

[c]
_TTSg5Vs5UInt8___TFSag9subscriptFSix(&v70, v60, v66);
v14 = &v69;
v16 = v70;
_TTSg5Vs5UInt8___TFSag9subscriptFSix(&v69, v60, v65);
if ( v16 != v69 )
{
swift_release(v113);
swift_release(v65);
swift_release(v66);
return 0;
}
[/c]
Bạn sẽ thắc mắc vì sao tôi không rename mấy cái biến trên. Câu hỏi rất hay, tôi xin phép không trả lời.

Dễ thấy rằng nó so sánh v16 với v69. Mà v16 = v70, v70 lấy từ câu lệnh (google “subscript swift” nếu bạn muốn):
[c]
_TTSg5Vs5UInt8___TFSag9subscriptFSix(&v70, v60, v66);
[/c]

v66, thật tình cờ, được quyết định ngay ở đầu hàm check – là kết quả trả về của hàm 0x403600. Trong hàm này có 2 hàm con, hãy để ý tên các hàm import từ Swift để suy ra chức năng của nó. Tôi tin bạn sẽ làm được, chúng ta đã đọc Conan cả chục năm rồi. Cụ thể: 0x402810 làm nhiệm vụ sort input của ta (bé đến lớn, dùng …ComparablerS_4sortfT_GSaWxS0_S1___, tên dài lắm), còn 0x403050 sẽ tính tổng từng cặp phần tử trong *input của ta* và *input của ta_đã sort*, kết quả chính là v66.

Tương tự như vậy, v69 lấy từ v65, v65 chính là arr1. Và tóm lại, 2 mảng arr1 và mảng v66 phải bằng nhau.

[python]
def check1(s):
s_sorted = s[::]
s_sorted.sort()

for i in xrange(len(arr1)):
assert s[i] + s_sorted[i] == arr1[i]
[/python]

Để vượt qua bước check này, bạn chỉ cần 5 phút. Để bỏ cuộc. Hãy sang phần check thứ hai.

12057133_687329614735151_682897352_n

Vẫn với skill dò ngược thần thánh, bạn sẽ thấy rằng nó so sánh một mảng x với arr2. Tất nhiên, quan trọng là mảng x được tính thế nào.
[c]
_TFSag9subscriptFSix(&v72, v54, v57, &TMVs9Character);
v52 = v72;
v36 = v73;
v51 = sub_402AD0(v72, v73);
if ( ~v36 & 1 )
swift_release(v52);
v71 = sub_403510(v111, v51);
v34 = &v71;
_TTSg5Vs5UInt8___TFSa6appendfxT_(&v71, &v112);
v111 = v51;
[/c]

Ở đoạn code trên, v112 chính là mảng x, v57 là *input của ta*, 0x403510 là hàm ror, 0x402AD0 tôi quên cmn rồi (hình như nó chả làm cái khỉ gì). Chuyển sang python:
[python]
def check2(s):
n = 0
for i in xrange(len(arr2)):
assert ror(s[i], n & 7) == arr2[i]
n = s[i]
[/python]

Nhìn qua thì chúng ta cảm giác rằng check2 dễ hơn check1, nhưng nếu thử code thì bạn sẽ thấy là nó không sai. Đoạn code này sẽ cho ta flag:
[python]
n = 0
s = [0] * 33
for i in xrange(33):
s[i] = rol(arr2[i], n & 7, 8)
n = s[i]
print ”.join(map(chr, s))
[/python]
Kiểm tra lại flag với check1, thấy đúng luôn. Kỳ lạ.

20163c25a8d8-c48c-411b-b324-f59ab1f26fd1

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *