VTS脆弱性検出フレームワークの概要


一、フレーム
1.テストケース管理
まず、VulnerabilityOrganizerクラスはこのすべてのテスト例を含むために使用され、VTSフレームワークを使用して脆弱性検出を行う場合、すべてのテスト例はこのクラスに登録する必要があります.
    public static List getTests(Context ctx){
        List 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 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 getSupportedArchitectures();
}
2.テスト実行
テストケースの実行はVulnerabilityTestRunnerによって管理され、テストケースの実行は時間のかかるプロセスである可能性があるため、非同期タスク方式が採用されている.
    @Override
    protected List doInBackground(Void... params) {
        Log.d(TAG, "Async execute called!!!!");

        List 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
"
, libname, dlerror() ); return -1; } native_handle_create = dlsym( handle, "native_handle_create" ); if( !native_handle_create ) { printf( "missing native_handle_create
"
); 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
"
); 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
"
); } else { LOG_D( "its vulnerable
"
); return 1; } break; default: LOG_D( "failed. version %d %d %d
"
, 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クラスに登録します.