Tag: RCE

Unpacking Malware with dnSpy

Lần đầu làm chuyện ấy…

À ý tôi là làm video thuyết minh tiếng Việt, nên rất là run và có chút bẽn lẽn. Trước đây tôi làm tiếng Anh thì quen hơn nhiều, chứ còn tiếng Việt thì rất hay nói lỗi bởi vì có mấy thuật ngữ tôi không bao giờ dịch sang tiếng Việt, cũng không biết giải thích theo nghĩa tiếng Việt như thế nào nữa, cho nên rất là bối rối, mong các bạn bỏ quá cho.  Xin hay ủng hộ tôi, chỉ cần một comment “hjhj d0 ng0’k” hoặc “bố em hút rất nhiều thuốc” thôi là đã thấy yêu thương vô hạn lắm rồi. Cảm ơn

Ở trong video này chúng ta sẽ cùng sử dụng dnSpy để unpack 1 dạng unknown packer được sử dụng bởi 1 mẫu malware giả mạo Steam Client, mà một người bạn gửi cho tôi. Đây (có lẽ) là phần 1, nếu có thời gian tôi sẽ tiếp tục phân tích tiếp, và tất nhiên sẽ cố gắng làm full hd không che như thế này.

Note: Dạo gần đây tôi phát hiện khá nhiều mẫu, sử dụng .NET để làm wrapper để vượt mặt các Antivirus, vì việc detect mấy kiểu file .NET tôi có cảm giác như các hãng AV đang làm khá mơ hồ, đơn cử như trong mẫu này, file unpack ra mới chỉ có 8 AV phát hiện ra dưới dạng tên chung chung kiểu MSIL/Injector, hay Trojan.Gen,… Link scan trên virustotal có tại đây.

Thực sự rất đáng báo động, vì bây giờ đa pần các thế hệ windows mới (7,8/8.1,10) có sắn .NET đi kèm, vậy nên cái risk khá là cao. Khi mà phần lớn các AV đều chưa nhận diện tốt các loại malware thế này. .NET Wrapper có thể làm nhiệm vụ anti-AV hoặc detect xem có đang bị analysis hay không, sau đó mới drop các “em bé” thứ thiệt xuống (điển hình là trong vụ malware skype vừa qua, tôi có những mẫu đầu tiên và đã phân tích hoàn chỉnh chúng hồi đầu tháng 5, là những mẫu wrapper sử dụng Confuser/ConfuserEx). Thực sự rất là quan ngại sâu sắc lắm

Các yêu cầu về mẫu malware, xin vui lòng liên hệ qua email.

Enjoy and stay safe,

Levis

Debugging .NET Application with DnSpy

This small video is small video to test the debugging feature of dnSpy (which is created by 0xd4d as a modification + improvement of free open-source .NET Decompiler ILSpy). More information of dnSpy can be found here:
https://github.com/0xd4d/dnSpy

The debugger is source-level debugger, means that you can directly debug decompiled code, not assembly-level, and it’s really cool feature

Only a small video, nothing special, feel free to watch it:

 

Personally i think it’s the best .NET Decompiler at the moment. So come and discover it power.

 

Regards,

Levis

How to learn Reverse Engineering?

Cứ một vài ngày lại có 1 gã mới xuất hiện ở Tuts4you và hỏi rằng: “Tôi muốn học Reverse Engineering, vậy thì tôi nên bắt đầu từ đâu?”

Hmmm….

Có rất nhiều lời khuyên được đưa ra, ví dụ như ở trong bài viết trên Reddit này. Một trong những lời khuyên phổ biến nhất là háy bắt đầu với loạt hướng dẫn của Lena151. Lí do là loạt hướng dẫn này cung cấp được cái nhìn tổng quan về những thao tác phổ biến cần thực hiện, những công cụ phổ biến cung cấp đến sự hài lòng ngay lập tức. Nhưng liệu những bài hướng dẫn đó có thực sự dạy bạn về Reverse Engineering? Tôi không nghĩ như vậy

Tại sao các bài hướng dẫn của Lena151 lại không tốt

Thực tế thì tôi không phải người duy nhất nghĩ như vậy:

Tôi đã nghĩ tại sao việc này lại xảy ra. Về cá nhân tôi, tôi đã bắt đầu học Reverse Engineering bằng cách xem loạt bài hướng dẫn của Lena151. Tôi đã nghĩ rằng chúng thật tuyệt vời chỉ đến khi Daeken nói với tôi rằng đó thật sự là một phương pháp tồi tệ để học Reverse Engineering.

Lúc đầu tôi vẫn không hiểu tại sao (anh ta nói) chúng là không tốt. Loạt bài viết của Lena đã dạy cho tôi để tôi có thể crack phần mềm đầu tiên.

Và đó chính là vấn đề đấy. Bạn chỉ muốn crack phần mềm. Đó là sự thỏa mãn trong thoáng chốc. Nhưng thực tế là bạn đã học được những gì? Chạy một vài công cụ phổ biến, tìm “từ huyệt (bad boy jump” và patch nó? Wow! Bạn thật sự “l33t” (một tính từ châm biếm, kiểu “pro, khủng” khi nói các trẻ trâu) đấy.

Trong thực tế, loạt bài hướng dẫn này đã tạo ra cả một thế hệ những kẻ “muốn làm cracker”, những kẻ chỉ biết sử dụng các công cụ có sẵn, nhưng thực tế thì không biết cách động não (script kiddies). Với mỗi vấn đề gặp phải, họ cần một video hướng dẫn. Chỉ cần một khúc mắc nhỏ thôi họ cũng lập một topic để yêu cầu giúp đỡ.

Đấy mới chỉ là một phần của vấn đề.

Những video hướng dẫn tạo bởi người mới (beginner) thậm chí còn tệ hơn

Albert Einstein đã từng nói:

Tôi càng biết nhiều, thì tôi càng nhận ra rằng càng nhiều thứ tôi không biết.

The more I learn, the more I realize how much I don’t know.

Những người mới, những người xem series của Lena151 không nhận ra điều này. Họ crack được một chương trình đầu tay và họ tự nhận mình là Reverser. Và điều tệ hơn nữa là, họ cố gắng chia sẻ “kiến thức” của họ bằng cách làm vô số những video hướng dẫn “siêu siêu tệ” để cho thế hệ những kẻ “muốn làm cracker” sau xem.

Tôi đã từng xem một video dài 15 phút có tiêu đề “Làm cách nào để Unpack CryptoObfuscator”. Bạn biết nó như thế nào ko? Bạn kéo-và-thả file vào trong de4dot. Chỉ đơn giản như vậy thôi. Vâng, cóngười đã làm một video 15p để dạy bạn làm như thế đấy.

Vậy, cách tiếp cận khác là gì?

Blog ReverseWithme khuyên nên học:

1. x86 Assembly (Giống như hệ thống điện và dây dẫn trong một chiếc ô tô)

2. Cách mà hệ điều hành hoạt động và cách chúng quản lý bộ nhớ (giống như động cơ xe)

3. Quá trình biên dịch từ code C thành Assembly (điều này tương tự như biết cách làm sao để lắp rap một chiếc xe)

4. Chu trình của một mã nhị phân (“The lìfe òf a binary”, khó dịch sát nghĩa) (tương tự như việc hiểu tất cả mọi thứ xảy ra trong chiếc xe từ khi tra khóa vào ổ để khởi động cho đến khi tắt máy).

Tôi cũng không nghĩ đây là hướng đi đúng đắn.

Giống như là bạn học một ngoại ngữ bằng cách đọc một quyển từ điển. Bắt đầu với chữ “a”, và một khi bạn kết thúc chữ “z”, bạn biết tất cả các từ. Có thể. Nhưng bạn sẽ không thể nào đặt được một câu hoàn chỉnh, hay giao tiếp được với một người bản xứ.

Lấy 1 ví dụ: Tôi đã làm Reverse các file .NET trong vòng 10 năm rồi. Tôi đã viết một vài unpackers cho một vài .NET Protectior đơn giản. Và đến giờ tôi vẫn không thể hiểu đưọc IL assembly một cách “thấu đáo” (by heart). Tại sao? Bởi vì tôi không cần phải thế? Dạng biểu diễn dễ nhớ của “branch-if-equal” (rẽ nhánh nếu bằng nhau, một chỉ lệnh IL) là gì? Nó là be, beq, hay bre? Nó lấy 1 hay 2 tham số từ stack ra?. Tôi không biết. Nếu khi nào tôi cần biết, thì chỉ việc tìm kiếm trên Google.

Vâng, để trở thành 1 Reverse tốt, bạn cần phải nắm vững hầu hết những mảng kiến thức đã nêu ở trên. Nhưng bạn không cần phải biết tất cả khi bạn mới bắt đầu tìm hiểu.

Nói tôi biết câu trả lời đi, mẹ kiếp! (Dịch nguyên văn)

À, hãy bắt đầu với loạt hướng dẫn của Lena151. Đúng, tôi nói chúng không tốt, nhưng đó là điều tốt nhất chúng ta có ở đây. Và nếu bạn làm theo một vài lời khuyên thêm dưới dây, bạn sẽ có một khởi đầu khá ổn:\

  • Học cách nghĩ cho bản thân mình. Đây là phần quan trọng nhất. Đừng chỉ mù quáng làm theo những bài hướng dẫn, mà hãy cố gắng để hiểu rằng tại sao lại như vậy, và điều đó xảy ra bằng cách nào.
  • Học cách tìm kiếm. Đa phần các câu hỏi đều đã được trả lời, bạn chỉ cần kiếm câu trả lời mà thôi. Hãy luôn nhớ rằng “Google is your friend”!
  • Tìm hiểu về các công cụ mà bạn có. Bạn không cần phải biết đến từng tùy chọn, và các tính năng của công cụ bạn có. Đa phần người ta chỉ sử dụng khoảng 10% tính năng của Microsoft Excel. Dân chuyên nghiệp thì có thể dùng đến khoảng 20%. Điều này cũng tương tự với các công cụ RE. Nếu bạn có thể sử dụng thuần thục 10% các tính năng của Olly hay IDA, là bạn đã có thể làm rất ổn rồi.
  • Và điều cuối cùng, hay luôn vui vẻ và đam mê! Không có gì hủy diệt năng suất làm việc của bạn nhanh hơn sự nhàm chán. Nếu như vấn đề quá khó, thì hãy bỏ nó đi, thử một vài thứ khác, và quay lại nghiên cứu tiếp vấn đề đó sau.

(Dịch từ bài viết trên blog của kao – Translated from English article at kao’s blog)

[Show-up] WhiteHat GrandPrix Final RE300 Challenge

Xin chào các bạn

Mấy bữa trước tôi nhận dược thông báo của BTC kêu réo là viết write-up cho mấy challenge trong phần thi jeopardy. Thực ra cũng không muốn viết đâu nhưng vì BTC gọi điện nhiều quá nên cũng đã write-up tạm bợ trong forum WhiteHat rồi. Nhưng tính tôi là thế, đã không làm thì thôi, đã làm thì phải làm cho hẳn hoi 1 tí. Âu cũng là để lưu giữ lại chút kỉ niệm chơi CTF, và chia sẻ với các bạn.

Tại sao lại gọi là “Show up”? Đơn giản bởi vì tôi không “Write” (viết) mà sẽ “show” (trình diễn, trình bày) cho các bạn thấy. Write-up nhiều quá nên chán rồi, nếu có viết ra nữa thì rất gượng gạo và mang tính đối phó, sẽ không kích thích cho lắm. Đây là 1 file video ghi lại quá trình phân tích, trace và tìm ra flag của bài RE300 (Link Download tại ĐÂY). Target có vẻ như là 1 phần của con malware nào đó được trích xuất ra, edit để embed flag vào trong, vì vậy các chương trình AV có thể sẽ detect và clean ngay lập tức (không phải lỗi của tôi, hãy hỏi BTC, lol xD).

Thôi không nói nhiều nữa, hãy cùng xem video phía dưới nhé:

Anyway, cảm ơn BTC đã tổ chức cuộc thi này để mọi người có thể “xích lại gần nhau hơn”, và tôi cũng đã quen được rất nhiều người bạn mới, và họ đều rất tuyệt vời.

Enjoy and best regards,

Levis

Radare – A Modern Reverse Engineering Framework

Radare is name of a Reverse Engineering Framework with full-featured tools, libraries which aims to create a completely reverse engineering environment for Reversers at any platform. I saw many members of MMD research group was using stuffs in this framework. and they’re really powerful. I think that in future, radare will be the most commonly used toolkit in Reverse Engineering.

Homepage: http://www.radare.org/

Features:
Multi-architecture and multi-platform
GNU/Linux, Android, *BSD, OSX, iPhoneOS, Windows{32,64} and Solaris
i8080, 8051, x86{16,32,64}, avr, arc{4,compact}, arm{thumb,neon,aarch64}, c55x+, dalvik, ebc, gb, java, sparc, mips, nios2, powerpc, whitespace, brainfuck, malbolge, z80, psosvm, m68k, msil, sh, snes, gb, dcpu16, csr, arc
pe{32,64}, te, [fat]mach0{32,64}, elf{32,64}, bios/uefi, dex and java classes
Highly scriptable
Vala, Go, Python, Guile, Ruby, Perl, Lua, Java, JavaScript, sh, ..
batch mode and native plugins with full internal API access
native scripting based in mnemonic commands and macros
Hexadecimal editor
64bit offset support with virtual addressing and section maps
Assemble and disassemble from/to many architectures
colorizes opcodes, bytes and debug register changes
print data in various formats (int, float, disasm, timestamp, ..)
search multiple patterns or keywords with binary mask support
checksumming and data analysis of byte blocks
IO is wrapped
support Files, disks, processes and streams
virtual addressing with sections and multiple file mapping
handles gdb:// and rap:// remote protocols

Filesystems support

allows to mount ext2, vfat, ntfs, and many others
support partition types (gpt, msdos, ..)
Debugger support
gdb remote and brainfuck debugger support
software and hardware breakpoints
tracing and logging facilities
Diffing between two functions or binaries
graphviz friendly code analysis graphs
colorize nodes and edges
Code analysis at opcode, basicblock, function levels
embedded simple virtual machine to emulate code
keep track of code and data references
function calls and syscall decompilation
function description, comments and library signatures
And more…

Download:
Binaries (compiled package) download link:
Binary packages for various platform download page (No ads)
Source code:
Source Code download section (No ads)
Documentations:
Documentation from official website(No ads)
Screenshot radare2 (r2 disassembler) running on Linux:

[Write-up] WhiteHat Grand Prix RE300 – x86 crackme

[Write-up] WhiteHat Grand Prix RE300 – x86 crackme

Công cụ sử dụng : OllyDbg 2,Protection_ID(hoặc exeinfoPE)

Link download re300: https://dl.dropboxusercontent.com/u/97334260/re300.7z

Được 1 file re300.exe, dùng một số công cụ để scan, detect thì thấy rằng file không bị pack, được viết bằng MSVC++  và còn nguyên cả debug symbol trong đó.


-=[ ProtectionID v0.6.5.5 OCTOBER]=-

(c) 2003-2013 CDKiLLER & TippeX

Build 31/10/13-21:09:09

Ready...

Scanning -> C:\Users\Levis\Desktop\re300.exe

File Type : 32-Bit Exe (Subsystem : Win CUI / 3), Size : 15872 (03E00h) Byte(s)

[File Heuristics] -> Flag : 00000100000001001101000000000000 (0x0404D000)

[Entrypoint Section Entropy] : 6.31

[Debug Info]

Characteristics : 0x0 | TimeDateStamp : 0x540EBCF2 | MajorVer : 0 / MinorVer : 0 -> (0.0)

Type : 2 -> CodeView | Size : 0x55 (85)

AddressOfRawData : 0x3428 | PointerToRawData : 0x2228

CvSig : 0x53445352 | SigGuid EE046B68-C49F-4C26-8AD6C890ABE27C1E

Age : 0x1 | Pdb : C:\Users\c4sp3_000\Desktop\WHGP\Source\RE-2\Release\RE-2.pdb

[CompilerDetect] -> Visual C/C++

[!] File appears to have no protection or is using an unknown protection

- Scan Took : 0.172 Second(s) [0000000ACh tick(s)] [533 scan(s) done]

 

Khi chạy thử chương trình sẽ yêu cầu nhập username và password. Username đã được cho sẵn: WhiteHat_Grand_Prix_2014. password đánh bừa vào: 123456789, một thông báo hiện ra: “Ooop!!!”. Việc cần làm là tìm ra password cho username đó, và password đó cũng là flag.

Tiến hành load file vào Olly, bấm chuột phải chọn Search for -> All Referenced Strings. Chúng ta thấy có một số string đáng chú ý: “Enter your username:”,”Enter your password:”, “Goodjob!”,”Ooop!!!”. Bấm vào bất kì string nào trong các string đó, chúng ta sẽ trở lại cửa sổ CPU trong olly, và các phần code ở đó chính là nằm trong hàm main() của chương trình. Chuyển lên code phía trên đầu của hàm:


00F712B0   $  55            PUSH EBP

00F712B1   .  8BEC          MOV EBP,ESP

00F712B3   .  83E4 F8       AND ESP,FFFFFFF8                              QWORD (8-byte) stack alignment

00F712B6   .  81EC E8000000 SUB ESP,0E8

00F712BC   .  A1 1850F700   MOV EAX,DWORD PTR DS:[re300.0F75018]

00F712C1   .  33C4          XOR EAX,ESP

00F712C3   .  898424 E40000 MOV DWORD PTR SS:[ESP+0E4],EAX

//---------Lấy username nhập vào---------------------------------------------

00F712CA   .  8B0D 6430F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cout@std@@3 ASCII "X!m]"

00F712D0   .  56            PUSH ESI

00F712D1   .  57            PUSH EDI

00F712D2   .  BA EC31F700   MOV EDX,OFFSET re300.00F731EC                 ASCII "Enter your username: 

"

00F712D7   .  E8 24070000   CALL re300.00F71A00

00F712DC   .  8B0D 6030F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cin@std@@3V

00F712E2   .  8D9424 800000 LEA EDX,[ESP+80]     <-- Địa chỉ lưu username

00F712E9   .  E8 42090000   CALL re300.00F71C30

///-------------------lấy password nhập vào------------------------------

00F712EE   .  8B0D 6430F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cout@std@@3 ASCII "X!m]"

00F712F4   .  BA 0432F700   MOV EDX,OFFSET re300.00F73204                 ASCII "Enter your password: 

"

00F712F9   .  E8 02070000   CALL re300.00F71A00

00F712FE   .  8B0D 6030F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cin@std@@3V

00F71304   .  8D5424 18     LEA EDX,[ESP+18] <-- Địa chỉ lưu password

00F71308   .  E8 23090000   CALL re300.00F71C30

///--------------Kiểm tra password nhập vào------------------------------

00F7130D   .  8D4C24 18     LEA ECX,[ESP+18]

00F71311   .  8D51 01       LEA EDX,[ECX+1]

00F71314   >  8A01          MOV AL,BYTE PTR DS:[ECX]

00F71316   .  41            INC ECX

00F71317   .  84C0          TEST AL,AL

00F71319   .^ 75 F9         JNE SHORT re300.00F71314

00F7131B   .  2BCA          SUB ECX,EDX

00F7131D   .  83F9 10       CMP ECX,10                        <--- Độ dài pass = 0x10?

00F71320   .  0F85 53010000 JNE re300.00F71479 <-- Nhảy nếu độ dài pass != 0x10

Đoạn code tiếp theo không quan trọng, chỉ là tìm và chuyển tất cả các kí tự chữ in hoa có trong username về kí tự viết thường:


00F71326   .  33C9          XOR ECX,ECX

00F71328   >  8A440C 18     MOV AL,BYTE PTR SS:[ECX+ESP+18]

00F7132C   .  3C 5B         CMP AL,5B                                     Switch (cases 41..5A, 2 exits)

00F7132E   .  7D 0A         JGE SHORT re300.00F7133A

00F71330   .  3C 40         CMP AL,40

00F71332   .  7E 06         JLE SHORT re300.00F7133A

00F71334   .  04 20         ADD AL,20                                     Cases 41 ('A'), 42 ('B'), 43 ('C'), 44 ('D'), 4...

00F71336   .  88440C 18     MOV BYTE PTR SS:[ECX+ESP+18],AL

00F7133A   >  8A440C 19     MOV AL,BYTE PTR SS:[ECX+ESP+19]               Default case of switch re300.0F7132C

00F7133E   .  3C 5B         CMP AL,5B                                     Switch (cases 41..5A, 2 exits)

00F71340   .  7D 0A         JGE SHORT re300.00F7134C

00F71342   .  3C 40         CMP AL,40

00F71344   .  7E 06         JLE SHORT re300.00F7134C

00F71346   .  04 20         ADD AL,20                                     Cases 41 ('A'), 42 ('B'), 43 ('C'), 44 ('D'), 4...

00F71348   .  88440C 19     MOV BYTE PTR SS:[ECX+ESP+19],AL

00F7134C   >  8A440C 1A     MOV AL,BYTE PTR SS:[ECX+ESP+1A]               Default case of switch re300.0F7133E

00F71350   .  3C 5B         CMP AL,5B                                     Switch (cases 41..5A, 2 exits)

00F71352   .  7D 0A         JGE SHORT re300.00F7135E

00F71354   .  3C 40         CMP AL,40

00F71356   .  7E 06         JLE SHORT re300.00F7135E

00F71358   .  04 20         ADD AL,20                                     Cases 41 ('A'), 42 ('B'), 43 ('C'), 44 ('D'), 4...

00F7135A   .  88440C 1A     MOV BYTE PTR SS:[ECX+ESP+1A],AL

00F7135E   >  8A440C 1B     MOV AL,BYTE PTR SS:[ECX+ESP+1B]               Default case of switch re300.0F71350

00F71362   .  3C 5B         CMP AL,5B                                     Switch (cases 41..5A, 2 exits)

00F71364   .  7D 0A         JGE SHORT re300.00F71370

00F71366   .  3C 40         CMP AL,40

00F71368   .  7E 06         JLE SHORT re300.00F71370

00F7136A   .  04 20         ADD AL,20                                     Cases 41 ('A'), 42 ('B'), 43 ('C'), 44 ('D'), 4...

00F7136C   .  88440C 1B     MOV BYTE PTR SS:[ECX+ESP+1B],AL

00F71370   >  83C1 04       ADD ECX,4                                     Default case of switch re300.0F71362

00F71373   .  83F9 10       CMP ECX,10

00F71376   .^ 7C B0         JL SHORT re300.00F71328

Đoạn code tiếp theo, để kiểm tra xem các kí tự trong username có phải là các kí tự biểu diễn hexadecimal hay không (“0″….”9″,”a”…”f”):


00F71378   .  33C9          XOR ECX,ECX

00F7137A   .  8D9B 00000000 LEA EBX,[EBX]

00F71380   >  8A440C 18     MOV AL,BYTE PTR SS:[ECX+ESP+18]

00F71384   .  3C 30         CMP AL,30

00F71386   .  0F8C ED000000 JL re300.00F71479

00F7138C   .  3C 66         CMP AL,66

00F7138E   .  0F8F E5000000 JG re300.00F71479

00F71394   .  3C 39         CMP AL,39

00F71396   .  7E 08         JLE SHORT re300.00F713A0

00F71398   .  3C 61         CMP AL,61

00F7139A   .  0F8C D9000000 JL re300.00F71479

00F713A0   >  41            INC ECX

00F713A1   .  83F9 10       CMP ECX,10

00F713A4   .^ 7C DA         JL SHORT re300.00F71380

Như vậy đến đây ta biết được, password là 1 chuỗi gồm 16 kí tự (0x10), và các kí tự đều nằm trong nhóm các kí tự biểu diễn hexadecimal (“0123456789abcdef”).

Đoạn code tiếp theo thực hiện convert password (dạng hexadecimal) thành 1 byte array. Với password là 16 kí tự hexadecimal ta có 1 byte array 8 phần tử:


00F713C4   .  8A4454 18     MOV AL,BYTE PTR SS:[EDX*2+ESP+18] <-- địa chỉ lưu password

00F713C8   .  C64414 10 00  MOV BYTE PTR SS:[EDX+ESP+10],0      <-- Địa chỉ lưu byte array

00F713CD   .  3C 60         CMP AL,60

00F713CF   .  7E 04         JLE SHORT re300.00F713D5

00F713D1   .  2C 57         SUB AL,57

00F713D3   .  EB 02         JMP SHORT re300.00F713D7

00F713D5   >  2C 30         SUB AL,30

00F713D7   >  8A4C54 19     MOV CL,BYTE PTR SS:[EDX*2+ESP+19]

00F713DB   .  C0E0 04       SHL AL,4

00F713DE   .  884414 10     MOV BYTE PTR SS:[EDX+ESP+10],AL

00F713E2   .  80F9 60       CMP CL,60

00F713E5   .  7E 05         JLE SHORT re300.00F713EC

00F713E7   .  80E9 57       SUB CL,57

00F713EA   .  EB 03         JMP SHORT re300.00F713EF

00F713EC   >  80E9 30       SUB CL,30

00F713EF   >  02C1          ADD AL,CL

00F713F1   .  884414 10     MOV BYTE PTR SS:[EDX+ESP+10],AL 

00F713F5   .  42            INC EDX

00F713F6   .  83FA 08       CMP EDX,8 <-- Kiểm tra độ dài

00F713F9   .^ 7C C3         JL SHORT re300.00F713BE

VD như nhập password là “0123456789ABCDEF” ta sẽ có 1 byte array (0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF) lưu tại [esp+10]

Đoạn code tiếp theo thực hiện 1 thuật toán cipher đơn giản:


00F713FB   .  33FF          XOR EDI,EDI

00F713FD   .  8D49 00       LEA ECX,[ECX]

00F71400   >  8A4C3C 10     MOV CL,BYTE PTR SS:[EDI+ESP+10] <-- pass byte array

00F71404   .  33C0          XOR EAX,EAX

00F71406   .  85F6          TEST ESI,ESI            <-- Độ dài của username

00F71408   .  7E 1E         JLE SHORT re300.00F71428

00F7140A   .  8D9B 00000000 LEA EBX,[EBX]

00F71410   >  0FBE9404 8000 MOVSX EDX,BYTE PTR SS:[EAX+ESP+80] <-- lấy từng char trong username

00F71418   .  0FB6C9        MOVZX ECX,CL  

00F7141B   .  33D1          XOR EDX,ECX <-- Thực hiện xor

00F7141D   .  40            INC EAX <-- Chuyển đến char tiếp theo trong username

00F7141E   .  8A8A 7032F700 MOV CL,BYTE PTR DS:[EDX+re300.0F73270] <-- Lấy giá trị trong 1 table với index là kết quả vừa xor được

00F71424   .  3BC6          CMP EAX,ESI <-- Kiểm tra xem đã hết kí tự trong username chưa?

00F71426   .^ 7C E8         JL SHORT re300.00F71410

00F71428   >  884C3C 10     MOV BYTE PTR SS:[EDI+ESP+10],CL <-- Lưu lại vào pass byte array

00F7142C   .  47            INC EDI       <-- Chuyển đến phần tử tiếp theo trong pass byte array

00F7142D   .  83FF 08       CMP EDI,8 <-- Kiểm tra xem đã hết phần tử trong pass byte array chưa?

00F71430   .^ 7C CE         JL SHORT re300.00F71400 <-- Nếu chưa hết, thực hiện lại vòng lặp

Quan trọng là ở code này:


00F7141E   .  8A8A 7032F700 MOV CL,BYTE PTR DS:[EDX+re300.0F73270] <-- Lấy giá trị trong 1 table với index là kết quả vừa xor được

chúng ta có 1 table với khoảng 256 phần tử. Đưa chuột vào lệnh này và bấm chuột phải chọn Follow In Dump -> Address Contstant. Phần tử của table này bắt đầu từ 70 3B 68 F3 4D DB… và kết thúc tại …73 24 E9 FE 11 A7 36 C6. Hãy copy lại toàn bộ các giá trị của table này.

Chúng ta có thể mô phỏng lại code như sau(với username là WhiteHat_Grand_Prix_2014, password 0123456789ABCDEF):


table = [0x70, 0x3B, 0x68, 0xF3, 0x4D, 0xDB, 0xA4, 0xB7, 0x46, 0xBE, 0x2B, 0x38, 0xE1, 0xFA, 0x6B, 0x50, 0xFC, 0xE5, 0xF7, 0x62, 0xB0, 0x77, 0x5A, 0x5C, 0xD0, 0x8C, 0xD5, 0x1A, 0x87, 0xDC, 0x12, 0x3D, 0xCD, 0x3A, 0x9B, 0x7B, 0x4A, 0xEC, 0x4B, 0x1E, 0x63, 0x1D, 0x60, 0xC2, 0x78, 0xAD, 0xF6, 0x94, 0x23, 0xBC, 0x97, 0x2D, 0x8D, 0xE3, 0x8E, 0x69, 0x88, 0x66, 0x2C, 0x98, 0x9D, 0xCB, 0x1B, 0xFB, 0x20, 0xAA, 0x5D, 0xB1, 0x05, 0x61, 0x52, 0xF9, 0x1F, 0xBB, 0x04, 0xFF, 0x31, 0x10, 0x89, 0x55, 0xF1, 0x82, 0x7A, 0x45, 0x25, 0x49, 0x6F, 0x64, 0xED, 0x18, 0x9E, 0x1C, 0xD6, 0xD3, 0x9A, 0xF4, 0xC9, 0xC0, 0x0F, 0x0A, 0xE2, 0x28, 0x7E, 0x33, 0xFD, 0x34, 0xA0, 0x2F, 0x91, 0x57, 0xDD, 0x03, 0x27, 0xB6, 0x6D, 0xCE, 0xBF, 0x01, 0x16, 0x43, 0xA3, 0x59, 0xEF, 0x4C, 0xDF, 0xD1, 0x71, 0x15, 0xE0, 0x7F, 0x47, 0x85, 0x48, 0xC4, 0xDE, 0x56, 0x76, 0x4F, 0x53, 0x75, 0x5B, 0xB9, 0x95, 0x2A, 0x09, 0x5F, 0x92, 0x32, 0xD2, 0x6C, 0x08, 0x26, 0xA5, 0x8A, 0x58, 0x07, 0xF5, 0x51, 0xE8, 0x9F, 0xAB, 0xD8, 0xC3, 0xB2, 0xEE, 0xC7, 0x81, 0x44, 0x17, 0x80, 0x0D, 0xD7, 0x29, 0xE4, 0xA9, 0x83, 0xC1, 0x99, 0xE6, 0xF0, 0x0E, 0x6A, 0xD4, 0xA1, 0x74, 0x0B, 0xEB, 0x3F, 0xAE, 0x0C, 0xA6, 0x41, 0xB4, 0x93, 0xCA, 0x30, 0x35, 0xAF, 0x79, 0x72, 0xAC, 0xA8, 0x7C, 0xBD, 0x84, 0x14, 0xB5, 0xB3, 0xCC, 0xCF, 0x9C, 0x13, 0xB8, 0xC5, 0x40, 0x39, 0xE7, 0x8B, 0x22, 0x02, 0x65, 0xDA, 0x96, 0xF2, 0x90, 0x54, 0x06, 0xEA, 0x2E, 0x21, 0x42, 0xF8, 0xC8, 0x3E, 0x3C, 0x00, 0x5E, 0x19, 0xD9, 0x67, 0x86, 0x8F, 0xA2, 0x7D, 0x4E, 0x6E, 0x37, 0xBA, 0x73, 0x24, 0xE9, 0xFE, 0x11, 0xA7, 0x36, 0xC6]

pass_bytes_array=[0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF]

username="WhiteHat_Grand_Prix_2014"

for i in range(0,len(pass_bytes_array)):

                xorval=pass_bytes_array[i]^ord(username[0])

                for j in range(1,len(username)):

                                xorval=table[xorval]^ord(username[j])

                pass_bytes_array[i]=table[xorval]

print(pass_bytes_array)

Code tiếp theo thực hiện đưa 2 constant vào memory và so sánh từng byte của các constant này với từng byte của byte pass array, nếu giống nhau thì đưa ra thông báo đúng, nếu khác nhau thì đưa ra thông báo sai:


00F71432   .  C74424 08 2FE MOV DWORD PTR SS:[ESP+8],BAA5E82F <-- Constant 1

00F7143A   .  C74424 0C A4A MOV DWORD PTR SS:[ESP+0C],BD0FA4A4 <-- Constant 2

00F71442   .  33C9          XOR ECX,ECX

00F71444   >  8A440C 08     MOV AL,BYTE PTR SS:[ECX+ESP+8]       <-- Chuyển từng byte vào al

00F71448   .  3A440C 10     CMP AL,BYTE PTR SS:[ECX+ESP+10] <-- So sánh với byte pass array

00F7144C   .  75 0D         JNE SHORT re300.00F7145B <-- Nếu sai thì đưa ra thông báo "Ooop!!!"

00F7144E   .  41            INC ECX                      <-- Chuyển đến phần tử tiếp theo

00F7144F   .  83F9 08       CMP ECX,8           <-- Kiểm tra xem đã hết phần tử hay chưa

00F71452   .^ 7C F0         JL SHORT re300.00F71444               <-- Tiếp tục so sánh

00F71454   .  BA 1C32F700   MOV EDX,OFFSET re300.00F7321C                 ASCII "Goodjob!!

"

00F71459   .  EB 05         JMP SHORT re300.00F71460

00F7145B   >  BA 2832F700   MOV EDX,OFFSET re300.00F73228                 ASCII "Ooop!!!

"

00F71460   >  8B0D 6430F700 MOV ECX,DWORD PTR DS:[<&MSVCP110.?cout@std@@3

00F71466   .  E8 95050000   CALL re300.00F71A00                 <-- Đưa ra màn hình

2 Constant là 0xBAA5E82F và 0xBD0FA4A4. Khi đưa vào memory ta sẽ có 1 byte array mới:

(0x2F,0xE8,0xA5,0xBA,0xA4,0xA4,0x0F,0xBD), tạm gọi là constant byte array.

Như vậy thuật toán để tìm ra được pass tương ứng với username đã cho (cũng chính là flag), thì ta phải thực hiện bruteforce từng giá trị trong password (8 giá trị tương đương với 16 kí tự hexadecimal). Có nghĩa là bruteforce trong khoảng 0x00 -> 0xFF, thực hiện thuật toán cipher phía trên và so sánh kết quả với constant byte array.


#declare table

table = [0x70, 0x3B, 0x68, 0xF3, 0x4D, 0xDB, 0xA4, 0xB7, 0x46, 0xBE, 0x2B, 0x38, 0xE1, 0xFA, 0x6B, 0x50, 0xFC, 0xE5, 0xF7, 0x62, 0xB0, 0x77, 0x5A, 0x5C, 0xD0, 0x8C, 0xD5, 0x1A, 0x87, 0xDC, 0x12, 0x3D, 0xCD, 0x3A, 0x9B, 0x7B, 0x4A, 0xEC, 0x4B, 0x1E, 0x63, 0x1D, 0x60, 0xC2, 0x78, 0xAD, 0xF6, 0x94, 0x23, 0xBC, 0x97, 0x2D, 0x8D, 0xE3, 0x8E, 0x69, 0x88, 0x66, 0x2C, 0x98, 0x9D, 0xCB, 0x1B, 0xFB, 0x20, 0xAA, 0x5D, 0xB1, 0x05, 0x61, 0x52, 0xF9, 0x1F, 0xBB, 0x04, 0xFF, 0x31, 0x10, 0x89, 0x55, 0xF1, 0x82, 0x7A, 0x45, 0x25, 0x49, 0x6F, 0x64, 0xED, 0x18, 0x9E, 0x1C, 0xD6, 0xD3, 0x9A, 0xF4, 0xC9, 0xC0, 0x0F, 0x0A, 0xE2, 0x28, 0x7E, 0x33, 0xFD, 0x34, 0xA0, 0x2F, 0x91, 0x57, 0xDD, 0x03, 0x27, 0xB6, 0x6D, 0xCE, 0xBF, 0x01, 0x16, 0x43, 0xA3, 0x59, 0xEF, 0x4C, 0xDF, 0xD1, 0x71, 0x15, 0xE0, 0x7F, 0x47, 0x85, 0x48, 0xC4, 0xDE, 0x56, 0x76, 0x4F, 0x53, 0x75, 0x5B, 0xB9, 0x95, 0x2A, 0x09, 0x5F, 0x92, 0x32, 0xD2, 0x6C, 0x08, 0x26, 0xA5, 0x8A, 0x58, 0x07, 0xF5, 0x51, 0xE8, 0x9F, 0xAB, 0xD8, 0xC3, 0xB2, 0xEE, 0xC7, 0x81, 0x44, 0x17, 0x80, 0x0D, 0xD7, 0x29, 0xE4, 0xA9, 0x83, 0xC1, 0x99, 0xE6, 0xF0, 0x0E, 0x6A, 0xD4, 0xA1, 0x74, 0x0B, 0xEB, 0x3F, 0xAE, 0x0C, 0xA6, 0x41, 0xB4, 0x93, 0xCA, 0x30, 0x35, 0xAF, 0x79, 0x72, 0xAC, 0xA8, 0x7C, 0xBD, 0x84, 0x14, 0xB5, 0xB3, 0xCC, 0xCF, 0x9C, 0x13, 0xB8, 0xC5, 0x40, 0x39, 0xE7, 0x8B, 0x22, 0x02, 0x65, 0xDA, 0x96, 0xF2, 0x90, 0x54, 0x06, 0xEA, 0x2E, 0x21, 0x42, 0xF8, 0xC8, 0x3E, 0x3C, 0x00, 0x5E, 0x19, 0xD9, 0x67, 0x86, 0x8F, 0xA2, 0x7D, 0x4E, 0x6E, 0x37, 0xBA, 0x73, 0x24, 0xE9, 0xFE, 0x11, 0xA7, 0x36, 0xC6]

#declare result value to compare

resultval = [0x2f,0xE8,0xA5,0xBA,0xA4,0xA4,0x0F,0xBD]

#declare username

username = 'WhiteHat_Grand_Prix_2014'

global hx              #hx is variable which keeps the xor'ed value

flag =''   #initalize variable to store flag

for i in range(0,len(resultval)): #for each value in resultval

 for j in range(0,255):                         #initalize value to bruteforce. Value in range 0x00 -> 0xFF

    hx = j^ord(username[0])                              #Set value to hx at the first time

    for n in range(1,len(username)): #manipulate each character in username

        hx=table[hx]^ord(username[n])#Xor'em

        if(table[hx]==resultval[i]):#if value in table equal to value in resultval

             flag+=hex(j)[2:]                                #then add the value of j to flag in hexadecimal string

print(flag)                            #print out the flag

chạy code và có kết quả: e76f63389797f52f -> flag: Flag{e76f63389797f52f}

Bổ sung:

Code của anh Bolzano, không cần bruteforce (đây có thể xem là code tốt nhất để solve bài này):


list_values = [0x70, 0x3B, 0x68, 0xF3, 0x4D, 0xDB, 0xA4, 0xB7, 0x46, 0xBE, 0x2B, 0x38, 0xE1, 0xFA, 0x6B, 0x50, 0xFC, 0xE5, 0xF7, 0x62, 0xB0, 0x77, 0x5A, 0x5C, 0xD0, 0x8C, 0xD5, 0x1A, 0x87, 0xDC, 0x12, 0x3D, 0xCD, 0x3A, 0x9B, 0x7B, 0x4A, 0xEC, 0x4B, 0x1E, 0x63, 0x1D, 0x60, 0xC2, 0x78, 0xAD, 0xF6, 0x94, 0x23, 0xBC, 0x97, 0x2D, 0x8D, 0xE3, 0x8E, 0x69, 0x88, 0x66, 0x2C, 0x98, 0x9D, 0xCB, 0x1B, 0xFB, 0x20, 0xAA, 0x5D, 0xB1, 0x05, 0x61, 0x52, 0xF9, 0x1F, 0xBB, 0x04, 0xFF, 0x31, 0x10, 0x89, 0x55, 0xF1, 0x82, 0x7A, 0x45, 0x25, 0x49, 0x6F, 0x64, 0xED, 0x18, 0x9E, 0x1C, 0xD6, 0xD3, 0x9A, 0xF4, 0xC9, 0xC0, 0x0F, 0x0A, 0xE2, 0x28, 0x7E, 0x33, 0xFD, 0x34, 0xA0, 0x2F, 0x91, 0x57, 0xDD, 0x03, 0x27, 0xB6, 0x6D, 0xCE, 0xBF, 0x01, 0x16, 0x43, 0xA3, 0x59, 0xEF, 0x4C, 0xDF, 0xD1, 0x71, 0x15, 0xE0, 0x7F, 0x47, 0x85, 0x48, 0xC4, 0xDE, 0x56, 0x76, 0x4F, 0x53, 0x75, 0x5B, 0xB9, 0x95, 0x2A, 0x09, 0x5F, 0x92, 0x32, 0xD2, 0x6C, 0x08, 0x26, 0xA5, 0x8A, 0x58, 0x07, 0xF5, 0x51, 0xE8, 0x9F, 0xAB, 0xD8, 0xC3, 0xB2, 0xEE, 0xC7, 0x81, 0x44, 0x17, 0x80, 0x0D, 0xD7, 0x29, 0xE4, 0xA9, 0x83, 0xC1, 0x99, 0xE6, 0xF0, 0x0E, 0x6A, 0xD4, 0xA1, 0x74, 0x0B, 0xEB, 0x3F, 0xAE, 0x0C, 0xA6, 0x41, 0xB4, 0x93, 0xCA, 0x30, 0x35, 0xAF, 0x79, 0x72, 0xAC, 0xA8, 0x7C, 0xBD, 0x84, 0x14, 0xB5, 0xB3, 0xCC, 0xCF, 0x9C, 0x13, 0xB8, 0xC5, 0x40, 0x39, 0xE7, 0x8B, 0x22, 0x02, 0x65, 0xDA, 0x96, 0xF2, 0x90, 0x54, 0x06, 0xEA, 0x2E, 0x21, 0x42, 0xF8, 0xC8, 0x3E, 0x3C, 0x00, 0x5E, 0x19, 0xD9, 0x67, 0x86, 0x8F, 0xA2, 0x7D, 0x4E, 0x6E, 0x37, 0xBA, 0x73, 0x24, 0xE9, 0xFE, 0x11, 0xA7, 0x36, 0xC6]

dict_values = {}

for i in xrange(0,len(list_values)):

    dict_values[i] = list_values[i]

inverted_dict = dict([[v,k] for k,v in dict_values.items()])

szUserName = 'WhiteHat_Grand_Prix_2014'

def trace_char(char, szUserName):

    lenUserName = len(szUserName)

    for i in xrange(0,lenUserName):

        char = inverted_dict[char] ^ ord(szUserName[lenUserName-i-1])

    return str(hex(char))[2:]

print trace_char(0x2f, szUserName) + trace_char(0xe8, szUserName) + trace_char(0xa5, szUserName) + trace_char(0xba, szUserName) + trace_char(0xa4, szUserName) + trace_char(0xa4, szUserName) + trace_char(0x0f, szUserName) + trace_char(0xbd, szUserName)

 

Enjoy and best regards

Levis