6

I have a vulnerable Linux Kernel module (32-bit), which I can successfully exploit, and have gained privileges out of it. Basically, my exploit uses an ROP chain to disable SMEP, and directly jumps to my shellcode mapped at userland. My shellcode at userland makes a call to commit_creds(prepare_kernel_creds(0)); and tries to return back to my userland code.

Now I don't understand how to return to user mode from kernel mode. Several articles point out that I should use iret assembly instruction to return to user-mode. I plainly inserted an iret after the shellcode, but it doesn't seem to work.

I write to a device file, and from the call trace:

? vfs_write
? SyS_write
? do_fast_syscall_32
? entry_SYSENTER_32

I note that this is a fast system call, and it must return through the sysexit instruction.

Now, how do I return back to userland without panicking the kernel? I need to know which call I need to perform( iret / sysexit ) and how to perform it cleanly.

(I have looked over the Intel manuals and a bunch of other resources, but nothing has helped me much until now.)

Gilles 'SO- stop being evil'
  • 50,912
  • 13
  • 120
  • 179
Mukesh Sai Kumar
  • 220
  • 1
  • 12

2 Answers2

4

I ended up writing my shellcode in a different way. As I could not figure out how to return, I let the kernel do the heavy lifting for me, in returning to userland. The idea was to execute my privilege escalation bit, and jump back to where the vulnerable function was supposed to return, with the registers and stack fixed up.

As soon as the kernel returned from the vulnerable function (when it was not overflowed), I noticed something via gdb. (The addresses are imaginary, but explains the concept anyhow.)

(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

So, immediately after the return, 0x40 bytes of stack is unused and would simply vanish with the add esp instruction. Thus, taking advantage of this fact, I constructed an ROP chain (I had already constructed it while writing this question, its job was to disable SMEP and jump to my userland shellcode), which was 24 bytes long. 4 bytes would overwrite the EIP at the time of return, and the remaining 20 (0x14) would follow it right into the unused stack. This leaves us with 0x2c bytes of unused stack still.

But if we returned back to 0xadd2, we would risk losing a further 0x14 bytes of valuable register information from the stack, and filling up the registers with invalid data. We could add 0x2c to esp in our userland shellcode, and directly jump to 0xadd3, skipping the actual add instruction.

Also noting from the debugging session, everything except eax and ebx were being restored properly. As my overflow had trashed both of them, I had to restore them with values similar to cases when the function made a clean return. (Doing this was simple: I set a breakpoint at 0xadd2, and extracted the values from info reg)

So, my final userland shellcode was this:

Execute privesc payload -> add esp,0x2c -> register fixup -> jump to 0xadd3

Doing this, the code path returned perfectly clean, and the kernel did the task of jumping back to user-mode for me.

Mukesh Sai Kumar
  • 220
  • 1
  • 12
-1

Please read about call_usermodehelper_setup() and call_usermodehelper_exec(). Using these calls you can execute code directly from kernel.

Example:

https://www.exploit-db.com/exploits/38454/

dev
  • 937
  • 1
  • 8
  • 23
  • Umm.. that seems like a good alternative, but I need the kernel to stay stable even after my shellcode is done executing. Doesn't answer my question. -1. – Mukesh Sai Kumar Jan 13 '18 at 07:52