카테고리 없음

Web Hacking_SQL Injection

Sono Hacking Study 2024. 9. 26. 11:32

1. 서론

DBMS에서 관리하는 DB에는 계정, 비밀글과 같은 민감한 정보가 포함되어 있을 수 있다. 공격자는 DB 파일 탈취, SQL Injection 공격 등으로 해당 정보를 확보하고 악용하여 금전적 이익을 얻을 수 있다. 따라서 임의 정보 소유자 이외의 이용자에게 해당 정보가 노출되지 않도록 해야한다.

 

Injection은 '주입'이라는 의미이다. 인젝션 공격은, 이용자의 입력값이 애플리케이션의 처리 과정에서 구조나 문법적인 데이터로 해석되어 발생하는 취약점을 의미한다. 이용자가 입력한 악의적인 입력값이 의도하지 않은 행의를 일으키는 것이다.


2. SQL Injection

SQL은 DBMS에 데이터를 질의하는 언어이다. 그리고 DBMS에서 사용하는 질의 구문인 SQL을 삽입하는 공격을 SQL Injection이라고 한다.

 

웹 서비스는 이용자의 입력을 SQL 구문에 포함해 요청하는 경우가 있다 ( 예를 들어 로그인 할 때 ID/PW를 포함할 때 ).

아래는 로그인할 때 애플리케이션이 DBMS에 질의하는 예시 쿼리이다.

-> account 테이블의 user_id가 dreamhack이고 user_pw가 password인 데이터를 조회하고 있다.

SELECT * FROM accounts WHERE user_id='dreamhack' and user_pw='password'

 

 

쿼리문을 살펴보면, 이용자가 입력한 'dreamhack'과 'password' 문자열을 SQL 구문에 포함하는 것을 확인할 수 있다. 이렇게 이용자가 SQL 구문에 임의 문자열을 삽입하는 행위를 SQL Injection 이라고 한다. SQL Injection이 발생하면 조작된 쿼리로 인증을 우회하거나 DB의 정보를 유출할 수 있다.

 

아래는 SQL Injection으로 조작한 쿼리문의 예시이다.

->account 테이블에서 user_id가 admin인 데이터를 조회하고 있다.

SELECT * FROM accounts WHERE user_id='admin'

쿼리를 살펴보면 위와 달리 user_pw=password 부분이 사라진 것을 확인할 수 있다. 

조작한 쿼리를 통해 질의하면 DBMS는 ID가 admin인 계정의 비밀번호를 비교하지 않고, 해당 정보를 반환하기 때문에 이용자는 admin 계정으로 로그인할 수 있다.


3. Blind SQL Injection

앞에서 살펴본 SQL injection은 '인증 우회'를 통해 의도하지 않은 결과를 반환했다. SQL Injection은 인증 우회 이외에도 다른 공격 기법을 통해 임의 DB의 데이터를 알아낼 수 있다.

 

Blind SQL Injection은 스무고개 게임과 유사한 방식으로 임의 데이터를 알아낸다. DBMS가 답변 가능한 형태로 질문하면서 데이터를 알아낼 수 있는 방식이다. 아래의 예시를 통해 이해하자.

  • Question #1. sonotri 계정의 비밀번호 첫 번째 글자는 'x' 인가요?
    • Answer. 아닙니다
  • Question #2. sonotri 계정의 비밀번호 첫 번째 글자는 'p' 인가요?
    • Answer. 맞습니다 (첫 번째 글자 = p)
  • Question #3. sonotri 계정의 비밀번호 두 번째 글자는 'y' 인가요?
    • Answer. 아닙니다.
  • Question #4. sonotri 계정의 비밀번호 두 번째 글자는 'a'인가요?
    • Answer. 맞습니다. (두 번째 글자 = a)

위와 같은 형태로 DBMS가 답변 가능한 형태로 질문하면서 sonotri 계정의 비밀번호인 password를 알아낼 수 있다. 이처럼 질의 결과를 이용자가 화면에서 직접 확인하지 못할 때 True/False 반환 결과로 데이터를 획득하는 공격 기법을 Blind SQL Injection이라고 한다.

 

 

아래는 Blind SQL Injection 공격 시에 사용할 수 있는 쿼리이다.

substr 함수는 문자열에서, 지정한 위치부터 길이까지의 값을 가져온다.

//substr 함수 사용 방법
substr(string, position, length)
substr('ABCD', 1, 1) = 'A'
substr('ABCD', 2, 2) = 'BC'
# 첫 번째 글자 구하기
SELECT * FROM user_table WHERE uid='admin' and substr(upw,1,1)='a'-- ' and upw=''; # False
SELECT * FROM user_table WHERE uid='admin' and substr(upw,1,1)='b'-- ' and upw=''; # True
# 두 번째 글자 구하기
SELECT * FROM user_table WHERE uid='admin' and substr(upw,2,1)='d'-- ' and upw=''; # False
SELECT * FROM user_table WHERE uid='admin' and substr(upw,2,1)='e'-- ' and upw=''; # True

-> user_table이라는 테이블에서 uid가 admin인 것을 조회하고, substr(upw,1,1)에서 upw 가 a/b인지 확인한다. -- 뒤는 주석처리된다.

질의 결과는 로그인 성공 여부로 판단할 수 있다. 만약 로그인이 실패할 경우, 첫 번째 문자가 a가 아님을 의미한다. 이처럼 쿼리문의 반환 결과를 통해 admin 계정의 비밀번호를 획득할 수 있다.


3. SQL Injection 실습

실습 모듈에서 사용하는 user_table은 다음과 같이 구현되어있다. 실습 모듈의 목표는 쿼리 질의를 통해 admin 결과를 반환하는 것이다.

 

먼저 guest-guest를 입력해보자. 그럼 이런 쿼리가 생성된다.

-> user_table로 부터 uid가 guest이고(and) upw가 guest인 레코드(=행)의 데이터를 조회한다.

SELECT uid FROM user_table WHERE uid='guest' and upw='guest'

 

guest-1234로 쿼리문이 어떻게 동작하는지 확실히 하고 넘어갔다.

SELECT uid FROM user_table WHERE uid='guest' and upw='1234'

 

SQL Injection 공격에서 제일 중요한 점은, 이용자의 입력값이 SQL 구문으로 해석되도록 해야하는 것이다.

실습 모듈의 쿼리문의 경우 이용자의 입력값을 문자열로 나타내기 위해 '문자를 사용할 수 있다.

 

 

1) 방법 1

uid에 아래 문자열을 입력하면, admin의 결과가 반환된다. 왜 그런지 살펴보자.

admin'or'1
SELECT uid FROM user_table WHERE uid='admin'or'1' and upw=''

이 쿼리는 or을 기준으로 두 개의 조건으로 나눌 수 있다.

  • uid = 'admin'
  • '1' and upw=' '

and는 두 조건이 모두 참이여야 전체 값이 참이고, or의 경우 두 조건중 하나만 참이여도 전체 값은 참으로 간주된다. 그리고 관계연산자 and가 or보다 우선순위가 높다. 따라서 두 조건중 뒤의 조건이 먼저 계산된다. 1 and upw=' '는 T and F이기 때문에 결과값은 F가 된다. 그리고 이제 uid = 'admin' or F(결과값)은 T or F로 계산되므로 T라는 결과값이 나온다.

참고로, 당연히 or 뒤에 오는 숫자는 1이 아니어도 상관 없다. and 조건 + upw=' '에 의해 결과값은 어차피 F이기 때문이다.

+) SQL에서 숫자 1은 논리적으로 참(True)으로 간주된다. 특별한 의미가 있는건 아니고 그냥 그렇다.

 

 

2) 방법 2

 guest-guest를 입력했을 때의 쿼리문은 아래와 같다.

SELECT uid FROM user_table WHERE uid='guest' and upw='guest'

 

이 상황에서 guest 뒤의 부분이 주석처리 된다면? upw를 신경쓸 필요가 없어진다. 그렇다면 어떻게 guest 뒷 부분을 주석처리할 수 있을까?

admin'--
--반환되는 쿼리문
SELECT uid FROM user_table WHERE uid='admin'--' and upw=''

uid='admin'--' and upw='' 이 부분에서 주황색으로 표시된 부분이 우리가 주석처리를 위해 작성한 문자열이다. 검은색 부분은 쿼리문의 기본 형식이다.  '를 admin 뒤에 추가함으로서 우리는 admin과 --를 분리시켰다. 따라서 -- 앞의 'admin'까지는 해석이 되지만, --뒤의 문자열인 'and upw='''은 주석처리되어 uid=admin이 T로 계산되어 admin 계정의 쿼리 값을 얻을 수 있다.


이번에는 SQL Injection 공격을 통해 admin의 비밀번호를 출력해보자.

이 문제에서 기본 쿼리문은 아래의 형태(SELECT uid FROM user_table)로 고정되어있기 때문에, upw를 얻기 위해서는 새로운 SELECT문을 작성해야한다. 이때 사용되는 것이 UNION이다.

SELECT uid FROM user_table WHERE uid='guest' and upw='guest'

 

아래 명령어를 uid를 입력하는 칸에 넣으면 admin의 upw를 얻을 수 있다.

admin' UNION SELECT upw FROM user_table WHERE uid='admin'--
SELECT uid FROM user_table WHERE uid='admin' union select upw from user_table where uid='admin'--' and upw=''

-> admin'을 통해 UNION부터의 문장이 문자열로 해석되지 않도록 해주고

-> UNION 뒤의 SELECT 문장을 통해 user_table 테이블의 uid가 admin인 upw를 반환하도록 한다.

-> uid='를 통해 뒤의 admin이 문자열로 해석되도록 해주고, admin'--를 통해 --를 문자열로 해석되지 않게 하여 뒤의 문장들을 주석처리시켜 해석되지 않도록 한다.


4. Blind SQL Injection 공격 스크립트

추후 작성..


5. Quiz

1) 다음 SQL Query의 (A) 부분에 입력 값을 넣을 수 있다고 할 때, id가 admin인 계정으로 로그인이 가능한 SQL Injection 페이로드를 모두 고르시오

SELECT * FROM accounts WHERE user_id='admin' and user_pw='(A)'

1. '(select user_pw from account where user_id='admin')+' O

2. 'or 1=2--1

3. "or 1=1--1

4. 'or 1=1--1 O

 

 

2) 다음 중 일반적인 상황에서 SQL Injection으로 할 수 없는 행위를 고르시오 (MySQL 기준)

1. 내부망 침투하기 O

2. 임의 계정으로 로그인하기

3. 임의 계정 삭제하기

4. 데이터베이스 정보 알아내기