cdcq

梦幻小鱼干

导航

【攻防世界】SECCON-CTF-2014 - Decrypt-It-easy

附件给了个可执行文件和一个加密文件,还给了个readme告诉你加密文件就是用可执行文件加密的

可执行文件逆出来是这样的:

是的,输入一个文件,给每个字节异或一个随机数再输出来……

由于加密的是png,前8位是固定文件头,那或许可以破解伪随机数

很自然的想法是爆破,但是时间戳太大了,枚举时间比较长

不过这里使用时间戳加密,因此可以灵稽一动,想到从这场比赛举办的时间附近开始枚举

比赛在2014年举办,那么从2014/01/01的时间戳开始枚举,结果还真的枚举到了囧

(需要注意一点,Linux上的gcc和Windows上的gcc编译完的程序使用相同种子似乎会得到不同的伪随机数,附件里的可执行文件是Linux平台的,因此爆破脚本也应该跑在Linux上)

解完之后是一张图片,这个图片才是真正意义上的密码学题

加密的柿子很简单,但是我没找到什么优美的办法,只能直接配方,然后pq拆成两个柿子,然后二次剩余,然后再中国剩余定理拼回来囧

这里需要注意一点,二次剩余是有重根的,因此对于解出来的m1和m2,还要分别枚举4种情况,找出最有可能是flag的解密串输出(一般判断有没有不可见字符就行了)

代码:

  1 from random import randint
  2 from Crypto.Util.number import *
  3 from gmpy2 import *
  4 
  5 
  6 def exgcd(a, b):
  7     if b == 0:
  8         return 1, 0
  9     x, y = exgcd(b, a % b)
 10     return y, x - a // b * y
 11 
 12 
 13 def crt(a, m):
 14     M = 1
 15     for i in m:
 16         M = M * i
 17 
 18     ans = 0
 19     for i in range(len(a)):
 20         x, y = exgcd(M // m[i], m[i])
 21         ans = (ans + a[i] * M // m[i] * x) % M
 22 
 23     return (ans + M) % M
 24 
 25 
 26 def mul_i(a, b, t, p):
 27     return [(a[0] * b[0] + a[1] * b[1] * t) % p, (a[0] * b[1] + b[0] * a[1]) % p]
 28 
 29 
 30 def pow_i(a, b, c, t, p):
 31     ans = [1, 0]
 32     z = [a, b]
 33     while c > 0:
 34         if c % 2 == 1:
 35             ans = mul_i(ans, z, t, p)
 36         z = mul_i(z, z, t, p)
 37         c = c // 2
 38 
 39     return ans
 40 
 41 
 42 def legendre(a, p):
 43     return pow(a, (p - 1) // 2, p)
 44 
 45 
 46 def residue(n, p):
 47     t = randint(0, p - 1)
 48     while legendre(t ** 2 - n, p) != p - 1:
 49         t = randint(0, p - 1)
 50 
 51     a = pow_i(t, 1, (p + 1) // 2, t ** 2 - n, p)
 52     # print('residue:', a[0] * a[0] % p == n)
 53     return a[0]
 54 
 55 
 56 def get_string(m):
 57     s = ''
 58     while m > 0:
 59         s = s + chr(m % 256)
 60         m = m // 256
 61 
 62     return s[::-1]
 63 
 64 
 65 def check_visible(s):
 66     for i in s:
 67         if not 32 <= ord(i) < 128:
 68             return 0
 69 
 70     return 1
 71 
 72 
 73 if __name__ == '__main__':
 74     '''
 75     n = 0xB8AE199365
 76     b = 0xFFEEE
 77     c = 0x8D5051562B
 78     '''
 79     N = [0xB8AE199365, 0xB86E78C811, 0x7BD4071E55]
 80     B = [0xFFEEE, 0xFFFEE, 0xFEFEF]
 81     C = [0x8D5051562B, 0x5FFA0AC1A2, 0x6008DDF867]
 82     t = ''
 83     for i in range(len(N)):
 84         n = N[i]
 85         b = B[i]
 86         c = C[i]
 87         p = 1
 88         for i in range(2, n):
 89             if n % i == 0:
 90                 p = i
 91                 q = n // i
 92                 break
 93 
 94         b2 = b * invert(2, p) % p
 95         r1 = residue((c + b2 * b2) % p, p)
 96         m1 = [(r1 - b2 + p) % p, (p - r1 - b2 + p) % p]
 97         b2 = b * invert(2, q) % q
 98         r2 = residue((c + b2 * b2) % q, q)
 99         m2 = [(r2 - b2 + q) % q, (q - r2 - b2 + q) % q]
100         for temp1 in m1:
101             for temp2 in m2:
102                 m = crt([temp1, temp2], [p, q])
103                 s = get_string(m)
104                 if check_visible(s):
105                     break
106 
107         # print('crt1:', m % p == m1)
108         # print('crt2:', m % q == m2)
109 
110         # print(s[::-1])
111         t = t + s
112 
113     print(t)
View Code

 

更新:
在我看了攻防世界admin题解之后,才终于明白怎么用中国剩余定理优雅地做这道题

n很小,qp更小,直接在[0,p)和[0,q)枚举m,就能得到m = m1 (mod p)和m = m2 (mod q)

然后中国剩余定理拼一下就vans了

 

posted on 2020-12-23 18:58  cdcq  阅读(110)  评论(0编辑  收藏