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.