Crypto [Week1] Basic Number theory
题目描述:数论并非那么简单…
下载附件并打开:
from Crypto.Util.number import *from secret import flagdef gift (m, prime ): return pow (m, (prime + 1 ) // 2 , prime) m = bytes_to_long(flag) p = getPrime(256 ) q = getPrime(256 ) print (f'p = {p} ' )print (f'q = {q} ' )print (f'gift1 = {gift(m, p)} ' )print (f'gift2 = {gift(m, q)} ' )
可以知道,gift=m^[(p+1)/2]=m*(m|p)(mod p),也就是gift=±m (mod p)
想到用CRT将gift1和gift2分别两种组合,一共四种组合合并为4个整数,并检验是否含有flag{}
EXP:
from Crypto.Util.number import long_to_bytesp = 71380997427449345634700552609577271052193856747526826598031269184817312570231 q = 65531748297495117965939047069388412545623909154912018722160805504300279801251 gift1 = 40365143212042701723922505647865230754866250738391105510918441288000789123995 gift2 = 10698628345523517254945893573969253712072344217500232111817321788145975103342 n = p * q def crt (a1, n1, a2, n2 ): inv = pow (n1, -1 , n2) k = ((a2 - a1) * inv) % n2 x = a1 + n1 * k return x def is_printable_ascii (bs ): if len (bs) == 0 : return False return all (32 <= b < 127 for b in bs) candidates = [] for s1 in (1 , -1 ): r1 = (s1 * gift1) % p for s2 in (1 , -1 ): r2 = (s2 * gift2) % q x = crt(r1, p, r2, q) candidates.append(x) seen = set () for idx, x in enumerate (candidates): if x in seen: continue seen.add(x) bs = long_to_bytes(x) try : txt = bs.decode('utf-8' , errors='ignore' ) except : txt = repr (bs) good = False if txt.startswith("flag{" ) or txt.startswith("FLAG{" ) or "flag{" in txt: good = True if is_printable_ascii(bs): good = True print (f"--- candidate #{idx+1 } ---" ) print (f"int (hex): {hex (x)} " ) print (f"bytes len: {len (bs)} " ) if good: print ("decoded (likely):" , txt) else : preview = txt[:200 ] + ("..." if len (txt) > 200 else "" ) print ("decoded (preview):" , preview) print ()
[Week1] Strange Machine
题目描述:nc连接
要连靶机,先看给的附件:
from secret import msg_len, offset, plaintextfrom Crypto.Util.number import long_to_bytesfrom random import randbytesfrom base64 import b64encodefrom pwn import xorimport osflag = os.getenv('FLAG' ) def banner (): print ("你发现了一个奇怪的机器,它对你的消息进行了加密。" ) print ("你截获了这个机器第一次的密文,同时可以继续使用该机器进行加密。" ) print ("注意:所有密文输出都经过 base64 编码,方便你复制分析。\n" ) def menu (): print (f"1. 加密消息" ) print (f"2. 校验明文是否正确" ) print (f"3. 退出" ) class Key : def __init__ (self ): self .seed = randbytes(msg_len) def generate (self ): self .seed = self .seed[offset:] + self .seed[:offset] return self .seed def pad (msg ): pad_len = msg_len - len (msg) return msg + pad_len * long_to_bytes(pad_len) def encrypt (msg, key ): cipher = xor(pad(msg), key) return b64encode(cipher) def main (): banner() key = Key() cur_key = key.generate() cipher1 = encrypt(plaintext, cur_key) print (f'[*] 首次密文(base64):{cipher1} \n' ) while True : try : menu() choice = int (input (f"[?] 请输入你的选择:" )) if choice == 1 : msg = input (f"[?] 请输入要加密的消息(长度小于等于{msg_len} ): " ).encode() if len (msg) > msg_len: print (f"[!] 输入消息过长,最长为 {msg_len} 字节\n" ) continue cur_key = key.generate() cipher = encrypt(msg, cur_key) print (f"[*] 你的消息已加密(密文): {cipher} \n" ) continue if choice == 2 : msg = input (f"[?] 请输入待校验的明文: " ).encode() if msg == plaintext: print (f"[*] 这是你的flag: {flag} \n" ) break print ("[!] 校验错误!\n" ) continue if choice == 3 : print ("再见~\n" ) break print ("[!] 无效输入\n" ) except Exception: print ("[!] 出现错误!\n" ) break if __name__ == "__main__" : main()
可以知道,只要任意调用菜单1的一次加密,且所提交的明文字符串长度恰好是msg_len,则返回的密文就是完整的此次密钥流。之后,则按照旋转量反推出seed,再算第一次被用来生成cipher1的密钥,经过XOR得到明文。
接下来,nc连接靶机,得到第一次的base64密文:
选择选项1,输入16位字符串(我这里输入16位A),得到第二次密文:
EXP:
import base64C1_b64 = "6Asv1s203wj+QtckNNfQXw==" C_oracle_b64 = "HOKwH+YiQu7jgL4v1m3yRQ==" msg_len = 16 def bxor (a,b ): return bytes (x^y for x,y in zip (a,b)) def rot_left (bs,r ): n=len (bs); r%=n return bs[r:]+bs[:r] def rot_right (bs,r ): n=len (bs); r%=n return bs[-r:]+bs[:-r] if r!=0 else bs def unpad_pk_like (data ): if len (data)==0 : return None last = data[-1 ] if last==0 or last>len (data): return None if data[-last:]==bytes ([last])*last: return data[:-last] return None c1 = base64.b64decode(C1_b64) cor = base64.b64decode(C_oracle_b64) P0 = b"A" *msg_len key_oracle = bxor(cor, P0) for offset in range (msg_len): r = (2 *offset) % msg_len seed = rot_right(key_oracle, r) key_initial = rot_left(seed, offset) padded = bxor(c1, key_initial) plain = unpad_pk_like(padded) if plain is not None : print ("found offset =" , offset) print ("plaintext =" , plain) break
选择选择2,输入明文进行校验,即可得到flag:flag{477fa58d-588e-448e-83b5-a737a1f652ed}
[Week1] beyondHex
题目描述:质疑,理解,成为!
下载附件,得到:
这是什么东西? 807G6F429C7FA2200F46525G1350AB20G339D2GB7D8
因为不太晓得这个编码,丢到GPT里,知道是用17进制表示的整数字符串,要将其当成一个以17为基数的大整数,再转成字节,即可得到flag。
要将每个字符按0..9,A..F,G -->0..16解析为base17,然后写为大端字节
EXP:
s = "807G6F429C7FA2200F46525G1350AB20G339D2GB7D8" digits = {c: i for i, c in enumerate ("0123456789ABCDEFG" )} val = 0 for ch in s: val = val * 17 + digits[ch.upper()] b = val.to_bytes((val.bit_length() + 7 ) // 8 , 'big' ) print (b.decode())
[Week1] two Es
题目描述:two Es, too Ez!
打开附件:
from Crypto.Util.number import *import randomfrom secret import flagp, q = getPrime(512 ), getPrime(512 ) n = p * q e1 = random.getrandbits(32 ) e2 = random.getrandbits(32 ) m = bytes_to_long(flag) c1 = pow (m, e1, n) c2 = pow (m, e2, n) print (f'{n = } ' )print (f'{e1 = } ' )print (f'{e2 = } ' )print (f'{c1 = } ' )print (f'{c2 = } ' )''' n = 118951231851047571559217335117170383889369241506334435506974203511684612137655707364175506626353185266191175920454931743776877868558249224244622243762576178613428854425451444084313631798543697941971483572795632393388563520060136915983419489153783614798844426447471675798105689571205618922034550157013396634443 e1 = 2819786085 e2 = 4203935931 c1 = 104852820628577684483432698430994392212341947538062367608937715761740532036933756841425619664673877530891898779701009843985308556306656168566466318961463247186202599188026358282735716902987474154862267239716349298652942506512193240265260314062483869461033708176350145497191865168924825426478400584516421567974 c2 = 43118977673121220602933248973628727040318421596869003196014836853751584691920445952955467668612608693138227541764934104815818143729167823177291260165694321278079072309885687887255739841571920269405948846600660240154954071184064262133096801059918060973055211029726526524241753473771587909852399763354060832968 '''
得到,有e1和e2,以及对应的c1和c2。
为同一明文m用相同模n、两不同指数e1,e2,得c1=m^{e1} mod n和c2=m^{e2} mod n。
若g = gcd(e1,e2) == 1,可用扩欧算法出s,t,使s*e1 + t*e2 = 1,得m=c1^s * c2^t (mod n)
若g = gcd(e1,e2) > 1,先得到m^g (mod n),并尝试直接对其求整数g次根。
EXP:
from Crypto.Util.number import long_to_bytes, inversefrom math import gcdn = 118951231851047571559217335117170383889369241506334435506974203511684612137655707364175506626353185266191175920454931743776877868558249224244622243762576178613428854425451444084313631798543697941971483572795632393388563520060136915983419489153783614798844426447471675798105689571205618922034550157013396634443 e1 = 2819786085 e2 = 4203935931 c1 = 104852820628577684483432698430994392212341947538062367608937715761740532036933756841425619664673877530891898779701009843985308556306656168566466318961463247186202599188026358282735716902987474154862267239716349298652942506512193240265260314062483869461033708176350145497191865168924825426478400584516421567974 c2 = 43118977673121220602933248973628727040318421596869003196014836853751584691920445952955467668612608693138227541764934104815818143729167823177291260165694321278079072309885687887255739841571920269405948846600660240154954071184064262133096801059918060973055211029726526524241753473771587909852399763354060832968 def xgcd (a, b ): x0, x1, y0, y1 = 1 , 0 , 0 , 1 while b: q, a, b = a // b, b, a % b x0, x1 = x1, x0 - q * x1 y0, y1 = y1, y0 - q * y1 return a, x0, y0 def mod_pow_with_neg (base, exp, mod ): if exp >= 0 : return pow (base, exp, mod) inv = inverse(base, mod) if inv is None : raise ValueError("base has no inverse mod n (gcd != 1)" ) return pow (inv, -exp, mod) def integer_nth_root (a, n ): if a < 0 : return None , False if a == 0 : return 0 , True low = 1 high = 1 << ((a.bit_length() + n - 1 ) // n + 1 ) while low + 1 < high: mid = (low + high) // 2 p = mid ** n if p == a: return mid, True if p < a: low = mid else : high = mid return low, low**n == a def try_recover (n, e1, e2, c1, c2, try_k_limit=2000 ): g = gcd(e1, e2) if g == 1 : g0, s, t = xgcd(e1, e2) assert g0 == 1 part1 = mod_pow_with_neg(c1, s, n) part2 = mod_pow_with_neg(c2, t, n) m = (part1 * part2) % n return m, 1 else : e1p = e1 // g e2p = e2 // g g0, s, t = xgcd(e1p, e2p) assert g0 == 1 part1 = mod_pow_with_neg(c1, s, n) part2 = mod_pow_with_neg(c2, t, n) mg_mod = (part1 * part2) % n root, exact = integer_nth_root(mg_mod, g) if exact: return root, g for k in range (1 , try_k_limit + 1 ): val = mg_mod + k * n root, exact = integer_nth_root(val, g) if exact: return root, g return None , g if __name__ == "__main__" : recovered, g = try_recover(n, e1, e2, c1, c2, try_k_limit=10000 ) if recovered is None : print ("无法直接恢复明文。可能需要更大范围枚举 k(尝试 mg + k*n),或 p/q 因子信息。" ) print ("gcd(e1,e2) =" , gcd(e1, e2)) else : print ("Recovered g =" , g) try : txt = long_to_bytes(recovered).decode() except Exception: txt = repr (long_to_bytes(recovered)) print ("Recovered message (decoded if possible):" ) print (txt)
MISC [Week1] 《关于我穿越到CTF的异世界这档事:序》
题目描述:metavi正在深夜刷题,屏幕突然弹出计算器窗口,紧接着浮现一句话: 【”即使引导早已破碎,也请您当上MISC之王。”】 一阵强光过后,他已然站在一片不可思议的大陆上,NPC告诉他,这里是“异世界·Misc大陆”,而他是被召唤来的挑战者。他被选中代表“Misc分域”挑战其他技术领域,否则 Misc 将被系统删除。挑战形式则是一道道Misc题的具象化场景。为了Misc也为了自己,metavi不得不接受挑战。 【第一层·base:请解出隐藏在base里的flag。】
下载附件,有两个文件:
alphabet.txt:VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXTgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDT== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXfgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDf== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXUgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDU== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXTgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDT== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXVgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDV== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXUgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDU== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXUgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDU== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXWgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDW== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXWgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDW== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXdgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDd== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXWgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDW== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXZgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDZ== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXXgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDX== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXTgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDT== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXWgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDW== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXTgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYuDT== VGhlIGtleSBoYXMgbmV2ZXIgYmVlbiBmYXIgYXdheTsgaXQgbGllcyBwZWFjZWZ1bGx5IHdpdGhpbiB0aGUgdGV4dCBpdHNlbGYu
和base8.txt:
Tsmssic?FT?ii?sFFi?iTimCTC?mcCmsTiTmmCCCFs?sCCiiTFTcmCmFTCscFicTTs?ciC?TFFTim?s?TTmsmCmFCmmiFCmsTFTimCCsFCmiTicTT?msFCTTTs?c??ssFCmi?mciCcT=====
把alphabet.txt里的文件放到PuzzleSolver里得到base64隐写?CTFmisc,
随后将字符串视为0..7,穷举映射为3位二进制,按8位分组得到字节,
最后是base64的ZmxhZ3tUaDNfUHIxbmMxcGwzXzBmX0Jhc2VfMXNfUzBfRXp6fQ==,
得到flag:flag{Th3_Pr1nc1pl3_0f_Base_1s_S0_Ezz}
[Week1] 俱乐部之旅(1) - 邀请函
题目描述:一天Tomoki在冲浪的时候,突然收到了一封来自“c5im俱乐部”的邮件,里面是一封俱乐部邀请函,但想要打开邀请函必须先解出附件中的flag……
下载附件,是压缩包,尝试解压缩发现要密码…
于是第一次尝试暴力破解,无效,
又丢到010Editor里,直接翻到最后,在文件尾看到hint:c5im????,于是
丢到ARCHPR里用已知明文攻击得到压缩包密码:c5im8467。
解压缩后是一个word文件steg.docx,打开发现空白,眼尖地发现一行小字:重要的内容就应该存在备注中
点击”视图”中的”属性”,得到hint:
将备注里的二进制放到cyberchief里,字节长度选择7时得到flag前一半:
尝试把.docx文件解压缩,在word里看到u_f0und_m3文件,得到十六进制字符串:2657656c63306d655f74305f7468335f6335696d5f433175627d
解得flag后一半:&Welc0me_t0_th3_c5im_C1ub}
即可得到完整flag:flag{W0rd_5t3g_is_1z&Welc0me_t0_th3_c5im_C1ub}
[Week1] 布豪有黑客(一)
题目描述:c3把心爱的flag藏在了服务器上,但粗心的他好像忘记关闭HTTP服务了,于是黑客成功偷走了c3的flag和解压密码…
下载附件,是流量分析的文件,打开,直接搜索flag,看到有flag.zip和password.txt:
首先知道了flag.zip的密码为?CTF2025,然后尝试提取flag.zip
点中zip文件对应的HTTP流量,右键Media Type,导出分组字节流,得到了flag.zip
在bandizip里打开,看到flag.txt,输入之前得到的密码,即得flag:flag{Wireshark_1s_4wes0m3}
[Week1] 文化木的侦探委托(一)
题目描述:文化木是一家仅有一人的灵异侦探所的员工,有一天,她收到了一张奇怪的照片……?
下载附件,直接丢到随波逐流里一把梭,修复宽高,得到提示:
我在红色通道的第1位,绿色通道的第0位,蓝色通道的第2位藏了一些东西 -- 你能找到吗?
于是打开StegSolve,按照提示操作,得到flag:flag{Please Find_ME}
Web [Week1] Ping??
题目描述:给你一个ping工具,然后狠狠的想办法ping我吧
打开靶机,尝试输入127.0.0.1:
试着用;来进行命令拼接来绕过,输入127.0.0.1;ls,得到
输入127.0.0.1;cat flag.txt,提示输入包含敏感内容,已被过滤!,
尝试用ca\t f\lag.txt绕过,拿到flag:flag{91aaaffa-dc90-4b3d-8476-5d0c95c06965}
[Week1] from_http
题目描述:从http入门吧~
打开靶机,简单的HTTP请求头伪造,一路操作,得到flag:
[Week1] 包含不明东西的食物?!
题目描述:将物品投入锅里即可收获神秘礼品😋
打开靶机,
没啥好看的,直接右键看源码:
<body > <div class ="card" > <h2 > 投入食材</h2 > <form action ="./include.php" method ="post" autocomplete ="off" > <div class ="file-label" style ="display: flex; align-items: center; gap: 8px;" > <input type ="text" name ="filename" id ="file-input" placeholder ="请输入食材名(如food1.webp)" required style ="flex: 1; padding: 4px 6px;" > </div > <br > <button type ="submit" > 投入</button > </form > <script > const fileInput = document .getElementById ('file-input' ); const fileNameSpan = document .getElementById ('file-name' ); fileInput.addEventListener ('input' , () => { if (fileInput.value .trim ().length > 0 ) { fileNameSpan.textContent = fileInput.value .trim (); fileNameSpan.style .color = '#000' ; }}); </script > </div > </body >
可以知道是文件包含,于是尝试输入/flag.txt,发现:
想到可能是目录穿越,于是果断输入../../../../../../../flag.txt,幸运地得到了flag:
flag{28e57be9-3a51-4120-b46f-4fa60ceda491}
Reverse [Week1] 8086ASM
题目描述:你的汇编入门了吗?
下载附件,得到.asm文件,用记事本打开得到:
.MODEL SMALL .STACK 100H .DATA WELCOME_MSG db 'Welcome to 8086ASM.', 0DH, 0AH, '$' INPUT_MSG db 'Input your flag:', '$' WRONG_MSG db 0DH, 0AH, 'Wrong.', 0DH, 0AH, '$' CORRECT_MSG db 0DH, 0AH, 'Correct.', 0DH, 0AH, '$' DATA1 DB 0BBH, 01BH, 083H, 08CH, 036H, 019H, 0CCH, 097H DB 08DH, 0E4H, 097H, 0CCH, 00CH, 048H, 0E4H, 01BH DB 00EH, 0D7H, 05BH, 065H, 01BH, 050H, 096H, 006H DB 03FH, 019H, 00CH, 04FH, 04EH, 0F9H, 01BH, 0D7H DB 0CH, 01DH, 0A0H, 0C6H DATA2 DW 01122H, 03344H, 01717H, 09090H, 0BBCCH INPUT_BUFFER db 37 dup(0) BUFFER db 37 dup(0) .CODE START: MOV AX, @DATA MOV DS, AX MOV AH, 09H MOV DX, OFFSET WELCOME_MSG INT 21H MOV DX, OFFSET INPUT_MSG INT 21H MOV AH,0AH MOV DX, OFFSET INPUT_BUFFER MOV BYTE PTR[INPUT_BUFFER], 37 INT 21H CALL ENCRYPT MOV DI, OFFSET DATA1 MOV SI, OFFSET INPUT_BUFFER + 2 MOV CX, 35 LOOP1: MOV AX, [DI] CMP AX, [SI] JNE WRONG_EXIT INC DI INC SI LOOP LOOP1 JMP CORRECT_EXIT WRONG_EXIT: MOV AH,09H LEA DX,WRONG_MSG INT 21H JMP EXIT CORRECT_EXIT: MOV AH,09H LEA DX,CORRECT_MSG INT 21H JMP EXIT EXIT: MOV AX, 4C00H INT 21H ENCRYPT PROC PUSH AX PUSH BX PUSH CX MOV SI, OFFSET INPUT_BUFFER + 2 MOV BX, OFFSET DATA2 MOV CX, 35 LOOP2: PUSH CX MOV CL, 2 MOV AL, [SI] ROR AL, CL POP CX MOV [SI], AL MOV AX, WORD PTR[SI] XOR AX, WORD PTR[BX] MOV WORD PTR[SI], AX INC SI ADD BX, 2 CMP BX, OFFSET DATA2 + 10 JNE CASE1 MOV BX, OFFSET DATA2 CASE1: LOOP LOOP2 POP CX POP BX POP AX RET ENCRYPT ENDP END START
结构很简单,就是读取最多37个字符到INPUT_BUFFER,然后调用ENCRYPT对输入进行加密,将其结果与DATA1中的固定字节比较,根据结果输出对/错。
加密过程:
加密过程(按字节循环): 对于 i = 0..34: 1.p_i 循环右移 2 位 → 得到 t_i。 2.取 (t_i, t_{i+1}) 组成 16 位字(小端:t_i 是低字节,t_{i+1} 是高字节)。 3.与 w_{i mod 5} 异或,得到 (c_i, c_{i+1})。 4.因为 t_{i+1} 在 i 时作为高字节被改过一次,在 i+1 时作为低字节又被改一次,所以每个字节(除了首尾)被加密两次(一次作为字低字节,一次作为字高字节)。 其中, 设输入字节序列为:p0,p1,p2,...,p34 DATA2字节序列:w0,w1,w2,w3,w4(每个16位) 加密后字节序列:c0,c1,c2,...,c34
解密推导:
加密顺序是 i=0..34,解密必须逆序 i=34..0,并逆操作。 加密步骤(对 i): t_i = ROR2(p_i) (t_i, t_{i+1}) ^= w_{i%5} → (c_i, c_{i+1}) 解密步骤(对 i 从 34 到 0): 先恢复 (t_i, t_{i+1}): (t_i, t_{i+1}) = (c_i, c_{i+1}) ^ w_{i%5} 然后 p_i = ROL2(t_i)(ROL2 是 ROR2 的逆,即循环左移 2 位)。 要注意:加密时,c_{i+1} 在下一步 i+1 时会被作为低字节再次修改,所以解密时必须先知道 c_{i+1} 的最终值,才能正确回溯。 但实际上,我们可以正向模拟加密,然后反向推导解密公式,但更简单的方法是直接逆向执行加密操作: 逆序 i 从 34 到 0: 当前 (c_i, c_{i+1}) 与 w_{i%5} 异或得到 (t_i, t_{i+1}) p_i = ROL2(t_i) 更新 c_i = p_i 但 c_{i+1} 在下一步 i+1-1 时会被重新计算,所以得注意顺序。
EXP:
def rol2 (x ): return ((x << 2 ) | (x >> 6 )) & 0xFF def ror2 (x ): return ((x >> 2 ) | ((x & 3 ) << 6 )) & 0xFF data1 = [ 0xBB , 0x1B , 0x83 , 0x8C , 0x36 , 0x19 , 0xCC , 0x97 , 0x8D , 0xE4 , 0x97 , 0xCC , 0x0C , 0x48 , 0xE4 , 0x1B , 0x0E , 0xD7 , 0x5B , 0x65 , 0x1B , 0x50 , 0x96 , 0x06 , 0x3F , 0x19 , 0x0C , 0x4F , 0x4E , 0xF9 , 0x1B , 0xD7 , 0x0C , 0x1D , 0xA0 , 0xC6 ] data2_words = [0x1122 , 0x3344 , 0x1717 , 0x9090 , 0xBBCC ] def decrypt (): c = data1[:] n = 35 c.append(0 ) bx_idx = [i % 5 for i in range (35 )] for i in range (34 , -1 , -1 ): w = data2_words[bx_idx[i]] word = c[i] | (c[i+1 ] << 8 ) word ^= w c[i] = word & 0xFF c[i+1 ] = (word >> 8 ) & 0xFF c[i] = rol2(c[i]) return c[:35 ] dec_bytes = decrypt() flag = '' .join(chr (b) for b in dec_bytes) print (flag)
[Week1] jvav
题目描述:觉得Main很史可以直接去看加密
下载附件,是apk文件,用jadx-gui打开,根据描述,直接定位到EncKt类,反编译得:
package com.utilis.enc;import java.util.Base64;import kotlin.Metadata;import kotlin.jvm.internal .Intrinsics;import kotlin.text.Charsets;@Metadata(d1 = {"\u0000\u0016\n\u0000\n\u0002\u0010\u0012\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0003\n\u0002\u0010\u000b\n\u0000\u001a\u000e\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0003\u001a\u000e\u0010\u0004\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0001\u001a\u000e\u0010\u0005\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0001\u001a\u000e\u0010\u0006\u001a\u00020\u00072\u0006\u0010\u0002\u001a\u00020\u0003¨\u0006\b" }, d2 = {"encoder" , "" , "input" , "" , "confuser" , "rounder" , "checker" , "" , "app_release" }, k = 2, mv = {2, 0, 0}, xi = 48) public final class EncKt { public static final byte[] encoder(String input) { Intrinsics.checkNotNullParameter(input, "input" ); byte[] bytes = input.getBytes(Charsets.UTF_8); Intrinsics.checkNotNullExpressionValue(bytes, "getBytes(...)" ); byte[] encode = Base64.getEncoder().encode(bytes); Intrinsics.checkNotNullExpressionValue(encode, "encode(...)" ); return encode; } public static final byte[] confuser(byte[] input) { Intrinsics.checkNotNullParameter(input, "input" ); int length = input.length; for (int i = 0 ; i < length; i++) { input[i] = (byte) (~((input[i] + 32 ) ^ 11 )); } return input; } public static final byte[] rounder(byte[] input) { Intrinsics.checkNotNullParameter(input, "input" ); byte[] bArr = new byte[input.length]; int length = input.length; for (int i = 0 ; i < length; i++) { bArr[i] = input[(i + 5 ) % input.length]; } return bArr; } public static final boolean checker(String input) { Intrinsics.checkNotNullParameter(input, "input" ); byte[] rounder = rounder(confuser(encoder(input))); byte[] bArr = {-89 , 96 , 102 , 118 , -89 , -122 , 103 , -103 , -125 , -95 , 114 , 117 , -116 , -102 , 114 , -115 , -125 , 108 , 110 , 118 , -91 , -83 , 101 , -115 , -116 , -114 , 124 , 114 , -123 , -87 , -87 , -114 , 121 , 108 , 124 , -114 }; if (rounder.length != 36 ) { return false ; } int length = rounder.length; for (int i = 0 ; i < length; i++) { if (rounder[i] != bArr[i]) { return false ; } } return true ; } }
主要就是三个步骤:
1.encoder:base64编码
2.confuser:混淆运算(byte) ~((input[i] + 32) ^ 11)
3.rounder:循环移位input[(i + 5) % length]
解密的话,也分三步:
a.逆向rounder:
原:bArr[i] = input[i + 5 % length]
逆向:input[i] = bArr[(i - 5 + length) % length]
b.逆向confuser:
原:result = ~((input + 32) ^ 11)
逆向:input = ((~result) ^ 11) - 32
c.base64解码
最后的EXP:
import base64def decrypt_flag (): encrypted = [ -89 , 96 , 102 , 118 , -89 , -122 , 103 , -103 , -125 , -95 , 114 , 117 , -116 , -102 , 114 , -115 , -125 , 108 , 110 , 118 , -91 , -83 , 101 , -115 , -116 , -114 , 124 , 114 , -123 , -87 , -87 , -114 , 121 , 108 , 124 , -114 ] length = len (encrypted) after_rounder = [0 ] * length for i in range (length): after_rounder[i] = encrypted[(i - 5 ) % length] after_confuser = [0 ] * length for i in range (length): result_byte = after_rounder[i] & 0xFF not_result = 0xFF - result_byte after_confuser[i] = ((not_result ^ 11 ) - 32 ) & 0xFF try : base64_bytes = bytes (after_confuser) flag_bytes = base64.b64decode(base64_bytes) flag = flag_bytes.decode('utf-8' ) return flag except Exception as e: print (f"错误: {e} " ) return None if __name__ == "__main__" : flag = decrypt_flag() print (f"解密得到的flag: {flag} " )
[Week1] ezCSharp
题目描述:用ida看不懂?那就对了,建议换个工具(你知道dnspy吗?)
下载附件,根据提示用dnspy打开,得到DecodeFlag:
private static string DecodeFlag (string encoded ){ char [] array = encoded.ToCharArray(); for (int i = 0 ; i < array.Length; i++) { char c = array[i]; char c2 = c; if (c2 != '!' ) { switch (c2) { case 'a' : array[i] = 'z' ; break ; case 'b' : case 'c' : case 'd' : case 'e' : case 'f' : case 'g' : case 'h' : case 'i' : case 'j' : case 'k' : case 'l' : case 'm' : case 'n' : case 'o' : case 'p' : case 'q' : case 'r' : case 's' : case 't' : case 'u' : case 'v' : case 'w' : case 'x' : case 'y' : case 'z' : array[i] -= '\u0001' ; break ; } } else { array[i] = '_' ; } } return new string (array); }
和[EncodedFlag("D1ucj0u!tqjwf!fohjoffsjoh!xj!epspqz!ju!gvo!2025")]
可以知道是除了数字、大写字母,小写字母逐位往前偏移一位(a变为z),若是!,变为_。
写出EXP:
enc_flag = "D1ucj0u!tqjwf!fohjoffsjoh!xj!epspqz!ju!gvo!2025" flag = "flag{" for i in range (len (enc_flag)): if enc_flag[i] == "!" : flag += "_" elif enc_flag[i] == "a" : flag += "z" elif "a" < enc_flag[i] <= "z" : flag += chr (ord (enc_flag[i])-1 ) else : flag += enc_flag[i] flag += "}" print (flag)
[Week1] PlzDebugMe
题目描述:超级简单呢~快去尝试debug出flag吧!
用IDA打开附件,start函数:
int sub_401697 () { int i; sub_401860(); sub_40160C("WeLcome to Reverse World!\n" ); sub_40160C("Length of the flag is 32.\n" ); sub_40160C("Please enter your flag:\n" ); sub_4015D0("%s" , byte_415060); if ( byte_415060[0 ] == 102 && byte_415061 == 108 && byte_415062 == 97 && byte_415063 == 103 && byte_415064 == 123 && byte_41507F == 125 ) { sub_401648(123456 ); for ( i = 0 ; i <= 31 ; ++i ) { byte_415060[i] = sub_40167D((unsigned __int8)byte_415060[i]); if ( byte_415060[i] != byte_410020[i] ) { sub_40160C("Pity! Wrong flag!\n" ); return 0 ; } } sub_40160C("Congratulations! You get the flag!\n" ); return 0 ; } else { sub_40160C("WRONG\n" ); return 0 ; } }
看出byte_415060为输入的input,byte_410020为原本的flag,
.data:00410020 byte_410020 db 5Bh, 50h, 0A1h, 25h, 84h, 8Eh, 61h, 0C4h, 6Bh, 0BBh .data:0041002A db 0AEh, 5, 0Bh, 0C6h, 3Dh, 42h, 5Ah, 0FBh, 0C1h, 0C9h .data:00410034 db 4Eh, 0E9h, 8Dh, 50h, 91h, 2 dup(87h), 24h, 0ADh, 0AFh .data:0041003E db 0D5h, 36h
而sub_40167D函数操作return (unsigned __int8)sub_401656() ^ a1;,
sub_401656():
unsigned int sub_401656() { dword_415080 = 1103515245 * dword_415080 + 12345; return HIWORD(dword_415080) & 0x7FFF; }
sub_401648(123456);中,dword_415080 = a1 = 123456,
得到思路:密文每一位模256后与随机数key模256后的结果进行异或。
EXP:
enc_flag = [0x5B , 0x50 , 0xA1 , 0x25 , 0x84 , 0x8E , 0x61 , 0xC4 , 0x6B , 0xBB , 0xAE , 0x05 , 0x0B , 0xC6 , 0x3D , 0x42 , 0x5A , 0xFB , 0xC1 , 0xC9 , 0x4E , 0xE9 , 0x8D , 0x50 , 0x91 , 0x87 , 0x87 , 0x24 , 0xAD , 0xAF , 0xD5 , 0x36 ] flag = "" class RandomGenerator : def __init__ (self, seed=123456 ): self .state = seed & 0xFFFFFFFF def rand (self ): self .state = (1103515245 * self .state + 12345 ) & 0xFFFFFFFF return (self .state >> 16 ) & 0x7FFF xor_key = RandomGenerator() for i in range (len (enc_flag)): enc_flag[i] %= 256 ch = enc_flag[i] ^ (xor_key.rand()%256 ) flag += chr (ch) print (flag)