[UMDCTF 2023] crypto 部分

这篇具有很好参考价值的文章主要介绍了[UMDCTF 2023] crypto 部分。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这个小赛只作了8个crypto简单部分,后边5个都不会

pokecomms

密码签到,给了堆字符,细看就两种,每行8组 CHU! 开头,显然是替换成01。然后出来就是flag,这个flag有1364个字符。是我见过最长的。

 CHU! PIKA CHU! PIKA CHU! PIKA CHU! PIKA
 CHU! PIKA CHU! CHU! PIKA PIKA CHU! PIKA
 CHU! PIKA CHU! CHU! CHU! PIKA CHU! CHU!
 CHU! PIKA CHU! CHU! CHU! CHU! PIKA PIKA
 CHU! PIKA CHU! PIKA CHU! PIKA CHU! CHU!
 CHU! PIKA CHU! CHU! CHU! PIKA PIKA CHU!

CBC-MAC 1

题目很长得慢慢读,在一个10次的循环里有两个功能,1是输入数据进行加密,返回密文的最后一块。2是输入明文和预测的密文,如果正确就能得到flag(不能是输入过的,废话)

import socket
import threading
from _thread import *
from Crypto import Random
from Crypto.Cipher import AES

from binascii import hexlify, unhexlify

HOST = '0.0.0.0'  # Standard loopback interface address (localhost)
PORT = 60001        # Port to listen on (non-privileged ports are > 1023)
FLAG = open('flag.txt', 'r').read().strip()
MENU = "\nWhat would you like to do?\n\t(1) MAC Query\n\t(2) Forgery\n\t(3) Exit\n\nChoice: "
INITIAL = "Team Rocket told me CBC-MAC with arbitrary-length messages is safe from forgery. If you manage to forge a message you haven't queried using my oracle, I'll give you something in return.\n"

BS = 16 # Block Size
MAX_QUERIES = 10
   
def cbc_mac(msg, key):
    iv = b'\x00'*BS
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    t = cipher.encrypt(msg)[-16:]
    return hexlify(t)


def threading(conn):
    conn.sendall(INITIAL.encode())

    key = Random.get_random_bytes(16)
    queries = []
    while len(queries) < MAX_QUERIES:
        conn.sendall(MENU.encode())
        try:
            choice = conn.recv(1024).decode().strip()
        except ConnectionResetError as cre:
            return

        # MAC QUERY
        if choice == '1':
            conn.sendall(b'msg (hex): ')
            msg = conn.recv(1024).strip()

            try:
                msg = unhexlify(msg)
                if (len(msg) + BS) % BS != 0:
                    conn.sendall(f'Invalid msg length. Must be a multiple of BS={BS}\n'.encode())
                else:
                    queries.append(msg)
                    t = cbc_mac(msg, key)
                    conn.sendall(f'CBC-MAC(msg): {t.decode()}\n'.encode())
            except Exception as e:
                conn.sendall(b'Invalid msg format. Must be in hexadecimal\n')

        # FORGERY (impossible as I'm told)
        elif choice == '2':
            conn.sendall(b'msg (hex): ')
            msg = conn.recv(1024).strip()
            conn.sendall(b'tag (hex): ')
            tag = conn.recv(1024).strip()

            try:
                msg = unhexlify(msg)
                if (len(msg) + BS) % BS != 0:
                    conn.sendall(f'Invalid msg length. Must be a multiple of BS={BS} bytes\n'.encode())
                elif len(tag) != BS*2:
                    conn.sendall(f'Invalid tag length. Must be {BS} bytes\n'.encode())
                elif msg in queries:
                    conn.sendall(f'cheater\n'.encode())
                else:
                    t_ret = cbc_mac(msg, key)
                    if t_ret == tag:
                        conn.sendall(f'If you reach this point, I guess we need to find a better MAC (and not trust TR). {FLAG}\n'.encode())
                    else:
                        conn.sendall(str(t_ret == tag).encode() + b'\n')
            except Exception as e:
                conn.sendall(b'Invalid msg format. Must be in hexadecimal\n')

        else:
            if choice == '3': # EXIT
                conn.sendall(b'bye\n')
            else: # INVALID CHOICE
                conn.sendall(b'invalid menu choice\n')
            break


    if len(queries) > MAX_QUERIES:
        conn.sendall(f'too many queries: {len(queries)}\n'.encode())
    conn.close()


if __name__ == "__main__":
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((HOST, PORT))
        s.listen()
        while True:
            conn, addr = s.accept()
            print(f'new connection: {addr}')
            start_new_thread(threading, (conn, ))
        s.close()

核心部分是cbc_mac函数,采用AES_CBC加密,IV是b'0'*16

AES是第1个被玩坏的加密算法,不管是ECB还是CBC都有固定的漏洞。CBC加入的前反馈用的是上一块的密文,比如如果明文是两块AB,返回的密文也是两块CD,这里的B=ENC(A),D=ENC(B^C) ,所以这个题如果输入A能得到B,输入AB得到CD,于是可以预测输入B^C可以得到D

┌──(kali㉿kali)-[~]
└─$ nc 0.cloud.chals.io 12769
Team Rocket told me CBC-MAC with arbitrary-length messages is safe from forgery. If you manage to forge a message you haven't queried using my oracle, I'll give you something in return.

What would you like to do?
        (1) MAC Query
        (2) Forgery
        (3) Exit

Choice: 1
msg (hex): 00000000000000000000000000000000
CBC-MAC(msg): aa4103b3e49bf200f9e3ded28d1287c4

What would you like to do?
        (1) MAC Query
        (2) Forgery
        (3) Exit

Choice: 1
msg (hex): 0000000000000000000000000000000000000000000000000000000000000000
CBC-MAC(msg): d34e824a19e71237a2a49a5cefcacc12

What would you like to do?
        (1) MAC Query
        (2) Forgery
        (3) Exit

Choice: 2
msg (hex): aa4103b3e49bf200f9e3ded28d1287c4
tag (hex): d34e824a19e71237a2a49a5cefcacc12
If you reach this point, I guess we need to find a better MAC (and not trust TR). UMDCTF{Th!s_M@C_Sch3M3_1s_0nly_S3cur3_f0r_f!xed_l3ngth_m3ss4g3s_78232813}
 

CBC-MAC 2 

第2题大体与第1题相似,只是cbc_mac有变化,只是多了个padding块,值是前边块的数量,这样再输什么后边就是密文与序号异或的值了。

def cbc_mac(msg, key):
    iv = b'\x00'*BS
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    ct = cipher.encrypt(msg + l2b(len(msg)//BS, BS))
    t = ct[-16:]
    return hexlify(t)

这个方法,一时没想出来就睡觉去了,第二天醒来想到一个,一试结果就行了。

这个方法先输入A得到 _A = ENC(ENC(A)^1), 再输入B得到 _B = ENC(ENC(B)^1)

这时候如果输入 A+1这时候的密文应该是Enc(A)+ Enc(Enc(A)^1) 第2段的密文就是上面第1步的密文已知,但返回的并不是这个,而的后边padding的值,所以输入第3段输入为_A把前边的反馈异或掉,那么异或以后就是对序号0进行加密然后拿密文异或padding的3,函数返回的就是Enc(Enc(0)^3),在这中间把前边的加密结果异或掉了。同样拿B这样作也能得到这个结果。

from pwn import *
from Crypto.Util.number import long_to_bytes as l2b


p = remote('0.cloud.chals.io', 31220)
context.log_level = 'debug'


def get_mac(msg, key):
    iv = b'\x00'*BS
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    ct = cipher.encrypt(msg + l2b(len(msg)//BS, BS))
    t = ct[-16:]
    return hexlify(t)


def get_msg(msg):
  p.sendlineafter(b'Choice: ', b'1')
  p.sendlineafter(b'msg (hex):', msg.hex().encode())
  p.recvuntil(b': ')
  return bytes.fromhex(p.recvline().strip().decode())

def chk_msg(msg,tag):
  p.sendlineafter(b'Choice: ', b'2')
  p.sendlineafter(b'msg (hex):', msg.hex().encode())
  p.sendlineafter(b'tag (hex):', tag.hex().encode())


c0 = get_msg(b'\x01'*16)   #0>c0,c0^1->c1,
c1 = get_msg(b'\x02'*16)

c2 = get_msg(b'\x01'*16+ l2b(1, 16)+c0)
#c3 = get_msg(b'\x02'*16+ l2b(1, 16)+c1)
#print(c2,c3)
chk_msg(b'\x02'*16+ l2b(1, 16)+c1, c2)

print(p.recvline())

p.interactive()
#UMDCTF{W3lp_l00k5_l!k3_I_n33d_t0_ch4ng3_th1s_ag41n_s4d_f4ce_3m0j1_927323}

D3sys

这个题是D^3CTF的一个题的,没作出来,作到了取出数据,后边不会,但是前边跟上边两题神似,所以一起写在这。

题目是用的自己写的SM4加密,国密算法,这个基本不可破了(说明国密算法好吧)

输入数据会用json打包成串,串包含name:id, 权限admin,nonce:16字节的原意用不到的数据,时间time

这些数据会附着在用counter通过ECB方法生成的加密流上。生成1-2-3的块把明文异或上去。

因为自动打包是admin:0,要求打包所admin:1 时间time不能变,这块比较简单,因为是异或附着上去的,直接对密文与原明文异或就能得到加密流,再与新明文异或就得到密文。

关键是安会对生成后的每个块sha256取前16字节异或明文,类似CBC方式反馈。最后校验生成的标签。

有了上边这题的经验,可以找一个位置利用明文对sha256形成的密文进行拦截。

就是分别计算原明文到某一位置的标签,和修改后的明文到同一位置的标签,用两标签异或的值作明文,这样修改后的标签在中间位置就会变成原明文的标签,通过校验。

这个位置定在没用的nonce上,这个域正好一个16字节整块,通过调整name的长度可以让他对齐。然后在这里拦截。

不过由于拦截后的数据在decode('utf-8')后可能会有数据无法转换造成json解失败,所以要多次才能成功,试了大概10次,但也可能一上来就碰上。

这题到这只是拿到数据,怎么解密,还不清楚怎么弄。是dp+(p-1)*getPrime(128)的后447位,这是一个方程3个未知数,当然还有对称的q也是这样。整理下知识库,库里没有。

from Crypto.Util.number import long_to_bytes,bytes_to_long,getPrime,inverse
from gmssl.SM4 import CryptSM4,SM4_ENCRYPT,xor
from hashlib import sha256
#from secret import secret
import signal
import time
import json
import os

secret = b'flag{test}'
FLAG = b'ant' + secret

banner = br"""
 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
'########::::'###::::'#######::::::'######::'########:'########:::::'#######::::'#####::::'#######:::'#######::
 ##.... ##::'## ##::'##.... ##::::'##... ##:... ##..:: ##.....:::::'##.... ##::'##.. ##::'##.... ##:'##.... ##:
 ##:::: ##:'##:. ##:..::::: ##:::: ##:::..::::: ##:::: ##::::::::::..::::: ##:'##:::: ##:..::::: ##:..::::: ##:
 ##:::: ##:..:::..:::'#######::::: ##:::::::::: ##:::: ######:::::::'#######:: ##:::: ##::'#######:::'#######::
 ##:::: ##:::::::::::...... ##:::: ##:::::::::: ##:::: ##...:::::::'##:::::::: ##:::: ##:'##:::::::::...... ##:
 ##:::: ##::::::::::'##:::: ##:::: ##::: ##:::: ##:::: ##:::::::::: ##::::::::. ##:: ##:: ##::::::::'##:::: ##:
 ########:::::::::::. #######:::::. ######::::: ##:::: ##:::::::::: #########::. #####::: #########:. #######::
 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::"""

MENU1 = br'''
 ====------------------------------------------------------------------------------------------------------====
 |    |              +---------------------------------------------------------------------+              |   |
 |    |              |            [R]egister     [L]ogin     [T]ime     [E]xit             |              |   |
 |    |              +---------------------------------------------------------------------+              |   |
 ====------------------------------------------------------------------------------------------------------====
'''

MENU2 = br'''
 ====------------------------------------------------------------------------------------------------------====
 |    |              +---------------------------------------------------------------------+              |   |
 |    |              |            [G]et_dp_dq     [F]lag     [T]ime     [E]xit             |              |   |
 |    |              +---------------------------------------------------------------------+              |   |
 ====------------------------------------------------------------------------------------------------------====
'''

def pad(msg):
    tmp = 16 - len(msg)%16
    return msg + bytes([tmp] * tmp)

def get_token(id:str,nonce:str,_time:int):
    msg = {"id":id,"admin":0,"nonce":nonce,"time":_time}
    print('TOKEN:', str(json.dumps(msg)).encode())
    return str(json.dumps(msg)).encode()

class CRT_RSA_SYSTEM:
    nbit = 1024
    blind_bit = 128
    unknownbit = 193

    def __init__(self):
        e = 0x10001
        p,q = [getPrime(self.nbit // 2) for _ in "AntCTF"[:2]]
        n = p * q
        self.pub = (n,e)

        dp = inverse(e,p - 1)
        dq = inverse(e,q - 1)
        self.priv = (p,q,dp,dq,e,n)
        self.blind()

    def blind(self):
        p,q,dp,dq,e,n = self.priv
        rp,rq = [getPrime(self.blind_bit) for _ in "D^3CTF"[:2]]
        dp_ = (p-1) * rp + dp
        dq_ = (q-1) * rq + dq
        self.priv = (p,q,dp_,dq_,e,n)

    def get_priv_exp(self):
        p,q,dp,dq,e,n = self.priv
        dp_ = dp & (2**(self.nbit//2 + self.blind_bit - self.unknownbit) - 1)
        dq_ = dq & (2**(self.nbit//2 + self.blind_bit - self.unknownbit) - 1)
        return (dp_,dq_)

    def encrypt(self,m):
        n,e = self.pub
        return pow(m,e,n)

    def decrypt(self,c):
        p,q,dp,dq,e,n = self.priv
        mp = pow(c,dp,p)
        mq = pow(c,dq,q)
        m = crt([mp,mq],[p,q])
        assert pow(m,e,n) == c
        return m

class SM4_CTR(CryptSM4):
    block_size = 16

    def _get_timers(self, iv, msgLen):
        blockSZ = self.block_size
        blocks = int((msgLen + blockSZ - 1) // blockSZ)
        timer = bytes_to_long(iv)
        timers = iv
        for i in range(1, blocks):
            timer += 1
            timers += long_to_bytes(timer)
        return timers

    #先根据IV生成timers: count,count+1, count+2  将这些用crypt_ecb加密后与明文异或
    def encrypt_ctr(self, input_data,count = None):
        assert len(input_data) % self.block_size == 0
        if count == None:
            count = os.urandom(16)

        counters = self._get_timers(count,len(input_data))   #count, count+1, count+2
        blocks = xor(self.crypt_ecb(counters), input_data)
        ciphertext = bytes(blocks)
        return count + ciphertext[:len(input_data)]

    def decrypt_ctr(self, input_data):
        assert len(input_data) % self.block_size == 0
        pt = self.encrypt_ctr(input_data[self.block_size:], input_data[:self.block_size])
        return pt[self.block_size:]

class D3_ENC:
    def __init__(self,key:bytes,authdate:bytes):
        self.crt_rsa = CRT_RSA_SYSTEM()
        self.block = SM4_CTR()

        self.block.set_key(key, SM4_ENCRYPT)
        self.authdate = bytes_to_long(authdate)

    def encrypt(self,msg):
        assert len(msg) % 16 == 0

        cipher = self.block.encrypt_ctr(msg)
        tag = self.get_tag(msg)
        return cipher,tag

    def decrypt(self,cipher):
        assert len(cipher) % 16 == 0

        msg = self.block.decrypt_ctr(cipher)
        tag = self.get_tag(msg)
        return msg,tag

    def get_tag(self,msg):
        auth_date = self.authdate
        msg_block = [bytes_to_long(msg[i*16:(i+1)*16]) for i in range(len(msg)//16)]

        for mi in msg_block:
            auth_date = int(sha256(str(self.crt_rsa.encrypt(auth_date)).encode()).hexdigest()[:32],16) ^ mi

        return int(sha256(str(self.crt_rsa.encrypt(auth_date)).encode()).hexdigest()[:32],16)

class D3_SYS:
    def register(self):
        print('Welcome to D^3 CTF 2023!')

        username = input("[D^3] USERNAME:")
        if username in self.UsernameDict:
            print(f'[D^3] Sorry,the USERNAME {username} has been registered.')
            return 

        elif len(username) >= 20:
            print('[D^3] Sorry,the USERNAME can\'t be longer than 20.')
            return

        nonce = os.urandom(8).hex()

        token = get_token(username,nonce,int(time.time()))
        cipher_token,tag = self.d3block.encrypt(pad(token))
        self.UsernameDict[username] = tag
        print('[D^3] ' + username + ', token is ' + cipher_token.hex() + '& nonce is ' + nonce)

    def login(self):
        print('Welcome to D^3 CTF 2023!')

        username = input("[D^3] USERNAME:")

        if username not in self.UsernameDict:   #用户名在注册字典中
            print('[D^3] Sorry,the username has never been registered.')
            return False

        veritag = self.UsernameDict[username]
        cipher_token = bytes.fromhex(input("[D^3] Token:"))
        try:
            msg,tag = self.d3block.decrypt(cipher_token)   #token可以正常解密
        except:
            print("[D^3] Something Wrong...quiting....")
            return False

        if tag != veritag :
            print('[D^3] Ouch! HACKER? Get Out! {tag} {veritag}')
            return False

        try:
            token_dict = json.loads(msg.decode('latin-1').strip().encode('utf-8'))  #可以正常解析
        except:
            print('[D^3] Sorry,try again plz..')
            return False

        ID = token_dict["id"]   #已注册的用户名
        if ID != username:
            print('[D^3] Change name?')
            return False

        elif abs(int(time.time()) - token_dict['time']) >= 1:  #时间少于1秒
            print('[D^3] oh...no...out....')
            return False

        elif token_dict['admin'] != True:
            print("[D^3] Logining.")
            return False

        else:
            print("[D^3] Logining.")
            return True

    def time(self):
        print('[D^3] D^3\' clock shows '+str(int(time.time())))
        return

    def get_dp_dq(self):
        return self.d3block.crt_rsa.get_priv_exp()

    def enc_flag(self):
        flag = FLAG + os.urandom(16)
        n,e = self.d3block.crt_rsa.pub
        enc_flag = pow(bytes_to_long(flag),e,n)
        return enc_flag

    def handle(self):
        print(banner.decode())

        key = os.urandom(16)
        authdate = os.urandom(16)
        self.d3block = D3_ENC(key,authdate)
        print('[D^3] My initial Authdate is ' + authdate.hex())
        print('[D^3] My Auth pubkey is ' + str(self.d3block.crt_rsa.pub))
        self.UsernameDict = {}

        admin = None
        # signal.alarm(60) # in server.......
        while 1:
            print(MENU1.decode())
            option = input('option >')
            if option == 'R':
                self.register()
            elif option == 'L':
                admin = self.login()
                break
            elif option == 'T':
                self.time()
            else:
                break

        if admin != True:
            print("ByeBye! You are not admin.......")
            exit()

        print("Hello,Admin.Now, you have 2 chances to operate.")
        for __ in 'D^3 CTF'[:2]:
            print(MENU2.decode())
            option = input('option >')
            if option == 'F':
                cip = self.enc_flag()
                print('Encrypted Flag: ' + hex(cip))

            elif option == 'G':
                dp,dq = self.get_dp_dq()
                print(f'dp,dq:{[dp,dq]}')

            elif option == 'T':
                self.time()

            else:
                break

if __name__ == "__main__":
    d3sys = D3_SYS()
    d3sys.handle()

前半块先存在这

from pwn import *
from hashlib import sha256 
from Crypto.Util.number import long_to_bytes,bytes_to_long 

#p = process(['python3', './server.py'])
p = remote('106.14.124.130', 32141)

def pad(msg):
    tmp = 16 - len(msg)%16
    return msg + bytes([tmp] * tmp)

def res_enc(r):
    return pow(r,e,n)

def get_tag(auth_date, msg):
    msg_block = [bytes_to_long(msg[i*16:(i+1)*16]) for i in range(len(msg)//16)]
    print('Authdate:', auth_date)
    print('msg_block', msg_block)
    for mi in msg_block[:4]:
        auth_date = int(sha256(str(res_enc(auth_date)).encode()).hexdigest()[:32],16) ^ mi
        print(auth_date)

    #return int(sha256(str(crt_rsa.encrypt(auth_date)).encode()).hexdigest()[:32],16)
    return long_to_bytes(auth_date)  #noce block

def register(name):
    p.sendlineafter(b'option >', b'R')
    p.sendlineafter(b"[D^3] USERNAME:", name.encode())
    p.recvuntil(b'token is ')
    token = p.recvuntil(b'& nonce is ', drop=True)
    noce  = p.recvline().decode().strip()
    return bytes.fromhex(token.decode()),noce 

def login(name, token):
    p.sendlineafter(b'option >', b'L')
    p.sendlineafter(b"[D^3] USERNAME:", name.encode())
    p.sendlineafter(b"[D^3] Token:", token.hex().encode())

def get_time():
    p.sendlineafter(b'option >', b'T')
    p.recvuntil(b'[D^3] D^3\' clock shows ')
    return int(p.recvline().decode().strip())
    
def get_enc():
    p.sendlineafter(b'option >', b'F')
    return p.recvline()

def get_dpq():
    p.sendlineafter(b'option >', b'G')
    return p.recvline()

'''
iv + ecb(timer)^input
{'id': 'A12345612345678', 'admin': 0, 'nonce': '863fd6c657125e0e', 'time': 1682729415}
'''
p.recvuntil(b'[D^3] My initial Authdate is ')
authdate = bytes_to_long(bytes.fromhex(p.recvline().decode().strip()))
p.recvuntil(b'[D^3] My Auth pubkey is ')
n,e = eval(p.recvline().decode())
print("AU,n,e:",hex(authdate),n,e)

#控制name的长度,让nonce在一个独立的块,通过nonce来平衡对admin:1数据变动造成的sha256变化
#由于平衡hash值生成的字节可能会包含json无法识别的utf-8字符,爆破多次才能成功
username = 'A12345612345678'
tok,noce = register(username)
print('Tok:',tok)
print('Noce:',noce)

stime = get_time()
#noce = 48:
data  = '{"id": "'+username+'", "admin": 0, "nonce": "'+ noce +'", "time": '+str(stime)+'}'
data1 = pad(data.encode())
print('ServerData:', data1)
data  = '{"id": "'+username+'", "admin": 1, "nonce": "'+ noce +'", "time": '+str(stime)+'}'
data2 = pad(data.encode())
print('ServerData:', data2)

tab1 = get_tag(authdate, data1) #取得真实和变更后的到nonce块对应的hash值,将变更后的hash值异或
tab2 = get_tag(authdate, data2)

twos = xor(noce, tab1, tab2)

data  = ('{"id": "'+username+'", "admin": 1, "nonce": "').encode() + twos + ('", "time": '+str(stime)+'}').encode()
data3 = pad(data)
print('ServerData:', data3)
tab3 = get_tag(authdate, data3)
print(tab1,tab3)

tok2 = tok[:16] + xor(tok[16:], data1, data3)

login(username, tok2)

#getencflag
enc = get_enc()
dpq = get_dpq()
print(n,e)
print(enc, dpq)

 Bulbeuler

这题比较短,我喜欢,将flag按位加密,如果位为1输出x,m-x如果为0输出x和y ,其中x,y都是随机数,并给了g^x,g^y

FLAG = open('flag.txt', 'r').read()

flag_bytes = [ord(c) for c in FLAG]
flag_bits = ''.join([f'{num:08b}' for num in flag_bytes])

p = random_prime(2^256)
G = GF(p, modulus="primitive")
g = G.gen()  #2 
m = p-1

print(f'p: {p}')
for bit in flag_bits:
	x = G.random_element()
	if bit == '1':
		y = m-x
	else:
		y = G.random_element()

	print(f'{g^x}, {g^y}')

'''
#太长,不复制了
136107697176749185543209332858662868346120393376514992101253214901716362745, 4489243525362911779914961649702230836859635666195232403274786388182562879361
313387399053126414484766711139354034169709558997548368221163057994217005353, 4029565080773491679563658568390472992550553731340161637356414983568392824062
4287900589192154417478352219166638748006136888749131614780146977634009070841, 4954649930416350408097954464190826193086987784562686571699186431239839447950
'''

这个解密很简单,拿两个相乘当为1的时候g^x*g^y = g^(x+y) = g^(x+m-x) = g^m 由于m=p-1所以g^(p-1)=1

v = open('output.txt').readlines()

p = 6596997572802685744626960256235787019476434707654191935397530271983648431547

#g^x*g^y = g^(x+m-x) = g^m = g^(p-1) = 1 mod p
flag = ''
for i in range(1, len(v)):
    a,b = eval(v[i])
    if a*b%p == 1:
        flag +='1'
    else:
        flag +='0'

print(flag)
flag = bytes([int(flag[i:i+8],2) for i in range(0, len(flag),8)])
print(flag)
#UMDCTF{I_d0nt_th1nk_bulby_!s_th3_n3xt_3ul3r_tbh}

Noisy Bits

这题对每字节进行编码,然后加入3位误码。这个估计有数学算法进行回纠,不过没见过。这里对所有情况进行遍历,23^3并不大

import random

POLY = 0xC75
FLAG_BIN_LENGTH = 360

def encode(cw):
    cw = (cw & 0xfff)
    c = cw
    for i in range(1, 12+1):
        if cw & 1 != 0:
            cw = cw ^ POLY
        cw = cw >> 1
    
    return (cw << 12) | c

flag = open('flag.txt', 'rb').read()

binary_str = bin(int.from_bytes(flag))[2:].zfill(FLAG_BIN_LENGTH)

blocks = [ ''.join([binary_str[12*i+j] for j in range(12)]) for i in range(FLAG_BIN_LENGTH // 12) ]
block_nos = [ int(s, 2) for s in blocks ]

encoded = [ encode(cw) for cw in block_nos ]

# flip some bits for fun
for i in range(len(encoded)):
    encoded[i] = encoded[i] ^ (1 << random.randint(0,22))
    encoded[i] = encoded[i] ^ (1 << random.randint(0,22))
    encoded[i] = encoded[i] ^ (1 << random.randint(0,22))

encoded_bin = [ bin(e)[2:].zfill(23) for e in encoded ]

print(' '.join(encoded_bin))

爆破式,可以发现没有一位由于误码造成误解,应该是可能通过哪些位的异或啥的纠错。

a1 = {}
for i in range(0x1000):
    a1[encode(i)]=i

c = '01011100111010101110100 10111111010110100001100 00011010110011000110100 10000000111010101100110 10111011111000110110110 00001000110111100110000 10000010101001110010011 11001001000010101110001 10011100110010111010010 11100110111011101101010 01111111110001101001110 00000001011111001101100 10101101111000110110110 11011001100010000011101 10011100111011111010010 11001011100010101110010 11110001101000100110001 01111010100111110101100 00101001110001000000111 11101100000000001111111 11000010011010000000101 10101110001111100110000 10111111110011100000011 01101010101000001100010 01101011100000011000110 00101010010000101001101 11111011011010011110011 11100100100110001011110 10110111101011101010111 11010010000110100001010'

c = [int(i,2) for i in c.split(' ')]

m = []
for v in c:
    tmp = []
    for i in range(23):
        for j in range(23):
            for k in range(23):
                u = v^(1<<i)^(1<<j)^(1<<k)
                if u in a1:
                    if a1[u] not in tmp:
                        tmp.append(a1[u])
    m.append(tmp)

#m = [[1364], [3396], [1077], [1094], [1974], [3632], [1683], [1401], [1526], [872], [838], [3694], [822], [3125], [1526], [370], [821], [3950], [775], [1119], [1029], [3952], [1827], [98], [1734], [1357], [1523], [1119], [1879], [3338]]

for i in range(0,len(m),2):
    for v1 in m[i]:
        for v2 in m[i+1]:
            print(chr(v1>>4)+chr(((v1&0xf)<<4)|(v2>>8)) + chr(v2&0xff), end='')

#UMDCTF{n0i5y_ch4nn3l5_ar3_n0t_@_pr0bleM_4_u}

Hidden Message

只有一段密文,猜是摩尔斯码(大小写和空格),但flag试了好久,就是flag要小写!换成下划线,最后一个叹号删除。

GO CATCH tHe bug parAS! AbRa LIKES His speED! GO catCH zubaT Or not! like paraS cutE zUBAT Is FUn! hes GREAT! GO CATch RoCk onix! onIx LOVes My new mATTE rocKS lol!

UMDCTF{m0rs3_c0d3_m34ns_h4v1ng_s0_m8ch_f8ns13s} 

Reduce Thyself

题目给了n,e,c并提供解密,显然是通过给c*k^e来解题,但要求输入的是素数,所以爆破个k就行

import socket
import random
import threading
from _thread import *
from Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2l, getPrime, isPrime, inverse
from math import gcd
from binascii import hexlify, unhexlify

HOST = '0.0.0.0'  # Standard loopback interface address (localhost)
PORT = 60003        # Port to listen on (non-privileged ports are > 1023)
FLAG = open('flag.txt', 'r').read().strip().encode()

def decrypt(flag_ct, ct, d, n):
    pt = 'null'
    if isPrime(ct) and ct != flag_ct:
        pt = hexlify(l2b(pow(ct, d, n))).decode()
    return pt

def gen_params():
    while True:
        p,q = getPrime(1024), getPrime(1024)
        n = p*q
        e = 0x10001
        phi = (p-1)*(q-1)
        if gcd(phi, e) == 1:
            d = inverse(e, phi)
            break
    return n,e,d

def threading(conn):
    n,e,d = gen_params()
    flag_ct = pow(b2l(FLAG), e, n)
    print(f'n: {n}\ne: {e}\nd: {d}')
    conn.sendall(f'n: {hex(n)}\ne: {hex(e)}\nflag_ct: {hex(flag_ct)}\n\n'.encode())
    while True:
        conn.sendall(b'Gimme ct (hex): ')
        try:
            ct = int(conn.recv(1024).strip().decode(), 16)
        except Exception as e:
            conn.sendall(b'invalid ct')
            break
        
        pt = decrypt(flag_ct, ct, d, n)
        conn.sendall(f'result: {pt}\n\n'.encode())

    conn.close() 


if __name__ == "__main__":
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((HOST, PORT))
        s.listen()
        while True:
            conn, addr = s.accept()
            print(f'new connection: {addr}')
            start_new_thread(threading, (conn, ))
        s.close()



for i in range(500):
    if isPrime(c*pow(i+1, e, n)%n):
        print(i)

 

from pwn import *
from Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2l, getPrime, isPrime, inverse

p = remote('0.cloud.chals.io', 33047)
context.log_level = 'debug'

p.recvuntil(b"n: ")
n = int(p.recvline(),16)

p.recvuntil(b"flag_ct: ")
flag_ct = int(p.recvline(),16)

e = 0x10001
for i in range(1,2000):
    ct = flag_ct*pow(i,e,n)%n
    if isPrime(ct):
        print(i)
        p.sendlineafter(b'Gimme ct (hex): ', hex(ct)[2:].encode())
        p.recvuntil(b'result: ')
        m = int(p.recvline(),16)
        m //= i 
        print(l2b(m))
        break

AES-TR

题目给出两个功能,随机出一个数0/1,可以询问,回复根据随机数返回询问的两个值AES_ECB加密的结果。这里IV会和密文一起给出,密文是counter序列1-2-3...与输入相加的结果

import socket
import random
import threading
from _thread import *
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2l
from Crypto.Util.strxor import strxor
from binascii import hexlify, unhexlify

HOST = '0.0.0.0'  # Standard loopback interface address (localhost)
PORT = 60000        # Port to listen on (non-privileged ports are > 1023)
FLAG = open('flag.txt', 'r').read().strip()
MENU = "\nWhat would you like to do?\n\t(1) Encryption Query\n\t(2) Check Bit\n\t(3) Exit\n\nChoice: "
INITIAL = "Welcome to the best symmetric encryption scheme ever. I'll give you a flag if you can prove this scheme insecure under IND-CPA, but I know it's impossible!! >:)\n"

BS = 16 # Block Size
MS = 30 # Maximum blocks per query
MAX_QUERIES = 10
NUM_BITS = 128

def encrypt(m):
    m = unhexlify(m)
    iv = Random.get_random_bytes(16)
    key = Random.get_random_bytes(16)
    cipher = AES.new(key, AES.MODE_ECB)

    blocks = [m[i:i+BS] for i in range(0, len(m), BS)]
    ct = iv
    for i in range(len(blocks)):
        ctr = l2b((b2l(iv)+i+1) % pow(2,BS*8))
        ctr = b'\x00'*(BS - len(ctr)) + ctr # byte padding if ctr < pow(2,BS*8 - 1)
        ct += cipher.encrypt(strxor(ctr, blocks[i]))

    assert len(ct) - len(m) == BS
    return hexlify(ct)
    

def threading(conn):
    conn.sendall(INITIAL.encode())

    for bit in range(NUM_BITS):
        queries = 0
        b = random.randint(0,1)
        while queries < MAX_QUERIES:
            conn.sendall(MENU.encode())
            try:
                choice = conn.recv(1024).decode().strip()
            except ConnectionResetError as cre:
                return

            # ENCRYPTION QUERY
            if choice == '1':
                queries += 1
                conn.sendall(b'm0 (hex): ')
                m0 = conn.recv(1024).strip()
                conn.sendall(b'm1 (hex): ')
                m1 = conn.recv(1024).strip()

                if (len(m0) % 2 != 0) or ((len(m0) // 2) % BS != 0) or ((len(m0) // (2*BS)) > MS):
                    conn.sendall(b'invalid m0\n')
                elif (len(m1) % 2 != 0) or ((len(m1) // 2) % BS != 0) or ((len(m1) // (2*BS)) > MS):
                    conn.sendall(b'invalid m1\n')
                elif len(m0) != len(m1):
                    conn.sendall(b'messages must be same length\n')
                else:
                    if b == 0:
                        ct = encrypt(m0)
                    else:
                        ct = encrypt(m1)
                    conn.sendall(b'ct: ' + ct + b'\n')
                    continue

            # CHECK BIT
            elif choice == '2':
                conn.sendall(b'Bit (b) guess: ')
                b_guess = conn.recv(1024).strip().decode()
                if b_guess == str(b):
                    conn.sendall(b'correct!\n')
                    break
                else:
                    conn.sendall(b'wrong\n')
            
            # EXIT
            elif choice == '3':
                conn.sendall(b'bye homie\n')
            
            # INVALID
            else:
                conn.sendall(b'invalid menu choice\n')

            # close connection on exit, invalid choice, wrong bit guess, invalid encryption query
            conn.close()
            return

        if queries > MAX_QUERIES:
            conn.sendall(f'too many queries: {queries}\n'.encode())
            conn.close()
            return
            
    # Bits guessed correctly
    conn.sendall(f'okay, okay, here is your flag: {FLAG}\n'.encode())
    conn.close()


if __name__ == "__main__":
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((HOST, PORT))
        s.listen()
        while True:
            conn, addr = s.accept()
            print(f'new connection: {addr}')
            start_new_thread(threading, (conn, ))
        s.close()

由于query会返回两个序列加密的结果,可以通过输入加密后的结果判断是选的哪一项。

如果能通过输入的值来构造出相同的密文,判断密文就可知选的是哪个

如果iv的尾两位为00,转入 0...01,10,11,00 恰 好与序列相同,那么生成的密文4段全都相同,但当iv是01时会是什么样呢,在11后会有进位,导致不仅尾两位变化,所以这里的输入扩展到5块,对应下表

IV\输入 01 10 11 00 01 10
00 01 10 11 - - -
01 - - 00 01 10 11
10 - 00 01 10 11 -
11 00 01 10 11 - -

由于IV尾数不为00会在第几个序号后回到00,当一组00-11为尾数时不会产生进位,所以通过一组不会产生进位的位置中的相同关系来确定随机选择的是哪个。标蓝的部分结果相同。

from pwn import *
from Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2l


p = remote('0.cloud.chals.io', 24524)

context.log_level = 'debug'

def query():
    p.sendlineafter(b"Choice: ", b'1')
    p.sendlineafter(b'm0 (hex): ', c1+c2+c3+c4+c1) #0
    p.sendlineafter(b'm1 (hex): ', c4*5)
    p.recvuntil(b'ct: ')
    ct = p.recvline().strip().decode()
    cs = [int(ct[i*32:(i+1)*32],16) for i in range(6)]
    if (cs[0]&3 == 0 and cs[1]==cs[2]) or (cs[0]&3 == 1 and cs[3]==cs[5]) or (cs[0]&3 == 2 and cs[2]==cs[3]) or (cs[0]&3 == 3 and cs[1]==cs[3]):
        return 0
    else:
        return 1

def guess(b):
    p.sendlineafter(b"Choice: ", b'2')
    p.sendlineafter(b'Bit (b) guess: ', str(b).encode())

c1 = b'00000000000000000000000000000001'
c2 = b'00000000000000000000000000000002'
c3 = b'00000000000000000000000000000003'
c4 = b'00000000000000000000000000000000'

for i in range(128):
    b = query()
    guess(b)

p.recvline()
p.recvline()
p.recvline()

p.interactive()
#UMDCTF{N0t_@_v3ry_gr34t_5ch3m3_8ae61b7910099}

其它题等WP文章来源地址https://www.toymoban.com/news/detail-431451.html

到了这里,关于[UMDCTF 2023] crypto 部分的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • PolarD&N2023秋季个人挑战—Crypto全解

    (额。。这个题看错了不是挑战赛的。这里当做好题记录下来了) 题目信息:500 分 5 Polar币 n不互素问题,利用欧几里得求公约数 exp: 题目信息:100 分 1 Polar币 查表把附件里的年份转为数字,分别得到: 14,13,5,19,14,13,5,19,29,29,8,23 ,提示加上甲子,就是加60 然后转ASCII码得到:

    2024年02月08日
    浏览(36)
  • NewStarCTF 2023 公开赛道 WEEK1|CRYPTO全解

    附件信息 在线工具一把梭 题目信息 凯撒解码 题目信息 栏栅密码 题目信息 维吉尼亚呀呀呀!!根据flag前缀通过偏移量手算key就行,是kfc呀嘿嘿   题目信息 flag分啦三部分,分别来解 part1:base64 part2:base32 part3:UUencode 题目信息 脚本: 题目信息 识别:e特别大 在RSA中d也称

    2024年02月08日
    浏览(35)
  • [BUUCTF NewStar 2023] week5 Crypto/pwn

    最后一周几个有难度的题 也是个板子题,不过有些人存的板子没到,所以感觉有难度,毕竟这板子也不是咱自己能写出来的。 给了部分p, p是1024位给了922-101位差两头。 直接用双值copper这个双值和多值还是有些区别的,应该是作了些优化。 这个题有9个人完成,我估计有一半

    2024年02月05日
    浏览(42)
  • [DASCTF 2023 & 0X401七月暑期挑战赛] crypto

    密码只有3道题,最后一道被卡了,赛后在师傅一点点提示下完成。   题目很短,分两个RSA一个用小写表示一个用大写表示,小写n用大写加密,大写的给出了P和Q16的提示。 原来爆破过这种,后来保存了一个外国人写的,现在看来还不如自己写的。 原理很简单:爆破+裁剪 由

    2024年02月14日
    浏览(30)
  • [BUUCTF NewStarCTF 2023 公开赛道] week4 crypto/pwn

    再补完这个就基本上完了. Schmidt-Samoa密码系统看上去很像RSA,其中N=pqq, 给的e=N给了d NTRU又一个格的基本应用   当E.order() == p时   p-1光滑时的分解 此题先是p-1光滑分解,然后是e=3*0x10000先求3次根再用rabin求16次    求误差,虽然被分成3个数组,但本质上是一个,可以连到一起求解. 

    2024年02月07日
    浏览(37)
  • 2023年SWPU NSS 秋季招新赛 (校外赛道)WP—Crypto

    题目信息 根据第一行新表base64解码即可 题目附件 基础RSA exp: 题目附件 典型的dp泄露 exp: 提示 p/q接近类型 附件信息 exp: 附件信息 题目提示 RSA多项式问题,构造多项式环,sage解 得到x后,直接解 附件信息 兔子流密码(无key型)+栅栏+base,好好好 题目提示 附件信息 有密码,

    2024年02月08日
    浏览(34)
  • 2023寒鹭Tron-CTF迎新赛 CRYPTO Misc 全WP

    1、题目信息 2、解题方法 兔子密码,在线工具直接解 1、题目信息 2、解题方法 flag有三部分 第一部分:BrainFuck解码 第二部分:ook! 第三部分:UUencode解码 1、题目信息 2、解题方法 像摩斯,但不是摩斯,是摩斯的变形。。。 把 . 换成 0 , / 换成 1,二进制解码: 最后把flag换

    2024年02月08日
    浏览(48)
  • 【2023NewStar】#Week1 Web和Crypto 全题解!涉及知识扩展~

    泄露的秘密 www.zip Begin of Upload 打开源码 找到限制是在前端 我们抓包 上传正常后缀的文件 比如jpg结尾 但是这样传上去服务器是无法解析的 所以我们进行抓包 然后在bp中修改后缀名 将我们上传的后缀jpg在请求包中改为php 服务器就可以解析我们的语句了 一句话木马: ?php eval

    2024年02月06日
    浏览(49)
  • 前端js加密库的简单使用——crypto-js、jsrsasign、jsencrypt

    个人经验,这三个加密库的组合是最佳解决方案 crypto-js、jsrsasign、jsencrypt crypto-js 进行 AES 对称加密 jsrsasign 生成 RSA 密钥对 jsencrypt 进行 RSA 加解密

    2024年02月11日
    浏览(44)
  • midjourney如何使用?这个方法简单好用

    近年来,随着人工智能技术的不断发展和普及,AI 绘画技术已经成为了绘画领域的一个热门话题。这种技术可以通过机器学习和计算机视觉等技术,自动生成艺术作品,midjourney作为AI 绘画技术下诞生的一款人工智能绘画工具,也为艺术家和设计师提供了一种全新的创作体验。

    2024年02月12日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包