# WU Bi0sCTF
## BaeBPF
* server nc đã bị đóng nên em xin mượn tạm ảnh của bạn em ạ

* Load vào server sẽ có 2 option 1 là `Generate assembly dump` và 2 là `cat any file`
* ta chọn option 1, server sẽ dump cho ta 1 đoạn asm và các key với các giá trị tương ứng cho từng key


* Tiếp đó là check thử các kí tự file name
```
25: (85) call bpf_probe_read_compat#-115168
26: (71) r1 = *(u8 *)(r10 -24)
27: (55) if r1 != 0x66 goto pc+368
34: (85) call bpf_probe_read_compat#-115168
35: (71) r1 = *(u8 *)(r10 -24)
36: (55) if r1 != 0x6c goto pc+359
44: (85) call bpf_probe_read_compat#-115168
45: (71) r1 = *(u8 *)(r10 -24)
46: (55) if r1 != 0x61 goto pc+349
53: (85) call bpf_probe_read_compat#-115168
54: (71) r1 = *(u8 *)(r10 -24)
55: (55) if r1 != 0x67 goto pc+340
63: (85) call bpf_probe_read_compat#-115168
64: (71) r1 = *(u8 *)(r10 -24)
65: (55) if r1 != 0x2e goto pc+330
72: (85) call bpf_probe_read_compat#-115168
73: (71) r1 = *(u8 *)(r10 -24)
74: (55) if r1 != 0x74 goto pc+321
82: (85) call bpf_probe_read_compat#-115168
83: (71) r1 = *(u8 *)(r10 -24)
84: (55) if r1 != 0x78 goto pc+311
91: (85) call bpf_probe_read_compat#-115168
92: (71) r1 = *(u8 *)(r10 -24)
93: (55) if r1 != 0x74 goto pc+302
```
```
25: (85) call bpf_probe_read_compat#-115168
26: (71) r1 = *(u8 *)(r10 -24)
27: (55) if r1 != 0x66 goto pc+368
Translation: If the value stored in r1 is not equal to 'f' (0x66), go to pc+368.
34: (85) call bpf_probe_read_compat#-115168
35: (71) r1 = *(u8 *)(r10 -24)
36: (55) if r1 != 0x6c goto pc+359
Translation: If the value stored in r1 is not equal to 'l' (0x6c), go to pc+359.
44: (85) call bpf_probe_read_compat#-115168
45: (71) r1 = *(u8 *)(r10 -24)
46: (55) if r1 != 0x61 goto pc+349
Translation: If the value stored in r1 is not equal to 'a' (0x61), go to pc+349.
53: (85) call bpf_probe_read_compat#-115168
54: (71) r1 = *(u8 *)(r10 -24)
55: (55) if r1 != 0x67 goto pc+340
Translation: If the value stored in r1 is not equal to 'g' (0x67), go to pc+340.
63: (85) call bpf_probe_read_compat#-115168
64: (71) r1 = *(u8 *)(r10 -24)
65: (55) if r1 != 0x2e goto pc+330
Translation: If the value stored in r1 is not equal to '.' (0x2e), go to pc+330.
72: (85) call bpf_probe_read_compat#-115168
73: (71) r1 = *(u8 *)(r10 -24)
74: (55) if r1 != 0x74 goto pc+321
Translation: If the value stored in r1 is not equal to 't' (0x74), go to pc+321.
82: (85) call bpf_probe_read_compat#-115168
83: (71) r1 = *(u8 *)(r10 -24)
84: (55) if r1 != 0x78 goto pc+311
Translation: If the value stored in r1 is not equal to 'x' (0x78), go to pc+311.
91: (85) call bpf_probe_read_compat#-115168
92: (71) r1 = *(u8 *)(r10 -24)
93: (55) if r1 != 0x74 goto pc+302
Translation: If the value stored in r1 is not equal to 't' (0x74), go to pc+302.
```
* Convert các hex sang ascii ta sẽ được file name là `flag.txt`
```
120: (15) if r6 == 0x0 goto pc+2
121: (61) r1 = *(u32 *)(r6 +0)
122: (a7) r1 ^= 5
```
* đoạn này sẽ check `r6`, nếu `r6` không bằng không, chương trình sẽ đọc một giá trị từ bộ nhớ, thực hiện phép XOR với 5, và lưu kết quả vào `r1`. ta viết solved script
```
key =[83,108,119,100,105,108,113,124]
print("".join(chr(i^5) for i in key))
```

* Ta sẽ được key đến level 2, nhập key vào và reload lại server

* Ta nhận được các output dạng hex và một hàm encryption
```
void encrypt_function():
0: (bf) r6 = r10
1: (07) r6 += -4
2: (18) r9 = 0xffffffe0
4: (b7) r8 = 0
; uint32_t v[2] = {0,0};
5: (b7) r1 = 0
6: (7b) *(u64 *)(r10 -8) = r1
7: (63) *(u32 *)(r10 -12) = r8
; uint32_t *temp = bpf_map_lookup_elem((void *)bpf_pseudo_fd(1, -1), &index);
8: (18) r1 = map[id:14]
10: (bf) r2 = r10
11: (07) r2 += -12
; uint32_t *temp = bpf_map_lookup_elem((void *)bpf_pseudo_fd(1, -1), &index);
12: (07) r1 += 272
13: (61) r0 = *(u32 *)(r2 +0)
14: (35) if r0 >= 0x400 goto pc+3
15: (67) r0 <<= 3
16: (0f) r0 += r1
17: (05) goto pc+1
18: (b7) r0 = 0
19: (bf) r7 = r0
; if (temp != NULL && *temp != 0)
20: (15) if r7 == 0x0 goto pc+78
21: (61) r1 = *(u32 *)(r7 +0)
22: (15) if r1 == 0x0 goto pc+76
23: (63) *(u32 *)(r10 -8) = r1
; uint32_t *temp2 = bpf_map_lookup_elem((void *)bpf_pseudo_fd(1, -2), &index);
24: (18) r1 = map[id:13]
26: (bf) r2 = r10
27: (07) r2 += -12
; uint32_t *temp2 = bpf_map_lookup_elem((void *)bpf_pseudo_fd(1, -2), &index);
28: (07) r1 += 272
29: (61) r0 = *(u32 *)(r2 +0)
30: (35) if r0 >= 0x400 goto pc+3
31: (67) r0 <<= 3
32: (0f) r0 += r1
33: (05) goto pc+1
34: (b7) r0 = 0
35: (15) if r0 == 0x0 goto pc+63
36: (61) r1 = *(u32 *)(r7 +0)
37: (15) if r1 == 0x0 goto pc+61
// Thuật toán mã hóa bắt đầu từ đây
38: (b7) r1 = 32
39: (18) r2 = 0x9e3779b9
41: (61) r7 = *(u32 *)(r10 -8)
42: (61) r4 = *(u32 *)(r0 +0)
43: (63) *(u32 *)(r10 -4) = r4
44: (bf) r3 = r4
45: (67) r3 <<= 4
46: (07) r3 += 305402420
47: (bf) r5 = r4
48: (0f) r5 += r2
49: (af) r3 ^= r5
50: (bf) r5 = r4
51: (5f) r5 &= r9
52: (77) r5 >>= 5
53: (07) r5 += 305402420
54: (af) r3 ^= r5
55: (0f) r3 += r7
56: (bf) r5 = r3
57: (67) r5 <<= 4
58: (07) r5 += 305402420
59: (bf) r0 = r2
60: (0f) r0 += r3
61: (af) r5 ^= r0
62: (bf) r0 = r3
63: (5f) r0 &= r9
64: (77) r0 >>= 5
65: (07) r0 += 305402420
66: (af) r5 ^= r0
67: (0f) r5 += r4
68: (07) r2 += -1640531527
69: (07) r1 += -1
70: (bf) r0 = r1
71: (67) r0 <<= 32
72: (77) r0 >>= 32
73: (bf) r4 = r5
74: (bf) r7 = r3
75: (15) if r0 == 0x0 goto pc+1
76: (05) goto pc-33
77: (63) *(u32 *)(r10 -4) = r5
78: (63) *(u32 *)(r10 -8) = r3
; bpf_map_update_elem((void *)bpf_pseudo_fd(1, -3), &index,&v[0], BPF_ANY);
79: (18) r1 = map[id:12]
81: (bf) r7 = r10
82: (07) r7 += -12
83: (bf) r3 = r10
84: (07) r3 += -8
; bpf_map_update_elem((void *)bpf_pseudo_fd(1, -3), &index,&v[0], BPF_ANY);
85: (bf) r2 = r7
86: (b7) r4 = 0
87: (85) call array_map_update_elem#296464
; bpf_map_update_elem((void *)bpf_pseudo_fd(1, -4), &index,&v[1], BPF_ANY);
88: (18) r1 = map[id:11]
; bpf_map_update_elem((void *)bpf_pseudo_fd(1, -4), &index,&v[1], BPF_ANY);
90: (bf) r2 = r7
91: (bf) r3 = r6
92: (b7) r4 = 0
93: (85) call array_map_update_elem#296464
94: (07) r8 += 1
95: (bf) r1 = r8
96: (67) r1 <<= 32
97: (77) r1 >>= 32
98: (55) if r1 != 0x80 goto pc-94
99: (95) exit
```
* Sau một vài hint và đọc lại thread ở câu lạc bộ thì em biết thêm một dạng encrypt mới ở bài này là `XTEA`
* XTEA là thuật toán mã hóa khối với kích thước khối dữ liệu là 64 bit và kích thước khóa là 128 bit. Bao gồm các bước như:
1. Chia Dữ Liệu: Dữ liệu cần mã hóa được chia thành các khối có kích thước 64 bit.
2. Rounds (Vòng Lặp): Mỗi khối dữ liệu sau đó được truyền qua một loạt các vòng lặp. Số vòng lặp thường được đề xuất là 64.
3. Key Schedule : Trước khi bắt đầu mã hóa, khóa 128 bit được chia thành 32 phần, mỗi phần được gọi là một từ khóa. Một phần của khóa được sử dụng trong mỗi vòng lặp mã hóa.
4. Subkey Generation (Tạo Khóa Phụ): Trong mỗi vòng lặp, các từ khóa được tạo ra từ từ khóa gốc và được sử dụng để mã hóa khối dữ liệu.
5. Feistel Network (Mạng Feistel): Trong mỗi vòng lặp, một mạng Feistel được áp dụng cho khối dữ liệu. Đây là quá trình mã hóa chính của XTEA.
6. Feistel Function (Hàm Feistel): Mạng Feistel bao gồm các bước lặp đi lặp lại sử dụng một hàm F. Hàm này bao gồm các phép toán như XOR, thay thế và dịch bit.
7. Output: Sau khi tất cả các vòng lặp được hoàn thành, khối dữ liệu đã được mã hóa sẽ được trả về.
* Từ đây ta có thể viết hàm để decrypt nguồn tham khảo mình sẽ để ở đây `https://cryptography.fandom.com/wiki/XTEA`
```
#include <iostream>
void decode(unsigned int& v0, unsigned int& v1) {
unsigned int k1 = 305402420;
unsigned int k2 = 305402420;
unsigned int k3 = 305402420;
unsigned int k0 = 305402420;
unsigned int delta = 0x9e3779b9;
unsigned int sum = delta * 32;
for (int i = 0; i < 32; ++i) {
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
sum -= delta;
}
std::cout << std::hex << v0 << v1;
}
int main() {
unsigned int lst[] = {0x33ae2685, 0x230bcdd5, 0x4f5ac093, 0x3dc3e00a, 0xda19d0a1, 0x32c52ad0, 0xc904ffac, 0x3037b842, 0x9c7bf31e, 0x4b8dfebc, 0x33335ba7, 0x4c4c9188, 0xa555d9a9, 0xaa069852, 0xa177367f, 0x79daa10f, 0x29ca035c, 0x319fbbc8, 0xd51b4a1c, 0x4a1b63b6, 0x99f5d2f1, 0xf35fdd82, 0x7e70314f, 0x42077d00, 0x4f84cb2b, 0x4a73846a, 0xbbb0581e, 0x8c33c34f, 0x4eb73143, 0xac45de0, 0x82592087, 0xc02544fa, 0x56590be4, 0xd2f78e08, 0xb2c9d125, 0x65e106d8, 0x46711844, 0xcf16ec7f, 0xc85dde46, 0x051d873d, 0x50319f0f, 0x8e5370bd, 0x80145a76, 0xbdbe90a6, 0x3a10947e, 0xfaf968c7, 0xac700a03, 0x47e061be, 0xe9e65b90, 0xe3c65a80, 0xd707d969, 0x40e93f77, 0x447cf10e, 0xbc69c7df, 0xd8c669de, 0x36c05ccf, 0x876411ba, 0xb37a6436, 0xcdbeac33, 0x7ba23db9, 0xc18251bd, 0x926d7a16, 0x9ffb0134, 0xc7f9ab96, 0xc635711e, 0x0045b69a, 0x7b0fdd2e, 0xf54849a7, 0x61e5d839, 0x1f12687d, 0xb39a4ba1, 0xd4fa2f5a, 0xc308a7fd, 0xcc0f199b, 0x06b35768, 0xecb39e48, 0xb2c9d125, 0x65e106d8, 0x9e9a0f73, 0xc58bdf39, 0xa9bb76d1, 0x0c75ccd7, 0x008473c6, 0x008a4ed0, 0x00ae1dcf, 0x09214f0e, 0xfb6bf695, 0x56e45cc6, 0x47e4e2b9, 0x8e2107d1, 0x5a24b1dc, 0x70599ee2, 0x6cd313ec, 0x4fa221e8, 0x6696e856, 0x62fde305, 0x79958e01, 0x1b99f294, 0x00876fd3, 0x00059c1d, 0x00000000, 0x00000000};
for (int i = 0; i < sizeof(lst) / sizeof(lst[0]); i += 2) {
decode(lst[i], lst[i + 1]);
}
return 0;
}
```
* output sẽ cho ra một mã đoạn encode
```
646566207265636375722869293a200a20202020206966286e6f742069293a0a20202020202020202072657475726e20310a202020202069662869203d3d2031293a0a20202020202020202072657475726e2020330a202020202076616c5f32203d2032202a72656363757228692d31290a202020202072657475726e2076616c5f32202b20332a2072656363757228692d3229200a2020202020657869742829200a656e635f666c6167203d205b3130322c37352c3136332c3233392c3135362c3135382c372c3134332c39322c3132302c302c35342c3138332c36352c3139392c3235332c36302c3138322c3230345d200a666f72206920696e2072616e6765283230293a0a20202020666c61675f76616c203d20656e635f666c61675b695d0a202020206374725f76616c203d20726563637572282869202a2069292b312925203235360a2020202076616c203d20666c61675f76616c205e206374725f76616c200a202020207072696e742820290a202020207072696e74286368722876616c292c656e643d222229202020875a6ff8d42f51b0
```
* Decode ra ascii sẽ được một hàm đệ quy
```
def reccur(i):
if(not i):
return 1
if(i == 1):
return 3
val_2 = 2 *reccur(i-1)
return val_2 + 3* reccur(i-2)
exit()
enc_flag = [102,75,163,239,156,158,7,143,92,120,0,54,183,65,199,253,60,182,204]
for i in range(20):
flag_val = enc_flag[i]
ctr_val = reccur((i * i)+1)% 256
val = flag_val ^ ctr_val
print( )
print(chr(val),end="")
```
* Nhưng vấn đề ở đây hàm sẽ recurr rất là lâu nên ta chỉ cần đổi một chút ở hàm exit và cách recurr là xong
```
def iter_reccur(n):
if not n:
return 1
if n == 1:
return 3
reccur_vals = [1, 3]
for i in range(2, n + 1):
val = 2 * reccur_vals[i - 1] + 3 * reccur_vals[i - 2]
reccur_vals.append(val)
return reccur_vals[n]
enc_flag = [102, 75, 163, 239, 156, 158, 7, 143, 92, 120, 0, 54, 183, 65, 199, 253, 60, 182, 204]
flag = ""
for i in range(19):
flag_val = enc_flag[i]
ctr_val = iter_reccur((i * i) + 1) % 256
val = flag_val ^ ctr_val
flag += chr(val)
print(flag)
```

## Beehive
* Ta chỉ được một file object beehive.o, sau khi check thì biết được đó là file eBPF.
* Để decompile được ta cần tải thêm processor cho IDA vì IDA k hỗ trợ mở file eBPF
* Link để tải plugin ở [đây]( https://github.com/zandi/eBPF_processor)
* Load vào IDA, cứ để loader mặc định và chọn process option là eBPF
* Nhìn vào `Segment data` ban đầu ta sẽ thấy được chuỗi nhập và check key cùng với một mảng const array

```
0x56,0xae,0xce,0xec,0xfa,0x2c,0x76,0xf6,0x2e,0x16,0xcc,0x4e,0xfa,0xae,0xce,0xcc,0x4e,0x76,0x2c,0xb6,0xa6,0x2,0x46,0x96,0xc,0xce,0x74,0x96,0x76
```

* Ta dễ dàng thấy chương trình sẽ nhận Input từ user và kí tự đầu tiên được lưu vào `r5`


* Tiếp đến chương trình check các kí tự sẽ được mã hóa nếu có bất kì kí tự nào sau khi mã hóa và không bằng số trong mảng thì set `r3 = 0` cụ thể 3 biến có r1, r2, r3 có tác dụng như sau
```
r1 --> biến đếm vòng lặp cho mã hóa từng byte
r2 --> chứa con trỏ tới byte được mã hóa hiện tại
r3 --> cờ check
```
* Cụ thể thì chương trình sẽ mã hóa như sau ban đầu chương trình set r7 = r5 sau đó AND bit giữa r7 và 15 (0b1111) để chỉ giữ lại 4 bit cuối cùng của r7 `r7 &= 15`
* Tiếp theo Dịch trái (<<) giá trị của r7 4 bit sau đó thực hiện phép AND bit giữa r5 và 240 (0b11110000) để chỉ giữ lại 4 bit đầu tiên của r5 và ịch phải (>>) giá trị của r5 4 bit.
* Thực hiện phép OR bit giữa r5 và r7 để kết hợp các giá trị đã được thay đổi của r5 và r7. Tiếp tục quá trình tương tự với các bước 2 đến 6, nhưng thay đổi các giá trị là 15, 4, 51, 2, 85, 1 tương ứng.
* Cuối cùng là check với mảng lúc đầu
* Vậy để solve ta chỉ cần lấy từng byte đầu vào , lật các bit (được đệm 8), sau đó so sánh để check với const array là được 4
```
const_Arr = [0x56,0xae,0xce,0xec,0xfa,0x2c,0x76,0xf6,0x2e,0x16,0xcc,0x4e,0xfa,0xae,0xce,0xcc,0x4e,0x76,0x2c,0xb6,0xa6,0x2,0x46,0x96,0xc,0xce,0x74,0x96,0x76]
for i in const_Arr:
i = '{:08b}'.format(i)
i = i[::-1]
i = int(i, 2)
print(chr(i), end = "")
```

> `jus7_4noth3r_us3rn4me@bi0s.in`
## T0y-b0x
* Bài cho ta một file thực thi và 1 file `cipher.txt` có lẽ như đã bị stripped, tiến hành phân tích bằng IDA
```
__int64 __fastcall main(int a1, char **a2, char **a3)
{
void *v3; // rsp
void *v4; // rsp
__int64 *v5; // rax
__int64 v6; // rdx
void *v8; // [rsp+0h] [rbp-8D0h] BYREF
int v9; // [rsp+Ch] [rbp-8C4h]
int i; // [rsp+1Ch] [rbp-8B4h]
int j; // [rsp+20h] [rbp-8B0h]
int v12; // [rsp+24h] [rbp-8ACh]
unsigned int v13; // [rsp+28h] [rbp-8A8h]
int v14; // [rsp+2Ch] [rbp-8A4h]
int v15; // [rsp+30h] [rbp-8A0h]
int v16; // [rsp+34h] [rbp-89Ch]
int v17; // [rsp+38h] [rbp-898h]
int c; // [rsp+3Ch] [rbp-894h]
__int64 v19; // [rsp+40h] [rbp-890h]
void **v20; // [rsp+48h] [rbp-888h]
void *src; // [rsp+50h] [rbp-880h]
void *v22; // [rsp+58h] [rbp-878h]
__int64 v23; // [rsp+60h] [rbp-870h]
void *dest; // [rsp+68h] [rbp-868h]
__int64 v25[2]; // [rsp+70h] [rbp-860h] BYREF
char v26[16]; // [rsp+80h] [rbp-850h] BYREF
char s[32]; // [rsp+90h] [rbp-840h] BYREF
char v28[1024]; // [rsp+B0h] [rbp-820h] BYREF
char v29[1032]; // [rsp+4B0h] [rbp-420h] BYREF
unsigned __int64 v30; // [rsp+8B8h] [rbp-18h]
v9 = a1;
v8 = a2;
v30 = __readfsqword(0x28u);
v12 = 0xB0;
v19 = 0xAFLL;
v3 = alloca(0xB0LL);
v20 = &v8;
__isoc99_scanf("%[^\n]16s", v28);
__isoc99_scanf("%1024s", v29);
v13 = 0x10;
src = (void *)sub_5652804E025F("%1024s", v29);
sub_5652804E17E7(v20, v28, v13, v12);
qmemcpy(s, "[yb2zg5w7k|yiyi$", 0x10);
s[0x10] = 0xF;
memcpy(&unk_5652804E50E0, src, 0x400uLL);
if ( dword_5652804E50C0 )
{
v22 = malloc(0x400uLL);
sub_5652804E02CA(v22);
memcpy(&unk_5652804E50E0, v22, 0x400uLL);
}
sub_5652804E12EB((__int64)s);
v14 = strlen(s);
v15 = strlen(v29);
v16 = (v15 + v14) / 0x10 + 1;
v17 = 0x10 * v16;
v23 = 0x10 * v16 - 1LL;
v4 = alloca(0x10 * ((0x10 * v16 + 0xFLL) / 0x10uLL));
dest = &v8;
memcpy(&v8, s, v14);
memcpy((char *)dest + v14, v29, v15);
c = v17 - v14 - v15;
memset((char *)dest + v15 + (__int64)v14, c, c);
for ( i = 0; i < v16; ++i )
{
v5 = (__int64 *)((char *)dest + 0x10 * i);
v6 = v5[1];
v25[0] = *v5;
v25[1] = v6;
sub_5652804E1F85(v25, v26, v28, 0x10LL);
for ( j = 0; j <= 0xF; ++j )
{
printf("%2.2x", (unsigned __int8)v26[j]);
}
}
putchar(0xA);
return 0LL;
}
```
* Đầu tiền nhìn vào main ta có thể thấy ban đầu chương trình nhận đầu vào từ người dùng thông qua hàm scanf bằng cách đọc string và lưu vào`v28`. Sau đó, nó đọc tiếp một string tối đa 1024 ký tự và lưu vào `v29`

* Quan sát hàm này, giá trị check sẽ là một `dword_5652804E50C0`, cụ thể nếu hàm check đúng chương trình sẽ nhảy vào thực thi 3 dòng lệnh trong đó và sinh ra biến v22 rồi ghi đè `byte_5617E6D730E0`

* Check xrefs ta có thể thấy chỉ có 2 xrefs, 1 trong 2 thì nhiệm vụ check kiểm tra như ta đã thấy ở main cái còn lại thì khá đáng nghi

* Đây có vẻ là một hàm anti_debug cụ thể là `ptrace` cụ thể là `sys_ptrace`. Đối số được set trong `eax` 0x1a = 26, dùng để kiểm tra chương trình có đang được chạy thông qua debugger hay không, nếu có nó sẽ trả về giá trị khác và out khỏi process
* Bypass bằng cách patch điều kiện jump hoặc nop toàn bộ, ở đây mình patch nó không nhảy vào if thế là xong phần antidebug
* Thông qua việc đọc thread thì mình còn biết được thuật toán AES được dùng trong bài mặc dù vẫn chưa thể nắm hết được AES nhưng vẫn có cái nhìn tổng quát về nó, nguồn để tham khảo [ở đây](https://youtu.be/gP4PqVGudtg?si=A9X4L2Q-jvvEAKc0)
* AES hoạt động trên các khối dữ liệu có kích thước 128 bit và sử dụng khóa có độ dài 128, 192 hoặc 256 bit. Quá trình mã hóa AES bao gồm một loạt các phép biến đổi trên các khối dữ liệu theo các vòng lặp. Các phép biến đổi chính trong AES bao gồm thay thế byte (SubBytes), tráo đổi chéo byte (ShiftRows), trộn cột (MixColumns) và thêm khóa (AddRoundKey).
* Ta cũng bắt gặp một vài chi tiết về AES ở phần Input đầu vào của user khi bắt nhập key_1 là 16 byte, còn key_2 sẽ là 1024 byte
* Từ đó ta có thể nhận ra bài này thực chất dùng AES để mã hóa với Sbox (0, 256)

```
int expandedKeySize = 176;
unsigned char expandedKey[expandedKeySize];
unsigned char key[1024];
unsigned char plaintext[1024];
scanf("%[^\n]16s", key);
scanf("%1024s", plaintext);
enum keySize size = SIZE_16;
int *arr = generateNumbers();
expandKey(expandedKey, key, size, expandedKeySize);
char prefix[] = {91, 121, 98, 50, 122, 103, 53, 119, 55, 107, 124, 121, 105, 121, 105, 36, 15};
memcpy(sbox, arr, 1024);
if (is_debugged)
{
unsigned int *fake_sbox = malloc(256 * sizeof(int));
init_fake_sbox(fake_sbox);
memcpy(sbox, fake_sbox, 1024);
}
decrypt_string(prefix);
int prefixLength = strlen(prefix);
int plaintextLength = strlen(plaintext);
int numBlocks = (plaintextLength + prefixLength) / 16 + 1;
int paddedLength = numBlocks * 16;
unsigned char paddedPlaintext[paddedLength];
memcpy(paddedPlaintext, prefix, prefixLength);
memcpy(paddedPlaintext + prefixLength, plaintext, plaintextLength);
int paddingBytes = paddedLength - prefixLength - plaintextLength;
memset(paddedPlaintext + prefixLength + plaintextLength, paddingBytes, paddingBytes);
for (int i = 0; i < numBlocks; i++)
{
unsigned char block[16];
memcpy(block, paddedPlaintext + i * 16, 16);
unsigned char blockCiphertext[16];
aes_encrypt(block, blockCiphertext, key, SIZE_16);
for (int j = 0; j < 16; j++)
{
printf("%2.2x", blockCiphertext[j]);
}
}
printf("\n");
return 0;
```
* Do có lục được nguồn C của chương trình nên mình có thể có cái nhìn trực quan và dễ hơn đối với chall này
* Khai báo biến và nhập dữ liệu: Đầu tiên, các biến được khai báo để lưu trữ khóa mở rộng, khóa, văn bản gốc và các thông tin khác. Sau đó, dữ liệu được nhập vào từ bàn phím bằng cách sử dụng hàm scanf.
* Mở rộng khóa: Khóa được mở rộng từ kích thước ban đầu (ở đây là 16 byte) thành một khóa mở rộng 176 byte bằng cách sử dụng hàm expandKey.
* Khởi tạo S-box: Một S-box được sử dụng để thay thế các byte trong quá trình mã hóa. S-box này được khởi tạo từ một mảng số được sinh ra bởi hàm generateNumbers. Điều này thường được thực hiện để tăng tính ngẫu nhiên và độ phức tạp của mã hóa.
* Kiểm tra chế độ debug: Hàm này đã phân tích nên mình sẽ bỏ qua.
* Giải mã chuỗi prefix: Chuỗi prefix được giải mã bằng hàm decrypt_string. Sau đó, tính độ dài của prefix.
* Mã hóa từng khối dữ liệu: Văn bản được chia thành các khối 16 byte và mỗi khối được mã hóa sử dụng AES. Trong hàm này, các bước chính của AES được thực hiện:
* AddRoundKey: Cộng khóa vòng (round key) vào khối dữ liệu.
* SubBytes: Thay thế mỗi byte trong khối dữ liệu bằng một giá trị từ S-box.
* ShiftRows: Dịch các hàng của khối dữ liệu sang trái.
* MixColumns: Pha hoán vị các cột của khối dữ liệu.. Kết quả mã hóa được in ra dưới dạng hexa.
* Đầu tiên ta cần decrypt chuỗi prefix trước tiên, đây là code để decrypt sau khi mình check hàm decrypt_string của chương trình
```
#include <stdio.h>
void actually_decrypt_string(char *a, char *flag)
{
for (int i = 0; a[i]; i++)
{
a[i] = a[i] ^ (i + 15);
}
}
void decrypt_string(char *a)
{
char flag[] = "69*x^0x8we82130d";
actually_decrypt_string(a, flag);
}
int main()
{
char prefix[] = {91, 121, 98, 50, 122, 103, 53, 119, 55, 107, 124, 121, 105, 121, 105, 36, 15};
decrypt_string(prefix);
printf("Decrypted string: %s\n", prefix);
return 0;
}
```

* Ta được chuỗi `Tis is a secret: `
```
from copy import deepcopy
SBOX = [
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
]
dest = [i for i in range(256)]
key = [
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x55, 0x54, 0x54, 0x50, 0x61, 0x61, 0x62, 0x67, 0x59, 0x58, 0x03, 0x05, 0x3A, 0x3C, 0x66, 0x63,
0x6B, 0x32, 0x37, 0x6A, 0x0A, 0x53, 0x55, 0x0D, 0x53, 0x0B, 0x56, 0x08, 0x69, 0x37, 0x30, 0x6B,
0x58, 0x02, 0x5C, 0x03, 0x52, 0x51, 0x09, 0x0E, 0x01, 0x5A, 0x5F, 0x06, 0x68, 0x6D, 0x6F, 0x6D,
0x3D, 0x6D, 0x31, 0x6B, 0x6F, 0x3C, 0x38, 0x65, 0x6E, 0x66, 0x67, 0x63, 0x06, 0x0B, 0x08, 0x0E,
0x26, 0x65, 0x3F, 0x6D, 0x49, 0x59, 0x07, 0x08, 0x27, 0x3F, 0x60, 0x6B, 0x21, 0x34, 0x68, 0x65,
0x32, 0x0D, 0x5A, 0x4C, 0x7B, 0x54, 0x5D, 0x44, 0x5C, 0x6B, 0x3D, 0x2F, 0x7D, 0x5F, 0x55, 0x4A,
0x2D, 0x58, 0x10, 0x31, 0x56, 0x0C, 0x4D, 0x75, 0x0A, 0x67, 0x70, 0x5A, 0x77, 0x38, 0x25, 0x10,
0x95, 0x7D, 0x00, 0x46, 0xC3, 0x71, 0x4D, 0x33, 0xC9, 0x16, 0x3D, 0x69, 0xBE, 0x2E, 0x18, 0x79,
0xA0, 0x65, 0x79, 0xF8, 0x63, 0x14, 0x34, 0xCB, 0xAA, 0x02, 0x09, 0xA2, 0x14, 0x2C, 0x11, 0xDB,
0xBA, 0x74, 0xA2, 0xEC, 0xD9, 0x60, 0x96, 0x27, 0x73, 0x62, 0x9F, 0x85, 0x67, 0x4E, 0x8E, 0x5E,
]
rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A]
def subBytes(state):
for i in range(4):
for j in range(4):
state[i][j] = dest[state[i][j]]
return state
def subBytesInv(state):
for i in range(len(state)):
state[i] = SBOX[state[i]]
def rotate(word, n):
return word[n:]+word[0:n]
def shiftRows(state):
for i in range(4):
state[i] = state[i][i:] + state[i][:i]
return state
def shiftRowsInv(state):
for i in range(4):
state[i] = state[i][4-i:] + state[i][:4-i]
return state
from copy import deepcopy as copy
def galoisMult(a, b):
p = 0
hiBitSet = 0
for i in range(8):
if b & 1 == 1:
p ^= a
hiBitSet = a & 0x80
a <<= 1
if hiBitSet == 0x80:
a ^= 0x1b
b >>= 1
return p % 256
def mixColumn(column):
temp = copy(column)
column[0] = galoisMult(temp[0],2) ^ galoisMult(temp[3],1) ^ \
galoisMult(temp[2],1) ^ galoisMult(temp[1],3)
column[1] = galoisMult(temp[1],2) ^ galoisMult(temp[0],1) ^ \
galoisMult(temp[3],1) ^ galoisMult(temp[2],3)
column[2] = galoisMult(temp[2],2) ^ galoisMult(temp[1],1) ^ \
galoisMult(temp[0],1) ^ galoisMult(temp[3],3)
column[3] = galoisMult(temp[3],2) ^ galoisMult(temp[2],1) ^ \
galoisMult(temp[1],1) ^ galoisMult(temp[0],3)
return column
def mixColumns(data):
data = flatten_matrix(data)
new = bytearray(16)
for i in range(4):
column = [data[i], data[i+4], data[i+8], data[i+12]]
column = mixColumn(column)
data[i], data[i+4], data[i+8], data[i+12] = column[0], column[1], column[2], column[3]
# new[i*4: i*4+4] = column[0], column[1], column[2], column[3]
return reshape_to_matrix(data)
def mixColumnInv(column):
temp = copy(column)
column[0] = galoisMult(temp[0],14) ^ galoisMult(temp[3],9) ^ \
galoisMult(temp[2],13) ^ galoisMult(temp[1],11)
column[1] = galoisMult(temp[1],14) ^ galoisMult(temp[0],9) ^ \
galoisMult(temp[3],13) ^ galoisMult(temp[2],11)
column[2] = galoisMult(temp[2],14) ^ galoisMult(temp[1],9) ^ \
galoisMult(temp[0],13) ^ galoisMult(temp[3],11)
column[3] = galoisMult(temp[3],14) ^ galoisMult(temp[2],9) ^ \
galoisMult(temp[1],13) ^ galoisMult(temp[0],11)
return column
def mixColumnsInv(data):
data = flatten_matrix(data)
new = bytearray(16)
for i in range(4):
column = [data[i], data[i+4], data[i+8], data[i+12]]
column = mixColumnInv(column)
data[i], data[i+4], data[i+8], data[i+12] = column[0], column[1], column[2], column[3]
# new[i*4: i*4+4] = column[0], column[1], column[2], column[3]
return reshape_to_matrix(data)
def xorArr(arr1, arr2):
for i in range(len(arr1)):
for j in range(len(arr1[i])):
arr1[i][j] ^= arr2[i][j]
return arr1
def addRoundKey(key, round):
key = reshape_to_matrix(key)
expanded_key = [[0 for i in range(4)] for j in range(4)]
col_3 = [0 for i in range(4)]
col_3[0] = key[3][1]
col_3[1] = key[3][2]
col_3[2] = key[3][3]
col_3[3] = key[3][0]
for i in range(4):
col_3[i] = dest[col_3[i]]
col_3[0] ^= rcon[round]
expanded_key[0][0] = key[0][0] ^ col_3[0]
expanded_key[0][1] = key[0][1] ^ col_3[1]
expanded_key[0][2] = key[0][2] ^ col_3[2]
expanded_key[0][3] = key[0][3] ^ col_3[3]
for i in range(1, 4):
for j in range(4):
expanded_key[i][j] = expanded_key[i-1][j] ^ key[i][j]
return flatten_matrix(expanded_key)
def printArr(data):
print("---")
for i in range(4):
for j in range(4):
print(hex(data[i][j])[2:], end=" ")
print()
def reshape_to_matrix(lst):
return [lst[i:i+4] for i in range(0, len(lst), 4)]
def transpose_matrix(matrix):
return [list(row) for row in zip(*matrix)]
def flatten_matrix(matrix):
return [item for sublist in matrix for item in sublist]
def encrypt(input):
input = transpose_matrix(reshape_to_matrix(input))
key0 = transpose_matrix(reshape_to_matrix(key[:16]))
plain = xorArr(input, key0)
for i in range(1, 10):
temp_key = transpose_matrix(reshape_to_matrix(key[i*16:(i+1)*16]))
plain = subBytes(plain)
plain = shiftRows(plain)
plain = mixColumns(plain)
plain = xorArr(plain, temp_key)
temp_key = transpose_matrix(reshape_to_matrix(key[160:]))
plain = subBytes(plain)
plain = shiftRows(plain)
plain = xorArr(plain, temp_key)
plain = transpose_matrix(plain)
return plain
init_key = 'Tis is a secret:'
key = [ord(c) for c in init_key]
test = reshape_to_matrix(key)
test = transpose_matrix(test)
print(''.join([chr(i) for i in flatten_matrix(test)]))
for i in range(1, 11):
temp = deepcopy(key[-16:])
key += addRoundKey(temp, i)
output = "62dab26a74a375d01402ada3879a4031d8a485ec177380f44ca4ab9441545999003a8070e7eacd6ceabbf280d53859e278e7bc0e69b3f60de7493b510088dc8b"
for i in range(0, len(output), 32):
cipher = list(bytearray.fromhex(output[i:i+32]))
cipher = transpose_matrix(reshape_to_matrix(cipher))
temp_key = transpose_matrix(reshape_to_matrix(key[160:]))
cipher = xorArr(cipher, temp_key)
cipher = shiftRowsInv(cipher)
for i in range(9, 0, -1):
temp_key = transpose_matrix(reshape_to_matrix(key[i*16:(i+1)*16]))
cipher = xorArr(cipher, temp_key)
cipher = mixColumnsInv(cipher)
cipher = shiftRowsInv(cipher)
temp_key = transpose_matrix(reshape_to_matrix(key[:16]))
cipher = xorArr(cipher, temp_key)
cipher = transpose_matrix(cipher)
cipher = flatten_matrix(cipher)
print(''.join([chr(i) for i in cipher]))
```
* Từ plaintext nầy ta đã có thể decrypt chương trình bằng chuỗi này bằng cách XOR round key với khối đầu vào. Thay thế mỗi byte bằng giá trị tương ứng trong bảng SBOX cuối cùng là chạy hàm `ShiftRows` `MixColumns`. Cuối cùng XOR với khóa con, chạy hàm shiftRowsInv, mixColumnsInv, và subBytesInv.
> bi0sctf{L1n34rl
y_Un5huffl3d_T0y
5}