# **Writeups bi0sCTF 2024**
***
Cũng 5 tháng rồi từ lần cuối làm writeups nên nay quyết định làm một cái khởi đầu năm mới. Đợt này mình tham gia với team `TheRoundTable` và được hạng 21.
***

***
Trong thời gian giải mở thì mình làm được 2 bài bpf (đọc asm chay time), sau khi end giải thì mình có đi kiếm wu của bài `t0y-b0x` nhưng không một ai rep nên thôi tryhard tự làm luôn.
***
OKE giờ vào writeups nào!!!
***
## **1/BEEHIVE**

***
### **REV**
Đề cho một file `ELF 64-bit LSB relocatable, eBPF` và mình chạy thử thì bị lỗi
***

***
Sau một hồi research thì mình kiếm được tool là `llvm-objdump` để dump code asm ra đọc.
***

***
Tới đây thì mình rev thuần chay thôi, việc kiếm tài liệu khá là mất nhiều thời gian vì trong bpf có một vài hàm gọi là `platform-agnostic helper functions` như có thể thấy ở dòng 14 có lệnh `call 4`, những hàm helper này sẽ không được giải thích ra dù sau đó mình đã cố tìm tool khác để xem nó có giải thích phần này không, nhưng các tool đều chỉ hiện lệnh asm là `call 4` mà thôi. Vì vậy mình đã tốn rất nhiều thời gian để tìm tài liệu về phần này.
Đây là một số tài liệu để tham khảo:
* eBPF Instruction Set: `https://www.ietf.org/archive/id/draft-thaler-bpf-isa-00.html#platform-agnostic-helper-functions`
* BPF helper functions: `https://github.com/iovisor/bpf-docs/blob/master/bpf_helpers.rst/`
Bên trên là 2 tài liệu mình thấy có đầy đủ những gì mình cần để làm bài này, đặc biệt là phần helper functions mình không nhớ sao tìm được luôn nhưng bên trong nó sẽ không ghi rõ ứng với function nào thì bên asm sẽ call index nào. Mình đã rev hết flow của chương trình để biết được index 6 sẽ print chuỗi ra màn hình, vì vậy mình đã kiếm tài liệu nào có hàm print, tài liệu trên có hàm print được khai báo thứ 6, và như trong tài liệu cũng nêu ra
***

***
Đây là sau khi mình rev lại flow của chương trình.
```a!
beehive.o: file format elf64-bpf
Disassembly of section raw_tracepoint/sys_enter:
0000000000000000 <weird_function>:
0: bf 13 00 00 00 00 00 00 r3 = r1
1: b7 01 00 00 00 00 00 00 r1 = 0
2: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1
3: 7b 1a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r1
4: 7b 1a e8 ff 00 00 00 00 *(u64 *)(r10 - 24) = r1
5: 7b 1a e0 ff 00 00 00 00 *(u64 *)(r10 - 32) = r1
6: 7b 1a d8 ff 00 00 00 00 *(u64 *)(r10 - 40) = r1
7: 7b 1a d0 ff 00 00 00 00 *(u64 *)(r10 - 48) = r1
8: 7b 1a c8 ff 00 00 00 00 *(u64 *)(r10 - 56) = r1
9: 79 36 00 00 00 00 00 00 r6 = *(u64 *)(r3 + 0)
10: 07 03 00 00 08 00 00 00 r3 += 8
11: bf a1 00 00 00 00 00 00 r1 = r10
12: 07 01 00 00 c8 ff ff ff r1 += -56
13: b7 02 00 00 08 00 00 00 r2 = 8
14: 85 00 00 00 04 00 00 00 call MAYBE_READ_STORE_TO_R1_ADDRESS
15: 79 a1 c8 ff 00 00 00 00 r1 = *(u64 *)(r10 - 56)
16: 55 01 5f 00 37 13 03 00 if r1 != 201527 goto +95 <EXIT_FUCTION> ; *(u64 *)(r10 - 56) == 201527
17: bf 63 00 00 00 00 00 00 r3 = r6
18: 07 03 00 00 70 00 00 00 r3 += 112
19: bf a1 00 00 00 00 00 00 r1 = r10
20: 07 01 00 00 d0 ff ff ff r1 += -48
21: b7 02 00 00 08 00 00 00 r2 = 8
22: 85 00 00 00 04 00 00 00 call MAYBE_READ_STORE_TO_R1_ADDRESS
23: 07 06 00 00 68 00 00 00 r6 += 104
24: bf a1 00 00 00 00 00 00 r1 = r10
25: 07 01 00 00 d8 ff ff ff r1 += -40
26: b7 02 00 00 08 00 00 00 r2 = 8
27: bf 63 00 00 00 00 00 00 r3 = r6
28: 85 00 00 00 04 00 00 00 call MAYBE_READ_STORE_TO_R1_ADDRESS
29: 79 a3 d0 ff 00 00 00 00 r3 = *(u64 *)(r10 - 48)
30: bf a6 00 00 00 00 00 00 r6 = r10
31: 07 06 00 00 a8 ff ff ff r6 += -88
32: bf 61 00 00 00 00 00 00 r1 = r6 ; address của buffer chứa input
33: b7 02 00 00 20 00 00 00 r2 = 32
34: 85 00 00 00 72 00 00 00 call 114 ; lười đếm để xác định đây là hàm gì cụ thể trong helpers nhưng đây là đang nhận input và lưu vào (r10-88)
35: 18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll ; 'Enter your key: %s'
37: b7 02 00 00 13 00 00 00 r2 = 19 ; len
38: bf 63 00 00 00 00 00 00 r3 = r6
39: 85 00 00 00 06 00 00 00 call PRINT
40: 71 a5 a8 ff 00 00 00 00 r5 = *(u8 *)(r10 - 88)
41: 15 05 13 00 00 00 00 00 if r5 == 0 goto +19 <CORRECT>
42: b7 07 00 00 00 00 00 00 r7 = 0
43: b7 01 00 00 01 00 00 00 r1 = 1
44: 18 02 00 00 14 00 00 00 00 00 00 00 00 00 00 00 r2 = 20 ll; khi gán biến r2 = 20 thêm 'll' là đang gán address ở .rodata index 20; lúc này đang trỏ vào ciphertext
46: b7 04 00 00 00 00 00 00 r4 = 0
47: b7 03 00 00 01 00 00 00 r3 = 1
48: b7 00 00 00 00 00 00 00 r0 = 0
49: 05 00 0f 00 00 00 00 00 goto +15 <LBB0_3>
0000000000000190 <LBB0_13>:
50: 07 02 00 00 04 00 00 00 r2 += 4 ; nhảy sang phần tử tiếp theo của ciphertext
51: bf a5 00 00 00 00 00 00 r5 = r10
52: 07 05 00 00 a8 ff ff ff r5 += -88
53: 0f 15 00 00 00 00 00 00 r5 += r1 ; r1 là index đang được check tới
54: 07 01 00 00 01 00 00 00 r1 += 1
55: 71 55 00 00 00 00 00 00 r5 = *(u8 *)(r5 + 0); lấy value của element đang được trỏ tới trong input
56: bf 67 00 00 00 00 00 00 r7 = r6
57: 55 05 07 00 00 00 00 00 if r5 != 0 goto +7 <LBB0_3>
00000000000001d0 <LBB0_14>:
58: 67 03 00 00 20 00 00 00 r3 <<= 32
59: 77 03 00 00 20 00 00 00 r3 >>= 32
60: 15 03 2f 00 00 00 00 00 if r3 == 0 goto +47 <INCORRECT>
00000000000001e8 <CORRECT>:
61: 18 01 00 00 88 00 00 00 00 00 00 00 00 00 00 00 r1 = 136 ll ; truy cập vào .rodata, phần nhớ ở 136 chính là 0x88 là chuỗi "Key is correct!"
63: b7 02 00 00 10 00 00 00 r2 = 16
64: 05 00 2e 00 00 00 00 00 goto +46 <PRINT_FUCTION>
0000000000000208 <LBB0_3>:
65: bf 58 00 00 00 00 00 00 r8 = r5
66: b7 06 00 00 01 00 00 00 r6 = 1
67: 15 08 01 00 40 00 00 00 if r8 == 64 goto +1 <LBB0_5>
68: bf 76 00 00 00 00 00 00 r6 = r7
0000000000000228 <LBB0_5>:
69: bf 68 00 00 00 00 00 00 r8 = r6
70: 67 08 00 00 20 00 00 00 r8 <<= 32
71: 77 08 00 00 20 00 00 00 r8 >>= 32
72: b7 07 00 00 01 00 00 00 r7 = 1
73: 15 08 01 00 00 00 00 00 if r8 == 0 goto +1 <LBB0_7>
74: b7 07 00 00 00 00 00 00 r7 = 0
0000000000000258 <LBB0_7>:
75: b7 09 00 00 01 00 00 00 r9 = 1
76: 55 08 01 00 00 00 00 00 if r8 != 0 goto +1 <LBB0_9>
77: b7 09 00 00 00 00 00 00 r9 = 0
0000000000000270 <LBB0_9>:
78: 0f 94 00 00 00 00 00 00 r4 += r9
79: 0f 70 00 00 00 00 00 00 r0 += r7
80: bf 47 00 00 00 00 00 00 r7 = r4
81: 0f 07 00 00 00 00 00 00 r7 += r0
82: 67 07 00 00 20 00 00 00 r7 <<= 32
83: 77 07 00 00 20 00 00 00 r7 >>= 32
84: 25 07 1b 00 20 00 00 00 if r7 > 32 goto +27 <EXIT_FUCTION>
85: 15 01 e4 ff 1e 00 00 00 if r1 == 30 goto -28 <LBB0_14>
86: bf 57 00 00 00 00 00 00 r7 = r5 ; r7 = x
87: 57 07 00 00 0f 00 00 00 r7 &= 15
88: 67 07 00 00 04 00 00 00 r7 <<= 4 ; r7 = (x&15) << 4
89: 57 05 00 00 f0 00 00 00 r5 &= 240;
90: 77 05 00 00 04 00 00 00 r5 >>= 4 ; r5 = (x&240) >> 4
91: 4f 75 00 00 00 00 00 00 r5 |= r7 ;
92: bf 57 00 00 00 00 00 00 r7 = r5 ; r7 = r5 = ((x&240) >> 4 | (x&15) << 4)
93: 57 07 00 00 33 00 00 00 r7 &= 51;
94: 67 07 00 00 02 00 00 00 r7 <<= 2 ; r7 = (((x&240) >> 4 | (x&15) << 4)&51) << 2
95: 77 05 00 00 02 00 00 00 r5 >>= 2
96: 57 05 00 00 33 00 00 00 r5 &= 51 ; r5 = (((x&240) >> 4 | (x&15) << 4)>>2)&51
97: 4f 75 00 00 00 00 00 00 r5 |= r7
98: bf 57 00 00 00 00 00 00 r7 = r5 ; r7 = r5 = (((((x&240) >> 4 | (x&15) << 4)>>2)&51) | ((((x&240) >> 4 | (x&15) << 4)&51) << 2))
99: 57 07 00 00 55 00 00 00 r7 &= 85
100: 67 07 00 00 01 00 00 00 r7 <<= 1; r7 = (((((((x&240) >> 4 | (x&15) << 4)>>2)&51) | ((((x&240) >> 4 | (x&15) << 4)&51) << 2)))&=85)<<1
101: 77 05 00 00 01 00 00 00 r5 >>= 1
102: 57 05 00 00 55 00 00 00 r5 &= 85 ((((((x&240) >> 4 | (x&15) << 4)>>2)&51) | ((((x&240) >> 4 | (x&15) << 4)&51) << 2))>>1)&85
103: 4f 75 00 00 00 00 00 00 r5 |= r7
104: 61 27 00 00 00 00 00 00 r7 = *(u32 *)(r2 + 0) ; r7 lúc này là phần tử của ciphertext
105: 1d 57 c8 ff 00 00 00 00 if r7 == r5 goto -56 <LBB0_13>
106: b7 03 00 00 00 00 00 00 r3 = 0
107: 05 00 c6 ff 00 00 00 00 goto -58 <LBB0_13>
0000000000000360 <INCORRECT>:
108: 18 01 00 00 98 00 00 00 00 00 00 00 00 00 00 00 r1 = 152 ll ; 'Key is incorrect!'
110: b7 02 00 00 12 00 00 00 r2 = 18 ; len
0000000000000378 <PRINT_FUCTION>:
111: 85 00 00 00 06 00 00 00 call PRINT
0000000000000380 <EXIT_FUCTION>:
112: b7 00 00 00 00 00 00 00 r0 = 0
113: 95 00 00 00 00 00 00 00 exit
Disassembly of section .relraw_tracepoint/sys_enter:
0000000000000000 <.relraw_tracepoint/sys_enter>:
0: 18 01 00 00 00 00 00 00
1: 01 00 00 00 0f 00 00 00
2: 60 01 00 00 00 00 00 00
3: 01 00 00 00 0f 00 00 00
4: e8 01 00 00 00 00 00 00
5: 01 00 00 00 0f 00 00 00
6: 60 03 00 00 00 00 00 00
7: 01 00 00 00 0f 00 00 00
Disassembly of section .rodata:
0000000000000000 <weird_function.____fmt>:
0: 45 6e 74 65 72 20 79 6f
1: 75 72 20 6b 65 79 3a 20
2: 25 73 00 00 56 00 00 00
3: ae 00 00 00 ce 00 00 00
4: ec 00 00 00 fa 00 00 00
5: 2c 00 00 00 76 00 00 00
6: f6 00 00 00 2e 00 00 00
7: 16 00 00 00 cc 00 00 00
8: 4e 00 00 00 fa 00 00 00
9: ae 00 00 00 ce 00 00 00
10: cc 00 00 00 4e 00 00 00
11: 76 00 00 00 2c 00 00 00
12: b6 00 00 00 a6 00 00 00
13: 02 00 00 00 46 00 00 00
14: 96 00 00 00 0c 00 00 00
15: ce 00 00 00 74 00 00 00
16: 96 00 00 00 76 00 00 00
0000000000000088 <weird_function.____fmt.1>:
17: 4b 65 79 20 69 73 20 63
18: 6f 72 72 65 63 74 21 00
0000000000000098 <weird_function.____fmt.2>:
19: 4b 65 79 20 69 73 20 69
20: 6e 63 6f 72 72 65 63 74
21: 21
21: 00
```
***
Phần encrypt input có nhiều phép toán quá nên mình bruteforce luôn
***
### **SOL**
```python=
a = bytes.fromhex('56aeceecfa2c76f62e16cc4efaaececc4e762cb6a60246960cce749676')
flag = ""
for i in (a):
for x in range(33,127):
if (((((((x&240) >> 4 | (x&15) << 4)>>2)&51) | ((((x&240) >> 4 | (x&15) << 4)&51) << 2))>>1)&85) | ((((((((x&240) >> 4 | (x&15) << 4)>>2)&51) | ((((x&240) >> 4 | (x&15) << 4)&51) << 2)))&85)<<1) == i:
flag+=chr(x)
break
print(len(flag))
print(f"bi0sctf{{{flag}}}")
```
***
### **FLAG**
**`bi0sctf{jus7_4noth3r_us3rn4me@bi0s.in}`**
***
## **2/BAEBPF**

***
Đọc chay bài đầu quen rồi nên sang bài này mình thấy đọc nhanh hơn tí.
***
### **REV**

***
Nếu chọn dump assembly thì ta nhận được flow của chương trình. Rev lại đống đó thì hẵng qua cat file.
***
Ngoài assembly dump thì ta sẽ có thêm cả map dump

***
Code asm của phần này rất dễ, chỉ là check tên file muốn đọc, phần password chỉ là phép xor với 5.
***


***
Tiếp tục chọn 1 để dump code asm ra rev.
Đây là code mình đã rev thay đổi một số thứ nên sẽ khác với code được dump ra.
```a!
void encrypt_function():
0: (bf) r6 = r10
1: (07) r6 += -4
2: (18) mask = 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) delta = r10
11: (07) delta += -12 ; uint32_t *temp = bpf_map_lookup_elem((void *)bpf_pseudo_fd(1, -1), &index);
12: (07) r1 += 272
13: (61) r0 = *(u32 *)(delta +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) delta = r10
27: (07) delta += -12 ; uint32_t *temp2 = bpf_map_lookup_elem((void *)bpf_pseudo_fd(1, -2), &index);
28: (07) r1 += 272
29: (61) r0 = *(u32 *)(delta +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
38: (b7) r1 = 32
39: (18) delta = 0x9e3779b9
41: (61) r7 = *(u32 *)(r10 -8)
42: (61) r4 = *(u32 *)(r0 +0) ; v[0]
43: (63) *(u32 *)(r10 -4) = r4
44: (bf) r3 = r4
45: (67) r3 <<= 4
46: (07) r3 += key ((r3<<4)+key)^(r3+0x9e3779b9)
47: (bf) r5 = r4
48: (0f) r5 += delta
49: (af) r3 ^= r5
50: (bf) r5 = r4
51: (5f) r5 &= mask
52: (77) r5 >>= 5
53: (07) r5 += key
54: (af) r3 ^= r5 (((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))
55: (0f) r3 += r7
56: (bf) r5 = r3 ((((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))+ r7)
57: (67) r5 <<= 4
58: (07) r5 += key (((((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))+ r7)<<4)+key
59: (bf) r0 = delta
60: (0f) r0 += r3 ((((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))+ r7) + 0x9e3779b9
61: (af) r5 ^= r0 ((((((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))+ r7)<<4)+key ) ^ (((((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))+ r7) + 0x9e3779b9)
62: (bf) r0 = r3
63: (5f) r0 &= mask
64: (77) r0 >>= 5
65: (07) r0 += key (((((((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))+ r7)&0xffffffe0)>>5)+key)
66: (af) r5 ^= r0
67: (0f) r5 += r4 (((((((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))+ r7)<<4)+key ) ^ (((((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))+ r7) + 0x9e3779b9))^ (((((((r3 & 0xffffffe0)>>5)+key)^(((r3<<4)+key)^(r3+0x9e3779b9))+ r7)&0xffffffe0)>>5)+key) + r3
68: (07) delta += -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) delta = 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) delta = 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
```
***
Thì đây chỉ là phép mã hóa TEA mà thôi. Mình mất hơn nửa ngày để tìm cách implement lại trên python từ các phép tính có trong asm dump nhưng mà không được. Mình định sủi giải thì anh `@jinn` nói code bằng c đi.


***
### **SOL LV2**
Lúc này mình đang nằm ngủ trưa mới dậy thấy tin nhắn liền thử leo lên máy kiếm code implement của tea thử.
```c=
#include <stdio.h>
#include <stdint.h>
uint32_t KEY[4] = {305402420,305402420,305402420,305402420}; // Key space for bit shifts
void encrypt(uint32_t* v) {
uint32_t v0=v[0], v1=v[1], sum=0, i; // set up
uint32_t delta=0x9e3779b9; // a key schedule constant
for (i=0; i < 32; i++) { // basic cycle start
sum += delta;
v0 += ((v1<<4) + KEY[0]) ^ (v1 + sum) ^ ((v1>>5) + KEY[1]);
v1 += ((v0<<4) + KEY[2]) ^ (v0 + sum) ^ ((v0>>5) + KEY[3]);
} // end cycle
v[0]=v0; v[1]=v1;
}
void decrypt (uint32_t* v) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; // set up
uint32_t delta=0x9e3779b9; // a key schedule constant
for (i=0; i<32; i++) { // basic cycle start
v1 -= ((v0<<4) + KEY[2]) ^ (v0 + sum) ^ ((v0>>5) + KEY[3]);
v0 -= ((v1<<4) + KEY[0]) ^ (v1 + sum) ^ ((v1>>5) + KEY[1]);
sum -= delta;
} // end cycle
v[0]=v0; v[1]=v1;
}
int main()
{
uint32_t enc[] = {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,0x51d873d,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,0x45b69a8,0x7b0fdd2e,0xf54849a7,0x61e5d839,0x1f12687d,0xb39a4ba1,0xd4fa2f5a,0xc308a7fd,0xcc0f199b,0x6b35768,0xecb39e48,0xb2c9d125,0x65e106d8,0x9e9a0f73,0xc58bdf39,0xa9bb76d1,0xc75ccd7,0x8473c66,0x8a4ed0e5,0xae1dcf9a,0x214f0ed5,0xfb6bf695,0x56e45cc6,0x47e4e2b9,0x8e2107d1,0x5a24b1dc,0x70599ee2,0x6cd313ec,0x4fa221e8,0x6696e856,0x62fde305,0x79958e01,0x1b99f294,0x876fd3a,0x59c1d749,0x0,0x0};
for (int i = 0; i <= 99; i = i+2) {
uint32_t v[] = {enc[i], enc[i+1]};
decrypt(v);
printf("%X%X", v[0], v[1]);
}
return 0;
}
```
Key ở đây là 305402420, nếu diễn giải đống asm trên sẽ thấy từng vị trí khớp với algo của TEA.

***
Chạy sẽ được một chuỗi hex rất dài. Mình nhìn đã thấy sú sú không thể là flag được. Đổi ra thì lại được một chương trình python.


***
### **SOL LV3**
Chương trình chỉ đơn giản là in flag thôi nhưng do phải qua hàm reccur nên sẽ mất rất nhiều thời gian. Tới đây mình cho lưu lại vào một mảng hết.
```python=
arr = [[x,0] for x in range(10000)]
def reccur(i):
if(not i):
arr[i][1] = 1
# return 1
if(i == 1):
arr[i][1] = 3
# return 3
if arr[i][1] == 0:
val_2 = 2 *reccur(i-1)
res = val_2 + 3* reccur(i-2)
arr[i][1] = res
return arr[i][1]
# exit()
enc_flag = [102,75,163,239,156,158,7,143,92,120,0,54,183,65,199,253,60,182,204]
dec_flag = ""
for i in range(19):
flag_val = enc_flag[i]
ctr_val = reccur((i * i)+1)% 256
val = flag_val ^ ctr_val
dec_flag += chr(val)
print(f"bi0sctf{{{dec_flag}}}")
```
***

***
### **FLAG**
**`bi0sctf{eBPF_wtF_1s_th4t???}`**
***
## **3/T0Y-B0X**

(đã rev còn thêm quả tag `Cryptography` trôn vl)
***
Bài này thì sau giải mình làm lại do hỏi sol script trong server nhưng không được trả lời. Với đang mò một vài cái hay ho, dù mấy cái đó không áp dụng được trong bài này nhưng yeah mình thấy rất vui. Mình sẽ không đề cập trong đây mà tập trung về việc giải bài này.
***
### **REV**
Đề cho mình 2 file, file thực thi chall và một file chứa ciphertext. Nhìn thấy 2 file này kèm với tag `Crytography` thì mình biết phải đi tìm cách input bị encrypt rồi đi decrypt lại ciphertext.

***
Chạy thử file chall thì nhận của mình 2 input.

***
Ngó qua hàm main
```c=
__int64 __fastcall main(int a1, char **a2, char **a3)
{
void *v3; // rsp
void *v4; // rsp
__int64 *v5; // rax
__int64 v6; // rdx
char **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]
char ***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 = 176;
v19 = 175LL;
v3 = alloca(176LL);
v20 = &v8;
__isoc99_scanf("%[^\n]16s", v28);
__isoc99_scanf("%1024s", v29);
v13 = 16;
src = (void *)sub_125F("%1024s", v29);
sub_27E7(v20, v28, v13, v12);
qmemcpy(s, "[yb2zg5w7k|yiyi$", 16);
s[16] = 15;
memcpy(&unk_60E0, src, 0x400uLL);
if ( dword_60C0 )
{
v22 = malloc(0x400uLL);
sub_12CA(v22);
memcpy(&unk_60E0, v22, 0x400uLL);
}
sub_22EB(s);
v14 = strlen(s);
v15 = strlen(v29);
v16 = (v15 + v14) / 16 + 1;
v17 = 16 * v16;
v23 = 16 * v16 - 1LL;
v4 = alloca(16 * ((16 * v16 + 15LL) / 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 + 16 * i);
v6 = v5[1];
v25[0] = *v5;
v25[1] = v6;
sub_2F85(v25, v26, v28, 16LL);
for ( j = 0; j <= 15; ++j )
printf("%2.2x", (unsigned __int8)v26[j]);
}
putchar(10);
return 0LL;
}
```
Nếu đi sâu vào các hàm con thì sẽ thấy gọi thêm nhiều hàm khác nữa.
Và sau khi tryhard + research thì mình đã reverse lại tất cả các hàm cũng như flow bài.
```c=
__int64 __fastcall main(int a1, char **a2, char **a3)
{
void *v3; // rsp
void *v4; // rsp
__int64 *v5; // rax
__int64 v6; // rdx
void *dest_; // [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 LEN_S; // [rsp+2Ch] [rbp-8A4h]
int LEN_SECOND; // [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 **ptr; // [rsp+48h] [rbp-888h]
void *src; // [rsp+50h] [rbp-880h]
void *fake_; // [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 res[16]; // [rsp+80h] [rbp-850h] BYREF
char LOOKLIKE_IV[32]; // [rsp+90h] [rbp-840h] BYREF
char key[1024]; // [rsp+B0h] [rbp-820h] BYREF
char PLAINTEXT[1032]; // [rsp+4B0h] [rbp-420h] BYREF
unsigned __int64 v30; // [rsp+8B8h] [rbp-18h]
v9 = a1;
dest_ = a2;
v30 = __readfsqword(0x28u);
v12 = 0xB0;
v19 = 175LL;
v3 = alloca(176LL);
ptr = &dest_;
__isoc99_scanf("%[^\n]16s", key);
__isoc99_scanf("%1024s", PLAINTEXT);
v13 = 16;
src = CREATE_255_ARRAY("%1024s");
expand_key((__int64)ptr, (__int64)key, v13, v12);
qmemcpy(LOOKLIKE_IV, "[yb2zg5w7k|yiyi$", 16);
LOOKLIKE_IV[16] = 0xF;
memcpy(SBOX, src, sizeof(SBOX));
if ( debug_flag )
{
fake_ = malloc(0x400uLL);
FAKE_ARRAY(fake_);
memcpy(SBOX, fake_, sizeof(SBOX));
}
XOR((__int64)LOOKLIKE_IV);
LEN_S = strlen(LOOKLIKE_IV);
LEN_SECOND = strlen(PLAINTEXT);
v16 = (LEN_SECOND + LEN_S) / 16 + 1;
v17 = 16 * v16;
v23 = 16 * v16 - 1LL;
v4 = alloca(16 * ((16 * v16 + 15LL) / 0x10uLL));
dest = &dest_;
memcpy(&dest_, LOOKLIKE_IV, LEN_S);
memcpy((char *)dest + LEN_S, PLAINTEXT, LEN_SECOND);
c = v17 - LEN_S - LEN_SECOND;
memset((char *)dest + LEN_SECOND + (__int64)LEN_S, c, c);
for ( i = 0; i < v16; ++i )
{
v5 = (__int64 *)((char *)dest + 16 * i);
v6 = v5[1];
v25[0] = *v5;
v25[1] = v6;
AES((__int64)v25, (__int64)res, (__int64)key, 0x10u);
for ( j = 0; j <= 15; ++j )
printf("%2.2x", (unsigned __int8)res[j]);
}
putchar(10);
return 0LL;
}
```
Tóm tắt qua bài này đi thì đây là một bài sử dụng mã hóa AES mod ECB nhưng với SBOX là mảng các phần tử từ 0 đến 255. Vì cái SBOX này nên mình thực sự tốn nhiều thời gian để giải quyết nó. Cơ bản mình không rành gì về mấy thuật toán mã hóa, nhưng sau bài này cũng nắm sơ về AES.
Nếu để ý trên code mình rev lại thì chuỗi s ở dòng 43 đã được mình rename lại thành `LOOKLIKE_IV`. Và ye đó là một lỗi sai ngớ ngẩn của mình khi làm bài này. Mình bắt đầu ngồi rev lúc cả nhóm mới học xong và ghé quán cafe trường ngồi nghỉ, nên thời gian làm cũng không có nhiêu.
Vì vậy mình đã không debug mà đọc sơ qua code, đọc sơ xong nhầm key thành IV luôn. Thực chất chuỗi s đó là key. Vì nhầm lẫn nên mình tưởng đây là một bài AES sử dụng mode CBC.

Tới lúc rev lại tất cả các hàm của bài thì mình nhận ra không có đoạn nào xor IV với cả block data cả. Vì vậy mới debug kĩ thì thấy đây là mode ECB. Nhưng mode nào cũng không quan trọng vì đồng đội crypto của mình `@tranminhprvt01` đã kiếm được một paper rất hay để giải quyết vấn đề dễ dàng.
Vậy nói sơ qua về flow bài:
* Nhận 2 input
* Input 1 chính là key
* Input 2 là plaintext
* Khởi tạo SBOX = [0x00, 0x01,..., 0xff]
* Check anti-debug (cái này tác giả đã viết hàm check ptrace bằng asm dạng gọi syscall và còn đặt trong initarray nên không để ý sẽ không biết đây là flag check debug vì ban đầu flag này khai báo là 1 và 1 nghĩa là đang debug, qua hàm check debug nếu không debug nó sẽ trở thành 0 nên dễ nghĩ điều kiện 1 trong if là dĩ nhiên)
* Nếu bị phát hiện debug thì SBOX đổi thành một mảng khác.
* Sau khi chuỗi `LOOKLIKE_IV` qua hàm xor sẽ thành chuỗi `Tis is a secret:`, thôi gọi đây là chuỗi s nhá, mình sẽ còn đề cập tới chuỗi này.
* dest sẽ chứa: 16 byte mảng s + plaintext
* Sau đó qua hàm AES (ECB) encrypt rồi in ra thôi
Lúc làm bài với teammate mình đã thử lấy chuỗi s làm input đầu vì mình thấy nó khá là sú. Và đúng thật s chính là key.

Đến lúc này thì mình đã biết rõ chương trình làm gì rồi. Ta đã có key và ciphertext, việc còn lại chỉ cần decrypt ra flag mà thôi.
***
Việc tìm code implement aes để decrypt rất mất thời gian...
***
Do lúc làm bài này mình muốn tự làm thật kĩ và làm hết nên mình tự đi kiếm code implement của aes, nhưng như đã nói ở trên. Lúc đầu mình nhầm nó là aes cbc 256 nên đi kiếm code mãi mà không được. Tới tối mình mới biết nó là ECB 128 bit, cũng thử kiếm code nhưng cái khó là thằng SBOX.
Mình không biết làm sao để tính được inverse của thằng SBOX đặc biệt này. Trong lúc mình đang ngồi search cách tính inv SBOX thì teammate crypto của mình `@tranminhprvt01` đã kiếm dược một trang nói về vấn đề này.

***
Cũng ra gì đấy, chuẩn vấn đề mình đang gặp luôn >:0
***
### **SOL**
Đào vô sâu thì đã có một người đưa ra giải pháp cho vấn đề này. Cơ bản thì sau khi thay thế SBOX mới, nếu ta thay plaintext đầu vào bằng các giá trị 0 thì ta sẽ nhận được một mảng gọi là `equivalent_key`...(Chắc chờ cryptomate giải thích phần này rồi bổ sung writeup sau, mình không thực sự hiểu rõ cách làm này lắm nên không dám múa rìu qua mắt thợ)

Vào POC của họ lấy code về thì để thử decrypt với key đã tìm được thì...

***
Update giải thích súc tích của cryptomate.

***
### **FLAG**
**`bi0sctf{L1n34rly_Un5huffl3d_T0y5}`**