AFLplusplus / LibAFL

Advanced Fuzzing Library - Slot your Fuzzer together in Rust! Scales across cores and machines. For Windows, Android, MacOS, Linux, no_std, ...
Other
2.03k stars 319 forks source link

Windows Missing exception code. #360

Closed tokatoka closed 2 years ago

tokatoka commented 3 years ago

IMPORTANT

  1. You have verified that the issue to be present in the current main branch yes

Describe the bug Reported by @expend20 Some exception are not covered here https://github.com/AFLplusplus/LibAFL/blob/3d436b75196f3b065f6e40c65df94f36bfabf8f7/libafl/src/bolts/os/windows_exceptions.rs#L88 so they crash at this unwrap() https://github.com/AFLplusplus/LibAFL/blob/3d436b75196f3b065f6e40c65df94f36bfabf8f7/libafl/src/bolts/os/windows_exceptions.rs#L324

One example is error code "0x4001000a" (DBG_PRINTEXCEPTION_WIDE_C)

tokatoka commented 3 years ago

but I find this err code is not even listed here https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 how can we just list all possible exception code then....?

tokatoka commented 3 years ago

I think we can add a ExceptionCode::Others to transfer all the miscellaneous errors to a crash handler what do you think @andreafioraldi

expend20 commented 3 years ago

afaik, not all exceptions listed in msdn and user can raise his own exception with RaiseException. I think the best strategy is to handle most common ones, and leave room (i.e. not to panic) for unknown ones

expend20 commented 3 years ago

Just fyi, not all the exception are meaningful for fuzzer on windows. For example C++ exceptions for x64, which can happen a lot during fuzzing (and it's ok), is built on top this mechanism too. UPD: The are first and second chance exceptions as well. For example one strategy for fuzzer is to consider only second chance exceptions as a stop, because fuzzing targets can expect and handle first chance ones. But theoretically first chance could be also meaningful from security perspective.

expend20 commented 3 years ago

Tried to use LibAFL on a baby target which simply exhaust the stack:

extern "C" __declspec(dllexport) size_t WINAPIV
    FuzzMeStackOverflow(const char *data, unsigned int len)
{

    char sdata[1024];
    memset(sdata, 0, sizeof(sdata));
    memcpy(sdata, data, sizeof(sdata) < len ? sizeof(sdata) : len);

    if (len >= 8 
            && *(uint8_t *)(data + 0) == '1'
            && *(uint8_t *)(data + 1) == '3'
            && *(uint8_t *)(data + 2) == '3'
            && *(uint8_t *)(data + 3) == '7'
            ) {
        return FuzzMeStackOverflow(sdata, len);
    }
    return 0;
}

I got these logs:

[Testcase    #1]  (GLOBAL) clients: 2, corpus: 6, objectives: 0, executions: 26500, exec/sec: 8833
                  (CLIENT) corpus: 6, objectives: 0, executions: 26500, exec/sec: 8833, edges: 22/65536 (0%)

thread 'main' has overflowed its stack
The application panicked (crashed).
Message:  Fuzzer-respawner: Storing state in crashed fuzzer instance did not work, no point to spawn the next client! (Child exited with: exit code: 0xc0000354)
Location: C:\git\LibAFL\libafl\src\events\llmp.rs:864

Backtrace omitted.

Run with RUST_BACKTRACE=1 environment variable to display it.
Run with RUST_BACKTRACE=full to include source snippets.

and session stopped. In my understanding of it, LibAFL should handle this as a crash case and continue running.

domenukk commented 3 years ago

That should definitely work, yes. Seems like there is bugs to be fixed on Windows still. :) Thanks for reporting!

domenukk commented 3 years ago

FWIW the overflow gets printed in a rust-internal exception handler, here: https://github.com/rust-lang/rust/blob/1ddd4e6d7ed446934abd428a08e18535faef5e03/library/std/src/sys/windows/stack_overflow.rs#L26

/edit: They use AddVectoredExceptionHandler to add the handler https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-addvectoredexceptionhandler

s1341 commented 3 years ago

Babyfuzzer doesn't run in a secondary process, does it?

domenukk commented 3 years ago

According to the logs, it's a baby (as in, simple) harness inside a Restarting Mgr

tokatoka commented 3 years ago

yes if rust runtime panicked before reporting a signal then we can't catch it.

domenukk commented 3 years ago

We definitely can, if we call AddVectoredExceptionHandler(1,.., we will be called first

expend20 commented 3 years ago

Not quite sure if Restarting Mgr == secondary process, but the case above with FuzzMeStackOverflow() is based on frida_libpng, using dlls for harnessing, and it should be in the secondary process anyways. But it looks like the rust runtime is present there too, and it interferes with exception handling of fuzzer itself.

domenukk commented 3 years ago

With the InProcessExecutor the fuzzer gets injected into the target, so there is zero overhead for IPC calls, and good scaling. The only culprit is that we have to handle all exceptions from inside the target (vs old-skool AFL where the fuzzer lives around the target)

tokatoka commented 3 years ago

ok. then one solution would be to use AddVectoredExceptionHandler to override stackoverflow handler in setup_exception_handler

expend20 commented 3 years ago

Could be related to #372