2025-11-18-FZNCTF_WP

2025-11-18-FZNCTF_WP

十一月 18, 2025

FZNCTF 2025

这次最高18名,后面打不动了掉到了29名

WEB 签到 check_in

1
2
3
4
5
6
曰:北天始精灵玉仙凶乐玄梵神生梵融劫冥华炁渺霄阿东陀冥色净荡象象空仙阿魂东灵渺莽空西魄命梵玄坤生威人莽玉魂观终威仙道霄浩太劫始周东凶魂照龙浩仙浩仙清精北色御尊尊
曰:北茫天实虚周北茫灵魂微始道魄色净度劫华照始真幽玄融观阎虚净照莽度照幽运融天地东罗鬼阿冥界渺人道莽炁魄吉太魂西冥鬼华玉天地净霄观北鬼天渺界元坤灵幽人清运度魔道灵清终华玉劫南地观太人始渺幽微终龙南乐灵凶始清虚地阿渺玄真终荡浩南北吉鬼象道界终色净阎周地界观北龙玄魔魂度观量茫南精幽坤浩仙真魄空罗渺实阿地魄冥鬼天魂玉净阎南周阿度西终命精度观鬼净融华梵炁真实太北南幽西真莽炁量度灵界炁霄威魔融阎魄人净劫元陀威幽量炁浩灵太色陀梵实人终元象精炁空真浩龙终冥魂御命终玄道冥鬼阿鬼命灵周陀魄东劫运元玉度阿地莽吉灵命始道威量微灵命幽凶凶凶仙界玉地魄生色吉神生东东太荡运观南度华精御真华梵运灵北周太天龙神劫梵魂实吉周太照乐真界魔冥生凶龙元华观界劫冥人冥度莽阿阎威命东微坤威劫阎人度浩阎太威东劫界人微观生陀御仙阎南融南荡量真元南人神陀融仙西乐神凶乐元罗阎天莽东地南西西魄运
曰:太太周威西御魂乐灵度坤融梵虚鬼玄炁色元渺道玉神魄炁陀仙仙北阿劫人荡茫真浩西灵冥荡终霄坤魂空元元浩陀坤天观荡威清荡幽净劫阎色精凶玄冥观北虚玉照凶虚终冥量坤观神莽乐魄幽实照度浩象浩道御周威微阿东运魄华天魔吉元吉空实陀魄劫西元西周命观阿神南命天空神劫乐空神元界罗冥命精幽地凶融坤龙始冥度鬼凶华神界道凶梵度东威精炁命坤渺茫坤清终冥虚地御色融南陀人罗梵神神魔坤空净坤冥幽玄人玉象渺凶命西阿净象真阎净玄清虚凶照运界北灵度凶色精象魄灵清空鬼微陀虚运罗界人南始虚周西命终凶南陀元阎劫命清御凶命南度度界乐劫元玉浩清华浩阿霄终界华劫始浩陀玄灵运净御阿南始劫阎微荡融净坤色清观渺仙终阿精虚始莽梵玉虚虚尊
曰:坤魄实周界神色凶真道罗真威陀吉坤威幽莽人实霄始太融西运霄东魂霄象浩玉实魂坤北照微色魄地观命冥神度色人荡人幽玉神实元照坤精威浩吉霄魄威霄玉灵清坤空乐荡炁真东度乐魂道人梵玉空威灵度实命南荡元净渺威照始罗威始吉地道虚威华玉精御命莽仙量真梵荡灵象周霄浩魂元量浩道坤清御凶阿太乐幽劫运天融凶浩道威天虚华周道龙命量真阎炁威魄实运地魄乐精实冥精融生西人华观魄鬼运霄界精东仙北凶元量人人清地玉梵真浩南浩运真神仙陀命神西精西太劫莽照天空炁太魔御度照空罗荡仙量华威幽人生魂坤莽魔生净魂量地乐劫龙实生南坤灵地御威实人玄度神生道天观南罗色魂南魂界鬼玄实融象荡量周天空净莽微人冥吉太终劫乐始梵罗空量道地东玉罗真观清仙荡梵命命灵北运魔天玄量浩西空渺

Raboutttt说CTF很有意思,先这样再那样最后再这样就好了,学会了吗,你来试试吧
  • 题目提示需要工具,以为是工具搜集题目,在网上找到了题目的原作者 天书加密
  • https://github.com/BlackCat184/Sealed-Book
  • 但是发现没有密钥解不出来,于是继续猜测最终结果如下:
  • 访问/robots.txt 拿到关键信息 1
    • 提示有1.py 2.py两个脚本可以下载

脚本1

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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 项目改自:https://github.com/BlackCat184/Sealed-Book


import argparse
import base64
import hashlib
import os
import sys
from typing import Tuple

try:
from Crypto.Cipher import AES
except ImportError:
print("[错误] 需要安装 pycryptodome,请运行:pip install pycryptodome", file=sys.stderr)
raise


DEFAULT_PASSWORD = "F.Z.N CTF"


def evp_bytes_to_key(password: bytes, salt: bytes, key_len: int, iv_len: int) -> Tuple[bytes, bytes]:
"""OpenSSL EVP_BytesToKey (MD5) 派生 key 和 iv。"""
d = b""
prev = b""
while len(d) < key_len + iv_len:
prev = hashlib.md5(prev + password + salt).digest()
d += prev
key = d[:key_len]
iv = d[key_len:key_len + iv_len]
return key, iv


def pkcs7_pad(data: bytes, block_size: int = 16) -> bytes:
pad_len = block_size - (len(data) % block_size)
return data + bytes([pad_len] * pad_len)


MAP_FORWARD = [
("e", "渺"), ("E", "莽"), ("t", "茫"), ("T", "乐"),
("a", "生"), ("A", "终"), ("o", "仙"), ("O", "鬼"),
("i", "人"), ("I", "吉"), ("n", "凶"), ("N", "清"),
("s", "灵"), ("S", "空"), ("h", "命"), ("H", "精"),
("r", "炁"), ("R", "神"), ("d", "魔"), ("D", "梵"),
("l", "周"), ("L", "量"), ("c", "道"), ("C", "天"),
("u", "地"), ("U", "荡"), ("m", "度"), ("M", "罗"),
("w", "色"), ("W", "元"), ("f", "始"), ("F", "玄"),
("g", "御"), ("G", "浩"), ("y", "劫"), ("Y", "虚"),
("p", "界"), ("P", "真"), ("b", "实"), ("B", "华"),
("v", "威"), ("V", "运"), ("k", "魂"), ("K", "魄"),
("j", "融"), ("J", "象"), ("x", "霄"), ("X", "冥"),
("q", "照"), ("Q", "净"), ("z", "微"), ("Z", "幽"),
("0", "观"), ("1", "陀"), ("2", "阿"), ("3", "龙"),
("4", "阎"), ("5", "东"), ("6", "西"), ("7", "南"),
("8", "北"), ("9", "玉"), ("+", "太"), ("/", "坤"), ("=", "尊"),
]


def apply_forward_mapping(s: str) -> str:
for a, b in MAP_FORWARD:
s = s.replace(a, b)
return s


def seal_encrypt(plaintext: str, password: str = DEFAULT_PASSWORD) -> str:

salt = os.urandom(8)
key, iv = evp_bytes_to_key(password.encode("utf-8"), salt, key_len=32, iv_len=16)

data = plaintext.encode("utf-8")
cipher = AES.new(key, AES.MODE_CBC, iv)
ct = cipher.encrypt(pkcs7_pad(data))

raw = b"Salted__" + salt + ct
b64 = base64.b64encode(raw).decode("ascii")

assert b64.startswith("U2FsdGVkX1"), "Base64 前缀不符"
mapped = apply_forward_mapping(b64[10:])
return "曰:" + mapped


def main():
parser = argparse.ArgumentParser(description="天书加密(Python)")
parser.add_argument("--text", required=True, help="待加密明文")
parser.add_argument("--key", default=DEFAULT_PASSWORD, help="密钥(默认 BlackCat184)")
args, _unknown = parser.parse_known_args()

out = seal_encrypt(args.text, args.key)
print(out)


if __name__ == "__main__":
main()

脚本2

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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 项目改自:https://github.com/BlackCat184/Sealed-Book

import argparse
import base64
import hashlib
import sys
from typing import Tuple

try:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
except ImportError:
print("[错误] 需要安装 pycryptodome,请运行:pip install pycryptodome", file=sys.stderr)
raise

DEFAULT_PASSWORD = "BlackCat184"

# 与加密脚本完全对应的反向字符映射
MAP_BACKWARD = [
("渺", "e"), ("莽", "E"), ("茫", "t"), ("乐", "T"),
("生", "a"), ("终", "A"), ("仙", "o"), ("鬼", "O"),
("人", "i"), ("吉", "I"), ("凶", "n"), ("清", "N"),
("灵", "s"), ("空", "S"), ("命", "h"), ("精", "H"),
("炁", "r"), ("神", "R"), ("魔", "d"), ("梵", "D"),
("周", "l"), ("量", "L"), ("道", "c"), ("天", "C"),
("地", "u"), ("荡", "U"), ("度", "m"), ("罗", "M"),
("色", "w"), ("元", "W"), ("始", "f"), ("玄", "F"),
("御", "g"), ("浩", "G"), ("劫", "y"), ("虚", "Y"),
("界", "p"), ("真", "P"), ("实", "b"), ("华", "B"),
("威", "v"), ("运", "V"), ("魂", "k"), ("魄", "K"),
("融", "j"), ("象", "J"), ("霄", "x"), ("冥", "X"),
("照", "q"), ("净", "Q"), ("微", "z"), ("幽", "Z"),
("观", "0"), ("陀", "1"), ("阿", "2"), ("龙", "3"),
("阎", "4"), ("东", "5"), ("西", "6"), ("南", "7"),
("北", "8"), ("玉", "9"), ("太", "+"), ("坤", "/"), ("尊", "="),
]


def apply_backward_mapping(s: str) -> str:
"""将中文字符反向映射回Base64字符[3](@ref)"""
for chinese_char, base64_char in MAP_BACKWARD:
s = s.replace(chinese_char, base64_char)
return s


def evp_bytes_to_key(password: bytes, salt: bytes, key_len: int, iv_len: int) -> Tuple[bytes, bytes]:
"""OpenSSL EVP_BytesToKey (MD5) 派生 key 和 iv,与加密完全一致[1,6](@ref)"""
d = b""
prev = b""
while len(d) < key_len + iv_len:
prev = hashlib.md5(prev + password + salt).digest()
d += prev
key = d[:key_len]
iv = d[key_len:key_len + iv_len]
return key, iv


def seal_decrypt(ciphertext: str, password: str = DEFAULT_PASSWORD) -> str:
"""解密天书加密的内容[1,7](@ref)"""

if ciphertext.startswith("曰:"):
ciphertext = ciphertext[2:]

base64_str = apply_backward_mapping(ciphertext)

full_base64 = "U2FsdGVkX1" + base64_str

try:
raw_data = base64.b64decode(full_base64)
except Exception as e:
raise ValueError("Base64解码失败,请检查密文格式") from e

if len(raw_data) < 16 or not raw_data.startswith(b"Salted__"):
raise ValueError("无效的密文格式:缺少'Salted__'前缀")

salt = raw_data[8:16]
encrypted_data = raw_data[16:]


key, iv = evp_bytes_to_key(password.encode("utf-8"), salt, key_len=32, iv_len=16)

cipher = AES.new(key, AES.MODE_CBC, iv)
try:
decrypted_padded = cipher.decrypt(encrypted_data)
decrypted = unpad(decrypted_padded, AES.block_size)
except ValueError as e:
raise ValueError("解密失败:可能是密码错误或密文损坏") from e

return decrypted.decode("utf-8")


def main():
parser = argparse.ArgumentParser(description="天书解密(Python)")
parser.add_argument("--text", required=True, help="待解密的密文(以'曰:'开头)")
parser.add_argument("--key", default=DEFAULT_PASSWORD, help="密钥(必须与加密时相同)")
args = parser.parse_args()

try:
plaintext = seal_decrypt(args.text, args.key)
print(f"解密结果: {plaintext}")
except Exception as e:
print(f"解密失败: {e}")
sys.exit(1)


if __name__ == "__main__":
main()

一个加密一个解密 在加密脚本里找到了密钥

DEFAULT_PASSWORD = "F.Z.N CTF"

随后使用脚本解密 得出flag

重定向?

(题目名字忘了)一直等官方wp一直没出

  • 访问题目网站发现有个?link参数 可以跳转到别的访问

  • 访问帮助页面发现flag位置在 题目地址/flag

猜测这是通过重定向来访问文件实现越权

先通过引发报错,得到了内网监听的端口是 7000

  • 构造URL link=http://127.0.0.1:7000/flag

发现被过滤,多个地址均被过滤,但是域名未被过滤

于是我手动DNS了一个域名指向本地地址

xxx.xxx.xx/flag

然后让构造访问,直接拿到flag


不过找到了当时做的笔记

1.url = https://www.baidu.com GET

// 服务器 访问 baidu.com

直接把数据 返回给我们

//

  1. 重定向访问
  2. 已知:flag位置 http://5000-4a2fb514-5ce4-4056-abb1-80ae1e33c1ee.challenge.ctfplus.cn/flag
  3. Access Denied
  4. 猜测: 这个flag资源只能被内网请求 () 127.0.0.1 是一个特殊的IPv4 地址,称为本地回环地址或 localhost,用于指代“本计算机”
  5. 尝试:
  6. http://5000-4a2fb514-5ce4-4056-abb1-80ae1e33c1ee.challenge.ctfplus.cn/fetch?url=http://127.0.0.1:5000/flag
  7. 内网地址重入 攻击获取 flag
  8. 黑名单过滤 ,loacl , 127.. ,
  9. 手动实现dns
  10. baidu.com > ip
  11. a.curesky.site > 127.0.0.1
  12. 5000

http://5000-4a2fb514-5ce4-4056-abb1-80ae1e33c1ee.challenge.ctfplus.cn/fetch?url=http://a.curesky.site:5000/flag

诸神的三试炼

第一段flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
_BOOL8 __fastcall flag1(const char *a1)
{
char buf[32]; // [rsp+40h] [rbp-20h] BYREF

_mingw_snprintf(
buf,
0x14ui64,
"%s%s%s%s",
(const char *)f_str,
(const char *)l_str,
(const char *)open_brace,
G00d_str);
return strcmp(a1, buf) == 0;
}

第二段flag

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
_BOOL8 __fastcall flag2(const char *a1)
{
int v1; // eax
int v2; // eax
int v3; // eax
int v4; // eax
char Str2[14]; // [rsp+22h] [rbp-3Eh] BYREF
_DWORD v7[4]; // [rsp+30h] [rbp-30h]
int v8; // [rsp+40h] [rbp-20h]
int v9; // [rsp+44h] [rbp-1Ch]
int v10; // [rsp+48h] [rbp-18h]
int data; // [rsp+4Ch] [rbp-14h]
char v12; // [rsp+50h] [rbp-10h]
char v13; // [rsp+51h] [rbp-Fh]
char v14; // [rsp+52h] [rbp-Eh]
char v15; // [rsp+53h] [rbp-Dh]
int v16; // [rsp+54h] [rbp-Ch]
int i; // [rsp+58h] [rbp-8h]
int v18; // [rsp+5Ch] [rbp-4h]

data = get_data(0i64);
v10 = get_data(1i64);
v9 = get_data(2i64);
v8 = get_data(3i64);
memset(Str2, 0, sizeof(Str2));
v18 = 0;
v7[0] = data;
v7[1] = v10;
v7[2] = v9;
v7[3] = v8;
for ( i = 0; i <= 3; ++i )
{
v16 = v7[i];
v14 = BYTE1(v16);
v15 = v16;
v12 = HIBYTE(v16);
v13 = BYTE2(v16);
if ( v18 <= 12 )
{
v1 = v18++;
Str2[v1] = v15;
}
if ( v18 <= 12 )
{
v2 = v18++;
Str2[v2] = v14;
}
if ( v18 <= 12 )
{
v3 = v18++;
Str2[v3] = v13;
}
if ( v18 <= 12 )
{
v4 = v18++;
Str2[v4] = v12;
}
}
Str2[13] = 0;
return strcmp(a1, Str2) == 0;
}

__int64 __fastcall get_data(int a1)
{
if ( a1 == 3 )
return 95i64;
if ( a1 <= 3 )
{
switch ( a1 )
{
case 2:
return 1849713503i64;
case 0:
return 1999654495i64;
case 1:
return 1429240159i64;
}
}
return 0i64;
}

直接写逆向脚本来解

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
154
155
156
157
158
159
160
161
import struct

def decrypt_flag1():
"""解密第一部分flag"""
print("=== Part 1 Decryption ===")

# 根据flag1函数,第一部分是字符串拼接
# f_str + l_str + open_brace + G00d_str

# 从.data段可以推断这些字符串的内容
# f_str 可能对应 "f" 或 "flag"
# l_str 可能对应 "l" 或 "lag"
# open_brace 可能是 "{"
# G00d_str 是 "G00d!!!"

# 常见的flag开头是 "flag{"
part1 = "flag{G00d!!!"
print(f"Part 1: {part1}")
return part1

def decrypt_flag2():
"""解密第二部分flag"""
print("\n=== Part 2 Decryption ===")

# 根据flag2函数,从get_data获取数据并重组
data_values = [
get_data(0), # 1999654495
get_data(1), # 1429240159
get_data(2), # 1849713503
get_data(3) # 95
]

print(f"Data values: {[hex(x) for x in data_values]}")

# 将每个32位整数拆分为4个字节
Str2 = []
v18 = 0

for value in data_values:
# 拆分为字节: [b0, b1, b2, b3]
bytes_list = [
value & 0xFF, # v15 - 最低字节
(value >> 8) & 0xFF, # v14
(value >> 16) & 0xFF, # v13
(value >> 24) & 0xFF # v12 - 最高字节
]

print(f"Value {hex(value)} -> bytes: {[hex(b) for b in bytes_list]}")

# 按顺序添加到Str2
for byte in bytes_list:
if v18 <= 12:
Str2.append(byte)
v18 += 1

# 转换为字符串
part2 = ""
for byte in Str2:
if 32 <= byte <= 126:
part2 += chr(byte)
else:
part2 += f"\\x{byte:02x}"

print(f"Part 2 bytes: {[hex(b) for b in Str2]}")
print(f"Part 2: {part2}")

# 尝试ASCII显示
ascii_part2 = ""
for byte in Str2:
if 32 <= byte <= 126:
ascii_part2 += chr(byte)
else:
ascii_part2 += "?"

print(f"Part 2 (ASCII): {ascii_part2}")

return ascii_part2

def get_data(index):
"""模拟get_data函数"""
if index == 3:
return 95
elif index == 2:
return 1849713503
elif index == 0:
return 1999654495
elif index == 1:
return 1429240159
else:
return 0

def decrypt_flag3():
"""解密第三部分flag - 使用正确的随机种子"""
print("\n=== Part 3 Decryption ===")

encrypted_part3 = [0xCD, 0xCE, 0xD0, 0x58, 0x5D, 0x92, 0x42, 0xB1, 0x7C, 0xA9, 0xFF]

# 尝试不同的随机种子
seeds_to_try = [0x1337, 0x1234, 0xDEAD, 0xBEEF, 0xCAFE, 0x1337C0DE, 0, 1, 1337]

for seed in seeds_to_try:
random.seed(seed)
rand_sequence = [random.randint(0, 255) & 0xFF for _ in range(len(encrypted_part3))]

decrypted = []
for i in range(len(encrypted_part3)):
decrypted_byte = encrypted_part3[i] ^ rand_sequence[i]
decrypted.append(decrypted_byte)

# 检查是否都是可打印ASCII
all_printable = all(32 <= byte <= 126 for byte in decrypted)

if all_printable:
part3 = ''.join(chr(b) for b in decrypted)
print(f"Found valid Part 3 with seed {hex(seed)}: {part3}")
return part3

# 如果没有找到完全可打印的,显示最可能的结果
print("No fully printable sequence found. Trying seed 0x1337 (common CTF seed):")
random.seed(0x1337)
rand_sequence = [random.randint(0, 255) & 0xFF for _ in range(len(encrypted_part3))]

decrypted = []
for i in range(len(encrypted_part3)):
decrypted_byte = encrypted_part3[i] ^ rand_sequence[i]
decrypted.append(decrypted_byte)

part3 = ''.join(chr(b) if 32 <= b <= 126 else f'\\x{b:02x}' for b in decrypted)
ascii_part3 = ''.join(chr(b) if 32 <= b <= 126 else '?' for b in decrypted)

print(f"Part 3 bytes: {[hex(b) for b in decrypted]}")
print(f"Part 3: {part3}")
print(f"Part 3 (ASCII): {ascii_part3}")

return ascii_part3

def combine_flag():
"""组合完整的flag"""
print("\n" + "="*50)
print("COMBINING COMPLETE FLAG")
print("="*50)

part1 = decrypt_flag1()
part2 = decrypt_flag2()
part3 = decrypt_flag3()

print(f"\n=== FINAL FLAG ===")
print(f"Part 1: {part1}")
print(f"Part 2: {part2}")
print(f"Part 3: {part3}")

# 组合flag格式: flag{part1_part2_part3}
# 但根据flag1,part1已经包含 "flag{" 开头
final_flag = f"{part1}_{part2}_{part3}"
print(f"\nFINAL FLAG: {final_flag}")

# 重新导入random
import random

if __name__ == "__main__":
combine_flag()
1
2
3
4
5
6
=== FINAL FLAG ===
Part 1: flag{G00d!!!
Part 2: _N0w_y0U_c@n_
Part 3: ???{By??H??

FINAL FLAG: flag{G00d!!!__N0w_y0U_c@n__???{By??H??

第三段flag点进去后会发现有一个假函数 也就是 flag3_fake

通过逆向找到真函数代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void __noreturn gift2()
{
int j; // [rsp+28h] [rbp-8h]
int i; // [rsp+2Ch] [rbp-4h]

srand(0x1234u);
for ( i = 0; i < dword_7FF7D240202C; ++i )
encrypted[i] ^= rand();
_mingw_printf("Decrypted part 3: ");
for ( j = 0; j < dword_7FF7D240202C; ++j )
putchar(encrypted[j]);
putchar(10);
puts("\nPress any key to exit...");
system("pause");
exit(0);
}

数据

1
2
3
4
5
6
7
8
9
.data:00007FF7D2402020                                         ; DATA XREF: gift2+28↑o
.data:00007FF7D2402020 ; gift2+3D↑o ...
.data:00007FF7D240202A db 0FFh, 0
.data:00007FF7D240202C dword_7FF7D240202C dd 0Bh ; DATA XREF: gift2:loc_7FF7D23F198B↑r
.data:00007FF7D240202C ; gift2:loc_7FF7D23F19CC↑r
.data:00007FF7D2402030 ; Function-local static variable
.data:00007FF7D2402030 ; func_ptr *p_0
.data:00007FF7D2402030 p_0 dq offset qword_7FF7D2401560
.data:00007FF7D2402030 ; DATA XREF: __do_global_

但是直接逆向有点困难

于是我们直接修改 flag3_fake()的汇编代码,让其永久循环被跳过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; Attributes: noreturn bp-based frame

; void __noreturn flag3_fake()
public flag3_fake
flag3_fake proc near
push rbp
mov rbp, rsp
sub rsp, 20h
lea rax, aWhereIsFlag3Le ; "\nWhere is flag3? Let me think..."
mov rcx, rax ; Buffer
call puts
lea rax, aMaybeYouNeedTo ; "Maybe you need to use IDA to find the r"...
mov rcx, rax ; Buffer
call puts
loc_7FF7D23F172E: ; dwMilliseconds
mov ecx, 3E8h
mov rax, cs:__imp_Sleep
flag3_fake endp ; sp-analysis failed

即可解出flag

babyRSA

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
import gmpy2
from Crypto.Util.number import long_to_bytes

n = 20116181115185256137151544962617461969127485079539874123483790179691451026659727409952002522890170053136432251484598758202749921581090222404128778286934496528266389586853622784941814905724374659722595150921699219297623333597701057604954651692729250881800409786303147914726282180533059864870515367790648247781809163302813093152120079755029826297153011886828297246071460547641705486357297417233767122432498694486964352342801912061641754894572829465334992075139630449930115173945624459850685926333265959551792326024379566586598102224813413893610109613076229994854338779713676554568790353735720642800095499489845542499243
c1 = 12574615186232508658058781239986357997690221455470707882708919538344867889046756940392586425215083828833917124513057649780713973391884806015516720789553246206689136824212927709627728801288304629797527956697243414600762350309939098109707454770146886759387428373061131102235552303687641823437812335805429238591967849935403611629054866383093303269886760991083623181420555220699498615783586898665752705379697907365417049724321589193519522361497873285356095894318360385694468646045503420079559869794438944147268173957051158766280963203638656844473553699835099487100606197891044561669390594778573658401240732422863142027023
c2 = 8662571349644155432847824828393121776098409938437916669251107156874603954233944520039184762114790042807930511195272715449923236367967421315595229284040395158368328305331059953339506075385510377512416235056474493145376149687125036012499178684540639274797034307771820863938251633851132423882652884158338847776108707048056678532767030904154141724336547346190841924329986431350894721191694788035732072169271856487733779585742838903331792853668246043440328934976370767441620664696662437609635592559825455438053522844176591659902503216608366261175995953153271651775486985191394510128456927543378761783994086684673696547021

e1 = 65537
e2 = 2333

# 使用扩展欧几里得算法找到 s 和 t,使得 e1*s + e2*t = gcd(e1, e2) = 1
g, s, t = gmpy2.gcdext(e1, e2)

# 根据 s 和 t 的符号计算 m
if s < 0:
part1 = pow(gmpy2.invert(c1, n), -s, n)
else:
part1 = pow(c1, s, n)

if t < 0:
part2 = pow(gmpy2.invert(c2, n), -t, n)
else:
part2 = pow(c2, t, n)

m = (part1 * part2) % n

# 将明文转换为字节串
flag = long_to_bytes(m)
print(flag.decode())

Lulala

这题是时间隐写,直接搞出gif时间戳写脚本做就行了


后面不太好讲了,有道AES解密找不到当时写的脚本了,等后面如果有题目复现继续补充