[Crypto] nanoDiamond-rev
题目
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
|
# from Crypto.Util.number import *
import string
import secrets
from hashlib import sha256
from random import randint, shuffle, choice
def proof_of_work():
s = ''.join([secrets.choice(string.digits + string.ascii_letters)
for _ in range(20)])
print(f'sha256(XXXX+{s[4:]}) == {sha256(s.encode()).hexdigest()}')
if input('Give me XXXX: ') != s[:4]:
exit(1)
ROUND_NUM = 50
PREROUND_NUM = 13
CHEST_NUM = 6
with open('flag', 'r') as f:
flag = f.read()
white_list = ['==','(',')','0','1','and','or','B0','B1','B2','B3','B4','B5']
def calc(ans, chests, expr):
B0, B1, B2, B3, B4, B5 = chests
return ans(eval(expr))
def round():
chests = [choice((True, False)) for _ in range(CHEST_NUM)]
print("Six chests lie here, with mimics or treasure hidden inside.")
print("But don't worry. Skeleton Merchant knows what to do.")
print("Be careful, Skeleton Merchant can lie twice!")
truth = lambda r: not not r
lie = lambda r: not r
lie_num = randint(0, 2)
lie_status = [truth] * (PREROUND_NUM - lie_num) + [lie] * lie_num
shuffle(lie_status)
for i in range(PREROUND_NUM):
try:
question = input('Question: ').strip()
for word in question.split(' '):
assert word in white_list, f"({word}) No treasure for dirty hacker!"
result = calc(lie_status[i], chests, question)
print(f'Answer: {result}!')
except Exception as e:
print("Skeleton Merchant fails to understand your words.")
print(e)
print('Now open the chests:')
return chests == list(map(int, input().strip().split(' ')))
if __name__ == '__main__':
proof_of_work()
print('Terraria is a land of adventure! A land of mystery!')
print('Can you get all the treasure without losing your head?')
for i in range(ROUND_NUM):
if not round():
print('A chest suddenly comes alive and BITE YOUR HEAD OFF.')
exit(0)
else:
print('You take all the treasure safe and sound. Head to the next vault!')
print(f"You've found all the treasure! {flag}")
|
WP
首先我们有异或运算:
r1 xor r2 = ((r1 and r2) == 0 ) and (r1 or r2)
考虑先询问每个值一次,询问方法类似:
B0 == 1
得到每个bool变量的初始值。
由于可能说谎,验证一下,询问:
B0 xor B1 == 1
B2 xor B3 == 1
B4 xor B5 == 1
假设上面的询问与第一轮得到的值有矛盾,则说明A、B、A xor B 中有一个假信息。(有两个假信息概率较小)
此时已经出现一次错误,我们认定后面的回答都是正确的(再次出现假信息概率较小)
考虑再次询问 A xor B:
若答案和之前相同,则认为A xor B正确,询问A可以得到A和B哪个正确,更新A和B的值。
若答案和之前不同则认为A xor B错误,保持A和B值不变。
由于最多可能出现两次矛盾,每次矛盾需要2次询问验证,总询问次数最多为6+3+2+2=13。
但是前面忽略的几种“概率较小”的情况加起来并且在连续进行50轮的情况下出现的概率是非常高的。跑通的概率大概和手游抽卡差不多。
但是不怕,我是欧皇,跑了几百次就出flag了。
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
116
117
118
119
|
from pwn import *
context.log_level='info'
from Crypto.Util.number import *
from pwnlib.util.iters import mbruteforce
from hashlib import sha256
from gmpy2 import *
table = string.ascii_letters+string.digits
def passpow():
io.recvuntil(b"XXXX+")
suffix = io.recv(16).decode("utf8")
io.recvuntil(b"== ")
cipher = io.recvline().strip().decode("utf8")
gg = 0
print(suffix)
print(cipher)
for i1 in range(len(table)):
for i2 in range(len(table)):
for i3 in range(len(table)):
for i4 in range(len(table)):
sss = sha256((table[i1]+table[i2]+table[i3]+table[i4]+suffix).encode()).hexdigest()
if sss == cipher:
gg = 1
io.sendline((table[i1]+table[i2]+table[i3]+table[i4]).encode())
break
if gg == 1:
break
if gg == 1:
break
if gg == 1:
break
#io.sendline(proof.encode())
def Xor(a, b):
return f"( ( ( {a} and {b} ) == 0 ) and ( {a} or {b} ) )"
def Not(x):
if x == 1:
return 0
else:
return 1
def solve(a, b, vala, valb):
io.recvuntil(b"Question: ")
io.sendline(f'{Xor(f"B{a}", f"B{b}")} == 1'.encode())
io.recvuntil(b"Answer: ")
tmp = io.recvline().strip()[:-1]
if (eval(tmp)) == ((vala ^ valb) == 1):
return vala, valb, 1
io.recvuntil(b"Question: ")
io.sendline(f'{Xor(f"B{a}", f"B{b}")} == 1'.encode())
io.recvuntil(b"Answer: ")
tmp2 = io.recvline().strip()[:-1]
if (eval(tmp2)) == ((vala ^ valb) == 1):
return vala, valb, 2
io.recvuntil(b"Question: ")
io.sendline(f'B{a} == 1'.encode())
io.recvuntil(b"Answer: ")
tmp = io.recvline().strip()[:-1]
if (eval(tmp)) == vala:
return vala, Not(valb), 3
else:
return Not(vala), valb, 3
def exp():
passpow()
print("DONEPOW")
ROUND_NUM = 50
#io.interactive()
io.recvuntil(b"Can you get all the treasure without losing your head?")
for i in range(ROUND_NUM):
io.recvuntil(b"Be careful, Skeleton Merchant can lie twice!")
print(f'round = {i}')
ans = []
for j in range(6):
io.recvuntil(b"Question: ")
io.sendline(f'B{j} == 1'.encode())
io.recvuntil(b"Answer: ")
tmp = io.recvline().strip()[:-1]
if(eval(tmp)):
ans.append(1)
else:
ans.append(0)
num = 6
a, b, c = solve(0, 1, ans[0], ans[1])
num += c
ans[0] = a
ans[1] = b
a, b, c = solve(2, 3, ans[2], ans[3])
num += c
ans[2] = a
ans[3] = b
a, b, c = solve(4, 5, ans[4], ans[5])
num += c
ans[4] = a
ans[5] = b
remain = 13 - num
for j in range(remain):
io.recvuntil(b"Question: ")
io.sendline(f'B{j} == 1'.encode())
io.recvuntil(b"Answer: ")
tmp = io.recvline().strip()[:-1]
tosend = str(ans)[1:-1].replace(',','')
io.recvuntil(b'Now open the chests:\n')
io.sendline(tosend)
io.interactive()
while True:
try:
io = remote("1.13.154.182", 32664)
exp()
except Exception as e:
print(e)
|