1. Secret-Key Encryption Lab
手册
进度:Task 6/7
Overview
- 使用工具编写加解密程序
- 了解常见错误、利用漏洞进行攻击
主题:
- 密钥加密(对称加密)
- 代替密码与频率分析
- 加密模式、IV向量、填充
- 使用加密算法的常见错误
- 使用加密库进行编程
Lab Environment
Seed Ubuntu
共享文件夹
$ mkdir -p ~/Share
$ sudo mount -t vboxsf VM_Shared ~/Share
2024/4/16重启虚拟机忽然发现共享文件夹空了,然后发现之前配成了固定分配,重新输一下命令就好了。
docker(Task6.3用到)
-
首先在虚拟机里装docker
sudo apt-get update curl -fsSL https://test.docker.com -o test-docker.sh sudo sh test-docker.sh
-
利用Labsetups中的docker-compose.yml文件建立docker容器
-
运行容器并获取容器ID(使用指令别名)
-
启动shell(使用指令别名)
获取shell时可以不写完整ID,只需要前几个字符,只要能唯一表示那个容器。
-
(关闭)
Task 1: Frequency Analysis
- 加密过程
- Step 1:随机一个a到z序列作为替换表(即密钥)
这里是zvsbunjphmrqkdaytxwiclgfoe 。 - Step 2:简化初始文章(大写转小写、去除标点、保留空格)
- Step 3:加密
$ tr ’abcdefghijklmnopqrstuvwxyz’ ’zvsbunjphmrqkdaytxwiclgfoe’ < plaintext.txt > ciphertext.txt
- Step 1:随机一个a到z序列作为替换表(即密钥)
频率分析
Step 1:运行frac.py统计密文信息
1-gram (top 20): n: 488 y: 373 v: 348 x: 291 u: 280 q: 276 m: 264 h: 235 t: 183 i: 166 p: 156 a: 116 c: 104 z: 95 l: 90 g: 83 b: 83 r: 82 e: 76 d: 59
2-gram (top 20): yt: 115 tn: 89 mu: 74 nh: 58 vh: 57 hn: 57 vu: 56 nq: 53 xu: 52 up: 46 xh: 45 yn: 44 np: 44 vy: 44 nu: 42 qy: 39 vq: 33 vi: 32 gn: 32 av: 31
3-gram (top 20): ytn: 78 vup: 30 mur: 20 ynh: 18 xzy: 16 mxu: 14 gnq: 14 ytv: 13 nqy: 13 vii: 13 bxh: 13 lvq: 12 nuy: 12 vyn: 12 uvy: 11 lmu: 11 nvh: 11 cmu: 11 tmq: 10 vhp: 10
Step 2:进行频率分析攻击
(从小写(密文)转到大写(明文)方便看变化)
-
根据频率最高的字幕组合猜测ytn是THE:
tr 'ytn' 'THE' < ciphertext.txt > answer.txt
-
根据单个v和vT猜测v是A
tr 'ytnv' 'THEA' < ciphertext.txt > answer.txt
-
。。。。
- 最终结果
tr 'ytnvxuplqhmesckbifrgjzdaow' 'THEAONDWSRIPKMXFLVGBQUYCJZ' < ciphertext.txt > answer.txt
Task 2: Encryption using Different Ciphers and Modes
用到的openssl命令
openssl
$ openssl enc -ciphername [-in filename] [-out filename] [-pass arg] [-e] [-d] [-a/-base64]
[-A] [-k password] [-kfile filename] [-K key] [-iv IV] [-S salt] [-salt] [-nosalt] [-z] [-md]
[-p] [-P] [-bufsize number] [-nopad] [-debug] [-none] [-engine id] //enc对称加密
$ man enc //查询命令
尝试三种加密算法与加密模式的组合
1. 【ECB vs CBC】分别使用AES-128-ECB和AES-128-CBC加密图片
ECB
openssl enc -aes-128-ecb -e -in pic_original.bmp -out AES128ECB.bmp -K 00112233445566778889aabbccddeeff
加密原始图片
(图片路径“..\Labsetup\Files\pic_original.bmp”)
在010 Editor中打开原来的bmp图片文件,找到bmp开头的那串,复制
再打开加密后的bmp文件,把开头复制过去
然后就可以看到加密后的图片
ECB模式会将同样的明文块加密成同样的密文块,因此加密后图片中还是能看出原图特征。
CBC
$ openssl enc -aes-128-cbc -e -in -in pic_original.bmp -out AES128CBC.bmp \
-K 00112233445566778889aabbccddeeff \
-iv 0102030405060708
CBC模式用到前一块的密文,因此相同明文也会加密得到不同结果,从加密后图片中就看不出原先图片的特征。
2. 【流密码特征】使用AES-128-CBC与AES-128-CFB加密长度21字节的明文
openssl enc -aes-128-cbc -e -in plain.txt -out cipher1.txt \
-k 00112233445566778899AABBCCDDEEFF \
-iv 000102030405060708090a0b0c0d0e0f
openssl enc -aes-128-cfb -e -in plain.txt -out cipher2.txt \
-k 00112233445566778899AABBCCDDEEFF \
-iv 000102030405060708090a0b0c0d0e0f
ls -l plain.txt cipher1.txt cipher2.txt
(晕晕,怎么都比示例图片上的大16bytes)
加密后开头都多了相同的16字节,是默认加盐了,可以使用命令-nosalt
取消默认加盐
3. 【IV对密文影响】——用AES-128-CBC进行两次加密,IV相差一位
openssl enc -aes-128-cbc -e -in plain.txt -out iv1.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
openssl enc -aes-128-cbc -e -in plain.txt -out iv2.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0e
xxd -p iv1.txt
xxd -p iv2.txt
可以看到虽然iv向量只修改一位,但密文除了开头16字节全变了
Task 3: Encryption Mode – ECB vs. CBC
(才发现Task2里不小心把Task3给直接做了)
ECB模式会将同样的明文块加密成同样的密文块,因此加密后图片中还是能看出原图特征。
CBC模式用到前一块的密文,因此相同明文也会加密得到不同结果,从加密后图片中就看不出原先图片的特征。
Task 4:Padding
CBC
创建文件
创建长度分别为5bytes、10bytes、16bytes的文件(p5、p10、p16)
$ echo -n "1234567890" > p10.txt
$ echo -n "1234567890123456" > p16.txt
加密
openssl enc -aes-128-cbc -e -in p5.txt -out c5.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
openssl enc -aes-128-cbc -e -in p10.txt -out c10.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
openssl enc -aes-128-cbc -e -in p16.txt -out c16.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
解密
注意加上-nopad
防止解密时去除填充内容
openssl enc -aes-128-cbc -d -in c5.txt -out CBC5.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f -nopad
openssl enc -aes-128-cbc -d -in c10.txt -out CBC10.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f -nopad
openssl enc -aes-128-cbc -d -in c16.txt -out CBC16.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f -nopad
查看填充
hexdump -C CBC5.txt
xxd CBC5.txt
一开始用大写P加数字命名,然后忽然意识到不区分文件名大小写,会把原文件覆盖掉,就换成了加密模式...
截图上还是p5、p10、p16,因为懒得重新截图了
之后三种加密模式就是重复的copy+paste
ECB
openssl enc -aes-128-ecb -e -in p5.txt -out cECB5.txt -K 00112233445566778889aabbccddeeff
openssl enc -aes-128-ecb -d -in cECB5.txt -out ECB5.txt -K 00112233445566778889aabbccddeeff -nopad
hexdump -C ECB5.txt
CFB
openssl enc -aes-128-cfb -e -in p5.txt -out cCFB5.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
openssl enc -aes-128-cfb -d -in cCFB5.txt -out CFB5.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f -nopad
hexdump -C CFB5.txt
OFB
openssl enc -aes-128-ofb -e -in p5.txt -out cOFB5.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
openssl enc -aes-128-ofb -d -in cOFB5.txt -out OFB5.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f -nopad
hexdump -C OFB5.txt
Summary
-
ECB、CBC都有填充:
- 因为ECB和CBC每次加密都是对于一整块分组,因此必须填充满最后一个分组
- 填充规则为填充至明文长度为分组长度的整数倍,且当填充前明文长度已经是分组长度整数倍时填充一个分组长度。
- 填充的每一个字节的内容都表示填充的字节长度
-
CFB、OFB没有填充:
- 因为CFB、OFB是流密码,每个分组中都是一位一位加密的,因此不需要填充
-
CTR也是流密码,也没有填充
Task 5: Error Propagation – Corrupted Cipher Text
损坏的密文影响多少信息?
- ECB:一个分组,16bytes
一位被改变,这个密文所在的块就被改变。但由于ECB各个块相互独立,所以只影响改动位置所在的块。 - CBC:最多两个分组
仅第55bytes所在密文块被改变,由于ECB解密是并行的,每一块只和自己和上一块的密文有关,因此最多影响两个分组 - CFB:?
IV向量左移$$j$$位,出错的块至多传播到$$\lceil \frac{n}{j} \rceil $$块
晕晕,openssl默认$$j$$是多少啊 - OFB:仅改动的一位,1bit
密钥流独立于密文块,不受影响
实验过程
创建一个1000+字节的文件
写脚本生成了一个有规律的文件(这样容易看出解密后影响的部分)
使用AES-128加密文件
命令合集
# ECB
openssl enc -aes-128-ecb -e -in plaintext.txt -out cECB.txt -K 00112233445566778889aabbccddeeff
openssl enc -aes-128-ecb -d -in cECB.txt -out ECB.txt -K 00112233445566778889aabbccddeeff
# CBC
openssl enc -aes-128-cbc -e -in plaintext.txt -out cCBC.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
openssl enc -aes-128-cbc -d -in cCBC.txt -out CBC.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
# CFB
openssl enc -aes-128-cfb -e -in plaintext.txt -out cCFB.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
openssl enc -aes-128-cfb -d -in cCFB.txt -out CFB.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
# OFB
openssl enc -aes-128-ofb -e -in plaintext.txt -out cOFB.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
openssl enc -aes-128-ofb -d -in cOFB.txt -out OFB.txt -k 00112233445566778899AABBCCDDEEFF -iv 000102030405060708090a0b0c0d0e0f
破坏文件的第55字节中的一个bit
使用010Editor打开四个密文文件,以CBC为例,找到第55个字节——“EC”。
看到右边的“Binary”-“Value”以0结尾,直接给“EC”加上1变成“ED”就是只改动一位了
同理改掉其他三个密文文件(奇数-1,偶数+1)
解密
结果
直接打开:
(可以直观看出OFB只被影响了1bit)
010Editor:
CBC
明文丢失了16bytes(一个分组)的信息
ECB
明文丢失了16bytes的信息
CFB
明文丢失了16bytes的信息
OFB
没必要用010Editor看
只丢失1bit
Task 6: Initial Vector (IV) and Common Mistakes
Task 6.1. IV Experiment
过程
随便整个明文并使用aes-128-cbc加密,这里加上-nosalt
取消默认加盐
echo -n "This is a plaintext......" > plaintext.txt
openssl enc -aes-128-cbc -e -in plaintext.txt -out ciphertext.txt \
-k 00112233445566778899AABBCCDDEEFF \
-iv 000102030405060708090a0b0c0d0e0f \
-nosalt
使用两个不同IV加密
-k 00112233445566778899AABBCCDDEEFF \
-iv 000102030405060708090a0b0c0d0e0e \
-nosalt
使用两个相同IV加密
openssl enc -aes-128-cbc -e -in plaintext.txt -out sameIV.txt \
-k 00112233445566778899AABBCCDDEEFF \
-iv 000102030405060708090a0b0c0d0e0f \
-nosalt
再用010Editor打开三个密文文件,使用相同IV加密结果是一样的。
Why does IV need to be unique?
Task 6.2. Common Mistake: Use the Same IV
修改了一下"Labsetup\Files\sample_code.py"
#!/usr/bin/python3
# XOR two bytearrays
def xor(first, second):
return bytearray(x^y for x,y in zip(first, second))
MSG = "This is a known message!"
HEX_1 = "a469b1c502c1cab966965e50425438e1bb1b5f9037a4c159"
HEX_2 = " bf73bcd3509299d566c35b5d450337e1bb175f903fafc159"
# Convert ascii string to bytearray
D1 = bytes(MSG, 'utf-8')
# Convert hex string to bytearray
D2 = bytearray.fromhex(HEX_1)
D3 = bytearray.fromhex(HEX_2)
r1=xor(D1, D2)
r2=xor(r1, D3)
# Convert bytearray to ascii string
print(r2.decode('utf-8'))
得到$$P_2$$:
Task 6.3. Common Mistake: Use a Predictable IV
首先运行docker
P1,知道范围是“Yes”或者“No”
C1已知
IV1已知,Bob用来加密的IV
IV2已知,Bob用来加密下一个明文的IV
构造$$P_1'=P_1\oplus IV_1\oplus IV_2$$,使得$$C_1=C_2$$
# XOR two bytearrays
def xor(first, second):
return bytearray(x^y for x,y in zip(first, second))
# construct the guess (with the padding)
YES = b"Yes" + bytes("\x0d"*13, 'utf-8')
NO = b"No" + bytes("\x0e"*14, 'utf-8')
IV1 = "32fd14233c573eb07b7e913c94400a23"
IV2 = "cb0bf5503c573eb07b7e913c94400a23"
# Calculate the plaintext
r1 = xor(bytearray.fromhex(IV1), bytearray.fromhex(IV2))
r2 = xor(r1, YES)
r3 = xor(r1, NO)
print(r2.hex()) #猜测Yes构造的明文
print(r3.hex()) #猜测No构造的明文
猜测是Yes(注意AES填充规则)
把用“Yes”构造出的明文放进去,得到$$C_2$$,发现第一块与$$C_1$$相同(后面一块是padding加密后的结果,无意义)。
所以Bob的秘密消息就是"YES"
*Task 7: Programming using the Crypto Library
(不想写了,之后有空再填坑)