문제분석 & 풀이
strcmp 함수를 우회하면 flag를 획득할 수 있다고 한다.
strcmp 함수와 관련된 취약점을 활용하는 문제인 것 같다.
패스워드 입력창과 코드가 주어진다.
코드를 분석해보자.
<?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..";
}
}
?>
○ 코드분석
1) sha1 해시값을 $password에 저장한다.
2) 전송한 password와 $password를 strcmp 함수로 비교해서 결과가 같으면 flag 출력한다.
문제를 해결하기 위해서는 $password를 알아야 할 것 같은데, 아쉽게도 랜덤값이라서 알아낼 수 없다.
코드에서 strcmp()로 2개의 값을 비교하고 있는 것을 알 수 있다.
힌트에서는 strcmp()를 우회하면 flag를 얻을 수 있다고 했다. 따라서 strcmp()와 관련된 취약점을 조사했다.
strcmp(String, Array())
PHP 5.3 버전에서는 strcmp()의 인자로 strcmp(String, Array())와 같은 형태가 들어가면 결과로 NULL을 반환한다고 한다.
만약, strcmp(String, Array())를 완성하는 password를 입력하면 strcmp(String, Array())는 NULL을 반환할 것이고 결과는 NULL == 0 이된다.
[표]는 "==" 로 느슨한 비교 시에 리턴되는 결과를 표로 정리한 것이다.
if문에서 "=="로 느슨한 비교를 하고 있고 [표]를 보면 "=="를 이용한 느슨한 비교 시, NULL == 0 의 결과가 TRUE라고 나와있다.
if문 결과가 TRUE가 되면 flag를 획득할 수 있다.
공격시나리오
1) strcmp(String, Array()) 형태를 만들기 위해 password를 배열로 전송한다.
2) strcmp(String, Array())의 결과는 NULL이 되고 NULL == 0 은 PHP의 느슨한 비교로 인해 TRUE라는 결과가 리턴된다.
3) 결국, if문이 TRUE가 되고 flag가 화면에 출력된다.
공격 시나리오대로 진행해보자.
배열의 형태로 password를 전송하기 위해 위와 같이 password[] 으로 input 태그의 name을 바꿨다.
password를 배열의 형태로 바꿨으니 아무값이나 입력하고 요청하자.
flag가 나왔다.
이와 같은 취약점이 발생하지 않기 위해 조치할 수 있는 방법은 무엇일까?
"==" 대신에 "===" 를 비교 구문에서 사용하는 것을 통해 방지할 수 있다.
[표]를 보면 "=="로 비교할 때와 달리 NULL === 0 의 결과가 false 라는 것을 알 수 있다. 따라서 개발자는 되도록 "==="를 사용하는 것을 지향해야한다.