웹해킹 워게임 1레벨을 풀다가.. 웹해킹 advanced는 학습을 안해서 이부분만 먼저 학습 깔짝.. 해보기..ㅎㅎ
먼저 Error based SQL Injection은 임의로 에러를 발생시켜 데이터베이스 및 운영 체제의 정보를 획득하는 공격기법이다.
다음은 해당 기법을 실습하기 위한 에제 코드로, Flask 프레임워크를 사용한다.
## pip3 install PyMySQL //pymysql 라이브러리를 설치하기 위한 명령어
from flask import Flask, request
import pymysql
app = Flask(__name__)
def getConnection():
return pymysql.connect(host='localhost', user='dream', password='hack', db='dreamhack', charset='utf8')
@app.route('/' , methods=['GET'])
def index():
username = request.args.get('username')
sql = "select username from users where username='%s'" %username
conn = getConnection()
curs = conn.cursor(pymysql.cursors.DictCursor)
curs.execute(sql)
rows = curs.fetchall()
conn.close()
if(rows):
return "True"
else:
return "False"
app.run(host='0.0.0.0', port=8000, debug=True)
코드를 살펴보면 디버그 모드가 활성화되어 있는 것(debug=True)을 확인할 수 있으며 이용자 입력값이 별다른 검사 없이 SQL 쿼리에 포함되어 SQL Injection 취약점이 발생한다. 그러나 게시물, 알림과 같이 SQL 실행 결과를 출력하는 코드가 존재하지 않고 쿼리 실행 결과만을 판단한다.
Error based SQL Injection 공격 방법
Flask 프레임워크로 개발된 애플리케이션에서 디버스 모드를 활성화하면 코드에서 오류가 발생할 때 발생 원인을 출력한다.
공격자는 오류 메시지를 통해 공격에 필요한 다양한 정보를 수집하고, 원하는 데이터를 획득할 수 있다.
아래는 admin 1' 쿼리를 삽입했을 때 에러 메시지가 발생한 모습이다.

에러 메시지를 살펴보면 이는 admin 1' 쿼리를 입력하면서 문법 에러(Syntax Error)가 발생하였다는 메시지이다. 에러 메시지와 같이 쿼리 실행 결과를 직접 노출하지는 않는다. 그러나 중요한 정보를 노출하는 방법들이 존재한다.
Error based SQL Injection 공격 코드 작성
애플리케이션에서 발생하는 에러를 이용해 공격하려 한다면 문법 에러와 같이 DBMS에서 쿼리가 실행되기 전에 발생하는 에러가 아닌 런타임(Runtime) 즉, 쿼리가 실행되고나서 발생하는 에러가 필요하다.
아래는 MySQL 환경에서 해당 기법으로 공격할 때 많이 사용하는 쿼리와 실행 결과이다.
SELECT extractvalue(1,concat(0x3a,version()));
/*
ERROR 1105 (HY000): XPATH syntax error: ':5.7.29-0ubuntu0.16.04.1-log'
*/
결과를 살펴보면, 에러 메시지에 운영 체제에 대한 정보가 포함되어 있는 것을 확인할 수 있다. 공격자는 해당 정보를 통해서 1-day 또는 0-day 공격을 통해 서버를 장악할 수 있다. 해당 쿼리를 이해하려면 extractvalue 함수에 대해 알고 있어야 한다.
extractvalue 함수는 첫 번째 인자로 전달된 XML 데이터에서 두 번째 인자인 XPATH 식을 통해 데이터를 추출한다. 두 번째 인자가 올바르지 않은 XPATH식일 경우 올바르지 않은 XPATH 식이라는 에러 메시지와 함께 잘못된 식을 출력한다. 아래 두 결과는 각각 올바른 사용 예시와 올바르지 않은 예시이다.
mysql> SELECT extractvalue('<a>test</a> <b>abcd</b>', '/a');
+-----------------------------------------------+
| extractvalue('<a>test</a> <b>abcd</b>', '/a') |
+-----------------------------------------------+
| test |
+-----------------------------------------------+
1 row in set (0.00 sec)
extractvalue 올바른 예시