This is probably of special interest to the Linux community, as at least RedHat has mgetty in their contrib section. mgetty has been around for a while, and judging from the mailing list traffic it is in use at a significant number of sites. Program: mgetty+sendfax Version: all those that support FAX_NOTIFY_PROGRAM Platform: all Vulnerability 1: mgetty does not properly parse input from remote fax modem Impact: if the FAX_NOTIFY_PROGRAM feature is used, and fax reception is allowed, anyone who can send a fax to a machine running mgetty can execute up to 17 bytes of shell command as root on that machine. This can be escalated to full-blown root access if the attacker has a shell account on the receiving machine. It does not matter what the FAX_NOTIFY_PROGRAM does; the vulnerability can be exploited as long as mgetty has support for such a program. Workaround: disable fax reception. Recompile mgetty with 'FAX_NOTIFY_PROGRAM' undefined in policy.h. mgetty 0.98 is distributed with this macro defined by default. Exploit: Call a machine running mgetty+sendfax and send it a fax, with the fax modem's local ID string set to ';17-byte-command; The 17-byte-command will be executed using /bin/sh with root privileges. Fix: in faxlib.c, function fax_wait_for(), add code to remove characters not in the set {alphanumeric, dot, dash, plus} from string 'fax_remote_id', and enforce the limit of 20 characters. Discussion: RTFM, and you'll find the bug. Start with: http://www.leo.org/~doering/mgetty/mgetty_19.html#SEC19: >If you define FAX_NOTIFY_PROGRAM in `policy.h', mgetty will call this >program (or shell script) when a fax has been completely received. It >will be called with the following command line arguments: > >FAX_NOTIFY_PROGRAM '' \ > ... > > is 0 if the receive was successful, non-zero otherwise. > is the fax identification string received from the other >side. is the full path name for each received page. > >A sample command line might look like this: > >/usr/local/bin/new_fax 0 "+49 89 3243328" 1 /var/spool/fax/ff-01.a123 If this specification is naively implemented, it should be possible to inject a command into mgetty by storing it between "';" and ";" in the sending fax modem's ID string. A quick check of the source code reveals that this is the case. Actually, the sample command line really looks like: /usr/local/bin/new_fax 0 '+49 89 3243328' 1 /var/spool/fax/ff-01.a123 There are other instances in the source code where the incorrect form (with " characters) is used in comments, including 'policy.h-dist'. >From http://www.nb.rockwell.com/ref/1048PR3a.html: >6.5.4 +FLID=, Local ID String >Write syntax: +FLID="" >Valid value: 20-character ASCII string >Default value: Empty > >If FLID is not a null string, it generates a TSI or CSI frame. Table >3/T.30 includes digits 0-9, "+" and space. > >If the DCE supports use of Table 3/T.30 only, the response to a +FLID=? >command is "(20) (32, 43, 48-57)." If the DCE supports printable ASCII ><, the response is: "(20) (32-127)." The first "(20)" represents >string length: the second (character values) field reports supported >string values. > >1. The string is saved in RAM. >2. Non-numeric characters are not filtered out. >3. The string is right justified. As far as I can tell, this specifies the allowed format of the +FTSI and related commands for setting and reading fax modem ID strings. Note that any printable ASCII data can be sent, not just numbers. This is even mentioned elsewhere in the mgetty manual. The function fax_get_line is replaced by mdm_get_line() in mgetty version 0.99, but it is otherwise the same as in 0.98. The remote TSI is read in faxlib.c in fax_get_line(), into a 1000-byte buffer: [do {... -zb] [get a byte from fax modem in 'c' -zb] > if ( isprint( c ) && > bufferp < sizeof(buffer) ) > { > buffer[ bufferp++ ] = c; > } [} while... -zb] The set of characters for which isprint() is true includes most of the shell metacharacters. Now, in faxrec.c, in fax_notify_program(), we have: >char * line; ... > line = malloc( fax_fn_size + sizeof( FAX_NOTIFY_PROGRAM) + 100 ); ... > sprintf( line, "%s %d '%s' %d %s >%s 2>&1 FAX_NOTIFY_PROGRAM, > fax_hangup_code, > fax_remote_id, > pagenum, > fax_file_names, > CONSOLE); [ouch, only 100 bytes for a string that could be 1000! -zb] ... > r = system( line ); Note that the Rockwell specs only allow 20 characters for fax ID. Certainly if the fax protocol and modem firmware allow more than 20 characters, then there is a buffer-overrun attack possible against mgetty. It might be possible to do this by violating the fax protocol and exploiting a naive modem on the receiving end. However, you don't need any of this to get root with mgetty, as mgetty conveniently invokes system() for an attacker with data supplied by that attacker as root. Impact: If a perfectly valid and harmless fax ID such as "Joe's Diner" is used, the fax notification part of mgetty will break as a side-effect of this bug (due to mismatched ' characters in system()). Whatever command can fit in 17 ASCII bytes (20 minus the "';" and ";" necessary for the resulting string to be parsed by the shell) with values between 32 and 126 can be executed as root on the receiving machine. This is good for remote denial-of-service attacks (strlen("/bin/rm -rf /")<17), but less useful for appending entries to /etc/passwd and such, as the commands probably require more than 17 bytes of shell code. stdin/stdout/stderr are not available; they are closed in fax_notify_program() before the system() call. The modem is not in data or fax receive mode at this time, so an attacker would not be able to communicate with the command as it ran unless the command took control of the modem. If the attacker has an account on the machine and write access to a directory with a short name, then the attacker can pre-arrange for a program or shell script to be present and invoke it as root by exploiting this bug. Recommended Fixes: Fix 1. Disallow the character ' in fax_remote_id. (Actually, it probably doesn't hurt to disallow anything but {alphanumerics, dot, dash, plus} and convert everything else to underscore or ignore it. Explicitly limit the length of fax_remote_id to 20 characters. Fix 2. Replace the system() call with an execlp(), thus preventing a shell from becoming involved and also avoiding the potential buffer-overrun bug. Explicitly limit the length of fax_remote_id to 20 characters. Also make a note in the documentation about this sort of problem, as it might rear its ugly head again if someone uses a naive shell script as FAX_NOTIFY_PROGRAM. A variation is to put the ID in an environment variable. This is better design but incompatible with existing practice. Fix 3. All of the above. Extra paranoia never hurt anyone. Interestingly enough, I found the following code in faxrec.c, in fax_get_page_data(), while looking for existing code that might plug this hole: > /* filter out characters that will give problems in the shell */ > for ( j=0; fax_remote_id[j] != 0; j++ ) > { > char c = fax_remote_id[j]; > if ( c == ' ' || c == '/' || c == '\\'|| c == '&' || > c == '(' || c == ')' || c == '>' || c == '<' ) > { > if ( temp[i-1] != '-' ) temp[i++] = '-' ; > } > else if ( c != '"' && c != '\'' ) temp[i++] = c; > } > if ( temp[i-1] == '-' ) i--; > sprintf( &temp[i], ".%02d", pagenum ); This prevents incoming fax filenames from having common obnoxious characters in them. However, the list is incomplete; notably absent are characters like ` and |. The code also doesn't modify fax_remote_id, which would have quite effectively plugged this hole. -- Zygo Blaxell, Network admin, Linux system support, Windows '95 moral support Myrus Design Inc. Tel: +1 613 233 2339 Suite 203, 275 Bank St. 93 Glebe Avenue Ottawa, Ontario, Canada K2C 1E3 Ottawa, Ontario, Canada K1S 2C2 -- Zygo Blaxell. Former Unix/soft/hardware guru, U of Waterloo Computer Science Club. Current sysadmin for Myrus Design, Inc. 10th place, ACM Intl Collegiate Programming Contest Finals, 1994. Administer Linux nets for food, clothing, and anime. "I gave up $1000 to avoid working on windoze... *sigh*" - Amy Fong -- [ route@infonexus.com ] Guild member, Information enthusiast, Hacker, demon ...it's the nature of my circuitry... ...the me that you know is now made up of wires...