Pwnable.kr - leg

Writeup for leg of pwnable.kr

key points:

  • Return value of a function in assembling level is stored in $eax when it returns.

  • $eax, $ebx, $ecx, $edx are defined as r1, r2, r3, r4 in x86_64 linux c code.

0x00 Puzzle

Daddy told me I should study arm.  
But I prefer to study my leg!

Download : http://pwnable.kr/bin/leg.c  
Download : http://pwnable.kr/bin/leg.asm

ssh leg@pwnable.kr -p2222 (pw:guest)  

0x01 Explore

ssh

ssh leg@pwnable.kr 2222  
/ $ ls -l
total 628  
drwxrwxr-x    2 root     0                0 Nov 10  2014 bin  
drwxrwxr-x    2 root     0                0 Nov 10  2014 boot  
drwxrwxr-x    2 root     0                0 Nov 10  2014 dev  
drwxrwxr-x    3 root     0                0 Nov 10  2014 etc  
-r--------    1 1001     0               38 Nov 10  2014 flag
---s--x---    1 1001     1000        636419 Nov 10  2014 leg
lrwxrwxrwx    1 root     0               11 Nov 10  2014 linuxrc -> bin/busybox  
dr-xr-xr-x   33 root     0                0 Jan  1 00:00 proc  
drwxrwxr-x    2 root     0                0 Nov 10  2014 root  
drwxrwxr-x    2 root     0                0 Nov 10  2014 sbin  
drwxrwxr-x    2 root     0                0 Nov 10  2014 sys  
drwxrwxr-x    4 root     0                0 Nov 10  2014 usr  

This time we got a virtual env which only have a flag text file and a binary file leg to play with.

Let's run the leg and feed some random data (1234)

/ $ ./leg
Daddy has very strong arm! : 123  
I have strong leg :P  

Seems the only input makes sense.

The Puzzle provides both c source and asm file of the binary.

leg.c

#include <stdio.h>
#include <fcntl.h>
int key1(){  
    asm("mov r3, pc\n");
}
int key2(){  
    asm(
    "push   {r6}\n"
    "add    r6, pc, $1\n"
    "bx r6\n"
    ".code   16\n"
    "mov    r3, pc\n"
    "add    r3, $0x4\n"
    "push   {r3}\n"
    "pop    {pc}\n"
    ".code  32\n"
    "pop    {r6}\n"
    );
}
int key3(){  
    asm("mov r3, lr\n");
}
int main(){  
    int key=0;
    printf("Daddy has very strong arm! : ");
    scanf("%d", &key);
    if( (key1()+key2()+key3()) == key ){
        printf("Congratz!\n");
        int fd = open("flag", O_RDONLY);
        char buf[100];
        int r = read(fd, buf, 100);
        write(0, buf, r);
    }
    else{
        printf("I have strong leg :P\n");
    }
    return 0;
}

leg.asm

(gdb) disass main
Dump of assembler code for function main:  
   0x00008d3c <+0>:    push    {r4, r11, lr}
   0x00008d40 <+4>:    add r11, sp, #8
   0x00008d44 <+8>:    sub sp, sp, #12
   0x00008d48 <+12>:    mov r3, #0
   0x00008d4c <+16>:    str r3, [r11, #-16]
   0x00008d50 <+20>:    ldr r0, [pc, #104]  ; 0x8dc0 <main+132>
   0x00008d54 <+24>:    bl  0xfb6c <printf>
   0x00008d58 <+28>:    sub r3, r11, #16
   0x00008d5c <+32>:    ldr r0, [pc, #96]   ; 0x8dc4 <main+136>
   0x00008d60 <+36>:    mov r1, r3
   0x00008d64 <+40>:    bl  0xfbd8 <__isoc99_scanf>
   0x00008d68 <+44>:    bl  0x8cd4 <key1>
   0x00008d6c <+48>:    mov r4, r0
   0x00008d70 <+52>:    bl  0x8cf0 <key2>
   0x00008d74 <+56>:    mov r3, r0
   0x00008d78 <+60>:    add r4, r4, r3
   0x00008d7c <+64>:    bl  0x8d20 <key3>
   0x00008d80 <+68>:    mov r3, r0
   0x00008d84 <+72>:    add r2, r4, r3
   0x00008d88 <+76>:    ldr r3, [r11, #-16]
   0x00008d8c <+80>:    cmp r2, r3
   0x00008d90 <+84>:    bne 0x8da8 <main+108>
   0x00008d94 <+88>:    ldr r0, [pc, #44]   ; 0x8dc8 <main+140>
   0x00008d98 <+92>:    bl  0x1050c <puts>
   0x00008d9c <+96>:    ldr r0, [pc, #40]   ; 0x8dcc <main+144>
   0x00008da0 <+100>:    bl  0xf89c <system>
   0x00008da4 <+104>:    b   0x8db0 <main+116>
   0x00008da8 <+108>:    ldr r0, [pc, #32]   ; 0x8dd0 <main+148>
   0x00008dac <+112>:    bl  0x1050c <puts>
   0x00008db0 <+116>:    mov r3, #0
   0x00008db4 <+120>:    mov r0, r3
   0x00008db8 <+124>:    sub sp, r11, #8
   0x00008dbc <+128>:    pop {r4, r11, pc}
   0x00008dc0 <+132>:    andeq   r10, r6, r12, lsl #9
   0x00008dc4 <+136>:    andeq   r10, r6, r12, lsr #9
   0x00008dc8 <+140>:            ; <UNDEFINED> instruction: 0x0006a4b0
   0x00008dcc <+144>:            ; <UNDEFINED> instruction: 0x0006a4bc
   0x00008dd0 <+148>:    andeq   r10, r6, r4, asr #9
End of assembler dump.  
(gdb) disass key1
Dump of assembler code for function key1:  
   0x00008cd4 <+0>:    push    {r11}       ; (str r11, [sp, #-4]!)
   0x00008cd8 <+4>:    add r11, sp, #0
   0x00008cdc <+8>:    mov r3, pc
   0x00008ce0 <+12>:    mov r0, r3
   0x00008ce4 <+16>:    sub sp, r11, #0
   0x00008ce8 <+20>:    pop {r11}       ; (ldr r11, [sp], #4)
   0x00008cec <+24>:    bx  lr
End of assembler dump.  
(gdb) disass key2
Dump of assembler code for function key2:  
   0x00008cf0 <+0>:    push    {r11}       ; (str r11, [sp, #-4]!)
   0x00008cf4 <+4>:    add r11, sp, #0
   0x00008cf8 <+8>:    push    {r6}        ; (str r6, [sp, #-4]!)
   0x00008cfc <+12>:    add r6, pc, #1
   0x00008d00 <+16>:    bx  r6
   0x00008d04 <+20>:    mov r3, pc
   0x00008d06 <+22>:    adds    r3, #4
   0x00008d08 <+24>:    push    {r3}
   0x00008d0a <+26>:    pop {pc}
   0x00008d0c <+28>:    pop {r6}        ; (ldr r6, [sp], #4)
   0x00008d10 <+32>:    mov r0, r3
   0x00008d14 <+36>:    sub sp, r11, #0
   0x00008d18 <+40>:    pop {r11}       ; (ldr r11, [sp], #4)
   0x00008d1c <+44>:    bx  lr
End of assembler dump.  
(gdb) disass key3
Dump of assembler code for function key3:  
   0x00008d20 <+0>:    push    {r11}       ; (str r11, [sp, #-4]!)
   0x00008d24 <+4>:    add r11, sp, #0
   0x00008d28 <+8>:    mov r3, lr
   0x00008d2c <+12>:    mov r0, r3
   0x00008d30 <+16>:    sub sp, r11, #0
   0x00008d34 <+20>:    pop {r11}       ; (ldr r11, [sp], #4)
   0x00008d38 <+24>:    bx  lr
End of assembler dump.  
(gdb) 

We need to calculate the value of key1() + key2() + key3() and input it then we can get the flag.

key points:

  • Return value of a function in assembling level is stored in $eax when it returns.

  • $eax, $ebx, $ecx, $edx are defined as r1, r2, r3, r4 in x86_64 linux c code.

0x0100 return value of key1

let's look at the assembling code of key1:

(gdb) disass key1
Dump of assembler code for function key1:  
   0x00008cd4 <+0>:    push    {r11}       ; (str r11, [sp, #-4]!)
   0x00008cd8 <+4>:    add r11, sp, #0
   0x00008cdc <+8>:    mov r3, pc
   0x00008ce0 <+12>:    mov r0, r3
   0x00008ce4 <+16>:    sub sp, r11, #0
   0x00008ce8 <+20>:    pop {r11}       ; (ldr r11, [sp], #4)
   0x00008cec <+24>:    bx  lr
End of assembler dump.  

The return value is from 0x00008cdc <+8>: mov r3, pc this line.
pc means program counter, alias of ip (instruction pointer).

There are two modes: ASM mode and Thumb mode, in ASM mode, pc always points to the next instruction's address while in Thumb mode pc always points to the next NO.2 instruction's adress. (ref)

Currently we don't which mode the system is, but after try both of them it turns out that it's Thumb mode.

So for Thumb mode, the return value of key1() is 0x00008ce4, which is the address of the one two instructions ahead of 0x00008cdc <+8>: mov r3, pc.

0x0101 return value of key2

(gdb) disass key2
Dump of assembler code for function key2:  
   0x00008cf0 <+0>:    push    {r11}       ; (str r11, [sp, #-4]!)
   0x00008cf4 <+4>:    add r11, sp, #0
   0x00008cf8 <+8>:    push    {r6}        ; (str r6, [sp, #-4]!)
   0x00008cfc <+12>:    add r6, pc, #1
   0x00008d00 <+16>:    bx  r6
   0x00008d04 <+20>:    mov r3, pc
   0x00008d06 <+22>:    adds    r3, #4
   0x00008d08 <+24>:    push    {r3}
   0x00008d0a <+26>:    pop {pc}
   0x00008d0c <+28>:    pop {r6}        ; (ldr r6, [sp], #4)
   0x00008d10 <+32>:    mov r0, r3
   0x00008d14 <+36>:    sub sp, r11, #0
   0x00008d18 <+40>:    pop {r11}       ; (ldr r11, [sp], #4)
   0x00008d1c <+44>:    bx  lr
End of assembler dump.  

Similarly, the return value of key2() is 0x00008d08 + 4.

0x0102 return value of key3

(gdb) disass key3
Dump of assembler code for function key3:  
   0x00008d20 <+0>:    push    {r11}       ; (str r11, [sp, #-4]!)
   0x00008d24 <+4>:    add r11, sp, #0
   0x00008d28 <+8>:    mov r3, lr
   0x00008d2c <+12>:    mov r0, r3
   0x00008d30 <+16>:    sub sp, r11, #0
   0x00008d34 <+20>:    pop {r11}       ; (ldr r11, [sp], #4)
   0x00008d38 <+24>:    bx  lr
End of assembler dump.  
(gdb) 

For this one, the return value comes from $lr, which is link register (wikipedia).

$lr points to the return address, which is address of the instruction that the program is going to process right after current function returned.

So the return value of key3() is 0x00008d80.

0x02 Solution

key1() + key2() + key3() = 0x00008ce4 + 0x00008d08 + 4 + 0x00008d80 = 108400

/ $ ./leg
Daddy has very strong arm! : 108400  
Congratz!  
My daddy has a lot of ARMv5te muscle!  

Done.