HCMUS CTF 2023
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:
Đây là đoạn code khai báo callable_funcs[]
và 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!