Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capture __fastfail exceptions on windows #395

Open
tokatoka opened this issue Nov 30, 2021 · 12 comments · Fixed by #804
Open

Capture __fastfail exceptions on windows #395

tokatoka opened this issue Nov 30, 2021 · 12 comments · Fixed by #804

Comments

@tokatoka
Copy link
Member

#258

When software call __fastfail, the exception can't be caught
https://github.com/googleprojectzero/winafl/blob/ea5f6b85572980bb2cf636910f622f36906940aa/winafl.c#L728
winafl wraps two functions in kernelbase.dll

for frida fuzzers, I think this can be done in a similar way as winafl,
but for sancov backend fuzzers, we would have to look at what libfuzzer is doing.

@tokatoka tokatoka added the good first issue Good for newcomers label Nov 30, 2021
@tokatoka tokatoka removed the good first issue Good for newcomers label Feb 16, 2022
@expend20
Copy link
Contributor

For the record, the problem indeed exist, here is the walkthrough

How to repro

#include <string.h>
extern "C"
{
    __declspec(dllexport)
    void fuzz_function(const char *src, size_t len)
    {
        char buf[512];
        if (len < 4)
            return;
        if (src[0] != 'B')
            return;
        if (src[1] != 'O')
            return;
        if (src[2] != 'O')
            return;
        if (src[3] != 'M')
            return;
        memset(buf, 0, sizeof(buf)*2);
        return;
    }
}

compile string: cl /Zi boom.cpp /link /DEBUG:FULL /DLL /OUT:boom.dll
fuzz run: frida_gdiplus.exe -H boom.dll -F fuzz_function

You'll see the debugger popped up, if you have postmortem one installed

debug

security_check_cookie -> __report_gsfailure

void __fastcall __noreturn _report_gsfailure(unsigned __int64 stack_cookie)
{
  void *retaddr; // [rsp+38h] [rbp+0h]
  unsigned __int64 stack_cookiea; // [rsp+40h] [rbp+8h] BYREF

  stack_cookiea = stack_cookie;
  if ( IsProcessorFeaturePresent(0x17u) )
    __fastfail(2u);
  capture_previous_context(&GS_ContextRecord);
  GS_ContextRecord.Rip = (unsigned __int64)retaddr;
  GS_ContextRecord.Rsp = (unsigned __int64)&stack_cookiea;
  GS_ExceptionRecord.ExceptionAddress = retaddr;
  GS_ContextRecord.Rcx = stack_cookiea;
  GS_ExceptionRecord.ExceptionCode = 0xC0000409;
  GS_ExceptionRecord.ExceptionFlags = 1;
  GS_ExceptionRecord.NumberParameters = 1;
  GS_ExceptionRecord.ExceptionInformation[0] = 2i64;
  j___raise_securityfailure(&GS_ExceptionPointers);
}
...
void __fastcall _raise_securityfailure(_EXCEPTION_POINTERS *const exception_pointers)
{
  HANDLE CurrentProcess; // rax

  SetUnhandledExceptionFilter(0i64);
  UnhandledExceptionFilter(exception_pointers);
  CurrentProcess = GetCurrentProcess();
  TerminateProcess(CurrentProcess, 0xC0000409);
}

__fastfail(2) is mov ecx,2 && int 29h

And IsProcessorFeaturePresent just checks KUSER_SHARED_DATA.ProcessorFeatures:

char __fastcall RtlIsProcessorFeaturePresent(unsigned int a1)
{
  if ( a1 >= 0x40 )
    return 0;
  else
    return *(_BYTE *)(a1 + 0x7FFE0274i64);
}

the solution

WinAFL's solution is partially right, to catch the exception we need to hook IsProcessorFeaturePresent and return 0 for PF_FASTFAIL_AVAILABLE. But then we need to just handle C0000409 exception, cause UnhandledExceptionFilter() will lead to VEH handler anyway (presumably, need to verify).

the implementation

So far stuck on hook part. @tokatoka have any link how to hook win api with Frida? I found so far only this and libafl_frida\src\asan\hook_funcs.rs which is somewhat hard to grasp.

@expend20
Copy link
Contributor

expend20 commented Sep 25, 2022

But then we need to just handle C0000409 exception, cause UnhandledExceptionFilter() will lead to VEH handler anyway (presumably, need to verify).

Confirmed, VEH handler is called if IsProcessorFeaturePresent(23) return false

@tokatoka
Copy link
Member Author

nice!

@expend20
Copy link
Contributor

expend20 commented Sep 27, 2022

Confirmed, VEH handler is called if IsProcessorFeaturePresent(23) return false

Actually I was wrong, it's not the case. I confused it with exit process code 😆 Anyway I'll try to use hook

@expend20
Copy link
Contributor

Somehow the PR works:

...
[Stats       #1]  (GLOBAL) run time: 0h-0m-0s, clients: 2, corpus: 4, objectives: 0, executions: 231, exec/sec: 0
                  (CLIENT) corpus: 4, objectives: 0, executions: 231, exec/sec: 0, edges: 17/65536 (0%)
[Testcase    #1]  (GLOBAL) run time: 0h-0m-0s, clients: 2, corpus: 5, objectives: 0, executions: 4544, exec/sec: 0
                  (CLIENT) corpus: 5, objectives: 0, executions: 4544, exec/sec: 0, edges: 17/65536 (0%)
[Stats       #1]  (GLOBAL) run time: 0h-0m-0s, clients: 2, corpus: 5, objectives: 0, executions: 4544, exec/sec: 0
                  (CLIENT) corpus: 5, objectives: 0, executions: 4544, exec/sec: 0, edges: 19/65536 (0%)
[Testcase    #1]  (GLOBAL) run time: 0h-0m-0s, clients: 2, corpus: 6, objectives: 0, executions: 7798, exec/sec: 0
                  (CLIENT) corpus: 6, objectives: 0, executions: 7798, exec/sec: 0, edges: 19/65536 (0%)
IsProcessorFeaturePresent(23) returning false
Calling handle_exception
Received STATUS_STACK_BUFFER_OVERRUN
Crashed with STATUS_STACK_BUFFER_OVERRUN
Child crashed!
Child crashed!
Waiting for broker...
Bye!
Spawning next client (id 1)
[Objective   #1]  (GLOBAL) run time: 0h-0m-2s, clients: 2, corpus: 6, objectives: 1, executions: 7798, exec/sec: 3427
                  (CLIENT) corpus: 6, objectives: 1, executions: 7798, exec/sec: 3427, edges: 19/65536 (0%)

@tokatoka tokatoka closed this as completed Oct 5, 2022
@tokatoka tokatoka reopened this Oct 6, 2022
@tokatoka
Copy link
Member Author

tokatoka commented Oct 6, 2022

This is not done yet...
For frida, it's ok. But for inprocess-fuzzing we can't use frida APIs.

@tokatoka
Copy link
Member Author

tokatoka commented Oct 6, 2022

maybe we can use something like this (although, it looks really old)
https://github.com/Jascha-N/minhook-rs

and then in the constructor for windows executor, hook IsProcessFeaturePresent

@domenukk
Copy link
Member

domenukk commented Oct 6, 2022

Requires nightly though, doesn't work on aarch, and is not maintained anymore/archived

@domenukk
Copy link
Member

domenukk commented Oct 6, 2022

There's a pr for newer (nightly) versions though

@tokatoka
Copy link
Member Author

tokatoka commented Oct 6, 2022

yeah I suppose we can do this hooking manually, shouldn't be difficult
(like, in linux, just a LD_PRELOAD, and it's good)
but I'm not familiar with windows internals.

here's a good blogpost about how to do this manually
http://kylehalladay.com/blog/2020/11/13/Hooking-By-Example.html

@expend20
Copy link
Contributor

somehow missed the latest conversation, @tokatoka what is inprocess-fuzzing in general, and how is that separated from Frida? I thought currently Frida is the only option for windows binary targets. If you have compiler instrumentation, perhaps you don't need this hook?

@domenukk
Copy link
Member

You can build inprocess-fuzzers with clang from source, and those will run into the same exception issues. We'll need a solution for these, too.
Also, there is a branch with tinyinst that isn't done, however.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants