公司动态
从CTF实战看文件上传与包含漏洞的攻防链:源码审计到权限提升
1. 赛题复盘与核心思路拆解2022年的那场国赛现在回想起来依然觉得肾上腺素飙升。作为中职组网络安全赛项的参与者我独自一人面对那套涵盖Web渗透、系统加固、应急响应、密码学等多维度的综合试题整个过程就像在迷宫里打着手电筒找出口每一步都充满了未知和挑战。今天我想抛开官方题解和标准答案纯粹从一个“单兵作战”选手的视角复盘我当时面对第六道综合题时的真实思考路径、技术抉择以及那些“踩坑”后总结出的野路子。这道题通常不是最难的但绝对是检验选手基本功和临场应变能力的试金石它往往伪装成一个简单的业务场景内里却布满了陷阱。这道题的核心场景我记得是一个模拟的“企业内部文档管理系统”。页面看起来人畜无害功能也简单就是上传、查看、管理文档。但题干里那句“管理员发现系统存在异常访问请协助排查并加固”的提示就像侦探小说开头的那句“这是一个平静的夜晚……”意味着平静之下暗流涌动。我的目标很明确第一找出存在的安全漏洞攻击面第二利用漏洞获取关键信息Flag第三提出有效的加固建议。这实际上是一个“攻击-取证-防御”三位一体的综合任务非常考验选手对漏洞原理的理解深度和横向串联能力。2. 攻击面分析与初步侦察面对一个未知系统莽撞地直接上工具扫描是最低效的甚至可能触发防护机制被“封IP”。我的习惯是先用人眼和浏览器开发者工具像用户一样把系统摸一遍。2.1 前端信息搜集与功能点枚举第一步永远是手动点击。我依次访问了首页、登录页、注册页、文档列表页、上传页面和个人中心。在这个过程中我高度关注以下几点页面参数URL中的id、page、file等参数尝试修改它们。比如把?id1改成?id2或者?id1观察页面回显差异。这道题里在查看文档详情页面URL形如/view.php?id5修改id值确实能访问到不同文档初步判断存在数据库交互。隐藏内容与注释右键查看页面源代码是基本操作。我仔细搜索了HTML注释有时开发人员会在这里留下测试账号、后端接口路径甚至是一些提示。虽然这次没在注释里找到“宝藏”但这个习惯不能丢。JavaScript文件分析按F12打开“网络(Network)”标签刷新页面查看加载了哪些JS文件。特别关注那些看似处理用户输入、发起AJAX请求的JS代码。有时客户端校验逻辑会在这里泄露服务器端预期的数据格式或接口地址。Cookie与本地存储检查Application标签下的Cookies和Local Storage。赛题中常设置一些看似无关的Cookie值可能经过编码Base64、Hex等解码后或许有意外发现。这次我发现了一个名为session的Cookie其值是一串很长的、看似随机的字符这通常是会话标识符暂时不动它但记下来。注意在CTF或竞赛环境中频繁的非法请求可能导致IP被临时屏蔽。手动测试时动作要“轻”两次测试之间间隔几秒并随时准备更换浏览器或使用Burp Suite的“拦截(Intercept)”功能暂停请求避免误操作。2.2 目录与敏感文件探测手动浏览完表面功能后就需要借助一些字典进行深度探测。我习惯用dirsearch或者gobuster但在比赛紧张时一个精心准备的自定义小字典往往更高效。我的字典里通常包含以下内容常见后台路径/admin,/manage,/backend,/wp-admin。常见敏感文件/robots.txt,/.git/,/.svn/,/.env,/phpinfo.php,/backup.zip,/www.zip。接口或配置文件/api/,/config.php,/database.php。这次运行扫描后有两个关键发现/robots.txt访问它发现里面有一条Disallow: /backup/。这简直是明示立刻访问/backup/目录果然列出了一个名为www_20220910.zip的压缩包。下载它这是源码备份。/index.php.bak扫描还发现了首页的备份文件。下载下来可以与当前运行的index.php做对比有时能发现被修复的漏洞点或隐藏的逻辑。获取到源码战局就明朗了一半。这步操作的核心思路是寻找开发或运维人员无意中遗留在生产环境的“开发痕迹”这些痕迹是通往系统内部最直接的捷径。3. 源码审计与漏洞定位拿到www_20220910.zip后我迅速在本地环境解压并用像VS Code这样的编辑器全局搜索关键函数和危险操作。3.1 上传功能审计漏洞点一我直奔upload.php文件。文件上传是永恒的漏洞富矿。我快速浏览代码寻找几个关键点文件类型校验是检查Content-Type$_FILES[file][type]还是文件扩展名pathinfo($_FILES[file][name], PATHINFO_EXTENSION)题目中代码仅用$_FILES[file][type] image/jpeg来判断是否为图片这是一个经典漏洞。Content-Type在HTTP请求头中完全由客户端控制可以被Burp Suite轻易篡改。文件内容校验有没有使用getimagesize()函数进行真正的图片内容验证这段代码没有。存储路径与文件名上传后的文件被重命名了吗还是保留了原始文件名代码显示文件被移动到uploads/目录下但文件名采用了md5(时间戳原文件名) .jpg的方式。这意味着即使我上传了一个.php文件只要绕过前端校验服务器端也会以.jpg后缀保存无法直接解析执行。但是这里存在一个致命的逻辑缺陷。我继续审计与上传文件相关的“查看”功能。在view.php中我发现文件路径是通过id参数从数据库查询得到的。追踪数据库查询发现文件路径存储在数据库中而文件后缀名也一并存了进去。那么如果我在上传时通过Burp Suite将Content-Type改为image/jpeg但文件名保持shell.php.jpg服务器端校验通过存入数据库的路径可能就是uploads/md5值.php.jpg。关键在于服务器如Apache如何解析这个文件如果配置了AddType application/x-httpd-php .php那么.php.jpg不会被当作PHP执行。但常见的疏忽是Apache的mod_negotiation模块开启时对于shell.php.jpg这样的文件如果请求shell.php服务器可能会返回shell.php.jpg的内容但这通常也不执行PHP。真正的突破口不在这里。我继续搜索“文件包含”函数。很快在download.php中发现了宝藏include($_GET[template] . .php);。这里存在一个本地文件包含LFI漏洞。参数template用户可控且直接拼接.php后传入include。这意味着我可以控制包含的PHP文件路径。3.2 文件包含漏洞利用攻击链串联现在攻击链清晰了利用不严谨的上传校验上传一个内容为PHP代码的图片文件例如在GIF文件头部添加 但保存后的后缀是.jpg。利用文件包含漏洞通过download.php?template参数去包含我上传的这张“图片”。因为include函数会执行被包含文件中的PHP代码无论其后缀名是什么。具体操作如下制作一个Webshell图片copy normal.jpg /b shell.php /b webshell.jpgWindows或者使用exiftool将PHP代码写入图片的EXIF信息exiftool -Comment webshell.jpg。我选择了后者因为更隐蔽。使用Burp Suite拦截上传请求将Content-Type修改为image/jpeg然后放行。上传成功得到文件路径例如uploads/a1b2c3d4e5f6.jpg。访问download.php?template../uploads/a1b2c3d4e5f6。这里../用于目录穿越定位到上传目录。服务器会执行include(../uploads/a1b2c3d4e5f6.php);由于我们上传的文件实际是.jpg但include会尝试包含a1b2c3d4e5f6.php这个文件不存在。所以需要利用PHP的封装协议。更优雅的利用PHP封装协议。PHP的php://filter协议可以用于读取文件源码zip://可以用于解压包含shell的压缩包。但这里我们上传的是图片直接包含无法执行。这时另一个协议phar://可以帮我们反序列化但构造复杂。最直接的是我注意到上传的文件服务器能访问。那么我可以尝试利用include包含远程文件RFI但比赛环境通常禁用了allow_url_include。所以我回到LFI的经典利用日志文件包含。3.3 日志注入与GetShell如果服务器开启了错误日志并且我知道日志文件的路径常见如/var/log/apache2/access.log我就可以通过User-Agent或HTTP请求参数注入PHP代码然后通过LFI漏洞包含这个日志文件从而执行代码。步骤探测日志路径利用已有的LFI尝试包含一些常见路径如../../../../var/log/apache2/access.log。通过download.php?template../../../../var/log/apache2/access_log%00如果PHP版本允许空字节截断或者直接尝试。我通过返回的页面内容大量HTTP访问记录确认了日志路径正确。注入PHP代码到日志使用Burp Suite或curl发送一个特殊的HTTP请求在User-Agent字段中携带PHP代码。curl -A http://target-site.com/包含日志文件执行代码访问download.php?template../../../../var/log/apache2/access.log。服务器会执行日志文件中我们注入的PHP代码。为了精确执行我需要找到我刚刚注入的那条日志记录。通常我可以先注入一个简单的来测试如果成功页面上会显示 phpinfo 的信息。确认成功后再注入Webshell代码例如。实操心得日志文件通常很大包含整个文件可能会导致PHP内存超限或执行超时。一个技巧是在注入代码后立即发送大量其他请求将自己的恶意日志记录“顶”到日志文件末尾附近然后包含日志文件时可以尝试只包含最后几行例如使用tail命令的思路但在LFI中不易实现。更稳妥的方法是注入一个能写入独立Webshell文件的代码比如 。这样下次直接访问/uploads/shell.php即可更稳定。通过日志注入我最终在服务器上建立了一个稳定的Webshell连接获得了系统命令执行权限。接下来就是寻找Flag。4. 权限提升与信息搜集获得Webshell通常是www-data用户权限后真正的挑战才开始。Flag往往不在Web目录下或者需要更高权限才能读取。4.1 系统内部信息搜集我首先执行了一些基础命令了解环境whoami/id查看当前用户和权限。uname -a查看系统内核版本。cat /etc/passwd查看系统用户。ps aux查看运行进程寻找以root身份运行的服务或可疑进程。sudo -l非常关键查看当前用户可以使用sudo以root身份执行哪些命令。如果配置不当可能直接找到提权路径。这道题中sudo -l返回(ALL) NOPASSWD: /usr/bin/find。这是一个经典的SUID提权点find / -perm -4000 -type f 2/dev/null查找所有SUID权限的文件。SUID文件执行时会以文件所有者的身份运行。同样发现了/usr/bin/find。4.2 利用SUID的Find命令提权既然find命令具有SUID权限且属于root那么我就可以利用它来执行任意命令并且这些命令将以root权限运行。利用方式# 在获得的Webshell中执行 find / -exec whoami \; # 如果返回 root则证明可以利用 # 更直接地获取一个root shell find / -exec /bin/sh -p \; # 或者 touch /tmp/rootme find /tmp/rootme -exec /bin/sh -p \;执行find / -exec /bin/sh -p \;后我成功获得了root权限的shell。-p参数在某些系统上用于保留特权模式。4.3 定位并获取Flag提权到root后寻找Flag就简单了。通常Flag文件的名字或路径在题目描述中会有提示或者遵循惯例如/flag/root/flag.txt。我使用以下命令进行搜索find / -name *flag* -type f 2/dev/null find / -name *.txt -exec grep -l flag{ {} \; 2/dev/null最终我在/root/目录下找到了一个名为final_flag.txt的文件使用cat命令成功读取到Flag内容。5. 安全加固建议与反思作为攻击方完成任务后换位到防守方我需要针对发现的漏洞提出加固建议。这部分在竞赛中同样占分。5.1 针对已发现漏洞的加固文件上传漏洞白名单校验不仅校验MIME类型更要对文件扩展名实施严格的白名单机制如只允许.jpg,.png,.gif。文件内容校验使用getimagesize()、exif_imagetype()等函数验证文件确实是有效的图片。重命名与隔离上传的文件应使用随机生成的文件名如UUID并去除原扩展名统一赋予白名单内的安全扩展名。将上传目录设置为不可执行脚本通过配置Web服务器或使用.htaccess禁止PHP解析。文件类型检测可以考虑使用更底层的文件头魔数Magic Number检测。文件包含漏洞避免动态包含尽量避免使用用户输入直接作为文件路径进行包含操作。固定包含路径如果必须动态包含应使用白名单机制只允许包含预设的几个文件。过滤输入对输入参数进行严格过滤禁止目录穿越字符../..\和空字节%00。设置open_basedir在PHP配置中设置open_basedir将PHP可操作的文件限制在指定目录树内。日志文件包含权限控制确保Web服务器日志文件如access.log对Web运行用户如www-data仅有读取权限绝无写入权限。更好的做法是将日志文件放在Web目录之外。日志内容过滤对写入日志的内容进行过滤转义或删除可能被解释为代码的特殊字符。权限配置不当SUID提权最小权限原则定期审计系统SUID/SGID文件使用find / -perm /6000 -type f列出所有此类文件移除非必要的SUID/SGID位。审查sudoers严格管理/etc/sudoers文件遵循最小授权原则避免赋予普通用户过高的、无密码的sudo权限。对于像find这样的强大命令尤其要谨慎。5.2 纵深防御体系建设建议除了修补具体漏洞还应从体系上思考输入输出全面过滤对所有用户输入进行验证、过滤和转义。输出到HTML时进行HTML实体编码防止XSS输出到系统命令时使用参数化或白名单。错误信息处理关闭生产环境的PHP错误显示display_errors Off使用自定义错误页面避免泄露路径、数据库结构等敏感信息。定期安全更新与审计保持操作系统、Web服务器、PHP及所有依赖库更新至最新稳定版。定期进行代码安全审计和渗透测试。网络层防护使用WAFWeb应用防火墙防御常见Web攻击。对服务器进行适当的网络隔离。回顾这道赛题从信息搜集到源码审计再到漏洞串联利用最后提权取Flag是一个完整的渗透测试流程。它考察的不仅是某个漏洞的利用更是选手在压力下的逻辑思维、知识串联和问题解决能力。独自解题时保持冷静、有条理地层层推进比掌握所有漏洞利用技巧更重要。每一次“踩坑”后的复盘都是技术视野的一次拓宽。