1import socket 2import csv as csvmod 3import json as jjson 4from xml.dom import minidom 5 6from wfuzz.externals.moduleman.plugin import moduleman_plugin 7from wfuzz.plugin_api.base import BasePrinter 8from wfuzz.exception import FuzzExceptPluginBadParams 9 10 11@moduleman_plugin 12class magictree(BasePrinter): 13 name = "magictree" 14 author = ("Xavi Mendez (@xmendez)",) 15 version = "0.1" 16 summary = "Prints results in magictree format" 17 category = ["default"] 18 priority = 99 19 20 def __init__(self, output): 21 BasePrinter.__init__(self, output) 22 self.node_mt = None 23 self.node_service = None 24 25 def __create_xml_element(self, parent, caption, text): 26 # Create a <xxx> element 27 doc = minidom.Document() 28 el = doc.createElement(caption) 29 parent.appendChild(el) 30 31 # Give the <xxx> element some text 32 ptext = doc.createTextNode(text) 33 34 el.appendChild(ptext) 35 return el 36 37 def header(self, summary): 38 doc = minidom.Document() 39 40 # <magictree class="MtBranchObject"> 41 self.node_mt = doc.createElement("magictree") 42 self.node_mt.setAttribute("class", "MtBranchObject") 43 44 # <testdata class="MtBranchObject"> 45 node_td = doc.createElement("testdata") 46 node_td.setAttribute("class", "MtBranchObject") 47 self.node_mt.appendChild(node_td) 48 49 # <host>209.85.146.105 50 host = summary.seed.history.host 51 if host.find(":") > 0: 52 host, port = host.split(":") 53 else: 54 port = 80 55 if summary.seed.history.scheme.lower() == "https": 56 port = 443 57 58 try: 59 resolving = socket.gethostbyname(host) 60 node_h = self.__create_xml_element(node_td, "host", str(resolving)) 61 except socket.gaierror: 62 node_h = self.__create_xml_element(node_td, "host", str(host)) 63 64 # <ipproto>tcp 65 node_ipr = self.__create_xml_element(node_h, "ipproto", "tcp") 66 67 # <port>80<state>open</state><service>http 68 node_port = self.__create_xml_element(node_ipr, "port", str(port)) 69 self.__create_xml_element(node_port, "state", "open") 70 if summary.seed.history.scheme.lower() == "https": 71 node_port = self.__create_xml_element(node_port, "tunnel", "ssl") 72 73 self.node_service = self.__create_xml_element(node_port, "service", "http") 74 75 def result(self, fuzz_result): 76 node_url = self.__create_xml_element( 77 self.node_service, "url", str(fuzz_result.url) 78 ) 79 80 if "Server" in fuzz_result.history.headers.response: 81 self.__create_xml_element( 82 node_url, "HTTPServer", fuzz_result.history.headers.response["Server"] 83 ) 84 85 location = "" 86 if "Location" in fuzz_result.history.headers.response: 87 location = fuzz_result.history.headers.response["Location"] 88 89 if fuzz_result.code == 301 or fuzz_result.code == 302 and location: 90 self.__create_xml_element(node_url, "RedirectLocation", location) 91 92 self.__create_xml_element(node_url, "ResponseCode", str(fuzz_result.code)) 93 self.__create_xml_element(node_url, "source", "WFuzz") 94 95 def footer(self, summary): 96 self.f.write(self.node_mt.toxml()) 97 98 99@moduleman_plugin 100class html(BasePrinter): 101 name = "html" 102 author = ( 103 "Carlos del Ojo", 104 "Christian Martorella", 105 "Adapted to newer versions Xavi Mendez (@xmendez)", 106 ) 107 version = "0.1" 108 summary = "Prints results in html format" 109 category = ["default"] 110 priority = 99 111 112 def __init__(self, output): 113 BasePrinter.__init__(self, output) 114 115 def header(self, summary): 116 url = summary.url 117 118 self.f.write( 119 '<html><head></head><body bgcolor=#000000 text=#FFFFFF><h1>Fuzzing %s</h1>\r\n<table border="1">\r\n<tr><td>#request</td><td>Code</td><td>#lines</td><td>#words</td><td>Url</td></tr>\r\n' 120 % (url) 121 ) 122 123 def result(self, fuzz_result): 124 htmlc = "<font>" 125 126 if fuzz_result.code >= 400 and fuzz_result.code < 500: 127 htmlc = "<font color=#FF0000>" 128 elif fuzz_result.code >= 300 and fuzz_result.code < 400: 129 htmlc = "<font color=#8888FF>" 130 elif fuzz_result.code >= 200 and fuzz_result.code < 300: 131 htmlc = "<font color=#00aa00>" 132 133 if fuzz_result.history.method.lower() == "post": 134 inputs = "" 135 for n, v in list(fuzz_result.history.params.post.items()): 136 inputs += '<input type="hidden" name="%s" value="%s">' % (n, v) 137 138 self.f.write( 139 '\r\n<tr><td>%05d</td>\r\n<td>%s%d</font></td>\r\n<td>%4dL</td>\r\n<td>%5dW</td>\r\n<td><table><tr><td>%s</td><td><form method="post" action="%s">%s<input type=submit name=b value="send POST"></form></td></tr></table></td>\r\n</tr>\r\n' 140 % ( 141 fuzz_result.nres, 142 htmlc, 143 fuzz_result.code, 144 fuzz_result.lines, 145 fuzz_result.words, 146 fuzz_result.description, 147 fuzz_result.url, 148 inputs, 149 ) 150 ) 151 else: 152 self.f.write( 153 "\r\n<tr><td>%05d</td><td>%s%d</font></td><td>%4dL</td><td>%5dW</td><td><a href=%s>%s</a></td></tr>\r\n" 154 % ( 155 fuzz_result.nres, 156 htmlc, 157 fuzz_result.code, 158 fuzz_result.lines, 159 fuzz_result.words, 160 fuzz_result.url, 161 fuzz_result.url, 162 ) 163 ) 164 165 def footer(self, summary): 166 self.f.write("</table></body></html><h5>Wfuzz by EdgeSecurity<h5>\r\n") 167 168 169@moduleman_plugin 170class json(BasePrinter): 171 name = "json" 172 summary = "Results in json format" 173 author = ("Federico (@misterade)", "Minor rework by Ilya Glotov (@ilyaglow)") 174 version = "0.2" 175 category = ["default"] 176 priority = 99 177 178 def __init__(self, output): 179 BasePrinter.__init__(self, output) 180 self.json_res = [] 181 182 def header(self, res): 183 pass 184 185 def result(self, res): 186 server = "" 187 if "Server" in res.history.headers.response: 188 server = res.history.headers.response["Server"] 189 location = "" 190 if "Location" in res.history.headers.response: 191 location = res.history.headers.response["Location"] 192 elif res.history.url != res.history.redirect_url: 193 location = "(*) %s" % res.history.url 194 post_data = [] 195 if res.history.method.lower() == "post": 196 for n, v in list(res.history.params.post.items()): 197 post_data.append({"parameter": n, "value": v}) 198 199 res_entry = { 200 "chars": res.chars, 201 "code": res.code, 202 "payload": res.description, 203 "lines": res.lines, 204 "location": location, 205 "method": res.history.method, 206 "post_data": post_data, 207 "server": server, 208 "url": res.url, 209 "words": res.words, 210 } 211 self.json_res.append(res_entry) 212 213 def footer(self, summary): 214 self.f.write(jjson.dumps(self.json_res)) 215 216 217@moduleman_plugin 218class raw(BasePrinter): 219 name = "raw" 220 author = ("Xavi Mendez (@xmendez)",) 221 version = "0.1" 222 summary = "Raw output format" 223 category = ["default"] 224 priority = 99 225 226 def __init__(self, output): 227 BasePrinter.__init__(self, output) 228 229 def header(self, summary): 230 self.f.write("Target: %s\n" % summary.url) 231 232 if summary.total_req > 0: 233 self.f.write("Total requests: %d\n" % summary.total_req) 234 else: 235 self.f.write("Total requests: <<unknown>>\n") 236 237 if self.verbose: 238 self.f.write( 239 "==============================================================================================================================================\n" 240 ) 241 self.f.write( 242 "ID C.Time Response Lines Word Chars Server Redirect Payload \n" 243 ) 244 self.f.write( 245 "==============================================================================================================================================\n" 246 ) 247 else: 248 self.f.write( 249 "==================================================================\n" 250 ) 251 self.f.write( 252 "ID Response Lines Word Chars Request \n" 253 ) 254 self.f.write( 255 "==================================================================\n" 256 ) 257 258 def _print_verbose(self, res): 259 self.f.write("%05d: " % res.nres) 260 self.f.write("%.3fs C=" % res.timer) 261 262 location = "" 263 if "Location" in res.history.headers.response: 264 location = res.history.headers.response["Location"] 265 elif res.history.url != res.history.redirect_url: 266 location = "(*) %s" % res.history.url 267 268 server = "" 269 if "Server" in res.history.headers.response: 270 server = res.history.headers.response["Server"] 271 272 if res.exception: 273 self.f.write("XXX") 274 else: 275 self.f.write("%05d: C=%03d" % (res.nres, res.code)) 276 277 self.f.write( 278 ' %4d L\t %5d W\t %5d Ch %20.20s %51.51s "%s"\n' 279 % ( 280 res.lines, 281 res.words, 282 res.chars, 283 server[:17], 284 location[:48], 285 res.description, 286 ) 287 ) 288 289 for plugin_res in res.plugins_res: 290 if plugin_res.is_visible(self.verbose): 291 self.f.write( 292 " |_ {} {}\n".format( 293 plugin_res.issue, plugin_res.data if plugin_res.data else "" 294 ) 295 ) 296 297 def _print(self, res): 298 if res.exception: 299 self.f.write("XXX") 300 else: 301 self.f.write("%05d: C=%03d" % (res.nres, res.code)) 302 303 self.f.write( 304 ' %4d L\t %5d W\t %5d Ch\t "%s"\n' 305 % (res.lines, res.words, res.chars, res.description) 306 ) 307 308 for plugin_res in res.plugins_res: 309 if plugin_res.is_visible(self.verbose): 310 self.f.write( 311 " |_ {} {}\n".format( 312 plugin_res.issue, plugin_res.data if plugin_res.data else "" 313 ) 314 ) 315 316 def result(self, res): 317 if self.verbose: 318 self._print_verbose(res) 319 else: 320 self._print(res) 321 322 def footer(self, summary): 323 self.f.write("\n") 324 self.f.write("Total time: %s\n" % str(summary.totaltime)[:8]) 325 326 if summary.backfeed() > 0: 327 self.f.write( 328 "Processed Requests: %s (%d + %d)\n" 329 % ( 330 str(summary.processed())[:8], 331 (summary.processed() - summary.backfeed()), 332 summary.backfeed(), 333 ) 334 ) 335 else: 336 self.f.write("Processed Requests: %s\n" % (str(summary.processed())[:8])) 337 self.f.write("Filtered Requests: %s\n" % (str(summary.filtered())[:8])) 338 self.f.write( 339 "Requests/sec.: %s\n" 340 % str( 341 summary.processed() / summary.totaltime if summary.totaltime > 0 else 0 342 )[:8] 343 ) 344 345 346@moduleman_plugin 347class field(BasePrinter): 348 name = "field" 349 author = ("Xavi Mendez (@xmendez)",) 350 version = "0.1" 351 summary = "Raw output format only showing the specified field expression. No header or footer." 352 category = ["default"] 353 priority = 99 354 355 def __init__(self, output): 356 BasePrinter.__init__(self, output) 357 358 def header(self, summary): 359 pass 360 361 def result(self, res): 362 if res._fields: 363 to_print = res._field("\n") 364 if to_print: 365 print(to_print) 366 else: 367 raise FuzzExceptPluginBadParams( 368 "You need to supply valid --field or --efield expression for unsing this printer." 369 ) 370 371 def footer(self, summary): 372 pass 373 374 375@moduleman_plugin 376class csv(BasePrinter): 377 name = "csv" 378 author = ( 379 "@Yoginski initial version", 380 "Adapted by @egilas to work in newer version of wfuzz", 381 ) 382 summary = "CSV printer ftw" 383 version = "1.0" 384 category = ["default"] 385 priority = 99 386 387 def write(self, e): 388 self.f.write(e) 389 pass 390 391 def __init__(self, output): 392 BasePrinter.__init__(self, output) 393 self.csv_writer = csvmod.writer(self) 394 395 def header(self, summary): 396 self._print_csv( 397 ["id", "response", "lines", "word", "chars", "request", "success"] 398 ) 399 400 def result(self, res): 401 line = [ 402 res.nres, 403 res.code, 404 res.lines, 405 res.words, 406 res.chars, 407 res.description, 408 0 if res.exception else 1, 409 ] 410 self._print_csv(line) 411 412 def noresult(self, res): 413 pass 414 415 def footer(self, summary): 416 pass 417 418 def _print_csv(self, values): 419 self.csv_writer.writerow(values) 420