PeopleSoft Token
I came upon an article concerning the PS_TOKEN vulnerability where an attacker can brute force the SHA1 signature value in order to obtain the node password for PeopleSoft systems.
Since my university uses PeopleSoft, I took a deeper look into how PS_TOKEN is generated and parsed.
Getting PS_TOKEN
A google search can turn up a few hundred sites running PeopleSoft. Just google for:
peoplesoft inurl:cmd=login
PeopleSoft's web application URLs look something similar to:
http://people-soft.leung.xyz/psp/SiteName
Requesting for a specific page such as the password reset page will usually cause a new PS_TOKEN to be generated.
http://people-soft.leung.xyz/psp/SiteName/EMPLOYEE/HRMS/c/JHR_SECURITY.JHR_FORGOT_PSWD.GBL
Cookie Format
The PS_TOKEN cookie is encoded using base64. The data structure I was able to figure out looks like this:
0 : Int32 - length of the entire token
4 : Int32 - Some magic number
??? - unknown fields
20 : Int32 Header Length
24 : 00 padding?
String - Header string (Shdr)
String - Unknown (N)
00 padding? - 1 byte
String - Version (8.10)
String - SHA1 Signature, 20 bytes as hex
Int32 - Data Section Length
00 padding? - 1 byte
String - Data Header (Sdata)
String - Data Value (zlib data. String values starting at 20 bytes in)
The endianness of the values differ based on the hardware PeopleSoft is running. A good chunk of the servers I checked returned values in big endian which implies that they are hosted on a non-intel based server, most likely Sun/Oracle's SPARC machines.
Parsing one of the cookies I captured shows the following contents I was able to read.
Array
(
[endian] => Little
[length] => 167
[magic1] => 16909060
[headerLength] => 44
[headerString] => Shdr
[unknownField] => N
[version] => 8.10
[signature] => d6ad9e49c2822b3b8df7b874ed13ba5e4ecb
[dataLength] => 103
[dataHeader] => Sdata
[data] => Array
(
[user] => APPLICANT
[lang] => ENG
[server] => PSFT_HR
[datetime] => 2015-09-26-22.46.33.000660
)
[prefix] => APPLICANTENG2015-09-26-22.46.33.000660PSFT_HR
)
A Simple Parser
Here's a parser which I wrote in PHP. It will correctly detect the endianness of the values and read the binary properly. It also checks the hash against some default passwords.
<phorkie>https://leo.leung.xyz/phorkie/11/embed</phorkie>
Signature Generation
According to Oracle's documentation, the SHA1 signature is generated using:
SHA1_Hash ( UserID + Lang + Date Time issued + Issuing System + Local Node Pswd )
Basically, the first 4 values which are given as part of the PS_TOKEN value (acting as the salt) is hashed with the node password in order to create a signature. This is then used on subsequent requests to the server to verify the person's identity. Since the salt and the hash is known, an offline brute force attack can be done in order to find the node password.
Cracking The Node Password
Since I can't figure out how to get John to crack a SHA1 hash with a prefixed string, I modified the parser script I wrote in PHP to read passwords through stdin to test.
What I'm unsure at this point is how PeopleSoft hashes the values to generate the signature. The string values I read from the PS_TOKEN cookie appears to have null values inserted between each ASCII character which almost seems to hint that the string is encoded in UTF-8.
At the moment, I hash the guess three times: Once with the null bytes before each ASCII character, one after each ASCII character, and one without any null bytes.
See Also
- Zlib compression header, used to store the data values. http://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like
- http://docs.oracle.com/cd/E15645_01/pt850pbr0/eng/psbooks/tsec/chapter.htm?File=tsec/htm/tsec10.htm
- http://psadmin.io/tag/ps_token/