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
base: master
from

Conversation

Projects
None yet
@jjarmoc
Contributor

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 #3261

todb-r7 and others added some commits Apr 10, 2014

Allow vhost to be maybe opts['rhost']
This enables passing rhost and rport directly to send_request_cgi
without having to monkey with the datastore.

See #8498
Update wol.rb to specify rhost/rport directly
 - [ ] 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
Include a vhost for Shodan or else it complains
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
==========
````
Add custom rhost/rport, remove editorializing desc
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.
Replace RHOST reassing with just host
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]
Two more java payloads that wanted to write RHOST
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]
Dont report creds
We dont know if a DOMAIN or IP is specified etc.
Merge remote-tracking branch 'upstream/master' into netapi_change_passwd
Conflicts:
	lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb
First little bit at Bug 8498
[FixRM #8489] rhost/rport modification
@FireFart

This comment has been minimized.

Show comment
Hide comment
@FireFart

FireFart Apr 16, 2014

Contributor

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

Contributor

FireFart commented Apr 16, 2014

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

@jjarmoc

This comment has been minimized.

Show comment
Hide comment
@jjarmoc

jjarmoc Apr 16, 2014

Contributor

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...

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@jlee-r7

jlee-r7 Apr 16, 2014

Contributor

Your MODE option sounds like it should be an Action.

Contributor

jlee-r7 commented Apr 16, 2014

Your MODE option sounds like it should be an Action.

@julianvilas

This comment has been minimized.

Show comment
Hide comment
@julianvilas

julianvilas Apr 16, 2014

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).

Contributor

julianvilas commented Apr 16, 2014

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

This comment has been minimized.

Show comment
Hide comment
@FireFart

FireFart Apr 17, 2014

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

Contributor

FireFart commented Apr 17, 2014

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

@todb-r7 todb-r7 added the heartbleed label Apr 17, 2014

@jlee-r7

This comment has been minimized.

Show comment
Hide comment
@jlee-r7

jlee-r7 Apr 17, 2014

Contributor

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

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

This comment has been minimized.

Show comment
Hide comment
@jlee-r7

jlee-r7 Apr 17, 2014

Contributor

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.

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

This comment has been minimized.

Show comment
Hide comment
@jjarmoc

jjarmoc Apr 17, 2014

Contributor

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.

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@FireFart

FireFart Apr 17, 2014

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?

Contributor

FireFart commented Apr 17, 2014

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

This comment has been minimized.

Show comment
Hide comment
@jjarmoc

jjarmoc Apr 17, 2014

Contributor

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.)

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@kernelsmith

kernelsmith Apr 17, 2014

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.

Contributor

kernelsmith commented Apr 17, 2014

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

This comment has been minimized.

Show comment
Hide comment
@kernelsmith

kernelsmith Apr 17, 2014

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.

Contributor

kernelsmith commented Apr 17, 2014

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 some commits Apr 16, 2014

Adds RSA key extraction via Heartbleed
Lots of refactoring too.
Use 'Actions' instead of 'MODE' Optenum
Also changes the TLS_CALLBACKS Enum, per egyp7's request.
resolving merge conflicts (I hope)
Conflicts:
	modules/auxiliary/scanner/ssl/openssl_heartbleed.rb
@jjarmoc

This comment has been minimized.

Show comment
Hide comment
@jjarmoc

jjarmoc Apr 17, 2014

Contributor

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?

Contributor

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

This comment has been minimized.

Show comment
Hide comment
@FireFart

FireFart Apr 17, 2014

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.

Contributor

FireFart commented Apr 17, 2014

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

This comment has been minimized.

Show comment
Hide comment
@jjarmoc

jjarmoc Apr 17, 2014

Contributor

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

Contributor

jjarmoc commented Apr 17, 2014

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

@FireFart

This comment has been minimized.

Show comment
Hide comment
@FireFart

FireFart Apr 17, 2014

Contributor

Closing, see other PR for ongoing discussion and merge

Contributor

FireFart commented Apr 17, 2014

Closing, see other PR for ongoing discussion and merge

@FireFart FireFart closed this Apr 17, 2014

@gozubuyukoglu

This comment has been minimized.

Show comment
Hide comment
@gozubuyukoglu

gozubuyukoglu Jan 9, 2015

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 commented Jan 9, 2015

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

This comment has been minimized.

Show comment
Hide comment
@gozubuyukoglu

gozubuyukoglu Jan 9, 2015

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...

gozubuyukoglu commented Jan 9, 2015

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

This comment has been minimized.

Show comment
Hide comment
@FireFart

FireFart Jan 9, 2015

Contributor

@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.

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

This comment has been minimized.

Show comment
Hide comment
@jjarmoc

jjarmoc Jan 9, 2015

Contributor

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.

Contributor

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.

@varkum19

This comment has been minimized.

Show comment
Hide comment
@varkum19

varkum19 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.

varkum19 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-r7

This comment has been minimized.

Show comment
Hide comment
@wvu-r7

wvu-r7 May 1, 2016

Contributor

@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.

Contributor

wvu-r7 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.

@varkum19

This comment has been minimized.

Show comment
Hide comment
@varkum19

varkum19 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.

varkum19 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