Archive for the ‘Uncategorized’ Category


Ở các phần trước, tôi đã hướng dẫn chi tiết quá trình unpack một unpackme được packed bởi tElock có áp dụng kĩ thuật chuyển hướng IAT (IAT redirected). Trong các phần tới đây, nếu có thời gian tôi sẽ tiếp tục nâng dần độ khó của packer lên. Với phần này, tôi sẽ dành thời gian để thực hành với một unpackme khác là UnPackMe_YodasCrypter1.3.e.exe.

Để đảm bảo cho quá trình làm việc với các packers, OllyDbg cần được trang bị các plugins cần thiết để tránh bị phát hiện bởi các cơ chế anti-debug. Các plugins thì các bạn có thể tìm đọc trong các phần tôi viết về Anti-Debug hoặc tìm hiểu thêm thông qua các trang khác như tuts4you.com, v.v…

Download toàn bộ bài viết tại đây:

https://mega.nz/#!a0kX0ISC!Se1ABV9eySLlkpn3hYIL4OSzBemQYPeYRWkzNRj3Nug

Regards,

m4n0w4r

Advertisements

OllyDbg_tut31

Posted: December 16, 2017 in OllyDbg_tut31, Uncategorized
Tags: ,

Đã quá lâu để ai đó còn nhớ về một bộ tut còn đang dang dở …  too_sad Tính ra mỗi  phần trung bình cỡ gần 20 trang giấy, tính cả tut này nhân lên thì tôi đã viết khoảng 600 trang …29 Tôi đang nghĩ không biết sau này có nên đóng lại thành quyển để xuất bản bán lấy tiền hay không? extreme_sexy_girl

Ở phần trước, thông qua UnPackMe_tElock0.98.exe, tôi đã giới thiệu với các bạn về kĩ thuật IAT Redirection, một kĩ thuật rất hay gặp ở các packers/protectors. Trong phần 31 này, tôi sẽ áp dụng một số phương pháp fix IAT, để làm sao khi ImpREC thực hiện Get Imports thì thông tin về hàm API thu được sẽ đầy đủ nhất phục vụ việc fix dump. Đảm bảo cho file sau khi fix chạy mượt mà, không lỗi.

Cũng tương tự như phần trình bày các phương pháp làm thế nào để tới được OEP, ở phần này tôi cũng sẽ áp dụng một số phương pháp tổng quát nhất, để sau đó, khi chúng ta gặp các trình packers khác, ta sẽ tùy biến các phương pháp này hoặc nghiên cứu một cách thức hoàn toàn mới nhằm phù hợp với tính huống thực tế mà ta đang gặp phải, có thể chưa được đề cập đến trong bài viết này.

Download toàn bộ bài viết tại đây:

https://mega.nz/#!D4EjgICK!_JHdeZCTHbuC2bLLjG4-_I6Fs8YSZAcE7ksfnukBUT4

Regards,

m4n0w4r

ST:

Hôm ấy mê man uống,
Không vì dưỡng tính linh.
Thấy người say khướt cả,
Đâu nỡ tỉnh riêng mình!


Mới đây, tại hội thảo BotConf2017, các chuyên gia của Avast đã giới thiệu tới cộng đồng công cụ decompiler với tên gọi là RetDec (Retargetable Decompiler). Theo các chuyên gia chia sẻ, sau bảy năm phát triển, Avast quyết định open-source công cụ này nhằm trợ giúp cho cộng đồng an ninh mạng trong cuộc chiến chống lại các phần mềm độc hại. Thông qua công cụ này sẽ cho phép những người hoạt động trong lĩnh vực phân tích/nghiên cứu có thể biết được hoạt động của một ứng dụng, mà không cần phải thực thi.

Hiện tại, RetDec hỗ trợ các tính năng sau:

  • Supported file formats: ELF, PE, Mach-O, COFF, AR (archive), Intel HEX, and raw machine code.
  • Supported architectures (32b only): Intel x86, ARM, MIPS, PIC32, and PowerPC.
  • Static analysis of executable files with detailed information.
  • Compiler and packer detection.
  • Loading and instruction decoding.
  • Signature-based removal of statically linked library code.
  • Extraction and utilization of debugging information (DWARF, PDB).
  • Reconstruction of instruction idioms.
  • Detection and reconstruction of C++ class hierarchies (RTTI, vtables).
  • Demangling of symbols from C++ binaries (GCC, MSVC, Borland).
  • Reconstruction of functions, types, and high-level constructs.
  • Integrated disassembler.
  • Output in two high-level languages: C and a Python-like language.
  • Generation of call graphs, control-flow graphs, and various statistics.
  • IDA plugin that allows decompilation of files directly from the IDA disassembler.

Thông tin chi tiết có thể xem thêm tại blog: https://blog.avast.com/avast-open-sources-its-machine-code-decompiler

RetDec hỗ trợ plugin để có thể sử dụng với IDA. Mô hình hoạt động của plugin này như sau:

Plugin hỗ trợ phiên bản IDA từ 6.6 trở lên (không làm việc với IDA 7.x). Tải plugin tại đây: https://retdec.com/idaplugin/. Việc cài đặt để sử dụng rất đơn giản, trong bài viết này tôi minh họa sử dụng plugin với IDA 6.8.

Các bước cài đặt Plugin:

  1. Chép file retdec.plw vào thư mục plugin của IDA (/plugins).
  2. Mặc định, plugin này đăng ký phím tắt là Ctrl-D. Nếu đã sử dụng phím tắt này cho một plugin khác hoặc muốn sử dụng một phím tắt khác, cần phải sửa đổi tệp cấu hình plugin của IDA ( /plugins/plugins.cfg). Bên cạnh đó, plugin này hỗ trợ nhiều chế độ decompile: selective (decompile hàm tại vị trí con trỏ); full (decompile toàn bộ binary), do vậy khuyến nghị cấu hình plugins.cfg như sau:

Thực hiện Decompile:

RetDec hỗ trợ hai chế độ decompile:

  • Remote API Decompilation: sử dụng API được cấp bởi RetDec, API này sẽ kết nối và gửi dữ liệu lên máy chủ của RetDect. Quá trình decompile sẽ do máy chủ của RetDect thực hiện và trả kết quả về khi decompile xong.
  • Local decompilation: Ở chế độ này, phải cài đặt RetDec trên máy và đảm bảo plugin có thể truy cập tới file decompile.sh.

Remote API Decompilation: Với chế độ này các bước cấu hình cơ bản như dưới đây:

  • Đăng kí một tài khoản miễn phí tại https://retdec.com. Sau khi kích hoạt tài khoản, vào phần quản lý account để tạo API key tương tự như hình dưới:

  • Cấu hình Retdec plugin trong IDA để sử dụng API key đã tạo ra ở bước trên:

Kết quả chạy plugin sau khi cấu hình thành công:


Local decompilation: Để sử dụng chế độ này thì việc cấu hình có phức tạp hơn chút. Các bước cơ bản như sau:

  • Phiên bản mới nhất là của RetDec là v3.0. Tùy vào phiên bản của OS mà lựa chọn bản cài đặt phù hợp tại đây. Sau khi tải về, bung nén vào một thư mục tùy ý. Ví dụ:

Lưu ý: Nhớ bổ sung đường dẫn tới thư mục bin vào system PATH của Windows.

  • Sau khi cài đặt xong, kiểm tra thử xem có chạy được không. Nếu kết quả như hình là ok:

  • Cấu hình lại plugin sử dụng chế độ Local decompilation như sau:

  • Kết quả có được sau khi tiến hành decompile như sau:

Cơ bản là như vậy, chi tiết các chức năng khác có thể đọc thêm tại https://retdec.com và trong tài liệu userguide đi kèm với plugin.

End!

m4n0w4r

 

Bruce Dang…

Posted: December 2, 2017 in Bruce Dang..., Uncategorized
Tags:

Nhân sự kiện Ngày ATTT Việt Nam 2017 diễn ra hôm 1/12 vừa rồi, tôi có cơ hội được gặp và nói chuyện trực tiếp với anh Bruce Dang (https://twitter.com/brucedang) , đồng tác giả của cuốn sách “Practical Reverse Engineering”. Ban đầu, tôi cũng không định đến vì nhiều lý do … 11, nhưng sau tôi đã thay đổi lại sau khi đọc mấy thông tin bên lề khi biết Bruce sẽ trình bày một chủ đề khá hot “Tình tiết và bài học từ một số sự cố tấn công APT” vào “cuối” buổi chiều… hell-yes-onion-head-emoticon

Gặp Bruce tại hội thảo cũng thật tình cờ … đúng kiểu đến là gặp luôn chả phải đi tìm :D. Việc đầu tiên là tôi xin chữ kí vào cuốn sách do anh là đồng tác giả:

IMG_20171202_161943

Theo như anh chia sẻ thì toàn bộ số tiền bán sách đều dành cho mục đích từ thiện và anh cũng cảm ơn tôi vì đã góp phần vào mục tiêu đó của anh. (Các bạn sinh viên học ATTT, đang còn trên ghế nhà trường, ngày có thể bớt vài ba cốc trà sữa để dành tiền mua sách của anh, vừa là nâng cao kiến thức và cũng là để ủng hộ mục đích đầy nhân ái này của anh)

Do khoảng thời gian tới lúc Bruce bước lên Showbiz còn dài nên tôi và mấy anh em khác được nói chuyện với anh khá lâu. Tôi đùa bảo: “Họ để anh talk cuối cùng chắc để giữ khách hả anh?” … Bruce cười khà khà. Anh nói, mới về VN cũng không biết mọi người quan tâm tới cái gì, thôi thì làm slide để kể chuyện về APT. Tôi bảo: “Anh làm được chục slides là hơn đứt Thái-DN rồi anh ơi!.. Thái làm có một slide có hình viên đạn không à adore. Mà có lần sau chắc anh còn được present tiếp, chứ Thái thì …. 😀 “

Tuy lần đầu tiên tiếp xúc và nói chuyện trực tiếp với Bruce nhưng cá nhân tôi thấy anh là người rất thoải mái, thân thiện, nhiệt huyết và rất sẵn lòng chia sẻ các vấn đề liên quan đến kĩ thuật. Cảm giác Bruce có thể nói liên tục không ngừng nghỉ … chỉ chờ có người hỏi hoặc chuyển chủ đề là lại tuôn ra như suối 29. Người gầy nhẳng mà nói khỏe thế waaaht!!!

Do nhiều năm sống và làm việc tại nước ngoài nên Bruce tự nhận Tiếng Việt mình kém và nhiều khi không hiểu được người đối diện đang nói gì :D… Bruce hay đệm những từ đại loại như “so Cool”, “wow”,  “yeah”…”Blah Blah Blah”. Tôi có trêu anh: “Anh nói nhiều quá! Anh phải giữ giọng tí còn talk chứ!”. Anh bảo không thành vấn đề, quen rồi.

Mải chém gió, sau sực nhớ xin anh bức ảnh làm kỉ niệm. Tks Sơn-NV chụp hộ anh nhá:

IMG_20171201_142403

Tới giờ Bruce phải dấn thân vào Showbiz, và như bất kì một bài talk liên quan đến kĩ thuật .. tôi chỉ để lại bức ảnh này và xin phép “không bình luận” gì thêm. Không biết anh đứng một mình trên đó có thấy lạnh không? Chứ tôi với mấy anh em ngồi dưới thấy phòng rộng và lạnh quá :

IMG_20171201_165206

Chúc anh luôn dồi dào sức khỏe và hi vọng có dịp lại được nói chuyện với anh.

My idol 29

Regards,

m4n0w4r

P.S: Tôi có thu âm buổi talk của anh, bạn nào muốn nghe giọng anh thì liên hệ, tôi sẽ upload!

 

 

 


Blog này tôi xây dựng với tinh thần chia sẻ những kiến thức mà tôi biết trong quá trình nghiên cứu, làm việc và học hỏi. Do vậy, tôi cũng hi vọng các bạn vẫn thường xuyên ghé Blog của tôi cũng có được tinh thần như thế36

Gần đây, hasherezade trên trang twitter cá nhân của mình có đăng tải một crackme do cô ấy viết dành cho @Malwarebytes. Là một lập trình viên và là người nghiên cứu độc lập nhưng đặc biệt quan tâm tới lĩnh vực InfoSec, Hasherezade dành nhiều thời gian với công việc phân tích mã độc và chia sẻ thông tin về những mối nguy hại cho cộng đồng thông qua trang blog cá nhân hoặc qua Malwarebytes Team.

Trong cộng đồng cybersecurity nhung nhúc toàn nam giới, việc xuất hiện những bóng hồng tài năng như cô ấy quả thật rất hiếm. Cá nhân tôi cảm giác một mình cô ấy cân cả team ở Malwarebytes 🙂 . Tôi thích cô ấy bởi thái độ làm việc nghiêm túc, ngoài ra còn vì một lý do đặc biệt khác, là một người nghiên cứu độc lập có lẽ cô ấy chưa biết tới các cụm từ “chuyên gia bảo mật tự phong”, “nhà ngoại cảm bảo mật” … Nếu không biết các cụm từ này thật quả là đáng tiếc!! Hi vọng nếu có cơ hội du lịch qua dải đất hình chữ S này hoặc qua màn ảnh nhỏ cũng được, cô ấy có thể cập nhật các cụm từ trên vào vốn từ của mình…Tôi đảm bảo tương lai sẽ rộng mở hơn, báo chí sẽ săn đón nhiều hơn …4

Download crackme tại đường link trên: https://www.hybrid-analysis.com/sample/4ba96615dd4f38d5bf75c192c6bee81ecac595fda911d6974739557118eda032?environmentId=100

Thử chạy crackme, nhận được lời giới thiệu: challenge này được tạo ra dành cho những người làm công việc liên quan tới phân tích mã độc. Khi hoàn thành toàn bộ các nhiệm vụ sẽ tìm được flag có dạng là flag{…}. Challenge này có rất nhiều màn cần phải vượt qua để tới được đích cuối cùng.

Tiếp theo load thử crackme vào một trình debugger bất kì (ví dụ: OllyDBG/x64dbg), được trang bị sẵn các plugin chống các cơ chế anti-debug. Chạy thử crackme trong debugger, mục đích nhằm để kiểm tra xem crackme có thực thi được bình thường không hay là có sử dụng các cơ chế anti-debug nào khác nữa:

Như đã thấy trên hình, crackme vẫn thực thi được hoàn toàn bình thường. Giờ dựa vào chuỗi “I am so sorry, you failed! :(“ để tìm ngược lại đoạn code check liên quan. Trước khi load crackme vào IDA, kiểm tra thêm các thông tin cơ bản khác.

Compiler: Microsoft Visual C/C++(2012)[-]. Như vậy là crackme không bị pack.

Crypto:

Crackme có sử dụng kĩ thuật anti-debug, sử dụng các hàm API liên quan tới mã hóa thuộc thư viện ADVAPI32.dll, có khả năng sử dụng cả Base64.

Unicode String:

ANSI String:

Căn cứ vào thông tin của string thì khả năng crackme có kiểm tra để kết nối internet, tạo payload khác và thực thi payload này.

Quá trình thu thập các thông tin để có cái nhìn tổng quan về crackme này cơ bản đã xong. Để tìm hiểu và phân tích kĩ hơn, tiến hành load crackme này vào IDA. IDA sẽ thực hiện quá trình phân tích tự động cho tới khi cửa sổ Output window xuất hiện thông báo: “The initial autoanalysis has been finished”, có nghĩa là quá trình phân tích của IDA đã hoàn tất. Chuyển tới cửa số Strings Window (Shift+F12), tìm chuỗi “I am so sorry, you failed! 😦“:

Nhấp đúp chuột tại chuỗi này sẽ tới địa chỉ .rdata:00420A38 tại màn hình IDA View:

Sử dụng chức năng Cross-References của IDA để tìm ra vùng code gọi tới chuỗi này:

Với kết quả trên hình, biết được chuỗi này chỉ được sử dụng ở một nơi duy nhất, đó là tại hàm main() của crackme. Nhấn đúp chuột tại địa chỉ có được, tôi sẽ tới hàm main() của crackme:


Stage1 — Get the final binary

1.1. Khôi phục URL bị mã hóa

Quan sát tại hàm main(), sau khi gọi các hàm printf() để in ra màn hình các thông tin giới thiệu về crackme, tôi thấy có lệnh call sub_004014F0() tại địa chỉ 0x00401972 (tôi đã đổi tên thành GetDecryptedUrl()), sau hàm call này sẽ có quá trình kiểm tra để rẽ nhánh như trên hình. Thanh ghi al sẽ lưu kết quả trả về sau khi gọi hàm này, nếu <> 0 (hay True) thì sẽ nhảy tới nhánh “right_track”. Do đó, cần phải phân tích nhiệm vụ của hàm call này. Tổng thể toàn bộ code tại sub_004014F0() như dưới đây:

Tôi sẽ đi lần lượt từ đầu, đầu tiên sẽ gặp lệnh rdtsc, lệnh này hay được malware áp dụng để anti-debug. Sau khi thực hiện lệnh này, kết quả được lưu vào thanh ghi eax (chứa 32 bit thấp) và edx (chứa 32 bit cao). Các giá trị này sau đó được lưu vào biến để sử dụng sau:

Tiếp theo, tại địa chỉ 00401510 sẽ gọi tới sub_004019D0 (detect_debugger()) làm nhiệm vụ phát hiện xem crackme có đang bị debug không thông qua các API IsDebuggerPresent() & CheckRemoteDebuggerPresent():

Đọc kĩ đoạn code trên sẽ thấy mục đích của sub này không phải để anti-debug, mà thông qua việc kiểm tra xem có đang bị debug hay không để thiết lập giá trị cho một mảng, tôi đặt tên là global_bytes_array[]. Kết thúc quá trình kiểm tra, mảng sẽ được gán giá trị đầu tiên là 0x81B22A94, sau đó index của mảng sẽ được tăng dần để phục vụ lưu giá trị tiếp theo.

Tại địa chỉ 00401520 sẽ gọi tới sub_00401A50() (au_re_RaiseException()) nhằm thực hiện RaiseException:

Sau đoạn code trên, mảng sẽ được gán giá trị thứ hai là 0x18E309F0. Tiếp theo, tại địa chỉ 00401525 gọi tới sub_ 00401B00() (check_hw_registers()), mục đích của sub này sẽ kiểm tra xem có đặt hardware breakpoint hay không bằng cách kiểm tra giá trị các thanh ghi Dr0; Dr1; Dr2; Dr3. Nếu không đặt hw bp thì sẽ không gán giá trị vào mảng, do đó cần phải đặt hw bp để có được giá trị được gán tiếp theo cho mảng.

Với việc có thiết lập hw bp, mảng sẽ được gán giá trị thứ ba là 0xEFF4652B. Tại đỉa chỉ 0040152A, ta sẽ gặp sub_00401C20() (check_PEB_flags()). Sub này làm nhiệm vụ kiểm tra NtGlobalFlag & BeingDebugged trong cấu trúc PEB, nếu các trường này có giá trị non-zero thì sẽ thực hiện gán giá trị cho mảng. Đây cũng là cách anti-debug hay gặp ở malware. Tuy nhiên, ở đây ta thấy mục đích của crackme này rõ ràng không phải là để anti-debug:

Như vậy, sau quá trình kiểm tra, mảng của chúng ta sẽ được gán giá trị thứ tư là 0xBA521C56. Sau đoạn code kiểm tra flags trong PEB, tại địa chỉ 00401531 gọi tới sub_00402730() (find_blacklisted_devices()).

Sub này sẽ build một danh sách các checksum cho các blacklist device, sau đó sử dụng API QueryDosDeviceA() để lấy toàn bộ các thông tin về tên các MS-DOS device, và lưu tại buffer là lpTargetPath. Sau đó sẽ duyệt danh sách này, tính toán một giá trị check_sum của từng device name và so sánh với danh sách device đã thiết lập từ trước. Nếu như tìm thấy có blacklist device thì mới gán giá trị cho mảng. Do trên máy tôi không có blacklist device nào tương ứng, do đó tôi phải patch tại lệnh nhảy để chuyển tới code gán giá trị cho mảng:

Kết quả ta sẽ có được giá trị thứ tư gán vào mảng là 0x2CBE186A. Tiếp theo, tới sub_00402880() (anti_virtualBox()) tại địa chỉ 00401536, sub này sẽ kiểm tra xem ta có đang phân tích crackme trong môi trường VirtualBox hay không?

Do tôi không sử dụng VirtualBox nên phải patch để tới đoạn code gán giá trị cho mảng. Tới đây, ta có giá trị thứ 5 được gán vào mảng là 0xF5C1D288. Tiếp tục tới địa chỉ 0040153D, tại đây gọi tới sub_00402B70() (find_blacklisted_modules()).

Sub này cũng build một danh sách các checksum cho các module (nhìn vào đây thì chịu không rõ là module nào được tạo checksum).

Tiếp theo sẽ thực hiện quá trình tìm kiếm các blacklist module thông qua các hàm API GetCurrentProcessId(), CreateToolhelp32Snapshot(), Module32First(), Module32Next(). Nếu tìm thấy mới thực hiện gán giá trị cho mảng:

Do trên máy tôi, không có module nào thỏa mãn nên tôi cũng patch để tới đoạn code gán giá trị thứ 6 cho mảng là 0x8005F916.

Tại địa chỉ 00401544, gọi tới sub_00402DE0() (find_blacklisted_processes()). Sub này build một danh sách các checksum cho các process, thực hiện quá trình tìm kiếm các blacklist process thông qua các hàm API CreateToolhelp32Snapshot(), Process32First(), Process32Next(). Nếu tìm thấy sẽ tiến hành gán giá trị cho mảng.

Tiếp tục patch cờ ZF để tới đoạn code gán giá trị thứ 7 cho mảng là 0xFB18EAD7. Cuối cùng tới đoạn code lấy lại các giá trị timestamp của lệnh rdtsc để sử dụng cho sub_00401BC0() (timing_based_detection()) tại địa chỉ 00401555.

Đây là một cách mà các malware thường hay áp dụng để phát hiện debugger. Tiến hành patch để tới đoạn code gán giá trị cuối cùng cho mảng là 0x82CF939D. Sau khi vượt qua được hết các quá trình kiểm tra trên, tôi có được mảng với toàn bộ các giá trị được gán như sau:

Đoạn code tiếp theo khởi gán các giá trị cho một mảng (tôi đặt là szEncrypted_Url), sau đó sẽ thực hiện quá trình giải mã để lấy plaintext url và vào szUrl. Quá trình giải mã URL có sự tham gia của mảng đã gán ở trên, do vậy khả năng đây có thể là key để phục vụ việc giải mã. URL sau khi được giải mã sẽ được tính checksum để so sánh với giá trị mặc định là 0x3B47B2E6. Cơ bản tại sub_004031C0() (decrypt_url()) sẽ thực hiện các công việc dưới đây.

Sử dụng API CryptAcquireContextW() để lấy handle với các tham số sau:

Sau khi lấy được handle sẽ sử dụng API CryptCreateHash() để khởi tạo và trả về một handle sử dụng cho việc hash dữ liệu bằng các hàm CryptHashData() và CryptHashSessionKey(). Thuật toán được sử dụng để phục vụ hash dữ liệu là SHA_256:

Thực hiện hash dữ liệu với hàm API CryptHashData(), tham số pbData trỏ tới mảng bytes đã được khởi gán ở trên. Như vậy, khi thực hiện xong, hàm này sẽ cập nhật một giá trị hash dựa trên mảng byte đã có:

Tại máy tôi, giá trị hash được cập nhật có giá trị sau:

Dựa vào hash có được, sử dụng hàm API CryptDeriveKey() để tạo ra một key phục vụ cho việc giải mã dữ liệu:

Key AES 128 bit cuối cùng được tạo ra:

Với key được tạo ra như trên, thực hiện giải mã encrypted_URL để có link gốc:

Nếu giải mã thành công ta sẽ có được URL chính xác như sau:

Với URL được giải mã đúng sẽ qua được phần kiểm tra check sum với giá trị mặc định là 0x3B47B2E6h. Cùng với đó, tôi sẽ không tới đoạn code in ra màn hình thông báo: “I am so sorry, you failed! :(\n”. Truy xuất URL trên, tôi nhận thấy đây là một dữ liệu rất dài bị mã hóa bằng Base64:


1.2. Giải mã và dump file PE mới

Đi sâu vào phân tích call sub_401690() tại địa chỉ 004019A5. Tại đây, đầu tiên sẽ kiểm tra xem kết nối Internet có sẵn sàng hay không bằng hàm API InternetGetConnectedState(). Nếu không sẽ in ra màn hình thông báo: “I need internet!\n”.

Tiếp theo, thực hiện cấp phát một vùng nhớ với toàn các bytes 0 với số lượng (0x1FA0Eu*1) bytes. Vùng nhớ này sẽ được sử dụng để chứa dữ liệu tải về từ URL ở trên:

get_base64_data_from_url() sử dụng các API InternetOpenA(), InternetOpenUrlA(), InternetReadFile() với user_agent là ‘Mal-Zilla’. Thực hiện download toàn bộ nội dung từ URL và lưu vào vùng nhớ đã cấp phát. Download xong sẽ in ra màn hình thông báo: “You are on the right track!\n” và tính toán lại kích thước thật sự của vùng dữ liệu đã download là 0xC20B bytes, đồng thời cấp phát vùng nhớ khác toàn bytes 0 với kích thước thật này. Thực hiện giải mã toàn bộ khối dữ liệu Base64 đã download và lưu vào vùng nhớ mới được cấp phát:

Vùng buffer sau khi decrypt vẫn ở dạng bị mã hóa:

crackme tiếp tục cấp phát một vùng nhớ mới, sử dụng API RtlDecompressBuffer() để giải nén buffer ở trên (CompressedBufferSize = 0xC20B):

Vùng buffer (trên máy tôi) sau khi được giải nén có kết quả như hình dưới, với kích thước sau giải nén FinalUncompressedSize = 0xE400:

Với vùng buffer có được này tôi thấy vẫn chưa có thông tin gì cụ thể cả, nhưng nhận thấy một điểm đặc biệt là có rất nhiều chuỗi “malwarebytes” được lặp đi lặp lại tại vùng buffer này. Tiếp tục phân tích code phía dưới tôi nhận ra một số điểm khá thú vị:

  1. Thực hiện khởi tạo một vùng nhớ mới.
  2. Gọi sub_00406B20() (Get_Clipboard_Data()): sử dụng các hàm API IsClipboardFormatAvailable(), OpenClipboard(), GetClipboardData() để copy dữ liệu của clipboard vào vùng nhớ đã cấp phát.
  3. Gọi sub_004011A0() (xor_decrypt_new_PE()): sử dụng vùng nhớ chứa dữ liệu clipboard làm xor key giải mã để vùng buffer trên.
  4. Kiểm tra xem vùng buffer được giải mã có phải là PE file hay không thông qua vdấu hiệu “MZ”. Nếu không đúng sẽ hiện thị thông báo “Better luck next time!”, “Nope :(” bằng API MessageBoxA().
  5. Dựa trên dấu hiệu so sánh với “MZ”, tôi thực hiện lấy mẫu để xor với vùng buffer trên và đi đến kết luận, xor key dùng để giải mã chính là “malwarebytes”.

Tiến hành copy chuỗi “malwarebytes” vào clipboard và thực hiện đoạn code trên để lấy được PE file:

Lúc này, ta có thể dump toàn bộ vùng nhớ này bằng OllyDumpEx plugin và save lại với tên mới (ví dụ: mb_crackme_dump.exe):

Thử chạy file vừa dump xem có thực thi được không, nhận được thông báo:

Tới đây có thể dừng lại để chuyển sang phân tích binary của Stage2.

Tuy nhiên, nếu tiếp tục quá trình phân tích sau khi vùng buffer được giải mã chính xác như trên, tôi gặp một kĩ thuật thường thấy khi phân tích malware đó là process hollowing. Trước khi thực hiện process hollowing, crackme sử dụng API ExpandEnvironmentStringsW() để tạo một command đánh lừa như sau: %SystemRoot%\system32\rundll32.exe secret.dll,#1 .

Lên quan đến kĩ thuật hollowing, cụ thể ở crackme sẽ thực hiện một số bước như sau:

  • Khởi tạo một instance mới của một tiến trình hợp lệ (ở chế độ CREATE_SUSPENDED) thông qua API CreateProcess().

PROCESS_INFORMATION: là struct chứa thông tin liên quan tới ProcessId & ThreadId để giúp xác định tiến trình được tạo. Ví dụ trên máy tôi:

  • Lấy thông tin về context của hThread ứng với tiến trình vừa được khởi tạo ở trên thông qua API GetThreadContext(hThread, &Context).
  • Từ thông tin về context có được, truy xuất tới PEB để lấy ImageBaseAddress của tiến trình (baseAddress = (DWORD *) contx.Ebx+8). Sử dụng API ReadProcessMemory(hProcess, (LPCVOID)(Context.Ebx + 8), &Buffer, 4u, NULL) để kiểm tra toàn bộ dữ liệu tại base address và vùng nhớ với kích thước được chỉ định có thể truy cập để đọc dữ liệu hay không, nếu không, và nếu không thể truy cập thì hàm sẽ bị lỗi.
  • Sử dụng VirtualAllocEx()/VirtualAlloc() để cấp phát một vùng nhớ mới có quyền “PAGE_EXECUTE_READWRITE” dùng để lưu code của PE đã giải mã.
  • Tiến hành copy toàn bộ dữ liệu từ vùng PE buffer đã được giải mã ở trên vào vùng nhớ mới được tạo ra:

  • Sau đó lấy thực hiện việc lấy Entry Point, lưu vào _Eax, sử dụng các API SetThreadContext() và ResumeThread() để thực thi process từ EP.

Note: Nếu như thực hiện toàn bộ quá trình trên, thì PE file này sẽ thực thi dưới process là rundll32.exe với một tham số giả là “secret.dll, #1” (đánh lừa chúng ta như kiểu nó đang load file dll là secret.dll và gọi tới hàm được export dll có thứ tự là #1).

Như vậy, để phân tích được PE file đã giải mã thì có thể thực hiện:

  • Cách 1: Sau khi file đã giải mã hoàn toàn, thực hiện dump file như tôi đã làm ở trên.
  • Cách 2: Sử dụng Process Hacker, dump file từ memory (nhớ lưu thông tin địa chỉ base address. Ví dụ trong hình là 0xd0000). Sau đó, dùng công cụ pe_unmapper của chính hasherezade để fix file đã dump:

  • Cách 3: trước khi thực hiện API SetThreadContext(), tìm trong Context địa chỉ của EP, sau đó dùng Process Hacker để patch bytes tại entry point thành 0xEB 0xFE để tạo infinite loop (lưu ý nhớ lại byte gốc của EP), sau khi patch xong cho ResumeThread() để process thực thi bình thường và rơi vào vòng lặp vô tận, tiến hành attach tiến trình mới này vào một trình debugger khác để debug tiếp:


2. Stage 2 — Get the final flag

Có được binary mới bằng phương pháp dump ở trên với kích thước 57.0 KB (58,368 bytes). Khi chạy tôi nhận được thông báo “You failed 😦 . Better luck next time”. Load file mới vào IDA, tìm chuỗi này để dò ngược lại code. Tôi tới được hàm main() của file.

Tại main(), đầu tiên sẽ gặp sub_004010C0() (get_api_address()) có nhiệm vụ lấy địa chỉ của các hàm API “NtQueueApcThread”, “ZwSetInformationThread”, “ZwCreateThreadEx”, “RtlCreateUserThread”, thuộc thư viện ntdll.dll.

Sử dụng API GetModuleFileNameA() để lấy đường dẫn đầy đủ của file đang phân tích. Dựa trên đường dẫn này thực tính ra một giá trị checksum. Dùng API ExpandEnvironmentStringsA() để thiết lập một đường dẫn khác là %SystemRoot%\\system32\\rundll32.exe (C:\Windows\system32\rundll32.exe). Với đường dẫn này cũng thực hiện tính giá trị checksum khác.

Sau khi qua đoạn kiểm đường dẫn trên, tới đoạn code mà binary sử dụng hàm API EnumWindows() để tìm toàn bộ các top-level windows hiện có trên màn hình bằng cách truyền handle tới từng window, thông qua một callback function đã được định nghĩa trước. Ở đây, giá trị được truyền cho hàm callback là mã hash (0x3C5FE025) của một window nào đó mà tôi không biết.

Code tại hàm callback EnumFunc() thực hiện việc lấy ClassName của các window, sau đó tính checksum dựa trên ClassName này, so sánh với giá trị mặc định là (0x3C5FE025). Nếu bằng nhau thì sẽ ẩn cửa sổ có ClassName tương ứng, lấy process_id, qua đó lấy ra open hanle bằng API OpenProcess():

Ở đây tôi dùng một trick đế lấy handle của một Window khác (ví dụ của Calc.exe) và truyền handle này vào cho hàm API GetClassNameA():

Khi đó, ClassName sẽ chứa tên Class của ứng dụng (Calc.exe):

Patch đoạn so sánh checksum để tới đoạn code lấy process id, và open handle của calc.exe. Tiếp theo tới đoạn code lấy giá trị của PEB.BeingDebugged Flag (nếu bị debug sẽ có giá trị là 1), sau đó lấy 4 bytes đầu tiên của một vùng nhớ (tôi đã đổi thành shell_code) để thực hiện tính toán thông qua vòng lặp xor. Các bytes sau khi được giải mã được gán lại vào shell_code, tính checksum cho vùng shell_code có độ dài 0x177 bytes và so sánh với giá trị checksum mặc định là 0xCA1C7FCF. Nếu không bằng thì sẽ hiện thông báo: “You failed :(\nBetter luck next time!”, “Stage 2”.

Đoạn code tiếp theo sẽ thực hiện inject toàn bộ shell_code vào victim_process thông qua handle đã có được ở trên. Sử dụng ZwCreateSection() để tạo section có thuộc tính PAGE_EXECUTE_READWRITE = 0x40, NtMapViewOfSection() để maps section được tạo vào trong vùng nhớ của process hiện tại. Sau đó copy toàn bộ shell_code vào section đã được map thực hiện map vào process mà ta lấy được handle ở trên (của tôi là handle của calc.exe). Điều này đồng nghĩa với việc thực hiện inject toàn bộ shell_code vào calc.exe.

Kết quả sau khi đã thực hiện việc inject thành công:

Cuối cùng, tính toán một giá trị random bằng GetTickCount() % 3. Tùy thuộc vào kết quả trả về bằng 0, 1 hay 2 mà gọi các hàm liên quan tới thread khác nhau để thực thi shellcode.

Nếu thực hiện thành công, tôi có được flag 11:

Cảm ơn haserezade! Đây là một challenge thú vị với nhiều kĩ thuật khác nhau hay được sử dụng bởi malware, vừa tầm để những người không phải “chuyên gia bảo mật” như tôi nghiên cứu, học hỏi thêm để nâng cao kĩ năng.29

Hết!

m4n0w4r

(Bài viết có chỗ nào sai/thiếu sót mong bạn đọc góp ý)

“Khắc ghi sâu trong tim từng nét mi đường mày
Đong đầy nỗi nhớ nhung vào bức họa
Thấm nhuộm cả sắc mực tràn
Sách ngàn chữ cùng đều ố vàng
Đêm tĩnh mịch, rèm thưa đã mờ mờ sáng
Phất tay áo lên điệu múa trong mộng bỗng thật bồi hồi
Lòng dần dâng tràn nỗi niềm tương tư
Nàng quyến luyến rơi hoa lê
Lặng yên họa, hồng nhan đợi ai quay về
Trống vắng, người ấy cứ dần dần mà hao gầy …
Hương vị môi son ấy…
Vén rèm châu lên là vì ai?
Cớ sao vẫn không thấy người
Trăng khuya sáng vằng vặc, tình này thật khó thay…
Mưa phùn khẽ giăng giăng buổi sớm ngày đầu xuân
Bâng khuâng gọi lộc non thức tỉnh
Nghe tiếng gió nhẹ thổi bên tai
Than dòng nước chảy, thương cánh hoa rơi
Là ai ở nơi mây khói, gảy tiếng đàn đưa…”

Image result for vén rèm châu


Customize theme for x64dbg:

CPU screenshot:

CPU_screenshot

CPU screenshot

Graph screenshot:

Graph_screenshot

Graph screenshot


Đợt rồi lọ mọ kiếm sample phục vụ cho một project nhỏ của mấy anh em trong team VReT, vô tình “nhặt” được một sample cũng hay hay, do:

  • Có nội dung liên quan chính trị,  khả năng sử dụng để targeted attacks.
  • Áp dụng CVE-2017-0199 để lây nhiễm mã độc lên máy nạn nhân.

Hash của sample: 7b65b7f4678e9b915640d41d38151621076d4f539b677b0e3f98971547d68fd0

Dùng file/ Trid để kiểm tra thông tin sample (để khẳng định chắc chắn nó là định dạng RTF):

file

Fig 1

trid

Fig 2

Thử chuyển đổi sang định dạng PDF để xem qua nội dung:

rtf_content

Fig 3

Đọc thấy nội dung chuẩn bị có vẻ công phu lắm…  107

Hiện nay, có hai công cụ của hai chuyên gia nổi tiếng dùng để phân tích file có định dạng RTF là:

Sử dụng rtfdump, sau khi parse file, công cụ phát hiện có object data được nhúng tại vị trí 1201:

rtfdump_1

Fig 4

Tiếp tục dùng rtfdump, lựa chọn vị trí 1201 để dump, thêm tùy chọn –H để decode và xem dưới dạng hexa, -i để in ra các thông tin liên quan tới object tại vị trí được lựa chọn:

rtfdump_2

Fig 5

Theo thông tin thì object được nhúng có định dạng là OLE file:

rtfdump_3

Fig 6

Sử dụng tùy chọn –d để dump, ta có được thông tin:

rtfdump_4

Fig 7

Như vậy, khi mở tài liệu này nó sẽ tự động kết nối và download một tập tin khác tại hxxps://cdn-gmirror.appspot.com/template.rtf. Thử download tập tin template.rtf (tính đến thời điểm viết bài):

rtfdump_5

Fig 8

Kiểm tra file vừa tải về, trid báo unknown còn file báo định dạng là HTML chứ không phải là RTF:

file_2

Fig 9

Mở file bằng một trình Text Editor, thấy đây là một VBScript, thực hiện việc gọi powershell để download file tại hxxps://tp-qbm.appspot.com/icon.png và lưu với tên là unikey.exe tại hai thư mục là “%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup” và “%ProgramData%\Microsoft\Windows\Start Menu\Programs\Startup”:

VBScript

Fig 10

Thử download file tại hxxps://tp-qbm.appspot.com/icon.png, tính đến thời điểm viết bài thì file này không còn nữa:

icon_not_found

Fig 11

End.

m4n0w4r