文档说明
本文作者:SwBack
创作时间:2025年5月20日,09:42:51
知乎:https://www.zhihu.com/people/back-88-87
优快云:https://blog.youkuaiyun.com/qq_30817059
百度搜索: SwBack
签到题
无需多说
web1-CatBank
palu{a25a28964181450cab538d428a3a0c7e}
注册两个账户,一个admin用户一个test用户,使用test用户给admin用户转账,发现网站没有限制金额任意转即可,在cookie中发现用户的id值,admin最先注册的是9那么test就是10,根据转账id值只要转给admin用户1000000然后admin用户把多余的钱转给其他人,就获得了flag值
web2-猫猫的秘密
/login没什么用,审计前端代码发现新接口/get_secret
结合/login接口和token信息,构造JWT字段验证
验证返回public,但是前端有三个权限
猜测存在权限验证字段,bp爆破一下(省略),最终得到role,直接构造admin跑一下
web3-CatNet
palu{ef42848e4eeb4ca4a4af93bff1e2a57c}
主页为截图功能,没啥用,打了半天ssrf…
爆破目录发现有admin页面,截图功能正常访问,直接访问被拒,构造xxf绕过得到flag链接
根据内容访问需要auth字段,构造默认的提示code错误,写个脚本对验证字段000进行FUZZ
(环境重开的,链接懒得改了)
WEB4-ezblog
比赛结束容器打不开。。。
题目描述:/app
下载附件app.jar,小茶杯反编译看源码,在app.class中发现路径别名映射
在dashboard.class中查看映射的接口,发现backdoor,但是需要key
继续跟进文件组件审计,FileStaticRepository组件中的find方法没有对路径进行过滤,该方法可以正常解析…/造成目录穿越和遍历的漏洞下载任意文件
bp构造数据包爆破一下上级发现app.jar包
payload:/assets/…/app.jar
分析下载的app.jar,继续用小茶杯打开查看,发现backdoor的key,构造POST请求得到flag
curl http://ip/backdoor --data ‘key=NlRVANA@007’
Crypto-循环锁链
palu{iC7uDoJJMAWnIhkkCNiIoCZZVmiPrk9}
先把那串十六进制字符串变成字节序列,然后根据已知明文的第一个字符是 ‘p’(也就是 p[0] = ord(‘p’)),用公式 p[i+1] = p[i] ⊕ c[i] 一次推算出后面的每一个明文字节,把所有字节拼到一起再解码,
得到答案:palu{iC7uDoJJMAWnIhkkCNiIoCZZVmiPrk9}
脚本:
hex_str = "110d190e122a7442312b2500070c163927210300280d2720262c19000c3b0439221952440d"
c = bytes.fromhex(hex_str)
n = len(c)
p = bytearray(n)
p[0] = ord('p') # 已知起点
# 利用 p[i+1] = p[i] ⊕ c[i] 递推恢复
for i in range(n-1):
p[i+1] = p[i] ^ c[i]
flag = p.decode()
print(flag)
Crypto-轮回密码
palu{reincarnation_cipher}
在提供的encode.py脚本中,观察到整个加密流程共分为四个关键步骤:
位移加密
Base85编码
再次位移
异或加密
从密文入手,对其进行与密钥的异或运算,撤销最后一步加密操作;
然后将结果按相同位移步长向左恢复原状,撤销第二次的位移操作;
接着使用Base85解码得到第一次位移前的中间数据;最后再次进行左轮转操作,还原出原始明文。
最终解出flag:palu{reincarnation_cipher}。
参考脚本:
import base64
def rotate_right(byte, step):
return ((byte >> step) | ((byte << (8 - step)) & 0xFF)) & 0xFF
def rotate_left(byte, step):
return ((byte << step) | (byte >> (8 - step))) & 0xFF
def samsara_decrypt(cipher, key_word):
cycle_step = len(key_word) % 6 + 1
phase3 = bytes([cipher[i] ^ key_word[i % len(key_word)] for i in range(len(cipher))])
phase2 = bytes([rotate_left(c, cycle_step) for c in phase3])
phase1 = base64.b85decode(phase2)
plain = bytes([rotate_left(c, cycle_step) for c in phase1])
return plain
# 用bytes字节写密文
cipher_bytes = b"y\xa6\x81_\x9b6\x19>X\xacy\x96!,!n\xa1mS\x1fa\xdc\xf1\xfc\xeb\x15\x18\x979\xbc\x116\x99\r"
key = b"Bore"
flag = samsara_decrypt(cipher_bytes, key)
print(flag.decode())
MISC-时间循环的信使
palu{Time_1s_cycl1c@l_0x}
时间戳排序-》保留|右侧字符重复数据-》取右侧1位组成hex解码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import re
import binascii
def extract_hex_from_logs(input_file='log.txt'):
# 读取日志文件
with open(input_file, 'r') as f:
lines = f.readlines()
# 过滤和排序
pattern = re.compile(r'^([0-9a-fA-F])\1{7}$')
parsed_lines = []
for line in lines:
line = line.strip()
if not line:
continue
parts = line.split('|', 1)
if len(parts) == 2:
try:
timestamp = int(parts[0])
data = parts[1]
if len(data) == 8 and pattern.match(data):
parsed_lines.append((timestamp, data))
except ValueError:
continue
# 按时间戳排序
sorted_lines = sorted(parsed_lines, key=lambda x: x[0])
# 提取每行右侧最后一个字符并拼接
hex_result = ''.join(data[-1] for _, data in sorted_lines)
# 输出hex结果
print(f"提取的hex: {hex_result}")
# 解码hex为ASCII/字符串
try:
# 确保hex字符串长度是偶数
if len(hex_result) % 2 != 0:
hex_result += "0" # 补0使长度为偶数
decoded = binascii.unhexlify(hex_result).decode('utf-8', errors='replace')
print(f"解码结果: {decoded}")
# 保存解码结果到文件
with open("flag.txt", 'w') as f:
f.write(decoded)
except Exception as e:
print(f"解码失败: {e}")
# 保存原始hex到文件
with open("flag.txt", 'w') as f:
f.write(hex_result)
if __name__ == "__main__":
input_file = sys.argv[1] if len(sys.argv) > 1 else 'log.txt'
extract_hex_from_logs(input_file)
MISC-几何闪烁的秘密
palu{master_of_geometry}
先找个gif分割的网站,把gif图片分割成100张图片,
然后从第一张开始分别记录四列数据值
圆:cGFsXttcFsdXtcGFdXtt
方形:YXN0XJfYN0ZXfYXNZXJf
三角:b2Zf2VvbZfZ2vb2ZZ2Vv
五边:bWV0nI9bV0cn9bWVcnI9
cGFsXttcFsdXtcGFdXtt
YXN0XJfYN0ZXfYXNZXJf
b2Zf2VvbZfZ2vb2ZZ2Vv
bWV0nI9bV0cn9bWVcnI9
四列数据都只由典型的 Base64 字符(正斜杠、加号、大小写字母、数字和等号)组成,直觉上可以尝试将它们按顺序直接拼接成一个完整的 Base64 串;拼接后得到一个长度为20×4=80字符的字符串,再对其进行 Base64 解码时会出现部分二进制数据或不可见字符,但仔细留意可见 ASCII 区域可以提取到诸如 “pal” 、 “u{” 、 “mast” 、 “er\” 、 “of\” 、 “geometry” 和 “r}” 等连续可读片段;也就是说,拼接后完整的 Base64 串解码后呈现的文本大致是 “pal<u{mast er\of\ geomet rr=}” 这种格式,
我们去掉中间那些二进制、占位符或不可见字符并将可见字符连续起来,注意到“master_of_ geometrr” 显然不是一个合理的英文短语,但它看上去很接近 “master_of_geometry” 也就是“几何大师”。,把 “geo” + “metr” 再加上一个“y”可以得到“geometry”。经过把噪声字节忽略、挑出一连串可读的小写字母之后,剩下的 “geometrr” 其实只差一个 “y” 才能成为常见的英文单词“geometry”。字面意义上的 “y” 对应位置其实被噪声字节占掉了,但是因为“geometrr”本身并不是有效单词,合情合理地把它修正成“geometry”。最终就能拼出一句完整的 palu{master_of_geometry}
MISC-时间折叠(TimeFold Paradox)
palu{This_is_A_Sample_Flag_Change_Me!!}
整个解密流程可以拆成三步,按顺序:先提取十六进制低字节用正则表达式 在日志字符串里搜索,每次匹配都把包含在括号里的两位十六进制字符提取出来,结果生成一个列表 hex_lows。然后做异或运算把列表里每个十六进制字符串转换成整数,与固定的异或密钥 0x8E(十进制 142)做异或运算(^0x8E),运算结果就是一个新的整数值,对应一个 ASCII 码。最后把解密后的字符拼成字符串,对每个异或运算得到的整数,用 chr 转成字符,然后用 ‘’.join 把这些字符按顺序连接,得到最终的解密结果 palu{This_is_A_Sample_Flag_Change_Me!!}
脚本:
import re
# Sample log data
log_data = """
[1970-01-01 08:00:00] System boot sequence initiated
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 00000000fe ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 10000000ef ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 20000000e2 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 30000000fb ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 40000000f5 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 50000000da ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 60000000e6 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 70000000e7 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 80000000fd ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 90000000d1 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 100000000e7 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 110000000fd ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 120000000d1 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 130000000cf ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 140000000d1 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 150000000dd ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 160000000ef ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 170000000e3 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 180000000fe ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 190000000e2 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 200000000eb ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 210000000d1 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 220000000c8 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 230000000e2 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 240000000ef ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 250000000e9 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 260000000d1 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 270000000cd ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 280000000e6 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 290000000ef ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 300000000e0 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 310000000e9 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 320000000eb ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 330000000d1 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 340000000c3 ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 350000000eb ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 360000000af ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 370000000af ns
[1970-01-01 08:00:00] SYSTEM ALERT: Time anomaly detected at 380000000f3 ns
[1970-01-01 08:00:00] System entering chronostasis mode
"""
# 正则提取每行最后两位十六进制数
hex_lows = re.findall(r'([0-9A-Fa-f]{2}) ns', log_data)
# 将每个字节与异或密钥 0x8E 解密
decoded_chars = [chr(int(h, 16) ^ 0x8E) for h in hex_lows]
# 拼接得到 flag
flag = ''.join(decoded_chars)
print(flag)
时空交织的密语
我们在暗网服务器中发现了一个神秘文件,据说是某个黑客组织的「时空密钥」,文件内容似乎由大量时间戳构成。情报显示,只有将时间维度与二进制低语结合才能解开秘密。线索隐藏在时空的起点与终点之间。
16进制打开.找到70616c75 palu的16进制 规律.从而拿到flag… 纯手敲
MISC-screenshot
palu{why_you_spy_me}
苹果手机打开照片用黑白滤镜拉到最右侧微调,然后调高手机亮度仔细看palu{why_you_spy_me}
Crypto-欧几里得
palu{48b635a7a2474ef743e333478b67a2f5}
思路:
已知一个超级大的数 c 等于两个秘密数 m1 和 m2 相加,找到那个我们想要的 m1(也就是 flag)。
先猜出 m2 是什么样的:猜它是由一对字节 b0、b1 反复拼成 70 字节,枚举所有 256×256 种可能;
对每一种可能的字节模式,把它当成一个大整数 m2,再用 c–m2 算出 m1;
然后把这个 m1 转成字节串,看它去掉前导零后是不是以 “palu{” 开头、以 “}” 结尾。
如果符合,就把它打印出来当做 flag,然后结束整个循环。
通过这种暴力枚举加格式校验的方法,就能从 c 中最后获得值:palu{48b635a7a2474ef743e333478b67a2f5}。
脚本:
import itertools
# 已知的解密结果 c(m1 + m2)
c = 1426774899479339414711783875769670405758108494041927642533743607154735397076811133205075799614352194241060726689487117802867974494099614371033282640015883625484033889861
BYTE_LEN_C = (c.bit_length() + 7) // 8
for b0, b1 in itertools.product(range(256), repeat=2):
# 构造 70 字节的 m2
pattern = bytes([b0, b1]) * 35
m2 = int.from_bytes(pattern, 'big')
if c <= m2:
continue
m1 = c - m2
try:
m1_bytes = m1.to_bytes(BYTE_LEN_C, 'big').lstrip(b'\x00')
if m1_bytes.startswith(b'palu{') and m1_bytes.endswith(b'}'):
print("flag:", m1_bytes.decode())
break
except OverflowError:
continue
RE-PositionalXOR
palu{48b635a7a2474ef743e333478b67a2f5}
思路:
先把加密后的内容 `encrypted = b"qcoqVh{ebccocH^@Lgt{gt|g"` 存到一个字节串里,
然后用 enumerate 遍历每个字节和它的下标 `i`,对每个字节 `b` 做异或运算 `b ^ (i+1)`(下标从 0 开始,所以加 1),这样就把对应位置的加密给去掉了,得到一个新的字节列表,
再把它们拼成一个字节串 bytes([…]),最后 .decode()成普通字符串并打印出来,
这个打印结果就是解密后的 palu{48b635a7a2474ef743e333478b67a2f5}
脚本:
encrypted = b"qcoq~Vh{e~bccocH^@Lgt{gt|g"
flag = bytes([b ^ (i+1) for i, b in enumerate(encrypted)]).decode()
print(flag)
Crypto-RSA_Quartic_Quandary
palu{This_is_a_fake_flag_change_it_for_real_use}
Crypto-易如反掌
连分数攻击
取 E0,N0→ 计算 E0/(N02) 的连分数展开 → 检查其各项逼近分数 k/d→ 找到分母 d 是800位素数 → MD5(d) 得到flag。
import gmpy2
import hashlib
N_list = [
23796646026878116589547283793150995927866567938335548416869023482791889761195291718895745055959853934513618760888513821480917766191633897946306199721200583177442944168533218236080466338723721813833112934172813408785753690869328477108925253250272864647989241887047368829689684698870160049332949549671046125158024445929082758264311584669347802324514633164611600348485747482925940752960745308927584754759033237553398957651216385369140164712159020014009858771182426893515016507774993840721603911101735647966838456333878426803669855790758035721418868768618171692143354466457771363078719423863861881209003100274869680348729,
19552522218179875003847447592795537408210008360038264050591506858077823059915495579150792312404199675077331435544143983146080988327453540449160493126531689234464110427289951139790715136775261122038034076109559997394039408007831367922647325571759843192843854522333120187643778356206039403073606561618190519937691323868253954852564110558105862497499849080112804340364976236598384571278659796189204447521325485338769935361453819608921520780103184296098278610439625935404967972315908808657494638735904210709873823527111315139018387713381604550946445856087746716671838144925662314348628830687634437271225081272705532826343,
20588310030910623387356293638800302031856407530120841616298227518984893505166480372963166394317326422544430837759332223527939420321960057410073228508230111170414845403161052128790464277007579491219950440477721075788978767309211469555824310913593208232853272958011299985202799390532181335087622499894389777412111445377637396650710486263652440053717323053536700098339137819966260269752816515681602936416736576044630343136577023173210517247609888936337876211461528203642347119434700140264859102502126842250671976238033270367185358966766106988830596616311824691409766437473419074865115209866730272194297815209976737570183,
18468380817178794606027384089796802449939260582378979728469492439450780893746976934315768186829245395964644992296264093276556001477514083927556578752836255491334765496791841945178275793885002188397918857222419803612711637177559554489679414049308077300718317502586411333302434329130562745942681716547306138457088216901181646333860559988117376012816579422902808478175975263110581667936249474308868051767856694498210084853797453949193117835061402537058150493808371384063278793041752943930928932275052745657700368980150842377283198946138726219378646040515809994704174471793592322237777371900834531014326150160506449286179
]
E_list = [
229904181453273080302209653709086531153804577507365859149808244958841045687064628362978517491609413507875726243121473678430010600891588643092042173698830147997497783886459583186019270582236955524620567373560535686287255124958954671737097645556109314142383275516997850786599322033792080045303427363366927030304214333894247469120513426641296678531965795930756543043851154646310114366477311633838078242963665452936523438928643273392454483600446242320078010627755587492056369779661382734170244060951095344418599686788550312205964136120979823565225768814898285224838691541122088693411388097496320157113230752327025862802020421665288007529320920942060329299409362236414929126050037144149017275031336018100081931062647888329912802477032857776085190828105602067426203163344931483638271679183910241511044338001446584634203146294743522375846913845041274967653508735863706778364499099286484552570083394223973734909997825522191349543295855925973354640349809770822075226834555111927586299176453943116511915434890643239957459427390624136283086434711471863737451011157026905191204496081860277138227247744470804087252965368757930797560277881668806206419629425126031049566579233056222579590529869798537893505779097868221221068867624660759084762471141,
374749619911728044650812367560174497001343067563440477135516664935394734686391543012901514676044211541958613458868769659861216149364768233000844624035620893309356372294598009760824255187442531508754966566917198975934706398309982525100772311586501118200858124845012643495006029930202324305874402291277845166060497038915773767003006049720519011634861166208163030159519901867416488082395270295488885724507937683469910251316231210838654273986152493722244271430422693265608430755620420680629979226285393465423870727975987787149515374769359243334743541460110042872587610309611770320600248289328406805995688596910226273861759369388105641549933915686192055533242723330981192183310876306968103333706140401422550917946410378174896274789619184565321544130428008804628699594759946577979319393247067750024729672029363433673084437510430506410293512293930056667971242862448029841846596288648691077795207341975907335202945548990662460491169957175452745622341245617265849042542964819126377775749222973138584978725470886059043251544634105653274564085280013340679259157119014619894553239015777411757887293044706448625760604242512494466386343040583010961386979963779928616733980046763291988848903515836247301007113187121999960487508948748354549628160741,
111738429639840672983162926852338651562094139707285850255632987705635459657893186493838711733560515475806567653354737245246745810892238414756414117557971683747269900627524702653772058841085258035513296218047505149691384287812041721130367506731427022265277885965948486359682023555050085264531256406043361391744086539522028829421284667293339869140564699750714145488199268791908205712660933607330454849730499840287271163350865799682565216636393526339218836244889719975150503253630419647851422620890082315396457329065508602521784001607236788620811397449483104884860551374031790663030220424841642241965983726516537123807061999084476076850833658360594525986997125319941689903869138176347916707622148840226672408554102717625456819726220575710494929111642866840516339713870850732638906870325693572445316904688582043485093120585767903009745325497085286577015692005747499504730575062998090846463157669448943725039951120963375521054164657547731579771203443617489609201617736584055562887243883898406182052632245189418568410854530995044542628531851356363297989653392057214167031332353949367816700838296651167799441279086074308299608106786918676697564002641234952760724731325383088682051108589283162705846714876543662335188222683115878319143239781,
185935167438248768027713217055147583431480103445262049361952417166499278728434926508937684304985810617277398880507451351333771783039360671467147075085417403764439214700549777320094501151755362122677245586884124615115132430034242191429064710012407308619977881929109092467325180864745257810774684549914888829203014922855369708286801194645263982661023515570231007900615244109762444081806466412714045462184361892356485713147687194230341085490571821445962465385514845915484336766973332384198790601633964078447446832581798146300515184339036127604597014458389481920870330726947546808739829589808006774479656385317205167932706748974482578749055876192429032258189528408353619365693624106394913101463023497175917598944803733849984703912670992613579847331081015979121834040110652608301633876167262248103040352053621027994984419469689886224948280910784030347396491408399653891297071583411037119697061333228629642728635603657687121010776933023901744994067564045429384172315640135483480089769992730928266885675143187679290648773060781987273082229827156531141515679114580622348238382074084270808291251400949744720804368426414308355267344210055608246286737478682527960260877955900464059404976906697164610891962198768354924180929300959036213841843941
]
def get_convergents_pq(x, y):
P_m2, P_m1 = 0, 1
Q_m2, Q_m1 = 1, 0
temp_x, temp_y = x, y
while temp_y != 0:
a = temp_x // temp_y
P = a * P_m1 + P_m2
Q = a * Q_m1 + Q_m2
yield (P, Q)
P_m2, P_m1 = P_m1, P
Q_m2, Q_m1 = Q_m1, Q
temp_x, temp_y = temp_y, temp_x % temp_y
N0 = N_list[0]
E0 = E_list[0]
M0 = N0 * N0
found_d = None
print(f"Calculating convergents for E0 / (N0^2)")
for k_candidate, d_candidate in get_convergents_pq(E0, M0):
if d_candidate == 0:
continue
if d_candidate.bit_length() > 800:
break
if d_candidate.bit_length() == 800:
if gmpy2.is_prime(d_candidate):
print(f"Prime candidate d found: {d_candidate}")
found_d = d_candidate
break
if found_d:
print(f"\nSuccessfully found d: {found_d}")
d_str = str(found_d)
md5_hash = hashlib.md5(d_str.encode('utf-8')).hexdigest()
flag = "palu{" + md5_hash + "}"
print(f"The flag is: {flag}")
else:
print("\nCould not find d matching the criteria.")
RE-PaluFlat
拖IDA分析,查看main函数整体流程
跟进550函数看处理过程,编写脚本逆推
已知目标输出字节 (v5[i]) -> 按位取反 -> 结果加上85 -> 结果左旋4位 -> 结果XOR对应位置密钥 -> 得到原始输入字节。
循环对v5数组的19个字节执行此逆向操作,解题脚本如下
import ctypes
target_bytes = [
84, -124, 84, 68, -92, -78, -124, 84, 98, 50, -113, 84, 98, -78, 84, 3, 20, 0x80, 67
]
target_unsigned_bytes = [b & 0xFF for b in target_bytes]
key_flat = b"flat"
key_palu = b"palu"
decrypted_input = []
def rotate_left_4(byte):
byte &= 0xFF
return ((byte << 4) | (byte >> 4)) & 0xFF
def reverse_byte_ops(target_byte_unsigned, key_byte_unsigned):
intermediate3 = (~target_byte_unsigned) & 0xFF
intermediate2 = (intermediate3 + 85) & 0xFF
intermediate1 = rotate_left_4(intermediate2)
original_byte = (intermediate1 ^ key_byte_unsigned) & 0xFF
return original_byte
for i in range(len(target_unsigned_bytes)):
target_byte = target_unsigned_bytes[i]
current_key = key_flat if (i & 1) != 0 else key_palu
key_byte = current_key[i % 4]
original_byte = reverse_byte_ops(target_byte, key_byte)
decrypted_input.append(chr(original_byte))
flag = "".join(decrypted_input)
print("Calculated input (flag):")
print(flag)
MISC-帕鲁迷宫
运行game把迷宫全貌跑出来,拖到本地,ai辅助写脚本根据提示跑出最优路径,路径太多了。。。一个一个试
import numpy as np
from queue import Queue
import hashlib
from itertools import product
# 迷宫数据
maze_raw = """
############################## #
#Y# # X
# # ############# ### # ### ## #
# # # # # # # ##
# ##### ####### ### # ### ### ##
# # # # # # # # ##
### # ### ##### # ####### # ####
# # # # # # # # ##
# ##### # ### # # ### # ##### ##
# # # # # # ##
# ######### # # ############# ##
# # # # # # ##
### # # ######### # ######### ##
# # # # # # # ##
# # # ############# # ##### # ##
# # # # # # ##
X ## ### ### # ##### # ########
# # # # # # # # ##
### ### ######### # # # # # # ##
# # # # # # # # # # # # ##
# # ### # # # # # # # ### # # ##
# # # # # # # # # # ##
# ### ### ########### # # # # ##
# # # # # # # # ##
# ##### # ######### # # ### # ##
# # # # # # # # ##
### # ####### ####### # # ### ##
# # # # # # # # ##
# ##### # # # # ##### ##### # ##
# # # # #
#X ############ X ########### X#
################################
"""
# 处理迷宫数据
maze_lines = maze_raw.strip().split('\n')
maze_width = max(len(line) for line in maze_lines)
maze = np.array([list(line.ljust(maze_width, '#')) for line in maze_lines])
# 寻找起点(Y)和所有终点(X)
def find_special_points(maze):
start_point = None
exit_points = []
for i in range(maze.shape[0]):
for j in range(maze.shape[1]):
if maze[i, j] == 'Y':
start_point = (i, j)
elif maze[i, j] == 'X':
exit_points.append((i, j))
return start_point, exit_points
# BFS查找两点间的所有最短路径
def find_all_shortest_paths(maze, start, end):
rows, cols = maze.shape
directions = [
(-1, 0, 'w'), # 上
(1, 0, 's'), # 下
(0, -1, 'a'), # 左
(0, 1, 'd') # 右
]
# 初始化
distances = {}
distances[start] = 0
parents = {}
parents[start] = []
queue = Queue()
queue.put((start, 0, "")) # 位置, 距离, 路径
# BFS寻找最短路径
while not queue.empty():
(x, y), dist, path = queue.get()
# 如果找到终点,不需要继续处理
if (x, y) == end and path != "":
continue
# 探索四个方向
for dx, dy, move in directions:
nx, ny = x + dx, y + dy
new_pos = (nx, ny)
if (0 <= nx < rows and 0 <= ny < cols and
maze[nx, ny] != '#'):
new_dist = dist + 1
# 如果是第一次访问或找到相同距离的路径
if new_pos not in distances or new_dist == distances[new_pos]:
if new_pos not in distances:
distances[new_pos] = new_dist
parents[new_pos] = []
queue.put((new_pos, new_dist, path + move))
# 添加父节点和移动方向
parents[new_pos].append(((x, y), move))
# 回溯所有可能的最短路径
if end not in distances:
return [], float('inf')
def backtrack(node):
if node == start:
return [""]
paths = []
for parent, move in parents[node]:
for p in backtrack(parent):
paths.append(p + move)
return paths
all_paths = backtrack(end)
return all_paths, distances[end]
# 使用动态规划解决TSP问题
def solve_tsp(start, exits, maze):
n = len(exits) + 1
points = [start] + exits
# 计算所有点之间的距离矩阵
dist_matrix = np.zeros((n, n), dtype=int)
paths_matrix = {}
for i in range(n):
for j in range(n):
if i != j:
paths, dist = find_all_shortest_paths(maze, points[i], points[j])
dist_matrix[i, j] = dist
paths_matrix[(i, j)] = paths
# 使用动态规划解决TSP
memo = {}
def dp(current, mask):
if mask == (1 << (n - 1)) - 1: # 已访问所有城市
return 0, [[]]
if (current, mask) in memo:
return memo[(current, mask)]
min_dist = float('inf')
best_paths = []
for next_city in range(1, n):
if mask & (1 << (next_city - 1)): # 如果已经访问过
continue
next_mask = mask | (1 << (next_city - 1))
sub_dist, sub_paths = dp(next_city, next_mask)
candidate_dist = dist_matrix[current, next_city] + sub_dist
if candidate_dist < min_dist:
min_dist = candidate_dist
best_paths = []
for path in sub_paths:
best_paths.append([next_city] + path)
elif candidate_dist == min_dist:
for path in sub_paths:
best_paths.append([next_city] + path)
memo[(current, mask)] = (min_dist, best_paths)
return min_dist, best_paths
total_dist, optimal_routes = dp(0, 0)
# 为每条路径生成所有可能的移动序列
all_movement_sequences = []
for route in optimal_routes:
movement_sequences = [[]]
# 从起点开始,添加每一段路径的移动
current = 0 # 起点索引
for next_point in route:
paths = paths_matrix[(current, next_point)]
# 更新当前移动序列,添加这段路径的所有可能移动
new_sequences = []
for sequence in movement_sequences:
for path in paths:
new_sequences.append(sequence + [path])
movement_sequences = new_sequences
current = next_point
# 将每条路径展平为单个字符串
for sequence in movement_sequences:
full_path = ''.join(sequence)
all_movement_sequences.append(full_path)
return total_dist, all_movement_sequences
def main():
# 解析迷宫
start_point, exit_points = find_special_points(maze)
# 解决旅行商问题
total_steps, all_paths = solve_tsp(start_point, exit_points, maze)
print(f"最短总步数: {total_steps}")
# 筛选出最后四步为'ssds'的路径
valid_paths = [path for path in all_paths if path.endswith('ssds')]
for i in range(len(valid_paths)):
print(valid_paths[i])
flag = hashlib.md5(valid_paths[i].encode('utf-8')).hexdigest()
print(f"Flag: palu{{{flag}}}")
if __name__ == "__main__":
main()
RE-CatchPalu
ida加载发现大量无意义字节序列,花指令特征
把0xE9替换成0x90,nop打包去壳分析
程序获取用户输入和定义的密码进行匹配输出,
仔细发现原本输出的MessageBoxA 已被劫持,实际上会执行 sub_401360 函数,所以即便是输入正确密码也看不到flag
跟进sub_401360函数分析,401100是RC4自定义算法,密钥为forpalu
sub_401270为生成流密码的伪随机生成算法 (PRGA) 部分,实现了一个基于 RC4 变种的流密码
通过ai辅助分析写出解密脚本
def sub_401100_ksa_modified(S, key):
"""
Custom KSA based on sub_401100.
Initializes and permutes the S-box.
"""
key_len = len(key)
T = [key[i % key_len] for i in range(256)]
j_ksa = 0
# Outer loop runs 3 times
for _ in range(3):
# Inner loop
for k in range(256):
# Note: (unsigned __int8) cast is important for calculations
# j = (T[k] + j + S[k]) % 233
j_ksa = (T[k] + j_ksa + S[k]) % 233 # *** Modulo 233 is the key difference ***
# Swap S[k] and S[j]
S[k], S[j_ksa] = S[j_ksa], S[k]
def sub_401270_prga_decrypt(S, encrypted_data):
"""
RC4 PRGA based on sub_401270.
Generates keystream and decrypts data.
"""
i = 0
j = 0
decrypted_data = []
for byte in encrypted_data:
i = (i + 1) % 256
j = (j + S[i]) % 256
# Swap S[i] and S[j]
S[i], S[j] = S[j], S[i]
# Keystream byte generation: K = S[(S[i] + S[j]) % 256]
keystream_byte = S[(S[i] + S[j]) % 256]
# Decrypt: PlaintextByte = EncryptedByte XOR KeystreamByte
decrypted_byte = byte ^ keystream_byte
decrypted_data.append(decrypted_byte)
return bytes(decrypted_data) # Return as bytes
# --- Main decryption process ---
# The key (bytes are important for XOR operations)
key = b"forpalu"
# The encrypted data from v7 (convert signed values to unsigned bytes)
encrypted_bytes_signed = [
13, -80, -65, 10, -115, 47, 2, 56, 111, 25, -82, -103, 25, -57, 110, -9, 79, -53, -112, 78, 85, -114, -47, 16, -64
]
encrypted_data = bytes([(b + 256) % 256 for b in encrypted_bytes_signed]) # Convert signed to unsigned bytes
# Initialize S-box
S = list(range(256))
# Perform the modified KSA
sub_401100_ksa_modified(S, key)
# Perform the PRGA/Decryption
decrypted_result = sub_401270_prga_decrypt(S, encrypted_data)
# Print the decrypted result (try decoding as ASCII)
print("Decrypted Data:", decrypted_result)
try:
print("Decrypted String:", decrypted_result.decode('ascii'))
except UnicodeDecodeError:
print("Could not decode as ASCII.")
MISC-dorodoro
四个输入框,根据提示名字为哦润吉,测试数量时发现第一个输入框四个连续的哦润吉显示正确
Doro中的字母D在字母表中排第四个,对应数字4,根据第一个判断猜测和字母位置有关,测试字母o(15个)
没问题,继续得到最终flag
MISC-TopSecret
palu{You_re_a_real_50w}