Saya akhirnya menulis kode shell saya dengan cara yang berbeda. Karena saya tidak tahu bagaimana cara mengembalikannya, saya membiarkan kernel melakukan pekerjaan berat untuk saya, dengan kembali ke tanah pengguna. Idenya adalah untuk mengeksekusi bit eskalasi hak istimewa saya, dan melompat kembali ke tempat fungsi yang rentan seharusnya dikembalikan, dengan register dan tumpukan diperbaiki.
Segera setelah kernel kembali dari fungsi yang rentan (ketika tidak meluap), saya melihat sesuatu melalui gdb
. (Alamatnya imajiner, tetapi menjelaskan konsepnya.)
(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
Jadi, segera setelah dikembalikan, 0x40 byte tumpukan tidak digunakan dan akan hilang begitu saja dengan add esp
petunjuk. Jadi, dengan memanfaatkan fakta ini, saya membuat rantai ROP (saya telah membuatnya saat menulis pertanyaan ini, tugasnya adalah menonaktifkan SMEP dan melompat ke kode shell userland saya), yang panjangnya 24 byte. 4 byte akan menimpa EIP pada saat pengembalian, dan 20 sisanya (0x14
) akan mengikutinya langsung ke tumpukan yang tidak digunakan. Ini meninggalkan kita dengan 0x2c
byte tumpukan yang tidak terpakai.
Tetapi jika kita kembali ke 0xadd2
, kita berisiko kehilangan 0x14
lebih lanjut byte informasi register yang berharga dari stack, dan mengisi register dengan data yang tidak valid. Kita bisa add 0x2c
ke esp
di shellcode userland kami, dan langsung melompat ke 0xadd3
, melewatkan add
yang sebenarnya instruksi.
Juga mencatat dari sesi debug, semuanya kecuali eax
dan ebx
sedang dipulihkan dengan benar. Karena luapan saya telah menghancurkan keduanya, saya harus memulihkannya dengan nilai yang mirip dengan kasus ketika fungsi menghasilkan pengembalian yang bersih. (Melakukan ini sederhana:saya menyetel breakpoint di 0xadd2
, dan mengekstrak nilai dari info reg
)
Jadi, kode shell userland terakhir saya adalah ini:
Execute privesc payload -> add esp,0x2c -> register fixup -> jump to 0xadd3
Melakukan ini, jalur kode kembali bersih sempurna, dan kernel melakukan tugas melompat kembali ke mode pengguna untuk saya.