7

In our course in university, we recently moved on from standard buffer-overflows to SEH based ones.

My exploit is already ready and working thanks to some nice tutorials, although I am still not sure I completely understand, when and why we need the SEH buffer-overflow.

My understanding so far is that, if there are no explicit exception handlers in the source code, every thread will get an automatic one, specific handlers will be there additionally.

For programs, which are vulnerable to SEH BOF a buffer-overflow will cause the e.g. Immunity debugger to pause the program at an exception. Only after the exception is passed to the program the EIP register is overwritten with the malicious buffer.

Question 1:

Why does this not happen for every program then since there should be an automatic exception handler in any case?

After passing the exception EIP and ESP are overwritten, with values from my buffer, yet other registers are zeroed out (EAX, EBX, ESI & EDI in my example). So I can control EIP and ESP, but the tutorials mention that it is useless, due to the zeroed out registers. However, they never explain why the zeroed out registers are the problem - so here is my second lack of understanding.

Question 2:

What exactly is the deal with the zeroed out registers and why would they break shellcode execution?

Now we also overwrite the NSEH and SEH records and using a pattern can figure out the exact offset to overwrite those. Then comes the magic with referencing some module which has POP POP RET to get to ESP + 8.

Plus eventually yet another jump.

Question 3:

Why do I need POP POP RET and the final jump?


I have the exploit working and can hand it in like this, yet it feels very unsatisfactory and pointless without actually understanding what is going on. I only started working with low level stuff recently and still have a lot to learn, so I am very thankful for every help.

6

First of all, read this:

https://www.blackhat.com/presentations/bh-asia-03/bh-asia-03-litchfield.pdf

That's pretty much how all this started.

A SEH buffer overflow is a specific stack overflow that targets the EXCEPTION_REGISTRATION_RECORD sitting some arbitrary distance down the stack.

Why does this not happen for every program then since there should be an automatic exception handler in any case?

Yes it would, as you can't really disable SEH on Windows. Provided your buffer overflow can reach the EXCEPTION_REGISTRATION_RECORD and you can trigger an exception.

After passing the exception EIP and ESP are overwritten, with values from my buffer, yet other registers are zeroed out (EAX, EBX, ESI & EDI in my example).

from my ntdll.dll version 10.0.17134.254...

before being XOR'd...

EAX holds a pointer to the current EXCEPTION_REGISTRATION_RECORD so if you have overwritten EXCEPTION_REGISTRATION_RECORD->Next with the payload address and set the EXCEPTION_REGISTRATION_RECORD->Handler to a random instruction that peformed JMP/CALL [EAX], that might be an attack vector.

EBX is already set to 0 at the start of RtlDispatchException It previously contained the PEXCEPTION_RECORD.

ESI is the PEXCEPTION_RECORD and EDI is the PCONTEXT.

What exactly is the deal with the zeroed out registers and why would they break shellcode execution?

They don't necessarily. Before we get to execution of the handler we have to go through ntdll!KiDispatchUserException -> ntdll!RtlDispatchException which end up overwriting all the registers anyway.

It's why the kernel saves them in the CONTEXT struct before returning to usermode.

This exploit isn't a simple EIP hijack. There are big changes to the stack and registers before we get execution after the exception.

Why do I need POP POP RET and the final jump?

Do you mean why not just set the EXCEPTION_REGISTRATION_RECORD->Handler directly to the shellcode?

This is a ROP gadget to redirect execution to the value of ESP+8. If you get this far in the exploit then the data at ESP+8 is controllable by you, but you might not necessarily know where that is in advance.

Secondly, there are many sanity and security checks between the exception and the handler execution. If any of these fail the program is terminated before the handler is run. What these consist of depend on the version of Windows you are targeting. SafeSEH is one of them. Very simply it validates the handler address against a whitelist, but it can't validate for handlers in modules that were not compiled with SafeSEH. So if the POP/POP/RET comes from a module loaded and compiled without SafeSEH, ntdll cannot determine if it's malicious.

Plus eventually yet another jump.

This is the best part!

Because ESP+8 is where we will be landing, we can put the shellcode here. Except it's the EXCEPTION_REGISTRATION_RECORD that we need to be intact so EXCEPTION_REGISTRATION_RECORD->Handler makes the first part of the exploit work.

Luckily, EXCEPTION_REGISTRATION_RECORD->Prev (which you have as NSEH) represents the first 4 bytes to the shellcode where POP/POP/RET will return.

A short jump in x86 can be encoded with just 2 bytes. So this final jump skips 6 byte over the NSEH so it can remain valid for the exploit to work.

This blog :

https://dkalemis.wordpress.com/2010/10/27/the-need-for-a-pop-pop-ret-instruction-sequence/

Explains the final part far better than I can.

  • It might be worth noting that this kind of stack-based exploit does no longer work in Microsoft's 64-bit world. See the article osronline.com/article.cfm?article=469. With 32-Bit programs (C++), MS offers the /SAFESEH linker option preventing such exploits as well (if I understood right). – josh Sep 18 '18 at 11:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.