[20210731 DAS geek summit UIUCTF2021 cryptoctf] Crypto&Re WP

Posted by johnbrayn on Sun, 19 Dec 2021 17:20:01 +0100

I went to the north to support teaching in the past two weeks. When I came back, I went to the Red Hat Cup. Ha ha, I ran half of China and gained a lot. But the Red Hat Cup's zero contribution once again made me know the necessity of learning pwn. Return and start training. You can settle down

The tail game in July has not been completely reproduced. It starts first in one breath, and then there is a game

2021DASCTF actual combat elite summer camp and DASCTF July X CBCTF 4th

Crypto yusa's cryptography sign in -- BlockTrick

from Crypto.Cipher import AES
import os
def pad(a):
	size = (16-len(a)%16)%16
	a += chr(size)*size
	return a

iv = os.urandom(16)
key = os.urandom(16)
enc = AES.new(key,AES.MODE_CBC,iv)
print(iv.encode('hex'))


for _ in range(2):
	try:
		trick = raw_input("")
		trick = pad(trick.decode('hex'))
		cipher = enc.encrypt(trick)
		if trick == cipher and trick != "":
			with open("flag.txt") as f:
				print(f.read())
				exit()
		else:
			print(cipher.encode('hex'))
			print("Try again")
	except:
		exit()

The idea given by master Shang, two rounds of AES_CBC, use known IV to XOR. The specific process is still to look at the CBC process. It's difficult to think about it, but it's difficult to think through it

# !/usr/bin/env python
# -*- coding:utf-8 -*-
from pwn import *

context.log_level = 'debug'
sh = remote('node4.buuoj.cn', 25321)
iv = sh.recv()

# first time
sh.send(iv)
cipher = sh.recvline()
sh.recv()

# second time
sh.send(cipher)
sh.recv()

Crypto yusa's cryptography class - SEDSED (recycling)

import os
from My_box import *


def gen_key(key):
    key_64 = ""
    for i in range(16):
        key_64 += '{0:04b}'.format(int(key[i], 16))
    key_56 = gen_56bit(key_64)
    left_key, right_key = key_56[:28], key_56[28:]
    round_keys = list()
    for index in range(2):
        L = circular_shift(left_key, round_shifts[index])
        R = circular_shift(right_key, round_shifts[index])
        round_key = gen_48bit(L + R)
        round_keys.append(round_key)
        left_key = L
        right_key = R
    return round_keys


def circular_shift(key, n):
    temp = ""
    temp = key[n:] + key[:n]
    return temp


def gen_56bit(key):
    key_56 = ""
    for x in KEY_P1:
        key_56 += key[x - 1]
    return key_56


def gen_48bit(key):
    key_48 = ""
    for x in KEY_P2:
        key_48 += key[x - 1]
    return key_48


def encrypt(plain_text, sub_keys):
    plain_textb = ""
    for i in range(16):
        plain_textb += '{0:04b}'.format(int(plain_text[i], 16))

    plain_textp = permutation(plain_textb)
    left, right = plain_textp[:32], plain_textp[32:]
    out = func(right, sub_keys[0])
    temp = int(out, 2) ^ int(left, 2)
    left, right = right, '{0:032b}'.format(temp)
    out = func(right, sub_keys[1])

    temp = int(out, 2) ^ int(left, 2)
    left = '{0:032b}'.format(temp)
    final = inv_permutation(left + right)
    cipher = hex(int(final, 2))[2:]
    return cipher


def permutation(plain_text):
    p = ""
    for x in IP:
        p += plain_text[x - 1]
    return p


def func(text, key):
    exp = expand(text)
    s_input = '{0:048b}'.format(int(exp, 2) ^ int(key, 2))
    s_out = sbox(s_input)
    f_final = per_func(s_out)
    return f_final


def expand(text):
    temp = ""
    for x in E:
        temp += text[x - 1]
    return temp


def inv_permutation(text):
    final = ""
    for x in Inv_IP:
        final += text[x - 1]
    return final


def per_func(s_output):
    s_final = ""
    for x in P:
        s_final += s_output[x - 1]
    return s_final


def sbox(s_input, index=None):
    s_out = ""
    if index != None:
        i = index
        row = int(s_input[0] + s_input[5], 2)
        column = int(s_input[1:5], 2)
        s_out += '{0:04b}'.format(S[i][row][column])
        return s_out
    else:
        for i in range(8):
            row = int(s_input[6 * i] + s_input[6 * i + 5], 2)
            column = int(s_input[6 * i + 1:6 * i + 5], 2)
            s_out += '{0:04b}'.format(S[i][row][column])
        return s_out


def enc(plain_text):
    sub_keys = gen_key(key_64)
    cipher_text = encrypt(plain_text, sub_keys)
    return cipher_text


def pad(plain_text):
    size = (16 - (len(plain_text) % 16)) % 16
    plain_text += size * '0'
    return plain_text


def myenc(plain_text):
    plain_text = pad(plain_text)
    cipher_text = ""
    for i in range(0, len(plain_text), 16):
        block = plain_text[i:i + 16]
        block_enc = enc(block).rjust(16, '0')
        cipher_text += block_enc
    return cipher_text


def enc2(plain_text, key):
    sub_keys = gen_key(key)
    cipher_text = encrypt(plain_text, sub_keys)
    return cipher_text


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

teststring = "testtest"
print(enc2(teststring.encode().hex(), key_64))

with open("flag.txt") as f:
    flag = f.read()

print("flag: ", myenc(flag.encode().hex()))

# 77a35598c47aeea6
# flag:  86721c7c1ebe2d0af8aa8e073073931b4a5ae6dcf03c784e3c70b5f8ce71cf9eb87f9b836eea0118

My_box

# 64->56
KEY_P1 = [57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
          63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4]
round_shifts = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
# 56->48
KEY_P2 = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47,
          55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32]

IP = [58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32,
      24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47,
      39, 31, 23, 15, 7]
E = [32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21,
     22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1]

S = [
    # Box-1
    [
        [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7],
        [0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],
        [4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0],
        [15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13]
    ],
    # Box-2

    [
        [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],
        [3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],
        [0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],
        [13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]
    ],

    # Box-3

    [
        [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],
        [13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],
        [13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],
        [1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]

    ],

    # Box-4
    [
        [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],
        [13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],
        [10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],
        [3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]
    ],

    # Box-5
    [
        [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],
        [14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],
        [4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],
        [11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]
    ],
    # Box-6

    [
        [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],
        [10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],
        [9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],
        [4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]

    ],
    # Box-7
    [
        [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],
        [13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],
        [1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],
        [6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]
    ],
    # Box-8

    [
        [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],
        [1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],
        [7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],
        [2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]
    ]

]
P = [16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4,
     25]
Inv_P = [9, 17, 23, 31, 13, 28, 2, 18, 24, 16, 30, 6, 26, 20, 10, 1, 8, 14, 25, 3, 4, 29, 11, 19, 32, 12, 22, 7, 5, 27,
         15, 21]

Inv_IP = [40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13,
          53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26,
          33, 1, 41, 9, 49, 17, 57, 25]


def inverse_table(table):
    new = list()
    for x in range(len(table)):
        if (x + 1) in table:
            num = table.index(x + 1) + 1
            new.append(num)
        else:
            new.append(x)
    return new

After listening to the V God, I still didn't understand it, but I saw that master Shang had made it, ignited it and reappeared his fighting spirit

I was ashamed to shrink up. Although I studied feistel and DES a little after the game, I was still ignored_ 64 += '{0:04b}'. The sentence format (int (key [i], 16)) discouraged me because I couldn't understand {0:04b} and felt it was so complicated; It's still very inappropriate. I really don't understand. Look at your output during the game

After checking, it is expressed in 4-bit binary, and 4-bit binary is enough for 1-bit hexadecimal

So learn from the bitter experience. Since I don't understand it, I try to reproduce the whole des process. Red indicates the known and directly pushable parts, and green indicates the places that need to be blasted. Now it's clear

svg diagram is not supported, paste one link , masters who are interested in guidance can open the link in the browser and download it. (finally, I found that it is not necessary to draw in such detail. It is almost enough to know a func; but after all, I didn't understand it before. Detour is the shortest shortcut for me.)

The idea of the whole problem is to encrypt the known testtest with a group of random keys to get the result 77a35598c47aeea6, and encrypt our flag with the same key; Then the idea of solving the problem is also very clear. Try to get the key through the known ciphertext, then write the decryption function, and finally return to

Start with key1 (I wrote key1 casually in the figure), and just focus on the feistel structure above

Some parts can be directly reversed, such as inv_permutation and per_func these two functions can be reversed in this way. It's a little smelly

def inv_permutation(text):
    final = ""
    for x in Inv_IP:
        final += text[x - 1]
    return final


def per_func(s_output):
    s_final = ""
    for x in P:
        s_final += s_output[x - 1]
    return s_final
null = ['' for _ in range(64)]


def permutation(plain_text):
    assert len(plain_text) == len(Inv_IP)
    final = null
    for x in range(len(Inv_IP)):
        final[Inv_IP[x]-1] = plain_text[x]
    return ''.join(final)

Then the key to this topic is to explode this sbox; The working principle of sbox is simply to convert 48 bits to 32 bits, and the middle is to convert 8 bits every 6 bits. For example, in the wiki encyclopedia, taking sbox 5 as an example, the beginning and end of 011011 is 01, and the middle is 1101, that is, the corresponding coordinates 1,13, The corresponding sandbox (which can be checked in the table) is 1001. After this operation, the original 48 bit sandbox is divided into 8 groups, and each group of 6 bits becomes 4 bits, so the final total length is 32. Obviously, this process is irreversible

Carefully observe the sandbox and find that the 16 numbers 0 ~ 15 in a row are out of order, so a sandbox needs to be exploded in four cases

I wrote the script, but I felt that my code ability was really poor. Variable naming became a problem. Finally, I couldn't check out any errors. I'll make up later

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from My_box import *
import sys
from Crypto.Util.number import long_to_bytes

teststring = "testtest"
cipher = 0x77a35598c47aeea6
final = bin(cipher)[2:].rjust(64, '0')
c = '86721c7c1ebe2d0af8aa8e073073931b4a5ae6dcf03c784e3c70b5f8ce71cf9eb87f9b836eea0118'

null64 = ['' for _ in range(64)]
null32 = ['' for _ in range(32)]


def my_permutation(plain_text):
    assert len(plain_text) == len(Inv_IP)
    per_final = null64
    for x in range(len(Inv_IP)):
        per_final[Inv_IP[x]-1] = plain_text[x]
    return ''.join(per_final)


def expand(text):
    temp = ""
    for x in E:
        temp += text[x - 1]
    return temp


def inv_per_func(s_final):
    assert len(s_final) == len(P)
    s_output = null32
    for x in range(len(P)):
        s_output[P[x] - 1] = s_final[x]
    return ''.join(s_output)


def func(text):
    exp = expand(text)
    return exp


def find_column(num, index, row):
    for find_j in range(16):
        if S[index][row][find_j] == num:
            return find_j


def permutation(plain_text):
    p = ""
    for x in IP:
        p += plain_text[x - 1]
    return p


def encrypt(plain_text):
    plain_textb = ""
    for en_i in range(16):
        plain_textb += '{0:04b}'.format(int(plain_text[en_i], 16))
    plain_textp = permutation(plain_textb)
    en_left, en_right = plain_textp[:32], plain_textp[32:]
    out = func(en_right)
    return en_left, en_right, out


def sbox(s_input):
    s_out = ''
    for sbox_i in range(8):
        s_row = int(s_input[6 * sbox_i] + s_input[6 * sbox_i + 5], 2)
        s_column = int(s_input[6 * sbox_i + 1:6 * sbox_i + 5], 2)
        s_out += '{0:04b}'.format(S[sbox_i][s_row][s_column])
    return s_out


def per_func(s_output):
    s_final = ""
    for x in P:
        s_final += s_output[x - 1]
    return s_final


def inv_permutation(cipher_text):
    assert len(cipher_text) == len(IP)
    p = null64
    for x in range(len(IP)):
        p[IP[x] - 1] = cipher_text[x]
    return ''.join(p)


def decrypt(cipher_text, d_sub_keys):
    d_final = bin(int(cipher_text, 16))[2:].rjust(64, '0')
    d_final = my_permutation(d_final)
    d_left, d_right = d_final[:32], d_final[32:]
    # RIGHT
    d_EXP = func(d_left)
    # inv_func
    d_s_input = '{0:048b}'.format(int(d_EXP, 2) ^ int(d_sub_keys[1], 2))
    d_s_out = sbox(d_s_input)
    out = per_func(d_s_out)

    d_RIGHT = bin(int(out, 2) ^ int(d_right, 2))[2:].rjust(32, '0')

    # LEFT
    d_EXP = func(d_RIGHT)
    d_s_input = '{0:048b}'.format(int(d_EXP, 2) ^ int(d_sub_keys[0], 2))
    d_s_out = sbox(d_s_input)
    out = per_func(d_s_out)

    d_LEFT = bin(int(out, 2) ^ int(d_left, 2))[2:].rjust(32, '0')
    plaintext = d_LEFT + d_RIGHT
    plaintext = inv_permutation(plaintext)
    plaintext = ''.join(hex(int(plaintext[_:_+4], 2))[2:] for _ in range(0, len(plaintext), 4))
    return plaintext


def inv_rjust(cipher_text):
    for rjust_i in range(len(cipher_text)):
        if cipher_text[rjust_i] != '0':
            return cipher_text[rjust_i:]


def inv_pad(cipher_text):
    for pad_i in range(len(cipher_text)-1, -1, -1):
        if cipher_text[pad_i] != '0':
            return cipher_text[:pad_i]


def mydec(ciphertext, d_sub_keys):
    plaintext = ''
    for de_i in range(0, len(ciphertext), 16):
        block_enc = inv_rjust(ciphertext[de_i:de_i + 16])
        block = decrypt(block_enc, d_sub_keys)
        plaintext += block
    plaintext = inv_pad(plaintext)
    return plaintext


LEFT, RIGHT, EXP1 = encrypt(teststring.encode().hex())
final = my_permutation(final)
left, right = final[:32], final[32:]
EXP2 = func(left)

# key1
f_final1 = bin(int(left, 2) ^ int(LEFT, 2))[2:].rjust(32, '0')
# inv_func
s_out1 = inv_per_func(f_final1)
# inv_sbox
for i in range(65536):
    bin_i = bin(i)[2:].rjust(16, '0')
    a1 = [int(bin_i[_]+bin_i[_+1], 2) for _ in range(0, len(bin_i), 2)]
    # j's sbox
    j1 = 0
    # i's row
    s_input1 = ''
    for i1 in a1:
        t1 = int(s_out1[i1 * 4:i1 * 4 + 4], 2)
        column1 = find_column(t1, j1, i1)
        j1 += 1
        s_input1 += bin(i1)[2:].rjust(2, '0')[0] + bin(column1)[2:].rjust(4, '0') + bin(i1)[2:].rjust(2, '0')[-1]
    key1 = bin(int(s_input1, 2) ^ int(EXP1, 2))[2:].rjust(48, '0')
    # key2
    f_final2 = bin(int(right, 2) ^ int(RIGHT, 2))[2:].rjust(32, '0')
    s_out2 = inv_per_func(f_final2)
    for j in range(65536):
        bin_j = bin(j)[2:].rjust(16, '0')
        a2 = [int(bin_j[_] + bin_j[_ + 1], 2) for _ in range(0, len(bin_j), 2)]
        j2 = 0
        s_input2 = ''
        for i2 in a2:
            t2 = int(s_out2[j2 * 4:j2 * 4 + 4], 2)
            column2 = find_column(t2, j2, i2)
            j2 += 1
            s_input2 += bin(i2)[2:].rjust(2, '0')[0] + bin(column2)[2:].rjust(4, '0') + bin(i2)[2:].rjust(2, '0')[-1]
        key2 = bin(int(s_input2, 2) ^ int(EXP2, 2))[2:].rjust(48, '0')
        sub_keys = [key1, key2]
        flag = long_to_bytes(int(mydec(c, sub_keys), 16))
        print(i, i*j)
        if b'flag' in flag:
            print(flag)
            sys.exit(0)

Misc-red_vs_blue

Classic red blue duel, Misc's sign in question, but to be honest, I'm a little numb

Given an nc, you can know that there are two situations for you to guess. You can get the flag 66 times in a row. Each time you win or lose an nc, the results are different

This was my script at that time. I thought there was no problem, but the interaction made me hemoptysis several times and all kinds of IO confusion

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *


def rest():
    # not so fast
    time.sleep(.05)


context.log_level = 'debug'

res = []
r = b'r'
b = b'b'
sh = remote('node4.buuoj.cn', 28719)

# begin info
sh.recv()
while 1:
    # classical try red one
    rest()
    bug = sh.sendline(r)
    feedback = sh.recv()
    if b'Sorry' in feedback:
        res.append(b)
        sh.sendline(b'y')
    else:
        res.append(r)
        continue
    sh.recv()
    for i in res:
        rest()
        sh.sendline(i)
        flag = sh.recv()
        if b'flag' in flag:
            print(flag)
            sh.close()
            break
    print(len(res))

Then I asked pwn how to achieve good interaction effect; Finally, President Peng gave a script, learned it, wrote it well and neatly, and set a good example for our generation. Post it and learn again in the future

Mainly some format conversion, and then receive it sentence by sentence

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

# connection
p = remote('node4.buuoj.cn', 28719)
# receive intro text
p.recvline()  # Here are 66 AWD Games will begin!
p.recvline()  # The winner is between Red Team and Blue Team
p.recvline()  # To get the flag if you predict the results of all games successfully!
p.recvline()  # Game 1
p.recvline()  # choose one [r] Red Team,[b] Blue Team:

# record the results
s = ''

while 1:

    p.sendline(bytes("r", encoding="utf-8"))
    print("r")
    print(p.recvline().decode(), end="")  # Your choice xxx Team
    print(p.recvline().decode(), end="")  # The result xxx Team
    # win: The number of successful predictions xx
    # lose: Sorry!You are wrong!
    result = p.recvline().decode()
    print(result)
    # print(result.find("successful"))
    if result.find("successful") != -1:
        # print("continue..")
        s += "r"
        print(p.recvline().decode(), end="")  # Game x
        p.recvline()  # choose one [r] Red Team,[b] Blue Team:

    elif result.find("wrong") != -1:
        # print("retry..")
        s += "b"
        p.sendline(bytes("y", encoding="utf-8"))
        print(p.recvline().decode(), end="")  # Play again? (y/n):Game 1
        print("y")
        # repeat
        for i in range(len(s)):
            # print(p.recvline().decode(),end="")# Game x
            p.recvline()
            # print(p.recvline().decode(),end="")# choose one [r] Red Team,[b] Blue Team:
            p.sendline(bytes(s[i], encoding="utf-8"))
            print(s[i])
            p.recvline()  # Your choice xxx Team
            p.recvline()  # The result xxx Team
            p.recvline()  # win: The number of successful predictions xx

            print(p.recvline().decode(), end="")  # Game x
        print(p.recvline().decode(), end="")  # choose one [r] Red Team,[b] Blue Team:

Geek peak

Crypto-crtrsa

from secret import flagn,p,q
#p and q are two primes generated by getPrime
import random
def key_gen():
	while True:
		dp = random.randint(1,1<<20)
		dq = random.randint(1,q-1)
		if gcd(dp, p - 1) == 1 and gcd(dq, q - 1) == 1:
			d = crt([dp,dq],[p-1,q-1])
			phi = (p-1)*(q-1)
			R = Integers(phi)
			e = R(d)^-1
			return p*q,e
n,e = key_gen()
print(e)
print(n)
print(pow(flagn,int(e),n))
e = 2953544268002866703872076551930953722572317122777861299293407053391808199220655289235983088986372630141821049118015752017412642148934113723174855236142887
N = 6006128121276172470274143101473619963750725942458450119252491144009018469845917986523007748831362674341219814935241703026024431390531323127620970750816983
flag = 4082777468662493175049853412968913980472986215497247773911290709560282223053863513029985115855416847643274608394467813391117463817805000754191093158289399

E is big, but obviously d is also big, so neither of those guys can; From the known point of view, it looks like dp leakage, but e is too large to enumerate; Finally see A Master Blog RSA - Small CRT Private Exponents are recorded on the. Unfortunately, the master didn't give exp; Then I also found relevant papers, which were all over the street, but I couldn't understand them; Then I found the implementation on github, which is a tool small from the RsaCtfTool of password dog hand_ crt_ Exp, post the link https://github.com/Ganapati/RsaCtfTool , but I didn't quite understand how to use the command line. I took the source code directly and pieced it together. The complete exp is

# sage
from sage.libs.ntl.ntl_ZZ_pX import ntl_ZZ_pContext, ntl_ZZ_pX
from functools import reduce
import binascii
import math
import logging
import random

logger = logging.getLogger("global_logger")

try:
    import gmpy2 as gmpy

    gmpy_version = 2
    mpz = gmpy.mpz
    logger.info("[+] Using gmpy version 2 for math.")
except ImportError:
    try:
        import gmpy

        gmpy_version = 1
        mpz = gmpy.mpz
        logger.info("[+] Using gmpy version 1 for math.")
    except ImportError:
        gmpy_version = 0
        mpz = int
        gmpy = None
        logger.info("[+] Using python native functions for math.")


def getpubkeysz(n):
    size = int(math.log2(n))
    if size & 1 != 0:
        size += 1
    return size


def _gcdext(a, b):
    if a == 0:
        return [b, 0, 1]
    else:
        d = b // a
        r = b - (d * a)
        g, y, x = _gcdext(r, a)
        return [g, x - d * y, y]


def _isqrt(n):
    if n == 0:
        return 0
    x, y = n, (n + 1) >> 1
    while y < x:
        x, y = y, (y + n // y) >> 1
    return x


def _gcd(a, b):
    while b:
        a, b = b, a % b
    return abs(a)


def _introot(n, r=2):
    if n < 0:
        return None if r & 1 == 0 else -_introot(-n, r)
    if n < 2:
        return n
    if r == 2:
        return _isqrt(n)
    lower, upper = 0, n
    while lower != upper - 1:
        mid = (lower + upper) >> 1
        m = pow(mid, r)
        if m == n:
            return mid
        elif m < n:
            lower = mid
        elif m > n:
            upper = mid
    return lower


def _introot_gmpy(n, r=2):
    if n < 0:
        return None if r & 1 == 0 else -_introot_gmpy(-n, r)
    return gmpy.root(n, r)[0]


def _introot_gmpy2(n, r=2):
    if n < 0:
        return None if r & 1 == 0 else -_introot_gmpy2(-n, r)
    return gmpy.iroot(n, r)[0]


def _invmod(a, m):
    a, x, u = a % m, 0, 1
    while a:
        x, u, m, a = u, x - (m // a) * u, a, m % a
    return x


def _is_square(n):
    i = _isqrt(n)
    return i ** 2 == n


def miller_rabin(n, k=40):
    # Taken from https://gist.github.com/Ayrx/5884790
    # Implementation uses the Miller-Rabin Primality Test
    # The optimal number of rounds for this test is 40
    # See http://stackoverflow.com/questions/6325576/how-many-iterations-of-rabin-miller-should-i-use-for-cryptographic-safe-primes
    # for justification

    # If number is even, it's a composite number

    if n == 2:
        return True

    if n & 1 == 0:
        return False

    r, s = 0, n - 1
    while s & 1 == 0:
        r += 1
        s >>= 1
    i = 0
    for i in range(0, k):
        a = random.randrange(2, n - 1)
        x = pow(a, s, n)
        if x == 1 or x == n - 1:
            continue
        j = 0
        while j <= r - 1:
            x = pow(x, 2, n)
            if x == n - 1:
                break
            j += 1
        else:
            return False
    return True


def _fermat_prime_criterion(n, b=2):
    """Fermat's prime criterion
    Returns False if n is definitely composite, True if posible prime."""
    return pow(b, n - 1, n) == 1


def _is_prime(n):
    """
    If fermats prime criterion is false by short circuit we dont need to keep testing bases, so we return false for a guaranteed composite.
    Otherwise we keep trying with primes 3 and 5 as base. The sweet spot is primes 2,3,5, it doesn't improvee the runing time adding more primes to test as base.
    If all the previus tests pass then we try with rabin miller.
    All the tests are probabilistic.
    """
    if (
        _fermat_prime_criterion(n)
        and _fermat_prime_criterion(n, b=3)
        and _fermat_prime_criterion(n, b=5)
    ):
        return miller_rabin(n)
    else:
        return False


def _next_prime(n):
    while True:
        if _is_prime(n):
            return n
        n += 1


def erathostenes_sieve(n):
    """ Returns  a list of primes < n """
    sieve = [True] * n
    for i in range(3, isqrt(n) + 1, 2):
        if sieve[i]:
            sieve[pow(i, 2) :: (i << 1)] = [False] * (
                (n - pow(i, 2) - 1) // (i << 1) + 1
            )
    return [2] + [i for i in range(3, n, 2) if sieve[i]]


_primes = erathostenes_sieve


def _primes_yield(n):
    p = i = 1
    while i <= n:
        p = next_prime(p)
        yield p
        i += 1


def _primes_yield_gmpy(n):
    p = i = 1
    while i <= n:
        p = gmpy.next_prime(p)
        yield p
        i += 1


def _primes_gmpy(n):
    return list(_primes_yield_gmpy(n))


def _fib(n):
    a, b = 0, 1
    i = 0
    while i <= n:
        a, b = b, a + b
        i += 1
    return a


def _invert(a, b):
    return pow(a, b - 2, b)


def _lcm(x, y):
    return (x * y) // _gcd(x, y)


def _ilog2_gmpy(n):
    return int(gmpy.log2(n))


def _ilog_gmpy(n):
    return int(gmpy.log(n))


def _ilog2_math(n):
    return int(math.log2(n))


def _ilog_math(n):
    return int(math.log(n))


def _ilog10_math(n):
    return int(math.log10(n))


def _ilog10_gmpy(n):
    return int(gmpy.log10(n))


def _mod(a, b):
    return a % b


if gmpy_version > 0:
    gcd = gmpy.gcd
    invmod = gmpy.invert
    gcdext = gmpy.gcdext
    is_square = gmpy.is_square
    next_prime = gmpy.next_prime
    is_prime = gmpy.is_prime
    fib = gmpy.fib
    primes = _primes_gmpy
    lcm = gmpy.lcm
    invert = gmpy.invert
    powmod = gmpy.powmod
    ilog = _ilog_gmpy
    ilog2 = _ilog2_gmpy
    mod = gmpy.f_mod
    log = gmpy.log
    log2 = gmpy.log2
    log10 = gmpy.log10
    ilog10 = _ilog10_gmpy
    if gmpy_version == 2:
        isqrt = gmpy.isqrt
        introot = _introot_gmpy2
    else:
        isqrt = gmpy.sqrt
        introot = _introot_gmpy
else:
    gcd = _gcd
    isqrt = _isqrt
    introot = _introot
    invmod = _invmod
    gcdext = _gcdext
    is_square = _is_square
    next_prime = _next_prime
    fib = _fib
    primes = erathostenes_sieve
    is_prime = _is_prime
    fib = _fib
    primes = _primes
    lcm = _lcm
    invert = _invmod
    powmod = pow
    ilog = _ilog_math
    ilog2 = _ilog2_math
    mod = _mod
    log = math.log
    log2 = math.log2
    log10 = math.log10
    ilog10 = _ilog10_math


def trivial_factorization_with_n_phi(N, phi):
    m = N - phi + 1
    i = isqrt(pow(m, 2) - (N << 2))  # same as isqrt((m**2) - (4*n))
    roots = int((m - i) >> 1), int((m + i) >> 1)
    if roots[0] * roots[1] == N:
        return roots


__all__ = [
    getpubkeysz,
    gcd,
    isqrt,
    introot,
    invmod,
    gcdext,
    is_square,
    next_prime,
    is_prime,
    fib,
    primes,
    lcm,
    invert,
    powmod,
    ilog2,
    ilog,
    ilog10,
    mod,
    log,
    log2,
    log10,
    trivial_factorization_with_n_phi,
]


def poly_fast_ntl(ctx, f, xs):
    # Fast multipoint evaulation from Modern Computer Algebra 3rd edition 10.1
    n = len(xs)
    rems = [0] * (4 * n)  # segment tree max size

    def build_tree(i, l, r):
        if l + 1 == r:
            x = xs[l] if l < len(xs) else 0
            rems[i] = ntl_ZZ_pX([-x, 1], ctx)
            return
        mid = (l + r) >> 1
        build_tree(i * 2, l, mid)
        build_tree(i * 2 + 1, mid, r)
        rems[i] = rems[i * 2] * rems[i * 2 + 1]

    build_tree(1, 0, n)

    def compute(f, i, l, r):
        if l + 1 == r:
            yield f % rems[i]
            return
        mid = (l + r) >> 1
        yield from compute(f % rems[2 * i], 2 * i, l, mid)
        yield from compute(f % rems[2 * i + 1], 2 * i + 1, mid, r)

    return map(lambda r: Integer(r.list()[0]), compute(f, 1, 0, n))


def factor(n, e, bound):
    # https://mathoverflow.net/questions/120160/attack-on-crt-rsa
    D = ceil(sqrt(bound))
    ctx = ntl_ZZ_pContext(n)  # NTL's polynomial multiplication is much faster
    x = randint(1, n - 1)
    xe = int(powmod(x, e, n))
    poly_factors = []
    for a in range(0, D):
        poly_factors.append(ntl_ZZ_pX([-x, power_mod(xe, a, n)], ctx))
    poly = product(poly_factors)
    xed = int(powmod(xe, D, n))
    ys = [int(powmod(xed, b, n)) for b in range(0, D)]
    for t in poly_fast_ntl(ctx, poly, ys):
        p = gcd(t, n)
        if p > 1 and p < n:
            return p, n // p


if __name__ == "__main__":
    n = 6006128121276172470274143101473619963750725942458450119252491144009018469845917986523007748831362674341219814935241703026024431390531323127620970750816983
    e = 2953544268002866703872076551930953722572317122777861299293407053391808199220655289235983088986372630141821049118015752017412642148934113723174855236142887
    bound = 2**20  # upper bound of min(d_p, d_q)
    for _ in range(3):  # Retrying
        r = factor(n, e, bound)
        if r is not None:
            p, q = r
            print(p)
            exit()
    print(0)  # Prints 0 if failed

This can be used to decompose p, which will not be repeated later; The routine of doing password problems is to know and find the source code

The cipher master from the destiny team directly deduced it. Starting from dp, which can be exploded, he obtained p, strong. Stick down Destiny team link

Crypto-MedicalImage(recuring)

from PIL import Image
from decimal import *
import numpy as np
import random
getcontext().prec = 20

def f1(x):
    # It is based on logistic map in chaotic systems
    # The parameter r takes the largest legal value
    assert(x>=0)
    assert(x<=1)
    ...

def f2(x):
    # same as f1
    ...
def f3(x):
    # same as f1
    ...

def encryptImage(path):
    im = Image.open(path)
    size = im.size
    pic  = np.array(im) 
    im.close()
    r1 = Decimal('0.478706063089473894123')
    r2 = Decimal('0.613494245341234672318')
    r3 = Decimal('0.946365754637812381837')
    w,h = size
    for i in range(200):
        r1 = f1(r1)
        r2 = f2(r2)
        r3 = f3(r3)
    const = 10**14
    for x in range(w):
        for y in range(h):
            x1 = int(round(const*r1))%w
            y1 = int(round(const*r2))%h
            r1 = f1(r1)
            r2 = f2(r2)
            tmp = pic[y,x]
            pic[y,x] = pic[y1,x1]
            pic[y1,x1] = tmp
    p0 = random.randint(100,104)
    c0 = random.randint(200,204)
    config = (p0,c0)
    for x in range(w):
        for y in range(h):
            k = int(round(const*r3))%256
            k = bin(k)[2:].ljust(8,'0')
            k = int(k[p0%8:]+k[:p0%8],2)
            r3 = f3(r3)
            p0 = pic[y,x]
            c0 = k^((k+p0)%256)^c0
            pic[y,x] = c0

    return pic,size,config
def outputImage(path,pic,size):
    im = Image.new('P', size,'white')
    pixels = im.load()
    for i in range(im.size[0]):
        for j in range(im.size[1]):
            pixels[i,j] = (int(pic[j][i]))

    im.save(path)


def decryptImage(pic,size,config):
    .....
    
enc_img = 'flag.bmp'
out_im = 'flag_enc.bmp'

pic,size,_ = encryptImage(enc_img)
outputImage(out_im,pic,size)

I don't know why it didn't come out. I found the confusion function, and then encryption and decryption are very similar. Finally, I ran 6400 pictures and didn't solve it. Wait for WP; Scripts used

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PIL import Image
from decimal import *
import numpy as np
getcontext().prec = 20
enc_img = 'flag.bmp'
out_im = 'flag_enc.bmp'


def f1(x):
    # It is based on logistic map in chaotic systems
    # The parameter r takes the largest legal value
    x = 4 * x * (1-x)
    assert(x >= 0)
    assert(x <= 1)
    return x


def outputImage(path, pic, size):
    im = Image.new('P', size, 'white')
    pixels = im.load()
    for i in range(im.size[0]):
        for j in range(im.size[1]):
            pixels[i, j] = (int(pic[j][i]))

    im.save(path)


def decryptImage(p1, config, p0x):
    im = Image.open(out_im)
    size = im.size
    pic = np.array(im)
    im.close()
    r1 = Decimal('0.478706063089473894123')
    r2 = Decimal('0.613494245341234672318')
    r3 = Decimal('0.946365754637812381837')
    w, h = size
    rx1 = []
    rx2 = []
    rx3 = []
    for i in range(200):
        r1 = f1(r1)
        r2 = f1(r2)
        r3 = f1(r3)
        rx1.append(r1)
        rx2.append(r2)
        rx3.append(r3)
    for x in range(w-1, -1, -1):
        for y in range(h-1, -1, -1):
            r1 = f1(r1)
            r2 = f1(r2)
            r3 = f1(r3)
            rx1.append(r1)
            rx2.append(r2)
            rx3.append(r3)
    const = 10 ** 14
    p0, c0 = config
    for x in range(w-1, -1, -1):
        for y in range(h-1, -1, -1):
            k = int(round(const * r3)) % 256
            k = bin(k)[2:].ljust(8, '0')
            k = int(k[p0 % 8:] + k[:p0 % 8], 2)
            rx3.pop()
            r3 = rx3[-1]
            p0 = p0x
            c0 = k^((k+p0)%256)^c0
            pic[y, x] = c0
    for x in range(w-1, -1, -1):
        for y in range(h-1, -1, -1):
            x1 = int(round(const * r1)) % w
            y1 = int(round(const * r2)) % h
            rx1.pop()
            rx2.pop()
            r1 = rx1[-1]
            r2 = rx2[-1]
            tmp = pic[y, x]
            pic[y, x] = pic[y1, x1]
            pic[y1, x1] = tmp
    outputImage(p1, pic, size)

i = 1
for p0 in range(100, 105):
    for c0 in range(200, 205):
        for p0xx in range(0, 256):
            decryptImage(str(i)+'.bmp', (p0, c0), p0xx)
            i += 1

After looking at the WP from the destiny team, it's still the problem of inverse algorithm. It's too simple to think that encryption is decryption. Just change it a little

Put forward the attitude of re, first cp write down the source code and write it down again

from PIL import Image
from decimal import *
import numpy as np
import random

getcontext().prec = 20


def f1(x):
    assert (x >= 0)
    assert (x <= 1)
    return 4 * x * (1 - x)


def f2(x):
    assert (x >= 0)
    assert (x <= 1)
    return 4 * x * (1 - x)


def f3(x):
    assert (x >= 0)
    assert (x <= 1)
    return 4 * x * (1 - x)


im = Image.open('flag_enc.bmp')
size = im.size
pic = np.array(im)
im.close()
r1 = Decimal('0.478706063089473894123')
r2 = Decimal('0.613494245341234672318')
r3 = Decimal('0.946365754637812381837')
w, h = size
for i in range(200):
    r1 = f1(r1)
    r2 = f2(r2)
    r3 = f3(r3)
const = 10 ** 14
p = [100, 101, 102, 103]
c = [200, 201, 202, 203]
R1, R2, R3 = r1, r2, r3
l1 = []
l2 = []
for i in range(w * h):
    l1.append(r1)
    l2.append(r2)
    r1 = f1(r1)
    r2 = f2(r2)


def decrypt(p0, c0, r1=R1, r2=R2, r3=R3):
    for x in range(w):
        for y in range(h):
            k = int(round(const * r3)) % 256
            k = bin(k)[2:].ljust(8, '0')
            k = int(k[p0 % 8:] + k[:p0 % 8], 2)
            r3 = f3(r3)
            pic[y, x] = ((pic[y, x] ^ c0 ^ k) - k) % 256
            p0 = pic[y, x]
            c0 = k ^ ((k + p0) % 256) ^ c0
    t = w * h - 1
    for x in range(w - 1, -1, -1):
        for y in range(h - 1, -1, -1):
            x1 = int(round(const * l1[t])) % w
            y1 = int(round(const * l2[t])) % h
            tmp = pic[y, x]
            pic[y, x] = pic[y1, x1]
            pic[y1, x1] = tmp
            t -= 1
    return pic, size


enc_img = 'flag.bmp'
count = 0
for p0 in p:
    for c0 in c:
        pic, size = decrypt(p0, c0)
        im = Image.new('P', size, 'white')
        pixels = im.load()
        for i in range(im.size[0]):
            for j in range(im.size[1]):
                pixels[i, j] = (int(pic[j][i]))
        count += 1
        im.save(str(count) + enc_img)

All right, I'm starting to reappear. It was really hasty in the game. Many points were not found

Well, I'm a waste. I feel like I'm finished, but I just can't get out; Just take a look at the master's, just one look. emmmmm, horizontal trough! I never thought I'd die here

Operation priority. XOR is not bracketed, which annoys you. It is recommended to copy the priority table a hundred times. The priority of bit operation is lower than addition and subtraction

Incidentally, I found that the master only exploded 16 cases

Then lie down to Wanquan kerosene Baode Baguio, because I came out with 25 flag s

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PIL import Image
from decimal import *
import numpy as np
getcontext().prec = 20


def f1(x):
    # It is based on logistic map in chaotic systems
    # The parameter r takes the largest legal value
    assert (x >= 0)
    assert (x <= 1)
    x = 4 * x * (1 - x)
    return x


def f2(x):
    # same as f1
    return f1(x)


def f3(x):
    # same as f1
    return f1(x)


def outputImage(path, pic, size):
    im = Image.new('P', size,'white')
    pixels = im.load()
    for i in range(im.size[0]):
        for j in range(im.size[1]):
            pixels[i, j] = (int(pic[j][i]))
    im.save(path)


def decryptImage(path, p0c0):
    im = Image.open(in_img)
    size = im.size
    pic = np.array(im)
    im.close()
    r1 = Decimal('0.478706063089473894123')
    r2 = Decimal('0.613494245341234672318')
    r3 = Decimal('0.946365754637812381837')
    w, h = size
    const = 10 ** 14
    for _ in range(200):
        r1 = f1(r1)
        r2 = f2(r2)
        r3 = f3(r3)
    r1_list, r2_list = [], []
    for _ in range(w * h):
        r1_list.append(r1)
        r2_list.append(r2)
        r1 = f1(r1)
        r2 = f2(r2)
    p0 = p0c0[0]
    c0 = p0c0[1]
    for x in range(w):
        for y in range(h):
            k = int(round(const * r3)) % 256
            k = bin(k)[2:].ljust(8, '0')
            k = int(k[p0 % 8:] + k[:p0 % 8], 2)
            r3 = f3(r3)
            pic[y, x] = ((pic[y, x] ^ c0 ^ k) - k) % 256
            c0 = k ^ ((k + pic[y, x]) % 256) ^ c0
            p0 = pic[y, x]
    for x in range(w-1, -1, -1):
        for y in range(h-1, -1, -1):
            x1 = int(round(const*r1_list.pop())) % w
            y1 = int(round(const*r2_list.pop())) % h
            pic[y, x], pic[y1, x1] = pic[y1, x1], pic[y, x]
    outputImage(path, pic, size)


in_img = 'flag_enc.bmp'
i = 1
for p in range(100, 105):
    for c in range(200, 205):
        decryptImage(str(i)+'.bmp', (p, c))
        i += 1

UIUCTF 2021

Re-hvhpgs{synt}

First, you can see the two most conspicuous strings of characters. After the word frequency, you can find meaningful characters. Then you can find that it is rot and displacement. Tear it directly by hand and enter i_propose_to_consider_the_question_can_machines_think to get the flag

Crypto-dhke_intro

Topic tips

Small numbers are bad in cryptography. This is why.

import random
from Crypto.Cipher import AES

# generate key
gpList = [ [13, 19], [7, 17], [3, 31], [13, 19], [17, 23], [2, 29] ]
g, p = random.choice(gpList)
a = random.randint(1, p)
b = random.randint(1, p)
k = pow(g, a * b, p)
k = str(k)

# print("Diffie-Hellman key exchange outputs")
# print("Public key: ", g, p)
# print("Jotaro sends: ", aNum)
# print("Dio sends: ", bNum)
# print()

# pad key to 16 bytes (128bit)
key = ""
i = 0
padding = "uiuctf2021uiuctf2021"
while (16 - len(key) != len(k)):
    key = key + padding[i]
    i += 1
key = key + k
key = bytes(key, encoding='ascii')

with open('flag.txt', 'rb') as f:
    flag = f.read()

iv = bytes("kono DIO daaaaaa", encoding = 'ascii')
cipher = AES.new(key, AES.MODE_CFB, iv)
ciphertext = cipher.encrypt(flag)

print(ciphertext.hex())

The hint is to exchange Diffie Hellman keys, but they can be exploded. Pay attention to the hex code. Catch a jo Chef

# !/usr/bin/env python
# -*- coding:utf-8 -*-
from Crypto.Cipher import AES
from binascii import unhexlify

ciphertext = 'b31699d587f7daf8f6b23b30cfee0edca5d6a3594cd53e1646b9e72de6fc44fe7ad40f0ea6'
gpList = [[13, 19], [7, 17], [3, 31], [13, 19], [17, 23], [2, 29]]
iv = bytes("kono DIO daaaaaa", encoding='ascii')
for g, p in gpList:
	for a in range(1, p+1):
		for b in range(1, p+1):
			k = str(pow(g, a * b, p))
			key = ''
			i = 0
			padding = 'uiuctf2021uiuctf2021'
			while 16 - len(key) != len(k):
				key = key + padding[i]
				i += 1
			key = key + k
			key = bytes(key, encoding='ascii')
			cipher = AES.new(key, AES.MODE_CFB, iv)
			flag = cipher.decrypt(unhexlify(ciphertext))
			if flag.startswith(b'uiuctf'):
				print(flag)

Crypto-back_to_basics

from Crypto.Util.number import long_to_bytes, bytes_to_long
from gmpy2 import mpz, to_binary
#from secret import flag, key

ALPHABET = bytearray(b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ#")

def base_n_encode(bytes_in, base):
	return mpz(bytes_to_long(bytes_in)).digits(base).upper().encode()

def base_n_decode(bytes_in, base):
	bytes_out = to_binary(mpz(bytes_in, base=base))[:1:-1]
	return bytes_out

def encrypt(bytes_in, key):
	out = bytes_in
	for i in key:
		print(i)
		out = base_n_encode(out, ALPHABET.index(i))
	return out

def decrypt(bytes_in, key):
	out = bytes_in
	for i in key:
		out = base_n_decode(out, ALPHABET.index(i))
	return out

"""
flag_enc = encrypt(flag, key)
f = open("flag_enc", "wb")
f.write(flag_enc)
f.close()
"""

Of course, there is a flag_ The ENC file and word are too big. You should bear it~

Very interesting topic. Master Shang woke me up, that is, the conversion of 2 ~ 36 base systems and the key functions

def base_n_decode(bytes_in, base):
	bytes_out = long_to_bytes(int(bytes_in, base=base))
	return bytes_out

I have to say that python's int is quite perfect

Then I didn't write a one-time script and tore it by hand, because for example, the last digit of the key is W-ary, but U and V don't appear in the ciphertext. Isn't the script perfect

Crypto-dhke_adventure

It's also the problem of chef jo. It's obvious that the cipher problem of discrete logarithm system. Because hint has smoother, it can be thought of as the smooth number problem of dissociating discrete logarithm

from random import randint
from Crypto.Util.number import isPrime
from Crypto.Cipher import AES
from hashlib import sha256

print("I'm too lazy to find parameters for my DHKE, choose for me.")
print("Enter prime at least 1024 at most 2048 bits: ")
# get user's choice of p
p = input()
p = int(p)
# check prime valid
if p.bit_length() < 1024 or p.bit_length() > 2048 or not isPrime(p):
    exit("Invalid input.")
# prepare for key exchange
g = 2
a = randint(2,p-1)
b = randint(2,p-1)
# generate key
dio = pow(g,a,p)
jotaro = pow(g,b,p)
key = pow(dio,b,p)
key = sha256(str(key).encode()).digest()

with open('flag.txt', 'rb') as f:
    flag = f.read()

iv = b'uiuctf2021uiuctf'
cipher = AES.new(key, AES.MODE_CFB, iv)
ciphertext = cipher.encrypt(flag)

print("Dio sends: ", dio)
print("Jotaro sends: ", jotaro)
print("Ciphertext: ", ciphertext.hex())

Known

d i o = g a   m o d   p dio=g^a\ mod\ p dio=ga mod p

j o t a r o = g b   m o d   p jotaro=g^b\ mod\ p jotaro=gb mod p

k e y = d i o b   m o d   p key=dio^b\ mod\ p key=diob mod p

The key to solving the problem is that p can be provided by the attacker. It's good to directly provide a smooth number + 1. In this way, the problem of solving DLP discrete logarithm becomes computable. For specific derivation, refer to pohlig Hellman attack principle, which can be reproduced

Some small points are directly scripted here. First, a smooth number is generated

from Crypto.Util.number import *
from gmpy2 import *

item = 2
p = 1
while 1:
    p *= item
    item = next_prime(item)
    if 1024 < p.bit_length() < 2048 and isPrime(p + 1):
        print(p+1)
        break

Lazy dog, directly receive manually, and then find a and b in sgae; you raise me up of WANGDING cup Qinglong group has been demonstrated before. The final script is as follows

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Cipher import AES
from binascii import unhexlify
from hashlib import sha256

p = 20404068993016374194542464172774607695659797117423121913227131032339026169175929902244453757410468728842929862271605567818821685490676661985389839958622802465986881376139404138376153096103140834665563646740160279755212317501356863003638612390661668406235422311783742390510526587257026500302696834793248526734305801634165948702506367176701233298064616663553716975429048751575597150417381063934255689124486029492908966644747931
a = 1706514538409217609380184471483970486111601581179909732547081795755601848099714892783660477820125268651686071436129197042742970850298506167342840465201954449860957095450958345421698689291401652404764413822088773220645950460091121901749681603487210460064157239812734204314334694247020508700704643391774792654182943517813353983667531492481271292815208332729128642069655591289999915464139472699283049217926350224615448461688066
b = 14466632275269913037069509287532932279884213206515600538938395240468925268146374104530207395909468350257778074856758057525057561502009116834555962090895520108072724285442706303410135968614985268443945781896987070215220814783356743958044861941110284092061774699164461968593879814604175818707171934938878476262296832165434644133455246857171139473183371584687955863862012348287764247314297996868855594079156695942227229848338865
dio = 16167922424137241666246302388507094614219683075656937696624976269971219170930381539629329712953190516880647147637220034502113432117052541565403357952291692484827614380683288824529855182911429985823152492290185107775580207164746921109360997835543504066225649925051809637197356760554700827177215351020335554912004088973554778245562345835228507025035376705826984822212122075068467338353809039042992813485576139824709322511611135
assert dio == pow(g, a, p)
assert jotaro == pow(g, b, p)
key = pow(dio, b, p)
key = sha256(str(key).encode()).digest()
jo = 8478318935358501390014596426908185869306605857407596116840606911586830527583698320042764633027826654399751539088096707226001027141331485792640154020208368663060333097725346477808040686885134694513306486060638308544451042554707764114958622916238232491752753035945375341164707378481832386716731243612254449779955395389475168461403985894687650228947467124029857674022920610930987210652487673276585269753595356252512713370074208
ciphertext = 'f21c554f4e520e3122eb9708bd88de356b7d0d8d9728536b39d22b706afcdaecd7bed753666a763f8c0d'
iv = b'uiuctf2021uiuctf'
cipher = AES.new(key, AES.MODE_CFB, iv)
flag = cipher.decrypt(unhexlify(ciphertext))
print(flag)

The newly configured windows terminal is really nice. It won't be disordered. Show it

Crypto CTF

At that time, it was over before I had time to fight, so I posted a flag on everything I made. Thank you. The organizers don't care

Sign in and don't say

Farm

#!/usr/bin/env sage

from sage.all import *
import string, base64, math
from flag import flag

ALPHABET = string.printable[:62] + '\\='

F = list(GF(64))

def keygen(l):
	key = [F[randint(1, 63)] for _ in range(l)] 
	key = math.prod(key) # Optimization the key length :D
	return key

def maptofarm(c):
	assert c in ALPHABET
	return F[ALPHABET.index(c)]

def encrypt(msg, key):
	m64 = base64.b64encode(msg)
	enc, pkey = '', key**5 + key**3 + key**2 + 1
	for m in m64:
		enc += ALPHABET[F.index(pkey * maptofarm(chr(m)))]
	return enc

# KEEP IT SECRET 
key = keygen(14) # I think 64**14 > 2**64 is not brute-forcible :P

enc = encrypt(flag, key)
print(f'enc = {enc}')

Originally, I saw that it was abandoned and set several layers. Isn't GF a finite field? How did it come out???

But now I want to reproduce it. I've hardened my head. I didn't expect to come out for the first time. It's similar to the relationship between dictionary key and value. Just blow up the key. Originally, I wanted to draw a flow chart, but that is, the replacement of some positions and values from the alphabet to f and F to the alphabet is not well reflected. Since it is a simple question, I won't draw it. Paste script

import string
import base64
enc = '805c9GMYuD5RefTmabUNfS9N9YrkwbAbdZE0df91uCEytcoy9FDSbZ8Ay8jj'
ALPHABET = string.printable[:62] + '\\='

F = list(GF(64))

for pkey in F:
    c = ''
    for i in enc:
        p1 = ALPHABET.index(i)
        p2 = 0
        if pkey!= 0:
            p2 = F.index(F[p1] / pkey)
        c += ALPHABET[p2]
    try:
        print(base64.b64decode(c))
    except:
        pass

Then find the flag in the 64 results

CCTF{EnCrYp7I0n_4nD_5u8STitUtIn9_iN_Fi3Ld!}