1. pyCryptodome庫
pip3 install pyCryptodome -i https://pypi.douban.com/simple
這個庫是實現各種算法和協議的密碼模塊的集合,包含Cipher,Hash,Protocol,PublicKey,Signature,Util這些子包;
1.1. Cipher子包
實現了分組加密(AES,DES,DES3,CAST,Blowfish,RC2),流加密(RC4,XOR)與公鑰加密(RSA PKCS#1與PKCS#1 OAEP,這兩個區別在於加密前對數據的填充不同);如下是文檔給出的例子:
from Crypto.Cipher import AES
from Crypto import Random
key = b'Sixteen byte key'
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CFB, iv)
msg = iv + cipher.encrypt(b'Attack at dawn')
print msg.encode('hex')
有時在解RSA的題時,已經知道了密鑰,解出來的flag卻是亂碼,也許是加了一些填充如OAEP,PKCS#1導致的;這時就需要使用對應的秘鑰解密;如下是一個PKCS#1填充的例子,加密的明文(字節)長度必須嚴格小於密鑰(字節)長度-11
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from gmpy2 import invert
from base64 import b64decode
cip='bT633yPu4dOHEL66eKCHjg6cZb09CElt2mSSQZkRDHk='
n=87924348264132406875276140514499937145050893665602592992418171647042491658461L
e=65537L
#http://factordb.com/
p=275127860351348928173285174381581152299L
q=319576316814478949870590164193048041239L
phi=(p-1)*(q-1)
d=invert(e,phi)
privkey=RSA.construct((n,e,long(d),p,q))
#原生RSA
print privkey.decrypt(b64decode(cip))
#PKCS#1填充
key= PKCS1_v1_5.new(privkey)
print key.decrypt(b64decode(cip),'')
再舉個OAEP填充的例子,加密的明文(字節)長度必須嚴格小於密鑰(字節)長度減去41;你可以試一下在下面代碼中的明文中多加一個字符運行就會報錯。
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from base64 import b64encode,b64decode
rsa_key=RSA.generate(1024)
key=PKCS1_OAEP.new(rsa_key)
cip=b64encode(key.encrypt('PCTF{256b_i5_m3dium}PCTF{256b_i5_m3dium}PCTF{256b_i5_m3dium}PCTF{256b_i5_m3dium}abcdef'))
print cip
msg=key.decrypt(b64decode(cip))
print msg
1.2. Hash子包
實現了哈希算法(MD2,MD4,MD5,RIPEMD,SHA,SHA224,SHA256,SHA384,SHA512,HMAC,hashalgo);如下是文檔給出的例子:
from Crypto.Hash import MD2
h = MD2.new()
h.update(b'Hello')
print h.hexdigest()
但是沒有實現SHA1,要使用SHA1,需要導入hashlib庫,正好驗證一下Google之前給出的求得兩個 sha1 值一樣的pdf對:https://shattered.io/
from hashlib import sha1
with open('shattered-1.pdf','rb') as f1:
da1=f1.read()
with open('shattered-2.pdf','rb') as f2:
da2=f2.read()
print sha1(da1).hexdigest()
print sha1(da2).hexdigest()
1.3. PublicKey子包
實現了公鑰加密和簽名算法(RSA,DSA,ElGamal);文檔給出的例子:
from Crypto.PublicKey import RSA
key0 = RSA.generate(2048)
with open('mykey.pem','w') as f:
f.write(key0.exportKey('PEM'))
with open('mykey.pem','r') as g:
key = RSA.importKey(g.read())
1.4. Util子包
實現了各種有用的模塊和功能(Util.number:數論函數,Util.randpool:隨機數生成,Util.RFC1751:在128位鍵和可讀的字串之間進行轉換,Util.asn1:對ASN.1 DER編碼的最小支持);自己寫個例子如下:
from Crypto.Util import number
s='this is a demo'
#字節轉換為long型整數
ls=number.bytes_to_long(s)
bits=8*len(s)
#生成長度為bits的素數
gp=number.getPrime(bits)
#生成長度小於bits的隨機數
gri=number.getRandomInteger(bits)
#生成長度為bits的隨機數
grnbi=number.getRandomNBitInteger(bits)
#生成長度不超過bits的隨機數
grn=number.getRandomNumber(bits)
#生成grn與2*grn之間的隨機數
grr=number.getRandomRange(grn,2*grn)
#生成強的素數(gsp-1,gsp+1均至少具有一個大的素因子)
gsp=number.getStrongPrime(1024)
#計算gri在模grn下的逆
iin=number.inverse(gri,grn)
#判斷iin是否為素數
ip=number.isPrime(iin)
#long型整數轉換為字節
tb=number.long_to_bytes(ls)
由於這個庫很多函數都很有用,因此我們直接使用
from Crypto.Util.number import *
來導入這個包的所有函數。
2. pwntools庫
pip3 install pwntools -i https://pypi.douban.com/simple
這個庫內容很多,只介紹比賽中用到的。
2.1. mbruteforce函數
多線程窮舉函數,詳細定義如下:mbruteforce(func, alphabet, length, method='upto', start=None, threads=None),其中,
- func輸入參數為字符串,輸出布爾值,mbruteforce窮舉直到func輸出True;
- alphabet為組成輸入參數字符串的字符集合;
- length指定輸入參數字符串的長度上界;
- method默認為'upto',指定窮舉的字符串長度從1增大到length;另外兩個選項為'fixed'、'downfrom',fixed'指定窮舉的字符串長度僅為ength,'downfrom'指定窮舉的字符串長度從length減小到1;
- start=(N,i),就是把搜索空間分成N塊從第i塊開始窮舉;默認為(1,1)
- threads指定窮舉時的線程數,默認值是內核的數量;
舉個例子:
from pwn import pwnlib
from pwnlib.util.iters import mbruteforce
mbruteforce(lambda x: x == 'hello','helo',5,method='fixed')
2.2. remote類
用來與服務器交互;常用函數的用法注釋到如下腳本中。
from pwn import pwnlib
from pwnlib.tubes.remote import remote
#創建到遠程主機的TCP或udp連接,主機為cn.bing.com,連接主機的端口為443
#ssl=True代表用SSL包裝套接字
r=remote('cn.bing.com',443,ssl=True)
#向主機發送數據,只不過數據只能是一行
r.sendline('GET /')
#向主機發送數據,數據可以是多行
r.send(b'\r\n\r\n')
##上面兩行代碼等價於r.send(b'GET /\r\n\r\n')
#從主機接收4個字節的數據
re=r.recvn(4)
print re
#從主機一直接收數據直到'Please'出現
re=r.recvuntil('Please')
print re
#從主機一直接收數據,最多接收4096字節
re=r.recv()
print re
#關閉連接
r.close()