VTS漏洞检测框架浅析

一、框架

1. 测试用例管理

首先,VulnerabilityOrganizer类用于包含该所有的测试用例,使用VTS框架来进行漏洞检测时,所有的测试用例需要在这个类中注册。

    public static List<VulnerabilityTest> getTests(Context ctx){
        List<VulnerabilityTest> allTests = new ArrayList<>();
        allTests.add(new ZipBug9950697());
        allTests.add(new ZipBug8219321());
        allTests.add(new ZipBug9695860());
       // allTests.add(new JarBug13678484());
        allTests.add(new CVE_2013_6282());
        allTests.add(new CVE_2011_1149());
        allTests.add(new CVE_2014_3153());
        allTests.add(new CVE_2014_4943());
        //tests.add(new StumpRoot());
        allTests.add(new WeakSauce());
        allTests.add(new GraphicBufferTest());
        allTests.addAll(StageFright.getTests(ctx));
        allTests.add(new CVE_2015_6602());
        allTests.add(new OpenSSLTransientBug());
        allTests.add(new CVE_2015_3636());
        //tests.add(new ZergRush()); // Hide super old bugs?
        allTests.add(new SamsungCREDzip());
        allTests.add(new CVE_2015_6608());
        allTests.add(new CVE20151528());
        allTests.add(new CVE_2015_6616());
        allTests.add(new CVE20153860());
        allTests.add(new CVE_2016_0807());

        List<VulnerabilityTest> filteredTest = new ArrayList<>();
        String cpuArch1 = SystemUtils.propertyGet(ctx, "ro.product.cpu.abi");
        String cpuArch2 = SystemUtils.propertyGet(ctx, "ro.product.cpu.abi2");


        /*
            The logic here is:
              The test must support every architecture that the device lists
         */
        for(VulnerabilityTest vt : allTests){

            if(vt.getSupportedArchitectures() == null) {
                Log.d(TAG, "architectures is null for : " + vt.getCVEorID());
            }

            if(vt.getSupportedArchitectures().contains(CPUArch.ALL)){
                filteredTest.add(vt);
            } else {
                if(isArchitectureSupported(vt, cpuArch1) &&
                   isArchitectureSupported(vt, cpuArch2)){
                    filteredTest.add(vt);
                }
            }
        }
        return filteredTest;
    }

所有的测试用例采用接口式设计,即所有的测试用例都需要实现VulnerabilityTest接口,这样便于框架的管理。这个类其实只有几个接口方法,其中isVulnerable()方法用于确定漏洞是否存在。

public interface VulnerabilityTest extends Serializable {
   public String getCVEorID();
   public boolean isVulnerable(Context context) throws Exception;
   public List<CPUArch> getSupportedArchitectures();
}

2. 测试执行

测试用例的执行由VulnerabilityTestRunner管理,由于测试用例执行可能是个耗时过程,所以采用了异步任务的方式。

    @Override
    protected List<VulnerabilityTestResult> doInBackground(Void... params) {
        Log.d(TAG, "Async execute called!!!!");

        List<VulnerabilityTestResult> results = new ArrayList<>();

        for (int i = 0; i < tests.size(); i++) {
            mProgressDialog.setProgress(i);

            VulnerabilityTest test = tests.get(i);
            Log.d(TAG, "Running test: " + test.getCVEorID());
            Exception x = null;
            boolean isVuln = false;
            try {
                isVuln = test.isVulnerable(mCtx);
                Log.d(TAG, test.getCVEorID() + "  isVulnerable: " + isVuln);

            } catch (Exception e) {
                e.printStackTrace();
                Log.d(TAG, test.getCVEorID() + "  failed with: " + e.getMessage());
                x = e;
            }
            results.add(new VulnerabilityTestResult(test, isVuln, x));
        }

        return results;
    }

3. 测试结果管理

测试结果由VulnerabilityTestResult类管理,其实就是定义了一个数据结构。

public class VulnerabilityTestResult implements Serializable {

    private final VulnerabilityTest mTest;
    private final boolean mIsVuln;
    private final Exception mE;

    public VulnerabilityTestResult(VulnerabilityTest test, boolean isVuln, Exception e){
        mIsVuln = isVuln;
        mE = e;
        mTest = test;
    }

    public String getCVEorID(){
       return mTest.getCVEorID();
    }

    public Boolean getResult(){
        return mIsVuln;
    }

    public Exception getException(){
        return mE;
    }

    public boolean isVulnerable() { return mIsVuln; }
}

二、 测试用例检测原理

1. 纯静态检测

直接检测patch中的特征,例如字符串。比如CVE-2016-0807的检测。

此外,CVE-2015-3860也是静态检测。

//patch code

 if (nhdr.n_type == NT_GNU_BUILD_ID) {
           // Skip the name (which is the owner and should be "GNU").
           addr += NOTE_ALIGN(nhdr.n_namesz);
-          uint8_t build_id_data[128];
-          if (nhdr.n_namesz > sizeof(build_id_data)) {
-            ALOGE("Possible corrupted note, name size value is too large: %u",
-                  nhdr.n_namesz);
+          uint8_t build_id_data[160];
+          if (nhdr.n_descsz > sizeof(build_id_data)) {
+            ALOGE("Possible corrupted note, desc size value is too large: %u",
+                  nhdr.n_descsz);
             return false;
           }
           if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
//check code
    public boolean isVulnerable(Context context) throws Exception {

        File debuggerd = new File("/system/bin/debuggerd");
        if(!debuggerd.exists() || !debuggerd.isFile()){
            throw new Exception("debuggerd doesn't exist or is not a file");
        }

        String patchedString = "Possible corrupted note, desc size value is too large: %u";
        String unpatchedString = "Possible corrupted note, name size value is too large: %u";

        ByteArrayOutputStream debuggerdBAOS = new ByteArrayOutputStream((int)debuggerd.length());
        BinaryAssets.copy(new FileInputStream(debuggerd), debuggerdBAOS);
        byte[] debuggerdBin = debuggerdBAOS.toByteArray();

        KMPMatch binMatcher = new KMPMatch();

        int indexOf = binMatcher.indexOf(debuggerdBin, patchedString.getBytes());
        boolean hasPatchedString = indexOf == -1;

        indexOf = binMatcher.indexOf(debuggerdBin,  unpatchedString.getBytes());
        boolean hasUnpatchedString = indexOf == -1;


        return hasPatchedString && !hasUnpatchedString;
    }

2. 动态方式

通过触发漏洞等方式来检测漏洞存在,利用通过对so中的函数调用,根据崩溃或者返回值来确定漏洞是否存在。CVE-2015-1528就是这种检测方式。

int Check_CVE_2015_1528()
{
    const char *libname = "libcutils.so";
    size_t * ( *native_handle_create )( int numFds, int numInts ) = NULL;

    void *handle = dlopen( libname, RTLD_NOW | RTLD_GLOBAL );
    if( !handle )
    {
        printf( "error opening %s: %s\n", libname, dlerror() );
        return -1;
    }

    native_handle_create = dlsym( handle, "native_handle_create" );
    if( !native_handle_create )
    {
        printf( "missing native_handle_create\n" );
        return -2;
    }

    int ret = -3;

    int numFds = 1025;
    int numInts = 1;
    size_t *bla = native_handle_create( numFds, numInts );
    if( !bla )
    {
        // fixed
        printf( "looks fixed to me\n" );
        ret = 0;
        goto done;
    }

    // sanity checks
    switch(bla[0])// version
    {
    case 12://android wear 5.0.2 LWX49K
        if( bla[1] != numFds || bla[2] != numInts )
        {
            LOG_D( "got back unexpected values\n" );
        }
        else
        {
            LOG_D( "its vulnerable\n" );
            return 1;
        }
        break;
    default:
        LOG_D( "failed.  version %d  %d %d\n", bla[0], bla[1], bla[2] );
        break;
    }


done:
    // done with this
    dlclose( handle );

    // should be allocated with malloc
    //! if its already null, then free does nothing
    free( bla );

    return ret;
}

三、漏洞检测Testcase添加

  • 继承VulnerabilityTest接口,实现接口方法;
  • 向VulnerabilityOrganizer类注册。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值