4 minute read

Trường mình vừa mới tổ chức contest HCMUS CTF warm-up và bằng cách thần kì nào đó team tụi mình đã đạt được giải 3. Sau đây là wu mình viết cho các bài mình đã giải được.

Sanity check

Bài này chỉ đơn giản gõ @ vào chatbox discord để xem các role hiện có. Trong đó sẽ có 1 role là flag của bài.

Simple RSA

Tóm tắt đề bài

from Crypto.Util.number import * 
import hashlib

p = getPrime(1024) 
q = getPrime(1024) 
n = p*q 

e = 65537

flag = b'Phan&^%$#{}_+)_)(&('

m = len(flag) 
if m%2: 
    flag+= bytes(0x01) 


def dbytes2int(b): 
    return b[0]*256+b[1] 


ciphertxt = b''
for i in range(0, len(flag), 2): 
    plt = dbytes2int(flag[i:i+2])
    c = pow(plt,e,n) 
    h = hashlib.sha256(long_to_bytes(c)).hexdigest()
    print(h)
    k = bytes.fromhex(h[:8])
    ciphertxt+= k
with open("enc_msg.bin", "wb") as f: 
    f.write(ciphertxt)

và 1 file enc_msg.bin cho trước để tìm ra flag ban đầu.

Ý tưởng

Bởi vì lúc encrypt rsa mình chỉ enc với 2 ký tự liên tiếp nên mình sẽ có hướng tiếp cận là brute force các cặp ký tự có thể xảy ra và thử với từng vị trí trong xâu. Mình sẽ thử tới khi mã hash tính được trùng với mã hash cho trước trong file bin.

Code decryption

from Crypto.Util.number import *
import hashlib

n = 23224532538354093672379357099376204562765147218316169465645631030957066881732589286654782640372013272274528713165551271730292269147414997004559012778878128775588268363947779256188992480976868953812179421618313169703672076733185168584410241693820588070557296210563343817050007107667370776509728217694854166447627310658869597326213622488489021054114287785644335297167271107224670984582289270217243923884072000743914363499422589262915277524608721287047038750520500253158814039348850275152914016416818428489361530148705780071300717813798191258416054338676752365781072907126134837206342531010254399078830958070503700507373
encmsg = ''

with open('enc_msg.bin', 'rb') as f:
    encmsg = f.read()

print(encmsg)

table = [[0 for x in range(128)] for y in range(128)]

for i in range(128):
    for j in range(128):
        c = pow(i * 256 + j, 65537, n)
        h = hashlib.sha256(long_to_bytes(c)).hexdigest()
        k = bytes.fromhex(h[:8])
        table[i][j] = k

for t in range(0, len(encmsg), 4)
    sub = encmsg[t:t+4]
    for i in range(0, 128):
        for j in range(0, 128):
            if table[i][j] == sub:
                print(chr(i) + chr(j), end='')

print()

Flag

HCMUS-CTF{r54-!5-51Mp 3-r1gH+?}

IFLOW

Đề bài

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

#define ARG "cat /home/iflow/flag"
#define MAX 3

int main(short argc, char **argv)
{
	char *helper[] = {"strlen", "atoi", "printf", "puts"};
	void (*callable_funcs[])(char *) = {strlen, atoi, printf, puts};
	void (*uncallable_funcs[])(char *) = {system};
	short i, pos = 0;

	setresuid(geteuid(), geteuid(), geteuid());

	for (i = 1; i < argc; i++) {
		pos += strlen(argv[i]);
	}

	if (pos <= MAX) {
		(callable_funcs[MAX-1])("Calling ");
		(callable_funcs[MAX-1])(helper[pos]);
		(callable_funcs[MAX-1])(".\n");
		(callable_funcs[pos])(ARG);
	} else {
		(callable_funcs[MAX])("Out of bounds !\n");
	}

	return 0;
}

Ý tưởng

Sau khi dùng gdb để đọc code thì mình thấy như sau:

assembly code

Đây là đoạn code khai báo callable_funcs[]uncallable_funcs[]. Để ý kĩ mình sẽ thấy địa chỉ của uncallable_funcs[0] bắt đầu ở ebp-0x40, đồng thời nằm trước 4 bytes so với callable_funcs[0] bắt đầu ở ebp-0x3c. Như vậy chỉ cần gọi callable_funcs[-1] là nó sẽ gọi tới uncallable_funcs[0] (mỗi phần tử trong mảng đều chiếm 4 bytes vì nó là pointer).

Để ý thì pos là biến đếm nhưng nó có kiểu short nên mình có thể khiến nó tràn số để nhận lại giá trị -1 được.

Như vậy mình chỉ cần truyền vào 65535 ký tự bất kì để lỗi tràn số xảy ra khiến pos có giá trị là -1.

Flag

HCMUS-CTF{so_you_know_how_to_overflow_a_number}

NOR machine

Tóm tắt đề

#!/usr/bin/python3
import pickle
import sys

mem = []
final = [93, 103, 205, 127, 111, 17, 115, 110, 52, 127]

def NOR_machine(data):
    with open("image.bin", "rb") as f:
        mem = pickle.load(f)
        
    mem[60001:60011] = data
        
    while True:
        if mem[4] == 1:
            print("VM DONE")
            break
        i = mem[0]
        a1 = mem[i]
        a2 = mem[i + 1]
        a3 = mem[i + 2]
        
        tmp = (~(mem[a1] | mem[a2])) & 0xffff

        mem[a3] = tmp
        mem[1] = ((tmp >> 15) & 1) | ((tmp & 0x7FFF) << 1)
        mem[0] = i + 3
       
    return mem[60011:60021]

user_name = input("Username: ").strip()

l = len(user_name)

if not (1 <= l <= 5):
    print("Incorrect format!")
    sys.exit(1)
    
key = input("Key: ").strip()

if not (len(key) == 3 * l):
    print("Incorrect format!")
    sys.exit(1)

if not (key[2*l:] == user_name[::-1]):
    print("Incorrect signature")
    sys.exit(2)
    
# Check passed ! proceed to VM
data = [i^j for i,j in zip(key[:2*l].encode(), user_name.encode() * 2)]
print(data)

if NOR_machine(data) == final:
    print("Correct key!, wraps your key with HCMUS-CTF{}")
else:
    print("Incorrect key!")

Ý tưởng

Mình có mem[0] = 43 và từ phần tử thứ 43 tới khoảng phần tử thứ 20k, mình có bộ ba mem[i], mem[i + 1], mem[i + 2] là vị trí của 3 vị trí khác tạm gọi là a1, a2, a3, sau đấy gán mem[a3] = mem[a1] nor mem[a2] (phép nor được định nghĩa ở (~(mem[a1] | mem[a2])) & 0xffff).

Sau khi phân tích mảng mem, mình nhận ra mảng data có length là 10 và thứ tự encrypt đi từ cuối về đầu. Nên mình có thể brute force từng kí tự từ cuối lên đầu. Sau đấy gọi nor_machine() để check lại với mảng final.

Code

#!/usr/bin/python3
import pickle
import sys

mem = []
final = [93, 103, 205, 127, 111, 17, 115, 110, 52, 127]

def NOR_machine(data):
    with open("image.bin", "rb") as f:
        mem = pickle.load(f)
        
    mem[60001:60011] = data
        
    while True:
        if mem[4] == 1:
            print("VM DONE")
            break
        i = mem[0]
        a1 = mem[i]
        a2 = mem[i + 1]
        a3 = mem[i + 2]
        
        tmp = (~(mem[a1] | mem[a2])) & 0xffff

        mem[a3] = tmp
        mem[1] = ((tmp >> 15) & 1) | ((tmp & 0x7FFF) << 1)
        mem[0] = i + 3
       
    cnt = 0
    for i in range(9, -1, -1):
        if data[i] == -1:
            break
        cnt += 1
        
    for i in range(cnt):
        if mem[60011 + i] != final[i]:
            return False
    return True

data = [-1] * 10
key = 'HCMUS'
ans = ['*'] * 10

for j in range(9, -1, -1):
    for i in range(128):
        data[j] = i
        if NOR_machine(data):
            ans[j] = chr(i ^ ord(key[j % 5]))
            break

print(''.join(ans))

Flag

Sau khi chạy code trên mình thu được string w3ird_n0r_. Đem xâu này chạy lại chương trình đầu với username: HCMUS, key: w3ird_n0r_SUMCH để lấy format của flag.

HCMUS-CTF{w3ird_n0r_SUMCH}

Wu lần này được speedrun để nộp cho btc nên chưa được trau truốt :< Mong mọi người thông cảm!