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)


Used Tools
GNU bash (version 4.3.11)



2. Gathering Informations
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();    // random value!

    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...
Posted by RevDev
,