Skip to main content

ARM buffer overflow: chapter 7 ಠ-ಠ (day 77) (writing ARM shellcode)



In the last post of learning shellcode, we are taking a look at how to create our own simple shellcode in 32-bit intel architecture but this time let's switch to ARM architecture so we have a better understanding of how the program works in more low level.

note: this post is heavily based on Azeria labs you guys can check the corresponding post in here. All I do is just reiterating the concept and probably explain a little bit more detail about it

Let's start from the beginning "Hello world"

in this section, we are going to just create a really simple hello world program from scratch using arm assembly. Type the following code below and save it as "hello_world.s"



let's break it down in detail what is this code really doing:

1. ".data" section used to initialized data or constant in memory and remember this section cannot be changed during runtime by default.

In the source code, we declare two variable which is "string" that will hold value "hello world\n" notice before setting the value of the variable there is what is called "string definition directives" this used to allocate memory for the string, we use .asciz to automatically put null byte at the end of the string after declaration

next is variable "after_string" that holds the value of the length of the "string" variable

2 ".text" section used to contain all of the source code, unlike normal higher programming assembly used "_start" as the main function.

3. As you know in x86 architecture function call all the parameters are stored in the stack, like the following figure:


link:http://hack-r.com/cdecl-and-stdcall-calling-conventions-stack-clearing-and-the-eax-register/

but in ARM architecture they used the register to stored the value of the parameter for the function called. ARM used register R0-R6 to stored the parameter and R7 for the syscall value

in _start: we can see that in order to print "hello world" to the screen we use syscall from write() that has three parameters.

ssize_t write(int fd, const void *buf, size_t count);

1st is for storing file descriptor which is one for stdout
2nd is the actual string
3rd will be the size of the string

then we put the syscall value (which is 4) for write() to R7

finally, we execute by executing swi instruction

_exit used just for making a clean exit and addr_of_string used for referencing the memory of string "hello world" stored in .data section

Now, all we have to do is just compile and link the source code, like this:




Dissect the program:

COOL! we just making our first shellcode, let's try to load in the gdb and see how the process is really execute



take a look at _start function we can see that the location of string variable is converted to pc, #16 you don't have to worry about this changing this is just how the compiler work and then take a look at the last instruction, as we recall we used swi instruction and once we compiled it change to "svc" again you don't have to worry.

this is basically because swi instruction is deprecated and the compiler smart enough to change it using svc

try to put a breakpoint at the beginning of the _start function, once you run it, take a look at the register notice that there is no value assigned to it at first



but as you step it through until svc instruction it fills with our parameter and at the end, it will spit out the "hello world" string again





Now let's create shellcode that can invoke a shell:

we are ready to create our first shellcode, type this source code into your favorite text editor:



I think this is pretty straightforward, to invoke shell we need to call execve that have this parameter.

int execve(const char *filename, char *const argv[],char *const envp[]);

we only care about the first parameter and the rest of it could be set to 0 and for execve it has syscall 11

remember if we use a direct number such as (0-9) we need to put # before the number.

then you may be wondering what's with the pc, #12 assign to R0. you don't have to worry about it this is just another way to refer to the memory location of "/bin/sh\0"



compile it and we can see that we able to call a shell :)



unfortunately, if we dump the binary we are going to have a lot of null bytes that make our shellcode useless.

so how to get rid of null bytes then?

you can use thumb mode in the arm. In arm assembly there are two modes first is the arm mode which takes 32 bits or space and thumb mode that takes 16 bits.


.code 32 and .code 16 is just a directive to tell the compiler to generate a selected set of instruction, in here we define two-part first is the arm code (.code 32) and the thumb code (.code 16)

.code32 section we want to force the program to switch to the thumb mode adding 1 to the pc register and then take a jump using b/blx/bx command

once we enter thumb mode it will start executing the instruction at .code16 then in arm thumb mode we must align the code to be 4 bytes so we add nop with mov r5,r5 (this is basically don't do anything)

to zero out r1 and r2 we can use eor which is stands for Exclusive OR and then rest of it is self explanatory

 

but we are not done yet since as you can see at the last .word instruction we still have null bytes append to the string /bin/sh right ?

how do we overcome this ?

you can modify the value of the string at runtime to be append with a null byte, in the following way



we will be using instruction strb stands for store byte, as you know our R2 register contains a null byte so we can overwrite the "x" string which is located at the 7th character in the string stored in the register R0.

so at runtime, we change "/bin/shx" to "/bin/sh\0" after executing strb instruction

if you compile it, it will encounter segmentation fault as I mentioned earlier that .data cannot be edit at runtime by default so you must append extra parameter "-N" at ld utility to prevent this error





check the opcode, wallaaa we are free from null bytes. Now all we have to do is dump the binary and convert it into hexadecimal so we can use it in our exploit

Use this following python script:





Cool so in this post we just building a simple shellcode that could give us a shell and that's all folks I hope you enjoy this material 

see you at the next post of arm buffer overflow

Comments

Popular posts from this blog

Having fun analyzing nginx log to find malicious attacker in the net (ง'̀-'́)ง (day 37)

  What makes you sleepless at night? is it because of a ghost or scary stories? is it because you have an important meeting tomorrow? or is it because you have an exam? For me, what keeps me up all night is that I keep thinking about what happens to a website that I just created, is it safe from an attacker (certainly not) or did I missing some security adjustments that lead to vulnerability? well I'm not the best secure programmer in the world, I'm still learning and there is a big possibility that I can make a mistake but for me, a mistake can be a valuable investment to myself or yourself to be better so from this idea, I want to know more about what attackers casually do when attacking a website. Here in this post, I'm going to show you how I analyzed attack to the website that I have permission to design and also some interesting findings that I could get from the analysis Background: All of this analysis comes from the traffic that is targeted to th

Utilize Pwntools for crafting ROP chain :') (day 69)

who doesn't like pwntools? it is a very versatile tool and can be customized according to our need using the python script but did you need to know that pwntools itself can help us to automatically craft a rop chain for us? so in this post, I will show you how to make rop chain less painful and make pwntools do all the heavy lifting. To demonstrate this I will use the binary challenge callme 64 bit from ropemporium link: https://ropemporium.com/challenge/callme.html Crashing the app: Like any other exploitation process, we need to crash the program by generating a long string pattern to determine the offset. based on the information from the above figure we can see that we required to provide 40 bytes of offset Fun stuff: now this where the fun stuff began write the following python script: as in the guideline of the challenged said we need to chain the function call by first to call the callme_one function, callme_two function and then callme_three funct

WriteUp PWN tarzan ROP UNICTF ಠ_ಠ (day 61)

So in this post, I'm going to talk about how to solve the Tarzan pwn challenge from UNICTF 2019. Back in the day when the competition is still going I couldn't finish it and don't have any clue to solve this but this time I was able to finish it :) Also in this post, we will be going to be heavily focused on how to utilize pwntools to construct a ROP chain. If you kinda confused about my explanation in this post you can refer to this following youtube video, link: https://www.youtube.com/watch?v=gWU2yOu0COk I build the python script based on this video Ok, let's get started! In this challenge, you will get two binary first go with tarzan and libc-2.29.so by providing .so file it tell us what version library that the target machine is using this could help us to do ROP chain. first, we run the Tarzan binary to get the basic idea of the program work and as you can see it just show you some text, newline and when you try to input something it doesn't gi