Shellcoding Linux x86 JMP-CALL-POP technique (1/3)

Currently I’m studying SLAE certification of Pentester Academy and I found a really interesting video that explained this technique. I’m going to follow the course instructions step by step and try to explain it here the best as I can. By the way, I totally recommend this course, everything is really well explained and the content is really good.

So, following the SLAE topics, in this second article related with Assembly I am going to explain how to develop a small shellcode that prints “Hello World!” in the screen. In the last blog post, I already showed you how to write a Hello World, but to convert this into a shellcode we have 2 restrictions:


1) We can not have any 0x00 instructions. This are null bytes and usually break our shellcode execution.


2) We can not use any hard coded addresses. Our code should be able to run in different computer and programs, and the addresses need to be generated dynamically.

We start from this code:

; HelloWorld.nasm
; Author:  Vivek Ramachandran
; Website:  https://www.pentesteracademy.com
; Student: Xavi Bel

global _start

section .text

_start:

	; write syscall
	mov eax, 0x4
	mov ebx, 0x1
	mov ecx, message
	mov edx, mlen
	int 0x80

	; exit syscall
	mov eax, 0x1
	mov ebx, 0x0
	int 0x80

section .data

	message: db "Hello World!"
	mlen equ $-message

So let’s check if our code contains any null values or hard coded addresses. To do this we can use objdump utility:

objdump -d HelloWorldShellcode -M intel

So we have both problems, we have a lot of null bytes and the memory address of the “Hello World!” string.

Let’s start removing the null bytes, to do that we need to modify all the mov instructions that generated null bytes to equivalent instructions.

The way to do this is the following:
When we use an xor operation between the same values, the result is always zero. There is another thing that we need to know, the register EAX, can be segmented in AH(High) and AL(Low), please watch the image below, So we can mov a 4 into AL.

So instead of:

mov eax, 0x4

We can do:

xor eax, eax
mov al, 0x4

We can repeat this process for all the mov instructions that generated a 0x00.

Once we do that, we need to fix the other problem, the hard coded address of the variable message. I will explain what we are going to do: first we are going to move the array declaration inside the data segment, after that we are going to create a jmp to a procedure named call_shellcode, this procedure will contain two lines, the first one is a call shellcode, and the second one is the message, so once the call shellcode instruction will be executed, the direction of message will be saved in ecx register and we fix the original problem.

So, being squematic, the code will have this structure:

jmp short call_shellcode

shellcode:
   ...
   ...
   pop ecx ; So now the memory address of message is saved in ecx
   ...
   ...

call_shellcode:
   call shellcode
   message: db "Hello World!", 0xA

Doing this we are going to fix the 2 problems and now our shellcode is going to work correctly.

Here is the final code:

; HelloWorldShellcode.nasm
; Author:  Vivek Ramachandran
; Website:  https://www.pentesteracademy.com
; Student: Xavi Bel

global _start

section .text

_start:
jmp short call_shellcode

shellcode:

	pop ecx;
	
	; write syscall
	xor eax, eax
	mov al, 0x4

	xor ebx, ebx
	mov bl, 0x1	

	
	mov edx, 13
	int 0x80

	; exit syscall
	xor eax, eax
	mov al, 0x1

	xor ebx, ebx

	int 0x80


call_shellcode:
	call shellcode
	message: db "Hello World!", 0xA

Now we can extract the instructions using objdump:

objdump -d ./HelloWorldShellcode|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

And we can verify, that we have not any null bytes.

Now using this small C program we can verify ir our shellcode works:

#include<stdio.h>
#include<string.h>

unsigned char code[] = \
"\xeb\x18\x59\x31\xc0\xb0\x04\x31\xdb\xb3\x01\xba\x0d\x00\x00\x00\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe3\xff\xff\xff\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x0a";

main()
{

	printf("Shellcode Length:  %d\n", strlen(code));

	int (*ret)() = (int(*)())code;

	ret();

}

We compile it using:

gcc -fno-stack-protector -z execstack shellcode.c -o shellcode

And it works!

I hope you liked the post, probably I’m going to write a new one soon. See you!

This entry was posted in Exploiting and tagged , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published.