mupnp
mupnp copied to clipboard
Crashes in HTTP server found with fuzzing
Note: this all based on latest master branch. I used default configure options except I built with ASAN when fuzzing . Given that a basic HTTP server (which does not even consider the request) is affected, I assume anything that uses this HTTP library like the SOAP server for UPNP would also be affected.
I found out of bounds read errors and some cases where memcpy would be called with negative size. In the string tokenizer, because lastPos is a size_t and is calculated by doing strlen(value)-1, when the tokenizer was initialized with an empty value it would overflow. Some calculations are performed on indexes in uri.c that can result in a negative value are passed to mupnp_string_setvalue which results in a negative value being passed to memcpy.
These changes stopped the crashes:
diff --git a/src/mupnp/net/uri.c b/src/mupnp/net/uri.c
index 2f77bd5..6b181e0 100644
--- a/src/mupnp/net/uri.c
+++ b/src/mupnp/net/uri.c
@@ -9,6 +9,7 @@
*
******************************************************************/
+#include <string.h>
#include <mupnp/net/uri.h>
#include <mupnp/util/log.h>
#include <mupnp/util/string.h>
@@ -149,7 +150,8 @@ void mupnp_net_uri_setvalue(mUpnpNetURI* uri, const char* value)
mupnp_string_setnvalue(uri->user, value + currIdx, colonIdx);
mupnp_string_setnvalue(uri->password, value + currIdx + colonIdx + 1, atIdx - (colonIdx + 1));
}
- else
+ else if (atIdx >= currIdx)
+ // fix negative size memcpy
mupnp_string_setnvalue(uri->user, value + currIdx, atIdx - currIdx);
currIdx += atIdx + 1;
}
@@ -174,13 +176,16 @@ void mupnp_net_uri_setvalue(mUpnpNetURI* uri, const char* value)
/**** host ****/
mupnp_string_setnvalue(uri->host, mupnp_string_getvalue(hostStr), colonIdx);
host = mupnp_net_uri_gethost(uri);
+ // recalculate len to fix potential OOB read
+ hostLen = strlen(host);
if (0 < hostLen) {
if (host[0] == '[' && host[hostLen - 1] == ']')
mupnp_string_setnvalue(uri->host, mupnp_string_getvalue(hostStr) + 1, colonIdx - 2);
}
/**** port ****/
portStr = mupnp_string_new();
- mupnp_string_setnvalue(portStr, mupnp_string_getvalue(hostStr) + colonIdx + 1, hostLen - colonIdx - 1);
+ // fix negative size memcpy
+ mupnp_string_setnvalue(portStr, mupnp_string_getvalue(hostStr) + colonIdx + 1, (hostLen == colonIdx) ? 0: hostLen - colonIdx - 1);
uri->port = atoi(mupnp_string_getvalue(portStr));
mupnp_string_delete(portStr);
mupnp_string_delete(hostStr);
diff --git a/src/mupnp/util/string_tokenizer.c b/src/mupnp/util/string_tokenizer.c
index b9648dd..bbe9ee7 100644
--- a/src/mupnp/util/string_tokenizer.c
+++ b/src/mupnp/util/string_tokenizer.c
@@ -29,7 +29,10 @@ mUpnpStringTokenizer* mupnp_string_tokenizer_new(const char* value, const char*
strToken->delim = mupnp_strdup(delim);
strToken->delimCnt = mupnp_strlen(strToken->delim);
strToken->nextStartPos = 0;
- strToken->lastPos = mupnp_strlen(value) - 1;
+ // prevent size_t overflow
+ if ((strToken->lastPos = mupnp_strlen(value)) > 0) {
+ strToken->lastPos--;
+ };
strToken->currToken = NULL;
strToken->nextToken = NULL;
mupnp_string_tokenizer_nexttoken(strToken);
But the software hangs in some cases and I don't know enough about the architecture of it to really tell why.
If I build from master, without ASAN and without the patches I mentioned above (tested on Debian 12 and Void Linux with gcc 12.2.0), and then build this test http server:
#include <string.h>
#include <unistd.h>
#include <mupnp/http/http.h>
#define MUPNP_TESTCASE_HTTP_PORT 8080
#define MUPNP_TESTCASE_HTTP_PAGE "<HTML><BODY>Hello World</BODY></HTML>"
#define MUPNP_TESTCASE_HTTP_URL "/index.html"
void ClinkTestcaseHttpRequestRecieved(mUpnpHttpRequest* httpReq) {
mUpnpHttpResponse* httpRes;
httpRes = mupnp_http_response_new();
mupnp_http_response_setstatuscode(httpRes, MUPNP_HTTP_STATUS_OK);
mupnp_http_response_setcontent(httpRes, MUPNP_TESTCASE_HTTP_PAGE);
mupnp_http_response_setcontenttype(httpRes, "text/html");
mupnp_http_response_setcontentlength(httpRes, strlen(MUPNP_TESTCASE_HTTP_PAGE));
mupnp_http_request_postresponse(httpReq, httpRes);
mupnp_http_response_delete(httpRes);
}
int main(int argc, const char *const argv[]) {
mUpnpHttpServer* httpServer = mupnp_http_server_new();
mupnp_http_server_open(httpServer, MUPNP_TESTCASE_HTTP_PORT, NULL);
mupnp_http_server_setlistener(httpServer, ClinkTestcaseHttpRequestRecieved);
mupnp_http_server_start(httpServer);
while (1) {
sleep(1);
};
mupnp_http_server_stop(httpServer);
return EXIT_SUCCESS;
}
I get a segfault if I do:
$ nc 127.0.0.1 8080 < out/SIGABRT.PC.7ffff76a47dc.STACK.cbe64d865.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz
I get a segfault if I do:
$ nc 127.0.0.1 8080 < out/SIGABRT.PC.7ffff76a47dc.STACK.1a4d1cc9b7.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz
$ nc 127.0.0.1 8080 < out/SIGABRT.PC.7ffff76a47dc.STACK.1a4d1cc9b7.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz
Here is report (see attached for crash files):
=====================================================================
TIME: 2023-12-28.15:00:26
=====================================================================
FUZZER ARGS:
mutationsPerRun : 5
externalCmd : NULL
fuzzStdin : FALSE
timeout : 3 (sec)
ignoreAddr : (nil)
ASLimit : 0 (MiB)
RSSLimit : 0 (MiB)
DATALimit : 0 (MiB)
wordlistFile : httpd.wordlist
dynFileMethod :
fuzzTarget : ./server
CRASH:
DESCRIPTION: AddressSanitizer: heap-buffer-overflow on address 0x602000039571 at pc 0x555555563c85 bp 0x7fffc91ab780 sp 0x7fffc91ab770
ORIG_FNAME: 0b701e39ffca5fd9fead0a7c5558eba5.00004d2b.honggfuzz.cov
FUZZ_FNAME: out/SIGABRT.PC.555555563c85.STACK.18f46012ff.CODE.-6.ADDR.0.INSTR.mov____-0x78(%rbp),%rax.fuzz
PID: 14033
SIGNAL: SIGABRT (6)
PC: 0x555555563c85
FAULT ADDRESS: 0x0
INSTRUCTION: mov____-0x78(%rbp),%rax
STACK HASH: 00000018f46012ff
STACK:
<0x0000555555563c84> [func:mupnp_string_tokenizer_nexttoken file:../../src/mupnp/util/string_tokenizer.c line:91 module:]
<0x0000555555564048> [func:mupnp_string_tokenizer_new file:../../src/mupnp/util/string_tokenizer.c line:35 module:]
<0x000055555556220e> [func:mupnp_http_packet_read_headers file:../../src/mupnp/http/http_packet.c line:364 module:]
<0x000055555555f359> [func:mupnp_http_request_read file:../../src/mupnp/http/http_request.c line:740 module:]
<0x0000555555560bf8> [func:mupnp_http_server_clientthread file:../../src/mupnp/http/http_server.c line:186 module:]
<0x0000555555564359> [func:PosixThreadProc file:../../src/mupnp/util/thread.c line:169 module:]
<0x00007ffff76a2ae3> [func:start_thread file:nptl/pthread_create.c line:442 module:]
<0x00007ffff772393b> [func:clone3 file:../sysdeps/unix/sysv/linux/x86_64/clone3.S line:81 module:]
=====================================================================
=====================================================================
TIME: 2023-12-28.15:00:26
=====================================================================
FUZZER ARGS:
mutationsPerRun : 5
externalCmd : NULL
fuzzStdin : FALSE
timeout : 3 (sec)
ignoreAddr : (nil)
ASLimit : 0 (MiB)
RSSLimit : 0 (MiB)
DATALimit : 0 (MiB)
wordlistFile : httpd.wordlist
dynFileMethod :
fuzzTarget : ./server
CRASH:
DESCRIPTION: AddressSanitizer: heap-buffer-overflow on address 0x6020000763d1 at pc 0x555555563c85 bp 0x7fffaa4307f0 sp 0x7fffaa4307e0
ORIG_FNAME: 8ce621921c127db7ffdcdfee4205831c.000090ca.honggfuzz.cov
FUZZ_FNAME: out/SIGABRT.PC.555555563c85.STACK.1a4d1cc9b7.CODE.-6.ADDR.0.INSTR.mov____-0x78(%rbp),%rax.fuzz
PID: 14086
SIGNAL: SIGABRT (6)
PC: 0x555555563c85
FAULT ADDRESS: 0x0
INSTRUCTION: mov____-0x78(%rbp),%rax
STACK HASH: 0000001a4d1cc9b7
STACK:
<0x0000555555563c84> [func:mupnp_string_tokenizer_nexttoken file:../../src/mupnp/util/string_tokenizer.c line:91 module:]
<0x0000555555564048> [func:mupnp_string_tokenizer_new file:../../src/mupnp/util/string_tokenizer.c line:35 module:]
<0x000055555555f17c> [func:mupnp_http_request_read file:../../src/mupnp/http/http_request.c line:706 module:]
<0x0000555555560bf8> [func:mupnp_http_server_clientthread file:../../src/mupnp/http/http_server.c line:186 module:]
<0x0000555555564359> [func:PosixThreadProc file:../../src/mupnp/util/thread.c line:169 module:]
<0x00007ffff76a2ae3> [func:start_thread file:nptl/pthread_create.c line:442 module:]
<0x00007ffff772393b> [func:clone3 file:../sysdeps/unix/sysv/linux/x86_64/clone3.S line:81 module:]
=====================================================================
=====================================================================
TIME: 2023-12-28.15:03:28
=====================================================================
FUZZER ARGS:
mutationsPerRun : 5
externalCmd : NULL
fuzzStdin : FALSE
timeout : 3 (sec)
ignoreAddr : (nil)
ASLimit : 0 (MiB)
RSSLimit : 0 (MiB)
DATALimit : 0 (MiB)
wordlistFile : httpd.wordlist
dynFileMethod :
fuzzTarget : ./server
CRASH:
DESCRIPTION:
ORIG_FNAME: 0922b49fc7f2eda643ae7f915f4c4182.0000e088.honggfuzz.cov
FUZZ_FNAME: out/SIGABRT.PC.7ffff76a47dc.STACK.1a4d1cc9b7.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz
PID: 20076
SIGNAL: SIGABRT (6)
PC: 0x7ffff76a47dc
FAULT ADDRESS: 0x0
INSTRUCTION: mov____%eax,%ebp
STACK HASH: 0000001a4d1cc9b7
STACK:
<0x0000555555563c84> [func:mupnp_string_tokenizer_nexttoken file:../../src/mupnp/util/string_tokenizer.c line:91 module:]
<0x0000555555564048> [func:mupnp_string_tokenizer_new file:../../src/mupnp/util/string_tokenizer.c line:35 module:]
<0x000055555555f17c> [func:mupnp_http_request_read file:../../src/mupnp/http/http_request.c line:706 module:]
<0x0000555555560bf8> [func:mupnp_http_server_clientthread file:../../src/mupnp/http/http_server.c line:186 module:]
<0x0000555555564359> [func:PosixThreadProc file:../../src/mupnp/util/thread.c line:169 module:]
<0x00007ffff76a2ae3> [func:start_thread file:nptl/pthread_create.c line:442 module:]
<0x00007ffff772393b> [func:clone3 file:../sysdeps/unix/sysv/linux/x86_64/clone3.S line:81 module:]
=====================================================================
=====================================================================
TIME: 2023-12-28.15:05:50
=====================================================================
FUZZER ARGS:
mutationsPerRun : 5
externalCmd : NULL
fuzzStdin : FALSE
timeout : 3 (sec)
ignoreAddr : (nil)
ASLimit : 0 (MiB)
RSSLimit : 0 (MiB)
DATALimit : 0 (MiB)
wordlistFile : httpd.wordlist
dynFileMethod :
fuzzTarget : ./server
CRASH:
DESCRIPTION:
ORIG_FNAME: 016aefb90f31dc9fa8380ae9ccdffba8.00017f81.honggfuzz.cov
FUZZ_FNAME: out/SIGABRT.PC.7ffff76a47dc.STACK.18f46012ff.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz
PID: 24884
SIGNAL: SIGABRT (6)
PC: 0x7ffff76a47dc
FAULT ADDRESS: 0x0
INSTRUCTION: mov____%eax,%ebp
STACK HASH: 00000018f46012ff
STACK:
<0x0000555555563c84> [func:mupnp_string_tokenizer_nexttoken file:../../src/mupnp/util/string_tokenizer.c line:91 module:]
<0x0000555555564048> [func:mupnp_string_tokenizer_new file:../../src/mupnp/util/string_tokenizer.c line:35 module:]
<0x000055555556220e> [func:mupnp_http_packet_read_headers file:../../src/mupnp/http/http_packet.c line:364 module:]
<0x000055555555f359> [func:mupnp_http_request_read file:../../src/mupnp/http/http_request.c line:740 module:]
<0x0000555555560bf8> [func:mupnp_http_server_clientthread file:../../src/mupnp/http/http_server.c line:186 module:]
<0x0000555555564359> [func:PosixThreadProc file:../../src/mupnp/util/thread.c line:169 module:]
<0x00007ffff76a2ae3> [func:start_thread file:nptl/pthread_create.c line:442 module:]
<0x00007ffff772393b> [func:clone3 file:../sysdeps/unix/sysv/linux/x86_64/clone3.S line:81 module:]
=====================================================================
=====================================================================
TIME: 2023-12-28.15:07:14
=====================================================================
FUZZER ARGS:
mutationsPerRun : 5
externalCmd : NULL
fuzzStdin : FALSE
timeout : 3 (sec)
ignoreAddr : (nil)
ASLimit : 0 (MiB)
RSSLimit : 0 (MiB)
DATALimit : 0 (MiB)
wordlistFile : httpd.wordlist
dynFileMethod :
fuzzTarget : ./server
CRASH:
DESCRIPTION: AddressSanitizer: heap-buffer-overflow on address 0x602000012196 at pc 0x55555556a6eb bp 0x7fffdb4d1840 sp 0x7fffdb4d1830
ORIG_FNAME: 81e0e77432db3eecc1b20b5a4fe5e542.00000040.honggfuzz.cov
FUZZ_FNAME: out/SIGABRT.PC.55555556a6eb.STACK.3bf1c84c9.CODE.-6.ADDR.0.INSTR.mov____-0x40(%rbp),%rdi.fuzz
PID: 26875
SIGNAL: SIGABRT (6)
PC: 0x55555556a6eb
FAULT ADDRESS: 0x0
INSTRUCTION: mov____-0x40(%rbp),%rdi
STACK HASH: 00000003bf1c84c9
STACK:
<0x000055555556a6ea> [func:mupnp_net_uri_setvalue file:../../src/mupnp/net/uri.c line:178 module:]
<0x000055555555f2de> [func:mupnp_http_request_read file:../../src/mupnp/http/http_request.c line:729 module:]
<0x0000555555560bf8> [func:mupnp_http_server_clientthread file:../../src/mupnp/http/http_server.c line:186 module:]
<0x0000555555564359> [func:PosixThreadProc file:../../src/mupnp/util/thread.c line:169 module:]
<0x00007ffff76a2ae3> [func:start_thread file:nptl/pthread_create.c line:442 module:]
<0x00007ffff772393b> [func:clone3 file:../sysdeps/unix/sysv/linux/x86_64/clone3.S line:81 module:]
=====================================================================
=====================================================================
TIME: 2023-12-28.15:07:57
=====================================================================
FUZZER ARGS:
mutationsPerRun : 5
externalCmd : NULL
fuzzStdin : FALSE
timeout : 3 (sec)
ignoreAddr : (nil)
ASLimit : 0 (MiB)
RSSLimit : 0 (MiB)
DATALimit : 0 (MiB)
wordlistFile : httpd.wordlist
dynFileMethod :
fuzzTarget : ./server
CRASH:
DESCRIPTION:
ORIG_FNAME: 6516e17094d9c1dd277f34631511e95d.00000104.honggfuzz.cov
FUZZ_FNAME: out/SIGABRT.PC.7ffff76a47dc.STACK.3bf1c84c9.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz
PID: 1093
SIGNAL: SIGABRT (6)
PC: 0x7ffff76a47dc
FAULT ADDRESS: 0x0
INSTRUCTION: mov____%eax,%ebp
STACK HASH: 00000003bf1c84c9
STACK:
<0x000055555556a6ea> [func:mupnp_net_uri_setvalue file:../../src/mupnp/net/uri.c line:178 module:]
<0x000055555555f2de> [func:mupnp_http_request_read file:../../src/mupnp/http/http_request.c line:729 module:]
<0x0000555555560bf8> [func:mupnp_http_server_clientthread file:../../src/mupnp/http/http_server.c line:186 module:]
<0x0000555555564359> [func:PosixThreadProc file:../../src/mupnp/util/thread.c line:169 module:]
<0x00007ffff76a2ae3> [func:start_thread file:nptl/pthread_create.c line:442 module:]
<0x00007ffff772393b> [func:clone3 file:../sysdeps/unix/sysv/linux/x86_64/clone3.S line:81 module:]
=====================================================================
=====================================================================
TIME: 2023-12-28.15:08:38
=====================================================================
FUZZER ARGS:
mutationsPerRun : 5
externalCmd : NULL
fuzzStdin : FALSE
timeout : 3 (sec)
ignoreAddr : (nil)
ASLimit : 0 (MiB)
RSSLimit : 0 (MiB)
DATALimit : 0 (MiB)
wordlistFile : httpd.wordlist
dynFileMethod :
fuzzTarget : ./server
CRASH:
DESCRIPTION: AddressSanitizer: negative-size-param: (size=-1)
ORIG_FNAME: 1b6df3114644e8c40ad37f9df618687f.00000040.honggfuzz.cov
FUZZ_FNAME: out/SIGABRT.PC.7ffff76a47dc.STACK.cbe64d865.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz
PID: 8307
SIGNAL: SIGABRT (6)
PC: 0x7ffff76a47dc
FAULT ADDRESS: 0x0
INSTRUCTION: mov____%eax,%ebp
STACK HASH: 0000000cbe64d865
STACK:
<0x00007ffff7848f4f> [func:__interceptor_memcpy file: line:0 module:/usr/lib/libasan.so.8+0x48f4f]
<0x0000555555562ef7> [func:mupnp_string_setnvalue file:../../src/mupnp/util/string.c line:147 module:]
<0x000055555556a3e0> [func:mupnp_net_uri_setvalue file:../../src/mupnp/net/uri.c line:153 module:]
<0x000055555555f2de> [func:mupnp_http_request_read file:../../src/mupnp/http/http_request.c line:729 module:]
<0x0000555555560bf8> [func:mupnp_http_server_clientthread file:../../src/mupnp/http/http_server.c line:186 module:]
<0x0000555555564359> [func:PosixThreadProc file:../../src/mupnp/util/thread.c line:169 module:]
<0x00007ffff76a2ae3> [func:start_thread file:nptl/pthread_create.c line:442 module:]
<0x00007ffff772393b> [func:clone3 file:../sysdeps/unix/sysv/linux/x86_64/clone3.S line:81 module:]
=====================================================================
Crash cases: fuzz.zip
Confirmed the same crashes affect the UPNP control point HTTP server initialized like this:
#include <string.h>
#include "TestDevice.h"
#include <unistd.h>
#include <assert.h>
////////////////////////////////////////
// testControlPoint
////////////////////////////////////////
int main(void){
mUpnpControlPoint* testCp = mupnp_controlpoint_new();
assert(mupnp_controlpoint_start(testCp));
while (1) {
sleep(1);
}
assert(mupnp_controlpoint_stop(testCp));
mupnp_controlpoint_delete(testCp);
}