陇原战疫2021网络安全大赛_Crypto_复现

mostlycommon

k e y w o r d s : keywords: keywords: 共模攻击

D e c r y p t i o n   c o d e Decryption~code Decryption code

from Crypto.Util.number import *
import gmpy2

e1 = 65536
e2 = 270270
n = 122031686138696619599914690767764286094562842112088225311503826014006886039069083192974599712685027825111684852235230039182216245029714786480541087105081895339251403738703369399551593882931896392500832061070414483233029067117410952499655482160104027730462740497347212752269589526267504100262707367020244613503
c1 = 39449016403735405892343507200740098477581039605979603484774347714381635211925585924812727991400278031892391996192354880233130336052873275920425836986816735715003772614138146640312241166362203750473990403841789871473337067450727600486330723461100602952736232306602481565348834811292749547240619400084712149673
c2 = 43941404835820273964142098782061043522125350280729366116311943171108689108114444447295511969090107129530187119024651382804933594308335681000311125969011096172605146903018110328309963467134604392943061014968838406604211996322468276744714063735786505249416708394394169324315945145477883438003569372460172268277

_,s,t = gmpy2.gcdext(e1,e2)
print(s,t)
m = pow(c1,s,n) * pow(c2,t,n) % n
print(gmpy2.gcd(e1,e2))
print(bytes.decode(long_to_bytes(gmpy2.iroot(m,2)[0])))

easytask

k e y w o r d s : keywords: keywords: GGHembedded technique

解题细节思路请见paper,(20条消息) GGH非对称密码体制破解方法_M3ng@L的博客-CSDN博客

简单来说,使用embedded technique方法将 C V P CVP CVP问题转换为 S V P SVP SVP问题,进而求得干扰向量 e e e的大小,减去 e e e再乘以公钥向量 B ′ B' B的逆即可

D e c r y p t i o n   c o d e Decryption~code Decryption code

from sage.modules.free_module_integer import IntegerLattice
c = e = [151991736758354,115130361237591,58905390613532,130965235357066,74614897867998,48099459442369,45894485782943,7933340009592,25794185638]
B = W = [[-10150241248,-11679953514,-8802490385,-12260198788,-10290571893,-334269043,-11669932300,-2158827458,-7021995],
[52255960212,48054224859,28230779201,43264260760,20836572799,8191198018,14000400181,4370731005,14251110],
[2274129180,-1678741826,-1009050115,1858488045,978763435,4717368685,-561197285,-1999440633,-6540190],
[45454841384,34351838833,19058600591,39744104894,21481706222,14785555279,13193105539,2306952916,7501297],
[-16804706629,-13041485360,-8292982763,-16801260566,-9211427035,-4808377155,-6530124040,-2572433293,-8393737],
[28223439540,19293284310,5217202426,27179839904,23182044384,10788207024,18495479452,4007452688,13046387],
[968256091,-1507028552,1677187853,8685590653,9696793863,2942265602,10534454095,2668834317,8694828],
[33556338459,26577210571,16558795385,28327066095,10684900266,9113388576,2446282316,-173705548,-577070],
[35404775180,32321129676,15071970630,24947264815,14402999486,5857384379,10620159241,2408185012,7841686]]

c = matrix(c)
B = matrix(B)
temp = B.stack(c).augment(vector([0]*B.ncols()+[1]))
e = IntegerLattice(temp).shortest_vector()[:-1]
m = B.solve_left(c - matrix(e))
print(m)
m = [ 877  619  919  977  541  941  947 1031  821]

from Crypto.Util.number import *
from Crypto.Cipher import AES
import hashlib
c = 0x1070260d8986d5e3c4b7e672a6f1ef2c185c7fff682f99cc4a8e49cfce168aa0
m = [877,619,919,977,541,941,947,1031,821]
key = hashlib.sha256(str(m).encode()).digest()
aes = AES.new(key,AES.MODE_ECB)
flag = aes.decrypt(long_to_bytes(c))
print(flag)

P e r f e r e n c e Perference Perference

hxp |伏尔加CTF 2016资格:crypto300"XXY"写

Mathematics of Public Key Cryptography (auckland.ac.nz)

GGH | 4XWi11的博客

Nguyen’s Attack:Intended Solution to GGH in GYCTF 2020 | Soreat_u’s Blog (soreatu.com)

Civet cat for Prince

k e y w o r d s : keywords: keywords: AES_CBCxor狸猫换太子

D e s c r i p t i o n Description Description

from Crypto.Cipher import AES
import os
from hashlib import sha256
import socketserver
import signal
import string
import random

table = string.ascii_letters + string.digits
BANNER = br'''
 .d8888b.  d8b                   888                            888             
d88P  Y88b Y8P                   888                            888             
888    888                       888                            888             
888        888 888  888  .d88b.  888888        .d8888b  8888b.  888888          
888        888 888  888 d8P  Y8b 888          d88P"        "88b 888             
888    888 888 Y88  88P 88888888 888          888      .d888888 888             
Y88b  d88P 888  Y8bd8P  Y8b.     Y88b.        Y88b.    888  888 Y88b.           
 "Y8888P"  888   Y88P    "Y8888   "Y888        "Y8888P "Y888888  "Y888          
                                                                                
                                                                                
                                                                                
 .d888                        8888888b.          d8b                            
d88P"                         888   Y88b         Y8P                            
888                           888    888                                        
888888  .d88b.  888d888       888   d88P 888d888 888 88888b.   .d8888b  .d88b.  
888    d88""88b 888P"         8888888P"  888P"   888 888 "88b d88P"    d8P  Y8b 
888    888  888 888           888        888     888 888  888 888      88888888 
888    Y88..88P 888           888        888     888 888  888 Y88b.    Y8b.     
888     "Y88P"  888           888        888     888 888  888  "Y8888P  "Y8888
'''

guard_menu = br'''
1.Tell the guard my name
2.Go away
'''

cat_menu = br'''1.getpermission
2.getmessage
3.say Goodbye
'''


def Pad(msg):
    return msg + os.urandom((16 - len(msg) % 16) % 16)


class Task(socketserver.BaseRequestHandler):
    def _recvall(self):
        BUFF_SIZE = 2048
        data = b''
        while True:
            part = self.request.recv(BUFF_SIZE)
            data += part
            if len(part) < BUFF_SIZE:
                break
        return data.strip()

    def send(self, msg, newline=True):
        try:
            if newline:
                msg += b'\n'
            self.request.sendall(msg)
        except:
            pass

    def recv(self, prompt=b'[-] '):
        self.send(prompt, newline=False)
        return self._recvall()

    def proof_of_work(self):
        proof = (''.join([random.choice(table) for _ in range(12)])).encode()
        sha = sha256(proof).hexdigest().encode()
        self.send(b"[+] sha256(XXXX+" + proof[4:] + b") == " + sha)
        XXXX = self.recv(prompt=b'[+] Give Me XXXX :')
        if len(XXXX) != 4 or sha256(XXXX + proof[4:]).hexdigest().encode() != sha:
            return False
        return True

    def register(self):
        self.send(b'')
        username = self.recv()
        return username

    def getpermission(self, name, iv, key):
        aes = AES.new(key, AES.MODE_CBC, iv)
        plain = Pad(name)+b"a_cat_permission"
        return aes.encrypt(plain)

    def getmessage(self, iv, key, permission):
        aes = AES.new(key, AES.MODE_CBC, iv)
        return aes.decrypt(permission)

    def handle(self):
        signal.alarm(50)
        if not self.proof_of_work():
            return
        self.send(BANNER, newline=False)
        self.key = os.urandom(16)
        self.iv = os.urandom(16)
        self.send(b"I'm the guard, responsible for protecting the prince's safety.")
        self.send(b"You shall not pass, unless you have the permission of the prince.")
        self.send(b"You have two choices now. Tell me who you are or leave now!")
        self.send(guard_menu, newline=False)
        option = self.recv()
        if option == b'1':
            try:
                self.name = self.register()
                self.send(b"Hello " + self.name)
                self.send(b"Nice to meet you. But I can't let you pass. I can give you a cat. She will play with you")
                self.send(b'Miao~ ' + self.iv)
                for i in range(3):
                    self.send(b"I'm a magic cat. What can I help you")
                    self.send(cat_menu, newline=False)
                    op = self.recv()
                    if op == b'1':
                        self.send(b"Looks like you want permission. Here you are~")
                        permission = self.getpermission(self.name, self.iv, self.key)
                        self.send(b"Permission:" + permission)
                    elif op == b'2':
                        self.send(b"Looks like you want to know something. Give me your permission:")
                        permission = self.recv()
                        self.send(b"Miao~ ")
                        iv = self.recv()
                        plain = self.getmessage(iv, self.key, permission)
                        self.send(b"The message is " + plain)
                    elif op == b'3':
                        self.send(b"I'm leaving. Bye~")
                        break
                self.send(b"Oh, you're here again. Let me check your permission.")
                self.send(b"Give me your permission:")
                cipher = self.recv()
                self.send(b"What's the cat tell you?")
                iv = self.recv()
                plain = self.getmessage(iv, self.key, cipher)
                prs, uid = plain[16:],plain[:16]
                if prs != b'Princepermission' or uid != self.name:
                    self.send(b"You don't have the Prince Permission. Go away!")
                    return
                else:
                    self.send(b"Unbelievable! How did you get it!")
                    self.send(b"The prince asked me to tell you this:")
                    f = open('flag.txt', 'rb')
                    flag = f.read()
                    f.close()
                    self.send(flag)
            except:
                self.request.close()
        if option == b'2':
            self.send(b"Stay away from here!")
        self.request.close()


class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass


class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
    pass


if __name__ == "__main__":
    HOST, PORT = '0.0.0.0', 10005
    print("HOST:POST " + HOST + ":" + str(PORT))
    server = ForkedServer((HOST, PORT), Task)
    server.allow_reuse_address = True
    server.serve_forever()

A n a l y s i s Analysis Analysis

首先是执行proof of work函数,简单的爆破四个字符使得其连接上剩余已知字符的sha256等于函数给出的sha256

然后进入正题,第一段守卫处没有有用信息,需要输入你的name(之后称为m1),然后在magic cat处可以获得

  • getpermission()

得到对m1+b'a_cat_permission'的AES_CBC加密之后得到的密文,其中iv是系统生成的已知量,key未知

    def getpermission(self, name, iv, key):
        aes = AES.new(key, AES.MODE_CBC, iv)
        plain = Pad(name)+b"a_cat_permission"
        return aes.encrypt(plain)
  • getmessage()

输入一段密文进行AES_CBC解密,其中iv由我们给出,key是之前使用的,得到解密之后给出的明文

    def getmessage(self, iv, key, permission):
        aes = AES.new(key, AES.MODE_CBC, iv)
        return aes.decrypt(permission)

总共有三次询问的机会(循环次数为3),循环完成之后由守卫进行验证

提交给守卫某个密文和自己给出的偏移量iv,使得进行相同的key的AES_CBC解密之后得到的明文是m1+b'Princeperimission'

self.send(b"Give me your permission:")
cipher = self.recv()
self.send(b"What's the cat tell you?")
iv = self.recv()
plain = self.getmessage(iv, self.key, cipher)
prs, uid = plain[16:],plain[:16]
if prs != b'Princepermission' or uid != self.name:
    ...

以上就是对题目代码的分析

简单来说,我们需要想办法把从magic cat处得到的perimission = m1 + b'a_cat_perimission'的密文换成m1 + b'Princepermission'的密文

其中可以用到的是magic cat会提供一次或两次解密的机会(用自己给出的iv

显然没有办法知道key进而直接求解,但是我们不需要老老实实地直接找 n e e d _ m 1 + n e e d _ m 2 need\_m_1+need\_m_2 need_m1+need_m2的密文,因为我们可以构造合适的iv代入最后的AES_CBC解密

先令

m 1 = i n p u t _ n a m e m_1 = input\_name m1=input_name

m 2 = a _ c a t _ p e r m i s s i o n m_2 = a\_cat\_permission m2=a_cat_permission

n e e d _ m 1 = m 1 need\_m_1=m_1 need_m1=m1

n e e d _ m 2 = P r i n c e p e r m i s s i o n need\_m_2=Princepermission need_m2=Princepermission

由于AES_CBC加密是分块加密,而每一块的大小是128bits也就是16字节,而由于题目要求, m 1 m_1 m1 m 2 m_2 m2以及 n e e d _ m 2 need\_m_2 need_m2都是16字节大小的

这就意味着加密得到的密文块会有特殊的性质

c 1 = p e r m i s s i o n [ : 16 ] c_1=permission[:16] c1=permission[:16] c 2 = p e r m i s s i o n [ 16 : ] c_2=permission[16:] c2=permission[16:];其中 p e r m i s s i o n permission permissionmagic cat通过getpermission()给出的密文

通过图片明确一下AES_CBC加解密过程

加密过程是,先将明文按16字节一分组拆开,然后进行如图加密(先进行异或,非明文分组1的异或对象是前一个密文分组或者是明文分组1的异或对象初始化向量iv),再分别投入加密器,得到各个密文分组后拼接起来成为最后的密文

解密过程是,将密文按16字节一分组拆开,然后分别投入解密器,得到的结果与前一个密文分组或者初始化向量进行异或,最后得到各个明文分组再直接拼接在一起组成最后的明文

其中加密器和解密器在本题算是黑盒,只需要知道进去的数据和原来是不一样的即可

我们构造的密文要先经过一次解密器,这就意味着非经过AES_CBC加密产生的密文(比如 c 1 , c 2 c_1,c_2 c1,c2)都会产生一个未知的数据;那么这时就需要用到magic cat提供的解密的机会

经过解密器之后会进行异或,那么这就是本题的关键之一了,异或的性质就是两个一样的数异或均为0,也就是说对其他一起的数不会有影响,利用这个性质我们构造

f a k e _ c 1 = c 1 ⊕ m 2 ⊕ n e e d _ m 2 fake\_c_1=c_1\oplus m_2\oplus need\_m_2 fake_c1=c1m2need_m2

使用系统给出的iv代入magic cat提供的一次解密的机会

先经过解密器得到 m 1 ⊕ i v ⊕ d e c r y p t e d _ m 2 ⊕ d e c r y p t e d _ n e e d _ m 2 m_1\oplus iv\oplus decrypted\_m_2\oplus decrypted\_need\_m_2 m1ivdecrypted_m2decrypted_need_m2

在与初始化向量iv异或得到 m = m 1 ⊕ d e c r y p t e d _ m 2 ⊕ d e c r y p t e d _ n e e d _ m 2 m=m_1\oplus decrypted\_m_2\oplus decrypted\_need\_m_2 m=m1decrypted_m2decrypted_need_m2

接着构造

f a k e _ i v = m ⊕ m 1 ⊕ i v = d e c r y p t e d _ m 2 ⊕ d e c r y p t e d _ n e e d _ m 2 ⊕ i v fake\_iv=m\oplus m_1\oplus iv=decrypted\_m_2\oplus decrypted\_need\_m_2\oplus iv fake_iv=mm1iv=decrypted_m2decrypted_need_m2iv

那么最后传入的密文是 f a k e _ c 1 + c 2 fake\_c_1+c_2 fake_c1+c2,传入的iv也就是 f a k e _ i v fake\_iv fake_iv

推导一下最后的解密过程,(AES_CBC是分块解密的)

f a k e _ c 1 fake\_c_1 fake_c1单独进行解密得到 m 1 ⊕ i v ⊕ d e c r y p t e d _ m 2 ⊕ d e c r y p t e d _ n e e d _ m 2 m_1\oplus iv\oplus decrypted\_m_2\oplus decrypted\_need\_m_2 m1ivdecrypted_m2decrypted_need_m2

然后再与 f a k e _ i v fake\_iv fake_iv异或得到

m 1 ⊕ i v ⊕ d e c r y p t e d _ m 2 ⊕ d e c r y p t e d _ n e e d _ m 2 ⊕ d e c r y p t e d _ m 2 ⊕ d e c r y p t e d _ n e e d _ m 2 ⊕ i v m_1\oplus iv\oplus decrypted\_m_2\oplus decrypted\_need\_m_2\oplus decrypted\_m_2\oplus decrypted\_need\_m_2\oplus iv m1ivdecrypted_m2decrypted_need_m2decrypted_m2decrypted_need_m2iv

= m 1 =m_1 =m1

得到明文分组1,符合最后的判断条件

然后 c 2 c_2 c2单独进行解密,得到 c 1 ⊕ m 2 c_1\oplus m_2 c1m2

再与 f a k e _ c 1 fake\_c_1 fake_c1进行异或得到 c 1 ⊕ m 2 ⊕ n e e d _ m 2 ⊕ c 1 ⊕ m 2 = n e e d _ m 2 c_1\oplus m_2\oplus need\_m_2 \oplus c_1\oplus m_2=need\_m_2 c1m2need_m2c1m2=need_m2

这就是明文分组2,符合最后的判断条件

D e c r y p t i o n   c o d e Decryption~code Decryption code

from re import L
from pwn import *
import hashlib,string,random
from Crypto.Cipher import AES

io = remote("node4.buuoj.cn","27370")
temp = io.recvline()
# print(temp)
temp1 = temp.split(b"==")
# print(temp1)
part_proof = bytes.decode(temp1[0].split(b"XXXX")[1])[1:-2]
sha = bytes.decode(temp1[1]).strip()
table = string.ascii_letters + string.digits
while True:
    XXXX = "".join([random.choice(table)for _ in range(4)])
    temp_proof = XXXX + part_proof
    temp_sha = hashlib.sha256(temp_proof.encode()).hexdigest()
    if sha == temp_sha:
        io.recvuntil(b"[+] Give Me XXXX :")
        io.sendline(XXXX.encode())
        break
io.recvuntil(b"[-] ")
io.sendline(b"1")
io.sendline(b"1" * 16)
io.recvuntil(b'Miao~ ')
iv = io.recvuntil(b"\n")[:-1]
# print(iv)
io.recvuntil(b'[-]')
io.sendline(b'1')
io.recvuntil(b'Permission:')
cat_permission = io.recvline()[:-1]
# print(cat_permission)
io.recvuntil(b"[-]")
io.sendline(b'2')
io.recvuntil(b"Looks like you want to know something. Give me your permission:")

m2 = b"a_cat_permission"
m1 = b'1' * 16 # your name,直接16字节,不会进行pad()函数,如果那样的话,m1也会成为一个未知量
need_m2 = b"Princepermission"
need_m1 = m1
c1 = cat_permission[:16]
c2 = cat_permission[16:]
fake_c1 = xor(xor(c1,m2),need_m2)

io.sendline(fake_c1)
io.recvuntil(b"Miao~ ")
io.sendline(iv)
io.recvuntil(b"The message is ")
m = io.recvuntil(b"\n")[:-1]
# print(plain)
io.recvuntil(b"[-]")
io.sendline(b'3')
io.recvuntil(b"Give me your permission:")

fake_permission = fake_c1 + c2
fake_iv = xor(xor(m,m1),iv)

io.sendline(fake_permission)
io.recvuntil(b"What's the cat tell you?")
io.sendline(fake_iv)
io.interactive()

P e r f e r e n c e Perference Perference

完美起航-20211107陇原战疫Crypto方向WP (okgoes.cn)

(11条消息) CBC模式解读_实践求真知-CSDN博客_cbc模式

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M3ng@L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值