AppSheet 中的 SSRF 漏洞 - Google VRP
Webhook 中的 SSRF
我研究了 AppSheet 的功能,发现了一个名为 Workflows 的部分(今天被“AppSheet Bots”取代)。AppSheet Workflow 通过定义特定规则实现了应用程序行为的自动化。例如 - 当用户在表中创建新行时发送电子邮件通知。定义这些规则有多种选项。其中一个选项是在规则触发器上调用 webhook。这听起来很有希望,所以我研究了这个功能
我创建了一个新的工作流,规则如下:当表中的数据发生变化时调用 webhook。我尝试做的第一件事就是调用元数据 API。但是,我遇到了第一个问题。没有创建 GET 请求的选项,而这是获取元数据所必需的(至少我是这么认为的)。我只能使用 POST、DELETE 和 PATCH 发出请求。经过一段时间的思考和谷歌搜索,我想到了一个主意,那就是使用重定向从 POST 创建 GET 请求。
在 HTTP重定向文档中,我发现某些重定向类型“可能会或可能不会”将 HTTP 方法更改为 GET。我用一些 HTTP 客户端和库进行了测试,实际上,它们都将方法更改为 GET。这正是我需要的。
因此,在我拥有的域名上,我创建了一个 301 重定向(使用 Location 标头):
Webhook 中的 SSRF
我研究了 AppSheet 的功能,发现了一个名为 Workflows 的部分(今天被“AppSheet Bots”取代)。AppSheet Workflow 通过定义特定规则实现了应用程序行为的自动化。例如 - 当用户在表中创建新行时发送电子邮件通知。定义这些规则有多种选项。其中一个选项是在规则触发器上调用 webhook。这听起来很有希望,所以我研究了这个功能
我创建了一个新的工作流,规则如下:当表中的数据发生变化时调用 webhook。我尝试做的第一件事就是调用元数据 API。但是,我遇到了第一个问题。没有创建 GET 请求的选项,而这是获取元数据所必需的(至少我是这么认为的)。我只能使用 POST、DELETE 和 PATCH 发出请求。经过一段时间的思考和谷歌搜索,我想到了一个主意,那就是使用重定向从 POST 创建 GET 请求。
在 HTTP重定向文档中,我发现某些重定向类型“可能会或可能不会”将 HTTP 方法更改为 GET。我用一些 HTTP 客户端和库进行了测试,实际上,它们都将方法更改为 GET。这正是我需要的。
因此,在我拥有的域名上,我创建了一个 301 重定向(使用 Location 标头):
http://<MY_DOMAIN>/<ROUTE>到http://169.254.169.254/<ROUTE>
预期的行为是 AppSheet HTTP 客户端将 POST 方法更改为 GET:
POST http://<MY_DOMAIN>/<ROUTE>到GET http://169.254.169.254/<ROUTE>
我在具有以下设置的 webhook 中使用了此重定向(请求指向旧式元数据 API):
发布 http://<我的域>/computeMetadata/v1beta1/instance/service-accounts/default/token
我保存了工作流程并通过在 AppSheet 应用程序的表中添加新行来触发操作。
几分钟后,我打开应用程序日志并找到了 webhook 调用的记录。日志显示了来自元数据服务器的响应主体。响应中包含 AppSheet 应用程序的访问令牌。
CVE-2022-0778
发现的漏洞会BN_mod_sqrt()在解析椭圆曲线密钥时触发 OpenSSL 函数中的无限循环。这意味着恶意制作的 X.509 证书可以对任何未打补丁的服务器实施 DoS 攻击。
该漏洞的核心在于解析压缩格式的点的 EC 密钥:解析此类密钥时,OpenSSL 将尝试扩展压缩点,尝试计算p定义曲线的素数的平方根模数。但是,在任何地方都不会检查素数p,即使在要求素数的情况下也不会BN_mod_sqrt();因此,实现中的错误将导致无限循环,因为p不是预期的素数
函数BN_mod_sqrt()实现了求模平方根的Tonelli-Shanksa算法,即给定一个整数和一个素数p,返回一个值r使得r^2 == a (mod p)。
通过分析修补漏洞的提交i,我们发现罪魁祸首是寻找最小索引的循环b^(2^i)==1 (mod p),其中b已经在算法中定义过。
发现的漏洞会BN_mod_sqrt()在解析椭圆曲线密钥时触发 OpenSSL 函数中的无限循环。这意味着恶意制作的 X.509 证书可以对任何未打补丁的服务器实施 DoS 攻击。
该漏洞的核心在于解析压缩格式的点的 EC 密钥:解析此类密钥时,OpenSSL 将尝试扩展压缩点,尝试计算p定义曲线的素数的平方根模数。但是,在任何地方都不会检查素数p,即使在要求素数的情况下也不会BN_mod_sqrt();因此,实现中的错误将导致无限循环,因为p不是预期的素数
函数BN_mod_sqrt()实现了求模平方根的Tonelli-Shanksa算法,即给定一个整数和一个素数p,返回一个值r使得r^2 == a (mod p)。
通过分析修补漏洞的提交i,我们发现罪魁祸首是寻找最小索引的循环b^(2^i)==1 (mod p),其中b已经在算法中定义过。
Oracle Access Manager 预授权 RCE(CVE-2021-35587 分析)
您可能知道,Oracle Access Manager (OAM) 是一款流行的 SSO 产品,被 Oracle、VMware、华为、高通等多家大公司使用……
这个漏洞是我和Peterjson在分析和构建另一个超级 0day(现在还没有修复 ;))的 PoC 时偶然发现的。
访问入口点并利用漏洞非常容易,因此建议立即应用补丁!它可能使攻击者能够访问 OAM 服务器,创建具有任何权限的任何用户,或者只是在受害者的服务器中执行代码。
您可能知道,Oracle Access Manager (OAM) 是一款流行的 SSO 产品,被 Oracle、VMware、华为、高通等多家大公司使用……
这个漏洞是我和Peterjson在分析和构建另一个超级 0day(现在还没有修复 ;))的 PoC 时偶然发现的。
访问入口点并利用漏洞非常容易,因此建议立即应用补丁!它可能使攻击者能够访问 OAM 服务器,创建具有任何权限的任何用户,或者只是在受害者的服务器中执行代码。
日志、监控和故障排除
您可以随时监控系统的资源使用情况、正常运行时间和会话的负载杠杆率
用于lscpu查看系统的 CPU 使用情况和其他详细信息。
系统事件和进程的踪迹通常以日志的形式保存在/var/log目录中。日志分为两类:1. 通过 保存的基本系统日志journald,默认情况下,这些日志会在启动时被清除(可以配置为持久保存)。2.rsyslog默认情况下持久保存并组织在文件夹内的日志/var/log/。Linux 中的日志记录机制主要遵循syslog系统消息、事件、安全事件、邮件和作业日志的标准协议,而其他程序可能遵循或不遵循syslog相同的格式
您可以随时监控系统的资源使用情况、正常运行时间和会话的负载杠杆率
用于lscpu查看系统的 CPU 使用情况和其他详细信息。
系统事件和进程的踪迹通常以日志的形式保存在/var/log目录中。日志分为两类:1. 通过 保存的基本系统日志journald,默认情况下,这些日志会在启动时被清除(可以配置为持久保存)。2.rsyslog默认情况下持久保存并组织在文件夹内的日志/var/log/。Linux 中的日志记录机制主要遵循syslog系统消息、事件、安全事件、邮件和作业日志的标准协议,而其他程序可能遵循或不遵循syslog相同的格式
os.path.join工作原理
我们想提醒大家注意os.path.join在某些情况下函数的使用不安全。即使用户输入已被过滤,并且“ ..”字符串已被删除以防止路径遍历,在第二个参数中仍有可能使用所需目录的绝对路径。
通常,即使有可能上传恶意文件以获取任意远程代码执行,也需要完全重新启动 Python Web 应用程序才能获取此新代码。不幸的是,对于 VMware 来说,这次WSGIScriptAliashttpd 配置中的别名意味着脚本不会被缓存,并且会在每次用户请求 URL 时加载到内存中并执行/logupload。
考虑到这一点,我们决定log_upload_wsgi.py用自己的恶意代码覆盖原始脚本。我们只有一次尝试上传有效的 Python 脚本的机会,否则我们将破坏 Web 应用程序。我们用 Python 语言创建了一个 WSGI Web Shell,并尝试将其上传到/etc/httpd/html/wsgi_log_upload/名为log_upload_wsgi.pyfilename 的文件夹中
我们想提醒大家注意os.path.join在某些情况下函数的使用不安全。即使用户输入已被过滤,并且“ ..”字符串已被删除以防止路径遍历,在第二个参数中仍有可能使用所需目录的绝对路径。
通常,即使有可能上传恶意文件以获取任意远程代码执行,也需要完全重新启动 Python Web 应用程序才能获取此新代码。不幸的是,对于 VMware 来说,这次WSGIScriptAliashttpd 配置中的别名意味着脚本不会被缓存,并且会在每次用户请求 URL 时加载到内存中并执行/logupload。
考虑到这一点,我们决定log_upload_wsgi.py用自己的恶意代码覆盖原始脚本。我们只有一次尝试上传有效的 Python 脚本的机会,否则我们将破坏 Web 应用程序。我们用 Python 语言创建了一个 WSGI Web Shell,并尝试将其上传到/etc/httpd/html/wsgi_log_upload/名为log_upload_wsgi.pyfilename 的文件夹中
破解 Google Drive 集成
您是否曾在漏洞赏金目标中观察到 Google Drive 集成,并想知道除了 OAuth CSRF 之外还可能存在什么?是否有可能进一步破解此集成?这正是我们今天要探索的内容。
在讨论此漏洞之前,让我们先来看看 Google Drive 通常是如何集成到应用程序中的。
一般有三种方式;
客户端嵌入。
在客户端获取CDN Url并在服务器端下载。
通过 Gdrive API 在服务器端获取文件。
前两个测试相对简单,但最后一个才是乐趣所在。
为了理解这一点,请考虑一个简单的应用程序,它从 Gdrive 检索并呈现选定的图像文件。我知道这不是必要的,但为了让读者理解,我试图展示幕后的工作方式。
这是负责列出并呈现您的 Google Drive 图像文件的两条路线,提供您的访问令牌
上面的代码应该更容易理解 - 我们可以通过 file_id 控制对谷歌发出的 HTTP 请求的路径。这意味着我们可以进行路径遍历并添加查询参数
您是否曾在漏洞赏金目标中观察到 Google Drive 集成,并想知道除了 OAuth CSRF 之外还可能存在什么?是否有可能进一步破解此集成?这正是我们今天要探索的内容。
在讨论此漏洞之前,让我们先来看看 Google Drive 通常是如何集成到应用程序中的。
一般有三种方式;
客户端嵌入。
在客户端获取CDN Url并在服务器端下载。
通过 Gdrive API 在服务器端获取文件。
前两个测试相对简单,但最后一个才是乐趣所在。
为了理解这一点,请考虑一个简单的应用程序,它从 Gdrive 检索并呈现选定的图像文件。我知道这不是必要的,但为了让读者理解,我试图展示幕后的工作方式。
这是负责列出并呈现您的 Google Drive 图像文件的两条路线,提供您的访问令牌
#List images from Gdrive
@app.route('/cloud/gdrive/list')
def list_files():
token = request.args.get('access_token')
html = ""
if token:
r = requests.get('https://www.googleapis.com/drive/v2/files/',headers={'Authorization': 'Bearer '+token})
resp = json.loads(r.text)
print(resp)
for file in resp['items']:
if file['mimeType'].startswith('image/'):
html += "<a href='/cloud/gdrive/fetch?file_id="+file['id']+"&access_token="+token+"'>"+file['title']+"</a><br>"
return "Select Gdrive image (max 1mb) to fetch <br><br>" + html
else:
return "Error"
#Render provided image id from gdrive
@app.route('/cloud/gdrive/fetch')
def fetch_gdrive_video():
token = request.args.get('access_token')
file_id = request.args.get('file_id')
if token and file_id:
try:
r = requests.get('https://www.googleapis.com/drive/v2/files/'+file_id,headers={'Authorization': 'Bearer '+token})
download_url = json.loads(r.text)['downloadUrl']
d = requests.get(download_url,headers={'Authorization': 'Bearer '+token})
except Exception as e:
return Response(str(e), headers={'Content-type':'text/plain'})
return Response(d.content, headers={'Content-type':'image/png'})
else:
return "Error"
上面的代码应该更容易理解 - 我们可以通过 file_id 控制对谷歌发出的 HTTP 请求的路径。这意味着我们可以进行路径遍历并添加查询参数