Return Oriented Programming Tutorial

Hi in this tutorial we will go throw a very basic way of creating a ROP (Return Oriented Programming) Chain in order to bypass SMEP and get kernel mode execution on latest windows installation despite microsoft mitigation’s.

Setup:

This tutorial is meant to be an active tutorial meaning that its best you will download the binary provided for the tutorial and experiment on your own with the main idea’s presented.
So this is what you will need in order to run the full tutorial by your own:
HEVD: download from here.
Windows 10 RS3
Here.
WinDbg & Symbols: * kd.

  • Symbols.
    Hyper-V: * How To unable hyper-v.
    Setup File sharing beetwin the machine and the host:* Setup File Sharing..
    My Debug Binary: * download link..

    Introduction:

    Return Oriented Programming (in the computer security context) is a technique used to bypass certain defence mechanisms, like DEP (Data execution Prevention) & SMEP. if you would like to read more about smep you can check out the link at the main README.md file of this project. the main charicteristic of this method is that instead of running pure shell code directlly from a user supplied buffer we instead use small snipets of code called gadgets.
    say for example i want to place 0x1FA5 in rsp, useally i will simply write in my shellcode:
    mov rsp, 0x1FA5
    instead when using rop we will try to find some address in memory (this can be a dll an exe image or the kernel image), that will do exactly the same. and instead of writing it in the payload we will place that memory address of that function to be executed instead. so lets say i know that at a certain Offset from the base address of some dll say hal.dll there is a good instruction, then assuming that i can get code execution if i will pass the address of that function to the exploit target on runtime it will get executed. when building a rop chain the chain will be computed from many small gagdets like this one, you can think a bout it like shellcoding with snippets from other executable memory. here is a little snippent to visualize this:

so in that picture as an example we will send to our exploit target a buffer that contains:

hal+0x6bf0 followed by hal+0x668e .. and so on.
You may ask yourself: why would we want to do that? why not simply write the shellcode as is?
Well if you can simply write the shellcode as is then it is far easy to do that, but as mentioned b4 it may not allways be possible. so lets say a little about smep. smep is a security massure that uses hardware features in order to protect the endpoint from exploits such as kernel exploits. the main idea is to mark eache page allocated in the memory as eather kernel address space (K-executable/r/w) or user space. this way when the kernel executes code that code address is being checked (if the hardware offers that possiblety) if its a user space address or kernel mode address. if it was found that the code is marked as user space the kernel will stop the execution flow with a critical error, bsod.
so if we will simply try to exploit a stack overflow like we did on windows 7, we will get this outcome:

so the main purpose in this exampled rop chain is to make the execution flow throw a kernel executable address that can pass the check until we can execute our own payload.

enough talk lets debug!!!

assuming that you have set up the environment as stated above, and you have a working machine, then open an administrator command, and type as follow: b4 running anything hit break on the debugger (open debug window and click break), next open view -> registers. scoll all the way down and the resault should be: next up type g and hit enter the machine should be running as normal, run the sample exe that i have provided, you should get a break point and this output should go on the debugger: as you can see this break point is different from the one we hit b4. first take a look at the address that triggered the break point: at the break point b4 we hit :
0xfffff80391595050 cc int 3
and now we got:
Break instruction exception - code 0x80000003 (first chance) 0x0000017039f00046 cc int 3

as you can see the first address is a kernel space address and the second a user address. this is becouse i have place xcc in my shellcode. next up open the registers again the outcome should be as follows:
you may ask yourself, why is cr4 register changed and how is it that we do not get the bsod msg as b4? well becouse the binary build a rop chain as follows:

  	// To better align the buffer,
	// it is usefull to declare a
	// memory structure, other-wise you will get holes
	// in the buffer and end up with an access violation.
	typedef struct _RopChain {
		PUCHAR HvlEndSystemInterrupt;
		PUCHAR Var;
		PUCHAR KiEnableXSave;
		PUCHAR payload;
		// PUCHAR deviceCallBack;
	} ROPCHAIN, *PROPCHAIN;


  	// Pack The buffer as:  

	ROPCHAIN Chain;

	// nt!HvlEndSystemInterrupt+0x1e --> Pop Rcx; Retn;
	Chain.HvlEndSystemInterrupt = Ntos + 0x17d970;

	// kd> r cr4
	// ...1506f8
	Chain.Var = (PUCHAR)0x506f8;


	// nt!KiEnableXSave+0x7472 --> Mov Cr4, Rcx; Retn;
	Chain.KiEnableXSave = Ntos + 0x434a33;

meaning that we have sent the vuln driver a stack overflow buffer, but instead of supplying our shell code we have given the driver the buffer above that is first composed of:
Pop Rcx <– kernel mode address
Retn
0x506f8
Mov Cr4, Rcx
Retn
(ShellCodeAddress)
So basically we have “jump” to our shellcode from other kernel mode address’s so we did not got the bsod, simply cuz we have given the kernel a kernl-mode address so we passed the check, next up we flipped the bit on the cr4 register value to trick the system that smep is not supported on the firmware.
we can see that by running kv command. you can see the kernel mode address on the stack call and can see the execution flow, as well as the nop’s we have placed in our buffer. hit g again and you should see the below outcome:
as you can see we have hit access violation, this is becouse in this demo i did not fix the return address pointer, so we can do this together, hit r on the debugger:

as you can see the stack is a mess & the registers as well, the return address try’s to read a pointer from rax, that is pointing to zero address, so we got access violation.
so like we had to “jump” to our shell code to avoid SMEP, we need to jump back to a reasonable state, but how can we know what is a good address to jump back to? when we looked at the stack b4 we could see the execution flow,
So 0x00007fffb29013aa the lowest address called the ioctl then we got to our shellcode from the overflow (the nop’s), a good thing to do would be to make a return to one of our original call’s on the stack to resume execution. if we take a look at 0x00007fffb29013aa we can see its marked as UREV user executable, so if we will jump to that address we will be in the same situation as b4 (bsod) so we need to find another place on the stack to jump to. lets see about nt!IofCallDriver+0x59 that is on the stack as well, we can even see what code is contained at this location running the below command: So we can see that this function simply returns back to the caller and is also KREV kernel exec. so it will be a good choise. while i was finding gadgets i was doing exactly the same, looking for KREV address that contain good code for me like mov cr4, rcx. with the ‘u’ command on the kd.
ok, but how can we jump to that address? open up the registers again and copy the first instruction address in the output of
kd> u nt!IofCallDriver+0x59
as follows: place it in rip (in view registers..)

If we Combine this sequence together we get:


now hit g again. back in box you should have a command running as local system.

So this is the end of the tutorial, i hope it will be usefull, now you know a bit more about ROP, and got some basic tools to build your own rop chain. my example code can be found here: C0de.
and i challenge you to fix the return address programmatically!
for more information please go to the main readme of this project and go to the links provided (how to find gadgets rop smep.. etc..)

For more Windows kernel exploitation you can see: The full series.