본문 바로가기

malware

Trolling Memory for Credit Cards in POS / PCI Environments

728x90

In a recent penetration test, I was able to parlay a network oversight into access to a point of sale terminal.  Given the discussions these days, the next step for me was an obvious one - memory analysis.

My first step was to drive to the store I had compromised and purchase an item.

I'm not a memory analysis guru, but the memory capture and analysis was surprisingly easy.  First, dump memory:
dumpit
Yup, it's that simple, I had the dumpit executable locally by that point (more info here https://isc.sans.edu/diary/Acquiring+Memory+Images+with+Dumpit/17216)
or, if you don't have keyboard access (dumpit requires a physical "enter" key, I/O redirection won't work for this):
win32dd /f memdump.img
(from the SANS Forensics Cheat Sheet at https://blogs.sans.org/computer-forensics/files/2012/04/Memory-Forensics-Cheat-Sheet-v1_2.pdf )

Next, I'll dig for my credit card number specifically:

strings memdump.img | grep [mycardnumbergoeshere] | wc -l
     171

Yup, that's 171 occurences in memory, unencrypted.  So far, we're still PCI complaint - PCI 2.0 doesn't mention cardholder data in memory, and 3.0 only mentions it in passing.  The PCI standard mainly cares about data at rest - which to most auditors means "on disk or in database", or data in transit - which means on the wire, capturable by tcpdump or wireshark.  Anything in memory, no matter how much of a target in today's malware landscape, is not an impact on PCI compliance.

The search above was done in windows, using strings from SysInternals - by default this detects strings in both ASCII and Unicode.  If I repeat this in linux (which by default is ASCII only), the results change:
strings memdump.img | grep [mycardnumbergoeshere] | wc -l
     32

To get the rest of the occurences, I also need to search for the Unicode representations,  which "strings" calls out as "little-endian" numbers:
strings -el memdump.img | grep [mycardnumbergoeshere] | wc -l
     139

Which gives me the same total of 171.

Back over to windows, let's dig a little deeper - how about my CC number and my name tied together?
strings memdump.img | grep [myccnumbergoeshere] | grep -i vandenbrink | wc -l
     1

or my CC number plus my PIN  (we're CHIP+PIN in Canada)
strings memdump.img | grep [mycardnumbergoeshere] | grep [myPINnumber]
     12

Why exactly the POS needs my PIN is beyond me!

Next, let's search this image for a number of *other* credit cards - rather than dig by number, I'll search for issuer name so there's no mistake.  These searches are all using the Sysinternals "strings" since the defaults for that command lend itself better to our search:

CAPITAL ONE       85
VISA             565
MASTERCARD      1335
AMERICAN EXPRESS  20

and for kicks, I also searched for debit card prefixes (I only search for a couple with longer IIN numbers):
Bank of Montreal   500766     245
TD CAnada Trust    589297    165

Looking for my number + my CC issuer in the same line gives me:
strings memdump.img | grep [myccnumbergoeshere] | grep [MASTERCARD] | wc -l
gives me a result of "5"

So, assuming that this holds true for others (it might not, even though the patterns are all divisible by 5), this POS terminal has hundreds, but more likely thousands of valid numbers in memory, along with names, PIN numbers and other informaiton

Finally, looking for a full magstripe in memory:

The search for a full stripe:
grep -aoE "(((%?[Bb]?)[0-9]{13,19}\^[A-Za-z\s]{0,26}\/[A-Za-z\s]{0,26}\^(1[2-9]|2[0-9])(0[1-9]|1[0-2])[0-9\s]{3,50}\?)[;\s]{1,3}([0-9]{13,19}=(1[2-9]|2[0-9])(0[1-9]|1[0-2])[0-9]{3,50}\?))" memdump.img  | wc -l
    0

where:

    -a = Processes a binary file as text
    -o = Shows only the matched text
    -E = Treats the pattern as an extended regular expression

or using this regex to find Track strings only:

((%?[Bb]?)[0-9]{13,19}\^[A-Za-z\s]{0,26}\/[A-Za-z\s]{0,26}\^(1[2-9]|2[0-9])(0[1-9]|1[0-2])[0-9\s]{3,50}\?)
gives us 0 results.

or this regex to find Track 2 strings only:

([0-9]{13,19}=(1[2-9]|2[0-9])(0[1-9]|1[0-2])[0-9]{3,50}\?)  
Gives us 162  (I'm not sure how much I trust this number)

Anyway, what this tells me is that this store isn't seeing very many folks swipe their cards, it's all CHIP+PIN (which you'd expect)

(Thanks to the folks at bromium for the original regular expressions and breakdown:http://labs.bromium.com/2014/01/13/understanding-malware-targeting-point-of-sale-systems/)

Getting system uptime (from the system itself) wraps up this simple analysis - the point of this being "how long does it take to collect this much info?"

net statistics server | find "since""
shows us that we had been up for just under 4 days.

Other ways to find uptime?
from the CLI:
systeminfo " find "Boot Time"
or, in powershell:
PS C:\> Get-WmiObject win32_operatingsystem | select csname, @{LABEL='LastBootUpTime';EXPRESSION={$_.ConverttoDateTime($_.lastbootuptime)}}
or, in wmic:
wmic get os last bootuptime
or, if you have sysinternals available, you can just run "uptime"


What does this mean for folks concerned with PCI compliance?
Today, not so much.  Lots of environments are still operating under PCI 2.0.  PCI 3.0 simply calls for education on the topic of good coding practices to combat memory scraping.  Requirement 6.5 phrases this as "Train developers in secure coding techniques, including how to avoid common coding vulnerabilities, and understanding how sensitive data is handled in memory.  Develop applications based on secure coding guidelines."

Personally (and this is just my opinion), I would expect/hope that the next version of PCI will call out encryption of card and personal information in memory specifically as a requirement.  If things play out that way, What this will mean to the industry is that either:
a/ folks will need to move to card readers that encrypt before the information is on the POS terminal
or
b/ if they are using this info to collect sales / demographic information, they might instead tokenize the CC data for the database, and scrub it from memory immediately after.  All  I can say to that approach is "good luck".  Memory management is usually abstracted from the programming language, so I'm not sure how successful you'd be in trying to scrub artifacts of this type from memory.

728x90