|
Writeup | 春秋杯春季赛Crypto&Reverse类题目解析原创 春秋GAME 春秋伽玛 昨天
最后一波春季赛的官方writeup内容已放出,本篇包含3道Crypto以及3道Reverse方向的题目详解,赶快收藏慢慢学习吧~
01
Crypto-time
出题人:crypto_lee@Venom
1.题目选取p和q的方式是利用random库产生随机数,再判断是否为素数,并且随机种子使用当前时间戳进行初始化,这里因为random库产生的随机数是伪随机数,只要得到时间种子就可以获得p和q,而且题目将时间戳和时间差作为了hint,相当于给定了时间戳的范围,并且初始化时间种子的时候限定了精度,所以只需要知道hint就可以遍历时间戳范围进而分解n1,从而得出flag。
2.hint的加密采用了另一对p和q。注意到p和q的选取很接近,直接对n2开二次方,就可以得到p和q之间的中间数,进而使用next_prime()方法求出p和q。这里使用的公钥发现与phi有公因数6,可以先将e除以gcd(e,phi),再求出d,进一步求出明文m^gcd(e,phi),然后再对求出的m'开gcd(e,phi)次方即可。
解题脚本如下:
#!/usr/bin/python3import randomfrom decimal import *import timefrom gmpy2 import *from Crypto.Util.number import *n1=449389319572014470973230701130712522617269811294117721031111991008431585050360094769229957883125144692171901233101564003762866045169460601700254891441878998743810037070537170855056466702105713343963726437141651081516150688905082129849562489646846756872586122801890652177495174154676725874072596207108180178755008058522945203093993733331873167443162263746938403044201113440457668163574342481361454421154163291201432319013385234751292922268166299917246608164679144459355173725895117315662317330324505156929077922670671914823996700321766811999551043586214703010131467697982137884162143358399786007967117340789211505834848813471803797101083738140019127129356248286854522464787917597152171952002118368747491099723673529923018951195522714900400225757160886780159504182902140132857743474716967975019342128486236748643365288547828026395873270777532526144687248477070253203339428149026199554962987646174586568177797380361174318868448467648171416588868990979461995030684403161898497550408697577695094783000808003197203603068043364687265824456069673330314452567416143754983137647607219859034406220590219587869898727679465514015732749105798134650890143215022311113143974714405713221391896497966231925409438383535066648782822255201778169275281703c1=401489532945377793610793684722119773544780301600066410745513430092738470870254235710677331662917458735216486816223654944666672512914069274195244871184555059503843032370247894353363929996813688885280857203101709839776411016868985545599944237897372546401710041402579657231598311853522596463296090177664832759103736040102780428858301594805054838853076238331373482465072766841859415172741329175814259920949065993852870119473982972519779774470087935094963692097673607052443800723940091927016071427616754775915389245004208964143538205414854133746775614714447645057516665851780628694481268861808310238416724971673673850671343955386054575121362377868533055225219936712709474708031320743579595394353267596208729974198335174409673325772679039734706841988107519076516144633301371408301344929883397145523702189363308278963683356102257750758738297143036846718834812930796483716912661191147997005333798193659914573185697664706530774799114804036170190966523509331071964600280701252628276934131586398546602582672541075820188456265586487083315680880306767899236884202195162421559689536613740573005749847000477194096162091015847386298782974979446476432285118251267512895876728805199744415821498028998351171848530921695254428997423725875714923331577073n2=15155267112260254814859334094046172735826002259080171081726998162357946701645037659523881592272031544049749021927125983252197909993392636398184049160807707719999605547760868696059871234441249045293267592302009677249269002811886149589869652213333369608947939768457152200437978105250737118847430275142343554191304134468404921824671763164876924921101985937224297479095246132228051655664880892772136476378294042631659251586654877292836243536438334288184387617801131434535466414123998495813296765847561162680781100446656391077184870802746066619879552452560641945080540683058930700833439189784657690146144976738557801495769c2=2468578221703379861458008098241051507850837382948845085288946175636556753744182763176189585173648323464054032011039944322939163396161712722541432975739789351064988098201326803586126788175259878398963744821667593495161587855894677387881240566285601934559118064797092685909593247713834262369686831071897653756217369182373679039639016628932948775518889507209432291038498366540263588850133348471811624348709494110881127292350302658720685976197632586315945770832849119141593343924518836983738868912332048462058640564551286493338707636203013048694776131295632886983835291684044170004544049944003732133038154121113518892438e1 = 65537e2 = 196611q2 = next_prime(iroot(n2,2)[0])p2 = n2//q2phi2 = (p2-1)*(q2-1)GCD = gcd(e2,phi2) e2 = e2//gcd(e2,phi2)d2 = invert(e2, phi2)m2 = iroot(pow(c2,d2,n2),GCD)[0]print(long_to_bytes(m2))a = time.mktime((2021,4,28,20,42,6,2,118,0)) #自己填#time.struct_time(tm_year=2021, tm_mon=4, tm_mday=28, tm_hour=20, tm_min=42, tm_sec=6, tm_wday=2, tm_yday=118, tm_isdst=0)p = 0s = 0r = 0for i in range(3): for j in range(1000000): random.seed(s) x= random.getrandbits(2048) s = a+i-5+j*10**-5 if n1%x == 0: p = x r = 1 break if r: breakq = n1 // pphi = (p-1)*(q-1)e = 65537d = invert(e,phi)m = pow(c1,d,n1)print(long_to_bytes(m))
02
Crypto-logon
出题人:Soreat_u@Nu1L
1. 简单审计,发现给了一个菜单,有登陆和注册两个功能,如果能够以Administrator用户登陆则可以getflag
2. 登陆协议如下图所示:
client先给server发送一个client challenge并提供登录的用户名,随后server检查用户名是否已注册,若已注册则返回一个server challenge。
client此时可以需要自己的密码(secret)和client challenge、server challenge计算出session key,并用session key对client challenge加密,将密文(client credential)发送给server。
server收到client credential后,用相同的方式根据本地数据库中存储的该用户对应的secret计算出credential,并与client credential对比,若一致,则证明client一定知道该用户的密码,从而登陆成功。
3. 问题出现在计算client credential的encrypt函数,此函数使用AES-CFB8模式进行加密,且IV默认为16个0字节。
AES-CFB8模式加密过程如下图所示:
该模式存在一个弊端:若IV全0且后面8字节的明文也都是0的话,那么密文有1/256的概率也全都是0
4. 利用AES-CFB8模式的漏洞,可以发送client challenge全为0,username为Administrator,且client credential全为0,有1/256的概率可以通过check,getflag。
5. exp如下:
from pwn import *HOST = ""PORT = 9999conn = remote(HOST, PORT)for _ in range(0x1337): conn.sendlineafter(b"> ", b"1") conn.sendlineafter(b"client challenge: ", b"0"*16) conn.sendlineafter(b"username: ", b"Administrator") conn.sendlineafter(b"client credential: ", b"0"*16) recv = conn.recvline() if recv != b"Login failed!\n": print(conn.recvline()) conn.sendlineafter(b"> ", b"3") breakconn.close()
03
Crypto-whitegiveCMA
出题人:Hermes@Nu1L
本题考点:共模攻击及相关数论知识的运用。
解题脚本如下,请使用python3.8以上版本运行。
from Crypto.Util.number import *# 数据区N = 105505147972090189190855626395970289980349478849272766172689292153677504113157945203168037148505500911004204286230594141359278451217226220464199578711317870524009399925159330085268891611702732693725184219479126235131294548787119706541901289970313500513673347826843758751748580989418278226520156936626924327059c10 = 44594830705726833484542446021626687468793315446440491300242562132383572214838383396893045170251524822879178116449143571522880843372875585376945929248227672182668860765415910480567064826316000549996363948338866696321566948086096629421501295545617054417677393365598002537621340410326525247666639842346491047367c16 = 44714055038608255661217009118149230833323795474489853825600830897116376207102845946405793287156728475664726801969608599734105778546317268118984070287308254520723161252793270372766239693849125114583526383289679577470203270954263611671265610573237844472924495914342697052049517874123620614228215081764500905625p10 = 2021q10 = -529r10 = 999p16 = 0x2021q16 = -0x529r16 = -0xfff# 先分离与p和q有关的参数,以便后续操作。cp = pow(c16,-q10,N)*pow(c10,q16,N)%Ncq = pow(c10,-p16,N)*pow(c16,p10,N)%Nw = p10*q16-q10*p16ep = q16*r10-q10*r16+weq = p10*r16-p16*r10+w# 经过预处理后得到的cp和cq分别只和p与q有关,即cp=pow(m,w*(p-1)+ep,N),cq=pow(w*(q-1)+eq,N)。# 对得到的cp、cq运用一个重要的性质,因为pow(m,p-1,p)=1,pow(m,q-1,q)=1,所以cp%p=pow(m,ep,p),cq%q=pow(m,eq,q)。# 令Kp = pow(cp,eq,N),Kq = pow(cq,ep,N),很显然,Kp%p=pow(m,ep*eq,p),Kq%q=pow(m,ep*eq,q),此时若求出K=pow(m,ep*eq,N)的值,则Kp-K、Kq-K分别是p、q的倍数。# 这个性质有两个作用,一个是可以得到方程(K-Kp)(K-Kq)=0(mod N),另一个就是得到K之后就可以用p = GCD(Kp-K,N),q = GCD(Kq-K,N)分解N。Kp = pow(cp,eq,N)Kq = pow(cq,ep,N)# 此时暂时不知道N的分解式,所以为了求得K,还需要知道关于K的另一个方程。# 这时考察c0=pow(cp*cq,ep*eq,N)=pow(m,ep*eq*(w*(p-1)+ep+w*(q-1)+eq),N)=pow(K,w*(p+q-2)+ep+eq,N)# 因为pow(K,(p-1)*(q-1),N)=1,所以c0=pow(K,w*(p+q-2+(p-1)*(q-1))+ep+eq,N)=pow(K,w*N+ep+eq-w,N)。c0 = pow(cp*cq,ep*eq,N)e0 = w*N+ep+eq-w# 这时关于K有(K-Kp)(K-Kq)=0(mod N)和c0=pow(K,e0,N)两个方程,这些量里面未知的只有K,就可以考虑运用共模攻击了。# 这时把得到的两个方程看成两个多项式,就可以把第一个方程变形成K^2=(Kp+Kq)*K-Kp*Kq(mod N),所以有# (a1*K+b1)*(a2*K+b2)# = a1*a2*K^2+(a1*b2+a2*b1)*K+(b1*b2)# = (a1*b2+a2*b1+a1*a2*(Kp+Kq))*K+(b1*b2-a1*a2*Kp*Kq)def mul(a1,b1,a2,b2): return (a1*b2+a2*b1+a1*a2*(Kp+Kq))%N,(b1*b2-a1*a2*Kp*Kq)%N# 然后根据上述等式用多项式的二分乘法把pow(K,e0,N)化成关于K的一次多项式。a0 = 1b0 = 0a = 0b = 1while(e0): if(e0%2): a,b = mul(a,b,a0,b0) a0,b0 = mul(a0,b0,a0,b0) e0 = e0//2# 化完之后就得到一个关于K的一元一次方程,求解之K = ((c0-b)*inverse(a,N))%N# 再分解Np = GCD(Kp-K,N)q = GCD(Kq-K,N)# 最后正常解密e10 = p10*p+q10*q+r10e16 = p16*p+q16*q+r16d = inverse(e16,(p-1)*(q-1))m = pow(c16,d,N)flag = long_to_bytes(m)print(flag)
04
Reverse-backd00r
出题人:Vince@Venom
拖入 IDA 发现有 go_buildid 函数,并且发现大量的 runtime 的函数,这是 Golang 的特征
直接进入 main_main 分析入口逻辑
首先在位置 0x00000000004D78E0 处看到调用了 net_Listen 函数
.text:00000000004D78CA lea rax, unk_50A182.text:00000000004D78D1 mov [rsp+88h+var_78], rax.text:00000000004D78D6 mov [rsp+88h+var_70], 0Eh.text:00000000004D78DF nop.text:00000000004D78E0 call net_Listen
unk_50A182 指向了
.rdata:000000000050A182 unk_50A182 db 31h ; 1 ; DATA XREF: main_main+4A↑o.rdata:000000000050A183 db 32h ; 2.rdata:000000000050A184 db 37h ; 7.rdata:000000000050A185 db 2Eh ; ..rdata:000000000050A186 db 30h ; 0.rdata:000000000050A187 db 2Eh ; ..rdata:000000000050A188 db 30h ; 0.rdata:000000000050A189 db 2Eh ; ..rdata:000000000050A18A db 31h ; 1.rdata:000000000050A18B db 3Ah ; :.rdata:000000000050A18C db 38h ; 8.rdata:000000000050A18D db 37h ; 7.rdata:000000000050A18E db 32h ; 2.rdata:000000000050A18F db 31h ; 1
监听了地址 127.0.0.1:8721
再往下分析,发现调用了一个新的过程
.text:00000000004D7969 mov dword ptr [rsp+88h+var_88], 10h.text:00000000004D7970 lea rax, off_512A90.text:00000000004D7977 mov [rsp+88h+var_80], rax.text:00000000004D797C mov rcx, [rsp+88h+var_38].text:00000000004D7981 mov [rsp+88h+var_78], rcx.text:00000000004D7986 mov rcx, [rsp+88h+var_40].text:00000000004D798B mov [rsp+88h+var_70], rcx.text:00000000004D7990 call runtime_newproc
从 lea rax, off_512A90 得到过程的地址
.rdata:0000000000512A90 off_512A90 dq offset main_handleConnection
监听的数据会传递给这个函数
跟进这个函数发现首先是创建了一个数组
.text:00000000004D7A80 call runtime_newobject.text:00000000004D7A85 mov rax, [rsp+80h+var_78].text:00000000004D7A8A mov [rsp+80h+var_20], rax.text:00000000004D7A8F mov rcx, 0CF6E7633149F46F5h.text:00000000004D7A99 mov [rax], rcx.text:00000000004D7A9C mov rcx, 0D33674C27C6FE28Ah.text:00000000004D7AA6 mov [rax+8], rcx.text:00000000004D7AAA mov rcx, 0F592D63BE79440D9h.text:00000000004D7AB4 mov [rax+10h], rcx.text:00000000004D7AB8 mov rcx, 0D33CF4C0B83E001Bh.text:00000000004D7AC2 mov [rax+18h], rcx.text:00000000004D7AC6 mov rcx, 8B615DC202F30A50h.text:00000000004D7AD0 mov [rax+20h], rcx.text:00000000004D7AD4 mov rcx, 181C7380D6FF6BBh.text:00000000004D7ADE mov [rax+28h], rcx.text:00000000004D7AE2 mov rcx, [rsp+80h+arg_0].text:00000000004D7AEA test [rcx], al.text:00000000004D7AEC lea rdx, [rcx+18h].text:00000000004D7AF0 mov qword ptr [rsp+80h+var_18+8], rdx.text:00000000004D7AF5 mov rdx, [rsp+80h+arg_8].text:00000000004D7AFD mov qword ptr [rsp+80h+var_18], rdx.text:00000000004D7B02 mov [rsp+80h+var_31], 1.text:00000000004D7B07 lea rbx, unk_4E73E0.text:00000000004D7B0E mov [rsp+80h+var_80], rbx.text:00000000004D7B12 mov [rsp+80h+var_78], 400h.text:00000000004D7B1B mov [rsp+80h+var_70], 400h.text:00000000004D7B24 call runtime_makeslice
还有一个数组的值是可知的
*v21 = 0xCF6E7633149F46F5LL;v21[1] = 0xD33674C27C6FE28ALL;v21[2] = 0xF592D63BE79440D9LL;v21[3] = 0xD33CF4C0B83E001BLL;v21[4] = 0x8B615DC202F30A50LL;v21[5] = 0x181C7380D6FF6BBLL;
这里将接收到的数据做了一个 base32 计算
.text:00000000004D7B69 call rdx.text:00000000004D7B6B mov rax, [rsp+80h+var_60].text:00000000004D7B70 cmp [rsp+80h+var_58], 0.text:00000000004D7B76 jnz short loc_4D7B35.text:00000000004D7B78 lea rcx, [rax-1].text:00000000004D7B7C nop dword ptr [rax+00h].text:00000000004D7B80 cmp rcx, 400h.text:00000000004D7B87 ja loc_4D7C79.text:00000000004D7B8D mov [rsp+80h+var_30], rcx.text:00000000004D7B92 mov rax, cs:encoding_base32_StdEncoding.text:00000000004D7B99 mov [rsp+80h+var_80], rax.text:00000000004D7B9D mov rax, [rsp+80h+var_28].text:00000000004D7BA2 mov [rsp+80h+var_78], rax.text:00000000004D7BA7 mov [rsp+80h+var_70], rcx.text:00000000004D7BAC mov [rsp+80h+var_68], 400h.text:00000000004D7BB5 call encoding_base32___Encoding__EncodeToString
与 unk_50CEDF 做比较
.text:00000000004D7BCE mov [rsp+80h+var_80], rax.text:00000000004D7BD2 lea rax, unk_50CEDF.text:00000000004D7BD9 mov [rsp+80h+var_78], rax.text:00000000004D7BDE mov [rsp+80h+var_70], rcx.text:00000000004D7BE3 call runtime_memequal
unk_50CEDF 可以直接提取出来
.rdata:000000000050CEDF unk_50CEDF db 4Dh ; M ; DATA XREF: main_handleConnection+192↑o.rdata:000000000050CEE0 db 34h ; 4.rdata:000000000050CEE1 db 59h ; Y.rdata:000000000050CEE2 db 44h ; D.rdata:000000000050CEE3 db 43h ; C.rdata:000000000050CEE4 db 59h ; Y.rdata:000000000050CEE5 db 4Ch ; L.rdata:000000000050CEE6 db 4Fh ; O.rdata:000000000050CEE7 db 4Dh ; M.rdata:000000000050CEE8 db 35h ; 5.rdata:000000000050CEE9 db 42h ; B.rdata:000000000050CEEA db 47h ; G.rdata:000000000050CEEB db 43h ; C.rdata:000000000050CEEC db 59h ; Y.rdata:000000000050CEED db 33h ; 3.rdata:000000000050CEEE db 4Ch ; L.rdata:000000000050CEEF db 4Dh ; M.rdata:000000000050CEF0 db 51h ; Q.rdata:000000000050CEF1 db 59h ; Y.rdata:000000000050CEF2 db 44h ; D.rdata:000000000050CEF3 db 41h ; A.rdata:000000000050CEF4 db 34h ; 4.rdata:000000000050CEF5 db 51h ; Q.rdata:000000000050CEF6 db 3Dh ; =
做 base32 解码得到g01angBackd00r
再往后跑了一个 main_Decrypt
如果只是拿 flag 到这里就结束了,跑程序,然后发送 g01angBackd00r 就可以拿到 flag
后面的函数其实是做了一个 XXTEA 解密,可以通过 0x9E3779B9 特征识别。
05
Reverse-flagInStream
出题人:Vince@Venom
这题 flag 在数据包里,用的 CDMA 码片序列封装,发送端会用哈夫曼编码做一次数据压缩,然后再封装在 CDMA 码片序列里发送给接收端,需要结合发送端和接收端逆向出这两块的逻辑
题目第一个关键点要计算出 CDMA 的 key,长度不长,爆破到 4 位就可以出来了
第二个关键点是哈夫曼树的生成,这里图片里已经给出了大部分,剩下几位爆破一下,然后写个哈夫曼解码就可以出 flag
贴代码
import heapqfrom queue import Queue,PriorityQueuehuffmanCode_dict1 = {} # 编码为 value 结点值为 keyhuffmanCode_dict2 = {} # 编码为 key 结点值为 value# 获得反码def getInverseCode(code) : result = "" for i in code : if(i=='1') : result += '0' else : result += '1' return result# 计算 key 用于生成对应的CDMA码片序列def calcKey() : coded = "0100001110111011010011010100000110111100010001001011001010111110010000110100010001001101010000010100001110111011101100100100000110111100101110111011001010111110101111000100010010110010101111100100001101000100101100100100000101000011101110110100110101000001010000111011101101001101010000010100001110111011010011010100000101000011010001000100110110111110010000110100010010110010010000011011110010111011101100100100000101000011010001000100110101000001010000111011101110110010010000011011110010111011010011010100000101000011101110111011001010111110010000110100010001001101010000011011110001000100101100100100000110111100101110110100110101000001101111001011101110110010010000010100001101000100101100101011111010111100010001000100110110111110101111000100010010110010010000011011110010111011101100100100000110111100101110110100110101000001010000111011101101001101010000010100001110111011010011011011111010111100101110111011001001000001101111000100010001001101010000011011110001000100101100100100000101000011101110111011001010111110101111001011101101001101010000010100001101000100101100100100000110111100010001001011001001000001101111000100010001001101101111100100001101000100010011011011111010111100010001001011001001000001" # key [a-zA-Z] character_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" for c1 in character_set : for c2 in character_set : for c3 in character_set : for c4 in character_set : correct = 0 c = [c1,c2,c3,c4] for i in range(0,4) : if( ( coded[i*8:i*8+8] == bin(ord(c))[2:].zfill(8) or coded[i*8:i*8+8] == getInverseCode(bin(ord(c))[2:].zfill(8)) ) ) : correct += 1 if correct == 4 : print("key : " +c1+c2+c3+c4 +"\n") return c1+c2+c3+c4# 根据 key 将CDMA 码片序列还原为哈夫曼编码def toHuffmanCode(key) : coded = "0100001110111011010011010100000110111100010001001011001010111110010000110100010001001101010000010100001110111011101100100100000110111100101110111011001010111110101111000100010010110010101111100100001101000100101100100100000101000011101110110100110101000001010000111011101101001101010000010100001110111011010011010100000101000011010001000100110110111110010000110100010010110010010000011011110010111011101100100100000101000011010001000100110101000001010000111011101110110010010000011011110010111011010011010100000101000011101110111011001010111110010000110100010001001101010000011011110001000100101100100100000110111100101110110100110101000001101111001011101110110010010000010100001101000100101100101011111010111100010001000100110110111110101111000100010010110010010000011011110010111011101100100100000110111100101110110100110101000001010000111011101101001101010000010100001110111011010011011011111010111100101110111011001001000001101111000100010001001101010000011011110001000100101100100100000101000011101110111011001010111110101111001011101101001101010000010100001101000100101100100100000110111100010001001011001001000001101111000100010001001101101111100100001101000100010011011011111010111100010001001011001001000001" t = 0 result = "" for j in range(0,int(len(coded)/8)) : if t == len(key) : t = 0 code = bin(ord(key[t]))[2:].zfill(8) if(coded[j*8:j*8+8] == code) : result += '1' else : result += '0' t+=1 print("huffmanCode : "+result+"\n") return result# 结点类class TreeNode : def __init__(self, data) : self.value = data[0] # 结点值 self.priority = data[1] # 结点权重 self.lChild = None # 左子结点 self.rChild = None # 右子结点 self.code = "" # 哈夫曼编码 def __lt__(self, other) : if self.priority < other.priority : return True else : return False# 创建树结点优先队列def createNodePriorityQueue(node) : priority_queue = PriorityQueue() for i in node : priority_queue.put(TreeNode(i)) return priority_queue# 创建哈夫曼树def createHuffmanTree(node_prioity_queue) : while node_prioity_queue.qsize() != 1 : # 只剩一个根结点时哈夫曼树创建完成 node1 = node_prioity_queue.get() # 当前最小权重的结点 node2 = node_prioity_queue.get() # 当前次小权重的结点 node3 = TreeNode([None, node1.priority+node2.priority]) node3.lChild = node1 node3.rChild = node2 node_prioity_queue.put(node3) return node_prioity_queue.get() # 返回根结点# 由哈夫曼树得到哈夫曼编码表def getHuffmanCode(head, x) : if head : getHuffmanCode(head.lChild,x+"0") head.code += x if head.value : huffmanCode_dict2[head.code] = head.value huffmanCode_dict1[head.value] = head.code getHuffmanCode(head.rChild,x+"1")# 根据哈夫曼编码表对字符串编码def huffmanEncode(string_s) : code = "" for c in string_s : code += huffmanCode_dict1[c] return code# 根据哈夫曼编码表解码def huffmanDecode(string_s) : code = "" result = "" for c in string_s : code += c if code in huffmanCode_dict2 : result += huffmanCode_dict2[code] code ="" return resultkey = calcKey()huffmanCode = toHuffmanCode(key)for x1 in range(0,10) : for x2 in range(0,10) : for x3 in range(0,10) : huffmanCode_dict1 = {} # 编码为 value 结点值为 key huffmanCode_dict2 = {} # 编码为 key 结点值为 value data = [['0',1],['1',2],['2',6],['3',2],['5',2],['6',2],['7',4],['9',2],['a',2],['b',x1],['c',x2],['d',x3],['e',1],['f',3],['g',1],['l',1],['{',1],['}',1]] priority_queue = createNodePriorityQueue(data) huffmanTree_root = createHuffmanTree(priority_queue) t_code = "" # 存放编码临时变量 getHuffmanCode(huffmanTree_root,t_code) flag_code = huffmanCode_dict1['f']+huffmanCode_dict1['l']+huffmanCode_dict1['a']+huffmanCode_dict1['g'] if(flag_code == huffmanCode[:len(flag_code)]) : print(huffmanDecode(huffmanCode)) exit()
06
Reverse-hardchal
出题人:yype@Nu1L
解题思路
1. 根据 Icarus Verilog 关键字搜索到其开源代码,定位到 VVP SIMULATION ENGINE 相关部分,可以发现文档中有对于 Icarus Verilog compiler 输出语法 Icarus Verilog(vvp) assembly 的详细注解;参考文档进行逆向,从 chal 中恢复出程序大致的 Verilog 逻辑(难点);
2. 根据恢复出的逻辑与自身逆向经验,可以判断出程序验证逻辑:使用大端序读入 flag,对其进行 TEA 加密(程序的大部分逻辑为开源 TEA 实现,可以直接结合密钥个数、block 大小等信息进行搜索与猜测),使用的加密密钥为:0xdeadbeef, 0xcafebabe, 0x10101010, 0x01010101
加密过后的密文应为:
0xbf3e3eda,0x193f659b,0x29b74d79,0x63f67c27,0x6330fb62,0xce7d5340,0xd29371e9,0xae2f0140,0x87ab6341,0x02069c5d,0xe10392fe,0xbb0e1442;
3. 编写 TEA 解密代码,求解 flag。
参赛选手:@划水运动员
解题思路
从flag文件读取flag
编译一个devel版的vvp运行chal文件,linux需要改module的路径
使用-s参数可以看出一些有用的信息
看到sum,delta…很容易想到tea算法
定位到key
提取出cmp后面的常量
#include <stdio.h> #include <stdint.h> void decrypt (uint32_t* v, uint32_t* k) { uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */ uint32_t delta=0x9e3779b9; /* a key schedule constant */ uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */ for (i=0; i<32; i++) { /* basic cycle start */ v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3); v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1); sum -= delta; } /* end cycle */ v[0]=v0; v[1]=v1; } int main() { uint32_t k[4]={0xdeadbeef ,0xcafebabe,0x10101010, 0x1010101}; uint32_t vals[100]={3208527578, 423585179, 699878777, 1677098023, 1664154466, 3464319808, 3532878313, 2922316096, 2276156225, 33987677, 3775107838, 3138262082}; for(int i=0;i<12;i+=2) { uint32_t v[2]={vals, vals[i+1]}; decrypt(v, k); printf("%c%c%c%c%c%c%c%c",*((char*)&v[0]+3),*((char*)&v[0]+2),*((char*)&v[0]+1),*((char*)&v[0]+0),*((char*)&v[1]+3),*((char*)&v[1]+2),*((char*)&v[1]+1),*((char*)&v[1]+0)); } return 0; }
END
|
|