一、前言
二、技术背景:
1、Compiler Instrumentation:如果存在源码的情况下,这种方式做代码覆盖率统计和fuzz是最可靠且高效的,例如利用LLVM、GCC等,但可惜很多情况下拿不到源码。
https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html
https://llvm.org/docs/CommandGuide/llvm-cov.html
2、Execute Simulation:例如基于QEMU、Unicorn模式的AFL就会在QEMU准备翻译执行基本块之前插入覆盖率统计的代码,缺点是执行效率有点低。
https://github.com/edgarigl/qemu-etrace
https://andreafioraldi.github.io/articles/2019/07/20/aflpp-qemu-compcov.html
3、Runtime Trace:这种形式的代码路径覆盖比较灵活,实现形式也比较多样化,也是本文所用到的主要方法。较为常见的方式例如使用frida动态插桩、调试启动、或者直接改造手机ROM等方式都可以实现,缺点是由于架构复杂稳定性很难保证,很多时候还没等target crash,fuzzer自己先crash了。文章后面会详细一些的介绍用到的frida stalker工具。
https://frida.re/docs/stalker/
4、Binary Rewrite:这种方法主要是针对二进制disassemble的每个基本语句块进行插桩,如果做的比较理想,效果可能仅次于Compiler Instrumentation,但缺点是它太难了,如果仅仅是个比较简单的binary问题倒还不大,但如果是个复杂度很高的系统实现起来就会困难重重,例如有些binary自带VM的情况、binary存在runtime rewrite自身的机制、binary使用了一些CPU的特殊Architecture Feature、各种binary重定位问题等等,这些处理不好都会让rewriten binary无法顺利执行。
https://github.com/GJDuck/e9patch
https://github.com/utds3lab/multiverse
https://github.com/talos-vulndev/afl-dyninst
5、Hardware Trace:这可能成为未来binary fuzzing的主流方向,硬件对软件天然就处在上帝视角中,这里要明确一下我所说的Hardware Trace这个范畴,并不是真的需要搞个专用于fuzzing的硬件,这里主要是说利用硬件与操作系统之间的那一层的能力去完成fuzz的目的,这对fuzz操作系统自身尤其有效,例如利用hypervisor、硬件调试器等的能力,当然也不排除有一天会有人搞出个FPGA甚至ASIC来跑fuzz,哈哈,那简直太硬核了!
https://github.com/gamozolabs/applepie
https://the-elves.github.io/Ajinkya-GSoC19-Submission/
三、目标选择
关于如何选择fuzzing目标这点,主要从安卓so库的安全风险角度分享一下我的经验和思路:
1、有攻击面:这是最先要确认的一个点,虽然理论上说任何程序都有攻击面,但是有大有小,有多有少,我们倾向于选择攻击面大的目标,这样才更有价值,例如有些so库可能会直接接收用户的外部数据进行处理,例如视频播放器、图片解析引擎、js解析引擎等等,这些so库如果出现漏洞,大概率上比较容易直接利用。
2、高频应用:更高频被用到的so库也是值得重点考量的,越是高频被用到,就越易于攻击利用,风险也就越大,例如有些工具util性质的so库,可能会被好多其他库调用,出问题的概率很大。
3、复杂度高:理论上说,漏洞的产生的概率与系统复杂度成正比,越是复杂就越是容易出问题。
4、消减措施缺失:某些so库在编译过程中可能没有考虑到安全性,没有开启安全编译选项,这就会导致其上的漏洞很容易被利用,风险也很大。
四、样本生成
C++ |
AVFrame *pFrame;
//...
pFrame->best_effort_timestamp = gen_fuzz_integer();
pFrame->channel_layout = gen_fuzz_integer();
pFrame->best_effort_timestamp = gen_fuzz_integer();
pFrame->coded_picture_number = gen_fuzz_integer();
pFrame->decode_error_flags = gen_fuzz_integer();
pFrame->flags = gen_fuzz_integer();
pFrame->height =gen_fuzz_integer();
pFrame->width = gen_fuzz_integer();
pFrame->nb_extended_buf = gen_fuzz_integer();
pFrame->key_frame = gen_fuzz_integer();
pFrame->nb_samples = gen_fuzz_integer();
pFrame->palette_has_changed = gen_fuzz_integer();
if(i%9 == 0) {pFrame->pkt_duration = gen_fuzz_integer();}
pFrame->pkt_pos = gen_fuzz_integer();
if(i%99 == 0){pFrame->pts = gen_fuzz_integer();}
//...参数过多不一一列举
视频参数也进行些随机多样化的设置:
C++ |
AVCodecContext *pCodecCtx;
//...
pCodecCtx = video_st->codec;
pCodecCtx->codec_id = AV_CODEC_ID_HEVC;//H265
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
pCodecCtx->width = abs(gen_fuzz_integer())%32768;
pCodecCtx->height = abs(gen_fuzz_integer())%32768;
pCodecCtx->bit_rate_tolerance = abs(gen_fuzz_integer());
pCodecCtx->bit_rate = 1000 + abs(gen_fuzz_integer())%200000;
pCodecCtx->gop_size = abs(gen_fuzz_integer())%10000;
pCodecCtx->time_base.num = 1+abs(gen_fuzz_integer())%10;
pCodecCtx->time_base.den = 25+abs(gen_fuzz_integer())%10000;
pCodecCtx->qmin = abs(gen_fuzz_integer())%10;
pCodecCtx->qmax = abs(gen_fuzz_integer())%1000;
pCodecCtx->max_qdiff = gen_fuzz_integer()%128;
pCodecCtx->qcompress = gen_fuzz_float();
pCodecCtx->me_range = gen_fuzz_integer()%128;
pCodecCtx->active_thread_type = gen_fuzz_integer()%X265_MAX_FRAME_THREADS;
pCodecCtx->sample_rate = abs(gen_fuzz_integer())%400000;
//...参数过多不一一列举
还可以选择性的在视频流封装前搞点事情:
C++ |
unsigned char *mutated_data = (unsigned char *) malloc_and_fillfuzzdata(mutated_size);
memcpy((unsigned char *) pkt.data + mutated_offset, mutated_data, mutated_size);
这样生成出来的视频文件其实已经经过一定程度的变异,甚至可以直接fuzz出一些crash了。
五、覆盖率引导
JavaScript |
// Always trust code.
Stalker.trustThreshold = 0;
var stalker_events = []
// attach to target and initial stalker
settarget: function(target) {
debug("Target: " + target)
target_function = ptr(target)
Interceptor.attach(target_function, {
onEnter: function (args) {
stalker_attached = true
stalker_finished = false
Stalker.queueCapacity = 100000000
Stalker.queueDrainInterval = 1000*1000
debug("follow @ " + target);
Stalker.follow(Process.getCurrentThreadId(), {
events: {
call: false,
ret: false,
exec: false,
block: false,
compile: true
},
onReceive: function (events) {
stalker_events.push(events)
debug("onReceive: len(stalker_events)=" + stalker_events.length)
}
});
},
onLeave: function (retval) {
debug("unfollow @ " + target);
Stalker.unfollow(Process.getCurrentThreadId())
Stalker.flush();
if(gc_cnt % 100 == 0){
Stalker.garbageCollect();
}
gc_cnt++;
stalker_finished = true
}
});
},
// get the coverage
getcoverage: function(args) {
debug("getcoverage: len(stalker_events)=" + stalker_events.length)
if(stalker_events.length == 0)
return undefined;
var accumulated_events = []
for(var i = 0; i < stalker_events.length; i++) {
var parsed = Stalker.parse(stalker_events[i], {stringify: false, annotate: false})
accumulated_events = accumulated_events.concat(parsed);
}
return accumulated_events;
},
// log coverage
logcoverage: function() {
debug("log coverage");
var cov = rpc.exports.getcoverage();
debug("getcoverage done");
rpc.exports.clearcoverage();
if(cov != undefined) {
debug("covlen: " + cov.length);
Memory.writeU32(prev_hit, 0);
var hit_count = 0;
for (var i = 0; i < cov.length; i += 1) {
var start = cov[i][0];
var end = cov[i][1];
if((start >= trace_modules[0]["base"] && end <=trace_modules[0]["end"]) ||
(start >= trace_modules[1]["base"] && end <=trace_modules[1]["end"]) ||
(start >= trace_modules[2]["base"] && end <=trace_modules[2]["end"]) ) {
BITMAP.cov_log(BITMAP.trace_bits, ptr(prev_hit), ptr(start));
}
}
debug("log coverage done.")
return
} else {
return "no coverage"
}
},
//check coverage
checkcoverage: function() {
debug("check coverage once");
var cnt = BITMAP.calc_bitmap_count2(BITMAP.trace_bits);
BITMAP.map_rate = cnt * 100 / CONFIG.MAP_SIZE;
return BITMAP.map_rate;
},
六、结束语
fuzzing技术作为漏洞挖掘的经典手段,一直受到安全从业人员的喜爱,无恒实验室也一直致力于使用fuzzing技术发现产品的安全缺陷,提升产品质量。在这个过程中我们发现了大量安全性及稳定性问题,但是路漫漫其修远兮,未来无恒实验室会继续在fuzzing的智能化、高效化、精准化等方向持续投入研究,并且向业界贡献成果。
无恒实验室更多详情请点击阅读原文
本文始发于微信公众号(字节跳动安全中心):安卓Native层共享库fuzzing技术思路及实践
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论