wargame.kr의 7번째 문제인 strcmp다.
이번 문제는 strcmp 함수를 우회하는 것이 목적이다. Start 버튼을 눌러 문제를 확인해보자.
간단한 입력폼 하나와 소스 코드를 보는 링크가 있다. POST로 비밀번호를 보낸다는 것 말고는 얻을 수 있는 정보가 없으니 소스를 분석해보자.
<?php
require("../lib.php"); // for auth_code function
$password = sha1(md5(rand().file_get_contents("/var/lib/dummy_file")).rand());
if (isset($_GET['view-source'])) {
show_source(__FILE__);
exit();
}else if(isset($_POST['password'])){
sleep(1); // do not brute force!
if (strcmp($_POST['password'], $password) == 0) {
echo "Congratulations! Flag is <b>" . auth_code("strcmp") ."</b>";
exit();
} else {
echo "Wrong password..";
}
}
?>
$password를 생성한 후 if 조건문을 타고 있는데 위의 분기는 소스 코드를 보여주는 동작이므로 아래쪽을 보면 된다. 브루트 포스 방지를 위해 1초간 대기한 후 사용자가 입력한 password 값과 생성된 $password가 같은지 비교하여 플래그를 제공하고 있는데, 그렇다면 $password는 어떻게 생성되는지 알아보자.
이런저런 함수를 호출하고 있지만 결론부터 말하자면 이는 절대로 사용자가 예측할 수 없도록 생성한 비밀번호다. 보면 $password는 sha1(md5(rand().file_get_contents("/var/lib/dummy_file")).rand())로 생성되고 있는데 이는 난수 + dummy_file의 내용 + 난수로 구성된 문자열을 md5, sha1 해싱하고 있다. 그래서 이를 알아내기란 거의 불가능하며 그렇다면 사용자가 이 비밀번호를 똑같이 입력해서 strcmp()를 통과할 방법이 없다는 얘기가 된다. 그렇다면 어떻게 우회할 수 있는 것일까?
제일 먼저 살펴볼 것은 strcmp() 함수가 어떤 상황일 때 어떤 값을 반환하는지다. 함수 자체는 C언어와 비슷하기 때문에 두 문자열이 일치하면 0을 반환하고 그렇지 않으면 음수나 양수를 반환하는데 이는 피연산자1에서 피연산자2를 뺄셈하여 얻은 결과다. 즉 문자열이 일치한다면, 예를 들어 "a"와 "a"를 비교했을 경우 둘 다 ASCII 값으로 97이기 때문에 97 - 97 = 0이 된다. 그래서 문자열이 일치한다면 0을 반환하는 것이다. 그것 말고도 다른 경우는 없을까? 한번 PHP의 문서를 읽어보기로 했다.
문서 자체에도 반환값은 두 문자열을 비교해서 음수, 0, 양수로 반환하는 것 말고는 별다른 정보가 적혀있지 않았다. 그런데 유저들이 남긴 노트란에 신경쓰이는 내용이 있었는데 strcmp 함수를 사용할 때 두 매개변수가 모두 확실하게 문자열이 아닌 이상 이 함수를 사용하는 것은 예기치 않은 결과를 불러올 수 있다는 것이다. 이게 무슨 소리인가 해서 자세히 살펴보니 다음과 같은 비교 예시를 볼 수 있었다.
즉, strcmp() 함수에서 매개변수로 문자열이 아닌 다른 형식의 값이 올 경우 함수의 반환값은 우리가 예상한 것과 다르게 나타날 수 있다는 것이다. 이에 관해서 좀 더 조사해보니 다음과 같은 표를 확인할 수 있었다.
여기서 보면 놀랍게도 분명히 서로다른 자료형임에도 비교 결과가 true가 되는 경우가 꽤 많이 있는것을 볼 수 있었다. 등호를 세 개 사용하는 엄격한 비교(===)에서는 흔히 생각하는 것처럼 같은 자료형의 같은 값만 비교 결과가 true가 나왔지만 등호를 두 개 사용하는 느슨한 비교(==)에서는 위처럼 true가 간간히 보인다. 이런걸 보면 php는 c언어와 참 닮은것 같기도 한데... 아무튼 그렇다면 이를 활용해서 strcmp() 함수에 매개변수를 서로다른 자료형을 갖도록 하면 true, 즉 0을 반환해서 저 로직을 우회할 수 있으리라 추측해볼 수 있다.
그런데 결국 strcmp()에는 사용자가 POST입력으로 보낸 데이터가 그대로 들어가기 때문에 우리가 일반적으로 입력하는, 즉 폼에 데이터를 입력하고 버튼을 눌러 보내는 방식으로는 문자열 데이터밖에 전송되지 않아 같은 자료형인 $password와 항상 정상적으로 비교된다. 그러면 어떻게 문자열이 아닌 다른 자료형의 데이터를 전달해줄 수 있을까? 검색해보면 이런 자료들을 금방 찾을 수 있었다.
폼으로 전달되는 요소의 이름을 'password[]' 처럼 바꿔주면 이를 php에서 배열로 인식한다고 한다. 그래서 다음과 같이 HTML 코드를 수정해서 배열로 바꿔주고 입력폼에 아무 값이나 입력해서 버튼을 누르면 플래그를 볼 수 있다.
이번 문제에서는 함수가 무조건 완전하게 동작한다는 틀에 박힌 사고방식때문에 푸는데 시간이 꽤 걸린 문제였다. C언어에서든 어디서든 strcmp() 같은 함수를 사용하면 요구되는 매개변수 자료형을 맞추지 않을 경우 IDE가 대부분 경고를 해주기 때문에 이를 항상 맞춰 넣었다. 그래서 다른 자료형의 데이터를 넣었을 때 어떻게 동작할지는 거의 생각해보지 않았는데 지금은 프로그램 개발이 아닌 해킹을 하고있는 것이기 때문에 이렇게 함수를 일부러 고장내보면서 반환값이나 어떤 행동을 하는지 관찰해보는것도 좋은 습관이 될 것 같다. 원래는 문제를 풀 때 일체 검색같은걸 하지 않고 지금 내 머리에 있는 지식으로만 풀려고 했지만 이걸 풀면서 공부를 하려는 건데 미련하게 끙끙대고 있는 건 바보같다고 생각해서 이제부터는 직접적으로 풀이를 검색하진 않고 관련 키워드를 검색하며 공부하는 방식으로 풀어보려고 한다.
wargame.kr의 자체 튜토리얼 영상에도 이 문제의 핵심을 다루는 강의가 있다. 한 번 보면 좋을 듯.
'챌린지 > Wargame.kr' 카테고리의 다른 글
bughela - DB is really GOOD (0) | 2020.12.22 |
---|---|
bughela - md5 password (0) | 2020.12.22 |
bughela - fly me to the moon (0) | 2020.12.16 |
bughela - WTF_CODE (0) | 2020.12.14 |
bughela - login filtering (0) | 2020.12.11 |