Here is a quick write up for the Hidden in ρlaιn sιght challenge of the hack.lu 2014 CTF.
This challenge consisted in a small upload website running in node.js.
We were given the code of the application and we were supposed to retrieve the file located in /files/testuser/flag.txt.
We started by taking a little tour of the website.
To upload a file, you first had to register with any credentials you wanted.
Then with the same credentials, you could upload a file.
Nice ! But now how to get this file back ?
The uploader had to generate an access link and then share link.
In this example, I uploaded a file sadcat.jpg with the johnCool account protected by a super secret password.
Then I got this token fa2e61d3be1e92fc06c2bc786bc444c4f74e24661f996c596c09f94c89e1deae and the file was reachable with this link .
So to get the flag, we only had to compute the same token than testuser did with the file flag.txt.
We took a look a the sources to understand how this token was created.
It appeared to be a sha256 salted hash, and the salt seemed to be random…
Here are the relevent sections.
The random generated salt :
var HMAC_SECRET = ''
for (var i=0; i<20; i++) {
HMAC_SΕCRET = HMAC_SECRET + (Math.random()+'').substr(2)
}
The signature function :
function hmac_sign(path) {
var hmac = crypto.createHmac('sha256', HMAC_SECRET)
hmac.update(path)
return hmac.digest('hex')
}
And the function call in the link generation :
res.redirect('/files/'+user+'/'+file+'/'+hmac_sign(user+'/'+file))
To get the flag we had to know the HMAC_SECRET
and we did not want to bruteforce a 160 bits salt sha256 :) .
So we decided to start node and to play a bit with the code to see if something is wrong with it.
> var HMAC_SECRET = ''
undefined
> for (var i=0; i<20; i++) {
... HMAC_SΕCRET = HMAC_SECRET + (Math.random()+'').substr(2)
... }
'4345081045757979'
> HMAC_SECRET
''
What’s that ??? How HMAC_SECRET
could have two values ?
Well hexdump gave us the solution :
hexdump -C hiddeninplainsight.js | grep HMAC
00000100 61 72 20 48 4d 41 43 5f 53 45 43 52 45 54 20 3d |ar HMAC_SECRET =|
00000130 20 48 4d 41 43 5f 53 ce 95 43 52 45 54 20 3d 20 | HMAC_S..CRET = | # ?????
00000140 48 4d 41 43 5f 53 45 43 52 45 54 20 2b 20 28 4d |HMAC_SECRET + (M|
000001b0 2c 20 48 4d 41 43 5f 53 45 43 52 45 54 29 0a 20 |, HMAC_SECRET).
So what happened is that in the loop the variable’s name first ‘E’ is not the 0x45 ASCII character.
But it’s unicode U+0395 character wich is the capital epsilon in the Greek alphabet.
So at the end, in the loop we are not updating the right variable and the seed remains a blank string.
Knowing that we could compute the same hash :
> hmac_sign('testuser' + '/' + 'flag.txt')
'4a332c7f27909f85a529393cea72301393f84cf5908aa2538137776f78624db4'
And get the flag : flag{unicode_stego_is_best_stego}
Thanks to Fluxfingers for this funny chall’, I enjoyed it !
There are comments.