首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何对整个API进行模糊测试,而不是使用文件输入?

如何对整个API进行模糊测试,而不是使用文件输入?
EN

Stack Overflow用户
提问于 2022-02-18 12:03:11
回答 2查看 418关注 0票数 1

我正在学习模糊测试C应用程序的方法。据我所知,在大多数情况下,当进行模糊测试时,都会有一个C函数来接收/读取文件。模糊器被赋予一个有效的示例文件,随机或使用覆盖启发法修改它,并使用这个新的输入执行函数。

但是现在我不想模糊一个接受文件输入的函数,而是想模糊一些共同构成API的函数。例如:

代码语言:javascript
复制
int setState(int state);
int run(void); // crashes when previous set state was == 123

这样做的目的是对整个API进行测试,并检测误用和调用函数的顺序是否错误(这里:调用setState(123)run())是否会导致某些地方崩溃。

怎么能做这种事呢?我正在寻找模糊测试框架(不一定是C)、一般概念和示例。

我尝试从LLVM中使用libFuzzer,并逐字节“消耗”它的模糊器数据。我读取一个字节来确定要调用哪个函数,然后在需要时读取一个参数,最后调用该函数。然后我重复一遍,直到不再留下模糊器输入数据。它看起来像这样:

代码语言:javascript
复制
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    while(/* not end of fuzzer data reached */)
        switch (fuzzerConsumeByte()) {
        case 0:
            setState(fuzzerConsumeInt());
            break;
        case 1:
            run();
            break;
        default:
            break;
        }
    }
    return 0;
}

我发现提到的这种令人毛骨悚然的风格的消息来源如下:

..。从公共API中随机选择函数,并以随机参数的随机顺序调用它们。码智能

这看起来不像是基于输入文件的模糊器的良好或有效使用。不过,模糊测试libFuzzer会在几秒钟后发现该错误。但我认为,如果我用多个其他功能扩展 API,可能会需要很长时间。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-02-24 20:29:32

来回答我自己的问题:

是的,这就是API模糊化的方法。为了按顺序使用数据,可以使用libFuzzer #include <fuzzer/FuzzedDataProvider.h> (C++)提供的函数。问题是:崩溃转储和模糊语料库将不会人类可读的。

对于更具可读性的模糊程序,为libFuzzer实现一个libFuzzer自定义数据变送器是有益的。

我使用预先制作的数据突变器脂原突变体 (C++)来模糊示例API。它根据协议缓冲区定义生成有效的输入数据,而不仅仅是(半)随机字节。不过,它确实会使毛刺变慢一点。给出的示例API中的bug在2分钟后就被发现了,与基本字节消耗设置下的30秒相比,发现了bug。但我确实认为,对于更大(真实的)API来说,它的扩展性要好得多。

票数 0
EN

Stack Overflow用户

发布于 2022-03-07 16:03:37

在用这种方式对有状态应用程序的API进行模糊化时,还要记住的另一件事是,您应该确保在每个模糊测试之间重置应用程序,或者使用AFL而不是libFuzzer来对将要测试的每个新输入进行分叉。否则,您发现的崩溃可能无法在崩溃转储中重现,因为崩溃取决于先前测试用例对目标应用程序所做的一些更改。

我还想提到,我们使用的是“.从您的公共API中随机选择函数,并以随机参数的随机顺序调用它们。”模糊方法也适用于更大的实际API(多达几百个函数),并且在合理的时间内实现了良好的代码覆盖率和结果。

您是对的,崩溃转储不是人类可读的,而是一些基于反馈的模糊工具,您不仅可以获得崩溃输入的转储,而且还可以获得其他信息,比如堆栈跟踪,可以帮助您分析根本原因。

编辑:

这里有一个使用这种模糊方法并使用FuzzedDataProvider的示例模糊测试:

代码语言:javascript
复制
#include <stdint.h>
#include <stddef.h>

#include "FuzzedDataProvider.h"

#include "GPS_module_1.h"
#include "crypto_module_1.h"
#include "crypto_module_2.h"
#include "key_management_module_1.h"
#include "time_module_1.h"

// Wrapper function for FuzzedDataProvider.h
// Writes |num_bytes| of input data to the given destination pointer. If there
// is not enough data left, writes all remaining bytes and fills the rest with zeros.
// Return value is the number of bytes written.
void ConsumeDataAndFillRestWithZeros(void *destination, size_t num_bytes, FuzzedDataProvider *fuzz_data) {
  if (destination != nullptr) {
    size_t num_bytes_with_fuzz_data = fuzz_data->ConsumeData(destination, num_bytes);
    if (num_bytes > num_bytes_with_fuzz_data) {
      size_t num_bytes_with_zeros = num_bytes - num_bytes_with_fuzz_data;
      std::memset((char*)destination+num_bytes_with_fuzz_data, 0, num_bytes_with_zeros);
    }
  }
}

extern "C" int FUZZ(const uint8_t *Data, size_t Size) {
    // Ensure a minimum data length
    if(Size < 100) return 0;

    // Setup FuzzedDataProvider
    FuzzedDataProvider fuzz_data_provider(Data, Size);
    FuzzedDataProvider *fuzz_data = &fuzz_data_provider;

    // Reset the state of the target software
    // to ensure that crashes are reproducible
    crypto::init();

    int number_of_functions = fuzz_data->ConsumeIntegralInRange<int>(1,100);
    for (int i=0; i<number_of_functions; i++) {
      int func_id = fuzz_data->ConsumeIntegralInRange<int>(0, 15);
      switch(func_id) {
         case 0: {
            // Create a struct and fill it with fuzz data
            GPS::position struct_0 = {0};
            ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
            GPS::get_current_position(&struct_0);
            break;
          }
         case 1: {
            GPS::get_destination_position();
            break;
          }
         case 2: {
            GPS::init_crypto_module();
            break;
          }
         case 3: {
            GPS::position struct_0 = {0};
            ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
            GPS::set_destination_position(struct_0);
            break;
          }
         case 4: {
            // Create a vector of "random" size
            // and fill it with fuzz data
            std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
            size_t fuzz_size_0 = fuzz_data_0.size();
            crypto::hmac struct_0 = {0};
            ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
            crypto::calculate_hmac(fuzz_data_0.data(), fuzz_size_0, &struct_0);
            break;
          }
         case 5: {
            crypto::get_state();
            break;
          }
         case 6: {
            crypto::init();
            break;
          }
         case 7: {
            crypto::key struct_0 = {0};
            ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
            crypto::set_key(struct_0);
            break;
          }
         case 8: {
            crypto::nonce struct_0 = {0};
            ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
            crypto::set_nonce(struct_0);
            break;
          }
         case 9: {
            std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
            size_t fuzz_size_0 = fuzz_data_0.size();
            crypto::hmac struct_0 = {0};
            ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
            crypto::verify_hmac(fuzz_data_0.data(), fuzz_size_0, &struct_0);
            break;
          }
         case 10: {
            crypto::key struct_0 = {0};
            ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
            crypto::verify_key(struct_0);
            break;
          }
         case 11: {
            crypto::nonce struct_0 = {0};
            ConsumeDataAndFillRestWithZeros(&struct_0, sizeof(struct_0), fuzz_data);
            crypto::verify_nonce(&struct_0);
            break;
          }
         case 12: {
            std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
            size_t fuzz_size_0 = fuzz_data_0.size();
            key_management::create_key(fuzz_data_0.data(), fuzz_size_0);
            break;
          }
         case 13: {
            std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
            size_t fuzz_size_0 = fuzz_data_0.size();
            key_management::create_nonce(fuzz_data_0.data(), fuzz_size_0);
            break;
          }
         case 14: {
            std::vector<uint8_t> fuzz_data_0 = fuzz_data->ConsumeBytes<uint8_t>(fuzz_data->ConsumeIntegral<uint8_t>());
            size_t fuzz_size_0 = fuzz_data_0.size();
            key_management::generate_random_bytes(fuzz_data_0.data(), fuzz_size_0);
            break;
          }
         case 15: {
            time_management::current_time();
            break;
          }
      }
    }

    return 0;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71173169

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档