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-1
ifstring1
is less thanstring2
;1
ifstring1
is greater thanstring2
, and0
if 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
가 되도록 해야 한다.
