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

Adds RSA key extraction via Heartbleed #3268

Closed
wants to merge 36 commits into from
Closed

Adds RSA key extraction via Heartbleed #3268

wants to merge 36 commits into from

Conversation

jjarmoc
Copy link
Contributor

@jjarmoc jjarmoc commented Apr 16, 2014

This pull adds the ability to retrieve Private Keys from memory. There's also significant refactoring, mostly splitting things into functions to allow for better reuse.

msftidy comes back clean.

I've tested this against my own vulnerable nginx box (set up similar to https://news.ycombinator.com/item?id=7577659), and it works well with 1024 and 2048 bit keys. After restart it gets the key every time. Under light load it takes a little longer, as expected. I also used this on www.cloudflarechallenge.com successfully (see https://gist.github.com/jjarmoc/10890697)

Some new options are added. Notably;

MODE is an Enum that determines how the module operates. Choices are:

  • SCAN - A generic scan for vulnerable hosts.
  • DUMP - Stores retrieved memory (same as STOREDUMP true in the previous version)
  • KEYS - Try to get private keys from memory (This is my new stuff)

In KEYS mode, two new options are used;

  • MAX_KEYTRIES - This is the maximum number of times to try for a private key on each host
  • STATUS_EVERY - status is reported modulo this may retries when trying for keys. Just lets us adjust how noisy reporting is/isn't.

Some caveats:

  • STARTTLS isn't currently supported for KEYS mode. I need to work out how to parse the public key....
  • Doesn't address non-RSA certs (but that's uncommon)
  • I changed the heartbeat response read to use sock.get() instead of sock.get_once. The latter was badly truncating the response data; I'd see lots of data on the wire that never got read. See Heartbeat responses truncated - bug? #3261

todb-r7 and others added 27 commits Apr 10, 2014
This enables passing rhost and rport directly to send_request_cgi
without having to monkey with the datastore.

See #8498
 - [ ] Fire up tcpdump on the listening interface
 - [ ] Run the module and see the pcap:

listening on vmnet8, link-type EN10MB (Ethernet), capture size 65535
bytes
20:56:02.592331 IP 192.168.145.1.41547 > 255.255.255.255.9: UDP, length
102
Works now. The rhost option was not keeping the custom vhost option.

````
msf auxiliary(shodan_search) > rexploit
[*] Reloading module...

[*] Total: 13443 on 269 pages. Showing: 1
[*] Country Statistics:
[*] United States (US): 2006
[*] Germany (DE): 1787
[*] Korea, Republic of (KR): 1061
[*]     Italy (IT): 916
[*] Hungary (HU): 604
[*] Collecting data, please WaitUntilAuthEmptyt...

IP Results
==========
````
Verification:

````
resource (./a.rc)> run
[*] Connecting to FTP server ....
[*] FTP recv: "220 ProFTPD 1.3.3a Server (My FTP server)
[*] Connected to target FTP server.
[*] Authenticating as anonymous with password mozilla@example.com...
[*] FTP send: "USER anonymous\r\n"
[*] FTP recv: "331 Anonymous login ok, send your complete email address
as your password\r\n"
````

...etc.
This looks okay from debug (the host looks like it's generating okay)
but there may be some subtle thing I'm not seeing here. @wchen-r7 can
you glance at this please?

[SeeRM #8498]
There are three total, and they're all copy-pasted from the original
module from 2009. I suspect this idiom isn't used at all any more -- I
can't detect a difference in the payload if I just declare a host being
cli.peerhost, rather than rewriting RHOST to be cli.peerhost.

[SeeRM #8498]
We dont know if a DOMAIN or IP is specified etc.
Conflicts:
	lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb
[FixRM #8489] rhost/rport modification
Lots of refactoring too.
@firefart
Copy link
Contributor

Awesome work! I hope I will find some time during this week to test this

@jjarmoc
Copy link
Contributor Author

jjarmoc commented Apr 16, 2014

Thanks for the kind words, @firefart and for getting the msf module in gear in the first place!

One note to hopefully head off what I'm sure will be a FAQ; The private key extracted might not match the legit privatekey's PEM bytewise. It depends on the order factors are stored, and whether optional params such as CRT constants are included or not. However, it will be a valid private key for the retrieved public, and that's what matters...

@jlee-r7
Copy link
Contributor

jlee-r7 commented Apr 16, 2014

Your MODE option sounds like it should be an Action.

@julianvilas
Copy link
Contributor

In my Apache it seems that you don't need so much lucky. After every reboot, you get the key on first attempt.

And after running 20.000 POST requests with Content-Length: 35260, it continues leaking the key (first attempt), but after more or less 30.000 it starts needing more attempts. When 40.000, is not leaking the key (at least with more than 100 hundred attempts).

@firefart
Copy link
Contributor

Ok I tried it over night --> No result after ~7000 tries.
I tried it again with this command run in parallel:
while true; do curl -sk https://192.168.224.148 > /dev/null; done
--> Hit after 21 attempts
So it looks like you need some traffic on the server to "shuffle" memory and get the keys

@jlee-r7
Copy link
Contributor

jlee-r7 commented Apr 17, 2014

What about changing MAX_KEYTRIES to something like ATTEMPTS and have it leak that many times in both DUMP and KEYS mode?

@jlee-r7
Copy link
Contributor

jlee-r7 commented Apr 17, 2014

Having options be specific to an action is something I've thought about in the past. Unfortunately, it would require major re-work of the options system.

@jjarmoc
Copy link
Contributor Author

jjarmoc commented Apr 17, 2014

That seems like a good idea @jlee-r7.

I had considered it, but I wasn't sure if the store_loot() in DUMP could append data to the existing loot. It'd obviously be messy to create a seperate loot for each dump.

I was also trying to minimize changes to other functions of the module, in the interest of making it easier to review and land.

@firefart
Copy link
Contributor

Hi @jjarmoc I just merged another PR for this module and now there are merge conflicts on this PR (I think the authors). Can you please rebase your PR and fix the conflicts?

@jjarmoc
Copy link
Contributor Author

jjarmoc commented Apr 17, 2014

Hmm.. I'm trying. Rebase shows the merge conflict, but I don't see an easy way to identify exactly what's conflicting, beyond the fact that it's this file (obviously.)

@kernelsmith
Copy link
Contributor

Maybe git diff filename. Not sure if that will give you exactly what's conflicting, but will at least show the differences of course

-Josh

On Apr 17, 2014, at 13:41, Jeff Jarmoc notifications@github.com wrote:

Hmm.. I'm trying. Rebase shows the merge conflict, but I don't see an easy way to identify exactly what's conflicting, beyond the fact that it's this file (obviously.)


Reply to this email directly or view it on GitHub.

@kernelsmith
Copy link
Contributor

One of the options for store loot decides what to do when there's a collision. Can't remember the name (am in the dr office ATM) but it's well doc'd

-Josh

On Apr 17, 2014, at 11:23, Jeff Jarmoc notifications@github.com wrote:

That seems like a good idea @jlee-r7.

I had considered it, but I wasn't sure if the store_loot() in DUMP could append data to the existing loot. It'd obviously be messy to create a seperate loot for each dump.

I was also trying to minimize changes to other functions of the module, in the interest of making it easier to review and land.


Reply to this email directly or view it on GitHub.

jjarmoc added 5 commits Apr 17, 2014
Lots of refactoring too.
Also changes the TLS_CALLBACKS Enum, per egyp7's request.
Conflicts:
	modules/auxiliary/scanner/ssl/openssl_heartbleed.rb
@jjarmoc
Copy link
Contributor Author

jjarmoc commented Apr 17, 2014

Uhh yah... that sort of worked. My branch is resolved, but the PR seems to have picked up all the intermediate commits as well. Is that going to pose a problem in landing it?

@firefart
Copy link
Contributor

Ok this PR is now **** up. Would you mind creating a new branch, add the changes and submit a new PR? You can create a patch from this branch with some git-diff magic and apply it on the new branch.

@jjarmoc
Copy link
Contributor Author

jjarmoc commented Apr 17, 2014

See #3274 If we land that one, this one can just be closed out...

@firefart
Copy link
Contributor

Closing, see other PR for ongoing discussion and merge

@firefart firefart closed this Apr 17, 2014
@gozubuyukoglu
Copy link

Hi jjarmoc, first of all thank you for that wonderful plugin, unfortunately I am unsuccesful , Please advice me where I am doing wrong. detailes below
Regards,
Arda

Module options (auxiliary/scanner/ssl/openssl_heartbleed):

   Name              Current Setting             Required  Description
   ----              ---------------             --------  -----------
   DUMPFILTER                                    no        Pattern to filter leaked memory before storing
   MAX_KEYTRIES      50                          yes       Max tries to dump key
   RESPONSE_TIMEOUT  10                          yes       Number of seconds to wait for a server response
   RHOSTS            heartbleed.hacking-lab.com  yes       The target address range or CIDR identifier
   RPORT             443                         yes       The target port
   STATUS_EVERY      5                           yes       How many retries until status
   THREADS           20                          yes       The number of concurrent threads
   TLS_CALLBACK      None                        yes       Protocol to use, "None" to use raw TLS sockets (accepted: None, SMTP, IMAP, JABBER, POP3, FTP, POSTGRES)
   TLS_VERSION       1.1                         yes       TLS/SSL version to use (accepted: SSLv3, 1.0, 1.1, 1.2)


Auxiliary action:

   Name  Description
   ----  -----------
   KEYS  Recover private keys from memory


msf auxiliary(openssl_heartbleed) >

Also tried SslV3 TLS 1.2 & 1.0 but no success yet...

@gozubuyukoglu
Copy link

additionally I receive response and all of them are 64k

[*] 192.168.200.179:443 - Sending Client Hello...
[!] SSL record #1:
[!]     Type:    22
[!]     Version: 0x0301
[!]     Length:  54
[!]     Handshake #1:
[!]             Length: 50
[!]             Type:   Server Hello (2)
[!]             Server Hello Version:           0x0301
[!]             Server Hello random data:       5376c1523d79ed236718d4c866abaf1a8b469c4c2bae12ea6ef9c2831855e016
[!]             Server Hello Session ID length: 0
[!]             Server Hello Session ID:
[!] SSL record #2:
[!]     Type:    22
[!]     Version: 0x0301
[!]     Length:  631
[!]     Handshake #1:
[!]             Length: 627
[!]             Type:   Certificate Data (11)
[!]             Certificates length: 624
[!]             Data length: 627
[!]             Certificate #1:
[!]                     Certificate #1: Length: 621
[!]                     Certificate #1: #<OpenSSL::X509::Certificate subject=/C=CH/ST=SG/L=Jona/O=Hacking-Lab/OU=Heartbleed/CN=heartbleed.hacking-lab.com, issuer=/C=CH/ST=SG/L=Jona/O=Hacking-Lab/OU=Heartbleed/CN=heartbleed.hacking-lab.com, serial=10767679993171866209, not_before=2014-04-23 10:19:01 UTC, not_after=2015-04-23 10:19:01 UTC>
[!] SSL record #3:
[!]     Type:    22
[!]     Version: 0x0301
[!]     Length:  203
[!]     Handshake #1:
[!]             Length: 199
[!]             Type:   Server Key Exchange (12)
[!] SSL record #4:
[!]     Type:    22
[!]     Version: 0x0301
[!]     Length:  4
[!]     Handshake #1:
[!]             Length: 0
[!]             Type:   Server Hello Done (14)
[*] 192.168.200.179:443 - Sending Heartbeat...
[*] 192.168.200.179:443 - Heartbeat response, 65535 bytes
[*] 192.168.200.179:443 - Sending Client Hello...

@firefart
Copy link
Contributor

firefart commented Jan 9, 2015

@gozubuyukoglu dumping keys can take a while and maybe needs some tries. Some time ago I tried the module against the hacking lab server and it worked. I ran a script in the background to generate some traffic on the server like
while true; do curl -sk 'https://heartbleed.hacking-lab.com/RANDOM_STRING'; sleep 1; done
Just try the DUMP action and if data is returned, the module is working.

@jjarmoc
Copy link
Contributor Author

jjarmoc commented Jan 9, 2015

nod @firefart pretty much nails it. You may need more than the 50 tries you're configured for (like, as many as several thousand) and/or might need to send other requests to the server to get it to move memory around, etc. Finding keys in memory isn't entirely deterministic - it depends on the values being there which requires some degree of stars aligning.

Also, commenting on a close pull for help probably isn't appropriate. You may want to jump in #metasploit on freenode or open an issue or something in the future.

@ghost
Copy link

ghost commented May 1, 2016

I only have the scan option. Instead of
Auxiliary action:

Name Description


KEYS Recover private keys from memory

it gives me SCAN, and no KEYS.

@wvu
Copy link
Contributor

wvu commented May 1, 2016

@varkum19: Please submit an issue if you have a bug report. You are using show options instead of show actions. The former shows the currently selected action, while the latter shows all possible actions.

@ghost
Copy link

ghost commented May 1, 2016

@wvu-r7: thanks! I figured that problem out. Now, when I try "run," it scans for private keys but fails, stating that I should increase max_keytries or heartbeat length. I am not sure what I can do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet