정보 · 테크
PHP 의 parse_str 함수 우회와 원인 분석

저는 먼 과거에 wargame.kr 에 문제를 하나 출제한 적이 있었습니다.
그런데 혹시 제 문제를 풀어보신 분이라면
아래 필터링이 우회가 가능하다는 사실 알고 계셨나요?

저는 그 사실을 문제 출제 후 한참이 지나서야 발견하였습니다.

if(preg_match('/session/isUD',$_SERVER['QUERY_STRING'])) {
exit('not allowed');
}
parse_str($_SERVER['QUERY_STRING']);

해당 문제에서는 세션이 따로 분리되어있는 상태기 때문에
SESSION 변수를 조작할 수 있다고 해도 어떤 악의적인 행위를 할 수는 없었다는 사실이
그나마 다행입니다.

그렇다면 해당 코드에는 어떤 취약점이 존재하는 것일까요?
기존에 알려진 트릭 중 ] 등의 일부 문자가 _ 로 바뀌어 들어가는 현상이 있지만
위 상황에선 활용할 수 없습니다.

결론을 말하자면

_%53ESSION[is_admin]=1

와 같이 URL 인코딩을 통해 필터링을 우회할 수 있습니다.
결론만 필요하신 분은 여기까지 읽으셔도 됩니다.

이제부터는 이러한 현상이 발생하는 이유에 대해 알아보겠습니다.
분석 방법은 정적 분석으로 이루어졌으며
정적 분석으로 어려운 경우에 한해 gdb 를 사용하였습니다.
기회가 된다면 php / python 등을 gdb 로 분석하는 방법에 대해서도 글을 써보도록 하겠습니다.

  1. PHP_FUNCTION(parse_str) 에서는 마지막에
    sapi_module.treat_data(PARSE_STRING, res, arrayArg); 를 호출합니다.

  2. 그런데 sapi_module.treat_data 함수는 초기에 $_GET, $_POST 등의 전역 변수를 세팅하는
    php_startup_auto_globals 함수의 처리 과정 에도
    sapi_module.treat_data(PARSE_GET, NULL, NULL); 와 같이 유사하게 호출됩니다.

  3. sapi_module.treat_data 는
    mbstr_treat_data 또는 php_default_treat_data 함수를 polymorphic 하게 호출하기 위한 함수 포인터 입니다.

  4. mbstr_treat_data 를 예를 들자면
    이 함수에서는 첫 번째 인자의 종류에 관계 없이
    _php_mb_encoding_handler_ex 를 호출하는데, 함수 내부에서는 url decoding 이 이루어집니다.

결론지어 url 인코딩은 첫번째 인자가 PARSE_STRING 이 아닐 때에만 이루어져야 하는데
PHP 개발자의 실수로 PARSE_STRING 일 때도 이루어져 위와 같은 현상이 발생한 것 입니다.

이상입니다.

#url_encoding #parse_str #php_code_auditing
작성자 정보