Pwnable.kr Write-Up | By RevDev
Toddler’s Bottle - random [1pt]
1. Ready to Solve
Problem
Daddy, teach me how to use random value in programming!
ssh random@pwnable.kr -p2222 (pw:guest)
GNU bash (version 4.3.11)
File List
random@ubuntu:~$ ls -al
total 36
drwxr-x--- 4 root random 4096 Aug 20 2014 .
dr-xr-xr-x 44 root root 4096 Jul 5 07:52 ..
d--------- 2 root root 4096 Jun 30 2014 .bash_history
-r--r----- 1 random2 root 49 Jun 30 2014 flag
dr-xr-xr-x 2 root root 4096 Aug 20 2014 .irssi
-r-sr-x--- 1 random2 random 8538 Jun 30 2014 random
-rw-r--r-- 1 root root 301 Jun 30 2014 random.c
Source Code
#include <stdio.h>
int main(){
unsigned int random;
random = rand();
unsigned int key=0;
scanf("%d", &key);
if( (key ^ random) == 0xdeadbeef ){
printf("Good!\n");
system("/bin/cat flag");
return 0;
}
printf("Wrong, maybe you should try 2^32 cases.\n");
return 0;
}
Disassembled Code
Dump of assembler code for function main:
0x00000000004005f4 <+0>: push rbp
0x00000000004005f5 <+1>: mov rbp,rsp
0x00000000004005f8 <+4>: sub rsp,0x10
0x00000000004005fc <+8>: mov eax,0x0
0x0000000000400601 <+13>: call 0x400500 <rand@plt>
0x0000000000400606 <+18>: mov DWORD PTR [rbp-0x4],eax
0x0000000000400609 <+21>: mov DWORD PTR [rbp-0x8],0x0
0x0000000000400610 <+28>: mov eax,0x400760
0x0000000000400615 <+33>: lea rdx,[rbp-0x8]
0x0000000000400619 <+37>: mov rsi,rdx
0x000000000040061c <+40>: mov rdi,rax
0x000000000040061f <+43>: mov eax,0x0
0x0000000000400624 <+48>: call 0x4004f0 <__isoc99_scanf@plt>
0x0000000000400629 <+53>: mov eax,DWORD PTR [rbp-0x8]
0x000000000040062c <+56>: xor eax,DWORD PTR [rbp-0x4]
0x000000000040062f <+59>: cmp eax,0xdeadbeef
0x0000000000400634 <+64>: jne 0x400656 <main+98>
0x0000000000400636 <+66>: mov edi,0x400763
0x000000000040063b <+71>: call 0x4004c0 <puts@plt>
0x0000000000400640 <+76>: mov edi,0x400769
0x0000000000400645 <+81>: mov eax,0x0
0x000000000040064a <+86>: call 0x4004d0 <system@plt>
0x000000000040064f <+91>: mov eax,0x0
0x0000000000400654 <+96>: jmp 0x400665 <main+113>
0x0000000000400656 <+98>: mov edi,0x400778
0x000000000040065b <+103>: call 0x4004c0 <puts@plt>
0x0000000000400660 <+108>: mov eax,0x0
0x0000000000400665 <+113>: leave
0x0000000000400666 <+114>: ret
End of assembler dump.
3. Solve
이 문제는 특정 변수의 값을 입력을 통해 0xdeadbeef
로 설정하는 문제이다.
그러나 여기서는 입력 값에 rand()함수로 만들어진 난수와 XOR연산을 실행하여 해커가 원하는 대로 변수를 조작할 수 없게 만들었다. 그러나 분명히 어디엔가 취약점은 존재한다. 그것을 찾아보자.
가장 먼저 나는 소스 코드에서 random = rand();
에 주목하였다.
대게 랜덤 함수는 srand()
, 즉, 랜덤 시드 설정 함수와 같이 쓰이는데 위의 코드에서는 랜덤 함수만 쓰이고 있다.
혹시 저 rand() 함수 내부에 다른 점이 있을까 하여 나는 rand()를 따라가 보았다.
Dump of assembler code for function rand@plt:
0x0000000000400500 <+0>: jmp QWORD PTR [rip+0x200b1a]
# 0x601020 <rand@got.plt>
0x0000000000400506 <+6>: push 0x4
0x000000000040050b <+11>: jmp 0x4004b0
End of assembler dump.
하지만 일반적인 libc함수였을 뿐, 따로 시드를 생성하는 부분은 보이지 않았다.
따라서, 본인은 시드 변경을 하지 않는 랜덤 함수는 같은 값만을 생성해낸다는 것에 주목하였다.
다음 부분에 집중해보자.
0x0000000000400601 <+13>: call 0x400500 <rand@plt>
0x0000000000400606 <+18>: mov DWORD PTR [rbp-0x4],eax
0x0000000000400609 <+21>: mov DWORD PTR [rbp-0x8],0x0
0x0000000000400610 <+28>: mov eax,0x400760
0x0000000000400615 <+33>: lea rdx,[rbp-0x8]
0x0000000000400619 <+37>: mov rsi,rdx
0x000000000040061c <+40>: mov rdi,rax
0x000000000040061f <+43>: mov eax,0x0
0x0000000000400624 <+48>: call 0x4004f0 <__isoc99_scanf@plt>
0x0000000000400629 <+53>: mov eax,DWORD PTR [rbp-0x8]
0x000000000040062c <+56>: xor eax,DWORD PTR [rbp-0x4]
0x000000000040062f <+59>: cmp eax,0xdeadbeef
0x0000000000400634 <+64>: jne 0x400656 <main+98>
여기서 살펴볼 부분은 <+18>
, <+21>
, <+33>
, <+53>
, <+56>
, <+59>
이다.
가장 먼저 <+18>
과 <+21>
을 묶어 함께 보자.
여기서 rbp-0x4
에 rand()의 결과 값을 넣고, rbp-0x8
을 0으로 초기화하고 있다.
그 다음으로는 <+33>
을 보면 rbp-0x8
에는 scanf()의 입력 값이 들어간다는 것을 유추할 수 있다.
마지막으로 <+53>
, <+56>
, 그리고 <+59>
를 한꺼번에 살펴보자.
<+53>
에서 eax에 rbp-0x8
에서 입력 값을 올리면서 연산 준비를 하고, <+56>
에서 rand()의 결과 값인 rbp-0x4
와 XOR연산을 수행하게 된다.
그리고 최종적으로 해당 값이 0xdeadbeef
와 동일한지 확인하게 된다.
이제 모든 루틴의 분석이 끝났다. 만일 rand()의 결과 값이 일정하다는 것만 증명되면 우리는 XOR연산의 대칭성으로부터 무슨 입력을 넣어야 변수를 0xdeadbeef
로 변조할 수 있는지 알 수 있다. GDB를 통해 계속해 분석해보자.
b *main+53
으로 scanf()함수의 실행 직후부터 분석하자. 우리가 필요한 부분은 그곳 뿐이다.
해당 브레이크 포인트에서 멈춘 직후의 rbp-0x4
이다.
(gdb) x/xw $rbp-4
0x7fff2132edec: 0x6b8b4567
우리는 rand()의 결과 값이 고정되어 있다면 그 값이 0x6b8b4567
임을 알 수 있다. 이제 rand()의 결과 값이 일정한지만 살펴보면 된다.
GDB를 다시 처음부터 진행하여 해당 브레이크 포인트에서의 rbp-0x4
에 들어 있는 값이 0x6b8b4567
라면 모든게 끝난거다.
(gdb) x/xw $rbp-4
0x7fff6fa06d3c: 0x6b8b4567
빙고.
예상대로 rand()의 수행 결과 값은 고정되어 있었다.
현재까지 알아낸 점을 정리해 보자.
1) 랜덤 시드가 바뀌지 않는다.
2) rand() 결과 값이 고정되어 있다.
3) 그 결과 값은 0x6b8b4567이다.
이제 프로그래머 계산기를 꺼내서 16진수 계산을 하자.
0x6b8b4567 ^ 0xdeadbeef
는? 0xb526fb88
이다.
좋다. 마지막 단계가 남아있다. 이 값을 어떻게 넣을 것인가.
간단하다. 분명 scanf()로 입력받는다. 그럼 스크립트 언어를 이용해 입력 버퍼에 16진수 값을 넣어주면 되는 것 뿐이다. 마침내 공격코드가 나왔다. 다음과 같다.
(python -c 'print(0xb526fb88)';cat)|./random
이렇게 공격코드를 쏴주면 flag에 저장된 Auth Key가 나온다.
4. Flag
random@ubuntu:~$ (python -c 'print(0xb526fb88)';cat)|./random
Good!
Mommy, I thought libc random is unpredictable...