Dreamhack - error based sql injection

·2025년 8월 29일

Dreamhack-Writeups

목록 보기
36/52

error based sql injection

문제 링크

https://dreamhack.io/wargame/challenges/412

문제 설명

Simple Error Based SQL Injection !

풀이과정

  1. 문제 이름과 설명에 Error Based 란 말이 들어가 있으니, 에러 메시지를 이용하여 해결하는 문제임을 유추해 볼 수 있습니다. 이를 염두해 보고 코드를 확인해 보았습니다.

         try:
             cur = mysql.connection.cursor()
             cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")
             return template.format(uid=uid)
         except Exception as e:
             return str(e)

    코드를 확인해 보니 에러를 사용해야 하는 이유를 찾을 수 있었습니다. cur.execute(...) 를 통해 SQL은 실행하지만, 실행 결과를 화면에 출력하는 코드를 찾을 수 없습니다. 즉, 아무리 uid에 어떤 공격을 하더라도, 결과를 출력하는 코드가 없기에 확인할 수 없는 것입니다.

    실제로 uid에 admin을 입력하였지만 아무런 반응이 없습니다.

    만약 에러가 발생했을 경우에는 Exception문으로 넘어가게 됩니다. Exception as e 코드로 에러 객체를 e라는 변수에 담고, str(e)를 통해 에러 객체를 문자열로 변환해서 출력 하는 것을 확인할 수 있습니다.
    즉, 원래라면 flask에서 에러가 났을 때 기본 에러 메시지 화면만 보여줬을 텐데, 이 코드는 일부로 에러 메세지 자체를 브라우저에 그대로 보여주도록 설계된 것입니다.

  2. 에러를 발생시킬 수 있는 함수를 사용합니다. 가장 대표적인 MySQL의 extractvalue() 함수를 사용합니다.

    extractvalue() 함수의 기본 구조 : extractvalue(xml_document, xpath_expression)

    • xml_document는 XML 형식의 문자열이여야 하며, 아니라면 오류가 발생합니다.
    • xpath_expression는 XML 문서 안에서 특정 노드를 찾는 경로를 나타냅니다.
    • 오류가 발생한다면 에러 메세지에 두 번째 인자가 그대로 노출됩니다.
  3. uid 입력 란에 sql 인젝션 공격을 시도합니다.

    ' AND extractvalue(1, concat(0x7e, (SELECT upw FROM user WHERE uid='admin'), 0x7e))--
    • xml_document자리의 1은 XML형식이 아닌 값을 대입하여 에러메세지에 두 번째 인자가 노출되도록 유도합니다.
    • concat() 함수는 MySQL에서 문자열을 이어붙이는 함수입니다.
    • 0x7e는 아스키문자로 ~ 을 뜻합니다. 에러 메세지 안에서 플래그 시작과 끝을 확실하게 구별하기 위해서 사용하였습니다. 꼭 포함되지 않아도 되지만, 플래그를 편하게 구분하기 위해서는 활용하는것이 좋습니다.
    • (SELECT upw FROM user WHERE uid='admin') 를 통해 user테이블에서 uid='admin'인 계정의 upw 컬럼 값, 즉 플래그를 가져옵니다.
    • 이걸 합쳐 문자열로 출력한다면 ~FLAG~ 형식일 것입니다.
    • -- 는 이 뒤에 나오는 코드를 주석으로 처리합니다. -- 는 뒤에 공백이 꼭 하나 있어야 작동함을 주의합니다.
  4. submit을 해 보았습니다. 플래그를 성공적으로 얻어낼 수 있을거란 생각과 다르게 플래그가 잘려서 나왔습니다

  1. 그 이유는 extractvalue()함수의 에러메세지 길이에 제한이 있기 때문입니다. 그래서 긴 문자열이 잘려서 나온 것입니다.
  2. 문자열을 잘라서 출력할 수 있도록 substring()함수를 사용합니다. substring() 함수는 문자열의 일부분을 잘라내는 함수입니다.
  3. ' AND extractvalue(1, concat(0x7e, substring((SELECT upw FROM user WHERE uid='admin'),1,20),0x7e))-- , ' AND extractvalue(1, concat(0x7e, substring((SELECT upw FROM user WHERE uid='admin'),20,50),0x7e))-- 와 같이 범위를 나누어 여러번 호출하여 플래그 값을 얻어줍니다.
  1. 나온 값들을 차례로 이어붙어주어 정상적으로 플래그값을 획득할 수 있었습니다.

배운점

  • 그 동안 풀어본 sql 인젝션 문제들과는 다르게 에러를 일부로 발생시켜 플래그를 획득하는 문제는 처음 풀어보아 이런식으로도 중요정보를 획득할 수 있음을 배웠습니다. 에러창이라고 단순히 에러만 뜬다고 생각하는게 아니라, 공격자가 이를 어떠한 방식으로 사용할지는 모르기에 에러 창 까지 보안을 철저하게 하지 않으면 안되겠다는 생각을 하였습니다.
  • extractvalue()substring()와 같이 MySQL에서 쓰이는 함수를 알게 되었습니다. 특히 extractvalue()는 에러를 유발하는 공격을 할 때에 매우 유용하게 사용될 수 있음을 배울 수 있었습니다.

Summary (English)

  • The challenge is an error-based SQL injection where query results are not shown, but error messages are directly returned using str(e).
  • To exploit this, the MySQL function extractvalue() is used to trigger an error and leak the second argument in the error message.
  • A crafted payload concatenates delimiters (0x7e = ~) with the result of a subquery fetching the admin user’s password (the flag).
  • The error message length is limited, causing the flag to be truncated.
  • To bypass this, substring() is applied to split the flag into smaller parts and retrieve it piece by piece.
  • By combining the extracted parts, the full flag can be reconstructed.
  • The exercise demonstrates how exposing raw error messages can be exploited to leak sensitive information.
profile
CTF 풀이 및 실습 중심 학습을 기록합니다.

0개의 댓글