0xricksanchez / AFL_Runner

Scaling best-practice AFLPlusPlus fuzzing campaigns made easy
https://crates.io/crates/afl_runner
Apache License 2.0
51 stars 7 forks source link

Allow to specify options for certain instances only #39

Open wizche opened 1 week ago

wizche commented 1 week ago

For example I would like to be able to specify AFL_USE_QASAN (or COMPCOV preload and level) but only for certain instances (using a count or a percentage)

0xricksanchez commented 1 week ago

One way I could think of would be introducing something like afl_flags_partial, similar to the already existing afl_flags.

The format could either be based on AFL environment variables like AFL_USE_QASAN=X:0.3, which could enable QASAN usage for like 30% of all instances. Alternatively, it could be purely based on AFL command-line flags in a similar manner like -F:0.3,-G:0.6 to enable the pseudo option F and G for 30% and 60% respectively.

Generally the idea seems valid but the logic needs to take Into account the parsing, options that interfere with each other and such. Might need a big rework on how the commands are being generated

wizche commented 1 week ago

I added the more complex scenario where you enable compcov where you need multiple env variables:

AFL_PRELOAD=/path/to/libcompcov.so
AFL_COMPCOV_LEVEL=1

in this case we need to group them with something like (AFL_PRELOAD=/libcompcov.so;AFL_COMPCOV_LEVEL=1):0.3 or alike. But again, you may preload different libraries so you need to merge the env variable in the end....

It may be easier to include the libcompcov.so everywhere (if used) with AFL_PRELOAD=/libcompcov.so and AFL_COMPCOV_LEVEL=0 so its disabled by default and than use the AFL_COMPCOV_LEVEL=1:0.1 to enable it on a subset.

Also CLI flags is difficult because u may need to pass an argument, e.g. -x /tmp/dictionary1. In this case is easier to group them with (-x /tmp/dictionary1):0.3,(-x /tmp/dictionary2):0.3. Maybe i'm just over-complicating it...

0xricksanchez commented 1 week ago

Good observation regarding the cases where you need multiple env vars and/or more complex parameters..

I couldnt come up with something that fits nicely for an environment variable yet but something like this looks decent for the toml based configuration…

[afl_flags_partial]
# Single parameters
AFL_USE_QASAN = 0.3

# Groups of parameters
[[afl_flags_partial.groups]]
probability = 0.2
AFL_PRELOAD = "/path/to/libcompcov.so"
AFL_COMPCOV_LEVEL = 1

[[afl_flags_partial.groups]]
probability = 0.4
-x = "/tmp/dictionary1"

[[afl_flags_partial.groups]]
probability = 0.4
-x = "/tmp/dictionary2"

Then again I think this feature requires some smart design as we probably need something like a map of parameters that definitely don’t work in combination with others. Also, if a parameter is defined in the partial group and it overlaps with a parameter that’s already defined in the “best configuration” one has to take precedence over the other..

wizche commented 1 week ago

The TOML solution looks very good. I wouldn.t even bother supporting it via env variable. What I also would do is completely remove the take over of the AFL_ env variables from the current shells and propagate them over the instances. I mean if you plan to run a campaign I would prefer to have them "fixed" in the toml config. I say this because It happen to me that I had to unset some of them because I forget I had them set in the current shell.

I would also suggest to change

[afl_flags_partial]
# Single parameters
AFL_USE_QASAN = 0.3

to become something like:

[afl_flags_partial]
probability = 0.3
AFL_USE_QASAN = 1

By always enforcing a probability and the envs it will simplify the parsing code and all entries will look the same in the config file. But its just a suggestion. Furthermore we could support probability and count (mutually exclusive).

0xricksanchez commented 1 week ago

Something like this could work. I would like to keep the option to have AFLRunner parse the AFL* flags, which could be useful for quick tests or similar where it’s quicker to set an ENV variable than to find and edit the specific configuration file..

0xricksanchez commented 5 days ago

I just thought about something. I updated some of the implementations in #52 [1] and #53 [2] for how arguments are added to the command, either independently or mutually exclusive.

If we changed our TOML format to something like this, we would have basically what is asked from this function signature(s)


[[afl_flags_partial.groups]]
probability = 0.4
cmd = "-x /tmp/dictionary2"

There are two issues:

  1. We still need to parse all *_partial groups to find those that are mutually exclusive, but once done we can again reuse the function mentioned earlier.
  2. partial environment variables need their own logic/function, as those need to be prepended on top of figuring out mutual exclusivity
wizche commented 5 days ago

I guess I need to rebase my feature branch... Do you have or plan to write the same function for env variable? In that case we need to merge certain variables like AFL_PRELOAD AFL_PRELOAD / LD_PRELOAD What happens for example in the case we specify a dictionary in the main config e.g. /tmp/dictionary1 and specify /tmp/dictionary2 for a subset? I guess the latter must superseed the main one for the specified amount/percentage. The way I will probably use this feature is in combination with afl_use_defaults and where I will specify (all) the options myself.

Btw If I'm not mistaken u implemented both the suggestions for CI fuzzing and the one in Using multiple cores. I noticed this because I started to see flags like AFL_KEEP_TIMEOUTS or AFL_EXPAND_HAVOC_NOW. Do we really need that? Or maybe should the user specify if its a CI-based run or a long-running campaign?

0xricksanchez commented 5 days ago

I guess I need to rebase my feature branch...

Sorry for that :smile:

Do you have or plan to write the same function for env variable? In that case we need to merge certain variables like AFL_PRELOAD AFL_PRELOAD / LD_PRELOAD

I'll look into refactoring the current code to accommodate this behavior

What happens for example in the case we specify a dictionary in the main config e.g. /tmp/dictionary1 and specify /tmp/dictionary2 for a subset?

That's up to us. This could be implemented either way. If a main token file is defined, ignore partial ones. Or apply partial ones first and apply token dictionary to those that were not getting a dictionary yet..

Btw If I'm not mistaken u implemented both the suggestions for CI fuzzing...

Good catch. I was experimenting with this. I'll push a branch that specifically removes the 2 more CI-centric flags. I'll keep the CMPLOG distribution, in case a CMPLOG binary is provided as that yielded good results.

PS: Fixed in #54