White Security
Leg 문제풀이 본문
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)
Toddler's Bottle 8번 문제.
ARM Assembly 의 반대를 Leg 라고 표현하는
고급 유머가 들어있습니다.
ssh 에 접속하게 되면
busybox 환경으로 기본 명령어들을 제외하고는
대부분 사용이 불가능하므로
소스를 읽고 풀어내야 합니다.
#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)
leg.c 에 key을 알아내는 부분을
어셈블리로 구성해 무슨 값인지 알기 힘듭니다.
/ $ uname -a Linux (none) 3.11.4 #5 Sat Oct 12 00:15:00 EDT 2013 armv5tejl GNU/Linux
gcc, vc, clang 모두에서
돌려 보았는데 작동이 되질 않아서
무슨 일이지 했더니
자주보던 intel 어셈블리가 아닌
ARM 어셈블리였군요..
그래서 사용환경을 가상 busybox로 구성했나 봅니다.
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
arm 어셈블리에 대한 문서를 읽고보니
key 함수를 호출하고나서
r0 인자의 값을 사용하여 연산하고 있습니다.
intel x86 에서 eax 에 리턴값을 전달해 주는 것처럼
여기에서는 r0 레지스터가 그 역할을 하고 있나봅니다.
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
따라서 key 함수에서 r0에 값을 저장하는 값이
곧 리턴값이라는 것이지요.
따라서 key1 의 리턴값은
0x00008cdc 실행시의 pc 값이 됩니다.
pc 값을 구하는 데에 필요한 개념이 있습니다.
ARM 은 ARM7DMI 버전부터 파이프라인이라는 개념을 도입하여
실행 중인 다음의 명령어를 동시에 decode 하고
다다음의 명령어를 fetch 함으로서
실행 속도를 보다 효율적으로 하였는데요.
때문에 ARM 프로세서의 pc 값은 다다음 명령어의 주소값이 되겠네요.
This results in the typical ARM program being denser than expected with fewer memory accesses; thus the pipeline is used more efficiently. The ARM processor also has features rarely seen in other RISC architectures, such as PC-relative addressing (indeed, on the 32-bit[1] ARM the PC is one of its 16 registers) and pre- and post-increment addressing modes. The ARM instruction set has increased over time. Some early ARM processors (before ARM7TDMI), for example, have no instruction to store a two-byte quantity.
사실 여기에서 의문점이 하나 있습니다.
분명 3단계 파이프라인 개념이 도입된
arm7tdml 버전 이전에는 2 바이트 양을
저장하는 지침이 없었다고 하는데요.
그 이전 환경인 armv5teji 을 사용하는
이번 pwnable.kr 환경에서는
pc 값이 두 단계 앞에 있습니다.
혹시 이유를 아시는 분은 댓글로 달아주시면
감사하겠습니다.
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
다시 key1 함수로 돌아와서 보면
0x00008cdc 에서의 pc 값이 곧 리턴값이니
두 바이트 앞의 값인
0x00008ce4 가 key1()이 되겠군요.
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
다음은 key2 함수입니다.
윗 부분에 r6 를 다루는 부분이 있어
다소 헷갈렸는데
여기서 r0 는 0x00008d04 때의 pc 값 + 4 이며
r6 값과는 별 연관이 없습니다.
따라서 key2() 는 0x00008d08 + 4 인
0x00008d0c 가 되겠군요.
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
마지막 key3 함수입니다.
lr 값이 r0 가 되는데요.
ARM 어셈블리에서 lr 레지스터는
함수의 리턴 주소를 가리키므로
key3 함수 call 주소의 바로 뒷 주소가 되겠군요.
따라서 key3() 는 0x00008d80 가 되겠습니다.
>>> 0x00008d0c + 0x00008ce4 + 0x00008d80 108400
/ $ ./leg Daddy has very strong arm! : 108400 Congratz! My daddy has a lot of ARMv5te muscle!
모든 key 값을 합하여
제출하니 성공적으로 플래그를 얻을 수 있었습니다.
'Wargame Writeups > pwnable.kr' 카테고리의 다른 글
Shellshock 문제풀이 (0) | 2019.02.08 |
---|---|
Mistake 문제풀이 (0) | 2019.02.08 |
Input 문제풀이 (0) | 2019.02.07 |
Random 문제풀이 (0) | 2019.02.06 |
Passcode 문제풀이 (0) | 2019.02.06 |