Parsia's Den

Because no one wants to be the other guy from Wham!

May 25, 2014 - 2 minute read - Comments - Python

Pasting Shellcode in GDB using Python

A few days ago I was trying to write an exploit for a buffer overflow with GDB. This was a console application and pasting shellcode would mess with it.

There are a few options:

  • Writing shellcode to a file and then using it as input for GDB.
# you can also include GDB commands like setting up breakpoints (e.g. b * 0xDEADBEEF)
# remember to include a new line after each command
$ python -c 'print "b * 0xDEADBEEF" + "\n" + "\x41"*1000 + "\n"' > input

# $ perl -e for perl

# start debugging with GDB
# -q (quiet mode): no text at startup
$ gdb executable1 -q
(gdb) run < input

After this you can manually debug in GDB.

  • Writing a Python script for interactive debugging When I wrote this, I thought it was a clever idea but then someone told me I could have written a GDB script. However, I have already written this snippet so here it goes.
#!/usr/bin/python

from subprocess import Popen , PIPE
from time import sleep

# shellcode
shellcode = "\x41" * 1000 + "\n"

# opens gdb with parameter executable
# you can also manage stdout and stderr here
proc = Popen( ['gdb' , 'executable'] , bufsize=1 ,stdin=PIPE )

# sample breakpoint
# notice the new line after each command
proc.stdin.write('b *DEADBEEF\n')

# half a second of sleep after each command
sleep(0.5)

# r or run to start debugging the program with GDB
proc.stdin.write('r\n')
sleep(0.5)

# any other commands go here

# this is a loop, will get every command and pass it to GDB
# "leave" == quit GDB and terminate process
# "dump"  == paste shellcode
while True:
    mycommand = raw_input()
    if (mycommand == "leave"):
        # quit gdb
        proc.stdin.write("quit\n")
        break

    # paste shellcode
    if (mycommand == "dump"):
        proc.stdin.write(shellcode)
    # more custom commands go here

    # not a custom command? send it as-is
    else:
        mycommand = mycommand + '\n'
        proc.stdin.write(mycommand)
        sleep(0.5)

# close our pipe
proc.stdin.close()

I think that this code can be modified and become a very simple fuzzer. We have control over stdin and can read stdout and stderr. Change input, record output, rinse and repeat.

subprocess is a very powerful module. For example to normally run an application with an argument we can write subprocess.call(['gdb','executable']) .

but let’s say we want to run executable with input (containing shellcode):

import subprocess

shellcode = "\x41" * 100

subprocess.call( ['gdb' , 'executable'] , shellcode)