Project: pupnp (pupnp/pupnp), language: c++
Vulnerability instance: pupnp.cve-2020-13848
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/pupnp/pupnp/issues/177
## Description:
Issue: pupnp/pupnp#177
Title: NULL pointer dereference in FindServiceControlURLPath
State: closed
Created by: pjlantz
Created at: 2020-06-02 08:54:59+00:00

Issue Body:
There is a NULL pointer dereference in the function FindServiceControlURLPath in genlib/service_table/service_table.c.

A segmentation fault occurs if the string controlURLPath is NULL. This crash can be triggered by sending a malformed SUBSCRIBE or UNSUBSCRIBE using any of the attached files.

`cat <subscribe/unsubscribe>.txt | nc <ip> 49152`

With ASan enabled, this will  result in the following output using subscribe.txt

```
>> AddressSanitizer:DEADLYSIGNAL
=================================================================
==1382600==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f9314e77878 bp 0x000000000002 sp 0x7f930eef98d0 T6)
==1382600==The signal is caused by a READ memory access.                                                                                                                      
==1382600==Hint: address points to the zero page.
    #0 0x7f9314e77877 in FindServiceControlURLPath src/genlib/service_table/service_table.c:357
    #1 0x7f9314e85b83 in GetDeviceHandleInfoForPath src/api/upnpapi.c:4246
    #2 0x7f9314e7fda8 in gena_process_subscription_renewal_request src/gena/gena_device.c:1464
    #3 0x7f9314e7641b in dispatch_request src/genlib/miniserver/miniserver.c:157
    #4 0x7f9314e7641b in handle_request src/genlib/miniserver/miniserver.c:230
    #5 0x7f9314e6ffca in WorkerThread src/threadutil/ThreadPool.c:576
    #6 0x7f9314e3efb6 in start_thread /build/glibc-suXNNi/glibc-2.29/nptl/pthread_create.c:486
    #7 0x7f9314d702ce in __clone (/lib/x86_64-linux-gnu/libc.so.6+0xfa2ce)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV src/genlib/service_table/service_table.c:357 in FindServiceControlURLPath
Thread T6 created by T0 here:
    #0 0x7f9314ed09b2 in pthread_create (/lib/x86_64-linux-gnu/libasan.so.5+0x399b2)
    #1 0x7f9314e6f97b in CreateWorker src/threadutil/ThreadPool.c:651

==1382600==ABORTING
```

and using unsubscribe.txt

```
>> AddressSanitizer:DEADLYSIGNAL
=================================================================
==1382441==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f0c2ac29878 bp 0x000000000002 sp 0x7f0c23cf78e0 T8)
==1382441==The signal is caused by a READ memory access.                                                                                                                      
==1382441==Hint: address points to the zero page.
    #0 0x7f0c2ac29877 in FindServiceControlURLPath src/genlib/service_table/service_table.c:357
    #1 0x7f0c2ac37b83 in GetDeviceHandleInfoForPath src/api/upnpapi.c:4246
    #2 0x7f0c2ac32001 in gena_process_unsubscribe_request src/gena/gena_device.c:1577
    #3 0x7f0c2ac2841b in dispatch_request src/genlib/miniserver/miniserver.c:157
    #4 0x7f0c2ac2841b in handle_request src/genlib/miniserver/miniserver.c:230
    #5 0x7f0c2ac21fca in WorkerThread src/threadutil/ThreadPool.c:576
    #6 0x7f0c2abf0fb6 in start_thread /build/glibc-suXNNi/glibc-2.29/nptl/pthread_create.c:486
    #7 0x7f0c2ab222ce in __clone (/lib/x86_64-linux-gnu/libc.so.6+0xfa2ce)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV src/genlib/service_table/service_table.c:357 in FindServiceControlURLPath
Thread T8 created by T0 here:
    #0 0x7f0c2ac829b2 in pthread_create (/lib/x86_64-linux-gnu/libasan.so.5+0x399b2)
    #1 0x7f0c2ac2197b in CreateWorker src/threadutil/ThreadPool.c:651

==1382441==ABORTING
```

This was tested on the current master branch and on release 1.6.6. Earlier versions may also be affected.

[subscribe.txt](https://github.com/pupnp/pupnp/files/4715990/subscribe.txt)
[unsubscribe.txt](https://github.com/pupnp/pupnp/files/4715991/unsubscribe.txt)



Comments:
Comment by mrjimenez on 2020-06-04 15:05:58+00:00:
Hi Patrik,

I have just committed a fix. If possible, please test.

Regards,
Marcelo.


---

Comment by pjlantz on 2020-06-04 20:00:26+00:00:
This is fixed now.

Thanks,
Patrik

---

Comment by mrjimenez on 2020-06-04 20:11:44+00:00:
Nice! Thanks!

Marcelo.

---

Comment by carnil on 2020-06-05 15:04:55+00:00:
This issue was assigned CVE-2020-13848

Attachments:
https://github.com/pupnp/pupnp/files/4715990/subscribe.txt
https://github.com/pupnp/pupnp/files/4715991/unsubscribe.txt

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
./bootstrap
./configure CC="$CC" CFLAGS="$CFLAGS" LDFLAGS="$CFLAGS" --enable-debug --disable-shared
make -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y autoconf libtool-bin pkg-config
RUN git clone https://github.com/pupnp/pupnp pupnp
RUN git -C pupnp checkout ef04e5578a8cde2919e8c32cd6b91cb0ae6afd2e
WORKDIR $SRC/pupnp
COPY build.sh $SRC/

Additional files:
filename: FuzzIxml.c
content: /* Copyright 2022 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
      http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "ixml.h"
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#define kMinInputLength 10
#define kMaxInputLength 5120

int CheckXML(char *filename){

    int rc;
    DOMString s;
    IXML_Document *doc = NULL;

    rc = ixmlLoadDocumentEx(filename, &doc);
    if (rc != IXML_SUCCESS) {
        return rc;
    }

    s = ixmlPrintDocument(doc);
    if (s == NULL || s[0] == '\0') {
        ixmlDocument_free(doc);
        return 1;
    }

    ixmlFreeDOMString(s);
    ixmlDocument_free(doc);

    return 0;
}

extern int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {

    if (Size < kMinInputLength || Size > kMaxInputLength){
        return 1;
    }
    
    int ret;
    char filename[256];

    sprintf(filename, "/tmp/libfuzzer.%d", getpid());
    FILE *fp = fopen(filename, "wb");
    if (!fp) {
        return 0;
    }

    fwrite(Data, Size, 1, fp);
    fclose(fp);

    ret = CheckXML(filename);
    unlink(filename);
    return ret;
}