I placed pwn in with programming, because it relies heavily on programming concepts and knowledge
Take a quick look at the file before jumping into analysis
readelf -h ./binary # Analyze the ELF headers
readelf -sW ./binary | grep FUNC # List all functions in binary
objdump -M intel -d ./binary | awk -v RS= '/^[[:xdigit:]]+ <main>/' # Display disassembly for main() only
ltrace ./binary # Watch library calls as program executes
strings ./binary # The classic
printf $(python -c 'print("A"*15)') | ./int-overflow # Easy way to change amount of bytes passed to program
echo -n -e ' \x41\x41\x41\x41\x41\x41\x42' > bytes # Put bytes directly into a file
(cat ./exploit; cat) | ./program # Pass the exploit to the program, leaves stdin open
./checksec.sh ./program # Runs the checksec.sh script to determine binary mitigations in place
# Use MSF to create the pattern and identify offsets
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 200 -q 6641396541386541
# Similar to MSF, PEDA can support patterns too. Inside GDB
# Disable ASLR
echo 0 > /proc/sys/kernel/randomize_va_space
# Check if ASLR is enabled or not
# Compile a program to be vulnerable
gcc -no-pie -m32 -fno-stack-protector -z execstack binary.c -o binary
# Strip internal symbols from a program
# GDB-PEDA check security of a binary
Program used to help debug activities as they occur. Use
set disassembly-flavor intelto get rid of nasty AT&T syntax. Can analyze core dumps to help identify vulnerable locations in memory for binaries as well.
Generally, we want to set a break AFTER a vulnerable function to see what the stack looks like.
Change the libc used in GDB with the command
# Execute inside GDB to get correct libc offsets
set exec-wrapper env 'LD_PRELOAD=/home/chris/libc.so.6'
# Execute outside of GDB with command:
LD_PRELOAD=./libc.so.6 | python -c 'print("1\n"+"A"*40)' | ./vuln_elf
# Find /bin/sh in memory while using GDB
# Find "system" function in memory (has to be started first to link libs)
# View env vars at a break after starting execution
# Start a program with values passed as input (not argv)
run < <(python -c 'print("A"*64 + "BBBB")')
# Read the disassembly from main()
# Print the value of hexadecimal to ASCII
print (char ) 0x24424142
# Pass Args to a program when starting in GDB
run `python -c 'print("A"*50)'`
# Pass input to a program when starting in GDB
run < <(python -c 'print("A"*50)')
# Enable core dumps
ulimit -c unlimited
sudo chmod -s ./binary
# With core dumps enabled, crash the binary and load the dump
If we find a program which calls some of these functions, we may be able to take control with a buffer overflow based attack.
calloc, malloc, realloc, fscanf, gets, scanf, sprintf, sscanf, strcat, strcpy, strncat, strncmp, strncpy, memchr, memcmp, memcpy, memmove, memset, scanf, gets, fwscan, sscanf
Stripped programs have their symbol tables removed, which makes identifying user created functions difficult to find compared with non-stripped binaries. If we search for functions, we will only see functions called in linked libraries.
What we can do, is set a break on some of the functions that are called. Once that break is hit, we can inspect the
btto identify where the return pointer will hit a call.
# Inspect in GDB for functions
# Set break on a linked function
# View backtrace on break to see where entry point into function
This is a technique where we use stack overflows to reach linked functions in libc and gain execution.
# strings to find offset for /bin/sh, this will be 0x00131a7a
strings -atx /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh
In the case of a terminator canary, keep in mind that many functions such as gets() will place a null byte at the end for us! If we overrun the buffer, we can strategically reconstruct a canary value.
ASLR makes exploitation more difficult, however there are some nifty tricks we can use to still get around it. Trampoline calls can be used to call the stack, we just need to search for opcodes which are either a
CALL ESP. By calling the stack pointer, we can execute our data on the stack (as long as DEP is not present!!).
PEDA makes this easy with the command
Additionally, we can check if the libraries loaded by a program are staticly loaded or not. This can be done using the
>>> import pwn
One of the easiest ways to create shellcode is using msfvenom. We can always list out the options with --list-options as a switch.