crypto初级题目

古典密码

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加密图解

1564747039050

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

1564748060926

第二组的第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()

文章作者: hh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hh !
  目录