Writeup: Pwnable.tw
Calc (150 points)
Phân tích chương trình:
- Challenge này mô phỏng một ứng dụng máy tính mô phỏng (calculator) cho phép người dùng nhập vào một biểu thức toán học, sau đó phân tích và trả về kết quả. Ứng dụng này sử dụng ELF 32-bit, No Pie và bật bảo vệ NX ngăn chặn việc thực thi trực tiếp shellcode.
- Lỗ hổng cốt lõi của chương trình nằm ở sự thiếu đồng bộ giữa bộ phân tích cú pháp
parse_exprvà hàm thực thi toán họceval. - Các đoạn code quan trọng: (lưu ý: một số biến trong file nhị phân được reverse bởi IDA đã được mình đổi lại cho dễ nhớ và dễ tiếp cận)
1: Hàm parse_expr
1 | if ( num > 0 ) { |
stack_so_hanglà một mảng đóng vai trò như Ngăn xếp Toán hạng (Operand Stack), với phần tử đầu tiên (*stack_so_hang) là biến đếm số lượng phần tử.- Đoạn code trên có nhiệm vụ: Nếu giải mã được một con số hợp lệ
(num > 0), nó sẽ tăng biến đếm lên 1 và đẩy con số đó vào mảng. - Giả định kịch bản Hacker: Nếu hacker cố tình nhập một biểu thức bắt đầu ngay bằng một toán tử (ví dụ: +360), chương trình sẽ không tìm thấy số nào trước dấu + nên num = 0.
- Khối lệnh if bị bỏ qua, dẫn đến việc biến đếm
*stack_so_hangkhông tăng (vẫn bằng 0), nhưng dấu + thì vẫn được đẩy vào Ngăn xếp Toán tử.
2: Hàm eval
1 | if ( stack_toan_tu == '+' ) { |
- Hàm eval lôi 2 con số trên đỉnh của ngăn xếp ra để thực hiện phép toán (ở đây là phép cộng), ghi đè kết quả vào toán hạng thứ nhất và sau đó giảm biến đếm đi 1(
--*stack_so_hang). - Giả định kịch bản Hacker (Tiếp nối): Tiếp tục với chuỗi +360.
- Sau khi đọc số 360, mảng có biến đếm
*stack_so_hang = 1 và stack_so_hang[1] = 360. - Khi gọi eval với dấu +, mã C thực thi:
stack_so_hang[*stack_so_hang - 1] += stack_so_hang[*stack_so_hang];(stack_so_hang[0] += stack_so_hang[1];) - Biến_đếm = 0 + 360 = 360.
- Hacker đã thành công biến chính con trỏ quản lý của mảng thành dữ liệu toán học, ép chỉ mục (index) nhảy thẳng đến vị trí 360 trên bộ nhớ (vượt ra ngoài ranh giới cho phép).
Hướng đi để Exploit
Bằng việc chiếm được quyền kiểm soát con trỏ chỉ mục, chúng ta có trong tay một công cụ Đọc/Ghi ngoài vùng nhớ (Out-of-Bounds Read/Write).
Mục tiêu cuối cùng là xây dựng một chuỗi ROP (Return-Oriented Programming) trên stack để gọi hệ thống
execve("/bin/sh", NULL, NULL).Bước 1: Rò rỉ địa chỉ bộ nhớ (Stack Leak / OOB Read)
Khoảng cách từ mảng stack_so_hang đến giá trị Saved EBP của hàm main là 360 phần tử (mỗi phần tử 4 byte). Gửi chuỗi +360. Ứng dụng sẽ thực hiện toán học tại vị trí này và in kết quả ra màn hình. Kết quả này chính là địa chỉ của bộ nhớ ngăn xếp hiện tại, giúp ta làm mốc để tính toán địa chỉ của chuỗi “/bin/sh” sẽ được bơm vào sau này.
Bước 2: Dọn dẹp Ngăn xếp (Clean Stack được dùng để ROP).
Nhằm mục đích chuyển hết value của vùng địa chỉ này về 0x0 để dễ tận dụng
evalghi đè value mong muốn. Hàmclean_rop_stack(20)của mình ở mục 3 sẽ thực hiện tốt bước này.Bước 3: Lắp ráp ROP Chain (OOB Write)
Do không thể ghi đè bộ nhớ một cách trực tiếp, ta dùng toán học:
Đọc giá trị hiện tại ở index, tính độ lệch (delta) so với địa chỉ Gadget mong muốn, rồi gửi biểu thức +index+delta hoặc +index-delta để cộng/trừ vào.
Ta sẽ rải lần lượt chuỗi ROP từ vị trí 361 (Return Address của hàm calc) trở đi.
Bước 4: Kích hoạt Shell
Gửi một chuỗi bất kỳ không hợp lệ (như xxx) để ngắt vòng lặp nhận dữ liệu của ứng dụng, ép hàm kết thúc và kích hoạt chuỗi ROP vừa rải trên ngăn xếp.
PoC
1 | #!/usr/bin/env python3 |








