Initial commit
This commit is contained in:
commit
394e3be54d
41 changed files with 16315 additions and 0 deletions
404
suid/README.FR.md
Normal file
404
suid/README.FR.md
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
# Lab RCE racine par SUID — `foosd` (démon) + `foosc` (exploit)
|
||||
|
||||
Un compagnon du laboratoire principal (`food` / `fooc`, un démon ordinaire où
|
||||
un débordement de tampon vous donne un shell *utilisateur*). Celui-ci ajoute le
|
||||
changement d'un seul caractère le plus dangereux d'Unix : **le bit setuid**.
|
||||
|
||||
> `chmod u+s` transforme « l'attaquant peut exécuter du code sur cette machine »
|
||||
> en « l'attaquant peut exécuter du code en tant que **root** sur cette
|
||||
> machine ».
|
||||
|
||||
Cette phrase, c'est tout le laboratoire. Tout ce qui suit est le mécanisme
|
||||
qu'il y a dessous, écrit noir sur blanc, pour que lorsque vous écrivez votre
|
||||
propre logiciel, vous sachiez précisément quels deux ou trois attributs de
|
||||
système de fichiers et flags de compilateur décident si un bug de sécurité
|
||||
mémoire dans votre code est une nuisance ou un shell root.
|
||||
|
||||
La démo finale, quand `foosd` est setuid-root, est un **shell root** ouvert sur
|
||||
le réseau en exécutant 32 octets de shellcode écrite à la main.
|
||||
|
||||
---
|
||||
|
||||
## 1. Ce que fait réellement le bit setuid
|
||||
|
||||
Chaque processus Linux porte trois user-ID, et le bit setuid touche à la
|
||||
relation entre eux :
|
||||
|
||||
| ID | Nom | Signification |
|
||||
|----|------|---------|
|
||||
| `ruid` | user-ID réel | le compte qui a *démarré* le processus |
|
||||
| `euid` | user-ID effectif | ce que le noyau vérifie quand il applique les accès |
|
||||
| (saved) | set-user-ID sauvegardé | un « créneau » auquel un processus privilégié peut revenir plus tard |
|
||||
|
||||
Un programme normal a `ruid == euid`. Quand vous exécutez un binaire avec le
|
||||
bit setuid posé, appartenant à root :
|
||||
|
||||
```text
|
||||
ruid = vous (ex. 1000, « hanez »)
|
||||
euid = le propriétaire (ex. 0, « root »)
|
||||
```
|
||||
|
||||
Le processus a donc **l'autorité de root**, même si l'utilisateur qui l'a
|
||||
lancé est parfaitement ordinaire. Chaque contrôle que le noyau effectue — ce
|
||||
processus peut-il lire `/etc/shadow` ? écrire un fichier ? tuer un autre
|
||||
processus ? — est tranché avec `euid`, donc « oui, il est root ».
|
||||
|
||||
`foosd` est un démon réseau. Il lie un port puis `fork()` un enfant par
|
||||
connexion. Un fork *hérite* de l'euid, donc chaque enfant qui traite une
|
||||
connexion est aussi root. Le débordement dans `vulnerable_handler()` de
|
||||
`foosd` est donc un débordement *à l'intérieur d'un processus root*.
|
||||
|
||||
**Diagnostiquez-le vous-même quand le démon tourne :**
|
||||
|
||||
```console
|
||||
$ ./foosd ... # voyez la ligne de log qu'il affiche au démarrage
|
||||
[foosd 1234] startup: ruid=1000 euid=0 -> ROOT process
|
||||
```
|
||||
|
||||
et depuis l'exploit :
|
||||
|
||||
```console
|
||||
$ ./foosc -t leak
|
||||
foosc: target euid=0 ruid=1000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Le lab en un coup d'œil
|
||||
|
||||
| Fichier | Rôle |
|
||||
|------|------|
|
||||
| `foosd.c` | Le démon volontairement vulnérable (propriétaire des bugs). Exécutez-le en *binaire setuid-root* pour la démo du shell root. |
|
||||
| `foosc.c` | L'exploit. Utilise par défaut la technique de shellcode `setreuid + execve` de 32 octets. |
|
||||
| `shellcode.S` | La shellcode de référence ; `make verify` la diff contre le tableau d'octets dans `foosc.c`. |
|
||||
| `tests/pty_suid_test.c` | Harnesse de test. Conduit `foosc` à travers un pseudo-terminal et prouve à la fois « un shell a tourné » *et* « il était root » (`uid=0(`). |
|
||||
| `Makefile` | Compilation, helpers `setuid`/`unsetuid`, matrice de test. |
|
||||
| `README.md` | Ce fichier. |
|
||||
|
||||
> **Pourquoi un pty ?** La dernière action de l'exploit est de relayer votre
|
||||
> terminal vers le shell qui tourne sur la victime. Un pipe ou un here-doc
|
||||
> arrive du mauvais côté du relais ; un vrai terminal est requis.
|
||||
|
||||
---
|
||||
|
||||
## 3. Démarrage rapide
|
||||
|
||||
```console
|
||||
$ make # compilez tout, en tant que votre utilisateur normal
|
||||
$ make setuid # une fois, demande sudo : chown root + chmod u+s
|
||||
$ make run # démarre foosd sur 127.0.0.1:2343
|
||||
$ make test-suid # matrice complète ; shellcode + ret2win-root doivent donner root
|
||||
```
|
||||
|
||||
Test de fumée interactif :
|
||||
|
||||
```console
|
||||
$ ./foosc -t shellcode
|
||||
...
|
||||
foosc: target euid=0 ruid=1000
|
||||
foosc: shell is on the victim (root if foosd is SUID); relaying
|
||||
# id
|
||||
uid=0(root) gid=0(root) groups=0(root) <-- vous êtes root, sur la victime
|
||||
# exit
|
||||
```
|
||||
|
||||
Quand vous avez fini :
|
||||
|
||||
```console
|
||||
$ make stop
|
||||
$ make unsetuid # hygiène : ne laissez jamais un binaire root SUID traîner
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. *Quand dois-je poser le bit SUID ?* — la réponse que vous avez demandée
|
||||
|
||||
Exactement **une fois, après la compilation, avant de démarrer le démon pour
|
||||
les démos de shell root** — et uniquement sur une machine qui est à vous,
|
||||
bonne à jeter et déconnectée du réseau :
|
||||
|
||||
```console
|
||||
$ make # compilez foosd, foosc, les tests
|
||||
$ make setuid # <-- L'INSTANT. sudo chown root:root foosd && sudo chmod u+s foosd
|
||||
$ make run # démarrez APRÈS avoir posé le bit
|
||||
```
|
||||
|
||||
Deux règles plus importantes que le moment précis :
|
||||
|
||||
1. **Posez-le seulement quand le binaire est fini.** Si vous recompilez
|
||||
(`make` / `make clean`) après avoir posé le bit, vous tombez sur un
|
||||
« Permission denied » en écrivant les fichiers de sortie appartenant à root
|
||||
— et si vous forcez la recompilation, la chaîne d'outils recrée le fichier
|
||||
**sans** le `s` et défait silencieusement la configuration. L'ordre
|
||||
canonique à chaque recompilation est donc
|
||||
|
||||
```console
|
||||
$ make unsetuid && make && make setuid
|
||||
```
|
||||
|
||||
2. **Enlevez-le quand vous avez fini.** `make unsetuid`. Un binaire setuid
|
||||
vivant, appartenant à root, avec un bug exploitable dans votre arborescence,
|
||||
ce n'est pas un outil pédagogique, c'est un trou root avec une erreur de
|
||||
compilation entre lui et rien. Sur une machine partagée ou de production :
|
||||
**ne faites rien de tout cela.** Le démon refuse d'ailleurs par défaut de se
|
||||
lier ailleurs qu'en loopback (voir §7).
|
||||
|
||||
Si vous exécutez l'exploit *sans* jamais poser le bit, rien ne casse — la
|
||||
payload atterrit toujours, et vous obtenez toujours un shell. La différence
|
||||
tient en un seul chiffre, et l'exploit le dit à voix haute :
|
||||
|
||||
```console
|
||||
foosc: WARNING: the daemon is NOT running with euid 0.
|
||||
The payload will still land, but the shell will be
|
||||
a plain user shell, not root.
|
||||
Fix: sudo make setuid
|
||||
```
|
||||
|
||||
Le résultat « ça marche, mais pas root » fait lui-même partie du lab. Gardez-le
|
||||
en tête pour la section suivante.
|
||||
|
||||
---
|
||||
|
||||
## 5. Le mécanisme — et la pirouette qui rend SUID intéressant
|
||||
|
||||
### 5.1 Le débordement (identique à `food`)
|
||||
|
||||
Le handler de `foosd` donne à un `read()` 512 octets de confiance en lui
|
||||
tendant un tampon de pile de 64 octets :
|
||||
|
||||
```c
|
||||
char buf[64];
|
||||
n = read(fd, buf, 512); /* <- CWE-120 : 448 octets par-dessus le bord */
|
||||
```
|
||||
|
||||
Sur x86-64, la pile croît vers le bas. L'exploit écrit 64 octets de bourrage
|
||||
pour remplir `buf`, 8 pour remplir le pointeur de trame sauvegardé et 8 de
|
||||
plus pour remplacer l'**adresse de retour sauvegardée**. Quand
|
||||
`vulnerable_handler` exécute `ret`, le CPU pousse la valeur de l'attaquant
|
||||
dans `RIP` — une exécution de code contrôlée par l'attaquant. L'exploit trouve
|
||||
la distance exacte (88 octets pour cette compilation) en analysant la sortie
|
||||
de `objdump` au lieu de la hardcoder, donc le chiffre survit aux
|
||||
recompilations.
|
||||
|
||||
### 5.2 La pirouette : le shell refuse d'être root
|
||||
|
||||
Voici où penser « bug SUID → spawn /bin/sh → root » irait de travers, et
|
||||
pourquoi ce lab a exactement la forme qu'il a.
|
||||
|
||||
Quand un programme setuid-root tourne, son `ruid` est toujours l'utilisateur
|
||||
qui l'a lancé, et son `euid` est root. Si le programme — ou l'attaquant — lance
|
||||
maintenant un shell :
|
||||
|
||||
* `execve("/bin/sh")` ne change **pas** les uids ; le nouveau processus hérite
|
||||
de `(ruid=1000, euid=0)`.
|
||||
* bash (et dash) **vérifie exactement cet état au démarrage**. D'après le
|
||||
manuel de bash : *« If the shell is started with the effective user (group)
|
||||
id not equal to the real user (group) id, and the -p option is not supplied,
|
||||
… the effective user id is set to the real user id. »*
|
||||
|
||||
Donc le shell se regarde et *lâche root* — une défense que les auteurs de
|
||||
shell ont construite précisément contre cette attaque (la justification
|
||||
historique était le problème des shell setuid / scripts setuid). Le résultat,
|
||||
ce sont les cas « ça marche, mais pas root » :
|
||||
|
||||
| Technique | Ce qu'elle exécute | uid résultant |
|
||||
|-----------|------------------|---------------|
|
||||
| `ret2win` | `win()` de `foosd` → `execl("/bin/sh")` | **1000** — shell atterri, root réinitialisé par bash |
|
||||
| `ret2libc` | `system("/bin/sh")` → `sh -c '/bin/sh'` tout frais | **1000** — même réinitialisation, un niveau plus bas |
|
||||
| `ret2win-root` | `win_root()` de `foosd` → `setreuid(0,0); execl("/bin/sh")` | **0** — ruid nettoyé depuis le C |
|
||||
| `shellcode` | 32 octets : `setreuid(0,0); execve("/bin/sh")` | **0** — ruid nettoyé depuis le code machine |
|
||||
|
||||
Celles qui atteignent root diffèrent de celles qui ne l'atteignent pas par
|
||||
exactement une idée : **elles nettoient l'uid *réel*, pas seulement
|
||||
l'effectif.**
|
||||
|
||||
```c
|
||||
setuid(0) /* met euid à 0, mais ruid reste 1000 :
|
||||
bash voit toujours euid != ruid et réinitialise QUAND MÊME. */
|
||||
setreuid(0, 0) /* met LES DEUX : ruid = euid = 0.
|
||||
bash voit des uids égaux et garde root. */
|
||||
```
|
||||
|
||||
C'est pourquoi la shellcode `/bin/sh` classique que vous trouvez partout sur
|
||||
Internet commence par un syscall de nettoyage d'uid — et c'est pourquoi la
|
||||
shellcode fait ici 32 octets au lieu de 23 : les cinq premières instructions
|
||||
sont
|
||||
|
||||
```asm
|
||||
xor edi, edi ; ruid = 0
|
||||
xor esi, esi ; euid = 0
|
||||
push 0x71 ; 113 = __NR_setreuid
|
||||
pop rax
|
||||
syscall
|
||||
```
|
||||
|
||||
### 5.3 Alors, c'est quoi l'exploit, de bout en bout ?
|
||||
|
||||
1. `foosc` lit le banner de `foosd` sur la socket. Il obtient :
|
||||
- `ids=0/1000` — euid/ruid (l'autodiagnostic SUID)
|
||||
- `stack=…` et `libc=…` — des pointeurs (les fuites ASLR)
|
||||
- `BUF=…` — l'adresse exacte du tampon qu'il s'apprête à faire déborder
|
||||
2. Depuis le binaire cible (via `objdump`), il apprend `rip_off` et les
|
||||
adresses de `win()` / `win_root()`.
|
||||
3. Depuis *sa propre* libc (via `/proc/self/maps` + `dlsym` + un scan mémoire),
|
||||
il mesure les offsets de `system`, `read`, `/bin/sh` et un gadget
|
||||
`pop rdi; ret` — rien n'est hardcodé.
|
||||
4. Il assemble la payload. Pour `-t shellcode`, c'est :
|
||||
`[code setreuid+execve de 32 octets][bourrage jusqu'à RIP][ret-fix][adresse de buf]`.
|
||||
5. Le `read()` de `foosd` déborde ; le `ret` atterrit sur la shellcode ; le
|
||||
noyau exécute `setreuid(0,0)` (pas de souci : euid 0 est privilégié) puis
|
||||
`execve` de `/bin/sh`. bash démarre avec `ruid == euid == 0` et reste root.
|
||||
6. `foosc` relaie votre terminal vers ce shell root, jusqu'à ce que vous
|
||||
tapiez `exit`.
|
||||
|
||||
Un détail de commodité qui coûte cher à beaucoup de gens s'il est manqué :
|
||||
l'exploit teste chaque comportement de nettoyage d'uid **sans** avoir besoin du
|
||||
bit setuid au préalable. Lancez `make test` avant `make setuid`, et vous verrez
|
||||
chaque technique atterrir un shell avec `ROOT=MISSING` ; lancez `make test-suid`
|
||||
après `make setuid`, et `ROOT=SEEN` apparaît pour les deux techniques qui
|
||||
nettolent l'uid réel. Ce A/B est toute la leçon, jouée en dix secondes.
|
||||
|
||||
---
|
||||
|
||||
## 6. Les vieux one-liners — et pourquoi la plupart sont morts
|
||||
|
||||
Si vous avez lu sur SUID, vous avez lu sur les détournements de `PATH`, sur
|
||||
`LD_PRELOAD` et sur les shells setuid. Les trois sont classiques, et les trois
|
||||
échouent sur un système moderne contre *ce programme*. Ça vaut le coup de
|
||||
savoir exactement pourquoi, parce que les raisons sont les défenses que vous
|
||||
avez gratuitement :
|
||||
|
||||
| Classe d'attaque | Vieille affirmation | Pourquoi elle échoue sur une machine moderne |
|
||||
|--------------|-----------|------------------------------|
|
||||
| `LD_PRELOAD` d'une bibliothèque malveillante | « Le programme setuid charge mon `.so` et exécute mon code en tant que root. » | Le noyau marque un binaire setuid comme **AT_SECURE** ; glibc ignore alors `LD_PRELOAD`, `LD_LIBRARY_PATH`, `LD_DEBUG` et compagnie. L'environnement est traité comme *entrée non fiable*. `LD_PRELOAD` contre un binaire setuid est un no-op. |
|
||||
| Détournement de `PATH` (`system("ls")` avec un PATH empoisonné) | « Pointez PATH vers un répertoire avec mon faux `ls` ; le programme root l'exécutera. » | Un autre visage de la même défense : un processus AT_SECURE reçoit un **PATH assaini** (une valeur sûre par défaut, grossièrement `/usr/local/bin:/usr/bin:/bin`) pour `system()`/`execvp`, donc le répertoire empoisonné n'est jamais consulté. |
|
||||
| Injection de commande `system()` setuid | « La commande injectée s'exécute avec euid 0. » | `system()` exécute la commande dans un `/bin/sh` tout frais, et ce shell — §5.2 — réinitialise `euid = ruid` au démarrage. La commande injectée s'exécute avec l'uid *réel*. (C'est toujours un bug ; ça n'escalade juste plus via `/bin/sh`.) |
|
||||
| Shell root setuid sur disque (`cp /bin/sh /tmp; chmod u+s`) | « Exécute-le, obtiens root. » | Exactement la défense ci-dessus, et c'est pourquoi les distros modernes ne livrent aucun shell root setuid. Même si vous réussissez à en fabriquer un, bash refuse de garder euid 0 sauf s'il est lancé avec `-p`. |
|
||||
|
||||
Ce qui reste vivant, et c'est ce lab : **le programme est *déjà* root quand il
|
||||
tourne.** Vous n'avez besoin ni de l'environnement ni de `system()` ; vous avez
|
||||
besoin que le programme exécute *votre* code (via un bug de corruption
|
||||
mémoire) pendant qu'il est privilégié, et que votre code soit assez soigneux
|
||||
pour corriger lui-même le mismatch d'uid — `setreuid(0,0)` — avant de vous
|
||||
tendre un shell. Corruption mémoire + SUID est la combinaison qui finit encore
|
||||
en `uid=0`, et c'est exactement pourquoi les langages sûrs en mémoire, les
|
||||
canaries et les piles no-execute ne sont pas une décision de mode.
|
||||
|
||||
---
|
||||
|
||||
## 7. Les garde-fous intégrés au démon
|
||||
|
||||
`foosd` est volontairement le *pire* morceau de logiciel de ce dépôt, alors il
|
||||
porte aussi le plus de garde-fous :
|
||||
|
||||
1. **Loopback seulement, imposé.** `foosd` refuse toute adresse de bind hors
|
||||
loopback, sauf si vous passez `-L`. Un listener setuid-root sur une vraie
|
||||
interface est un service root distant ; le refus est la valeur par défaut,
|
||||
pour que l'état dangereux doive être tapé délibérément.
|
||||
2. **Autodiagnostic.** Au démarrage, il journalise `ruid`/`euid` et s'il
|
||||
tourne en root, pour que la console montre l'état dont dépend l'exploit.
|
||||
3. **Le log n'atteint jamais le client.** Le démon réserve un descripteur de
|
||||
log privé avant que les sockets ne remplacent fd 1, pour que la sortie du
|
||||
crash-reporter et les chemins internes ne puissent pas être relus sur le
|
||||
fil par l'attaquant.
|
||||
4. **Crash-reporter.** Un handler SIGSEGV journalise `RIP`/`RSP` — la valeur
|
||||
que l'attaquant a écrite dans l'adresse de retour — pour qu'une prise de
|
||||
contrôle réussie soit visible dans `foosd.log` au lieu d'être une mort
|
||||
silencieuse.
|
||||
5. **`make unsetuid`.** Retirer le bit est scripté, parce que le laisser posé
|
||||
est le mode d'échec que les gens ont réellement.
|
||||
|
||||
---
|
||||
|
||||
## 8. Contre-mesures — ce que chacune arrête et ce qu'elle *n'arrête pas*
|
||||
|
||||
Appliquées à `foosd` via `make hardened`, une par une ou ensemble :
|
||||
|
||||
| Contre-mesure | Ce qu'elle arrête | Ce qu'elle *n'arrête pas* |
|
||||
|------------|---------------|-------------------------|
|
||||
| `-fstack-protector-strong` (canary) | Le débordement : `ret` détecte une canary écrasée et abort avant que l'adresse de l'attaquant soit utilisée. Arrête ici **les quatre** techniques — elles partagent le même `read()` vulnérable. | Rien par *conception* : le binaire est toujours setuid-root ; un autre bug (format-string-`%n`, heap-overflow, use-after-free) n'a pas de canary à déclencher. |
|
||||
| `-fPIE -pie` (ASLR pour le binaire) | L'utilisation d'adresses `win()`/`win_root()` prévisibles (les techniques ret2win). | La technique shellcode, si une adresse de pile fuite encore (ligne `BUF=`). |
|
||||
| `-z noexecstack` (NX / W^X) | La shellcode : le CPU refuse de chercher des instructions sur une page data-only, donc un saut vers `buf` est un SIGSEGV. | ROP — exécuter du code qui existe déjà (`ret2libc`). |
|
||||
| Les trois ensemble | Un binaire difficile à déborder, randomisé, avec une pile non exécutable. Voilà à quoi ressemble une build durcie normale. | Le bit setuid. **Un binaire SUID durci reste un binaire SUID.** S'il survit un bug mémoire atteignable, c'est toujours « bug dans un processus root ». |
|
||||
|
||||
La preuve console, c'est `make test-hardened`, qui échange la build durcie et
|
||||
montre les techniques mourir à la canary, pendant que `foosd_hardened.log`
|
||||
capture `*** stack smashing detected ***`.
|
||||
|
||||
Deux contre-mesures de niveau conception, qu'aucun flag de compilateur ne
|
||||
fournit, et que le lab principal (`food`) utilise aussi :
|
||||
|
||||
- **Moindre privilège.** Un démon pour un port non privilégié (2343 > 1024)
|
||||
n'a aucun besoin légitime de root. Un `foosd` correct lierait puis ferait
|
||||
`setgroups`/`setgid`/`setuid` vers un compte non privilégié et *confirmerait
|
||||
que ça a tenu* (la version correcte est dans la source sous le nom de
|
||||
`drop_privs()`, jamais appelée — le non-appel est le bug n° 3 du lab).
|
||||
- **Limitez le read.** `n = read(fd, buf, sizeof(buf) - 1)`. Une ligne correcte
|
||||
surpasse tous les flags de compilateur du tableau.
|
||||
|
||||
---
|
||||
|
||||
## 9. Le protocole filaire (pour que vous puissiez lire le démon avec netcat)
|
||||
|
||||
```text
|
||||
FOOSD 1.0 - deliberately vulnerable SUID service
|
||||
Type 'quit' to disconnect. Buffer = 64 bytes, read accepts 512.
|
||||
FOOSD 1.0 ids=0/1000 leak stack=0x7ffd... libc=0x7f...
|
||||
BUF=0x7ffd...
|
||||
```
|
||||
|
||||
* `ids=euid/ruid` — ne pouvait pas être affiché comme `euid=`/`ruid=`, parce
|
||||
que la harnesse de test prouve un shell en grepant le `uid=` littéral, et le
|
||||
banner ne doit pas le contenir (une sonde qui partage la signature de la
|
||||
réponse est un piège classique de faux positif ; voir le commentaire dans
|
||||
`foosd.c`). La harnesse exige en outre la forme stricte de sortie `id` —
|
||||
`uid=NNN(...)` — pour que rien de ce que le démon ou l'exploit affiche ne
|
||||
puisse satisfaire le contrôle par accident : le propre « target euid=… ruid=… »
|
||||
de `foosc` contient `uid=` comme sous-chaîne, ce qui a un jour fait
|
||||
rapporter à un test durci un shell qui n'avait jamais tourné.
|
||||
* `stack=`, `libc=`, `BUF=` — les fuites ASLR : elles permettent à la
|
||||
shellcode et à ret2libc de calculer des adresses exactes.
|
||||
|
||||
---
|
||||
|
||||
## 10. Exercices
|
||||
|
||||
1. **Considérez la descente non-root.** Lancez `make test` *avant* `make
|
||||
setuid`, puis encore après. Expliquez le passage à `ROOT=SEEN` avec
|
||||
l'histoire ruid/euid de la §5.2.
|
||||
2. **Lisez le crash.** Lancez `./foosc -t demo -n` puis lisez `foosd.log`.
|
||||
La ligne `RIP=0x4141414141414141` est le bourrage de l'attaquant — la
|
||||
preuve que c'est le débordement, pas le hasard, qui contrôle l'exécution.
|
||||
3. **Ajoutez la canary.** `make hardened` et modifiez vous-même la boucle
|
||||
`test-hardened` ; la ligne de log `*** stack smashing detected ***` est la
|
||||
défense qui fonctionne.
|
||||
4. **Désactivez la fuite.** Commentez la ligne `BUF=` dans `foosd.c`, recompilez
|
||||
et voyez `-t shellcode` passer de déterministe à jeu de devinettes. Cette
|
||||
seule ligne est la raison pour laquelle les vrais bypass d'ASLR sont tout un
|
||||
domaine.
|
||||
5. **L'expérience `-p`.** Dans une copie de `win()`, changez `execl("/bin/sh",
|
||||
"sh", NULL)` en `execl("/bin/sh", "sh", "-p", NULL)` et observez root. `-p`
|
||||
est la sortie de secours documentée du gardien du shell — et la raison pour
|
||||
laquelle le conseil « spawn juste un shell » des vieux write-ups est
|
||||
incomplet.
|
||||
6. **Pourquoi pas `setuid(0)` ?** Réécrivez la shellcode pour appeler
|
||||
`setuid(0)` au lieu de `setreuid(0,0)` (syscall 105). Le shell atterrit
|
||||
quand même — et retombe quand même à `uid=1000`. C'est l'expérience d'une
|
||||
seule ligne la plus instructive de tout le dépôt.
|
||||
|
||||
---
|
||||
|
||||
## 11. Sécurité et nettoyage
|
||||
|
||||
- Loopback uniquement, par défaut et par conception ; `-L` lie plus loin, et
|
||||
seule une VM bonne à jeter devrait même l'envisager.
|
||||
- C'est un lab de shell root. Ne le faites pas tourner sur une machine qui
|
||||
compte, et ne pointez pas `foosc -h` vers quelque chose que vous ne possédez
|
||||
pas.
|
||||
- Rituel de nettoyage : `make stop` puis `make unsetuid`, et si vous voulez
|
||||
l'arborescence impeccable à nouveau : `sudo make clean`.
|
||||
|
||||
```console
|
||||
$ make stop
|
||||
$ make unsetuid
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue