GDB with PEDA and Pwntools are two tools that we will be using extensively throughout the course. This section is designed to run through their basic use and to work out any possible kinks that might arise.
Throughout the section we will be using pre-built binaries in the build
folder. From the base repository directory, please navigate as follows:
ubuntu@ubuntu-xenial:/vagrant$ cd lessons/3_intro_to_tools/
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools$ cd build/
There should be a couple of binaries already in the directory. They are standard ELF files that you can run.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/build$ ./1_sample
Hello, I am a sample program.
PEDA (Python Exploit Development Assistance) is an extension to GDB that adds on a whole bunch of useful commands and quality of life improvements to the standard GDB experience. The provisioning script should have made the necessary additions to the GDB configuration so all you need to do to start it is launch GDB.
Let's walk through an example with the 1_sample
binary.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/build$ gdb ./1_sample
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./1_sample...(no debugging symbols found)...done.
gdb-peda$ r
Starting program: /vagrant/lessons/3_intro_to_tools/build/1_sample
Hello, I am a sample program.
[Inferior 1 (process 11030) exited normally]
Warning: not running or target is remote
gdb-peda$
The prompt should show gdb-peda
. If it does not, something has gone wrong with
the environment setup. To start off, let's break on main and explore what is
offered by PEDA.
gdb-peda$ br main
Breakpoint 1 at 0x40052a
gdb-peda$ r
Starting program: /vagrant/lessons/3_intro_to_tools/build/1_sample
[----------------------------------registers-----------------------------------]
RAX: 0x400526 (<main>: push rbp)
RBX: 0x0
RCX: 0x0
RDX: 0x7fffffffe5e8 --> 0x7fffffffe811 ("XDG_SESSION_ID=3")
RSI: 0x7fffffffe5d8 --> 0x7fffffffe7e0 ("/vagrant/lessons/3_intro_to_tools/build/1_sample")
RDI: 0x1
RBP: 0x7fffffffe4f0 --> 0x400540 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffe4f0 --> 0x400540 (<__libc_csu_init>: push r15)
RIP: 0x40052a (<main+4>: mov edi,0x4005c4)
R8 : 0x4005b0 (<__libc_csu_fini>: repz ret)
R9 : 0x7ffff7de78e0 (<_dl_fini>: push rbp)
R10: 0x846
R11: 0x7ffff7a2e740 (<__libc_start_main>: push r14)
R12: 0x400430 (<_start>: xor ebp,ebp)
R13: 0x7fffffffe5d0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x400521 <frame_dummy+33>: jmp 0x4004a0 <register_tm_clones>
0x400526 <main>: push rbp
0x400527 <main+1>: mov rbp,rsp
=> 0x40052a <main+4>: mov edi,0x4005c4
0x40052f <main+9>: call 0x400400 <puts@plt>
0x400534 <main+14>: mov eax,0x0
0x400539 <main+19>: pop rbp
0x40053a <main+20>: ret
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe4f0 --> 0x400540 (<__libc_csu_init>: push r15)
0008| 0x7fffffffe4f8 --> 0x7ffff7a2e830 (<__libc_start_main+240>: mov edi,eax)
0016| 0x7fffffffe500 --> 0x0
0024| 0x7fffffffe508 --> 0x7fffffffe5d8 --> 0x7fffffffe7e0 ("/vagrant/lessons/3_intro_to_tools/build/1_sample")
0032| 0x7fffffffe510 --> 0x100000000
0040| 0x7fffffffe518 --> 0x400526 (<main>: push rbp)
0048| 0x7fffffffe520 --> 0x0
0056| 0x7fffffffe528 --> 0x3061aa2b46d3584b
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Breakpoint 1, 0x000000000040052a in main ()
gdb-peda$
Notice that the default display is a made lot more informative than with the
vanilla GDB. Other than making it a lot easier to step through programs and view
the changes as they happen, PEDA provides a ton of other functionality as well.
To view the full list of them, you can use the peda
command.
gdb-peda$ peda
PEDA - Python Exploit Development Assistance for GDB
For latest update, check peda project page: https://github.com/longld/peda/
List of "peda" subcommands, type the subcommand to invoke it:
aslr -- Show/set ASLR setting of GDB
asmsearch -- Search for ASM instructions in memory
assemble -- On the fly assemble and execute instructions using NASM
checksec -- Check for various security options of binary
cmpmem -- Compare content of a memory region with a file
context -- Display various information of current execution context
context_code -- Display nearby disassembly at $PC of current execution context
context_register -- Display register information of current execution context
context_stack -- Display stack of current execution context
crashdump -- Display crashdump info and save to file
deactive -- Bypass a function by ignoring its execution (eg sleep/alarm)
distance -- Calculate distance between two addresses
dumpargs -- Display arguments passed to a function when stopped at a call instruction
tracecall -- Trace function calls made by the program
... snip ...
traceinst -- Trace specific instructions executed by the program
unptrace -- Disable anti-ptrace detection
utils -- Miscelaneous utilities from utils module
vmmap -- Get virtual mapping address ranges of section(s) in debugged process
waitfor -- Try to attach to new forked process; mimic "attach -waitfor"
xinfo -- Display detail information of address/registers
xormem -- XOR a memory region with a key
xprint -- Extra support to GDB's print command
xrefs -- Search for all call/data access references to a function/variable
xuntil -- Continue execution until an address or function
Type "help" followed by subcommand for full documentation.
gdb-peda$
We will go through a few of the interesting commands.
The checksec
command lists the protections that are enabled for the binary.
This is useful when figuring out how to craft your exploit.
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
gdb-peda$
Often, calculating offsets from addresses is required when crafting your payload in an exploit. This command makes it easy to find the distance between two addresses.
gdb-peda$ distance 0x7fffffffe4f0 0x7fffffffe528
From 0x7fffffffe4f0 to 0x7fffffffe528: 56 bytes, 14 dwords
gdb-peda$
If you ever needed to get the address for certain symbols in a binary (if you
are lucky and it is not stripped), you can use the elfsymbol
command.
gdb-peda$ elfsymbol
Found 2 symbols
puts@plt = 0x400400
__libc_start_main@plt = 0x400410
gdb-peda$
The pattern generator is one of the features of PEDA I most use. What it does is generate a De Brujin Sequence of a specified length. A De Brujin Sequence is a sequence that has unique n-length subsequences at any of its points. In our case, we are interested in unique 4 length subsequences since we will be dealing with 32 bit registers. This is especially useful for finding offsets at which data gets written into registers.
Let's say we create a pattern of length 64.
gdb-peda$ pattern create 64
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH'
gdb-peda$
Imagine that we have triggered a buffer overflow and find that the instruction pointer crashes on the address 0x48414132 ('2AAH' in ASCII). We can figure out the exact offset of our data to place our address to redirect code execution to.
gdb-peda$ pattern offset 0x48414132
1212236082 found at offset: 60
gdb-peda$ pattern offset 2AAH
2AAH found at offset: 60
gdb-peda$
This command parses information from the /proc/pid/x
directory and presents it
to you.
gdb-peda$ procinfo
exe = /vagrant/lessons/3_intro_to_tools/build/1_sample
fd[0] -> /dev/pts/0
fd[1] -> /dev/pts/0
fd[2] -> /dev/pts/0
pid = 11038
ppid = 11028
uid = [1000, 1000, 1000, 1000]
gid = [1000, 1000, 1000, 1000]
It is particularly useful to view which file descriptors are open.
vmmap
displays the memory mapping of the process. It is simple to invoke.
gdb-peda$ vmmap
Start End Perm Name
0x00400000 0x00401000 r-xp /vagrant/lessons/3_intro_to_tools/build/1_sample
0x00600000 0x00601000 r--p /vagrant/lessons/3_intro_to_tools/build/1_sample
0x00601000 0x00602000 rw-p /vagrant/lessons/3_intro_to_tools/build/1_sample
0x00007ffff7a0e000 0x00007ffff7bcd000 r-xp /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p mapped
0x00007ffff7dd7000 0x00007ffff7dfd000 r-xp /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7fec000 0x00007ffff7fef000 rw-p mapped
0x00007ffff7ff6000 0x00007ffff7ff8000 rw-p mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p [stack]
0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall]
gdb-peda$
What is important to glean from the listing above is the permissions flags of each of the segments. Often when developing your exploit, you will need to place some data somewhere. This data can be arguments to functions expecting a string pointer or even shellcode. What is required is that the segment that is being written to is marked writable.
Additionally, if you have a pointer from a memory leak and want to figure out where exactly the pointer is pointing to, you can drill down specifically on that address.
gdb-peda$ vmmap 0x00601000
Start End Perm Name
0x00601000 0x00602000 rw-p
/vagrant/lessons/3_intro_to_tools/build/1_sample
gdb-peda$
The find
command is an alias for the searchmem
peda command. It searches
memory for a given pattern. It is particularly useful to figure out where data
is or how it flows in a process.
For example, something that is often sought for is the string "/bin/sh". Perhaps
it lays in memory somewhere. We can use find
to look for it.
gdb-peda$ find /bin/sh
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0x7ffff7b9a177 --> 0x68732f6e69622f ('/bin/sh')
gdb-peda$
Pwntools is a Python library that provides a framework for writing exploits. Typically, it is used heavily in CTFs. There are a ton of useful functions provided by Pwntools but I will briefly describe the process I personally use.
There are three ways you can use Pwntools:
- Interactively through the python/iPython consoles
- In a python script
- Pwntools command line tools
Often, you want to try things out before actually writing an actual script when
developing your exploit. The iPython console is a great way to explore the
Pwntools API. For convenience, we will import everything in the pwn
package to
the global namespace.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/build$ ipython
Python 2.7.12 (default, Nov 19 2016, 06:48:10)
Type "copyright", "credits" or "license" for more information.
IPython 5.1.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: from pwn import *
In [2]:
iPython provides tab completion and a built-in system to look up documentation
in docstrings. For example, if we want to look at what the p32
function does,
we can look it up with the ?
sigil.
In [4]: p32?
Signature: p32(*a, **kw)
Docstring:
p32(number, sign, endian, ...) -> str
Packs an 32-bit integer
Arguments:
number (int): Number to convert
endianness (str): Endianness of the converted integer ("little"/"big")
sign (str): Signedness of the converted integer ("unsigned"/"signed")
kwargs (dict): Arguments passed to context.local(), such as
``endian`` or ``signed``.
Returns:
The packed number as a string
File: /usr/local/lib/python2.7/dist-packages/pwnlib/context/__init__.py
Type: function
In [5]: p32(0x41424344)
Out[5]: 'DCBA'
In [6]:
I like to begin with the following template when starting a new exploit.
#!/usr/bin/python
from pwn import *
def main():
pass
if __name__ == '__main__':
main()
Running the script is as simple as calling python on it. Try running this script:
#!/usr/bin/python
from pwn import *
def main():
p = process("/bin/sh")
p.interactive()
if __name__ == '__main__':
main()
Running the script:
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$ python 2_shellsample.py
[+] Starting local process '/bin/sh': Done
[*] Switching to interactive mode
$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd)
$
Pwntools installs the pwn
python script in /usr/local/bin. It provides
frontends to useful features of the library. To get a list of all available
frontends, you can execute pwn -h
.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$ pwn -h
usage: pwn [-h]
{asm,checksec,constgrep,cyclic,disasm,elfdiff,elfpatch,errno,hex,phd,pwnstrip,scramble,shellcraft,unhex,update}
...
Pwntools Command-line Interface
positional arguments:
{asm,checksec,constgrep,cyclic,disasm,elfdiff,elfpatch,errno,hex,phd,pwnstrip,scramble,shellcraft,unhex,update}
asm Assemble shellcode into bytes
checksec Check binary security settings
constgrep Looking up constants from header files. Example:
constgrep -c freebsd -m ^PROT_ '3 + 4'
cyclic Cyclic pattern creator/finder
disasm Disassemble bytes into text format
elfdiff Compare two ELF files
elfpatch Patch an ELF file
errno Prints out error messages
hex Hex-encodes data provided on the command line or stdin
phd Pwnlib HexDump
pwnstrip Strip binaries for CTF usage
scramble Shellcode encoder
shellcraft Microwave shellcode -- Easy, fast and delicious
unhex Decodes hex-encoded data provided on the command line
or via stdin.
update Check for pwntools updates
optional arguments:
-h, --help show this help message and exit
You can investigate the available options at your own time. Take a look at the documentation for a more detailed description of each of them.
Your target might expose itself through different vectors. Today we will focus on attacking remotely running binaries that you can connect to over the network. First, let's see how we might interact with a local copy of a binary that accepts input on stdin and returns output on stdout.
To begin with, we will look at the 2_interactive binary:
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/build$ ./2_interactive
Welcome to the Super Secure Shell
Password: HelloWorld?
Incorrect password!
For completeness sake, here is the source code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void give_shell() {
system("/bin/sh");
}
int main() {
// Disable buffering on stdin and stdout to make network connections better.
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
char * password = "TheRealPassword";
char user_password[200];
puts("Welcome to the Super Secure Shell");
printf("Password: ");
scanf("%199s", user_password);
if (strcmp(password, user_password) == 0) {
puts("Correct password!");
give_shell();
}
else {
puts("Incorrect password!");
}
}
The point of the program is to check the user input against a hardcoded password. If it matches, then an interactive shell is spawned.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/build$ ./2_interactive
Welcome to the Super Secure Shell
Password: TheRealPassword
Correct password!
$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd),999(docker)
$ exit
Now that we know how to craft the input, we can write our sample exploit using Pwntools.
#!/usr/bin/python
from pwn import *
def main():
# Start a local process
p = process("../build/2_interactive")
# Get rid of the prompt
data1 = p.recvrepeat(0.2)
log.info("Got data: %s" % data1)
# Send the password
p.sendline("TheRealPassword")
# Check for success or failure
data2 = p.recvline()
log.info("Got data: %s" % data2)
if "Correct" in data2:
# Hand interaction over to the user if successful
log.success("Success! Enjoy your shell!")
p.interactive()
else:
log.failure("Password was incorrect.")
if __name__ == "__main__":
main()
Take some time to go through the code and understand what it does. Take note of
the process("../build/2_interactive")
line. It starts a new process and
allows you to treat the object like a socket. Run the script and verify it
works:
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$ python 3_interactive.py
[+] Starting local process '../build/2_interactive': Done
[*] Got data: Welcome to the Super Secure Shell
Password:
[*] Got data: Correct password!
[+] Success! Enjoy your shell!
[*] Switching to interactive mode
$ ls -la
total 20
drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 11 12:28 .
drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 11 12:04 ..
-rw-rw-r-- 1 ubuntu ubuntu 98 Jan 10 11:36 1_template.py
-rw-rw-r-- 1 ubuntu ubuntu 136 Jan 10 11:56 2_shellsample.py
-rw-rw-r-- 1 ubuntu ubuntu 570 Jan 11 12:28 3_interactive.py
$
[*] Interrupted
[*] Stopped program '../build/2_interactive'
It is very easy to turn a console-based application into a networked one and there are multiple ways to do it. The exercises that come later in the docker containers use xinetd, a server daemon, to listen for network requests and then launch the binary to serve these requests. For now, we can use socat to do the same thing.
First, we will start a new screen session so that we can background our socat terminal.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$ screen bash
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$
Next, we run the following command to start a listener on port 1330.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/build$ socat TCP4-listen:1330,reuseaddr,fork EXEC:./2_interactive
It should hang there. Now return to your original bash session by holding down
the following key sequence: CTRL-A-D. If you run the command screen -ls
you
should see that the socat screen session is in the background.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$ screen -ls
There is a screen on:
5371.pts-0.ubuntu-xenial (01/11/2017 01:04:01 PM) (Detached)
1 Socket in /var/run/screen/S-ubuntu.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$
To verify that the listener is indeed listening on port 1330, we can run netcat.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$ nc localhost 1330
Welcome to the Super Secure Shell
Password: Hello
Incorrect password!
Now, here comes the magic. To modify the first script we had to work with
local binaries, we may simply comment out the process()
line and replace the
line with remote("localhost", 1330)
.
#!/usr/bin/python
from pwn import *
def main():
# Start a local process
#p = process("../build/2_interactive")
p = remote("localhost", 1330)
# Get rid of the prompt
data1 = p.recvrepeat(0.2)
log.info("Got data: %s" % data1)
# Send the password
p.sendline("TheRealPassword")
# Check for success or failure
data2 = p.recvline()
log.info("Got data: %s" % data2)
if "Correct" in data2:
# Hand interaction over to the user if successful
log.success("Success! Enjoy your shell!")
p.interactive()
else:
log.failure("Password was incorrect.")
if __name__ == "__main__":
main()
Now, if we run this it should give us our shell.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$ python 4_networked.py
[+] Opening connection to localhost on port 1330: Done
[*] Got data: Welcome to the Super Secure Shell
Password:
[*] Got data: Correct password!
[+] Success! Enjoy your shell!
[*] Switching to interactive mode
$ ls -la
total 40
drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 10 12:06 .
drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 11 13:21 ..
-rwxrwxr-x 1 ubuntu ubuntu 8608 Jan 9 21:00 1_sample
-rwxrwxr-x 1 ubuntu ubuntu 9064 Jan 9 21:00 2_interactive
-rw------- 1 ubuntu ubuntu 501 Jan 10 12:06 .gdb_history
-rw-rw-r-- 1 ubuntu ubuntu 12 Jan 10 12:02 peda-session-1_sample.txt
$
[*] Closed connection to localhost port 1330
Pwntools does provide GDB functionality but we will be taking a more manual approach since that is the way I am used to doing it. If you are interested in using this functionality, you can view the documentation here.
We will be using the 3_reversing binary to walkthrough this section. First, lets run it vanilla.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/build$ ./3_reversing
Name: This is a Name
Token: AAAA
Submitted Name: This is a Name
Submitted Token: 0x41414141
Incorrect credentials.
To gain a deeper insight into what is going on, we can use ltrace
to
investigate.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/build$ ltrace ./3_reversing
__libc_start_main(0x4007b1, 1, 0x7ffed30e1418, 0x4008f0 <unfinished ...>
setvbuf(0x7f661ca358e0, 0, 2, 0) = 0
setvbuf(0x7f661ca36620, 0, 2, 0) = 0
printf("Name: "Name: ) = 6
read(0This is a Name
, "This is a Name\n", 63) = 15
printf("Token: "Token: ) = 7
read(0AAAA
, "AAAA", 4) = 4
printf("Submitted Name: %s\n", "This is a Name\n"Submitted Name: This is a Name
) = 32
printf("Submitted Token: 0x%x\n", 0x41414141Submitted Token: 0x41414141
) = 28
strcmp("Santo & Johnny", "This is a Name\n") = -1
puts("Incorrect credentials."Incorrect credentials.
) = 23
+++ exited (status 0) +++
Using the following script, we can print the process id before the interaction with the program happens.
#!/usr/bin/python
from pwn import *
def main():
# Start a new process
p = process("../build/3_reversing")
# Name and Token
name = "Test Name".ljust(63, "\x00")
token = 0x41414141
# Print pid
raw_input(str(p.proc.pid))
# Send name and token
p.send(name)
p.send(p32(token))
# Start an interactive session
p.interactive()
if __name__ == "__main__":
main()
First, start another ssh session and run the script. Note the process id that
gets printed. Also note the use of p32()
to pack integers into little endian
strings.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$ python 5_gdb.py
[+] Starting local process '../build/3_reversing': Done
5652
Return to the original bash shell and run GDB as root. Now, we can attach to the process.
ubuntu@ubuntu-xenial:/vagrant/lessons/3_intro_to_tools/scripts$ sudo gdb
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
gdb-peda$ attach 5652
Attaching to process 5652
Reading symbols from /vagrant/lessons/3_intro_to_tools/build/3_reversing...(no debugging symbols found)...done.
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.23.so...done.
done.
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/ld-2.23.so...done.
done.
[----------------------------------registers-----------------------------------]
RAX: 0xfffffffffffffe00
RBX: 0x0
RCX: 0x7f2156e17680 (<__read_nocancel+7>: cmp rax,0xfffffffffffff001)
RDX: 0x3f ('?')
RSI: 0x7ffc16545500 --> 0x0
RDI: 0x0
RBP: 0x7ffc16545550 --> 0x4008f0 (<__libc_csu_init>: push r15)
RSP: 0x7ffc165454e8 --> 0x400844 (<main+147>: mov edi,0x40098a)
RIP: 0x7f2156e17680 (<__read_nocancel+7>: cmp rax,0xfffffffffffff001)
R8 : 0x7f2157304700 (0x00007f2157304700)
R9 : 0x6
R10: 0x37b
R11: 0x246
R12: 0x400680 (<_start>: xor ebp,ebp)
R13: 0x7ffc16545630 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7f2156e17677 <read+7>: jne 0x7f2156e17689 <read+25>
0x7f2156e17679 <__read_nocancel>: mov eax,0x0
0x7f2156e1767e <__read_nocancel+5>: syscall
=> 0x7f2156e17680 <__read_nocancel+7>: cmp rax,0xfffffffffffff001
0x7f2156e17686 <__read_nocancel+13>: jae 0x7f2156e176b9 <read+73>
0x7f2156e17688 <__read_nocancel+15>: ret
0x7f2156e17689 <read+25>: sub rsp,0x8
0x7f2156e1768d <read+29>: call 0x7f2156e354e0 <__libc_enable_asynccancel>
[------------------------------------stack-------------------------------------]
0000| 0x7ffc165454e8 --> 0x400844 (<main+147>: mov edi,0x40098a)
0008| 0x7ffc165454f0 --> 0x0
0016| 0x7ffc165454f8 --> 0x4141414100000000 ('')
0024| 0x7ffc16545500 --> 0x0
0032| 0x7ffc16545508 --> 0x0
0040| 0x7ffc16545510 --> 0x0
0048| 0x7ffc16545518 --> 0x0
0056| 0x7ffc16545520 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x00007f2156e17680 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:84
84 ../sysdeps/unix/syscall-template.S: No such file or directory.
gdb-peda$
At this point, the program is paused somewhere in libc. We can setup our
breakpoints. Let's assume that we have done some preliminary reverse engineering
and have discovered that the input gets passed to the check_creds()
. Here's
the disassembly of the function.
gdb-peda$ disas check_creds
Dump of assembler code for function check_creds:
0x0000000000400776 <+0>: push rbp
0x0000000000400777 <+1>: mov rbp,rsp
0x000000000040077a <+4>: sub rsp,0x10
0x000000000040077e <+8>: mov QWORD PTR [rbp-0x8],rdi
0x0000000000400782 <+12>: mov DWORD PTR [rbp-0xc],esi
0x0000000000400785 <+15>: mov rax,QWORD PTR [rbp-0x8]
0x0000000000400789 <+19>: mov rsi,rax
0x000000000040078c <+22>: mov edi,0x400974
0x0000000000400791 <+27>: call 0x400650 <strcmp@plt>
0x0000000000400796 <+32>: test eax,eax
0x0000000000400798 <+34>: je 0x4007aa <check_creds+52>
0x000000000040079a <+36>: cmp DWORD PTR [rbp-0xc],0xdeadc0de
0x00000000004007a1 <+43>: jne 0x4007aa <check_creds+52>
0x00000000004007a3 <+45>: mov eax,0x0
0x00000000004007a8 <+50>: jmp 0x4007af <check_creds+57>
0x00000000004007aa <+52>: mov eax,0x1
0x00000000004007af <+57>: leave
0x00000000004007b0 <+58>: ret
End of assembler dump.
gdb-peda$
Let's place some breakpoints on the function and step through the execution to discover what inputs we need to supply to pass the check.
gdb-peda$ br check_creds
Breakpoint 1 at 0x40077a
gdb-peda$ c
Continuing.
gdb-peda$
Back on the terminal with the script running, press enter to trigger the data sending. On the gdb terminal, the debugger should have broken. Continue on with the standard gdb debugging process to obtain the values.
Please attempt the exercises in this section on your own before looking at the solution.
Continuing on from where we left off in the guided portion, please find the right values for the name and token and modify the exploit code such that a shell is obtained.
Solution script can be found here.
Please find the flag file on an instance of the service running at 127.0.0.1:1900.
Solution script can be found here.