首先下载附件后.html中有中文版本的加密流程解析,每次都是从最后一位开始的解密,那么最后一位只可能是0或者1。
密文显示了wheel中的01序列的约束条件。先前积累的算法知识可以看出来这就一个很简单的二分图着色问题。写一个二分图着色算法分别尝试最后一位是0和1的两种情况,再通过hash值判断哪一个是我们需要的答案。
import hashlib
import ast
from collections import defaultdict, deque
from typing import List
with open('ciphertext.txt', 'r') as f:
ciphertext = ast.literal_eval(f.readline().strip())
with open('flag_hash.txt', 'r') as f:
hash_salt = f.read().strip()
hash_val, salt = hash_salt.split(':')
def decode(data:bytes) -> bytes:
"""将2进制码转换为字符"""
out = bytearray()
for b in data:
b = b & 0b01111111
out.append(b)
return bytes(out)
def decrypt(wheel: List[int], distances: List[int]) -> bytes:
k = 64
state = -1
bits = []
for d in distances:
state = (state + d) % k
bits.append(wheel[state])
bit_str = ''.join(map(str, bits))
byte_array = bytearray()
for i in range(0, len(bit_str), 8):
byte_bits = bit_str[i:i+8]
if len(byte_bits) < 8:
continue
byte_val = int(byte_bits, 2)
byte_array.append(byte_val)
return bytes(byte_array)
def compute_state_sequence(distances: List[int], k: int = 64) -> List[int]:
states = [-1]
for d in distances:
s_prev = states[-1]
s_next = (s_prev + d) % k
states.append(s_next)
return states
def solve(states: List[int], distances: List[int], k: int = 64):
graph = defaultdict(list)
n = len(distances)
for t in range(1, n + 1):
s_prev = states[t-1]
s_curr = states[t]
d_val = distances[t-1]
if d_val > 1:
for d_step in range(1, d_val):
p = (s_prev + d_step) % k
i = s_curr
graph[p].append(i)
graph[i].append(p)
color = [-1] * k
is_bipartite = True
groups = {0: set(), 1: set()}
for node in range(k):
if color[node] == -1:
queue = deque([node])
color[node] = 0
groups[0].add(node)
while queue:
curr = queue.popleft()
for neighbor in graph[curr]:
if color[neighbor] == -1:
color[neighbor] = 1 - color[curr]
groups[color[neighbor]].add(neighbor)
queue.append(neighbor)
elif color[neighbor] == color[curr]:
is_bipartite = False
break
if not is_bipartite:
break
if not is_bipartite:
break
return groups
def recover_wheel_and_decrypt():
distances = ciphertext
k = 64
states = compute_state_sequence(distances, k)
groups = solve(states, distances, k)
group0 = groups[0]
group1 = groups[1]
wheel_candidates = []
wheel1 = [0 if i in group0 else 1 for i in range(k)]
wheel_candidates.append(wheel1)
wheel2 = [1 if i in group0 else 0 for i in range(k)]
wheel_candidates.append(wheel2)
for wheel in wheel_candidates:
encrypted_bytes = decrypt(wheel, distances)
decrypted_bytes = decode(encrypted_bytes)
candidate_text = decrypted_bytes.decode('ascii')
candidate_flag = candidate_text.strip()
h = hashlib.sha256((salt + candidate_flag).encode()).hexdigest()
if h == hash_val:
print(candidate_text)
return candidate_text
if __name__ == "__main__":
recover_wheel_and_decrypt()
# DoubleUmCtF[S33K1NG_tru7h-7h3_w1s3-f1nd_1n57e4d-17s_pr0f0und-4b5ence_n0w-g0_s0lv3-th3_3y3s-1n_N0ita]
# NSSCTF{S33K1NG_tru7h-7h3_w1s3-f1nd_1n57e4d-17s_pr0f0und-4b5ence_n0w-g0_s0lv3-th3_3y3s-1n_N0ita}
