문제 분류
난이도 : 하하
1. 사전 탐색&정찰 (reconnaissance)
사이트에 접속해보면 Login이 보입니다.
그리고 코드를 함께 살펴봅시다.
#!/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));')
db.execute(f'insert into users(userid, userpassword) values ("guest", "guest"), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}");')
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:
userid = request.form.get('userid')
userpassword = request.form.get('userpassword')
res = query_db(f'select * from users where userid="{userid}" and userpassword="{userpassword}"')
if res:
userid = res[0]
if userid == 'admin':
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)
/(index) /login 두 탭이 존재하는 걸 알 수 있고
guest계정과 admin 계정 두가지가 생성되어 있음을 알 수 있습니다.
2. 스캐닝 및 취약점 분석(Scanning and Enumeration)
위에 공개되어 있는 코드 중 우리가 주목해야 할 코드만 간추려서 살펴봅시다.
#query 문을 통해 계정 두개가 생성되어있음
DATABASE = "database.db"
if os.path.exists(DATABASE) == False:
db = sqlite3.connect(DATABASE)
db.execute('create table users(userid char(100), userpassword char(100));')
db.execute(f'insert into users(userid, userpassword) values ("guest", "guest"), ("admin", "{binascii.hexlify(os.urandom(16)).decode("utf8")}");')
db.commit()
db.close()
해당 코드를 통해 db에 query문이 삽입되어 계정이 생성되었음을 알 수 있고
admin계정의 암호는 dict 형태로 16진수 난수 형태로 생성됨을 알 수 있습니다.(binascii.hexlify : binary 데이터 16진수 표현법)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
userid = request.form.get('userid')
userpassword = request.form.get('userpassword')
res = query_db(f'select * from users where userid="{userid}" and userpassword="{userpassword}"')
if res:
userid = res[0]
if userid == 'admin':
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>'
또한 login 탭의 코드를 살펴보면
res라는 값이 userid와 userpassword값이 담겨 반환되는 걸 알 수 있습니다.
즉 admin 계정으로 로그인해야 하므로 이는 기본적인 Injection을 통해 관리자(admin) 계정을 탈취하거나
조회하는 방식이 아닌 단순히 로그인만 하면 되는 문제입니다.
거기에 따로 인증 우회를 해야하도록 injection 방지 코드나 sql 구문 필터링이 없기 때문에
아주 간단한 방식의 sql injection을 통해 로그인 우회를 할 수 있습니다.
3. 침투 (Gaining Access)
userid와 password를 입력해야 합니다.
먼저 guest계정으로 로그인 해봅시다.
guest로의 로그인은 잘 됩니다.
다음은 관리자인 admin계정에 로그인해봅시다.
일반적으로 로그인을 시도하면 로그인이 되질 않습니다.
admin계정의 암호는 난수 값(random)이므로 서비스 이용자인 우리는 알 수 없습니다.
당연하게도 틀린 값이라고 나옵니다.
자 그러면 이제 admin계정으로 로그인하는 코드를 짜 봅시다.
제 다른 글들에도 나오듯 SQL Injection의 기본 원리는 코드를 참값으로 만드는 데 있습니다.
//SQL의 원리
‘1 'or' 1 '=' 1
1 ‘or’ 1 ‘like’ 1
//양쪽이 참값이 되는 코드를 query문에 붙인다.
SELECT * FROM users WHERE userid="admin"-- " AND userpassword="---"
//userid 가 admin인 계정을 우리는 알고있다.
//--(주석) 을 통해 뒤에 userpassword 조회 쿼리는 주석처리 한다.
//userid 조회 값은 뒤의 AND 구문이 사라지므로 (1) 참값이 된다.
두 번째 과정인 취약점 분석 항목에서 res에 담기는 쿼리문을 살펴보았을 때
- 'select * from users where userid="{userid}" and userpassword="{userpassword}"'
위와 같은 쿼리문이 결과로 반환되는 걸 알 수 있는데
우리는 이미 admin계정의 userid 값이 admin인걸 알고 있습니다.
이 또한 취약점 분석 탭에서 admin계정이 생성되어 있는 걸 확인했습니다.
그러므로 SQL 문의 주석 처리 문법인 -- 을 통해 ID 검색 조건만을 처리하도록 두고
뒤의 userpassword 조회 부분은 주석처리해버리면 참값이 됩니다.
4. 권한상승 및 탈취 (Privilege Escalation)
userid에 admin계정 그리고 "-- 주석처리 문을 넣어줍니다.
이렇게 되면 res에 담기는 쿼리문은 아래와 같습니다.
SELECT * FROM users WHERE userid="admin"-- " AND userpassword="??"
이러면 query문은 참값으로 판정됩니다.
admin계정으로 로그인되어 flag가 출력됩니다.
기본 숙지 개념 : flask, SQL Injection, SQL 구문(주석, 조회문), python3
작업환경 : VIsual Studio Code, Web
버전 : 3.10.0
사용언어 : Python_flask, Python, SQL
작업도구 : -
추천 자료 :
'정보보안 > Web Hacking' 카테고리의 다른 글
파일 다운로드 취약점에서 자주 쓰이는 Path Traversal 취약점 정리 - filedownload, path traveral (0) | 2022.08.25 |
---|---|
드림핵 문제풀이 - Carve party // 웹 해킹 기초, JavaScript 문법기초, JavaScript 함수 (0) | 2022.08.02 |
드림핵 문제풀이 - CSRF-2 // CSRF, Web hacking, 세션 (0) | 2022.04.06 |
드림핵 문제풀이 - XSS-2 // XSS, Cross Site Scripting, 우회, 인증우회 (0) | 2022.03.28 |
드림핵 문제풀이 - Session Basic // Cookie, Session, 인증, Web hacking (0) | 2022.03.21 |
댓글