作者:瘦蛟舞
公众号:小米安全中心
安全性提升,有效对抗绝大部分中间人攻击。
中间人攻击部分场景
HTTP劫持
DNS劫持
ARP欺骗
钓鱼WIFI
伪基站
TLS的主要目标是使SSL更安全,并使协议的规范更精确和完善,本文统一使用SSL泛指。
设备ROM发版相对app发版要复杂许多,所以设备的证书验证场景复杂度更高,先将设备抽象成两大类:
如果设备是第一类通用操作系统比较好处理
如果证书是CA签发的,只需信任系统证书即可,最好同时开启系统分区保护。
如果证书是自签发的,除了信任系统证书以外额外只信任此自签发证书即可,切勿为了跑通业务盲目信任所有证书。一些业务刚开发的时候可能还没买证书,所以初期代码是信任所有证书,后来买正式证书后忘记修复证书信任代码。例如没买证书之前curl使用了-k参数,买完证书后忘记统一除去此参数。
-k, --insecure Allow connections to SSL sites without certs (H)
如果设备是第二类RTOS,首先得确认其是否支持SSL,其上运行的业务是否需要SSL。如果需要且支持,则可以通过自行预制根证书。
0x02中已经有一个ssl的简单错误示范了,接下来再讲一个错误开发案例。
ssl相关开发的时候遇到如下错误提示,就是证书验证不通过:
curl_easy_perform failed : SSL peer certificate or SSH remote key was not OK
项目着急上线,就使用网上的提示的方法解决这个报错。看似解决问题,实际上留下了安全隐患:
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
因为将verify参数设置为0,此时会忽略所有的证书相关错误,也就意味着会对所有的证书都信任。如果有攻击者进行证书注入劫持,那么他将能看到明文通信,SSL失去了其本来的作用:
#include <curl/curl.h>CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSL_VERIFYPEER, long verify);
被劫持的后的效果如下,设备的通信的信息完全暴露给攻击者。
我们是如何快速定位这个漏洞的呢?这里安利一下小米安全开发的MiEye自动化IoT安全测试系统,可以完全捕获此类漏洞。https://eye.dun.mi.com/
最后汇总下此类错误操作:
上面的错误操作会使设备处于安全等级0,这里建议IoT设备应根据业务处于2或3的等级。
本文不是理论向文章,但是方便大家抓住重点,还是提一些基本知识点。
SSL握手过程(RSA)如下图,根据使用的密钥交换算法的不同协商细节略有不同但总体类似:
信任链的关键:双方共有的根证书保证了秘钥协商的可靠性。
同理API的使用关键就在证书的处理环节。
正确的SSL证书验证设置如下,默认就是如此,若要对其进行修改需要十分谨慎:
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
如果设备内没有对应的根证书,需要先找到对应域名的根证书,可以通过浏览器观察。比如这个case里的证书是Godaddy的,之后下载GoDaddy Root Certificate Authority - G2这个根证书并埋入设备。
Godaddy根证书下载地址:https://ssl-ccp.godaddy.com/repository?origin=CALLISTO
若参考curl文档中的方法拿到的证书是站点证书,有效期较短.而IoT设备的发版周期较长,易造成可用性降低。故本文一直是围绕根证书展开。
注意:下面命令拿到的是有效期较短的站点证书而非根证书。
openssl s_client -servername www.example.com -connect www.example.com:443 < /dev/null | sed -n "/-----BEGIN/,/-----END/p" > www.example.com.pem
当然一个设备可能访问很多个域,然后需要预制很多个根,假设另一个常用根是DigiCert,再去DigiCert下载对应根证书埋入设备。
DigiCert根证书下载地址:https://www.digicert.com/digicert-root-certificates.htm
可以通过CURLOPT_CAINFO来指定一个根证书:
CURLOPT_CAINFO - path to Certificate Authority (CA) bundle
This option is by default set to the system path where libcurl's cacert bundle is assumed to be stored, as estab-lished at build time.
浏览器和操作系统中一般都会自带大量可信CA根证书,例如:
除此之外还有类似aosp.pem,apple.pem,microsoft.pem,java.pem,mozilla.pem等作为补充。Firefox的内置证书如下: https://wiki.mozilla.org/CA/Included_Certificates
可以把缺失的证书追加到证书的列表,也可以考虑直接用Mozilla的证书列表:
cat newcert.pem >> /etc/pki/tls/certs/ca-bundle.crt
还可以通过CURLOPT_CAPATH来指定对应根证书目录,如果有多个ca需要添加可以考虑它(新增):
CURLOPT_CAPATH - specify directory holding CA certificates
#include <curl/curl.h> CURLcode curl_easy_setopt(CURL *handle, CURLOPT_CAPATH, char *capath);
SSL pinning新增对抗场景:
客户端安装恶意证书,IoT上这种情景极少
一些WiFi需要你添加根证书信任才能使用互联网
一些网站需要你添加根证书信任才能不反复红叉提示
其他CA恶意签发站点证书
WoSign和Symantec都有过一段时期签发的证书不受信任的历史
https://news.mindynode.com/zh/events/50(还有StartCom和CNNIC)
最常见的就是HTTPS用SSL和TLS作为通信进行加密,然后再进行http的传输。除了http外SSL/TLS也可以用保护其他的协议:FTP,IMAP, POP3,SMTP等等。
不过一般不推荐使用CURLOPT_PINNEDPUBLICKEY,其他库同理不推荐在IoT设备上做站点证书锁定。前面也提到过了站点证书有效期太短,而IoT设备发版周期漫长,做一套可靠证书指纹更新的方案性价比极低,稍有不慎就会导致业务不可用。
NAME CURLOPT_PINNEDPUBLICKEY - set pinned public keySYNOPSIS #include <curl/curl.h> CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PINNEDPUBLICKEY, char *pinnedpubkey);
看文档没说支持intermediate & root certificate pin,所以curl自带的锁定只支持leaf certificate pin
https://curl.haxx.se/docs/todo.html
13.11 Support intermediate & root pinning for PINNEDPUBLICKEY
CURLOPT_PINNEDPUBLICKEY does not consider the hashes of intermediate & root certificates when comparing the pinned keys. Therefore it is not compatible with "HTTP Public Key Pinning" as there also intermediate and root certificates can be pinned. This is very useful as it prevents webadmins from "locking themself out of their servers".
更多的证书锁定的细节可以去参考我的另一篇文章:SSL.Pinning.Practice
https://github.com/WooyunDota/DroidDrops/blob/master/2018/SSL.Pinning.Practice.md
https://blog.cloudflare.com/keyless-ssl-the-nitty-gritty-technical-details/
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1157/