-
-
[原创]利用auxv控制canary
-
发表于: 2019-12-10 17:11 6441
-
利用绕过canary的另一种方法。通过修改AUXV(Auxiliary Vector)结构体使 canary 值可控。因为在ld的时候,canary是通过AUXV中的一个成员生成的。
题目文件
(文件在附件中也上传了)
发现upx有壳,upx -d
一下(附件中depack文件时脱壳后的),然后发现脱壳后的程序有canary
脱壳后的main函数,很明显gets有个栈溢出。但是有canary,这里通过修改AUXV
中的AT_RANDOM
来控制我们的canary,后面会介绍。
简言之,canary是由ld.so进行初始化的,而这个值又是通过Auxiliary Vector
参考
https://www.elttam.com.au/blog/playing-with-canaries/auxv
结构可以在elf/elf.h里看到:
这是一个entry struct
,以AT_HWCAP
为例,这个结构体就会是p64(entry_type_no) + a_un
这个a_type的取值可以参见源码,以下列举部分
我们关注的是这里面的AT_RANDOM
,在libc中,它和生成canary有着紧密的关系。
也就是说,canary是通过dl_random
上设置的。
通过测试和文章分析可知,AUXV
结构体中AT_RANDOM
的值对应了canary的值(The value is a pointer to sixteen random bytes provided by the kernel. The dynamic linker uses this to implement a stack canary)
上面的这篇文章也介绍了通过程序获取auxv的方法
其中,type传入auxv的a_type即可。
我们可以写一个带canary的程序,编译的时候gcc的选项带上-fstack-protector-all
然后看一下AT_RANDOM
这个地址的值。
还有比较重要的是,程序一开始AT_RANDOM
、AT_EXECFN
、AT_PLATFORM
和其他的值都会被 push 到栈上。
最后基本可以知道canary
的起源是如下的方式:
也就是说,如果我们可以在ld之前修改auxv struct
,当程序调用ld后,我们就能控制canary了。
关于修改auxv细节
The auxv structure above is everything. The at_random address gdb tells you is found using that structure. So, you CANNOT just use the address of at_random info auxv to see if the at_random is modified. Actually, at_random's address is contained in that structure, it is like p64(0x19) + p64(at_random_addr). To actually modify the at_random, the only thing you can do is to modify that address followed by the 0x19 number in the auxv. Changed it to point to some address which we already know the contents. (I used the address which is initiated to zero here)
大意就是我们要修改at_random的那个指针,确保这个指针指向的内容我们已知或者可控。
upx壳是有意义的,其不仅提供了RWX段。而且我们可以在没有被脱壳解密的情况下,没有被载入前覆盖掉auxv。第一次加载壳的时候可以输入长为0x4096的字符串,前八位则要求必须是12345678才能过 check。接下来解壳之后就可以溢出到auxv。
注意,我们覆写auxv的时候不需要把每个种类的Elf64_auxv_t都写上,除了必要的AT_RANDOM
外,详见exp。
sub_40099E
是start第一个运行的函数。
程序刚进入这个函数后就pop rsi
,而后rsp在读入之前就没有变
我们在0x4009E1
下断点,第一次断下来的时候,rsi就是buf的起始地址了
第一次断在0x4009E1
的时候
然后查看栈上auxv的距离
我们发现0x7fffffffe1a8
地址处保存了RANDOM的指针,距buf偏移为0x03a8-0x8
。
我们修改的时候还要修改其a_type,也就是0x7fffffffe1a0地址处表示的0x19.0x7fffffffe0b0
地址开始就是auxv的开始地址,之前和env
数组有一个0的分隔。我们在覆盖的时候,不必将auxv每一个a_type对应的值都覆写上。0x7fffffffde80
处的1保存的是argc(参数个数)。0x7fffffffde88
则是argv
控制canary之后,我们只要通过ROP,在RWX段用gets写入shellcode,然后执行即可。
经测试,auxv覆盖的时候,下面的成员值一定要保证正确覆写
其他成员不写上调试的时候也没有出问题
https://github.com/D-I-E/writeups/tree/master/2017-ctfs/20170602-TCTF-Final/pwn-upxof
https://qianfei11.github.io/2019/02/15/%E7%BB%95%E8%BF%87ELF%E7%9A%84%E5%AE%89%E5%85%A8%E9%98%B2%E6%8A%A4%E6%9C%BA%E5%88%B6Canary/#2017-TCTF-Final-upxof
https://www.elttam.com.au/blog/playing-with-canaries/
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
- [翻译]现代化地编写LLVM Pass -- part II 9951
- [翻译]现代化地编写LLVM Pass -- part I 18733
- [原创]利用auxv控制canary 6442
- [翻译]使用IDAPython寻找二进制漏洞 11047