Skip to content

Latest commit

 

History

History
911 lines (746 loc) · 31.3 KB

File metadata and controls

911 lines (746 loc) · 31.3 KB

Introduction to PEDA and Pwntools

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

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.

checksec

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$

distance

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$

elfsymbol

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$

pattern

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$

procinfo

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

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$

find aka searchmem

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

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.

Using Pwntools

There are three ways you can use Pwntools:

  1. Interactively through the python/iPython consoles
  2. In a python script
  3. Pwntools command line tools

Interactively through the Console

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]:

In a Python Script

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 Command Line Tools

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.

Interacting with Target Binaries

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.

Local Copy of Binary

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'

Simulating a Networked Application Locally

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

Debugging with GDB

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.

Exercises

Please attempt the exercises in this section on your own before looking at the solution.

Ex 3.1: Getting the Right Name and Token

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.

Ex 3.2: Launching the Attack Against a Remote Target

Please find the flag file on an instance of the service running at 127.0.0.1:1900.

Solution script can be found here.