Friday, November 20, 2015

When read-only is too much -- how to obtain programmatic access to EMC RSA Data Protection Manager crypto keys.

The audience for this post is quite limited - not many folks (relatively) use EMC's RSA DPM product. I'm gonna make NO effort to really describe how the RSA DPM system works. If you know the system, this will all make sense. If you do not, it probably won't though the general color and ideas should resonate. In addition, what I'm about to show is a known behavior -- the developer guides for the RSA DPM (at least for java) recommend the steps necessary to mitigate what I'm showing here. The c# and c++ guides make no mention of this that I could find, but the same behavior exists on those frameworks as well. What's not really called out clearly is what the risk entails.

The RSA DPM is a cryptography and key management solution for enterprises. The server-side manages cryptography keys, and clients obtain the keys when needed to perform crypto operations locally on the client. The clients do this by importing the necessary library files, and "registering" themselves with the server. The registration process essentially sets up a certificate-based authentication mechanism between client and server. Once registration is completed, a certificate and some "fingerprint" files are generated. At run-time, the client requests keys from the server over the authenticated channel using the certificate. The "fingerprint" files do some magic to ensure that the client system has not been changed in some way that might indicate compromise or movement of the client.

Here's the "weakness". If the "bad-guy" has read-only access to these registration files, it is a TRIVIAL matter to copy the files to another location on the same system and use the files to make perfectly valid calls to the RSA DPM server via a small custom application, accessing all the crypto keys that should (by intent) only be accessible to the orignal client application.

The example below is in java. You can do the same for c# or c++. The idea/approach is equally valid there. Our outline of activity:
1) Copy RSA DPM registration files to a new location on the original client that is write-able by the user.
2) Use a custom application making reference to these copied registration files to pull crypto keys from the RSA DPM server. This is NOT hard -- the RSA DPM client modules come pre-packaged with sample applications to help developers learn how to use the client. We'll just use one of these sample programs.
3) ??? Well, you now have crypto keys. That's not good for whoever the keys belong to. How you approach mitigating this is up to you and probably depends on how your business operates. It should be NO surprise that the "fix" for this problem is:
4) Lock down read access to the original client registration files. Only the application using these files needs access to them, so nothing/no one else should be able to read them. Basic security principles here. Lock. Down. Access.

Here is the process and minimal code samples that walk through the steps to access crypto keys. Whatever system you are on will almost certainly differ in the details, primarily paths and file names. But this should be enough to get the idea across. We're going to be demonstrating this on a *nix system running WebSphere that has an app incorporating the EMC RSA DPM. This assumes (as I've described above) that the bad guy has read access to the application files, and write/execute access to some path, somewhere, on the same server.

1) Copy all the "registration files" to a new writeable location. These files are (assuming the app developers follow the nomenclature of the RSA DPM examples).
*.p12
*.bin
*.cache
Optionally, copy the RSA DPM client jar files, too. You can always just reference them in their existing location when you run your app later to extract keys.

2) Do some environment setup. Nothing fancy here, we're essentially just setting ourselves up for running java
export Java_Home=/apps/websphere/java/jre
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/apps/websphere/java/jre/bin:/apps/websphere/java/jre/bin/classic:/apps/websphere/java/jre/bin
export CLASSPATH=/home/usr/me/rsadpm/*.*

3) Execute one of the sample RSA DPM client apps, specifying the copied registration files.  Note that I'm being a little obtuse here and NOT showing the small modifications made to the demo EncryptAndDecryptData code -- the changes from how this file is delivered from EMC are minor. Just change the path references, and update the the config file to reference the files you copied in step 1.
/apps/websphere/java/jre/bin/java -cp /home/usr/me/rsadpm:/home/usr/me/rsadpm/certj.jar:/home/usr/me/rsadpm/cryptoj.jar:/home/usr/me/rsadpm/cryptojFIPS.jar:/home/usr/me/rsadpm/kmsclient.jar:/home/usr/me/rsadpm/LB.jar:/home/usr/me/rsadpm/LBImpl.jar:/home/usr/me/rsadpm/log4j-1.2.15.jar:/home/usr/me/rsadpm/sslj.jar rkmjc.simpleapi.EncryptAndDecryptData

 4) Here's what output looks like. In this, I'm simply encrypting/decrypting a hard-coded value using a key obtained from the server. It'd be just as easy to export the key to file or do whatever else you wanted.

Running Sample EncryptAndDecryptData
Plain Text:
  0000: 31 32 33 34 31 32 33 34 31 32 33 34 31 32 33 34 [1234123412341234]

Cipher Text:
  0000: 52 4b 4d 43 32 31 30 00 ff ff ff ff 00 00 00 05 [RKMC210.........]
  0010: 75 75 69 64 00 00 00 00 10 16 68 f2 8c 96 ca 4a [uuid......h....J]
  0020: 86 9c f4 f0 83 30 f6 9a db ff ff ff ff 00 00 00 [.....0..........]
  0030: 05 6f 72 69 64 00 00 00 00 20 0b 76 58 dc 76 07 [.orid.... .vX.v.]
  0040: 59 7e c5 9d f3 ba 66 cd f9 20 a2 fb 82 8f fd 2b [Y~....f.. .....+]
  0050: 88 fe 1b 05 2e a8 a2 6c 63 2b ff ff ff ff 00 00 [.......lc+......]
  0060: 00 05 63 73 75 6d 00 00 00 00 20 9f e4 86 dd 57 [..csum.... ....W]
  0070: 2a 72 fa 9f cb 08 e6 d5 e8 79 14 89 3d a9 05 17 [*r.......y..=...]
  0080: bd 03 94 b7 ea 9d 73 8a 07 3e f8 ce d8 18 71 e8 [......s..>....q.]
  0090: 69 c3 02 16 90 65 56 ff 28 8f 29 d8 c3 63 85 6c [i....eV.(.)..c.l]
  00a0: 30 1a c9 72 7a b3 15 d8 fe c6 5c                [0..rz.....\     ]

Recovered Plain Text:
  0000: 31 32 33 34 31 32 33 34 31 32 33 34 31 32 33 34 [1234123412341234]

Successful Ending EncryptAndDecryptData


So, what's the takeaway here? If you are using the EMC RSA DPM client, please be sure to lock down access to the DPM files -- NOTHING should be able to get to them that doesn't absolutely need them. Otherwise you open yourself to exfiltration of keys.


Google Authenticator One Time Passwords with PowerShell

I like two-factor authentication. Hopefully you do, too. Google Authenticator's OTP is a very popular app for using 2FA. The code below implements one-time passwords and is entirely interoperable with Google Authenticator. Details for use are in the function headers, and I should point out here that the bulk of the smarts in this code are simply translated from a javascript implementation I found online (and credit in the code). Why would you want this in PowerShell? No idea! But isn't in fun to know/see how this stuff works?




  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
<#
.SYNOPSIS

 Implementation of the Time-based One-time Password Algorithm used by Google Authenticator.



.DESCRIPTION

 As described in http://tools.ietf.org/id/draft-mraihi-totp-timebased-06.html, the script generates a one-time password based on a shared secret key and time value.

 This script generates output identical to that of the Google Authenticator application, but is NOT INTENDED FOR PRODUCTION USE as no effort has been made to code securely or protect the key. For demonstration-use only.

 Script code is essentially a transation of a javascript implementation found at http://jsfiddle.net/russau/uRCTk/

 Output is a PSObject that includes the generated OTP, the values of intermediate calculations, and a URL leading to a QR code that can be used to generate a corresponding OTP in Google Authenticator applications.
 
 The generated QR code contains a URL that takes the format "otpauth://totp/<email_address_here>?secret=<secret_here>", for example: otpauth://totp/tester@test.com?secret=JBSWY3DPEHPK3PXP

 The generated OTP is (obviously) time-based, so this script outptu will only match Google Authenticator output if the clocks on both systems are (nearly) in sync.

 The acceptable alphabet of a base32 string is ABCDEFGHIJKLMNOPQRSTUVWXYZ234567.

 Virtually no parm checking is done in this script. Caveat Emptor.



.PARAMETER sharedSecretKey

 A random, base32 string shared by both the challenge and reponse side of the autheticating pair. This script mandates a string length of 16.



.EXAMPLE

 .\Get-OTP.ps1 -sharedSecret "JBSWY3DPEHPK3PXP" | Select SharedSecret, Key, Time, HMAC, URL, OTP


.NOTES
  
 FileName: Get-OTP.ps1
 Author: Jim Nelson nelsondev1

#>
[CmdletBinding()]
param
(
 [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
 [ValidateLength(16,16)]
 [string]$sharedSecret
)



#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Converts the supplied Int64 value to hexadecimal.
#------------------------------------------------------------------------------
function Convert-DecimalToHex($in)
{
 return ([String]("{0:x}" -f [Int64]$in)).ToUpper()
}

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Converts the supplied hexadecimal value Int64.
#------------------------------------------------------------------------------
function Convert-HexToDecimal($in)
{
 return [Convert]::ToInt64($in,16)
}

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Converts the supplied hexadecimal string to a byte array.
#------------------------------------------------------------------------------
function Convert-HexStringToByteArray($String)
{
 return $String -split '([A-F0-9]{2})' | foreach-object { if ($_) {[System.Convert]::ToByte($_,16)}}
}

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Converts the supplied base32 string to a hexadecimal string
#------------------------------------------------------------------------------
function Convert-Base32ToHex([String]$base32)
{
 $base32 = $base32.ToUpper()
 $base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
 $bits = ""
 $hex = ""

 # convert char-by-char of input into 5-bit chunks of binary
 foreach ($char in $base32.ToCharArray())
 {
  $tmp = $base32chars.IndexOf($char)
  $bits = $bits + (([Convert]::ToString($tmp,2))).PadLeft(5,"0")
 }
 
 # leftpad bits with 0 until length is a multiple of 4
 while ($bits.Length % 4 -ne 0)
 {
  $bits = "0" + $bits
 }
 
 # convert binary chunks of 4 into hex
 for (($tmp = $bits.Length -4); $tmp -ge 0; $tmp = $tmp - 4)
 {
  $chunk = $bits.Substring($tmp, 4);
  $dec = [Convert]::ToInt32($chunk,2)
  $h = Convert-DecimalToHex $dec
  $hex = $h + $hex  
 }

 return $hex
}


#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Get the currentUnix epoch (div 30) in hex, left-padded with 0 to 16 chars
#------------------------------------------------------------------------------
function Get-EpochHex()
{
 # this line from http://shafiqissani.wordpress.com/2010/09/30/how-to-get-the-current-epoch-time-unix-timestamp/
 $unixEpoch = ([DateTime]::Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000
 $h = Convert-DecimalToHex ([Math]::Floor($unixEpoch / 30))
 return $h.PadLeft(16,"0")
}

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Get the HMAC signature for the supplied key and time values.
#------------------------------------------------------------------------------
function Get-HMAC($key, $time)
{
 $hashAlgorithm = New-Object System.Security.Cryptography.HMACSHA1
 $hashAlgorithm.key = Convert-HexStringToByteArray $key
 $signature = $hashAlgorithm.ComputeHash((Convert-HexStringToByteArray $time))
 $result = [string]::join("", ($signature | % {([int]$_).toString('x2')}))
 $result = $result.ToUpper()
 return $result
}

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Get the OTP based on the supplied HMAC
#------------------------------------------------------------------------------
function Get-OTPFromHMAC($hmac)
{
 $offset = Convert-HexToDecimal($hmac.Substring($hmac.Length -1))
 $p1 = Convert-HexToDecimal($hmac.Substring($offset*2,8))
 $p2 = Convert-HexToDecimal("7fffffff")
 [string]$otp = $p1 -band $p2
 $otp =  $otp.Substring($otp.Length - 6, 6)
 return $otp
}

# -------------------------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------------------------
# MAIN PROGRAM
# -------------------------------------------------------------------------------------------------------
# -------------------------------------------------------------------------------------------------------
$reportObject = New-Object PSObject Property @{'SharedSecret' = "";
                                      'Key'   = "";
            'Time'   = "";
            'HMAC'   = "";
            'OTP'   = "";
            'URL'   = "";
                                     }

#google can generate a QR code of the secret for their authenticator app at this url...
$email = "look@that.com"
$url = "https://chart.googleapis.com/chart?chs=200x200&cht=qr&chl=200x200&chld=M|0&cht=qr&chl=otpauth://totp/" + $email + "%3Fsecret%3D" + $sharedSecret

$key  = Convert-Base32ToHex $sharedSecret
$time = Get-EpochHex
$hmac = Get-HMAC $key $time
$otp  = Get-OTPFromHMAC $hmac

$reportObject.SharedSecret = $sharedSecret
$reportObject.Key = $key
$reportObject.Time = $time
$reportObject.HMAC = $hmac
$reportObject.OTP = $otp
$reportObject.URL = $url

$reportObject

Encode and Decode WebSphere passwords with PowerShell

Sometimes you need to give WebSphere passwords. Maybe it's for database connections, or to open truststores or keystores etc. It's no secret where WebSphere keeps these passwords, nor is it a secret how WebSphere encodes the passwords prior to storage. It's simple XOR encoding. Lots of folks have posted functions to encode/decode this data, and there are forms online where you can do the conversion in your browser.  I've just never seen one in PowerShell, so here ya go. Why would you need this? No idea. I guess it's always nice to see how things work...so there's that. Code is below.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
function Unprotect-WebSpherePassword {
################################################################
#.Synopsis
# Decodes the provided encoded password from a WebSphere 
# configuration or properties file, returning the plain text
# password. The encoded password may be provided with or
# without the leading {xor} string.
#.Parameter String
# Encoded password
#.Example
# Unprotect-WebSpherePassword "{xor}LDo8LTor"
# secret
################################################################
[CmdletBinding()] Param (
 [Parameter(Mandatory = $True, ValueFromPipeline = $True)] [string]$encodedPassword
)
 $encodedPassword = $encodedPassword.Replace("{xor}","")
 [byte[]]$bytes = [Convert]::FromBase64String($encodedPassword)
 for ($i=0;$i -lt $bytes.Length;$i++)
 {
  $bytes[$i] = $bytes[$i] -bxor 0x5F
 }
 [string]$decoded = [System.Text.Encoding]::ASCII.GetString($bytes)
 return $decoded
}

function Protect-WebSpherePassword {
################################################################
#.Synopsis
# Encodes the provided plain text password to a WebSphere 
# encoded password suitable for inclusion in configuration
# or properties files. The encoded password will include the 
# required {xor} prefix.
#.Parameter String
# Plaintext password
#.Example
# Protect-WebSpherePassword secret
# "{xor}LDo8LTor"
################################################################
[CmdletBinding()] Param (
 [Parameter(Mandatory = $True, ValueFromPipeline = $True)] [string]$plainPassword
)
 [byte[]]$bytes = [System.Text.Encoding]::ASCII.GetBytes($plainPassword)
 for ($i=0;$i -lt $bytes.Length;$i++)
 {
  $bytes[$i] = $bytes[$i] -bxor 0x5F
 }
 [string]$encoded = [Convert]::ToBase64String($bytes)
 $encoded = "{xor}" + $encoded
 return $encoded
}