环境
nc6.5
漏洞分析
接口/lfw/core/rpc,对应hotwebs/lfw/WEB-INF/web.xml中的LfwDispatcherServlet,nc.uap.lfw.core.servlet.LfwDispatcherServlet在pubwebrt_mvcLevel-1.jar包中。
![]() |
|---|
实例化了LfwCoreController类,并调用了handleRequest方法
![]() |
|---|
调用了PresentPluginFactory.getControlPlugin(path)方法
![]() |
|---|
调用req.getPathInfo()获取到的值为rpc,创建new RpcControlPlugin()对象
![]() |
|---|
在RpcControlPlugin#handle方法中,调用了RpcHelper.processJsonRequest(req, res)方法
![]() |
|---|
从request中获取rpcdata参数,并创建json对象,以json对象作为参数调用call(jsonReq)方法
![]() |
|---|
在call方法中,首先从json中获取rpcname和method参数作为类名和方法名,再循环获取params,添加到paramList中。
![]() |
|---|
获取完成后,会遍历paramList,拿到参数值,调用LfwJsonSerializer.getInstance().fromJsObject(value)方法。
1 | for(int i = 0; i < paramCount; ++i) { |
调用this.fromJSON(s)方法
![]() |
|---|
调用tok.nextValue()
![]() |
|---|
在nextValue方法中,首先会调用this.nextClean()方法
![]() |
|---|
在this.nextClean()中,解析value中注释区域,并将指向value的指针指向注释区域后。
![]() |
|---|
然后会判断当前所指向的value是否为" 或者 ',如果不是的话会跳过解析,直接调用this.nextString(c);,如果是的话会继续判断是{ ,会将value解析成JSONObject,是[会将value解析成JSONArray对象。
![]() |
|---|
只有是"或者'的情况下会调用this.nextString(c),参数c就是"或者',如果value被"或者'包裹起来,那么就会返回被包裹的字符串。
![]() |
|---|
![]() |
|---|
后续还会判断是否以$S_开头,如果以$S_开头会截取$S_后的内容,接着会对value进行一次url解码。
![]() |
|---|
接着会以className作为参数调用ServiceLocator.getService(className)
![]() |
|---|
调用ServiceLocator.getService(className)这种方式为服务定位器模式,这种设计模式在使用JNDI加载类时,第一次加载后会对类进行缓存,第二次加载会从缓存中去加载,从而提高性能。
![]() |
|---|
指定的className为类路径,那么会在nameIndices中进行查找,最后得到一个实例化对象。
![]() |
|---|
最后并对其进行反射加载。
![]() |
|---|
反射调用利用类
nc.uap.portal.service.itf.IPortalSpecService#createSkinFile可实现任意路径下写入任意内容
![]() |
|---|
在进行使用../跨目录写入文件的时候,不会保留/关键字
![]() |
|---|
绕过的方式有三种,一个是使用反斜杠\,但是在Linux下可能会写入失败;第二种是将路径通过"、或者'包裹起来,这样就不会进入过滤点;第三种是将路径进行两次URL编码,在如上的分析中,可以发现,最后String类型的参数进行了URL解码。
漏洞复现
写入webshell
1 | POST /lfw/core/rpc HTTP/1.1 |
![]() |
|---|
访问 http://172.20.10.19:8010/hello.jsp rebeyond
![]() |
|---|






















