Binary
Intro book to to binary exploitation / reverse engineering.
Pwntools Cheatsheet
Syscalls for x64
Syscalls for x86
Syscalls for arm64
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>
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>
Last updated