Binary
Intro book to to binary exploitation / reverse engineering.
Pwntools Cheatsheet
Syscalls for x64
Syscalls for x86
Syscalls for arm64
Compare tools on the forefront of static analysis in your web browser
To be expanded with PEDA, GEF or PWNDBG.
Start
gdb <ELF>
Run gdb on that program. Be careful with addresses (it would be better to use PID).
gdb --pid <PID>
Run gdb on a process. Run the program in the background, get PID and provide it to gdb.
gdb --args <ELF> <ARGs>
Run gdb on that program passing it arguments.
Execution
file <ELF>
Load the program to analyze
set args <ARGs>
Set the arguments to use on each bugger run.
r
Run
r <ARGs>
Run with arguments
r ${python3 -c ‘print "\xBYTES"’}
Run with byte arguments using Python
r ${echo -e “\xBYTES”}
run with byte arguments using Echo
Breakpoints
b <function>
b *<address>
Set a breakpoint.
Also possible to do: b *main+10
i b
Info breakpoint
enable <ID>
Enable breakpoints. If no ID is specified, enable all breakpoints.
disable <ID>
Disable breakpoints. If no ID is specified, disable all breakpoints.
d <ID>
Delete breakpoint If no ID is specifyied, deletes all breakpoints.
c
Continue. Normal execution.
s
Step. IN function.
n
Next. NO IN function.
finish
Continue until the current function returns.
k
Kill. Stop the current execution.
Visualization
disassemble <FUNCTION>
Get assembly and their memory addresses.
i fu
Get the list of program functions with their addresses.
bt [full]
Show backtrace (function calls list).
With full
print even the local variables of each frame, if you have access to them.
i s
Info Stack Frame.
i f
Info Current Stack Frame. (ex. $RIP in BOF)
i r
i all-r
Info Registers.
i dll
list of used DLLs
p/<F> <expr>
Print what you want in the specified format <F>
.
ex. p/x $esp
p/x $esp+8
p <FUN>
p <VAR>
x/<N><F><U> <addr>
display/<N><F><U> <addr>
As x/ but are saved and printed/displayed at each step.
i display
undisplay <ID>
enable display <ID>
disable display <ID>
If you don't put ID it applies to all.
ex. display/5i $pc
(next 5 instruction)
Format
a
Pointer.c
Read as integer, print as character.d
Integer, signed decimal.f
Floating point number.o
Integer, print as octal.s
String.t
Integer, print as binary (t = "two").u
Integer, unsigned decimal.x
Integer, print as hexadecimal.i
Disassembly.
Edit memory
set {type}address = value
Change the value of the addresses.
ex. set{int}0x650000 = 0x42
set{char[1]}0x650000 = 0x42
set variable = value
Change the value of variables
set $reg = value
Change the value of the registers
set *address = value
Change the value of an address
Utility
call <FUNCTION>()
Call function.
ex. call (void)win()
echo $((16#<NUM>))
From bash convert hex to decimal.
r << <CommandFile>
It is possible to specify a file with the inputs that should be given to the program in read, scanf, etc.
Like cat <CommandFile> | ./<ELF>
checksec <ELF>
pwn checksec <ELF>
Context
Architecture
context.arch = '<ARCH>'
'aarch64'
, 'arm'
, 'i386'
, 'amd64'
Log Output
context.log_level = '<VALUE>'
'debug'
, 'info'
, 'warn'
, 'error'
Endianness
context.endian = '<ENDIANNESS>'
'big'
, 'little'
Start
ELF
elf = ELF('<PATH_BINARY>')
libc = ELF("./libc.so.6")
Process
p = process('<PATH_BINARY>')
Remote
p = remote('<IP/DOMAIN>', PORT)
Hybrid
elf = ELF('<PATH_BINARY>')
p = elf.process()
Debugger
p = gdb.debug('<PATH_BINARY>', aslr=False, gdbscript='b *main+123')
Encoding e Packing
Hex
enhex(b'/flag')
('2f666c6167')
unhex('2f666c6167')
(b'/flag')
Base64
b64e(b'/flag')
('L2ZsYWc=')
b64d('L2ZsYWc=')
(b'/flag')
Packing
p32(0x41424344)
(b'\x44\x43\x42\x41')
p8
, p16
, p64
Unpacking
u32(b'\x44\x43\x42\x41')
(0x41424344)
u8
, u16
, u64
Address
Function address
address = elf.sym['<Name_Function>']
address = elf.symbols.<Name_Function>
address = elf.plt[’<Name_Function>’]
(in PLT)
address = elf.got[’<Name_Function>’]
(in GOT)
String address
address = next(elf.search(b'<STRING>'))
Or with ghidra in .rodata
, .text
Cyclic
Create Search String
cyclic(16)
(b'aaaabaaacaaadaaa')
cyclic(16, n=8)
(b'aaaaaaaabaaaaaaa')
Find Offset
cyclic_find(0x61616164)
(12)
cyclic_find(0x6161616161616162, n=8)
(8)
cyclic_find(io.corefile.fault_addr, n=8)
(in BOF)
In BOF use setuid=False
in p = process()
Shellcode
Shellcraft, must be NULL-free.
Assembly
code = shellcraft.cat('/flag')
code = shellcraft.amd64.linux.sh()
Bytecode
shellcode = asm(code)
(use the context architecture)
shellcode = asm(code, arch='x86_64')
Start
rop = ROP(elf)
rop = ROP(elf, badchars=b'\n')
(char not to be used in the chain)
rop = ROP([elf, libc])
(specify double elf)
Extract Gadget
pop_ = rop.find_gadget(['pop <REG>', 'ret']).address
pop_ = rop.<REG>.address
or ROPgadget.
ROP chain
rop.call(<ADDR_FUNCTION>, [<ADDR_ARG1>, ... ])
rop.puts(); rop.main()
(adds puts and main to the chain) rop.chain()
(builds the chain with the elements called before) rop.dump()
(text representation, to be put in log.info(X)
)
rop.clear()
(clears the rop chain)
rop.execve(next(libc.search(b'/bin/sh\0')), 0, 0)
(calls execv with parameters)
Set Base Address (ex. libc)
libc = ELF(<PATH_LIBC>)
(ex ./glibc/libc.so.6)
libc.address = <INT_ADDRESS_BASE>
rop_libc = ROP(libc)
(after set base libc)
Format String
%p
Pointer
%s
String
%d
Int
%x
Hex
%c
Char
%n
Writes the number of characters printed in the specified pointer.
%n
= int (8 byte)
%hn
= short (4 byte)
%hhn
= byte (1 byte)
%<P>x
Specifies the precision <P> (filled with 0)
%<N>$x
Take the <N>-th argument
fmtstr_payload(offset, writes, numbwritten=0, write_size='byte')
offset
: Offset to the bufferwrites
: Dictionary with address:value{addr: value, addr2: value2, etc.}
numbwritten
: Number of bytes already written by printf() ex.printf(hello <VAR>)
= 6write_size
: The write size: byte, short or int (hhn, hn or n)
Note: Set the right <ARCH>
import angr
import claripy
import sys
FILE = './<ELF>'
FLAG_LENGTH = 30
FLAG_KNOW = 'FLAG{'
FIND_ADDR = 0x1def
AVOID_ADDR = 0x1df8
# Define program
project = angr.Project(FILE, main_opts={"base_addr": 0x0}) # auto_load_libs=False
# Define the initial state simply
OPTION_NO_ERROR = {angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY, angr.options.ZERO_FILL_UNCONSTRAINED_REGISTERS}
initial_state = project.factory.entry_state(add_options = OPTION_NO_ERROR)
# ---------------- OR ----------------
# OPTIONAL: Define variable format
# known_chars = [claripy.BVV(FLAG_KNOW[i]) for i in range(len(FLAG_KNOW))]
# flag_chars = [claripy.BVS(f"flag_{i}",8) for i in range(FLAG_LENGTH-len(FLAG_KNOW))]
# flag_input = claripy.Concat(*known_chars+flag_chars)
# OPTIONAL: Define how to make the input
# Define initial state with interactive input
# initial_state = project.factory.entry_state(stdin=flag_input, add_options = OPTION_NO_ERROR)
# Define initial state with input from argument
# initial_state = project.factory.entry_state(args=[FILE, flag_input], add_options = OPTION_NO_ERROR)
# ------------------------------------
# Create a simulator with the initial state
simgr = project.factory.simulation_manager(initial_state)
# Start the exploration of the program path
simgr.explore(find=FIND_ADDR, avoid=AVOID_ADDR)
# If Angr finds a path that meets the success criterion.
if simgr.found:
# Extract the input that led to the path to success
flag_solution = simgr.found[0].posix.dumps(0)
print("Flag found:", flag_solution.decode())
else:
print("No flag found.")
# ---------------- OR ----------------
# If there are multiple inputs
# if simgr.found:
# print("[+] Input:")
# for state in simgr.found:
# solution = state.posix.dumps(0)
# print(solution.decode())
# else:
# print("No solution found.")
# ------------------------------------
Last updated
Was this helpful?