前置知识编码哈希加密密钥无无有可逆性可逆,任何人不可逆凭密钥可逆输出长度变长固定变长目的数据格式转换完整性校验/指纹机密性编码PYTHONimport base64 # 编码 data = b"admin:123456" encoded = base64.b64encode(data) print(encoded) # 解码 decoded = base64.b64decode(encoded) print(decoded)Base64字符集固定:A-Z、a-z、0-9、+/=,末尾有可能有0-2个等号。编码不隐藏任何信息,仅仅改变表现形式。HTTP Basic 认证头里就是Base64编码的账密,抓包直接解码;JWT:第二部分是Base64url编码的json,直接解析就能看到载荷。哈希PYTHONimport hashlib md5_1 = hashlib.md5(b"password").hexdigest() md5_2 = hashlib.md5(b"Password").hexdigest() md5_3 = hashlib.md5(b"admin").hexdigest() print(md5_1) print(md5_2) print(md5_3) sha256_1 = hashlib.sha256(b"password").hexdigest() sha256_2 = hashlib.sha256(b"admin").hexdigest() print(sha256_1) print(sha256_2)输出是固定长度(MD5:128bit->32位HEX),(SHA256:256bit->64位HEX)雪崩效应:输入/改变1bit,输出变化巨大。渗透:弱HASH(MD5、SHA1)可碰撞,但最实际的是彩虹表。加盐(salt):hash(salt+password)让相同密码产生不同哈希,但盐值在代码里泄露等于没加。加密与密钥定义:加密是将明文经密钥控制转换为不可读密文,密钥长度决定暴力破解难度。对称非对称一、算法与随机数分类:对称加密(Symmetric):加密和解密使用同一个密钥。AES/DES/3DES适用场景:加密大文件、视频、大量数据传输。非对称加密(Asymmetric):加密和解密使用一对儿密钥(公钥加密,私钥解密,或私钥签名,公钥验签)RSA/ECC(椭圆曲线)适用场景:SSL/HTTPS握手、数字签名、安全传输对称密钥。哈希算法(Hash/摘要):单项函数、将任意长度数据转为固定长度指纹,不可逆。MD5/SHA-1/SHA256。适用场景:验证文件的完整性(防篡改)、存储密码(服务器只存指纹、不存明文密码)。MD5算法(已经被破解)二、对称加密核心:模式决定安全性,而不是算法本身。2.1 分组密码与填充分组密码(AES):一次处理固定块(16字节)。不足的块要填充。PKCS#7填充:缺N个字节就填N个 0xN。如块大小16数据15字节,补一个0x01;数据9字节,补7个0x07。填充就是Padding Oracle攻击的根源。PYTHONfrom Crypto.Util.Padding import pad data = b"Hello World" # 11字节 padded = pad(data,16) print(padded) # Hello World\x05\x05\x05\x05\x05 print(padded.hex()) # 48656c6c6f20576f726c640505050505 # 缺5个字节,填充5个0x052.2 ECB模式(电子密码本模式)算法定义:各分组完全独立加密缺陷:相同明文块产生相同密文块,导致密文保留了明文的统计特征。示例下载:https://cloud.komll.com/s/6RUN这是ECB前的图片PYTHONfrom Crypto.Cipher import AES import os import sys filename = "C:\\Users\\82375\\Desktop\\1.bmp" with open(filename, "rb") as f: bmp_data = f.read() # 获取像素数据偏移量(注意:这里不要加逗号!) offset = int.from_bytes(bmp_data[10:14], 'little') # 去掉末尾逗号 # 分离头部和像素数据 header = bmp_data[:offset] # 头部(包含文件头+信息头+调色板) body = bmp_data[offset:] # 实际的像素数据 # 补齐到16字节倍数(AES要求) if len(body) % 16 != 0: body += b"\x00" * (16 - len(body) % 16) # 用ECB模式加密图像数据 key = bytes.fromhex("00112233445566778899aabbccddeeff") cipher = AES.new(key, AES.MODE_ECB) encrypted_body = cipher.encrypt(body) # 组合并保存 ecb_bmp = header + encrypted_body output = filename.replace(".bmp", "-ecb.bmp") with open(output, "wb") as f: f.write(ecb_bmp) print(f"已生成: {output}") ECB后的图片轮廓仍清晰可见。对于渗透测试来说:如果你在一个应用里发现了ECB模式,(如:Cookie里相同16字节片段反复出现):你就可以:构造块替换攻击:把已知明文块对应的密文块填混到另一个位置越权。通过字节移动泄露明文(字节-字节攻击)电子密码本(ECB模式)的弱点:在加密时,将明文分成固定大小16(字节)的块,每块独立加密,相同明文块-->相同密文块,快之间没有任何关联。就像一本密码本,只要看到某个密文块,就知道它对应哪个明文块。攻击者可以任意替换、删除、重拍密文块,就知道它对应哪个明文块。ECB识别与块替换攻击演示:PYTHONfrom Crypto.Cipher import AES from Crypto.Util.Padding import pad,unpad import base64 # pad自动填充到16的倍数PKCS#7 # 密文用Base64编码方便传输。 KEY = b"0123456789abcdef" def encrypt_cookie(data): cipher = AES.new(KEY,AES.MODE_ECB) ct = cipher.encrypt(pad(data.encode(),16)) return base64.b64encode(ct).decode() def decrypt_cookie(cookie): ct = base64.b64decode(cookie) cipher = AES.new(KEY,AES.MODE_ECB) pt = unpad(cipher.decrypt(ct),16).decode() return pt # 注册用户“admin”,得到其cookie admin_cookie = encrypt_cookie("user=admin&role=user") admin_ct = base64.b64decode(admin_cookie) # 注册用户attacker attacker_cookie = encrypt_cookie("user=attacker&role=user") attacker_ct = base64.b64decode(attacker_cookie) # 分析块结构(打印16进制块) # 这段代码将密文按16字节(32个十六进制字符)打印,方便观察块边界。 # 明文块划分(关键):user=admin&role=user print("Admin密文块:") for i in range(0,len(admin_ct),16): print(f"块{i//16}:{admin_ct[i:i+16].hex()}") print("Attacker密文块:") for i in range(0,len(attacker_ct),16): print(f"块{i//16}:{attacker_ct[i:i+16].hex()}") # 识别重复块——ECB特征 blocks = [admin_ct[i:i+16] for i in range (0, len(admin_ct), 16)] for i, b in enumerate(blocks): for j,b2 in enumerate(blocks): if i < j and b == b2: print(f"\n[!]发现重复块:块{i} == 块{j} ->确认时ECB") # 构造越权cookie # 把admin的第二块(包含role信息)换到attacker的第二块 crafted_ct = attacker_ct[:16] + admin_ct[16:32] crafted_cookie = base64.b64encode(crafted_ct).decode() print(f"构造的Cookie:{crafted_cookie}") print(f"解密后:{decrypt_cookie(crafted_cookie)}")结果:SHELLSCRIPTAdmin密文块: 块0:6f8a05c7971cc285c7a9aa951d6b1635 块1:8f5adeb79e6020f2664c61e9cdd30816 Attacker密文块: 块0:c442f72b1166ac3eadb61fc68a4546a9 块1:0d9c28061a931af91156aa00e8e3ee80 构造的Cookie:xEL3KxFmrD6tth/GikVGqY9a3reeYCDyZkxh6c3TCBY= 解密后:user=attacker&rouser攻击者Cookie明文PYTHON"user=attacker&role=user"长度:23字节->需要填充到32字节(两块)填充值:\x09 * 9块内容块0user=attcker&ro块1lle=user+\x09*9管理员Cookie明文:PYTHON"user=admin&role=admin"长度:20字节->填充到32字节(2块)填充值:\x0c*12块内容块0user=admin&role=块1admin+\x0c*12操作PYTHONcrafted_ct = attacker_ct[:16] + admin_ct[16:32]保留攻击者的第一个密文块,替换管理员的第二个密文块给攻击者解密后:第一个块解密-->user=attacker&ro第二个块解密-->admin+\x0c*12结果:SHuser=admin&ro=admin通过填充调整,使role=出现在块1起始位置,然后替换块1位管理员的对应块(包含role=admin)部分。为什么不是role=admin因为在攻击者明文中,role=中的ro位于块0的末尾,le位于块1的开头。我们只替换了块1所以我替换的是le=user这一部分而不是整个role=user。所以结果就是块0开头的ro与块1的新内容admin拼成了roadmin,原本的le= 消失了那怎么办?方法一:构造攻击者的明文,使得role=完整地位于一个块的开头。PYTHON"user=attacker&x=role=user"方法二:同时替换两个块(更复杂的拼接)如果攻击者明文长度恰好使role=跨块,你需要同时替换两个块,才能完整换role=user-->role=admin会导致用户名也被改变所以必须精心设计攻击者明文,使user=和role=分开在不同的块中,且各自独立。2.3 CBC(密码分组链接模式)算法定义:首块需要使用随机切不可预测的初始化向量。工程缺陷:无完整性保障,攻击者可以通过位反转攻击(Bit-Flipping),精准操控解密后的明文特定比特位(修改金额、权限位)致命的侧信道:配置填充方案时。极易遭受填充语言攻击(Padding Oracle Attack)。2.3.1 什么是异或(XOR)一句话来说:“相同为0,不同为1”(针对二进制)0 ⊕ 0 = 0 1 ⊕ 1 = 0 0 ⊕ 1 = 1 1 ⊕ 0 = 12.3.2 异或(XOR)的数学性质(加密就靠它)性质公式人话自反性A⊕B⊕B = A同一个数异或两次,回到自己。(解密)可交换/结合顺序随便换多个异或可以任意调换顺序重点:加密时做一次 密文=明文⊕密钥,解密时再做一次明文= 密文⊕密钥,两次异或就还原了。1⊕1⊕1=1 明文⊕密钥=密文 密文⊕密钥=明文2.3.3 CBC模式与IV安全IV ⊕ P1 --> E(K) = C1IV和P1先混合一下,把混合后的结果送去AES加密,得到C1拆解:IV = 0x0F (十六进制,二进制00001111)P1 = 0xA5(二进制:10100101)AES加密先不管内部,把它看做一个黑盒子,输入什么输出什么(假设加密函数E(K)只是简单的把输入*2)第一步 IV ⊕ P11 0 1 0 0 1 0 1(P1) ⊕ 0 0 0 0 1 1 1 1(VI) --------------------------------- 1 0 1 0 1 0 1 0(结果=0xAA)第二步:把混合的结果送去加密函数E(K)E(K)的输入 = 0xAA假设加密函数(用密钥K)把输入乘以2:E(K,0xAA) = 0xAA * 2 = 0x154第三步:加密函数的输出就是密文块C1C1 = 0x154第四步:整个公式连起来看:-IV--⊕--P1---->--------E(K)---------=-----C1---- 0x0F ⊕ 0xA5 -->得到 0xAA -->送去加密-->得到了0x154 IV:随机种子 P1:明文数据 E(K): E()加密方法 如AES K是密钥 C1:加密后结果为啥要把这么简单事情搞这么复杂?如果不做异或,直接加密P1:同样的P1永远得到同样的C1(漏洞)攻击者能看出你发两次相同的指令做了异或后:只要IV变了,IV⊕P1的结果就变了。送进去加密函数的输入完全不同最终C1也不同漏洞点:IV必须不可预测。若攻击者可以在加密前控制IV(如CSRF配合加密接口),可构造选择明文攻击。IV固定甚至硬编码为0:导致相同前缀明文产生相同密文块,泄露信息。PYTHONfrom Crypto.Cipher import AES from Crypto.Util.Padding import pad key = b"0123456789abcdef" data = b"A" * 32 # 错误:固定IV bad_iv = b"\x00" * 16 cipher1 = AES.new(key,AES.MODE_CBC,iv=bad_iv) ct1 = cipher1.encrypt(pad(data, 16)) cipher2 = AES.new(key,AES.MODE_CBC,iv=bad_iv) ct2 = cipher2.encrypt(pad(data, 16)) print(f"两次加密结果相同:{ct1 == ct2}") # 正确:随机IV import os good_iv1 = os.urandom(16) good_iv2 = os.urandom(16) cipher3 = AES.new(key,AES.MODE_CBC,iv=good_iv1) ct3 = cipher3.encrypt(pad(data, 16)) cipher4 = AES.new(key,AES.MODE_CBC,iv=good_iv2) ct4 = cipher4.encrypt(pad(data, 16)) print(f"两次加密结果相同:{ct3 == ct4}")运行结果:两次加密结果相同:True 两次加密结果相同:FalseIV是什么?你可以把它想象成加密的“盐salt”或初始种子:它是一个随机数,在加密开始时使用长度固定(AES-CBC 是 16字节)不需要保密,可以随密文一起传输。为什么需要IV?如果没有IV或固定IV时:明文 "Hello" --> 加密 --> "abc123" 明文 "Hello" --> 加密 --> "abc123" (相同明文-->相同密文)攻击者看到两次相同密文,就知道你发了相同的内容。有随机IV时:明文 "Hello" --> 加密 --> "xyz789" 明文 "Hello" --> 加密 --> "def456" (相同明文-->不同密文)每次结果都不同,攻击者无法通过密文模式推断信息。随机IV作为密文的一部分,明文传输,解密时直接拿出来用。IV不是秘密,它只是扰乱工具,真正保密的是密钥K[16字节IV]+[密文C1]+[密文C2]+.......先读取前16字节-->得到IV用这个IV解第一块后续块用前一块密文(不需要额外传)PYTHONfrom Crypto.Cipher import AES from Crypto.Util.Padding import pad,unpad import os key = b"0123456789abcdef" data = b"Hello World! This is a secret." # 加密方 iv = os.urandom(16) # 随机生成IV cipher = AES.new(key, AES.MODE_CBC, iv = iv) ct = cipher.encrypt(pad(data,16)) # 发送给解密方: IV + 密文 message = iv + ct # 把密文拼接在前面 print(f"发送的数据长度:{len(message)}字节") # 解密方 # 从消息中拆出IV和密文 received_iv = message[:16] # 取前16字节 received_ct = message[16:] # 剩下的都是密文 # 用同一个IV解密 cipher2 = AES.new(key, AES.MODE_CBC, iv=received_iv) decrypted = unpad(cipher2.decrypt(received_ct),16) print(f"解密结果:{decrypted.decode()}")运行结果:SHELLSCRIPT发送的数据长度:48字节 解密结果:Hello World! This is a secret.CTR(计数器模式):算法定义:将计数器与随机数Nonce拼接,假面生成密钥流,再与明文异或。
前置知识
编码
哈希
加密
密钥
无
无
有
可逆性
可逆,任何人
不可逆
凭密钥可逆
输出长度
变长
固定
变长
目的
数据格式转换
完整性校验/指纹
机密性
编码
哈希
加密与密钥
定义:加密是将明文经密钥控制转换为不可读密文,密钥长度决定暴力破解难度。
一、算法与随机数分类:
二、对称加密
核心:模式决定安全性,而不是算法本身。
2.1 分组密码与填充
分组密码(AES):一次处理固定块(16字节)。不足的块要填充。
PKCS#7填充:缺N个字节就填N个 0xN。如块大小16数据15字节,补一个0x01;数据9字节,补7个0x07。
填充就是Padding Oracle攻击的根源。
2.2 ECB模式(电子密码本模式)
示例下载:https://cloud.komll.com/s/6RUN
这是ECB前的图片
ECB后的图片
轮廓仍清晰可见。
对于渗透测试来说:如果你在一个应用里发现了ECB模式,(如:Cookie里相同16字节片段反复出现):你就可以:
电子密码本(ECB模式)的弱点:在加密时,将明文分成固定大小16(字节)的块,每块独立加密,相同明文块-->相同密文块,快之间没有任何关联。就像一本密码本,只要看到某个密文块,就知道它对应哪个明文块。
攻击者可以任意替换、删除、重拍密文块,就知道它对应哪个明文块。
ECB识别与块替换攻击演示:
结果:
块
内容
块0
user=attcker&ro
块1
lle=user+\x09*9
块
内容
块0
user=admin&role=
块1
admin+\x0c*12
解密后:
结果:
通过填充调整,使role=出现在块1起始位置,然后替换块1位管理员的对应块(包含role=admin)部分。
为什么不是role=admin
因为在攻击者明文中,role=中的ro位于块0的末尾,le位于块1的开头。
我们只替换了块1所以我替换的是le=user这一部分而不是整个role=user。
所以结果就是块0开头的ro与块1的新内容admin拼成了roadmin,原本的le= 消失了
那怎么办?
方法一:
方法二:
同时替换两个块(更复杂的拼接)
如果攻击者明文长度恰好使role=跨块,你需要同时替换两个块,才能完整换role=user-->role=admin会导致用户名也被改变
所以必须精心设计攻击者明文,使user=和role=分开在不同的块中,且各自独立。
2.3 CBC(密码分组链接模式)
2.3.1 什么是异或(XOR)
一句话来说:“相同为0,不同为1”(针对二进制)
2.3.2 异或(XOR)的数学性质(加密就靠它)
性质
公式
人话
自反性
A⊕B⊕B = A
同一个数异或两次,回到自己。(解密)
可交换/结合
顺序随便换
多个异或可以任意调换顺序
重点:加密时做一次 密文=明文⊕密钥,解密时再做一次明文= 密文⊕密钥,两次异或就还原了。
2.3.3 CBC模式与IV安全
IV和P1先混合一下,把混合后的结果送去AES加密,得到C1
拆解:
IV = 0x0F (十六进制,二进制00001111)
P1 = 0xA5(二进制:10100101)
第一步 IV ⊕ P1
第二步:把混合的结果送去加密函数E(K)
E(K)的输入 = 0xAA
假设加密函数(用密钥K)把输入乘以2:
第三步:加密函数的输出就是密文块C1
第四步:整个公式连起来看:
为啥要把这么简单事情搞这么复杂?
如果不做异或,直接加密P1:
做了异或后:
漏洞点:
运行结果:
IV是什么?
为什么需要IV?
如果没有IV或固定IV时:
攻击者看到两次相同密文,就知道你发了相同的内容。
有随机IV时:
每次结果都不同,攻击者无法通过密文模式推断信息。
随机IV作为密文的一部分,明文传输,解密时直接拿出来用。
IV不是秘密,它只是扰乱工具,真正保密的是密钥K
运行结果:
CTR(计数器模式):