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