AG直营平台 Hades开源白盒审计系统V1.0.0

 AG直营平台     |      2020-03-07 09:18

Source点:

.local v5,"statement":Ljava/sql/Statement;

invoke-virtual {v6, v7},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

invoke-direct {p0},Ljavax/servlet/http/HttpServlet;->V

v2 == in0

"1: .registers 13",

"41: const-string v7, \\INSERTINTO users (username, password) VALUES (\\'foo\\',\\'\\",

.fieldprivate static final serialVersionUID:J = 0x1L

value = {

Ljavax/servlet/ServletException;,

https://github.com/zsdlove/Hades

move-result v0

那么,我为什么要选择smali作为分析java程序的目标代码呢?引言中已经提到了,一方面,最近在做一个android相关的项目,项目中已经实现了smali层面的污点跟踪引擎,如果能够直接复用之前写的这套污点分析代码,那岂不是很爽,不用继续造轮子了。另一方面,相较java字节码来说,smali字节码更加的简单,因为smali字节码是一种基于寄存器的指令系统,它的指令是二地址和三地址混合的,指令中指明了操作数的地址。而JVM是基于栈的虚拟机,JVM将本地变量放到一个本地变量列表中,在进行指令解释的时候,将变量push到操作数栈中,由操作码对应的解释函数来进行解释执行。所以,java字节码操作无疑会比smali字节码更复杂一些,复杂主要体现在后续的堆栈设计以及代码解释执行。

value = {

"35: const-string v6,\\UTF-8\\",

.endmethod

out0 v1 == in0

"invoke-virtual":self.handle_invoke, "invoke-virtual/range": self.handle_invoke, "invoke-static": self.handle_invoke, "invoke-direct": self.handle_invoke, "invoke-super": self.handle_invoke_super, "invoke-static/range": self.handle_invoke, "invoke-interface": self.handle_invoke, "invoke-interface-range": self.handle_invoke, "invoke-super/range": self.handle_invoke_super, "invoke-direct/range": self.handle_invoke, "move-result-object": self.handle_move_result, "move-result": self.handle_move_result, "move-result-wide": self.handle_move_result, "move-exception": self.handle_move_result, "move-object": self.handle_move, "move-object/from16":self.handle_move, "move-object/16":self.handle_move, "move-wide": self.handle_move, "move-wide/from16": self.handle_move, "move-wide/16": self.handle_move, "move": self.handle_move, "move/from16": self.handle_move, "move/16": self.handle_move, "const-string": self.handle_const, "const-string-jumbo": self.handle_const, "const": self.handle_const, "const/4":self.handle_const, "const/8":self.handle_const, "const/16":self.handle_const, "const/high16":self.handle_const, "const-wide/16":self.handle_const, "const-wide/32":self.handle_const, "const-wide":self.handle_const, "const-wide/high16":self.handle_const, "const-class": self.handle_const, "goto":self.handle_goto, "goto/16":self.handle_goto, "goto/32":self.handle_goto, ".registers":self.handle_registers, ".line":self.handle_line, "check-cast":self.default, "if-eqz":self.default, "default":self.default, "return":self.handle_return, "return-object":self.handle_return, "return-void":self.handle_return, "return-wide":self.handle_return, "new-array": self.handle_array_create, "aput-object": self.handle_aput, "aget-object": self.handle_aget, "aget-wide": self.handle_aget, "aput-wide": self.handle_aput, "aget-boolean": self.handle_aget, "aput-boolean": self.handle_aput, "aget-byte": self.handle_aget, "aput-byte": self.handle_aput, "aget-char": self.handle_aget, "aput-char": self.handle_aput, "aget-short": self.handle_aget, "aput-short": self.handle_aput, "iput-object": self.handle_aput, "iget-object": self.handle_aget, "iget-wide": self.handle_aget, "iput-wide": self.handle_aput, "iget-boolean": self.handle_aget, "iput-boolean": self.handle_aput, "iget-byte": self.handle_aget, "iput-byte": self.handle_aput, "iget-char": self.handle_aget, "iput-char": self.handle_aput, "iget-short": self.handle_aget, "iput-short": self.handle_aput, "sput-object": self.default, "sget-object": self.default, "sget-wide": self.default, "sput-wide": self.default, "sget-boolean": self.default, "sput-boolean": self.default, "sget-byte": self.default, "sput-byte": self.default, "sget-char": self.default, "sput-char": self.default, "sget-short": self.default, "sput-short": self.default, "new-instance": self.default, "packed-switch": self.default, "sparse-switch": self.default,

* This file is part of the Open Web Application Security Project (OWASP)

}

.source"BenchmarkTest00018.java"

.line 45

packageorg.owasp.benchmark.testcode;

String param = "";

.line 59

move-result-object v2

1、寄存器

二、整体架构设计 1、前端方面

return;

:catch_55

return-void

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V66",

param = java.net.URLDecoder.decode(param, "UTF-8");

.line 46

三、smali字节码简介

.end local v3 # "param":Ljava/lang/String;

"19: invoke-interface {p1, v7},Ljavax/servlet/http/HttpServletRequest;->getHeaders(Ljava/lang/String;)Ljava/util/Enumeration;",

"",

invoke-direct {v6},Ljava/lang/StringBuilder;->V

- - - - - - - - - –

try {

invoke-interface {v2},Ljava/util/Enumeration;->hasMoreElementsZ

.local v1,"e":Ljava/io/IOException;

"",

invoke-virtual {v7, v8},Ljava/io/PrintWriter;->println(Ljava/lang/String;)V

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V62",

.end annotation

.line 62

.line 58

1、 QQ:747289639(微信同)

importjava.io.IOException;

.end annotation

"68: move-result-object v7",

* OWASPBenchmark v1.2

Hades中的堆的设计并不是很多,也不是很完善,目前主要用来存放数组信息,用于处理数组成员的污点追踪操作。

"",

invoke-virtual {v7, v5},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

Ljava/io/IOException;

"65: invoke-virtual {v7, v0},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",

2、污点栈

.param p1, "request" # Ljavax/servlet/http/HttpServletRequest;

.line 37

publicclass BenchmarkTest00018 extends HttpServlet {

https://github.com/zsdlove/Hades

"",

"",

"/cmdi-00/BenchmarkTest00017"

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V20",

Ljava/sql/Statement;->executeUpdate(Ljava/lang/String;)I

"16: .line 46",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V68",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V25",

"43: move-result-object v6",

"45: move-result-object v6",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V34",

invoke-virtual {p0, p1, p2},Lorg/owasp/benchmark/testcode/BenchmarkTest00018;->doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V

4、 程序入口点规则配置。

.registers 3

"sink":"Ljava/sql/Statement;->executeUpdate(Ljava/lang/String;)I",

const-string v7, "\')"

.local v4,"p":Ljava/lang/Process;

invoke-virtual {v6},Ljava/lang/StringBuilder;->toStringLjava/lang/String;

move-result-object v7

}

六、用到的第三方工具

通过redis消息队列接收到任务信息,新建一个任务线程对上传的文件进行分析。针对源码的处理是,如果是maven管理的项目则使用mvn compile进行编译。如果是普通的java项目的话则使用javac进行单文件编译(考虑到整体编译的话会造成编译失败,遗失字节码文件)。然后,将最终得到的java字节码文件转为smali字节码文件(为什么要转为smali字节码文件呢?后面会进行说明),最后将最终的字节码文件作为输入传递给主引擎程序进行执行分析。

"50: const/4 v8, -0x1",

- -

out1 v2 == in1

const-string v0, ""

.end method

.param p2, "response" # Ljavax/servlet/http/HttpServletResponse;

.line 66

move-result-object v2

五、规则配置部分

}

0×1 指令控制流图构造

3、底层引擎方面

本测试单元,对应benchmark中的第17个靶场。

.endmethod

"39: const-string v0, \\\\",

}

if-eqz v7, :cond_1b

String cmd = "";

doPost(request, response);

.restart local v5 # "param":Ljava/lang/String;

.endmethod

:try_end_45

.param p1, "request" # Ljavax/servlet/http/HttpServletRequest;

"49: move-result v7",

"",

.local v4,"sql":Ljava/lang/String;

],

String param = "";

好,下面我们使用Hades来对该程序进行检测。

}

.methodpublic constructor V

局部放大版:

"10: .prologue",

"14: .line 45",

return;

**/

private static final longserialVersionUID = 1L;

.prologue

"sinkBelongTo":"org/owasp/benchmark/testcode/BenchmarkTest00018; doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V",

org.owasp.benchmark.helpers.DatabaseHelper.outputUpdateComplete(sql,response);

importjavax.servlet.ServletException;

"sourceBelongTo":"org/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V"

"",

—————–

"38: .line 56",

"12: const-string v7,\\text/html;charset=UTF-8\\",

"13: invoke-interface {p2, v6},Ljavax/servlet/http/HttpServletResponse;->setContentType(Ljava/lang/String;)V",

要实现指令控制流图的构造,首先我们需要对目标函数的字节码信息进行切片处理,解析出一个个的指令代码块,然后将各个指令代码块进行依赖分析,并保存为图的形式。但是总不至于针对每一个函数都构造指令控制流图吧,这样太费时,而且有些函数,程序在执行过程中不一定会调用到的,那么我们构建它又有什么意义呢?。我们知道,每一个程序都会有一个入口函数,比如java程序中的main函数,或者一些涉及网络请求映射处理的函数,比如servlet中的doPost,doGet等。只要从这些入口点进行指令控制流图的构建AG直营平台,就可以尽可能的覆盖程序执行过程中可能走过的路线。

invoke-static {},Lorg/owasp/benchmark/helpers/DatabaseHelper;->getSqlStatementLjava/sql/Statement;

"18: const-string v7,\\BenchmarkTest00017\\",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V55",

*

move-result v7

],

invoke-interface {p2},Ljavax/servlet/http/HttpServletResponse;->getWriterLjava/io/PrintWriter;

response.setContentType("text/html;charset=UTF-8");

@Override

const-string v6, "UTF-8"

"30: .end local v3 # \\param\\:Ljava/lang/String;",

Ljavax/servlet/http/HttpServletRequest;->getHeaders(Ljava/lang/String;)Ljava/util/Enumeration;

invoke-static {},Lorg/owasp/esapi/ESAPI;->encoderLorg/owasp/esapi/Encoder;

.catch Ljava/io/IOException; {:try_start_3c.. :try_end_54} :catch_55

param = headers.nextElement; // just grab first element

.line 71

move-result-object v4

2、后端方面

"19: invoke-interface {p1, v6},Ljavax/servlet/http/HttpServletRequest;->getHeaders(Ljava/lang/String;)Ljava/util/Enumeration;",

@WebServlet(value="/cmdi-00/BenchmarkTest00017")

private static final long serialVersionUID = 1L;

.line 49

move-result-object v7

"25: move-result v6",

if (headers != null &&headers.hasMoreElements) {

}

v4 == in2

invoke-interface {p2, v6},Ljavax/servlet/http/HttpServletResponse;->setContentType(Ljava/lang/String;)V

invoke-static {v4, p2},Lorg/owasp/benchmark/helpers/Utils;->printOSCommandResults(Ljava/lang/Process;Ljavax/servlet/http/HttpServletResponse;)V

{

value = {

"59: move-result-object v6",

import javax.servlet.http.HttpServlet;

"46: .local v3,\\osName\\:Ljava/lang/String;",

0×5 污点追踪

move-exception v1

""

"55: move-result-object v0",

throw v6

这里AG直营平台,只显示字节码AG直营平台,和源码的关联性还没有做,不过要做的话也容易,因为字节码里都有对该部分的字节码对应的java源码的行号标记,即.line xx。我们根据字节码里的行号标记在源码里的对应行找到相应源码即可。

1、 sink点规则配置。

"",

.end local v4 # "p":Ljava/lang/Process;

#annotations

由于指令控制流图太大,无法清晰的展示中间的指令信息,所以下面重点展示下source点和sink点相关的片段。

# virtualmethods

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017; doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V14",

"11: .line 42",

if (osName.indexOf("Windows") != -1) {

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V57",

# directmethods

2、 source点规则配置。

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018; doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V55",

new-instance v6,Ljavax/servlet/ServletException;

下面是污点传递的示意图

Ljavax/servlet/ServletException;,

"27: .line 49",

.registers 1

@Override

move-result-object v6

:catch_46

"",

来自androidSDK,用于将java字节码转为smali字节码。

简要的说明下该程序代码:

.end local v5 # "param":Ljava/lang/String;

"source":"Ljavax/servlet/http/HttpServletRequest;->getHeaders(Ljava/lang/String;)Ljava/util/Enumeration;",

.classpublic Lorg/owasp/benchmark/testcode/BenchmarkTest00017;

"26: if-eqz v6, :cond_1b",

invoke-direct {v7},Ljava/lang/StringBuilder;->V

2、测试2 SQL注入漏洞

.line 68

value = {

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V24",

"40: .line 57",

"",

"20: move-result-object v2",

value = {

@Override

将Ljava/lang/Runtime;->exec(Ljava/lang/String;)Ljava/lang/Process;作为sink点进行分析。

.local v6,"r":Ljava/lang/Runtime;

这里我们将doPost作为我们的入口点进行指令控制流的构造,那么如何生成一个入口点呢?这里可以使用我项目中的一个入口点生成工具funcInvokeGenerate.py即可生成期望得到的入口点信息,只需要输入入口点函数名称和相应的所属的jar包路径即可。

.line 61

"",

@WebServlet(value="/sqli-00/BenchmarkTest00018")

importjavax.servlet.http.HttpServletRequest;

move-result v6

move-result-object v0

—————– <— frame pointer:for oncreate

import javax.servlet.ServletException;

const-string v8, "Problem executingcmdi - TestCase"

.line 45

.registers 13

"48: move-result-object v6",

Process p = r.exec(cmd param);

Hades解释执行的指令:

invoke-virtual {v7, v0},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

.param p2, "response" # Ljavax/servlet/http/HttpServletResponse;

前端采用easyUI编写,本来打算使用vue的,不过vue方面还不是特别的熟悉,考虑到解决bug来可能会比较耗时,所以采用比较简单的easyUI来编写前端。

"",

.endannotation

// URL Decode the header value since req.getHeaders doesn't. Unlike req.getParameters.

.methodpublic constructor V

"58: invoke-static {},Ljava/lang/Runtime;->getRuntimeLjava/lang/Runtime;",

"70: move-result-object v7",

"37: move-result-object v5",

"12: const-string v6,\\text/html;charset=UTF-8\\",

{

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V20",

"1: .registers 11",

Hades污点栈结构设计参考了DVM的的栈设计方案,有点类似’寄存器窗口’,这也是dvm解释器栈的一个特色,当然这只是大概的示意图,具体的栈结构和栈帧结构在上图的结构的基础上做了一些更加适配污点跟踪分析的设计,在此就不给出了。下面解释下栈帧顶部的out区域和in区域的作用,这里out和int区域的设计主要是为了函数间参数的传递,虽然我们不需要真实参数值/对象的传递,但是我们需要获取上一个函数传入的参数的污点属性。为了获取上一个函数中的污点属性,需要在上一个函数执行到函数调用的时候,将参数push到当前栈帧的顶部out区域,然后参数由out区域传入到in区域,当执行到调用的函数的时候,程序从栈帧的底部in区域获取到参数信息(包含了污点属性的参数信息,以及一些必要的值信息)。依据此污点栈设计,我们就可以对函数间的污点传播进行跟踪分析。

其中linearcode中记录的是从source点到sink点这条污点路线走过的所有指令。这里,我本来是想只解释执行source点到sink点之间的代码的,不过,后来发现如果只解释执行source点到sink点之间的指令代码的话,针对数组方面的污点分析能力就会变弱。为什么呢?因为数组成员的一些赋值操作是在source点之前进行的,如果没有对这些赋值操作进行解析,那么后续针对数组成员的污点分析无法实现,只能将数组整体标记为污点,认定从该数组取出来的元素都为污点,这样无疑会增加误报的几率。

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V53",

invoke-interface {p2, v7},Ljavax/servlet/http/HttpServletResponse;->setContentType(Ljava/lang/String;)V

if (headers != null && headers.hasMoreElements) {

move-result-object v5

4、 程序入口点规则配置。

.superLjavax/servlet/http/HttpServlet;

1、测试1 命令执行漏洞

import javax.servlet.http.HttpServletResponse;

后端程序通过request.getheader函数获取客户端网络请求的头部信息,并从中读取第一个element信息作为参数进入到拼接形式的sql语句中,并最终进入到sql操作函数中。

invoke-interface {p1, v6},Ljavax/servlet/http/HttpServletRequest;->getHeaders(Ljava/lang/String;)Ljava/util/Enumeration;

.prologue

"50: move-result-object v4",

"33: .restart local v3 # \\param\\:Ljava/lang/String;",

invoke-interface {p1, v7},Ljavax/servlet/http/HttpServletRequest;->getHeaders(Ljava/lang/String;)Ljava/util/Enumeration;

"7: Ljava/io/IOException;",

.endmethod

"36: invoke-static {v5, v7},Ljava/net/URLDecoder;->decode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",

.annotation system Ldalvik/annotation/Throws;

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V27",

"",

.line 37

以下是Hades生成的smali字节码

"sinkBelongTo":"org/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V",

"42: const-string v7,\\os.name\\",

"66: move-result-object v7",

—————–

.local v1,"e":Ljava/sql/SQLException;

"",

"44: invoke-virtual {v6, v3},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",

invoke-virtual {v1},Ljava/io/IOException;->getMessageLjava/lang/String;

"32: .line 53",

:goto_54

"",

"25: move-result v7",

}

invoke-interface {v8, v9},Lorg/owasp/esapi/Encoder;->encodeForHTML(Ljava/lang/String;)Ljava/lang/String;

move-result-object v3

3、堆部分

"69: invoke-virtual {v7},Ljava/lang/StringBuilder;->toStringLjava/lang/String;",

"",

"26: if-eqz v7, :cond_1b",

.param p2, "response" # Ljavax/servlet/http/HttpServletResponse;

"6:Ljavax/servlet/ServletException;,",

4、软件整体的执行流程示意图如下

"55: move-result-object v5",

invoke-static {v7},Ljava/lang/System;->getProperty(Ljava/lang/String;)Ljava/lang/String;

Name,value,isTained,分别对应对应寄存器名,寄存器值,及污点属性。之前说了,Hades的虚拟执行系统不需要值的介入,这里为什么还要有一个value呢?这里,虽然Hades不care值的处理,但是,为了能够准确的定位到数组索引,处理数组操作相关的污点传播问题,还是需要对这部分的值进行保存的。

- out0 -

"11: .line 42",

const-string v7,"text/html;charset=UTF-8"

#annotations

.line 60

"28: invoke-interface {v2},Ljava/util/Enumeration;->nextElementLjava/lang/Object;",

:try_start_3c

0×2 通路计算

"4: .annotation systemLdalvik/annotation/Throws;",

"17: .local v3,\\param\\:Ljava/lang/String;",

"9: .end annotation",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V1",

以下是我们需要配置的sink点:

Ljavax/servlet/ServletException;,

}

"23: if-eqz v2, :cond_1b",

这里我们以Benchmark的靶场作为测试的示例程序。

invoke-virtual {v6, v7},Ljava/lang/Runtime;->exec(Ljava/lang/String;)Ljava/lang/Process;

"",

invoke-virtual {v7, v8},Ljava/io/PrintStream;->println(Ljava/lang/String;)V

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V1",

.method public doGet(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V

}

} catch (java.sql.SQLException e){

"21: .line 48",

.annotation systemLdalvik/annotation/Throws;

Ljava/io/IOException;

return-void

这里我们将Ljavax/servlet/http/HttpServletRequest;->getHeaders(Ljava/lang/String;)Ljava/util/Enumeration;作为source点,

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V37",

response.getWriter.println(

"10: .prologue",

invoke-static {},Ljava/lang/Runtime;->getRuntimeLjava/lang/Runtime;

value = {

}

以下是Hades生成的指令控制流图

if-eq v7, v8, :cond_38

.method public doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V

"13: invoke-interface {p2, v7},Ljavax/servlet/http/HttpServletResponse;->setContentType(Ljava/lang/String;)V",

move-result-object v6

Runtime r = Runtime.getRuntime;

const-string v3, ""

1、dx.jar

if-eqz v6, :cond_1b

—————– ——————-

.line 57

}

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V44",

"31: check-cast v3,Ljava/lang/String;",

"56: .line 60",

invoke-direct {v6, v1},Ljavax/servlet/ServletException;->(Ljava/lang/Throwable;)V

.source"BenchmarkTest00017.java"

"51: if-eq v7, v8, :cond_38",

const-string v7, "os.name"

String sql = "INSERT INTOusers (username, password) VALUES ('foo','" param "')";

invoke-virtual {v6, v7},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V43",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V49",

"",

.line 69

"41: .local v0,\\cmd\\:Ljava/lang/String;",

2、zhengsidie@gmail.com

public class BenchmarkTest00017 extends HttpServlet {

"38: .line 56",

"37: move-result-object v3",

0×6 污点净化部分

.line 53

"47: invoke-virtual {v6, v7},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",

const-string v7, "UTF-8"

"Errorprocessing request."

.registers 11

benchmark靶场项目地址:https://github.com/OWASP/Benchmark

invoke-interface {v5, v4},Ljava/sql/Statement;->executeUpdate(Ljava/lang/String;)I

—————– ——————-

经过调研,发现java字节码可以通过安卓sdk中的dx.jar转为smali字节码。这样一来,实现java白盒的代价就小了很多。虽然现在并不是在做白盒相关的工作,但是还是希望凭借自己对语言的理解,以及编码能力,自己构造出一个像样的白盒审计系统,所以便有了Hades这个白盒审计系统的项目。而之所以开源这套系统,也是希望能够吸引更多对白盒引擎开发感兴趣的人才加入到这个项目当中,一起维护这个项目。

以下是Hades生成的smali字节码

invoke-static {v7},Lorg/owasp/benchmark/helpers/Utils;->getOSCommandString(Ljava/lang/String;)Ljava/lang/String;

"35: const-string v7,\\UTF-8\\",

"14: .line 45",

Hades构造的指令控制流图大致效果图:

"61: .local v6, \\r\\:Ljava/lang/Runtime;",

);

—————– ——————-

"63: new-instance v7, Ljava/lang/StringBuilder;",

"34: :cond_1b",

.line 42

本测试单元对应的是benchmark的靶场18

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V65",

.annotation systemLdalvik/annotation/Throws;

"3: .param p2, \\response\\ #Ljavax/servlet/http/HttpServletResponse;",

move-result-object v7

规则方面,由于都是字节码形式的规则,手动收集的话会比较麻烦,所以我额外开发了一个规则生成器,在utils包中,只要输入函数名及所属的jar包的路径,即可生成相应的函数调用形式。

invoke-virtual {v7},Ljava/lang/StringBuilder;->toStringLjava/lang/String;

"",

"52: .local v4,\\sql\\:Ljava/lang/String;",

"",

org.owasp.esapi.ESAPI.encoder.encodeForHTML(e.getMessage)

check-cast v5, Ljava/lang/String;

下面我分六个部分,详细说明下我的白盒引擎实现思路。

"54: invoke-static {},Lorg/owasp/benchmark/helpers/DatabaseHelper;->getSqlStatementLjava/sql/Statement;",

"linearCode": [

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V50",

"67: invoke-virtual {v7, v5},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",

source点

}

"9: .end annotation",

"40: invoke-direct {v6},Ljava/lang/StringBuilder;->V",

0×3 解释执行

"24: invoke-interface {v2},Ljava/util/Enumeration;->hasMoreElementsZ",

"54: invoke-static {v7},Lorg/owasp/benchmark/helpers/Utils;->getOSCommandString(Ljava/lang/String;)Ljava/lang/String;",

.local v5,"param":Ljava/lang/String;

:cond_38

sink点

java.util.Enumeration headers = request.getHeaders("BenchmarkTest00017");

"46: const-string v7,\\\\')\\",

.registers 1

目前该项目中只支持java源码的审计,后续计划支持php,c/c ,js等语言。如果你对白盒感兴趣,并具有一定的编程能力(最好全栈),可先加我为好友,我将把你拉近Hades白盒审计系统开源项目的交流群中。

invoke-direct {p0},Ljavax/servlet/http/HttpServlet;->V

.end method

—————– <– 栈起始位置

// some code

"29: move-result-object v3",

"sink":"Ljava/lang/Runtime;->exec(Ljava/lang/String;)Ljava/lang/Process;",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V45",

.superLjavax/servlet/http/HttpServlet;

"43: invoke-static {v7},Ljava/lang/System;->getProperty(Ljava/lang/String;)Ljava/lang/String;",

// some code

else throw newServletException(e);

.line 56

- -

"8: }",

"36: invoke-static {v3, v6},Ljava/net/URLDecoder;->decode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",

.methodpublicdoPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V

.fieldprivate static final serialVersionUID:J = 0x1L

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V29",

doPost(request, response);

v0 == local0

import javax.servlet.annotation.WebServlet;

const-string v7, "INSERT INTO users(username, password) VALUES (\'foo\',\'"

.param p1, "request" # Ljavax/servlet/http/HttpServletRequest;

if-eqz v2, :cond_1b

.line 65

.local v0, "count":I

——————- <– stack pointer

import javax.servlet.http.HttpServletRequest;

.line 30

Hades之初并不将净化函数加入到污点分析过程中,主要的考虑到没有一个第三方安全厂商能够百分百保证他们的安全sdk中的净化函数不存在被绕过的风险。但是实际攻击过程中,必要的防护代码确实能够防住绝大部分的攻击测试。后续,Hades方面将会逐步增加净化函数方面规则的支持。

const-string v6,"BenchmarkTest00018"

.catch Ljava/sql/SQLException;{:try_start_3a .. :try_end_45} :catch_46

// URL Decode the header valuesince req.getHeaders doesn't. Unlike req.getParameters.

.prologue

.line 59

"48: invoke-virtual {v3, v7},Ljava/lang/String;->indexOf(Ljava/lang/String;)I",

v0 == local0

"60: .line 65",

我们在通过对函数引用进行解释之后,获得了污点网络中的第一个污点信息,那么现在我们来思考下接下来污点是如何分析的呢?其实我们知道,污点的传播无非是几个方式,一种是通过赋值的指令进行传递,第二种是通过数组操作指令将污点传递给了数组成员,第三种是通过函数调用,将污点传递给了函数执行结束的返回值(这里是寄存器)。所以我们只需要注重对这些赋值操作进行解释执行即可实现对污点的跟踪分析。

"53: const-string v7,\\echo\\",

——————- <– frame pointer:for func1

.line 49

用于将dex文件反编译为smali。

sget-object v7,Ljava/lang/System;->out:Ljava/io/PrintStream;

:cond_1b

invoke-interface {v2},Ljava/util/Enumeration;->nextElementLjava/lang/Object;

} catch (IOException e) {

const-string v7, "Windows"

"",

Ljava/io/IOException;

"56: .line 62",

1、 QQ:747289639(微信同)

"18: const-string v6,\\BenchmarkTest00018\\",

move-result-object v9

if(org.owasp.benchmark.helpers.DatabaseHelper.hideSQLErrors) {

invoke-static {v5, v7},Ljava/net/URLDecoder;->decode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

check-cast v3, Ljava/lang/String;

"71: invoke-virtual {v6, v7},Ljava/lang/Runtime;->exec(Ljava/lang/String;)Ljava/lang/Process;",

"",

}

return-void

const-string v5, ""

好,下面我们使用Hades来对该程序进行检测。

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

.annotation systemLdalvik/annotation/Throws;

java.sql.Statementstatement = org.owasp.benchmark.helpers.DatabaseHelper.getSqlStatement;

importjavax.servlet.annotation.WebServlet;

"15: const-string v5, \\\\",

以下是我们需要配置的source点:

为什么程序没有真正执行起来,还需要有栈结构呢?这里,设计污点栈的目的是为了污点的保存。在函数间污点分析的过程中,我们在跟入分析一个被调用的函数的时候,也需要对当前函数的污点现场进行保存,以便在在分析完被调用函数回来之后,能够恢复到原来的污点现场继续向下进行污点分析,所以一个合理的栈结构的设计是十分必要的。下面是Hades污点栈结构示意图:

"4: .annotation systemLdalvik/annotation/Throws;",

"16: .line 46",

下面是Hades的检测报告:

.param p1, "request" # Ljavax/servlet/http/HttpServletRequest;

"8: }",

"20: move-result-object v2",

"52: .line 59",

.line 46

.prologue

3、 clean点配置。

}

.line 30

"64: invoke-direct {v7},Ljava/lang/StringBuilder;->V",

sink点

"",

Ljava/io/IOException;

"22: .local v2,\\headers\\:Ljava/util/Enumeration;,\\Ljava/util/Enumeration;\\",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V70",

.line 48

move-result-object v8

.line 53

.line 36

现在我们知道了污点追踪分析的方法,但是仅仅对指令进行解释执行是不够的,因为指令在解释执行的时候是需要操作寄存器和内存的,如果没有内存的介入,那么指令解释毫无意义,所以,我们还需要设计一个用于污点分析的内存结构。Hades的内存结构主要包括三个部分,分别是,污点寄存器,污点栈及污点堆部分。污点寄存器,包含属性如下:

#virtual methods

.local v3,"osName":Ljava/lang/String;

.end local v0 # "count":I

return-void

"",

"34: :cond_1b",

"source":"Ljavax/servlet/http/HttpServletRequest;->getHeaders(Ljava/lang/String;)Ljava/util/Enumeration;",

"5: value = {",

const-string v7,"BenchmarkTest00017"

.line 56

String osName = System.getProperty("os.name");

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V25",

@Override

}

以下是hades构造的指令控制流图(由于生成的控制流图过大,故我只截取了部分的控制流图)

# directmethods

invoke-static {v4, p2},Lorg/owasp/benchmark/helpers/DatabaseHelper;->outputUpdateComplete(Ljava/lang/String;Ljavax/servlet/http/HttpServletResponse;)V

因为本白盒程序主要基于smali文件进行分析的,所以这里我先简要介绍下smali字节码。而纯粹讲smali字节码,可能不太好理解。所以,我先讲下程序的编译过程。程序的编译一般需要经过六个过程,即: 词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成。下面简要说明下这六个过程的各自的工作。词法分析,主要是对输入的源码文件中的字符从左到右逐个进行分析,输出与源代码等价的token流。语法分析,主要是基于输入的token流,根据语言的语法规则,做一些上下文无关的语法检查,语法分析结束之后,生成AST语法树。语义分析,主要是将AST语法树作为输入,并基于AST语法树做一些上下文相关的类型检查。语义分析结束后,生成中间代码,而此时的中间代码,是一种易于转为目标代码的一种中间表示形式。代码优化,则是针对中间代码进行进一步的优化处理,合并其中的一些冗余代码,生成等价的新的中间表示形式,最后生成目标代码。这针对DVM而已,这里的最终生成的代码即smali字节码,smali字节码也是DVM的解释执行的底层汇编代码。

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V14",

.registers 3

package org.owasp.benchmark.testcode;

"2: .param p1, \\request\\ # Ljavax/servlet/http/HttpServletRequest;",

return-void

intcount = statement.executeUpdate( sql );

}

.annotationruntime Ljavax/servlet/annotation/WebServlet;

.classpublic Lorg/owasp/benchmark/testcode/BenchmarkTest00018;

以下是Hades用到的第三方工具:

来自androidSDK,用于将java字节码转为smali字节码。

"17: .local v5,\\param\\:Ljava/lang/String;",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V48",

八、项目地址

v1 == local1

.line 62

"62: :try_start_3c",

3、 clean点配置。

Ljavax/servlet/ServletException;,

"21: .line 48",

其中,default表示该指令目前Hades暂不解释,但在之后的版本中,可能会考虑对其进行解释。

:try_start_3a

代码:

move-result v7

move-result-object v5

move-exception v1

move-result-object v6

response.getWriter.println(

- -

v3 == in1

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V34",

"7: Ljava/io/IOException;",

"",

:cond_1b

.local v0,"cmd":Ljava/lang/String;

下面是污点传递的示意图

"51: .line 59",

.restart local v3 # "param":Ljava/lang/String;

.local v2,"headers":Ljava/util/Enumeration;,"Ljava/util/Enumeration;"

move-result-object v7

"49: invoke-virtual {v6},Ljava/lang/StringBuilder;->toStringLjava/lang/String;",

const/4 v8, -0x1

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V59",

importjavax.servlet.http.HttpServletResponse;

"",

1、 sink点规则配置。

.line 67

以下是Hades的分析报告:

Hades的规则配置部分以下几个部分:

"",

java.util.Enumerationheaders = request.getHeaders("BenchmarkTest00018");

"",

invoke-virtual {v3, v7}, Ljava/lang/String;->indexOf(Ljava/lang/String;)I

param =headers.nextElement; // just grab first element

"58: invoke-interface {v5, v4},Ljava/sql/Statement;->executeUpdate(Ljava/lang/String;)I",

"57: :cond_38",

原标题:Hades开源白盒审计系统V1.0.0

"",

后端使用django实现文件接收接口,并对文件类型和合法性进行校验,通过redis消息队列将任务消息发布到消息队列中,传递给后端引擎。

System.out.println("Problem executing cmdi - TestCase");

}

"22: .local v2,\\headers\\:Ljava/util/Enumeration;,\\Ljava/util/Enumeration;\\",

.annotationruntime Ljavax/servlet/annotation/WebServlet;

try {

0×4 内存模拟

.local v3,"param":Ljava/lang/String;

invoke-interface {v2},Ljava/util/Enumeration;->hasMoreElementsZ

为什么要开发这么一个白盒审计系统呢?其实,自己开发白盒审计系统的想法已经在我的脑海中存在很久了。一方面,之前大学毕业之后在白盒方面花了挺多时间的,不过由于各方面原因,一直没有特别突出的进展,之后又转做木马病毒方面的攻防研究,白盒的方面的研究搁置了很久,心里一直耿耿于怀。另一方面,最近在做一个安卓相关的项目,项目中自己实现了一套针对smali字节码的虚拟解释执行引擎,然后就突发一个想法,android也是使用java进行开发的,那么能不能直接复用这套虚拟解释执行引擎来对java源码进行分析呢?

/**

const-string v6,"text/html;charset=UTF-8"

.param p2, "response" # Ljavax/servlet/http/HttpServletResponse;

cmd = org.owasp.benchmark.helpers.Utils.getOSCommandString("echo");

invoke-virtual {p0, p1, p2},Lorg/owasp/benchmark/testcode/BenchmarkTest00017;->doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V

"45: .line 58",

return-void

"15: const-string v3, \\\\",

);

# staticfields

—————–

2、zhengsidie@gmail.com

.end annotation

goto :goto_54

}

构建好了指令控制流图之后,我们先别急着对代码进行解释执行分析,到这一步还无法进污点分析。我们可以先设想一下,如果我们每一条路线,每个指令都解释执行一遍,那肯定是十分耗时的,所以考虑到执行效率方面,Hades会先找到sink点和source点各自所处的代码块节点,通过图的查询算法,查询两个节点(代码块节点)之间的通路(这个通路可能不止一条),然后将这条通路中的所有代码块合并在一起,重新组成一段线性的代码块,最后对这段代码块记性解释执行。其他更高级的减少分析成本的方案暂时还没想出来,或许你们可以给我提些建议。

"53: :try_start_3a",

.line 48

—————–

"24: invoke-interface {v2},Ljava/util/Enumeration;->hasMoreElementsZ",

invoke-interface {v2},Ljava/util/Enumeration;->nextElementLjava/lang/Object;

.line 74

"39: new-instance v6, Ljava/lang/StringBuilder;",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V52",

param =java.net.URLDecoder.decode(param, "UTF-8");

/**

"3: .param p2, \\response\\ #Ljavax/servlet/http/HttpServletResponse;",

invoke-virtual {v6, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

# staticfields

}

new-instance v6, Ljava/lang/StringBuilder;

.prologue

"23: if-eqz v2, :cond_1b",

:try_end_54

报告展示页面

"",

2、 source点规则配置。

move-result-object v4

四、白盒引擎实现详细说明

move-result-object v5

move-result-object v6

org.owasp.benchmark.helpers.Utils.printOSCommandResults(p, response);

if-eqz v2, :cond_1b

import java.io.IOException;

在讲解释执行之前,我先简要说明下虚拟解释器的原理。虚拟解释器并不是真正的解释器,但是它应该具备解释器的主要功能,即对字节码指令进行解释,对堆栈进行存取处理。而如果我们希望在解释执行的时候能够收集到污点的一些传递情况,那么单纯的进行解释执行肯定是不够的,我们还需要在指令解释执行的基础上,增加污点的传播分析。那么,如何实现污点的传播分析呢?这是我们需要着重考虑的点。我们知道,传统的解释器,在解释执行的程序的时候,需要根据用户传入的参数,进行一些值的处理的。而与传统解释器不同的是,我们的虚拟执行引擎并不是真正的执行起来,也不需要让程序真正的执行起来,我们解释执行的目的不是为了对传入的值进行处理,而是分析传入的值的走势(即数据是怎么流动的),我们关注的是它怎么走的,这个点。所以,我们会把用户传入的参数认定为污点,而相应的获取用户输入参数的函数的返回值所存储的寄存器便会被我们标记为污点,这个函数是整个污点分析的起点,而这个污点寄存器则是这个污点网络主干路线上的第一个污点。

"Lorg/owasp/benchmark/testcode/BenchmarkTest00017;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V24",

""

* OWASP Benchmark v1.2

.line 70

const-string v7, "echo"

"44: move-result-object v3",

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

"2: .param p1, \\request\\ #Ljavax/servlet/http/HttpServletRequest;",

response.setContentType("text/html;charset=UTF-8");

new-instance v7, Ljava/lang/StringBuilder;

先展示下前端效果。(应该不是很违和)

"",

.end local v5 # "statement":Ljava/sql/Statement;

2、baksmali.jar

invoke-static {v3, v6},Ljava/net/URLDecoder;->decode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

move-result-object v3

"",

用于将dex文件反编译为smali。

"6:Ljavax/servlet/ServletException;,",

.line 36

*本文作者:默安科技安全研究院前沿研究部 郑斯碟,转载请注明来自FreeBuf.COM

展开全文

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V37",

public void doGet(HttpServletRequestrequest, HttpServletResponse response) throws ServletException, IOException {

一、引言

—————–

public void doPost(HttpServletRequestrequest, HttpServletResponse response) throws ServletException, IOException {

.methodpublicdoGet(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V

"sourceBelongTo":"org/owasp/benchmark/testcode/BenchmarkTest00018; doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V"

.end annotation

"linearCode": [

"47: const-string v7,\\Windows\\",

move-result-object v8

move-result-object v3

联系方式:

"5: value = {",

.line 69

.prologue

.line 42

"42: invoke-virtual {v6, v7},Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",

.line 69

"57: .local v5,\\statement\\:Ljava/sql/Statement;",

"Lorg/owasp/benchmark/testcode/BenchmarkTest00018;doPost(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V41",

污点追踪是整个系统最重要的环节,前面的设计也是为了该部分服务。Hades的污点传播方式分为栈帧型污点传播,和指令型污点传播。指令型污点传播,顾名思义,就是通过解释执行字节码指令来进行污点传播的,在上面我们也提过,那么什么是栈帧型污点传播呢?在进行一个新函数的污点分析之前,Hades会为新函数分配一个污点栈帧,在这个污点栈帧中会为新函数分配好指令解释执行过程中需要的所有虚拟寄存器,以满足指令解释执行的需要。(smali是一种基于寄存器操作的字节码,值的操作都是基于虚拟寄存器的,不像c及java等语言字节码,有大量值的存取操作)新栈帧中一部分寄存器来自函数的入参,入参来自上一个栈帧的输出区域。在上一个函数发生函数调用的时候,Hades会将函数的传入参数(包含寄存器中的污点信息,值信息)保存在上一个函数栈帧的输出区域中,在解释执行新函数的之前,旧栈帧会对新栈帧进行一个污点的传递,将污点信息传递到新函数的入参中,这样就完成了函数间的污点传递,即栈帧与栈帧之间的污点传播。

importjavax.servlet.http.HttpServlet;

这里简要的分析一下程序:

"/sqli-00/BenchmarkTest00018"

.endannotation

.local v2,"headers":Ljava/util/Enumeration;,"Ljava/util/Enumeration;"

后端程序通过request.getheader函数获取客户端网络请求的头部信息,并从中读取第一个element信息作为参数进入到命令执行函数中,这就构成了一个命令执行漏洞。

原标题:[公司]中国银河:母公司2月份净利润为6.94亿

原标题:【周运2.17-2.23】天象综述:水逆来也,双子处女重灾区,双鱼射手逃不掉