[web hacking] strcmp 풀이
문제 소개
https://dreamhack.io/wargame/challenges/328/
[wargame.kr] strcmp
Description if you can bypass the strcmp function, you get the flag.
dreamhack.io
wargame.kr 사이트에 있던 문제입니다.
strcmp 함수
strcmp(string $string1, string $string2): int
문자열 두 개를 비교하는 함수입니다.
string1이 string2보다 작으면 -1을, 크다면 1을, 같다면 0을 반환합니다.
코드 분석
<?php
require("./lib.php"); // for FLAG
$password = sha1(md5(rand().rand().rand()).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>" . $FLAG ."</b>";
exit();
} else {
echo "Wrong password..";
}
}
?>
<br />
<br />
<form method="POST">
password : <input type="text" name="password" /> <input type="submit" value="chk">
</form>
<br />
<a href="?view-source">view-source</a>
소스코드를 살펴보면 3번째 줄에 password가 rand함수, md5 암호화, sha-1 암호화 과정을 거쳐 만들어진 것을 볼 수 있습니다.
그리고 10번 줄에서 사용자가 입력한 password가 암호화를 통해 생성한 password와 일치하는지 비교하고 있습니다.
일반적으로는 두 문자열이 같을 때 flag값을 얻어낼 수 있겠지만, rand함수는 시드에 따라 값이 변하기 때문에 유추해 낼 수 없습니다.
해결법
저희는 php5의 strcmp 함수에 있는 취약점을 이용할 것입니다.
기본적으로 두 문자열을 비교하는 함수이지만, strcmp(string, array) 이런 식으로 배열을 전달하게 되면, null을 리턴하게 됩니다.
또한 php의 == 비교 연산자를 살펴보면, 아래 자료와 같이 null과 0을 비교할 때 true를 반환하게 되어있습니다.
따라서 우리는 post 방식으로 password를 전달할 때, 문자열이 아닌 배열로 전달하면 될 것입니다.
<!--
<form method="POST">
password : <input type="text" name="password"> <input type="submit" value="chk">
</form>
-->
<form method="POST">
password : <input type="text" name="password[]"> <input type="submit" value="chk">
</form>
여기서 name="password"를 다음과 같이 name="password[]" 로 변경하고 아무 password나 입력한 후 submit을 하면,
post 방식으로 배열이 전달되어 flag 값을 취득할 수 있습니다.
취약점 보완법
strcmp 함수의 결과로 null이 나올 수 있으므로, 비교 연산자를 == 을 사용하는 것이 아닌, ===을 사용하게 되면,
null === 0 -> false 결과를 반환하기 때문에, 취약점을 보완할 수 있습니다.