Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sslstrip & some related issues in http(s).proxy and dns.spoof #723

Merged
merged 3 commits into from
Jul 3, 2020
Merged

Fix sslstrip & some related issues in http(s).proxy and dns.spoof #723

merged 3 commits into from
Jul 3, 2020

Conversation

Petitoto
Copy link
Contributor

This pull request fixes sslstrip (related to #428). Some sslstrip's related issues are fixed in http(s).proxy and dns.spoof.

What was fixed:

  • always "hit max redirections" when stripping a URL
    Bettercap used to send HTTP requests to real websites, even if they tried to redirect it to HTTPS. So bettercap caused infinite redirections. Now it forwards stripped requests to the real websites with the HTTPS schema (target - bettercap uses HTTP whereas bettercap - server uses HTTPS). "Max redirections" is never reached and was removed. Moreover, a part of the code tried to forward HTTPS requests to HTTP (to the real websites). I don't understand why : because of HSTS, websites will only process HTTPS requests (so if we can handle an HTTPS request thanks to the HTTPS proxy, we need to transmit it in HTTPS).
  • subdomain was modified (www to wwwww)
    Subdomains of the same domain can be protected by HSTS with the includeSubDomains flag. So now sslsttrip edits the Top-Level Domain (TLD).
  • new option sslstrip.useIDN to choose between 2 TLD modifications
    If false, sslstrip doubles the last TLD's character. If true, it adds an international character (currently ノ) to create an Internationalized Domain Name. IDN is really efficient to hide a stripped domain name (example.com is edited to example.comノ) but some browsers don't show them by default (example.xn--com-mn4b). Sslstrip only records the ASCII format (thanks to idna) but edits links with the internationalized one. So the target will see example.comノ on his webpages, but his browser will reach example.xn--com-mn4b. What is printed on the URL bar depends on the browser (I tested on some browsers with a European language and default configuration, and ノ was printed on a lot while setting useIDN to true).
  • in case of a redirection response, sslstrip was tracking the original host and not the new host
    New host is stripped, not the original one. That's why we need to track the new one.
  • support cookies
    When a domain name is changed, cookies need to be edited. So now, responses of stripped requests are edited to the stripped domain. To do this, I added a function in the host tracker to get the stripped domain of an unstripped already tracked one, and now a host tracker is composed of 2 maps (easier in order to add the new function). Moreover, when editing cookies, HttpOnly and Secure flags are stripped. Thus, cookies can be sent through HTTP (required to support cookies) and injected scripts can access cookies (just useful).
  • strippers from different HTTP(s) proxies were different
    When using HTTP and HTTPS proxies with both sslstrip activated, users want that links stripped by the HTTPS proxy can be intercepted by the HTTP proxy. So now, proxies save their stripper, and Configure() checks for another module using sslstrip and merges strippers (thanks to the modules' Extra() function).
  • javascript code in injectjs couldn't be edited
    When configuring an HTTP proxy, function checked that p.jsHook was an empty string before setting it (when users want to put a javascript code directly). So now, it resets p.jsHook when calling Configure().
  • conflict between sslstrip and injectjs / script
    When sslstrip was loaded, injectjs and script caused pages to be never loaded properly. It seems to occur because of the new response which was created by bettercap (I don't really know why). So now it modifies the current response instead of creating a new one. Moreover, you can now set together script and injectjs options.
  • sslstrip had his own dnsReply()
    The latest updates of dns.spoof weren't available in sslstrip. Now, dnsReply() from dns.spoof can be called without a DNSSpoofer, and sslstrip calls it for more stability (I removed his own dnsReply()).
  • tag of an HTTP proxy
    When HTTPProxy() was called, the tag (to print information) was set to "http.proxy". HTTPS proxies had to modify it after. However, for some information (like "enabling forwarding" or my new "found another proxy using sslstrip"), it didn't work. So now, when calling HTTPProxy(), you need to specify the tag to use.

Screenshots of some target browsers I tested while sslstripping:
screenshots

If you encounter issues while using this sslstrip, it could be because of:

  • target directly reached HTTPS
  • HSTS is loaded on the target website (HSTS-preload / target already visited the website before -> clearing browser data could be useful)
  • links on a stripped webpage are using an HTTP schema (or a relative one), but HSTS is enabled on these links (sslstrip won't edit http links)
  • stripped webpage dynamically loads links from encoded strings using javascript (using a specific script could be useful)
  • stripped website verifies the hostname (using a specific script could be useful)
  • if target visited stripped url without sslstrip being launched or if an error occurred when sending the DNS replies, the target could cache "wrong" DNS responses of stripped hostnames (flushing DNS cache could be useful)
  • target uses DNS over HTTPS
  • cookies are edited for a stripped host, but if user visits the unstripped one, they will be lost (could occur, especially when changing subdomains or when using HTTP and HTTPS proxies together)

@OscarAkaElvis
Copy link

Can anybody check this please? @buffermet @evilsocket . This could fix this: #428

Could be awesome for us. Thanks.

@evilsocket
Copy link
Member

@OscarAkaElvis this is a pretty significant change and i won't be able to test it anytime soon, maybe you can test it to see if it fixes your usecase?

@OscarAkaElvis
Copy link

Sorry for the delay, we were testing this deeply. The results are unpredictable. Sometimes it works... sometimes it doesn't work. Of course we know what we do and victim browser's cache is deleted on each test..... we were able to spoof successfully sometimees trello.com page and get the credentials. Tested also with another non HSTS random domain: farmaciasdirect.com. But the most of the times it doesn't work and we don't know why... 😢 It is very unstable.

Another thing I saw is that it spoofs even non ssl domains ¿? so if I want to get a creds from a http non ssl site, let's suppose whatever.com it tries to spoof it to whatever.comm and then it fails to open. In my opinion, non-ssl domains should not be spoofed, it is pointless.

@Petitoto can you check this? we tested with a couple of caplet config files. One of them is this:

caplets.update
set http.proxy.port 8080
set http.proxy.sslstrip true
set http.proxy.script /usr/share/bettercap/caplets/hstshijack/hstshijack.js

set net.sniff.verbose true
net.sniff on

events.ignore net.sniff.http.response
events.ignore http.proxy.spoofed-response
events.ignore net.sniff.dns
events.ignore net.sniff.tcp
events.ignore net.sniff.udp

set hstshijack.ignore *
set hstshijack.obfuscate true
set hstshijack.encode false
set hstshijack.payloads *:/usr/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/share/bettercap/caplets/hstshijack/payloads/keylogger.js

set dns.spoof.domains *
dns.spoof on
http.proxy on
net.recon off

The other we tested with similar results is this:

caplets.update
set http.proxy.port 8080
set http.proxy.sslstrip true
set http.proxy.script /usr/share/bettercap/caplets/hstshijack/hstshijack.js

set net.sniff.verbose true
net.sniff on

events.ignore net.sniff.http.response
events.ignore http.proxy.spoofed-response
events.ignore net.sniff.dns
events.ignore net.sniff.tcp
events.ignore net.sniff.udp

set hstshijack.ignore *
set hstshijack.targets *.com, *.net, *.es, *.co.uk, www.*
set hstshijack.replacements *.corn, *.nel, *.ss, *.cc.uk, wwww.*
set hstshijack.obfuscate true
set hstshijack.encode false
set hstshijack.payloads *:/usr/share/bettercap/caplets/hstshijack/payloads/sslstrip.js,*:/usr/share/bettercap/caplets/hstshijack/payloads/keylogger.js

set dns.spoof.domains *.corn, *.nel, *.ss, *.cc.uk, wwww.*
dns.spoof on
http.proxy on
net.recon off

Using this last caplet config file we noticed that a .com domain sometimes is redirected to .comm instead of to .corn file ¿?

Thanks @JBalanza for helping me on this testing.

@Petitoto
Copy link
Contributor Author

Petitoto commented Jun 22, 2020

Thank you for testing and for your interest.

First, you should not use any caplet. I made this sslstrip because sometimes I run into troubles with hstshijack, and because I wanted to implement a “pure” sslstrip, without any javascript injection. That’s why it behaves differently (like redirecting to .comm and not to .corn). Moreover, when using it with hstshijack, it could produce other issues. Could you try without any caplet?

If problem persists, could you try with various browsers, especially a more “basic” one, and inspect DNS queries and responses? During my tests I sometimes had problems resembling what you describe, but it was quite rare and seemed to be a DNS issue.

Regarding non SSL domains, I will try to check the issue later this week. As far as I remember (I published this sslstrip 2 months ago 😅), it shouldn’t try to spoof them.
EDIT : I checked, and I can't reproduce the issue. Non ssl domains should not be spoofed. Could you confirm this when using this sslstrip without any caplet ? @OscarAkaElvis

@OscarAkaElvis
Copy link

Awesome, it is working!!! I've been able to sniff a password sslstripping trello.com and the BeEF injection is also working:

image

image

This is the very basic used caplet:

set http.proxy.script ag.bettercap.js
set http.proxy.sslstrip true
net.sniff on

set net.sniff.verbose true
set dns.spoof.domains *
dns.spoof on
http.proxy on

net.recon off

For some reason, still can't sniff password for non-ssl pages. Please @Petitoto can you check this point?

Please @evilsocket merge this awesome PR!! maybe the fix for the non-ssl pages should be done first... but this is the right path!!

@evilsocket evilsocket merged commit b53b5b0 into bettercap:master Jul 3, 2020
@evilsocket
Copy link
Member

thanks a ton for the PR and for testing :D

@Petitoto
Copy link
Contributor Author

Petitoto commented Jul 3, 2020

Thanks for testing and merging :D

@OscarAkaElvis, I checked and I'm able to sniff passwords on non-ssl pages. Here is what I used :

net.recon on
set arp.spoof.targets 192.168.1.63

set http.proxy.sslstrip true

http.proxy on
arp.spoof on
net.sniff on

(By the way, as it's already included in my sslstrip, you don't need to enable dns.spoof: just set http.proxy.sslstrip and possibly http.proxy.sslstrip.useIDN)

Then I was able to strip https://trello.com and load http://testasp.vulnweb.com, and of course sniff passwords :
Screen1
Screen2

If the problem persists, maybe you could open a new issue with screenshots and the configuration used ?

@buffermet
Copy link
Member

buffermet commented Jul 6, 2020

@OscarAkaElvis don't use the hstshijack caplet in conjunction with the builtin sslstrip module, it will result in the proxy doing twice the amount of work, potentially interfering with one another.


always "hit max redirections" when stripping a URL
Bettercap used to send HTTP requests to real websites, even if they tried to redirect it to HTTPS. So bettercap caused infinite redirections. Now it forwards stripped requests to the real websites with the HTTPS schema (target - bettercap uses HTTP whereas bettercap - server uses HTTPS). "Max redirections" is never reached and was removed. Moreover, a part of the code tried to forward HTTPS requests to HTTP (to the real websites). I don't understand why : because of HSTS, websites will only process HTTPS requests (so if we can handle an HTTPS request thanks to the HTTPS proxy, we need to transmit it in HTTPS).

👍 This is correct behaviour, bettercap can spoof HTTPS connections as long as it strips the responses.

subdomain was modified (www to wwwww)
Subdomains of the same domain can be protected by HSTS with the includeSubDomains flag. So now sslsttrip edits the Top-Level Domain (TLD).

This is true, any and all subdomains can be protected if the 'includeSubDomains' flag is set in the HSTS header, which is why I implemented the option to set your own domain replacements in the hstshijack module.

new option sslstrip.useIDN to choose between 2 TLD modifications
If false, sslstrip doubles the last TLD's character. If true, it adds an international character (currently ノ) to create an Internationalized Domain Name. IDN is really efficient to hide a stripped domain name (example.com is edited to example.comノ) but some browsers don't show them by default (example.xn--com-mn4b). Sslstrip only records the ASCII format (thanks to idna) but edits links with the internationalized one. So the target will see example.comノ on his webpages, but his browser will reach example.xn--com-mn4b. What is printed on the URL bar depends on the browser (I tested on some browsers with a European language and default configuration, and ノ was printed on a lot while setting useIDN to true).

I suggest removing this as the results are incosistent across browsers and other HTTP clients, some of which only supporting ASCII characters in URL fields.

in case of a redirection response, sslstrip was tracking the original host and not the new host
New host is stripped, not the original one. That's why we need to track the new one.

If with "tracking" you mean changing spoofed domains back to the original (and vice versa) 👍

support cookies
When a domain name is changed, cookies need to be edited. So now, responses of stripped requests are edited to the stripped domain. To do this, I added a function in the host tracker to get the stripped domain of an unstripped already tracked one, and now a host tracker is composed of 2 maps (easier in order to add the new function). Moreover, when editing cookies, HttpOnly and Secure flags are stripped. Thus, cookies can be sent through HTTP (required to support cookies) and injected scripts can access cookies (just useful).

I wasn't aware that cookies can have a secure flag, thanks :) 👍

strippers from different HTTP(s) proxies were different
When using HTTP and HTTPS proxies with both sslstrip activated, users want that links stripped by the HTTPS proxy can be intercepted by the HTTP proxy. So now, proxies save their stripper, and Configure() checks for another module using sslstrip and merges strippers (thanks to the modules' Extra() function).

I'm not sure if I'm following, but if you already have access to HTTPS packets (with bettercap's CA cert), you don't need to SSLstrip and probably shouldn't. Otherwise if downgrading insecure connections that were excepted by the victim, this is definitely an improvement 👍


I'd like to see some of the optionality of hstshijack in the builtin proxy, for instance the ability to choose how a TLD/domain is spoofed (e.g. .com -> .corn), currently HTTP proxy modules lack the ability to make asynchronous calls, as well as making additional HTTP requests.

Because of this I built an alphanumeric domain indexer, making the module a lot faster and scalable, potentially allowing us to import the entire HSTS preload list of domains so that we don't have to request each host to learn if they use HTTPS.

Domains are filtered and indexed on launch, and concatination occurs at runtime with the use of index ranges, reducing overhead:

Screenshot from 2020-05-15 16-48-05

@buffermet
Copy link
Member

I might as well make Go modules for such indexers, because e.g. bettercap's hostname tracker could also benefit from this.

@buffermet
Copy link
Member

@OscarAkaElvis

For some reason, still can't sniff password for non-ssl pages. Please @Petitoto can you check this point?

set events.stream.http.request.dump true
events.stream off
events.stream on

@evilsocket I feel like the HTTP request dump should be part of the net.sniff module, I sometimes miss out on logs when restarting the events.stream and sometimes it stops printing logs alltogether (even though events.stream is running).

@Petitoto
Copy link
Contributor Author

Petitoto commented Jul 6, 2020

Thanks @buffermet for your report.

Using both HTTPS proxy and sslstrip could be interesting if you want to try to strip websites, but if https is directly loaded, you want to send your own CA cert. In this situation, it's interesting to strip links on websites intercepted by HTTPS proxy.
For example, if target loads trello.com without https and hsts, bettercap will be able to strip it. However, if target directly loads https://www.google.com, you can't strip it so you would like to send your own CA cert (in hope that the target will accept it or has excepted it). Then, when the target will search for websites, you would like to strip links directly in google in order not to send your CA cert on other websites. In this situation, it's required to share configurations of https.sslstrip and http.sslstrip (this is what I fixed).

I totally agree with you about implementing an option like the one in hstshijack (I love the .com -> .corn). I haven't implemented it first and I have no time to do it yet, but I'd be happy to add it in the future (maybe I could do it in a few weeks :D).
I think that the 'sslstrip.useIDN' option is a bit strange, but I would like to keep the IDN spoof integrated in such optionality.

Indeed, regarding Internationalized Domain Names, browsers and HTTP clients will reach example.xn--com-mn4b. So, even if they support only ASCII characters, they will load stripped websites. The "problem" is what is displayed to the user: because of IDN homograph attacks, a lot of browsers have decided to display some characters as punycode (https://en.wikipedia.org/wiki/IDN_homograph_attack). However, some others are displayed as unicode (because they could not lead to an IDN homograph attack and are used in real websites). For example, I chose ノ because it's really difficult to see it if you're not aware, and because it is displayed on a lot of browsers (during my tests, only Edge and Internet Explorer displayed example.x--com-mn4b, others like Chrome and Firefox displayed correctly example.comノ). Moreover, IDN support could be interesting for a few browsers vulnerable to IDN homograph attacks (on them you could strip a domain without any visible modificiation!).

Because of this I built an alphanumeric domain indexer, making the module a lot faster and scalable, potentially allowing us to import the entire HSTS preload list of domains so that we don't have to request each host to learn if they use HTTPS.
Domains are filtered and indexed on launch, and concatination occurs at runtime with the use of index ranges, reducing overhead:

Your indexer is interesting, it would be a good improvement to make Go modules for such indexers :D

@jepunband
Copy link

hello, I'm unable to get a downgrade some sites from www to 'wwww' , (websites that aren't preloaded with 'HSTS'), there are lot of websites that this should work with as the previous version of Bettercap 1.6 would do this easily.

Current version does not render the website at all. Any help on this please? i'm using parrot os and bettercap comes pre-installed.
bettercap v2.21.1 (built for linux amd64 with go1.11.6)

example commands used:

set http.proxy.sslstrip true
set net.sniff.verbose false
set arp.spoof.targets 192.168.1.5
arp.spoof on
http.proxy on
net.sniff on

@Petitoto
Copy link
Contributor Author

Hello @jepunband,
Prior to this fix, sslstrip of bettercap 2 wasn't working correctly. This fix is included in bettercap v2.28, but you're using bettercap v2.21.1.

Note that sslstrip won't replace www by wwww because of the "includeSubDomains" flag. Instead, it will edit top-level domains.

@Petitoto Petitoto deleted the sslstrip branch August 23, 2020 18:42
@jepunband
Copy link

Hi @Petitoto , thanks for the info i will update it with the current release and try again.

buffermet added a commit that referenced this pull request May 1, 2021
revert changes from #723 that prevented HTTP response header spoofing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet