SSH Hijacking

ssh hijacking

sshd

ssh-agent

ssh

Client communicates with ssh-agent, which 

has a cached credential in memory.

sshd

ssh-agent

ssh

ssh-agent performs auth to sshd across network

sshd

ssh-agent

ssh

ssh-agent acts as a pipe between two sockets for the client/server

sshd

ssh-agent

ssh

client can now execute code

sshd

ssh-agent

ssh

Attackers can ask ssh-agent to auth for them, and since the creds are cached this works

evil.sh

sshd

ssh-agent

ssh

But if we enforce 2FA on sshd, attacker is stopped

evil.sh

sshd

ssh-agent

ssh

with control master the ssh-agent never releases the connection to the server, so clients don't have to re-auth if there's MFA

one time mfa

sshd

ssh-agent

ssh

now attacker can leverage the existing connection, bypassing 2fa

evil.sh

shit alright so what do we do, we were banking on mfa

drinking grapl, obviously

notice how all of the GRAPHics were actually graphs, coincidence?

sshd

ssh-agent

ssh

evil.sh

What's suspicious about this whole scenario?

sigs

  • some proc other than ssh is talking to ssh-agent
  • fucking ban control master entirely, that should be a policy

it actually gets worse

sshd

so the client agent opens a socket to the server sshd, and then that server sshd uses the client to auth *more* connections to *other* servers

sshd

sshd

legit ssh from eng

evil ssh from ian

sshd

so the client agent opens a socket to the server sshd, and then that server sshd uses the client to auth *more* connections to *other* servers

sshd

sshd

legit ssh from eng

evil ssh from ian

ian says "auth me" to eng

sshd

ian uses 'eng' agent to auth to some other server that previously he didnt have access to

basically he owns EVERY SINGLE USER who forwards their key to that purple ssh

sshd

sshd

legit ssh from eng

evil ssh from ian

ian says "auth me" to eng

class SshIpc(Analyzer):
	def get_queries(self) -> IpcQuery:
        return (
        	IpcQuery()
            .with_ipc_from(
            	ProcessQuery().with_process_name(eq=Not("/bin/ssh"))
            )
            .with_ipc_to(
            	ProcessQuery().with_process_name(eq=Not("/usr/bin/ssh-agent"))
            )
        )
        
class RareParentOfSsh(Analyzer):
	def get_queries(self) -> IpcQuery:
        return (
        	SshQuery()
            .parent_process().with_process_name()
        )
    def on_response(self, response, chn):
    	if count(response.process_name, response.parent.process_name) <= 1:
        	pass # emit the sig

# Some SSH client ssh'd, and then performed IPC, and then SSH'd again
class ChainedSsh(Analyzer):
	def get_queries(self) -> IpcQuery:
        return (
        	SshQuery()
            .with_ssh_connection_to(
            	SshQuery()
                .with_ipc_to()
                .with_ssh_connection_to(
					SshQuery()
                )
            )
        )