古典密码
base64
base64_table = ['=','A', 'B', 'C', 'D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0', '1', '2', '3','4','5','6','7','8','9','+', '/'][::-1]
def encode_b64(s):
l = len(s)
i = 0
result = ''
while i < l:
# 将字符转换为二进制编码,然后对齐
s1 = s[i]
b1 = bin(ord(s1))[2:]
cb1 = b1.rjust(8, '0')
i += 1
if i >= l:
cb2 = '00000000'
else:
s2 = s[i]
b2 = bin(ord(s2))[2:]
cb2 = b2.rjust(8, '0')
i += 1
if i >= l:
cb3 = '00000000'
else:
s3 = s[i]
b3 = bin(ord(s3))[2:]
cb3 = b3.rjust(8, '0')
# 将三字节转换为四字节
cb = cb1 + cb2 + cb3
rb1 = cb[:6]
rb2 = cb[6:12]
rb3 = cb[12:18]
rb4 = cb[18:]
# 转换后的编码转为十进制备用
ri1 = int(rb1, 2)
ri2 = int(rb2, 2)
ri3 = int(rb3, 2)
ri4 = int(rb4, 2)
# 处理末尾为0的情况,以'='填充
if i - 1 >= l and ri3 == 0:
ri3 = -1
if i >= l and ri4 == 0:
ri4 = -1
result += base64_table[ri1] + base64_table[ri2] + base64_table[ri3] + base64_table[ri4]
i += 1
return result
print encode_b64(open("flag","r").read())
#output: mZOemISXmpOTkKCHkp6Rgv==
output是一个类base64编码,但是编码函数中的表的顺序换了,将顺序恢复就行了。
from base64 import b64decode
base64_table = ['=','A', 'B', 'C', 'D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0', '1', '2', '3','4','5','6','7','8','9','+', '/'][::-1]
# print base64_table
old_base64_table = ['A', 'B', 'C', 'D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0', '1', '2', '3','4','5','6','7','8','9','+', '/','=']
s=""
output= 'mZOemISXmpOTkKCHkp6Rgv=='
for i in output:
s+=old_base64_table[base64_table.index(i)]
print s.decode("base64")
caesar
def caesar_encrypt(m,k):
r=""
for i in m:
r+=chr((ord(i)+k)%128)
return r
from secret import m,k
print caesar_encrypt(m,k).encode("base64")
#output:bXNobgJyaHB6aHRwdGgE
凯撒密码,爆破一下,k为7时候得到flag
def caesar_decrypt(m,k):
r=""
for i in m:
r+=chr((ord(i)-k)%128)
return r
output="bXNobgJyaHB6aHRwdGgE"
t=output.decode("base64")
# for i in range(10):
# print caesar_decrypt(t,i)
print caesar_decrypt(t,7)
分组密码
分组密码(block cipher)的数学模型是将明文消息编码表示后的数字(简称明文数字)序列,划分成长度为n的组,每组分别在密钥的控制下变换成等长的密文。
cbc加密图解
CBC字节翻转攻击
from Crypto.Cipher import AES
m="hahahahahahahaha=1;admin=0;uid=1"
key="1234567890abcdef"
iv="fedcba0987654321"
cipher = AES.new(key, AES.MODE_CBC, iv)
c=cipher.encrypt(m)
print c.encode("hex")
#49a98685a527bdfa4077c400963a4e3c9effb4148566f10bce9e07ccbb731896
第二组的第10个字符的明文异或过第一组的第10个字符的密文解密的时候会先AES decrypt,然后和第一组的密文异或,得到第二组的明文。通过通过修改第一组的密文就可以控制第二组的明文,把admin=0改成admin=1。
Dec(C2)[i]=C1[i]^M2[i]
• 目标:M2[i]->X
• M2[i]= Dec(C2)[i]^C1[i]
• X= Dec(C2)[i]^(C1[i]^X^M2[i])
• 在第一个分组对应位置上异或上X和M2[i]即可
chr(ord(m[position[i]])^ord(target[i])^ord(c[change]))
例题xbitf
import random
import time
flag=open("/root/xbitf/flag","r").read()
from Crypto.Cipher import AES
import os
def aes_cbc(key,iv,m):
handler=AES.new(key,AES.MODE_CBC,iv)
return handler.encrypt(m).encode("hex")
def aes_cbc_dec(key,iv,c):
handler=AES.new(key,AES.MODE_CBC,iv)
return handler.decrypt(c.decode("hex"))
key=os.urandom(16)
iv=os.urandom(16)
session=os.urandom(8)
token="session="+session.encode("hex")+";admin=0"
checksum=aes_cbc(key,iv,token)
print token+";checksum="+checksum
for i in range(10):
token_rcv=raw_input("token:")
if token_rcv.split("admin=")[1][0]=='1' and token_rcv.split("session=")[1][0:16].decode("hex")==session:
c_rcv=token_rcv.split("checksum=")[1].strip()
m_rcv=aes_cbc_dec(key,iv,c_rcv)
print m_rcv
if m_rcv.split("admin=")[1][0]=='1':
print flag
from pwn import *
p = remote('47.97.215.88',10000)
p.recvuntil("session=")
session = p.recvuntil(";",drop=True)
p.recvuntil("checksum=")
checksum = p.recvuntil("\n",drop=True)
c1de=c1.decode('hex')
t=ord(c1de[15])^ord('1')^ord('0')
cks = checksum[:30]+chr(t).encode('hex')+checksum[32:]
token = "session="+session+";admin=1;checksum="+cks
p.recvuntil("token:")
p.sendline(token)
p.interactive()
CBC选择密文攻击
通过CBC模式的选择密文攻击,可以很快地恢复出IV,明文每次加密前会和IV异或,IV每组会更新为上一组的密文
当待解密的密文为:C|C时
Decrypt(C)^C=M1 和 Decrypt(C)^IV=M0
综合上面两个信息的话, Decrypt(C)^C^Decrypt(C)^IV=M1^M0
所以 IV=M1^M0^C
也就是说如果我们能够通过一个oracle或者其他方式得到C|C这种模式的密文的话,就可以逆推出IV
例题xcc
import random
import time
flag=open("/root/xcc/flag","r").read()
from Crypto.Cipher import AES
import os
def aes_cbc(key,iv,m):
handler=AES.new(key,AES.MODE_CBC,iv)
return handler.encrypt(m).encode("hex")
def aes_cbc_dec(key,iv,c):
handler=AES.new(key,AES.MODE_CBC,iv)
return handler.decrypt(c.decode("hex"))
key=os.urandom(16)
iv=flag
for i in range(10):
c=raw_input("c:")
print aes_cbc_dec(key,iv,c).encode("hex")
构造密文32个A,让两组密文相同。得到解密后的明文分成m0和m1,M1^M0^C异或时先转成int,最后得到IV
from pwn import *
from Crypto.Util.number import long_to_bytes,bytes_to_long
p = remote("47.97.215.88",10001)
p.recvuntil('c:')
print ("A"*32)
print (("A"*32).encode('hex'))
p.sendline(("A"*32).encode('hex'))
c = p.recv(64)
print c
m0 = c[:32]
m1 = c[32:]
print "btol"
print bytes_to_long(m0.decode('hex'))
print bytes_to_long(m1.decode('hex'))
print bytes_to_long("A"*16)
iv = bytes_to_long(m0.decode('hex'))^bytes_to_long(m1.decode('hex'))^bytes_to_long("A"*16)
print long_to_bytes(iv)
p.interactive()
padding oracle攻击
公钥密码
格式转化:将字符串转化成数字
#python2
#数字转字符串
num=584734024210391580014049650557280915516226103165
print hex(num)[2:].decode("hex") #看长度是否为偶数
print hex(num)[2:-1].decode("hex") #flag{this_is_a_flag}
#字符串转数字
s="flag{this_is_a_flag}"
print int(s.encode("hex"),16) #584734024210391580014049650557280915516226103165
#python3
from libnum import n2s,s2n
from Crypto.Util.number import long_to_bytes,bytes_to_long
#数字转字符串
print long_to_bytes(m)
print n2s(m)
#字符串转数字
print bytes_to_long(x)
print s2n(x)
直接模数分解
rsa的数学难题:分解大数n
私钥额外的知识:知道p和q(phi(n))
当N的长度较小,可直接分解
题目已知N,e,c
n=3161262255255421133292506694323988711204792818702640666084331634444148712428915950639954540974469931426618702044672318134908678730641981414037034058320359158246813987154679178159391832232990193738454116371045928434239936027006539348488316754611586659587677659791620481200732564068367148541242426533823626586574915275209508300120574819113851895932912208783915652764568319771482309338434364094681579135086703127977870534715039005822312878739611630155714313119545610939253355808742646891815442758660278514976431521933763272615653261044607041876212998883732724662410197038419721773290601109065965674129599626151139566369
e=65537
c=631583911592660652215412683088688785438938386403323323131247534561958531288570612134139288090533619548876156447498627938626419617968918299212863936839701943643735437264304062828205809984533592547599060829451668240569384130130080928292082888526567902695707215660020201392640388518379063244487204881439591813398495285025704285781072987024698133147354238702861803146548057736756003294248791827782280722670457157385205787259979804892966529536902959813675537028879407802365439024711942091123058305460856676910458268097798532901040050506906141547909766093323197363034959926900440420805768716029052885452560625308314284406
用yafu直接分解N,得到p,q
import gmpy2
from Crypto.Util.number import long_to_bytes,bytes_to_long
e=65537
c=631583911592660652215412683088688785438938386403323323131247534561958531288570612134139288090533619548876156447498627938626419617968918299212863936839701943643735437264304062828205809984533592547599060829451668240569384130130080928292082888526567902695707215660020201392640388518379063244487204881439591813398495285025704285781072987024698133147354238702861803146548057736756003294248791827782280722670457157385205787259979804892966529536902959813675537028879407802365439024711942091123058305460856676910458268097798532901040050506906141547909766093323197363034959926900440420805768716029052885452560625308314284406
p=56225103425920179745019828423382255030086226600783237398582720244250840205090747144995470046432814267877822949968612053620215667790366338413979256357713975498764498045710766375614107934719809398451422359883451257033337168560937824719275885709824193760523306327217910106187213556299122895037021898556005848927
q=56225103425920179745019828423382255030086226600783237398582720244250840205090747144995470046432814267877822949968612053620215667790366338413979256357713975498764498045710766375614107934719809398451422359883451257033337168560937824719275885709824193760523306327217910106187213556299122895037021898556005848447
N=p*q
phin = (p - 1) * (q - 1)
d = gmpy2.invert(e, phin)
print "d",d#137443939677632407697822373106774146170757453506013404320961
flag = gmpy2.powmod(c, d, N)
print hex(flag)[2:].decode('hex')
共模攻击
识别:两组及以上的RSA加密过程,而且其中两次的m和n都是相同的,但是e不同,那么就可以使用共模攻击
c1 = m^e1 mod N
c2 = m^e2 mod N
r*e1 + s*e2 = 1 mod N #拓展欧几里得算法
c1^r*c2^s = m^(r*e1)*m^(s*e2) mod N
= m^(r*e1+s*e2) mod N
= m mod N
例题xgm:已知n1,e1,c1,n2,e2,c2。
from Crypto.Util.number import long_to_bytes,bytes_to_long,getPrime,isPrime
import primefac
def same_n_sttack(n,e1,e2,c1,c2):
def egcd(a, b):
x, lastX = 0, 1
y, lastY = 1, 0
while (b != 0):
q = a // b
a, b = b, a % b
x, lastX = lastX - q * x, x
y, lastY = lastY - q * y, y
return (lastX, lastY)
s = egcd(e1, e2)
s1 = s[0]
s2 = s[1]
if s1<0:
s1 = - s1
c1 = primefac.modinv(c1, n)
if c1<0:
c1+=n
elif s2<0:
s2 = - s2
c2 = primefac.modinv(c2, n)
if c2<0:
c2+=n
m=(pow(c1,s1,n)*pow(c2,s2,n)) % n
return m
n1=21660190931013270559487983141966347279666044468572000325628282578595119101840917794617733535995976710097702806131277006786522442555607842485975616689297559583352413160087163656851019769465637856967511819803473940154712516380580146620018921406354668604523723340895843009899397618067679200188650754096242296166060735958270930743173912010852467114047301529983496669250671342730804149428700280401481421735184899965468191802844285699985370238528163505674350380528600143880619512293622576854525700785474101747293316814980311297382429844950643977825771268757304088259531258222093667847468898823367251824316888563269155865061
e1=65537
c1=11623242520063564721509699039034210329314238234068836130756457335142671659158578379060500554276831657322012285562047706736377103534543565179660863796496071187533860896148153856845638989384429658963134915230898572173720454271369543435708994457280819363318783413033774014447450648051500214508699056865320506104733203716242071136228269326451412159760818676814129428252523248822316633339393821052614033884661649376604245744651142959498917235138077366818109892738298251161767344501687113868331134288984466294415889635863660753717476594011236542159800099371872396181448655448842148998667568104710807411358117939831241620315
n2=21660190931013270559487983141966347279666044468572000325628282578595119101840917794617733535995976710097702806131277006786522442555607842485975616689297559583352413160087163656851019769465637856967511819803473940154712516380580146620018921406354668604523723340895843009899397618067679200188650754096242296166060735958270930743173912010852467114047301529983496669250671342730804149428700280401481421735184899965468191802844285699985370238528163505674350380528600143880619512293622576854525700785474101747293316814980311297382429844950643977825771268757304088259531258222093667847468898823367251824316888563269155865061
e2=70001
c2=8180690717251057689732022736872836938270075717486355807317876695012318283159440935866297644561407238807004565510263413544530421072353735781284166685919420305808123063907272925594909852212249704923889776430284878600408776341129645414000647100303326242514023325498519509077311907161849407990649396330146146728447312754091670139159346316264091798623764434932753276554781692238428057951593104821823029665203821775755835076337570281155689527215367647821372680421305939449511621244288104229290161484649056505784641486376741409443450331991557221540050574024894427139331416236263783977068315294198184169154352536388685040531
print long_to_bytes(same_n_sttack(n1,e1,e2,c1,c2))
小指数明文爆破
观察加密:c=m^e mod n
如果e很小,比如e=3,那么如果明文也很小的情况下可能会出现这种情况: m^e < n
此时直接对c开e次根号就可以得到m
另一种情况:k*n < m^3 < (k+1)*n
,m^e虽然大于n了但是没有超出太多
例题xbk
n=47966708183289639962501363163761864399454241691014467172805658518368423135168025285144721028476297179341434450931955275325060173656301959484440112740411109153032840150659
e=3
c=10968126341413081941567552025256642365567988931403833266852196599058668508079150528128483441934584299102782386592369069626088211004467782012298322278772376088171342152839
m^3=c+i*n => m=(c+i*n)^(1/3) 爆破i
import gmpy2
n=47966708183289639962501363163761864399454241691014467172805658518368423135168025285144721028476297179341434450931955275325060173656301959484440112740411109153032840150659
e=3
c=10968126341413081941567552025256642365567988931403833266852196599058668508079150528128483441934584299102782386592369069626088211004467782012298322278772376088171342152839
i=0
while 1:
if(gmpy2.iroot(c+i*n, 3)[1]==1):
t= gmpy2.iroot(c+i*n, 3)[0]
print hex(t)[2:].decode("hex")
break
i=i+1
序列密码
babyrpd
import random
import time
flag=open("/root/level0/flag","r").read()
random.seed(int(time.time()))
def check():
recv=int(raw_input())
if recv==random.randint(0,2**64):
print flag
return True
else:
print "atum tql"
return False
while 1:
if check():
break
random.randint伪随机数,只要时间种子一样就可以得到flag。目标服务器的时间可能会和本地差几秒,爆破一下。注意在更新时间种子之后,要执行相同次数的random.randint函数,保证本地随机数的生成顺序和目标服务器的是一样的。
import random
import time
from pwn import *
p = remote("47.97.215.88",20000)
def getstream(t1):
for i in range(t1-1):
random.randint(0, 2 ** 64)
return random.randint(0, 2 ** 64)
t=int(time.time())
t1=0
for i in range(-5,5):
print i
t1=t1+1
random.seed(t)
p.sendline(str(getstream(t1)))
p.interactive()
hardrpd
from random import *
while 1:
a=raw_input("#")
target=getrandbits(32)
if a!=str(target):
print target
else:
print open("flag","rb").read()
getrandbits(k) 生成一个k比特长的随机整数
通过提供的Main.class Main.java爆破,需要java环境。再向服务器得到了624个随机数后可以预测下一个。
from pwn import *
import subprocess
import random
def sign(iv):
# converts a 32 bit uint to a 32 bit signed int
if(iv&0x80000000):
iv = -0x100000000 + iv
return iv
def crack_prng(outputs_624_list):
get_in=[]
for i in outputs_624_list:
get_in.append(sign(i))
o = subprocess.check_output(["java", "Main"] + map(str, get_in))
stateList = [int(s) % (2 ** 32) for s in o.split()]
r = random.Random()
state = (3, tuple(stateList + [624]), None)
r.setstate(state)
return r
p = remote("47.97.215.88",20002)
l = []
for i in range(624):
p.recvuntil("#")
p.sendline("1")
l.append(int(p.recvuntil("\n",drop=True)))
r = crack_prng(l)
p.recvuntil("#")
p.sendline(str(r.getrandbits(32)))
p.interactive()
mediumrpd
import subprocess
o = subprocess.check_output(["java", "Main"])
tmp=[]
for i in o.split("\n")[0:3]:
tmp.append(int(i.strip()))
v1=tmp[0] % 0xffffffff
v2=tmp[1] % 0xffffffff
v3=tmp[2] % 0xffffffff
print v1
print v2
v3_get=int(raw_input())
if v3_get==v3:
print flag
import java.util.Random;
public class Main {
public static void main(String[] args) {
Random random = new Random();
System.out.println(random.nextInt());
System.out.println(random.nextInt());
System.out.println(random.nextInt());
}
}
python通过调用Main.java的random.nextInt()函数生成随机数。题目会先生成三个数,一开始会给我们两个数。我们通过爆破种子得到连续的两个数等于v1,v2可知这个种子是正确的,从而得到v3。exp多打几次
import random
import time
from pwn import *
def liner(seed):
return ((seed*25214903917+11)&0xffffffffffff)
p = remote("47.97.215.88",20001)
v1 = int(p.recvuntil("\n",drop=True))
v2 = int(p.recvuntil("\n",drop=True))
for i in range(0xffff):
seed = v1*65536+i
if (liner(seed)>>16) == v2:
p.sendline(str(liner(liner(seed))>>16))
info("failed:(")
p.interactive()