某靶机平台的Valentine机器我一直想复现一遍倒不是因为它难度有多高而是这台机器把两个很经典的提权思路放在同一个环境里一个是通过复用运行中的tmux会话来继承高权限上下文另一个是用DirtyCow这种内核竞态漏洞去改写只读文件。两条路径的目标一致但玩法完全不同。对走打点路线、想积累真实渗透经验的同行来说这台机器非常值得从头到尾走一遍。整台机器的链路其实很清晰先通过Heartbleed漏洞从目标的OpenSSL服务里把内存中的私钥读出来用私钥解开SSH登录拿到一个低权限用户进去之后枚举进程和临时目录发现一个以root运行的tmux会话直接复用就拿到了root。如果不想走tmux这条路内核版本也正好踩中DirtyCow可以编译利用代码覆盖/etc/passwd新建一个root权限账号。两条路线都验证成功整个过程既是漏洞利用练习也是一次提权思路的完整复盘。1. 目标概览与思路拆解1.1 信息收集的起点拿到Valentine这台机器时第一件事永远是端口扫描和服务识别。一般在授权靶机环境里我不会跑特别重的全端口扫描优先探测常见的80和443端口因为Web服务往往是最容易出问题的入口。扫描结果出来之后看到开放了22、80、443同时没有其他可疑的高位端口基本可以把注意力集中在Web服务和OpenSSL上。访问80端口时站点本身没有太多有价值的内容。真正值得关注的是443端口这个HTTPS服务返回了自签名证书而且证书里暴露了一些暗示性的文本。自签名证书在靶机环境里不奇怪但多看了一眼证书扩展信息之后我意识到这台机器很可能在考SSL/TLS层面的漏洞利用。此时脑子里跳出来的第一个候选就是Heartbleed因为它在2014年影响极大而且这回OpenSSL版本明显偏老。1.2 从Heartbleed到root的完整路径打点阶段核心要解决两个问题拿什么权限进入系统以及进去之后怎么提升权限。Valentine这台机器的设计其实非常典型它不是通过Web应用漏洞直接给你shell而是用一个内存泄露漏洞把系统里最关键的私钥dump出来。私钥一旦到手SSH登录就顺理成章。拿到用户权限之后系统里还有很多平时容易忽略的细节比如/tmp下挂着的tmux socket、用户目录下的tmux配置文件以及处于运行中的进程快照。完整路径可以整理成下面这条链443端口存在Heartbleed漏洞通过心跳包越界读取内存拿到一段base64编码的私钥数据。将私钥修复并保存用对应账号通过SSH登录目标。在低权限shell中枚举进程、临时目录和配置文件发现/tmp/tmux-1000/default这个socket。查看运行进程后发现tmux服务由root启动直接使用tmux attach接入会话获得root权限。备用方案是检查内核版本确认存在DirtyCow漏洞编译利用代码覆盖/etc/passwd生成新root用户也能达到同样目的。这种多路径设计在靶机里很常见它考验的不是“你会不会用现成exp”而是你能不能从系统当前状态里找到“不该出现的会话”或“不该被覆盖的文件”。对初学者来说tmux会话继承比DirtyCow更容易理解但DirtyCow这条路线能帮你加深对内核竞态和写时拷贝机制的认知两条路都值得记下来。2. 第一步打点Heartbleed 漏洞利用的实战记录2.1 环境识别与漏洞研判Heartbleed对应的编号是CVE-2014-0160出问题的组件是OpenSSL的TLS heartbeat扩展。简单说客户端向服务器发送一个心跳包服务器收到后应当原样返回相同长度的数据但OpenSSL在实现时没有严格校验请求的数据长度导致服务器会把内存中多余的数据一并返回。攻击者只要把请求中的payload length字段设置得比实际数据长就能越界读出内存里残留的敏感信息比如私钥、cookie、会话ID之类的。对Valentine这台机器我先用openssl命令主动连接443端口发送心跳包并检查响应的长度。如果响应数据超过了正常心跳响应应有的字节数基本就能确认漏洞存在。实际操作时我会一次性发送多个心跳请求因为内存偏移位置是动态的单个请求不一定每次都能命中私钥区域。某些请求返回的数据很短某些请求则能读到一长串夹杂着二进制内容的字符串这时候就要反复尝试直到拿到完整可用的私钥片段。2.2 为什么能从内存里拿到私钥这里需要解释一下原理因为很多新手拿着脚本去跑却不知道数据是怎么来的。私钥是服务器在握手阶段用于签名和密钥交换的关键材料它会被加载到进程内存中。Heartbleed越界读取时返回的内存内容取决于当前内存页里恰好保留着什么。在目标进程处理TLS连接的过程中私钥很可能就驻留在这片内存附近。实际我在Valentine上跑通了这一过程。脚本发起大量带伪造长度字段的心跳请求并抓取返回数据中可打印的字符片段。其中有几次返回的数据在开头能明显看到类似-----BEGIN RSA PRIVATE KEY-----的标记。这就是我们需要的东西。要注意的是私钥本身是base64编码的中间夹杂着大量无意义的数据必须把首尾标记之间的内容完整截出来然后修复换行格式才能交给ssh或者openssl使用。2.3 私钥修复与保存的细节这里有一个很关键的坑。Heartbleed脚本直接把内存数据打印出来时私钥的每一行可能不是标准的64字符长度也可能在中间混入了其他内存残片。直接保存会报“无效的私钥”或“无法加载Key”。正确的做法是把以BEGIN开头到END结尾的完整块提取出来去掉所有非base64字符然后重新按64个字符一行进行格式化最后保存为id_rsa这样的文件。如果私钥片段不完整就继续多跑几轮心跳请求直到拿到完整版本。拿到私钥后我验证了一下确认它是有效的RSA私钥。这时候还缺一个登录账号名。在Valentine环境里通过之前页面里的暗示、系统用户枚举以及私钥中的注释信息可以猜到一个低权限用户名。凭经验我优先尝试heartbleed这个用户名结果一次就登进去了。这也印证了靶机设计者的思路他把用户名和漏洞名称绑定在一起降低了解谜门槛。3. tmux 会话继承从低权限shell到root3.1 低权限shell中的系统枚举使用私钥成功SSH登录后我获得了一个受限的低权限shell。很多人拿到shell就开始急着找提权exp其实第一步应该是冷静地收集信息。我按照固定顺序执行了以下检查查看当前用户和sudo权限用id和sudo -l确认自己是什么身份是否有免密sudo条目。查看系统进程列表尤其是root用户启动的进程。检查/tmp和/var/tmp目录下的临时文件靶机环境经常把sensor文件、socket或脚本放在这里。查看用户目录中的隐藏文件重点关注.bash_history、.tmux.conf这类可能泄露口令或配置信息的文件。就是这一轮枚举中我发现了几个有意思的点。首先是用户目录下有一个.tmux.conf文件里面内容很简单但提示了tmux的存在。其次/tmp/tmux-1000/default这个socket文件非常显眼它表明有一个tmux服务正在运行而且socket目录的用户标识是1000说明tmux服务是给某个普通用户或root创建的。结合进程列表里tmux server正在以root身份运行答案基本就浮出水面了。3.2 tmux会话权限继承的原理tmux是个终端复用器它允许用户在一个终端里创建多个会话。核心机制是tmux server会监听一个unix socket客户端通过这个socket和server通信。默认情况下tmux server启动时的用户身份就是后续创建会话的权限基础。如果root用户启动了一个tmux server并且socket文件允许其他用户读写那普通用户就能通过tmux attach连接到这个会话从而直接获得一个root shell。说白了就是你不需要破解root密码只需要“继承”这个已经存在的root会话。Valentine环境里root通过tmux创建会话后没有正确限制socket权限也没有使用独立的socket路径。这导致低权限用户只要找到socket文件就能尝试复用。这里还有一个细节tmux默认的socket路径是/tmp/tmux-用户名/目录下的default文件而实际存在的路径是/tmp/tmux-1000/这说明启动tmux的用户uid是1000结合进程信息就能确定它属于root还是某个服务账号。说回操作细节。查看socket目录权限时需要注意当前用户是否对该socket有读写权限。如果权限不足tmux attach会直接报错。如果权限足够直接执行tmux attach -t default或使用默认会话名即可进入。进入后你会发现环境变量里显示为rootid命令返回的uid是0。需要注意的是假如会话中已经存在多个窗口不一定每个窗口都处于root状态最好进去后先确认一下身份再决定后续操作。3.3 tmux attach 的实操过程我实际在Valentine机器上执行时流程非常顺畅。在目标shell中先输入tmux ls命令返回了一个运行的会话。这里需要说明直接执行tmux ls会读取当前用户对应的socket目录如果socket路径不在当前用户目录下就需要用tmux -L或tmux -S指定socket路径。不过Valentine环境比较特殊tmux自动发现了/tmp/tmux-1000/default这个socket说明当前用户对socket有访问权限。接着输入tmux attach-session -t default终端很快就切换到了root shell。我随手敲了id和cat /root/root.txt权限直接就是root。整个提权过程没有任何利用代码纯粹是“系统里存在一个可被复用的高权限会话”这种情况下只需要正确找到socket并接入即可。这里值得提醒的是当你进入一个已经被占用的tmux会话时如果对方正在输入你会看到屏幕上出现命令回显甚至可能打断对方的操作。在靶机环境里这无所谓但在真实场景中务必确认是否有授权否则很容易造成操作干扰或留下痕迹。另外attach的时候最好带上-d参数即tmux attach -d让当前客户端脱离时服务端继续保持运行避免会话被意外关闭。3.4 会话继承这个思路的延展思考tmux会话继承看起来只是一个小技巧但它的排查思路适用于很多场景。比如如果你在系统中发现了一个由数据库服务或Web服务创建的socket文件并且能够通过客户端程序连接那么你就有可能以该服务身份执行操作。再比如很多后台任务通过screen或tmux启动却忘了设置正确的权限掩码结果导致其他用户可以直接接管。这类问题在真实环境中比想象中常见。检测时建议优先查看进程列表里的tmux、screen进程以及/tmp下的socket文件。用ls -la /tmp/tmux*查看目录权限。如果当前用户是目录属主或socket权限为777基本就存在会话继承风险。修复方式也很简单tmux配置里设置set-option -g lock-command或创建专用socket并限制目录权限即可或者干脆避免在会话中保留root权限。4. DirtyCow 提权细节备用路线的完整复现4.1 DirtyCow 的原理与触发条件Valentine这台机器的内核版本比较老正好落在DirtyCow漏洞的影响范围内。DirtyCow对应CVE-2016-5195是一个Linux内核竞态漏洞问题根源在于内核处理写时拷贝Copy-on-Write也就是COW时存在一个可被利用的竞争窗口。攻击者可以通过这个竞态窗口绕过只读文件映射的保护对只读文件进行修改。原理稍微展开一下。正常情况下内核mmap一个文件时如果文件是只读的内核会设置页表为只读当进程尝试写入时会触发缺页异常并执行COW把页复制一份给进程原文件不受影响。DirtyCow的问题在于在COW过程中存在一个时间窗口攻击者可以同时使用另一个线程修改页表属性使得原本应该只写入副本的写入操作最终直接写到了原始文件映射的页面上。结果是即使文件宿主上是只读的只要文件被以可写方式mmap过或者满足利用条件就能篡改内容。这个漏洞的影响面非常广因为它不需要特殊的权限任何能够执行代码的用户都可以尝试提权。在Valentine机器上确认内核版本号uname -r之后基本就能判断系统属于受影响版本。为了稳妥我特意查看了一下内核发布日期和发行版的补丁情况确认没有打过相关补丁这才敢继续往下走。4.2 利用代码的选择与编译网上流传的DirtyCow利用代码有好几个版本。我一般推荐选择那种“覆盖/etc/passwd生成新root用户”的版本因为它的使用方式最直观不涉及内核符号解析和内存地址计算。整个利用过程分为三步编译exp、运行exp、使用生成的新账号登录。在目标机器上执行编译时需要确保系统中有gcc和make。一般靶机为了保证环境完整通常会内置编译工具链。我使用了一款经典的DirtyCow exp它的源码里包含一个参数允许指定要覆盖的只读文件和目标内容。编译命令很简单就是gcc -pthread dirty.c -o dirty -lcrypt因为这里面用到了crypt函数来生成加密后的密码哈希。编译完成后运行./dirty 需要传入新密码脚本会自动在/etc/passwd中创建一个名为firefart的root权限账号并把密码哈希写入对应字段。这里有个重要的细节脚本执行过程需要一点时间因为它在不断触发竞态写入所以看到程序卡住不要立刻中断耐心等待提示写入成功即可。我在Valentine机器上运行了大约两分钟脚本输出成功覆盖的消息然后就可以用firefart账号和指定密码登录root了。4.3 竞态利用后的账号登录与清理新账号创建成功后我用ssh登录或者直接su切换到firefart账号。这里要注意如果当前shell环境不够干净su的时候可能会遇到“无法切换用户”的报错。推荐直接通过SSH新开一个连接或者使用/usr/bin/su - firefart来获得干净的登录shell。切换成功后id显示uid0说明已经是root权限此时可以读取任意root文件或修改系统配置。操作完成后别忘了清理痕迹。DirtyCow利用会把/etc/passwd里原有的root用户的uid改成0并添加一个新账号。如果你不想破坏系统状态需要把/etc/passwd恢复原样或者至少恢复root用户的uid为0。这个在靶机演练中很重要因为后续练习者可能会花很长时间排查为什么root用户异常。恢复方式也很简单利用firefart账号重新写回原始/etc/passwd内容即可。另外还要提醒一点部分DirtyCow exp会在/tmp下生成临时文件、日志或者原始文件备份。使用完最好将这些文件删除同时清除shell历史和exp源码保持环境干净。4.4 tmux路径与DirtyCow路径的对比两条提权路径放在一起对比能看出设计者在考不同的能力。tmux会话继承考验的是信息收集和系统状态感知能力你不需要编译任何代码只要发现一个高权限的tmux会话并正确attach即可全程无文件落地隐蔽性很好。DirtyCow考验的是漏洞知识、利用代码的编译调试能力以及内核版本的识别过程更“硬核”但文件落地明显容易留下痕迹。如果是在真实授权测试中我会优先尝试tmux这类无文件落地的方案因为它对目标系统的影响最小。DirtyCow这种覆盖系统文件的方案无论如何都会破坏原文件状态除非你能精确备份并恢复否则极有可能影响系统稳定性甚至触发告警。所以我的建议是先用信息收集的方式找“合理的高权限上下文”找不到再考虑内核漏洞。5. 常见问题与排查技巧实录5.1 Heartbleed读取数据不稳定怎么办这是很多人在Valentine这台机器上遇到的第一个问题。Heartbleed利用对内存位置非常敏感有时候读回来的数据是乱码有时候只有几行私钥片段。我的经验是不要只跑一轮而是循环发起大量心跳请求在每次返回数据里都用正则匹配BEGIN和END标记。拿到私钥数据后如果openssl加载报错不要急着否认漏洞先尝试格式化修复往往只是换行问题。修复私钥时可以用如下命令处理# 将提取的原始私钥内容写入 /tmp/leaked_key sed s/\\n/\n/g /tmp/leaked_key | sed -n /-----BEGIN/,/-----END/p id_rsa # 验证私钥格式 openssl rsa -in id_rsa -check如果仍然不完整说明内存在那个时间点没有暴露完整的私钥。加大心跳频率多请求几十次区间偏移不一样刷到完整私钥的概率会大很多。另外访问HTTPS时建议使用老版本OpenSSL库来连接因为不同OpenSSL版本对心跳包的处理细节略有差异一般常用的Heartbleed检测器在发送payload时会有协议版本选择如果你用的是自编脚本注意TLS版本要设置为TLSv1.0或TLSv1.1避免服务器选择TLS1.2后心跳扩展行为不同。5.2 私钥登录失败可能的原因拿到私钥后SSH登录报Permission denied常见原因有三个一是私钥格式损坏二是用户名不对三是目标的sshd配置禁用了公钥认证。Valentine这台机器私钥登录失败时我先确认了本地私钥权限确保id_rsa文件的权限是600否则OpenSSH会拒绝加载。其次仔细检查了用户名这个步骤看似简单却经常因为大小写或者拼写问题被卡住。如果你怀疑用户名有问题可以回到收集到的信息里找线索比如证书里的字段、网页源码里的注释、文件下载的暗示。还有一种不太常见的情况就是私钥对应的账号可能没有设置可用shell导致登录后秒退。靶机一般不会这么设计但真实服务器上有可能。排查时用ssh -v加上私钥路径连接观察认证过程中是否走到了公钥验证阶段。如果OpenSSH提示“Offering public key”后服务器返回失败就说明密钥本身可能不匹配如果根本没有走到密钥阶段则可能是网络策略或用户名问题。5.3 tmux attach失败的排查tmux attach提示“找不到会话”或“权限拒绝”大概率是socket路径或者属主权限的问题。先看/tmp/tmux-*目录是否存在这是tmux默认的socket目录目录名由tmux server启动者的uid决定。如果目录名是/tmp/tmux-1000就说明启动者uid为1000当前用户的uid如果不是1000默认情况下tmux会自动寻找当前uid对应的目录这时就需要用-S参数手动指定socket路径例如tmux -S /tmp/tmux-1000/default list-sessions tmux -S /tmp/tmp-1000/default attach -t 目标会话名如果提示权限拒绝则说明socket文件权限不够当前用户无法对该socket发起连接。此时要么切换用户要么检查目录的属主和acl权限看看是否可以通过sudo或者组权限访问。另外某些场景下tmux会话名不是default而是其他名称先执行list-sessions确认会话名再attach避免不必要的误操作。5.4 DirtyCow利用失败时的检查清单DirtyCow利用失败的情况比较常见原因集中在几个方面内核版本不受影响、编译环境缺失、目标文件被重新挂载或SELinux限制、以及运行时的竞态成功率问题。先确认uname -r再查看/etc/os-release和系统的补丁时间如果发行版已经修复就可以直接放弃这条路线。编译时如果gcc不存在可以用cc或者尝试在本地编译好后上传注意体系架构必须一致。如果exp编译通过但运行后没有任何输出或一直卡住可能需要调整线程数或运行参数。有些exp会不断尝试写入直到修改成功这种建议多等几分钟。还有一点值得特别注意如果/etc/passwd所在分区被以只读方式挂载DirtyCow也是无法写入的。可以用mount查看根分区的挂载方式确认rw权限。最后系统如果开启了SELinux也会阻止攻击者改文件此时要么临时关闭SELinux要么寻找其他提权路径。不过在Valentine这类老版本机器上SELinux通常没有启用所以这条不是主要问题。5.5 一个容易被忽略的细节历史命令与配置文件的暗示很多靶机不止设计了一条提权路径而是在系统各个角落隐藏了辅助线索。Valentine环境里的tmux会话一下就能发现但如果你没有执行ps aux和ls -la /tmp就很可能错过这个入口。这提醒我们提权不只是“跑个exp”而是靠枚举和观察。我在这台机器上还检查过.bash_history虽然没发现直接可用的口令但里面确实反映了root用户曾经执行过的tmux相关命令这进一步确认了会话继承这条路是可行的。这个经验可以迁移到所有类似场景进入任何低权限shell后先看5分钟再动手。进程列表能告诉你哪些服务在跑/tmp能告诉你哪里的文件被临时创建过历史记录能告诉你管理员习惯用什么方式管理系统而这些看似零散的信息往往就是提权成功的钥匙。写在最后的个人体会把Valentine这台机器整个打下来后我自己最大的收获不是“会用某个漏洞”而是养成了一套稳定的排查节奏。刚开始做这类靶机的时候我也习惯扫描完端口就直接甩exp上来就跑Heartbleed拿到私钥却因为格式问题浪费了快百分之六十的时间。后来学聪明了先做信息整理把私钥、用户名、隐藏配置全部归类再按照链路一步步推进效率一下高了不少。另外一个小经验是同一台机器如果存在多条提权路径不要只满足于拿到root就收工。有条件的话把两条路都走一遍。tmux会话继承让你明白权限上下文的重要性DirtyCow让你体会到内核竞态的实际利用过程。两种思路以后遇到真实的授权测试都能派上用场前者隐蔽、影响小后者在高版本系统上未必可行但能帮你理解系统防御的边界。工具和脚本永远在变真正核心的是你能从一个看似普通的进程列表和临时目录里迅速判断出该往哪个方向走。