October 16, 2016

Extracting Last Pass Site Credentials From Memory

Extracting LastPass credentials from memory with Volatility

Extracting Last Pass Site Credentials From Memory

Let me start by stating this is not an exploit or a vulnerability in LastPass. This is just extracting any data that may remain in memory during a forensics acquisition. At some point the data must be in clear.

I was reading the Art Of Memory Forensics, (if you don't own this I highly recommend it. ) On one of the pages it was talking about pulling passwords from the browser. When you log in to a website using a standard username and password method you typically make a POST request that includes your username and password. These are sent in clear text. (I'm not talking about SSL here inside the SSL its still clear text.)

the book goes on to describe how to locate these POST requests and extract them. This is useful if you capture a browser that just logged in, but no so much if this is logged in using a session.

As I was reading this I looked at my browser and saw my LastPass plugin icon was showing its normal icon with the number 1. Representing that it has one set of credentials stored for the domain I am currently on.

At some point LastPass has to put the credentials in clear in to the logon fields for the POST request. This makes sense if im on the logon page with auto fill enabled. Thats not always the case. In most cases if the domain is loaded in an active tab regardless of page this prompt is displayed.

I wanted to know if the credentials are unencrypted only when the form fields are present or any time the domain is loaded. So I setup a test environment to see what I could find.

The Setup

I wanted to do all of this in memory which meant I had to find an easy and repeatable way of making changes, looking at memory searching for data.

The obvious method is to create a mem dump every time I make a change. This is made incredibly easy with VM Workstation all I had to do was create a snapshot every time I made a change. Then I had a copy of memory at that point in time that I could compare against any other snapshots I would take

From here the rest of the setup is pretty simple. Install the most common browsers. Chrome, IE and FF. Setup a LastPass account and fill it with some saved sites.

I started with browser plugins and left all the settings on their default settings and used the plugin to generate and store passwords of different lengths. All the passwords used only mixed case alphanum.

I also copied all these passwords in to a document on the Host so I could use them later as described in The Method

After all the passwords were stored in LastPass and copied out to my host I logged out of every account, cleared all the history and temporary internet files then restarted and finally powered of the machine.

The Method

This started pretty simple but started to get quite complex when trying to find the limits but basically it went something like this:

  • Open Browser
  • Log in to LastPass Plugin (If it logged itself out)
  • Log in to Site
  • Check memory for presence of clear text password
  • Change something
  • Close Tab
  • Re Use Tab
  • Log Out
  • Repeat

Test One

I know all the usernames and passwords so this seems like an obvious point to start. I log in to the first site a temporary FaceBook account. Once I'm past the logon page I navigate Facebook for a couple of pages then with the browser tab still open I take my first snapshot.

my first search is a simple one.

grep -a 03rRJaYNMuoXG7hs Win7x64-MemTesting-Snapshot3.vmem | strings > ~/Desktop/fb.txt

-a tells grep to treat my binary mem file as text and the pipe to strings just clears up the output a little before writing the output to a text file.

The resulting text file reveals a fair bit of information.

The two items that jump out immediately are

{
  "reqinfo": {
    "from": "fillfield_A",
    "index": 28,
    "time": 1475875701411,
    "uniqid": 85097549
  },
  "docid": 0,
  "sharedsite": 0,
  "automaticallyFill": 1,
  "is_launch": false,
  "manualfill": false,
  "name": "pass",
  "value": "O3rRJaYNMuoXG7hs",
  "type": "password",
  "checked": false,
  "otherfield": false,
  "doconfirm": 1,
  "allowforce": 0,
  "from_iframe": 0,
  "humanize": false,
  "v2humanize": false,
  "delayquants": 0,
  "force_fillbest": false,
  "originator": "FILL_F4",
  "cmd": "fillfield",
  "clearfilledfieldsonlogoff": 0,
  "dontfillautocompleteoff": 0,
  "realurl": "https://www.facebook.com/",
  "aid": "607245038900433242",
  "tabid": 2,
  "custom_js": "",
  "domains": "facebook.com,facebook.com,messenger.com",
  "formname": "",
  "topurl": "https://www.facebook.com/",
  "urlprefix": "chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/",
  "lplanguage": ""
}

passO3rRJaYNMuoXG7hspassword

JSON object with password, domain name, timestamps and a lot of other information that looks to be related to how form fields are automatically populated which makes sense.

The second one looks a bit out of place. Either way one down more to go .

Test Two

The next snapshot was several domains logged in and their tabs left open following the same process of logging out of all the accounts before shutting down and starting the VM again. I wanted to make sure my memory image was as clean as I could get it each time.

Additionally to make sure that the browser itself wasn't keeping hold of this data I would flush the history of everything.

You can see in the screenshot all the tabs are loaded and logged in with the exception of QNAP. I went to the main page where you can see the LastPass plugin shows me it has a matching set of credentials, but I have not yet loaded a page that has the form fields so nothing has been 'Auto Filled'.

With the snapshot saved I used the same grep commands, for all the domains I had loaded in to the open tabs.

All the domains I had logged in to all had the same data structure sat in memory.

{
  "reqinfo": {
    "from": "fillfield_A",
    "index": 157,
    "time": 1475878291419,
    "uniqid": 65765520
  },
  "docid": 0,
  "sharedsite": 0,
  "automaticallyFill": 1,
  "is_launch": false,
  "manualfill": false,
  "name": "ca414a13646af9ceb5293a5eeded1704",
  "value": "5DAhhkOvZDTC0MYA14",
  "type": "password",
  "checked": false,
  "otherfield": false,
  "doconfirm": 1,
  "allowforce": 0,
  "from_iframe": 0,
  "humanize": false,
  "v2humanize": false,
  "delayquants": 0,
  "force_fillbest": false,
  "originator": "FILL_F4",
  "cmd": "fillfield",
  "clearfilledfieldsonlogoff": 0,
  "dontfillautocompleteoff": 0,
  "realurl": "http://androidforums.com/login/login/register",
  "aid": "5988558277865511241",
  "tabid": 14,
  "custom_js": "",
  "domains": "androidforums.com",
  "formname": "",
  "topurl": "http://androidforums.com/",
  "urlprefix": "chrome-extension://hdokiejnpimakedhajhdlcegeplioahd/",
  "lplanguage": ""
}

I also noticed a couple of other data structures

{
  "cmd": "save",
  "url": "https://www.phpbb.com/community/ucp.php?mode=login",
  "formdata": "login\tusername\tpeters.lastpass%40gmail.com\ttext\tseen\nlogin\tpassword\tSG5P2GRgqYeL4nvzi8C1XnZs\tpassword\tseen\nlogin\tautologin\ton-0\tcheckbox\tseen\nlogin\tviewonline\ton-0\tcheckbox\tseen\nlogin\tredirect\t.%2Fucp.php%3Fmode%3Dlogin\thidden\tnotseen\nlogin\tsid\t32ff2e6ecf53aaac43b88f123ad86b04\thidden\tnotseen\nlogin\tredirect\tindex.php\thidden\tnotseen\n0\taction\thttps%3A%2F%2Fwww.phpbb.com%2Fcommunity%2Fucp.php%3Fmode%3Dlogin\taction\n0\tmethod\tpost\tmethod\n",
  "current_pw_field_name": "",
  "docnum": 0,
  "timestamp": 1475878195546,
  "username": "peters.lastpass@gmail.com",
  "password": "SG5P2GRgqYeL4nvzi8C1XnZs",
  "tld": "phpbb.com"
}

This one had the username, domain name and the password all in clear text.

The QNAP Site which was loaded but had not yet been filled in was not present in memory.

While i was searching though memory trying to identify other artifacts i came across an interesting piece of information.

A Piece of data labeled as LastPassPrivateKey. I am still in the process of figuring out if I can use this to get either the master password or to decrypt the vault file if we can find that either on disk or in memory.

While I was considering how I might be able to use this PrivateKey it dawned on me that I had not yet looked to see if the master password itself existed in memory. I just assumed to was wiped once the crypt had been decrypted.

Using grep I actually found the username and password in clear text. But only in one of my snapshots.

The next thing I tried was a set of variations with open tabs, closed tabs, reused tabs the conclusion I came to is that if a tab is open on the domain and has been logged in then credentials can in most cases be found.

As tabs are reused with other domains, opened and closed it becomes more difficult to find intact data structures. The information is still in memory and if you know the value you can find it but without a structure its impossible to find blind.

At this point i had enough data to start automating the process of extracting these credentials from a memory image using a Volatility plugin.

A couple of years ago Brian Baskin published a post where he created a Volatility plugin that uses yara rules to search process memory and extract the data it finds there. This was exactly what I need to use as the base of my plugin. You can read his original post here.

From there it was a simple matter of some regex yara rules and some post processing of the data to collate the results.

When you run it against a memory image you should get something similar to:

localadmin@tech-server:~$ vol.py --plugins=/home/localadmin/github/volatility_plugins/lastpass --profile=Win7SP1x86 -f /home/localadmin/Desktop/lastpass-mem.vmem lastpass
Volatility Foundation Volatility Framework 2.5
Searching for LastPass Signatures
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3400)
Found pattern in Process: chrome.exe (3840)
Found pattern in Process: chrome.exe (3840)
Found pattern in Process: chrome.exe (3840)
Found pattern in Process: chrome.exe (3840)
Found pattern in Process: chrome.exe (3912)
Found pattern in Process: chrome.exe (3912)
Found pattern in Process: chrome.exe (3912)
Found pattern in Process: chrome.exe (3912)
Found pattern in Process: chrome.exe (3912)
Found pattern in Process: chrome.exe (3912)
Found pattern in Process: chrome.exe (4092)
Found pattern in Process: chrome.exe (4092)
Found pattern in Process: chrome.exe (4092)
Found pattern in Process: chrome.exe (4092)
Found pattern in Process: chrome.exe (2036)
Found pattern in Process: chrome.exe (2036)
Found pattern in Process: chrome.exe (2036)

Found LastPass Entry for hackforums.net
UserName: peters.lastpass
Pasword: jRvTpQoTHS4OTcl

Found LastPass Entry for facebook.com
UserName: peters.lastpass@gmail.com
Pasword: Unknown

Found LastPass Entry for sainsburys.co.uk
UserName: peters.lastpass@gmail.com
Pasword: mt5JwaPctWFzBj

Found LastPass Entry for leakforums.net
UserName: peterslastpass
Pasword: rmH61HSabrVJ9a2

Found LastPass Entry for facebook.com,facebook.com,messenger.com
UserName: Unknown
Pasword: O3rRJaYNMuoXG7hs


Found Private Key
LastPassPrivateKey<308204BD020100300D06092A864886F70D0101010500048204A7308204A30201000282010100D0D534BEA030F199144DD4B1B0A69D6462BC13CF074B77CDEC0B4C06D3773B39F0D3353D58732D35809E2A45C9A70B94C366DE4E8B591178F5366A4328C96A82A51E8B1573A9546F859EA6C13EB1E08F1F69749598429244B96AFCAE0787CFC4CC19311D80F90679CE4C395FDBF22F9201381E0AEC345C724E1E61CE8EEE0A37EE38B04D5EEA1AB2562D815242E4D6379D23940ACD800921853787F0F1B37F249DE284780CE1D1FFF10FFCE778CD03A442C7A487C47A27D4F11EE98CF5E8B2AA8A7DEE0710B9C2D430CB33EC747E37298E16103493C6DF8A539F4893F30CCFA74D84E5FC40E1ED39316EA038D16748F58AE873704DD61B028940ECB083E7F1790940D4BD2A01C73DBA4AC26A2BB98CE7A2CDF02011102820100624636F04B62539354D955085321324102818100D05799FCC7514DBFC0DEC6E06E2A8715E9E46911DBBFCC59F1569A82930FDBD195A685C1BF13EABB75B46CC8484EB3771E102E76CE3D3756CEF13666C6581861EE23EC11ABB658BA2F815EF2D406FDD85830F6B599443004CEA4B1A79DAAAAEE86755FE354498C770EF3BE2077DD19EE3E7E53A9935FC0D76BD90D3B887F50575FA01A617A43F5D2C44815098207299381229DDA8F91713B7012D8F29A087A3918A2F76A222BA4202E8A0997D63D1EEF02F246DC0A5C0AC869191B9231DCD6D657FB4E6591DDFAF3026522F84E2D1EF2D5C05289EFF9D7E2F2A722374E0204C8FAA326024DF520B97505146AFB1AF7469B862977B1152430911BFA112E76A51C352D7F1B2C39669B4CF102818100FCFCA8B2F2074C1FB357A859AA583651D5DC9EA0446E0B33A7B41D9B7C9955832BEFB4E2151D17DDB851A1F46B16E26ECC6515BCB1C802DD73DA4ACD89083168E2678DE363EF1B45BAA1BA40F845D8396AFC269503F9A4F04D39271F02819A665D47036F0CA3628D78987102818100CEFC910EF956B3590A9A0907F59EB44CD25FF10032DAFE48C359057F75FBA5AF1CC1C6E11E37CF4F825D0E1540B5DA77FF6777FEE55621C1D0EF85D3C12702150D542A90CC8021FA132EF383835DA4358A0781E168897C779F2DA6A834DA20DBFDA4F643738B4DF6BBDD768947D9EC577466E18100D351EC8A77C7582E0A78C5ACA9D86068BB82D721B0841962F959A25A01FA80FFB765DE228798>LastPassPrivateKey

Found Private Key
LastPassPrivateKey<\x00\xb2\x88\x10\x02N;$\x02&\x00\x00\x00>LastPassPrivateKey
localadmin@tech-server:~$

I have more work to do before I mark this as complete but in the mean time you can find the plugin on my github https://github.com/kevthehermit/volatility_plugins/tree/master/lastpass