2021天翼杯 密码官方wp
2021天翼杯
crypto
TryHash
审计题目代码,题目给出了一个feistel结构的加密算法,密钥长度为8字节。用随机生成的密钥对flag进行了加密。攻击者可以提供一段明文让服务器用同样的密钥进行加密。
密钥长度为8字节,直接爆破的复杂度为2^64,是不太现实的。
本题密码算法的设计漏洞其实在于其轮函数f的设计。具体来说其轮函数具有较差的差分性质。
def g(self,v1,v2,x): value = (v1+v2+x)%256 value = ((value<<3) | (value>>5)) &0xff return value def f(self,value): v1,v2 = unpack('>2B',pack('>H',value)) v2 = self.g(v1,v2,1) v1 = self.g(v1,v2,0) value = unpack('>H',pack('>2B',v1,v2)) return value[0]
具体来说,通过数学推导,我们可以发现,对于f来说,当其两个输入的差分为0x8080时,其输出差分100%是0x400。根据这一差分性质,我们可以对该加密算法进行差分分析攻击。差分分析的具体原理可以参考这个blog http://www.theamazingking.com/crypto-feal.php
我们以对最后一轮加密(即第3轮)进行攻击为例,介绍攻击的流程。
我们构造两个特殊的输入 (L0,R0)和 (L0’,R0’)其中 L0 = L0‘, R0 = R0’^0x8080,让服务器加密,得到加密结果 (L3,R3),(L3’,R3’).通过对该加密算法的推导,我们可以得到关于第3轮轮函数f的运算关系。
f(round3_key^L0) = out1 f(round3_key^L0') = out2 out1^out2 = R0^L0^R0'^L0'^0x400
其中,只有round3_key是未知的,其他参数都是已知的。round3_key的大小为2个字节,完全可以通过爆破来得到正确的解。这样我们就把对于整个key的求解,拆分到对于轮密钥的求解,爆破复杂度从 2^64降低到了 2^16
需要注意的是对于一组明密文对,可能有多个符合关系的解,我们可以同时对多组明密文对进行求解,来过滤掉错误的解。
依次类推,可以用相似的方法得到第1,2,3轮的轮密钥。有了这三轮的轮密钥后,可以通过逆运算很块的求解出第0轮的密钥,最终恢复出整个密钥。
完整解题脚本
from hashlib import sha256
import random
from pwn import *
from pwnlib.util.iters import bruteforce
from struct import pack, unpack def g(v1,v2,x): value = (v1+v2+x)%256 value = ((value<<3) | (value>>5)) &0xff return value def f(value): v1,v2 = unpack('>2B',pack('>H',value)) v2 = g(v1,v2,1) v1 = g(v1,v2,0) value = unpack('>H',pack('>2B',v1,v2)) return value[0] def decrypt_ecb(cipher,key): msg = '' for i in range(0,len(cipher),4): msg += decrypt(cipher[i:i+4],key) return msg.strip('\x00') def decrypt(msg,key): subkeys = unpack('>4H',key) left,right = unpack('>2H',msg) left = right^left for i in range(3): left,right = right,left left = left^f(subkeys[2-i]^right) right = right^subkeys[3] return pack('>2H', left, right) def encrypt_ecb(msg,key): l = len(msg) if l%4 !=0: msg = msg+'\x00'*(4-(l%4)) cipher = '' for i in range(0,len(msg),4): cipher += encrypt(msg[i:i+4],key) return cipher def encrypt(msg,key): subkeys = unpack('>4H',key) left,right = unpack('>2H',msg) right = right^subkeys[3] for i in range(3): tmp = left^f(subkeys[i]^right) left = right right = tmp left = right^left return pack('>2H', left, right) def dfa_f(): for i in range(1000): input1 = random.randint(0,0xffff) output1 = f(input1) input2 = input1^0x8080 output2 = f(input2) assert(output1^output2 == 0x400) def genpayload1(num): payload = '' for i in range(num): data1 = random.randint(0,0xffff) data2 = random.randint(0,0xffff) data2diff = data2^0x8080 payload += pack('>2H',data1,data2) payload += pack('>2H',data1,data2diff) return payload def genpayload2(num): payload = '' for i in range(num): data1 = random.randint(0,0xffff) data2 = random.randint(0,0xffff) data2diff = data2^0x400 payload += pack('>2H',data1,data2) payload += pack('>2H',data1,data2diff) return payload def testkey_round3(pairs,key): for pair in pairs: output1 = pair[0] output2 = pair[1] output1_0,output1_1 = unpack('>2H',output1) output2_0,output2_1 = unpack('>2H',output2) f_out_diff = output1_1 ^ output2_1 ^0x400 f_in1 = key^output1_0^output1_1 f_in2 = key^output2_0^output2_1 if(f(f_in1)^f(f_in2)==f_out_diff): continue else: return False return True def testkey_round2(pairs,key,r3key): for pair in pairs: output1 = pair[0] output2 = pair[1] output1_0,output1_1 = unpack('>2H',output1) output2_0,output2_1 = unpack('>2H',output2) output1_r3_1 = output1_0^output1_1 output2_r3_1 = output2_0^output2_1 f_out_diff = output1_r3_1^output2_r3_1^0x400 f_in1 = key^output1_1^f(r3key^output1_r3_1) f_in2 = key^output2_1^ f(r3key^output2_r3_1) if(f(f_in1)^f(f_in2)==f_out_diff): continue else: return False return True def attack_round1(msg,cipher,keys): ciphers = [cipher[i:i+4] for i in range(0,len(cipher),4)] msgs = [msg[i:i+4] for i in range(0,len(msg),4)] c = ciphers[0] m = msgs[0] output0,output1 = unpack('>2H',c) output0 = output0^output1 input0,input1 = unpack('>2H',m) candkeys = [] for key in keys: r2k,r3k = key output_r2_1 = output0 output_r2_0 = output1^f(r3k^output0) output_r1_1 = output_r2_0 output_r1_0 = output_r2_1^f(r2k^output_r2_0) k0 = output_r1_0^input1 for k in range(0x10000): f_in = k^output_r1_0 f_out = output_r1_1^input0 if f(f_in) == f_out: candkeys.append([k,r2k,r3k,k0]) return candkeys def attack_round2(msg,cipher,keys): ciphers = [cipher[i:i+4] for i in range(0,len(cipher),4)] cipher_pairs = [(ciphers[i],ciphers[i+1]) for i in range(0,len(ciphers),2)] candkeys = [] for r3k in keys: for key in range(0x10000): if testkey_round2(cipher_pairs,key,r3k): candkeys.append([key,r3k]) return candkeys def attack_round3(msg,cipher): ciphers = [cipher[i:i+4] for i in range(0,len(cipher),4)] cipher_pairs = [(ciphers[i],ciphers[i+1]) for i in range(0,len(ciphers),2)] candkeys = [] for key in range(0x10000): if testkey_round3(cipher_pairs,key): candkeys.append(key) return candkeys def exploit(): con = remote('127.0.0.1',10005) context.log_level = 'debug' con.recvuntil("XXXX+") d = con.recvuntil(")")[:-1] con.recvuntil(" == ") target = con.recvline().strip() ans = bruteforce(lambda x: sha256(x+d).hexdigest() == target,string.letters+string.digits,4) con.sendlineafter("Give me XXXX",ans) con.recvuntil('is:') flag = con.recvline().strip() payload = genpayload1(6)+genpayload2(6) con.sendlineafter(':',payload) cipher = con.recv(len(payload)) cipher_round3 = cipher[:48] msg_round3 = payload[:48] possible_keys = attack_round3(msg_round3,cipher_round3) print 'round3 keys maybe:', possible_keys cipher_round2 = cipher[48:96] msg_round2 = payload[48:96] possible_keys = attack_round2(msg_round2,cipher_round2,possible_keys) print 'round2 keys maybe:', possible_keys possible_keys = attack_round1(msg_round2,cipher_round2,possible_keys) print 'round1&0 keys maybe:',possible_keys for key in possible_keys: real_key = pack('>4H',*key) print 'decrypt with key ',repr(real_key) print repr(decrypt_ecb(flag,real_key)) con.close() def exploit_local(): key = os.urandom(8) print repr(key) payload = genpayload1(6)+genpayload2(6) cipher = encrypt_ecb(payload,key) cipher_round3 = cipher[:48] msg_round3 = payload[:48] possible_keys = attack_round3(msg_round3,cipher_round3) print 'round3 keys maybe:', possible_keys cipher_round2 = cipher[48:96] msg_round2 = payload[48:96] possible_keys = attack_round2(msg_round2,cipher_round2,possible_keys) print 'round2 keys maybe:', possible_keys possible_keys = attack_round1(msg_round2,cipher_round2,possible_keys) print 'round1&0 keys maybe:',possible_keys flag = 'flag{test}' flag = encrypt_ecb(flag,key) print decrypt_ecb(flag,key) for key in possible_keys: real_key = pack('>4H',*key) print 'decrypt with key ',repr(real_key) print repr(decrypt_ecb(flag,real_key)) exploit()
babypack
由于题目中方案设计的缺陷,导致在公钥中
$$
2*A[i+1]-A[i]\approx n
$$
这其中插值于pq的乘积在高位比特上大多数是相等的,因此需要通过check功能拿到部分的低位比特。但仍然无法拿到完整的n,且就算拿到完整的n也无法通过分解来解出p,q。
这是注意发现,公钥A是通过UV和中国剩余定理来生成的,因此可以通过解二元一次方程得到uv值。再用公钥与uv的差值与n的最大公因数来确定n和得到p,q。
具体详见exp
from netcat import *
from copy import deepcopy
from gmpy2 import iroot, gcd
import time start = time.time()
r = remote("47.100.138.126", 10002)
r.recv_until(b"your pubkey:\n")
s = r.recv_until(b"\n").strip().decode()
pub = eval(s)
add = int(r.recv_until(b"\n").strip().decode())
mult = int(r.recv_until(b"\n").strip().decode())
#print(add)
#print(mult)
M = max(2 * pub[2] - pub[1], 2 * pub[3] - pub[2])
Mbin = bin(M)[2:532]
n = ["0"] * M.bit_length()
for i in range(530): n[i] = Mbin[i] r.recv_until(b">")
r.sendline(b"1")
r.recv_until(b">")
tmp = int("".join(n), 2)
r.sendline(str(tmp).encode())
count = int(r.recv_until(b"\n").strip())
#print(count) for i in range(498): r.recv_until(b">") r.sendline(b"1") r.recv_until(b">") n2 = deepcopy(n) n2[530 + i] = str(int(n2[530 + i]) ^ 1) tmp = int("".join(n2), 2) r.sendline(str(tmp).encode()) count2 = int(r.recv_until(b"\n").strip()) if count2 > count: n[530 + i] = str(int(n[530 + i]) ^ 1) count = count2 #print(count) M = int("".join(n), 2)
u = (add + iroot(add**2-4*mult, 2)[0]) // 2
v = (add - iroot(add**2-4*mult, 2)[0]) // 2
count = 0
while True: tmp1 = gcd(pub[0] - u, M) tmp2 = gcd(pub[0] - v, M) if tmp1.bit_length() > 100 or tmp2.bit_length() > 100: p = max(tmp1, tmp2) break else: M += 1 count += 1 #print(count) q = M // p
if p < q: p, q = q, p r.recv_until(b">")
r.sendline(b"2")
r.recv_until(b"\n")
s = r.recv_until(b"\n").strip().decode()
ct = int(s)
c1 = ct % p
c2 = ct % q
pt = abs(c1 - c2)
r.recv_until(b">")
r.sendline(str(pt).encode())
#print(pt)
print(r.recv(1024))
r.close()
end = time.time()
#print(end - start)
Crypto_mycipher
from hashlib import sha256
import random
from pwn import *
from pwnlib.util.iters import bruteforce
from struct import pack, unpack def g(v1,v2,x): value = (v1+v2+x)%256 value = ((value<<3) | (value>>5)) &0xff return value def f(value): v1,v2 = unpack('>2B',pack('>H',value)) v2 = g(v1,v2,1) v1 = g(v1,v2,0) value = unpack('>H',pack('>2B',v1,v2)) return value[0] def decrypt_ecb(cipher,key): msg = '' for i in range(0,len(cipher),4): msg += decrypt(cipher[i:i+4],key) return msg.strip('\x00') def decrypt(msg,key): subkeys = unpack('>4H',key) left,right = unpack('>2H',msg) left = right^left for i in range(3): left,right = right,left left = left^f(subkeys[2-i]^right) right = right^subkeys[3] return pack('>2H', left, right) def encrypt_ecb(msg,key): l = len(msg) if l%4 !=0: msg = msg+'\x00'*(4-(l%4)) cipher = '' for i in range(0,len(msg),4): cipher += encrypt(msg[i:i+4],key) return cipher def encrypt(msg,key): subkeys = unpack('>4H',key) left,right = unpack('>2H',msg) right = right^subkeys[3] for i in range(3): tmp = left^f(subkeys[i]^right) left = right right = tmp left = right^left return pack('>2H', left, right) def dfa_f(): for i in range(1000): input1 = random.randint(0,0xffff) output1 = f(input1) input2 = input1^0x8080 output2 = f(input2) assert(output1^output2 == 0x400) def genpayload1(num): payload = '' for i in range(num): data1 = random.randint(0,0xffff) data2 = random.randint(0,0xffff) data2diff = data2^0x8080 payload += pack('>2H',data1,data2) payload += pack('>2H',data1,data2diff) return payload def genpayload2(num): payload = '' for i in range(num): data1 = random.randint(0,0xffff) data2 = random.randint(0,0xffff) data2diff = data2^0x400 payload += pack('>2H',data1,data2) payload += pack('>2H',data1,data2diff) return payload def testkey_round3(pairs,key): for pair in pairs: output1 = pair[0] output2 = pair[1] output1_0,output1_1 = unpack('>2H',output1) output2_0,output2_1 = unpack('>2H',output2) f_out_diff = output1_1 ^ output2_1 ^0x400 f_in1 = key^output1_0^output1_1 f_in2 = key^output2_0^output2_1 if(f(f_in1)^f(f_in2)==f_out_diff): continue else: return False return True def testkey_round2(pairs,key,r3key): for pair in pairs: output1 = pair[0] output2 = pair[1] output1_0,output1_1 = unpack('>2H',output1) output2_0,output2_1 = unpack('>2H',output2) output1_r3_1 = output1_0^output1_1 output2_r3_1 = output2_0^output2_1 f_out_diff = output1_r3_1^output2_r3_1^0x400 f_in1 = key^output1_1^f(r3key^output1_r3_1) f_in2 = key^output2_1^ f(r3key^output2_r3_1) if(f(f_in1)^f(f_in2)==f_out_diff): continue else: return False return True def attack_round1(msg,cipher,keys): ciphers = [cipher[i:i+4] for i in range(0,len(cipher),4)] msgs = [msg[i:i+4] for i in range(0,len(msg),4)] c = ciphers[0] m = msgs[0] output0,output1 = unpack('>2H',c) output0 = output0^output1 input0,input1 = unpack('>2H',m) candkeys = [] for key in keys: r2k,r3k = key output_r2_1 = output0 output_r2_0 = output1^f(r3k^output0) output_r1_1 = output_r2_0 output_r1_0 = output_r2_1^f(r2k^output_r2_0) k0 = output_r1_0^input1 for k in range(0x10000): f_in = k^output_r1_0 f_out = output_r1_1^input0 if f(f_in) == f_out: candkeys.append([k,r2k,r3k,k0]) return candkeys def attack_round2(msg,cipher,keys): ciphers = [cipher[i:i+4] for i in range(0,len(cipher),4)] cipher_pairs = [(ciphers[i],ciphers[i+1]) for i in range(0,len(ciphers),2)] candkeys = [] for r3k in keys: for key in range(0x10000): if testkey_round2(cipher_pairs,key,r3k): candkeys.append([key,r3k]) return candkeys def attack_round3(msg,cipher): ciphers = [cipher[i:i+4] for i in range(0,len(cipher),4)] cipher_pairs = [(ciphers[i],ciphers[i+1]) for i in range(0,len(ciphers),2)] candkeys = [] for key in range(0x10000): if testkey_round3(cipher_pairs,key): candkeys.append(key) return candkeys def exploit(): con = remote('127.0.0.1',10005) context.log_level = 'debug' con.recvuntil("XXXX+") d = con.recvuntil(")")[:-1] con.recvuntil(" == ") target = con.recvline().strip() ans = bruteforce(lambda x: sha256(x+d).hexdigest() == target,string.letters+string.digits,4) con.sendlineafter("Give me XXXX",ans) con.recvuntil('is:') flag = con.recvline().strip() payload = genpayload1(6)+genpayload2(6) con.sendlineafter(':',payload) cipher = con.recv(len(payload)) cipher_round3 = cipher[:48] msg_round3 = payload[:48] possible_keys = attack_round3(msg_round3,cipher_round3) print 'round3 keys maybe:', possible_keys cipher_round2 = cipher[48:96] msg_round2 = payload[48:96] possible_keys = attack_round2(msg_round2,cipher_round2,possible_keys) print 'round2 keys maybe:', possible_keys possible_keys = attack_round1(msg_round2,cipher_round2,possible_keys) print 'round1&0 keys maybe:',possible_keys for key in possible_keys: real_key = pack('>4H',*key) print 'decrypt with key ',repr(real_key) print repr(decrypt_ecb(flag,real_key)) con.close() def exploit_local(): key = os.urandom(8) print repr(key) payload = genpayload1(6)+genpayload2(6) cipher = encrypt_ecb(payload,key) cipher_round3 = cipher[:48] msg_round3 = payload[:48] possible_keys = attack_round3(msg_round3,cipher_round3) print 'round3 keys maybe:', possible_keys cipher_round2 = cipher[48:96] msg_round2 = payload[48:96] possible_keys = attack_round2(msg_round2,cipher_round2,possible_keys) print 'round2 keys maybe:', possible_keys possible_keys = attack_round1(msg_round2,cipher_round2,possible_keys) print 'round1&0 keys maybe:',possible_keys flag = 'flag{test}' flag = encrypt_ecb(flag,key) print decrypt_ecb(flag,key) for key in possible_keys: real_key = pack('>4H',*key) print 'decrypt with key ',repr(real_key) print repr(decrypt_ecb(flag,real_key)) exploit()
2021天翼杯 密码官方wp相关推荐
- 2021长安杯再做--wp
笔记中缺少了网站重构的具体内容,还会再补. 笔记中标黄的是认为比较有价值的题目,其后的一些基础知识可以进一步进行学习. 1 请计算检材一Apk的SHA256值 3fece1e93be4f422c844 ...
- 2021长城杯pwn部分wp
前言: 就, 除了vmpwn都是基础glibc的heap题 king_in_heap_1: delete函数没有把free后的指针置零, 存在uaf, 然后用unsortedbin的fd指向io结构体 ...
- 2021 第二届天翼杯ctf
2021 天翼杯ctf wp Misc 签到 Browser Pwn ezshell Web eztp jackson easy_eval Tip Misc 签到 群公告 FLAG flag{e7gR ...
- [ctf misc][wp]一些内存取证的wp(含[2021蓝帽杯北部赛区分区赛]博人的文件)
wp 1.[V&N2020 公开赛]内存取证 1.找策略 volatility.exe -f C:\Users\shen\Downloads\mem.raw imageinfo 2.看进程 v ...
- 2021羊城杯CTF wp
2021羊城杯(部分)wp Web web1 only 4 web2 EasyCurl web3 Checkin_Go web4 Cross The Side Re Pwn BabyRop Cryp ...
- 2021 长城杯ctf wp
2021 长城杯 wp Misc 签到 你这flag保熟吗 Crypto baby_rsa Reverse Just_cmp funny_js Web java_url ez_python Pwn K ...
- 湖湘杯2019两个密码题wp
湖湘杯2019两个密码题wp 还是自己太菜的原因,这次湖湘杯只做出来4道题,然后5点的时候就放弃了去跟同学出去玩了,当时感觉进前50无望(这次湖湘杯py情况也很严重啊,可惜烽火台只报不封,挺恶心的 ...
- 2021 绿城杯 wp
2021 绿城杯 wp Web ezcms ezphp Misc [warmup]⾳频隐写 Re easyre Crypto RSA1 [warmup]加密算法 Pwn null_pwn uaf Gr ...
- 2021美亚杯(个人赛)练习记录
因为当时没有参加比赛,用的奇哥给的镜像和参考答案,然后自己重新做了一遍,记录一下自己的思路(我自封大娘级记录,保姆级懂吧). 指路奇哥(奇哥带好人 (๑•̀ㅂ•́)و✧):2021第七届美亚杯中国电子 ...
最新文章
- Type Ⅰ error(false positive) Type Ⅱ error(false negative)
- 04-VTK可视化管线(1)
- wamp如何更改网站根目录DocumentRoot
- s3c2440arm裸机编程之ADC触摸屏
- Android中怎样在工具类中获取Context对象
- Dalvik虚拟机相关的可执行程序
- Java开发以及Web 和移动程序员必须了解的10个框架
- 马云湖畔大学开学致辞:企业家要比谁都相信未来
- MongoDB初试备份及恢复
- 使用Json封装scroll,已处理其兼容性问题
- [Git]4.2 标签
- redhat7扩容linux,vmware中Centos 7 linux的LVM磁盘扩容
- 51nod-1337:翻转游戏
- 有效的python属性管理:描述符的使用
- (转)献给迷茫中的量化工作者
- ubuntu server 20.04安装vnc远程桌面xfce4
- 女生29岁 ,想学前端,还来得及么?
- 合肥学院ACM集训队第一届暑假友谊赛 B	FYZ的求婚之旅 D	计算机科学家 F	智慧码	 题解...
- git 进阶 (四)变基提交
- 按摩肺经,补足肺气眠自安
热门文章
- c语言程序怎么颠倒数据,急求如何将下列C语言程序数据存储到文件中?
- python 下划线转驼峰_json字符串中key值下划线命名转换为驼峰命名
- java 递归_Java的递归、如何与流相结合
- json移除一个元素_leetcode:203.移除链表元素,听说用虚拟头节点会方便很多?
- php drive mssql,PHP 连接 MSSQL 2005/2008 以UTF8存取 并让ADODB支持的安装设置
- 成功解决VirtualBox is not installed. Please re-run the Toolbox Installer and try again.
- ML之NBLoR:利用NB(朴素贝叶斯)、LoR(逻辑斯蒂回归)算法(+TfidfVectorizer)对Rotten Tomatoes影评数据集进行文本情感分析—五分类预测
- WRF,WPS,WRF-Chem安装及编译步骤及bug总结(2)
- [LeetCode] 461. Hamming Distance
- JAVA多线程提高十四: 面试题