Pwn5-bugku

pwn5

基础知识

  1. 格式化字符串漏洞:

    在c/c++的格式化输出中,使用的是printf函数,它的参数如下所示:

    int printf(const char *format, ...);
    

    使用方式如下:

    char buf[10] = {};
    read(0,buf,9);
    printf("%s",buf);
    

    如果直接使用printf输出buf的话,则存在格式化字符串漏洞:

    char buf[10] = {};
    read(0,buf,9);
    printf("%s",buf);
    

    原理:因为printf会从第一个参数读取格式化字符串,比如%x、%p,如果外部输入是“%x%x”这种字符串,那么printf就会从格式化字符串后面的数据读取对应格式的数据并输出,造成内存泄漏;

    利用:在64位机器中,调用printf函数时,会把参数以此push到寄存器rdi、rsi、rdx、rcx、r8、r9中,更多的参数则是push到栈中,那么printf函数在处理格式化时,也是先从寄存器中读取参数,如果参数超过上述寄存器数,则从栈中读取数据。回到漏洞利用,如果输入的格式化字符串为%p%p,那么printf会依次读取rsi、rdx中的数据(在读取格式化字符串时是从rdi读取的,所以第一个%p是从rsi开始)。有一种格式化字符可以指定输出第几个参数,如%0 p 是 从 第 0 个 参 数 寄 存 器 读 取 , 也 就 是 r d i , p是从第0个参数寄存器读取,也就是rdi,%1 p0rdip是从第1个参数寄存器读取,也就是rsi,%6$p是从第6个参数读取,由于寄存器只有6个,那么它就会从printf函数的栈中读取;

  2. libc

    通常所说的libc就是一个so库,里面包含了众多基本函数,比如system等。在运行一个程序前,比如helloworld,会首先加载libc,然后使用libc_start_main函数来加载我们自己写的main函数;

解题

image-20200809224452927

题目如上,本题存在两个漏洞1.格式化字符串漏洞,该漏洞可以用来泄漏libc的地址;2.栈溢出,该漏洞可以用来跳转到libc的system函数去,我们现在就要利用这两个漏洞拿到shell。

格式化字符串漏洞利用

首先需要利用格式化字符串漏洞获取libc的基地址,我们运行程序,运行到printf处,查看栈中的内容:
image-20200809232326356

栈里面有个返回地址0x00007f14af6360b3,可以看下内容:

image-20200809232435087

发现这个是__libc_start_main里面的代码,0x7f14af6360b3的地址为 __libc_start_main+243,因此__libc_start_main的地址为0x7f14af635fc0(0x7f14af6360b3-243)。

image-20200813212458118

然后可以利用ida查看该函数在libc中的偏移,计算得出libc的地址:

image-20200813212738776

因此可以由栈上的__libc_start_main函数的地址来计算的到libc的地址addr_libc=0x7f14af60f000=0x7f14af635fc0-0x0000000000026FC0。

我们可以使用vmmap命令验证下,看libc的地址是否为x7f14af60f000

image-20200813213009278

验证正确。(由于这是在我ubuntu上运行的,所以libc的地址为我机器上的,要获取题目中的libc地址,那么需要拿到对应系统版本的libc文件)

栈溢出利用

image-20200815153244683

题目后面存在一个栈溢出漏洞,缓存区s大小为0x20,但是读取了64个字节,所以可以控制栈。同时还需要注意要让代码跑到return 0语句,需要满足输入包含两个字符串needle和byte_4009BA,通过ida可以知道,这两个字符串为“真香”和“鸽子”:

needle

image-20200815153529680

byte_4009BA

image-20200815153654121

对应字符串

image-20200815153607201

所以之后输入真香鸽子即可跑到return 0语句。

这时候我们就需要构造ROP来拿到shell了。

image-20200815164152372

上图为我们可以控制的栈空间,总共0x40个字节,前0x28个是要填充内容的,后面0x18个是要放置相应跳转地址;

  1. 找到human中pop rdi | ret的地址0x0000000000026b72;

    ROPgadget --binary libc-2.31.so --only "pop|ret"
    

    image-20200815162258945

    在libc中找到“/bin/sh”字符串的地址addr_libc+0x00000000001B75AA:

    image-20200815162434748

    以及system函数的地址addr_libc+0x0000000000055410:

    image-20200815162504376

  2. 依次向栈中写入pop rdi |ret的地址、“/bin/sh”字符串的地址、system函数的地址;

    image-20200822180438953

当human运行完return 0之后,便会从栈中取出返回地址(已被修改为pop rdi|ret的地址),并跳转运行;

运行完pop rdi之后,“/bin/sh”的地址被存入rdi寄存器,ret运行之后,便从栈中取出返回地址(system函数地址)运行,这时候就相当于调用了system("/bin/sh"),拿到shell;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值