지원서 뜯어보기 [하] dreamhack simple_sqli_chatgpt
들어가며
2025년 32기 지원서는 상중하 세 문제로 이루어져 있었는데, 그 중 하 문제인 Dreamhack의 simple_sqli_chatgpt의 문제에 대한 라이트업을 써보려 한다.
뭐 인터넷에 이미 나와있는 라이트업이 많긴 하지만 지원서 문제를 낸 입장에서도 그렇고, 기술블로그에 올리기도 하고 싶어서 써보려 한다.
SQL Injection이란?
우선 본격적으로 라이트업을 쓰기 전에 SQL Injection이 뭔지에 대해 알아볼 것이다.
문제 이름이 simple sqli chatgpt인 만큼, sqli에 대해 알고 넘어가는 게 중요하다.
**SQL Injection(SQLi)**은 웹 애플리케이션에서 사용자 입력을 적절히 검증하지 않고 SQL 쿼리에 포함시킬 때 발생하는 보안 취약점이다.
공격자는 이를 이용해 데이터베이스의 정보를 조회, 수정, 삭제하거나 인증을 우회할 수 있다.
예를 들어, 다음과 같은 쿼리가 있다고 가정하겠다.
1 | SELECT * FROM users WHERE username = 'rlozll' AND password = 'swing31'; |
만약 사용자가 username에 ‘ OR ‘1’=’1을 입력하면, 쿼리는 다음과 같이 변형된다.
1 | SELECT * FROM users WHERE username = '' OR '1'='1' AND password = 'swing31'; |
이렇게 되면 1=1이라는 조건이 항상 참이지 인증을 우회할 수 있게 된다.
또다른 예시를 들어보겠다. 아래와 같은 코드가 있다고 가정하겠다.
1 | username = request.form['username] |
이때 사용자가 username에 admin’–을 입력하면 쿼리는 다음과 같이 된다.
1 | SELECT * FROM users WHERE username='admin' --' |
– 이후는 주석 처리되기 때문에 읽어내지 못한다.
따라서 조건이 조작되어 인증 우회나 정보 탈취가 가능하게 된다.
문제 개요: simple_sqli_chatgpt
문제 링크: https://dreamhack.io/wargame/challenges/769
이 문제는 로그인 페이지에서 사용자 입력을 통해 SQL Injection을 수행하여 플래그를 획득하는 것이 목표다.
로그인 페이지에는 userlevel이라는 단일 입력 필드를 제공하고 있으며, 이를 기반으로 사용자를 인증한다.
소스 코드 분석
문제에서 제공하는 코드 중 주요 부분만 추출해 보도록 하겠다.
1. 데이터베이스 생성
1 | db.execute('create table users(userid char(100), userpassword char(100), userlevel integer);') |
- userid: guest/admin
- userpassword: guest/랜덤값
- 모두 userlevel = 0으로 설정되어 있음
2. 쿼리 수행
1 | userlevel = request.form.get('userlevel') |
- userlevel 값을 사용자 입력에서 가져와 직접 쿼리에 삽입
- 파라미터 바인딩을 사용하지 않고, 문자열 포매팅을 사용 -> SQL Injection 가능
3. 조건 판단
1 | if res: |
- 조건을 만족하면 플래그 노출
- 즉, userlevel=0 이면서 userid=’admin’인 경우 플래그가 출력됨
핵심 취약점
1 | res = query_db(f"select * from users where userlevel='{userlevel}'") |
이 부분에서 userlevel에 아무런 필터링 없이 쿼리에 삽입된다.
이로 인해 공격자는 다음과 같이 조작할 수 있게 된다.
SQL Injection 시나리오
목표: userlvel 값 하나만 입력해서, userid가 ‘admin’인 행을 선택해야 함
공격 입력
다음 값을 userlevel에 입력하도록 하겠다.
1 | 0' and userid='admin' -- |
실제 쿼리 동작 방식
입력값을 반영한 최종 쿼리는 다음과 같다.
1 | SELECT * FROM users WHERE userlevel='0' and userid='admin' --' |
- userlevel=’0’ 조건 만족
- userid=’admin’ 조건 만족
- – 뒤는 주석 처리 -> 쿼리 구문 오류 방지 (읽지 못하도록)
공격 수행
이렇게 다음과 같은 플래그를 얻을 수 있었다.
Flag: DH{chatGPT_told_me_a_lullaby}
대응 방안
문제는 다 풀었지만, 대응 방안도 간단히 알아보도록 하겠다.
파라미터 바인딩 사용
1 | db.execute("SELECT * FROM users WHERE userlevel=?", (userlevel,)) |
입력 검증
- 숫자 입력값만 허용: int(userlevel)
- 화이트리스트 기반 검증
최소 권한 DB 사용자 설정
- 데이터 조회 권한만 있는 사용자 사용
- 시스템 테이블 접근 제한
마치며
드림핵의 simple_sqli_chatgpt 문제는 SQL Injection의 핵심과 위험성을 간단히 보여주는 예제다.
본인은 해당 문제를 1학년 때 처음 풀어봤는데, 꽤 재밌게 풀었던 기억이 난다.
그렇게 풋풋했는데 지금은 어느덧 사망년이다.
다음 기술블로그는 어떤 걸 써볼지 고민해봐야지.
출처
SQL Injection. (n.d.). OWASP. https://owasp.org/www-community/attacks/SQL_Injection
simple_sqli_chatgpt. (n.d.). Dreamhack. https://dreamhack.io/wargame/challenges/769