to my homepage
view my exploit code (requires libjh)
In this challenge, you have to exploit a server that allows you to download and upload files. The goal is to retrieve /home/sftp/flag, but the server blocks every filename that contains flag. And you can't move, rename, overwrite, ... that file because your user only has read access:
drwxr-x--- 4 root sftp  4096 May 17 00:11 .
drwxr-xr-x 4 root root  4096 May 16 23:51 ..
-rw-r--r-- 1 sftp sftp   220 Apr  9 01:03 .bash_logout
-rw-r--r-- 1 sftp sftp  3637 Apr  9 01:03 .bashrc
-rw-r--r-- 1 sftp sftp   675 Apr  9 01:03 .profile
-rw-r--r-- 1 root sftp    33 May 16 23:51 flag
drwxrwx--- 2 root sftp 12288 May 18 22:44 incoming
drwxrwx--- 2 root sftp  4096 May 18 12:10 incomming
-rwxr-xr-x 1 root root 13808 May 16 23:51 sftp
After connecting, you first need to log in:
+LegitimateBusinessSyndicate SFTP ServiceUSER defcon
defcon valid, send account and passwordPASS defcon2014
! Logged in
Then, you can upload files into /home/sftp/incoming. You really only need the APP mode which appends to files if they already exist:
STOR APP test123
+Will append to test123SIZE 11
+ok, waiting for filehello world
And you can retrieve them (and files from anywhere else on the system as long as you have access and the name isn't blacklisted):
RETR /home/sftp/incoming/test123
11SEND
hello world
Finally, you can also delete files – useful for cleaning up after running your exploit:
KILL /home/sftp/incoming/test123
+/home/sftp/incoming/test123 deleted
So how can you exploit this? Have a look at that RETR command again – there is really no reason why you'd need to confirm that you really want that file, right?Edit: There is a reason, see RFC 913 - a client might not have enough free space to store the file. Thanks to gynophage for pointing that out.
And this is where the vuln hides. The loop from 0x08049415 to 0x08049468 reads the file byte by byte into a buffer with the file's size that was allocated on the stack earlier, but this loop reads the entire file, not just the number of bytes it expects. Therefore, you can overflow the buffer by using two connections: You store an empty file over connection 2, send RETR over connection 1, append data to the file over connection 2 and send SEND over connection 1. Then, the buffer will only be big enough for an empty file and all the stuff in the file will be written over the stack.
However, exploiting the vuln needs some more work. For an initially empty file, the stackframe looks like this (relative to ebp):
-0x3a8     buffer
...
-0x37c    index into buffer used for writing, also used to determine how many bytes should be sent over the net
-0x378    buffer-pointer
-0x374    FILE*
...
-0xc      stack overflow protection canary
-0x8      stored esi
-0x4      stored edi
+0x0      stored ebp
+0x4      stored return pointer
So there's a canary in the way that you must not modify, and to make things even worse, behind the buffer-pointer is the FILE* that's in use – if you overwrite that, the program will crash.
The trick is: In front of all the stuff you aren't allowed to touch, there is the index into the buffer. Because you can only write one byte at a time, you can only modify one of the bytes in the index though, so you can't modify the index so that you jump between the canary and the stored return pointer. However, you can increment it by 0x400 or so and then stop writing – that won't write anything behind the counter, but it will cause 0x400 bytes from behind the counter to be seen as part of the buffer when the buffer is sent to you. Therefore, this lets you leak the stack including the canary value.
After you have grabbed the stack, you need to modify the stored return pointer and the space behind it to call a function of your choice with arguments of your choice. Also, when you attack the server with the new crafted stack, you have to keep in mind that the FILE* will be different this time, so you have to jump over a little bit of space behind the counter. For that, you overwrite the first byte of the counter with 0xff.
Now what method do you choose to call? You could try to repurpose the file-reading code in the binary to read the flag, but at least I think that if you can already overwrite stuff on the stack, you should aim for system() with an arbitrary string as argument. ASLR is on, and you can't read /proc/self/maps because that file seems to have zero size in procfs and therefore crashes the server when you try to read it. However, there is some pointer into the libc on the stack (at ebp+0x138), and by first dumping the stack and then reading /proc/self/maps once (it only crashes after it has dumped the maps data), you can find out which constant you need to subtract to find out where the libc starts in memory.
One thing to note about my exploit is that it doesn't work for commands that are too long for some reason – it's enough for cat ../flag though, and it's also enough for doing chmod +x aa and then ./aa to run an arbitrary uploaded file.
If you have comments/questions, feel free to ask me, either by email (consisting of jann, an @ sign and the domain this website is on) or on IRC (TheJH on freenode) – but for questions, please check whether my exploit answers them first.