2017. 10. 7. 19:42ㆍWebHacking/[OverTheWire]Natas
1> 첫 페이지 화면
2> 페이지 소스코드 ( PHP 코드 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | <? function debug($msg) { /* {{{ */ if(array_key_exists("debug", $_GET)) { print "DEBUG: $msg<br>"; } } /* }}} */ function print_credentials() { /* {{{ */ if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) { # 세션 admin 키 값이 1 일때 print "You are an admin. The credentials for the next level are:<br>"; print "<pre>Username: natas21\n"; print "Password: <censored></pre>"; } else { print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas21."; } } /* }}} */ /* we don't need this */ function myopen($path, $name) { //debug("MYOPEN $path $name"); return true; } /* we don't need this */ function myclose() { //debug("MYCLOSE"); return true; } function myread($sid) { debug("MYREAD $sid"); if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) { debug("Invalid SID"); return ""; } $filename = session_save_path() . "/" . "mysess_" . $sid; if(!file_exists($filename)) { debug("Session file doesn't exist"); return ""; } debug("Reading from ". $filename); $data = file_get_contents($filename); # 파일의 내용을 문자열로 읽어들인다 $_SESSION = array(); foreach(explode("\n", $data) as $line) { # explode(기준문자열,대상문자열) : 대상 문자열을 기준 문자열로 나눈다 debug("Read [$line]"); # 줄바꿈(\n) 을 기준으로 문자열을 나누어 $line에 저장되고 # $line 배열의 2개의 원소만을 $parts 배열에 저장 $parts = explode(" ", $line, 2); # $parts변수에는 공백을 기준으로 $line변수의 2개원소를 가져온다 if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1]; } # $parts[0]='admin', $parts[1]=1 을 조작한다면? return session_encode(); } # myread($sid)함수 # $filename = /var/lib/php5/sessions//mysess_a0d98un6odgbf2f1sf4u6771n5 의 내용을 문자열로 읽어들여서 출력한다 # $filename 을 조작해서 natas21의 비밀번호를 알아내야한다 function mywrite($sid, $data) { // $data contains the serialized version of $_SESSION // but our encoding is better debug("MYWRITE $sid $data"); // make sure the sid is alnum only!! if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid)) { # $sid의 문자가 주어진 문자열에 존재하는 만큼 개수 반환 debug("Invalid SID"); # $sid변수에는 숫자,알파벳으로 구성되어야 한다 return; } $filename = session_save_path() . "/" . "mysess_" . $sid; $data = ""; debug("Saving in ". $filename); ksort($_SESSION); foreach($_SESSION as $key => $value) { debug("$key => $value"); $data .= "$key $value\n"; } file_put_contents($filename, $data); chmod($filename, 0600); } /* we don't need this */ function mydestroy($sid) { //debug("MYDESTROY $sid"); return true; } /* we don't need this */ function mygarbage($t) { //debug("MYGARBAGE $t"); return true; } session_set_save_handler( "myopen", "myclose", "myread", "mywrite", "mydestroy", "mygarbage"); session_start(); if(array_key_exists("name", $_REQUEST)) { #사용자의 입력이 있다 $_SESSION["name"] = $_REQUEST["name"]; # 세션의 name키 값에 입력값 저장 debug("Name set to " . $_REQUEST["name"]); } print_credentials(); $name = ""; if(array_key_exists("name", $_SESSION)) { $name = $_SESSION["name"]; } <form action="index.php" method="POST"> Your name: <input name="name" value="<?=$name?>"><br> <input type="submit" value="Change name" /> </form> <div id="viewsource"><a href="index-source.html">View sourcecode</a></div> </div> </body> </html> ?> | cs |
3> 문제 파악하기
이번 문제의 목적
12 if($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1) 를 만족시키는 것입니다
해당 페이지에서는 특이한 방식으로 세션변수를 초기화 시키고 있습니다
session_set_save_handler() 라는 구조체의 mywrite()함수와 myread() 함수를 이용해서 세션값을 정의하고 있습니다
[1] mywrite()
foreach 반복문을 이용해서 $data변수에 " Key Value " 형태로 데이터를 대입한다
그 다음, file에 해당 데이터들을 입력한다
[2] myread()
① mywrite()함수에서 세션파일에 데이터를 입력하고, myread()함수에서는 그 파일을 읽어들인다
② explode("구분자(\n)" , 파일명 ) : 해당 파일에서 구분자를 기준으로 각 문자들을 가져온다
즉, foreach 반복문을 통해서 세션 파일의 각 " 한 줄(\n) " 들을 $line 변수에 저장한다
③ $parts 배열변수에 $line 변수에 저장되어 있는 문자열을 " 공백 " 을 기준으로 2개 저장한다
④ $_SESSION[$parts[0]] = $parts[1] 을 실행해서 SESSION변수의 키와 값을 지정한다
4> 페이지에서 실습
[ 4-1 ] 세션 파일에 name admin 입력 & 세션 아이디를 부여 받기 전
[ 4-2 ] 세션 파일에 name admin 입력 & 세션 아이디를 부여 받음
mywrite() 함수의 디버그 출력결과를 확인해보면
80 debug("$key => $value"); 코드의 실행결과 : name=>admin 을 확인할 수 있다
① 세션파일에 " name admin \n " 해당 데이터가 입력된다
② myread()함수에서 세션 파일의 " name admin \n" 라인을 읽어들이고
53 if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1]; 을 실행한다
=> $_SESSION[name] = admin
=> ③ $name변수는 admin으로 초기화된다
=> ④ 화면의 사용자 입력창에 admin 이 써있게 된다
# 취약점은 myread()함수에서 파일의 데이터를 읽어들일 때 foreach 반복문을 사용할 때 발생한다 !
5> 입력값 변조
입력 URL : natas20....overthewire.org/index.php?name=admin%0Aadmin%201&debug |
5-1> mywrite() 동작과정
$data = name admin %0a admin 1
file_put_contents() 함수에 의해 세션파일에 $data 값이 저장된다
5-2> myread() 동작과정
foreach( explode("\n", $data) as $line ) {
debug("Read [$line]");
$parts = explode( " ", $line, 2 );
if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
}
=> $_SESSION[name] = admin
=> $_SESSION[admin] = 1
Username: natas21
Password: IFekPyrQXftziDEsUr3x21sYuahypdg
'WebHacking > [OverTheWire]Natas' 카테고리의 다른 글
[ Natas 18 -> Natas19 Session Hijacking ] (0) | 2017.09.28 |
---|---|
[ Natas 17 -> Natas18 PHP Injection ] (0) | 2017.09.28 |
[ Natas 16 -> Natas17 `Command`,"$(Command)" ] (0) | 2017.09.28 |
[ Natas 15 -> Natas 16 PHP Injection ( 비밀번호 유추 ) ] (0) | 2017.09.28 |
[ Natas 14 -> Natas 15 PHP Injection ( 쿼리 조건문 변조 ) ] (0) | 2017.09.28 |