SUCTF 2025
本文最后更新于23 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

1.SU_BBRE

这道题加了一点pwn的知识,二进制不分家了属于是

附件打开有一个很明显的rc4,注意小端序,解出来第一段flag是We1com3ToReWorld

def KSA(key):
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    return S


def PRGA(S):
    i, j = 0, 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        K = S[(S[i] + S[j]) % 256]
        yield K
def RC4Decrypt(key, text):
    S = KSA(key)
    keystream = PRGA(S)
    res = []
    for char in text:
        res.append(char ^ next(keystream))
    return bytes(res)
#示例
key = b'suctf'
plaintext = [0x2f, 0x5a, 0x57, 0x65, 0x14, 0x8f, 0x69, 0xcd, 0x93, 0x29, 0x1a, 0x55, 0x18, 0x40, 0xe4, 0x5e]
Rc4encrypt = RC4Decrypt(key, plaintext)
print(Rc4encrypt)

.text:0040223D ; =============== S U B R O U T I N E =======================================
.text:0040223D
.text:0040223D ; Attributes: noreturn bp-based frame
.text:0040223D
.text:0040223D                 public function1
.text:0040223D function1       proc near
.text:0040223D
.text:0040223D var_1F          = dword ptr -1Fh
.text:0040223D var_1B          = dword ptr -1Bh
.text:0040223D var_17          = byte ptr -17h
.text:0040223D var_16          = byte ptr -16h
.text:0040223D var_C           = dword ptr -0Ch
.text:0040223D
.text:0040223D ; __unwind {
.text:0040223D                 push    ebp
.text:0040223E                 mov     ebp, esp
.text:00402240                 sub     esp, 28h
.text:00402243                 mov     [ebp+var_1F], 4D626D41h
.text:0040224A                 mov     [ebp+var_1B], 294E4953h
.text:00402251                 mov     [ebp+var_17], 28h ; '('
.text:00402255                 sub     esp, 0Ch
.text:00402258                 push    offset s        ; "hhh,you find me:"
.text:0040225D                 call    puts
.text:00402262                 add     esp, 10h
.text:00402265                 sub     esp, 8
.text:00402268                 lea     eax, [ebp+var_16]
.text:0040226B                 push    eax
.text:0040226C                 push    offset aS       ; "%s"
.text:00402271                 call    __isoc99_scanf
.text:00402276                 add     esp, 10h
.text:00402279                 mov     [ebp+var_C], 0
.text:00402280                 jmp     short loc_4022B5
.text:00402282 ; ---------------------------------------------------------------------------
.text:00402282
.text:00402282 loc_402282:                             ; CODE XREF: function1+7C↓j
.text:00402282                 lea     edx, [ebp+var_16]
.text:00402285                 mov     eax, [ebp+var_C]
.text:00402288                 add     eax, edx
.text:0040228A                 movzx   eax, byte ptr [eax]
.text:0040228D                 movsx   eax, al
.text:00402290                 sub     eax, [ebp+var_C]
.text:00402293                 mov     edx, eax
.text:00402295                 lea     ecx, [ebp+var_1F]
.text:00402298                 mov     eax, [ebp+var_C]
.text:0040229B                 add     eax, ecx
.text:0040229D                 movzx   eax, byte ptr [eax]
.text:004022A0                 movsx   eax, al
.text:004022A3                 cmp     edx, eax
.text:004022A5                 jz      short loc_4022B1
.text:004022A7                 sub     esp, 0Ch
.text:004022AA                 push    0               ; status
.text:004022AC                 call    exit
.text:004022B1 ; ---------------------------------------------------------------------------
.text:004022B1
.text:004022B1 loc_4022B1:                             ; CODE XREF: function1+68↑j
.text:004022B1                 add     [ebp+var_C], 1
.text:004022B5
.text:004022B5 loc_4022B5:                             ; CODE XREF: function1+43↑j
.text:004022B5                 cmp     [ebp+var_C], 8
.text:004022B9                 jle     short loc_402282
.text:004022BB                 sub     esp, 0Ch
.text:004022BE                 push    offset aCongratulate ; "congratulate!!!"
.text:004022C3                 call    puts
.text:004022C8                 add     esp, 10h
.text:004022CB                 sub     esp, 0Ch
.text:004022CE                 push    0               ; status
.text:004022D0                 call    exit
.text:004022D0 ; } // starts at 40223D
.text:004022D0 function1       endp

但是后面function1还有一段加密,其实就是字符减去下标,逆向用输入加下标就行

flag = ''
enc = [0x41, 0x6D, 0x62, 0x4D, 0x53, 0x49, 0x4e, 0x29, 0x28]
for i, j in enumerate(enc):
    flag+=chr(i+j)
print(flag)

解出来是AndPWNT00

这是一个栈溢出漏洞,RC4只加密了16个字符,但是主函数中输入 19 个字符,所有末尾 3个用来栈溢出,ret 到 function1

function1 的地址是 0x0040223D,转换成字符串就是 =”@

所有flag就是SUCTF{We1com3ToReWorld=”@AndPWNT00}

2.SU_minesweeper

有点像一个扫雷游戏

这里是一个16进制的转换,但是这个字符对应集和标准hex是不一样的,是 abcdef0123456789

byte_4020 是一个 20×20 的矩阵(共400个字节),有些为 -1,其他为0~8的数字

这里是判断规则

sub_13C9计算 (i, j) 及其九宫格内的 bit 为 1 的个数(即9个格子的1的数量)

sub_1352 返回 a1(50字节=400bit)里第 20*n19+n19_1 位的 bit

这道题的逻辑就是把输入经过处理(每两个字符拼成一个字节)后,然后变成了50字节,也就是400个bit,这里就形成了一个20×20的扫雷地图,其中1的位置就是地雷,byte_4020对应的就是这个地图里面的数字即附近地雷的数量

z3 找出满足条件的一组解,然后按以上规则转换为输入

from z3 import *

# Step 1: 把你的 byte_4020 贴成 20x20 列表
byte_4020_raw = [
    0x3,0x4,0xFF,0xFF,0xFF,0x5,0xFF,0xFF,0xFF,0xFF,0xFF,0x4,0x4,0xFF,0xFF,0xFF,0xFF,0x2,0xFF,0xFF,
    0x4,0xFF,0x7,0xFF,0xFF,0xFF,0x4,0x6,0x6,0xFF,0xFF,0xFF,0xFF,0x6,0x5,0x6,0x4,0xFF,0x5,0xFF,
    0x4,0x7,0xFF,0x8,0xFF,0x6,0xFF,0xFF,0x6,0x6,0x5,0xFF,0xFF,0xFF,0xFF,0xFF,0x3,0x3,0xFF,0x3,
    0xFF,0x5,0x6,0x6,0xFF,0xFF,0xFF,0xFF,0x4,0x5,0x4,0x5,0x7,0x6,0xFF,0xFF,0x4,0xFF,0x2,0x1,
    0xFF,0xFF,0xFF,0x3,0x4,0xFF,0xFF,0x5,0x4,0x3,0xFF,0xFF,0x7,0x4,0x3,0xFF,0xFF,0x1,0x1,0xFF,
    0xFF,0x4,0x3,0xFF,0x2,0xFF,0x4,0x3,0xFF,0xFF,0x2,0xFF,0x5,0x4,0xFF,0xFF,0x2,0x2,0xFF,0xFF,
    0x4,0xFF,0x4,0xFF,0x3,0x5,0x6,0xFF,0xFF,0x0,0xFF,0xFF,0xFF,0x2,0xFF,0xFF,0xFF,0x1,0x4,0xFF,
    0xFF,0x7,0x5,0xFF,0xFF,0x3,0x3,0x2,0xFF,0xFF,0x4,0xFF,0xFF,0x5,0x7,0xFF,0x3,0x2,0x4,0x4,
    0xFF,0x7,0x5,0x4,0x3,0xFF,0xFF,0x4,0xFF,0x2,0x4,0x5,0xFF,0xFF,0x6,0x5,0x4,0xFF,0x2,0xFF,
    0xFF,0x7,0x4,0xFF,0xFF,0x3,0xFF,0x4,0x4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x4,0x3,0x2,0x2,
    0xFF,0xFF,0x2,0x4,0x3,0x5,0xFF,0xFF,0x5,0xFF,0x4,0xFF,0x6,0xFF,0xFF,0x6,0xFF,0xFF,0xFF,0xFF,
    0x3,0x3,0xFF,0x4,0xFF,0xFF,0xFF,0xFF,0xFF,0x6,0xFF,0x6,0x6,0xFF,0x7,0x6,0x4,0xFF,0x4,0x3,
    0xFF,0x4,0x3,0x5,0x4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x4,0x6,0x7,0xFF,0xFF,0x4,0xFF,0xFF,
    0xFF,0x7,0xFF,0x5,0xFF,0x5,0xFF,0xFF,0x6,0x7,0x7,0xFF,0x5,0x6,0x6,0xFF,0xFF,0x2,0x4,0x4,
    0xFF,0xFF,0xFF,0xFF,0xFF,0x6,0xFF,0xFF,0x7,0x7,0x6,0xFF,0x6,0xFF,0xFF,0xFF,0xFF,0x3,0xFF,0x3,
    0x5,0xFF,0x7,0xFF,0x5,0xFF,0x6,0xFF,0x5,0xFF,0xFF,0x7,0x8,0xFF,0xFF,0x3,0xFF,0x3,0xFF,0xFF,
    0xFF,0xFF,0xFF,0x3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x6,0x5,0x3,0xFF,0x4,0x5,0x5,0x3,
    0xFF,0xFF,0x6,0x5,0x5,0x6,0xFF,0x6,0x5,0x2,0x4,0x3,0x4,0xFF,0xFF,0x3,0x4,0x4,0x6,0x5,
    0xFF,0x3,0xFF,0x5,0x5,0x5,0xFF,0xFF,0x5,0xFF,0xFF,0x4,0xFF,0xFF,0x4,0xFF,0x7,0x7,0x8,0x6,
    0xFF,0xFF,0xFF,0xFF,0x5,0xFF,0xFF,0xFF,0x4,0xFF,0x3,0xFF,0x3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x5,0x3
]
# reshape to 20x20
byte_4020 = [byte_4020_raw[i*20:(i+1)*20] for i in range(20)]

# Step 2: 定义 20x20 的二值变量
B = [[Bool(f'b_{i}_{j}') for j in range(20)] for i in range(20)]

s = Solver()

# Step 3: 对每个 (i,j),byte_4020 不为 0xFF 的格子,约束其九宫格内 1 的数量
for i in range(20):
    for j in range(20):
        val = byte_4020[i][j]
        if val != 0xFF:
            cells = []
            for di in [-1,0,1]:
                for dj in [-1,0,1]:
                    ni, nj = i+di, j+dj
                    if 0 <= ni < 20 and 0 <= nj < 20:
                        cells.append(B[ni][nj])
            s.add(Sum([If(x, 1, 0) for x in cells]) == val)

# Step 4: 求解
if s.check() != sat:
    print("No solution!")
    exit(1)
m = s.model()

# Step 5: 提取 bit 并打包成 50 字节
bits = []
for i in range(20):
    for j in range(20):
        bits.append(1 if m[B[i][j]] else 0)

bytes_ = []
for k in range(0, 400, 8):
    b = 0
    for p in range(8):
        if k+p < len(bits):
            b |= bits[k+p] << p
    bytes_.append(b)

# Step 6: 按题目自定义编码输出100位字符串

def inv_sub_122B(x):
    if 0 <= x <= 5:
        return chr(ord('a')+x)
    elif 6 <= x <= 15:
        return chr(ord('0')+x-6)
    else:
        raise ValueError("invalid nibble")

result = ''
for b in bytes_:
    hi = (b >> 4) & 0xF
    lo = b & 0xF
    result += inv_sub_122B(hi)
    result += inv_sub_122B(lo)
print(result)

f57503596fb80f955fa5cad3cb282aa18ac62922a1981ea7b53b07a30709b508f3176601154250d509b7bee0f2170b898617
之后转换成md5加上SUCTF{}就是flag了

SUCTF{d661b98e4241de7423ef2d953098329d}

3.SU_Harmony

ArkTS层只有调so层,验证逻辑还是要去so找

全局搜索check找到加密函数

加了混淆,代码很丑

在贴出的循环部分,sub_57B0 是唯一被反复对输入片段直接调用的子函数,也就是验证的核心代码

所有的函数都加了大量的混淆,只能一个一个点开慢慢看

最终分析一下前面那一坨函数并整理一下可以得到
sub_6D20:字符串 * 字符串
sub_8270:字符串 * 数字
sub_9890:字符串 + 字符串
sub_A8F0:字符串 – 字符串
sub_C160:字符串的值整除2

也就是一个二元一次方程


最后写脚本解密一下

import math
from Crypto.Util.number import long_to_bytes

dt = [
999272289930604998,
1332475531266467542,
1074388003071116830,
1419324015697459326,
978270870200633520,
369789474534896558,
344214162681978048,
2213954953857181622
]
for x in dt:
c = (2 * x) + 3
delta = 4 + 4 * c
delta = math.sqrt(delta)
sol = (-2 + delta) / 2
sol = int(sol)
print(long_to_bytes(sol)[::-1].decode(), end="")

SUCTF{Ma7h_WorldIs_S0_B3aut1ful}

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇