详解Tor Bridge及Pluggable Transport(Part 1)

阅读量    730642 |   稿费 200

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

0x00 前言

在西弗吉尼亚州查尔斯顿举行的SecureWV 2019网络安全会议上,我和Peixue做了名为“详解Tor Bridge及Pluggable Transport”的主题演讲。这里我们将通过两篇文章与大家分享更多细节,在第一篇文章中,我将介绍如何通过逆向工程找到Tor内置的Bridge(网桥),以及在启用Bridge时Tor浏览器的工作方式。

 

0x01 Tor浏览器及Tor网络

Tor浏览器是用来提供匿名Internet连接的一款工具,可以通过Tor网络实现多层加密。当用户使用Tor浏览器浏览网站时,真实IP地址会被Tor网络隐藏,因此目的网站永远不知道真实源IP地址。用户可以在Tor网络上自己创建站点,域名后缀为.onion。通过这种方式,只有Tor浏览器能够访问该站点,并且没有人知道站点的真实IP地址。这也是勒索软件犯罪分子要求受害者通过Tor浏览器访问.onion网站上付款页面的原因之一。Tor项目团队也知道这一点,在项目博客中明确指出“Tor已经被滥用于犯罪场景中”。

Tor浏览器是基于Mozilla Firefox的一个开源项目,我们可以从官网下载源代码。Tor网络是一个全球overlay网络,由数千个自愿加入的中继节点所组成。该网络有两种中继节点:普通中继节点以及bridge中继节点。普通中继节点可以通过Tor主目录查看,网络传感器可以轻松识别并阻止到普通中继节点的连接。

bridge的信息在Firefox的配置文件中定义,因此我们可以在Tor浏览器的地址栏中输入about:config中显示该信息,如图1所示:

图1. 在Tor浏览器中显示配置信息

然而,bridge中继节点并没有在Tor主目录中显示,这意味着传感器无法轻松阻止到这类节点的连接。在本文中,我将介绍如何利用Tor浏览器的内置功能来寻找这类bridge及中继节点。

为了在Tor浏览器中使用bridge中继,我们可以采用两种方式。Tor浏览器内置了一些bridge供用户选择。如果内置的bridge无法正常工作,用户可以从Tor网络设置中获取更多bridge。用户可以访问https://bridges.torproject.org/,或者向bridges@bridges.torproject.org发送邮件来获取该信息。

 

0x02 实验环境

我们的实验环境如下所示:

  • Windows 7 32-bit SP1
  • Tor浏览器8.0
  • TorLauncher 0.2.16.3(扩展)
  • Torbutton 2.0.6(扩展)

所使用的Tor浏览器版本信息如下:

图2. Tor浏览器版本信息

在分析过程中,Tor浏览器推出了一个新版本(2019年10月22日):Tor浏览器9.0版,大家可以阅读本文附录了解更多信息。

 

0x03 使用内置Bridge运行Tor浏览器

我分析的Tor浏览器提供了4种bridge:obfs4ftemeek-azure以及obfs3,这些bridge称为“Pluggable Transpoort”(可插拔传输器),具体设置如图3所示:

图3. 在Tor网络设置页面选择使用内置bridge

Tor官网强烈推荐使用Obfs4 Bridge,下文所有分析工作都基于这种bridge开展。当Tor浏览器建立obfs4连接时,我分析了通信流量,发现TCP会话由obfs4proxy.exe创建,该进程是bridge客户端进程。

使用obfs4启动Tor浏览器时的进程依赖情况如图4所示,其中firefox.ex启动tor.exe,后者随后启动obfs4proxy.exeobfs4proxy.exe进程位于Tor_installation_folder\Browser\TorBrowser\Tor\PluggableTransports路径中,一开始我认为内置的obfs4 bridge应该硬编码在obfs4proxy.exe进程中。

图4. 使用obfs4 bridge的进程树

 

0x04 分析obfs4proxy.exe进程

启动调试器,attach到obfs4proxy.exe,然后在connect API上设置断点,该API通常用来创建TCP连接。通常情况下,通过逆向工程我们可以通过该API快速找到IP地址及端口。然而当连接到我们创建的obfs4 bridge时,断点从来没被触发过。进一步分析obfs4proxy.exe进程后,我发现该进程实际使用的是来自mswsock.dll的另一个API:MSAFD_ConnectEx

图5. 调用MSAFD_ConnectEx API

如图5所示,obfs4proxy.exe即将调用mswsock.MSAFD_ConnectEx()来与内置obfs4 bridge建立TCP连接,使用的IP地址及端口为192.95.36.142:443。该函数的第二个参数为指向sockaddr_in结构变体的一个指针,其中包含待连接的IP地址及端口信息。随后,该函数会调用WSASendWSARecv来与obfs4 bridge通信。大家可能会注意到OllyDbg调试器无法正确识别该API,因为该API不属于mswsock.dll的导出函数。在IDA Pro对mswsock.dll的分析中,我们可以看到750A7842地址处为MSAFD_ConnectEx() API。此外,call dword ptr [ebx]指令用来调用obfs4proxy.exe所需的几乎所有系统API,这也是对抗API分析的一种方式。

根据我的分析,Tor使用的大多数PE文件(exe及dll文件,如obfs4proxy.exe)似乎使用GCC MINGW-64w编译器变异,该编译器始终使用mov [esp], …指令将参数传递给函数(而不是使用push ...指令)。不断跟踪从MSAFD_ConnectEx()开始的调用栈后,我发现一开始我并没有猜对,内置的IP地址及端口并没有硬编码在obfs4proxy.exe中,而是通过本地环回TCP连接来自于tor.exe父进程。

图6. obfs4proxy.exe接收某个obfs4 bridge的IP地址及端口

通常情况下,从tor.exe发往obfs4proxy.exe的第3个数据包中包含内置obfs4 bridge二进制格式的IP地址及端口,如图6所示。这是一个Socks5报文,大小为0xA字节。05 01 00 01为Socks5协议头,其余数据为IP地址及端口信息。该数据包用来指导obfs4proxy.exe使用二进制IP地址及端口与bridge建立连接。obfs4proxy.exe随后将解析数据包,将二进制的IP地址及端口转化为字符串格式,这里为154.35.22.13:16815

 

0x05 分析Tor.exe

tor.exe使用了名为libevent.dll的第三方模块,该模块来自于libevent库(一个事件通知库),用来驱动Tor执行各种任务。Tor的大多数socket任务(connect()send()recv()等)依赖于事件,由libevent自动调用。当在Tor.exe中跟踪包含bridge IP地址及端口的数据包时,可以在调用栈上下文中看到许多返回地址位于libevent.dll模块中。在图7中,Tor.exe正在调用ws2_32.send() API来发送包含bridge IP地址及端口的数据包,这与图6中收到的数据包类似。

图7. tor.exe使用libevnet模块将bridge的IP及端口发送到bridge进程

通过不断跟踪tor.exe发送的bridge IP地址及端口,我找到了某个地址,该地址用来启动新的事件,配合回调函数来发送bridge的IP地址及端口。如下所示的ASM代码片段为tor.exe中调用libevent.event_new()的上下文,该函数的第2个参数为socket句柄,第3参数为事件操作(这里为14H,代表EV_WRITEEV_PERSIST),第4个参数为回调函数(这里为sub_2833EE),第5个参数包含bridge的IP地址及端口,当libevent调用该函数时就会将地址及端口传递给回调函数(sub_2833EE)。

以下ASM代码片段来自于tor.exe,其中基址为00280000h

[…]   
.text:00281C84                 mov     edx, eax
.text:00281C86                 mov     eax, [ebp+var_2C] ; 
.text:00281C89                 mov     [eax+14h], edx
.text:00281C8C                 mov     eax, [ebp+var_2C] ; 
.text:00281C8F                 mov     ebx, [eax+0Ch]
.text:00281C92                 call    sub_5133E0
.text:00281C97                 mov     edx, eax
.text:00281C99                 mov     eax, [ebp+var_2C] 
.text:00281C9C                 mov     [esp+10h], eax       ; argument for callback function
.text:00281CA0                 mov     [esp+0Ch], offset sub_2833EE    ; the callback function
.text:00281CA8                 mov     [esp+8], 14h  ; #define EV_WRITE 0x04|#define EV_PERSIST 0x10
.text:00281CB0                 mov     [esp+4], ebx       ; socket
.text:00281CB4                 mov     [esp], edx
.text:00281CB7                 call    event_new    ; event_new(event_base, socket, event EV_READ/EV_WRITE, callback_fn, callback_args);
.text:00281CBC                 mov     edx, eax
.text:00281CBE                 mov     eax, [ebp+var_2C] 
.text:00281CC1                 mov     [eax+18h], edx
[…]

继续逆向分析tor.exe,我发现了一堆Obfs4 bridge,这些bridge位于SETCONF命令的数据结构中,如图8所示:

图8. 部分SETCONF命令数据

图8为SETCONF命令的部分片段,其中SETCONF为命令名,位于结构起始处,后面是内置的bridge信息。红框中标出的数据为一个Obfs4 bridge数据块,也就是bridge配置行。每个bridge节点都必须保存在一条bridge配置行中,这里总共有27个bridge配置行。大家可以看到,obfs4类型的bridge以及对应的IP地址及端口采用字符串格式。

bridge信息也没有硬编码在tor.exe进程中,现在我们遇到另一个问题:整个SETCONF数据源自何处?实际上该数据来自firefox.exe收到的某个TCP数据包,该进程是tor.exe的父进程。当tor.exe启动时,会打开9151/TCP控制端口接收来自firefox.exe的命令,也会打开9151/TCP代理端口。稍后我会介绍firefox.exe如何将命令发送给tor.exe

继续分析tor.exe,我注意到除了SETCONF命令之外,该进程还支持其他命令,如GETCONFSAVECONFGETINFOAUTHENTICATELOADCONF以及QUIT等。tor.exe有一个函数用来处理这些命令,会根据命令执行不同的代码分支。

tor.exe会根据命令执行不同的任务。比如,SAVECONF命令告诉tor.exe将bridge信息保存到Tor_installation_folder\Browser\TorBrowser\Tor\Data\torrc中的某个文件,而SETCONF命令告诉tor.exe具体的bridge信息,该信息随后会传递给bridge进程,以便创建bridge连接。

 

0x06 在Firefox.exe中寻找内置Bridge

Tor使用许多环回TCP连接来在进程间传递命令,以便执行具体任务。Wireshark从3.0.1版开始支持本地环回适配器,因此我们可以使用该工具在环回接口上捕捉通信流量,其中就包括firefox.exe通过本地环回接口上的TCP 9151控制端口向tor.exe发送的SETCONF数据包。RawCap是可以完成相同任务的另一款工具。firefox.exe采用一种特殊的方式向tor.exe发送命令数据包:每个数据包1个字节。如图9所示,其中有长度为1的许多数据包,重组这些数据包后,我们就可以还原出完整的SETCONF命令。

图9. firefox.exe发送命令数据包

Firefox浏览器可以通过第三方开发者开发的extensions扩展来执行更多功能。由于Tor浏览器基于Mozilla Firefox开发,因此也具备相同的能力。

在9.0版之前,Tor浏览器带有两个扩展:Torbutton以及TorLauncher。这些扩展位于Tor_installation_folder\Browser\TorBrowser\Data\Browser\profile.default\extensions目录中,对应的扩展文件分别为torbutton@torproject.org.xpitor-launcher@torproject.org.xpi,如图10所示,这些文件都为Zip归档文件,包含JS文件及模块。

图10. Tor扩展文件

然而从9.0版开始,Tor浏览器移除了这两个扩展,对应的代码及功能已经集成到Tor浏览器中。如果大家想了解更多细节,可参考本文附录。

Torbutton用来设置并显示Tor设置,也可以显示Tor信息(比如“关于Tor”)。我们可以点击Tor浏览器工具栏上的Tor图标来查看(如图3所示)。

TorLauncher负责根据Torbutton的具体设置来控制Tor进程。

TorLauncher在Firefox的xul.dll中加载,相关JS代码也会在该模块的上下文中执行。xul.dll是Mozilla Firefox的核心模块,也是向tor.exe发送命令的实际模块,这些命令包括SETCONF(在network-setting.js中实现)、GETCONFtl-protocol.js)以及GETINFOtl-protocol.jstorbutton.js)。xul.dll模块是一种JS引擎,可以用来解析并执行这些命令对应的JS代码。

现在来看一下当bridge启用时Tor浏览器的内部工作机制。一旦Tor浏览器中启用了bridge,就会执行如下操作:

1、firefox.exe加载基本配置、首选定义以及扩展(涉及到firefox.exexul.dll模块);

2、TorLauncher扩展使用命令行中的设置运行tor.exe(涉及到xul.dll模块);

3、用户可以使用Torbutton随时修改Tor设置(涉及到xul.dll模块);

4、Firefox通过本地环回TCP连接向tor.exe发送命令,告诉tor.exe如何根据具体设置来工作(涉及到xul.dlltor.exe模块);

5、随后tor.exe运行对应的一个bridge进程(obfs3obfs4对应obfs4proxy.exefte对应fteproxy.exemeek-azure对应terminateprocess-buffer.exe),以便创建bridge通信(涉及到tor.exe及对应的bridge进程);

6、tor.exe通过本地环回TCP连接与这些bridge进程通信(涉及到tor.exe及对应的bridge进程);

7、bridge进程连接到bridge中继节点(涉及到bridge进程)。

Tor浏览器通过本地环回接口来发送命令,控制Tor客户端的工作模式,然后再接收并解析来自Tor客户端的处理结果。根据我的分析,obfs4 bridge的所有信息都定义在一些全局命名变量中(也就是Firefox首选项),存放在本地文件中。当firefox.exe启动时会初始化这些信息,然后TorLauncher会读取首选项,根据对应的名称(取自xul.dll)来访问并使用这些信息。

出于安全原因,我在这里隐去了包含所有bridge完整信息的文件名。在我分析的Tor浏览器版本中,包含27个obfs4 bridge、4个obfs3 bridge、1个meek-azure bridge以及4个fte bridge。

 

0x07 Pluggable Transport

在前文分析中,我们提到了4种bridge类型:obfs4obfs3meek-azure以及fte。这些bridge都称之为Pluggable Transport(可插拔传输,PT),其主要任务就是转换并在客户端及第一跳(Tor中继节点)之间传输流量。因此,使用PT可以让传感器更加难以识别并阻止Tor流量。

Obfs4是更强大且更常用的PT。第一跳Obfs4节点通常是bridge中继点,并没有在Tor主目录中列出。Obfs4混淆器由Yawning Angel开发及维护,这是采用Go语言开发的开源项目,代码托管于GitHubObfs Bridge之前有多个版本,比如Obfs2Obfs3Obfs4与之前版本不大类似,更接近于ScrambleSuit,因为Obfs4的概念来自于Philipp Winter的ScrambleSuit协议,这也是为什么Obfs4客户端进程同样能够承担ScrambleSuit客户端功能的原因所在。

Obfs4的所有功能都由obfs4proxy.exe提供,后者是适用于Tor用户的Obfs4客户端进程。

前面提到过,Tor浏览器基于Firefox浏览器,使用Firefox扩展为用户提供匿名Internet访问服务。一旦Tor浏览器运行,就会启动firefox.exe,加载两个Tor扩展(TorLauncherTorbutton)。随后,其中一个Tor扩展启动tor.exe(Tor客户端进程)。当在“Tor网络设置”中设置Tor浏览器启用Obfs4 Bridge时,tor.exe就会运行obfs4proxy.exe

在下文中,我们将详细介绍这3个组件(firefox.exetor.exeobfs4proxy.exe)的交互过程。

 

0x08 Tor浏览器与Tor客户端通信

firefox.exe进程(Tor浏览器)及tor.exe(Tor客户端)会通过Tor的TCP端口(监听在本地环回接口)相互通信。

从图4可知,firefox.exe会运行tor.exe,更具体一点,tor.exe由Firefox扩展TorLauncher启动,该扩展由xul.dll加载并解析。当浏览器调用Windows原生API ShellExecuteExW()来运行tor.exe时,许多命令行参数会被传递给tor.exe。当firefox.exe准备启动tor.exe时,传递给tor.exe的命令行参数如图11所示。

图11. tor.exe使用的命令行参数

传递给tor.exe的参数总共有10个,这里我们将其拆分,逐行分析。在表1中,...\是Tor浏览器安装路径的缩写。

表1. tor.exe命令行参数中包含2个TCP端口

这里设置了2个端口,如表1中高亮部分文本所示,分别为+__ControlPort 9151以及+__SocksPort "127.0.0.1:9150 IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth"tor.exe会在环回接口(127.0.0.1)的91519150两个TCP端口上监听,这也是tor.exe默认使用的端口号。

用户可以修改这两个端口的默认值,默认值位于几个本地文件中。其中一个本地文件的示例代码如下:

// Proxy and proxy security
pref("network.proxy.socks", "127.0.0.1");
pref("network.proxy.socks_port", 9150);

TCP端口9151是控制端口,用来交换firefox.exetor.exe之间的控制命令及结果。Tor在TCP端口9150上提供SOCKS5代理服务,用来在firefox.exetor.exe之间传递SOCKS5数据包。

典型的控制命令如下所示。firefox.exe通过TCP端口9151GETINFO命令发送给tor.exe,请求某些信息,随后tor.exe解析命令,通过TCP端口9151将结果发送回firefox.exe

GETINFO status/bootstrap-phase

250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=0 TAG=starting SUMMARY="Starting"
250 OK

Tor在127.0.0.1:9150上提供代理功能,用来在firefox.exetor.exe之间传输普通数据包(未经加密)。WireShark的抓包结果如图12所示,我将其分成两部分:上半部分为Tor SOCKS5握手过程,下半部分传输的数据包。

图12. Tor通过TCP端口9150firefox.exetor.exe之间传输的SOCKS5代理数据

使用Tor浏览器访问https://www.google.com的数据包如图12所示。在握手过程中,firefox.exe在SOCKS5报文中告诉tor.exe待访问的目的地址为www.google.com。握手结束后,firefox.exe开始向tor.exe发送Google TLS客户端Hello报文,Tor客户端会加密该报文,然后发送回Google TLS服务端Hello报文。随后,firefox.exe开始通过TCP端口9150tor.exe发送请求报文及接收响应报文。

Tor客户端通过TCP端口9150接收数据包,完成SOCKS5握手。随后,客户端接收来自firefox.exe的正常报文,然后执行加密操作。经过Tor加密的数据包传递给选定的Tor电路(circuit)中的Tor入口中继节点。最终,Tor出口中继节点解密收到的数据包,还原正常报文,发送到目的服务器(如www.google.com)。

在相反路径上,当tor.exe从Tor入口中继节点收到加密报文后,会将报文解密获取明文报文,最终tor.exe通过TCP端口9150 将明文报文发送给firefox.exe(如图7所示)。

 

0x09 Tor客户端与Obfs4客户端通信

当Tor客户端(tor.exe)从Tor浏览器收到SETCONF命令后,就会启动Obfs4客户端obfs4proxy.exeobfs4proxy.exe会在环回接口上打开一个随机TCP端口,为Tor提供Obfs4 Bridge服务,通过进程间管道将TCP端口号通知父进程tor.exe

obfs4proxy.exe准备将TCP端口号通知tor.exe时,在WriteFile() Windows API上设置的断点就会被触发,参考图13中OllyDbg的输出结果。在图13所示的内存数据中,CMETHOD obfs4 socks5 127.0.0.1:49496为待发送给Tor客户端的数据,其中就包含TCP端口。我在测试时看到的TCP端口号为49496WriteFile()的第1个参数为进程间管道的文件句柄,此时为00000100

图13. obfs4proxy.exe通过进程间管道将TCP端口发送给tor.exe

随后,tor.exe与该TCP端口建连,以便与Obfs4客户端通信。Tor分别将Bridge节点信息发送至该端口,每次发送一个Bridge信息。在图14的TCPView结果中,我们可以看到tor.exe如何将bridge信息分别发送给obfs4proxy.exe

图14. TCPView显示Tor将Obfs4 bridge信息发送至Obfs4Proxy

以上就是Tor浏览器(firefox.exe)与Obfs4客户端(obfs4proxy.exe)通过Tor客户端(tor.exe)的完整通信过程。在下文中,我们将介绍Obfs4客户端(obfs4proxy.exe)如何与Obfs4 Bridge建连,以及Obfs4如何传输数据包以绕过网络传感器。

 

0x0A 附录

2019年10月份下旬,Tor浏览器发布了9.0版,该版本中移除了两个扩展(TorLauncherTorbutton)。在该版本中,Tor浏览器将执行这两个扩展先前的任务。实际上,根据我的分析,我发现新版本并没有移除这两个扩展的代码,而是将代码集成到名为omin.ja的Firefox JAR文件中。当Firefox启动时会加载并解析该文件,随后,用户可以依次通过“Options”、“Tor”菜单找到Tor网络设置选项,如图15所示。

图15. 两个扩展的功能已集成到Tor浏览器中

新版修改并没有影响Tor浏览器的工作机制,因此本文针对8.0版的分析结论依然有效。

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多