This is the second pwn of VolgaCTF; it is based on Web of Science. Stay tuned
for the write-up for the third and final one.
Now that NX is activated, Let’s do some real exploitation with a good old ROP chain!
From the organizers:
The binary has the same attributes as the previous one except that NX is now activated.
The binary is a little different than the previous one because some unused
functions were added to it. They can be fragments of the next binary.
The operation is the same as the previous one, therefore I won’t go into more
The vulnerabilities are the same as the previous one:
string format on the name;
stack buffer overflow on the responses to the ten additions.
The stack is layed out exactly the same as for the previous binary, therefore I
won’t reexplain both vulnerabilities.
The goal of the ROP chain is to create a remote shell so that we can find the
flag. The execve system call will be used to invoke /bin/sh.
Unfortunately the binary doesn’t contain some key gadgets to construct the ROP
chain easily, therefore we need to take them from the libc.
When we look at the strings contained inside the binary, we can see that it was
built with GCC on an Ubuntu machine:
We know that the version of Ubuntu is 14.04 and that the gcc package was
created on January 27. The latest libc version used by Ubuntu 14.04 is
2.19 and it was built on February 16. That could be the libc
that is running on the machine that runs the challenge, to be sure, we’ll have
to leak some part of it and compare.
Some libc are known to contain a magic gadget that execute /bin/sh by itself
which makes the exploitation rather easy because no ROP chain has to be built.
However, even if the magic gadget was executing without any problem, I don’t
understand why no shell was popped. Therefore a proper ROP chain must be
The string format vulnerability can be used to find pointers to the libc and
leak the memory they point to. To do that a few %p and a debugger is needed:
the 5th and 39th pointers point to somewhere in the libc:
Let’s see what they contain on the remote binary by using the %s format this
A tool to capture traffic is needed to see what is in the returned packet:
The leak starts right after the newline at 17944 + 62901 = ?. The byte number
0x58 is 0x08 which correspond to what is located at 0x7ffff79f9140. Then
there is 0x20 which is a whitespace. Then there is 0x83 which correspond
the the first byte located at 0x7ffff7677eee and the following bytes
correspond. It seems that we have the same libc \o/
ROP chain creation
The goal is to trigger the execve syscall with /bin/sh as argument. Here is
what must be set into the registers:
rax: syscall number: 59
rdi: filename: pointer to /bin/sh
rsi: argv: NULL because we don’t need it
rdx: envp: NULL because we don’t need it
All that information can be seen in the manpage of execve and syscall.
The addresses are offsets from the beginning of the libc. To that the base
address must be added. Earlier we leaked a pointer to the libc and found the base
address. The offset of the leaked address can be calculated as follow:
We still need to find a pointer to /bin/sh. There are two possibilities,
either we provide the string /bin/sh in the ROP chain and we find a gadget
that does mov rdi, rsp; ... or we find the address of an existing /bin/sh.
As I usually opt for the first option and I already explained in other
write-ups, let’s do the second one… or maybe this is because we’ve already
leaked an address to the libc.
As stated earlier, the libc contains the magic gadget, therefore it has to have
the string /bin/sh somewhere:
0x0017ccdb is the offset of the string /bin/sh. As for the gadgets, the
base address of the libc must be added.
We ended up with the following layout to put on the stack:
Now we have everything we need to create the payload:
136 bytes of padding (there was the shellcode in the previous challenge)
24 bytes of padding
Well… ASLR or not, that payload would still rekt you ;)