Home cyberapocarypse 2022 writeup
Post
Cancel

cyberapocarypse 2022 writeup

去年初めて参加した大会がこの大会で1問しか解けなかった。 でも、今年はcrypto 7/10解けたんで割と満足してます..

[crypto] Android-In-The-Middle [505 solve]

chall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
import hashlib
import random
import socketserver
import signal


FLAG = "HTB{--REDACTED--}"
DEBUG_MSG = "DEBUG MSG - "
p = 0x509efab16c5e2772fa00fc180766b6e62c09bdbd65637793c70b6094f6a7bb8189172685d2bddf87564fe2a6bc596ce28867fd7bbc300fd241b8e3348df6a0b076a0b438824517e0a87c38946fa69511f4201505fca11bc08f257e7a4bb009b4f16b34b3c15ec63c55a9dac306f4daa6f4e8b31ae700eba47766d0d907e2b9633a957f19398151111a879563cbe719ddb4a4078dd4ba42ebbf15203d75a4ed3dcd126cb86937222d2ee8bddc973df44435f3f9335f062b7b68c3da300e88bf1013847af1203402a3147b6f7ddab422d29d56fc7dcb8ad7297b04ccc52f7bc5fdd90bf9e36d01902e0e16aa4c387294c1605c6859b40dad12ae28fdfd3250a2e9
g = 2


class Handler(socketserver.BaseRequestHandler):
    def handle(self):
        signal.alarm(0)
        main(self.request)


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


def sendMessage(s, msg):
    s.send(msg.encode())


def recieveMessage(s, msg):
    sendMessage(s, msg)
    return s.recv(4096).decode().strip()


def decrypt(encrypted, shared_secret):
    key = hashlib.md5(long_to_bytes(shared_secret)).digest()
    cipher = AES.new(key, AES.MODE_ECB)
    message = cipher.decrypt(encrypted)
    return message


def main(s):
    sendMessage(s, DEBUG_MSG + "Generating The Global DH Parameters\n")
    sendMessage(s, DEBUG_MSG + f"g = {g}, p = {p}\n")
    sendMessage(s, DEBUG_MSG + "Calculation Complete\n\n")

    sendMessage(s, DEBUG_MSG + "Generating The Public Key of CPU...\n")
    c = random.randrange(2, p - 1)
    C = pow(g, c, p)
    sendMessage(s, DEBUG_MSG + "Calculation Complete\n")
    sendMessage(s, DEBUG_MSG + "Public Key is: ???\n\n")

    M = recieveMessage(s, "Enter The Public Key of The Memory: ")

    try:
        M = int(M)
    except:
        sendMessage(s, DEBUG_MSG + "Unexpected Error Occured\n")
        exit()

    sendMessage(s, "\n" + DEBUG_MSG + "The CPU Calculates The Shared Secret\n")
    shared_secret = pow(M, c, p)
    sendMessage(s, DEBUG_MSG + "Calculation Complete\n\n")

    encrypted_sequence = recieveMessage(
        s, "Enter The Encrypted Initialization Sequence: ")

    try:
        encrypted_sequence = bytes.fromhex(encrypted_sequence)
        assert len(encrypted_sequence) % 16 == 0
    except:
        sendMessage(s, DEBUG_MSG + "Unexpected Error Occured\n")
        exit()

    sequence = decrypt(encrypted_sequence, shared_secret)

    if sequence == b"Initialization Sequence - Code 0":
        sendMessage(s, "\n" + DEBUG_MSG +
                    "Reseting The Protocol With The New Shared Key\n")
        sendMessage(s, DEBUG_MSG + f"{FLAG}")
    else:
        exit()


if __name__ == '__main__':
    socketserver.TCPServer.allow_reuse_address = True
    server = ReusableTCPServer(("0.0.0.0", 1337), Handler)
    server.serve_forever()

solve

典型的なAES暗号と、DHの値を一意に定めよう問題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from Crypto.Util.number import *
from Crypto.Cipher import AES
import hashlib


p = 0x509efab16c5e2772fa00fc180766b6e62c09bdbd65637793c70b6094f6a7bb8189172685d2bddf87564fe2a6bc596ce28867fd7bbc300fd241b8e3348df6a0b076a0b438824517e0a87c38946fa69511f4201505fca11bc08f257e7a4bb009b4f16b34b3c15ec63c55a9dac306f4daa6f4e8b31ae700eba47766d0d907e2b9633a957f19398151111a879563cbe719ddb4a4078dd4ba42ebbf15203d75a4ed3dcd126cb86937222d2ee8bddc973df44435f3f9335f062b7b68c3da300e88bf1013847af1203402a3147b6f7ddab422d29d56fc7dcb8ad7297b04ccc52f7bc5fdd90bf9e36d01902e0e16aa4c387294c1605c6859b40dad12ae28fdfd3250a2e9
g = 2

m = b"Initialization Sequence - Code 0"
print(isPrime(p))

def encrypt(message, shared_secret):
    key = hashlib.md5(long_to_bytes(shared_secret)).digest()
    cipher = AES.new(key, AES.MODE_ECB)
    encrypted = cipher.encrypt(message)
    return encrypted

print(encrypt(m, 1).hex())
# HTB{7h15_p2070c0l_15_pr0tec73d_8y_D@nb3er_c0pyr1gh7_1aws}

[crypto] Jenny From The Block [312 solve]

chall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from hashlib import sha256
from Crypto.Util.Padding import pad, unpad
import signal
import subprocess
import socketserver
import os

allowed_commands = [b'whoami', b'ls', b'cat secret.txt', b'pwd']
BLOCK_SIZE = 32


def encrypt_block(block, secret):
    enc_block = b''
    for i in range(BLOCK_SIZE):
        val = (block[i]+secret[i]) % 256
        enc_block += bytes([val])
    return enc_block


def encrypt(msg, password):
    h = sha256(password).digest()
    print("firse_pass",h)
    if len(msg) % BLOCK_SIZE != 0:
        msg = pad(msg, BLOCK_SIZE)
    blocks = [msg[i:i+BLOCK_SIZE] for i in range(0, len(msg), BLOCK_SIZE)]
    ct = b''
    for block in blocks:
        enc_block = encrypt_block(block, h)
        h = sha256(enc_block + block).digest()
        ct += enc_block
    print("ct",ct)

    return ct.hex()


def run_command(cmd):
    if cmd in allowed_commands:
        try:
            resp = subprocess.run(
                cmd.decode().split(' '),  capture_output=True)
            output = resp.stdout
            return output
        except:
            return b'Something went wrong!\n'
    else:
        return b'Invalid command!\n'


def challenge(req):
    req.sendall(b'This is Jenny! I am the heart and soul of this spaceship.\n' +
                b'Welcome to the debug terminal. For security purposes I will encrypt any responses.')
    while True:
        req.sendall(b'\n> ')
        command = req.recv(4096).strip()
        output = run_command(command)
        response = b'Command executed: ' + command + b'\n' + output
        password = os.urandom(32)
        ct = encrypt(response, password)
        print("ct.hex",ct)
        req.sendall(ct.encode())


class incoming(socketserver.BaseRequestHandler):
    def handle(self):
        signal.alarm(30)
        req = self.request
        challenge(req)


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


def main():
    socketserver.TCPServer.allow_reuse_address = True
    server = ReusableTCPServer(("0.0.0.0", 1337), incoming)
    server.serve_forever()


if __name__ == "__main__":
    main()

solve

“Command executed: cat secret.txt”が丁度32文字なので鍵が復元できる これを繰り返して平文特定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from hashlib import sha256
from Crypto.Util.Padding import pad, unpad
from pwn import *
from Crypto.Util.number import *

BLOCK_SIZE = 32
io = remote("159.65.49.107",31207)
# io = remote("localhost",1337)
def decrypt_block_i(block,plain):
    return  (block-plain) % 256

def search_password(ct,plain):
    secret = b""
    for i in range(BLOCK_SIZE):
        k =  decrypt_block_i(ct[i],plain[i])
        secret+=bytes([k])
    print(secret)
    return secret
    
def encrypt_block(block, secret):
    enc_block = b''
    for i in range(BLOCK_SIZE):
        val = (block[i]-secret[i]) % 256
        enc_block += bytes([val])
    return enc_block


def encrypt(msg, password):
    # h = sha256(password).digest()
    h = password
    if len(msg) % BLOCK_SIZE != 0:
        msg = pad(msg, BLOCK_SIZE)
    blocks = [msg[i:i+BLOCK_SIZE] for i in range(0, len(msg), BLOCK_SIZE)]
    ct = b''
    # for block in blocks:
    for i in range(len(blocks)):
        dec_block = encrypt_block(blocks[i], h)
        h = sha256(blocks[i] + dec_block ).digest()
        ct += dec_block

    return ct
  


def connection(io):
    io.recvuntil("> ")
    command = b'cat secret.txt'
    io.sendline(command)
    ct = bytes.fromhex(io.recvline(None).decode())
    response = b'Command executed: ' + command + b'\n' + b'Invalid command!\n'
    print("ct",ct)
    print("ct[:32]",ct[:32])
    password = search_password(ct[:32],response[:32])
    print(encrypt(ct,password))
    
connection(io)
io.close()

[crypto] The Three-Eyed Oracle [264 solve]

chall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from tarfile import BLOCKSIZE
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import random
import signal
import subprocess
import socketserver
import os
# FLAG = b''
FLAG = b'HTB{--REDACTED--}'
# prefix = random.randbytes(12)
# key = random.randbytes(16)
prefix = os.urandom(12)
key = os.urandom(16)
print(prefix)

BLOCKSIZE = 16
def encrypt(key, msg):
    msg = bytes.fromhex(msg)
    crypto = AES.new(key, AES.MODE_ECB)
    padded = pad(prefix + msg + FLAG, 16)
    # print("padded",padded)
    print("padded",[padded[i*BLOCKSIZE:(i+1)*BLOCKSIZE] for i in range(len(padded)//BLOCKSIZE)])
    print("enc",[crypto.encrypt(padded)[i*BLOCKSIZE:(i+1)*BLOCKSIZE] for i in range(len(crypto.encrypt(padded))//BLOCKSIZE)])
    return crypto.encrypt(padded).hex()


def challenge(req):
    req.sendall(b'Welcome to Klaus\'s crypto lab.\n' +
                b'It seems like there is a prefix appended to the real firmware\n' +
                b'Can you somehow extract the firmware and fix the chip?\n')
    while True:
        req.sendall(b'> ')
        # try:
        msg = req.recv(4096).decode()
        print("msg decode  ",msg)

        ct = encrypt(key, msg)
        # except:
        #     req.sendall(b'An error occurred! Please try again!')

        req.sendall(ct.encode() + b'\n')


class incoming(socketserver.BaseRequestHandler):
    def handle(self):
        signal.alarm(1500)
        req = self.request
        challenge(req)


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


def main():
    socketserver.TCPServer.allow_reuse_address = True
    server = ReusableTCPServer(("0.0.0.0", 1337), incoming)
    server.serve_forever()


if __name__ == "__main__":
    main()

AES ECBのplaintext recovery attack 詳しくはggってもらって

solve

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import *
from tqdm import tqdm
import random

from pwn import *

io = remote("134.209.29.182",30681)
# io = remote("localhost",1337)
BLOCKSIZE = 16
PTSIZE = 32

def send_m(m):
        
    io.recvuntil(b"> ")
    # print("send_m",(b"1"*4+m).hex().encode())
    io.sendline((b"a"*4+m).hex().encode())
    re = bytes.fromhex(io.recvline(None).decode())
    return [re[i*BLOCKSIZE:(i+1)*BLOCKSIZE] for i in range(len(re)//BLOCKSIZE)]

def plaintxt_recavary():
    PTSIZE = 9
    print("PTSIZE",PTSIZE)
    # list
    m = b""
    for i in range(1,PTSIZE):
        target = send_m(b"0"*(BLOCKSIZE-i))[1]
        
        for k in tqdm(range(70,0x100)):
            a = b"0"*(BLOCKSIZE-i)+m+bytes([k])
            tmp = send_m(a)[1]    
            if tmp== target:
                m =m + bytes([k])
                break
    return m


print(plaintxt_recavary())

[crypto] How The Columns Have Turned [239 solve]

chall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import os


with open('super_secret_messages.txt', 'r') as f:
    SUPER_SECRET_MESSAGES = [msg.strip() for msg in f.readlines()]


def deriveKey(key):
    derived_key = []

    for i, char in enumerate(key):
        previous_letters = key[:i]
        new_number = 1
        for j, previous_char in enumerate(previous_letters):
            if previous_char > char:
                derived_key[j] += 1
            else:
                new_number += 1
        derived_key.append(new_number)
    return derived_key


def transpose(array):
    return [row for row in map(list, zip(*array))]


def flatten(array):
    return "".join([i for sub in array for i in sub])


def twistedColumnarEncrypt(pt, key):
    derived_key = deriveKey(key)
    print(derived_key)

    width = len(key)

    blocks = [pt[i:i + width] for i in range(0, len(pt), width)]
    print(blocks)
    blocks = transpose(blocks)
    print("blocks",blocks)
    print(derived_key.index(2))
    for i in range(width):
        print(derived_key.index(i + 1),blocks[derived_key.index(i + 1)])
    ct = [blocks[derived_key.index(i + 1)][::-1] for i in range(width)]
    print(ct)
    ct = flatten(ct)
    print(ct)
    return ct



class PRNG:
    def __init__(self, seed):
        self.p = 0x2ea250216d705
        self.a = self.p
        # self.b = int.from_bytes(os.urandom(16), 'big')
        self.b = 729513912306026
        self.rn = seed

    def next(self):
        self.rn = ((self.a * self.rn) + self.b) % self.p
        return self.rn


def main():
    seed = int.from_bytes(os.urandom(16), 'big')
    rng = PRNG(seed)

    cts = ""

    for message in SUPER_SECRET_MESSAGES:
        key = str(rng.next())
        ct = twistedColumnarEncrypt(message, key)
        cts += ct + "\n"

    with open('encrypted_messages.txt', 'w') as f:
        f.write(cts)

    dialog = "Miyuki says:\n"
    dialog += "Klaus it's your time to sign!\n"
    dialog += "All we have is the last key of this wierd encryption scheme.\n"
    dialog += "Please do your magic, we need to gather more information if we want to defeat Draeger.\n"
    dialog += f"The key is: {str(key)}\n"

    with open('dialog.txt', 'w') as f:
        f.write(dialog)


if __name__ == '__main__':
    main()

solve

PNRGかと思いきや\(a=p\)よりnext関数の出力値は\(b\)固定になり関係なくなる あとはブロック暗号を元に戻していくだけ

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


class PRNG:
    def __init__(self, seed):
        self.p = 0x2ea250216d705
        self.a = self.p
        self.b = 729513912306026
        self.rn = seed

    def next(self):
        self.rn = ((self.a * self.rn) + self.b) % self.p
        return self.rn

def deriveKey(key):
    derived_key = []
    # print("key",key)
    for i, char in enumerate(key):
        # print(" i, char", i, char)
        previous_letters = key[:i]
        new_number = 1
        for j, previous_char in enumerate(previous_letters):
            # print("i,j, previous_char ",i,j, previous_char )
            if previous_char > char:
                derived_key[j] += 1
            else:
                new_number += 1
        # print("new_number",new_number)
        # print("derived_key",derived_key)
        derived_key.append(new_number)
    return derived_key


def transpose(array):
    return [row for row in map(list, zip(*array))]


def flatten(array):
    return "".join([i for sub in array for i in sub])


def twistedColumnarEncrypt(pt, key):
    derived_key = deriveKey(key)
    print(derived_key)

    width = len(key)

    blocks = [pt[i:i + width] for i in range(0, len(pt), width)]
    print(blocks)
    blocks = transpose(blocks)
    print(blocks)
    print(derived_key.index(1))
    print(blocks[derived_key.index(1)][::-1])

    ct = [blocks[derived_key.index(i + 1)][::-1] for i in range(width)]
    print(ct)
    ct = flatten(ct)
    print(ct)
    return ct

twistedColumnarEncrypt("123456789012345543210987654321", str(key))

[crypto] MOVs Like Jagger [107 solve]

chall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from ecdsa import ellipticcurve as ecc
from random import randint

a = -35
b = 98
p = 434252269029337012720086440207
Gx = 16378704336066569231287640165
Gy = 377857010369614774097663166640
ec_order = 434252269029337012720086440208

E = ecc.CurveFp(p, a, b)
G = ecc.Point(E, Gx, Gy, ec_order)


def generateKeyPair():
    private = randint(1, 2**64)
    public = G * private
    return(public, private)


def calculatePointsInSpace():
    Q, nQ = generateKeyPair()
    P, nP = generateKeyPair()
    return [Q, nQ, P, nP]


def checkCoordinates(data: dict) -> list:
    if data['destination_x'] == "" or data['destination_y'] == "":
        raise ValueError('Empty coordinates...')

    try:
        destination_x = int(data['destination_x'], 16)
        destination_y = int(data['destination_y'], 16)
    except:
        raise ValueError('Coordinates are not in the right format (hex)')

    return (destination_x, destination_y)


def checkDestinationPoint(data: dict, P: ecc.Point, nQ: int, E: ecc.CurveFp) -> list:
    # destination_x, destination_y = checkCoordinates(data)
    destination_x, destination_y = data
    destination_point = ecc.Point(E, destination_x, destination_y, ec_order)
    secret_point = P * nQ
    print("secret_point = P * nQ",secret_point , P , nQ)
    same_x = destination_point.x() == secret_point.x()
    same_y = destination_point.y() == secret_point.y()

    if (same_x and same_y):
        return True
    else:
        return False


if "__main__"==__name__:
    Q, nQ, P, nP = calculatePointsInSpace()
    print(Q, nQ, P, nP )
    checkDestinationPoint([Gx, Gy],P,nQ,E)

solve

典型的なECCの問題。ただ、\(p\)が素数でないので因数分解を施すといい感じにばらけたのでPohlig–Hellman algorithmで解いていく

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from sage.all import *

#素数は小さければ小さいほうがいいが大きいものも場合によっては必要

#[s]P1 = P2
# fac = Ep.order())
def Pohlig_Hellman(P1,P2,fac):
    primes = []
    for i in range(len(fac)-1):
        primes.append(fac[i][0]**fac[i][1])
        #primes =[ 7 , 11 , 17 , 191 , 317 , 331 , 5221385621 , 5397618469 , 210071842937040101 , 637807437018177170959577732683]
    dlogs = []
    for fac in primes[:]:
        t = int(P1.order()) // int(fac)
        dlog = (t*P1).discrete_log(t*P2) #discrete_log(t*sGq, t*Gq, operation="+")
        dlogs += [dlog]
        print("factor: "+str(fac)+", Discrete Log: "+str(dlog)) #calculates discrete logarithm for each prime order
    return crt(dlogs, primes[:])


a = -35
b = 98
p = 434252269029337012720086440207
Gx = 16378704336066569231287640165
Gy = 377857010369614774097663166640
ec_order = 434252269029337012720086440208

E = EllipticCurve(GF(p),[a,b])
{"departed_x":"0x3b41ebf4c4afc44b98bc8542","departed_y":"0xd8d92015d026528a7dbc3309","present_x":"0x2f8756f6476af7a24952eb8e3","present_y":"0x1a61b777121c1d25bfd6c2f48"}

G = E(Gx,Gy)
Q = E(0x3b41ebf4c4afc44b98bc8542,0xd8d92015d026528a7dbc3309)
P = E(0x2f8756f6476af7a24952eb8e3,0x1a61b777121c1d25bfd6c2f48)
nP = Pohlig_Hellman(G,P,factor(ec_order))
print(nP,(360301137196997).bit_length())
for i in range(360301137196997):
    if ((ec_order//360301137196997)*i+nP)*G==P:
        print(i)
        break
nP = (ec_order//360301137196997)*i+nP
nQ = Pohlig_Hellman(G,Q,factor(ec_order))
print(nQ,(360301137196997).bit_length())
for i in range(360301137196997):
    if ((ec_order//360301137196997)*i+nQ)*G==Q:
        print(i)
        break
nQ=(ec_order//360301137196997)*i+nQ

print(nP*(nQ*G))

print()

[crypto] Find Marher’s Secret [70 solve]

chall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import random
import signal
import subprocess
import socketserver
import json
import os
from Crypto.Cipher import ARC4, AES
import os
import hashlib
from secret import FLAG, KEY


def encrypt(key, iv, pt):
    return ARC4.new(iv + key).encrypt(pt).hex()


def challenge(req):
    key = bytes.fromhex(KEY)
    assert(len(key) == 27)
    req.sendall(b'Connected to the cyborg\'s debugging interface\n')
    while True:
        req.sendall(
            b'\nOptions:\n1. Encrypt your text.\n2. Claim the key.\n> ')
        try:
            response = json.loads(req.recv(4096).decode())
            if response['option'] == 'encrypt':
                iv = bytes.fromhex(response['iv'])
                pt = bytes.fromhex(response['pt'])
                ct = encrypt(key, iv, pt)
                payload = {'response': 'success',
                           'pt': response['pt'], 'ct': ct}
                payload = json.dumps(payload)
                req.sendall(payload.encode())
            elif response['option'] == 'claim':
                answer = bytes.fromhex(response['key'])
                if hashlib.sha256(answer).hexdigest() == hashlib.sha256(key).hexdigest():
                    payload = {'response': 'success', 'flag': FLAG}
                    payload = json.dumps(payload)
                    req.sendall(payload.encode())
                else:
                    payload = {'response': 'fail',
                               'message': 'Better luck next time.'}
                    payload = json.dumps(payload)
                    req.sendall(payload.encode())

            else:
                payload = {'response': 'error', 'message': 'Invalid option!'}
                payload = json.dumps(payload)
                req.sendall(payload.encode())
        except Exception as e:
            payload = json.dumps(
                {'response': 'error', 'message': 'An error occured!'})
            req.sendall(payload.encode())
            return


class incoming(socketserver.BaseRequestHandler):
    def handle(self):
        signal.alarm(6000)
        req = self.request
        challenge(req)


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


def main():
    socketserver.TCPServer.allow_reuse_address = True
    server = ReusableTCPServer(("0.0.0.0", 1337), incoming)
    server.serve_forever()


if __name__ == "__main__":
    main()

solve

RC4かつオラクルが無制限に使えるのでFluhrer-Mantin-Shamir attackで終わり

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
from Crypto.Cipher import ARC4, AES
from pwn import *
import json
from collections import Counter
from tqdm import tqdm
io = remote("157.245.33.77",32157)
io.recvuntil(b"> ")




def encrypt_oracle(iv,pt):
    # def _encrypt(self, iv, key, p):
    # return ARC4.new(iv + key).encrypt(p)
    payload = {'option': 'encrypt',
           'iv': iv.hex(),
           'pt': pt.hex()}
    # print(payload)
    payload = json.dumps(payload)
    io.sendline(payload.encode())
    a = io.recvline(None).decode()
    # print(a)
    response = json.loads(a)
    # print(response['pt'])
    # print(response['ct'])
    io.recvuntil(b"> ")
    return bytes.fromhex(response['ct'])
    

def possible_key_bit(key, c):
    s = [i for i in range(256)]
    j = 0
    for i in range(len(key)):
        j = (j + s[i] + key[i]) % 256
        tmp = s[i]
        s[i] = s[j]
        s[j] = tmp

    return (c[0] - j - s[len(key)]) % 256


def attack(encrypt_oracle, key_len):
    """
    Recovers the hidden part of an RC4 key using the Fluhrer-Mantin-Shamir attack.
    :param encrypt_oracle: the padding oracle, returns the encryption of a plaintext under a hidden key concatenated with the iv
    :param key_len: the length of the hidden part of the key
    :return: the hidden part of the key
    """
    key = bytearray([3, 255, 0])
    for a in range(key_len):
        key[0] = a + 3
        possible = Counter()
        for x in tqdm(range(256)):
            key[2] = x
            # iv ,pt
            c = encrypt_oracle(key[:3], b"\x00")
            possible[possible_key_bit(key, c)] += 1
        key.append(possible.most_common(1)[0][0])
        print(key)

    return key[3:]
# print(attack(encrypt_oracle,27))

key = b'\x1f\xec\x07\x87\xbd\x1aR\xad\xe6:7\x9a <+\xe9+\x98\x1e\xb1\x17\xda\xc4\x03N\xcc\xe0'
# def _encrypt(self, iv, key, p):
# return ARC4.new(iv + key).encrypt(p)
payload = {'option': 'claim',
        'key': key.hex()}
# print(payload)
payload = json.dumps(payload)
io.sendline(payload.encode())
a = io.recvline(None).decode()
print(a)
response = json.loads(a)
print(response['flag'])
# print(response['ct'])
io.recvuntil(b"> ")




[crypto] Down the Rabinhole [74 solve]

chall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from Crypto.Util.number import getPrime, isPrime, bytes_to_long
from Crypto.Util.Padding import pad
import os


FLAG = b"HTB{--REDACTED--}"


def getPrimes(coefficient):
    while True:
        a = getPrime(512)
        p = 3 * coefficient * a + 2
        if isPrime(p):
            break
    while True:
        b = getPrime(512)
        q = 3 * coefficient * b + 2
        if isPrime(q):
            break
    return p, q


def encrypt(message, coefficient):
    p, q = getPrimes(coefficient)
    n = p * q

    padded_message = bytes_to_long(pad(message, 64))
    message = bytes_to_long(message)

    c1 = (message * (message + coefficient)) % n
    c2 = (padded_message * (padded_message + coefficient)) % n
    return (n, c1, c2)


def main():
    coefficient = getPrime(128)
    out = ""

    message = FLAG[0:len(FLAG)//2]
    n1, c1, c2 = encrypt(message, coefficient)
    out += f"{n1}\n{c1}\n{c2}\n"

    message = FLAG[len(FLAG)//2:]
    n2, c3, c4 = encrypt(message, coefficient)
    out += f"{n2}\n{c3}\n{c4}"

    with open("out.txt", "w") as f:
        f.write(out)


if __name__ == '__main__':
    main()

solve

\(n1=(3*coff*a_{1}+2)(3*coff*b_{1}+2)\),\(n2=(3*coff*a_2+2)(3*coff*b_2+2)\) より\(coff = gcd(n1-4,n2-4)\) よって\(coff\)が特定できたのでmod \(n\) 上の二次多項式を解けば答えが出る

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from tqdm import tqdm
from Crypto.Util.Padding import pad
from Crypto.Util.number import *
n1 = 59695566410375916085091065597867624599396247120105936423853186912270957035981683790353782357813780840261434564512137529316306287245132306537487688075992115491809442873176686026221661043777720872604111654524551850568278941757944240802222861051514726510684250078771979880364039814240006038057748087210740783689350438039317498789505078530402846140787188830971536805605748267334628057592989
c1 = 206131769237721955001530863959688756686125485413899261197125641745745636359058664398433013356663394210624150086689905532
c2 = 14350341133918883930676906390648724486852266960811870561648194176794020698141189777337348951219934072588842789694987397861496993878758159916334335632468891342228755755695273096621152247970509517996580512069034691932835017774636881861331636331496873041705094768329156701838193429109420730982051593645140188946
n2 = 56438641309774959123579452414864548345708278641778632906871133633348990457713200426806112132039095059800662176837023585166134224681069774331148738554157081531312104961252755406614635488382297434171375724135403083446853715913787796744272218693049072693460001363598351151832646947233969595478647666992523249343972394051106514947235445828889363124242280013397047951812688863313932909903047
c3 = 429546912004731012886527767254149694574730322956287028161761007271362927652041138366004560890773167255588200792979452452
c4 = 29903904396126887576044949247400308530425862142675118500848365445245957090320752747039056821346410855821626622960719507094119542088455732058232895757115241568569663893434035594991241152575495936972994239671806350060725033375704703416762794475486000391074743029264587481673930383986479738961452214727157980946




## coff part
print(gcd(n1-4,n2-4),int(gcd(n1-4,n2-4)).bit_length())

for i in range(1,1<<(int(gcd(n1-4,n2-4)).bit_length()-128)):
    if gcd(n1-4,n2-4)%i==0:
        print(i)
coff = 263063435253385937926984981365320113271

assert isPrime(coff)


# c1 = x(x+coff) mod n
# c2 = (x*1<<k+l)*(x*1<<k+l+coff) mod n

m_t = []
def search(k,N,C1,C2):
    
    l = bytes_to_long(pad(b"1"*(64-k), 64)[-1*k:])
    # print(pad(b"1"*(64-k), 64))
    # print(pad(b"1"*(64-k), 64)[-1*k:])
    # print(long_to_bytes(bytes_to_long(b"1"*(64-k))<<(8*k)))
    # print(l)
    # exit()
    
    padding = 1<<(8*k)
    #c1
    C1 = C1
    C1_coff = coff
    
    # #c2
    C2 = (C2-l*(l+coff))*pow(padding,-2,N)
    C2_coff = (2*l+coff)*pow(padding,-1,N)
    
    #C2-C1 = (C2_coff-C1_coff)*m
    m = ((C2-C1)*pow(C2_coff-C1_coff,-1,N))%N
    # print(long_to_bytes(m))
    return  long_to_bytes(m)
    
for i in range(2,64):
    if b'HTB{' in search(i,n1,c1,c2):
        print(search(i,n1,c1,c2))
for i in range(2,64):
    if search(i,n2,c3,c4).endswith(b"}"):
        print(search(i,n2,c3,c4))


# 'HTB{gcd_+_2_*_R@6in_.|5_thi5_@_cro55over_epi5ode?}
This post is licensed under CC BY 4.0 by the author.