### 第一次实验(0930)
**任务1**:题目给出一段 base64编码,该编码来自一段由秘钥重复异或明文所得的密文。且给出了秘钥的长度范围。要求解出对应明文。
原理:可见 [Challenge 6 Set 1 - The Cryptopals Crypto Challenges](https://cryptopals.com/sets/1/challenges/6)
代码:
```python
import re
import base64
with open("challenge6.txt","r") as fp:
wenben=[base64.b64decode(i) for i in fp.readlines()]
wenben="".join(wenben)
def english_test(sentence):
score = 0
freqs = {
'a': 0.0651738, 'b': 0.0124248, 'c': 0.0217339,
'd': 0.0349835, 'e': 0.1041442, 'f': 0.0197881,
'g': 0.0158610, 'h': 0.0492888, 'i': 0.0558094,
'j': 0.0009033, 'k': 0.0050529, 'l': 0.0331490,
'm': 0.0202124, 'n': 0.0564513, 'o': 0.0596302,
'p': 0.0137645, 'q': 0.0008606, 'r': 0.0497563,
's': 0.0515760, 't': 0.0729357, 'u': 0.0225134,
'v': 0.0082903, 'w': 0.0171272, 'x': 0.0013692,
'y': 0.0145984, 'z': 0.0007836, ' ': 0.1918182}
for x in sentence.lower():
if x in freqs:
score += freqs[x]
return score
def hanming(x,y):
num=0
for i in range(0,len(x)):
t=ord(x[i])^ord(y[i])
while t:
if t&1 : num+=1
t>>=1
return num
def thechar(st1):
score = 0
for i in range(0, 255):
tmp = []
for j in range(0,len(st1)):
tmp += chr(i ^ int(st1[j],16))
tmpstr = "".join(tmp)
num=english_test(tmpstr)
if num > score:
score = num
key = chr(i)
return key
ans = []
for i in range(1,41):
str1=[]
str2=[]
str3=[]
str4=[]
for j in range(0,i): str1+=[wenben[j]]
for j in range(i,2*i): str2+=[wenben[j]]
for j in range(2*i,3*i): str3+=[wenben[j]]
for j in range(3*i,4*i): str4+=[wenben[j]]
str1="".join(str1)
str2="".join(str2)
str3="".join(str3)
str4="".join(str4)
x1=float(hanming(str1,str2))/i
x2=float(hanming(str2,str3))/i
x3=float(hanming(str3,str4))/i
x4=float(hanming(str1,str4))/i
x5=float(hanming(str1,str3))/i
x6=float(hanming(str2,str4))/i
aa=(x1+x2+x3+x4+x5+x6)/6
ans+=[(i,aa)]
ans.sort(lambda x,y:cmp(x[1],y[1]))
for i in range(len(ans)):
print ans[i][0],ans[i][1]
wenben=wenben.encode('hex')
block=[re.findall(r'(.{2})',z) for z in re.findall(r'(.{58})',wenben)]
keyy = []
for i in range(0,29):
tmp=[]
for j in range(0,len(block)):
tmp+=[block[j][i]]
keyy+=[thechar(tmp)]
keyy="".join(keyy)
print keyy
keyy=keyy*10000
wenben=wenben.decode('hex')
an=[]
for i in range(0,len(wenben)):
an+=[chr(ord(wenben[i])^ord(keyy[i]))]
an="".join(an)
print an
# 5465726d696e61746f7220583a204272696e6720746865206e6f697365
'''I'm back and I'm ringin' the bell
A rockin' on the mike while the fly girls yell
In ecstasy in the back of me
Well that's my DJ Deshay cuttin' all them Z's
Hittin' hard and the girlies goin' crazy
Vanilla's on the mike, man I'm not lazy.
I'm lettin' my drug kick in
It controls my mouth and I begin
To just let it flow, let my concepts go
My posse's to the side yellin', Go Vanilla Go!
Smooth 'cause that's the way I will be
And if you don't give a damn, then
Why you starin' at me
So get off 'cause I control the stage
There's no dissin' allowed
I'm in my own phase
The girlies sa y they love me and that is ok
And I can dance better than any kid n' play
Stage 2 -- Yea the one ya' wanna listen to
It's off my head so let the beat play through
So I can funk it up and make it sound good
1-2-3 Yo -- Knock on some wood
For good luck, I like my rhymes atrocious
Supercalafragilisticexpialidocious
I'm an effect and that you can bet
I can take a fly girl and make her wet.
I'm like Samson -- Samson to Delilah
There's no denyin', You can try to hang
But you'll keep tryin' to get my style
Over and over, practice makes perfect
But not if you're a loafer.
You'll get nowhere, no place, no time, no girls
Soon -- Oh my God, homebody, you probably eat
Spaghetti with a spoon! Come on and say it!
VIP. Vanilla Ice yep, yep, I'm comin' hard like a rhino
Intoxicating so you stagger like a wino
So punks stop trying and girl stop cryin'
Vanilla Ice is sellin' and you people are buyin'
'Cause why the freaks are jockin' like Crazy Glue
Movin' and groovin' trying to sing along
All through the ghetto groovin' this here song
Now you're amazed by the VIP posse.
Steppin' so hard like a German Nazi
Startled by the bases hittin' ground
There's no trippin' on mine, I'm just gettin' down
Sparkamatic, I'm hangin' tight like a fanatic
You trapped me once and I thought that
You might have it
So step down and lend me your ear
'89 in my time! You, '90 is my year.
You're weakenin' fast, YO! and I can tell it
Your body's gettin' hot, so, so I can smell it
So don't be mad and don't be sad
'Cause the lyrics belong to ICE, You can call me Dad
You're pitchin' a fit, so step back and endure
Let the witch doctor, Ice, do the dance to cure
So come up close and don't be square
You wanna battle me -- Anytime, anywhere
You thought that I was weak, Boy, you're dead wrong
So come on, everybody and sing this song
Say -- Play that funky music Say, go white boy, go white boy go
play that funky music Go white boy, go white boy, go
Lay down and boogie and play that funky music till you die.
Play that funky music Come on, Come on, let me hear
Play that funky music white boy you say it, say it
Play that funky music A little louder now
Play that funky music, white boy Come on, Come on, Come on
Play that funky music '''
```
**任务2**:题目给出用户输入密码时使用到的按键,给出密码长度及密码经 SHA1 哈希的结果。要求解出用户的密码。
原理:爆破。
代码:
![[Pasted image 20220109152907.png]]
### 第二次实验(1024)
**任务1** :题目指定了一个服务器,该服务器尝试解密收到的任意密文,并返回解密成功或失败。题目给出了一段密文,要求解出对应的明文。
原理:Padding oracle attack
代码:
```python
from oracale_for_py3 import *
import sys
cipher = bytes.fromhex("9F0B13944841A832B2421B9EAF6D9836813EC9D944A5C8347A7CA69AA34D8DC0DF70E343C4000A2AE35874CE75E64C31")
cipher_len = len(cipher)
size = 16
block = [cipher[i:i+size] for i in range(0, cipher_len, size)]
def set_dummy_byte(dummy, i, j):
dummy = dummy[::-1]
dummy = dummy[:i] + bytes([j]) + dummy[i+1:]
dummy = dummy[::-1]
return dummy
def decry(begin, iv):
result = []
for i in begin:
result += i
result_byte = bytes(result)[::-1]
result_str = ''
for i in range(len(iv)):
result_str += chr(iv[i]^result_byte[i])
return result_str
def main():
plain = ''
for z in range(1,len(block)):
check = []
dummy = bytes([0])*size
padding = 1
for i in range(0, size):
tmp = []
print("processing", i, "bit")
for j in range(0, 0x100):
dummy = set_dummy_byte(dummy, i, j)
fake = dummy + block[z]
flag = 0
while (flag != 1):
try:
Oracle_Connect()
ret = Oracle_Send(fake, 2)
Oracle_Disconnect()
except Exception:
print('.',end='')
continue
else:
flag = 1
if chr(ret) == '1':
print('right: ', hex(j), '<===')
target = j ^ padding
tmp.append(target)
break
elif chr(ret) == '0':
pass
else:
print('error: ', hex(j))
input()
check.append(tmp)
padding += 1
print('padding:',padding)
for k in range(i+1):
dummy_byte_next = check[k][0] ^ padding
if len(check[k]) == 0:
print('no match')
elif len(check[k]) == 1:
dummy = set_dummy_byte(dummy, k, dummy_byte_next)
else:
print('more than one match')
input()
plain += decry(check,block[z-1])
print(plain)
if __name__ == '__main__':
main()
```
![[Pasted image 20220109153758.png]]
**任务2**:题目给出一个神谕 `AES-128-ECB(random-prefix || attacker-controlled || target-bytes, random-key)`,要求解出 target-bytes。
原理:根据 ECB 模式加密中各块相互独立的特点,进行枚举和比较解出 target-bytes。
代码:
```python
key = bytes(random_key(16))
random_prefix = random_key(randint(0, 256))
def encryption_oracle(data):
unknown_string = bytearray((
"Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg\n" +
"aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq\n" +
"dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg\n" +
"YnkK"
).decode("base64"))
plaintext = pad_pkcs7(
random_prefix + data + unknown_string,
AES.block_size,
)
return aes_128_ecb_enc(plaintext, key)
def get_prefix_size(oracle, block_size):
for prefix_padding_size in range(block_size):
reps = 10
prefix_padding = bytearray("A" * prefix_padding_size)
buffer = oracle(prefix_padding + bytearray("YELLOW SUBMARINE" * reps))
prev_block = count = index = None
for i in range(0, len(buffer), block_size):
block = buffer[i: i + block_size]
if block == prev_block:
count += 1
else:
index = i
prev_block = block
count = 1
if count == reps:
return index, prefix_padding_size
def get_unknown_string(oracle):
block_size = get_block_size(oracle)
prefix_size_rounded, prefix_padding_size = get_prefix_size(oracle, block_size)
unknown_string_size = (
get_unknown_string_size(oracle) -
prefix_size_rounded -
prefix_padding_size
)
unknown_string = bytearray()
unknown_string_size_rounded = (
((unknown_string_size / block_size) + 1) *
block_size
)
for i in range(unknown_string_size_rounded - 1, 0, -1):
d1 = bytearray("A" * (i + prefix_padding_size))
c1 = oracle(d1)[
prefix_size_rounded:
unknown_string_size_rounded + prefix_size_rounded
]
for c in range(256):
d2 = d1[:] + unknown_string + chr(c)
c2 = oracle(d2)[
prefix_size_rounded:
unknown_string_size_rounded + prefix_size_rounded
]
if c1 == c2:
unknown_string += chr(c)
break
return unknown_string
get_unknown_string(encryption_oracle)
# b"Rollin' in my 5.0\nWith my rag-top down so my hair can blow\nThe girlies on standby waving just to say hi\nDid you stop? No, I just drove by\n"
```
**任务3**:实现 padding 检查算法。
原理:padding 内容与长度的特点。
代码:
```python
def pkcs7_padding_validation(byte_string: bytes)->bytes:
last_byte = byte_string[-1]
if last_byte > len(byte_string):
return ValueError("bad padding")
for i in range(last_byte, 0, -1):
if byte_string[-i] != last_byte:
raise ValueError("bad padding")
return byte_string[:-last_byte]
pkcs7_padding_validation(b"ICE ICE BABY\x04\x04\x04\x04")
# pkcs7_padding_validation(b"ICE ICE BABY\x05\x05\x05\x05")
pkcs7_padding_validation(b"ICE ICE BABY\x01\x02\x03\x04")
```
![[Pasted image 20220109163652.png]]
**任务4**:题目给定明文格式,要求修改 comment2 的值。
原理:CBC 比特翻转攻击。
代码:
```python
from Crypto.Cipher import AES
from os import urandom
BLOCK_SIZE = 16
KEY_SIZE = 16
IV_SIZE = 16
key = urandom(KEY_SIZE)
iv = urandom(IV_SIZE)
prefix = b"comment1=cooking%20MCs;userdata="
suffix = b";comment2=%20like%20a%20pound%20of%20bacon"
def filter_and_pad(pt):
pt = pt.replace(b";",b"%").replace(b"=",b"%")
return prefix + pt + suffix
def PKCS7_padding(s):
if len(s)%BLOCK_SIZE != 0:
return s + chr((BLOCK_SIZE*(len(s)//BLOCK_SIZE)+BLOCK_SIZE)-len(s)).encode()*((BLOCK_SIZE*(len(s)//BLOCK_SIZE + 1))-len(s))
return s
def encrypt(pt):
pt = PKCS7_padding(pt)
aes = AES.new(key, AES.MODE_CBC, iv)
ct = aes.encrypt(pt)
return ct
def cbc_decrypt(ct):
aes = AES.new(key, AES.MODE_CBC, iv)
dec = aes.decrypt(ct)
if b";admin=true;" in dec:
return True
return False
def CBC_bitflipping_attack(ct):
semicolon = ct[len(prefix)-16] ^ ord("%") ^ ord(";")
equals = ct[len(prefix)-10] ^ ord("%") ^ ord("=")
return ct[:len(prefix) - 16] + bytes([semicolon]) + ct[len(prefix)-15:len(prefix) - 10] + bytes([equals]) + ct[len(prefix) - 9:]
def main():
pt = b";admin=true"
ct = encrypt(filter_and_pad(pt))
alt_ct = (CBC_bitflipping_attack(ct))
print (cbc_decrypt(alt_ct))
if __name__ == "__main__":
main()
```
![[Pasted image 20220109165317.png]]
### 第三次实验
**任务3**:解决2016全国高校密码数学挑战赛赛题三。Alice 使用某 RSA 加密软件发送密文,题目给出被截获的 21 个加密帧数据,加密帧数据的格式也被给出。要求尽可能多地解出 Alice 发送的明文。
原理:尝试使用常见的 RSA 攻击方式解出明文,如共模攻击、广播攻击、公因数攻击和 p - 1 分解。
代码:
```python
from Crypto.Util.number import inverse
def itos(num):
string = ''
for i in range(8):
string += chr(num % 256)
num >>= 8
string = string[::-1]
num >>= 352
ret = num & ((1 << 32) - 1)
return ret, string
def gcd(a, b):
if b == 0:
return a
return gcd(b, a % b)
def extend_gcd(a, b):
if b == 0:
return 1, 0
s1, s2 = extend_gcd(b, a % b)
ret = s1 - a // b * s2
return s2, ret
def common_modulus_attack(n, e1, c1, e2, c2):
s1, s2 = extend_gcd(e1, e2)
# print(x,y)
if s1 < 0:
s1 = -s1
c1 = inverse(c1, n)
if s2 < 0:
s2 = -s2
c2 = inverse(c2, n)
ret = pow(c1, s1, n) * pow(c2, s2, n) % n
return ret
def common_factor_attack(n1, e1, c1, n2, e2, c2):
p = gcd(n1, n2)
q1 = n1 // p
q2 = n2 // p
phi1 = (p - 1) * (q1 - 1)
phi2 = (p - 1) * (q2 - 1)
d1 = inverse(e1, phi1)
d2 = inverse(e2, phi2)
ret1 = pow(c1, d1, n1)
ret2 = pow(c2, d2, n2)
return ret1, ret2
def chinese_remainder_theorem(a, m):
M = 1
for i in m:
M *= i
ret = 0
for i in range(len(m)):
ret = (ret + a[i] * M // m[i] * inverse(M // m[i], m[i])) % M
return ret
def broadcast_attack(a, m, e):
c = chinese_remainder_theorem(a, m)
l = 1
r = c
while l + 1 < r:
md = (l + r) // 2
if md ** e < c:
l = md
else:
r = md
if l ** e == c:
return l
if r ** e == c:
return r
return 0
def pollard_p1(n, e, c, b):
k = 1
for i in range(b):
k *= i + 1
p = gcd(pow(2, k, n) - 1, n)
if p == 1 or p == n:
return 0
q = n // p
if p * q != n:
return 0
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
return pow(c, d, n)
def attack():
n = []
e = []
c = []
# 处理加密帧格式
for i in range(21):
f = open('./Frame' + str(i))
s = f.read()
n.append(int(s[:256], 16))
e.append(int(s[256:512], 16))
c.append(int(s[512:], 16))
m = [0 for i in range(21)]
# 共模攻击
print("尝试共模攻击..")
for i in range(21):
for j in range(i):
if n[i] == n[j]:
m[i] = common_modulus_attack(n[i], e[i], c[i], e[j], c[j])
m[j] = m[i]
print(i, j, "=>", itos(m[i])[1])
# 广播攻击
print("尝试广播攻击..")
index = [3, 8, 12, 16, 20]
temp = broadcast_attack([c[i] for i in index], [n[i] for i in index], 5)
for i in index:
m[i] = temp
print(index, "=>", itos(temp)[1])
# 公因数攻击
print("尝试公因数攻击..")
for i in range(21):
for j in range(i):
if gcd(n[i], n[j]) > 1 and n[i] != n[j]:
m[i], m[j] = common_factor_attack(n[i], e[i], c[i], n[j], e[j], c[j])
print(i, "=>", itos(m[i])[1])
print(j, "=>", itos(m[j])[1])
# Pollard p-1 分解
print("尝试 Pollard p-1 分解..")
for i in range(1, 21):
if m[i] > 0:
continue
temp = pollard_p1(n[i], e[i], c[i], 10000)
if temp > 0:
print(i, "=>", itos(temp)[1])
if __name__ == '__main__':
attack()
```