PHP strcmp 인증 우회 취약점
이 글에서는 PHP Type Juggling 과 함께 자주 사용되는 strcmp() 함수의 문자열 검증을 우회하는 취약점에 대해 소개한다.
strcmp()
strcmp() 함수는 다른 언어에서 사용되는 함수와 마찬가지로, 두 문자열을 인자로 전달받아 동일한 문자열인지 검증하는 함수이다. 검증 결과 string1 이 string2 보다 작으면 -1을 반환, 더 크면 1을 반환, 두 문자열이 동일하면 0을 반환한다.
출처 : https://www.php.net/manual/en/function.strcmp.php#refsect1-function.strcmp-returnvalues
Returns-1ifstring1is less thanstring2;1ifstring1is greater thanstring2, and0if they are equal.
취약점
아래 php 코드는 strcmp() 함수를 이용해 $pw 에 전달받은 사용자 입력 값을 $admin_pw 값을 비교하는 예제 코드이다.
<?php
$admin_pw = "secret_password";
$pw = "some_password";
if (!strcmp($pw, $admin_pw)) {
echo "Hello, admin!";
return;
}
?>
하지만PHP 5.3 이상, 8.0 미만의 버전에서는 strcmp() 에 전달되는 문자열에 Array() 타입의 데이터를 전달하면 항상 Null 을 반환하여 검증을 우회할 수 있다.
<?php
$admin_pw = "secret_password";
//$pw = "some_password";
$pw = array();
if (!strcmp($pw, $admin_pw)) {
echo "Hello, admin!";
return;
}
?>
위 코드를 약간 변경해서 strcmp() 결과 값이 0과 일치하는지 비교해도 동일하게 if 문을 실행하는 것을 알 수 있다. 이는 PHP 의 loose comparison 특성에 따라 Null 과 0은 == 로 비교 시 TRUE 를 반환하기 때문이다.
<?php
$admin_pw = "secret_password";
//$pw = "some_password";
$pw = array();
if (strcmp($pw, $admin_pw)==0) {
echo "Hello, admin!";
return;
}
?>

curl 로 POST method 패킷에 password 파라미터를 array 형태로 전달하는 예제는 다음과 같다.
curl http://strcmp.co.kr -X POST -d "password[]=qwer"
해결책
근본적인 해결 방법은 PHP 버전을 8.0 이상으로 서버를 운영하여 strcmp() 에 Array 가 인자로 전달되도 Null 을 반환하지 못하도록 하는 것이다.
또는 위에서 제시한 2번째 case처럼 strcmp() 결과를 0과 비교할 시, == 가 아닌 === 연산을 사용하여 strict comparison 하여 비교 결과가 FALSE 가 되도록 해야 한다.
