A recent vulnerability in GNU screen caused some people to reconsider their commitment to the venerable terminal multiplexing program. GNU screen is probably used by thousands of old sysadmins around the world to run long-standing processes and, particularly, IRC sessions, which are especially vulnerable to arbitrary garbage coming on ... screen, so to speak.

So this vulnerability matters, and you should definitely pay attention to it. If you haven't switched to tmux yet, now might be a good time to get your fingers trained. But don't switch to it just yet for your IRC session, and read on for a better, more secure solution.

After all, it's not because we found this flaw in screen that it doesn't exist in tmux (or your favorite terminal emulator, for that matter, a much scarier thought).

  1. Hardening my bouncer
  2. Splitting into another user
  3. Hardening irssi
  4. Password-less remote irssi
  5. Configuration management
  6. Updates

Hardening my bouncer

Back in March 2019, I had already switched away from screen for IRC, but not to tmux like many did, but to dtach. I figured that I didn't actually need multiplexing to run my long-running IRC session: I just needed to be able to reattach to the terminal. That's what dtach does. No windows, no panes, and, especially, no way to start a new shell, which is exactly the kind of hardening I need.

So I came up with this, to start irssi:

dtach -N /run/$USER/dtach-irssi.socket irssi

To attach:

dtach -a /run/$USER/dtach-irssi.socket

Fairly simple no? Already one attack vector gone: evil attacker can't get a new shell through my terminal multiplexer, yay.

Splitting into another user

But why stop there! Why am I running irssi as my main user anyways! Let's take the lessons from good UNIX security, and run this as a separate user altogether. This requires me to create another user, say foo-irc:

adduser foo-irc

... and run it as a systemd service, because how else are you going to start this thing anyways, cron? I came up with something like this unit file:

[Unit]
Description=IRC screen session
After=network.target

[Service]
Type=simple
Environment="TERM=screen.xterm-256color"
User=%i
RuntimeDirectory=%i
ExecStart=-/usr/bin/dtach -N /run/%i/dtach-irssi.socket irssi
ExecStop=-/bin/sh -c 'echo /quit stopping service... | exec /usr/bin/dtach -p /run/%i/dtach-irssi.socket'
ExecReload=-/bin/sh -c 'echo /restart | exec /usr/bin/dtach -p /run/%i/dtach-irssi.socket'

Notice this is a service template, because of the %i stuff. I don't actually remember how to enable this thing, but let's say you drop this in /etc/systemd/system/irssi@.service, then you run:

systemctl daemon-reload

And then, not sure about this bit, instantiate that template:

systemctl enable irssi@foo-irc.service

And then this should start the irssi session:

systemctl start irssi@foo-irc.service

To access the session:

sudo -u foo-irc dtach -a /run/foo-irc/dtach-irssi.socket

Obviously, you will probably need to migrate your irssi configuration over, otherwise you'll end up with a blank, old-school irssi. Take a moment to savor the view though. Nostalgia. Ah.

Hardening irssi

But this is still not enough. That pesky foo-irc user can still launch arbitrary commands, thanks to irssi /exec (and a generous Perl scripting environment). Let's throw the entire kitchen sink at it and see what sticks. At this point, the unit file becomes too long to just maintain in a blog post (which would be silly, but not unheard of), so just look at this git repository instead.

Password-less remote irssi

The neat thing with this hardening is that I now feel comfortable enough with the setup to just add a password-less SSH key to that (basically throwaway) account: worst that can happen if someone gets a hold of that SSH key is they land in a heavily sandboxed irssi session. So yay, no password to jump on chat. Like a real client or something.

Just make sure to secure the SSH key you'll deploy in authorized_keys with:

restrict,pty,command="dtach -a /run/foo-irc/dtach-irssi.socket" [...]

Obviously, make sure the keys are not writable by the user, by placing it somewhere outside their home, which might require hacking at your server's SSH configuration. Because otherwise a compromised user will be able to change his own authorized_keys, which could be bad.

Configuration management

And at this point, you may have noticed that you shouldn't actually followed my instructions to the letter. Instead, just use this neat little Puppet module which does all of the above, but also include some little wrapper so that mosh still works.

It also includes instructions on how to setup your SSH keys.

Enjoy, and let me know if (or rather, how) I messed up.

Updates

  1. it seems that dtach is not very active upstream: the last release (0.9) is from 2016, and the last commit (at the time of writing) is from 2017

  2. dtach is not necessarily safer than screen or tmux from arbitrary input from the outside, in fact there was a vulnerability on dtach CVE-2012-3368 that led to an attacker accessing stack memory (but maybe not code execution)

  3. after writing the Puppet module and publishing this article, I started to get weird behavior from dtach: i would leave the office at night and then return the next morning to find that I was timed out on servers. from my perspective, irssi noticed only when I re-attached the session:

    09:39:51 -!- Irssi: warning Broken pipe
    09:39:51 -!- Irssi: warning SSL write error: Broken pipe
    09:39:51 -!- Irssi: warning SSL write error: Broken pipe
    09:39:51 -!- Irssi: warning SSL write error: Broken pipe
    09:39:51 -!- Irssi: warning SSL write error: Broken pipe
    09:39:51 [bitlbee] -!- Irssi: Connection lost to localhost
    09:39:51 -!- Irssi: warning SSL write error: Broken pipe
    09:39:51 [gitter] -!- Irssi: Connection lost to irc.gitter.im
    09:39:51 [OFTC] -!- Irssi: Connection lost to irc.oftc.net
    09:42:34 [IMC] -!- Irssi: Connection lost to irc.indymedia.org
    09:42:34 -!- Irssi: Connection lost to irc.hackint.org
    09:42:34 -!- Irssi: Connection lost to chat.freenode.net
    09:42:34 -!- dtach_away: Set away
    

    from the outside I actually timed out a few minutes after I detached, which also makes for a weird asymmetry:

    22:31:00 -!- anarcat [~anarcat@ocean] has quit [Ping timeout: 250 seconds]
    

    that is eleven hours before the error I get.

  4. the mosh wrapper script seems to not work as well as it did before. somehow just running mosh $server hangs with a blank screen instead of instantly rejoining the session. I'm not sure it is related to the timeout problem but I did rewrite the wrapper before publication. this is the old version:

    #!/bin/sh
    
    # inspired by https://serverfault.com/questions/749474/ssh-authorized-keys-command-option-multiple-commands
    
    command="dtach -a /run/anarcat-irc/dtach-irssi.socket"
    
    case "$SSH_ORIGINAL_COMMAND" in
        mosh-server*)
        exec mosh-server -- $command
        ;;
        *)
        exec $command
        ;;
    esac
    

    I'm thinking of trying that one out for a while to see if it's related. The weirdest thing is that mosh "un-hangs" if i reattach with plain ssh, so there's definitely something fishy going on here.

What about dtach fork with newer releases

Thanks for that blog, sounds really interesting - I guess lots of people are running something like tmux/*irc.

Searching around for dtach I realized there is a fork of the original repo that includes several fixes and has a "release" 0.10 just 2 month ago. It also deals with ANSI sequences etc, so you might have better success with that one: https://github.com/xPMo/dtach (not related in anyway to me, just found it)

Comment by Norbert
Created . Edited .