Reading view

There are new articles available, click to refresh the page.

Some DOS bugs while processing Microsoft LNK files

As mentioned in the article about CVE-2020-1299 [1], this article I will present some bugs I found in processing the "LNK search" file of windows. In the article I will introduce pe-afl [2], a fuzzer I usually use instead of Winafl when Winafl is unstable on newer versions of windows.

 

The bugs I present below are not fixed, but this blog I wrote at the suggestion of MSRC, they will prepare every answer with customer questions around these unresolved bugs.


Introduction


I will use pe-afl to fuzz the "LNK search" file format, this is the file format I ignored until the ZDI public blog analyzed the error related to this file format [3], about the file structure "LNK search" is very clear in ZDI's blog (I think it is too complicated, I don't read much about it, lol).


Pe-afl


As I mentioned a lot that Winafl does not work well on new windows versions, plus Winafl using dynamic instruments to calculate coverage will result in overhead, which reduces performance.


Pe-afl is a fuzzer built on the AFL used for binary close sources. It uses static instruments to calculate coverage and feedback-driven. A static instrument is always better than a dynamic instrument. However, currently pe-afl only supports instruments for 32-bit binaries and has some limitations because not every binary can be instrumental (most binaries built with visual studio can be instrumental). This is an introduction slide for pe-afl [4] [5].

 

About using pe-afl is similar to Winafl, except that Winafl requires Dynamorio to calculate coverage + feedback-driven and pe-afl does not. Pe-afl will use IDA to find basic blocks, which are highlighted as shown in the picture.



Then pe-afl will insert the code at the beginning of these basic blocks to mark coverage. Binary instrumented by pe-afl is similar to afl-gcc instrument when building a program.



For pe-afl we will instrument the DLLs or even the EXE executables we want fuzz (pe-afl also supports fuzz, instrument kernel .sys). Then the fuzzer will do everything from calculating the coverage, feedback-driven like afl.



Windows.storage.search.dll and StructuredQuery.dll

 

Here I debug with the harness I used to fuzz in the previous article [1], I found the program also loads 2 more DLLs: windows.storage.search.dll and StructuredQuery.dll to parsing a file “LNK search”.



I want to use pe-afl to fuzz both DLLs, not just StructuredQuery.dll alone. However, if we use pe-afl to fuzz, we can only cover one module, so in order to cover more modules, I decided to fix a bit of pe-afl's source.

 

Pe-afl uses an array of 65536 bytes to store coverage for a module, so I doubled the size of this array to be able to cover another module. This way is quite simple but affects the running speed of fuzzer a lot. I don't know what Dynamorio used to cover multiple modules at a time, but for now, I can only use this simple method. I fixed the maximum pe-afl coverage to 3 modules at a time. In addition, I also integrated afl-fast [6] of @thuanpv_ and afl-mopt [7] (inspired by afl++ [8]).



Above is one of the pieces of code I modify pe-afl. Check how it works after I fix it with afl-showmap:



Now everything is available, but the most important thing is that corpus is not available. Corpus about the "LNK search" file format is not mentioned anywhere on the internet. The only way here is to create it manually. I use the search interface of Explorer, creating enough search cases under conditions such as Date, kind, size, ... but also only create about 200 files (boring work makes me impatient).



I then use afl-cmin to reduce the number of corpus and start fuzz. I use pe-afl fuzz on 2 DLLs that are windows.storage.search.dll and structuredquery.dll, after 1 week I checked the crash that fuzzer found and had 3 unique crashes:

-       Stack overflow (0xc00000fd)

-       2 null pointer dereference

 

All three crashes appear in structuredquery.dll and can cause explorer.exe to crash.

I reported to Microsoft but they said that these errors only caused DOS temporarily they would not fix. However, in my opinion, due to the nature of the LNK file, the default explorer will automatically handle them. For ordinary users who encounter these cases, it is often not clear the cause to fix (simply delete it but due to a continuous crash it will be very difficult to manipulate).



Conclusion

 

Above are some errors I found for the "LNK search" file format, I will not publish these DOS POCs, but if someone reads this blog and follow it, it can easily get those POCs, even there may be errors that cause RCE that I cannot find (I think there are still errors that exist in processing LNK files). Windows users should protect themselves, not arbitrarily download file formats such as LNK, maybe your computer will be exploited as soon as this file is saved.

 

[1] https://blog.vincss.net/2020/06/cve49-microsoft-windows-lnk-remote-code-execution-vuln-cve-2020-1299-eng.html

[2] https://github.com/wmliang/pe-afl

[3] https://www.zerodayinitiative.com/blog/2020/3/25/cve-2020-0729-remote-code-execution-through-lnk-files

[4] https://www.slideshare.net/wmliang/make-static-instrumentation-great-again-high-performance-fuzzing-for-windows-system

[5] https://www.youtube.com/watch?v=OipNF8v2His

[6] https://github.com/mboehme/aflfast

[7] https://github.com/puppet-meteor/MOpt-AFL

[8] https://github.com/AFLplusplus/AFLplusplus


--------------


Vietnamese version


Như đã đề cập ở bài viết về CVE-2020-1299 [1], bài này tôi sẽ trình bày 1 số lỗi tôi tìm thấy trong quá trình xử lý file LNK search của windows. Trong bài viết tôi sẽ giới thiệu pe-afl [2], 1 fuzzer tôi thường sử dụng thay thế winafl khi mà winafl chạy không ổn định trên các phiên bản windows mới hơn.

 

Những lỗi tôi trình bày dưới đây đều không được fix nhưng blog này tôi viết theo gợi ý của MSRC, họ sẽ chuẩn bị mọi câu trả lời với những câu hỏi của khách hàng quanh những lỗi không được fix này.


Introduction


Tôi sẽ sử dụng pe-afl để fuzz định dạng file LNK search, đây là định dạng file tôi đã bỏ qua mãi cho đến khi ZDI public blog phân tích lỗi liên quan đến định dạng file này [3], về cấu trúc file LNK search trong blog của ZDI nói rất rõ (tôi nhận định rằng nó quá phức tạp, tôi đọc cũng không đọng lại được gì nhiều).


Pe-afl


Như tôi đã đề cập rất nhiều rằng winafl chạy không ổn định trên các windows version mới, cộng thêm việc winafl sử dụng dynamic instrument để tính coverage thì sẽ xảy ra overhead, hiệu năng giảm.


Pe-afl là 1 fuzzer được xây dựng dựa trên afl sử dụng đối với các binary close source. Nó sử dụng static instrument để tính coverage và feedback driven. Tất niên static instrument sẽ luôn luôn tốt hơn dynamic instrument. Tuy nhiên hiện tại pe-afl chỉ hỗ trợ instrument đối với các binary 32 bit và có 1 số hạn chế vì không phải binary nào cũng có thể instrument được (đa số các binary được build với visual studio đều có thể instrument được). Đây là slide giới thiệu về pe-afl [4] [5].

 

Về cách sử dụng pe-afl cũng gần tương tự như winafl, chỉ khác cái là winafl cần có Dynamorio để tính coverage + feeback driven còn pe-afl thì không. Pe-afl sẽ sử dụng IDA để tìm các basic-block, các basic-block được highlight như ở trong hình.



Sau đó pe-afl sẽ chèn những đoạn code tại đầu các basic-block này để đánh dấu coverage. Binary được instrument bởi pe-afl cũng tương tự như afl-gcc instrument khi build 1 chương trình.



Đối với pe-afl ta sẽ instrument những DLL hay thâm chí là những file thực thi exe mà chúng ta muốn fuzz (pe-afl cũng hỗ trợ fuzz, instrument kernel .sys). Sau đó fuzzer sẽ làm mọi việc từ tính toán coverage, feeback driven giống như afl.



Windows.storage.search.dll and StructuredQuery.dll

 

Ở đây tôi debug với harness tôi sử dụng để fuzz ở bài viết trước [1], tôi nhận thấy chương trình còn load thêm 2 dll là windows.storage.search.dll và StructuredQuery.dll để parsing 1 file LNK search.



Tôi muốn sử dụng pe-afl để fuzz cả 2 DLL trên, chứ không chỉ 1 mình StructuredQuery.dll. Tuy nhiên nếu sử dụng pe-afl để fuzz thì chúng ta chỉ có thể coverage được 1 module, để có thể coverage được nhiều module hơn tôi quyết định sửa 1 chút source của pe-afl.

 

Pe-afl sử dụng 1 mảng 65536 bytes để lưu lại coverage đối với 1 module, tôi đã tăng kích thước của mảng này lên gấp đôi để có thể coverage thêm 1 module nữa. Cách này khá đơn giản nhưng ảnh hưởng đến tốc độ chạy của fuzzer rất nhiều. Tôi không biết Dynamorio dùng cách gì để có thể coverage nhiều module 1 lúc nhưng hiện tại thì tôi chỉ có thể sử dụng cách đơn giản này. Tôi sửa pe-afl tối đa có thể coverage được 3 module 1 lúc. Ngoài ra tôi còn tích hợp afl-fast [6] của anh @thuanpv_ và afl-mopt [7] (lấy cảm hứng từ afl++ [8]).



Trên đây là 1 trong 1 số những đoạn code tôi modify pe-afl. Kiểm tra nó hoạt động như thế nào sau khi tôi sửa với afl-showmap:



Bây giờ đã có đủ mọi thứ nhưng cái quan trọng nhất là corpus thì không hề có sẵn. Corpus về định dạng file LNK search không được đề cập ở bất cứ đâu trên internet. Cách duy nhất ở đây là chỉ có thể tự tạo bằng tay. Tôi sử dụng giao diện search của explorer, tạo đủ các trường hợp search theo điều kiện như Date, kind, size, … nhưng cũng chỉ tạo được khoảng hơn 200 files (công việc thật nhàn chán khiến tôi thiếu kiên nhẫn)



Sau đó tôi sử dụng afl-cmin để giảm số lượng corpus xuống và bắt đầu fuzz. Tôi sử dụng pe-afl fuzz trên 2 DLL là windows.storage.search.dll và structuredquery.dll, sau 1 tuần tôi kiểm tra đống crash mà fuzzer tìm được và có 3 unique crash:

-       Stack overflow (0xc00000fd)

-       2 null pointer dereference

 

Cả 3 crash này đều xuất hiện trong structuredquery.dll và có thể gây crash cho explorer.exe

Tôi report cho Microsoft nhưng họ nói rằng những lỗi này chỉ gây ra DOS tạm thời họ sẽ không sửa. Tuy nhiên theo tôi thì do tính chất của file LNK mặc định explorer sẽ luôn tự động xử lý chúng. Với những người dùng bình thường gặp những trường hợp này thường sẽ không rõ nguyên nhân để khắc phục (đơn giản là xóa nó đi nhưng do crash xảy ra liên tục nên sẽ rất khó thao tác).




Conclusion

 

Trên đây là 1 số lỗi tôi tìm thấy đối với định dạng file LNK search, tôi sẽ không public những POC gây DOS này tuy nhiên nếu ai đó đọc blog này và làm theo đều có thể dễ dàng có được những POC đó, thậm chí có thể sẽ tồn tại những lỗi gây ra RCE mà tôi không tìm thấy (tôi nghĩ vẫn còn những lỗi đó tồn tại trong quá trình xử lý file LNK). Người dùng windows nên tự bảo vệ chính mình, không nên tùy tiện tải về những định dạng file như LNK, có thể máy của bạn sẽ bị khai thác ngay sau khi file này được lưu xuống.

 

[1] https://blog.vincss.net/2020/06/cve49-microsoft-windows-lnk-remote-code-execution-vuln-cve-2020-1299-eng.html

[2] https://github.com/wmliang/pe-afl

[3] https://www.zerodayinitiative.com/blog/2020/3/25/cve-2020-0729-remote-code-execution-through-lnk-files

[4] https://www.slideshare.net/wmliang/make-static-instrumentation-great-again-high-performance-fuzzing-for-windows-system

[5] https://www.youtube.com/watch?v=OipNF8v2His

[6] https://github.com/mboehme/aflfast

[7] https://github.com/puppet-meteor/MOpt-AFL

[8] https://github.com/AFLplusplus/AFLplusplus

Microsoft's first bug

Continuing the series on fuzzing, this section I will share how I find attack surfaces on windows to fuzz. On windows handling a lot of file formats, learn and fuzz these file formats are a common way to find bugs on windows today. The approach and fuzz are exactly the same as finding fault in Irfanview I mentioned in the previous section.

Perhaps there are many people who wonder how to find an attack surface? It simply looks like this when you study something long enough, deep enough that you see the possible directions to attack on it. It sounds hard to put this situation in most beginners because not everyone is so good and excellent. But the interesting thing here is that there are so many good researchers who are willing to share everything they research and the bugs they find to the community. Google Project Zero (P0) [1] is an example, I see and track the bugs published on it (including bugs long ago). From there I learned about the types of bugs, surface attacks, components that often cause bugs on different platforms, etc. Or simply monthly I keep track of patches from Microsoft [2] and see if the bugs are patched. What's interesting and suitable for my fuzzing direction or not.

Introduction

Back to talking about fuzz file formats on windows, as we know on windows there are many DLLs, each DLL will have a separate task. For me, for the time being, I will focus on the DLLs that handle file formats. Some common file formats such as media: audio, video, image,... or some other file formats such as XML, XPS, PDF, registry,... These DLLs will export to APIs for developers to use to build the Windows applications, and Windows built-in components also use these APIs.

Microsoft itself has provided us MSDN [3], which is a repository for us to read and learn about using those APIs. Not only has the API document, but Microsoft is also generous in giving us a lot of sample code. I usually refer to the Microsoft GitHub repo [4]. It helps us a lot in building harness to fuzz file formats on windows.

Microsoft Font Subsetting


Windows fonts are a file format that I find very diverse, since the kernel-mode count user-mode has a font processing component. P0 public talks about fuzzing fonts of windows [5] [6], all of them are very clear and quality. In the font-related errors that P0 finds, I pay attention to the fontsub.dll library.



Up to the time of P0 public bugs of this library, no one has previously tried fuzz into the fontsub.dll library.


The Microsoft Font Subsetting DLL (fontsub.dll) is a default Windows helper library for subsetting TTF fonts; i.e. converting fonts to their more compact versions based on the specific glyphs used in the document where the fonts are embedded. It is used by Windows GDI and Direct2D.


The DLL exports two API functions: CreateFontPackage [7] and MergeFontPackage [8].


unsigned long CreateFontPackage(

  const unsigned char  *puchSrcBuffer,

  const unsigned long  ulSrcBufferSize,

  unsigned char        **ppuchFontPackageBuffer,

  unsigned long        *pulFontPackageBufferSize,

  unsigned long        *pulBytesWritten,

  const unsigned short usFlag,

  const unsigned short usTTCIndex,

  const unsigned short usSubsetFormat,

  const unsigned short usSubsetLanguage,

  const unsigned short usSubsetPlatform,

  const unsigned short usSubsetEncoding,

  const unsigned short *pusSubsetKeepList,

  const unsigned short usSubsetListCount,

  CFP_ALLOCPROC        lpfnAllocate,

  CFP_REALLOCPROC      lpfnReAllocate,

  CFP_FREEPROC         lpfnFree,

  void                 *lpvReserved

);

unsigned long MergeFontPackage(

  const unsigned char  *puchMergeFontBuffer,

  const unsigned long  ulMergeFontBufferSize,

  const unsigned char  *puchFontPackageBuffer,

  const unsigned long  ulFontPackageBufferSize,

  unsigned char        **ppuchDestBuffer,

  unsigned long        *pulDestBufferSize,

  unsigned long        *pulBytesWritten,

  const unsigned short usMode,

  CFP_ALLOCPROC        lpfnAllocate,

  CFP_REALLOCPROC      lpfnReAllocate,

  CFP_FREEPROC         lpfnFree,

  void                 *lpvReserved

);

 


P0 also publishes the harness they built [9], it's very good, covers all the parameters passed to these two functions. I use that harness to fuzz.


In addition to harness, P0 has a public tool that supports TTF/OTF [10] mutate files, this is a tool that I think is the key to help P0 find many bugs with such fonts.


Based on these, I began to find and create copus:

    1. Corpus from P0 public with previously published bugs + download on the internet

    2. Mutate these corpus based on the tool of P0

    3. Use winafl-cmin to reduce the number of corpus

    4. Check coverage

    5. Return to step 2


I do this task over and over again until the coverage I achieve with fontsub.dll is as follows:



With a test case, I can mutate 53.22% on DLL fontsub.dll, 81.08% for CreateFontPackage and 76.40% for MergeFontPackage. I think this is enough to start fuzz.


I used Winafl to run with 1 master and 7 slaves, after a few hours I started seeing the first crashes. After a few days, I went back and started checking for those crashes.


Most of them are stack overflow errors (0xc00000fd):



There are 2 errors that P0 reported earlier that Microsoft did not fix [11] [12].

And there is also a crash which, in my opinion, is quite similar to an error that P0 report earlier that Microsoft has fixed [13].



I report and Microsoft has accepted to fix this. It seems that this is a variant with a bug that Microsoft has fixed before. The bug was fixed in the T4/2020 patch (CVE-2020-0687), this is the root cause analysis of this error I wrote, everyone can read it [14], (In the article you should only pay attention to this error analysis, the impact is not written by me, of course, with errors like this, in fact, can not have a full exploit).


According to the google timeline, the bug fix was fixed in August 2019, but I did fuzz it and the bug persisted until January 2020 (the moment I reported it to Microsoft).


I am not surprised that Microsoft has not completely fixed the bug, but this P0 public project has not been used by anyone to find bugs.


Conclusion


This bug is not hard to find, everything is available in front of you but no one jumped into it. You can see that the steps I showed are clearly presented in the previous article, all the basic knowledge that you do not have to use reverse much to write harness. Microsoft's documentation is very complete, please read it, learn it, and try it out. If I just stopped reading the blog then I think it will not bring much results.


Error when fuzz file format of windows appears less and less. Because this way is quite a lot of users, it requires you to spend a lot of time researching for new surface attacks, file formats that have not been studied in order to be able to spot bugs.


The following blog will talk about a file format bug that Microsoft rewarded me with max bounty in Windows Insider Preview Bounty. Maybe I'll publish it after Microsoft's T6/2020 patch is released or longer.


[1] https://googleprojectzero.blogspot.com/

[2] https://portal.msrc.microsoft.com/en-us/security-guidance/acknowledgments

[3] https://docs.microsoft.com/en-us/

[4] https://github.com/microsoft/Windows-classic-samples

[5] https://googleprojectzero.blogspot.com/2016/06/a-year-of-windows-kernel-font-fuzzing-1_27.html

[6] https://googleprojectzero.blogspot.com/2016/07/a-year-of-windows-kernel-font-fuzzing-2.html

[7] https://docs.microsoft.com/en-us/windows/win32/api/fontsub/nf-fontsub-createfontpackage

[8] https://docs.microsoft.com/en-us/windows/win32/api/fontsub/nf-fontsub-mergefontpackage

[9] https://github.com/googleprojectzero/BrokenType/tree/master/ttf-fontsub-loader

[10] https://github.com/googleprojectzero/BrokenType/tree/master/ttf-otf-mutator

[11] https://bugs.chromium.org/p/project-zero/issues/detail?id=1863

[12] https://bugs.chromium.org/p/project-zero/issues/detail?id=1866

[13] https://bugs.chromium.org/p/project-zero/issues/detail?id=1868

[14] https://blog.vincss.net/2020/04/cve44-microsoft-font-subsetting-dll-heap-corruption-in-ReadTableIntoStructure-cve-2020-0687.html


-------


Vietnamese version


Tiếp tục seri về fuzzing, phần này tôi sẽ chia sẻ cách tôi tìm kiếm attack surface trên windows để fuzz. Trên windows xử lý rất nhiều định dạng file, tìm hiểu và fuzz các định dạng file này là một hướng phổ biến để tìm được bug trên windows hiện nay. Cách tiếp cận và fuzz hoàn toàn giống như tìm lỗi ở Irfanview tôi đã trình bày ở phần trước.


Có lẽ sẽ có nhiều người tự nhủ làm thế nào mà có thể tìm được attack surface? Đơn giản sẽ như thế này khi bạn nghiên cứu một cái gì đó đủ lâu, đủ sâu bạn sẽ thấy được những hướng có thể tấn công vào nó. Nghe thì thật khó khi đặt tình huống này vào phần lớn người mới bắt đầu vì không phải ai cũng giỏi và xuất sắc như thế. Tuy nhiên có một điều thú vị ở đây là có rất nhiều nhà nghiên cứu giỏi họ sẵn sàng chia sẻ mọi thứ mà họ nghiên cứu cũng như bug họ tìm được cho cộng đồng. Google Project Zero (P0) [1] là một ví dụ, tôi xem và theo dõi các bug được public trên đó (kể cả những bug cách đây rất lâu). Từ đó tôi biết được các loại bug, các attack surface, các thành phần thường gây ra lỗi trên các nền tảng khác nhau,… Hoặc đơn giản hơn hàng tháng tôi vẫn theo dõi các bản vá từ Microsoft [2] và xem các bug được vá có gì thú vị và phù hợp với hướng fuzzing của tôi hay không.


Introduction


Quay trở lại nói về fuzz các định dạng file trên windows, như chúng ta biết trên windows có rất nhiều dll, mỗi dll sẽ có một nhiệm vụ riêng biệt. Đối với tôi, hiện tại tôi sẽ chỉ tập trung vào những DLL có nhiệm vụ xử lý các định dạng file. Một số định dạng file phổ biến như media: audio, video, ảnh,… hoặc một số định dạng file khác như XML, XPS, PDF, registry… Các DLL này sẽ export ra các API cho các developer sử dụng để xây dựng các ứng dụng trên windows, và các thành phần built in của Windows cũng sử dụng những API này.


Bản thân Microsoft đã cung cấp cho chúng ta MSDN [3], đó là một kho tài liệu để ta có thể đọc, tìm hiểu về cách sử dụng các API đó. Không những có document về API mà Microsoft còn hào phóng cho chúng ta rất nhiều code mẫu. Tôi thường tham khảo tại repo github của Microsoft [4]. Nó giúp chúng ta rất nhiều trong việc xây dựng harness để fuzz các định dạng file trên windows.


Microsoft Font Subsetting


Phông chữ của windows là một định dạng file tôi thấy rất đa dạng, từ usermode đếm kernelmode đều có thành phần xử lý phông chữ. P0 public rất nhiều bài nói về fuzzing phông chữ của windows [5] [6], những bài đó đều rất rõ ràng và chất lượng. Trong các lỗi liên quan đến phông chữ mà P0 tìm ra, tôi chú ý đến thư viện fontsub.dll.



Tính tới thời điểm P0 public các lỗi của thư viện này thì trước đó chưa có ai thử fuzz vào thư viện fontsub.dll. 


Fontub.dll là một thư viện tạo, gom nhóm các phông chữ TTF, nó có thể chuyển đổi phông chữ thành các phiên bản nhỏ gọn hơn dựa trên các glyph và được sử dụng trong các file tài liệu như docx, ppt, pdf,… có phông chữ được nhúng. Nó cũng được Windows GDI và Direct2D sử dụng.


DLL export hai hàm API: CreateFontPackage [7] và MergeFontPackage [8].


unsigned long CreateFontPackage(

  const unsigned char  *puchSrcBuffer,

  const unsigned long  ulSrcBufferSize,

  unsigned char        **ppuchFontPackageBuffer,

  unsigned long        *pulFontPackageBufferSize,

  unsigned long        *pulBytesWritten,

  const unsigned short usFlag,

  const unsigned short usTTCIndex,

  const unsigned short usSubsetFormat,

  const unsigned short usSubsetLanguage,

  const unsigned short usSubsetPlatform,

  const unsigned short usSubsetEncoding,

  const unsigned short *pusSubsetKeepList,

  const unsigned short usSubsetListCount,

  CFP_ALLOCPROC        lpfnAllocate,

  CFP_REALLOCPROC      lpfnReAllocate,

  CFP_FREEPROC         lpfnFree,

  void                 *lpvReserved

);

unsigned long MergeFontPackage(

  const unsigned char  *puchMergeFontBuffer,

  const unsigned long  ulMergeFontBufferSize,

  const unsigned char  *puchFontPackageBuffer,

  const unsigned long  ulFontPackageBufferSize,

  unsigned char        **ppuchDestBuffer,

  unsigned long        *pulDestBufferSize,

  unsigned long        *pulBytesWritten,

  const unsigned short usMode,

  CFP_ALLOCPROC        lpfnAllocate,

  CFP_REALLOCPROC      lpfnReAllocate,

  CFP_FREEPROC         lpfnFree,

  void                 *lpvReserved

);

 


P0 cũng public cả harness mà họ xây dựng [9], nó rất tốt, cover được hết tất cả các tham số được truyền vào 2 hàm này. Tôi sử dụng harness đó để fuzz.

 

Ngoài harness, P0 còn public tool hỗ trợ mutate file TTF/OTF [10], đây là một tool tôi nghĩ nó là chìa khóa giúp P0 có thể tìm được nhiều lỗi với phông chữ như thế.

 

Dựa vào những thứ đó, tôi bắt đầu tìm và tạo copus:

1. Corpus từ P0 public kèm các lỗi đã public từ trước + download trên internet

2. Mutate các corpus này dựa theo tool của P0

3. Dùng winafl-cmin để giảm số lượng corpus xuống

4. Kiểm tra coverage

5. Quay lại bước 2

 

Tôi làm công việc này lặp đi lặp lại đến khi coverage tôi đạt được với fontsub.dll như say:



Với 1 testcase tôi mutate ra có thể đạt 53.22% trên DLL fontsub.dll, 81.08% đối với hàm CreateFontPackage và 76.40% đối với hàm MergeFontPackage. Tôi nghĩ thế này là đủ để có thể bắt đầu fuzz.

 

Tôi sử dụng winafl chạy với 1 master và 7 slave, sau một vài tiếng tôi bắt đầu thấy các crash đầu tiên. Sau một vài ngày tôi quay lại và bắt đầu kiểm tra các crash đó.

Phần lớn đều là lỗi stack overflow (0xc00000fd):



Xuất hiện 2 lỗi mà P0 report trước đó mà Microsoft không fix [11][12].

Và còn xuất hiện 1 crash mà theo tôi thấy thì khá giống với một lỗi mà P0 report trước đó mà Microsoft đã fix [13].



Tôi report và Microsoft đã chấp nhận sửa lỗi này. Có vẻ đây là một biến thể với lỗi mà Microsoft đã fix trước đó. Lỗi được sửa trong bản vá T4/2020 (CVE-2020-0687), đây là bài phân tích root cause của lỗi này tôi viết, mọi người có thể đọc thử [14] (trong bài viết bạn chỉ nên chú ý phần phân tích lỗi này, phần impact không phải do tôi tự viết, tất nhiên với những lỗi như thế này trong thực tế không thể có 1 full exploit).

 

Theo timeline google đưa ra bug này đã được fix vào tháng 8/2019, tuy nhiên tôi đã fuzz bản vá đó và lỗi đó vẫn tồn tại đến tận T1/2020 (thời điểm tôi report cho Microsoft).

 

Tôi không bất ngờ về việc Microsoft fix không hết bug, mà P0 public project này từ rất lâu rồi mà không hề có ai đó thử sử dụng để tìm bug.

 

Conclusion

 

Bug này không phải là khó tìm, mọi thứ đều ở sẵn trước mặt nhưng lại không có ai nhảy vào làm. Bạn có thể thấy các bước tôi làm đều trình bày rõ ở trong bài viết trước, đều là các kiến thức cơ bản bạn chưa phải sử dụng reverse nhiều để có thể viết được harness. Document của Microsoft rất đầy đủ, hãy chịu khó đọc, tìm hiểu và bắt tay vào làm thử. Nếu chỉ dừng lại ở việc đọc blog thì tôi nghĩ sẽ không mang lại nhiều kết quả.

 

Các lỗi khi fuzz định dạng file của windows xuất hiện càng ngày càng ít. Do cách này khá nhiều người sử dụng, đòi hỏi bạn phải bỏ nhiều thời gian để nghiên cứu tìm các attack surface mới, các định dạng file chưa ai nghiên cứu thì mới có thể ra được bug.

 

Blog sau tôi sẽ nói về bug của một định dạng file mà Microsoft đã thưởng cho tôi max bounty ở Windows Insider Preview Bounty. Có lẽ tôi sẽ public nó sau khi bản vá T6/2020 của Microsoft được release hoặc lâu hơn nữa.


[1] https://googleprojectzero.blogspot.com/

[2] https://portal.msrc.microsoft.com/en-us/security-guidance/acknowledgments

[3] https://docs.microsoft.com/en-us/

[4] https://github.com/microsoft/Windows-classic-samples

[5] https://googleprojectzero.blogspot.com/2016/06/a-year-of-windows-kernel-font-fuzzing-1_27.html

[6] https://googleprojectzero.blogspot.com/2016/07/a-year-of-windows-kernel-font-fuzzing-2.html

[7] https://docs.microsoft.com/en-us/windows/win32/api/fontsub/nf-fontsub-createfontpackage

[8] https://docs.microsoft.com/en-us/windows/win32/api/fontsub/nf-fontsub-mergefontpackage

[9] https://github.com/googleprojectzero/BrokenType/tree/master/ttf-fontsub-loader

[10] https://github.com/googleprojectzero/BrokenType/tree/master/ttf-otf-mutator

[11] https://bugs.chromium.org/p/project-zero/issues/detail?id=1863

[12] https://bugs.chromium.org/p/project-zero/issues/detail?id=1866

[13] https://bugs.chromium.org/p/project-zero/issues/detail?id=1868

[14] https://blog.vincss.net/2020/04/cve44-microsoft-font-subsetting-dll-heap-corruption-in-ReadTableIntoStructure-cve-2020-0687.html

Start fuzzing, Fuzz various image viewers

I will return to writing about what I have done in the past year, it has been 2 years since I came back to the blog. This article I will share the fuzzing experience I have learned through the process of using common fuzzer to find bug in non-source products. The environment I learn is Windows, the fuzzer I usually use is targeted at products on this environment. In this article, I will use a popular fuzzer called Winafl to find errors in popular image viewers such as Irfanview [1], Fast Stone [2], Xnview [3], etc.


Introduction


I will not elaborate on Winafl's architecture, nor how to use it. I will leave some links [4] [5] mentioning these issues at the end of the article, anyone interested can read it.


Why did I choose Image Viewers to approach fuzz? We can say file format fuzzing is a fuzzing direction is almost the most popular today. It takes little time to prepare, accessible, easy to find corpus (depending on the case),... and maybe logic parsing these file formats still have very high errors.


Here I will take an example of Irfanview, how I approach and use fuzzer to find errors parsing Irfanview image formats.


Reverse and understand


We can see Irfanview handles many image file formats. Some formats will be handled in the i_view32.exe program, some other file formats will be handled through plugins deployed through DLLs.


We need to understand how the flow of image files is pushed in. The reverse process to understand that logic is quick or slow, difficult, or easy depending on the complexity of each program. With complex programs, it will take a lot of time.


However in this case I will use DynamoRIO [6] (a tool used to calculate the coverage of the program when it executes) to support my reverse faster.


Using DynamoRIO with IDA's lighthouse plugin [7], we can tell with input how the program will go, what code to execute. Save a lot of time when reversing.


For example, when Irfanview processes a jpeg2000 image format, the command runs drrun.exe to generate file coverage:

drrun.exe -t drcov -- i_view32.exe _00042.j2k

With this command, DynamoRIO will generate a file containing information about the loaded DLLs and coverage of the program and each DLL file.


Here is an example output file coverage:



We can see the JPEG2000.dll DLL is loaded during the processing of _00042.j2k file.


Now use the lighthouse plugin on IDA to see the results. We can see that the commands highlighted in green are the ones that were executed when i_view32.exe processed the _00042.j2k file.



From there we will trace back the jpeg2000 image processing functions, by finding the related strings is the fastest and most effective way I often use. We can see that i_view32.exe will load library JPEG2000.dll and then call ReadJPG2000_W() function to process the jpeg2000 file.


Let's debug to see the parameters passed into the ReadJPG2000_W() function, we set the breakpoint at the address calling the ReadJPG2000_W() function:

According to the status of the stack at the time the ReadJPG2000_W() function is called, the parameters passed to the function are as follows:

- wchar_t *argv1: the name of the jpeg2000 file needs to be processed.

- int argv2: variable store value 0.

- wchar_t *argv3: a memory of size 2048.

- wchar_t *argv4: a memory of size 2048, initialized by the string "None".

- int argv5, argv6: used to save parameters while parsing jpeg2000 file.


It is very simple, it is possible to build a harness that calls this function and pass the above parameters to parsing a jpeg2000 image file. Because these parameters are completely independent from the program i_view32.exe.


Here is the harness that I wrote:



Run harness with input jpeg2000 file and check its coverage on JPEG2000.dll

Great, it works properly with i_view32.exe's jpeg2000 processing stream.

With this harness we will be able to use it for fuzzing. I use corpus on the GitHub repo:

 - openjpeg

 - go-fuzz-corpus

 - and some corpus from previous fuzz projects.

With lighthouse, we can also see coverage of each function in DLL.


Herewith the file I use the coverage of the ReadJPG2000_W function is only about 34%, of course, this figure is not ideal when fuzz, you need to find a corpus to push this number as high as possible.


Use Winafl to fuzz jpeg2000 with the harness I built above:


Looking at the interface Winafl we should be interested in some of the following parameters:

- exec speed: the number of test cases that can be executed on 1s

- stability: this indicator shows stability during fuzzing. When running Winafl there will be a certain number of iterations on that test case, in theory, the iterations on the same test case the coverage value must not be changed. If this value changes, the stability will not be high.

- map density: this parameter shows the coverage of the target when running with the current test case.


These three parameters must be high to be effective when the fuzz is high [4].


For other image file formats, Irfanview is treated the same as jpeg2000. The plugin responsible for parsing image files has the same functions for processing. In addition to fuzz jpeg2000 file format I also tried with other formats such as gif, dicom, djvu, ani, dpx, wbmp, webp, ...


Results: CVE-2019-17241, CVE-2019-17242, CVE-2019-17243, CVE-2019-17244, CVE-2019-17245, CVE-2019-17246, CVE-2019-17247, CVE-2019-17248 , CVE-2019-17249, CVE-2019-17250, CVE-2019-17251, CVE-2019-17252, CVE-2019-17253, CVE-2019-17254, CVE-2019-17255, CVE-2019-17256, CVE -2019-17257, CVE-2019-17258, CVE-2019-17259, CVE-2019-17261, CVE-2019-17262


Tips and Tricks


While using Winafl, I found Winafl to be most stable on Windows 7. Windows 10 is very bad, DynamoRIO has some problems with memory on Windows 10 that leads to fuzzer or crash.


When fuzzing, I recommend turning on the Page Heap for the harness, to better detect out-of-bounds and uninitialized memory errors.


Afl-tmin is a useful tool to help you minimize corpus, which will be very helpful with fuzzer during mutate corpus. However I usually do not use it because it is too slow. I think I will try using the halfempty tool [8] to replace afl-tmin in the future.


Speed ​​up: Harness is used to call the Windows API the less DynamoRIO instrument process faster. In the harness I wrote above, in the main function I use the LoadLibraryA function to load the DLL I need fuzz and my target_offset in the main function, it will greatly reduce the running speed of the fuzzer.


There are many workarounds. There are a few ways that I can read and read [9], changing the offset to start the instrument is quite good, but when using this method I check it when run in debug mode with iterator, my fuzzer is unstable, I Do not know why. Here, I use lief [10] to solve this problem. I will load the library I need to fuzz before executing the main function:


                
And this is the result, after this fuzz target, has been fixed my way:

                

Speed has improved, but this is not the best way because the speed depends on the target that you fuzz not only depends on the harness you build. I use it because on Windows there is one more fuzzer, but later I prefer to use it instead of Winafl, the way my library loads before the main function matches the architecture of that fuzzer. The following article I will mention more about that fuzzer more.


Corpus: Many people who are new to fuzz will often think of the most important and hardest to build a harness. For me, searching for corpus is the most difficult problem. Searching for corpus with high coverage is very rare, and with these corpus people often will not share because it is very valuable with fuzzing.


When finding a large corpus you should use winafl-cmin to reduce the number of test cases down. There will be test cases whose coverage is duplicated or included in other test cases.


Conclusion


This is the first target I use to learn fuzzing. When my bugs were submitted and got CVE, there were some who said that I farmed and was CVE grass. I also don't argue with those people, I just care what I do, what I will learn from it. Continuing this series on fuzzing, I will share how I approach and fuzz out the bugs of VMWare, Microsoft,... based on what I said in this article.


[3] https://www.xnview.com/en/

[4] https://www.apriorit.com/dev-blog/644-reverse-vulnerabilities-software-no-code-dynamic-fuzzing

[5] https://symeonp.github.io/2017/09/17/fuzzing-winafl.html

[6] https://github.com/DynamoRIO/dynamorio

[7] https://github.com/gaasedelen/lighthouse

[8] https://github.com/googleprojectzero/halfempty

[9] https://github.com/googleprojectzero/winafl/issues/4

[10] https://github.com/lief-project/LIEF




Vietnamese version


Tôi sẽ quay lại viết lách về những gì mình làm được trong năm vừa qua, cũng đã 2 năm rồi tôi mới quay lại viết blog. Bài viết này tôi sẽ chia sẽ những kinh nghiệm fuzzing mà tôi đúc kết được qua quá trình sử dụng các fuzzer phổ biến để tìm lỗi trong các sản phẩm không có mã nguồn. Môi trường tôi tìm hiểu là Windows, fuzzer tôi thường sử dụng đều target vào các sản phẩm trên môi trường này. Trong bài viết này tôi sẽ dụng một fuzzer phổ biến là Winafl để tìm các lỗi trong các trình image viewers phổ biến như Irfanview [1], Fast Stone [2], Xnview [3],…


Introduction


Tôi sẽ không trình bày quá kĩ về kiến trúc của Winafl, cũng như cách sử dụng nó. Tôi sẽ để 1 số link [4] [5] đề cập đến những vấn đề này ở cuối bài viết, ai quan tâm có thể đọc thử.


Tại sao tôi lại chọn các trình Image Viewers để tiếp cận fuzz. Có thể nói file format fuzzing là một hướng fuzzing gần như là phổ biến nhất hiện nay. Nó mất ít thời gian để chuẩn bị, dễ tiếp cận, dễ tìm corpus (tùy trường hợp),… và có thể logic parsing các định dạng file này vẫn còn tồn tại lỗi rất cao.


Ở đây tôi sẽ lấy ví dụ về Irfanview, cách tôi tiếp cận, sử dụng fuzzer như thế nào để tìm lỗi parsing các định dạng ảnh của Irfanview.


Reverse and understand


Ta có thể thấy Irfanview xử lý rất nhiều định dạng file ảnh. Một số định dạng sẽ được xử lý trong chương trình i_view32.exe, một số định dạng file khác được xử lý qua các plugin được triển khai qua các DLL.


yyyyy

Ta cần hiểu luồng xử lý các file ảnh được đẩy vào như thế nào. Quá trình reverse để hiểu logic đó nhanh hay chậm, khó hay dễ phụ thuộc vào độ phức tạp của từng chương trình. Với những chương trình phức tạp ta sẽ mất khá nhiều thời gian.


Tuy nhiên trong trường hợp này mình sẽ sử dụng DynamoRIO [6] (1 tool được sử dụng để tính coverage của chương trình khi nó thực thi) để hỗ trợ cho việc reverse của mình nhanh hơn.


Sử dụng DynamoRIO kèm plugin lighthouse [7] của IDA, chúng ta có thể biết được với input như thế chương trình sẽ đi như thế nào, sẽ thực thi những dòng lệnh nào. Tiết kiệm một lượng lớn thời gian khi reverse.


Ta lấy ví dụ khi Irfanview xử lý 1 định dạng ảnh jpeg2000, Lệnh chạy drrun để generate ra file coverage:

drrun.exe -t drcov -- i_view32.exe _00042.j2k

Với lệnh này DynamoRIO sẽ sinh ra 1 file chứa thông tin các DLL được load và coverage của chương trình và từng file DLL đó.

Dưới đây là output file coverage ví dụ:


Ta có thể thấy DLL JPEG2000.dll được load vào trong quá trình xử lý file _00042.j2k.

 

Bây giờ hãy sử dụng plugin lighthouse trên IDA để xem kết quả. Ta có thể thấy các lệnh được bôi xanh là những lệnh đã được thực thi khi i_view32.exe xử lý file _00042.j2k.


Từ đó ta sẽ trace ngược về các hàm xử lý ảnh jpeg2000, bằng việc find các string liên quan là cách hiệu quả và nhanh nhất mà tôi thường dùng. Ta có thể thấy i_view32.exe sẽ load library JPEG2000.dll sau đó gọi hàm ReadJPG2000_W() để xử lý file jpeg2000.



Hãy debug để xem các tham số truyền vào hàm ReadJPG2000_W(), ta đặt breakpoint tại địa chỉ gọi hàm ReadJPG2000_W():



Theo trạng thái của stack vào thời điểm function ReadJPG2000_W() được gọi, thì các tham số truyền vào hàm lần lượt như sau:

- wchar_t *argv1: tên của file jpeg2000 cần được xử lý

- int argv2: biến lưu giá trị 0

- wchar_t *argv3: một vùng nhớ có kích thước 2048

- wchar_t *argv4: một vùng nhớ có kích thước 2048, được khởi tạo bởi chuỗi None

- int argv5, argv6: dùng để lưu các thông số trong khi parsing file jpeg2000.


Nó rất đơn giản, ta hoàn toàn có thể xây dựng 1 harness gọi hàm này và truyền các tham số như trên để parsing 1 file ảnh jpeg2000. Vì các tham số này hoàn toàn độc lập so với chương trình i_view32.exe.

Dưới đây là harness mà tôi viết:



Chạy thử harness với input là file jpeg2000 và kiểm tra coverage của nó trên JPEG2000.dll



Tuyệt với nó hoạt động đúng với luồng xử lý jpeg2000 của i_view32.exe.

 

Với harness này ta sẽ có thể sử dụng để fuzzing. Tôi sử dụng corpus trên các repo github:

- Openjpeg

- go-fuzz-corpus

- và 1 số corpus từ các project tôi fuzz trước đó.

Với lighthouse chúng ta còn có thể xem coverage của từng hàm trong DLL.



Ở đây với file tôi sử dụng coverage của hàm ReadJPG2000_W chỉ đạt khoảng 34%, tất nhiên con số này là không lý tưởng khi fuzz, bạn cần tìm các corpus để đẩy con số này lên càng cao càng tốt.

 

Sử dụng Winafl để fuzz jpeg2000 với harness tôi xây dựng ở trên:



Nhìn vào giao diện Winafl chúng ta nên quan tâm 1 số thông số sau:

- exec speed: số testcase thực thi được trên 1s

- stability: chỉ số này thể hiện độ ổn định trong khi fuzzing. Khi thực hiện chạy Winafl sẽ có 1 số lần lặp nhất định trên testcase đó, về lý thuyết thì các lần lặp trên cùng 1 testcase giá trị coverage không được thay đổi. Nếu giá trị này thay đổi dẫn đến độ ổn định sẽ không cao.

-  map density: thông số này thể hiện coverage của target khi run với testcase hiện tại.

3 thông số này phải cao thì hiệu quả khi fuzz mới cao [4].

 

Đối với các định dạng file ảnh khác, irfanview đều xử lý tương tự như jpeg2000. Các plugin phụ trách parsing các file ảnh đều có các hàm tương tự để xử lý. Ngoài fuzz định dạng file jpeg2000 tôi còn thử với các định dạng khác như: gif, dicom, djvu, ani, dpx, wbmp, webp,…

 

Kết quả: CVE-2019-17241, CVE-2019-17242, CVE-2019-17243, CVE-2019-17244, CVE-2019-17245, CVE-2019-17246, CVE-2019-17247, CVE-2019-17248, CVE-2019-17249, CVE-2019-17250, CVE-2019-17251, CVE-2019-17252, CVE-2019-17253, CVE-2019-17254, CVE-2019-17255, CVE-2019-17256, CVE-2019-17257, CVE-2019-17258, CVE-2019-17259, CVE-2019-17261, CVE-2019-17262

 

Tips and Tricks


Trong quá trình sử dụng Winafl, tôi nhận thấy Winafl chạy ổn định nhất trên Windows 7. Windows 10 nó chạy rất tệ, DynamoRIO gặp 1 số vấn đề với memory trên Windows 10 dẫn đến fuzzer hay bị crash.


Khi fuzzing, tôi khuyên bạn nên bật Page Heap cho harness, để phát hiện tốt hơn các lỗi out-of-bounds và các lỗi uninitialized memory.


Afl-tmin là 1 tool hữu ích giúp bạn minimize corpus, sẽ rất hữu ích với fuzzer trong quá trình mutate corpus. Tuy nhiên tôi thường không sử dụng vì nó quá chậm. Tôi nghĩ tôi sẽ thử sử dụng tool halfempty [8] để thay thế afl-tmin trong tương lai.


Tăng tốc độ chạy: Harness được sử dụng call các api của windows càng ít thì quá trình DynamoRIO instrument càng nhanh. Ở harness tôi viết trên, trong hàm main tôi sử dụng hàm LoadLibraryA để load DLL tôi cần fuzz và target_offset tôi để ở hàm main, nó sẽ giảm tốc độ chạy của fuzzer đi nhiều.

Có nhiều cách giải quyết. Có 1 số cách mà tôi tìm đọc được [9], thay đổi offset bắt đầu instrument cũng khá hay, nhưng khi sử dụng cách này tôi kiểm tra nó khi run ở mode debug với iterator thì fuzzer của tôi chạy không ổn định, tôi không biết lý do tại sao. Ở đây, tôi sử dụng lief [10] để giải quyết vấn đề này. Tôi sẽ load library tôi cần fuzz lên trước khi thực thi hàm main:

 


Và đây là kết quả, sau khi fuzz target này, đã được sửa theo cách của tôi:



Tốc độ có cải thiện, tuy nhiên đây không phải là cách hay nhất vì tốc độ còn phụ thuộc vào target mà bạn fuzz không phải chỉ phụ thuộc vào harness mà bạn xây dựng. Tôi sử dụng nó bởi vì trên Windows còn 1 fuzzer nữa mà sau này tôi ưu tiên sử dụng nó thay vì Winafl, cách load library của tôi trước hàm main phù hợp với kiến trúc của fuzzer đó. Các bài viết sau tôi sẽ đề cập nhiều về fuzzer đó nhiều hơn.

 

Corpus: Nhiều người mới tiếp cận fuzz thường sẽ nghĩ xây dựng harness để fuzz quan trọng và khó nhất. Đối với tôi thì tìm kiếm corpus mới là vấn đề nan giải nhất. Tìm kiếm corpus với coverage cao rất hiếm, và với những corpus này thường mọi người sẽ không chia sẻ vì nó rất có giá trị với fuzzing.


Khi tìm được 1 lượng lớn corpus bạn nên dùng winafl-cmin để giảm số lượng testcase xuống. Sẽ có những testcase mà coverage của nó trùng lặp hoặc đã bao hàm trong testcase khác.

 

Conclusion


Đây là target đầu tiên tôi sử dụng để học fuzzing. Khi các bug của tôi được submit và lấy CVE có 1 số người nói rằng tôi farm và là CVE cỏ. Tôi cũng không tranh luận gì với những người đó, tôi chỉ quan tâm việc tôi làm thì tôi sẽ học được những gì từ đó thôi. Tiếp nối loạt bài về fuzzing này, tôi sẽ chia sẻ cách mà tôi tiếp cận và fuzz ra những bug của VMWare, Microsoft,… dựa trên những thứ tôi đã nói trong bài viết này.



[3] https://www.xnview.com/en/

[4] https://www.apriorit.com/dev-blog/644-reverse-vulnerabilities-software-no-code-dynamic-fuzzing

[5] https://symeonp.github.io/2017/09/17/fuzzing-winafl.html

[6] https://github.com/DynamoRIO/dynamorio

[7] https://github.com/gaasedelen/lighthouse

[8] https://github.com/googleprojectzero/halfempty

[9] https://github.com/googleprojectzero/winafl/issues/4

[10] https://github.com/lief-project/LIEF

RedVelvet - 75 pts

Bài này k có gì đặc biệt, nhập flag rồi check từng kí tự:


Các function từ fun1 -> fun15 sẽ check các kí tự của flag. Đoạn antidebug quá rõ nên bypass ezzz.
Khi check hết các function trên thì có nhiều flag, nói là nhiều nhưng chỉ có vài cái thôi, lôi chày cối sub thoải mái chả vấn đề gì. Còn không thì đi tiếp thêm 1 tí có đoạn check hash SHA256. Mình thì mình sẽ sub luôn =)).

Code solution mình code chày cối :( lúc làm mình thấy có hash nên sợ có nhiều đáp án dùng z3 sợ sau này phải tìm thêm kết quả.

easy_serial - 350pts

Đây là 1 challenge theo bản thân mình nghĩ là không văn minh. Hoặc ý đồ của người ra là bắt mình dùng tool để decompiler.
Ban đầu phấn khởi load lên IDA chạy. Dính ngay trick Virtual alarm có vẻ để đọc code mà bypass cái trick này khá rắc rối (không tính đến mấy cái trick tương tự mà chỉ gọi hàm alarm thì quá dễ). Mình đọc code để tìm hàm main của nó thì thấy cái tên khá là lạ “Main_main_infor”,… Search google thì biết được nó được code bằng Haskell. Check lại thì thấy ngay GHC.


Tiếp tục google xem có tool decompiler nó không thì mình tìm được 1 tool link mình để ở đây. Dùng khá dễ. Ngoài ra mình còn tìm được 1 link nó manually decompiler có thể tham khảo thêm cách họ làm. Sau khi decompiler thì nó ra 1 đống thế này đây chắc là opcode tượng trưng vì mình có search tìm cấu trúc của haskell thì nó khác cái đống này.

Cop sang notepad ngồi xem thấy có mấy đoạn text quen thuộc là khả thi rồi =)).

Đên đây thì bài này hết thú vị rồi. Vì nó rõ rành rành là so sánh các kí tự kia rồi. Và đây là script của bài này

Boom - 223pts

Bài này cũng được build đặc biệt multi platform. Nếu debug không quen nhay hết vào các hàm thì sẽ rất mất thời gian.
Ban tổ chức cho bài này quá dài. 1 bài VM mình làm chắc cũng chỉ dài đến tầm này.
Ở trong bài có rất nhiều nhánh. Input nhập vào thì cũng vậy. Mình mới chỉ làm 1 nửa thì dừng vì check server thì thấy đóng mất rồi. Khá tiếc không biết có mở lại hay k.

Vì nó dài nên mình định không viết bài này. Rất ngại đi vào chi tiết. Ở đây mình chỉ làm sơ bộ thì các phần mình đã giải rồi.
Nói qua 1 chút về mục đích của chương trình. Nó sẽ bắt mình nhập 1 chuỗi và kiểm tra nếu đúng nó sẽ đọc 1 file từ /tmp/files/?, và cứ thế đi sâu xuống, nhập đúng càng nhiều thì càng đọc được nhiều file, các file này được đánh số từ 1 -> 13.Chính vì mình chưa làm hết được các nhánh và server tắt rồi nên chưa chắc là file đó sẽ có gì. Có thể là các kí tự của FLAG

1. Đầu tiên khi khởi chạy chương trình, nó sẽ yêu cầu mình nhập 1 chuỗi.

Chuỗi mình nhập sẽ có 3 trường hợp:
Nếu nhập chuỗi “Know what? It's a new day~” thì sang nhánh 1 (mình đặt tên cho dễ viết thôi nhé)
Nếu nhập : ” It's cold outside..” sẽ sang nhánh 2. -> open file /tmp/files/2
Nếu nhâp: “We need little break!” sẽ sang nhánh 3 -> open file /tmp/files/3
 Mình sang nhánh 1 => mở và đọc file /tmp/files/1

2. Tiếp theo chương trình sẽ đọc vào 7 số. 7 số này sẽ được chuyển đổi qua 1 chuỗi kí tự và so sánh nếu đúng thì sẽ tiếp tục đọc thêm 1 file. Điều đặc biệt là có đến 3 chuỗi được đem ra so sánh =)).

key1 = "carame1" => [3 14 7 14 60 1 26] => /tmp/files/4
key2 = 'w33kend' => [49 15 15 31 1 23 13] => /tmp/files/5
key3 = 'pand0ra' => [57 14 23 13 50 7 14] => /tmp/files/6
Cả 3 dãy số trên đều đúng tuy nhiên mỗi dãy mở ra 1 file khác nhau. Rối rắm vc

3. Tiếp theo chương trình sẽ yêu cầu nhập 1 số và sẽ check số đó thong qua hàm main::fun12

If(main::fun12(0,number)==0x6b) => true
Cái điều kiện đó có rất nhiều số thỏa mãn nhé.Sau khi nhập đúng nó tiếp tục mở 1 file /tmp/files/13

4. Sau đó nó yêu cầu nhập tiếp 4 số. Đến đây mình đọc code không hiểu cái số đó nó làm gì và khá nản. Nhưng ai ngờ lỗi như nào chương trình này nó in cho mình xem hết.

Mình thử 1 vài input nữa và nhận ra số chỉ từ 1->9. Vậy thì tại sao không brute force =)).
Mình ngồi burte ra 1 số nghiệm thỏa mãn. Ex: 1 3 5 8
Và khi nhập đúng nó tiếp tục mở 1 file /tmp/files/8

5. Tiếp đến là 1 chuỗi để biến đổi và so sánh với “H_vocGfsg4p_xicwcrwexg4r”. Các thuật toán biến đổi mình sẽ có trong code solution.
Khi nhập đúng nó sẽ mở tiếp file /tmp/files/11
Sauk hi xong chuỗi này mình nhận ra rằng nó quay lại cái chỗ nhập 7 số. Và mình đã nhập tiếp các trường hợp còn lại ở trên.
- Đối với trường hợp key1 = "carame1" => [3 14 7 14 60 1 26] => /tmp/files/4 thì nó cư vòng vo quay đi quay lại các bước ở trên
- Duy chỉ với TH: key3 = 'pand0ra' => [57 14 23 13 50 7 14] => /tmp/files/6 thì sau đó nó yêu cầu mình nhập 27 số và kiểm tra. Và nó mở thêm 1 file /tmp/files/10
Đến đây mình đã check rất kĩ mà k thấy điều kiện thoát nó cứ vòng vo nhập số lại nhập chuỗi.
Nếu các file kia chứa các kí tự của flag thì chắc mình sẽ khởi chạy 3 lần, để lấy ra ki tự =)).
Thôi chắc đến đây thôi. Nếu server bật lại mình sẽ thử nốt =)). Code mình giải các problem ở đây.

Review nhẹ các bài Reverse ở Codegate

1. RedVelvet – 75 pts
    Đây là 1 bài đơn giản, đơn thuần check các kí tự của flag.
2. Welcome to droid - 125pts
    Bài này mình làm khá tù, mình xem write-up của họ thì patch lại entry point, trong khi mình đi đọc opcode dalvik rồi patch cái hàm random =)). Khá óc nên mình k viết bài này.
3. Boom - 223pts
   Bài này mình làm gần xong thì phát hiện server bị tắt nên dừng luôn. Về cơ bản nó khá là dài và được build củ chuối nữa.
4. easy_serial - 350pts
    Bài này cũng là 1 dạng củ chuối như bài boom. Nhưng nó còn củ chuối hơn nữa là có tool decompiler.
5. 6051 - 880pts
    Đây là challenge mà mình thấy hay nhất mà mình đã làm được trong đống này. Mạng nặng tính thuật toán.
6. CPU - 971pts
    1 Dạng VM có lẽ thế, vì mới xem qua code thì hình giống bài ở mates-round1, cũng bắt nhập opcode để thực thi. Đây cũng là 1 bài connect server vì server tắt nên mình chưa động chi đến nó =)). Ngụy biện =)) có bật chắc cũng khó mà chịch được nó.

Serialme

Đây là 1 dạng bài vm engine. Mình có đọc nhiều writeup của nhiều pro mà họ làm mình đọc không hiểu gì :v. Toàn giải văn minh thôi à.
Mong là sau này mình hiểu rõ hơn cái này để làm. Còn bây giờ mình giải được bài này là nhờ vào cái này
Mình có đọc và tìm hiểu qua về cơ chế của vm_engine.Cái cơ chế của nó tập trung ở vòng lặp “vm_loop”.
Nói qua về cơ chế của bài này. Đề yêu cầu mình nhập 10 số lần lượt, đúng đủ 10 số thì sẽ bung flag ra. Nói đơn giản vậy chứ cái vm_engine kia làm mình rối rắm trong khâu check các số đó lắm.
Cụ thể ở bài này cái vòng lặp vm nó nhìn tổng quát sẽ như thế này đây:

Ở cái vòng lặp này có đến 20 case, mỗi case trong đó cơ bản nó chỉ thực hiện 1 lệnh nào đó ví dụ như add, xor, mov,… Nên mình làm là mình trace trâu bò :v.

case 12: là case minh nhập vào số và xử lý ở đây. Sau khi cái đống code kia xử lý số nhập vào thì giá trị đó được lưu ở eax. Thực ra nếu đi sâu vào thì rắc rối phết. Vì tác giả chuyển đổi kiểu input thành input từ file đâm ra input số vào lại là chuỗi xong chuyển qua chuyển lại mới ra cái giá trị được lưu trong eax kia.
Đến đây thì từng số sẽ được đi qua các case để thực hiện các phép tính toán. Và cuối mỗi lần thực hiện đó nó lại dừng ở case 8 để so sánh. Ta cần làm nó thỏa mãn cái điều kiện ở đây thì mới có thể nhập tiếp được.

Đến đây thì ezz trâu bò ra flag rồi :v.

Debugme

Bài này mình sẽ giải thích sơ qua. Khi mình nhập flag vào, flag sẽ được mang đi mã hóa (AES 256). Sau đó sẽ được xor với 1 chuỗi :v. Đó tất cả đấy =)).
Về điều kiện từng cái như sau: độ dài flag = 9.

Sau đó chương trình sẽ tạo ra 1 thread. Trong thread này sẽ chạy 16 vòng lặp mã hóa. Nhưng đây là troll mình :v. Chạy 16 vòng y hệt nhau không phải gối lên nhau. Chung quy lại cái thread kia chỉ là mã hóa AES flag thôi.

- Đây là source code mã hóa AES. Cái thread này nó đánh lừa mình và làm mình mất khá nhiều thời gian. Như mình nói ở trên đây thực ra chỉ là mã hóa cái flag 1 lần chứ không phải 16 lần. Nhưng khi mình check kết quả sau khi chuỗi mình nhập chạy qua cái thread này nó lại trả giá trị khác với mã hóa 1 lần. Làm mình phân vân loay hoay ^^.
- Sau 1 hồi mình mới để ý cái hàm WaitForMultipleObjectsEx ở ngay dưới Thread.Mình sẽ đặt break point ở trước hàm sleep kia Vì mình kiểm tra gia trị bị thay đổi sau khi chạy qua hàm Sleep

- Để bắt được cái khoảnh khắc đó. Mình đặt 1 hard break point ở địa chỉ lưu trữ kết quả của chuỗi (byte_1D4CA0) và chạy bình thường. Để khi cái hàm làm thay đổi giá trị mã hóa thực hiện thì chương trình của mình sẽ dừng ở đó.
- Và đây là hàm thay đổi giá trị ciphertext, đơn giản là nó xor với giá trị ở xmmword_1A9EF0

- Đến đây là xong rồi ciphertext sau khi xor sẽ được mang đi so sánh với giá trị ở đây

- Tất cả các bước check giá trị ciphertext và plaintext mình thực hiện ở trên trang web: http://aes.online-domain-tools.com/

Unlockme

Bài này là 1 dạng serial, mình sẽ nhập name và key vào. Sau khi biến đổi name và key nó phù hợp theo đúng thuật toán của bài là sẽ có flag thôi :v.
Bài này code khá dài và lằng nhằng. Mình nói qua thuật toán bài này như sau:
- Về phần name, sau khi được nhập vào lần lượt các kí tự sẽ được biến đổi theo đoạn mã giả sau:

Sau khi biến đổi thì giá trị của biến base sẽ được dùng để check key mình nhập
- Về key thì định dạng của key sẽ là xx-xx-xx-xx => leng = 11. Nếu để ý thì sẽ có đoạn check độ dài của key:

Tiếp theo các giá trị ở xx kia sẽ được chuyển trực tiếp thành số. Ví dụ 12-13-3f-4a thì sẽ thành 4 số là 12,13,3f,4a
Các con số này sẽ được mang đi add với từng kí tự trong cái base ở trên kia theo thứ tự ngươc nhau và điều kiện ràng buộc như sau:
base[0] + số thứ 4 == base[1] + số thứ 3 == base[2] + số thứ 2 == base[3] + số thứ 1
(cái thứ tự là mình nói để dễ hình dung thôi nhá còn source code của tác giả có thể văn minh hơn của mình =)))
Khi mình làm đến đây rồi mình cứ nghĩ là ok, ai ngờ ban tổ chức troll lại còn fake flag :v. Sub sai sấp mặt

Tác giả ra đề còn anti debug đoạn in ra flag với hình như bắt mình nhập name phải dài hơn 1 kí tự đoạn này mình sửa luôn thanh ghi để bypass :v
WhiteHat{2f2a1ebcc9f4b69502343e04bc2ae7e185e4c01a}

Writeup Pyc

Sau khi nhận được file mình giải nén thì được file connect.pyc. Cái này dùng tool decompiler để lấy source thôi. Và đây là source nguồn của nó.

Đây là 1 cách thực thi code mà python hỗ trợ, cái đống data kia là 1 dạng binary sau khi được build từ source và có thể thực thi bằng cách marshal.loads như trên.
Đến đây mình có google để tìm hiểu về cái này và thấy đâu đâu cũng chỉ mình cách dùng disassemble của python để biểu diễn cái source kia sang 1 dạng ngôn ngữ tựa asm mà mình có thể hiểu.
Thấy thế mình nghĩ ezz là dis rồi xem cái đó nhưng mà không đơn giản tí nào :v

Phải mất 1 hồi để hiểu được cái cấu trúc của cái đống trong hình =)). Sau đó thì mình code lại cái nội dung mà đoạn mình vừa disassem ra ở trên để cho dễ theo dõi. Link file mình cover lại code

Về cách hoạt động để check flag của nó sau khi mình code lại bằng python cũng không có gì đặc biệt. Flag được tách ra thành 6 cụm mỗi cụm 4 kí tự lần lượt nằm trên các dòng và được lưu trong file có tên là flag.txt. Nó sẽ đọc flag ở trong file và kiểm tra.
Có 3 hàm kiểm tra chính là “xor” ,”check_flag” và “check_str” các nội dung trong file flag.txt sẽ được mang đi kiểm tra qua các hàm này.
Bài này không phải khó ở đoạn getflag mà chắc ý của ban tổ chức là cái đống bycode python mình cần disassembly để hiểu ở trên kia thôi
Mình có viết 1 đoạn Sript để lật ngược lại lấy được các cụm flag cũng dạng là brute thôi :v.
Có nhiều hơn 1 flag với cái Script của mình vì mình chỉ dùng có 2 điều kiện để check flag nhưng khi chạy thì nhìn được ngay đâu là flag đúng thôi :v
{Ch4ll3ngE_V3ry_FUN_R1gHt}

FeelMyPain

Bài này mình méo hiểu ý của ban tổ chức căn bản mình thử cờ nó đúng luôn nên không xem kĩ cái kỹ thuật ở đây là gì. Hình như nó bung ra cái SEH exeption.
Cái lệnh call esi đưa mình đến flag rồi :v

Flag: love_is_the_reason_why_there_is_pain

Writeup re250 (Picaso)

Loay hoay làm bài da-vinci (re150) 1 hồi mãi không hiểu ý của tác giả là gì mặc dù thuật toán bài đó rất rõ ràng.
Quay sang bài picaso(re250) :v không phải chọn nó vì tên mà xem số người làm được thôi.
Ban đầu nhìn dung lượng file này thấy ngờ ngợ (~10mb). Load lên ida mới giật mình thấy toàn là lệnh mov :v. Và đây là dạng mình làm rồi (đề thi chọn đội tuyển của UET).
Đây là 1 dạng obfuscate code với lệnh mov, cái này mà để nguyên debug thì chỉ có sấp mặt
  • Nếu cứ cố chấp trace thì có lẽ sẽ nhìn thấy cái chữ màu vàng trên hình kia khá khá khá nhiều lần :v.
  • Đối với dạng này đầu tiên cần phải deobfuscate được code. Cái này thì mình không biết nhưng thằng khác nó lại biết, còn viết cả code mình chỉ lấy tool nó viết về xài thôi
  • Sau khi demovobfuscate được thì code nhìn cũng không có gì khác biệt vẫn rất nhiều lệnh mov nhưng quan trọng ở chỗ là code bây giờ đã xuất hiện các lệnh JMP trước đó bị làm rối.
  • Đến đây thì giải bài này dễ hơn nhiều rồi, vì là đã làm rối code nên cái check flag của bài này rất đơn giản.
  • Cách mình lật ngược cái đoạn code check flag như thế này:
    • Mình tìm đoạn text in ra flag -> đặt nhãn mới cho đoạn code này
    • Tìm tiếp cái đoạn “No! Try another key!” kìa -> đặt tiếp cái nhãn đến đó
    • Và bắt đầu lật ngược code từ chỗ in flag lên Có đến 9 lệnh JMP đến cái nhãn No_flag => đoán sơ sơ ban đầu chắc 1 lần check leng, 8 lần check flag.
    • Nếu lật ngược lên từ đoạn code in flag thì có thể nhận ra dễ dàng là nó sẽ kiểm tra flag sau đó nếu thỏa mãn nó sẽ nhảy sang đoạn code check flag tiếp nếu k nó sẽ Jmp No_flag
    • Cứ như vậy cho đến đầu code (làm thế này rất nhanh mà không phải mò mẫm khi cứ thế trâu bò debug luôn) thì thấy đoạn check leng của flag:
  • Khi xong xuôi thì có thể debug luôn :v. tìm flag nhưng code vẫn còn rối với nhiều mov lắm, trace ra cờ chắc cũng mất thời gian. Nhưng trong lúc mình truy ngược lên mình có để ý ở trong các hàm check flag là code trong đó phần lớn là giống nhau, giống đoạn đầu code (đoạn jmp đến) cũng như đoạn cuối code (đoạn jmp đi) cấu trúc là như nhau vậy thì giữa code :v…
mov R3, edx
   mov R2, value
mov eax, R3
mov edx, R2
    • Cái mẩu code này là chìa khóa giải bài này cái giá trị value kia sẽ thay đổi qua từng lần JMP và tất cả các giá trị đó mình nhặt ra đây:
    • [8,0x71,0x69,0x65,0x65,0x76,0x74,0x69,0x74] => [qieevtit]
  • Cái số 8 kia có thể là độ dài của flag
  • Nhập thử thì đây không phải flag, bị hoán đổi vị trí rồi. Nhìn là nghĩ ngay đên brute chứ ngại debug cái đống code kia lắm. Do thời gian này đang học dùng mấy cái tool kiểu như z3, angr (mình gà lắm chả biết mấy cái tool này ,mấy bài mà giải được bằng nó thì toàn lôi notepad++ ra nháp).
  • Để mò nốt cái flag kia là gì mình dùng pin tool cái này đọc trong blog của sư phụ mình lâu rồi cái này mình thấy rất hay. Cái tool này đơn thuần là đếm số lệnh nó thực hiện khi chạy. Vậy thì mình sẽ thử vị trí của cái đống kí tự kia nếu số lượng lệnh tang lên theo các lần thử thì vị trí đó đúng nếu k thì sai :v. Và đây là code slove mình viết bằng python chạy khá ổn nhưng mà chậm không biết làm thế nào cho nhanh.
  • Mình chỉ thắc mắc là chỗ check leng của bài này. Khi xài tool kiểm tra lại leng của flag thì thấy lạ là đối với flag dài 7 kí tự đổ xuống thì số lệnh sẽ tăng lên dần dần, nhưng đến khi 8 kí tự trở lên thì số lệnh giảm 1 xíu so với leng = 7 và nó k thay đổi từ đó => mình vẫn mập mờ với cái đoạn check leng này. Nếu mà thế kia thì đoạn code check leng của tác giả khéo là chỉ kiểm tra nó >= 8, hoặc là gì gì đó mà mình không biết.
Và đây là kết quả mình thử lấy flag với pin tool:

=)) tieqviet
Và lấy flag di sub wôi:

❌