---
# System prepended metadata

title: Memshell
tags: [Java]

---

---
type: slide
slideOptions:
  transition: slide
  theme: white
---

![image.png](https://hackmd.io/_uploads/ryBI8qkXp.png)



---

<p style="font-size: 20px;">Trong quá trình làm việc, mỗi khi nhắc đến webshell chúng ta đã quen thuộc với webshell ở dạng file</p>

![](https://hackmd.io/_uploads/ByhgNZuM6.png)

<p style="font-size: 20px;">Tuy nhiên, các security researcher trên thế giới đã nghĩ ra một kiểu webshell khác trong java là memoryshell và đang có xu hướng ngày càng được khai thác rộng rãi</p>

---

So sánh Memoryshell và Webshell thông thường
![](https://hackmd.io/_uploads/Skn5OZ_GT.png)


---

Webshell thông thường

![](https://i.imgur.com/dIjM9lV.png)


---

MemoryShell

<p style="font-size: 24px;">Với memoryshell, <b style="color: red;">file shell chỉ cần được chạy một lần duy nhất</b> với mục đích <b style="color: red;">inject các class vào memory</b> bằng cơ chế Java reflection, load java bytecode,...</p>

![](https://i.imgur.com/UMkAJHK.png)

---

Mục đích và phân tích cách tấn công của Red team
![](https://i.imgur.com/0mSuO6j.jpg)


---

<!-- .slide: data-background="https://i.imgflip.com/2e8syk.jpg" data-background-size="50%" data-background-opacity="0.3"-->
Mục đích

Red team chắc chắn đã khai thác thành công lỗi upload file với webshell thông thường.

Sau đó, sẽ upload memoryshell với mục đích làm cho blue team **khó rà soát** và dù **file shell đã bị xóa đi thì vẫn duy trì được kiểm soát**

---

Phân tích phương thức tấn công

---

## Serverlet là gì?

```javascript= [1-8|12-19]
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(urlPatterns = {"/test-config"},initParams = {
        @WebInitParam(name = "name",value = "test parameter")
})
public class ServletConfigDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = super.getServletConfig().getInitParameter("name");
        resp.setContentType("text/html");
        PrintWriter printWriter = resp.getWriter();
        printWriter.println("Xin Chao " + name);
    }
}

```
<p style="font-size: 36px;">Servlet là API của java để tạo các ứng dụng web</p>
<p style="font-size: 36px;">Hỗ trợ nhiều interface và class trong lập trình web</p>
<p style="font-size: 36px;">Điểm mạnh: chịu tải tốt, JVM quản lý bộ nhớ<p>


---

### JSP file
<p style="font-size: 25px;">Java Server Page là công nghệ viết Web động</p>
<p style="font-size: 25px;">Sử dụng thẻ <%...%> để viết code Java trong HTML</p>
<p style="font-size: 25px;">JSP viết trên nền Java Servlet</p>
<p style="font-size: 25px;">File JSP có quyền truy cập toàn bộ Java API</p>

```html [1|8]
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
  <title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %></h1>
<br/>
<a href="./views/upload.jsp">Hello Servlet</a>
</body>
</html>
```

---

<p style="font-size: 25px;">Khi một .jsp được truy cập bởi Web server lần đầu tiên, nó sẽ được translate thành Java source file .java</p>

<p style="font-size: 25px;">Sau đó, Java source file sẽ được compile thành java bytecode (.class) file bởi JDK rồi sẽ được load vào memory JVM</p>

![](https://i.imgur.com/gdBbjyP.png)

---

Java reflection

<p style="font-size: 25px;">Java reflection là một cơ chế trong Java cho phép chúng ta <b style="color: blue;">truy cập thông tin của đối tượng(tên class, interfaces, các fileds hoặc các phương thức)</b> và <b style="color: blue;">chỉnh sửa các field của đối tượng (kể cả đó là field private)</b> trong quá trình runtime.</p>


```java [3-12]
package test.findName;

public class Bank {
    private int soDuTaiKhoan = 0;
    Bank(){}
    Bank(int soDuTaiKhoan){
        this.soDuTaiKhoan = soDuTaiKhoan;

    }
    public int getSoDu(){
        return this.soDuTaiKhoan;
    }

}

```

---



```java

public static void main(String[] args) throws Exception {
 Class<?> taiKhoan = Class.forName("test.findName.Bank");
 Bank taiKhoanTest = (Bank) taiKhoan.newInstance();
 Field soDu = taiKhoan.getDeclaredField("soDuTaiKhoan");
 soDu.setAccessible(true);
 soDu.set(taiKhoanTest, 10000000);
 int soDuTaiKhoan = (int) soDu.get(taiKhoanTest);
 System.out.println(soDuTaiKhoan);
}
//10000000
```






---

Ba thành phần của Servlet
![Imgur](https://i.imgur.com/LD0sjjA.png)

---

Listener
<p style="font-size: 25px;">
    Theo dõi các sự kiện khởi tạo hay hủy bỏ của những đối tượng như application, session hay request để thực thi các đoạn mã tương ứng với các sự kiện

</p>

```java [1|8-11|3-5]
public class Listener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        ServletRequestListener.super.requestDestroyed(sre);
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        ServletRequestListener.super.requestInitialized(sre);
        System.out.println("Request Initialized");
    }
}

```

---

```java [2|4|15-17]

public class ServletRequestEvent extends EventObject {
    private static final long serialVersionUID = -7467864054698729101L;
    private final transient ServletRequest request;

    public ServletRequestEvent(ServletContext sc, ServletRequest request) {
        super(sc);
        this.request = request;
    }

    public ServletRequest getServletRequest() {
        return this.request;
    }

    public ServletContext getServletContext() {
        return (ServletContext)super.getSource();
    }
}

```

---

Đã có thể lấy được thông tin của request  
Làm sao để nhận output sau khi thực thi command?


---

<p style="font-size: 25px;">
    Có thể thấy <b>ServletRequestListener</b> không hề có <b>HttpServletResponse</b>
    
</p>

```java
public class Listener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        ServletRequestListener.super.requestDestroyed(sre);
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        ServletRequestListener.super.requestInitialized(sre);
        System.out.println("Request Initialized");
    }
}

```

<p style="font-size: 25px;">
    Vậy nên chúng ta cần phải thêm một <b>Custom Listener</b> xử lí os command với <b>constructor có nhận agrument là HttpServletResponse</b>
    
</p>

---

Làm sao để thêm custom listener

---

<p style="font-size: 25px;">Debug thì có thể thấy request sẽ đi qua StandardContext#fireRequestInitEvent</p>

![Imgur](https://i.imgur.com/WUmwaXT.png)

---


<p style="font-size: 25px;fragment;">method getApplicationEventListeners() sẽ lấy tất cả listener được định nghĩa ở web.xml hoặc annotations</p>

```java= [2|9-10|12-13]
public boolean fireRequestInitEvent(ServletRequest request) {
 Object[] instances = this.getApplicationEventListeners();
 if (instances != null && instances.length > 0) {
    ServletRequestEvent event = new ServletRequestEvent(this.getServletContext(), request);
    Object[] var4 = instances;
    int var5 = instances.length;
    for(int var6 = 0; var6 < var5; ++var6) {
        Object instance = var4[var6];
if (instance != null && instance instanceof ServletRequestListener) {
         ServletRequestListener listener = (ServletRequestListener)instance;

    try {
           listener.requestInitialized(event);
    } catch (Throwable var10) {
            ExceptionUtils.handleThrowable(var10);
    this.getLogger().error(sm.getString("standardContext.requestListener.requestInit", new Object[]{instance.getClass().getName()}), var10);
                        request.setAttribute("javax.servlet.error.exception", var10);
                        return false;
                    }
                }
            }
        }

        return true;
    }

```
<p style="font-size: 25px;fragment;">Sau đó kiểm tra xem từng object có phải instanceof ServletRequestListener không</p>

<p style="font-size: 25px;fragment;">Nếu phải sẽ chạy requestInitialized()</p>

---

Làm thế nào để thêm được listener?

---

Ở trong StandardContext có một method là addEventListener
```java
    public void addApplicationEventListener(Object listener) {
        this.applicationEventListenersList.add(listener);
    }
```

---

<p style="font-size: 25px;">Để có được StandardContext thì ở request.getServletContext() trả về ApplicationContextFacade cái implement ServletContext</p>

```java [1|4]
public class ApplicationContextFacade implements ServletContext {
    private final Map<String, Class<?>[]> classCache;
    private final Map<String, Method> objectCache;
    private final ApplicationContext context;
```
<p style="font-size: 25px;">
    Mà trong ApplicationContextFacade lại có ApplicationContext và trong đó có StandardContext
</p>

```java [6]
public class ApplicationContext implements ServletContext {
    protected static final boolean STRICT_SERVLET_COMPLIANCE;
    protected static final boolean GET_RESOURCE_REQUIRE_SLASH;
    protected Map<String, Object> attributes = new ConcurrentHashMap();
    private final Map<String, String> readOnlyAttributes = new ConcurrentHashMap();
    private final StandardContext context;
```

---

![](https://i.imgur.com/67qoFTL.png)


---

![Imgur](https://i.imgur.com/uut2zS1.png)

---

![Imgur](https://i.imgur.com/Nj9sWuh.png)

---

Filter
<p style="font-size: 25px;">
    Khi request đến servlet và trả về response có thể đi qua một hoặc nhiều filter(còn gọi là filter chain). Filter có thể thực hiện kiểm tra, sửa đổi các thuộc tính của request hay response
</p>

```java
@WebFilter(urlPatterns = {"/*"})
public class Logger implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException{
        System.out.println("Filter initialized!!");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter doFilter");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("Filter Destroy");
    }
}

```

---

Debug Filter thì thấy có một đoạn stack frame đáng chú ý

```javascript
StandardWrapperValve#invoke()
    -> StandardWrapperValve#filterChain.doFilter()
        -> ApplicationFilterChain#this.internalDoFilter()
            -> ApplicationFilterChain#filter.doFilter()
                -> FilterTest#doFilter()
```

---

<p style="font-size: 25px;">
    Có thể thấy tại StandardWrapperValve#invoke() thì filterChain.doFilter() sẽ được gọi
</p>

![Imgur](https://i.imgur.com/NiKfRag.png)

---

biến filterChain thì được khởi tạo ở factory.createFilterChain()

```java 
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
```

---

Đi vào method này

```java [1-2]
StandardContext context = (StandardContext)wrapper.getParent();
FilterMap[] filterMaps = context.findFilterMaps();
if (filterMaps != null && filterMaps.length != 0) {
DispatcherType dispatcher = (DispatcherType)request.getAttribute("org.apache.catalina.core.DISPATCHER_TYPE");
String requestPath = null;
Object attribute = request.getAttribute("org.apache.catalina.core.DISPATCHER_REQUEST_PATH");
if (attribute != null) {
        requestPath = attribute.toString();
}

    String servletName = wrapper.getName();
    FilterMap[] var10 = filterMaps;
    int var11 = filterMaps.length;

    int var12;
    FilterMap filterMap;
    ApplicationFilterConfig filterConfig;
    for(var12 = 0; var12 < var11; ++var12) {
            filterMap = var10[var12];
    if (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath)) {
    filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
    if (filterConfig != null) {
            filterChain.addFilter(filterConfig);
        }
        }
}
```



---

<p style="font-size: 25px">
Ở đây method sẽ lấy list filterMaps từ context.findFilterMaps() dựa vào cấu hình ở file web.xml, các filterMap chứa toàn bộ thông tin về filter-mapping bao gồm cả filterName và urlPatterns:
</p>

![Imgur](https://i.imgur.com/ESs38NJ.png)


---

<p style="font-size: 25px">
List filterMaps này sau đó sẽ đi vào vòng lặp for để so sánh urlPattern của từng filterMap có match với requestPath của request hiện tại, nếu đúng sẽ lấy filterConfig từ filterMap này và cuối cùng đi vào filterChain.addFilter(filterConfig).
</p>

```java 
for(var12 = 0; var12 < var11; ++var12) {
    filterMap = var10[var12];
    if (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath)) {
    filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
    if (filterConfig != null) {
            filterChain.addFilter(filterConfig);
        }
    }
}
```


---

<p style="font-size: 25px"> Cấu trúc của (ApplicationFilterConfig)filterConfig như sau, chú ý member filterDef là object chứa các thông tin về filter như filterName hay filterClass:</p>

![Imgur](https://i.imgur.com/33KAotQ.png)

---

Vậy để thêm một custom Filter chúng ta cần phải:

<p style="font-size: 25px;">1. Tạo FilterDef object chứa thông tin về custom filter muốn thêm. </p>
<p style="font-size: 25px">2. Thêm FilterDef  vào StandardContext và regenerate lại filterConfig bằng StandardContext#filterStart()</p>
<p style="font-size: 25px">3. Thêm tiếp FilterMap chứa thông tin về mapping vào StandardContext</p>

```java [8-9|19-20]=
public boolean filterStart() {
if (this.getLogger().isDebugEnabled()) {
    this.getLogger().debug("Starting filters");
}

boolean ok = true;
synchronized(this.filterDefs) {
    this.filterConfigs.clear();
    Iterator var3 = this.filterDefs.entrySet().iterator();

    while(var3.hasNext()) {
        Map.Entry<String, FilterDef> entry = (Map.Entry)var3.next();
        String name = (String)entry.getKey();
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug(" Starting filter '" + name + "'");
        }

        try {
            ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, (FilterDef)entry.getValue());
            this.filterConfigs.put(name, filterConfig);
        } catch (Throwable var8) {
            Throwable t = ExceptionUtils.unwrapInvocationTargetException(var8);
            ExceptionUtils.handleThrowable(t);
            this.getLogger().error(sm.getString("standardContext.filterStart", new Object[]{name}), t);
            ok = false;
        }
    }
```

---

![Imgur](https://i.imgur.com/Llusgi4.png)

---

![Imgur](https://i.imgur.com/xFLCjmm.png)

---

Servlet
<p style="font-size: 25px">
Là module chính đóng vai trò như lớp trung gian giữa client và application phía server. Servlet tiếp nhận client request, xử lý rồi sau đó gửi trả response.
</p>

```java
@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
    private String message;
    public void init() {
        message = "Hello World!";
    }
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<h1>" + message + "</h1>");
    }
    public void destroy() {
    }
}
```


---

<p style="font-size: 25px">Đặt debug để xem sự thay đổi của standard context</p>

![Imgur](https://i.imgur.com/qYNwn7O.png)

<p style="font-size: 25px">Trong StandardContext có member children là một hashmap chứa các StandardWrapper</p>

![Imgur](https://i.imgur.com/4YbspwE.png)


---

<p style="font-size: 25px">servletMappings cũng là một HashMap với key – value là mapping giữa các urlPattern và servletr</p>

![Imgur](https://i.imgur.com/rI2CXA5.png)

---

Vậy để thêm một customer servlet chúng ta cần:

<p style="font-size: 25px">
1.Tạo StandardWrapper object wrap custom servlet cần thêm <br>
2.Thêm StandardWrapper object vào member children của StandardContext<br>
3.Thêm mapping giữa urlPattern và custom servlet vào member servletMapping của StandardContext
</p>

---

![Imgur](https://i.imgur.com/p3nS0DL.png)

---

![Imgur](https://i.imgur.com/UxLiazJ.png)

---

Demo phát hiện, xử lí và phòng chống Memoryshell

![Imgur](https://i.imgur.com/qelBPTt.png)

---

<p style="font-size: 25px">
    Đầu tiên, như đã trình bày trong phần mục đích. Chắc chắn attacker đã khai thác thành công lỗi upload file 
</p>

![Imgur](https://i.imgur.com/O9qJfO8.png)

<p style="font-size: 25px">
    Với phương hướng tấn công ban đầu, attacker sẽ upload file shell ở dạng thông thường và kiểm tra xem đã có thể thực thi được OS command chưa
    => Có thể rà soát ở access log xem có OS command hay tên file bất thường không
</p>

---

![Imgur](https://i.imgur.com/fuPsal6.jpg)

---

<p style="font-size: 25px">
    Tuy nhiên, access log chỉ cho biết <b>thời gian, phương thức của request, path, tên file, parameter nếu có </b>
</p>

![Imgur](https://i.imgur.com/CbPfvcN.jpg)


---

<p style="font-size: 25px">
    Attacker có thể sẽ đặt tên file rất legit và thực hiện với phương thức POST thì access log sẽ không hiển thị command hoặc command đã bị encode, mã hóa thì rất có thể rule không detect ra và không có alert
</p>

![Imgur](https://i.imgur.com/4dzxepK.png)


![Imgur](https://i.imgur.com/3zN1u8t.png)

---

![Imgur](https://i.imgur.com/1Miivip.png)

---

<p style="font-size: 25px">Nhưng shell nào thì mục đích vẫn là gọi đến cmd để thực thi payload 
nên có thể rà soát trong syslog với các event process create, process access 
</p>

![Imgur](https://i.imgur.com/pIxHYK8.png)

<p style="font-size: 25px">
    Sau đó map với thời gian thực hiện request trên access log
</p>

![Imgur](https://i.imgur.com/pT1HoEa.png)

<p style="font-size: 25px">
    Có thể check lại bằng cách truy cập trực tiếp đến file để check 
</p>

---

<p style="font-size: 25px">
    Tuy nhiên, như đã nói ở trên, sau khi đã upload thành công webshell thông thường. Attacker sẽ upload memshell và memshell thì thường file sẽ bị attacker xóa đi. Nên việc truy xuất để verify là rất khó.
</p>

![Imgur](https://i.imgur.com/fauJvtP.png)

<p style="font-size: 25px">
     Các class đã được inject vào memory nên có thể truy xuất với bất kỳ đường dẫn nào chỉ cần đúng parameter đã custom 
</p>

![](https://hackmd.io/_uploads/BkFS71aMT.png)


---

Vậy làm sao để phát hiện, xử lí và phòng chống?

---

<p style="font-size: 25px">
    Đầu tiên, thử xem log mặc định của apache tomcat server có gì?
    

</p>

![image](https://hackmd.io/_uploads/BJucZqtVT.png)
    
![image](https://hackmd.io/_uploads/rJfaWctET.png)
<p style="font-size: 25px">
   Có thể thấy thông tin là quá ít, chỉ có log liên quan đến deploy và contextInitialized
    

</p>

---


<p style="font-size: 25px">
    Để có thêm thông tin, chúng ta cần phải sửa file <b>logging.properties</b> trong config của apache tomcat server

</p>

![image](https://hackmd.io/_uploads/ByszwcY46.png)


---

<p style="font-size: 25px">
    Có thể thấy đã có thêm sự kiện readObject() và filter start.
</p>

![test](https://hackmd.io/_uploads/ry1mKcYNp.png)

<p style="font-size: 25px">
    Tuy nhiên, chỉ log khi attacker inject Filter còn đối với Listener và Servlet thì hoàn toàn không có thông tin.
</p>

---

<p style="font-size: 25px">
    Lý do là bởi mỗi khi filter start hàm FilterStart được gọi và trong hàm này trong lib đã có sẵn đoạn ghi log:
</p>

![image](https://hackmd.io/_uploads/r1RZ99tNp.png)

<p style="font-size: 25px">
    Và sau khi inject Filter thì attacker cần phải regenerate lại filterConfig bằng StandardContext#filterStart() nên sẽ có log, còn đối với Listener và Servlet thì không
</p>

---



Vậy phải làm sao?

![image](https://hackmd.io/_uploads/BkaQNsKEa.png)


---



![image](https://hackmd.io/_uploads/ryvrgsKEa.png)



---

<p style="font-size: 25px">
    Attacker có thể inject vào memory vậy thì chúng ta cũng có thể scan ở trong memory xem có điều gì bất thường.<br>Attacker đã có thể inject mà không cần restart server vậy chúng ta hoàn toàn có thể remove cũng không cần restart service, server.
</p>



---

Scan Memory

![image.png](https://hackmd.io/_uploads/ryrk7c1Qa.png)




---

![image.png](https://hackmd.io/_uploads/SJ_QX5yXp.png)


---

Scan memory

<p style="font-size: 25px">Tiếp theo là Filter, như đã trình bày ở trên để thêm được custom filter attacker cần phải thêm vào filterDefs, filterConfig và filterMaps. Vậy thì chúng ta sẽ lấy ra từng fields này ra.
 </p>
 
![image.png](https://hackmd.io/_uploads/HJgtXqkQa.png)



---


Lấy FilterConfig

![image.png](https://hackmd.io/_uploads/H1mcQcJQa.png)




---

Từ FilterConfig lấy FilterDefs

![image.png](https://hackmd.io/_uploads/SyuCX9J76.png)


---

Scan memory

<p style="font-size: 25px">Cuối cùng là servlet, attacker sẽ inject vào hashmap children và servletMappings</p>

![image.png](https://hackmd.io/_uploads/SJcNV9JXp.png)

![image.png](https://hackmd.io/_uploads/HkIBE51X6.png)


---

Unload Listener Shell

![image.png](https://hackmd.io/_uploads/Syb-S51QT.png)


---

Unload Filter Shell

![image.png](https://hackmd.io/_uploads/S1wDHqy7T.png)


---

Unload Servlet Shell

![image.png](https://hackmd.io/_uploads/HkxbLq1Xp.png)


---

Sử dụng Scan memory để cảnh báo sớm

![Untitled](https://hackmd.io/_uploads/Sk23OsYEa.png)



---

<p style="font-size: 25px">Chúng ta sẽ scan memory xem nếu có Listener, Filter hoặc Servlet được thêm vào thì sẽ sinh log</p>

```java [3|5-6|12|13|18]=
try {
    Object[] filterMaps1 = getFilterMaps(sre);
    if(filterMaps1.length > soFilterMacDinh) {
    Object fm = filterMaps1[filterMaps1.length - 1];
    Object appFilterConfig = filterConfigs.get(getFilterName(fm));
    Field _filter = appFilterConfig.getClass().getDeclaredField("filter");
    _filter.setAccessible(true);
    Object filter = _filter.get(appFilterConfig);
    String filterClassName = filter.getClass().getName();
    System.out.print(filterClassName);
    Map<String, String[]> parameterValues = sre.getServletRequest().getParameterMap();
    byte[] classBytes = Repository.lookupClass(filter.getClass()).getBytes();
    byte[] encoded = Base64.getEncoder().encode(classBytes);
    for (Map.Entry<String, String[]> entry : parameterValues.entrySet()) {
        String key = entry.getKey();
        String[] values = entry.getValue();
        for (String value : values) {
            logger.info("Param=" + key + " " + "Value=" + value + " FilterClassName=" + filterClassName + " " + new String(encoded));
        }
    }
    }
} catch (Exception e) {
throw new RuntimeException(e);
}

```

---

<p style="font-size: 25px">Log đẩy về kèm params, class Listener, Filter hoặc Servlet được dump</p>

![photo_2023-11-21_11-13-39](https://hackmd.io/_uploads/BJEoRoYET.jpg)

---

<p style="font-size: 25px">Như vậy đã verify được 80% vì việc thêm mới các Listener, Filter, Servlet không thường xuyên được dev thêm vào</p>

![image](https://hackmd.io/_uploads/Sk5mRoKEa.png)


---

<p style="font-size: 25px">Như vậy đã verify được 80% vì việc thêm mới các Listener, Filter, Servlet không thường xuyên được dev thêm vào. Vậy nên mỗi khi có log sẽ sinh cảnh báo để đội giám sát có thể check</p>

![image](https://hackmd.io/_uploads/B1IcxhF4T.png)



---

<p style="font-size: 25px">Sau đó từ dump có thể decompile với jadx hoặc các trình decompile khác để verify 100%
</p>

![image](https://hackmd.io/_uploads/B1Kp1hFN6.png)

---

Memory forensic
<p style="font-size: 25px">Ở trên chỉ là phần nổi của tảng băng chìm, là những gì chúng ta nhìn thấy.<br>Vậy điều gì xảy ra trong memory?<br> Điều gì khác biệt khi attacker inject shell? <br>Nếu attacker inject shell rồi unload shell sau đó thì có thể khôi phục không?
</p>


---

<p style="font-size: 25px">
Đầu tiên, sử dụng dumpit để dump memory của server. Sau đó sử dụng volatility để thấy được các tiến trình.
</p>

![image.png](https://hackmd.io/_uploads/rkDR7Fe76.png)

![image.png](https://hackmd.io/_uploads/By4k4FxQp.png)



---

<p style="font-size: 25px">
Sau đó, dump các tiến trình java.exe ra và bắt đầu phân tích. Sẽ có 2 trạng thái có thể xảy ra trên server là:<br>
- Attacker inject shell vào memory nhưng chưa unload<br>
- Attacker inject shell vào memory và unload đi sau đó
</p>

![image.png](https://hackmd.io/_uploads/SytQEYl76.png)

![image.png](https://hackmd.io/_uploads/Skl4VtlmT.png)
<p style="font-size: 25px">
Ở cả hai trạng thái này, chúng ta đều có thể điều tra theo hướng tìm các hàm mà attacker dùng để inject shell và các hàm dùng để unload shell. 
</p>


---

<p style="font-size: 25px">
Đầu tiên là với Listener, ta sẽ tìm theo <b>addApplicationEventListener(), setApplicationEventListener()</b>
</p>

![image.png](https://hackmd.io/_uploads/Hylh4Fxmp.png)




---

<p style="font-size: 25px">
Đối với Filter, attacker inject vào <b>FilterConfig, FilterDefs, FilterMaps, setFilterName, setFilterClass, setFilter, addFilterDef.</b> Chúng ta sẽ tìm theo những thuộc tính này
</p>

![image.png](https://hackmd.io/_uploads/BkmHBtlXa.png)


---

<p style="font-size: 25px">
Đối với Servlet, attacker inject vào hai hashmap là ServletMapings, children, các hàm <b>setServletName, setServlet, setServletClass, addServletMapping, addChild</b>
</p>

![image.png](https://hackmd.io/_uploads/rkcKSYemp.png)


---


---

Nguồn tham khảo:

https://gv7.me/articles/2020/kill-java-web-filter-memshell/
https://gv7.me/articles/2020/filter-servlet-type-memshell-scan-capture-and-kill/
https://sec.vnpt.vn/2022/12/ky-thuat-memory-webshell-trong-cac-dot-khai-thac-redteam/

---
