帮助中心 > 问题列表 > beego框架之WEB安全小系统

beego框架之WEB安全小系统

跨目录上传文件漏洞


添加代码

VIEWS部分

在views 文件夹里新建一个File,命名为FileController.tpl ,添加如下代码(即在body标签里添加两个表单,各放一个input 表示要上传的文件):


CONTROLLERS部分

在controllers 文件夹里新建一个go文件,命名为FileController.go ,添加如下代码(老惯例,仍然是声明了两个对比的控制器,并分别重写了Get和Post函数):


ROUTERS部分

对 routers/router.go 文件添加如下代码(即为上述两个控制器注册路由):


这样,无论url是访问/problems/FileUpload 还是/problems/SafeFileUpload,两种Get请求都能正确渲染FileController.tpl这个页面,然后当从表单发送Post请求时,一个表单会发送至FileController的Post函数响应并处理,而另一个表单会发送至SafeFileController的Post函数响应并处理。

进行实验

在浏览器中输入http://127.0.0.1:8080/problems/FileUpload :

正常情况

在“文件上传”的表单里选择任意文件并提交上传:

后台显示如下:

上传成功。

跨目录上传

使用burpsuite软件监听抓包在“文件上传”的表单里选择任意文件并提交上传:

burpsuite软件抓到的包如下:

将filename="1.png"修改为filename="../1.png"后,点击Forward按钮,让修改后的报文送达服务器。

后台显示如下:

可以看到,上传的文件“1.png”出现在了与upload文件夹同级的目录中(即static目录下)。

跨目录上传防范

在“文件上传防范”的表单里选择任意文件并提交上传:

burpsuite软件抓到的包如下:

先右键(或者CTRL+R)将其添加到Repeater

在Repeater这,将filename="2.png"修改为filename="../2.png"后,点击GO按钮,获得相应报文。

后台显示如下:

后台输出了文件上传目录的绝对路径,然后输出文件并不在安全目录下,上传失败。

在Repeater这,将filename="../2.png"修改为filename="abc/2.png"后,点击GO按钮,获得相应报文。

后台显示如下:

后台输出了文件上传目录的绝对路径,但是并没有输出文件并不在安全目录下,然而还是上传失败。

原因分析

表单的本意设计是可以选择一个本机内的文件,将其上传至服务器的\static\upload 目录下。

然而在FileController的Post函数中,直接取了表单上传的文件名作为参数,传入c.SaveToFile函数中


所以当文件名被burpsuite中间人修改为../1.png 后,上传的目录随之变成了static/upload/../1.png 。

“../”对系统来说表示上级目录,因此“static/upload/../1.png”中的“upload”与“../”是抵消的,最终实际上传目录为“static/1.png”。

于是便产生了跨目录上传漏洞,通过这个漏洞,攻击者可以将文件上传到任意目录(可以通过添加多个“../”来找到根目录)

推荐防范措施:先把目录传入filepath.Abs()函数,删除所有符号链接,获得绝对路径,然后再对标准化后的路径进行正则匹配,比较是否在安全目录下。


这里就是将标准化后的路径与\\static\\upload\\ 进行正则匹配,如果标准化后的路径缺乏\\static\\upload\\ 这一子串,说明肯定不是在安全路径下(即存在../跨目录的问题)。

而在实验当中,还存在将文件名改为abc/2.png 使得最后的文件路径变成\static\upload\abc\2.png 的做法,此时是符合正则匹配的。但是仍然上传失败了。还记得c.SaveToFile这个函数嘛,当发现要保存的文件的路径中存在未创建的文件夹是没法保存成功的,所有文件夹必须由我们事先创建好,因而将上传的文件名改为abc/2.png是没办法开一个子文件夹的。

健壮性

若用户不选择任何文件,直接点击文件提交,此时后台代码c.GetFile("uploadname") 获取key值为"uploadname"的文件失败,直接报错,整个服务器断开连接。


解决方案即在获取文件前进行一次非空判断:


问:怎么发现表单Post过来的文件就保存在c.Ctx.Request.MultipartForm.File当中呢?答:通过查看c.GetFile()的源码发现,返回值为c.Ctx.Request.FormFile(key)

于是再查看FormFile()的源码:

红框部分,r指的是Request,即c.Ctx.Request。而r.MultipartForm.File[key]即为我们要找的文件,因而组合起来就是c.Ctx.Request.MultipartForm.File["uploadname"]。

可以看到,有什么不懂的直接看源码也是可以解决问题的= =

现在再不选文件直接点提交试试:

小总结

上传是Web中最常见的功能,如果上传功能存在设计、编码缺陷,就容易形成上传漏洞,从而成为致命的安全问题。攻击者可以通过上传脚本木马,实现查看/篡改/删除源码和任意涂鸦网页,可以连接和操作对应的数据库,还可以通过操作系统漏洞、配置缺陷、信息泄露进行提权,获取操作系统提权。

今天这里只讲跨目录上传文件漏洞,所以只提到要对文件的路径进行标准化校验。在实际的WEB上传模块中,文件的后缀名,文件的类型,文件的大小都是需要严格把关的(白名单机制)。