Writeup: PCTF (21 – Key leak – 450pts)
Here is the write-up for the CTF challenge #21 organized by the team PPP. I liked this one pretty much, I think it was a very good challenge a bit tricky but still a very good one. Congrats to PPP for this original idea
We were the first team to solve this challenge, so we got some extra breakthrough points, and as far as I'm concerned we where the only ones who solved it.
first of all lets see what this little service does:
As can be seen in the image, it asks for a username and then for some input to be ciphered. After some text its supplied it returns the input encrypted, no more magic, as easy as sounds.
There are only two inputs so finding the overflow is quite easy as anyone could expect:
Lets see what is happening inside the program:
OK, we are overwriting the file descriptor pointer where the log will be written, but how can exploit this in a profitable way? Lets take a look to the code where the overflow takes place:
Well... The overflow happens at the snprintf() function, it prevents us from overwriting RET/EIP due to the 80 chars limit on it, but allows us to overwrite "int v4" and "FILE *stream" which are on the stack next to "char ptr". The variable "int v4" is set to 0 after fwrite() and not used since the BoF takes place and v4 being set to 0. So again, how could we take profit of this? We could replace the file descriptor with stdout but we will only get the logline printed to us which isn't very useful as it only prints a timestamp and the username previously supplied by us.
Lets take a look to the main code:
Just after the writelog() function where the BoF takes place, the key file is being opened. Does this suggest something to you? Lets inspect the file descriptors status just after the username has been supplied and the program asks for the text to be encrypted:
OK, now we are going to exploit the BoF to change the log file descriptor and point it to "stdin":
Weird, it says "Key length 0" and "Input length 30", but we didn't supply any more input than the username... lets inspect the file descriptors again to see what happened:
Nice!!! When replacing the logfile file descriptor pointer with the one from stdin, the program closes the logfile (in this case stdin) after writing the log, then it opens the key file which is assigned to the first free file descriptor number, in this case 0 which matches with stdin, so now the program is using the keyfile as input/stdin.
This would be a resume about what is happening:
Well this sounds pretty straight forward, but now we have to do the same in the server to retrieve the key and it has ASLR enabled... Lets see what we can do about it... These are three runs checking for a stdin pointer address:
ASLR is randomizing 12bits of the file descriptor pointer address so we have 2^12 = 4096 possible addresses, we can bruteforce all the possible addresses or stay at one of them hoping to match on one of the runs. We tried both and the first one to success was sticking to one of the addresses. Take a look to the screenshot bellow for a successful exploitation:
We should have redirected the output to a file to be able to extract the encrypted response which includes non printable characters. But lets see what we get and what the program returns:
It returns multiple data, if you take a look to the main code in one of the screenshots you will see that those three outputs are:
With all this information we can now code a program to decrypt the response and get the key to score some precious points...
Note that the code shown has no error checking to make the screenshot smaller. You can grab the original source code here.
And at last the result of the execution:
So the key was:
DescriptionCategory: pwnables We have obtained the binary for AED's internal data encryption service, running at a9.amalgamated.biz:10240. Obtain AED's data encryption key. Download |
We were the first team to solve this challenge, so we got some extra breakthrough points, and as far as I'm concerned we where the only ones who solved it.
first of all lets see what this little service does:
As can be seen in the image, it asks for a username and then for some input to be ciphered. After some text its supplied it returns the input encrypted, no more magic, as easy as sounds.
There are only two inputs so finding the overflow is quite easy as anyone could expect:
Lets see what is happening inside the program:
OK, we are overwriting the file descriptor pointer where the log will be written, but how can exploit this in a profitable way? Lets take a look to the code where the overflow takes place:
Well... The overflow happens at the snprintf() function, it prevents us from overwriting RET/EIP due to the 80 chars limit on it, but allows us to overwrite "int v4" and "FILE *stream" which are on the stack next to "char ptr". The variable "int v4" is set to 0 after fwrite() and not used since the BoF takes place and v4 being set to 0. So again, how could we take profit of this? We could replace the file descriptor with stdout but we will only get the logline printed to us which isn't very useful as it only prints a timestamp and the username previously supplied by us.
Lets take a look to the main code:
Just after the writelog() function where the BoF takes place, the key file is being opened. Does this suggest something to you? Lets inspect the file descriptors status just after the username has been supplied and the program asks for the text to be encrypted:
OK, now we are going to exploit the BoF to change the log file descriptor and point it to "stdin":
Weird, it says "Key length 0" and "Input length 30", but we didn't supply any more input than the username... lets inspect the file descriptors again to see what happened:
Nice!!! When replacing the logfile file descriptor pointer with the one from stdin, the program closes the logfile (in this case stdin) after writing the log, then it opens the key file which is assigned to the first free file descriptor number, in this case 0 which matches with stdin, so now the program is using the keyfile as input/stdin.
This would be a resume about what is happening:
- The program opens the logfile /home/keyleak/log (file descriptor 5)
- The overflow takes place and we replace the logfile file descriptor pointer with another one pointing to stdin
- The program closes the logfile descriptor which is now stdin (file descriptor 0)
- File descriptor 0 is now free and closed so there isn't input/stdin available
- The program opens /home/keyleak/key and assigns it the first free file descriptor in this case 0 which is also used for stdin (file descriptor 0)
- The program reads from user input (stdin) to encrypt it, but its really reading /home/keyleak/key
- The program reads /home/keyleak/key but as the read offset is already at the end due to the "user input read" it reads 0 bytes
- Now the program encrypts /home/keyleak/key with a NULL key and returns it to us
Well this sounds pretty straight forward, but now we have to do the same in the server to retrieve the key and it has ASLR enabled... Lets see what we can do about it... These are three runs checking for a stdin pointer address:
| Breakpoint 1, _IO_fgets (buf=0xffb2eb28 "", n=0x80, fp=0xb74ef420) at iofgets.c:42
Breakpoint 1, _IO_fgets (buf=0xffa178a8 "", n=0x80, fp=0xb755c420) at iofgets.c:42 Breakpoint 1, _IO_fgets (buf=0xffee09f8 "", n=0x80, fp=0xb7509420) at iofgets.c:42 |
ASLR is randomizing 12bits of the file descriptor pointer address so we have 2^12 = 4096 possible addresses, we can bruteforce all the possible addresses or stay at one of them hoping to match on one of the runs. We tried both and the first one to success was sticking to one of the addresses. Take a look to the screenshot bellow for a successful exploitation:
We should have redirected the output to a file to be able to extract the encrypted response which includes non printable characters. But lets see what we get and what the program returns:
It returns multiple data, if you take a look to the main code in one of the screenshots you will see that those three outputs are:
- IV (Random data) for PKCS5_PBKDF2_HMAC to derive the key
- IV (Random data) for EVP_DecryptInit_ex to perform the aes 256 cbc encryption
- The resulting ciphertext
With all this information we can now code a program to decrypt the response and get the key to score some precious points...
Note that the code shown has no error checking to make the screenshot smaller. You can grab the original source code here.
And at last the result of the execution:
So the key was:
I grow tomatoes in my garden.
Date: April 26th, 2011 | By: NighterMan



Pretty cool stuff…
Nice explanation, so even dummies like me would understand
Great work!