[UMDCTF 2023] crypto 部分

news/2024/5/20 0:14:40 标签: 密码学, CTF

这个小赛只作了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\输入011011000110
00011011---
01--00011011
10-00011011-
1100011011--

由于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


http://www.niftyadmin.cn/n/280955.html

相关文章

如何用ChatGPT做书籍、报告、文件的读取与互动式问答?故事人物活起来

该场景对应的关键词库&#xff08;15个&#xff09;&#xff1a; 书籍、报告、文件、详细信息、查询、主题、作者、出版日期、出版社、问题、方面、原则、方法、概括、主要观点、解释。 注意&#xff1a; ChatGPT的知识库截止于2021年9月&#xff0c;对于更新的数据或最新出版…

DFS与BFS超时的补救方法

题目&#xff1a;https://leetcode.cn/problems/word-break/description/?favorite2cktkvj 139. 单词拆分 中等 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意&#xff1a;不要求字典中出现的单词全部都使…

前端标签介绍

前端三剑客: html超文本标记语言 --> 超文本: 比普通的文本更强大更厉害.可以带图片,表格表单.音乐,视频,超链接 标记语言: 里面的代码是以标签带的形式书写.在网页显示.类似于一块块拼图 css层叠样式表 --> 美化网页内容(排版.颜色.大小.性能优化) 让网页看起来更好看,性…

[架构之路-181]-《软考-系统分析师》-19- 系统可靠性分析与设计 - 概览

前言&#xff1a; 可靠性工程是研究产品生命周期中故障的发生、发展规律&#xff0c;达到预防故障&#xff0c;消灭故 障&#xff0c;提高产品可用性的工程技术。 信息系统的可靠性是指系统在满足一定条件的应用环境中能够正常工作的能力&#xff0c;可以按一般工程系统的可靠性…

springboot3+react18+ts实现一个点赞功能

前端&#xff1a;vitereact18tsantd 后端&#xff1a;springboot3.0.6mybatisplus 最终效果大致如下&#xff1a; 后端&#xff1a; 引入pom依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring…

LeetCode1376. 通知所有员工所需的时间

【LetMeFly】1376.通知所有员工所需的时间 力扣题目链接&#xff1a;https://leetcode.cn/problems/time-needed-to-inform-all-employees/ 公司里有 n 名员工&#xff0c;每个员工的 ID 都是独一无二的&#xff0c;编号从 0 到 n - 1。公司的总负责人通过 headID 进行标识。…

ConcurrentHashMap的使用介绍和底层原理解析和开源框架的使用实例

文章目录 ConcurrentHashMap的使用介绍和底层原理解析和开源框架的使用实例1. ConcurrentHashMap介绍2. ConcurrentHashMap底层原理3. ConcurrentHashMap主要方法- put(K key, V value):添加元素。4. 总结5. 框架中的应用6. 操作技巧7. 运维部署(生产环境注意事项)8. Concurren…