Expand description
§⚙️ Network-mode vs. Buffer-mode
SaM implements two network simulation modes, selected based on the target binary’s characteristics:
- Buffer-mode: For malware sample using standard OS networking primitives, SaM intercepts the network buffer directly at the relevant network API call, injecting synthetic response data without completing the actual network operation. This approach integrates naturally with coverage-guided fuzzers (e.g., WinAFL) and offers high throughput.
- Network-mode: For samples that implement custom networking stacks (e.g., Go binaries), SaM delegates to a network emulation layer (e.g., FakeNet-NG) that intercepts traffic at the network interface. While slower, this approach handles cases where buffer-level interposition is impractical.
The following schematic compares the call stacks for the two modes. The additional traversal through the kernel network stack reduces throughput, which is a disadvantage when fuzzing.
Both modes apply the same instrumentation elsewhere (e.g., for collecting coverage information and conducting fuzzing rollbacks) - the choice is driven by compatibility rather than analytical capability.
§When and Why is Network-mode needed
We conducted research experiments with FrostyGoop (see our paper for more details) which was written in the Go programming language. We found out that the net package in the Go standard library uses an async network stack when building for Windows (as shown here in the golang source code).
Thankfully, the instrumentation to hook the functions in the Windows async network stack is included in SaM, so observing outbound requests from network clients written in Go (such as the FrostyGoop malware) was no problem.
However, injecting responses into an async networking stack is much more complicated. After writing to the correct buffer, the client runtime must be notified that the buffer is populated, and several mechanisms exist for this async signalling. Writing implementations to cover all mechanisms is a lot of development work, and at this stage we have opted to allow the OS kernel to do this work for us.
As such, Network-mode is required for binaries that use Windows async network APIs, or any other complex socket management workflows that are not currently handled in SaM’s socket simulation (e.g. getting and setting socket options with getsockopt and setsockopt).
§Why Network-mode is only a partial solution, and a suggested refactor for a full solution
It is currently a requirement that SaM observes the outbound requests from the target binary, even when using Network-mode to supply responses at the network-level. This precludes working with other complex networking stacks (such as a binary that uses low-level ioctl commands in favour of a high-level network API).
This is not a necessary constraint, it is an implementation artefact that can be resolved with the following code refactor:
- Currently, when an outbound request is observed on a network API hook, the configured
Trajectoryfor the run is used to determine the planned response. This logic all resides inlibinject, and executes in the runtime of the target binary during the hook. - If running in Network-mode, the decided response (along with the request) is piped to a server as a
HalfDuplexExchange. This server is responsible for interposing on the network event at the network-level. - In this setup, the server behaves as follows: i) receive a request, ii) block on the pipe waiting for a
HalfDuplexExchange, iii) verify that the request in theHalfDuplexExchangematches the received request, iv) respond to the request with the response included in theHalfDuplexExchange. - Naturally, given that the server receives the request independently of the network API hook in
libinject, the hook, pipe andHalfDuplexExchangecan all be removed - as long as the server can run the logic (in it’s own runtime) to decide how to respond to each request. - As such, the refactor should achieve the following:
- in Buffer-mode, use the configured
Trajectoryto decide responses to requests in the runtime of the target binary (i.e. inlibinject) - this is currently implemented - in Network-mode, pass the configured
Trajectoryto the server when it is initialised, and add logic to the server to use theTrajectoryto decide responses to requests in the runtime of the server (i.e the logic probably goes innavigator/src/server.rs).
- in Buffer-mode, use the configured
§Network-mode does not support fuzzing in parallel
Running fuzzing sessions in parallel greatly increases fuzzing throughput, as discussed in the Configuring Fuzzing Sessions section of the Getting Started guide.
Setting the number of fuzzers to set up in parallel (with the --parallel flag) is not permitted when running in Network-mode (which is set using the --server-managed flag).
This is because parallel fuzzing currently only supports running multiple fuzzers on one machine, and Network-mode requires an OS instance per fuzzer (because it delgates the response injection to the OS).
§A possible solution requiring further development
WinAFL does support multi-system parallelization, discussed in the docs here. However this has not yet been implemented for SaM.
It is possible that multi-system parallelization might provide the option to fuzz in parallel with Network-mode, by running one fuzzer per machine across mutliple machines.