浅谈被动式IAST产品与技术实现

渗透技巧 2年前 (2021) admin
450 0 0


浅谈被动式IAST产品与技术实现

V-lab实验室

浅谈被动式IAST产品与技术实现

笔者曾参与RASP研究与研发得到一些相关经验,近两年观察到IAST发展势头明显,但目前国内外对于IAST具体实现的细节相关文章较少,且笔者看到的开源IAST仅有洞态,故想通过笔者视角,对IAST的原理及技术实现进行探究及分享。

本文仅代表笔者个人观点,欢迎大家进行技术交流及学习。

什么是IAST

IAST是AST其中的一个类别,AST是Application Security Testing的简称,译为应用安全测试,在其之下衍生出来以下几种类型:

  • SAST(Static Application Security Testing)静态应用安全测试
  • DAST(Dynamic Application Security Testing)动态应用安全测试
  • MAST(Mobile Application Security Testing)移动应用安全测试
  • IAST (Interactive Application Security Testing)交互式应用安全测试

对于IAST的定义我并没有在Gartner找到其相关的术语表,但是在Gartner推荐的服务商中找到了一些关于IAST的定义,核心内容如下:

IAST使用运行时代理方法在测试阶段分析与监控应用程序的行为。这种类型的测试也不测试整个应用程序或代码,而只测试执行功能的部分。

有趣的是,多数人认为IAST是Gartner2012年提出来的术语,但我在Gartner的术语表内并没有找到IAST的相关定义(可能由于Gartner之前改版,导致这个术语丢失),于是我在Gartner推荐的服务商中找到了IAST相关的标签和简单的介绍。

浅谈被动式IAST产品与技术实现

关于IAST的细分,可参考以下文章 https://www.freebuf.com/sectool/290671.html 这篇文章对IAST的分类有比较清晰的描述。本文以下内容主要围绕被动式IAST进行分析介绍。

外IAST产品

笔者对国内外的IAST相关的产品公司进行了一些整理,内容如下(该数据不代表所有的IAST厂商,仅为笔者搜索到的部分厂商):


浅谈被动式IAST产品与技术实现

浅谈被动式IAST产品与技术实现

浅谈被动式IAST产品与技术实现

浅谈被动式IAST产品与技术实现

浅谈被动式IAST产品与技术实现


被动式IAST要想实现,那么其实和RASP差别不大,区别主要集中在埋点检测,从而达到对调用链的精准跟踪,在这一细小部分,我个人的理解是,对所有有可能导致source获取到的参数进行改变的方法进行埋点,包括但不限于类似以下几种情况(下面仅是伪代码,并不代表真实逻辑中的代码,仅便于大家理解):

new String(....)"aa".replace(...)StringBuilder sb = new StringBuilder();Base64.decode(...)

此链路需根据实际业务情况进行完善,例如实现某个加解密的类等,又或者是加入对souce进行安全过滤处理的方法,然后将所有经过预埋点的堆栈信息进行拼接,在这个过程中,可以去判断这条链路经过了安全过滤处理方法,那么或许可以粗暴的不上报这条调用链信息,认为这是一个安全的请求(当然这种情况还是要谨慎,毕竟研发中难免会犯一些错误,所以在情况允许的环境下,还是全部上报,交给人工进行复验、排除是更为妥当的解决方式),然后将数据上报到服务端,到此完成一个IAST的技术理念逻辑。

那么其实是不是可以使用一些APM的开源技术,对它进行改造,从而实现IAST的部分功能。如果想深度控制IAST的流程,更好的方式就是自己实现一套IAST埋点、检测逻辑。

现IAST所的一些技术

如果想要从零实现一个被动式的IAST,我们至少需要掌握关于字节码操作的技术,例如ASM、Javassist等,若不想从零或底层的方式去实现,可以试试使用AspectJ技术,或结合使用开源APM框架进行改造,让其成为一个简单的被动IAST。

本次所涉及的Demo源码已经公开,Github项目为: iiiusky/java_iast_example。

实验环境搭建

这次IAST相关的环境其实和之前的RASP环境基本差不多。大家可以参照之前的浅谈RASP技术攻防之实战[环境配置篇]文章内容去搭建一个本地的实验环境,唯一变的,可能就是包名了。

demo整体逻辑

这次实验的整体逻辑如果相比真正的IAST,肯定会有很多缺少的细节部分完善,所以仅仅适合用来学习了解被动IAST实现的大致流程,整体逻辑图如下:

浅谈被动式IAST产品与技术实现

从上图可以看到,其实在这次demo实现的过程中,逻辑也并不是很复杂,大致文字版说明如下:

http->enterHttp->enterSource->leaveSource
enterPropagator->leavePropagator(…………此过程重复n次…………)
enterSink->leaveSink(可省略)->leaveHttp

以上大致完成了整个污点跟踪链路流程,在初始化HTTP的时候,将新建一个LinkedList<CallChain>类型的对象,用来存储线程链路调用的数据。

package cn.org.javaweb.iast.visitor;
import org.objectweb.asm.MethodVisitor;
/** * @author iiusky - 03sec.com */public interface Handler {
MethodVisitor ClassVisitorHandler(MethodVisitor mv, final String className, int access, String name, String desc, String signature, String[] exceptions);}

实现Http埋点

为了方便对不同类型的点进行适配,抽象了一个Handler出来,然后在根据不同的类型实现具体的ClassVisitorHandler内容,Handler.java具体代码如下:

在Java EE中通过劫持javax.servlet.Servletservice方法和javax.servlet.Filter类的doFilter方法不但可以获取到原始的HttpServletRequestHttpServletResponse对象,还可以控制Servlet和Filter的程序执行逻辑。

可以将所有参数描述符为(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V的方法进行插入埋点,并缓存request、response对象。

实现的代码如下(示例代码为了便于理解未考虑异常处理):

package cn.org.javaweb.iast.visitor.handler;
import cn.org.javaweb.iast.visitor.Handler;import org.objectweb.asm.MethodVisitor;import org.objectweb.asm.Opcodes;import org.objectweb.asm.Type;import org.objectweb.asm.commons.AdviceAdapter;
import java.lang.reflect.Modifier;

/** * @author iiusky - 03sec.com */public class HttpClassVisitorHandler implements Handler {
private static final String METHOD_DESC = "(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V";
public MethodVisitor ClassVisitorHandler(MethodVisitor mv, final String className, int access, String name, String desc, String signature, String[] exceptions) { if ("service".equals(name) && METHOD_DESC.equals(desc)) { final boolean isStatic = Modifier.isStatic(access); final Type argsType = Type.getType(Object[].class);
System.out.println( "HTTP Process 类名是: " + className + ",方法名是: " + name + "方法的描述符是:" + desc + ",签名是:" + signature + ",exceptions:" + exceptions); return new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) { @Override protected void onMethodEnter() { loadArgArray(); int argsIndex = newLocal(argsType); storeLocal(argsIndex, argsType); loadLocal(argsIndex);
if (isStatic) { push((Type) null); } else { loadThis(); } loadLocal(argsIndex); mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/iast/core/Http", "enterHttp", "([Ljava/lang/Object;)V", false);
}
@Override protected void onMethodExit(int i) { super.onMethodExit(i); mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/iast/core/Http", "leaveHttp", "()V", false); } }; } return mv; }}

上面的代码将对所有实现javax.servlet.Servlet#service的方法进行了埋点处理(接口、抽象类除外),真正编译到jvm中的类如下:

浅谈被动式IAST产品与技术实现

可以看到,在对进入方法的时候调用了IAST中的方法cn.org.javaweb.iast.core.Http#enterHttp,在离开方法的时候,调用了cn.org.javaweb.iast.core.Http#leaveHttp其中enterHttp具体代码如下:

public static void enterHttp(Object[] objects) {    if (!haveEnterHttp()) {      IASTServletRequest request = new IASTServletRequest(objects[0]);      IASTServletResponse response = new IASTServletResponse(objects[1]);
RequestContext.setHttpRequestContextThreadLocal(request, response, null); } }

从上文中可以看到,传入的HttpServletRequestHttpServletResponse对象存到了当前线程的上下文中,方便后续对数据的调取使用。

leaveHttp具体代码如下:

public static void leaveHttp() {    IASTServletRequest request = RequestContext.getHttpRequestContextThreadLocal()        .getServletRequest();    System.out.printf("URL            : %s n", request.getRequestURL().toString());    System.out.printf("URI            : %s n", request.getRequestURI().toString());    System.out.printf("QueryString    : %s n", request.getQueryString().toString());    System.out.printf("HTTP Method    : %s n", request.getMethod());    RequestContext.getHttpRequestContextThreadLocal().getCallChain().forEach(item -> {      if (item.getChainType().contains("leave")) {        String returnData = null;        if (item.getReturnObject().getClass().equals(byte[].class)) {          returnData = new String((byte[]) item.getReturnObject());        } else if (item.getReturnObject().getClass().equals(char[].class)) {          returnData = new String((char[]) item.getReturnObject());        } else {          returnData = item.getReturnObject().toString();        }
System.out .printf("Type: %s CALL Method Name: %s CALL Method Return: %s n", item.getChainType(), item.getJavaClassName() + item.getJavaMethodName(), returnData); } else { System.out .printf("Type: %s CALL Method Name: %s CALL Method Args: %s n", item.getChainType(), item.getJavaClassName() + item.getJavaMethodName(), Arrays.asList(item.getArgumentArray())); } }); }

从当前线程中获取到在调用enterHttp时候存的数据,对其中的数据进行可视化的输出打印。

实现Source埋点

在Java EE中通过可以劫持获取输入源的所有方法,比如常用的getParametergetHeader等类似的方法,在这里将对调用的方法、以及返回的参数进行跟踪,这里为真正污点跟踪的起点。可以简单的理解为就是http各个get方法即为来源,但这一结论不保证完全适配所有情况。对于Source相关的点处理的代码如下(示例代码为了便于理解未考虑异常处理):

package cn.org.javaweb.iast.visitor.handler;
import cn.org.javaweb.iast.visitor.Handler;import org.objectweb.asm.MethodVisitor;import org.objectweb.asm.Opcodes;import org.objectweb.asm.Type;import org.objectweb.asm.commons.AdviceAdapter;
import java.lang.reflect.Modifier;

/** * @author iiusky - 03sec.com */public class SourceClassVisitorHandler implements Handler {
private static final String METHOD_DESC = "(Ljava/lang/String;)Ljava/lang/String;";
public MethodVisitor ClassVisitorHandler(MethodVisitor mv, final String className, int access, final String name, final String desc, String signature, String[] exceptions) { if (METHOD_DESC.equals(desc) && "getParameter".equals(name)) { final boolean isStatic = Modifier.isStatic(access);
System.out.println("Source Process 类名是: " + className + ",方法名是: " + name + "方法的描述符是:" + desc + ",签名是:" + signature + ",exceptions:" + exceptions); return new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) { @Override protected void onMethodEnter() { loadArgArray(); int argsIndex = newLocal(Type.getType(Object[].class)); storeLocal(argsIndex, Type.getType(Object[].class)); loadLocal(argsIndex); push(className); push(name); push(desc); push(isStatic);
mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/iast/core/Source", "enterSource", "([Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", false); super.onMethodEnter(); }
@Override protected void onMethodExit(int opcode) { Type returnType = Type.getReturnType(desc); if (returnType == null || Type.VOID_TYPE.equals(returnType)) { push((Type) null); } else { mv.visitInsn(Opcodes.DUP); } push(className); push(name); push(desc); push(isStatic); mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/iast/core/Source", "leaveSource", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", false); super.onMethodExit(opcode); } }; } return mv; }}

以上代码的逻辑,只是简单的对于getParameter进行了埋点处理,让其调用IAST的处理逻辑,编译到JVM的Class内容如下:

浅谈被动式IAST产品与技术实现

可以看到,在进入方法后调用了cn.org.javaweb.iast.core.Source#enterSource,具体内容如下:

public static void enterSource(Object[] argumentArray,	                               String javaClassName,	                               String javaMethodName,	                               String javaMethodDesc,	                               boolean isStatic) {		if (haveEnterHttp()) {			CallChain callChain = new CallChain();			callChain.setChainType("enterSource");			callChain.setArgumentArray(argumentArray);			callChain.setJavaClassName(javaClassName);			callChain.setJavaMethodName(javaMethodName);			callChain.setJavaMethodDesc(javaMethodDesc);			callChain.setStatic(isStatic);			RequestContext.getHttpRequestContextThreadLocal().addCallChain(callChain);		}	}

对参数、类名、方法名、描述符等信息添加到了callChain中. 在方法结束前获取了返回值,并且调用了cn.org.javaweb.iast.core.Source#leaveSource方法,将返回值传入了进去,那么在处理的时候,就将其结果放到了callChain.returnObject

浅谈被动式IAST产品与技术实现

实现Propagator埋点

传播点的选择是非常关键的,传播点规则覆盖的越广得到的传播链路就会更清晰。比如简单粗暴的对StringByte等类进行埋点,因为中间调用这些类的太多了,所以可能导致一个就是结果堆栈太长,不好对调用链进行分析,但是对于传播点的选择,可以更精细化一些去做选择,比如Base64decodeencode也可以作为传播点进行埋点,以及执行命令的java.lang.Runtime#exec也是可以作为传播点的,因为最终执行命令是最底层在不同系统封装的调用执行命令JNI方法的类,如java.lang.UNIXProcess等,所以将java.lang.Runtime#exec作为传播点也是一个选择。为了方便演示污点传播的效果,对Base64decode以及encodejava.lang.Runtime进行了埋点处理,具体实现代码如下(示例代码为了便于理解未考虑异常处理):

package cn.org.javaweb.iast.visitor.handler;
import cn.org.javaweb.iast.visitor.Handler;import org.objectweb.asm.MethodVisitor;import org.objectweb.asm.Opcodes;import org.objectweb.asm.Type;import org.objectweb.asm.commons.AdviceAdapter;
import java.lang.reflect.Modifier;

/** * @author iiusky - 03sec.com */public class PropagatorClassVisitorHandler implements Handler {
private static final String METHOD_DESC = "(Ljava/lang/String;)[B";
private static final String CLASS_NAME = "java.lang.Runtime";
@Override public MethodVisitor ClassVisitorHandler(MethodVisitor mv, final String className, int access, final String name, final String desc, String signature, String[] exceptions) { if ((name.contains("decode") && METHOD_DESC.equals(desc)) || CLASS_NAME.equals(className)) { final boolean isStatic = Modifier.isStatic(access); final Type argsType = Type.getType(Object[].class);
if (((access & Opcodes.ACC_NATIVE) == Opcodes.ACC_NATIVE) || className .contains("cn.org.javaweb.iast")) { System.out.println( "Propagator Process Skip 类名:" + className + ",方法名: " + name + "方法的描述符是:" + desc); } else { System.out .println("Propagator Process 类名:" + className + ",方法名: " + name + "方法的描述符是:" + desc); return new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) { @Override protected void onMethodEnter() { loadArgArray(); int argsIndex = newLocal(argsType); storeLocal(argsIndex, argsType); loadLocal(argsIndex); push(className); push(name); push(desc); push(isStatic);
mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/iast/core/Propagator", "enterPropagator", "([Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", false); super.onMethodEnter(); }
@Override protected void onMethodExit(int opcode) { Type returnType = Type.getReturnType(desc); if (returnType == null || Type.VOID_TYPE.equals(returnType)) { push((Type) null); } else { mv.visitInsn(Opcodes.DUP); } push(className); push(name); push(desc); push(isStatic); mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/iast/core/Propagator", "leavePropagator", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", false); super.onMethodExit(opcode); } }; } } return mv; }}

正运行在JVM中的类如下:

java.util.Base64$Decoder#decode

浅谈被动式IAST产品与技术实现

java.lang.Runtime

浅谈被动式IAST产品与技术实现

可以看到其实也是在方法进入后和方法离开前插入了IAST的代码逻辑,以便直观的观察到入参值以及返回值发生的变化。

实现Sink埋点

对于Sink点的选择,其实和找RASP最终危险方法的思路一致,只限找到危险操作真正触发的方法进行埋点即可,比如java.lang.UNIXProcess#forkAndExec方法,这种给java.lang.UNIXProcess#forkAndExec下点的方式太底层,如果不想这么底层,也可以仅对java.lang.ProcessBuilder#start方法或者java.lang.ProcessImpl#start进行埋点处理。本次实验选择了对java.lang.ProcessBuilder#start进行埋点处理,具体实现代码如下(示例代码为了便于理解未考虑异常处理):

package cn.org.javaweb.iast.visitor.handler;
import cn.org.javaweb.iast.visitor.Handler;import org.objectweb.asm.MethodVisitor;import org.objectweb.asm.Opcodes;import org.objectweb.asm.Type;import org.objectweb.asm.commons.AdviceAdapter;
import java.lang.reflect.Modifier;

/** * @author iiusky - 03sec.com */public class SinkClassVisitorHandler implements Handler {
private static final String METHOD_DESC = "()Ljava/lang/Process;";
@Override public MethodVisitor ClassVisitorHandler(MethodVisitor mv, final String className, int access, final String name, final String desc, String signature, String[] exceptions) { if (("start".equals(name) && METHOD_DESC.equals(desc))) { final boolean isStatic = Modifier.isStatic(access); final Type argsType = Type.getType(Object[].class);
System.out.println("Sink Process 类名:" + className + ",方法名: " + name + "方法的描述符是:" + desc); return new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) { @Override protected void onMethodEnter() { loadArgArray(); int argsIndex = newLocal(argsType); storeLocal(argsIndex, argsType); loadThis(); loadLocal(argsIndex); push(className); push(name); push(desc); push(isStatic);
mv.visitMethodInsn(INVOKESTATIC, "cn/org/javaweb/iast/core/Sink", "enterSink", "([Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", false); super.onMethodEnter(); } }; } return mv; }}

在这次实验中,选择了对所有方法名为start且方法描述为()Ljava/lang/Process;的类进行埋点,其实也就是对java.lang.ProcessBuilder#start进行埋点处理。最终运行在JVM中的class如下:

浅谈被动式IAST产品与技术实现

在方法进去后调用了IAST的cn.org.javaweb.iast.core.Sink#enterSink方法,以此来确定一个调用链是否已经到达危险函数执行点。对于Sink,除了整体处理逻辑与Propagator以及Source相似,多了一个setStackTraceElement的操作,目的是将在触发Sink点的堆栈将其保存下来,方便后面使用分析。具体代码如下:

public static void enterSink(Object[] argumentArray,	                             String javaClassName,	                             String javaMethodName,	                             String javaMethodDesc,	                             boolean isStatic) {		if (haveEnterHttp()) {			CallChain callChain = new CallChain();			callChain.setChainType("enterSink");			callChain.setArgumentArray(argumentArray);			callChain.setJavaClassName(javaClassName);			callChain.setJavaMethodName(javaMethodName);			callChain.setJavaMethodDesc(javaMethodDesc);			callChain.setStatic(isStatic);			callChain.setStackTraceElement(Thread.currentThread().getStackTrace());			RequestContext.getHttpRequestContextThreadLocal().addCallChain(callChain);		}	}

结果验证

全部实现完成后,写一个jsp来执行命令试试看,代码如下:

该JSP接收一个参数,然后对该参数进行base64解码后传入Runtime.exec中来执行命令,最后输出执行结果。

<%@ page import="java.io.InputStream" %><%@ page import="java.util.Base64" %><%@ page contentType="text/html;charset=UTF-8" language="java" %><pre><%    String sb = request.getParameter("cmd");    byte[] decode = Base64.getDecoder().decode(sb);    Process process = Runtime.getRuntime().exec(new String(decode));    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>

接着编译agent,将其加入到tomcat的启动命令中,部署jsp页面,访问结果。

浅谈被动式IAST产品与技术实现

可见,首先触发了getParameter方法中的Source埋点,传入的参数为cmd,获取到的结果为CHdK,接着连续触发了5次Propagator点。

第一次触发的Propagator点位于Base64类中decode方法,传入的参数是CHdK,返回值为pwd(原始返回为[]byte,为了方便展示,对其转为了字符串),这时候已经可以初步看到了参数的获取到base64解码,也就是原始source点已经发生了变化。

第二次触发的埋点信息为获取一个Runtime对象,调用的是java.lang.Runtime#getRuntime,传入的参数为空,返回的结果为一个Runtime的对象信息,其实就是实例化了一个java.lang.Runtime对象,这次可以观察到一个小细节,就是这个返回对象发生了变化,但是并没有传入任何参数。

浅谈被动式IAST产品与技术实现

第三次触发的埋点信息为调用java.lang.Runtime#exec方法(接收参数类型为:String),传入的值是pwn,在这次调用中可以看到,第一次Propagator点的返回值作为了入参传入了这次调用,但是紧接着并触发没有想象中的leavePropagator方法,而是调用了另一个exec方法。

浅谈被动式IAST产品与技术实现

第四次触发的埋点信息为调用java.lang.Runtime#exec方法(接收参数类型为:String、String[]、File),其中第一个参数的值为pwn,而其它参数为null(本文不讨论如何确定第几个参数是污染点的问题,这个可以通过加规则去逐步完善)。在这次调用中可以看到,第三次中传递过来的pwn没有发生变化,然而也没有触发leavePropagator方法,由此可以推测出来这个方法内部继续调用了在规则里面预先匹配到的方法。

浅谈被动式IAST产品与技术实现

第五次触发的埋点信息为调用java.lang.Runtime#exec方法(接收参数类型为:String[]、String[]、File),传入的值是[[Ljava.lang.String;@58ed07d8, null, null],这时候就看到了在传入的值由pwn变为了一个String数组类型的对象,返回到第四次触发的埋点看,其实就可以看到var6其实是最开始是由var1,也就是入参值pwn转换得到的。然后可以看到在当前调用的方法里面,又调用了规则中的Sink点(java.lang.ProcessBuilder#start)方法。

浅谈被动式IAST产品与技术实现

以上就是大概从Srouce点(getParameter),经过中间的Propagator点(java.util.Base64$Decoder#decode、java.lang.Runtime#getRuntime、java.lang.Runtime#exec)到最终Sink点(java.lang.ProcessBuilder#start)的整体大概流程了。

总结

在本次实验中,将java.lang.Runtime作为了传播点,其实在整体流程访问结束后,这个传播点才会有返回值返回回来,他是在传播的过程中调用到了Sink点。

浅谈被动式IAST产品与技术实现

那么对于这种情况,是否应该摒弃将java.lang.Runtime作为传播点呢?这其实应该就是仁者见仁智者见智了,对于整体IAST的流程,其实和RASP流程差不多,但是对于传播点的选择,目前大家更多的是基于规则(正则or继承类)判断去覆盖其中的传播链,或者更简单粗暴的对StringByte进行埋点,但是需要处理的细节也就更多了,以及对于在整条链路中的无用调用也需要处理。是否有一种一劳永逸的办法可以完整的拿到整条污点传播链路,从而抛弃基于规则的对传播点进行人为覆盖,这个可能就需要进行更加深入的研究了。

在这次实现的demo中,并没有结合真正业务去实现,以及IAST的其它功能点去展开研究,比如流量重放、SCA、污点在方法中的参数位置等功能。如果仅仅是想融入DevSecOps中,可以基于开源的APM项目实现一个简易的IAST,根据具体的一些公司开发规范,去定制一些规则点,来减少因为某些问题导致的误报情况。


参考链接

  • https://www.03sec.com/Ideas/qian-tanrasp-ji-shu-gong-fang-zhi-shi-zhan-huan-ji.html
  • https://www.freebuf.com/sectool/290671.html
  • https://www.gartner.com/reviews/market/application-security-testing
  • https://doc.dongtai.io/
  • http://rui0.cn/archives/1175
  • https://dzone.com/refcardz/introduction-to-iast
  • https://blog.secodis.com/2015/11/26/the-emerge-of-iast/



VLab实验室是墨云科技旗下专注于信息安全领域的渗透测试、红蓝对抗、攻击模拟、漏洞挖掘等方向前沿技术研究的实验室。目前,实验室的多项研究成果已成功应用于公司主流产品,同时也持续向外输出安全能力,以攻促防,共同推动互联网安全发展。
实验室长期招聘安全研究和安全开发人员,公司提供行业有竞争力的薪酬及自由快速的成长环境,诚邀各位大牛加入~

岗位JD


安全研发专家(自动化渗透、攻击模拟方向)

职位描述:
    1. 负责公司内部自动化安全产品的开发和维护,以及日常安全事务的处理;
    2. 跟踪业内攻防技术、研究、实现等,掌握实时业内安全动态;
任职要求:
    1. 有安全产品开发经验,包括但不限于漏洞扫描、态势感知、防火墙、入侵检测产品等;
    2. 熟悉高并发 & 高可用架构、负载均衡、消息队列、缓存、分布式存储的应用和开发;
    3. 熟悉常见漏洞攻击原理和验证扫描方法,能够给出相应的漏洞修复方案;
    4. 熟练掌握 Python、Go 、Java、ruby、C++等其中的一种,编写过完整的项目经验;
    5. 良好的分析问题和解决问题的能力,能够独立完成产品模块的设计与实现;


安全研究员(红蓝对抗、漏洞挖掘方向)

岗位职责:
    1、针对常见操作系统、软件漏洞挖掘与分析相关技术研究;
    2、网络、系统及应用的安全攻击和防御技术研究;
    3,安全研究成果输出,提升公司安全影响力;
    3、配合相关产品或项目进行研究成果间接转化;
任职要求:
    1、熟悉攻防技术以及安全漏洞原理,有过独立分析或挖掘漏洞的经验;
    2、对从事漏洞研究工作充满兴趣,善于分享;
    3、熟悉计算机和网络基本原理,有反汇编和逆向分析能力;

    4、在渗透测试、统安全、IoT、移动安全任意安全领域有一定研究;
    5、较强的独立工作和学习能力,做事认真细致,有钻研精神;
    6、有0day漏洞挖掘经验或安全会议演讲经验优先。


简历投递:[email protected](可备注:VLab实验室)
工作地点:北京(海淀区清河地铁站中关村发展大厦)


浅谈被动式IAST产品与技术实现

往期回顾

浅谈被动式IAST产品与技术实现
墨云安全VLab实验室再获Google公开致谢
Chrome V8引擎类型混淆漏洞分析(CVE-2021-38001)
Apache ShenYu管理系统登录认证绕过漏洞分析(CVE-2021-37580)

浅谈被动式IAST产品与技术实现
引领智能网络攻防科技
浅谈被动式IAST产品与技术实现

点击在看
分享给小伙伴

↓点击阅读原文,了解更多墨云信息

原文始发于微信公众号(VLab Team):浅谈被动式IAST产品与技术实现

版权声明:admin 发表于 2021年12月15日 下午7:30。
转载请注明:浅谈被动式IAST产品与技术实现 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...