Natas Level 20 풀이
로그인
Username: natas20
URL: http://natas20.natas.labs.overthewire.org
admin으로 로그인해야 한다.
소스코드를 보면,
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
<?php
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) {
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) {
debug("Read [$line]");
$parts = explode(" ", $line, 2);
if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
}
return session_encode() ?: "";
}
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)) {
debug("Invalid 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);
return true;
}
/* 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"];
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>
풀이
print_credentials()는 $_SESSION에서 admin 키가 존재하고 그 값이 1일 때 패스워드를 출력한다.
따라서 세션의 키-값 쌍으로 admin,1이 되도록 하면 될 것이다.
전체적으로 세션데이터를 수동으로 관리하는 코드인데
session_set_save_handler()로 세션 기능을 사용자 정의 함수들( myopen(), myclose(), myread(), mywrite(), mydestroy(), mygarbage() )로 설정하고 있다.
이 중 myread()와 mywrite()를 보면
myread()에서는 세션 파일에 추가된 키-값 쌍이 검증없이 그대로 $_SESSION에 로드되고,
mywrite()에서는 $_SESSION 데이터가 제대로된 검증없이 세션파일에 저장되고 있다.
burpsuite를 켜서, admin으로 이름 변경해보면 아무일도 일어나지 않는다. 
name을 바꾸는 요청을 인터셉트해서, 세션 파일에 admin 1 키-값이 추가되도록 name 파라미터에 admin 1을 url인코딩한 값으로 바꿔 보내자.

한 번 더 인터셉트해서 name 파라미터 값을 똑같이 %0Aadmin 1 로 변경해 보내면 된다.
첫번째로 포워드했을 때 세션 파일에 admin 1 키-값 쌍 추가해서 저장되고,
두번째로 포워드하면 세션 파일에 저장된 admin 1 키-값 쌍을 세션데이터로 로드해서 패스워드가 출력된다.

자세하게 쓴 write-up은 https://00yj.tistory.com/257~
