summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Laager <rlaager@wiktel.com>2025-07-11 18:19:31 -0500
committerRichard Laager <rlaager@wiktel.com>2025-07-11 18:43:59 -0500
commit661ecff45c5f4c41c22ef9fd4fe308100b97d6bf (patch)
tree3a8e174da9b3c4b65b55beac9c9ebdd50747a2ca
parent55c0bb748ec04354a5d63e95b3703fcec2cbd588 (diff)
downloadmonitoring-plugins-661ecff4.tar.gz
check_ssh: Fix buffer overflow
A buffer overflow was occurring when the server responded with: Exceeded MaxStartups\r\n glibc would then abort() with the following output: *** buffer overflow detected ***: terminated It was the memset() that was overflowing the buffer. But the memmove() needed fixing too. First off, there was an off-by-one error in both the memmove() and memset(). byte_offset was already set to the start of the data _past_ the newline (i.e. len + 1). For the memmove(), incrementing that by 1 again lost the first character of the additional output. For the memset(), this causes a buffer overflow. Second, the memset() has multiple issues. The comment claims that it was NULing (sic "null") the "rest". However, it has no idea how long the "rest" is, at this point. It was NULing BUFF_SZ - byte_offset + 1. After fixing the off-by-one / buffer overflow, it would be NULing BUFF_SZ - byte_offset. But that doesn't make any sense. The length of the first line has no relation to the length of the second line. For a quick-and-dirty test, add something like this just inside the while loop: memcpy(output, "Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0", sizeof("Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0")); And, after the memmove(), add: printf("output='%s'\n", output); If you fix the memset() buffer overflow, it will output: output='ext blah1 blah2 blah3 ' As you can see, the first character is lost. If you then fix the memmove(), it will output: output='next blah1 blah2 blah3' Note that this is still losing the "blah4". After moving the memset() after byte_offset is set to the new strlen() of output, then it works correctly: output='next blah1 blah2 blah3 blah4' Signed-off-by: Richard Laager <rlaager@wiktel.com>
-rw-r--r--plugins/check_ssh.c8
1 files changed, 5 insertions, 3 deletions
diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c
index 9d0d7cde..fd082e11 100644
--- a/plugins/check_ssh.c
+++ b/plugins/check_ssh.c
@@ -273,12 +273,14 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_
273 } 273 }
274 274
275 if (version_control_string == NULL) { 275 if (version_control_string == NULL) {
276 /* move unconsumed data to beginning of buffer, null rest */ 276 /* move unconsumed data to beginning of buffer */
277 memmove((void *)output, (void *)(output + byte_offset + 1), BUFF_SZ - len + 1); 277 memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset);
278 memset(output + byte_offset + 1, 0, BUFF_SZ - byte_offset + 1);
279 278
280 /*start reading from end of current line chunk on next recv*/ 279 /*start reading from end of current line chunk on next recv*/
281 byte_offset = strlen(output); 280 byte_offset = strlen(output);
281
282 /* NUL the rest of the buffer */
283 memset(output + byte_offset, 0, BUFF_SZ - byte_offset);
282 } 284 }
283 } else { 285 } else {
284 byte_offset += recv_ret; 286 byte_offset += recv_ret;