Renamed all README.DK.md files to README.DA.md.
This commit is contained in:
parent
21ee05cecd
commit
c366f104f0
3 changed files with 0 additions and 0 deletions
414
README.DA.md
Normal file
414
README.DA.md
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
# food / fooc — et stack-bufferoverløb, fra begge sider
|
||||
|
||||
Et C99-sikkerhedslaboratorium i to halvdele:
|
||||
|
||||
- **`food.c`** — en bevidst sårbar TCP-daemon. Den har et ægte,
|
||||
lærebogsagtigt stack-bufferoverløb ([CWE-120](https://cwe.mitre.org/data/definitions/120.html)) og et par fejl oveni.
|
||||
- **`fooc.c`** — et exploit til den. Det beregner overflow-offsettet ved at
|
||||
disassemblere target-programmet ved kørsel, læser adresse-leaks fra daemonen
|
||||
og får en shell på "offeret" ved at overskrive en gemt returadresse.
|
||||
|
||||
Pointen er ikke shellen. Pointen er, at du kan følge med hele vejen, hvordan
|
||||
en hukommelsessikkerhedsfejl bliver til vilkårlig kodeudførelse — og derefter
|
||||
se præcist, hvilke modforanstaltninger der stopper hvert trin i den kæde. Hver
|
||||
linje i begge programmer er kommenteret, fordi mekanismen er lektionen.
|
||||
|
||||
```
|
||||
din terminal
|
||||
|
|
||||
./fooc (exploit)
|
||||
|
|
||||
TCP 127.0.0.1:2342
|
||||
|
|
||||
./food (sårbar daemon)
|
||||
|
|
||||
fork() -> vulnerable_handler() -> overflow -> ret -> din kode
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Læs dette først
|
||||
|
||||
**`food` er en bevidst ødelagt netværkstjeneste. Den binder kun til
|
||||
`127.0.0.1`, og den standard er bevidst — lad den være der.**
|
||||
|
||||
- Kør den **ikke** på en maskine, du holder af, eller på noget med data på.
|
||||
- Bind den **ikke** til `0.0.0.0` eller en rigtig netværksgrænseflade. Den er
|
||||
bevidst eksternt udnyttelig.
|
||||
- At rette `fooc` mod en host, du ikke ejer eller ikke har skriftlig tilladelse
|
||||
til at teste, er en computerindbrudsforseelse i de fleste jurisdiktioner —
|
||||
også efter UK Computer Misuse Act og US Computer Fraud and Abuse Act.
|
||||
- Den binder til en uprivilegeret port (>1024), så du behøver ikke root. Forbedr
|
||||
den ikke ved at tilføje capabilities eller køre den som systemtjeneste.
|
||||
- Hver forbindelse håndteres i et `fork()`et barn, og `food` reaper det, så
|
||||
nedbrud hober sig ikke op. Hvis du bagefter finder dusinvis af strejfende
|
||||
`sh`-processer, er `pkill -x sh` oprydningen.
|
||||
|
||||
I tvivlstilfælde: Dette laboratorium er til en virtuel maskine eller container,
|
||||
på et netværk du kontrollerer, på en maskine uden noget, du ville savne.
|
||||
|
||||
---
|
||||
|
||||
## Hurtig start
|
||||
|
||||
```sh
|
||||
make # bygger food, fooc og test-harnessene
|
||||
make run # starter food på 127.0.0.1:2342, frakoblet i baggrunden
|
||||
make test # kører alle tre exploit-teknikker
|
||||
make stop # stopper daemonen
|
||||
```
|
||||
|
||||
Derefter i hånden:
|
||||
|
||||
```sh
|
||||
./fooc -t leak # se de adresse-leaks, food udleverer
|
||||
./fooc -t demo -v # send junk; se food dø med SIGSEGV
|
||||
./fooc -t ret2win -i # hop til en funktion, der allerede findes -> shell
|
||||
```
|
||||
|
||||
### Krav
|
||||
|
||||
| Værktøj | Hvortil | Bemærkninger |
|
||||
|---|---|---|
|
||||
| `gcc` (eller clang) | bygning | C99. Testet med gcc 16.2 |
|
||||
| `objdump` | `fooc` | binutils. `fooc` kalder det ved kørsel |
|
||||
| `nasm` | `make verify` | kun til at krydstjekke shellcoden; springes over, hvis ikke til stede |
|
||||
| `gdb` | `make debug` | valgfrit |
|
||||
| Linux, x86-64 | begge | payload og gadget-jagt er arkitekturafhængige |
|
||||
|
||||
`fooc` har også brug for `-ldl` til `dlsym()`; Makefile'et klarer det.
|
||||
|
||||
---
|
||||
|
||||
## Fejlen
|
||||
|
||||
Én linje i `food.c` er hele angrebsfladen:
|
||||
|
||||
```c
|
||||
char buf[FOOD_BUFSZ]; /* 64 bytes */
|
||||
n = read(fd, buf, FOOD_READMAX); /* op til 512 bytes fra netværket */
|
||||
```
|
||||
|
||||
64 bytes destination, 512 bytes accepteret. Angriberen overskriver 448 bytes
|
||||
forbi enden af bufferen, og fordi stacken vokser nedad, betyder "forbi enden"
|
||||
"ind i det ovenstående frame" — og det er præcis der, den gemte
|
||||
framepointer og den **gemte returadresse** ligger.
|
||||
|
||||
I en kompileret x86-64-funktion ved `-O0`:
|
||||
|
||||
```
|
||||
høje adresser
|
||||
+------------------------+ rbp + 16 : callerens lokale
|
||||
| ... |
|
||||
+------------------------+ rbp + 8 : GEMT RETURADRESSE <-- bliver til RIP
|
||||
| saved rbp (8 bytes) |
|
||||
+------------------------+ rbp : vores framepointer
|
||||
| line[128] |
|
||||
| buf[64] | <- rsp: det, read() fylder
|
||||
+------------------------+
|
||||
lave adresser
|
||||
```
|
||||
|
||||
Når funktionen returnerer, popper `leave; ret` de 8 bytes ind i `RIP`, og CPU'en
|
||||
hopper, hvor angriberen har bestemt. Alt andet i dette laboratorium er
|
||||
aritmetik om, hvorhen der skal peges.
|
||||
|
||||
For denne build er tallene: `buf` er 64 bytes, det gemte `rbp` er 8, så
|
||||
returadressen ligger på offset **88** fra starten af `buf`. `fooc` hardkoder
|
||||
ikke det — det disassemblerer `food` og finder `lea -0x50(%rbp)` foran
|
||||
`call read@plt`, så det fortsat virker, hvis du ændrer `FOOD_BUFSZ`.
|
||||
|
||||
> gcc fortæller dig allerede om det. At bygge `food` printer:
|
||||
> `warning: 'read' writing 512 bytes into a region of size 64 overflows the
|
||||
> destination [-Wstringop-overflow=]`. Undertryk aldrig den advarsel i ægte
|
||||
> kode. Den er gratis sikkerhed.
|
||||
|
||||
---
|
||||
|
||||
## De tre teknikker
|
||||
|
||||
`fooc -t <technique>`. De står i den rækkefølge, en ægte angriber ville
|
||||
arbejde sig igennem dem, fordi hver enkelt har brug for det, den forrige lærte
|
||||
dig.
|
||||
|
||||
### 1. `ret2win` — kontrollér instruktionsmarkøren
|
||||
|
||||
```
|
||||
[ 88 bytes junk ][ adressen på food's win() ]
|
||||
^ saved rbp
|
||||
^ bliver til RIP
|
||||
```
|
||||
|
||||
`win()` er en funktion i target-programmet, der exec'er `/bin/sh`. At
|
||||
overskrive returadressen med dens adresse er hele exploitet.
|
||||
|
||||
**Hvad det lærer:** du har vilkårlig kontrol over instruktionsmarkøren. Det
|
||||
kræver heller ikke noget leak, fordi binærfilen er bygget `-no-pie`, så `win()`
|
||||
sidder på en fast adresse for evigt.
|
||||
|
||||
**Den virkelige verdens ækvivalent** er ikke "angreb er nemme", men "skib ikke
|
||||
udokumenterede bagdøre i netværks-binærfiler". Hvis en funktion som `win()`
|
||||
findes i din binærfil, vil et bufferoverløb finde den. Det er bogstaveligt talt
|
||||
Juniper ScreenOS-bagdør-CVE-klassen.
|
||||
|
||||
**Forsvar:** `-fPIE` (eller ASLR) randomiserer load-adressen, så angriberen må
|
||||
kende adressen — hvilket som regel betyder, at de først har brug for et leak.
|
||||
Derfor fejler `ret2win` mod `food_hardened`.
|
||||
|
||||
### 2. `ret2libc` — kald hvad som helst, ved navn
|
||||
|
||||
```
|
||||
[ junk ][ pop rdi; ret ][ adressen på "/bin/sh" ][ adressen på system() ]
|
||||
^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
|
||||
sætter rdi strengen at sende funktionen at kalde
|
||||
```
|
||||
|
||||
Ved udførelse: `ret` popper `pop rdi; ret` ind i RIP; det popper
|
||||
`"/bin/sh"`-pointeren ind i `RDI`; dets `ret` popper `system()` ind i RIP, mens
|
||||
`RDI` stadig holder strengen. `system("/bin/sh")` kører.
|
||||
|
||||
Gadgets (`pop rdi; ret`) er ikke i `food` — denne glibc har intet
|
||||
`__libc_csu_init` — så `fooc` finder dem ved at scanne live libc-hukommelse
|
||||
efter byteparret `5f c3`. Det lokaliserer libc via `/proc/self/maps`, finder
|
||||
offsets for `system` og `"/bin/sh"` med `dlsym()` og beregner basen ud fra det
|
||||
leak, `food` offentliggør. Intet er hardkodet, så det overlever en
|
||||
libc-opdatering.
|
||||
|
||||
**Hvad det lærer:** når du først kan kontrollere `RIP`, kan du kæde
|
||||
*eksisterende* instruktioner sammen. Det er return-oriented programming, og det
|
||||
er sådan næsten alle virkelige exploits ser ud, fordi det ikke kræver
|
||||
angriberleveret eksekverbar hukommelse.
|
||||
|
||||
**Forsvar:** ingen af compiler-flagene stopper det alene. Det virker mod en
|
||||
PIE-binærfil, med NX, med canary — så længe angriberen har et leak.
|
||||
Forsvarene er "hav ikke overløbet" og "læk ikke adresser". Se tabellen nedenfor.
|
||||
|
||||
### 3. `shellcode` — kør din egen maskinkode
|
||||
|
||||
23 bytes, placeret i starten af bufferen, med `RIP` pegende på dem:
|
||||
|
||||
```asm
|
||||
xor esi, esi ; envp = NULL
|
||||
xor edx, edx ; argv = NULL
|
||||
movabs rdi, 0x68732f6e69622f ; rdi = "/bin/sh\0" som 8 rå bytes
|
||||
push rdi ; læg strengen på stacken
|
||||
mov rdi, rsp ; rdi = &"/bin/sh"
|
||||
push 0x3b ; 59 = __NR_execve
|
||||
pop rax
|
||||
syscall ; vi er nu en shell
|
||||
```
|
||||
|
||||
Det er den reneste form for fejlen: angriberen leverer *instruktionerne*, ikke
|
||||
bare adressen på instruktioner, der allerede findes. Ingen libc-offsets
|
||||
nødvendige, så det virker i princippet mod et statisk linket, fuldt
|
||||
randomiseret target.
|
||||
|
||||
`make verify` assemblerer `shellcode.S` og diff'er det mod byte-arrayet, der er
|
||||
indlejret i `fooc.c`, så de to ikke kan drive fra hinanden.
|
||||
|
||||
**Forsvar:** **NX** (også kaldet W^X, "no execute"). At markere stacken som
|
||||
ikke-eksekverbar får hardwaren til at nægte at hente instruktioner fra den, og
|
||||
`ret`-et lander på en side, der ikke kan køre. Det er derfor `make food`
|
||||
giver `-z execstack`: en normal Linux-stack er `rw-p`, ikke `rwx`, og teknikken
|
||||
dør med SIGSEGV ved `RIP = payloadens adresse`. Den absolut vigtigste lektion i
|
||||
laboratoriet er, at hver eneste af disse bytes kun virker, fordi compileren fik
|
||||
besked på at lade stacken være eksekverbar. Det flag er tændt til gavn for
|
||||
ingen.
|
||||
|
||||
### Også inkluderet
|
||||
|
||||
| Tilstand | Hvad den gør |
|
||||
|---|---|
|
||||
| `-t leak` | forbinder, printer leaks, sender intet |
|
||||
| `-t demo` | sender `rip_off + 8` bytes `0x41`, så `RIP` bliver `0x4141...` og daemonen dør. Beviser fejlen helt uden adresseviden |
|
||||
| `-t sled` | et ret-sled, bevidst beholdt som et **fejlende** eksempel. Uden et leak ville du brute-force ASLR ved at fylde bufferen med adressen på et `ret`. Det kan ikke virke her: `food` accepterer 512 bytes, så sleden har ~53 slots mod ~28 bit entropi. Implementeret, så du kan se det fejle og bekræfte, at mekanismen virkelig er "CPU'en følger en kæde af rets" |
|
||||
|
||||
---
|
||||
|
||||
## Tabellen over modforanstaltninger
|
||||
|
||||
Det er den del, man skal huske. Hver række er et ægte forsvar, og højre kolonne
|
||||
viser, hvad den rent faktisk gør ved begivenhedskæden.
|
||||
|
||||
| Modforanstaltning | Sådan aktiveres | Hvad den stopper | Hvad den *ikke* stopper |
|
||||
|---|---|---|---|
|
||||
| **Begræns read** | `n = read(fd, buf, sizeof buf - 1);` | **Alt.** Fejlen findes ikke, så intet nedstrøms betyder noget | Intet — det er den eneste fuldstændige fix |
|
||||
| **Stack-canary** | `-fstack-protector-strong` (gccs standard) | `ret`-et: canaryen tjekkes ved funktionens afslutning, så smadringen opdages, og processen abort'er, før `RIP` poppes | En fejl i en funktion *uden* array (intet at beskytte); et overflow, der holder sig under canaryen; alt, der ikke returnerer normalt |
|
||||
| **NX / W^X** | `-z noexecstack` (standarden) | Shellcode. Payloadens egne instruktioner kan ikke hentes | ret2win og ret2libc fuldstændigt. De er *grunden* til, at ROP findes |
|
||||
| **PIE + ASLR** | `-fPIE` + ASLR=2 (begge standard) | ret2wins hardkodede adresser. Alt flytter sig ved hver kørsel | Alt, hvor angriberen har et leak. ASLR hæver prisen på et exploit; det er ikke en fix. Bemærk, at stack, heap og mmap randomiseres, men hoved-binærfilens *indhold* gør ikke — det er det, ROP-kæder bruger |
|
||||
| **Læk ikke** | ingen `printf("%p")` til klienter; initialisér før du printer | Det informationsleak, der gør ASLR til "gratis" i stedet for "dyrt" | — |
|
||||
| **Brug ikke `printf(user_data)`** | `printf("%s", buf)` i stedet for `printf(buf)` | Format-string-fejl: `%x`-stack-reads, `%n`-vilkårlige skrivninger, hvilket er en *anden* vej til RCE | — |
|
||||
| **Brug ikke utroverdige stier** | validér og `openat()` under en fast mappe | Sti-traversal ([CWE-22](https://cwe.mitre.org/data/definitions/22.html)) | — |
|
||||
| **CET / shadow stack** | `-fcf-protection=full`, kernel- og CPU-understøttelse | `ret`-et selv: shadow stacken husker den *rigtige* returadresse og fault'er ved mismatch. Fanger ROP-kæder, der bruger hardware-`ret` | Angreb, der aldrig `ret` (call-oriented, eller at overskrive en funktionspegers mål med en gadget-kæde, der ikke behøver en retur) |
|
||||
| **Sikre sprog** | Rust, Go, C# til ny kode | Hele klassen. Bounds-tjek udføres ved kørsel, ikke håbet på ved review | — |
|
||||
|
||||
### Se det selv
|
||||
|
||||
```sh
|
||||
make run # sårbar daemon
|
||||
make test # alle tre teknikker virker
|
||||
|
||||
make test-hardened # samme kildekode, modforanstaltninger på
|
||||
```
|
||||
|
||||
`test-hardened` bygger `food_hardened` med `-fstack-protector-strong -fPIE -pie
|
||||
-z noexecstack`, bytter den ind, kører alle tre igen og lægger derefter den
|
||||
sårbare tilbage. Du vil se:
|
||||
|
||||
```
|
||||
### stack segment: 'rw-p' (NOT executable) is what you want to see
|
||||
--- ret2win was stopped by the mitigations (as expected)
|
||||
--- ret2libc was stopped by the mitigations (as expected)
|
||||
--- shellcode was stopped by the mitigations (as expected)
|
||||
```
|
||||
|
||||
Og i den hærdede daemons log, canaryen der udløses:
|
||||
|
||||
```
|
||||
*** stack smashing detected ***: terminated
|
||||
```
|
||||
|
||||
Læs det grundigt, for det er den vigtigste linje i hele laboratoriet: **canaryen
|
||||
fangede ret2win, ikke PIE.** Alle tre teknikker dør ved canaryen, fordi alle tre
|
||||
går gennem det samme `read()` og smadrer den samme frame. NX stopper kun
|
||||
yderligere shellcodens *kode*; PIE bryder kun yderligere den hardkodede adresse.
|
||||
Tænd dem enkeltvis, og du vil opdage, at de fleste enkelte modforanstaltninger
|
||||
efterlader dig udsat over for noget.
|
||||
|
||||
---
|
||||
|
||||
## Filer
|
||||
|
||||
| Fil | Formål |
|
||||
|---|---|
|
||||
| `food.c` | den sårbare daemon. 6 nummererede fejl, hver med sin fix i kommentaren |
|
||||
| `fooc.c` | exploitet. objdump-baseret offseterkendelse, `/proc`-baseret libc-erkendelse, 4 payload-buildere |
|
||||
| `shellcode.S` | de 23 shellcode-bytes som assembly, så de kan læses og verificeres. `fooc` bærer dem inline og behøver ikke dette ved kørsel |
|
||||
| `Makefile` | bygger, tester og den hærdede sammenligning |
|
||||
| `tests/pty_test.c` | driver `fooc` gennem et pseudo-terminal og tjekker for ægte shell-output |
|
||||
| `tests/sock_test.c` | uafhængig verifikator over en rå socket, så resultatet ikke afhænger af `fooc` |
|
||||
| `food.log` | daemonens log. Dit bevis på, hvad der skete |
|
||||
|
||||
---
|
||||
|
||||
## To fejl i dette laboratorium, der er værd at forstå
|
||||
|
||||
Det er ikke target-programmets fejl. Det er fejl i exploitet og i dets
|
||||
test-harness, og begge producerede overbevisende løgne. De er dokumenteret i
|
||||
kilden, hvor de bor; her står de, fordi fiaskomønstrene er lærerige.
|
||||
|
||||
### Stack-justering: nedbruddet, der ikke er en NULL-dereference
|
||||
|
||||
**Symptom.** Kapringen lander korrekt — `gdb` viser dig i `win()` — og så dør
|
||||
det allerførste, `win()` gør, et `dprintf()`. `SIGSEGV`-handleren rapporterer
|
||||
`RIP` dybt inde i glibcs formatter og en fejladresse på `(nil)`, hvilket ser
|
||||
præcis ud som en korrupt pointer.
|
||||
|
||||
**Årsag.** System V AMD64-ABI'en kræver 16-byte stack-justering. Et normalt
|
||||
`ret` genskaber `%rsp` præcis som det tilsvarende `call` gemte det, så
|
||||
invarianten bevares gratis. Vores nøgne `ret` gør ikke: efter det gælder
|
||||
`%rsp = buf + rip_off`. Her er `buf` 16-byte justeret og `rip_off` er 88, så
|
||||
callee'en får en stack, der er 8 mod 16. glibc er kompileret med SSE2, og
|
||||
`movaps` **fault'er** ved et fejljusteret operand. På x86 rejser det `#GP`, ikke
|
||||
`#PF`, så kernen har ingen fejladresse og rapporterer `si_addr = 0`. Det NULL
|
||||
er fingerpeg: en justeringsfejl forklædt som en NULL-dereference.
|
||||
|
||||
**Fix.** Et `ret`-gadget *ved offset `rip_off`*, der flytter det rigtige target
|
||||
8 bytes op, fordi hvert `ret` lægger præcis 8 til `%rsp`. Rækkefølgen er
|
||||
kritisk: en tidligere version hæftede `ret`-et *efter* target og producerede
|
||||
`[ padding | target | ret ]`, hvor det afsluttende `ret` aldrig nås, og fixen
|
||||
stille og roligt intet gør. Et vildfarent `ret`, der ligner en fejl, er næsten
|
||||
altid bevidst.
|
||||
|
||||
### Én socket, to læsere: det byte, der forsvandt
|
||||
|
||||
**Symptom.** Shellcode blev rapporteret som fungerende. Derefter blev
|
||||
pty-harnessen gjort strengere (slukning af `ECHO`, så terminalen holdt op med at
|
||||
ekko harnessens egen kommandolinje tilbage til sig selv), og teknikken begyndte
|
||||
at fejle. Dybere set tabte hver teknik præcis ét byte fra starten af hver
|
||||
udgangschunk: `uid=1000(hanez)` blev printet som `id=1000(hanez)`, `PWNED-OK`
|
||||
som `WNED-OK`, `Linux 7.2.7` som `inux 7.2.7`.
|
||||
|
||||
**Årsag.** `fooc` plejede at `dup2()`e socket'en på sit eget stdin/stdout og
|
||||
`execv()`e en *lokal* `/bin/sh`, mens et forket relay-barn også læste den samme
|
||||
socket for at flytte output til terminalen. Kernen er ligeglad med, at de to
|
||||
samarbejder. En streamsocket har **én** læse-cursor, og hver læser flytter den,
|
||||
så bytes deles uforudsigeligt mellem dem. Den lokale shell, der er en
|
||||
interaktiv login-shell, læste præcis ét byte og smed det væk — hver eneste
|
||||
gang. `strace -f` viste det øjeblikkeligt:
|
||||
|
||||
```
|
||||
read(0, "u", 1) <- den lokale shell, æder et byte
|
||||
read(4, "id=1000(hanez) gid=1000(hanez) g".., 310) <- relay'et, 1 byte for kort
|
||||
```
|
||||
|
||||
**Fix.** Der er slet ingen shell på denne side. Der er præcis én shell i hele
|
||||
billedet, og den er på offeret, inde i den kaprede proces, med
|
||||
TCP-forbindelsen som dens stdin/stdout. Denne side flytter kun bytes. Hvis du
|
||||
nogensinde har brug for to forbrugere af en stream, skal den stream have én
|
||||
eneste læser, der bevidst demultiplekser den.
|
||||
|
||||
**Meta-lektionen.** Det første "fungerende" resultat var et falsk positivt,
|
||||
produceret af at pty'en ekkoede harnessens egen kommandolinje tilbage til den,
|
||||
og fixen for det falske positive er det, der afslørede den rigtige fejl. Tests,
|
||||
der ikke kan fejle, er værre end ingen tests, fordi de forvandler "jeg ved
|
||||
ikke" til "det virker". En test-harness fortjener samme mistænksomhed som den
|
||||
kode, den tester.
|
||||
|
||||
---
|
||||
|
||||
## At pille ved det
|
||||
|
||||
Ting, der er værd at prøve, nogenlunde i den rækkefølge, du lærer mest af dem:
|
||||
|
||||
1. **Ændr `FOOD_BUFSZ` til 128.** Kør `fooc` igen. Det burde stadig virke uden
|
||||
ændringer, fordi det læser offset ud af disassembly'en. Bræk det så i
|
||||
hånden — hardkod 88 — og se det crashe. Tilføj derefter et andet array
|
||||
mellem `buf` og de gemte registre, og se den automatiske erkendelse klare
|
||||
det.
|
||||
|
||||
2. **Tilføj `-Wformat-security` og se, hvad format-string-stien gør.** Send
|
||||
`%p %p %p %n` og se `food` lække stacken.
|
||||
|
||||
3. **Brug gdb.** `make debug`, derefter:
|
||||
```gdb
|
||||
(gdb) break food.c:393 # det read(), der løber over
|
||||
(gdb) run -p 2342
|
||||
(gdb) info registers rsp rbp
|
||||
(gdb) x/24gx $rsp # bemærk, hvor returadressen ligger
|
||||
(gdb) c # i et andet terminal: ./fooc -t ret2win
|
||||
```
|
||||
`SIGSEGV`-handleren logger `REG_RIP` og `REG_RSP`, så `food.log` fortæller
|
||||
dig, om kapringen landede, selv når barnet dør, før du kan koble på.
|
||||
|
||||
4. **Slet justeringsfixen** i `fooc.c` og se `#GP`-fejlen med
|
||||
`si_addr = 0`-signaturen. Læs derefter `/proc/sys/kernel/randomize_va_space`
|
||||
og tænk over, hvad ASLR randomiserer, og hvad det ikke gør.
|
||||
|
||||
5. **Bræk libc-symbolopløsningen** og se `fooc` tilpasse sig. Hele pointen med
|
||||
`/proc/self/maps`-tilgangen er, at intet offset er hardkodet.
|
||||
|
||||
6. **Skriv en fjerde teknik.** En `ret2csu`-lignende kæde, hvis du kan finde
|
||||
`__libc_csu_init`, eller en SROP-kæde (`sigreturn`-frames lader dig
|
||||
kontrollere alle registre på én gang). Begge er rent ROP og behøver ingen
|
||||
eksekverbar hukommelse.
|
||||
|
||||
7. **Fix `food.c` ordentligt**, én fejl ad gangen, og kør exploitet igen efter
|
||||
hver fix. Rækkefølgen i tabellen øverst i `food.c` er nogenlunde den rigtige
|
||||
rækkefølge at tænke i: begræns først read'et, for intet andet betyder noget,
|
||||
før fejlen er væk.
|
||||
|
||||
---
|
||||
|
||||
## Oprydning
|
||||
|
||||
```sh
|
||||
make stop # stopper food
|
||||
make clean # fjerner build-produkter; lader food.log være i fred
|
||||
pkill -x sh # kun hvis du har strejfende shells fra en test, der gik skævt
|
||||
```
|
||||
|
||||
Bemærk: `pkill -x food` matcher proces**navnet** præcist. Brug ikke
|
||||
`pkill -f ./food` — det mønster matcher også den shell, du har skrevet det i,
|
||||
og dræber din egen session. Det er ikke en hypotese; det skete, mens dette
|
||||
laboratorium blev bygget.
|
||||
Loading…
Add table
Add a link
Reference in a new issue