[TetCTF 2024] Stress Release Service
개요
For a better New Year, we are introducing a service that can help you reduce stress.
문제 분석
웹페이지에 들어가면 입력 창과 버튼이 하나 있습니다. 버튼을 클릭하면 입력한 내용과 개구리가 함께 출력됩니다.
소스 코드가 제공된 문제로, 플래그는 secret.php
에 하드코딩되어 있습니다. 상단에 배포한 파일에는 실제 플래그를 적어놓기는 했지만, 대회 환경에서 배포된 파일에는 가짜 플래그가 적혀 있습니다.
1 |
|
index.php
를 보면 입력한 내용이 $voice
에 대입됩니다. 이후 validateInput()
함수의 검증을 통과할 경우 eval()
함수의 문자열 인자 중 일부로 전달됩니다. PHP의 eval()
함수는 문자열을 코드로 간주하여 실행하는 함수이기 때문에, 임의 코드 실행이 가능한 상황입니다.
1 | if (isset($_GET["shout"]) && !empty($_GET["shout"]) && is_string($_GET["shout"])) { |
validateInput()
함수는 입력이 특수문자로만 이루어져 있는지, 사용한 문자의 종류가 7종을 초과하는지 검사합니다. 즉 이 문제는 특수문자 7종 이하만을 사용한 PHP 코드 작성을 요구하고 있습니다.
1 | function validateInput($input) { |
문제 풀이
기존의 특수문자만을 이용한 PHP 코드 실행 문제를 보면 몇 가지 파훼법이 존재합니다. 예를 들어 아래 풀이는 $_
변수에 아무 문자열이나 대입한 후, $_++;
로 원하는 문자가 나올 때까지 증감 연산을 하는 방법을 사용합니다.
그런데 이 문제는 PHP 8.3 버전에서 동작하여 위와 같은 방식을 사용할 수 없습니다. 증감 연산자에 대한 PHP 문서를 보면 8.3 버전부터 문자열에 대한 증감 연산을 더 이상 지원하지 않습니다.
다만 PHP의 논리적 xor 연산자는 문자열 간의 연산을 지원하고 있습니다. php -a
로 인터랙티브 셸을 실행하여 확인할 수도 있습니다.
1 | Interactive shell |
그렇다면 특수문자 7종 간 xor 연산을 계속하여 코드 실행에 필요한 문자를 모두 얻을 수도 있을 것입니다.
index.php
코드를 보면 입력은 $res = '...
와 ...';
사이에 들어갑니다. 따라서 문자열과 코드를 분리하기 위한 '
와 ;
이 필요합니다. 또한 xor과 문자열 덧셈을 위한 ^
와 .
, xor 연산식을 감싸기 위한 (
와 )
를 선택했습니다. 7종을 채우려면 하나가 남는데, 역슬래시 \\
를 사용했습니다.
각 알파벳을 가장 적은 횟수의 xor 연산으로 얻는 완전 탐색을 실시하면, 선택한 특수문자 중 최대 6종만으로 모든 문자의 조합이 가능함을 확인할 수 있습니다.
1 | {'A': [39, 59, 40, 41, 92], |
추가적으로 PHP는 다음과 같이 함수명에 해당하는 문자열을 괄호로 감싸 함수 호출이 가능합니다.
1 | Interactive shell |
따라서 system()
함수를 호출해 임의 커맨드를 실행할 수 있습니다. 아래 코드는 완전 탐색 후 ("system")("cat s*")
를 실행하기 위한 특수문자 문자열을 조합합니다.
1 | #!/usr/bin/env python3 |
문자열을 입력하고 버튼을 누르면 개구리 말고 다른 내용은 보이지 않는데, 브라우저의 소스 코드 확인 기능을 이용하면 플래그가 포함되어 있는 것을 확인할 수 있습니다.