视频会议的开发与探索(一):WebRTC的狂野世界

WebRTC的狂野世界

在过去的五年中,已经有太多的方式使得网页和App中支持视频会议。

Facebook,WhatsApp,FaceTime和Signal是其中几种用户可以用来在网络中进行视频,音频通话的方式。尽管很多研究已经开始转为对视频会议的加密和隐私保护,关于这些平台的易受攻击程度的信息却很少。我们查阅了三个最为广泛使用的视频会议实现方式。在本文中,我们会对此描述。

这一部分将会讨论的WebRTC的分析。第二部分将会讨论对FaceTime的分析。第三部分将会讨论WhatsApp.第四部分将会描述一些对WhatsApp不起作用的攻击形式。最终,第五部分,将会讨论视频会议的未来和开发者应该如何使得自身WebRTC实现更安全。

典型视频会议结构

所有的视频会议实现至少要允许网络中任意两点的两点连接,来传输音频流。同时要求可靠性,和视频音频的质量,带来了很多挑战。首先,对于某一点,它需要找到其它任意一点并且建立连接,无论是NAT还是其它网络结构。接着它们需要交流,即使处于不同平台,App或浏览器。最后,它们需要保持音频视频质量,即使带宽较低。

几乎所有视频会议解决方案都趋向于一个简单架构。它假设两点之间可以通过一个安全的校验完整性的频道进行交流,这可能会导致低带宽或需要一个中介服务器,它会建立更快,更高带宽的两点连接频道。

建立连接的第一阶段称为发信。通过此过程两点交换用来建立连接的信息,包括网络地址,支持的编解码器,和密钥。通常,发送端发送通话请求,接着接收端以同样信息响应。SDP是一个用来交换信息的常见协议,但却不常用,大多数实现方式不遵循此规格。支持视频会议的网站通常使用WebSockets来交换信息,或使用HTTPS,通过网络服务器作为中介。

一旦发信完成,两点会使用STUN,TURN和ICE协议寻找通道交换流量。基于这些协议,两点可以基于网络状况建立UDP,UDP-over-STUN,和TCP连接。

一旦连接建立,两点使用实时传输协议交流。尽管此协议是标准协议,大多数实现方式都某种程度上与它偏离。RTP可以使用RTP进行加密,一些实现也会使用DTLS进行加密。这样加密可以使得多条流和信息同时交换。接着,介于RTP如何区分信息,它被传递到其它过程,例如视频编解码。流控制传输协议SCTP也用来在视频会议期间交换少量数据,但不如RTP常见。

即使进行加密,RTP经常不包括完整性保护,如果这样的话,它通常不会丢弃畸形数据。而它会尝试使用FEC之类的策略恢复它。大多数视频会议解决方案同样会检查频道何时处于低带宽状态,并尝试使用一种方案恢复,它可以获得最好的音频和视频质量。例如,发送更少的帧或改变编码器。RTCP用来交换网络质量的统计数据,并协助调整RTP流属性来适应网络状况。

WebRTC

WebRTC是一个开源项目,实现了视频会议。它是目前最常用的实现。Chrome,Safari,Firefox,Facebook Messager,Signal和许多其它移动App使用WebRTC.WebRTC看起来是了解视频会议的一个好的出发点,因为它常用,开源,并且文件充分。

WebRTC发信

我从发信过程出发,因为它是一个不需要任何用户交互的攻击点。RTP之类的协议会在用户接受视频电话之后处理,但是发信过程是在用户接受通话之前处理的。WebRTC使用SDP发信。

我查阅了WebRTC SDP解析器代码,但是没有发现任何bug。之后我发现WebRTC发信过程并不是一直通过浏览器实现。Chrome使用主要的WebRTC实现,Safari轻微不同,Firefox使用它们自己的方式。大多数使用WebRTC的移动App会使用自己的发信方式,并且没有使用SDP。因此WebRTC发信过程的bug不太可能造成很大的影响。

RTP模糊化

接着我开始探索RTP在WebRTC中是如何被处理的。尽管RTP不是一个少交互的攻击点,因为用户通常需要在RTP流量处理之前接受通话,但是接受通话对于用户来说是一个合理的行为。我首先查阅WebRTC源代码,但是很庞大,很复杂,因此我觉得模糊化是一个更好的方法。

WebRTC目录包括为OSS-Fuzz,为每一个协议写的模糊器,和由WebRTC支持的编码器,但是它们没有模仿各式解析器之间的交互,没有保持测试案例之间的状态,因此看起来端到端模糊化覆盖面会更广。

安装端到端模糊相对来说太耗时了,所以为了找到bug,我改变了Chrome,让它发送畸形RTP数据包。我修改了libsrtp中的srtp_protect函数,这样它就可以在每一个数据包上运行以下的模糊器:

void fuzz(char* buf, int len){

 int q = rand()%10;

 if (q == 7){
  int ind = rand()%len;
  buf[ind] = rand();
 }

 if(q == 5){
  for(int i = 0; i < len; i++)
   buf[i] = rand();

}

当使用此版本与Chrome的一个未作修改的实例进行WebRTC通话时,它大约每30秒崩溃一次。

大多数崩溃都是由于除以0的例外,但是还有3个有趣的错误。我改变了Chrome中WebRTC的源代码,这样它就可以产生同样的信息包,导致同样的崩溃,接着安装一个脱机的WebRTC来重现,因此没有必要通过重建Chrome来复制这些问题。

第一个问题,CVE-2018-6130是一个内存越界问题,由于对std::mapfind的使用。在下面的代码中,t10_pic_idx从RTP包中被抽取出来。

if (frame->frame_type() == kVideoFrameKey) {
   ...
   GofInfo info = gof_info_.find(codec_header.tl0_pic_idx)->second;
   FrameReceivedVp9(frame->id.picture_id, &info);
   UnwrapPictureIds(frame);
   return kHandOff;
 }

如果这个值在gof_info_数组中不存在,std::map find返回map的最终值。依赖于内存布局,间接引用这个迭代器或者引起崩溃,或者返回没有被分配的内存的内容。

第二个问题,CVE-2018-6129是一个更典型的读取越界问题,下标超出了RTP信息包,并且在被用来索引vector之前没有被验证。

第三个问题CVE-2018-6157,是一类混乱问题,当看起来像VP8的包被发送到H264解析器之后。信息包最终会被像H264包一样对待,即使它没有通过必要的H264检验步骤。此问题的影响限制在读取越界。

在浏览器中模糊化的方法存在很多限制。速度很慢,问题难以重现,并且难以模糊化大量测试案例,因为每一个通话都需要手动启动,但是某些属性,例如默认编码器,在通话中不能被改变。在我报告这些问题之后,WebRTC团队建议我使用video_replay工具,它可以用来重现所记录下的RTP流。我发现的问题有很多都不能使用此工具重现,因为它们在发信过程中使用了非默认WebRTC设置,所以我给这个工具增加了载入配置文件的能力。这就可以快速重现WebRTC中易受攻击点。

这个工具还能使模糊化速度加快,因为它可以通过模糊化RTP dump文件并将其载入到video_replay中模糊化RTP。

使用此工具模糊化导致了另外四个问题。

CVE-2018-6156或许是最具探索性的错误。它是FEC中的一个溢出。WebRTC用来处理FEC信息包的buffer大小是1500bytes,但是信息包在从RTP中抽取出来后,没有进行大小校验。实际上,信息包可能超过2000bytes.

CVE-2018-6155是一个在被称作VP8的视频编码器中的use-after-free,与WebRTC中的libvpx相对。很有趣,它影响了VP8库,有可能影响所有使用这个库的软件。目前已经发布了一个关于libvpx的解决办法。

CVE-2018-16071是一个存在于VP9处理中的use-after-free,某种程度上与 CVE-2018-6130类似。一个不受信任的下标被从信息包中取出,但是这一次它被当做一个vector清除操作的上限,因此在使用之前,可能会删除掉vector中的所有元素。

CVE-2018-16083是一个FEC中的读取越界问题,由于没有进行边界检验,引发了此问题。

总之,端到端模糊化在WebRTC中发现了很多bug,有一些问题比较严重。它们现在已经全部被修复了。这说明此方法对于发现视频会议解决方案中的易受攻击点很有效。在第二部分,我们将会尝试FaceTime上的相似方法,敬请期待!

原文标题:Adventures in Video Conferencing Part 1: The Wild World of WebRTC
链接:https://googleprojectzero.blogspot.com/2018/12/adventures-in-video-conferencing-part-1.html

填写常用邮箱,接收社区更新

WebRTC 中文社区由

运营