This level combines a stack overflow and network programming for a remote overflow.
Hints: depending on where you are returning to, you may wish to use a toupper() proof shellcode.
Core files will be in /tmp.
If we compile our own version of the final0 program, we can use it to learn things about the way that the stack should look in the real version.
$ gdb -q ./a.out (gdb) # We put a breakpoint on the line of the call to gets (gdb) break 21 (gdb) c (gdb) x/2a $ebp 0xbffff7b8: 0xbffff7e8 0x80485eb <main+14> (gdb) x/x buffer 0xbffff5a8: 0x00000000 (gdb) p/x 0x7b8-0x5a8 $1 = 0x210 (gdb) # We can conclude that &buffer+0x214 == &savedReturnAddress
With that in mind, it’s really simple to gain control of the program counter.
(gdb) shell python -c "print 'A'*0x214+'\xef\xbe\xad\xde'" > final0input (gdb) start < final0input Start it from the beginning? (y or n) y Temporary breakpoint 7 at 0x80485e6: file final0.c, line 42. Starting program: /home/user/a.out < final0input ... (gdb) c Program received signal SIGSEGV, Segmentation fault. 0xdeadbeef in ?? ()
Having to deal with
toupper() basically means that any byte that’s
between 0x61 (
a) & 0x7a (
z) in the buffer will be subtracted by
0x20 (aka converted into uppercase).
This means that our shellcode can’t simply push the values 0x6e69622f
/bin) & 0x68732f2f (
//sh) - and later, when those values are
interpreted as a string, actually resolve to a existing path.
Unless you’re running linux and have uppercase copies of every path on your system.
A common technique to sidestep this issue when writing shellcode is to push a value that is then modified to, in the end result in a lowercase, valid path.
Pro-tip: It’s simple to convert from a regular path to a encoded path in python
To get the original ASCII back, we just xor the value again with 0xf0f0f0f0.
Aiming for the stars, landing on the moon.
Before I looked for the buffer in the core-dumps. My assumption was that the buffer was located at 0xbffff5a8 (which I got from examining my own version of the program), but that was off by about 1184 (0x4a0) bytes.
After looking through a couple of shellcodes available from exploit-db.com, I lost patience and decided to write my own.
The file that’s being executed by our shellcode is just an example of what’s possible.
$ python final0_solution.py $ grep root /tmp/shadow root:$6$gOA4/iAf$EMw.4yshZLZxjlf./VmnEVQ20QsEmdzZa73csPGYGG6KC.riaGhLmESwWwB7Rnntu5JCDnRnOUTeeYWQk.iUq0:15302:0:99999:
This level is a remote blind format string level. The ‘already written’ bytes can be variable, and is based upon the length of the IP address and port number.
When you are exploiting this and you don’t necessarily know your IP address and port number (proxy, NAT / DNAT, etc), you can determine that the string is properly aligned by seeing if it crashes or not when writing to an address you know is good.
For those of you who are not familiar with formatting-exploits, I would suggest to go back and read up on the basics (shameless promo: my post on formatting exploits, or maybe this paper by scut / team teso).
Crashing the application.
"%08x"*10+"%n" we get segfault @
0xb7ed7a59 & stack-pointer
Another way of crashing the application would be to just send
Segmentationfault occurs because of
mov DWORD PTR [eax], edx, why?
eax is previously set to edi, which I believe corresponds to the parameter-pointer used by printf.
edx is the amount of bytes written so far.
By advancing the internal parameter-pointer with some
we can make printf read parameters from the buffer itself.
Reusing the python-code we wrote earlier, we can send some stuff like this
And observe that, upon crashing, eax is
Looking at the source,
syslog(LOG_USER|LOG_DEBUG, buf); (lineno 19) is
our entry-point or whatever you might call it.
man 3 syslog, we contemplate over the following passage.
Never pass a string with user-supplied data as a format, use the following instead:
syslog(priority, "%s", string);
syslog(priority, userInput) is somewhat equal to
BONUS: Working with GDB and coredumps
When loading up a coredump in gdb,
$ gdb -c <coredump>, any
non-stripped symbols from a executable are not loaded automagically.
Which means that if we want look at things like local variables,
functions - we simply can’t. After opening a core-file, loading
the executables symbols can be done via the file-command.
(gdb) file /opt/protostar/bin/final1.
Sharing is caring
In the mysterious case of final1, where the initial crash occurred in the
shared library function printf, or
If we’re working with a core-file and want to take a look at code from
a shared library; entering the
sharedlibrary command makes sure that
the relevant code is loaded for us to examine.
Achievement Unlocked: Control over program counter!
I achieved control over the program-counter by running the following piece of python code.
(gdb) x/i $eip 0x30303030: Cannot access memory at address 0x30303030
I overwrote the entry for
puts in the global offset table with 0x30.
$ objdump -R /opt/protostar/bin/final1 | grep puts 0804a194 R_386_JUMP_SLOT puts
There are still two things to be done.
1. Write some shellcode that will do
/tmp/exe we put whatever we want, and through the magic of television; I prepared
/tmp/exe with the following contents. Make sure to
chmod it, so it’s possible to execute it.
Pro-tip: When building my shellcode; I compiled it using nasm, sent it to my guest
protostar VM and linked it using
ld. At this point I had a working executable that
I could execute and see if the damn thing worked or handed me back a SIGSEGV
2. Overwrite the
puts-entry in the GOT with the address of our shellcode.
This step took some tinkering to get working. Because of some leading
text in the string, I had to 1) work out how many characters to write in order
to achieve the correct string-length to write
%n, and 2) make sure that
%$NUMn parameters were hitting the addresses in my input, while also
aligning my string to 4bytes. There’s a better way to do this.
After a lot of decrementing, shifting, tinkering and gdbing, I got my exploit working.
Putting all the pieces together
$ python final1_solution.py $ cat /tmp/lol Success!
Remote heap level :)
I would prefer a more explicit explanation. Having wrestled with this level for some time now, it’s a shame the folks over at exploit-exercises didn’t make this challenge more explicit; Word on the street is that the code provided on the website does not represent the binary found within the system.
The print-statement in the
check_path function never echoes back either;
so something is probably wrong.
I looked at the author of secwriteups solution to the problem when I got stuck on this level, I was having some trouble with getting the program to crash & secwriteups-post helped me a lot in progressing. Link Secwriteups post
The beginning goal is obviously to make it crash during the call to free, which we did the previous time we were exploiting dl-malloc.
From there, we can probably poke around enough to make sense of it all enough to be able to exploit things.
Running the code listed above generated the following crash:
root@protostar:/home/user# pidof final2 1485 root@protostar:/home/user# gdb -q -p 1485 Attaching to process 1485 [ snip-snip ] (gdb) c Continuing. [New process 27314] Program received signal SIGSEGV, Segmentation fault. [Switching to process 27314] 0x0804aabf in free (mem=0x804e008) at final2/../common/malloc.c:3643 3643 final2/../common/malloc.c: No such file or directory. in final2/../common/malloc.c 0x0804aabf <free+253>: 8b 40 04 mov eax,DWORD PTR [eax+0x4] 0x0804aac2 <free+256>: 83 e0 01 and eax,0x1 0x0804aac5 <free+259>: 89 45 e0 mov DWORD PTR [ebp-0x20],eax (gdb) x/x nextsize 0x42424240: Cannot access memory at address 0x42424240
Last time we exploited dlmalloc, we had a clear goal, redirect execution into the target function. This time we can do whatever we want. Let’s just keep it simple and shove some shellcode in there.
We did it!
Ladies and gentlemen, I’ve struggled a whole bunch with this level,
but I am proud to present to you, my own damn exploit that runs
nc -le /bin/sh, which allows me to connect to the randomly generated
port that the backdoor is listening to.
Upon connecting, we can let out a sigh of relief & rest assured that we are root.
$ python final02.py $ netstat -l Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State ... tcp 0 0 *:53955 *:* LISTEN ... $ nc localhost 53955 whoami root id uid=0(root) gid=0(root) groups=0(root)
There you have it. For the sake of completeness, here’s the exploit I wrote.
We’ve been through thick and thin, cried some, failed until hell froze over, laughed a whole lot and maybe even learned a thing or two, but we are officially done with the protostar challenges. FeelsGreatMan
It’s been a lot of fun trying to get my shellcode working and trying to exploit these sets of challenges. I’ve already started working on the fusion set of challenges, so keep an eye on my blog for solutions to those problems.
In the mean-while, have a good one & thanks for reading.