Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

That's exactly what it is, but the point is that it's not a Unix shell, and it's not running as a subcommand of SSH (which you could also do with a custom shell). There's no tty involved, and so the attack surface is reduced - if you exploit a logic error in the Go code (e.g. parameter pollution), you won't be able to do more than run one of the commands that it already runs. But if you exploit a logic error in a Unix shell with parameter pollution, then the attack surface is much greater. That said, of course if an attacker finds a bug in the Go code that enables arbitrary code execution, then the attack surface is just as wide as any other binary on the system, including a Unix shell, so you still need to lock down the privileges of the process itself.


I think the point is: how is bundling a custom ssh server into the same executable that interprets the commands, a reduced surface as opposed to using standard openssh's sshd, and defining a simple shell like so:

  #!/bin/bash

  cmd_foo() {
    printf "called foo with %s\n" "$*"
  }

  cmd_bar() {
    printf "called bar with %s\n" "$*"
  }

  run_cmd_call() {
    local cmd="$1"; shift
    local cmd_args=("$@")

    case "$cmd" in
      foo) cmd_foo "${cmd_args[@]}" ;;
      bar) cmd_bar "${cmd_args[@]}" ;;
      *) printf "%s: unknown command\n" "$cmd" >&2
    esac
  }

  while (( $# )); do
    case "$1" in
      -c)
        read -ra cmd_call <<< "$2"
        shift
      ;;

      *)
        printf "%s: unknown argument\n" "$1" >&2
        exit 1
      ;;
    esac
    shift
  done

  if [[ "$cmd_call" ]]; then
    run_cmd_call "${cmd_call[@]}"
  else
    while
      printf "> "
      read -ra cmd_call
    do
      run_cmd_call "${cmd_call[@]}"
    done
  fi
(then, of course,)

  sudo useradd -ms /path/to/custom-shell.sh userfoo
  sudo -u userfoo bash -c 'mkdir ~/.ssh; printf "%s\n" "$your_pubkey" >> ~/.ssh/authorized_keys'
How would you "do more than run one of the commands that it already runs" with this from a `ssh userfoo@localhost` call?


Don't forget to disable sftp in your sshd_config. and ssh port forwarding. and hopefully there's no sneaky way that an attacker can push an environment variable to break something like with LD_PRELOAD, and are you sure there are no directly/indirectly exploitable bugs/omissions in your script logic for invoking complex commands like git, and that your distributed filesystem works quickly across a few million home folders for your authorized_keys files, ...


EDIT: Crap. You're right. Port forwarding worked... So there's that. `ssh -J userfoo@localhost other-place` also worked... I would imagine that's all able to be disabled, but I see that the user is not fundamentally limited to what the shell allows. That's disappointing. I thought it would wrap everything in `custom-shell.sh -c` calls like it does with the sftp and scp servers. I imagine/hope that under a default configuration one is still not able to touch the contents of the server if the shell doesn't permit it, but being able to use it as a proxy or listen on its ports is not what I expected.

ORIG_REPLY:

> Don't forget to disable sftp in your sshd_config

Doing `strace -fp $(pgrep sshd)` then `sftp userfoo@localhost` in another terminal, I see:

  $ sftp userfoo@localhost
  Connection closed.  
  Connection closed
  $
then in the `strace` output:

  [pid 408173] execve("/tmp/custom-shell.sh", ["custom-shell.sh", "-c", "/usr/lib/ssh/sftp-server"], 0x55c5f3bae0d0 /* 14 vars */) = 0
  [pid 408173] write(1, "/usr/lib/ssh/sftp-server: unknown command\n", 42) = 42
As far as I understand, SSH's security of what a user can do is completely based on what the shell permits the user to do.

> are you sure there are no directly/indirectly exploitable bugs/omissions in your script logic for invoking complex commands like git

Including a ssh server in the same executable doesn't save you from such bugs.

> and that your distributed filesystem works quickly across a few million home folders for your authorized_keys files

OpenSSH's sshd seems quite flexible in ways to authenticate users. I don't think having a home folder per user is necessary if you don't want it.

> and hopefully there's no sneaky way that an attacker can push an environment variable to break something like with LD_PRELOAD

SSH's default is to not accept the client's environment, and this shell itself provides no way to manipulate them. They can be nonexistent in the shell's interface if you don't want them.

I put the script so someone can offer some concrete way (as in show me some code) to bypass it in a way that having a custom integrated SSH server would prevent it. I'd love to see a `ssh/sftp userfoo@localhost` call that does something other than call cmd_foo, cmd_bar, or erring out.


There was another hole if the target command was executed using /bin/bash in a certain way: https://en.wikipedia.org/wiki/Shellshock_(software_bug) It's probably not the case when the command is the user's shell, but it was exploitable when it was the command= in authorized_keys.

Additionally, maybe they are using a custom server because they don't want to fork a new instance of the handler on every login.

Here is a stackoverflow topic on limiting the SSH user: https://serverfault.com/questions/152726/how-can-ssh-allowed.... They suggest a few more options, probably not relevant for a gitlab-like use-case.

> SSH's default is to not accept the client's environment

sshd accepts environment variables specified by AcceptEnv in /etc/ssh/sshd_config. By default, they are mostly related to locales.

  $ LC_ALL=en_US.UTF8 ssh x date
  Tue Sep 19 01:14:41 AM UTC 2023
  $ LC_ALL=cs_CZ.UTF8 ssh x date
  Út 19. září 2023, 01:14:46 UTC


> There was another hole if the target command was executed using /bin/bash in a certain way: https://en.wikipedia.org/wiki/Shellshock_(software_bug)

I agree bash is not the best choice security-wise, but language choice is a bit beside the point with regards to whether or not to bundle a server.

> Additionally, maybe they are using a custom server because they don't want to fork a new instance of the handler on every login.

That's very valid, though the discussion was about having a reduced attack surface from adding a custom server.

> Here is a stackoverflow topic on limiting the SSH user: https://serverfault.com/questions/152726/how-can-ssh-allowed.... They suggest a few more options, probably not relevant for a gitlab-like use-case.

Thanks for that.

> sshd accepts environment variables specified by AcceptEnv in /etc/ssh/sshd_config. By default, they are mostly related to locales.

That seems to be a Debian-specific patch. Upstream doesn't include AcceptEnv in the default config file and there seems to be no default value. Your example commands fail to set LC_ALL on my system. `ssh -o SetEnv=LC_ALL=...` also fails.


Yeah it was a useful approach, "disprove me with code"

>> Don't forget to disable sftp in your sshd_config >Doing `strace -fp $(pgrep sshd)` then `sftp userfoo@localhost` in another terminal, I see: [...] >execve("/tmp/custom-shell.sh", ["custom-shell.sh", "-c", "/usr/lib/ssh/sftp-server"]

If your sshd_config had "Subsystem sftp internal-sftp" as a shipped default, then I don't think it would have gone through the user's shell. Not a major issue, but there are just so many little points to be certain are locked down

>I'd love to see a `ssh/sftp userfoo@localhost` call that does something other than call cmd_foo, cmd_bar, or erring out.

The reference to LD_PRELOAD was to hint at that (since if the attacker can get a binary on the system -- e.g. in a git repo or config file) they could change what functions bash is invoking... I'm not a wiley enough attacker, but I bet if you put something valuable behind it and put out a public challenge you'd be impressed with the opensshd vulnerabilities and other interesting features of ssh/tty/environment variables.

As you probably guessed, my comment was just listing a few of the things (today) that you're playing whack-a-mole with when it comes to making sure everything is secure. I think your "disprove me with code" approach is a great basis for a discussion.

Writing their own 'ssh server and shell' program is a different set of tradeoffs vs trying to fully lock down programs that are intended to be general-purpose... to be perfectly honest I don't think I'd want to be involved in either route, the stakes are stressfully high... but there sure is a lot less code to audit and test in writing your own ssh+shell than there is trying to secure and keep up-to-date sshd, pam, bash, etc. (especially since they'll be adding features that you explicitly do not want for your purposes)


Yeah, regarding whack-a-mole, I thought that wouldn't be necessary. I mistakenly believed:

>> As far as I understand, SSH's security of what a user can do is completely based on what the shell permits the user to do.

I was mistaken in believing that sshd was designed with the user being limited to their specific shell as their sole system interface (as their only way to get the system to do things) as a core design principle. With that sort of funnel, I thought a custom shell would suffice.

> If your sshd_config had "Subsystem sftp internal-sftp" as a shipped default, then I don't think it would have gone through the user's shell.

It's curious how much you can do on a secure "shell" server without involving shells at all.

You know, what's particularly interesting is that strace shows sshd does `custom-shell.sh -c /usr/lib/ssh/sftp-server` instead of `/usr/lib/ssh/sftp-server` directly. That feels like it has no other purpose than to adhere to the principle I believed it was following, but it doesn't apply it for everything. I think it should be possible wrap stuff like `-L`, `-R`, `-D`, `-J` et al. in their own shell calls to another executable, but they don't... Wonder what's the story there.

> The reference to LD_PRELOAD was to hint at that (since if the attacker can get a binary on the system -- e.g. in a git repo or config file) they could change what functions bash is invoking

I had mentioned that the client's environment is not accepted by sshd by default, so I did address this point in my previous comment. Maybe what you're getting at here is that the shell itself may allow modifying the environment through its own logic? But you're still going to have that issue regardless of whether you use sshd or a custom integrated ssh server.

> Writing their own 'ssh server and shell' program is a different set of tradeoffs vs trying to fully lock down programs that are intended to be general-purpose

Indeed. If the hassle to lock-down is large enough, it can be better to opt for simpler, more easily verifiable software. Tradeoffs aplenty, and one such is that you may end up adopting less mature software, by way of the chosen dependencies and your own code, however that server is implemented. That can be worth it.


Bash actually has this feature built in. Restricted shell they call it. Start it as rbash or bash -r.

It will lock down the shell to not allow cd, setting envvars, launching commands outside working directory and a lot more.

If you look at the full list of things they disable you realize how many obscure holes are available you never would have thought about.

Not that I would ever trust it enough to expose on a shared box. Likewise with a tailor made shellscript. I’d take a bespoke server in go any day.


It's not the same thing. You're comparing a white-listing approach with a black-listing approach. The example shell doesn't have any concept of directories or environment variables or anything other than cmd_foo and cmd_bar. The only things that exist are the things written to exist.


What difference does it being a subcommand make? Just use a seccomp filter. It'll apply to all descendent processes.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: