Fork me on GitHub

hackme-inndy-crypto-mutilayer

Points: 150

Solves: 6

一道多重加密的crypto,很好玩。

题目源码如下

为了能在本地跑起来我做了一点改动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import base64
import binascii
import collections
import hashlib
import os
import random
import string

from Crypto.Util import number

p = number.getPrime(1024)
q = number.getPrime(1024)
n = p * q
e = number.getPrime(24)
print('RSA(n=0x%x, e=0x%x)' % (n, e))

def rsa_encrypt(x):
v = number.bytes_to_long(x)
return pow(v, e, n)

def xor(a, b):
return bytes(i ^ j for i, j in zip(a, b))

flag = open('flag', 'rb').read()

assert flag.startswith(b'FLAG{') and flag.endswith(b'}\n')
print(hashlib.sha256(flag).hexdigest())

def layer1(data):
data = data.decode('ascii')

s = string.ascii_uppercase
t = list(s)
random.shuffle(t)
t = ''.join(t)

print(collections.Counter(data))

return data.translate(str.maketrans(s, t))

def layer2(data):
return bytes([ord(b) * 17 % 251 for b in data])

def layer3(data):
output = []
key = number.bytes_to_long(os.urandom(128))

for i in data:
key = (key * 0xc8763 + 9487) % 0x10000000000000000
output.append((i ^ key) & 0xff)

return bytes(output)

def layer4(data):
iv = os.urandom(256)
output = iv

hexencoded = binascii.hexlify(data)
length_target = (len(hexencoded) + 3) // 4
padded = hexencoded.ljust(length_target * 4, b'f')

for i in range(0, len(padded), 4):
r = rsa_encrypt(padded[i:i+4])
block = binascii.unhexlify('%.512x' % r)
output += xor(output[-256:], block)

return base64.b64encode(output)

flag = layer1(flag) #tihuan
print("layer1 -> {}".format(flag))
flag = layer2(flag)
print("layer2 -> {}".format(flag))

flag = layer3(flag)
print("layer3 -> {}".format(flag))

flag = layer4(flag)
print("layer4 -> {}".format(flag))

print(flag.decode('ascii'))

layer4

这个函数的流程大致是先生成一个长度为256的随机字符串outputpadded就是layer3生成的密文。

循环的功能是将padded的每四个字符进行rsa加密得到block,再将output的后256位字符与block进行异或,并将结果接在output后面。

我们可以在encrypted文件中得到最终output的base64编码,将其解码之后每256位分成一组,可以得到41组长度为256的字符串。按逆向的思想,以第二组为例,它是循环中第一次rsa加密的密文block和初始output进行异或的结果,由于异或的特性,我们把第二组和第一组再进行异或便可以得到第一次rsa加密的密文block。以此类推,最终我们可以得到全部rsa加密的密文。

接下来就是如何解密这个rsa,从文件encrypted中我们可以得到ne,但由于n很大,无法分解,所以无法正向解出。仔细分析可以知道,每次rsa加密的明文长度为4,并且均是16进制的串,例如"ffff",范围不大,直接爆破即可。

layer3

这个函数一开始看的时候恐怖的一批,一个长度为128的字符串转为十进制数后当做key来与明文data异或。。。

但后来仔细想想,不管key多大,和i异或之后都要模上0xff,那key不就相当于是在0-256之间吗。其实质是因为异或的过程中只有key的低八位在进行运算,其高位和0异或不变,之后模0xff就相当于其高位根本没起作用。

下面这张图验证了我的想法

那么key的范围确定在0-256之间了,怎样才算正确的key呢?接着往layer2看

layer2

这个函数的加密也很有意思,源码如下

1
2
def layer2(data):
return bytes([ord(b) * 17 % 251 for b in data])

解决方法如下

n = 251

e = 17

d = invert(e,n) = 192

cipher = plaintext * e (mod n)

plaintext = cipher * d(mod n)

原理(反证法 by唐老板)

证:m = c*d (mod n)

由于c = m * e (mod n)

==> m = { (plain e) mod n } d mod n

==> m = m*e*d mod n

由于d*e ≡ 1(mod n)

故m = c*d (mod n) 得证

举个例子

那么layer3和layer2都有解决办法了,直接一起解得到layer1的密文,通过观察结果是否均为可见字符判断layer3的key是否正确。

layer1

替换加密,直接在线网站解一波

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'L1B0'
#https://quipqiup.com/

import base64
import binascii
import collections
import hashlib
import os
import random
import string
import re
from libnum import s2n,n2s
import gmpy2
from Crypto.Util import number

cipher = open('encrypted','rb').read()
n=0x80dd2dec6684d43bd8f2115c88717386b2053bdb554a12d52840380af48088b7f1f71c3d3840ef4615af318bbe261d2d2d90616c0d2dcb6414e05c706f2b6d700ed98128048a2b79f57d2c6476add369ec96fb0fed936506d9aee4da5d36aaa97f117b082924c0638923e4367f250cc6cd23918702d98c5359bbb6bad2bef741c65362ad40355fd2edb35248256413d0ee576e7a351f17b9a5a3a7eebbbb2b22f27c342ef6dcaf1396085a105cf5e8b9bbf80e002053347fd9db6e83dc63599b1e1e5a81f7f2e4e2473bc2d14d040c9c6e6f62b9027853c7550a10df49c3a786962c9e9d5b95551a95077d0bd354b88ef31c5625e21edf98f721504f73e1b867
e=0xcf98d5

flag_counter = {' ': 14, 'O': 6, 'A': 5, 'U': 4, 'I': 4, 'T': 4, 'N': 3, 'D': 3, 'E': 3, 'L': 3, 'H': 3, 'Y': 3, 'R': 3, 'G': 2, 'C': 2, 'F': 2, 'W': 2, '.': 1, '}': 1, 'B': 1, 'V': 1, 'Q': 1, 'P': 1, 'X': 1, 'M': 1, '\n': 1, '{': 1, 'K': 1, 'J': 1, 'S': 1, 'Z': 1}

flag = "FLAG{"

def xor(a, b):
return s2n(''.join([chr(ord(i) ^ ord(j)) for i, j in zip(a, b)]))

def rsa_encrypt(m):

return pow(s2n(m),e,n)

def burp_m(c):

zidian = "0123456789abcdef"

for i in zidian:
for j in zidian:
for k in zidian:
for l in zidian:
m1 = i+j+k+l
if rsa_encrypt(m1) == c:
return m1
return 'false'

def decode_layer4():

cipher_layer4 = ""
for i,j in enumerate(cipher):
if cipher[i:i+4] == "eH/V":
cipher_layer4 = cipher[i:]
break
#print cipher_layer4
cipher_layer4 = base64.b64decode(cipher_layer4)
print type(cipher_layer4),len(cipher_layer4)

cipher4 = []
for i in range(41):
c = ''
for j in range(256):
c += cipher_layer4[i*256+j]
cipher4.append(c)
#print len(cipher4),cipher4

cipher3 = ""
for i in range(1,41):
a = xor(cipher4[i-1],cipher4[i])
print "Try {} -> {}".format(i,a)
b = burp_m(a)
if b != 'false':
print b
cipher3 += b
print cipher3

def layer3_and_layer2(text,key):

cipher_3= ""
for i in text:
key = (key * 0xc8763 + 9487)&0xff
cipher_3 += chr(ord(i)^key)

cipher_2 = ""
key_layer2 = gmpy2.invert(17,251)
print "key_layer2 -> {}".format(key_layer2)
for i in cipher_3:
cipher_2 += chr( (ord(i)*192) % 251 )
return cipher_2

def decode_layer3_layer2(cipher3):

cipher1 = []
'''
num = 0
flag_str = ""
print len(flag_counter)
for i,j in flag_counter.items():
num += j
flag_str += i
flag_str = "".join((lambda x:(x.sort(),x)[1])(list(flag_str)))

print "flag_len -> {}".format(num)
'''

for i in range(256):
temp = layer3_and_layer2(cipher3,i)
cipher1.append(temp)
print cipher1

if __name__ == "__main__":

print rsa_encrypt('1234')
# decode_layer4()
print "layer4 clear!"
cipher3 = binascii.unhexlify("58cf2de2cf8e72d8c28b1925e6962d51a3630af38a84923462d397d60665995fa1313e4444890cba0e201a43fa9ee2877c115e64a4e9116362fd4c34c68fc50c6edca071d795ee295ece1d3fd46efd0d")
decode_layer3_layer2(cipher3)