Initial commit
This commit is contained in:
commit
394e3be54d
41 changed files with 16315 additions and 0 deletions
309
wosuid/README.NO.md
Normal file
309
wosuid/README.NO.md
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
# `wosuid`-laboratoriet — root-RCE **uten** setuid-bit
|
||||
|
||||
```
|
||||
foowosd en bevisst sårbar daemon som er root fordi den ble *STARTET* som
|
||||
root (port 2344, bare loopback som standard)
|
||||
foowosc exploitet: forvandler et stack-overløp til en **root**-shell ved å
|
||||
utføre shellcode — de samme 23 bytene som knekte user-level-
|
||||
`food`-daemonen i hovedlaboratoriet
|
||||
```
|
||||
|
||||
Det er det tredje laboratoriet i serien. Samme exploit-verktøykjede, samme
|
||||
stil, én fundamental forskjell:
|
||||
|
||||
| Laboratorium | hvordan målprosessen blir root | `uid=0(root)`-shell? |
|
||||
|----------|------------------------------------------------|----------------------|
|
||||
| food/fooc| aldri — det er en vanlig user-daemon | nei |
|
||||
| foosd/foosc | SUID-biten (`chmod u+s`) — euid 0, ruid 1000 | ja (krever `setreuid` i shellcoden, fordi bash nullstiller euid→ruid) |
|
||||
| **foowosd/foowosc** | **ingen — root *starter* daemonen** (sudo / systemd `User=root`) | **ja (vanlig `execve`-shellcode)** |
|
||||
|
||||
Setuid-biten er et *transportmiddel* for privilegier — ikke privilegiene selv.
|
||||
En daemon startet av root har reell, effektiv og lagret uid alle lik 0. For
|
||||
kjernen er det root, punktum; den kan og vil ikke vite om prosessen kom dit via
|
||||
`+s` på en fil eller via `sudo ./foowosd`. Overløpet i en root-startet daemon er
|
||||
altså et root-exploit — *«jeg har ingen SUID-binærfiler» er ikke det samme som
|
||||
«jeg er ikke utnyttbar».*
|
||||
|
||||
Det er hele leksjonen i dette laboratoriet. Alt nedenfor er maskineriet.
|
||||
|
||||
---
|
||||
|
||||
## Rask start (det brukeren ba om)
|
||||
|
||||
```
|
||||
cd wosuid
|
||||
make # bygger daemonen, exploitet og test-harnessen
|
||||
```
|
||||
|
||||
### Det ekte — kjør daemonen som **root**
|
||||
|
||||
```
|
||||
sudo make run-root # starter foowosd som uid 0 (prosess, ikke filtillstand)
|
||||
make test-root # hver teknikk skal nå gi uid=0(root)
|
||||
```
|
||||
|
||||
### Ikke sudo? Den identiske kjernesti via en user-namespace
|
||||
|
||||
```
|
||||
make run-root-ns # uid 0 i en user-namespace — ingen passord nødvendig
|
||||
make test-root # samme dommer; brukes av CI og alle uten sudo
|
||||
```
|
||||
|
||||
### Baseline — daemon som din vanlige bruker (intet root noen steder)
|
||||
|
||||
```
|
||||
make run # foowosd kjører med dine uid-er
|
||||
make test # exploits lander shells, men `root` forventes MISSING
|
||||
```
|
||||
|
||||
### Oppryddingsritual (alltid: dette er et root-shell-laboratorium)
|
||||
|
||||
```
|
||||
make stop
|
||||
```
|
||||
|
||||
`foowosd` er *ikke* setuid, og intet i dette biblioteket gjør noen gang
|
||||
`chmod +s` — det er poenget. Den farlige tilstanden er **prosessen**, ikke
|
||||
filen.
|
||||
|
||||
---
|
||||
|
||||
## Når du blir bedt om å sette SUID-biten
|
||||
|
||||
Det blir du **ikke**. Dette laboratoriet har bevisst ingen SUID-bit:
|
||||
|
||||
- `foowosd` bygges, eies og har vanlige tillstander som ethvert annet program.
|
||||
- Det blir root, som ekte daemoner gjør — ved å bli *startet* av root.
|
||||
- `make run-root` bruker `sudo` til nøyaktig det, og `make run-root-ns` skaffer
|
||||
en ekte uid-0-prosess helt uten noe av det.
|
||||
|
||||
Suid-biten tilhører *søster*-laboratoriet (`foosd`). Kontrasten mellom de to
|
||||
er pensum:
|
||||
|
||||
1. SUID-laboratoriet: biten gir **euid 0, men ruid 1000** →
|
||||
`execve("/bin/sh")` nedgraderes av bashs vakt (`euid != ruid` → reset) →
|
||||
shellcoden må først kalle `setreuid(0,0)` (32-byte-payload).
|
||||
2. Dette laboratoriet: root **starter** prosessen → **ruid == euid == 0** →
|
||||
vakten har ingenting å nullstille → den vanlige 23-byte `execve`-shellcoden
|
||||
beholder root.
|
||||
|
||||
Samme overløp. Samme teknikk. Forskjellig *opprinnelse* av privilegier,
|
||||
annerledes payload-form. Det er leksjonen i miniatyr.
|
||||
|
||||
---
|
||||
|
||||
## Protokollen
|
||||
|
||||
Uansett hvilken slags klient som kobler til, hilser foowosd på den med:
|
||||
|
||||
```
|
||||
FOOWOSD 1.0 ids=0/0 leak stack=0x7ffd… libc=0x7f… (banneret + leaks)
|
||||
BUF=0x7ffd… (bufferadressen)
|
||||
```
|
||||
|
||||
`ids=euid/ruid` er *«er jeg root?»*-sidekanalen. foowosc skriver ut en høy
|
||||
advarsel når euid ikke er 0 (dvs. du startet daemonen som en vanlig bruker):
|
||||
payloaden lander fortsatt, men shellen blir en bruker-shell, og å kalle
|
||||
exploitet «i stykker» ville vært feil — det eskalerer bare ikke.
|
||||
|
||||
> Staveform-merknad: `ids=`, ikke `euid=`/`ruid=`. Test-harnessen beviser en
|
||||
> levende `id` ved å matche den bokstavelige formen `uid=NNN(`, så banneret må
|
||||
> aldri inneholde en delstreng som selv oppfyller sjekken. (I SUID-laboratoriet
|
||||
> produserte nøyaktig den fellen et spektakulært falskt positivt.)
|
||||
|
||||
---
|
||||
|
||||
## Exploit-teknikkene (`foowosc -t …`)
|
||||
|
||||
Alle fire exploit-stiene nedenfor virker mot foowosd. Når daemonen er root,
|
||||
gir **alle som spawner noe, root** — i motsetning til SUID-laboratoriet der
|
||||
ret2win/ret2libc stille og rolig ble nedgradert til uid 1000 av bashs vakt. Her
|
||||
er det intet mismatch å vokte mot.
|
||||
|
||||
| `-t` | hva som skjer | når daemonen er root |
|
||||
|---------------|---------------------------------------------------------------------|---------------------|
|
||||
| `shellcode` | 23-byte-`execve("/bin/sh", NULL, NULL)` kjører på stacken. | **root-shell** (standard) |
|
||||
| `ret2win` | hopp til `win()` → `execl("/bin/sh")` | **root-shell** |
|
||||
| `ret2libc` | ROP: `pop rdi; ret` → `"/bin/sh"` → `system()` | **root-shell** |
|
||||
| `demo` | bare søppel-overløp — forvent et SIGSEGV i daemonloggen | krasj, etter design |
|
||||
| `leak` | skriver bare ut leaks, sender ingen payload | n/a |
|
||||
|
||||
```
|
||||
./foowosc -t shellcode # interaktiv; standard-target 127.0.0.1:2344
|
||||
./foowosc -t shellcode -n # send og rapporter, ingen interaktiv økt
|
||||
```
|
||||
|
||||
En vellykket interaktiv økt videresender terminalen din til shellen *på
|
||||
offeret* — det er nøyaktig én shell i bildet, og det er `/bin/sh` som kjører
|
||||
som root inne i foowosd. Skriv `id` for å se `uid=0(root)`.
|
||||
|
||||
### Hvorfor det ikke finnes noen `ret2win-root`-teknikk her
|
||||
|
||||
foosc hadde én — den hoppet til et `win_root()` som kalte `setreuid(0,0)` før
|
||||
exec, fordi en setuid-prosess kjørte med en reell uid som fortsatt sa 1000. En
|
||||
root-*startet* prosess har den reelle uid-en allerede 0; det er ingenting å
|
||||
rydde, så den ekstra funksjonen og teknikken ville ikke lært noe. Fjernet.
|
||||
|
||||
---
|
||||
|
||||
## Hva foowosc gjør, steg for steg
|
||||
|
||||
1. **Statisk analyse** — `objdump -d` av `./foowosd`. Finner
|
||||
`vulnerable_handler`, `win()`, det `lea -0x50(%rbp)` som adresserer `buf`,
|
||||
og det første nakne `ret`. Ut fra forskyvningen beregner det
|
||||
`rip_off = 80 + 8 = 88`. Ingenting er hardkodet; det overlever en ombygging.
|
||||
2. **Selv-introspeksjon** — leser sitt eget `/proc/self/maps` og `dlsym()`er
|
||||
`system`/`read` for å lære libc-*offsets*. Targetets libc-base er
|
||||
`leaked_read − off_read`, deretter `system = base + off_system`, osv. Denne
|
||||
delta-aritmetikken er grunnen til at exploits overlever libc-versjoner.
|
||||
3. **Koble til** — leser banneret/leaks (`ids=`, `stack=`, `libc=`, `BUF=`).
|
||||
4. **Bygg payloaden** — for `shellcode`: 23 bytes maskinkode, padding til
|
||||
`rip_off`, deretter den lagrede RIP = `buf` (så `ret` hopper inn på koden).
|
||||
For `ret2win`/`ret2libc`: adresser beregnet ut fra analysen — ingen
|
||||
utførelse av stacken nødvendig.
|
||||
5. **Justeringsfixen** — et kapret nakent `ret` gir callee-en
|
||||
`rsp ≡ 8 (mod 16)`, og glibcs SSE2-kode `movaps`-feiler på en ikke-justert
|
||||
stack (crash-reporteren logger `si_addr=(nil)` — fingerpeket). foowosc
|
||||
setter inn ett ekstra `ret`-gadget før det ekte målet og gjenoppretter
|
||||
invarianten. Silkehansketeknikk i et shellcode-laboratorium, men det er
|
||||
forskjellen mellom en payload som «noen ganger virker» og en som alltid
|
||||
virker.
|
||||
6. **Send, deretter videresend** — offerprosessen *er* shellen; denne prosessen
|
||||
splisser bare bytes. Ingen lokal shell, ingen annen leser —
|
||||
single-read-cursor-feilen (ett spist byte per chunk) er dokumentert i
|
||||
`become_shell()`.
|
||||
|
||||
---
|
||||
|
||||
## Daemonens bevisste feil (alle i `foowosd.c`, alle ekte CWE-klasser)
|
||||
|
||||
| # | feil | CWE | notat |
|
||||
|---|-----|-----|------|
|
||||
| 1 | `read(fd, buf, 512)` inn i et 64-byte stack-buffer | CWE-120 | overløpet: 448 bytes forbi `buf`, lagret RIP ved +88 |
|
||||
| 2 | bare `snprintf(line, …, "%.*s", …)`; men angriper-`%` i ekko-stien | CWE-134 | leaket er her den reelle payloaden; et `%n` i en *root*-prosess ville vært write-what-where som root |
|
||||
| 3 | barn beholder root mens de håndterer upålitelige inputs | CWE-271 | det korrekte `drop_privs()` (setgroups→setgid→setuid, i den rekkefølgen, med verifikasjon) står i filen, kommentert, *bevisst aldri kalt* |
|
||||
| 4 | `ids=`, `stack=`, `libc=`, `BUF=` avslørt til enhver klient | CWE-200 | uten disse leaks kunne ikke shellcode- og ret2libc-teknikkene beregne adresser (ASLR ville beseiret dem) |
|
||||
|
||||
Handleren har nøyaktig samme `buf[64]`/`read(512)`-form som de to andre
|
||||
laboratoriene, så den felles objdump-baserte oppdagelsespipelinen virker
|
||||
uendret.
|
||||
|
||||
---
|
||||
|
||||
## Slik inspiserer du daemonen (læringssti)
|
||||
|
||||
```
|
||||
make status # kjører den? som hvilken uid? filtillstand vises
|
||||
make run-root # eller run / run-root-ns
|
||||
./foowosc -t leak # se banneret og leaks, send ingenting
|
||||
./foowosc -t demo # søppel-overløp -> SIGSEGV, logget med RIP/rsp
|
||||
./foowosc -t shellcode # den interaktive root-shellen
|
||||
make test-root # full matrise, alle teknikkene, --must-root
|
||||
```
|
||||
|
||||
Crash-reporter: Ved SIGSEGV logger daemonen feiladressen, RIP og RSP. Et `ret`
|
||||
inn i en ikke-kanonisk `0x4141…` feiler ved `ret`-et selv (RIP som `0x4028xx`,
|
||||
`si_addr=(nil)`) — verdt å vite før du feilleser en logglinje som en
|
||||
NULL-dereferanse.
|
||||
|
||||
---
|
||||
|
||||
## Hvorfor shellcoden er 23 bytes, ikke 32
|
||||
|
||||
```
|
||||
31 f6 xor esi, esi ; argv = NULL
|
||||
31 d2 xor edx, edx ; envp = NULL
|
||||
48 bf 2f62696e2f736800 movabs rdi, "/bin/sh\\0"
|
||||
57 push rdi
|
||||
48 89 e7 mov rdi, rsp
|
||||
6a 3b push 0x3b ; 59 = execve
|
||||
58 pop rax
|
||||
0f 05 syscall
|
||||
```
|
||||
|
||||
SUID-laboratoriet trenger `setreuid(0,0)` foran dette. Dette laboratoriet ikke,
|
||||
av den grunnen som gjentas overalt: **ruid er allerede 0**, fordi root startet
|
||||
prosessen. `make verify` beviser at bytene i `foowosc.c` er byte-for-byte det
|
||||
`shellcode.S` assemblerer til.
|
||||
|
||||
---
|
||||
|
||||
## Mottiltak — hva `make hardened` endrer
|
||||
|
||||
Hardet build (`-fstack-protector-strong -fPIE -pie -z noexecstack`):
|
||||
|
||||
| teknikk | sårbar `foowosd` | hardet `foowosd_hardened` |
|
||||
|-----------|----------------------|------------------------------|
|
||||
| `shellcode` | root-shell (kjørbar stack) | SIGSEGV ved canary-sjekken / NX |
|
||||
| `ret2win` / `ret2libc` | root-shell | canary aborter `ret` — men merk: en *PIE*-build gjør også disse adressene tilfeldige |
|
||||
| `demo` | SIGSEGV, logget | SIGSEGV, logget |
|
||||
|
||||
`make test-hardened` demonstrerer det live. Den viktige observasjonen er ikke
|
||||
bare at mottiltakene drepte teknikkene — det er at de **ikke** gjorde daemonen
|
||||
til «ikke-root». En hardet build som fortsatt *startes* som root, er fortsatt en
|
||||
root-daemon; mottiltaket hever bare lista for angriperen. Minste privilegium
|
||||
(`drop_privs()`) og minnesikkerhet er to forskjellige feil, og en daemon som
|
||||
ikke trenger root, bør ikke ha det.
|
||||
|
||||
---
|
||||
|
||||
## Sikkerhetsgelendere (samme politikk som SUID-laboratoriet)
|
||||
|
||||
- **Bare loopback.** foowosd nekter å binde noe annet enn `127.0.0.1` /
|
||||
`localhost` / `::1`, med mindre du gir `-L`. En root-daemon på et ekte
|
||||
grensesnitt er en *fjern* root-tjeneste. `-L` finnes bare for å vise vakten;
|
||||
ikke bruk den på noe som betyr noe.
|
||||
- **Tilstanden logges høyt.** Ved start skriver den ut `ruid/euid` og om dette
|
||||
er en root-prosess, så du alltid vet hvilket exploit-resultat du skal vente.
|
||||
- **Dommer kommer fra exit-status** i `make test*` (pty-harnessens
|
||||
returkode), aldri fra å greppe stdout-en dens — grepbar utdata lyver.
|
||||
- **pty-en må kjøre cooked + ECHO off**, ellers ekkoer harnessen sin egen
|
||||
kommandolinje og forfalsker markøren. Harnessen slår av ECHO og beholder
|
||||
ECHONL på.
|
||||
- **Oppryddingsritual:** `make stop` etter hver økt. Hvis daemonen er
|
||||
root-eid, sier `stop` deg å kjøre `sudo pkill -x foowosd`.
|
||||
- Kjør aldri dette på en vert du bryr deg om. Det finnes for å dele ut
|
||||
`uid=0`-shells over loopback-grensesnittet.
|
||||
|
||||
---
|
||||
|
||||
## Øvelser
|
||||
|
||||
1. Kjør `make run` (user-daemon), deretter `./foowosc -t shellcode`. Hvorfor er
|
||||
shellen ikke root? (Sjekk `ids=` i banneret — foowosc sier deg det før du i
|
||||
det hele tatt kobler til.)
|
||||
2. `make stop && sudo make run-root && make test-root`. Forklar ut fra
|
||||
bannerlinjen hvorfor alle fire teknikkene nå gir `uid=0(root)`.
|
||||
3. Finn i `foowosd.c` `drop_privs()` og les *hvorfor rekkefølgen* av
|
||||
`setgroups → setgid → setuid` betyr noe. Bestem hvor i `main()` den ville
|
||||
hørt hjemme, og hva laboratoriets angrepsflate blir når den faktisk kalles.
|
||||
4. Beregn `rip_off` for hånd ut fra `objdump -d foowosd`: finn `buf`s
|
||||
`lea -0xNN(%rbp)` inne i `vulnerable_handler`, deretter `NN + 8`. foowosc
|
||||
gjør nøyaktig det; sjekk regnestykket dens mot ditt eget.
|
||||
5. `make hardened && make test-hardened`. Hvilken teknikk faller for canaryen,
|
||||
og hvilken for NX? Hvorfor endrer ikke hærdning hva `make status` rapporterer
|
||||
om *prosessen*?
|
||||
6. Sammenlign de to laboratorienes shellcodes: 23 bytes her, 32 for foosd. Hva
|
||||
gjør de ekstra 9 bytene, og hvorfor trengs de bare i SUID-tilfellet?
|
||||
7. Les `become_shell()`s kommentar om den enkelte lese-cursoren. Gjenskap
|
||||
feiltilstanden mentalt: to lesere på én socket betyr at login-shellen spiser
|
||||
ett byte per chunk — «uid=1000…» ankommer som «id=1000…». Hvorfor kan en
|
||||
relay-prosess aldri ha denne feilen?
|
||||
|
||||
---
|
||||
|
||||
## Filer
|
||||
|
||||
```
|
||||
foowosd.c den sårbare root-daemonen (hver linje kommentert)
|
||||
foowosc.c exploitet (hver linje kommentert)
|
||||
shellcode.S referanse-assembly for 23-byte-payloaden
|
||||
tests/pty_wosuid_test.c pty-harnessen (markør + streng id-form-sjekk)
|
||||
Makefile build / run / run-root / run-root-ns / test /
|
||||
test-root / verify / hardened / clean …
|
||||
```
|
||||
|
||||
Søster-laboratorier: `../food.c`/`../fooc.c` (user-level-baseline, port 2342)
|
||||
og `../suid/` (SUID-root-daemon `foosd`/`foosc`, port 2343). Portene er bevisst
|
||||
forskjellige — du kan kjøre alle tre samtidig og kryssjekke `ids=`-linjene
|
||||
deres i banneret.
|
||||
Loading…
Add table
Add a link
Reference in a new issue