您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)怎樣淺談RASP技術(shù)的環(huán)境配置與代碼實現(xiàn),文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。
首先我們在IDEA中新建一個maven項目
取名為JavawebAgent
然后當(dāng)前的目錄結(jié)構(gòu)如下:
刪除src目錄,然后右鍵新建Module
依然選擇Maven項目
然后在ArtifactId處填入agent
然后確定即可
然后再次重復(fù)一遍上面的新建Module的操作,將第二小步中的ArtifactId改為test,第三小步中的Module Name 改為test-struts2,如下圖所示
這時候的目錄結(jié)構(gòu)如下
其中agent目錄為我們要實現(xiàn)agent的主要代碼區(qū)域,test-struts2為測試web代碼區(qū)域。(注:test-struts2不是必選的)
test-struts2部分的代碼這邊就不進(jìn)行復(fù)述了,大家可以去本項目的地址中直接下載test-struts2內(nèi)容。
agent這個pom包配置的話有坑,這個以后在說,先看pom.xml內(nèi)容吧。
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.1</version>
</dependency>
</dependencies>
<build>
<finalName>agent</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<archive>
<manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>commons-io:commons-io:jar:*</include>
<include>org.ow2.asm:asm-all:jar:*</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
將上述內(nèi)容復(fù)制到agent模塊下的pom.xml中
≡≡ 創(chuàng)建MAINFEST.NF文件
在resources目錄下創(chuàng)建MAINFEST.NF文件,文件內(nèi)容如下
Manifest-Version: 1.0
Can-Retransform-Classes: true
Can-Redefine-Classes: true
Can-Set-Native-Method-Prefix: true
在idea中右上部分找到Add Configurations , 然后點(diǎn)擊此按鈕
在彈出的窗口中點(diǎn)左上角的+,選擇maven
然后點(diǎn)下圖①的位置選擇工作目錄,在②的位置選擇agent,在③的位置填入clean install
完成以后如下圖所示,然后點(diǎn)擊OK保存即可
這時候右上角已經(jīng)可以看到我們剛剛配置的maven自動打包功能了,agent每改一處都需要重新build,不然無法生效。
在agent包下面的java文件夾下右鍵選擇新建package,然后填入你的包名,我這邊的包名為
cn.org.javaweb.agent
在cn.org.javaweb.agent包下新建一個類。
內(nèi)容如下:
/*
* Copyright sky 2019-04-03 Email:sky@03sec.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.javaweb.agent;
import java.lang.instrument.Instrumentation;
/**
* @author sky
*/
public class Agent {
public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new AgentTransform());
}
}
然后我們再新建一個AgentTransform類,該類需要實現(xiàn)ClassFileTransformer的方法,內(nèi)容如下:
/*
* Copyright sky 2019-04-03 Email:sky@03sec.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.javaweb.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
/**
* @author sky
*/
public class AgentTransform implements ClassFileTransformer {
/**
* @param loader
* @param className
* @param classBeingRedefined
* @param protectionDomain
* @param classfileBuffer
* @return
* @throws IllegalClassFormatException
*/
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
className = className.replace("/", ".");
System.out.println("Load class:" + className);
return classfileBuffer;
}
}
build Agent配置
點(diǎn)擊右上角的agent[clean,intall]進(jìn)行build。
由上圖可見我們的包的位置為
/Volumes/Data/code/work/JavawebAgent/agent/target/agent.jar
將改包的位置記錄下來,然后點(diǎn)開tomcat配置(這邊沒有對idea如何配置tomcat進(jìn)行講解,不會的可以自行百度|谷歌)
在VM options處填寫以下內(nèi)容:
-Dfile.encoding=UTF-8
-noverify
-Xbootclasspath/p:/Volumes/Data/code/work/JavawebAgent/agent/target/agent.jar
-javaagent:/Volumes/Data/code/work/JavawebAgent/agent/target/agent.jar
其中/Volumes/Data/code/work/JavawebAgent/agent/target/agent.jar的路徑為你在上一步編譯出來的agent的路徑,注意替換。
這時候我們在啟動tomcat,就可以看到我們在AgentTransform中寫的打印包名已經(jīng)生效了,如下圖:
上圖紅框區(qū)域為tomcat啟動的時候加載的所有類名。然后我們打開瀏覽器查看web是否正常。
可以看到web也正常啟動了。
然后我們新建一個TestClassVisitor類,需要繼承ClassVisitor類并且實現(xiàn)Opcodes類,代碼如下
/*
* Copyright sky 2019-04-03 Email:sky@03sec.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.javaweb.agent;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* @author sky
*/
public class TestClassVisitor extends ClassVisitor implements Opcodes {
public TestClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
System.out.println(name + "方法的描述符是:" + desc);
return mv;
}
}
然后回到AgentTransform中,對transform方法的內(nèi)容進(jìn)行修改,transform方法代碼如下:
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
className = className.replace("/", ".");
try {
if (className.contains("ProcessBuilder")) {
System.out.println("Load class: " + className);
ClassReader classReader = new ClassReader(classfileBuffer);
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
ClassVisitor classVisitor = new TestClassVisitor(classWriter);
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
classfileBuffer = classWriter.toByteArray();
}
} catch (Exception e) {
e.printStackTrace();
}
return classfileBuffer;
}
簡單介紹一下代碼塊內(nèi)容
首先判斷類名是否包含ProcessBuilder,如果包含則使用ClassReader對字節(jié)碼進(jìn)行讀取,然后新建一個ClassWriter進(jìn)行對ClassReader讀取的字節(jié)碼進(jìn)行拼接,然后在新建一個我們自定義的ClassVisitor對類的觸發(fā)事件進(jìn)行hook,在然后調(diào)用classReader的accept方法,最后給classfileBuffer重新賦值修改后的字節(jié)碼。
可能看起來比較繞,但是如果學(xué)會使用以后就比較好理解了。
我們在tomcat中新建一個jsp,用來調(diào)用命令執(zhí)行,代碼如下:
<%@ page import="java.io.InputStream" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<pre>
<%
Process process = Runtime.getRuntime().exec(request.getParameter("cmd"));
InputStream in = process.getInputStream();
int a = 0;
byte[] b = new byte[1024];
while ((a = in.read(b)) != -1) {
out.println(new String(b, 0, a));
}
in.close();
%>
</pre>
可以看到就是一個簡單的執(zhí)行命令的代碼;下面我們對就此更改過的內(nèi)容進(jìn)行build,看一下會輸出點(diǎn)什么。
biuld完成,啟動tomcat。
訪問
http://localhost:8080/cmd.jsp?cmd=whoami
可以看到已經(jīng)成功執(zhí)行命令,我們回到idea里面的控制臺看一下輸出了什么。
通過上圖可以完整的看到一個執(zhí)行命令所調(diào)用的所有調(diào)用鏈。
Load class: java.lang.ProcessBuilder
<init>方法的描述符是:(Ljava/util/List;)V
<init>方法的描述符是:([Ljava/lang/String;)V
command方法的描述符是:(Ljava/util/List;)Ljava/lang/ProcessBuilder;
command方法的描述符是:([Ljava/lang/String;)Ljava/lang/ProcessBuilder;
command方法的描述符是:()Ljava/util/List;
environment方法的描述符是:()Ljava/util/Map;
environment方法的描述符是:([Ljava/lang/String;)Ljava/lang/ProcessBuilder;
directory方法的描述符是:()Ljava/io/File;
directory方法的描述符是:(Ljava/io/File;)Ljava/lang/ProcessBuilder;
redirects方法的描述符是:()[Ljava/lang/ProcessBuilder$Redirect;
redirectInput方法的描述符是:(Ljava/lang/ProcessBuilder$Redirect;)Ljava/lang/ProcessBuilder;
redirectOutput方法的描述符是:(Ljava/lang/ProcessBuilder$Redirect;)Ljava/lang/ProcessBuilder;
redirectError方法的描述符是:(Ljava/lang/ProcessBuilder$Redirect;)Ljava/lang/ProcessBuilder;
redirectInput方法的描述符是:(Ljava/io/File;)Ljava/lang/ProcessBuilder;
redirectOutput方法的描述符是:(Ljava/io/File;)Ljava/lang/ProcessBuilder;
redirectError方法的描述符是:(Ljava/io/File;)Ljava/lang/ProcessBuilder;
redirectInput方法的描述符是:()Ljava/lang/ProcessBuilder$Redirect;
redirectOutput方法的描述符是:()Ljava/lang/ProcessBuilder$Redirect;
redirectError方法的描述符是:()Ljava/lang/ProcessBuilder$Redirect;
inheritIO方法的描述符是:()Ljava/lang/ProcessBuilder;
redirectErrorStream方法的描述符是:()Z
redirectErrorStream方法的描述符是:(Z)Ljava/lang/ProcessBuilder;
start方法的描述符是:()Ljava/lang/Process;
<clinit>方法的描述符是:()V
Load class: java.lang.ProcessBuilder$NullInputStream
<init>方法的描述符是:()V
read方法的描述符是:()I
available方法的描述符是:()I
<clinit>方法的描述符是:()V
Load class: java.lang.ProcessBuilder$NullOutputStream
<init>方法的描述符是:()V
write方法的描述符是:(I)V
<clinit>方法的描述符是:()V
接下來我們看看嘗試一下能否拿到所執(zhí)行的命令
新建一個名為ProcessBuilderHook的類,然后在類中新建一個名字為start的靜態(tài)方法,完整代碼如下:
/*
* Copyright sky 2019-04-04 Email:sky@03sec.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.javaweb.agent;
import java.util.Arrays;
import java.util.List;
/**
* @author sky
*/
public class ProcessBuilderHook {
public static void start(List<String> commands) {
String[] commandArr = commands.toArray(new String[commands.size()]);
System.out.println(Arrays.toString(commandArr));
}
}
這個方法干啥用的我們一會在說,先看下面。
打開TestClassVisitor,對visitMethod方法進(jìn)行更改。具體代碼如下:
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if ("start".equals(name) && "()Ljava/lang/Process;".equals(desc)) {
System.out.println(name + "方法的描述符是:" + desc);
return new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) {
@Override
public void visitCode() {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "java/lang/ProcessBuilder", "command", "Ljava/util/List;");
mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/agent/ProcessBuilderHook", "start", "(Ljava/util/List;)V", false);
super.visitCode();
}
};
}
return mv;
}
給大家解釋下新增加的代碼,從if判斷開始
判斷傳入進(jìn)來的方法名是否為start以及方法描述符是否為()Ljava/lang/Process;,如果是的話就新建一個AdviceAdapter方法,并且復(fù)寫visitCode方法,對其字節(jié)碼進(jìn)行修改,
mv.visitVarInsn(ALOAD, 0);
拿到棧頂上的this
mv.visitFieldInsn(GETFIELD, "java/lang/ProcessBuilder", "command", "Ljava/util/List;");
拿到this里面的command
mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/agent/ProcessBuilderHook", "start", "(Ljava/util/List;)V", false);
然后調(diào)用我們上面新建的ProcessBuilderHook類中的start方法,將上面拿到的this.command壓入我們方法。
ProcessBuilderHook類的作用就是讓這部分進(jìn)行調(diào)用,然后轉(zhuǎn)移就可以轉(zhuǎn)入到我們的邏輯代碼了。
我們再次編譯一下,然后啟動tomcat,訪問cmd.jsp看看.
訪問
http://localhost:8080/cmd.jsp?cmd=ls%20-la
可以看到已經(jīng)將當(dāng)前目錄下的內(nèi)容打印了出來。
我們到idea中看看控制臺輸出了什么。
可以看到我們輸入的命令
[whoami]
已經(jīng)輸出出來了,到此為止,我們拿到了要執(zhí)行的命令.
關(guān)于怎樣淺談RASP技術(shù)的環(huán)境配置與代碼實現(xiàn)就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。