Ho finito per scrivere il mio shellcode in un modo diverso. Poiché non riuscivo a capire come tornare, ho lasciato che il kernel facesse il lavoro pesante per me, tornando in userland. L'idea era di eseguire il mio bit di escalation dei privilegi e tornare al punto in cui la funzione vulnerabile avrebbe dovuto tornare, con i registri e lo stack sistemati.
Non appena il kernel è tornato dalla funzione vulnerabile (quando non era in overflow), ho notato qualcosa tramite gdb
. (Gli indirizzi sono immaginari, ma spiegano comunque il concetto.)
(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
Quindi, subito dopo la restituzione, 0x40 byte di stack non vengono utilizzati e svanirebbe semplicemente con il add esp
istruzione. Così, approfittando di questo fatto, ho costruito una catena ROP (l'avevo già costruita mentre scrivevo questa domanda, il suo compito era disabilitare SMEP e saltare al mio shellcode userland), che era lunga 24 byte. 4 byte sovrascriverebbero l'EIP al momento della restituzione e i restanti 20 (0x14
) lo seguirebbe direttamente nello stack inutilizzato. Questo ci lascia con 0x2c
byte di stack inutilizzati ancora.
Ma se tornassimo a 0xadd2
, rischieremmo di perdere un ulteriore 0x14
byte di preziose informazioni sui registri dallo stack e riempiendo i registri con dati non validi. Potremmo add 0x2c
a esp
nel nostro shellcode userland, e salta direttamente a 0xadd3
, saltando l'attuale add
istruzioni.
Notando anche dalla sessione di debug, tutto tranne eax
e ebx
venivano restaurati correttamente. Poiché il mio overflow li aveva cestinati entrambi, ho dovuto ripristinarli con valori simili ai casi in cui la funzione ha restituito un risultato pulito. (Fare questo è stato semplice:ho impostato un punto di interruzione a 0xadd2
, ed ho estratto i valori da info reg
)
Quindi, il mio shellcode finale per l'utente era questo:
Execute privesc payload -> add esp,0x2c -> register fixup -> jump to 0xadd3
In questo modo, il percorso del codice è tornato perfettamente pulito e il kernel ha svolto il compito di tornare alla modalità utente per me.