[Plaid CTF 2016] quite quixotic quest (Re300)

Hãy chuẩn bị tinh thần, vì đây là bài 300 điểm của một giải có rating 90.

2016fd1c9ed8-0e7f-40f3-b2b9-a23104c6361e

quite quixotic quest

Reversing (300 pts)

Well yes, it certainly is quite quixotic. (Yes, the flag format is PCTF{} )

Link dự phòng: quixotic

Đề bài là một file ELF 32 bit. Mặc dù đã có trong tay IDA phiên bản hịn (mà cả thế giới đều có), mình vẫn ưa 32 bit. Cười trong ô tô thì vẫn thích hơn là khóc trên xe đạp, right?

Xem danh sách string, dễ thấy nó chính là curl cải trang:

Mình đã định dùng BinDiff, nhưng thật thô bỉ khi đồng đội của mình chỉ cho mình cái này:

Tức là flag sẽ được nhập qua option –pctfkey. No fun.

Still no fun, chuỗi Validating key… kia đáng ra cần được che giấu kỹ càng hơn. Mình hoàn toàn cảm thấy bị xúc phạm khi mọi thứ cứ thẳng tuồn tuột như thế.

Vì EAX = magic_buf, rồi ESP = EAX, nên ESP = magic_buf. Sau khi debug một chút, chúng ta thấy rằng (có vẻ như) chương trình thực thi một rop chain (trỏ bởi magic_buf). Sau khi step over một hồi, chúng ta sẽ gặp badboy, được build từng char một (‘w’, ‘r’, ‘o’, ‘n’, ‘g’) bằng câu lệnh:

F8 không văn minh lắm, hãy chuyển qua chức năng trace của IDA. Đặt BP ở 0x08052AC7 và chạy lại, bật Instruction Tracing -> F9 -> Badboy -> Mở Trace window (mình quy ước rằng trace log mình post chỉ chứa những câu lệnh mà các bạn cần quan tâm):

Ta thấy EBX = len(key) (mình đang nhập key PCTF{123456}), sau đó EDX -= EBX, tiếp theo EAX = 0x94 và EBX = 0. Nếu cờ ZF = 0 thì lệnh CMOVNZ làm cho EAX = EBX = 0, ngược lại EAX giữ nguyên = 0x94. Sau đó EDI = EAX và ESP += EDI.

Do cờ ZF được quyết định bởi lệnh SUB EDX, EBX, nên kết quả của phép trừ EDX (0x35) cho EBX (len(key)) sẽ ảnh hưởng trực tiếp đến luồng thực thi của chương trình (hoặc ESP += 0, hoặc ESP += 0x94). Như vậy, len(key) = 0x35.

yOy5OI1

Nhập key mới (PCTF{1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJK}) và trace lại:

Đoạn code này làm nhiệm vụ cộng từng ký tự của key vào [EDX]. Khi đã nắm được ý tưởng, ta có thể filter log trace, hoặc đặt BP và F9 để qua nhanh nhưng vẫn đảm bảo việc theo dõi được vòng lặp. Thực tế thì chương trình chỉ tính sum(key[1:]) mà thôi.

Giá trị 01F9933D khi đổi qua DEC sẽ là 33133373, không có gì đặc biệt, mình chỉ nhắc thế thôi. Hãy chuyển sang debug để quan sát stack và các vùng nhớ liên quan. Tại hàm Curl_md5it, tham số thứ 2 trỏ đến 081CCF6C, đúng bằng [eax+4Ch], và chuỗi MD5 kết quả sẽ lưu ở 081CE9C0. Tổng kết lại, đến thời điểm này chúng ta có giả mã sau:

Tiếp tục:

Mã giả tương ứng:

Đoạn code sau sẽ cho ta biết giá trị của s:

Input lại một key phù hợp (PCTF{eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed}).

Đoạn trên là một vòng lặp, với EDX sẽ trỏ đến key và EBX+5E5B30C4 trỏ đến chuỗi MD5.
Sắp xong rồi, cố lên >:(

36

Debug để thấy giá trị các biến, ta thấy rằng chương trình thực hiện việc lấy từng dword trong key (sau khi đã xor với chuỗi MD5), rồi trừ cho ECX, và OR vào EDX. Khi bắt đầu, EDX = 0. Các giá trị của ECX lần lượt như sau:

Ở đây lại là một trick tương tự mà mình đã nhắc đến phần đầu bài, khi mà cờ ZF sẽ ảnh hưởng đến luồng thực thi của chương trình. Cờ ZF được quyết định ở câu lệnh AND EAX, EDX, và vì EAX = 0xFFFFFFFF, EDX cần bằng 0 để cờ này được bật.
Ta nhớ rằng EDX là phép OR của mọi giá trị EAX – ECX, do đó để EDX = 0 thì mọi hiệu EAX – ECX phải bằng 0, hay nói cách khác, EAX phải bằng ECX. Đoạn code sau, như mọi khi, mang lại cho chúng ta flag:

Nếu bạn thắc mắc tại sao với cách làm hư cấu thế này, mà mình lại có thể trở thành người giải ra thứ hai (:”>), thì bạn thắc mắc đúng rồi đấy. Trong lúc thi mình không hề dùng cách này. Hihi.

32

[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):

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):

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

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):

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.

Để 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.

Ở đ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:

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:

Kiểm tra lại flag với check1, thấy đúng luôn. Kỳ lạ.

20163c25a8d8-c48c-411b-b324-f59ab1f26fd1

[CSAW CTF 2013] keygenme (Re400)

keygenme – 400 Points

Solved by 100 teams.

nc 128.238.66.219 14549
keygenme32.elf

File dự phòng:

Nội dung chính

Đây là note thì đúng hơn là một writeup

Ở bài này chúng ta được yêu cầu nhập 3 tham số: username token1 token2. Trong đó từ username target sẽ tính ra 2 giá trị rồi thực hiện so sánh với token1 và token2. Cũng lưu ý rằng trước khi so sánh thì token1 và token2 sẽ được đụng chạm một chút, tuy nhiên nó khá đơn giản nên vấn đề sẽ chỉ là hiểu được quá trình xử lý username mà thôi.

Thật đáng tiếc, phần quan trọng nhất lại cũng chính là phần khó chịu nhất, khi mà lực lượng team mỏng, thời gian thì hạn chế, nên sau vài giây phút cố gắng đọc hiểu (để chứng tỏ rằng ta đây cũng thuộc hàng cứng), kết quả trả về gần như là null. Chuyển hướng, với mục tiêu là có được kết quả xử lý từ app thay vì tự code:

  • Một thằng tìm cách patch emo_popo_sure
  • Thằng còn lại tìm cách khác emo_popo_shame

Thành công khá muộn của bài này ghi đậm dấu ấn sắc nét mang đầy tính hổ báo cáo mèo của 2 con cú đêm đang hừng hực khí thế leo top. Chiến lược được vạch ra và triển khai nhanh chóng, đó là… sử dụng gdb làm trung gian. Hết, đơn giản vậy thôi emo_popo_pudency

Diễn giải ra thì: Connect đến server, nhận username > Chạy bằng gdb > đặt BP > Run > Đọc giá trị thanh ghi > Xác định token1, token2 > Đưa lên server > Lặp vài lần > Nhận flag.

Code minh họa:

File script cho gdb:

Kết quả:

flag = r3vers1ng_emul4t3d_cpuz_a1n7_h4rd!

[CSAW CTF 2013] Impossible (Re500)

Ưu tiên viết bài này trước, vì đây là lần đầu tiên chạm tới ngưỡng 500 đầy danh giá, mà có lẽ sẽ mãi cho đến về sau cũng khó lặp lại được too_sad

Impossible – 500 Points

Solved by 82 teams.

WTF, his hp is over 9000! Beat the game to get your key.
impossible.nds

Link dự phòng:

Nội dung chính

Google tìm emulator để chạy thử, kiếm được DeSmuME, tuy nhiên thằng này rất tồi, phải kiếm thằng khác thay thế. Google tiếp và có được No$gba Debugger:

Chúng ta có trong tay một trò chơi kiểu bắn phi thuyền, thằng trùm có lượng máu cực khủng (theo đề bài nói thì trên 9000), và đạn nó bắn ra thì nhiều hơn mưa, dày đặc đến mức không thể né được (vậy mới đặt tên là Impossible emo_popo_sweat).

Mình mất rất nhiều thời gian cho bài này vì đi sai hướng, do đã bỏ qua phần quan trọng nhất đó là cửa sổ hiển thị log của debugger. Bởi vì, game này hoàn toàn không đưa cho chúng ta bất cứ goodboy, badboy, hay đại loại là những thứ kiểu kiểu như vậy ở trên màn hình, nên nếu ta cố ra sức mò, thì thực sự là dù có nhích cũng không bao giờ đến đích.

Sau một ngày thăng trầm với chuyện tình yêu tình báo, giận hờn vu vơ, được trở về với cái bình yên tĩnh lặng, sự tập trung cũng theo đó là kéo đến vây quanh. Ngay kể từ khi phát hiện ra cái cửa sổ bên dưới, niềm tin của mình đối với con số 500 chợt vụt cháy bùng bùng:

ADD ENT chính là log mỗi khi tạo thêm một viên đạn của thằng trùm xấu xa, và chỉ cần nó ngừng bắn, thì… nó thua.

Search trong memory chuỗi đó, thấy rằng nó ở địa chỉ 0x02023698:

Giờ thì chạy game, chuyển qua cửa sổ Disam, trace để tìm vị trí gọi đến ADD ENT. Sau vài phút lang thang phiêu bạt, phát hiện nó ở 0x0200282E:

Chúng ta sẽ đặt BP ở đó, quay về chơi game, nó sẽ dừng. Trace cho đến khi out khỏi hàm đó để xác định câu lệnh gọi đến nó, và ta làm gì? NOP thôi still_dreaming

Sẽ có một vài chỗ gọi chứ không phải một, nên lời khuyên là bạn đừng vội bỏ BP ở 0x0200282E. Patch hết rồi thì thoải mái mà chơi thôi, vì thằng trùm không còn đạn để bắn ta nữa:

Thanh màu nâu nâu đỏ đỏ hồng hồng ở trên cùng chính là máu của hắn, tuy nhiên nó quá nhiều, và dường như con số 9000 mà ta được thông báo là không chính xác emo_popo_doubt

Dùng một cheat engine bất kỳ (ở đây mình dùng Cheat Engine 6.3), dễ dàng search ra:

emo_popo_waaaht Vậy là những 1000000 chứ không phải chỉ xấp xỉ 9000 như chúng ta nghĩ lúc đầu emo_popo_sweat

Sửa nó về 100 (kinh nghiệm cheat game của mình cho thấy, không nên sửa về 0 ngay, tránh bug). Bắn thêm vài nhát xinh xinh và:

Nếu chụp lại cái key và đảo ngược nó theo chiều dọc, ta có thể thấy được chút chút:

Dường như là: KEY IS DUBZFGJCRC, tuy nhiên khi submit lại không đúng.

Trên trang chủ có một cái hint cho bài này, đại ý là “Bạn có chắc bạn đã tìm được toàn bộ key?”. Ok, search trong memory coi sao:

Done, flag = ou6UbzM8fgEjZQcRrcXKVN.

Điểm 500 đầu tiên trong sự nghiệp CTF emo_popo_beauty

[CodeEngn] Basic RCE – Level 20

http://codeengn.com/challenges/basic/20

This program needs a key file.
What does the data in the file crackme3.key have to be to make it print the above message.
Ex) 41424344454647
(This problem has multiple answers, so post your answer on the messageboard in a private thread and we will verify it for you.)

Bài này hơi dài dòng, nếu so với các bài trước đó. Chúng ta sẽ mở nó trong IDA, kết hợp giữa việc debug và đọc code để hiểu xem nó làm gì. Kết quả có thể để như sau:

Thông tin tóm tắt:

  1. Đọc 18 byte từ file CRACKME3.KEY.
  2. Can thiệp 14 byte đầu, tính tổng của các giá trị thu được bằng việc XOR 14 byte này lần lượt với các giá trị từ 65 đến 78.
  3. XOR tổng thu được với 0x12345678, chuyển kết quả về ASCII.
  4. So sánh với 4 byte cuối trong 18 byte ở trên (little-endian).

Chúng ta sẽ đi theo một hướng mất ít công sức thôi, vì giờ cũng khuya rồi.

Tạo file CRACKME3.KEY có nội dung:

123456789012345678

Đặt BP tại 00401093:

F9 và xem kết quả:

Trong đó EAX = 38373635 chính là 5678 – 4 byte cuối đọc từ file của ta, còn 12345022 sẽ là giá trị ta cần để đạt được goodboy. Tiến hành thay đổi 5678 bằng x22x50x34x12. Chạy lại:

---------------------------
Good work cracker!
---------------------------
Cracked by: pppppppppzz~~z!
Now try the next crackme!
---------------------------
OK   
---------------------------

Bài này có nhiều đáp án, nên sẽ không có → flag = xxx. như mọi khi nữa. :xô xát: too_sad

 

 

[CodeEngn] Basic RCE – Level 19

http://codeengn.com/challenges/basic/19

How many milliseconds does it take for this program to terminate

Nhìn cái icon biết ngay là AutoIT, mở bằng Exe2Aut:

Đổi từ 11.12 giây qua mili giây, công thức là x = y * 1000.

→ flag = 11120.

[CodeEngn] Basic RCE – Level 17

http://codeengn.com/challenges/basic/17

What is Name when the Key is BEDA-2F56-BC4F4368-8A71-870B
Hint : The name is 1 letter and it could be either alphabetic or numeric.
Verify your solution with the MD5 value of the Name.

Không pack, không gì cả, là một đoạn code rất tươi sáng trong mắt người xem:

Đặt BP ở 0045BBA4, F9, nhập username:key = yeuchimse:123456, nhấn Check it!:

Khá là dễ chịu vì ta thấy ngay được goodboy.

Có khá nhiều hướng cho bài này, và hướng mà mình chọn để đáp ứng cho cái nhu cầu lười biếng của mình là… dò bằng tay emo_popo_beat_brick

Tuy nhiên cũng phải tỏ ra là ta đây có chút kiến thức, nên mình sẽ patch để goodboy được show ra ngay ở badboy, thay vì cứ chạy rồi dừng ở BP emo_popo_sure

Dò một hồi thì ra Name = F.

→ flag = 800618943025315f869e4e1f09471012.