error based sql injection | Write-Up

0xqury·2025년 5월 14일
0

WebWriteups

목록 보기
4/8

1. 문제


2. 풀이

해당 문제는 uid를 GET 파라미터로 입력받아 SQL 쿼리를 실행하는 구조였다. 주어진 코드를 보면 다음과 같이 입력값이 쿼리에 그대로 삽입되는 구조를 갖고 있다.

cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")

즉, 'uid' 값에 '나 SQL 예약어를 넣으면 쉽게 인젝션이 가능한 상태였지만, 문제는 쿼리 결과가 출력되지 않는 점이었다.

입력값은 HTML <pre> 영역에 쿼리 형태로만 노출되고, 실제 SELECT 결과는 출력되지 않기 때문에 일반적인 SQLi가 어렵다고 판단됐다.

이 상황에서 쿼리 결과를 유도하는 방법은 에러 기반(Error-Based) SQLi 중에서도 GROUP BY + RAND() + CONCAT()을 활용한 테크닉이었다.

최종적으로 사용한 payload는 다음과 같다:

' and (select 1 from (select count(*), concat((select upw from user where uid='admin'), floor(rand(0)*2)) a from information_schema.tables group by a) b) -- -

이 쿼리는 다음과 같은 구조로 동작한다:

  • floor(rand(0)*2)를 통해 0 또는 1 중 하나를 랜덤하게 생성
  • 이 값을 admin의 비밀번호와 concat하여 group key로 설정
  • MySQL은 GROUP BY 키가 중복되면 "Duplicate entry" 에러를 발생시키는데, 그 에러 메시지 안에 concat한 문자열이 들어가게 된다

즉, 랜덤 값이 중복되게 되면 에러 메시지를 유도할 수 있고, 그 안에 admin 계정의 upw 값이 포함되어 노출된다. 이 구조를 통해 페이지에 노출되는 쿼리 형태만 보고서도 에러 메시지 내에 플래그 문자열 DH{...}를 식별할 수 있었다.


3. 정리 및 느낀점

이번 문제는 일반적인 SQL Injection보다 한 단계 더 나아가, 에러를 유도하는 방식으로 정보를 추출해야 하는 고전적인 Error-Based SQLi 문제였다. 단순히 ' or 1=1 -- - 같은 payload가 통하지 않고, 결과가 직접 출력되지도 않기 때문에 문제를 이해하고 구조를 분석해야만 해결할 수 있었다.

특히 group by concat(..., floor(rand(0)*2))라는 익숙하지 않은 구조가 처음에는 어려웠지만, 이 테크닉이 MySQL의 에러 메시지를 활용해 데이터를 노출시키는 방식이라는 걸 이해한 뒤로는 구조가 명확하게 보였다.

결국 중요한 건,

  • 쿼리 결과가 출력되지 않을 때 어떻게 우회할 것인가
  • 에러를 통해 정보를 유출하는 고전적인 테크닉들을 알고 있느냐
  • 그 에러가 화면 어디에 반영될 수 있는지를 판단하는 능력

이 문제를 통해 SQLi 중에서도 에러 기반 기법에 대해 한층 더 깊이 익숙해질 수 있었다.
실전이나 CTF에서 꼭 다시 만나게 될 유형이라 자주 연습해둘 필요가 있을 듯하다.


profile
지망생

0개의 댓글