本文作者 7o8v 是第一个加入我的作者群的同学,根据之前文章数,奖励50元,请拉到下面看作者简介以及之前发过的所有文章,其中涉及了一些代码,如果想要利用也可以去查看作者的博客,点击阅读原文查看。
感觉自己还是太菜了,比赛结束前只做出来了三道题。
Evr_Q (level - 1) guestbook (level - 2) babystack (level - 3)
程序会先要求输入 User
载入到 IDA 进行分析
但是在进行 F5 反编译的时候发生了一个错误
Decompilation failure: 413238: positive sp value has been found
解决方法就是先 undefine 掉函数,再右键选择 Code,最后 Create function 就可以正常反编译了。
可以看到 User 的输入时保存在全局数组 Str 里的,然后下面的 sub_411316()
函数进行 User 的 check
这就是这个函数的主要部分了,我先爆破出第一个循环加密后的字符串,又因为第一个循环本身只用了异或,所以爆破出来之后再循环一次就是正确的 User 了,脚本如下:
User 验证成功以后,程序又要求输入 Start Code ,其实就是 flag 。
以上就是第二次输入 flag 的进行加密和验证的地方,最后的解密我也是通过爆破完成的,不算太难,脚本如下:
其实这个程序还加了一个检测调试器和一些工具进程的回调函数,如果需要动态调试的话,可以把这个函数对应跳表位置的jmp改为ret。:P
Arch: i386-32-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
程序主要有三个主要的功能
add() : 添加 guest see() : 打印 guest 信息 - name 和 phone del() : 删除 guest
add() 主要代码如下:
这里进行两次输入,输入 name 和 phone ,name 会保存在 bss 上,phone 会额外 malloc 一块内存进行保存。而且两个输入都有长度限制,而且 phone 还会通过 \_ctype\_b\_loc()
对字符类型进行检测,无法输入英文字母。
del():
流程比较简单:guest 标志位置 0 -> name 字段置 0 -> free 掉 phone 的内存 -> phone 的指针置 null
see():
这里通过 snprintf() 和 puts() 对 guest 的信息进行打印,snprintf() 通过格式控制先将信息打印到栈上,puts() 再将这些信息统一进行输出。
那这里就有一个问题,snprintf() 和 printf() 一样存在格式化字符串漏洞。
那我们就已经 get 到了一个格式化字符串的漏洞了。
程序保护全开,通过写 GOT 表肯定不可能了,于是我决定写 __free_hook
,但是刚开始想写一个 one_gadget
完事,结果发现三个 one_gadget
都没法用,于是我在程序段找了一个栈溢出的地方,写到那里去,之后通过泄漏 text 段地址和 canary 来通过栈溢出完成攻击。
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
很巧的是 pwnable.tw
上也有一道题叫 babystack ,而且对于这两道题我最后的解题思想也是基本一致的。
程序功能很简单,直接看 main 的代码
程序可以接受两次输入,第一次可以接受一个指针的值,之后会有一个 printf 函数将指针指向的内容以 %lld 格式打印出来。
再之后会进入一个我重命名的叫 load_filter 的函数,这个函数的作用就是创建一个系统调用白名单,这份白名单里有:
read() open() exit()
其他系统调用被调用时,内核会向进程发送 SIGSYS 信号并终止进程。
这个函数完成之后会进入下一个函数(也是我重命名过的),这个函数就可以进行栈溢出。
但是这里有一个问题就是,由于系统调用被禁用了,我们没有办法正常启 shell。
这时候有两个思路:
使过滤失效或者将 execve 加入到白名单中 利用仅有的三个系统调用获取 flag
刚开始我选择将重点放在过滤上,选择了第一种思路,走了很多弯路。因为我后来调试发现用来将过滤规则载入内核的 load 函数也是被禁用的状态。
于是后来我选择了第二种思路。
先通过 open() 打开 flag 文件,再通过 read() 将内容读入内存,再找一个同时带有 cmp 和跳转到一个被禁用的系统调用前的 je 或 jnz 的这么一个 gadget(有点难懂么? XD )。
由于跳到其他系统调用时进程接收到的信号时 SIGSYS ,而程序因为无效返回地址终止时接收到的信号是 SIGSEGV 。
这样我们就能对内存中的 flag 内容进行爆破了。
由于题目特殊性,exp 看看思路就好。
笔者是一名就读于成都信息工程大学的大二狗,Syclover 混吃等死只知后退不思进取二进制选手,主要学习二进制漏洞的分析和利用。
我是从大一进校之后才开始学习信安的,说实话进校之前我都不知道信息安全是干嘛的。
刚开始学习的是 php、sql 注入之类的东西,后来不知道怎么回事学到了二进制上面去 XD,觉得挺有意思,之后就一路坚持了下来。
实话就说这么多,求二进制大佬带一波 Orz。