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相关推荐

  1. 2021长安杯再做--wp

    笔记中缺少了网站重构的具体内容,还会再补. 笔记中标黄的是认为比较有价值的题目,其后的一些基础知识可以进一步进行学习. 1 请计算检材一Apk的SHA256值 3fece1e93be4f422c844 ...

  2. 2021长城杯pwn部分wp

    前言: 就, 除了vmpwn都是基础glibc的heap题 king_in_heap_1: delete函数没有把free后的指针置零, 存在uaf, 然后用unsortedbin的fd指向io结构体 ...

  3. 2021 第二届天翼杯ctf

    2021 天翼杯ctf wp Misc 签到 Browser Pwn ezshell Web eztp jackson easy_eval Tip Misc 签到 群公告 FLAG flag{e7gR ...

  4. [ctf misc][wp]一些内存取证的wp(含[2021蓝帽杯北部赛区分区赛]博人的文件)

    wp 1.[V&N2020 公开赛]内存取证 1.找策略 volatility.exe -f C:\Users\shen\Downloads\mem.raw imageinfo 2.看进程 v ...

  5. 2021羊城杯CTF wp

    2021羊城杯(部分)wp ​Web web1 only 4 web2 EasyCurl web3 Checkin_Go web4 Cross The Side Re Pwn BabyRop Cryp ...

  6. 2021 长城杯ctf wp

    2021 长城杯 wp Misc 签到 你这flag保熟吗 Crypto baby_rsa Reverse Just_cmp funny_js Web java_url ez_python Pwn K ...

  7. 湖湘杯2019两个密码题wp

    湖湘杯2019两个密码题wp   还是自己太菜的原因,这次湖湘杯只做出来4道题,然后5点的时候就放弃了去跟同学出去玩了,当时感觉进前50无望(这次湖湘杯py情况也很严重啊,可惜烽火台只报不封,挺恶心的 ...

  8. 2021 绿城杯 wp

    2021 绿城杯 wp Web ezcms ezphp Misc [warmup]⾳频隐写 Re easyre Crypto RSA1 [warmup]加密算法 Pwn null_pwn uaf Gr ...

  9. 2021美亚杯(个人赛)练习记录

    因为当时没有参加比赛,用的奇哥给的镜像和参考答案,然后自己重新做了一遍,记录一下自己的思路(我自封大娘级记录,保姆级懂吧). 指路奇哥(奇哥带好人 (๑•̀ㅂ•́)و✧):2021第七届美亚杯中国电子 ...

最新文章

  1. Type Ⅰ error(false positive) Type Ⅱ error(false negative)
  2. 04-VTK可视化管线(1)
  3. wamp如何更改网站根目录DocumentRoot
  4. s3c2440arm裸机编程之ADC触摸屏
  5. Android中怎样在工具类中获取Context对象
  6. Dalvik虚拟机相关的可执行程序
  7. Java开发以及Web 和移动程序员必须了解的10个框架
  8. 马云湖畔大学开学致辞:企业家要比谁都相信未来
  9. MongoDB初试备份及恢复
  10. 使用Json封装scroll,已处理其兼容性问题
  11. [Git]4.2 标签
  12. redhat7扩容linux,vmware中Centos 7 linux的LVM磁盘扩容
  13. 51nod-1337:翻转游戏
  14. 有效的python属性管理:描述符的使用
  15. (转)献给迷茫中的量化工作者
  16. ubuntu server 20.04安装vnc远程桌面xfce4
  17. 女生29岁 ,想学前端,还来得及么?
  18. 合肥学院ACM集训队第一届暑假友谊赛 B FYZ的求婚之旅 D 计算机科学家 F 智慧码 题解...
  19. git 进阶 (四)变基提交
  20. 按摩肺经,补足肺气眠自安

热门文章

  1. c语言程序怎么颠倒数据,急求如何将下列C语言程序数据存储到文件中?
  2. python 下划线转驼峰_json字符串中key值下划线命名转换为驼峰命名
  3. java 递归_Java的递归、如何与流相结合
  4. json移除一个元素_leetcode:203.移除链表元素,听说用虚拟头节点会方便很多?
  5. php drive mssql,PHP 连接 MSSQL 2005/2008 以UTF8存取 并让ADODB支持的安装设置
  6. 成功解决VirtualBox is not installed. Please re-run the Toolbox Installer and try again.
  7. ML之NBLoR:利用NB(朴素贝叶斯)、LoR(逻辑斯蒂回归)算法(+TfidfVectorizer)对Rotten Tomatoes影评数据集进行文本情感分析—五分类预测
  8. WRF,WPS,WRF-Chem安装及编译步骤及bug总结(2)
  9. [LeetCode] 461. Hamming Distance
  10. JAVA多线程提高十四: 面试题