Pwnable.kr 의 두번째 문제를 풀어보자.
이번 문제는 MD5 hash collision 에 대한 문제라는 힌트를 주고 있다.
그러면 Hash 는 무엇이고 Hash collision 이 무엇인지 간단하게 알아보자.
Hash 란 임의의 길이를 갖는 임의의 데이터에 대해 고정된 길이의 데이터로 매핑하는 것을 의미한다.
한 개의 입력값에 대해 한 개의 출력값이 나오는데, 이 입력값의 범위는 무한한 데 비해
출력값의 범위는 유한하기 때문에 입력이 다름에도 불구하고 드물게 동일한 값이 출력되는 경우가 존재한다.
이러한 경우를 '충돌' (Collsiion)이 발생했다고 한다.
이제 문제를 살펴보자.
역시나 flag 파일이 존재하고 권한이 없기 때문에 setUID 가 걸려있는 col 파일을 이용해서 문제를 해결해야겠다.
그럼 소스 코드를 한 번 확인해보자.
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;
}
hashcode 변수에 특정값으로 초기화를 하고, check_password 함수를 정의했다.
매개변수로 받은 데이터를 int 형으로 형 변환한 뒤, 반복문을 통해 4바이트씩 끊어서 (int 형 포인터이기 때문)
res 변수에 더한다.
main 함수를 살펴보자.
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;
}
인자로 넣은 데이터의 길이가 20 byte 가 아니라면 역시 메시지를 출력하고 종료한다.
if(hashcode == check_password(argv[1])){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\n");
return 0;
인자로 넣은 값을 check_password 함수로 전달해서 return 된 값을 hashcode 와 비교한다.
동일하다면 flag 파일을 출력하는 것을 볼 수 있다.
위의 조건문에 해당하는 것이 없다면 잘못된 passcode 라며 메세지를 출력하고 종료한다.
코드를 살펴봤으니 이제 해결방법을 찾아보자.
먼저 고민해야 할 것은 check_password 함수를 거친 이후 return 된 값이 어떻게 하면 hashcode 변수의 값과
동일하게 만들것인가이다.
check_password 함수를 보면 인자로 삽입한 값을 int 형으로 변환한 뒤 4바이트씩 끊어서 5번 더하고 있다.
그리고 main 함수에서 인자로 삽입한 값의 길이를 20 byte 로 맞춰야 한다는 것을 알려주고 있기 때문에
우리는 20 byte 의 데이터를 4바이트씩 나눠서 삽입해야 한다.
hashcode 에 저장된 값은 16진수로 0x21DD09EC 이다.
이것을 5로 나누면 (5번 더해서 hashcode 와 비교하기 때문에) 몫이 0x6C5CEC8 이고 나머지가 4이다.
0x6C5CEC8 를 4번 더하고 0x06C5CECC 를 1번 더하면 문제를 해결할 수 있다.
이것을 인자로 삽입하기 위해서 파이썬 스크립트를 사용하자.
쉘에서 python -c 를 이용하면 한 줄 스크립트를 바로 실행시킬 수 있다.
그 전에 'Endian' 이라는 개념을 알고 있어야 하는데,
Endian 이라는 것은 컴퓨터에 데이터가 저장되는 순서를 말한다.
Byte 단위로 데이터를 저장할 때 저장하는 순서(Byte oreder)가 아키텍처에 따라 다르다.
출쳐 : ko.wikipedia.org/wiki/엔디안
문제를 제공하고 있는 서버의 엔디안을 확인하는 명령어는 다음과 같다.
Little Endian 방식은 데이터를 저장할 때 작은 단위의 바이트, 즉 하위 바이트가 앞에 오는 방식이다.
그렇기 때문에 Byte 단위로 데이터를 삽입할 때 하위 바이트부터 삽입해야한다.
그렇다면 이제 python -c 를 이용해 스크립트를 짜보자.
0x6C5CEC8 를 4번, 0x6C5CECC 를 Little Endian 방식으로 삽입하면
"\xC8\xCE\xC5\x06" * 4 + "\xCC\xCE\xC5\x06"
다음과 같이 되고, 이를 python -c 를 이용해 스크립트로 작성하면
`python -c 'print "\xC8\xCE\xC5\x06" * 4 + "\xCC\xCE\xC5\x06"'`
다음과 같다.
쉘에서 백쿼터는 감싼 내용을 명령어로 인식하게 하는 역할을 한다.
백쿼터 안의 내용을 따로 실행해서 결과를 받아오고, 받아온 결과가 ./col 뒤 자리로 들어가면서 인자로 전달되는 것이다.
이제 문제를 해결해보자.
clear