cryptohack block ciphers

一、ECB CBC WTF

题目:

from Crypto.Cipher import AES


KEY = ?
FLAG = ?


@chal.route('/ecbcbcwtf/decrypt/<ciphertext>/')
def decrypt(ciphertext):
    ciphertext = bytes.fromhex(ciphertext)

    cipher = AES.new(KEY, AES.MODE_ECB)
    try:
        decrypted = cipher.decrypt(ciphertext)
    except ValueError as e:
        return {"error": str(e)}

    return {"plaintext": decrypted.hex()}


@chal.route('/ecbcbcwtf/encrypt_flag/')
def encrypt_flag():
    iv = os.urandom(16)

    cipher = AES.new(KEY, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(FLAG.encode())
    ciphertext = iv.hex() + encrypted.hex()

    return {"ciphertext": ciphertext}

hint:

Here you can encrypt in CBC but only decrypt in ECB. That shouldn't be a weakness because they're different modes... right?

译文:

在这里,您可以在CBC中加密,但只能在ECB中解密。这不应该是一个弱点,因为它们是不同的模式……对吧?

思路:

本题告诉了我们用CBC模式进行加密,但使用ECB进行解密,其中,我们已经知道了CBC模式中加密的IV值的大小,我们又已经知道了将密文用ECB解密的值,通过CBC模式的解密结构,我们知道将第一段密文解密后于IV进行异或运算既是原明文,而第一段密文则是第二段密文加密的IV值,所以解密时将其反过来用,作为IV解密即可:

wp:

import requests
from Crypto.Util.number import *
result=requests.get('http://aes.cryptohack.org/ecbcbcwtf/encrypt_flag')
ciphertext=result.json()["ciphertext"]
# print(result.text)

# print(len(bytes.fromhex(ciphertext)))
ciphertext=bytes.fromhex(ciphertext)
c1=hex(bytes_to_long(ciphertext[0:16]))[2:]
c2=hex(bytes_to_long(ciphertext[16:32]))[2:]
c3=hex(bytes_to_long(ciphertext[32:48]))[2:]
print(c1)
print(c2)
print(c3)

# C=hex(bytes_to_long(ciphertext))
# print(c1)
# print(c2)
# print(c3)
# c1=0x9d2691928a24ed9215416815e80164d1
# c2=0xf77ea0b2c315db6b56a48c8250b0c9b8
# c3=0x6a61b98b2e5d26af3976d7c36a76a847
# k1=int(c1) ^ int(c2)
# k3=int(c2) ^ int(c3)
a1=requests.get(f'http://aes.cryptohack.org/ecbcbcwtf/decrypt/{c2}')
a2=requests.get(f'http://aes.cryptohack.org/ecbcbcwtf/decrypt/{c3}')
m1=a1.json()["plaintext"]
m2=a2.json()["plaintext"]
M1=int(m1,16)^int(c1,16)
M2=int(m2,16)^int(c2,16)
print(long_to_bytes(M1)+long_to_bytes(M2))

 二、ECB Oracle

题目:

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad


KEY = ?
FLAG = ?


@chal.route('/ecb_oracle/encrypt/<plaintext>/')
def encrypt(plaintext):
    plaintext = bytes.fromhex(plaintext)

    padded = pad(plaintext + FLAG.encode(), 16)
    cipher = AES.new(KEY, AES.MODE_ECB)
    try:
        encrypted = cipher.encrypt(padded)
    except ValueError as e:
        return {"error": str(e)}

    return {"ciphertext": encrypted.hex()}

hint:

ECB is the most simple mode, with each plaintext block encrypted entirely independently. In this case, your input is prepended to the secret flag and encrypted and that's it. We don't even provide a decrypt function. Perhaps you don't need a padding oracle when you have an "ECB oracle"?

译文:

ECB是最简单的模式,每个明文块完全独立加密。在本例中,您的输入被预先添加到秘密标志并加密,仅此而已。我们甚至不提供解密函数。或许当你有了“欧洲央行的预言者”,你就不需要填充预言了?

思路:

 本题为一道典型的ECB加密解密问题,首先,我们即不知道明文也不知道密文。我们可以发现题目中给与了我们加密算法,所以我们可以自己设置明文代入进行尝试:

import requests
from tqdm import tqdm
from Crypto.Util.number import *
# m = ''
for i in range(30):
    m = i*'61'
    result = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
    try:
        a = result.json()["ciphertext"]
        print(i,len(a)//2)
    except:
        pass

从输出的值中,我们可以发现代码在第6和第22时发生改变。于是,我们可以推断明文为26bits长(32 - 6、48 - 22),而加密算法中是以16bits为块进行加密和解密(22 - 6)。

所以,我们可以代入15*a进行加密,这样前16为就为15 * a加上明文第一位,接着,我们对十六位进行暴力破解即可得到我们所需的明文第一位,第一到第十五位都为这个思路:

k=''
for i in range(15):
    m = '61'*(15-i)
    print(bytes.fromhex(m))
    result = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
    m = result.json()["ciphertext"]
    m1 = m[:32]
    # print(m1)
    for j in range(32,127):
        m = '61' * (15-i) + k
        m += str(hex(j)[2:])
        result1 = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
        n = result1.json()["ciphertext"]
        n1 = n[:32]
        # print(n1)
        try:
            if n1 == m1:
                k += str(hex(j)[2:])
                print(bytes.fromhex(k))
                break
        except:
            pass

# b'crypto{p3n6u1n5'

后11位思路类似,仅需要我们将代码扩张到前32位进行暴力破解运算:

k='63727970746f7b70336e3675316e35'
for i in range(11):
    m = '61'*(16-i)
    print(bytes.fromhex(m))
    result = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
    m = result.json()["ciphertext"]
    m1 = m[32:64]
    # print(m1)
    for j in range(32,127):
        m = '61' * (16-i) + k
        m += str(hex(j)[2:])
        result1 = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
        n = result1.json()["ciphertext"]
        n1 = n[32:64]
        # print(n1)
        try:
            if n1 == m1:
                k += str(hex(j)[2:])
                print(bytes.fromhex(k))
                break
        except:
            pass

wp:

import requests
from tqdm import tqdm
from Crypto.Util.number import *
# m = ''
# for i in range(30):
#     m = i*'61'
#     result = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
#     try:
#         a = result.json()["ciphertext"]
#         print(i,len(a)//2)
#     except:
#         pass

k=''
for i in range(15):
    m = '61'*(15-i)
    print(bytes.fromhex(m))
    result = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
    m = result.json()["ciphertext"]
    m1 = m[:32]
    # print(m1)
    for j in range(32,127):
        m = '61' * (15-i) + k
        m += str(hex(j)[2:])
        result1 = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
        n = result1.json()["ciphertext"]
        n1 = n[:32]
        # print(n1)
        try:
            if n1 == m1:
                k += str(hex(j)[2:])
                print(bytes.fromhex(k))
                break
        except:
            pass

# b'crypto{p3n6u1n5'

k='63727970746f7b70336e3675316e35'
for i in range(11):
    m = '61'*(16-i)
    print(bytes.fromhex(m))
    result = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
    m = result.json()["ciphertext"]
    m1 = m[32:64]
    # print(m1)
    for j in range(32,127):
        m = '61' * (16-i) + k
        m += str(hex(j)[2:])
        result1 = requests.get(f'http://aes.cryptohack.org/ecb_oracle/encrypt/{m}')
        n = result1.json()["ciphertext"]
        n1 = n[32:64]
        # print(n1)
        try:
            if n1 == m1:
                k += str(hex(j)[2:])
                print(bytes.fromhex(k))
                break
        except:
            pass

三、FLIPPING COOKIE

题目:

from Crypto.Cipher import AES
import os
from Crypto.Util.Padding import pad, unpad
from datetime import datetime, timedelta


KEY = ?
FLAG = ?


@chal.route('/flipping_cookie/check_admin/<cookie>/<iv>/')
def check_admin(cookie, iv):
    cookie = bytes.fromhex(cookie)
    iv = bytes.fromhex(iv)

    try:
        cipher = AES.new(KEY, AES.MODE_CBC, iv)
        decrypted = cipher.decrypt(cookie)
        unpadded = unpad(decrypted, 16)
    except ValueError as e:
        return {"error": str(e)}

    if b"admin=True" in unpadded.split(b";"):
        return {"flag": FLAG}
    else:
        return {"error": "Only admin can read the flag"}


@chal.route('/flipping_cookie/get_cookie/')
def get_cookie():
    expires_at = (datetime.today() + timedelta(days=1)).strftime("%s")
    cookie = f"admin=False;expiry={expires_at}".encode()

    iv = os.urandom(16)
    padded = pad(cookie, 16)
    cipher = AES.new(KEY, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(padded)
    ciphertext = iv.hex() + encrypted.hex()

    return {"cookie": ciphertext}

hint:

You can get a cookie for my website, but it won't help you read the flag... I think.

译文:

你可以为我的网站获得一块cookie,但它不会帮助你读flag…我认为。

思路: 

这是一到典型的CBC字节翻转问题,我们已知CBC的加密模式,其中,第一段密文为第一段明文于IV进行异或运算再进行AES加密,最终得到我们所需的第一段密文,第二段相比第一段既是将输入的IV改为我们求得的第一段密文。

我们已知第一段明文为:

a1 = b'admin=False;expi'

我们需要将admin改为True,从而让网站以为我们拥有网站的控制权,因为我们通过代码可知,当admin=True;时网站会返回我们flag值。所以我们需要构建一个新的IV使密文被加密后为True,从而骗过网站。

Dec (m1) = c1 ^ IV

c1_new  = Dec (m1) ^ IV_new

其中,各个条件我们都已知,除了IV_new,于是,我们可以构建出IV_new,并将新的IV返回到网站中输入,最终求得flag。

wp:

import requests
from Crypto.Util.number import *

result = requests.get('http://aes.cryptohack.org/flipping_cookie/get_cookie')
m = result.json()["cookie"]
M = bytes.fromhex(m)
# print(m)
IV = m[:32]
k = m[32:]

a1 = b'admin=False;expi'
a2 = b'admin=True;00000'
m1=hex(bytes_to_long(a1))
m2=hex(bytes_to_long(a2))
# print(m1)
IV_new = int(m1,16)^int(m2,16)^int(IV,16)
# print(IV_new)
IV_new = hex(IV_new)[2:]
print(IV_new)
result2 = requests.get(f'http://aes.cryptohack.org/flipping_cookie/check_admin/{k}/{IV_new}')
l = result2.json()
print(l)

四、LAZY CBC

题目:

from Crypto.Cipher import AES


KEY = ?
FLAG = ?


@chal.route('/lazy_cbc/encrypt/<plaintext>/')
def encrypt(plaintext):
    plaintext = bytes.fromhex(plaintext)
    if len(plaintext) % 16 != 0:
        return {"error": "Data length must be multiple of 16"}

    cipher = AES.new(KEY, AES.MODE_CBC, KEY)
    encrypted = cipher.encrypt(plaintext)

    return {"ciphertext": encrypted.hex()}


@chal.route('/lazy_cbc/get_flag/<key>/')
def get_flag(key):
    key = bytes.fromhex(key)

    if key == KEY:
        return {"plaintext": FLAG.encode().hex()}
    else:
        return {"error": "invalid key"}


@chal.route('/lazy_cbc/receive/<ciphertext>/')
def receive(ciphertext):
    ciphertext = bytes.fromhex(ciphertext)
    if len(ciphertext) % 16 != 0:
        return {"error": "Data length must be multiple of 16"}

    cipher = AES.new(KEY, AES.MODE_CBC, KEY)
    decrypted = cipher.decrypt(ciphertext)

    try:
        decrypted.decode() # ensure plaintext is valid ascii
    except UnicodeDecodeError:
        return {"error": "Invalid plaintext: " + decrypted.hex()}

    return {"success": "Your message has been received"}

hint:

I'm just a lazy dev and want my CBC encryption to work. What's all this talk about initialisations vectors? Doesn't sound important.

译文:

我只是一个懒惰的开发人员,希望我的CBC加密工作。这些关于初始化向量的讨论是怎么回事?听起来不重要。

思路:

在这道题中,我们有密文的生成代码,和密文的解密代码,以及flag的获得代码

CBC的生成思路:

c1 = Dec(m1) ^ IV  ——(1)

c2 = Dec(m2) ^ m1  ——(2)

c3 = Dec(m3) ^ m2  ——(3)

由于密文明文我们都可以自己设置,于是我们不妨让m2==0;m1=m3,于是让(1)和(3)进行异或运算,可以求得我们所需的IV值,将其返回网站即可:

wp:

import requests
from Crypto.Util.number import *
# a = b'0'*16
# A = hex(bytes_to_long(a))
# print(A)

b = '61616161616161616161616161616161'*3
result = requests.get(f'http://aes.cryptohack.org/lazy_cbc/encrypt/{b}')
m = result.json()["ciphertext"]
print(m)
k1 = m[:32] + '0'*32 + m[:32]
k = requests.get(f'http://aes.cryptohack.org/lazy_cbc/receive/{k1}')
k=k.json()
print(k)

b1 = '6161616161616161616161616161616116874d46ae7c02bcd5fe3d27fcba5b60966f8a5cc032ff235c45da24d505e288'
# result1 = requests.get(f'http://aes.cryptohack.org/lazy_cbc/encrypt/{b1}')
# result1 = result1.json()
# print(result1)

key = hex(int(b1[:32],16)^int(b1[64:96],16))[2:]
print(key)
m = requests.get(f'http://aes.cryptohack.org/lazy_cbc/get_flag/{key}')
m = m.json()
print(m)


M = 0x63727970746f7b35306d335f703330706c335f64306e375f3768316e6b5f49565f31355f316d70307237346e375f3f7d
print(long_to_bytes(int(M)))

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值