sif modules are yaml files that define security checks. they're similar to nuclei templates but designed specifically for sif.
- built-in:
modules/directory in the sif installation - user-defined:
~/.config/sif/modules/(linux/macos) or%LOCALAPPDATA%\sif\modules\(windows)
user modules can override built-in modules with the same id.
id: unique-module-id
info:
name: human readable name
author: your-name
severity: low|medium|high|critical|info
description: what this module checks for
tags: [tag1, tag2, tag3]
type: http
http:
method: GET
paths:
- "{{BaseURL}}/path"
matchers:
- type: status
status:
- 200unique identifier for the module. use lowercase with hyphens.
id: sqli-error-basedmetadata about the module.
info:
name: SQL Injection Detection
author: sif
severity: high
description: detects sql injection via error messages
tags: [sqli, injection, owasp-top10]severity levels:
info- informational findinglow- minor issuemedium- moderate security concernhigh- serious vulnerabilitycritical- critical security flaw
module type. http (request and match), tcp (raw connection probe) and
fingerprint (weighted technology detection) are supported.
type: httphttp request configuration.
http method to use.
http:
method: GETsupported: GET, POST, PUT, DELETE, HEAD, OPTIONS
urls to check. use {{BaseURL}} as placeholder for the target.
http:
paths:
- "{{BaseURL}}/.git/HEAD"
- "{{BaseURL}}/.git/config"
- "{{BaseURL}}/admin"values to inject into paths. use {{payload}} as placeholder.
http:
paths:
- "{{BaseURL}}/?id={{payload}}"
payloads:
- "'"
- "1' OR '1'='1"
- "1; DROP TABLE--"each payload creates a separate request for each path.
how paths and payloads combine into requests.
http:
attack: pitchforkclusterbomb(default) - every path is tried with every payloadpitchfork- path and payload are paired by index, stopping at the shorter list
a local file whose non-empty lines fuzz the {{word}} placeholder, one request
per word. paths without {{word}} are still requested as-is.
http:
wordlist: /usr/share/wordlists/dirs.txt
paths:
- "{{BaseURL}}/{{word}}"custom headers to send.
http:
headers:
User-Agent: "Mozilla/5.0"
X-Custom-Header: "value"request body for POST/PUT requests.
http:
method: POST
body: '{"username": "admin", "password": "{{payload}}"}'concurrent requests (default: 10).
http:
threads: 5raw tcp configuration. connects to a port, optionally sends a payload, and runs matchers and extractors against the response banner.
the tcp port to connect to (required, 1-65535). the port selects the service, so the target's own scheme and port are ignored.
tcp:
port: 6379an optional payload sent after connecting. sif decodes C-style escapes in the
value, so \r, \n, \t, \\ and \xHH reach the wire as the raw bytes no
matter how the yaml scalar is quoted; an unrecognized escape is left verbatim. a
server that only banners (ssh, smtp) needs no data at all.
tcp:
port: 6379
data: "INFO\r\n"tcp runs word, regex and size matchers (no status/favicon, those are
http only) against the banner string, and regex extractors pull values out of
it. there is no part selector: the banner is the only stream.
tcp:
port: 6379
data: "INFO\r\n"
matchers:
- type: word
words:
- "redis_version:"
extractors:
- type: regex
name: redis_version
regex:
- "redis_version:([0-9.]+)"
group: 1see modules/recon/redis-unauth-exposure.yaml for the full module.
matchers determine if a response indicates a finding.
match http status codes.
matchers:
- type: status
status:
- 200
- 301
- 302match words in response.
matchers:
- type: word
part: body
words:
- "admin"
- "login"
condition: orparts:
body- response bodyheader- response headers
conditions:
or- match any word (default)and- match all words
match regex patterns.
matchers:
- type: regex
part: body
regex:
- "SQL syntax.*MySQL"
- "ORA-[0-9]+"
- "PostgreSQL.*ERROR"
condition: ormatch the response body length in bytes (measured after the 5 MB response cap, so larger sizes never match).
matchers:
- type: size
size:
- 0
- 1337match the shodan-style mmh3 hash of the response body. point the module at a favicon and list the hashes of the tech you want to fingerprint.
http:
paths:
- "{{BaseURL}}/favicon.ico"
matchers:
- type: status
status:
- 200
- type: favicon
hash:
- -235701012 # jenkins
- 1278322581 # grafanathe hash is shodan's http.favicon.hash value. paste it signed or unsigned;
both 32-bit forms are accepted, so values from shodan or any favicon-hash tool
drop in without conversion. pair it with a status: 200 matcher so an error
page served for /favicon.ico is not hashed. a finding fires when the body
hashes to any listed value.
multiple matchers are combined with AND logic by default.
matchers:
- type: status
status:
- 200
- type: word
part: body
words:
- "ref: refs/"
condition: orthis matches responses with status 200 AND containing "ref: refs/".
set matchers-condition: or to fire when any matcher hits instead of all; it
applies to http and tcp modules alike.
http:
matchers-condition: or
matchers:
- type: status
status:
- 401
- type: status
status:
- 403this matches a 401 OR a 403 response. matchers-condition accepts and (the
default) or or; any other value fails at load.
extractors pull data from responses.
extractors:
- type: regex
name: version
part: body
regex:
- "version[\"']?\\s*[:=]\\s*[\"']?([0-9.]+)"
group: 1group: capture group to extract (0 = full match, 1+ = groups)
record every response header as a key-value pair, namespaced by name.
extractors:
- type: kv
name: headers
part: headerextract values from a json body by gjson path (github.com/tidwall/gjson); the first path that exists is stored under name.
extractors:
- type: json
name: version
part: body
json:
- "version"
- "data.version"a fingerprint module identifies a technology by weighted signatures rather
than a pass/fail match. each signature contributes its weight when it appears
in the body (or, with header: true, in a response header name or value). the
matched fraction of the total weight is the confidence; the module fires a
single finding, carrying that confidence, once it reaches the threshold.
this is the same scoring the built-in framework detectors use, in the module format, so a custom technology fingerprint lives alongside your other modules.
id: acme-server
info:
name: ACME Server
author: you
severity: info
tags: [tech, fingerprint]
type: fingerprint
fingerprint:
path: / # request path, defaults to /
confidence: 0.5 # minimum score to fire, defaults to 0.5
signatures:
- pattern: "acme" # matched against header name/value
weight: 0.6
header: true
- pattern: "powered by acme"
weight: 0.4 # body match; omit weight to default to 1
version: # optional: pull a version out of the body
regex: "acme/([0-9.]+)"
group: 1a response with both signatures scores 1.0; the header alone scores 0.6 and
still clears the 0.5 threshold, while the body alone (0.4) does not.
id: git-exposed
info:
name: exposed git repository
author: sif
severity: high
description: detects exposed .git directories
tags: [git, exposure, source-code]
type: http
http:
method: GET
paths:
- "{{BaseURL}}/.git/HEAD"
- "{{BaseURL}}/.git/config"
matchers:
- type: word
part: body
words:
- "ref: refs/"
- "[core]"
condition: or
- type: status
status:
- 200
extractors:
- type: regex
name: branch
part: body
regex:
- "ref: refs/heads/(.+)"
group: 1id: sqli-error-based
info:
name: sql injection (error-based)
author: sif
severity: high
description: detects sql injection via database errors
tags: [sqli, injection, database]
type: http
http:
method: GET
paths:
- "{{BaseURL}}/?id={{payload}}"
- "{{BaseURL}}/search?q={{payload}}"
payloads:
- "'"
- "1' OR '1'='1"
- "1; SELECT * FROM--"
threads: 10
matchers:
- type: regex
part: body
regex:
- "SQL syntax.*MySQL"
- "ORA-[0-9]+"
- "PostgreSQL.*ERROR"
- "Microsoft SQL Server"
condition: orid: security-headers
info:
name: security headers analysis
author: sif
severity: info
description: checks for missing security headers
tags: [headers, security, info]
type: http
http:
method: GET
paths:
- "{{BaseURL}}/"
matchers:
- type: status
status:
- 200
extractors:
- type: kv
name: headers
part: header-
use specific paths - don't just check
/, be specific about what you're looking for -
combine matchers - use status + content matchers together to reduce false positives
-
limit payloads - too many payloads slow down scans, pick the most effective ones
-
tag properly - use consistent tags so modules can be filtered with
-mt -
test locally - run your module against a test target before sharing
# list all modules
./sif -lm
# run specific module
./sif -u https://example.com -m git-exposed
# run multiple modules
./sif -u https://example.com -m git-exposed,sqli-error-based
# run by tag
./sif -u https://example.com -mt owasp-top10
# run all modules
./sif -u https://example.com -am