Post

(45) pwnable.kr collision 문제 풀이

문제 설명

Daddy told me about cool MD5 hash collision today.
I wanna do something like that too!

문제 풀이

파일이 뭐가 있는지 부터 확인해보겠습니다.

이렇게 3개의 파일이 존재 합니다. 소스코드 파일인 col.c를 열어보면

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
  int* ip = (int*)p;
  int i;
  int res=0;
  for(i=0; i<5; i++){
    res += ip[i];
  }
  return res;
}

int main(int argc, char* argv[]){
  if(argc<2){
    printf("usage : %s [passcode]\n", argv[0]);
    return 0;
  }
  if(strlen(argv[1]) != 20){
    printf("passcode length should be 20 bytes\n");
    return 0;
  }

  if(hashcode == check_password( argv[1] )){
    system("/bin/cat flag");
    return 0;
  }
  else
    printf("wrong passcode.\n");
  return 0;
}

코드를 보면 실행할 때 인자를 추가로 하나 더 받고 있습니다. 입력값은 20바이트(글자)이어야 하고, 입력값을 check_password함수에 넣은 결과가 hashcode와 일치해야 flag를 얻을 수 있도록 되어 있습니다.

check_password함수를 자세히 살펴봅시다.

1
2
3
4
5
6
7
8
9
unsigned long check_password(const char* p){
  int* ip = (int*)p;
  int i;
  int res=0;
  for(i=0; i<5; i++){
    res += ip[i];
  }
  return res;
}

char* 즉, 문자열을 받아서 int*로 변환합니다. 그리고 ip를 배열처럼 사용해서 20바이트를 4자리씩 끊어서 int로 해석한 다음 각 숫자를 전부 더해서 res에 넣고 res를 반환합니다.

우리가 해야하는건 이 조건, 즉 4자리씩 끊은 숫자 5개의 합이 hashcode 즉, 0x21DD09EC와 일치해야 합니다. 이를 만족하는 숫자는 여러개 있지만 가장 간단하게 생각해 볼 수 있는건 5로 나누는 겁니다. 하지만 0x21DD09EC라는 값은 5로 나누어 떨어지지 않기 때문에 몫만 구한뒤 마지막 하나에는 나머지를 더해주는 방식으로 구해보겠습니다.

위와 같이 구할 수 있습니다. 이걸 문자로 넣어줘야 하고, 리틀엔디안을 사용하기 때문에 각각의 숫자에 해당하는 값은 "\xc8\xce\xc5\x06""\xcc\xce\xc5\x06"입니다. 몫을 4번 붙이고, 그 뒤로 나머지를 더한 것을 붙여준 것을 col의 인자로 넘겨주면 됩니다.

이 값을 넘겨줄때는 python -c를 이용하는게 간편합니다. 아래와 같이 넘겨주게 된다면

1
./col `python -c 'print "\xc8\xce\xc5\x06"*4+"\xcc\xce\xc5\x06"'`

이렇게 flag가 출력됩니다.

This post is licensed under CC BY 4.0 by the author.