In August BlueFrostSecurity offers a challenge to win one out of seven tickets for the Ekoparty Security Conference. Last week finally I had time to look at and solve the challenge. The challenge binary was a PE file for Windows and the exploit should work on Windows 7, Windows 8.1 and Windows 10 64bit. The binary has been compiled with the options /NXCOMPAT, /DYNAMICBASE and /GS. If you start the application you were asked to vote for candidate
As soon as you have voted for a candidate, the application opens socket connection and wait for a synchronization client.
I started analysing the app wih IDA. Once opened it I found there several functions:
A look at the functions showed me that the applications binds a socket to port 5555. If a connection is established, the application expects a handshake. Here the application reads 4096 bytes from the socket.
The first six bytes are checked against “Hello\0”. All other bytes are ignored.
The connection will be closed if the handshake is not correct. If the handshake is correct, the application expects more data and starts reading from the socket. First, the application reads four bytes from the socket. The lower two bytes are used as the size of a further read.
The data is written to a buffer in the heap. That buffer is allocated with the size that is recieved with the first recv call. Afterwards the content of the buffer is copied to another buffer on the stack. That stack buffer has a size of 0x100 bytes. That is, if more than 0x100 bytes are sent, there is a bufferoverflow condition. However, the application is compiled with the /GS compiler flag. That means, it makes use of stack canaries and the application will crash, if the canary has a different as the expected value when the function returns. Since the canary is a random value, a memory leak is necessary to obtain the value of the canary. The application reads now a pointer out of an array. The index that is used here is taken from the upper two bytes of the four bytes that are recieved the first time and interpreted as the size. However, only one byte is used. That is the index can be up to 255.
The array contains a lot of ‘A’s except ot two positions. At position 102 we find an address to a function that calls IsBadReadPtr and returns 1 if the pointer is readable otherwise 0. At position 256 we find the address of the function system. Since the address of system is at position 256, it is not possible for us to call system. If another index is provided the application will crash, because of the ‘A’s. That is, it is only possible to call one of the identified functions.
The read pointer is called as a function and the address to the stack buffer is passed as an argument. After a successful function call, the application sends the previously read buffer back to the client. However, one byte more is send than received before. That means, there is a one byte memory leak and I could read values from the stack byte per byte.
My idea was to read some bytes from the stack using the memory leak to leak the canary and place the canary in the payload. Doing that made it possible to bypass the stack canary. I read several bytes more to read also the return address to obtain an address of the binary. With the return address it was possible to calculate the base address. So it was feasible to calulate the address of the system function to call it in a ret2func manner. Since that is 64 bit application I had to place the argument for the call of system in the RCX register. So I used ropper to look for gadgets to place the argument in RCX.
One optional requirement was that the application keeps running after exploit execution. Since parts of the previous stackframe is overwritten, I had to change the exploit. To accomplish that the application keeps running, I had to place the ropchain somewhere else. Therefore I read all bytes from the stack up to the buffer of the handshake. Since the application reads for the handshare 4096 bytes and just need six bytes of it, I could use that buffer to place the small ropchain there. Behind the ropchain I placed the values I read from the stack that are part of the previous stackframe.
I changed the exploit a little bit, because the application crashs if the application is exploited multiple times. Therefore, I changed the memory layout created by the exploit a little bit. The new version can be found here.