是该正视自己的过去了,关于自己在 Syclover 招新的第二次面试报告,也该让它作为自己博客文章的一员了。

任务一

0x01 搭建环境

首先抛出一个坑:在网站目录的配置文件里面添加的预加载文件选项是会报服务器内部错误,需要扔到 php.ini 文件里面。这是因为服务器使用的是 CGI 模式运行而不是 Apache 模式运行,在 CGI 模式下,php_value 无法被识别,会导致服务器500错误。

剩下的东西就按照文档里说的进行操作就能成功搭建网页了。

0x02 代码审计

1. CSRF漏洞防御

特意写了一个 xsrf.php 来对关键请求进行认证,来预防可能的 CSRF 攻击。防御手段是使用 uniqid() 生成时间串,然后将这个时间串 md5 加密之后作为 token 输出在 html 的一个隐藏表单里面,随着用户的点击一起通过 post 提交到后台,然后对这个 token 的值进行验证是否由服务器签发,如果不是,就终止程序,可以有效的防御 CSRF 攻击。

2. SQL注入防御

对于所有和 SQL 数据库交互的命令,都保证拼接参数可控,把用户输入的 username 和 mail 经过 base64 编码处理,password 经过 md5 加密处理,之后再拼接成 SQL 语句执行,防止 SQL 注入。

3. 用户密码保护

虽说这个不是防御漏洞,但是将用户的密码使用 md5 加密保存可以大大增加用户数据的隐私性,即使数据库被盗,用户的密码也不会被直接利用。

4. 文件包含漏洞防御

直接禁用 php phar zip 协议,然后使用自己定义的协议 master 来进行文件上传和搜索。仅限能够完成自己需要的用途。对于涉及到 include,file_put_contents,file_get_contents等,都进行了关键词过滤。比如 index.php 中的正则限制只能输入英文字母,几乎所有地方都用数据库或者程序生成的数据而不是用户输入。

5. XSS漏洞防御

在展示由用户自主上传的信息:用户名和邮箱地址,的时候,使用 htmlspecialchars() 进行了 html 标志词过滤并限制长度,防止用户上传恶意的 XSS 代码,在一些界面输出时激活。

6. 无权限操作漏洞防御

在所有需要账号操作的页面都使用Session进行登录验证,只有登录成功才可以进行相应的操作。

0x03 漏洞挖掘

1. 反向加钱漏洞

在 info.php 中,使用了以下代码来购买商品:

1
<input type="hidden" name="price" value="%price%">
1
2
3
4
5
<?php
$price = intval($_POST['price']);
$point -= $price;
$query = "update sshop.users set point=".$point." where username='".base64_encode($_SESSION['username'])."';";
?>

因为使用POST来提交价格,而且这个POST是用户可控的,那么就可以通过修改 price 的值实现 0元购或者反向加钱。

首先登录打开购买界面,然后启动 BurpSuite 拦截购买请求的包,将 POST 提交的数据 price 修改成负数:

image.png

然后提交数据包,就可以看到自己的积分瞬间变多了。

image.png

另外吐槽一下这个前端提高积分精度,直接拼接 ‘.0’ 可还行。


防御方法:在计算金钱的时候,用 $_SESSION['goodid'];  保存的商品编号作为索引在数据库中查询价格,保证数据的可控性。

2. 敏感信息泄露

存在文件 phpinfo.php 执行了 phpinfo() 代码,访问此页面会暴露 php 的各种信息,比如被禁用的些伪协议,php版本信息,预加载文件,文件路径,开启的选项,等。这些信息可以为黑客对网站渗透提供了便利,不利于网站的安全,建议删除。

3. 任意文件读取漏洞

当然由于我实力过菜,还没完整的找出来如何实现任意文件的读取漏洞。

基本的想法还是有的,实现任意文件读取漏洞就必须要用到 index.php ,而访问这个页面的前提就是覆盖掉 .htaccess 文件。可以通过上传头像的页面,上传一个空的 .htaccess 文件,控制路径到根路径即可。但是不会绕过控制路径,所以没法实现。

查了各种资料,看了很多任意文件读取漏洞,搜了各种绕过姿势……还是没有结果

(我的搜索引擎利用能力还是太弱了)

说一下发现的一些细节吧:

  • config.php
1
file_put_contents($path.$filename,$file);

这里的 $filename 可控,尝试利用文件名修改路径,但是无法成功。

  • shop.php

里面有一个 page 参数可以在 url 中控制,然后传进去 <?php echo $page+1;?>  但是传进去的是一个字符串,在和数字运算的时候会默认变成 0 ,所以暂时还不知道利用的方法。

任务一大概就这样了……

任务二

任务一做的真的是太稀烂了,剩下最后两天做一下任务二来补救一下。

首先搜了搜文章了解了一下 flask 框架的知识,大概知道了产生 SSTI 的原理,然后对着网站的接口试了一下,在 Your Name 这里尝试出来了存在 SSTI 漏洞,然后使用脚本验证关键词过滤。

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

URL="http://106.54.75.217:1099/index"
Names=[]

for i in range(32,127):
Names.append(chr(i))

for Name in Names:
Data={'username':Name}
Res=requests.post(url=URL,data=Data)
if 'banned' in Res.text:
print(Name,ord(Name))

确定了单个字符 + [ \ ] " # % &   ^ | 都被过滤,然后使用脚本确定被过滤的方法:

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

class Person():
pass

Names=dir(Person)
URL="http://106.54.75.217:1099/index"

for Name in Names:
Data={'username':Name}
Res=requests.post(url=URL,data=Data)
if 'banned' in Res.text:
print("Banned:",Name)
else:
print("Accpeded:",Name)

然后可以确定地数据目前可以使用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Accpeded: __le__
Accpeded: __lt__
Accpeded: __module__
Accpeded: __ne__
Accpeded: __new__
Accpeded: __reduce__
Accpeded: __reduce_ex__
Accpeded: __repr__
Accpeded: __delattr__
Accpeded: __dir__
Accpeded: __doc__
Accpeded: __eq__
Accpeded: __ge__
Accpeded: __gt__
Accpeded: __hash__
Accpeded: __sizeof__
Accpeded: __str__
Accpeded: __weakref__

以及被禁止的:

1
2
3
4
5
6
7
8
Banned: __class__
Banned: __dict__
Banned: __format__
Banned: __getattribute__
Banned: __init__
Banned: __init_subclass__
Banned: __setattr__
Banned: __subclasshook__

然后就不知道怎么办

大概查了很多很多篇文章,在攻防世界照着 WriteUp 做了一道 SSTI 的题目之后,才(可能)弄明白做 SSTI 的思路,以及 SSTI 过程的语句含义。(啥都不知道真的是太痛苦了)

但是这个过滤了一堆东西,尤其是过滤 class 搞得刚刚接触 SSTI 的我不知所措,疯狂搜索可以代替 class 的方式,因为 + [] 也被过滤了,而且我对 SSTI 还是一知半解,所以也没有搜到什么有用的方法。

经过了各种漫无目的的乱试,抓包,发现了提示 <!--you are a guest-->  ,原来不收我的原因是因为我是 guest ,还给我传了一个 Cookie ,应该是用来验证登陆的,所以猜测可能需要用模板注入偷管理员 Cookie 出来,或者找到管理员的名字。( 我还傻傻的去尝试了 admin/syclover/F4de/Longlone 去登录 )

网上找了偷 Cookie 的语句 handler.settings 发现 set 被禁用了,一时不知道如何是好,只能漫无目的的查资料,在这里卡了很久很久。终于在一篇伪造 session 越权的文章里面知道了 secret_key 。然后找到了目标,开始各种搜索 secret_key 的文章,明白了 secret_key 的作用。想到了抓包的时候发现的 Cookie 中的 session ,大概明白了需要搞到的东西是 secret_key 。

然后在网上搜索 SSTI 获得 secret_key 的方法,然后找了一个不需要 class 的,然后搜了 globals 不需要中括号的访问方式,搞出来一个我都不知道行不行的 payload: get_flashed_messages.__globals__.current_app.config  粘到了 Your Name 框框里面,好家伙,直接成功了,拿到了 secret_key : 'E81SRrCaLmjVxJkzlugcGdWX'

按照网上搜索,看到了 HCTF2018 admin 看样子好像一模一样。然后就是搜索用来解码的脚本,粘贴了一个运行之后 print 输出不回显,本地写了一个 print(“SYC”) 的脚本在 cmd 运行也没有输出。就去隔壁借用77的电脑跑脚本,这个时候他旁边的 fever_king 告诉我 python 有一个叫 flask-unsign 的模板,让我去试试。

pip 下完之后开始解密 Cookie ,有两个参数,一个是 username 一个是 uuid,果然是 guest 啊。改了 username 为 admin 之后再次访问页面,发现还是说我是 guest ,奇怪。

重新试了两次还是这样,就改了 uuid 为 1,再次上传,发现成功了,拿到了 Flag :SYC{This_is_F1rst_f1ag_AND_You_are_re11y_g00d}

做这题真的是太折磨人了,尤其是漫无目的的搜索,哭.jpg

本来想做一下第二题的,但是时间肯定不够了,JUNCTION 已经躺平了一天半来做二面任务了,比赛最后一天也不帮忙不太好,就去给无名侠端茶倒水造数据了。

最后

  • 感谢浪爷中途的关心和鼓励,不然报告里就可能就没有任务二了。

  • 三叶草的师傅们辛苦了,感谢你们对萌新们的付出!