We played as "rbtree fan club" and placed 3rd overall.

 

babycrypto 1

 

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
#!/usr/bin/env python
from base64 import b64decode
from base64 import b64encode
import socket
import multiprocessing
 
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
import hashlib
import sys
 
class AESCipher:
    def __init__(self, key):
        self.key = key
 
    def encrypt(self, data):
        iv = get_random_bytes(AES.block_size)
        self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + self.cipher.encrypt(pad(data, 
            AES.block_size)))
 
    def encrypt_iv(self, data, iv):
        self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + self.cipher.encrypt(pad(data, 
            AES.block_size)))
 
    def decrypt(self, data):
        raw = b64decode(data)
        self.cipher = AES.new(self.key, AES.MODE_CBC, raw[:AES.block_size])
        return unpad(self.cipher.decrypt(raw[AES.block_size:]), AES.block_size)
 
flag = open("flag""rb").read().strip()
 
COMMAND = [b'test',b'show']
 
def run_server(client, aes_key, token):
    client.send(b'test Command: ' + AESCipher(aes_key).encrypt(token+COMMAND[0]) + b'\n')
    client.send(b'**Cipher oracle**\n')
    client.send(b'IV...: ')
    iv = b64decode(client.recv(1024).decode().strip())
    client.send(b'Message...: ')
    msg = b64decode(client.recv(1024).decode().strip())
    client.send(b'Ciphertext:' + AESCipher(aes_key).encrypt_iv(msg,iv) + b'\n\n')
    while(True):
        client.send(b'Enter your command: ')
        tt = client.recv(1024).strip()
        tt2 = AESCipher(aes_key).decrypt(tt)
        client.send(tt2 + b'\n')
        if tt2 == token+COMMAND[1]:
            client.send(b'The flag is: ' + flag)
            client.close()
            break
 
if __name__ == '__main__':
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(('0.0.0.0'16001))
    server.listen(1)
 
    while True:
        client, address = server.accept()
 
        aes_key = get_random_bytes(AES.block_size)
        token = b64encode(get_random_bytes(AES.block_size*10))[:AES.block_size*10]
 
        process = multiprocessing.Process(target=run_server, args=(client, aes_key, token))
        process.daemon = True
        process.start()
cs

 

Here, we are given

  • an AES-CBC encrypted result of some token appended with "test"
  • access to an AES-CBC encryption oracle, with full control of IV and message

and we are supposed to find the AES-CBC encrypted result of the token appended with "show".

 

We can easily see that we just need to change the last block. Therefore, we only need to change the last block of the ciphertext as well. 

We can simply XOR the next to last block of the ciphertext and the padded "show" string, and send it to the encryption oracle with 0 as IV.

The result of this encryption can be used as the last block of our final ciphertext, and this solves the problem. Here's my dirty code.

 

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
def bytexor(a, b):
    assert len(a) == len(b)
    return bytes(x ^ y for x, y in zip(a, b))
 
= remote('35.200.115.41''16001')
 
def get_data():
    t = r.recvline()
    print(t)
    t = t.rstrip(b"\n")
    t = t.split()[-1]
    if t[:11== b"Ciphertext:":
        t = t[11:]
    print(t)
    return t
 
 
inp = b64decode(get_data())
IV = inp[:16]
CTXT = inp[16:]
 
print(CTXT)
print(r.recvline())
 
print(r.recv(1024))
 
ss = b64encode(b"\x00" * 16)
r.sendline(ss)
 
print(r.recv(1024))
 
val = bytexor(pad(b'show'16), CTXT[9*16:10*16])
val = b64encode(val)
r.sendline(val)
 
cc = b64decode(get_data())
cc = cc[16:32]
 
print(len(cc))
vv = IV + CTXT[:160+ cc
 
vv = b64encode(vv)
 
print(r.recv(1024))
 
r.sendline(vv)
 
print(r.recv(1024))
cs

 

babycrypto 2

 

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
#!/usr/bin/env python
from base64 import b64decode
from base64 import b64encode
import socket
import multiprocessing
 
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
import hashlib
import sys
 
class AESCipher:
    def __init__(self, key):
        self.key = key
 
    def encrypt(self, data):
        iv = get_random_bytes(AES.block_size)
        self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + self.cipher.encrypt(pad(data, 
            AES.block_size)))
 
    def encrypt_iv(self, data, iv):
        self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + self.cipher.encrypt(pad(data, 
            AES.block_size)))
 
    def decrypt(self, data):
        raw = b64decode(data)
        self.cipher = AES.new(self.key, AES.MODE_CBC, raw[:AES.block_size])
        return unpad(self.cipher.decrypt(raw[AES.block_size:]), AES.block_size)
 
flag = open("flag""rb").read().strip()
 
AES_KEY = get_random_bytes(AES.block_size)
TOKEN = b64encode(get_random_bytes(AES.block_size*10-1))
COMMAND = [b'test',b'show']
PREFIX = b'Command: '
 
def run_server(client):
    client.send(b'test Command: ' + AESCipher(AES_KEY).encrypt(PREFIX+COMMAND[0]+TOKEN) + b'\n')
    while(True):
        client.send(b'Enter your command: ')
        tt = client.recv(1024).strip()
        tt2 = AESCipher(AES_KEY).decrypt(tt)
        client.send(tt2 + b'\n')
        if tt2 == PREFIX+COMMAND[1]+TOKEN:
            client.send(b'The flag is: ' + flag)
            client.close()
            break
 
if __name__ == '__main__':
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(('0.0.0.0'16002))
    server.listen(1)
 
    while True:
        client, address = server.accept()
 
        process = multiprocessing.Process(target=run_server, args=(client, ))
        process.daemon = True
        process.start()
cs

 

This time, we don't have access to any oracle, but we now have to change the first block of the decrypted text.

This can be easily done by simply changing the IV as needed. Here's another dirty code that does the job.

 

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
def bytexor(a, b):
    assert len(a) == len(b)
    return bytes(x ^ y for x, y in zip(a, b))
 
= remote('35.200.39.68''16002')
 
def get_data():
    t = r.recvline()
    t = t.rstrip(b"\n")
    t = t.split()[-1]
    return t
 
 
inp = b64decode(get_data())
IV = inp[:16]
CTXT = inp[16:]
 
 
print(r.recv(1024))
 
IV = bytexor(IV, pad(b"Command: test"16))
IV = bytexor(IV, pad(b"Command: show"16))
 
vv = IV + CTXT
vv = b64encode(vv)
 
r.sendline(vv)
print(r.recv(1024))
 
print(r.recv(1024))
cs

 

babycrypto 3

 

The value of $n, e, c$ are given, and we have to decrypt it. 

 

Using RsaCTFTool, we can factorize $n$ (cm-factor is used) and RSA-decrypt the value of $c$. 

The resulting value of $m$, when converted to bytes, looks like a base64 encoded value of a string.

 

We take some suffix of the results and base64 decode it, to get "CLOSING THE DISTANCE.".

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
= 31864103015143373750025799158312253992115354944560440908105912458749205531455987590931871433911971516176954193675507337
= 65537
= 10879776433900426660843740332190892429769159527203392037251077478777616065501519198653853699716123394455804888854401574
 
# python3 RsaCtfTool.py -n 31864103015143373750025799158312253992115354944560440908105912458749205531455987590931871433911971516176954193675507337 -e 65537 --uncipher 10879776433900426660843740332190892429769159527203392037251077478777616065501519198653853699716123394455804888854401574 --private
 
val = 0x00026067ff851ecdcb61e50b83a515e3005130785055306c4f527942555345556752456c545645464f5130557543673d3d0a
= 291664785919250248097148750343149685985101
= 109249057662947381148470526527596255527988598887891132224092529799478353198637
 
val = long_to_bytes(val)
print(val)
 
tt = b'Q0xPU0lORyBUSEUgRElTVEFOQ0UuCg==\n' # remove prefix
print(b64decode(tt))
cs

 

babycrypto 4

 

We are given a secp160r1 ECDSA signature data, with 32-bit nonces. Also, the first 16 bits of nonces are leaked.

 

It's trivial from the ECDSA definition that given one "full" data of $r, s, k, h$ we can retrieve the value of the secret key.

Therefore, by brute forcing the remaining unleaked 16 bits, we can create a set of 65536 candidates of the secret key for each dataset. 

By taking the intersection of such datasets, we can find the secret key, solving the problem. Here's another code.

 

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
= [0* 20
= [0* 20
= [0* 20
= [0* 20
 
= open('output.txt''r')
for i in range(020):
    v = f.readline()
    cc = v.split()
    r[i] = int(cc[0][2:], 16)
    s[i] = int(cc[1][2:], 16)
    k[i] = int(cc[2][2:], 16)
    h[i] = int(cc[3][2:], 16
 
= 0x0100000000000000000001F4C8F927AED3CA752257
 
s1 = set()
s2 = set()
 
for i in range(01 << 16):
    # sk = h + r * pvk mod n 
    cc = ((s[0* (k[0+ i) - h[0]) * inverse(r[0], n)) % n
    s1.add(cc)
 
for i in range(01 << 16):
    cc = ((s[1* (k[1+ i) - h[1]) * inverse(r[1], n)) % n
    s2.add(cc)
 
print(s1 & s2)
 
cs

 

 

'CTF' 카테고리의 다른 글

zer0lfsr 2021 Full Writeup (0CTF/TCTF 2021)  (2) 2021.07.05
zer0lfsr 2021 Quick Solutions/Thoughts  (0) 2021.07.05
zer0pts CTF 2021 Crypto Writeups  (1) 2021.03.07
AeroCTF 2021 Crypto Writeups  (0) 2021.02.28
UnionCTF Crypto Writeups  (2) 2021.02.21