Poirot is still a work in progress !
To use Poirot, you will need the OCaml package manager, opam
. You can also follow these steps:
sh <(curl -sL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)
eval `opam env`
opam switch create 4.08.1
eval `opam env`
It is recommended that you add eval `opam env`
in the configuration file of your shell (most likely ~/.bashrc
or ~/.profile
)
To install Poirot, run the following steps. opam
will automatically install the dependencies of Poirot.
git clone https://github.com/PFGimenez/poirot.git
cd poirot
opam install .
eval `opam env`
You will need ANTLR4 to convert grammars from .g4 format and to use the prefix/suffix oracle generator. Make sure you have python 3 and Java JRE installed. Execute:
pip3 install --user -r antlr4-utils/requirements.txt
Run poirot -help
to get the list of parameters on how to use it. The workflow of injection searching in black-box systems is described in the following figure:
The following examples use the prefix/suffix oracle generator described in a later section.
Here is an example that uses the simple grammar msg_exec
. Run:
poirot -grammar bnf_grammars/toy/msg_exec.bnf -goal "Exe" -start "'value'" -oracle "oracles/prefix-suffix.py msg_exec axiom 'msg key = ' ' & key = value'"
It will generates the injection value ; exec cmd ; msg key = value
.
You can experiment with the more complex grammar parenthesis
as well. Run:
poirot -grammar bnf_grammars/toy/parenthesis.bnf -goal "'b'" -start "'a'" -oracle "oracles/prefix-suffix.py parenthesis axiom '([[([' '])]])'"
It will generate the injection a])]])b([[([a
.
Goal and start tokens can be either a terminal or a nonterminal symbol. Terminal symbols should be surrounded by single-quote: 'terminal'
.
Here is the list of the options of poirot
:
-avoid
List of characters to avoid (if for example some characters are filtered).-dict
Filename of the semantics dictionary (more information in the next section).-maxdepth
Set the max depth search (default: 10).-maxsteps
Set the max steps search (default: 1000).-oracle_timeout
Set the timeout to oracle calls (in seconds, -1 for no timeout).-oracle_interval
Set the minimal duration between two oracle calls (in seconds, -1 for no wait).-sgraph
Save the search graph in dot format.-nosave_h
Disable the heuristics save.-nosave_oracle
Disable the oracle calls save.-oneline_comment
The string that starts one-line comment. For example, use-oneline_comment "--"
for SQL grammars.-injg
Export the injection grammar in ANTLR4 format (you don't need to specify the .g4 extension).-lowercase
Convert all terminals to lowercase.-uppercase
Convert all terminals to uppercase.-verbose_lvl
Choose Poirot verbosity: debug, info, warning or error. "Info" by default.-v
Print the version of Poirot.
Using a semantics dictionary is a way to add semantics to Poirot. It is a simple text file that associate string to nonterminal symbols so Poirot can use them during fuzzing. An example:
<column_name>=login
<database_name>=db
<table_name>=users
If you know the query (i.e. the prefix and the suffix surrounding the injection point), you can directly use Poirot to get the grammar of the injection with the quotient
function (and possibly export it to an ANTLR4 grammar) along with an injection. White-box fuzzing is of course greatly faster than black-box fuzzing.
For example, you can run:
quotient_poirot -grammar bnf_grammars/toy/msg_exec.bnf -pf "msg key = " -sf " & key = value" -goal "Exe"
It will generate the injection value ; exec cmd ; msg key = value
.
Using Poirot in your project with dune
is easy: just add poirot
in the list of the dependencies. Poirot should be available to ocamlfind
as well (you can check with ocamlfind query poirot
).
The documentation is available online at https://pfgimenez.github.io/poirot/Poirot/index.html. To generate the documentation locally, make sure odoc
is installed (or install it with opam install odoc
). You can then generate the documentation with dune build @doc
. It will be stored in _build/default/_doc/_html/poirot/Poirot/index.html
.
Check src/poirot.ml
for an executable using the library.
Poirot uses a simple grammar format inspired from the BNF format.
You can find many grammars in ANTLR4 format in this repository: https://github.com/antlr/grammars-v4.
Go into the directory antlr4-utils
. If you have a grammar named test.g4
, you can simply write make test.bnf
to generate its BNF version. The Makefile will automatically download the ANTLR4 jar file if necessary.
If you have a grammar test.bnf
, run
bnf2antlr4 test.bnf
It will generate the file test.g4
containing the grammar in ANTLR4 format. Its axiom is named axiom
.
An oracle is a script that can send injection to the black-box system and, by examining this system, tell whether this injection is syntactically correct or not. Poirot gets the answer by checking its error code: 0 if the injection is syntactically correct, 180 otherwise. Make sure the executable permission is enabled (added with chmod +x
).
More precisely, Poirot will use this oracle by calling it with a single parameter, the injection. If you need more parameters for your oracle (e.g. an URL), you should give a partial application of the oracle to Poirot. For example, if your oracle script is oracle.sh param1 param2 param3 inj
, just tell Poirot your script is oracle.sh param1 param2 param3
.
The oracles are in the directory oracles
.
The workflow of creating an oracle with the prefix/suffix oracle generator is described in the following figure:
We provide an oracle generator that simulates a black-box, given a grammar, a prefix and a suffix. To use it, you must first create the ANTLR4 lexer and parser of its grammar. To do that, put the ANTLR4 grammar (such as example.g4
) into the antlr4-utils
directory and execute make exampleLexer.py
(change accordingly to the name of your grammar).
This oracle generator needs four parameters: the ANTLR4 grammar name (without the .g4
extension), the axiom, a prefix and a suffix. Beware, the grammar path is relative to the directory antlr4-utils
.