Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Gli indirizzi sono sempre in esadecimale (0x e vari byte a 0 come padding):
per convertire in ASCII, togliere 0x e convertire
Es: 0x46 -> 70 -> F
Questa conversione può essere utile anche nell'assembly
per individuare le operazioni sui byte
Es: cmp rax, 0x7 -> Sto comparando $rax con "7"
Assembly
Struttura -> istruzione [sorgente-src] [destinazione-dst]
- push
Fa push di src nello stack
- pop
Fa push di pop nello stack
- mov
Sposta un byte a word (con estensione di segno)
- inc
Incrementa di 1
- dec
Decrementa di 1
- neg
Negazione aritmetic
- not
Negazione logica
- cmp
Confronto aritmetico
- test
Confronto logico
- jmp
Salta all'etichetta/locazione specificata
- je -> Jump if Equal
- jz -> Jump if Zero
- jne -> Jump if not Equal
- jnz -> Jump if not Zero
- js -> Jump if negatuve
- jns -> Jump in notnegative
- jg -> Jump if greater
- jge -> Jump if greater or equal
- jl -> Jump if less
- jle -> Jump if less or equal
- ja -> Jump if above
- jae -> Jump if above or equal
- jb -> Jump if below
- jbe -> Jump if below or equal
- jnb -> Jump if Condition Is Met
- call
Push return address and jump to label 221
- leave
Set %rsp to %rbp, then pop top of stack into %rbp
- ret
Pop return address from stack and jump there
Writing a Function
Phases involved: Setting Up, Using the stack frame, Cleaning Up
foo:
pushq %rbx # Save registers, if needed
pushq %r12
pushq %r13
subq $0x18, %rsp # Allocate stack space
# Function body
addq $0x18, %rsp # Deallocate stack space
popq %r13 # Restore registers
popq %r12
popq %rbx ret # Pop return address and return control
# to caller
- Stack a 32 bit:
● Indirizzo di ritorno (4 byte);
● Puntatore di base (4 byte);
● Dati
- Stack a 64 bit:
● Indirizzo di ritorno (8 byte);
● Puntatore di base (8 byte);
● Dati
- The Stack
In computer architecture, the stack is a hardware manifestation
of the stack data structure (a Last In, First Out queue).
Conventionally, ebp/rbp contains the address of the top of the current stack frame,
and so sometimes local variables are referenced as an offset relative to ebp rather
than an offset to esp. A stack frame is essentially just the space used on the
stack by a given function.
Uses
The stack is primarily used for a few things:
- IDA
F5 /visualizza lo pseudocodice della funzione
(lo rende leggibile)
90 = nop /90 in esadecimale corrisponde a un
istruzione assembly
75 = JNZ /Jump if Not Zero
74 = JZ /Jump if Zero
In IDA nella graph view all'inizio di una funzione ci sono delle etichette (es.
"var1, var2")
con a fianco un numero negativo in hex (es. -10h, -18h).
Questi nomi vengono generati da IDA e i relativi numeri vengono usati
come offset per indicare dove iniziano quelle variabili:
se ad esempio leggi all'inizio "var1 = -10h" e nelle righe successive
trovi scritto [rbp+var1] in realtà l'istruzione sta indicando la variabile
che inizia a [rbp-16]
perché in x86-64 gli offset delle variabili locali rispetto all'indirizzo
in rbp sono per definizione negativi (sono più vicine alla cima dello stack)
-sprintf
-strcpy
-getss
-memcpy
-memmove
-strcpy
-snprintf
-strncat
fgets/fflush --> Da qualche parte in memoria si genera la flag
- Entrypoint --> I punti in cui il programma prende un input di qualche tipo o file
Controlli anti-debug:
- ptrace(); quando presente, controlla se c'è un debugger.
Occorre fare in modo di farle ritornare 0 e non 1
- Controllo sulle variabili d'ambiente
- Controllare la rilocazione dello heap (in BSS viene spostato e riassegnato)
- Controllare le sezioni .init e .fini o start (contengono i controlli anti-debug)
Installazione GDB-PEDA:
git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
Avviare un programma qualsiasi con gdb e si vede il prompt iniziare con gdb-peda
GDB (con gdb-peda) --> gdb -q per avviare in "quiet", quindi senza il prompt
verboso iniziale
c /continua dopo br
x/200bx $esp /mostra la stack, se non c'è esp usa rsp
r < a /da come input il file a (da usare con
cyclic)
r < $(python -c "print('A'*50)") /da come input il risultato dello script
set disassembly-flavor /spesso usato per impostare la
visualizzazione delle variabili
Esempio di output:
"Register container pattern buffer:
EBP+0 found at offset: 136
EIP+0 found at offset: 140 -> fine pattern (offset di 140)
oppure -> RBP+0 found at offset: 32 (offset di 32+8, per riallocazione
indirizzo)
Comandi utili
strings /mostra funzioni
file nome /mostra architettura (Ex:intel 80386)
pwn cyclic 100 > a /crea un pattern lungo 100 dentro al file a
objdump -d file /mostra tutto il dump del codice
ltrace -f file /mostra chiamate di sistema (con -f fa
vedere anche i processi figli)
strace -f file /alternativo al precedente con stesso scopo
grep -r --text 'picoCTF{.*}' /trovare testo ricorsivamente
egrep -r --text 'picoCTF{.*?} /alternativo al precedente con stesso scopo
ma vede anche regex (espressioni regolari)
readelf -s filebinario /permette di vedere simboli e funzioni
ldd <filename> /Librerie dinamiche caricate
nm <filename> /Simboli caricati nel file
cyclic <number> /Genera una sequenza di numeri per aiutare
nei buffer overflow
Es. cyclic 50 | ./vuln
cyclic -l <lookup_value> /Cerca la locazione di un valore nel pattern
ciclico
dmesg /Esamina o controlla il kernel ring buffer e
visualizzare messaggi (utile per capire dove ci sono errori/segfault)
Varie:
ASLR Address Space Layout Randomization:
- Randomizes stack, heap, libc and makes it harder to find important locations (es.
libc)
Solution:
Use a non executable stack coupled with ASLR. Use libc attacks
Radare2
r2 file /apre file
aaaa /inizializza e fa un'analisi completa del
file; alternative più veloci ("aa" e "aaa")
afl /mostra indirizzi funzioni e lista tutte le
funzioni
afll /tabella uguale alla precedente ma più
verbosa e graficamente migliore
pdf; agf /mostra la funzione; mostra un grafico per
il control flow
Utilizzo:
pdf @ funzione
Esempio:
pcp 0x23 @ obj.FLAG
import struct
buf = struct.pack ("35B", *[
0x84,0x93,0x81,0xbc,0x93,0xb0,0xa8,0x98,0x97,0xa6,0xb4,
0x94,0xb0,0xa8,0xb5,0x83,0xbd,0x98,0x85,0xa2,0xb3,0xb3,
0xa2,0xb5,0x98,0xb3,0xaf,0xf3,0xa9,0x98,0xf6,0x98,0xac,
0xf8,0xba])
q /quit
Uso del package manager di Radare2 (es. installazione Ghidra per decompilare il
codice in linea)
- r2pm update
- r2pm -ci r2ghidra
- r2pm -ci r2ghidra-sleigh
payload += stringhe
payload = payload.encode("ascii")
p = process("./file") /processa
correttamente un file binario
(oppure)
context.binary = "./file"
p = process()
(oppure)
io = process(context.binary.path) /creiamo
un'istanza del processo con cui interagire
main = io.unpack() /possiamo
prendere un indirizzo da quella funzione, nel qual caso il "main"
target_address=p64(e.symbols['print_flag']) /esempio di
utilizzo con ELF
exit_got=elf.got['exit'] /prendiamo
l'indirizzo della funzione exit
win_addr=elf.symbols['win'] /prendiamo
l'indirizzo di una funzione presente nel file, in questo caso "win"
(se usiamo context binary, allora avremo una cosa del tipo):
p = process()
p.sendline(b"y")
p.sendline(str(context.binary.functions["give_the_man_a_cat"].address).encode("asci
i"))
p.sendline(str(context.binary.got["exit"]).encode("ascii"))
p.sendlineafter('_str_', _msg_) /scrive una
stringa nel terminale solo dopo aver letto una certa stringa
p.recvuntil(">") /riceve dati
finché non arriva al delimitatore ">"
p.interactive() /permette di
interagire con il terminale
(normalmente, occorre mettere un input e si vede la flag, oppure si è spawnata
una shell e occorre fare "ls/cat flag.txt" e si vede così la flag)
Write-what-where
What -> function that gives a shell (give_the_man_a_cat, usefulFunction, some shit
like that)
Where -> exit function/puts function, usually found in GOT (especially the first
one)
ROP
(Siamo intenzionati a sfruttare dei gadget; questi hanno la sintassi [pop /
istruzione / ret])
rip contains the return address we want to send a payload for overflowing
instructions
like "puts", "gets" and similar ones.
So, we send a huge payload and then:
- if we have a bash/shell kind of function, we use that
- otherwise, we expect a response from the program giving us the flag
We do construct a "chain" of sort:
- payload + address + right offset (depending on file and register operations,
that somehow might move it around; usual correcting of the stack is making a
payload
and then adding 3 bytes. It's seen when program works sending a payload
but does nothing. In this case, it's stack relocation or just wrong architecture in
code written)
Stack pivoting:
Modificare il valore di "rsp" per farlo puntare ad altre aree della memoria:
Es. gadget -> "pop rsp; ret"
Cerchiamo con /R pop rsp
ROP Chain -> Stringa + indirizzo altra funzione + payload BO + gadget + indirizzo
di pivot
ROP Chain Stack Approved -> Stringa + indirizzo altra funzione + payload BO +
rop_gadget_in_piu + gadget + indirizzo di pivot
EIP instruction pointer --> holds the address of next instruction to be executed
(read only register)
ESP stack pointer --> points to the top of the stack at lower memory location
and contains the address of the data that would be removed from the stack
EBP base pointer --> points to higher memory address at the bottom of the stack
ESI source index
EDI destination index
EAX accumulator (return value always in assembly)
EBX base
ECX counter
EDX data
- ROPgadget
ROPgadget --binary file | grep "registro"
ROPgadget --binary binaryfile --ropchain (per sapere dove scrivere e come)
- Allineamento stack
Potrebbe accadere che lo stack non sia più allineato a 16 byte.
Istruzioni come "movaps" potrebbero disallinearlo.
Dobbiamo quindi allineare lo stack pointer ed è come inserire un NOP nelle reverse
challenge.
In questo caso, si tratta di aggiungere un ROP gadget contenente "ret", ad esempio
con il comando:
ROPgadget --binary file | grep "ret"
Vengono inoltre usate versioni diverse di "libc" a seconda dell'hardware.
- Payload usato
Eseguiamo di solito un payload dipendente dall'offset dei registri
oppure con (32+8) byte, per permettere il buffer overflow.
In generale è un padding utile per raggiungere l'indirizzo di ritorno, essendo
che abbiamo x86-32.
- Lazy binding
Il Lazy binding è una tecnica utilizzata dal linker dinamico
per ridurre il tempo di avvio del programma,
in cui la ricerca dei simboli per le chiamate a funzioni
in oggetti condivisi viene rinviata fino alla prima volta
in cui una funzione viene effettivamente chiamata.
Per ottenere questo effetto vengono utilizzate due sezioni
di programma: la tabella di collegamento delle procedure
(.plt) e parte della tabella degli offset globali (.got.plt).
(Code example)
libc = ELF(file_libc)
libc.address = puts - libc.symbols["puts"]
rop = ROP(libc)
rop.call(libc.symbols["system"], [libc.search(b"/bin/sh\x80")])
- Shellcode generico
http://shell-storm.org/shellcode/ per riferimento
Come si usa:
Letteralmente, basta eseguire un curl ad una delle varie opzioni presenti sulla
pagine e prendere uno shellcode qualsiasi, ad esempio con:
curl https://shell-storm.org/shellcode/files/shellcode-904.html
Cheatsheet Ghidra:
https://ghidra-sre.org/CheatSheet.html
Radare2:
https://book.rada.re/
https://www.rapidtables.com/convert/number/hex-to-ascii.html
http://www.brescianet.com/appunti/vari/tabellaascii/ascii.htm
IDA Offset:
https://books.google.it/books?
id=5p7VAwAAQBAJ&pg=PA337&lpg=PA337&dq=ida+offset+var&source=bl&ots=uyu2Nh8trp&sig=A
CfU3U32vriGuBWgDiMtBeRGLMl9Un2gkQ&hl=it&sa=X&ved=2ahUKEwiJms7Z4sL8AhUA87sIHQHhDvUQ6
AF6BAgYEAM#v=onepage&q=ida%20offset%20var&f=false
Stack:
https://ctf101.org/binary-exploitation/what-is-the-stack/