본문 바로가기

Let's Study/Hacker's CTF

[2014 Hack.lu] guess the flag write-up



몇달 전에 있었던 hack.lu ctf 문제 중 exploit 문제 Guess the Flag 입니다.


그 날 이 문제 풀면서 멘붕이 왔었는데 바쁜 일 다 끝나고 학교동아리 홈페이지에 이 문제를 아주 약간 바꿔서 올린걸 풀어보았습니다.






문제의 원래 코드는 이렇게 생겼습니다. 전 동아리 서버에 올린 문제로 풀이를 하겠습니다.


문제의 서버환경은 64bit이지만 동아리 서버는 32bit입니다. 문제풀이 하실 때에 크게 차이 없을거에요.


이 문제의 핵심적인 부분을 설명드리자면,


* guess> 뒤에 100글자를 입력해야 하고


 위의 is_flag_correct() 함수에서 


char value1 = bin_by_hex[flag_hex[i * 2      ]];

char value2 = bin_by_hex[flag_hex[i * 2 + 1]];


에서 * value1 에는 16진수의 앞자리가 들어가고 value2는 16진수의 뒷자리가 들어갑니다.


char diff = 0;

for (i = 0; i < 50; i++)

{

diff |= (flag[i] ^ given_flag[i]);

printf("diff = %d\n", diff);

}


return (diff == 0);



* 이 부분은 서버의 flag값과 자신이 입력한 100글자 값이 모두 일치할 경우 ( |= 를 썼기 때문에 하나라도 삐끗하면 diff 값이 절대 0이 나올 수 없습니다.) return 1을 반환합니다. 이럴 경우 handle() 함수에서는 correct, 일치했다고 받아들입니다.


* rtrim() 함수에서는 입력한 문자열에 \r\n 가 포함될 경우 그걸 짤라냅니다. 


코드를 찬찬히 훑어보면 이 코드에는 문제가 전혀 없습니다. 아주 완벽하게!! 코딩되었어요. untrusted input 값은 보이지도 않고요.

브루트 포스로 100글자를 돌려야되나요? 절대 안됩니다. 너무 오래걸려요.


이 문제는 배열을 음수로 쓸 수도 있다는 걸 감안해서 브루트포스 어택을 시도해야 합니다. 

가령 a[1] 이 있다면 a[-1]도 있겠죠.


char value1 = bin_by_hex[flag_hex[i * 2      ]];

char value2 = bin_by_hex[flag_hex[i * 2 + 1]];


여기에서는 입력한 값을 이용해서 


given_flag[i] = (value1 << 4) | value2;


given_flag 값을 생성합니다. 이 given_flag 는 실제 flag 배열에 있는 값과 xor연산을 하게 되는데


만약 배열을 역참조해서 flag값에 접근할 수 있다면 given_flag 값에 뭐가 들어있는지는 모르겠지만 flag 값에 접근할 수 있는 offset값을 찾아서(given_flag[offset]) flag값과 xor연산을 했을 때 , flag[i] ^ given_flag[i] == 0 이 나온다면 결국 correct, 일치한다는 답을 얻어오겠죠.



일치해서 출력되는 결과값 Yaaaay! you guessed the flag ... 가 나온다면 게임 끝.


앞에서건 뒤에서건 여러분이 원하는 방향에서 한글자 한글자씩 brute force로 값을 얻어올 수 있습니다.




* 위 flag값은 hack.lu 의 flag값은 아니고 임의로 지정한 flag입니다.




guess_the_flag 는 당시 hack.lu에 올라와있던 tar 파일이고

q2.c는 원본 텍스트를 한글로 조금 바꾼 파일

guess.py 는 제가 짠 exploit 코드입니다.



guess_the_flag_cb153ec1260946d13f5f1f0605e4dff9.tar


q2.c


guess.py