# micromicromicropython Write-up (Bản tiếng Việt)

## 1. Phân tích lỗ hổng & Cú lừa (The Decoy)
Thử thách cung cấp một môi trường MicroPython bị cắt giảm nặng nề (bản `minimal` và module `os` đã bị vô hiệu hóa). Kiểm tra mã nguồn, có một file `catflag.c` chứa một cờ (flag) được hardcode: `ctf{ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL_1FAEFB6177B4672DEE07F9D3AFC62588CCD2631EDCF22E8CCC1FB35B501C9C86}`.

Tuy nhiên, **đây chỉ là một cờ mồi (decoy)!** Cờ thật chỉ có thể lấy được trên môi trường remote bằng cách thực thi thành công file binary `/catflag`. Thử thách yêu cầu chúng ta tìm một lỗ hổng trong MicroPython để đạt được khả năng thực thi mã (RCE).

Lỗ hổng cốt lõi nằm ở hàm `mp_import_all` (được kích hoạt bởi `from module import *`). Khi một đối tượng `staticmethod` bọc một đối tượng `bytes` được chèn vào `sys.modules`, `mp_import_all` sẽ coi raw bytes đó như một cấu trúc `mp_map_t` và duyệt qua nó. Mặc dù việc đọc quá giới hạn của mảng byte ban đầu cuối cùng sẽ gây ra lỗi out-of-bounds read và crash (segfault/TypeError), nhưng **entry đầu tiên** trong map giả mạo này đã kịp được phân tích cú pháp thành công và import vào dictionary `globals()` trước khi crash xảy ra!

Điều này mang lại cho chúng ta một **primitive import raw pointer**: chúng ta có thể đặt một địa chỉ bộ nhớ bất kỳ vào đối tượng `bytes` và ép interpreter (trình thông dịch) hiển thị nó như một đối tượng Python.

## 2. Chiến thuật khai thác
Quá trình khai thác diễn ra qua nhiều giai đoạn bằng cách sử dụng primitive type confusion (nhầm lẫn kiểu dữ liệu) này.

### Bước 1: Khôi phục `VfsPosix`
Mặc dù module `os` đã bị loại bỏ, các lớp Virtual File System (VFS) nội bộ, chẳng hạn như `VfsPosix`, vẫn được biên dịch trong binary.
Bằng cách leak địa chỉ của một hàm đã biết (ví dụ: `id(exec)`) và cộng/trừ với một offset PIE cố định, chúng ta có thể tính toán được địa chỉ của type object `VfsPosix`. Sử dụng primitive import thông qua `staticmethod(bytes)`, chúng ta import lớp `VfsPosix` vào namespace của mình.

### Bước 2: Đọc file tùy ý (`/proc/self/maps` & `/proc/self/mem`)
Với việc có trong tay lớp `VfsPosix`, chúng ta có thể khởi tạo nó và sử dụng để mở các file trên hệ thống:
```python
fs = VfsPosix('/')
maps = fs.open('/proc/self/maps', 'r')
mem = fs.open('/proc/self/mem', 'rb')
```
Đọc file `/proc/self/maps` cho phép chúng ta leak base address của thư viện `ld-musl`. Từ base này, chúng ta có thể tính toán địa chỉ chính xác của hàm `system()`.

### Bước 3: Làm giả một Built-in Function (Hàm tích hợp sẵn)
Để gọi `system()`, chúng ta tạo một struct `mp_obj_fun_builtin_fixed_t` giả (một built-in function nhận 1 tham số) trong bộ nhớ.
Một bước quan trọng ở đây là bypass ASLR cho các con trỏ type nội bộ. Thay vì hardcode type pointer của một builtin function, chúng ta sử dụng primitive đọc bộ nhớ tùy ý (thông qua `/proc/self/mem`) để đọc trực tiếp header của một builtin function đang "sống" như hàm `len`. Chúng ta sao chép header hợp lệ này để tạo ra một đối tượng function giả mạo hoàn hảo, với function pointer đã bị ghi đè bằng địa chỉ của `system()`.

### Bước 4: Thực thi `/catflag`
Chúng ta cũng cần một tham số cho `system()`. Chúng ta tạo một đối tượng bytes `b"/catflag\x00"` và dùng `/proc/self/mem` để tìm ra con trỏ dữ liệu thô (raw data pointer) của nó. Sau đó, chúng ta sử dụng primitive import một lần cuối để import con trỏ chuỗi C thô này dưới dạng một biến Python.

Cuối cùng, chúng ta gọi hàm giả mạo với con trỏ chuỗi giả mạo:
```python
S(C) # Tương đương với system("/catflag")
```

## 3. Bài học rút ra
* **Mồi nhử (Decoys):** Đừng tin tưởng mù quáng vào các file source (như `catflag.c`) trong file zip nếu thử thách có gợi ý về "heavy stripping" (cắt giảm nặng) hoặc 0-days. Flag thực sự có thể được tạo động hoặc hoàn toàn khác biệt trên server remote.
* **Thực thi một phần vẫn là Thực thi:** Ban đầu, crash do type confusion trong `mp_import_all` có vẻ giống như đi vào ngõ cụt vì chắc chắn sẽ bị segfault. Tuy nhiên, việc hiểu rằng vòng lặp *đầu tiên* thành công và kịp thay đổi `globals()` trước khi crash chính là chìa khóa. Bạn không cần một đối tượng fake hoàn hảo từ đầu đến cuối; bạn chỉ cần nó "sống" đủ lâu để thực hiện được mục đích phá hoại.
* **Clone type động:** Việc hardcode các type pointer nội bộ (như `&mp_type_fun_builtin_1`) rất dễ vỡ (brittle) và dễ gặp lỗi trên các connection khác nhau. Việc sao chép (clone) động header của một đối tượng đang sống (như `len`) thông qua primitive đọc tùy ý giúp cho exploit hoạt động ổn định 100%.

## 4. Flag
`bctf{this_was_originally_going_to_be_a_0day_but_someone_reported_it}`
