概述
Flippin Bank是来自于HTB(hackthebox.com)的一个容易级密码学挑战,完成该挑战所需要掌握的知识点包括AES算法, CBC分组模式以及XOR运算。
题目分析
相关的任务文件包括Python源代码文件app.py以及一个在线的运行环境。
app.py内容节选如下
import socketserver
import socket, os
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
from Crypto.Random import get_random_bytes
from binascii import unhexlify
from secret import FLAG
wlcm_msg ='...'
key = get_random_bytes(16)
iv = get_random_bytes(16)
def encrypt_data(data):
padded = pad(data.encode(),16,style='pkcs7')
cipher = AES.new(key, AES.MODE_CBC,iv)
enc = cipher.encrypt(padded)
return enc.hex()
def decrypt_data(encryptedParams):
cipher = AES.new(key, AES.MODE_CBC,iv)
paddedParams = cipher.decrypt( unhexlify(encryptedParams))
print(paddedParams)
if b'admin&password=g0ld3n_b0y' in unpad(paddedParams,16,style='pkcs7'):
return 1
else:
return 0
def send_msg(s, msg):
enc = msg.encode()
s.send(enc)
def main(s):
send_msg(s, 'username: ')
user = s.recv(4096).decode().strip()
send_msg(s, user +"'s password: " )
passwd = s.recv(4096).decode().strip()
send_msg(s, wlcm_msg)
msg = 'logged_username=' + user +'&password=' + passwd
try:
assert('admin&password=g0ld3n_b0y' not in msg)
except AssertionError:
send_msg(s, 'You cannot login as an admin from an external IP.\nYour activity has been logged. Goodbye!\n')
raise
msg = 'logged_username=' + user +'&password=' + passwd
send_msg(s, "Leaked ciphertext: " + encrypt_data(msg)+'\n')
send_msg(s,"enter ciphertext: ")
enc_msg = s.recv(4096).decode().strip()
try:
check = decrypt_data(enc_msg)
except Exception as e:
send_msg(s, str(e) + '\n')
s.close()
if check:
send_msg(s, 'Logged in successfully!\nYour flag is: '+ FLAG)
s.close()
else:
send_msg(s, 'Please try again.')
s.close()
解题思路
以上的代码首先要求输入用户名和密码后构造一个'logged_username=' + user +'&password=' + passwd的字串, 条件是该字串不能包括admin&password=g0ld3n_b0y。 随后对其进行加密且返回密文,接着用户可以输入一个密文,如果其解密后对应的明文中包括admin&password=g0ld3n_b0y的话,就会显示该任务的Flag。
我们可以看到代码中使用的加密算法是AES(Advanced Encryption Standard)算法并使用CBC(Cipher-block chaining)分组模式。
在对称加密算法中,CBC是最为常用的分组模式,其加密过程首先将第一个明文块与一个初始化向量(IV)进行XOR操作后进行加密以生成密文块, 而其后的每一个明文块都与前一个密文块进行XOR后进行加密, 如下图所示。
其解密过程则首先将第一个密文块进行解密后与IV进行XOR以生成明文块,而其后的每一个密文块进行解密后与前一个密文块进行XOR以生成明文块,如下图所示。
我们注意到AES算法中,数据块的大小是16个字节,而字串logged_username=的长度刚好是16,也就是说,在CBC分组模式下,该字串是第一个明文块,其对应的密文则是第一个密文块。而用户名和密码则在后续的第二和第三个数据块中。 根据CBC的解密机制, 我们可以知道修改某个密文块的内容会改变其后面密文块的解密结果。 假设我们输入的用户名是abcde, 那么只要找到符合如下条件的X1就可以生成所需的解密结果
C1 XOR T = 'abcde&password=g'
X1 XOR T = 'admin&password=g'
以上的逻辑关系中可以用下图表示
我们不知道T的值,因此需要将其替换掉,将上下两式合并后,我们可以推算如下
C1 XOR T XOR X1 XOR T = 'abcde&password=g' XOR 'admin&password=g'
C1 XOR X1 = 'abcde&password=g' XOR 'admin&password=g'
X1 = 'abcde&password=g' XOR 'admin&password=g' XOR C1
解题过程
nc 167.99.89.94 30991
username: abcde
abcde's password: g0ld3n_b0y
########################################################################
# Welcome to the Bank of the World #
# All connections are monitored and recorded #
# Disconnect IMMEDIATELY if you are not an authorized user! #
########################################################################
Leaked ciphertext: 94408745deab125455e8021a33ba248459e36e86b78b172293453a59339468f1879bff6f2ad2aec0461f1eb39a1b81ba
使用Leaked ciphertext的数据我们可以计算出X1
from Crypto.Util.number import bytes_to_long
## Leaked ciphertext 中的第一个密文块
C1 = 0x94408745deab125455e8021a33ba2484
p1 = bytes_to_long(b'abcde&password=g')
p2 = bytes_to_long(b'admin&password=g')
X1 = C1 ^ p1 ^ p2
## 计算而得的X1用于替换第一个密文块
hex(X1)
## 结果: 0x94468948d5ab125455e8021a33ba2484
将X1替换第一个密文块作为新的输入
enter ciphertext: 94468948d5ab125455e8021a33ba248459e36e86b78b172293453a59339468f1879bff6f2ad2aec0461f1eb39a1b81ba
Logged in successfully!
Your flag is: ....