ipwn
[pwnable.kr] leg 본문
이번에 올릴 라업은 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; } |
이게 C코드인데 key함수 3개 return값 다 더한 값이랑 입력값이랑 똑같으면 flag를 읽어와주는데 인라인으로 asm 코딩 돼 있는 부분을 보면 arm아키텍쳐인 걸 알 수 있다.
arm아키텍쳐에서 알아둬야할 레지스터들과 그 레지스터들의 역할을 알아보겠다.
PC(Program Counter) or r15
현재 Instruction을 Fetch해 온 위치를 가리킨다. 현재 실행하는 위치가 아니라 Fetch해온 위치를 가리킨다.
즉 PC레지스터는 현재 진행되는 명령어의 다음 다음 명령어의 주소값을 가진다.
lr(link register) or r14
현재의 함수가 끝나고 돌아갈 곳의 주소값을 가리킨다.
sp(stack pointer) or r13
sp는 intel 아키텍쳐의 esp와 비슷한 역할을 하는데, stack pointer을 의미한다.
r0 ~ r12
그 외 레지스터는 intel 아키텍쳐와 같이 연산 작업에서 각자의 역할을 맡아서 하는데, 그 중에서도 r0레지스터는 함수의 return값을 가진다.
대충 이 정도만 기억하고 있는다면 이 문제는 간단히 풀 수 있다.
우리는 r0이 함수의 리턴값인 것을 알고 key1, key2, key3의 함수값을 모두 더한 값과 내 입력값이 같으면 플래그를 읽어와주는 걸 알기 때문에 key1, key2, key3이 각 함수들의 r0 레지스터의 값만 추적하면 된다.
이제 함수 하나하나마다의 어셈을 보자.
(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 |
이 곳이 메인함수인데 빨간색으로 칠해져있는 부분들이 각각의 key1, key2, key3의 lr의 값을 가지게 된다는 것을 알게될 것이다.
이제 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 |
이제 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. |
이 어셈블리어가 key2의 함수인데 이것도 마찬가지로 r0에 r3의 값을 넣어주는데, r3의 시작점을 보면 r3에 pc레지스터의 값이 들어가게 되고, 그 뒤에 다시 4를 더해준다.
즉 0x8d08의 값 + 4이므로 0x8d0c의 값이 key2 함수의 return값이 될 것이다.
이제 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. |
이 함수가 key3의 함수인데, 마찬가지로 r0의 레지스터에 r3레지스터의 값이 들어가고, r3 레지스터는 lr의 값이 들어가는 걸 확인할 수 있다.
위의 메인함수의 어셈블리를 다시 한 번 보자. 0x8d80이 key3의 함수가 끝나고 돌아가는 위치의 주소값임을 알 수 있다. 즉 key3 함수의 return값은 0x8d80의 값이 들어가게 된다.
즉 각 함수들의 리턴값을 모두 더한 값을 입력으로 넘겨주게 된다면 flag를 성공적으로 읽어올 수 있게 될 것이다.
ㅇㅋ 끝
'Write up > Pwnable.kr' 카테고리의 다른 글
[pwnable.kr] memcpy (0) | 2018.07.21 |
---|---|
[pwnable.kr] input (0) | 2018.03.13 |
[pwnable.kr] coin1 (0) | 2018.03.12 |
[pwnable.kr] uaf (0) | 2018.02.19 |
[pwnable.kr] shellshock (0) | 2018.01.06 |