SQL 인젝션 문제 풀어보기

Hae_To·2025년 5월 1일

CTF 1 : simple_sqli_chatgpt

  1. 문제 확인
    a. 워게임 사이트의 VM 실행을 통해 문제의 웹 페이지 접속
    b. 단순한 로그인 페이지로 구성된 것을 확인

  2. 문제 풀이
    a. 크롬 브라우저의 페이지 소스 보기를 통해 서버 코드의 파악 시도

<body>
    <!-- Fixed navbar -->
    <nav class="navbar navbar-default navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <a class="navbar-brand" href="/">Simple SQLi</a>
        </div>
        <div id="navbar">
          <ul class="nav navbar-nav">
            <li><a href="/">Home</a></li>
            <li><a href="#about">About</a></li>
            <li><a href="#contact">Contact</a></li>
          </ul>

          <ul class="nav navbar-nav navbar-right">
            <li><a href="/login">Login</a></li>
          </ul>

        </div><!--/.nav-collapse -->
      </div>
    </nav>

    <div class="container">
      
<h1>Login</h1><br/>
<form method="POST">
  <div class="form-group">
    <label for="InputId">userlevel</label>
    <input type="text" class="form-control" id="InputLevel" placeholder="userlevel" name="userlevel" required>
  </div>
  <button type="submit" class="btn btn-default">Login</button>
</form>

    </div> <!-- /container -->

    <!-- Bootstrap core JavaScript -->
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script> 
</body>

브라우저의 소스코드 보기 기능으로는 서버 코드를 확인이 불가함. 서버사이드렌더링(SSR) 방식으로 파악

b. 가장 기본적인 논리적 조건을 인증 우회 시도 (했으나 실패)

'or'1'='1'--

c. 제공된 서버 코드를 통한 분석 시도

# app.py
#!/usr/bin/python3
from flask import Flask, request, render_template, g
import sqlite3
import os
import binascii

app = Flask(__name__)
app.secret_key = os.urandom(32)

try:
    FLAG = open('./flag.txt', 'r').read()
except:
    FLAG = '[**FLAG**]'

DATABASE = "database.db"
if os.path.exists(DATABASE) == False:
    db = sqlite3.connect(DATABASE)
    db.execute('create table users(userid char(100), userpassword char(100), userlevel integer);')
    db.execute(f'insert into users(userid, userpassword, userlevel) values ("guest", "guest", 0), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}", 0);')
    db.commit()
    db.close()

def get_db():
    db = getattr(g, '_database', None)
    if db is None:
        db = g._database = sqlite3.connect(DATABASE)
    db.row_factory = sqlite3.Row
    return db

def query_db(query, one=True):
    cur = get_db().execute(query)
    rv = cur.fetchall()
    cur.close()
    return (rv[0] if rv else None) if one else rv

@app.teardown_appcontext
def close_connection(exception):
    db = getattr(g, '_database', None)
    if db is not None:
        db.close()

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        userlevel = request.form.get('userlevel')
        res = query_db(f"select * from users where userlevel='{userlevel}'")
        if res:
            userid = res[0]
            userlevel = res[2]
            print(userid, userlevel)
            if userid == 'admin' and userlevel == 0:
                return f'hello {userid} flag is {FLAG}'
            return f'<script>alert("hello {userid}");history.go(-1);</script>'
        return '<script>alert("wrong");history.go(-1);</script>'

app.run(host='0.0.0.0', port=8000)
  • 해당 서버 코드에서 userlevel이 0일 경우 admin 계정으로 접속이 되어 flag가 적혀져 있는 파일을 읽어 출력하는 것을 파악하였다.
  • 다른 유형의 인젝션 공격 방식으로 시도
  1. Union SQL 인젝션 공격 시도
    • SQL 쿼리를 조작하여 다른 데이터베이스를 접속해 Flag값을 확보 하기 위해 (userlevel)
0' and userid='admin

이 입력은 다음과 같은 SQL 쿼리로 변환된다.

select * from users where userlevel='0' and userid='admin'

💡 SQL Injection 분석
‘userlevel’ 필드에 ‘0’ and userid=’admin’을 입력했기 때문에, 이 쿼리는 ‘userlevel’이 ‘0’이고 ‘userid’가 ‘admin’인 레코드를 찾으려 한다. 이를 통해 ‘admin’ 사용자의 정보를 직접 조회하려는 시도를 한 것이다.

  1. 정답 및 결론

    위와 같은 방식으로 쿼리를 조작하면 ‘admin’ 사용자의 정보를 성공적으로 가져오고, 그 결과로 플래그를 획득할 수 있다.

profile
진인사대천명

0개의 댓글