Old - 61 Write Up
작성자 - LRTK
아무 것도 없이 소스코드만 던져줘서 코드를 확인하였다.
<?php
include "../../config.php";
if($_GET['view_source']) view_source();
$db = dbconnect();
if(!$_GET['id']) $_GET['id']="guest";
echo "<html><head><title>Challenge 61</title></head><body>";
echo "<a href=./?view_source=1>view-source</a><hr>";
$_GET['id'] = addslashes($_GET['id']);
if(preg_match("/\(|\)|select|from|,|by|\./i",$_GET['id'])) exit("Access Denied");
if(strlen($_GET['id'])>15) exit("Access Denied");
$result = mysqli_fetch_array(mysqli_query($db,"select {$_GET['id']} from chall61 order by id desc limit 1"));
echo "<b>{$result['id']}</b><br>";
if($result['id'] == "admin") solve(61);
echo "</body></html>";
?>
코드를 분석하자면,if(!$_GET['id']) $_GET['id']="guest";
일단 디폴트로 서버에 id=guest
라는 파라미터가 GET 메소드로 전송되고 있다.
$_GET['id'] = addslashes($_GET['id']);
if(preg_match("/\(|\)|select|from|,|by|\./i",$_GET['id'])) exit("Access Denied");
if(strlen($_GET['id'])>15) exit("Access Denied");
id 파라미터 값에 대한 검증이 addslashes 함수와 preg_match 함수로 이루어지고 있다.
필터링거나 이스케이프되는 문자열을 나열하면, ', ", \, Null, \(, \), select, from, ,, by, \.
로 이루어져 있다.
또한 id 파라미터 값의 길이가 15를 초과하면 Access Denied이 출력된다.
$result = mysqli_fetch_array(mysqli_query($db,"select {$_GET['id']} from chall61 order by id desc limit 1"));
echo "<b>{$result['id']}</b><br>";
검증이 마친 id 파라미터 값은 Query문에 들어가게 되는데, select ID파라미터 from chall61 order by id desc limit 1
로 구성되어 있다.
Query문에서 값이 반환 되면, 굵은 글자로 Query문의 결과 값을 출력한다.
현재의 상태는 id 파라미터를 따로 지정하지 않아서 guest로 되어있는 상태라, select guest from chall61 order by id desc limit 1
로 데이터를 요청하고 있다.
이 때문에 DB의 컬럼이 guest가 있지 않은 상태라, 결과값이 출력이 되지 않는 것으로 추측이 된다.
if($result['id'] == "admin") solve(61);
우리의 목표인 solve 함수를 출력하기 위해서는 Query문이 admin이라는 값을 반환하도록 해야한다.
일단 검색되는 컬럼이 guest로 되어 있으니, id로 변경하여 서버에 전송하겠다.
select id from chall61 order by id desc limit 1
로 데이터를 요청하였기 때문에 id 컬럼의 내림차순에서 조회를 하여 맨 위에 있는 딱 1개를 출력한다.
그러므로, 알파벳 순으로 맨 위에 있는 것은 test라는 것이고 a가 앞인 admin은 맨 밑에 있다는 뜻이다.
이를 파악한 나는 SQL Injection을 위해 도커 위에 있는 Mysql에 테이블을 구현하였다.
CREATE TABLE chall61(
id varchar(15)
);
INSERT INTO chall61 VALUES ('test');
INSERT INTO chall61 VALUES ('a');
INSERT INTO chall61 VALUES ('b');
위와 같은 테이블을 만들어서 SQL Injection를 시도할 것이다.
제일 먼저 나는 select ‘admin’ from chall61 order by id desc limit 1;
을 생각하였다.
이를 실행하게 되면, 무조건 admin을 출력하나 컬럼의 이름도 admin으로 변경되어 $result['id']
에 해당이 안된다는 것을 파악을 하였다.
그래서 나는 컬럼의 이름을 대신 사용하는 문법인 as를 이용하여 SQL Injection을 시도하였다.
이를 통해 서버로 'admin' as 'id'
을 전송하였지만,
아무 반응이 없다는 것을 확인할 수 있다.
정확한 확인이 필요하여, 페이지 코드를 참고하여 검증에 대한 프로그램을 만들었다.
<?php
$stdin = fopen('php://stdin', 'r');
$input = trim(fread($stdin, 1024));
fclose($stdin);
$input = addslashes($input);
if(preg_match("/\(|\)|select|from|,|by|\./i",$input)) echo "preg match!!!! ---->";
if(strlen($input)>15) echo "strlen!!!! ---->";
echo "select {$input} from chall61 order by id desc limit 1";
?>
'admin' as id
strlen!!!! ---->select \'admin\' as id from chall61 order by id desc limit 1
이를 통해 좀 더 간편하게 검증에 대해 생각할 수 있었다.
1시간이 넘게 시도했지만, 도저히 찾을 수 없어서 인터넷에 풀이를 알아봤다.
방법은 간단했다.'
이 사용이 불가능하니, 자동으로 String를 인식해야한다.
그 방법은 Hex값을 사용하는 것이다.
직접 사용해보니, 정말 String으로 인식이 되었다.
여기서 as id
를 추가하면 검증에 통과할 수 있는지 확인해봤다.
0x61646d696e as id
strlen!!!! ---->select 0x61646d696e as id from chall61 order by id desc limit 1
15글자를 넘어서 통과가 안되는 모습이다.
여기서 또 새로운 방법을 배울 수 있었다.
as를 굳이 쓰지 않고, 옆에 대신 사용할 값을 붙어주기만 하면 자동으로 as로 인식이 되는 것이다.
이제 서버로 저 값을 보내면,
flag를 획득할 수 있다.
'Season 1 > 워게임' 카테고리의 다른 글
Old - 12 Write Up (0) | 2021.05.19 |
---|---|
Old - 10 Write Up (0) | 2021.05.19 |
Old - 59 Write Up (0) | 2021.05.17 |
Old - 42 Write Up (0) | 2021.05.17 |
Old - 36 Write Up (0) | 2021.05.17 |