easy_ya

点击此处获得更好的阅读体验


WriteUp来源

本WP由Vanish提供

题目考点

解题思路

题目代码

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
from secret import flag,key
from random import randint
from libnum import n2s,s2n
from Crypto.Util.number import getPrime
limit = lambda n: n & 0xffffffff
padding="\xe6\xe6\xe7\xe6\xe5\xe6\xe5\xe9\xe5"
ek='\xe6\x84\xbf'
def encode(key,data):
pad = randint(0x10000000,0xffffffff)
Key = [ord(i) for i in key]
Data = [ord(i) for i in data]
a = limit((Key[0] << 24) | (Key[1] << 16) | (Key[2] << 8) | Key[3])
b = limit((Key[4] << 24) | (Key[5] << 16) | (Key[6] << 8) | Key[7])
c = limit((Key[8] << 24) | (Key[9] << 16) | (Key[10] << 8) | Key[11])
d = limit((Key[12] << 24) | (Key[13] << 16) | (Key[14] << 8) | Key[15])
y = limit((Data[0] << 24) | (Data[1] << 16) | (Data[2] << 8) | Data[3])
z = limit((Data[4] << 24) | (Data[5] << 16) | (Data[6] << 8) | Data[7])
pads = 0
for j in range(32):
pads = limit(pads + pad)
y = limit( y + ((z*16 + a) ^ (z + pads) ^ ((z>>5) + b)))
z = limit( z + ((y*16 + c) ^ (y + pads) ^ ((y>>5) + d)))
print hex((y << 52) ^ (pads << 20) ^ z)
#pow_check()
#token_check()
flag=flag.ljust(len(flag)+(-len(flag)&7),'\x00')
for i in range(0,len(flag)/8):
encode(key,flag[8*i:8*i+8])
for i in range(9):
ek+=padding[i] + key[2*i:2*i+2]
p=getPrime(2048)
e=0x10001
for i in range(2):
q=getPrime(2048)
n=p*q
print n
print pow(s2n(ek),e,n)

显然,我们第一步需要去交互,拿到四个大数,其中两个是有公因子p的大数n,还有两个就是ek的密文。这一步就是凑数的part,没点营养。吐槽,交互的时候什么沙雕PoW,有必要卡这个嘛

破PoW的脚本

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
from pwn import *
import string
import hashlib
context.log_level = 'debug'
sh = remote("39.96.90.217","17497")
sh.recvuntil("x[:20] = ")
pa = sh.recv(len('f91a551dfa31e47458df'))
sh.recvuntil('openssl_sha')
fun = sh.recvuntil(">")[:-1]
sh.recvuntil("> ")
table = string.printable
def getpow(fun,mess):
for i in table:
for j in table:
for k in table:
for l in table:
password=i+j+k+l
if fun == "256":
if hashlib.sha256(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
elif fun == "384":
if hashlib.sha384(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
elif fun == "1":
if hashlib.sha1(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
elif fun == "512":
if hashlib.sha512(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
elif fun == "224":
if hashlib.sha224(password.encode()).hexdigest()[:20] == mess:
return i+j+k+l
else:
print(fun)
else:
print("no ans")
sh.sendline(getpow(fun,pa))
sh.interactive()

然乎我们输入token,拿到自己的data,解得ek

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Crypto.Util.number import *
from gmpy2 import *
n1 =
n2=
p = gcd(n1,n2)
q1 = n1//p
q2 = n2//p
assert n1 == p*q1
assert n2 == p*q2
c1 =
e = 65537
phi = (p-1)*(q2-1)
d = inverse(e,phi)
m = pow(c2,d,n2)
print(long_to_bytes(m))
>>>愿我所爱无忧恙岁长安

python2下会得到这么个ek,有点意思嗷,但你还是沙雕出题人,谁让你用PoW卡我

python3运行得到

b'\xe6\x84\xbf\xe6\x88\x91\xe6\x89\x80\xe7\x88\xb1\xe6\x97\xa0\xe5\xbf\xa7\xe6\x81\x99\xe5\xb2\x81\xe9\x95\xbf\xe5\xae\x89'

然后去掉该去的就能得到key了'\x88\x91\x89\x80\x88\xb1\x97\xa0\xbf\xa7\x81\x99\xb2\x81\x95\xbf\xae\x89'

然后就是逆这个encode了。

其中我们交互的时候拿到五组密文,易得,每组密文中,保存了encode中32轮加密后的完整的y,pads和有一部分重合。但是pads加了32次pad后,最后5位已经变成0了。所以z我们可以知道20+5 = 25位,剩下七位未知。

然后我们就去爆pad,我们从密文里头能知道pad的52-32=20位。但这20位其实是中间部分的。最高的5位已经被limit搞没掉了。然后最低的7位我们也是不知道的。所以,,,爆。我们爆了低7位的同时,也能确定一个z,然后反加密看结果咯。

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
from Crypto.Util.number import *
limit = lambda n: n & 0xffffffff
key = '\x88\x91\x89\x80\x88\xb1\x97\xa0\xbf\xa7\x81\x99\xb2\x81\x95\xbf\xae\x89'
def init(key):
Key = [ord(i) for i in key]
#Data = [ord(i) for i in data]
a = limit((Key[0] << 24) | (Key[1] << 16) | (Key[2] << 8) | Key[3])
b = limit((Key[4] << 24) | (Key[5] << 16) | (Key[6] << 8) | Key[7])
c = limit((Key[8] << 24) | (Key[9] << 16) | (Key[10] << 8) | Key[11])
d = limit((Key[12] << 24) | (Key[13] << 16) | (Key[14] << 8) | Key[15])
return (a,b,c,d)

#y = limit((Data[0] << 24) | (Data[1] << 16) | (Data[2] << 8) | Data[3])
#z = limit((Data[4] << 24) | (Data[5] << 16) | (Data[6] << 8) | Data[7])
(a,b,c,d)=init(key)
ans = 0x9d98a72f5c82c26680a65
def check(s):
for i in s:
if i not in "flag{0123456789bcde-_}\x00":
return False
return True
d = [
0xcf4add0cf5469a49d19c,
0xcccd38faa373af5059b5f,
0xad0e355d4d8b1cc9771d1,
0x703ccca78d36e770d0613,
0x54e05275b03e2cdedc053
]
for ans in d:
for h in range(2**6):
for l in range(2**8):
y = ans >> 52 #取y
mix = ans & (2**52-1) #剩下的为不确定z和pad混合
pads_true = mix >> 32 #拿到pad的有效的20位
source_pad = (h<<27)| (pads_true<<7)| l #爆破pad
z = limit(mix ^ (source_pad<<25)) #根据猜的pad,然后mix中pad和z的混合部分,确定z
for round in range(32):
pads = limit(source_pad*(32-round)) #根据encode来用pads
z = limit( z - ((y*16 + c) ^ (y + pads) ^ ((y>>5) + d))) #加密函数反一下
y = limit( y - ((z*16 + a) ^ (z + pads) ^ ((z>>5) + b)))
flag = (y<<32)+z
#print flag
if check(long_to_bytes(flag)): #根据flag的可能字符来判断咯
print(long_to_bytes(flag))

Flag

本题为动态flag