最近(更多信息),我在我最近新安装的OS小牛10.9.2的日志中发现了一条奇怪的消息
Apr 27 15:26:47 Ivans-MacBook-Pro.local apsd[194]: Unrecognized leaf certificate
大约每15分钟就会出现一次。我已经在谷歌上搜索过了,还有很多其他用户将他们的日志(很多次甚至与这个问题无关)粘贴到了有这条信息的网上。它似乎至少出现了半年。
apsd是一个不断运行的Apple Push Notification服务守护进程
它随机连接到以下服务器之一:
1-courier.push.apple.com
2-courier.push.apple.com
3-courier.push.apple.com
4-courier.push.apple.com
。
。
。
200-courier.push.apple.com
端口5223 (自定义,但SSL)
经常(检查更新,我猜)
尝试:然而,浏览器中的https://1-courier.push.apple.com:5223显示出与服务器证书相关的问题。即“此证书无效(主机名不匹配)”。我认为这是因为苹果没有创建一个包含通配符的证书。然而,使用Wireshark,我看到apsd与苹果服务器之间的通信仍在继续。我想,这不可能是对的。apsd是否忽略了有效性检查?!
然后,我试图使用一个自我签名的证书来执行一名在中间攻击中的人。这就是我在日志里得到的:
Apr 27 15:42:07 Ivans-MacBook-Pro.local apsd[194]: CFNetwork SSLHandshake failed (-9807)
Apr 27 15:42:07 Ivans-MacBook-Pro.local apsd[194]: Failed to evaluate trust: No error. (0), result=5; retrying with revocation checking optional
Apr 27 15:42:07 Ivans-MacBook-Pro.local apsd[194]: failed to evaluate trust: No error. (0), result=5; retrying with system roots
Apr 27 15:42:07 Ivans-MacBook-Pro.local apsd[194]: Failed to evaluate trust: No error. (0), result=5
Apr 27 15:42:07 Ivans-MacBook-Pro.local apsd[194]: Untrusted peer, closing connection immediately
值得注意的是,我发现(在观察Wireshark的实际流量时)服务器需要客户端发送他的证书。apsd发送:
主题名称
通用名称XXXXXXXX XXXX-XXXXXXXXXXXX (UUID)
发行人名称
美国国家
组织苹果公司
组织单位苹果iPhone
通用名称苹果iPhone设备CA
。
。
。
奇怪的是,Mac正在发送一个似乎是公钥的东西,将它显示为iPhone?我确实安装了Xcode,如果它与安装iPhone模拟器有某种关系。但是,无论Xcode是否正在运行,我总是会收到所提到的日志消息。从互联网上的日志来看,显然其他人也是如此。
那是怎么回事?
怎么才能查出我的假证书并通过他们的证书呢?这里出什么问题了吗?
注意:我的MitM实现非常简单,使用DNS欺骗,我给apsd一个响应,即X-Courier.ush.apple.com是127.0.0.1。然后,我使用一个自签名证书侦听,该证书模仿Apple的服务器证书(国家、组织、公共名称.)。但我没有实现要求apsd提供客户端证书。所以我还不知道这是否与我失败的MitM有关。可能是吗?
在Mac上,你们是否在控制台中获得相同的日志消息(在启动时出现在“所有消息”中,但实际上在system.log中),在搜索框中键入“叶子”.
更新:
我已经实现了请求客户端提供它的证书。这是一样的(SSLHandshake失败/评估信任失败)。所以apsd正在检查一些东西,我只是不太确定它是以正确的方式实现的。希望有更多经验的人能调查这件事..。
发布于 2014-05-04 19:06:04
我现在没有太多的时间,也没有对此进行彻底的验证,所以这只是我几个月前调查这件事时所记得的。
在OSX10.9和iOS 7中,苹果为push服务引入了某种类型的证书,即不允许系统知道的任何CA对其push服务器证书进行签名,但只允许特定的CA。这个证书钉住有点奇怪,因为它有某种截止日期在2014年1月1日,但我没有太多的研究,这到底意味着什么,我不知道最新版本的OS和iOS是否仍然包括这一点。
未获承认的叶证明书
因此,根据我目前的理解,此消息并不意味着apsd根本无法验证证书,但可能意味着证书钉扎无法验证它(这意味着证书钉扎当前是可选的,这反过来可能与过期日期有关)。
您可能不必直接担心这一点,因为正常的SSL证书验证仍然有效(正如您的测试确认的那样)。另一方面,我想知道为什么证书钉扎显然不起作用,以及过期日期意味着什么。
更新:下面是一些关于为什么显示日志消息的更多信息。
您可以使用openssl显示服务器证书(似乎在端口443和5223上提供了相同的证书):
openssl s_client -prexit -connect 1-courier.push.apple.com:5223 2>/dev/null | openssl x509 -noout -text |grep -E '(Subject.*CN|Serial)'
截至2014-05-10年度,以下资料如下:
Serial Number: 1277288244 (0x4c21df34)
Subject: C=US, ST=California, L=Cupertino, O=Apple Inc., CN=courier.push.apple.com
apsd
二进制文件包含几个用于锁定证书的X.509证书。我写了一个脚本以查找二进制文件中的证书。。下面是在apsd
上运行它的摘录:
+ 469280 Found cert with CN "courier.sandbox.push.apple.com" and serial "1277027356"
+ 470416 Found cert with CN "courier.push.apple.com" and serial "1276925395"
+ 471584 Found cert with CN "Entrust.net Certification Authority (2048)" and serial "946059622"
[skipping some code signing certificates]
如您所见,courier.push.apple.com
的固定证书的序列号与苹果push服务器提供的序列号不同。因此,两者都是不同的证书,显然这就是您看到的Unrecognized leaf certificate
日志消息的原因。由于apsd
仍然连接到服务器,这意味着apsd
不需要固定的叶证书来匹配。
另一件您可以看到的是第三个证书,一个CA证书。去年,当我试图让apsd
连接到推送代理时,我不得不使用替换CA证书来制作apsd
信任推送代理。这意味着,在不检查叶证书的同时,apsd
可能只允许来自某个根CA的证书。我最近没有证实这一点。
为了彻底检查这一点,除了检查为SSL连接固定的CA证书是否仍然有效外,还必须检查另一件事情。在连接到推送服务器之前,apsd
下载一个“配置袋”。此包通过HTTP下载,但已签名。该包包含主机名apsd
,然后尝试连接到。这是一种用于推代理将apsd
重定向到另一台主机的方法。您可以在推送代理自述和生成这样一个包的脚本中找到有关该包的更多信息。
发布于 2014-05-01 18:27:16
因为还没有人回应,我想也许我应该把我的方法放在我如何测试这个,然后有人也许可以指出,如果这是是否可以的。
我用python编写了一个基本服务器,侦听端口5223,如下所示:
#!/usr/bin/python
import SocketServer
import ssl
class requestHandler(SocketServer.BaseRequestHandler):
def handle(self):
print self.client_address[0] + ' connected.'
receivedData = self.request.recv(4096)
print receivedData
#self.request.sendall('test')
return
server = SocketServer.TCPServer(('0.0.0.0', 5223), requestHandler)
server.socket = ssl.wrap_socket(server.socket, server_side=True, keyfile='privateKey.pem', certfile='selfSignedCert.pem')
#server.socket = ssl.wrap_socket(server.socket, server_side=True, keyfile='privateKey.pem', certfile='selfSignedCert.pem', cert_reqs=ssl.CERT_REQUIRED, ca_certs='expectedClient.pem')
print 'Listening on port 5223'
server.serve_forever()
最后注释的server.socket行是向客户端(Apsd)询问证书的版本。我通过在控制台上调用openssl来创建证书:
openssl req -new -x509 -days 365 -nodes -out selfSignedCert.pem -keyout privateKey.pem
我使用Wireshark从实际流量中提取出的expectedClient.pem格式为DER格式。因此,我用以下代码将其转换为PEM:
#!/usr/bin/python
import ssl
inFileHandle = open('clientCert.der', 'rb')
outFileHandle = open('clientCert.pem', 'wb')
outFileContent = ssl.DER_cert_to_PEM_cert(inFileHandle.read())
outFileHandle.write(outFileContent)
inFileHandle.close()
outFileHandle.close()
现在我需要欺骗apsd,xxx-courier.push.apple.com实际上是127.0.0.1。我编写了一个基本的DNS保镖(代理),它查找具有这样地址的查询,并做出IP为127.0.0.1的响应。
以下是代码:
#!/usr/bin/python
import SocketServer
import socket
DNS_server = '8.8.8.8' # Your ISP's DNS server(8.8.8.8 is Google Public DNS)
DNS_formatted_address_match = '-courier' + chr(4) + 'push' + chr(5) + 'apple' + chr(3) + 'com' + chr(0)
class requestHandler(SocketServer.BaseRequestHandler):
def handle(self):
queryData = self.request[0]
incomingSocket = self.request[1]
transactionID = queryData[:2] # First two bytes
DNS_formatted_query_address = queryData[12:].split(chr(0))[0] + chr(0) # First 12 bytes are DNS header
if DNS_formatted_address_match in queryData:
# DNS protocol explained: http://technet.microsoft.com/en-us/library/dd197470(v=ws.10).aspx
forgedResponse = (transactionID +
chr(129) + chr(128) +
chr(0) + chr(1) +
chr(0) + chr(1) +
chr(0) + chr(0) +
chr(0) + chr(0) +
DNS_formatted_query_address +
chr(0) + chr(1) +
chr(0) + chr(1) +
chr(192) + chr(12) + chr(0) + chr(1) +
chr(0) + chr(1) +
chr(0) + chr(0) + chr(0) + chr(60) + chr(0) + chr(4) +
chr(127) + chr(0) + chr(0) + chr(1))
incomingSocket.sendto(forgedResponse, self.client_address)
else:
outgoingSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
outgoingSocket.sendto(queryData, (DNS_server, 53))
responseData = outgoingSocket.recv(4096) # DNS response should not be more than 512 bytes so this should be more than enough
incomingSocket.sendto(responseData, self.client_address)
return
server = SocketServer.UDPServer(('0.0.0.0', 53), requestHandler)
print 'Listening on port 53'
server.serve_forever()
您需要使用sudo启动它,因为否则它将不希望绑定到端口53上。最后一步是将我的网络首选项中的DNS服务器(S)更改为127.0.0.1,这样DNS查找实际上会经过保镖,然后转到ISP。
就这样!
现在你可以自己测试如果你想..。
https://security.stackexchange.com/questions/56749
复制相似问题