[pwnable.kr] Toddler’s Bottle – leg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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; } |
code를 보면 key를 입력 받고, key1, key2, key3 세 함수의 반환 값과 같다면 flag를 출력하는 것 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
(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. |
r11은 ebp의 역할인 것 같습니다.
r0은 함수가 끝날 때마다 넣어주는 걸 보니 함수의 반환 값을 저장하는 것 같습니다.
key1 함수에서는,
pc를 r3에 넣고, r3을 r0에 넣습니다. pc 값을 알면 될 것 같습니다.
key2 함수에서는,
pc를 r3에 넣고, r3에 4를 더합니다. 그리고 r3을 r0에 넣습니다. pc + 4의 값이 되겠네요.
key3 함수에서는,
lr을 r3에 넣고, r3을 r0에 넣습니다. lr 값을 알면 될 것 같습니다.
그렇다면 pc와 lr을 알아야합니다.
우선 cpu에는 ‘명령 주기’가 있습니다. fetch -> decode -> execute 이게 반복적으로 이뤄집니다. 예전에는 하나의 명령이 저 주기를 다 거친 후에 다음 명령을 실행했지만, 지금은 pre-fetch라고 해서, fetch 후에 다음 명령 fetch가 바로 실행됩니다.
참고로 fetch는 다음과 같이 이뤄집니다.
MAR <- [PC]
PC <- [PC] + 1 ; MDR <- [Memory] address
CIR <- [MDR]
그리고 decode되고 execute가 일어나는데, 그동안 fetch도 같이 진행되어 pc <- [pc] +1가 두 번 더 실행됩니다. (두 번 더 실행되지만, pc는 execute할 때 시작된 fetch를 가리키고 있어서 다다음 명령을 가리킴.)
그래서 명령이 execute될 때에, pc 레지스터는 다다음 명령을 가리키게 됩니다.
이렇게 말입니다.
https://youtu.be/jFDMZpkUWCw
lr 레지스터는 return address라고 생각하면 될 것 같습니다.
이제 문제를 풀도록 하겠습니다.
1 2 3 |
0x00008cdc <+8>: mov r3, pc 0x00008ce0 <+12>: mov r0, r3 0x00008ce4 <+16>: sub sp, r11, #0 |
key1 함수에서 0x8cdc에서 mov 명령이 실행되므로 pc 값은 3번째 라인 0x8ce4 = 36068.
1 2 3 |
0x00008d04 <+20>: mov r3, pc 0x00008d06 <+22>: adds r3, #4 0x00008d08 <+24>: push {r3} |
key2 함수에서 0x8d04에서 mov 명령을 실행했으니 pc 값은 3번째 라인 0x8d08 = 36104.
여기에 4을 더하니 36108가 됩니다.
1 2 |
0x00008d7c <+64>: bl 0x8d20 <key3> 0x00008d80 <+68>: mov r3, r0 |
main 함수에서 key3을 호출하는 부분입니다.
return address는 2번째 라인 0x8d80 = 36224가 됩니다.
다 더하면 36068 + 36108 + 36224 = 108400입니다.
감사합니다.
'System&Write up > Pwnable.kr' 카테고리의 다른 글
[pwnable.kr] Toddler's Bottle - uaf (0) | 2018.01.31 |
---|---|
[pwnable.kr] Toddler's Bottle - input (0) | 2018.01.20 |
[pwnable.kr] Toddler's Bottle - coin1 (2) | 2017.12.31 |
[pwnable.kr] Toddler's Bottle - cmd2 (0) | 2017.12.30 |
[pwnable.kr] Toddler's Bottle - shellshock (0) | 2017.12.17 |