OP 19 April, 2020 - 01:15 PM
1.No null bytes in case of strcpy, which stops copying after it encounters a null byte
2.Size limit of on how much shellcode we can put
3.Only alphanumeric characters are copied
These conditions cause us to develop different methods to write our shellcode in a way that it will implement the same logic but still be able to bypass the limitations imposed by the program
Today we will attempt to solve case #1 by learning about techniques that we can use to tackle this issue, so shit right ? now gtfoh if u dont wanna listen XD
these techniques are often called Null Terminated Programming, as they allow us to compile assembly code that will not contain any null bytes in the final shellcode.
Please note that some of the techniques described here will probably be relevant for different assembly languages as well so i’ll leave it as an exercise for the reader to check these techniques on a different language.
Recommended Prerequisites:
which we can also use to compile our shellcode.
next, we can use this repository in order to debug and run our shellcode nicely
YOU SEE that i dont use any fucking program all making with some scripts from github or even handmade shits so for REVERSING U DONT ACTUALLY NEED TOOLS AND STUFF
JUST USE UR BRAIN AND PROGRAMMING SKILLS SUCK MA DICK NOW
Quick Refreshment on x64 syscallsIn x64 linux systems, Each syscall has a special number that represents it, when we want to perform a call to a certain syscall we first need to store the correct syscall number in RAX, then we pass the arguments 1-6 to the syscall using the registries RDI, RSI, RDX, RCX, R8, R9 accordingly.
Finally, we use the instruction syscall which performs the syscall itself and stores the return value in RAX.
The full x64 syscall map table can be found here
For windows syscal use here
Let’s have a look at this simple assembly code i wrote named printf_file.asm:
[/code]
This code performs a simple task, it reads 16 bytes from the file located in
/tmp/my_file and outputs those bytes to stdout.
Notice the cool trick we implemented in order to obtain the string that contains the path to the file.
to get that address, we perform a call to a label near that string called get_file_path,
afterwards we immediately perform a second call to the continue label that brings us back to the rest of our shellcode.
Because the second call was invoked, the address to return to after the second call now points to the string, because that is the first “instruction” after the call instruction. we then pop that address to RDI so that RDI (the first param in x64 syscall conventions) can point to the string of the file we wish to open
You can compile it the above assembly code by running
[/code]
and run it using
[/code]
Let’s check if the shellcode works properly
[/code]
901×42
So far so good.
But under the surface, hide a horrible secret…
It’s full of null bytes!!!
768×129
Hexdump just showed us that this shellcode is riddled with null bytes,
Let’s begin our work at curing this code by going over the correct ways to bypass situations where instructions generate null bytes
I will show the opcodes of the instruction on the left side
and the instruction itself on the right side
Note:I’ll be using this website in order to show the bytes generated from the instructions were about to show. I recommend to you all to test your instruction combinations there.
Method 1: Math is awesomeI’ll start of by saying that the mov instruction is many times obsolete when you have the power of math at your side
Loading 0 to a registerBad way:Lets look at the following instruction:
[/code]
it is 7 bytes long and more importantly, contains 4 null bytes!
we can easily use the following instructions instead
Good way:
[/code]
[/code]
in case the value of rbx is 0, we can execute this instruction.
(this can also be done with any other register with a 0 value)
[/code]
The mul instruction will multiply rax with the contents of rbx and store it in rax
because rbx is 0 in this case then 0 will also be stored in rax
**Loading large values to registries **What about putting large values in registries? For example, if i wanted to read a big file with my shellcode.
Bad way
[/code]
Good wayYou can use the shift operations in order to load large numbers
[/code]
This will result in rdx having the value 0x10000 at the end of the shift operation.
Method 2: Using your lower partsBefore you start thinking dirty, different parts of each register in x64 can be accessed as an operand.
These parts are mapped in the following way:
927×602
This allows us to use the al operand for example instead of the rax operand when we want to perform reading and writing actions on the lower 8 bits of the rax register.
When we do so, the instruction that is executed is much smaller and can also aid us when trying to avoid null bytes.
**Bad way:
[/code]
Good way
[/code]
Field TestAfter we learned these two new methods, let’s implement and modify the assembly code we saw at the beginning of the article
[/code]
After we compile this code, we can run it and see that it works exactly the same as the previous code:
970×49
let’s see if hexdumps finds any null bytes…
857×127
Awesome!
Note: Don’t be confused by the one null byte that hexdump found, that null byte belongs to the string in our shellcode and it’s placed at the end of the shellcode.
It doesn’t seem like it is in the end because memory is saved in little endian.
ConclusionsToday we learned about how we can compile our shellcode to be free of null bytes. We learned along the way about different ways we can perform the same resulting actions using different and sometimes shorter instructions(opcode wise) in x64.
the fucking procedure is goning to belike this no matter what sys u using u can allways bypass the null bytes like this it just require u to know assembly lang and some math dog shit nvm fuck u all bye
2.Size limit of on how much shellcode we can put
3.Only alphanumeric characters are copied
These conditions cause us to develop different methods to write our shellcode in a way that it will implement the same logic but still be able to bypass the limitations imposed by the program
Today we will attempt to solve case #1 by learning about techniques that we can use to tackle this issue, so shit right ? now gtfoh if u dont wanna listen XD
these techniques are often called Null Terminated Programming, as they allow us to compile assembly code that will not contain any null bytes in the final shellcode.
Please note that some of the techniques described here will probably be relevant for different assembly languages as well so i’ll leave it as an exercise for the reader to check these techniques on a different language.
Recommended Prerequisites:
- x64/x86 assembly knowledge
- Basic knowledge on building shellcodes
which we can also use to compile our shellcode.
next, we can use this repository in order to debug and run our shellcode nicely
YOU SEE that i dont use any fucking program all making with some scripts from github or even handmade shits so for REVERSING U DONT ACTUALLY NEED TOOLS AND STUFF
JUST USE UR BRAIN AND PROGRAMMING SKILLS SUCK MA DICK NOW
Quick Refreshment on x64 syscallsIn x64 linux systems, Each syscall has a special number that represents it, when we want to perform a call to a certain syscall we first need to store the correct syscall number in RAX, then we pass the arguments 1-6 to the syscall using the registries RDI, RSI, RDX, RCX, R8, R9 accordingly.
Finally, we use the instruction syscall which performs the syscall itself and stores the return value in RAX.
The full x64 syscall map table can be found here
For windows syscal use here
Let’s have a look at this simple assembly code i wrote named printf_file.asm:
Code:
[code]
SYS_READ equ 0
SYS_WRITE equ 1
SYS_OPEN equ 2
SYS_EXIT equ 60
AMOUNT_TO_READ equ 16
global _start
section .text
_start:
jmp get_file_path
continue:
; syscall to open the file
mov eax, SYS_OPEN
pop rdi ; pop address of string to rdi
mov rsi, 0 ; set O_RDONLY flag
syscall
; syscall to read file
sub sp, 0xff
lea rsi, [rsp]
; syscall to write file contents to stdout
mov rdi, rax ; use the returned fd
mov rdx, AMOUNT_TO_READ; amount to read
mov rax, SYS_READ
syscall
; syscall write to stdout
mov rdi,1 ; set stdout fd = 1
mov rdx, rax ; write to stdout the amount of bytes read
mov rax, SYS_WRITE
syscall
mov rax, SYS_EXIT
syscall ; finish execution
; jump here in order to get the address of the string
get_file_path:
call continue
file_path: db "/tmp/my_file", 0
This code performs a simple task, it reads 16 bytes from the file located in
/tmp/my_file and outputs those bytes to stdout.
Notice the cool trick we implemented in order to obtain the string that contains the path to the file.
to get that address, we perform a call to a label near that string called get_file_path,
afterwards we immediately perform a second call to the continue label that brings us back to the rest of our shellcode.
Because the second call was invoked, the address to return to after the second call now points to the string, because that is the first “instruction” after the call instruction. we then pop that address to RDI so that RDI (the first param in x64 syscall conventions) can point to the string of the file we wish to open
You can compile it the above assembly code by running
Code:
[code]
path_to_make_shellcode_ /make_shellcode_linux/make_shellcode.sh/ ./printf_file.asm 64
and run it using
Code:
[code]
path_to_shellrun/shellrun ./print_file.bin
Let’s check if the shellcode works properly
Code:
[code]
echo “this_is_my_data” > /tmp/my_file
901×42
So far so good.
But under the surface, hide a horrible secret…
It’s full of null bytes!!!
768×129
Hexdump just showed us that this shellcode is riddled with null bytes,
Let’s begin our work at curing this code by going over the correct ways to bypass situations where instructions generate null bytes
I will show the opcodes of the instruction on the left side
and the instruction itself on the right side
Note:I’ll be using this website in order to show the bytes generated from the instructions were about to show. I recommend to you all to test your instruction combinations there.
Method 1: Math is awesomeI’ll start of by saying that the mov instruction is many times obsolete when you have the power of math at your side
Loading 0 to a registerBad way:Lets look at the following instruction:
Code:
[code]
48 c7 c0 00 00 00 00 mov rax, 0
it is 7 bytes long and more importantly, contains 4 null bytes!
we can easily use the following instructions instead
Good way:
Code:
[code]
48 31 c0 xor rax, rax
Code:
[code]
48 c7 c0 ff ff ff ff mov rax,0xffffffffffffffff
48 ff c0 inc rax
in case the value of rbx is 0, we can execute this instruction.
(this can also be done with any other register with a 0 value)
Code:
[code]
48 f7 e3 mul rbx
The mul instruction will multiply rax with the contents of rbx and store it in rax
because rbx is 0 in this case then 0 will also be stored in rax
**Loading large values to registries **What about putting large values in registries? For example, if i wanted to read a big file with my shellcode.
Bad way
Code:
[code]
48 c7 c2 00 00 01 00 mov rdx,0x10000
Good wayYou can use the shift operations in order to load large numbers
Code:
[code]
48 31 d2 xor rdx,rdx
48 83 c2 02 add rdx,0x2
48 c1 e2 0f shl rdx,0xf
This will result in rdx having the value 0x10000 at the end of the shift operation.
Method 2: Using your lower partsBefore you start thinking dirty, different parts of each register in x64 can be accessed as an operand.
These parts are mapped in the following way:
927×602
This allows us to use the al operand for example instead of the rax operand when we want to perform reading and writing actions on the lower 8 bits of the rax register.
When we do so, the instruction that is executed is much smaller and can also aid us when trying to avoid null bytes.
**Bad way:
Code:
[code]
48 c7 c0 02 00 00 00 mov rax,0x2
48 c7 c3 ff 0f 00 00 mov rbx,0xfff
Good way
Code:
[code]
48 31 db xor rbx,rbx
48 31 c0 xor rax,rax
b0 02 mov al,0x2
66 bbff 0f mov bx,0xfff
Field TestAfter we learned these two new methods, let’s implement and modify the assembly code we saw at the beginning of the article
Code:
[code]
SYS_READ equ 0
SYS_WRITE equ 1
SYS_OPEN equ 2
SYS_EXIT equ 60
AMOUNT_TO_READ equ 16
global _start
section .text
_start:
jmp get_file_path
continue:
; syscall to open the file
xor rax, rax
add al, SYS_OPEN
pop rdi ; pop address of string to rdi
xor rsi, rsi ; set O_RDONLY flag
syscall
; syscall read file
sub sp, 0xfff
lea rsi, [rsp]
mov rdi, rax
xor rdx, rdx
add dl, AMOUNT_TO_READ; amount to read
xor rax, rax
syscall
; syscall write to stdout
xor rdi, rdi
add dl, 1 ; set fd to point to stdout
mov rdx, rax
xor rax, rax
add al, SYS_WRITE
syscall
mov al, SYS_EXIT
syscall ; finish execution
; jump here in order to get the address of the string
get_file_path:
call continue
flag: db "/tmp/my_file", 0
After we compile this code, we can run it and see that it works exactly the same as the previous code:
970×49
let’s see if hexdumps finds any null bytes…
857×127
Awesome!
Note: Don’t be confused by the one null byte that hexdump found, that null byte belongs to the string in our shellcode and it’s placed at the end of the shellcode.
It doesn’t seem like it is in the end because memory is saved in little endian.
ConclusionsToday we learned about how we can compile our shellcode to be free of null bytes. We learned along the way about different ways we can perform the same resulting actions using different and sometimes shorter instructions(opcode wise) in x64.
the fucking procedure is goning to belike this no matter what sys u using u can allways bypass the null bytes like this it just require u to know assembly lang and some math dog shit nvm fuck u all bye