ugly-duckling is a very basic (and currently alpha-quality) vulnerability scanner built by the reasearch team at Detectify. It exists so that members of Detectify Crowdsource can submit proof-of-concept modules in a way that we can test and implement efficiently.
ugly-duckling is not in use internally at Detectify and was built specifically for this purpose.
Although we have shared one of our internal module formats with our Crowdsource members in the past, it is a complex format with many features that only make sense in an internal Detectify context. We wanted people to be able to develop, test, and run modules in a simple and easy to document format.
We also considered using a pre-existing scanner for this purpose - there are many great scanners our there to choose from after all - but not having control over the module format and available features might make it harder to maintain a tool that translates things to our internal formats.
We welcome pull requests that implement bug fixes and minor improvements - we tried to implement only the bare minimum amount of functionality so we probably missed things!
If you have novel modules you would like to submit we recommend you join Detectify Crowdsource to submit them so that you can be rewarded for any hits they may produce.
ugly-duckling is written in Go and has no external dependencies.
You can install it with go get
:
▶ go get github.com/detectify/ugly-duckling
Or clone the repository and build it manually:
▶ git clone https://github.com/detectify/ugly-duckling.git
▶ cd ugly-duckling
▶ go install
ugly-duckling reads URLs on stdin
, and takes a list of modules as its arguments (defaulting to ./modules/*.json
if none are provided).
A standard invocation to run a single module against a single URL might look like this:
▶ echo https://example.com/ | ugly-duckling modules/test3.json
Or to run against multiple URLs contained in urls.txt
:
▶ cat urls.txt | ugly-duckling modules/test.json
-c <int> / --concurrency <int>
- set the concurrency for HTTP requests (defaults to 1)-v / --verbose
- display debug type output (e.g. which modules have been loaded)
Here is an example module that demonstrates all functionality in ugly-duckling:
{
"request": {
"method": "POST",
"path": "/anything",
"body": "{\"magicWord\": \"please!\"}",
"headers": [
"Content-Type: application/json",
"Accept: application/json"
]
},
"response": {
"matchesRequired": 2,
"matches": [
{"type": "static", "pattern": "please!", "required": true},
{"type": "regex", "pattern": "magic\\w"},
{"type": "status", "code": 200},
{"type": "header", "name": "Content-Type", "pattern": "application/.*"}
],
"mustNotMatch": [
{"type": "regex", "pattern": "(server error|not found)"}
]
}
}
The request
and response
sections are both required. The minimum possible module has a path
in the
request
section, and at least one thing in the matches
list in the response
section:
{
"request": {
"path": "/anything"
},
"response": {
"matches": [
{"type": "static", "pattern": "data"}
]
}
}
The request section contains details about the HTTP request to be sent.
method
- HTTP method to use;GET
,POST
,HEAD
etcpath
- Path and query string to append to the input URLbody
- Data to be sent as the request bodyheaders
- An array of headers to send with the request
The response section contains details about how to check the response for a hit.
matchesRequired
- How many matches must be made for a module to be considered a 'hit'matches
- An array of objects describing the matches to be performedmustNotMatch
- An array of objects describing things which must not be matched
Match objects can have one of a few different types:
static
- an exact text match is performed using thepattern
parameterregex
- a regular expression match is performed using thepattern
parameter; the regex engine is the default Go engine.status
- the status code of the response is compared to the integer in thecode
parameterheader
- a regular expression match is performed against the header specied in thename
parameter using the pattern specified in thepattern
parameter
Any match object in the matches
array can have a required
parameter set to true
so that a match must be met for a module to be considered a hit.