Package: ckermit
Version: 416~beta12-1
Severity: grave
Tags: security patch upstream
Justification: user security hole
X-Debbugs-Cc: Debian Security Team <[email protected]>

This security issue is a bit weird in that the behavior I'm covering here is
somewhat documented and was an explicit choice by upstream Kermit authors some
years back, which lingers today.  (ckermit has been around since the 1980s and
security expectations were different then)

Kermit, in its default configuration, permits a remote system to make changes
to, and retrieve data from, the local one.  A malicious remote could damage the
local system.  This is roughly akin to connecting to a FTP server where the FTP
server can also extract files from you, or change arbitrary files on your
system, without your involvement.

In the default configuration, a remote system can:

- change the working directory on the local

- overwrite files in the current working directory on the local

- download any arbitrary file from the local system

Of course, by replacing files like .bashrc or such, a malicious remote could
easily cause malicious code to be executed on the local.  Also, secret
exfiltration is easily possible with this.

This seems, to me, to be a pretty critical security issue.  The components of it
are somewhat documented in kermit help text.  I will admit to writing some time
back, at https://www.complete.org/kermit/, that "If you connect to untrusted
remote systems, I recommend running disable all to prevent the remote from doing
much to your local system other than sending files. For instance, rcd is enabled
by default and allows the remote to change the directory for you to receive
files."  But I didn't go the step further to question why this should be so and
think through the security process.

A simple patch will prevent it.

The normal mode of using kermit is to run the kermit command on your local
system.  Then, within kermit, you connect to a remote host in some way; perhaps
with ssh, or maybe with a serial line or something.

Then you can run kermit on the remote to interact with it.  In some cases, in
fact, the local kermit will automatically send "kermit -r" (when sending files
to the remote) or "kermit -x" (when receiving files from the remote) to put the
remote kermit in an appropriate receive or server mode.

When kermit is in server mode, you can issue certain REMOTE commands to it.
REMOTE CD, REMOTE DELETE, REMOTE SEND, etc.  These are all fine in the usual
case, where your local system is controlling a remote.

Further detail:

The remote system can send a series of bytes (actually a Kermit packet) that can
put the local system into server mode also.  This capability is typically used
to automatically start a file transfer when you type "kermit -s filename" to
the remote (or a similar SEND at a kermit prompt on the remote).  This is
governed by the SET TERMINAL AUTODOWNLOAD setting.  The help text states:

SET TERMINAL AUTODOWNLOAD { ON, OFF, ERROR { STOP, CONTINUE } }
  Enables/disables automatic switching into file-transfer mode when a valid
  Kermit or ZMODEM packet of the appropriate type is received during CONNECT
  mode.  Default is OFF.

  When TERMINAL AUTODOWNLOAD is ON, the TERMINAL AUTODOWNLOAD ERROR setting
  tells what to do if an error occurs during a file transfer or other
  protocol operation initiated by the terminal emulator: STOP (the default)
  means to remain in command mode so you can see what happened; CONTINUE
  means to resume the CONNECT session (e.g. so a far-end script can continue
  its work).

This is actually incorrect about the default; the default is ON, as SHOW
TERMINAL shows "Autodownload: on, error stop".  Autodownload doesn't just mean
the system will automatically download a file; it means it can automatically
enter server mode also.

SET FILE COLLISION is set to BACKUP by default.

This combines to make a nasty security situation.  For instance, let's do a
test.  The user is sitting at keyboardhost and is logging in to remotehost.

First, we'll prepare a test directory:

jgoerzen@keyboardhost:/$ cd /tmp
jgoerzen@keyboardhost:/tmp$ mkdir keyboardhosttest
jgoerzen@keyboardhost:/tmp$ cd keyboardhosttest
jgoerzen@keyboardhost:/tmp/keyboardhosttest$ echo 'Test file' > file1
jgoerzen@keyboardhost:/tmp/keyboardhosttest$ cd /

Now, we'll fire up kermit and ssh to a remote:

jgoerzen@keyboardhost:/$ kermit
(/) C-Kermit>ssh remotehost
jgoerzen@remotehost:~$

Now, we'll make a new test file:

jgoerzen@remotehost:~$ cd /tmp
jgoerzen@remotehost:/tmp$ echo 'Hi' > file1

And fire up kermit on the remote host, running inside the ssh session that is
inside our local kermit:

jgoerzen@remotehost:/tmp$ kermit
(/tmp/) C-Kermit>

Right now, our local kermit can't receive a bare file because its CWD is /,
which it doesn't have permission for.  We can, in fact, see this:

(/tmp/) C-Kermit>pwd
/tmp
(/tmp/) C-Kermit>remote pwd
Return to your local Kermit and give a SERVER command.

KERMIT READY TO SEND SERVER COMMAND...
----------------------------------------------------
Entering server mode on ssh -e none remotehost
Type Ctrl-C to quit.

C-Kermit server done
----------------------------------------------------
/
(/tmp/) C-Kermit>

Now we can issue a command *ON REMOTEHOST* to change the directory on
keyboardhost:

(/tmp/) C-Kermit>remote cd /tmp/keyboardhosttest

And now we can send our own file:

(/tmp/) C-Kermit>send file1
Return to your local Kermit and give a RECEIVE command.

KERMIT READY TO SEND...
 SENT: [/tmp/file1] To: [/tmp/keyboardhosttest/file1] (OK)

In a new terminal window on the local machine, we see:

jgoerzen@keyboardhost:~$ ls -l /tmp/keyboardhosttest/
total 2
-rw-r--r-- 1 jgoerzen jgoerzen  3 Dec  4 07:24 file1
-rw-rw-r-- 1 jgoerzen jgoerzen 10 Dec  4 07:22 file1.~1~

We have just allowed the remote machine to overwrite a file in an arbitrary
location on the local machine.

We can't delete it though:

(/tmp/) C-Kermit>remote del test1
Return to your local Kermit and give a SERVER command.

KERMIT READY TO SEND SERVER COMMAND...
Entering server mode on ssh -e none remotehost
Type Ctrl-C to quit.

C-Kermit server done

Even though this looks like it was successful, it wasn't.


Can remotehost extract arbitrary files from keyboardhost?  Yes, it turns out:

(/tmp/) C-Kermit>get /bin/bash

or

(/tmp/) C-Kermit>remote cd /bin
(/tmp/) C-Kermit>get bash

both work.

ckcmai.c defines:

#ifndef NOSERVER
/*
  Server services:
   0 = disabled
   1 = enabled in local mode
   2 = enabled in remote mode
   3 = enabled in both local and remote modes
  only as initial (default) values.
*/
int en_xit = 2;                         /* EXIT */
int en_cwd = 3;                         /* CD/CWD */
int en_cpy = 3;                         /* COPY   */
int en_del = 2;                         /* DELETE */
int en_mkd = 3;                         /* MKDIR */
int en_rmd = 2;                         /* RMDIR */
int en_dir = 3;                         /* DIRECTORY */
int en_fin = 3;                         /* FINISH */
int en_get = 3;                         /* GET */
#ifndef NOPUSH
int en_hos = 2;                         /* HOST enabled */
#else
int en_hos = 0;                         /* HOST disabled */
#endif /* NOPUSH */
int en_ren = 3;                         /* RENAME */
int en_sen = 3;                         /* SEND */
int en_set = 3;                         /* SET */
int en_spa = 3;                         /* SPACE */
int en_typ = 3;                         /* TYPE */
int en_who = 3;                         /* WHO */
#ifdef datageneral
/* Data General AOS/VS can't do this */
int en_bye = 0;                         /* BYE */
#else
int en_bye = 2;                         /* PCs in local mode... */
#endif /* datageneral */
int en_asg = 3;                         /* ASSIGN */
int en_que = 3;                         /* QUERY */
int en_ret = 2;                         /* RETRIEVE */
int en_mai = 3;                         /* MAIL */
int en_pri = 3;                         /* PRINT */
int en_ena = 3;                         /* ENABLE */
#else
int en_xit = 0, en_cwd = 0, en_cpy = 0, en_del = 0, en_mkd = 0, en_rmd = 0,
    en_dir = 0, en_fin = 0, en_get = 0, en_hos = 0, en_ren = 0, en_sen = 0,
    en_set = 0, en_spa = 0, en_typ = 0, en_who = 0, en_bye = 0, en_asg = 0,
    en_que = 0, en_ret = 0, en_mai = 0, en_pri = 0, en_ena = 0;
#endif /* NOSERVER */

SHOW SERVER duplicates this:

Function:          Status:
 GET                Enabled
 SEND               Enabled
 MAIL               Enabled
 PRINT              Enabled
 REMOTE ASSIGN      Enabled
 REMOTE CD/CWD      Enabled
 REMOTE COPY        Enabled
 REMOTE DELETE      Remote only
 REMOTE DIRECTORY   Enabled
 REMOTE HOST        Remote only
 REMOTE QUERY       Enabled
 REMOTE MKDIR       Enabled
 REMOTE RMDIR       Remote only
 REMOTE RENAME      Enabled
 REMOTE SET         Enabled
 REMOTE SPACE       Enabled
 REMOTE TYPE        Enabled
 REMOTE WHO         Enabled
 BYE                Remote only
 FINISH             Enabled
 EXIT               Remote only
 ENABLE             Enabled

keyboardhost can issue a "REMOTE DEL" to remotehost, but remotehost can't issue
a "REMOTE DEL" to keyboardhost.

So we can see that the remote system is able to control keyboardhost by issuing
any command where the enable mode is 1 or 3 in ckcmai.c.  Therefore, by changing
these defaults, the issues can be mitigated.

Note that "remote only" means the situation in which a kermit is in "remote
server" mode.  This would be the case for the system you have ssh'd to, not the
local system you're sitting at.

https://www.kermitproject.org/ckututor.html describes: "To upload files (send
them from your desktop computer to the remote Unix computer) do the same thing,
but use the -g (GET) option instead of -s on the remote computer: kermit -g
somefile.txt .  This causes your local Kermit to enter server mode; then the
remote Kermit program requests the named file and the local Kermit sends it and
returns automatically to Connect state when done. "

https://www.columbia.edu/~fdc/misc/kermit-file-transfer-overview.html

The attached patch adjusts these defaults.  I have verified that the remote
system can no longer control the local one in that way, but the local one can
still control the remote one.  This keeps all the usual use cases intact while
avoiding a more complex fate with DISABLE ALL.  The one that it might break is
kermit -g filename, executed on the remote, which would normally cause the local
system to transmit the given file.  Of course, this is one of the most serious
exfiltration risks.  The same transfer can easily (actually, MORE easily) be
initiated on the local system, without allowing the remote to have control,
using the local SEND command, so this is hardly a regression.

With the patch,

SHOW SERVER now shows:

Function:          Status:
 GET                Remote only
 SEND               Remote only
 MAIL               Remote only
 PRINT              Remote only
 REMOTE ASSIGN      Remote only
 REMOTE CD/CWD      Remote only
 REMOTE COPY        Remote only
 REMOTE DELETE      Remote only
 REMOTE DIRECTORY   Remote only
 REMOTE HOST        Remote only
 REMOTE QUERY       Remote only
 REMOTE MKDIR       Remote only
 REMOTE RMDIR       Remote only
 REMOTE RENAME      Remote only
 REMOTE SET         Remote only
 REMOTE SPACE       Remote only
 REMOTE TYPE        Remote only
 REMOTE WHO         Remote only
 BYE                Remote only
 FINISH             Remote only
 EXIT               Remote only
 ENABLE             Remote only

A remote can still initiate a transfer to the local and give a filename
to save it in.  This patch also disables the default "collision" mode,
which allows the remote to overwrite a local file (and renames the
original local file).  There may still be some reason for concern for
allowing the remote to place a file with an arbitrary name in the
local's CWD, but this is also the behavior for zmodem (in lrzsz), and
some CLI HTTP clients that follow redirects and use the resulting name
for the local.  It may not be ideal but it seems to match the generally
expected behavior.

-- System Information:
Debian Release: 13.2
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 6.12.57+deb13-amd64 (SMP w/16 CPU threads; PREEMPT)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_OOT_MODULE, 
TAINT_UNSIGNED_MODULE
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages ckermit depends on:
ii  debconf [debconf-2.0]  1.5.91
ii  libc6                  2.41-12
ii  libncurses6            6.5+20250216-2
ii  libpam0g               1.7.0-5
ii  libssl3t64             3.5.4-1~deb13u1
ii  libtinfo6              6.5+20250216-2

Versions of packages ckermit recommends:
ii  openssh-client [ssh-client]  1:10.0p1-7

Versions of packages ckermit suggests:
pn  openbsd-inetd | inet-superserver  <none>

-- debconf information excluded
commit dc4be0e1d17c674df976dd41c6fa381c767aa76d
Author: John Goerzen <[email protected]>
Date:   Thu Dec 4 07:46:09 2025 -0600

    Work around security issues
    
    Disable insecure remote commands for local users
    
    Also reject overwrites by default

diff --git a/ckcmai.c b/ckcmai.c
index a84b9cb..39a5049 100644
--- a/ckcmai.c
+++ b/ckcmai.c
@@ -724,7 +724,7 @@ struct ck_p ptab[NPROTOS] = {           /* Initialize the 
Kermit part ... */
 #ifdef VMS                              /* Default filename collision action */
     XYFX_X,                             /* REPLACE for VAX/VMS */
 #else
-    XYFX_B,                             /* BACKUP for everybody else */
+    XYFX_D,                             /* REJECT for everybody else */
 #endif /* VMS */
 
 #ifdef OS2                              /* Flag for file name conversion */
@@ -1574,37 +1574,37 @@ char * remdest = NULL;
   only as initial (default) values.
 */
 int en_xit = 2;                         /* EXIT */
-int en_cwd = 3;                         /* CD/CWD */
-int en_cpy = 3;                         /* COPY   */
+int en_cwd = 2;                         /* CD/CWD */
+int en_cpy = 2;                         /* COPY   */
 int en_del = 2;                         /* DELETE */
-int en_mkd = 3;                         /* MKDIR */
+int en_mkd = 2;                         /* MKDIR */
 int en_rmd = 2;                         /* RMDIR */
-int en_dir = 3;                         /* DIRECTORY */
-int en_fin = 3;                         /* FINISH */
-int en_get = 3;                         /* GET */
+int en_dir = 2;                         /* DIRECTORY */
+int en_fin = 2;                         /* FINISH */
+int en_get = 2;                         /* GET */
 #ifndef NOPUSH
 int en_hos = 2;                         /* HOST enabled */
 #else
 int en_hos = 0;                         /* HOST disabled */
 #endif /* NOPUSH */
-int en_ren = 3;                         /* RENAME */
-int en_sen = 3;                         /* SEND */
-int en_set = 3;                         /* SET */
-int en_spa = 3;                         /* SPACE */
-int en_typ = 3;                         /* TYPE */
-int en_who = 3;                         /* WHO */
+int en_ren = 2;                         /* RENAME */
+int en_sen = 2;                         /* SEND */
+int en_set = 2;                         /* SET */
+int en_spa = 2;                         /* SPACE */
+int en_typ = 2;                         /* TYPE */
+int en_who = 2;                         /* WHO */
 #ifdef datageneral
 /* Data General AOS/VS can't do this */
 int en_bye = 0;                         /* BYE */
 #else
 int en_bye = 2;                         /* PCs in local mode... */
 #endif /* datageneral */
-int en_asg = 3;                         /* ASSIGN */
-int en_que = 3;                         /* QUERY */
+int en_asg = 2;                         /* ASSIGN */
+int en_que = 2;                         /* QUERY */
 int en_ret = 2;                         /* RETRIEVE */
-int en_mai = 3;                         /* MAIL */
-int en_pri = 3;                         /* PRINT */
-int en_ena = 3;                         /* ENABLE */
+int en_mai = 2;                         /* MAIL */
+int en_pri = 2;                         /* PRINT */
+int en_ena = 2;                         /* ENABLE */
 #else
 int en_xit = 0, en_cwd = 0, en_cpy = 0, en_del = 0, en_mkd = 0, en_rmd = 0,
     en_dir = 0, en_fin = 0, en_get = 0, en_hos = 0, en_ren = 0, en_sen = 0,
diff --git a/ckuus2.c b/ckuus2.c
index 3e8bd45..cc3da82 100644
--- a/ckuus2.c
+++ b/ckuus2.c
@@ -4138,12 +4138,12 @@ static char *hmxyf[] = {
 "SET FILE COLLISION option",
 "  Tells what to do when a file arrives that has the same name as",
 "  an existing file.  The options are:",
-"   BACKUP (default) - Rename the old file to a new, unique name and store",
+"   BACKUP - Rename the old file to a new, unique name and store",
 "     the incoming file under the name it was sent with.",
 "   OVERWRITE - Overwrite (replace) the existing file; doesn't work for",
 "     a Kermit server unless you also tell it to ENABLE DELETE.",
 "   APPEND - Append the incoming file to the end of the existing file.",
-"   REJECT - Refuse and/or discard the incoming file (= DISCARD).",
+"   REJECT (default) - Refuse and/or discard the incoming file (= DISCARD).",
 "   RENAME - Give the incoming file a unique name.",
 "   UPDATE - Accept the incoming file only if newer than the existing file.",
 " ",
@@ -7929,7 +7929,7 @@ static char *hxyterm[] = {
 "SET TERMINAL AUTODOWNLOAD { ON, OFF, ERROR { STOP, CONTINUE } }",
 "  enables/disables automatic switching into file-transfer mode when a Kermit",
 "  or ZMODEM file transfer has been detected during CONNECT mode or while",
-"  an INPUT command is active.  Default is OFF.",
+"  an INPUT command is active.  Default is ON.",
 #else
 "SET TERMINAL AUTODOWNLOAD { ON, OFF, ERROR { STOP, CONTINUE } }",
 "  enables/disables automatic switching into file-transfer mode when a Kermit",

Reply via email to