Bài viết giải đề thi MTCTF lần thứ 4 (Mã hóa) - Schen

Bài viết giải đề thi MTCTF lần thứ 4 (Mã hóa)

Giới thiệu

Trong cuộc thi, mình đã giải 2 bài mã hóa, 1 bài Misc, đồng đội giải 2 bài Pwn và 1 bài RE. Tổng cộng có 241 đội đạt điểm, cuối cùng mình xếp hạng 36, còn cách vòng chung kết khá xa. Dưới đây là tổng hợp 2 bài mã hóa mà mình đã làm.

Ký hiệu đặc biệt

Bài này kiểm tra kiến thức về ký hiệu trong LaTeX

Một loạt ký hiệu lạ, dựa vào hiểu biết về chữ cái Hy Lạp, mình nhận ra quy luật là ánh xạ theo phát âm để tìm ra bản rõ.

Sau khi tra cứu nhiều tài liệu, phát hiện đây đều là ký hiệu trong LaTeX, nhanh chóng tìm được flag{fun_latex_math}

Tuy nhiên MD5 không khớp khi gửi, chú ý rằng chữ cái có cả in hoa và thường, nên sửa thành flag{fun_LaTeX_Math} --> gửi đúng.

Thừa nhận rằng MD5 mặc định ra 32 ký tự thường(không cần thử 4 trường hợp như mình)

RSA Bánh Hamburger

Đề bài:

from Crypto.Util.number import *

flag = open('flag.txt').read()
nbit = 64

while True:
    p, q = getPrime(nbit), getPrime(nbit)
    PP = int(str(p) + str(p) + str(q) + str(q))
    QQ = int(str(q) + str(q) + str(p) + str(p))
    if isPrime(PP) and isPrime(QQ):
        break

n = PP * QQ
m = bytes_to_long(flag.encode())
c = pow(m, 65537, n)
print('n =', n)
print('c =', c)


Một trong số p,q có 19 chữ số, một có 20 chữ số (vì nếu cả hai đều 20 hoặc 19 chữ số thì PP và QQ chắc chắn không phải số nguyên tố)

Rõ ràng 19 chữ số đầu của N chính là 19 chữ số đầu của p*q, 19 chữ số cuối của N là 19 chữ số cuối của p*q

Như vậy, p*q có 39 hoặc 40 chữ số, chỉ thiếu 1 chữ số, chỉ cần bạo lực chữ số còn lại rồi dùng sage để phân tích thừa số.

Script dưới đây chạy trong sage:

N=177269125756508652546242326065138402971542751112423326033880862868822164234452280738170245589798474033047460920552550018968571267978283756742722231922451193

low=str(N)[-19:]
high=str(N)[:19]

for i in ['']+[str(i) for i in range(10)]:
    n=int(high+i+low)
    f=factor(n)
    if len(f)==2:
        print(f)

Tìm được p,q là 9788542938580474429, 18109858317913867117

from Crypto.Util.number import*
PP=978854293858047442997885429385804744291810985831791386711718109858317913867117
QQ=181098583179138671171810985831791386711797885429385804744299788542938580474429
phi=(PP-1)*(QQ-1)
n=PP*QQ
c=47718022601324543399078395957095083753201631332808949406927091589044837556469300807728484035581447960954603540348152501053100067139486887367207461593404096
d=inverse(65537,phi)
m=pow(c,d,n)
print(long_to_bytes(m))

Tìm được flag

Máy mã hóa của Romeo

Bài này mình trì hoãn khá lâu mới quay lại

Khi thi thời gian đó mình quá yếu

Giờ nhìn lại thấy khá dễ

Trước tiên là đoạn code đề bài:

from Crypto.Util.number import*
from Crypto.Cipher import AES
from secret import msg,password,flag
import socketserver
import signal
assert len(msg) == 32
assert len(password) == 8

def padding(msg):
    return msg + bytes([0 for i in range((16 - len(msg))%16)])

class Task(socketserver.BaseRequestHandler):
    def _recvall(self):
        BUFF_SIZE = 2048
        data = b''
        while True:
            part = self.request.recv(BUFF_SIZE)
            data += part
            if len(part) < BUFF_SIZE:
                break
        return data.strip()

    def send(self, msg, newline=True):
        try:
            if newline:
                msg += b'\n'
            self.request.sendall(msg)       
        except:
            pass

    def recv(self):
        return self._recvall()

    def login(self):
        right_num = 0
        while 1:
            self.send(b'[~]Vui lòng nhập mật khẩu của bạn:')
            str1 = self.recv().strip()[:8]
            true_num = 0
            for i in range(len(password)):
                if str1[i] != password[i]:
                    login = False
                    self.send(b'Sai!')
                    break
                else:
                    true_num = i + 1 

                if right_num > true_num:
                    continue
                else:
                    right_num = true_num

                if true_num == len(password):
                    login = True
                check = b''
                for i in range(0x2000):
                    check = self.aes.encrypt(padding(check[:-1] + str1[:i+1]))

            if login == True:
                self.send(b"Đăng nhập thành công")
                return True,check[:16]
            
        return False

    def handle(self):
        signal.alarm(100)
        self.aes = AES.new(padding(password),AES.MODE_ECB)
        _,final_check = self.login()
        if _ == 1:
            assert msg.decode() == final_check.hex()
            self.send(b'Chào buổi sáng Master!')
            self.send(flag)
            
class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

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

if __name__ == "__main__":
    HOST, PORT = '0.0.0.0', 9999
    print("HOST:POST " + HOST+":" + str(PORT))
    server = ForkedServer((HOST, PORT), Task)
    server.allow_reuse_address = True
    server.serve_forever()

Phân tích:

Chỉ cần xem hàm login là đủ, các phần còn lại là thực hiện kết nối máy chủ

Chúng ta gửi mật khẩu, nó sẽ so sánh từng ký tự. Nếu ký tự đúng, sẽ vào vòng lặp mã hóa AES, tạo ra một giá trị check. Tuy nhiên giá trị này không liên quan đến chúng ta, không thể biết được, nhưng ý nghĩa của vòng lặp này là làm tiêu tốn thời gian đáng kể.

Do đó có ý tưởng: Liệt kê tất cả ký tự có thể nhìn thấy thành bảng, thực hiện bạo lực từng ký tự. Thời gian trên ký tự đúng sẽ rõ ràng lớn hơn thời gian trên các ký tự khác.

Code exploit (tham khảo từ blog 4XWi11):

from pwn import *
from tqdm import tqdm
from string import printable
import time
import sys

#context.log_level = 'debug'

table = printable
length = len(printable)

pwd = ''
t = pwd
index = 0
i = 0
tip = 1

for _ in range(8):
    while 1:
        io = remote('127.0.0.1', 9999)
        tip = 1
        try:
            signal.alarm(105)
            for i in tqdm(range(index, length)):
                t = pwd + table[i]
                io.recvline()
                io.sendline(t.encode())
                feedback = io.recvline()
                if b'Sai!' in feedback:
                    tip = 0
                    continue
                elif b'Thành công' in feedback:
                    pwd = t
                    tip = 1
                    assert 1 == 0
            signal.alarm(0)
        except:
            io.close()
            if tip:
                pwd = t
                if len(pwd) == 8:
                    print(pwd)
                    io.recvline()
                    io.recvline()
                    sys.exit(0)
                index = 0
                break
            else:
                index = i
                io.close()
                continue

Thẻ: LaTeX RSA aes MD5 python

Đăng vào ngày 22 tháng 6 lúc 06:48