>1. SQL injection이란?
: Web App에서의 정상적인 값이 아닌 RDMS에서 사용되는 구조화된 질의 언어인 SQL을 주입하는 공격
ex) /endpoint?idx=100이 아니라 /endpoint?idx={sql}
위험도, 파급력, OWASP 1순위로 계속 1순위로 분류될만큼 영향력 있는 공격
2. 취약점 발생 원인
공격 원리
공격자는 취약한 웹앱을 대상으로 sql 구문 주입
→ 미완성된 sql 구문이 사용자 입력 값을 통해 완성, 검증 없이 조합될때 취약점 발생
→ 이 완성된, 변조된 sql 구문이 데베에 질의 요청
그게 왜 가능한가?
WAS와 DB는 신뢰 관계이기 때문에 변조된 질의가 오더라도 대답을 해줌
= 공격자 악의적인 질의를 해도 이를 반환함
결국엔 입력값 검증 미흡으로 인해 공격 구문과 sql 구문이 조합되어 취약점 발생
3. 취약점 유무 판별 방법
1. 사용자 입력값이 숫자형
산술 연산자를 통해 판별 가능
입력값에 계산식을 보내면 sql에서 산술 연산을 함
이때 산술연산이 되면 취약하다고 판단 가능
case-when 구문을 통해서도 판단 가능
2. 문자형일 경우
연결 연산자를 통해 판별 가능
- te' 'st로 하면 두개를 붙임 그래서 te' 'st와 te'te'st를 검색해보면 mysql인지 아닌지 확인 가능
- test%' and '%'='
3. 인젝션 유무 확인 방법
' and 1=1#와 ' and 1=2#를 검색했을 때 결과가 다르게 나오는지 확인
4. sql인젝션 공격 종류
1. 인증 우회
: 인증 기능을 수행하는 앱(ex. 로그인)에 대해 정상(id/pw)적인 인증 값이 아닌 비정상(sql) 구문을 삽입을 통해 인증 우회
1) 목표
id/pw 없이 로그인
(DB: 로그인 성공시 1개의 레코드, 실패시 0개의 레코드를 반환 / 서버: 세션이나 쿠키 발급)
2) 방법
id/pw 입력창에 질의를 강제적으로 참으로 만드는 sql 구문을 삽입한다.
3) 공격 원리
pw의 경우 보통 해시함수로 암호화되어 DB에 저장하기 때문에 인젝션이 발생하지 않는다.(DB 쿼리를 조작할 수 없으므로)
== id 단에서만 인젝션 발생
select * from member where id = '{input}';
라는 구문에서 무조건 참을 만드는 입력값을 넣어 인증 우회
4) 종류
1. Terminating Query
: 앞에 참인 구문을 놓고 뒤에 주석을 둠으로써 뒤를 잘라버려 인증을 우회
입력값이 어떤 부분에 삽입되는지 모르기 때문에 모든 것을 이 방법으로 처리할 순 없음
select * from members where id = '' or 1=1 -- ...
or 연산자를 기준으로 id=''라는 피연산자와 1=1이라는 피연산자가 있는데 1=1이 참이므로 id=''가 거짓이어도 참이 됨
2. Inline Query
: 뒤를 살리고 논리 연산자를 통해 참을 만들어 인증을 우회
select * from members where id = '' or 1=1 and '1'='1' ...
sql 연산자 우선순위에 의해 and가 or 보다 먼저 처리가 됨. 1=1 and '1'='1'가 T이므로 or 바로 패스
5) dbms 별 주석
- Oracle, MSSQL : --
- MySQL : #, --(' -- '의 경우 --를 활용하려면 앞뒤로 공백을 줘야함)
6) SQL 논리 연산자 우선순위
NOT > AND > OR
* 여러 레코드가 반환되면 레코드 최상위에 있는 사용자로 로그인이 이뤄진다.(그게 먼저 소스코드로 들어가기 때문에)
2. 데이터 조회
: sql 구문 삽입을 통해 db안 데이터 조회
1) 목표
중요 데이터 판매, 계정 정보 등으로 타 사이트 로그인 시도 및 취약점 탐색
2) 데이터 조회 공격 기법
- 속도: union>error>>>blind
1. error-based(Inband)
: DBMS 에러를 통해 공격자가 의도한 데이터(버전 정보, 특정 컬럼/레코드 등) 유출
무조건 DBMS 에러가 발생해야 함(어플리케이션 에러가 발생한다고 에러 베이스가 가능한 것이 아님)
디버깅 용도로 에러를 반환하거나 그것을 주석 등으로 그대로 뒀을 때 공격 발생 가능
성립 조건
DBMS → WAS → Client의 흐름을 가져야 함
DBMS 에러 내에 공격자가 의도한 데이터를 실어야 하므로 WAS → Client만 있어서는 안됨
2. blind-based
: DBMS 에러가 발생되지 않는 환경에서 하나씩 대입하며 추론하는 방법
- 가장 빈번하게 발생하는 공격
- 속도가 느리고 구문이 길지만 가장 많이 사용된다.
- 1byte를 추론하기 위해 7번의 요청 필요(8bits인데 맨 앞은 보통 0이니까)
- 공격에 대한 응답으로 데이터가 반환되지는 않지만 응답으로 추론 필요
추론 기법
먼저 length를 통해 길이를 알아본다.
1) 순차 탐색
: 하나씩 올라가며 순차적으로 질의
select id from members where id='test' and substring(system_user(),N,1) = 'a', ... 'z';
맞을 경우에 결과가 출력된다.
2) 이진 탐색
: 중간부터 대소를 비교하며 판단(ascii 문자 활용)
select id from members where id='test' and ascii(substring(system_user(),N,1) > 80;
보통 32~126 사이(특수문자, 숫자, 알파벳)
이때 처음으로 F가 나오는 아스키문자가 해당 문자(이를 select char()로 확인)
3) 비트 단위 탐색
: 추론하고 싶은 데이터를 2진수로 표현하여 00000001부터 and 비트 연산 시도
- 참일 때 있고, 거짓일 때 없는 기준 문자로 (날짜 같은 것)
select id from members where id='test' and ascii(substring(system_user(),N,1)) & 2^M = 2^M;
결과의 T/F로 0인지 1인지 유추 가능
예시
admin 계정의 주민번호를 출력해보자! (테이블: customer_info, 컬럼: jumin 획득 가정)
컬럼 하나와 limit을 이용해 순차적으로 레코드를 출력해야 에러를 발생하지 않음
... %20and%20substring((select+jumin+from+customer_info+where+id='admin'),N,1))=M;
burp suite의 intruder를 이용해 자동 공격을 할 수 있는데 데모버전이라고 못 쓰게 함..
3. union-based(inband)
: UNION이라는 구문을 통해 내가 원하는 SQL문의 답이 출력되도록 하는 공격
- 두 번째로 빈번한 공격
- 공격의 특성상 속도가 굉장히 빠름
- 출력돼야할 건 안 되고 내가 원하는 테이블로만 조회하도록 함
- 게시판 목록이 출력되는 검색 기능에 많이 쓰임(목록은 특정 테이블에 대해 다수의 레코드가 반환되기 때문)
0) UNION
: 여러 쿼리문들을 합쳐서 하나의 쿼리문으로 만들어주는 방법
- 중복 제거(중복 제거를 하지 않는 UNION ALL보다 느림)
- 사용 형태
1. 컬럼명 동일(as로 맞춰주는 것 가능)
2. 컬럼별 데이터 타입 동일
3. 출력할 컬럼 개수 동일
- JOIN과의 차이점
조인은 새로운 열을 생성하지만 UNION은 새로운 행으로 합쳐짐
1) 권한 확인
select file_priv from mysql.user where user='root'; 라는 구문이 있을 때
' and 1=2 union select null,file_priv,null,...,null from mysql.user where user='root'#
' and 1=2 union select null, load_file('/etc/passwd'), null, ..., null#
2) 절차
1. order by 구문을 통해 컬럼 개수 식별(order by가 사용 가능하다 = union 사용이 가능하다)
' order by {숫자}#를 계속 검색해보면서 컬럼 개수 파악
2. union 구문 사용 가능 여부 파악
: 1번에서 파악한 개수만큼 null을 입력하여 union 구문이 사용 가능한지 확인
' union select null, ..., null#
3. 출력 포지션 파악
: 문자열을 옮겨보며 어떤 위치에서 출력이 되는지 파악한다.
' union select 'test', ..., null#
위의 구문은 게시글이 너무 많으면 뒤로 밀림
∴ 유연한 공격을 위해 앞의 select 절을 거짓으로 만들어 레코드 출력 방지 필요
4. Out-Of-Band(OOB)
: 대역 외 공격, 요청은 가는데 dbms가 다른 행위(해커 ip로 http/dns/db connection.... 이해 안됨)
DBMS 자체의 특정 함수나 패키지를 써야하는데 오늘날은 막아둬서 사용하기 까다로움
3) 데이터 조회 공격 순서(모든 데이터 조회 공격 기법 동일)
1. 기본 정보(버전: version(), 사용자: system_user(), 현재 DB명: database()) 목록화
2. 메타 데이터 목록화(데이터 사전에 있다. 데이터 사전?)
MySQL: information_schema.schemata, information_schema.tables, information_schema.columns
MsSQL: master.sys.databases, [db].sys.objects, [db].sys.columns
Oracle: all_tables, all_tab_columns
* 데이터베이스 목록화
' and 1=2 union select null, schema_name, null, ..., null from information_schema.schemata#
* 테이블 목록화
' and 1=2 union select null, table_name, null, ..., null from information_schema.tables where table_schema='pentest'#
* 컬럼 목록화
- 테이블: customer_info
' and 1=2 union select null, coulumn_name, null, ..., null from information_schema.columns where table_schema='pentest' and table_name='customer_info'#
3. 데이터 목록화(2가 되어야 가능함)
' and 1=2 union select id, password, null, null, jumin#, null, null, null, null from customer_info#
3. 시스템 명령어 실행
환경적으로 조건이 까다로워 크리티컬하지만 거의 이뤄지지 않는다
4. 데이터 조작
insert, update, delete 관련
진단할때 건들지 않는다 잘못하면 큰일
5. 대응방안
1) 대분류
1. 인젝션에 대한 방어
2. 파일 권한 제거
2) 대응 방안 개요
방화벽을 막는다 하더라도 WAS가 취약하면 의미가 없음 ∴ 시큐어 코딩이 중요
3) prepared statement 사용
1. SQL 구문 실행 단계
1) 구문 분석 및 정규화
구문 검사 및 구문의 의미 검사(from 테이블, where 조건 칼럼 존재 여부)
2) 컴파일
구문을 쪼개서 컴퓨터가 이해할 수 있도록 변환
3) 쿼리 최적화
쿼리를 실행할 방법을 찾기 위한 의사결정트리(쿼리 실행의 방법 수, 방법, 비용 등) → 최상의 방법 찾기
ex) 1=2 and 1=1 일 때, 앞이 F인데 굳이 and 뒤까지 볼 필요 없음
4) 캐시
저장해두고 동일한 쿼리가 들어올 때마다 1~3을 건너뛰고 실행
5) 실행
제공된 퀴리 실행 및 결과 반환
2. 안전한 이유
select * from members where id=?;
위와 같이 쿼리가 완성되지 않은 상태에서 질의하고 ? 부분은 나중에 사용자 입력값으로 치환될 수 있도록 placeholder가 됨
→ ?를 set{type}를 통해서 채운다
이때 ?가 비어있는 상태로 질의가 되어 있는 상태이기 때문에 SQL문이 들어간다고 해도 공격이 수행되지 않음
* prepared 사용 전에 사용자 입력값을 바인딩하면 안전하지 않으므로 주의
* 순수한 사용자 입력값에만 set 사용 가능
3. 프레임워크
- iBatis, myBatis : '%#{입력값}%'
- hibernate : :{입력값}
4) 사용자 입력값 타입에 따른 입력값 검증 로직 구현
- 정규식 사용으로 입력 제한
- 발생 가능한 포지션: 숫자, 문자, 테이블/컬럼, 키워드
1. 숫자 - 정규식 제한
Java - Pattern.matches()
PHP - !is_numeric()
2. 문자열
Java - MsSQL/ORACLE : ' 은 '' 로, \"은 \"\"로 치환
MySQL : ' 은 \' 로, \ 은 \\로 치환
PHP - real_escape_string()
3. 테이블/컬럼 - 정규식 제한(화이트리스트 방식)
4. 키워드
equals asc/desc로 검증
5) 길이 제한
- 불필요하게 긴 길이 제한
- SQL Injection은 길이에 민감한데 공격구문이 굉장히 길기 때문에 길이 제한이 있으면 공격이 어려움
Java - length()
PHP - strlen()
6. 기타
1) 생각
- 서버 흐름을 예상해 보는 것이 도움이 되는 것 같다
- 특수문자를 막는 것만으로도 Injection 막는 데에 큰 도움될듯.
2) 큰 흐름
어떤 입력을 버프 스위트로 가로채서 그걸 통해 SQL문을 유추한 뒤 논리 연산자를 잘 판단해서 공격문을 정함
3) 연결 연산자
- MySQL: te' 'st
- Oracle: te'||'st
- MsSQL: te'+'st
23:11
'Security > 취약점분석' 카테고리의 다른 글
| [BackDoor] 섹션5. XSS (0) | 2024.05.23 |
|---|---|
| [BackDoor] 섹션4. XXE Injection (0) | 2024.05.23 |
| [BackDoor] 섹션3. OS Command Injection (0) | 2024.05.23 |
| [BackDoor] 섹션 0. 프롤로그 & 섹션 1. 웹 해킹에 대한 이해 (0) | 2024.04.04 |
| [BackDoor] 취약점 분석/aws 1주차 (0) | 2024.04.01 |