Buổi 09: Off-by-one & Return-to-libc
Tổng quan – Buffer Overflow và các biến thể
Khai thác lỗ hổng tràn bộ đệm (buffer overflow) trong trường hợp lý tưởng yêu cầu:
- Kiểm soát được return address (không có stack canary, có thể ghi đè đến vị trí return addr).
- Có thể truyền shellcode vào stack và thực thi (biên dịch với
-z execstack).
Tuy nhiên, thực tế thường có nhiều ràng buộc hơn. Từ đó xuất hiện các kỹ thuật tấn công nâng cao:
| Kỹ thuật | Tình huống áp dụng |
|---|---|
| Off-by-one | Chỉ ghi đè được thêm 1 byte, không đủ để ghi đè return addr trực tiếp |
| Return-to-libc | Stack không cho phép thực thi code, hoặc shellcode quá dài/phức tạp |
| ROP (Return Oriented Programming) | Stack không cho phép thực thi, cần chuỗi gadget phức tạp hơn |
Phần 1 – Tấn công Off-by-one
Khái niệm
Off-by-one là lỗi lập trình xảy ra khi vòng lặp hoặc thao tác bộ đệm truy xuất vượt ra ngoài giới hạn đúng 1 phần tử. Lỗi này thường xảy ra do nhầm lẫn giữa < và <= khi kiểm tra điều kiện vòng lặp.
Mặc dù chỉ ghi đè được 1 byte ngoài phạm vi buffer, kẻ tấn công có thể lợi dụng đây để thao túng gián tiếp luồng thực thi của chương trình.
Ví dụ lỗi điển hình
void bar() {
char buf[256];
int i;
for (i = 0; i <= 256; i++) // Lỗi: dùng <= thay vì
buf[i] = getchar();
// other statements...
}
void foo() {
bar();
}Cơ chế stack và saved EBP
Khi hàm foo() gọi bar(), stack có dạng (địa chỉ tăng từ dưới lên):
+------------------------+ ← địa chỉ cao hơn
| Return addr of foo |
| Saved EBP of foo |
| Local variables of foo |
+------------------------+
| Return addr of bar |
| Saved EBP of bar | ← buf[256] sẽ ghi đè 1 byte thấp nhất tại đây
| buf[0..255] |
| Other local vars bar |
+------------------------+ ← địa chỉ thấp hơnTác động khi ghi đè 1 byte của saved EBP
Nếu byte thấp nhất (LSB – Least Significant Byte) của saved EBP của bar bị thay đổi thành 0x00 (hoặc giá trị nhỏ hơn), thì EBP sẽ trỏ tới một vị trí thấp hơn trong stack – cụ thể là đâu đó bên trong buf.
Trước tấn công:
saved EBP của bar = 0xbffff4e0 → trỏ đúng vào stack frame của foo
Sau tấn công (ghi đè byte thấp = 0x00):
saved EBP của bar = 0xbffff400 → trỏ vào đâu đó thấp hơn, nằm trong buf!Hệ quả: Khi bar thực thi xong và trả về foo, CPU sẽ restore EBP từ giá trị đã bị thay đổi → foo sẽ không tìm được stack frame đúng của mình → không truy cập được biến cục bộ và return address chính xác.
Khai thác: Giả mạo stack frame
Ý tưởng tấn công là nhúng một stack frame giả (fake stack frame) vào bên trong buf, rồi khiến saved EBP trỏ vào đó.
+------------------------+ ← địa chỉ cao hơn
| Return addr of foo |
| Saved EBP of foo |
| Local variables of foo |
+------------------------+
| Return addr of bar |
| Saved EBP of bar (bị sửa → trỏ vào fake frame bên dưới)
+------------------------+
| [Fake stack frame | ← giả mạo stack frame của foo
| fake return addr | ← địa chỉ attacker muốn nhảy tới
| fake saved EBP |
| fake local vars |
| ] |
| Other local vars bar |
+------------------------+ ← địa chỉ thấp hơnKhai thác nâng cao: Kết hợp Injected Code
Attacker có thể đặt thêm shellcode ngay cạnh fake stack frame, và đặt return address trong fake frame trỏ vào shellcode đó:
+---------------------------+
| Return addr of bar |
| Saved EBP of bar (sửa 1 byte) |
+---------------------------+
| Injected shellcode | ← code thực thi
| [Fake frame của foo] |
| ret addr → shellcode |
| fake EBP |
| buf (padding) |
+---------------------------+Ví dụ lỗi off-by-one khác
void receive(int socket) {
char buf[MAX];
int nbytes = recv(socket, buf, sizeof(buf), 0);
buf[nbytes] = '\0'; // Lỗi: nếu nbytes = MAX thì ghi ra ngoài mảng
...
}Tóm tắt Off-by-one
Tóm tắt
- Lỗi do dùng
<=thay<, hoặc thiếu kiểm tra giới hạn nghiêm ngặt. - Chỉ cần ghi đè 1 byte vào saved EBP là đủ để thao túng gián tiếp.
- Attacker xây dựng fake stack frame bên trong buffer để kiểm soát return address.
- Kết hợp với injected code hoặc Return-to-libc để thực thi code tùy ý.
- Rất khó phát hiện và ngăn chặn vì lỗi chỉ sai lệch 1 đơn vị.
Phần 2 – Tấn công Return-to-libc
Khái niệm
Return-to-libc (còn gọi là Arc Injection) là kỹ thuật khai thác buffer overflow mà không cần inject shellcode. Thay vào đó, attacker chuyển hướng luồng thực thi sang các hàm có sẵn trong thư viện C chuẩn (libc).
Tại sao cần Return-to-libc?
| Vấn đề | Giải pháp |
|---|---|
| Stack được đánh dấu non-executable (NX/DEP) | Không inject shellcode được → dùng code đã có sẵn |
| Shellcode quá dài, không vừa buffer | Dùng hàm libc ngắn gọn |
| Shellcode quá phức tạp | Gọi trực tiếp system() |
Ví dụ chương trình bị khai thác
int main(int argc, char *argv[]) {
char buf[4];
strcpy(buf, argv[1]); // Lỗi: không kiểm tra độ dài
return 0;
}Ý tưởng tấn công
Mục tiêu: Mở shell tương tác mà không truyền shellcode bằng cách gọi:
system("/bin/bash");Để làm điều này, attacker cần:
- Ghi đè return address của
mainbằng địa chỉ của hàmsystem()trong libc. - Đặt tham số
/bin/bashvào đúng vị trí màsystem()sẽ đọc (ngay sau fake return address củasystem).
Cấu trúc stack trước và sau khai thác
Trước khai thác:
+---------------------------+
| Other data in stack |
| Return addr of main | ← địa chỉ hợp lệ
| Saved EBP |
| buf[4] |
+---------------------------+Sau khai thác:
+---------------------------+
| Addr of "/bin/bash" | ← tham số thứ 1 của system()
| Return addr of system | ← sau khi system() xong trả về đâu (thường là exit)
| Address of system() | ← ghi đè return addr của main
| Saved EBP (overwritten) |
| buf (padding) |
+---------------------------+Tìm địa chỉ cần thiết
1. Địa chỉ hàm system():
$ gdb ./vulnerable_program
(gdb) run
(gdb) print system
$1 = {<text variable, no debug info>} 0xb7e5f430 <system>2. Địa chỉ chuỗi /bin/bash:
Các cách tìm địa chỉ chuỗi `/bin/bash`
- Cách 1: Biến môi trường
SHELL=/bin/bashthường tồn tại sẵn.(gdb) x/s *((char **)environ+i) # i là thứ tự biến môi trường - Cách 2: Tự tạo biến môi trường chứa chuỗi mong muốn:Rồi dùng gdb để đọc địa chỉ của nó tương tự.
export MYVAR="/bin/bash"
Gọi chuỗi nhiều hàm (Chained Return-to-libc)
Đôi khi attacker cần thực thi nhiều hàm libc liên tiếp (ví dụ: setuid(0) → system("/bin/bash")). Kỹ thuật này gọi là chaining.
Nguyên tắc
- Mỗi hàm cần một fake stack frame riêng trong payload.
- Return address của hàm trước trỏ vào đoạn code của hàm sau (bỏ qua phần set-up stack của hàm sau để không làm hỏng fake frame).
- Saved EBP của từng hàm cần trỏ đúng vào vị trí EBP trong fake frame tiếp theo.
Sơ đồ chuỗi gọi hàm f1 → f2 → f3
Cấu trúc stack chi tiết
+-------------------------------+
| Parameters for f3 |
| Return addr for f3 (→ exit) |
| Fake saved EBP of f3 | ← f3's frame
| Local variables of f3 |
+-------------------------------+
| Parameters for f2 |
| Return addr for f2 (→ f3) |
| EBP trỏ vào fake frame f3 | ← f2's frame
| Local variables of f2 |
+-------------------------------+
| Parameters for f1 |
| Return addr for f1 (→ f2) |
| EBP trỏ vào fake frame f2 | ← f1's frame
+-------------------------------+
| Overwritten ret addr (→ f1) |
| Overwritten EBP |
| buf (padding) |
+-------------------------------+Tóm tắt Return-to-libc
Tóm tắt
- Không cần shellcode → bypass NX/DEP.
- Tận dụng các hàm libc như
system(),execve()để thực thi lệnh hệ thống. - Cần biết địa chỉ chính xác của hàm libc và chuỗi tham số trong bộ nhớ.
- Có thể kết hợp nhiều hàm thành chuỗi để thực hiện tác vụ phức tạp.
- Là tiền thân của kỹ thuật ROP (Return Oriented Programming) hiện đại hơn.
Biện pháp phòng chống
| Biện pháp | Mô tả |
|---|---|
| Stack Canary | Đặt một giá trị ngẫu nhiên giữa buffer và saved EBP/return addr. Nếu bị ghi đè → phát hiện ngay. |
| Stack Randomization (ASLR) | Ngẫu nhiên hóa địa chỉ stack mỗi lần chạy → attacker không đoán được địa chỉ. |
| Library Randomization (ASLR) | Ngẫu nhiên hóa địa chỉ load của libc → attacker không biết địa chỉ system(). |
| StackGuard | Công cụ biên dịch tích hợp canary tự động. |
| StackShield | Lưu return address ở vùng nhớ an toàn để so sánh trước khi ret. |
Câu hỏi & Trắc nghiệm
Câu 1. Lỗi Off-by-one thường xuất hiện do nguyên nhân nào sau đây?
- A. Sử dụng hàm
malloc()không đúng cách - B. Dùng
<=thay vì<trong điều kiện vòng lặp khi duyệt mảng - C. Quên khởi tạo biến
- D. Sử dụng con trỏ NULL
Câu 2. Trong ví dụ for(i = 0; i <= 256; i++) buf[i] = getchar(); với buf[256], phần tử buf[256] nằm ở đâu trong stack (giả sử buf nằm liền kề saved EBP)?
- A. Trong vùng local variables của hàm
- B. Tại vị trí của saved EBP của hàm hiện tại
- C. Tại vị trí return address của hàm
- D. Nằm ngoài stack hoàn toàn
Câu 3. Tấn công Off-by-one ghi đè 1 byte vào saved EBP nhằm mục đích gì?
- A. Xóa toàn bộ stack frame hiện tại
- B. Làm cho EBP trỏ vào một vị trí attacker kiểm soát được (fake stack frame trong buf)
- C. Ghi đè trực tiếp return address
- D. Làm crash chương trình
Câu 4. Sau khi saved EBP của bar bị ghi đè 1 byte thành 0x00, điều gì xảy ra khi bar trả về foo?
- A.
foothực thi bình thường - B. Chương trình bị segmentation fault ngay lập tức
- C.
fookhôi phục EBP từ giá trị đã bị thay đổi → không tìm được đúng stack frame → không truy xuất được biến cục bộ và return address chính xác - D. Stack bị xóa trắng
Câu 5. Tấn công Return-to-libc còn có tên gọi khác là gì?
- A. Stack Smashing
- B. Arc Injection
- C. Heap Spray
- D. Format String Attack
Câu 6. Return-to-libc được sử dụng để bypass biện pháp bảo vệ nào?
- A. Stack Canary
- B. ASLR
- C. NX (Non-Executable Stack) / DEP
- D. PIE (Position Independent Executable)
Câu 7. Trong kỹ thuật Return-to-libc, sau khi ghi đè return address, payload cần có thêm gì?
- A. Một NOP sled
- B. Return address giả của
system()và địa chỉ chuỗi tham số (vd:/bin/bash) - C. Toàn bộ shellcode
/bin/sh - D. Địa chỉ của hàm
main
Câu 8. Trong GDB, lệnh nào được dùng để tìm địa chỉ hàm system() trong libc?
- A.
info system - B.
print system - C.
find system - D.
locate system
Câu 9. Để tìm địa chỉ chuỗi /bin/bash từ biến môi trường trong GDB, ta dùng lệnh nào?
- A.
print env SHELL - B.
x/s *((char **)environ+i) - C.
info environment - D.
show env
Câu 10. Đoạn code buf[nbytes] = '\0'; trong hàm receive() có thể gây ra off-by-one khi nào?
- A. Khi
nbytes = 0 - B. Khi
nbytes = sizeof(buf)(nhận đúng MAX bytes) - C. Khi socket bị ngắt kết nối
- D. Khi
bufchưa được khởi tạo
Câu 11. Tấn công Off-by-one khác với tràn bộ đệm thông thường ở điểm gì?
- A. Off-by-one không sử dụng buffer overflow
- B. Off-by-one chỉ ghi đè được tối đa 1 byte ngoài phạm vi buffer thay vì ghi đè trực tiếp nhiều byte đến return address
- C. Off-by-one chỉ xảy ra trên heap
- D. Off-by-one không thể khai thác được
Câu 12. Khi thực hiện Return-to-libc để gọi system("/bin/bash"), return address của hàm system() trong payload thường được đặt là gì?
- A. Địa chỉ của hàm
main - B. Địa chỉ của hàm
exit()hoặc một địa chỉ hợp lệ để thoát sạch - C.
0x00000000 - D. Địa chỉ quay lại vùng buf
Câu 13. Kỹ thuật Return-to-libc là tiền thân của kỹ thuật nào sau đây?
- A. Heap Spray
- B. Format String Attack
- C. ROP (Return Oriented Programming)
- D. Use-After-Free
Câu 14. Stack Canary bảo vệ chống overflow như thế nào?
- A. Ngăn chặn việc ghi vào stack hoàn toàn
- B. Đặt một giá trị ngẫu nhiên giữa buffer và saved EBP/return address; kiểm tra trước khi hàm trả về
- C. Mã hóa toàn bộ stack
- D. Xóa stack sau mỗi lần hàm trả về
Câu 15. Tại sao ASLR (Address Space Layout Randomization) làm khó tấn công Return-to-libc?
- A. ASLR mã hóa các hàm libc
- B. ASLR ngẫu nhiên hóa địa chỉ load của libc mỗi lần chạy → attacker không biết địa chỉ chính xác của
system() - C. ASLR vô hiệu hóa libc hoàn toàn
- D. ASLR đặt libc vào vùng nhớ không thể truy cập
Câu 16. Trong calling convention x86 (cdecl), tham số của hàm được truyền bằng cách nào?
- A. Qua thanh ghi EAX, EBX
- B. Qua stack, được đẩy từ phải sang trái trước khi gọi hàm
- C. Qua thanh ghi ECX, EDX
- D. Qua một vùng nhớ toàn cục
Câu 17. Trong chained Return-to-libc gọi f1 → f2 → f3, return address của f1 phải trỏ đến đâu?
- A. Đầu của hàm f2 (bao gồm cả phần prologue)
- B. Sau phần prologue của f2 để không làm hỏng fake stack frame đã chuẩn bị
- C. Địa chỉ của f3
- D. Địa chỉ của
exit()
Câu 18. Hàm strcpy() nguy hiểm vì lý do gì?
- A. Nó chạy chậm hơn
memcpy() - B. Nó không kiểm tra độ dài của chuỗi đích, có thể ghi vượt quá kích thước buffer
- C. Nó không hỗ trợ Unicode
- D. Nó yêu cầu quyền root để chạy
Câu 19. Tùy chọn -z noexecstack khi biên dịch có tác dụng gì?
- A. Tắt stack canary
- B. Đánh dấu vùng nhớ stack là không thể thực thi code (NX)
- C. Tối ưu hóa stack
- D. Tắt ASLR
Câu 20. Công cụ nào sau đây dùng để kiểm tra các biện pháp bảo vệ của một binary?
- A.
nm - B.
objdump - C.
checksec - D.
strings
Câu 21. Khi khai thác Return-to-libc, tại sao attacker lại nhắm đến hàm system() thay vì tự viết shellcode?
- A. Vì
system()nhanh hơn shellcode - B. Vì
system()luôn tồn tại trong bộ nhớ của process dưới dạng code thực thi, không cần inject code mới - C. Vì shellcode bị antivirus phát hiện
- D. Vì
system()không cần tham số
Câu 22. Trong Off-by-one attack, byte bị ghi thêm thường là byte nào trong saved EBP bị ảnh hưởng?
- A. Byte cao nhất (MSB)
- B. Byte thấp nhất (LSB)
- C. Byte giữa
- D. Tất cả 4 byte
Câu 23. Tùy chọn -m32 khi biên dịch dùng để làm gì?
- A. Tối ưu hóa cho kiến trúc 32-bit
- B. Biên dịch ra binary 32-bit trên máy 64-bit
- C. Bật chế độ debug 32-bit
- D. Giới hạn bộ nhớ sử dụng xuống 32MB
Câu 24. StackShield bảo vệ như thế nào khác với Stack Canary?
- A. StackShield mã hóa return address
- B. StackShield lưu bản sao của return address vào vùng nhớ an toàn riêng biệt và so sánh trước khi thực thi
ret - C. StackShield tắt hoàn toàn khả năng ghi vào stack
- D. StackShield và Canary hoàn toàn giống nhau
Câu 25. Lệnh export MYSHELL=/bin/bash trong shell có tác dụng gì trong ngữ cảnh tấn công?
- A. Thay đổi shell mặc định của hệ thống
- B. Tạo biến môi trường chứa chuỗi
/bin/bashtrong bộ nhớ của process → attacker có thể dùng GDB tìm địa chỉ của chuỗi này để dùng làm tham số chosystem() - C. Cài đặt bash mới
- D. Tắt các biện pháp bảo mật
Câu 26. Trong stack layout của x86, saved EBP nằm ở đâu so với return address?
- A. Saved EBP nằm phía dưới return address (địa chỉ thấp hơn)
- B. Saved EBP nằm phía trên return address (địa chỉ cao hơn)
- C. Saved EBP và return address cùng vị trí
- D. Vị trí của saved EBP không cố định
Câu 27. Tại sao tấn công Off-by-one rất khó ngăn chặn?
- A. Vì nó yêu cầu phần cứng đặc biệt
- B. Vì lỗi chỉ sai lệch 1 đơn vị rất dễ bỏ qua khi review code, và một số biện pháp bảo vệ như canary không phát hiện được nếu chỉ ghi đè EBP mà không đụng đến canary
- C. Vì không có công cụ phân tích tĩnh nào phát hiện được
- D. Vì nó không tạo ra bất kỳ lỗi nào trong quá trình chạy
Câu 28. ROP (Return Oriented Programming) khác Return-to-libc như thế nào?
- A. ROP không sử dụng bộ nhớ của libc
- B. ROP ghép các “gadgets” nhỏ (đoạn code kết thúc bằng
ret) thay vì nhảy vào toàn bộ một hàm, cho phép thực thi tác vụ phức tạp hơn - C. ROP chỉ hoạt động trên Linux
- D. ROP và Return-to-libc hoàn toàn giống nhau
Câu 29. Hàm recv() trong đoạn code int nbytes = recv(socket, buf, sizeof(buf), 0); trả về giá trị gì?
- A. Số bytes đã gửi
- B. Số bytes đã nhận thực tế
- C. Kích thước của buffer
- D. Luôn trả về 0 nếu thành công
Câu 30. Trong payload tấn công Return-to-libc cho chương trình có buf[4], padding trước return address thường có kích thước là bao nhiêu (giả sử không có canary)?
- A. 4 bytes
- B. 4 bytes (buf) + 4 bytes (saved EBP) = 8 bytes
- C. Đúng bằng kích thước của buf
- D. Tùy thuộc vào giá trị canary
Câu 31. Trong đoạn code for(i = 0; i <= 256; i++) buf[i] = getchar();, nếu buf nằm cách saved EBP 8 bytes (có thêm biến cục bộ khác), off-by-one có còn khai thác được không?
- A. Vẫn khai thác được như cũ
- B. Không khai thác được vì byte ghi thêm rơi vào biến cục bộ khác, không chạm đến saved EBP
- C. Khai thác được nhưng cần kỹ thuật khác
- D. Chỉ phụ thuộc vào compiler
Câu 32. Biện pháp bảo vệ nào có thể ngăn cả Off-by-one lẫn Return-to-libc cùng lúc?
- A. Chỉ cần NX/DEP
- B. Kết hợp ASLR + Stack Canary + NX
- C. Chỉ cần Stack Canary
- D. Chỉ cần ASLR
Câu 33. Lệnh leave trong assembly x86 tương đương với gì?
- A.
pop ebp; mov esp, ebp - B.
mov esp, ebp; pop ebp - C.
push ebp; mov ebp, esp - D.
pop ebp; pop esp
Câu 34. Tại sao kỹ thuật “Fake Stack Frame” trong Off-by-one được gọi là tấn công gián tiếp?
- A. Vì attacker không cần trực tiếp viết vào stack
- B. Vì attacker không ghi đè trực tiếp return address mà thay đổi EBP để trỏ vào fake frame, từ đó gián tiếp kiểm soát return address khi hàm cha thực thi
- C. Vì tấn công xảy ra từ xa qua mạng
- D. Vì cần thêm một process khác để thực hiện
Câu 35. Trong x86, thanh ghi EBP có vai trò gì?
- A. Lưu địa chỉ trả về của hàm
- B. Trỏ tới đỉnh của stack
- C. Trỏ tới đáy của stack frame hiện tại (base pointer), dùng để truy cập biến cục bộ và tham số một cách ổn định
- D. Lưu kết quả phép tính
Câu 36. Kỹ thuật nào giúp attacker thực thi setuid(0) rồi system("/bin/bash") để leo thang đặc quyền?
- A. Single Return-to-libc
- B. Chained Return-to-libc (gọi chuỗi nhiều hàm)
- C. Off-by-one đơn giản
- D. Stack Spray
Câu 37. Điều gì xảy ra nếu không cung cấp tham số cho system() trong payload Return-to-libc?
- A.
system()tự động dùng/bin/sh - B. Chương trình bị crash hoặc hành vi không xác định vì
system()đọc dữ liệu rác tại vị trí tham số - C.
system()không làm gì và trả về 0 - D.
system()mở terminal mặc định
Câu 38. Công cụ CTF nào được đề cập trong tài liệu để học về binary exploitation?
- A. Metasploit
- B. Burp Suite
- C. CTF Wiki, Modern Binary Exploitation (MBE), Nightmare
- D. Wireshark
Câu 39. Trong GDB, lệnh x/s có nghĩa gì?
- A. Thực thi chương trình
- B. Examine memory và hiển thị nội dung dưới dạng chuỗi ký tự (string)
- C. Export symbol table
- D. Extract section
Câu 40. Tùy chọn -z execstack khi biên dịch có tác dụng gì?
- A. Tắt tối ưu hóa stack
- B. Cho phép thực thi code trên stack (ngược với noexecstack)
- C. Tạo stack lớn hơn
- D. Bật stack canary
Câu 41. Khi thực hiện chained Return-to-libc, saved EBP của f1 cần trỏ đến đâu?
- A. Saved EBP của f3
- B. Vị trí EBP trong fake stack frame của f2
- C. Địa chỉ bắt đầu của buf
- D. Địa chỉ
0x00000000
Câu 42. Biện pháp phòng thủ nào TỐT NHẤT để ngăn lỗi off-by-one ở tầng phát triển phần mềm?
- A. Dùng ASLR
- B. Kiểm tra cẩn thận điều kiện vòng lặp, sử dụng hàm an toàn (
strncpythaystrcpy,fgetsthaygets), và áp dụng static analysis tools - C. Dùng ngôn ngữ lập trình Assembly
- D. Tắt stack canary để dễ debug
Câu 43. Trong stack x86, địa chỉ “tăng” theo hướng nào?
- A. Stack tăng về phía địa chỉ cao hơn khi push
- B. Stack tăng về phía địa chỉ thấp hơn khi push (stack grows downward)
- C. Stack tăng về phía địa chỉ cao hơn khi pop
- D. Stack không có chiều tăng cố định
Câu 44. Nếu attacker tự tạo biến môi trường export EXPLOIT="/bin/bash" và chuỗi này nằm ở địa chỉ 0xbffffed0 trong bộ nhớ, attacker sẽ đặt giá trị nào vào vị trí tham số của system() trong payload?
- A. Chuỗi
/bin/bashtrực tiếp - B.
0xbffffed0(địa chỉ của chuỗi trong bộ nhớ) - C. Độ dài của chuỗi (10 bytes)
- D. Địa chỉ của biến môi trường (không phải chuỗi)
Câu 45. Điều nào sau đây ĐÚNG về sự khác biệt giữa heap overflow và stack overflow trong ngữ cảnh tấn công?
- A. Heap overflow không nguy hiểm
- B. Stack overflow ghi đè trực tiếp return address/saved EBP; heap overflow thường phá vỡ metadata của heap allocator hoặc con trỏ hàm lưu trên heap
- C. Heap overflow luôn dễ khai thác hơn
- D. Cả hai hoàn toàn giống nhau
Câu 46. Trong bài giảng, ví dụ về off-by-one với buf[nbytes] = '\0' – điều gì xảy ra nếu attacker gửi đúng MAX bytes qua socket?
- A. Chương trình hoạt động bình thường
- B.
nbytes = MAX→buf[MAX]được gán'\0'→ ghi 1 byte0x00ra ngoài mảng, có thể ghi đè LSB của saved EBP thành 0 - C. Chương trình từ chối nhận thêm dữ liệu
- D.
recv()trả về lỗi
Câu 47. Tại sao việc gọi system() mở shell lại hữu ích cho attacker hơn là chỉ thực thi một lệnh đơn lẻ?
- A. Vì
system()nhanh hơn - B. Vì shell tương tác cho phép attacker tiếp tục tương tác với hệ thống sau khai thác, thực thi nhiều lệnh tùy ý, leo thang đặc quyền, v.v.
- C. Vì shell không cần quyền root
- D. Vì
system()không bị ghi log
Câu 48. Khi thực hành tấn công Return-to-libc trong môi trường lab, tại sao ASLR thường được tắt?
- A. ASLR làm chậm hệ thống
- B. ASLR ngẫu nhiên hóa địa chỉ libc và stack mỗi lần chạy, nên attacker không thể hardcode địa chỉ trong payload → cần tắt để học cơ chế cơ bản trước
- C. ASLR không hoạt động trên máy ảo
- D. ASLR chỉ áp dụng cho kernel
Câu 49. Tổng kết: Điểm chung giữa Off-by-one và Return-to-libc là gì?
- A. Cả hai đều cần inject shellcode
- B. Cả hai đều là biến thể của khai thác buffer overflow nhằm kiểm soát luồng thực thi (control flow hijacking), dù cách thức khác nhau
- C. Cả hai chỉ hoạt động trên Windows
- D. Cả hai đều bị ngăn chặn hoàn toàn bởi stack canary
Câu 50. Nếu một chương trình được biên dịch với đầy đủ ASLR + NX + Stack Canary + PIE, kỹ thuật tấn công nào vẫn còn khả thi nhất?
- A. Shellcode injection
- B. Return-to-libc đơn giản
- C. ROP chain kết hợp với information leak để bypass ASLR
- D. Off-by-one trực tiếp