安全矩阵

 找回密码
 立即注册
搜索
查看: 2579|回复: 0

Writeup | 春秋杯春季赛Crypto&Reverse类题目解析

[复制链接]

221

主题

233

帖子

792

积分

高级会员

Rank: 4

积分
792
发表于 2021-6-10 18:01:08 | 显示全部楼层 |阅读模式
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


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-29 04:28 , Processed in 0.018406 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表