Continuing with the fuzz and getting more targeted

OK folks, back at it this evening (wtf am I a weatherman??). Since I’ve found ~10 0-days in the Shell32/explorer.exe automation library I think it’s time we admit that I’m going to be finding 0-days all fucking day in this thing. So now I need to get a little smarter and choose some targets that are going to make the most impact. How I do dis? Well, we’re gonna start using an RE tool (yay?) that lights up the parts of a binary that have been visited. It’s called lighthouse, it’s primarily used by people that fuzz to ensure that they’re getting good coverage after a fuzz run. I don’t care about that right now, what I really want is to know which functions are called when I pop open explorer.exe. That way I can target those functions, 0-day one of them (for example one that lists the contents of a folder), try to control that 0-day vuln and write a 0-day exploit for it. Why? Because I can. And because it could also be worth some money. I used to think of this as a dirty practice, but I realized that in reality all this does is make your hunting sustainable and we all want me to keep finding 0-days right?? Bug bounties certainly won’t cut it, I don’t work for 500 bucks for a week’s worth of work and an “I <3 microsoft” fucking t-shirt. Anyway let’s get at it. First things first, I’ve downloaded DynamoRIO and I’m going to go ahead and instrument it using drcov. I have Binary Ninja installed (because fuck IDA), and the lighthouse plugin installed. Just follow the instructions, they work. Let’s get going on collecting coverage information. Of course another technique I’ll use in tandem is simply dynamic analysis, I’ll pop a debugger open on explorer.exe and just check out which functions are called. I downloaded all of the necessary symbols in a previous post somewhere in there, but let’s do one at a time. To be totally honest I’ve never tried this lighthouse thing out and I have no idea if it’ll work how I think it will, but I’ve used it for fuzzing purposes before and it’s worked nicely there.

OK so I pop open Binary Ninja. Aaaand you know what? I’m fucking tired. I’m picking this back up tomorrow. Later peeps, sorry for the lame duck post.

Alright, so I’ve lost data a bunch of times trying to test explorer.exe so hopefully this time it makes it. Here’s what I did, I went ahead and downloaded all symbols by using the Microsoft Symbol Downloader, which makes quick work of stuff, and I’ve stored everything in C:\symbols. Now I’m going to try a simple operation - open a folder using the explorer.exe command and see if I can make sense of what’s going on by using Binary Ninja and/or x64dbg. I’ve downloaded DynamoRIO and opened a folder like this:

drrun.exe -t drcov -- explorer.exe doc/

Then I close the folder. This produces a file called something-coverage.log, then I go ahead and open explorer.exe in BNinja. Right after opening I point it to C:\symbols and tell it to reanalyze, giving me some nice function names in my disassembly. It hsould look something like this:

shelldisasm

Great, now I go up to Tools -> lighthouse -> Load Coverage File and choose my loaded file. For me, this opened a tiny window I couldn’t find called the Coverage Explorer, if you don’t see it look harder or you can explicitly open it with Tools -> lighthouse -> Coverage View. OK cool, so now I have a coverage view of functions that were called in the order that they were called in. It looks like this:

coverage

After a fuckton of scrolling and looking for a function that would cause something interesting to happen I found one possible candidate. I say possible candidate because it may not end up being interesting, and the attack vector may not end up being any good. We’ll see though, it’d be a win just to be able to take control of the program and do some nasty stuff. The functions I found were: SHExplorerParseCmdLine(uint16_t* arg1, struct _ITEMIDLIST_ABSOLUTE** arg2); which then calls SHGetFolderLocation(0, 5, 0, 0, pidlAbsolute);. It looks to me like what’s happening is this is parsing the name I feed to the command line and running SHGetFolderLocation from it. So let’s try to find a vuln in SHGetFolderLocation - from previous research, this library is swiss cheese, so I’d be shocked if I didn’t find SOMETHING. Let’s go back to my old code and try to do something interesting. I quickly realized I didn’t even need that. This time it takes a lot fewer lines:

#include <shlobj.h>
#include <shlwapi.h>
#include <iostream>
#include <objbase.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int fuzzMeDrZaus(const uint8_t *Data, size_t size)
{


    PIDLIST_ABSOLUTE pidlAbsolute;
    pidlAbsolute = ILCreateFromPath((PCTSTR) Data)
    SHGetFolderLocation(0, 5, 0, 0, pidlAbsolute);

    return 0;
};


extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {

  fuzzMeDrZaus(Data, Size);
  return 0;
}

Notice I’m creating a PIDLIST_ABSOLUTE object pointer and shoving it into SHGetFolderLocation. If we can find a name in which explorer.exe cannot handle or causes some corruption, that’s a win. So let’s get fuzzing to find that name. We compile:

C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ clang-cl.exe /Zi -fsanitize=fuzzer,address -fsanitize-recover=address shell32_pwn_targeted.cpp ole32.lib shell32.lib shlwapi.lib
shell32_pwn_targeted.cpp(18,5): error: no matching function for call to 'SHGetFolderLocation'
    SHGetFolderLocation(0, 5, 0, 0, pidlAbsolute);
    ^~~~~~~~~~~~~~~~~~~
C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\um\shlobj_core.h(922,10): note: candidate function not viable: no known conversion from 'LPITEMIDLIST'
      (aka '__unaligned _ITEMIDLIST *') to 'LPITEMIDLIST *' (aka '__unaligned _ITEMIDLIST **') for 5th argument; take the address of the argument with &
SHSTDAPI SHGetFolderLocation(_Reserved_ HWND hwnd, _In_ int csidl, _In_opt_ HANDLE hToken, _In_ DWORD dwFlags, _Outptr_ PIDLIST_ABSOLUTE *ppidl);
         ^
1 error generated.

And it won’t… looks like that function is in shobj_core.h, so let’s add that header. I think we can keep our linked libs the same. And another mistake, I had to be more careful about my pointers:

C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ clang-cl.exe /Zi -fsanitize=fuzzer,address -fsanitize-recover=address shell32_pwn_targeted.cpp ole32.lib shell32.lib shlwapi.lib
shell32_pwn_targeted.cpp(17,20): error: incompatible pointer types assigning to 'LPITEMIDLIST *' (aka '__unaligned _ITEMIDLIST **') from 'LPITEMIDLIST'
      (aka '__unaligned _ITEMIDLIST *')
    pidlAbsolute = ILCreateFromPath((PCTSTR) Data);
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\um\shlobj_core.h(679,33): note: expanded from macro 'ILCreateFromPath'
#define ILCreateFromPath        ILCreateFromPathA
                                ^
1 error generated.

C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ clang-cl.exe /Zi -fsanitize=fuzzer,address -fsanitize-recover=address shell32_pwn_targeted.cpp ole32.lib shell32.lib shlwapi.lib
   Creating library shell32_pwn_targeted.lib and object shell32_pwn_targeted.exp

And looks like I get some near-immediate crashes, sometimes after a lot of executions:

C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ .\shell32_pwn_targeted.exe
INFO: Seed: 2782582928
INFO: Loaded 1 modules   (2 inline 8-bit counters): 2 [00007FF6695D9088, 00007FF6695D908A),
INFO: Loaded 1 PC tables (2 PCs): 2 [00007FF669584718,00007FF669584738),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED cov: 2 ft: 2 corp: 1/1b exec/s: 0 rss: 79Mb
=================================================================
==12516==ERROR: AddressSanitizer: attempting to call malloc_usable_size() for pointer which is not owned: 0x00000046c680
    #0 0x7ff669451af4 in __sanitizer::BufferedStackTrace::UnwindImpl(unsigned __int64, unsigned __int64, void *, bool, unsigned int) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\asan\asan_stack.cpp:77
    #1 0x7ff66946d166 in __asan::asan_malloc_usable_size(void const *, unsigned __int64, unsigned __int64) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\asan\asan_allocator.cpp:986
    #2 0x7ff8776536e8  (C:\Windows\System32\ucrtbase.dll+0x1800136e8)
    #3 0x7ff877653603  (C:\Windows\System32\ucrtbase.dll+0x180013603)
    #4 0x7ff87765349a  (C:\Windows\System32\ucrtbase.dll+0x18001349a)
    #5 0x7ff87765344f  (C:\Windows\System32\ucrtbase.dll+0x18001344f)
    #6 0x7ff87765ab60  (C:\Windows\System32\ucrtbase.dll+0x18001ab60)
    #7 0x7ff878d663b1  (C:\Windows\System32\SHELL32.dll+0x1801063b1)
    #8 0x7ff878d663d4  (C:\Windows\System32\SHELL32.dll+0x1801063d4)
    #9 0x7ff878cf41f6  (C:\Windows\System32\SHELL32.dll+0x1800941f6)
    #10 0x7ff878cc54a9  (C:\Windows\System32\SHELL32.dll+0x1800654a9)
    #11 0x7ff878d711a4  (C:\Windows\System32\SHELL32.dll+0x1801111a4)
    #12 0x7ff878d711fb  (C:\Windows\System32\SHELL32.dll+0x1801111fb)
    #13 0x7ff878cb309c  (C:\Windows\System32\SHELL32.dll+0x18005309c)
    #14 0x7ff87782370f  (C:\Windows\System32\windows.storage.dll+0x1800e370f)
    #15 0x7ff87783e1a4  (C:\Windows\System32\windows.storage.dll+0x1800fe1a4)
    #16 0x7ff87783daf4  (C:\Windows\System32\windows.storage.dll+0x1800fdaf4)
    #17 0x7ff877840b5e  (C:\Windows\System32\windows.storage.dll+0x180100b5e)
    #18 0x7ff877846a8b  (C:\Windows\System32\windows.storage.dll+0x180106a8b)
    #19 0x7ff878cbb8bb  (C:\Windows\System32\SHELL32.dll+0x18005b8bb)
    #20 0x7ff878ead731  (C:\Windows\System32\SHELL32.dll+0x18024d731)
    #21 0x7ff878ead69e  (C:\Windows\System32\SHELL32.dll+0x18024d69e)
    #22 0x7ff669431149 in fuzzMeDrZaus(unsigned char const *, unsigned __int64) C:\Users\Garrett McParrot\Desktop\0day\shell32an\shell32_pwn_targeted.cpp:17
    #23 0x7ff66943125a in LLVMFuzzerTestOneInput C:\Users\Garrett McParrot\Desktop\0day\shell32an\shell32_pwn_targeted.cpp:26
    #24 0x7ff669499b1a in fuzzer::Fuzzer::ExecuteCallback(unsigned char const *, unsigned __int64) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:559
    #25 0x7ff669498f96 in fuzzer::Fuzzer::RunOne(unsigned char const *, unsigned __int64, bool, struct fuzzer::InputInfo *, bool *) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:471
    #26 0x7ff66949b221 in fuzzer::Fuzzer::MutateAndTestOne(void) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:702
    #27 0x7ff66949be25 in fuzzer::Fuzzer::Loop(class std::vector<struct fuzzer::SizedFile, class fuzzer::fuzzer_allocator<struct fuzzer::SizedFile>> &) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:838
    #28 0x7ff6694b24f0 in fuzzer::FuzzerDriver(int *, char ***, int (__cdecl *)(unsigned char const *, unsigned __int64)) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerDriver.cpp:847
    #29 0x7ff669474222 in main C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerMain.cpp:20
    #30 0x7ff6694b9e9f in __scrt_common_main_seh d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #31 0x7ff879377c23  (C:\Windows\System32\KERNEL32.DLL+0x180017c23)
    #32 0x7ff87a70d4d0  (C:\Windows\SYSTEM32\ntdll.dll+0x18006d4d0)

Address 0x00000046c680 is a wild pointer.
SUMMARY: AddressSanitizer: bad-malloc_usable_size C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\asan\asan_stack.cpp:77 in __sanitizer::BufferedStackTrace::UnwindImpl(unsigned __int64, unsigned __int64, void *, bool, unsigned int)
==12516==ABORTING
MS: 2 ChangeBinInt-InsertRepeatedBytes-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x1,
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\x01
artifact_prefix='./'; Test unit written to ./crash-73d2b1c2778048fee86f58a3818c24f8f400e88f
Base64: XFxcXFxcXFxcXFxcXFxcXFxcXFwB

C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ attempting to call malloc_usable_size() for pointer which is not owned: 0x00000046c680
C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ .\shell32_pwn_targeted.exe
INFO: Seed: 3454158725
INFO: Loaded 1 modules   (2 inline 8-bit counters): 2 [00007FF6695D9088, 00007FF6695D908A),
INFO: Loaded 1 PC tables (2 PCs): 2 [00007FF669584718,00007FF669584738),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED cov: 2 ft: 2 corp: 1/1b exec/s: 0 rss: 79Mb
=================================================================
==5600==ERROR: AddressSanitizer: attempting to call malloc_usable_size() for pointer which is not owned: 0x0000005c09b0
    #0 0x7ff669451af4 in __sanitizer::BufferedStackTrace::UnwindImpl(unsigned __int64, unsigned __int64, void *, bool, unsigned int) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\asan\asan_stack.cpp:77
    #1 0x7ff66946d166 in __asan::asan_malloc_usable_size(void const *, unsigned __int64, unsigned __int64) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\asan\asan_allocator.cpp:986
    #2 0x7ff8776536e8  (C:\Windows\System32\ucrtbase.dll+0x1800136e8)
    #3 0x7ff877653603  (C:\Windows\System32\ucrtbase.dll+0x180013603)
    #4 0x7ff87765349a  (C:\Windows\System32\ucrtbase.dll+0x18001349a)
    #5 0x7ff87765344f  (C:\Windows\System32\ucrtbase.dll+0x18001344f)
    #6 0x7ff87765ab60  (C:\Windows\System32\ucrtbase.dll+0x18001ab60)
    #7 0x7ff878d663b1  (C:\Windows\System32\SHELL32.dll+0x1801063b1)
    #8 0x7ff878d663d4  (C:\Windows\System32\SHELL32.dll+0x1801063d4)
    #9 0x7ff878cf41f6  (C:\Windows\System32\SHELL32.dll+0x1800941f6)
    #10 0x7ff878cc54a9  (C:\Windows\System32\SHELL32.dll+0x1800654a9)
    #11 0x7ff878d711a4  (C:\Windows\System32\SHELL32.dll+0x1801111a4)
    #12 0x7ff878d711fb  (C:\Windows\System32\SHELL32.dll+0x1801111fb)
    #13 0x7ff878cb309c  (C:\Windows\System32\SHELL32.dll+0x18005309c)
    #14 0x7ff87782370f  (C:\Windows\System32\windows.storage.dll+0x1800e370f)
    #15 0x7ff87783e1a4  (C:\Windows\System32\windows.storage.dll+0x1800fe1a4)
    #16 0x7ff87783daf4  (C:\Windows\System32\windows.storage.dll+0x1800fdaf4)
    #17 0x7ff877840b5e  (C:\Windows\System32\windows.storage.dll+0x180100b5e)
    #18 0x7ff877846a8b  (C:\Windows\System32\windows.storage.dll+0x180106a8b)
    #19 0x7ff878cbb8bb  (C:\Windows\System32\SHELL32.dll+0x18005b8bb)
    #20 0x7ff878ead731  (C:\Windows\System32\SHELL32.dll+0x18024d731)
    #21 0x7ff878ead69e  (C:\Windows\System32\SHELL32.dll+0x18024d69e)
    #22 0x7ff669431149 in fuzzMeDrZaus(unsigned char const *, unsigned __int64) C:\Users\Garrett McParrot\Desktop\0day\shell32an\shell32_pwn_targeted.cpp:17
    #23 0x7ff66943125a in LLVMFuzzerTestOneInput C:\Users\Garrett McParrot\Desktop\0day\shell32an\shell32_pwn_targeted.cpp:26
    #24 0x7ff669499b1a in fuzzer::Fuzzer::ExecuteCallback(unsigned char const *, unsigned __int64) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:559
    #25 0x7ff669498f96 in fuzzer::Fuzzer::RunOne(unsigned char const *, unsigned __int64, bool, struct fuzzer::InputInfo *, bool *) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:471
    #26 0x7ff66949b221 in fuzzer::Fuzzer::MutateAndTestOne(void) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:702
    #27 0x7ff66949be25 in fuzzer::Fuzzer::Loop(class std::vector<struct fuzzer::SizedFile, class fuzzer::fuzzer_allocator<struct fuzzer::SizedFile>> &) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:838
    #28 0x7ff6694b24f0 in fuzzer::FuzzerDriver(int *, char ***, int (__cdecl *)(unsigned char const *, unsigned __int64)) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerDriver.cpp:847
    #29 0x7ff669474222 in main C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerMain.cpp:20
    #30 0x7ff6694b9e9f in __scrt_common_main_seh d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #31 0x7ff879377c23  (C:\Windows\System32\KERNEL32.DLL+0x180017c23)
    #32 0x7ff87a70d4d0  (C:\Windows\SYSTEM32\ntdll.dll+0x18006d4d0)

Address 0x0000005c09b0 is a wild pointer.
SUMMARY: AddressSanitizer: bad-malloc_usable_size C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\asan\asan_stack.cpp:77 in __sanitizer::BufferedStackTrace::UnwindImpl(unsigned __int64, unsigned __int64, void *, bool, unsigned int)
==5600==ABORTING
MS: 5 ChangeBinInt-ChangeBit-ShuffleBytes-ChangeByte-CopyPart-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
0x5c,0x5c,
\\\\
artifact_prefix='./'; Test unit written to ./crash-9e94758983980504af303ef297fd2bf9d9cea063
Base64: XFw=

C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ .\shell32_pwn_targeted.exe
INFO: Seed: 4034090806
INFO: Loaded 1 modules   (2 inline 8-bit counters): 2 [00007FF6695D9088, 00007FF6695D908A),
INFO: Loaded 1 PC tables (2 PCs): 2 [00007FF669584718,00007FF669584738),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED cov: 2 ft: 2 corp: 1/1b exec/s: 0 rss: 79Mb
#4096   pulse  cov: 2 ft: 2 corp: 1/1b lim: 43 exec/s: 2048 rss: 81Mb
#8192   pulse  cov: 2 ft: 2 corp: 1/1b lim: 80 exec/s: 2730 rss: 81Mb
=================================================================
==2008==ERROR: AddressSanitizer: attempting to call malloc_usable_size() for pointer which is not owned: 0x000000461570
    #0 0x7ff669451af4 in __sanitizer::BufferedStackTrace::UnwindImpl(unsigned __int64, unsigned __int64, void *, bool, unsigned int) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\asan\asan_stack.cpp:77
    #1 0x7ff66946d166 in __asan::asan_malloc_usable_size(void const *, unsigned __int64, unsigned __int64) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\asan\asan_allocator.cpp:986
    #2 0x7ff8776536e8  (C:\Windows\System32\ucrtbase.dll+0x1800136e8)
    #3 0x7ff877653603  (C:\Windows\System32\ucrtbase.dll+0x180013603)
    #4 0x7ff87765349a  (C:\Windows\System32\ucrtbase.dll+0x18001349a)
    #5 0x7ff87765344f  (C:\Windows\System32\ucrtbase.dll+0x18001344f)
    #6 0x7ff87765ab60  (C:\Windows\System32\ucrtbase.dll+0x18001ab60)
    #7 0x7ff878d663b1  (C:\Windows\System32\SHELL32.dll+0x1801063b1)
    #8 0x7ff878d663d4  (C:\Windows\System32\SHELL32.dll+0x1801063d4)
    #9 0x7ff878cf41f6  (C:\Windows\System32\SHELL32.dll+0x1800941f6)
    #10 0x7ff878cc54a9  (C:\Windows\System32\SHELL32.dll+0x1800654a9)
    #11 0x7ff878d711a4  (C:\Windows\System32\SHELL32.dll+0x1801111a4)
    #12 0x7ff878d711fb  (C:\Windows\System32\SHELL32.dll+0x1801111fb)
    #13 0x7ff878cb309c  (C:\Windows\System32\SHELL32.dll+0x18005309c)
    #14 0x7ff87782370f  (C:\Windows\System32\windows.storage.dll+0x1800e370f)
    #15 0x7ff87783e1a4  (C:\Windows\System32\windows.storage.dll+0x1800fe1a4)
    #16 0x7ff87783daf4  (C:\Windows\System32\windows.storage.dll+0x1800fdaf4)
    #17 0x7ff877840b5e  (C:\Windows\System32\windows.storage.dll+0x180100b5e)
    #18 0x7ff877846a8b  (C:\Windows\System32\windows.storage.dll+0x180106a8b)
    #19 0x7ff878cbb8bb  (C:\Windows\System32\SHELL32.dll+0x18005b8bb)
    #20 0x7ff878ead731  (C:\Windows\System32\SHELL32.dll+0x18024d731)
    #21 0x7ff878ead69e  (C:\Windows\System32\SHELL32.dll+0x18024d69e)
    #22 0x7ff669431149 in fuzzMeDrZaus(unsigned char const *, unsigned __int64) C:\Users\Garrett McParrot\Desktop\0day\shell32an\shell32_pwn_targeted.cpp:17
    #23 0x7ff66943125a in LLVMFuzzerTestOneInput C:\Users\Garrett McParrot\Desktop\0day\shell32an\shell32_pwn_targeted.cpp:26
    #24 0x7ff669499b1a in fuzzer::Fuzzer::ExecuteCallback(unsigned char const *, unsigned __int64) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:559
    #25 0x7ff669498f96 in fuzzer::Fuzzer::RunOne(unsigned char const *, unsigned __int64, bool, struct fuzzer::InputInfo *, bool *) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:471
    #26 0x7ff66949b221 in fuzzer::Fuzzer::MutateAndTestOne(void) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:702
    #27 0x7ff66949be25 in fuzzer::Fuzzer::Loop(class std::vector<struct fuzzer::SizedFile, class fuzzer::fuzzer_allocator<struct fuzzer::SizedFile>> &) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:838
    #28 0x7ff6694b24f0 in fuzzer::FuzzerDriver(int *, char ***, int (__cdecl *)(unsigned char const *, unsigned __int64)) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerDriver.cpp:847
    #29 0x7ff669474222 in main C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerMain.cpp:20
    #30 0x7ff6694b9e9f in __scrt_common_main_seh d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #31 0x7ff879377c23  (C:\Windows\System32\KERNEL32.DLL+0x180017c23)
    #32 0x7ff87a70d4d0  (C:\Windows\SYSTEM32\ntdll.dll+0x18006d4d0)

Address 0x000000461570 is a wild pointer.
SUMMARY: AddressSanitizer: bad-malloc_usable_size C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\asan\asan_stack.cpp:77 in __sanitizer::BufferedStackTrace::UnwindImpl(unsigned __int64, unsigned __int64, void *, bool, unsigned int)
==2008==ABORTING
MS: 1 InsertRepeatedBytes-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0x5c,0xa,
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\x0a
artifact_prefix='./'; Test unit written to ./crash-2b6d0b6bf096a9b464f85a44b0127d539d758db4
Base64: XFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcCg==

OK so this has found a couple of things. One of these crashes shows that a folder names “\” will crash SHGetFolderName, meaning that explorer.exe “\” for a folder that exists named that should crash. Let’s come back to that, because that’s pretty confusing, let’s try to find some other bugs first. I go ahead and kick off libFuzzer, this time disabled ASAN so that I can get more runs and more crashes without it constantly stopping:

C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ .\shell32_pwn_targeted.exe
WARNING: Failed to find function "__sanitizer_acquire_crash_state".
WARNING: Failed to find function "__sanitizer_print_stack_trace".
WARNING: Failed to find function "__sanitizer_set_death_callback".
INFO: Seed: 183197610
INFO: Loaded 1 modules   (2 inline 8-bit counters): 2 [00007FF769800008, 00007FF76980000A),
INFO: Loaded 1 PC tables (2 PCs): 2 [00007FF7697C0660,00007FF7697C0680),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED cov: 2 ft: 2 corp: 1/1b exec/s: 0 rss: 37Mb
#8192   pulse  cov: 2 ft: 2 corp: 1/1b lim: 80 exec/s: 2730 rss: 41Mb
#16384  pulse  cov: 2 ft: 2 corp: 1/1b lim: 163 exec/s: 2340 rss: 42Mb
#32768  pulse  cov: 2 ft: 2 corp: 1/1b lim: 325 exec/s: 2340 rss: 44Mb
#65536  pulse  cov: 2 ft: 2 corp: 1/1b lim: 652 exec/s: 2340 rss: 47Mb
#131072 pulse  cov: 2 ft: 2 corp: 1/1b lim: 1300 exec/s: 2299 rss: 53Mb

Now how about we make this program take in an input file and actually use a corpus this time? Let’s do it. It actually looks like all I really have to do is provide it with a corpus of some examples of the data we want. That means files with simple strings to directories I think. Let’s try it out:

Ha wait. OK as I was generating that corpus and libfuzzer was running, it looks like it crashed:

C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ .\shell32_pwn_targeted.exe
WARNING: Failed to find function "__sanitizer_acquire_crash_state".
WARNING: Failed to find function "__sanitizer_print_stack_trace".
WARNING: Failed to find function "__sanitizer_set_death_callback".
INFO: Seed: 183197610
INFO: Loaded 1 modules   (2 inline 8-bit counters): 2 [00007FF769800008, 00007FF76980000A),
INFO: Loaded 1 PC tables (2 PCs): 2 [00007FF7697C0660,00007FF7697C0680),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED cov: 2 ft: 2 corp: 1/1b exec/s: 0 rss: 37Mb
#8192   pulse  cov: 2 ft: 2 corp: 1/1b lim: 80 exec/s: 2730 rss: 41Mb
#16384  pulse  cov: 2 ft: 2 corp: 1/1b lim: 163 exec/s: 2340 rss: 42Mb
#32768  pulse  cov: 2 ft: 2 corp: 1/1b lim: 325 exec/s: 2340 rss: 44Mb
#65536  pulse  cov: 2 ft: 2 corp: 1/1b lim: 652 exec/s: 2340 rss: 47Mb
#131072 pulse  cov: 2 ft: 2 corp: 1/1b lim: 1300 exec/s: 2299 rss: 53Mb
#262144 pulse  cov: 2 ft: 2 corp: 1/1b lim: 2611 exec/s: 2240 rss: 66Mb
#524288 pulse  cov: 2 ft: 2 corp: 1/1b lim: 4096 exec/s: 2202 rss: 91Mb
==17048== ERROR: libFuzzer: deadly signal
NOTE: libFuzzer has rudimentary signal handlers.
      Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal
MS: 3 ChangeBit-ChangeByte-InsertRepeatedBytes-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
0xd1,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,
\xd14444444444444444444444
artifact_prefix='./'; Test unit written to ./crash-627c14dd9280cdc3304b4e6f0a921ae90091aef0
Base64: 0TQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ=

This is good! A “deadly signal” crashed and it wrote the crash to that file. Let’s see what’s in it. OK so this is fairly interesting, these are the contents from a hex editor:

Ñ4444444444444444444444 (that first char is 0xD1 in hex)

Now I tried creating a folder with that name and opening it in explorer. This should in theory run SHGetFolderLocation on this and crash, however it doesn’t, it just opens up the folder as if it were totally valid. The only thing it could really be is that this causes ILCreateFromPath to crash. Let’s add some exception handling first of all:

#include <shlobj_core.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <iostream>
#include <objbase.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

//clang-cl.exe /Zi /GX -fsanitize=fuzzer,address -fsanitize-recover=address shell32_pwn.cpp ole32.lib shell32.lib shlwapi.lib

int fuzzMeDrZaus(const uint8_t *Data, size_t size)
{
    PIDLIST_ABSOLUTE pidlAbsolute;
    try{
        pidlAbsolute = ILCreateFromPath((PCTSTR) Data);
    }
    catch(...){
        return 0;
    }
    SHGetFolderLocation(0, 5, 0, 0, &pidlAbsolute);

    return 0;
};


extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {

  fuzzMeDrZaus(Data, Size);
  return 0;
}

and then set it to run. It’s looking good, lots of executions per second (in the thousands and going up). Now I’m trying to figure out how this corpus deal works with libFuzzer… in AFL you need to have your program take in an argument, so it would be something like (1) give filename arg (2) load file contents as string (3) process it and test the function. With libFuzzer I don’t see any mention to making your program take in an argument. It looks to me like perhaps Data is just replaced with mutated file content. So let’s go with that for now while we research and make sure that’s the case.

C:\Users\Garrett McParrot\Desktop\cmder
λ cat co
CORPUS\  config\
C:\Users\Garrett McParrot\Desktop\cmder
λ cat co
CORPUS\  config\
C:\Users\Garrett McParrot\Desktop\cmder
λ cat CORPUS\*
cat: 'CORPUS*': No such file or directory

C:\Users\Garrett McParrot\Desktop\cmder
λ cat CORPUS\
1.txt  2.txt
C:\Users\Garrett McParrot\Desktop\cmder
λ cat CORPUS\1.txt
C:\symbols

My corpus is 1.txt and 2.txt, and they both just have some normal looking paths that start with C:. All good there, let’s run our shit:

λ .\shell32_pwn_targeted.exe -max_len=5000000000 .\CORPUS\
WARNING: Failed to find function "__sanitizer_acquire_crash_state".
WARNING: Failed to find function "__sanitizer_print_stack_trace".
WARNING: Failed to find function "__sanitizer_set_death_callback".
INFO: Seed: 3421504413
INFO: Loaded 1 modules   (2 inline 8-bit counters): 2 [00007FF743AA0008, 00007FF743AA000A),
INFO: Loaded 1 PC tables (2 PCs): 2 [00007FF743A60660,00007FF743A60680),
INFO:        8 files found in .\CORPUS\
INFO: seed corpus: files: 8 min: 1b max: 41b total: 82b rss: 37Mb
#9      INITED cov: 2 ft: 2 corp: 1/1b exec/s: 0 rss: 38Mb
#8192   pulse  cov: 2 ft: 2 corp: 1/1b lim: 80 exec/s: 4096 rss: 41Mb
#16384  pulse  cov: 2 ft: 2 corp: 1/1b lim: 163 exec/s: 4096 rss: 42Mb
#32768  pulse  cov: 2 ft: 2 corp: 1/1b lim: 325 exec/s: 4096 rss: 44Mb

Everything looks to be running smoothly, you’ll note that it says there’s 8 files found in corpus/ when there’s only 2, but uh… whatever? Let’s see where this takes us. We seem to be targeting the function we want with a small corpus. Now let’s try something else while that’s going. Instead of that IL function, let’s explicitly define our PIDLIST_ABSOLUTE explicitly, that way there is no error in other functions. Here we go, let’s try this out:

#include <shlobj_core.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <iostream>
#include <objbase.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int fuzzMeDrZaus(const uint8_t *Data, size_t size)
{

    LPITEMIDLIST pidlSystem;
    pidlSystem->mkid.cb = (USHORT) size;
    std::memcpy(pidlSystem->mkid.abID, (const void *) Data, size);   
    SHGetFolderLocation(0, 5, 0, 0, pidlSystem);

    return 0;
};


extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {

  fuzzMeDrZaus(Data, Size);
  return 0;
}

And let’s give it our corpus since a PIDLIST_ABSOLUTE is supposed to start with a root folder (e.g. C:\ as our corpus starts with). First we compile…


C:\Users\Garrett McParrot\Desktop\0day\shell32an
λ .\shell32_pwn_absexp.exe
INFO: Seed: 1173217470
INFO: Loaded 1 modules   (2 inline 8-bit counters): 2 [00007FF62C2B9088, 00007FF62C2B908A),
INFO: Loaded 1 PC tables (2 PCs): 2 [00007FF62C264710,00007FF62C264730),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED cov: 2 ft: 2 corp: 1/1b exec/s: 0 rss: 78Mb
=================================================================
==13756==ERROR: AddressSanitizer: access-violation on unknown address 0x000000000024 (pc 0x7ff87a6e024f bp 0x000000000000 sp 0x00000014eff0 T0)
==13756==The signal is caused by a READ memory access.
==13756==Hint: address points to the zero page.
    #0 0x7ff87a6e024e  (C:\Windows\SYSTEM32\ntdll.dll+0x18004024e)
    #1 0x7ff87782716f  (C:\Windows\System32\windows.storage.dll+0x1800e716f)
    #2 0x7ff877826cf6  (C:\Windows\System32\windows.storage.dll+0x1800e6cf6)
    #3 0x7ff877826c09  (C:\Windows\System32\windows.storage.dll+0x1800e6c09)
    #4 0x7ff8778cc0af  (C:\Windows\System32\windows.storage.dll+0x18018c0af)
    #5 0x7ff62c111293 in fuzzMeDrZaus(unsigned char const *, unsigned __int64) C:\Users\Garrett McParrot\Desktop\0day\shell32an\shell32_pwn_absexp.cpp:17
    #6 0x7ff62c11133a in LLVMFuzzerTestOneInput C:\Users\Garrett McParrot\Desktop\0day\shell32an\shell32_pwn_absexp.cpp:25
    #7 0x7ff62c179bfa in fuzzer::Fuzzer::ExecuteCallback(unsigned char const *, unsigned __int64) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:559
    #8 0x7ff62c179076 in fuzzer::Fuzzer::RunOne(unsigned char const *, unsigned __int64, bool, struct fuzzer::InputInfo *, bool *) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:471
    #9 0x7ff62c17b301 in fuzzer::Fuzzer::MutateAndTestOne(void) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:702
    #10 0x7ff62c17bf05 in fuzzer::Fuzzer::Loop(class std::vector<struct fuzzer::SizedFile, class fuzzer::fuzzer_allocator<struct fuzzer::SizedFile>> &) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerLoop.cpp:838
    #11 0x7ff62c1925d0 in fuzzer::FuzzerDriver(int *, char ***, int (__cdecl *)(unsigned char const *, unsigned __int64)) C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerDriver.cpp:847
    #12 0x7ff62c154302 in main C:\src\llvm_package_1100-final\llvm-project\compiler-rt\lib\fuzzer\FuzzerMain.cpp:20
    #13 0x7ff62c199f7f in __scrt_common_main_seh d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
    #14 0x7ff879377c23  (C:\Windows\System32\KERNEL32.DLL+0x180017c23)
    #15 0x7ff87a70d4d0  (C:\Windows\SYSTEM32\ntdll.dll+0x18006d4d0)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: access-violation (C:\Windows\SYSTEM32\ntdll.dll+0x18004024e)
==13756==ABORTING
MS: 5 InsertByte-EraseBytes-InsertRepeatedBytes-ChangeBit-CrossOver-; base unit: adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
0xa,0xa,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa,0xa,0x1f,0xa,0xa,0x1f,0xa,0x1f,0x1f,0xa,0x1f,0x1f,0x1f,0x1f,0x1f,0xa,0xa,0x1f,0x1f,0x1f,0xa,0x1f,0x1f,0xa,0xa,0x1f,0x1f,0x1f,0x1f,0x1f,0xa,0x1f,0xa,0x1f,0x1f,0xa,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0xa,0xa,0xa,0x1f,0xa,0x1f,0x1f,0xa,0xa,0x1f,0xa,0xa,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
\x0a\x0a\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x0a\x1f\x0a\x0a\x1f\x0a\x1f\x1f\x0a\x1f\x1f\x1f\x1f\x1f\x0a\x0a\x1f\x1f\x1f\x0a\x1f\x1f\x0a\x0a\x1f\x1f\x1f\x1f\x1f\x0a\x1f\x0a\x1f\x1f\x0a\x1f\x1f\x1f\x1f\x1f\x1f\x0a\x0a\x0a\x1f\x0a\x1f\x1f\x0a\x0a\x1f\x0a\x0a\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00
artifact_prefix='./'; Test unit written to ./crash-4c20b2a9796fda56ade5122764b7f4e632a1ba54
Base64: Cgr//////////////wAAAAAACgAAAAAAAAAAAAoKHwoKHwofHwofHx8fHwoKHx8fCh8fCgofHx8fHwofCh8fCh8fHx8fHwoKCh8KHx8KCh8KCgAACgAAAAAAAAA=

Annoyingly this keeps crashing immediately with a null page deref. I don’t want a million crashes again with that infinite loop script, so let’s see what we can do.