J'ai fini par écrire mon shellcode d'une manière différente. Comme je ne savais pas comment revenir, j'ai laissé le noyau faire le gros du travail pour moi, en retournant en mode utilisateur. L'idée était d'exécuter mon bit d'escalade de privilèges et de revenir à l'endroit où la fonction vulnérable était censée revenir, avec les registres et la pile réparés.
Dès que le noyau est revenu de la fonction vulnérable (quand il n'était pas débordé), j'ai remarqué quelque chose via gdb
. (Les adresses sont imaginaires, mais expliquent le concept de toute façon.)
(gdb) x/i $eip
0xadd1: ret
(gdb) x/xw $esp
0xadd1: 0xadd2
(gdb) x/6i 0xadd2
0xadd2: add esp,0x40
0xadd3: pop ...
0xadd4: pop ...
0xadd5: pop ...
0xadd6: pop ...
0xadd7: ret
Ainsi, immédiatement après le retour, 0x40 octets de pile sont inutilisés et disparaîtrait simplement avec le add esp
instruction. Ainsi, profitant de ce fait, j'ai construit une chaîne ROP (je l'avais déjà construite en écrivant cette question, son travail consistait à désactiver SMEP et à passer à mon shellcode userland), qui faisait 24 octets de long. 4 octets écraseraient l'EIP au moment du retour, et les 20 octets restants (0x14
) le suivrait directement dans la pile inutilisée. Cela nous laisse avec 0x2c
octets de pile inutilisés encore.
Mais si nous retournions à 0xadd2
, nous risquerions de perdre encore 0x14
octets d'informations précieuses sur les registres de la pile et en remplissant les registres avec des données invalides. Nous pourrions add 0x2c
à esp
dans notre shellcode userland, et passez directement à 0xadd3
, en sautant le add
réel instruction.
Notant également à partir de la session de débogage, tout sauf eax
et ebx
étaient restaurés correctement. Comme mon débordement les avait détruits tous les deux, j'ai dû les restaurer avec des valeurs similaires aux cas où la fonction effectuait un retour propre. (C'était simple :j'ai défini un point d'arrêt à 0xadd2
, et extrait les valeurs de info reg
)
Donc, mon dernier shellcode userland était celui-ci :
Execute privesc payload -> add esp,0x2c -> register fixup -> jump to 0xadd3
Ce faisant, le chemin du code est revenu parfaitement propre et le noyau s'est chargé de revenir en mode utilisateur pour moi.