题目概述
这是一个涉及侧信道攻击的密码学挑战。题目名称"Do you have good eyes?"暗示我们需要仔细观察某些细微的差异,这些差异可能来自时间、功率消耗或其他侧信道信息。
题目信息:
连接地址:archive.cryptohack.org 43607
挑战文件:do-you-have-good-eyes.zip
目标:获取 BZHCTF{FLAG} 格式的flag
初步分析
1. 下载并解压挑战文件
bash
复制
下载
unzip do-you-have-good-eyes.zip
查看文件结构:
bash
复制
下载
ls -la
通常这类挑战会包含:
服务端代码(server.py)
加密/认证实现
可能的测试数据
2. 分析服务端行为
连接服务查看其行为:
python
复制
下载
from pwn import *
r = remote('archive.cryptohack.org', 43607)
print(r.recvline())
print(r.recvline())
print(r.recvline())
典型的服务端会:
显示一些加密后的数据
要求你区分两种不同的情况
根据你的回答决定是否给出flag
3. 理解挑战类型
根据题目名称和"Can you tell the difference?"的描述,这很可能是一个选择明文攻击或区分攻击的挑战。
常见的场景:
Padding Oracle Attack:通过观察填充错误的时间差异
Timing Attack:通过测量操作时间差异
Bleichenbacher Attack:RSA PKCS#1 v1.5 填充攻击
GCM/DSS 侧信道:通过时间/功率差异恢复密钥
深入分析挑战文件
查看源代码
python
复制
下载
# 假设 server.py 的内容类似
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os
import time
FLAG = "BZHCTF{...}"
def encrypt():
key = os.urandom(16)
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
# 某些操作...
return ciphertext
def main():
print("Can you tell the difference?")
# 显示一些信息
# 要求用户判断
关键漏洞识别
根据经验,这类挑战常见的漏洞是:
1. RSA解密时间差异
python
复制
下载
def decrypt(ciphertext):
# 不安全的解密实现
if ciphertext[0] != 0x00:
return "Invalid padding"
# 解密操作...
# 如果padding正确,返回时间更长
2. AES-CBC MAC验证
python
复制
下载
def verify_mac(data, tag):
# 使用比较时提前返回
for i in range(len(tag)):
if data[i] != tag[i]:
return False
time.sleep(0.01) # 故意添加延迟
return True
3. 字符串比较漏洞
python
复制
下载
def check_password(password):
# 不安全的字符串比较
for i in range(len(password)):
if password[i] != secret[i]:
return False
return True
具体攻击方法
场景1:Timing Attack - 字符串比较
如果挑战涉及比较两个字符串,攻击者可以通过测量响应时间来逐字符恢复秘密。
python
复制
下载
from pwn import *
import time
def timing_attack():
r = remote('archive.cryptohack.org', 43607)
# 逐字符恢复
secret = ""
charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}_"
for position in range(50): # 假设flag长度
best_char = ''
best_time = 0
for char in charset:
guess = secret + char + 'A' * (50 - len(secret) - 1)
# 测量响应时间
start = time.time()
r.sendline(guess.encode())
response = r.recvline()
end = time.time()
elapsed = end - start
if elapsed > best_time:
best_time = elapsed
best_char = char
secret += best_char
print(f"Recovered: {secret}")
if '}' in secret:
break
r.close()
return secret
场景2:Padding Oracle Attack
如果服务端使用CBC模式且会返回填充错误信息,可以实施Padding Oracle攻击。
python
复制
下载
from Crypto.Util.Padding import pad, unpad
from Crypto.Cipher import AES
import os
def padding_oracle_attack():
"""
Padding Oracle Attack on CBC mode
当服务器返回"Invalid padding"或"Padding error"时
"""
def xor_bytes(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
# 获取目标密文
r = remote('archive.cryptohack.org', 43607)
data = r.recvline().decode().strip()
# 解析IV和密文
iv = bytes.fromhex(data[:32])
ciphertext = bytes.fromhex(data[32:])
# 逐字节解密
blocks = [ciphertext[i:i+16] for i in range(0, len(ciphertext), 16)]
plaintext = b''
for block_idx in range(len(blocks)):
# 为每个字节构造padding
block = blocks[block_idx]
decrypted_block = [0] * 16
for byte_pos in range(15, -1, -1):
# 尝试所有可能的字节值
for guess in range(256):
# 构造中间值
# 根据padding oracle的原理
pass
# 使用oracle验证
# 如果成功,记录中间值
return plaintext
场景3:GCM/DSS 时间侧信道
如果是GCM相关的挑战(与上一个挑战相关),可能涉及:
python
复制
下载
def gcm_timing_attack():
"""
利用GCM验证时间差异
如果验证tag时提前返回,可以逐个字节猜测tag
"""
r = remote('archive.cryptohack.org', 43607)
# 获取密文和IV
r.recvuntil(b'ciphertext: ')
ciphertext = bytes.fromhex(r.recvline().strip().decode())
r.recvuntil(b'IV: ')
iv = bytes.fromhex(r.recvline().strip().decode())
# 逐个字节爆破tag
forged_tag = b''
for pos in range(16):
best_byte = 0
max_time = 0
for b in range(256):
# 构造部分tag
test_tag = forged_tag + bytes([b]) + b'\x00' * (15 - pos)
# 测量响应时间
start = time.perf_counter()
r.sendlineafter(b'tag: ', test_tag.hex().encode())
response = r.recvline()
elapsed = time.perf_counter() - start
if elapsed > max_time:
max_time = elapsed
best_byte = b
forged_tag += bytes([best_byte])
print(f"Recovered tag bytes: {forged_tag.hex()}")
return forged_tag
场景4:概率性攻击 - 区分两种加密模式
如果挑战是区分两种不同的加密算法或模式,可以通过统计分析:
python
复制
下载
def distinguish_attack():
"""
区分AES-CBC和AES-CTR模式
CBC模式下,相同明文块会产生不同密文块
CTR模式下,相同明文块会产生相同密文块
"""
r = remote('archive.cryptohack.org', 43607)
# 请求两个相同明文的加密
plaintext = b'AAAAAAAAAAAAAAAA' # 16字节,对齐的块
r.sendlineafter(b'plaintext: ', plaintext.hex().encode())
ct1 = bytes.fromhex(r.recvline().strip().decode())
r.sendlineafter(b'plaintext: ', plaintext.hex().encode())
ct2 = bytes.fromhex(r.recvline().strip().decode())
# 判断是CBC还是CTR
if ct1 == ct2:
# 如果两次加密结果相同,可能是CTR模式(使用相同IV)
# 也可能是ECB模式
return "CTR"
else:
# 如果不同,可能是CBC模式(使用随机IV)
return "CBC"
完整的解决方案
基于实际挑战的推测
根据挑战名称"Do you have good eyes?"和前面的"Authentification 2"挑战(同一个作者skilo),这个挑战很可能也涉及GCM或认证加密,但这次是关于观察差异。
可能是:
相同明文在GCM下的不同表现:使用相同的nonce或不同的nonce
时间侧信道攻击:验证MAC时的微小时间差异
区分攻击:区分随机数据和加密数据
通用攻击脚本
python
复制
下载
#!/usr/bin/env python3
from pwn import *
import time
import hashlib
from Crypto.Util.Padding import pad, unpad
from Crypto.Util.number import bytes_to_long, long_to_bytes
import json
# 配置
HOST = 'archive.cryptohack.org'
PORT = 43607
class EyesAttack:
def __init__(self):
self.r = None
self.flag = ""
def connect(self):
self.r = remote(HOST, PORT)
log.info("Connected to server")
def get_challenge(self):
"""接收挑战数据"""
data = self.r.recvuntil(b'Your answer: ')
return data.decode()
def timing_oracle(self, payload):
"""时间Oracle:发送payload并测量响应时间"""
start = time.perf_counter()
self.r.sendline(payload.encode())
response = self.r.recvline()
elapsed = time.perf_counter() - start
return elapsed, response
def byte_by_byte_attack(self):
"""逐字节恢复秘密"""
recovered = ""
charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}_!@#$%^&*()"
for pos in range(100): # 最多100个字符
best_char = ''
best_time = 0
for c in charset:
# 构造猜测
test_str = recovered + c
# 填充到固定长度(如果需要)
test_str = test_str.ljust(100, 'A')
# 发送并测量时间
elapsed, _ = self.timing_oracle(test_str)
log.debug(f"Testing '{c}': {elapsed:.6f}s")
if elapsed > best_time:
best_time = elapsed
best_char = c
recovered += best_char
log.info(f"Recovered: {recovered}")
# 检查是否完成
if recovered.endswith('}'):
break
return recovered
def side_channel_attack(self):
"""侧信道攻击主函数"""
self.connect()
# 获取初始信息
banner = self.r.recvline()
log.info(f"Banner: {banner.decode().strip()}")
# 执行特定的攻击
# 根据服务端的具体行为选择不同的攻击向量
# 示例:时间攻击
self.flag = self.byte_by_byte_attack()
# 发送最终答案
self.r.sendline(self.flag.encode())
final_response = self.r.recvall()
log.info(final_response.decode())
return self.flag
def padding_oracle_attack(self):
"""Padding Oracle攻击"""
self.connect()
# 获取加密数据
self.r.recvuntil(b'Encrypted: ')
encrypted = bytes.fromhex(self.r.recvline().strip().decode())
# 解析IV和密文
iv = encrypted[:16]
ciphertext = encrypted[16:]
# 解密每个块
plaintext = b''
blocks = [ciphertext[i:i+16] for i in range(0, len(ciphertext), 16)]
for i in range(len(blocks)):
# 对每个块进行解密
decrypted = self.decrypt_block(iv if i == 0 else blocks[i-1], blocks[i])
plaintext += decrypted
return plaintext
def decrypt_block(self, iv, block):
"""解密单个块(Padding Oracle)"""
decrypted = bytearray(16)
for byte_pos in range(15, -1, -1):
# 尝试所有可能的字节
for guess in range(256):
# 构造中间块
test_iv = bytearray(iv)
# 设置padding
padding_val = 16 - byte_pos
for j in range(byte_pos + 1, 16):
test_iv[j] = decrypted[j] ^ padding_val
test_iv[byte_pos] = guess
# 发送测试请求
test_data = bytes(test_iv) + block
self.r.recvuntil(b'Enter ciphertext: ')
self.r.sendline(test_data.hex().encode())
response = self.r.recvline()
# 检查是否返回有效填充
if b'Valid' in response:
decrypted[byte_pos] = guess ^ padding_val
break
# 去除填充
padding_len = decrypted[-1]
return bytes(decrypted[:-padding_len])
def gcm_forgery_attack(self):
"""GCM伪造攻击(利用时间差异)"""
self.connect()
# 获取挑战数据
self.r.recvuntil(b'IV: ')
iv = bytes.fromhex(self.r.recvline().strip().decode())
self.r.recvuntil(b'Ciphertext: ')
ciphertext = bytes.fromhex(self.r.recvline().strip().decode())
# 逐字节猜测标签
forged_tag = b''
for pos in range(16):
best_byte = 0
max_delay = 0
for b in range(256):
test_tag = forged_tag + bytes([b]) + b'\x00' * (15 - pos)
# 测量验证时间
start = time.perf_counter()
self.r.sendlineafter(b'Tag: ', test_tag.hex().encode())
response = self.r.recvline()
delay = time.perf_counter() - start
# 有效字节会导致更长的处理时间(因为要继续验证后续字节)
if delay > max_delay:
max_delay = delay
best_byte = b
forged_tag += bytes([best_byte])
log.info(f"Recovered tag byte {pos+1}: {best_byte:02x}")
return forged_tag
def main():
attack = EyesAttack()
try:
# 根据提示选择攻击方法
# 方法1:时间侧信道攻击
flag = attack.side_channel_attack()
# 方法2:Padding Oracle攻击
# plaintext = attack.padding_oracle_attack()
# 方法3:GCM时间攻击
# tag = attack.gcm_forgery_attack()
log.success(f"Flag: {flag}")
except Exception as e:
log.error(f"Attack failed: {e}")
finally:
if attack.r:
attack.r.close()
if __name__ == '__main__':
main()
实际利用思路
最可能的漏洞场景
基于题目名称"Do you have good eyes?",最可能的攻击是:
1. 时序攻击(Timing Attack)
服务端使用不安全的比较函数:
python
复制
下载
def check_tag(provided_tag, computed_tag):
for i in range(len(provided_tag)):
if provided_tag[i] != computed_tag[i]:
return False
# 微妙的时间差异
return True
2. 盲注式攻击
通过观察"正确"和"错误"响应的差异,逐字节恢复flag。
快速利用脚本
python
复制
下载
from pwn import *
import time
def exploit():
r = remote('archive.cryptohack.org', 43607)
# 接收提示
print(r.recvline())
print(r.recvline())
flag = ""
charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}_"
# 逐字符爆破flag
for i in range(50):
for c in charset:
guess = flag + c
# 发送猜测
r.sendline(guess.encode())
start = time.time()
response = r.recvline()
elapsed = time.time() - start
# 根据响应时间判断
if elapsed > 0.1: # 阈值根据实际情况调整
flag += c
print(f"Found: {flag}")
break
else:
break
print(f"Flag: {flag}")
r.close()
if __name__ == '__main__':
exploit()
解决方案总结
关键要点
识别漏洞类型:通过观察服务端行为判断是时间攻击、Padding Oracle还是其他侧信道
收集信息:
响应时间差异
错误消息差异
加密数据格式
构造攻击:
逐字符或逐字节猜测
利用时间差异作为Oracle
自动化攻击过程
验证结果:提交flag确认
防御措施
使用常数时间比较(hmac.compare_digest)
统一错误消息
添加随机延迟(但这不是根本解决方案)
使用恒定时间算法实现
最终答案
成功利用漏洞后,服务端返回的flag格式为:BZHCTF{...}
具体的flag值需要在运行时通过攻击获取。实际获得的flag取决于服务端的具体实现和挑战数据。
注意:由于我没有实际连接到服务端,以上代码提供了多种可能的攻击向量。在实际解题时,需要根据服务端的响应和行为选择最合适的攻击方法。通常,时间侧信道攻击是最常见的"好眼力"挑战类型。