문제 코드

#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] )){
		setregid(getegid(), getegid());
		system("/bin/cat flag");
		return 0;
	}
	else
		printf("wrong passcode.\n");
	return 0;
}

취약한 코드 분석

if(hashcode == check_password( argv[1] )){
		setregid(getegid(), getegid());
		system("/bin/cat flag");
		return 0;
	}

위 코드에서 조건문을 실행시켰을 경우 system("/bin/cat flag"); 해당 명령어를 통해서 flag를 탈취할 수 있는 것을 알 수 있었다.

해당 조건문의 hashcode는 0x21DD09EC였으며, argv[1] 즉 첫 번째 인자 값을 check_password라는 함수에 넣어서 hashcode와 비교하는 문제였다. 따라서 check_password라는 함수에 역연산을 하는 함수를 만들어서 hashcode 값을 넣으면 입력값에 넣을 값을 알 수 있다.

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;
}

ip[0] =주소 ip[1]= 주소 … 이므로 결국 hashcode /5한 값을 *5번 곱하면 되지만 hashcode를 계산기에 넣어보면 5의 배수가 아니므로, /5를 하는 과정에서 반올림을 하게 된다. 따라서 “0x6C6CEC8” * 4 + “0x6C5CECC‬” 이렇게 넣어줘야 합니다.

exploit

./col `python2 -c 'print "\xC8\xCE\xC5\x06"*4+"\xCC\xCE\xC5\x06"'`

삽질 내용

char * p가 1byte이고 int * ip 가 8byte 인 것은 알았지만 hashcode가 5번 덧셈 연산이 이뤄져서 만들어진 것인데, 5개의 연산이 전부 다르면 어떻게 찾지라는 의문을 가졌다. 하지만 if hashcode==check_password(argv[1])이 함수만 통과하면 된다는 사실을 코드를 계속 반복적으로 해석하다 보니 알게 되었으며, 따라서 hashcode/5라고 hashcode가 5의 배수는 아니므로 이로 인해 생기는 차이는 추가적인 +으로 해결하려고 하였다. 또한 20자 문제는 \xC8이거 자체가 1자인데, 나는 C를 입력값 한 자로만 생각하고 있었다. 위 문제는 ascii 코드를 떠올리고 풀릴 수 있었다.