Egg Hunter — How it works?

Orhan YILDIRIM
9 min readJul 5, 2020

--

In this article I will talk about the “Egg Hunter” logic. With the “Egg Hunter” method, which is one of the methods frequently used by Exploit developers, we will examine how our shellcode on the memory area breaks down and gets to know each other in different areas.

In this example, the application we will work on is the “KSTET” command of the VULNSERVER application.

You can access the related application from the link below.

What is Egg Hunter?

In the normal Stack Based Buffer Overflow technique, there was as much space as we wanted to write the shellcode on the memory.

However, when examining an application that we believe may be BoF, if there is not enough space for the shellcode on the memory area, by loading the shellcode to a different part of the memory and assigning a TAG to it, the shell containing the code we will send in the 2nd part such as ROOT or w00d and the shellcode we sent on the memory. it is recognized by the TAG egghunter and shellcode is executed.

In this way, if there is not enough space on the memory to send a shellcode at a time, it divides the code into 2 and finds and runs each other with the TAG we have determined. This is the EggHunter logic.

An app that you can exploit with Stack based BoF and Egg Hunter can look like the screenshot below on memory.

In the blue column on the left side of the screenshot above, there is an area that can fit 220 bytes written as “junk”. It can be written in the 4 Byte EIP field that will come after 220 A characters entered in this field.

With the “JMP ESP” command placed in the EIP field, the field belonging to our shellcode, which will be executed in sequence, is displayed.

As seen in the screenshot, the shellcode prepared takes up 365 Bytes of space and appears to have 120 Bytes left in the stack area even after all operations.

But on the other side;

In the blue column on the right in the screenshot, it is seen that the number of Bytes where the same application crashed is 515 Bytes.

With the “JMP ESP” command written after 515 “A” characters, the location of our shellcode on the memory area is targeted, but it is seen that the same shellcode does not fit in the remaining suitable space in the stack area.

As seen in the screenshot, only 190 bytes of our shellcod could only fit.

The Solution: Egg Hunter Method

In such cases, the egg hunter method will save lives. The Egghunter shellcode we will use briefly performs the following process.

The code sets itself a label called “w00t”. This label is called egg. It travels all memory and tries to find this tag. It places the shellcode (reverse shell, bind shell, pop up, calculator run etc.) that we want to do just behind the label and if it did, it jumps there with the jmp edi command and runs our shellcode.

Vulnserver — KSTET Command

We compile the code in the screenshot below to connect to the vulnserver application serving on the “9999” port of the target system.

Note: The part specified as “TRUN” must be “KSTET”.

When we send 200 bytes to the application, we see that there is a crash. By sending a 200 byte of Uniqe, we detect which byte of the application crashed.

We send 200 bytes uniqe string statement that we created with msf-pattern_create as a junk. Our goal in this step is to learn which byte we can write to the EIP field.

Using the python script mona, which we can download from Immunity Debugger from the internet, we can find out which byte the application crashed using the command below.

!mona findmsp

We can see the usage of the related command and the determination of the offset value of the application is 69 bytes in the screenshot below.

We determine that we could not write to the EIP register 69 bytes as it appears from the logs.

We can run the code given in the screenshot below to check that the 4 “B” characters we will send after 69 “A” characters can be written on the EIP field.

After the code we run, we can see that the hex value of the “BBBB” characters that we aim to write on the EIP field with the “42424242” on the EIP register from the registers field via the Immunity Debugger application can be written.

Now that we can control on the EIP register, we need to see the characters that can break the payload we will write in the field by sending badchars for this field. However, badchars contain too many characters and we can only fill this field with 69 A characters.

For this reason, we can send badchars into 69 pieces by dividing them into pieces, and in order to distinguish them, we can send C characters to complement 1000 characters later.

As seen in the screenshot above, we sent the badchars1 section as a total of 79 bytes and the remaining payload to be completed to 1000 characters, and the rest as a C character.

As seen in the screenshot, we can say that there is no badchars because there is no corruption in the badchars between 01 and 4F, but let’s not forget that we do not send the \ x00 character. We will need to specify this when creating a shellcode.

By sending badchars2–3 and 4 in order, we check if there is corruption in the badchars, but there is no badchars in the vulnserver application and we will only use \ x00 badchar when creating the shellcode.

Now that we can check the EIP register, we are now trying to jump to the ESP register. We will use the mona module for the JMP ESP structer.

!mona jmp -r esp

Then we aim to jump to the ESP register.

Before running the command here, firstly, by placing a breakpoint in the JMP ESP structer in the debugger application, we can examine the application flow.

As seen in the screenshot below, we can jump to the ESP register after JMP ESP. The C character that we have specified in our code can now be written on the ESP register.

Now we can control the ESP on the application.

After 69 A characters in our command, we came to the ESP register with 69 + 4 characters with JMP ESP (with 4 characters) struncter.

As we can remember from the stack based bofs in the ESP register, we aimed to write our shellcode here, but as we can see, we wrote only 73 characters with 69 + 4 characters and then we wanted to fill all the remaining characters with the C character in order to complete our payload to a total of 1000 characters. However, as can be seen in the screenshot above, only 20 C characters (43) can be written.

This field is not enough for us to write shellcode. For this reason, after this step, we will switch to the EGGHunter bof method. As we can remember, we were able to write 69 A characters after the KSTET command. Here we can use some of this field to write EGGHunter code.

This process requires 45–50 bytes in order to fit an estimated EGGHunter code. So we have to go back 50 bytes from this ESP register that we jumped. Our goal here is to be able to write the EGGHunter code on memory for now. So we will send a few A characters in the first place, then we will send the EGGHunter code and the C character to complete 1000 characters again. For this, we find the characters that we can jump to 50 characters before with the help of nasm_shell.

If we write \ xEB \ xCC characters in the ESP field, it will take us 50 characters ahead.

As shown in the screenshot below, we went back 52 bytes to enter the ECX register. (If we drop the EBCC bytes we used to go back, we actually went to 50 bytes.)

In this field we will write our EGGHunter code.

We can create a TAG for our EGGHunter code. As can be seen in the screenshot above, the Egghunter code is 32 bytes in total.

!mona egg -t ROOT

We will send this code in 69 A codes that we sent first, but only after that. So the last 32 characters of 69 characters will be EGGHunter code. Because we were able to run the shellcode there by jumping directly to the ESP register for stack based bof, but we saw that we could only write 20 C characters in the ESP register.

For this reason, we will send a certain number of A characters, then send the EGGHunter code and jump to the ESP register with the JMP ESP code. We had already entered the jump back 50 characters command there. This way we will try to get back to the beginning of the EGGHunter code.

Here in the field of 69 characters;

69 character A
4 JMP ESP
2 JMP -50 Characters were written.

Since we will write our EGGHunter code where we go back 50 characters, we will have to recalculate the A characters we will send first. Accordingly, we will make our calculation as follows;

69 + 4–50 = 23 In this calculation, we do not take into account the EBCC characters we found with a 2 character nasm shell because the nasm shell is going back 50 characters except these characters.

If we now send a new string such as “23 A + egghunter codes + 4 JMP ESP + 2 JMP -50 characters + A * (69-len (string))”, and after examining the movement of the payload on memory, we sent it with the help of breaktpoint after JMP ESP We will go back 50 characters and return to where our egghunter code starts.

As seen in the screenshot below, when our code works, we have now reached the place where the EGGHunter code started. The code will then start looking for TAG only in the loop.

We will now create our shellcode.

When we run the above command (note the egghunter TAG) we now send our shellcod as 2 stages separated.

In the first command, we connect to the application with STAT command and send our shellcode to the memor with the ROOTROOT TAG. In the second part, we send the string by connecting to the application with the KSTET command. Now that we have sent our egghunter code, the shellcode we sent in part 1 will be recognized by ROOTROOT TAG, and we will have gone shortly when the shellcode started. And now 4444 port is opened on the target and the bind shell is removable.

--

--

Orhan YILDIRIM

Experienced in Network Technologies, Network Security, Network Pentesting, Information Security, Web Application Security. Network Defence, Dos/DDoS protection,