天山固网-2025网络安全技能竞赛-WriteUp
MISC
Children of the stream
纯脑洞题,不想说什么了
下载附件以后是一个流量包然后可以看到有一个rar压缩包
把这个保存下来发现需要解压密码
导出全部文件可以看到有图片
看到这个流量我们追踪一下,然后可以看到有一大串的英文字符,这个就是脑洞的地方,就是要按照大小写转换成0和1然后再转换成二维码
先用这个脚本转换成0和1然后统计一下字符长度开个根号就是图片的大小
然后大概就是42我们再使用0和1转换成二维码的脚本就可以得到二维码了
二维码就不放出来了
脚本:
text =""""""
# 转换大小写为 0/1
result = []
for ch in text:
if ch.isupper():
result.append("1")
elif ch.islower():
result.append("0")
# else:
# result.append(ch) # 空格或其他符号保持不变
binary_str = "".join(result)
print(binary_str)
脚本:
from PIL import Image
MAX = 42
pic = Image.new("RGB",(MAX, MAX))
str = ""
i=0
for y in range (0,MAX):
for x in range (0,MAX):
if(str[i] == '1'):
pic.putpixel([x,y],(0, 0, 0))
else:
pic.putpixel([x,y],(255,255,255))
i = i+1
pic.show()
pic.save("flag.png")
得到这个字符串以后我们可以试一下:ssdsahjkhsdfhhkjjhksdfjhds
发现并不是压缩包的解压密码
然后我们之前不是看到 Dvorak键盘么然后我们在网上找一下这个键盘
得到的这个就是解压密码:ooeoadhtdoeuddthhdtoeuhdeo
flag:DASCTF{jhughudshhjg_qiwjains_jsmka}
Crypto
shuffle secret
这个题目描述就是随机数
然后我们可以看到这个代码
这个random.seed(0) 固定了随机数种子
这样 random.shuffle(secret) 的结果是确定性的(固定输入 → 固定输出)。
然后我们就使用它这个代码生成2000组数字,可以得到原来的素因子
得到p然后我们就可以做rsa了
生成2000组数字的代码就在原来的代码上进行修改一下就可以
import random
from Crypto.Util.number import *
import secrets
def shuffle_secret(secret):
random.seed(0)
secret = list(secret)
random.shuffle(secret)
return ''.join(secret)
def myPrime():
while True:
if isPrime(p := random.getrandbits(64)):
return p
def get_factors():
factors = [myPrime() for _ in range(2000)]
return factors
if __name__ == '__main__':
secret = 'rCr3h0s1ry_t__s4pB_teg_yipF__dnMyFF'
shuffle_secret_val = shuffle_secret(secret)
print(f"{shuffle_secret_val=}")
factors = get_factors()
print(f"{len(factors)=}")
print(f"{factors=}")
生成以后我们就用下面的脚本可以跑出p(Pollard’s p-1 分解法)
import gmpy2
import random
from Crypto.Util.number import *
n =
e =65537
c =
t = pow(2,1024)
k = 2
x =
for i in range(len(x)):
k = pow(k,x[i],n)
if k>t:
if i%15 ==0:
g = gmpy2.gcd(k-1,n)
if g!=1:
print("因子:",g)
break
然后就做rsa就可以了
flag:DASCTF{Dont_be_LazY_try_hard_to_le4rn_Crypt0gr4phy}
X0r_Mast3r
看一下题目给的附件,然后我们可以看到gift生成是异或了一下然后我们就想到了剪枝
附件里面有一个publickey文件然后我们使用tools.qsnctf.com解析一下
def pq_high_xor(p="", q=""): lp, lq = len(p), len(q) tp0 = int(p + (512-lp) * "0", 2) tq0 = int(q + (512-lq) * "0", 2) tp1 = int(p + (512-lp) * "1", 2) tq1 = int(q + (512-lq) * "1", 2) if tp0 * tq0 > n or tp1 * tq1 < n: return if lp == leak_bits: pq.append(tp0) return if xor[lp] == "1": pq_high_xor(p + "0", q + "1") pq_high_xor(p + "1", q + "0") else: pq_high_xor(p + "0", q + "0") pq_high_xor(p + "1", q + "1") def pq_low_xor(p="", q=""): lp, lq = len(p), len(q) tp = int(p, 2) if p else 0 tq = int(q, 2) if q else 0 if tp * tq % 2**lp != n % 2**lp: return if lp == leak_bits: pq.append(tp) return if xor[-lp-1] == "1": pq_low_xor("0" + p, "1" + q) pq_low_xor("1" + p, "0" + q) else: pq_low_xor("0" + p, "0" + q) pq_low_xor("1" + p, "1" + q) #for i in range(200,300): leak = leak << 215 leak_bits = 512-215 xor = bin(leak)[2:].zfill(512) pq = [] pq_high_xor() # print(pq) for p_high in pq: x = PolynomialRing(Zmod(n), 'x').gen() f = p_high + x res = f.monic().small_roots(X=2**215, beta=0.44,epsilon=1/16) if res: p = int(p_high+res[0]) print(p)
得到p和q以后就是rsa了
exp
from Crypto.Util.number import *
p=12849041580187712114287340210939396034424272221812148338878681353480052959626982507824137597050709378850940287288828758690514971324264679315212899800699649
q=12873211897459556383148381598553582828419349814854356501114717109080249055667413399225499444590500163395293435005743026518120878091777335879008061804074573
n=165408434941024994158369042449788401910294988407636867955496509360675146233043101006492521091959823423540542967459193522177826921732843078657511418718277068974990517074551984306261365213932983751617872550238835015429529631523973823071640817856886713108845020372883255650628788819532336017444722209944370924877
e=65537
c = 32514400111560285767114059428272978369671794111207540192987911899965917441745193707727404259848540711110916899114259203696404123229134392494554874352785281551973075869313034289563994886386821257391009141162559968535288461313309953574507706583653092150741608080020850440840533409996254003612114636139855553078
m=pow(c,inverse(e,(p-1)*(q-1)),n)
print(long_to_bytes(m))
REVERSE
AndroidDemo
反编译一下,在Java_com_example_androiddemo_MainActivity_check这个函数里面有关键比较
得到key
然后分析一下发现是aes加密的ecb模式
解密一下就可以了
You say i do
直接动调
要输入50长度
输入DASCTF{123456789ABCDEFHIGKLMN987654321qwertyuiopl}
这里去看eax,发现是从25的位置上的字符K开始cmp的,如果对比一致就接着往下对比
看[rsp+0C8h+var_24],0x5F也就是“_”,所以我们直接从内存拿剩下部分就行,可以拿到后半段flag:_@nd_s33k_15_interesting}
然后接着看
观察他取代码的方式,通过获取我前面flag的输入字符,加上edx(i<<8),整体再左移3+全局变量ptr,赋给rdx,进行call,通过前几个字符串DASCTF观察,rdx通常被赋0xB0 0x?? 0x30, 0xE0, 0xC3(xor),所以我们直接去遍历ptr,按上面的算法反推就可拿到前半段flag
import idaapi import idc def find_pattern_and_decode(start_addr): pattern = [0x30, 0xE0, 0xC3] seg = idaapi.getseg(start_addr) if not seg: print("无法获取段信息") return end_addr = seg.end_ea print(f"搜索范围: 0x{start_addr:X} - 0x{end_addr:X}") current_addr = start_addr found_count = 0 result_chars = [] while current_addr < end_addr - 2: if (idc.get_wide_byte(current_addr) == 0x30 and idc.get_wide_byte(current_addr + 1) == 0xE0 and idc.get_wide_byte(current_addr + 2) == 0xC3): print(f"找到模式 30 E0 C3 在地址: 0x{current_addr:X}") found_count += 1 # 向上搜索 0xB0 search_addr = current_addr found_b0 = False for i in range(12): search_addr -= 1 if search_addr < start_addr: break if idc.get_wide_byte(search_addr) == 0xB0: # 计算偏移 offset = search_addr - start_addr print(f" 偏移: 0x{offset:X} ({offset})") # 右移3位并 & 0xFF result = (offset >> 3) & 0xFF print(f" 右移3位后: 0x{result:X} ({result})") if 32 <= result <= 126: char = chr(result) print(f" 字符: '{char}'") result_chars.append(char) found_b0 = True break if not found_b0: print(f" 未找到对应的 0xB0") current_addr += 1 if result_chars: print(f"flag: {''.join(result_chars)}") else: print("未解码出任何字符") def main(): start_address = 0x000055555558DAC0 print(f"起始地址: 0x{start_address:X}") find_pattern_and_decode(start_address) if __name__ == "__main__": main()
flag: DASCTF{l1s7en_7o_Me__Hide
然后拼起来
DASCTF{l1s7en_7o_Me__Hide_@nd_s33k_15_interesting}
没有环境打的时候忘记把解题过程截图了