logo

leg

The eighth challenge from pwnable.kr

Description

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)

Solution

Let’s try to ssh into the server and see what we can find.

ctftools ❯ ssh leg@pwnable.kr -p2222

We’re immediately greeted with a bunch of error logs dumped to the screen, but let’s look around.

/ $ ls -la
total 628
drwxr-xr-x   11 root     0                0 Nov 10  2014 .
drwxr-xr-x   11 root     0                0 Nov 10  2014 ..
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

We’ll need leg to read flag for us.

Let’s examine the C and ASM source code of the two provided files.

#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;
}
(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)

Let’s try to compile the C code.

ctftools ❯ gcc -Wall leg.c
leg.c: In function ‘main’:
leg.c:31:3: warning: implicit declaration of function ‘read’ [-Wimplicit-function-declaration]
   int r = read(fd, buf, 100);
   ^
leg.c:32:3: warning: implicit declaration of function ‘write’ [-Wimplicit-function-declaration]
   write(0, buf, r);
   ^
leg.c: In function ‘key1’:
leg.c:5:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
leg.c: In function ‘key2’:
leg.c:19:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
leg.c: In function ‘key3’:
leg.c:22:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
leg.c: Assembler messages:
leg.c:4: Error: too many memory references for `mov'
leg.c:7: Error: invalid char '{' beginning operand 1 `{r6}'
leg.c:8: Error: too many memory references for `add'
leg.c:9: Error: no such instruction: `bx r6'
leg.c:10: Error: unknown pseudo-op: `.code'
leg.c:11: Error: too many memory references for `mov'
leg.c:12: Error: operand size mismatch for `add'
leg.c:13: Error: invalid char '{' beginning operand 1 `{r3}'
leg.c:14: Error: invalid char '{' beginning operand 1 `{pc}'
leg.c:15: Error: unknown pseudo-op: `.code'
leg.c:16: Error: invalid char '{' beginning operand 1 `{r6}'
leg.c:21: Error: too many memory references for `mov'

This is ARM code, so we’re unable to compile on an x86 machine.

According to the C program, we can print flag if we make it inside the following condition:

if( (key1()+key2()+key3()) == key )

Let’s start reversing each key function.

Key 1

int key1(){
	asm("mov r3, pc\n");
}

pc in ARM is the value of the current instruction plus 8 bytes, so key1() should return the address of the second next instruction at that moment.

0x00008cdc <+8>:	mov	r3, pc
0x00008ce0 <+12>:	mov	r0, r3
0x00008ce4 <+16>:	sub	sp, r11, #0

key1() is equal to 0x00008ce4.

Key 2

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"
	);
}

The first section of this function puts the CPU into Thumb State with the bx r6 instruction, with the single bit set from the previous command indicating this state.

The CPU is now in 16-bit mode, which means values like pc are +4, not +8.

Next the program adds 0x4 to pc, then returns that value. So let’s take a look at the assembly to get the right addresses.

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 ; r6 = 0x00008d04 + 1 =
0x00008d00 <+16>:	bx	r6
0x00008d04 <+20>:	mov	r3, pc
0x00008d06 <+22>:	adds	r3, #4 ; r3 = 0x00008d08 + 4 = 0x00008d0c
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

At instruction 0x00008d04, the pc is 0x00008d08, and adding 4 gives us 0x00008d0c.

After that, the program pushes this value stored in r3 onto the stack, then returns from Thumb State after popping r6, and moves r3 into r0, the return register.

key2() is equal to 0x00008d0c

Key 3

int key3(){
	asm("mov r3, lr\n");
}

lr in ARM is Link Register, which stores the return address of the function.

0x00008d7c <+64>:	bl	0x8d20 <key3>
0x00008d80 <+68>:	mov	r3, r0

key3() is equal to 0x00008d80.

key1()+key2()+key3() is 0x00008ce4 + 0x00008d0c + 0x00008d80 = 0x1a770 which is 108400 in decimal.

Capturing the Flag

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