├── 1_vulnerability_analysis_whitepapers ├── README.md ├── cve-2020-15999 │ ├── CVE-2020-15999_data │ │ ├── Untitled.png │ │ └── asanlog.txt │ ├── README.md │ ├── bitmap_chunk에_할당하는_과정.md │ ├── libpng_내에서_Heap_Overflow가_발생하는_위치.md │ └── png_ptr,info_ptr값_저장_과정.md ├── cve-2020-16002 │ ├── README.md │ └── img │ │ ├── Untitled 1.png │ │ ├── Untitled.png │ │ ├── poc.drawio_(1).png │ │ ├── poc_asan.txt │ │ └── uaf0.pdf ├── cve-2020-6449 │ ├── README.md │ └── img │ │ ├── Untitled 1.png │ │ ├── Untitled 2.png │ │ ├── Untitled 3.png │ │ ├── Untitled 4.png │ │ ├── Untitled 5.png │ │ └── Untitled.png └── cve-2021-30519 │ ├── CVE-2021-30519 b0d2dd219c0842beab126cdbab162ac2 │ ├── CVE2.drawio.png │ ├── InkedCVE1.jpg │ ├── codeaudit.png │ └── cve3.drawio.png │ └── README.md ├── 2_fuzzer_development ├── Domino │ ├── Domino_img │ │ ├── Untitled 1.png │ │ ├── Untitled 2.png │ │ ├── Untitled 3.png │ │ └── Untitled.png │ └── README.md ├── Glitch │ ├── Glitch-TTF와 멀티미디어 파일에 관하여.md │ ├── Glitch_img │ │ ├── Glitch-TTF-Image │ │ │ ├── Untitled 1.png │ │ │ ├── Untitled 2.png │ │ │ ├── Untitled 3.png │ │ │ ├── Untitled 4.png │ │ │ ├── Untitled 5.png │ │ │ ├── Untitled 6.png │ │ │ ├── Untitled 7.png │ │ │ └── Untitled.png │ │ ├── Structure.png │ │ ├── Untitled.png │ │ ├── glitch_structure.drawio.png │ │ └── unknown.png │ └── README.md ├── Pleaser │ ├── Pleaser_img │ │ ├── Untitled 1.png │ │ ├── Untitled 2.png │ │ └── Untitled.png │ ├── README.md │ └── WebIDL로_파이어폭스_퍼징하기.md ├── README.md └── watTF │ ├── README.md │ └── img │ ├── Untitled 1.png │ ├── Untitled 2.png │ ├── Untitled 3.png │ ├── Untitled 4.png │ ├── Untitled 5.png │ └── Untitled.png ├── 3_codeql ├── Designing_CodeQL_queries.md ├── Overview.md ├── README.md ├── img │ ├── QLben_complex.drawio.svg │ └── 간단한큐엘벤다이어그램.svg └── related_work │ ├── Blackhat_usa_2021_chrome.md │ ├── Review_of_chromium_IPC_vulnerabilities.md │ ├── Variant_analysis_of_WebAudio_callback_vulnerabilities_in_chrome_studylog.md │ └── img │ ├── blackhat1.png │ ├── blackhat2.png │ ├── ipc1.png │ ├── ipc2.png │ └── webaudio1.png ├── 4_conclusion └── README.md └── README.md /1_vulnerability_analysis_whitepapers/README.md: -------------------------------------------------------------------------------- 1 | # 1_vulnerability_analysis_whitepapers 2 | 3 | ## Intro 4 | 5 | 6 | 퍼징을 위한 어택 벡터 선정을 위해 크롬 원데이를 재현하고 루트커즈 분석을 진행했다. 우리가 분석[^id1] 혹은 이미 분석이 되어 있는 글을 번역한 보고서를 공유한다. 번역과 함께 원데이를 재현한 후 직접 루트커즈 분석을 했음을 미리 알린다. 7 | 8 | 또한 개인 사정에 따라 공개하지 않는 보고서가 있음에 미리 양해를 구한다. 9 | 10 | [^id1]: 인터넷에 분석 글이 공개되어 있지 않은 경우, 소스코드 오디팅을 통해 직접 분석을 진행함. 11 | 12 | 13 | ## Contents 14 | 15 | - CVE-2018-6036[^id2] 16 | - [crbug](https://bugs.chromium.org/p/chromium/issues/detail?id=789952) 17 | - wasm의 custom 섹션을 해석하는 `DecodeCustomSections`함수에서 payload의 길이 검증을 하지 않아 `integer underflow` 취약점이 발생하여 `Out-Of-Bound Read`가 발생할 수 있음 18 | 19 | [^id2]: 손현지가 분석 진행 20 | 21 | - [CVE-2018-6092](./../2_fuzzer_development/watTF/README.md#11-배경이-된-취약점-cve-2018-6092)[^id2] 22 | - [crbug](https://bugs.chromium.org/p/chromium/issues/detail?id=819869&q=819869&can=2) 23 | - 32bit 플랫폼에서 `integer overflow`를 이용하여 로컬 함수의 수를 검증하는 루틴을 건너뛸 수 있기 때문에 많은 수의 로컬함수가 할당(insert)되어 `memory corruption`이 발생할 수 있음 24 | 25 | - [CVE-2020-6449](./cve-2020-6449/README.md)[^id3] 26 | - [crbug](https://bugs.chromium.org/p/chromium/issues/detail?id=1059686) 27 | - `BreakConnections`함수가 호출되고 frame을 닫아(`BaseAudioContext`를 파괴하여) `ClearHandlersToBeDeleted` 함수가 호출되면, `active_source_handlers_`가 지워지게 되는데, 이때 이것과 연관되어 있는 `finished_source_handlers_`는 동시에 지워지지 않고 `BreakConnections` 함수에서 사용되어 `UAF`가 발생할 수 있음 28 | - [원글1](https://securitylab.github.com/advisories/GHSL-2020-040-chrome/)과 [원글2](https://securitylab.github.com/research/CVE-2020-6449-exploit-chrome-uaf/)[^secret] 번역 진행 29 | 30 | [^id3]: 손현지가 번역 진행 31 | [^secret]: 번역한 내용 비공개 32 | 33 | - [CVE-2020-6551](./../3_codeql/Designing_CodeQL_queries.md#cve-2020-6551)[^id2] 34 | - [crbug](https://bugs.chromium.org/p/chromium/issues/detail?id=1107815&q=CVE-2020-6551&can=1) 35 | - `XRSystem::FocusedFrameChanged`함수의 반복문 안에서 이터레이터를 얻어와 처리하는 도중, JS 콜백이 호출되어 맵이 재할당되기 때문에 댕글링 포인터가 된 이터레이터를 사용하면 `UAF`가 발생할 수 있음 36 | 37 | - CVE-2020-6576[^id4] 38 | - [crbug](https://bugs.chromium.org/p/chromium/issues/detail?id=1111737) 39 | - 텍스트 렌더러 기능을 하는 `DrawTextInternal` 함수에서 free된 PaintCanvas 포인터가 재사용하는 것으로 인해 `UAF` 취약점이 발생할 수 있음 40 | 41 | [^id4]: 이주협이 분석 진행 42 | 43 | - [CVE-2020-15999](./cve-2020-15999/)[^id5] 44 | - [crbug](https://bugs.chromium.org/p/chromium/issues/detail?id=1139963&q=CVE-2020-15999&can=1) 45 | - FreeType에서 4바이트의 PNG width, height 값을 2바이트로 저장하기 때문에 더 적은 크기의 비트맵 chunk가 할당되고, 그 곳에 비트맵 데이터를 입력받으면 `heap overflow` 취약점이 발생할 수 있음 46 | 47 | [^id5]: 홍택균이 분석 진행 48 | 49 | - [CVE-2020-16002](./cve-2021-16002)[^id6] 50 | - [crbug](https://bugs.chromium.org/p/chromium/issues/detail?id=1137630&q=label%3Asecurity%20component%3AInternals%3EPlugins%3EPDF&can=1&colspec=ID%20Pri%20Type%20Component%20Status%20Summary%20Owner%20Target%20M%20Reporter%20Modified%20Opened%20CVE%20Reward) 51 | - CPWL_ListBox 에서 한 member의 destructor logic에서 다른 member에 대한 참조가 필요한데, destructor 호출 전에 해당 member가 delete되어 `UAF`취약점이 발생할 수 있음 52 | 53 | [^id6]: 심준용이 분석 진행 54 | 55 | - [CVE-2021-30519](./cve-2021-30519)[^id7] 56 | - [crbug](https://bugs.chromium.org/p/chromium/issues/detail?id=1194058&q=label%3ACVE-2021-30519&can=1) 57 | - Payments API에서 관련 객체가 삭제되었음에도 불구하고 삭제되지 않은 `WebContentsModalDialogManagerDelegate`에 엑세스함으로써 `UAF` 취약점이 발생할 수 있음 58 | 59 | [^id7]: 이윤희가 분석 진행 60 | 61 | -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-15999/CVE-2020-15999_data/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-15999/CVE-2020-15999_data/Untitled.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-15999/CVE-2020-15999_data/asanlog.txt: -------------------------------------------------------------------------------- 1 | [169132:169132:1001/031750.164466:INFO:content_main_runner_impl.cc(973)] Chrome is running in full browser mode. 2 | [169132:169132:1001/031750.974658:WARNING:account_consistency_mode_manager.cc(63)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured. 3 | [169172:169190:1001/031753.250480:WARNING:http_cache_transaction.cc(1179)] Unable to open or create cache entry 4 | ================================================================= 5 | ==169196==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61d000060c60 at pc 0x7f939927488d bp 0x7f932b3d5f40 sp 0x7f932b3d5f38 6 | WRITE of size 4 at 0x61d000060c60 thread T8 (CompositorTileW) 7 | #0 0x7f939927488c in cr_png_combine_row ./../../third_party/libpng/pngrutil.c:3580:36 8 | #1 0x7f939922aa6c in cr_png_read_row ./../../third_party/libpng/pngread.c:599:10 9 | #2 0x7f939922b1de in cr_png_read_image ./../../third_party/libpng/pngread.c:753:10 10 | #3 0x7f9398bb8aec in Load_SBit_Png ./../../third_party/freetype/src/src/sfnt/pngshim.c:439:5 11 | #4 0x7f9398bb1e54 in tt_face_load_sbix_image ./../../third_party/freetype/src/src/sfnt/ttsbit.c:1546:15 12 | #5 0x7f9398b7f8b8 in tt_face_load_sbit_image ./../../third_party/freetype/src/src/sfnt/ttsbit.c:1628:15 13 | #6 0x7f9398c1c41a in load_sbit_image ./../../third_party/freetype/src/src/truetype/ttgload.c:2429:13 14 | #7 0x7f9398c1a5ad in TT_Load_Glyph ./../../third_party/freetype/src/src/truetype/ttgload.c:2829:15 15 | #8 0x7f9398bd9cad in tt_glyph_load ./../../third_party/freetype/src/src/truetype/ttdriver.c:474:13 16 | #9 0x7f9398a3510e in FT_Load_Glyph ./../../third_party/freetype/src/src/base/ftobjs.c:948:15 17 | #10 0x7f93e83391f8 in SkScalerContext_FreeType::generateImage(SkGlyph const&) ./../../third_party/skia/src/ports/SkFontHost_FreeType.cpp:1365:20 18 | #11 0x7f93e9ae5d62 in SkScalerContext::getImage(SkGlyph const&) ./../../third_party/skia/src/core/SkScalerContext.cpp:572:9 19 | #12 0x7f93e957630c in SkGlyph::setImage(SkArenaAlloc*, SkScalerContext*) ./../../third_party/skia/src/core/SkGlyph.cpp:89:24 20 | #13 0x7f93e9ad2e2c in SkScalerCache::prepareImage(SkGlyph*) ./../../third_party/skia/src/core/SkScalerCache.cpp:109:16 21 | #14 0x7f93e9ad7f3e in SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer*)::$_1::operator()(unsigned long, SkGlyphDigest, SkPoint) const ./../../third_party/skia/src/core/SkScalerCache.cpp:185:45 22 | #15 0x7f93e9ad4e33 in unsigned long SkScalerCache::commonFilterLoop(SkDrawableGlyphBuffer*, SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer*)::$_1&&) ./../../third_party/skia/src/core/SkScalerCache.cpp:171:17 23 | #16 0x7f93e9ad4615 in SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer*) ./../../third_party/skia/src/core/SkScalerCache.cpp:181:26 24 | #17 0x7f93e9595cad in SkStrikeCache::Strike::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer*) ./../../third_party/skia/src/core/SkStrikeCache.h:103:44 25 | #18 0x7f93e959185d in SkGlyphRunListPainter::drawForBitmapDevice(SkGlyphRunList const&, SkMatrix const&, SkGlyphRunListPainter::BitmapDevicePainter const*) ./../../third_party/skia/src/core/SkGlyphRunPainter.cpp:130:21 26 | #19 0x7f93e94efbc7 in SkDraw::drawGlyphRunList(SkGlyphRunList const&, SkGlyphRunListPainter*) const ./../../third_party/skia/src/core/SkDraw_text.cpp:135:19 27 | #20 0x7f93e9387557 in SkBitmapDevice::drawGlyphRunList(SkGlyphRunList const&) ./../../third_party/skia/src/core/SkBitmapDevice.cpp:557:5 28 | #21 0x7f93e9588d8a in SkGlyphRunBuilder::drawTextBlob(SkPaint const&, SkTextBlob const&, SkPoint, SkBaseDevice*) ./../../third_party/skia/src/core/SkGlyphRun.cpp:202:17 29 | #22 0x7f93e9426773 in SkCanvas::onDrawTextBlob(SkTextBlob const*, float, float, SkPaint const&) ./../../third_party/skia/src/core/SkCanvas.cpp:2629:34 30 | #23 0x7f93e94273df in SkCanvas::drawTextBlob(SkTextBlob const*, float, float, SkPaint const&) ./../../third_party/skia/src/core/SkCanvas.cpp:2663:11 31 | #24 0x7f93e60ff11f in cc::DrawTextBlobOp::RasterWithFlags(cc::DrawTextBlobOp const*, cc::PaintFlags const*, SkCanvas*, cc::PlaybackParams const&)::$_111::operator()(SkCanvas*, SkPaint const&) const ./../../cc/paint/paint_op_buffer.cc:1626:8 32 | #25 0x7f93e60e1852 in void cc::PaintFlags::DrawToSk(SkCanvas*, cc::DrawTextBlobOp::RasterWithFlags(cc::DrawTextBlobOp const*, cc::PaintFlags const*, SkCanvas*, cc::PlaybackParams const&)::$_111) const ./../../cc/paint/paint_flags.h:168:7 33 | #26 0x7f93e60e1502 in cc::DrawTextBlobOp::RasterWithFlags(cc::DrawTextBlobOp const*, cc::PaintFlags const*, SkCanvas*, cc::PlaybackParams const&) ./../../cc/paint/paint_op_buffer.cc:1625:10 34 | #27 0x7f93e6112c65 in cc::Rasterizer::RasterWithFlags(cc::DrawTextBlobOp const*, cc::PaintFlags const*, SkCanvas*, cc::PlaybackParams const&) ./../../cc/paint/paint_op_buffer.cc:134:5 35 | #28 0x7f93e60f6cc3 in cc::$_47::operator()(cc::PaintOp const*, cc::PaintFlags const*, SkCanvas*, cc::PlaybackParams const&) const ./../../cc/paint/paint_op_buffer.cc:169:51 36 | #29 0x7f93e60f6c7c in cc::$_47::__invoke(cc::PaintOp const*, cc::PaintFlags const*, SkCanvas*, cc::PlaybackParams const&) ./../../cc/paint/paint_op_buffer.cc:169:51 37 | #30 0x7f93e60ee330 in cc::PaintOpWithFlags::RasterWithFlags(SkCanvas*, cc::PaintFlags const*, cc::PlaybackParams const&) const ./../../cc/paint/paint_op_buffer.cc:2399:3 38 | #31 0x7f93e60f2266 in cc::PaintOpBuffer::Playback(SkCanvas*, cc::PlaybackParams const&, std::__Cr::vector > const*) const ./../../cc/paint/paint_op_buffer.cc:2800:19 39 | #32 0x7f93e60de268 in cc::PaintOpBuffer::Playback(SkCanvas*, cc::PlaybackParams const&) const ./../../cc/paint/paint_op_buffer.cc:2660:3 40 | #33 0x7f93e60e07bd in cc::DrawRecordOp::Raster(cc::DrawRecordOp const*, SkCanvas*, cc::PlaybackParams const&) ./../../cc/paint/paint_op_buffer.cc:1592:15 41 | #34 0x7f93e610df45 in cc::Rasterizer::Raster(cc::DrawRecordOp const*, SkCanvas*, cc::PlaybackParams const&) ./../../cc/paint/paint_op_buffer.cc:121:5 42 | #35 0x7f93e60f5d1b in cc::$_14::operator()(cc::PaintOp const*, SkCanvas*, cc::PlaybackParams const&) const ./../../cc/paint/paint_op_buffer.cc:155:64 43 | #36 0x7f93e60f5ce4 in cc::$_14::__invoke(cc::PaintOp const*, SkCanvas*, cc::PlaybackParams const&) ./../../cc/paint/paint_op_buffer.cc:155:64 44 | #37 0x7f93e60eaf91 in cc::PaintOp::Raster(SkCanvas*, cc::PlaybackParams const&) const ./../../cc/paint/paint_op_buffer.cc:2168:3 45 | #38 0x7f93e60f239e in cc::PaintOpBuffer::Playback(SkCanvas*, cc::PlaybackParams const&, std::__Cr::vector > const*) const ./../../cc/paint/paint_op_buffer.cc:2803:11 46 | #39 0x7f93e600564d in cc::DisplayItemList::Raster(SkCanvas*, cc::ImageProvider*) const ./../../cc/paint/display_item_list.cc:100:20 47 | #40 0x7f93c3661599 in cc::RasterSource::PlaybackDisplayListToCanvas(SkCanvas*, cc::ImageProvider*) const ./../../cc/raster/raster_source.cc:127:20 48 | #41 0x7f93c36611da in cc::RasterSource::PlaybackToCanvas(SkCanvas*, gfx::Size const&, gfx::Rect const&, gfx::Rect const&, gfx::AxisTransform2d const&, cc::RasterSource::PlaybackSettings const&) const ./../../cc/raster/raster_source.cc:116:3 49 | #42 0x7f93c365d6a2 in cc::RasterBufferProvider::PlaybackToMemory(void*, viz::ResourceFormat, gfx::Size const&, unsigned long, cc::RasterSource const*, gfx::Rect const&, gfx::Rect const&, gfx::AxisTransform2d const&, gfx::ColorSpace const&, bool, cc::RasterSource::PlaybackSettings const&) ./../../cc/raster/raster_buffer_provider.cc:107:22 50 | #43 0x7f93c364f5d6 in cc::OneCopyRasterBufferProvider::PlaybackToStagingBuffer(cc::StagingBuffer*, cc::RasterSource const*, gfx::Rect const&, gfx::Rect const&, gfx::AxisTransform2d const&, viz::ResourceFormat, gfx::ColorSpace const&, cc::RasterSource::PlaybackSettings const&, unsigned long, unsigned long) ./../../cc/raster/one_copy_raster_buffer_provider.cc:353:5 51 | #44 0x7f93c364c683 in cc::OneCopyRasterBufferProvider::PlaybackAndCopyOnWorkerThread(gpu::Mailbox*, unsigned int, bool, gpu::SyncToken const&, cc::RasterSource const*, gfx::Rect const&, gfx::Rect const&, gfx::AxisTransform2d const&, gfx::Size const&, viz::ResourceFormat, gfx::ColorSpace const&, cc::RasterSource::PlaybackSettings const&, unsigned long, unsigned long) ./../../cc/raster/one_copy_raster_buffer_provider.cc:289:3 52 | #45 0x7f93c364bf90 in cc::OneCopyRasterBufferProvider::RasterBufferImpl::Playback(cc::RasterSource const*, gfx::Rect const&, gfx::Rect const&, unsigned long, gfx::AxisTransform2d const&, cc::RasterSource::PlaybackSettings const&, GURL const&) ./../../cc/raster/one_copy_raster_buffer_provider.cc:127:39 53 | #46 0x7f93c38f4150 in cc::(anonymous namespace)::RasterTaskImpl::RunOnWorkerThread() ./../../cc/tiles/tile_manager.cc:132:21 54 | #47 0x7f93db3a3094 in content::CategorizedWorkerPool::RunTaskInCategoryWithLockAcquired(cc::TaskCategory) ./../../content/renderer/categorized_worker_pool.cc:430:28 55 | #48 0x7f93db39fe4e in content::CategorizedWorkerPool::RunTaskWithLockAcquired(std::__Cr::vector > const&) ./../../content/renderer/categorized_worker_pool.cc:408:7 56 | #49 0x7f93db39fac0 in content::CategorizedWorkerPool::Run(std::__Cr::vector > const&, base::ConditionVariable*) ./../../content/renderer/categorized_worker_pool.cc:292:10 57 | #50 0x7f93db3a3f58 in content::(anonymous namespace)::CategorizedWorkerPoolThread::Run() ./../../content/renderer/categorized_worker_pool.cc:75:32 58 | #51 0x7f93ef378b21 in base::SimpleThread::ThreadMain() ./../../base/threading/simple_thread.cc:75:3 59 | #52 0x7f93ef4e6cae in base::(anonymous namespace)::ThreadFunc(void*) ./../../base/threading/platform_thread_posix.cc:87:13 60 | #53 0x7f934e9f4608 in start_thread /build/glibc-eX1tMB/glibc-2.31/nptl/pthread_create.c:477:8 61 | 62 | 0x61d000060c60 is located 60 bytes to the right of 1956-byte region [0x61d000060480,0x61d000060c24) 63 | allocated by thread T8 (CompositorTileW) here: 64 | #0 0x56396e6f495d in malloc /b/s/w/ir/cache/builder/src/third_party/llvm/compiler-rt/lib/asan/asan_malloc_linux.cpp:145:3 65 | #1 0x7f93e7e7f347 in malloc_throw(unsigned long) ./../../skia/ext/SkMemory_new_handler.cpp:74:64 66 | #2 0x7f93e7e7f038 in sk_malloc_flags(unsigned long, unsigned int) ./../../skia/ext/SkMemory_new_handler.cpp:122:14 67 | #3 0x7f93e8341179 in sk_malloc_throw(unsigned long) ./../../third_party/skia/include/private/SkMalloc.h:59:12 68 | #4 0x7f93e832b4a8 in sk_ft_alloc(FT_MemoryRec_*, long) ./../../third_party/skia/src/ports/SkFontHost_FreeType.cpp:117:16 69 | #5 0x7f9398a6bab9 in ft_mem_qalloc ./../../third_party/freetype/src/src/base/ftutil.c:75:15 70 | #6 0x7f9398a39e54 in ft_mem_alloc ./../../third_party/freetype/src/src/base/ftutil.c:54:25 71 | #7 0x7f9398a45bd7 in ft_glyphslot_alloc_bitmap ./../../third_party/freetype/src/src/base/ftobjs.c:526:11 72 | #8 0x7f9398bb8701 in Load_SBit_Png ./../../third_party/freetype/src/src/sfnt/pngshim.c:425:15 73 | #9 0x7f9398bb1e54 in tt_face_load_sbix_image ./../../third_party/freetype/src/src/sfnt/ttsbit.c:1546:15 74 | #10 0x7f9398b7f8b8 in tt_face_load_sbit_image ./../../third_party/freetype/src/src/sfnt/ttsbit.c:1628:15 75 | #11 0x7f9398c1c41a in load_sbit_image ./../../third_party/freetype/src/src/truetype/ttgload.c:2429:13 76 | #12 0x7f9398c1a5ad in TT_Load_Glyph ./../../third_party/freetype/src/src/truetype/ttgload.c:2829:15 77 | #13 0x7f9398bd9cad in tt_glyph_load ./../../third_party/freetype/src/src/truetype/ttdriver.c:474:13 78 | #14 0x7f9398a3510e in FT_Load_Glyph ./../../third_party/freetype/src/src/base/ftobjs.c:948:15 79 | #15 0x7f93e83391f8 in SkScalerContext_FreeType::generateImage(SkGlyph const&) ./../../third_party/skia/src/ports/SkFontHost_FreeType.cpp:1365:20 80 | #16 0x7f93e9ae5d62 in SkScalerContext::getImage(SkGlyph const&) ./../../third_party/skia/src/core/SkScalerContext.cpp:572:9 81 | #17 0x7f93e957630c in SkGlyph::setImage(SkArenaAlloc*, SkScalerContext*) ./../../third_party/skia/src/core/SkGlyph.cpp:89:24 82 | #18 0x7f93e9ad2e2c in SkScalerCache::prepareImage(SkGlyph*) ./../../third_party/skia/src/core/SkScalerCache.cpp:109:16 83 | #19 0x7f93e9ad7f3e in SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer*)::$_1::operator()(unsigned long, SkGlyphDigest, SkPoint) const ./../../third_party/skia/src/core/SkScalerCache.cpp:185:45 84 | #20 0x7f93e9ad4e33 in unsigned long SkScalerCache::commonFilterLoop(SkDrawableGlyphBuffer*, SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer*)::$_1&&) ./../../third_party/skia/src/core/SkScalerCache.cpp:171:17 85 | #21 0x7f93e9ad4615 in SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer*) ./../../third_party/skia/src/core/SkScalerCache.cpp:181:26 86 | #22 0x7f93e9595cad in SkStrikeCache::Strike::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer*) ./../../third_party/skia/src/core/SkStrikeCache.h:103:44 87 | #23 0x7f93e959185d in SkGlyphRunListPainter::drawForBitmapDevice(SkGlyphRunList const&, SkMatrix const&, SkGlyphRunListPainter::BitmapDevicePainter const*) ./../../third_party/skia/src/core/SkGlyphRunPainter.cpp:130:21 88 | #24 0x7f93e94efbc7 in SkDraw::drawGlyphRunList(SkGlyphRunList const&, SkGlyphRunListPainter*) const ./../../third_party/skia/src/core/SkDraw_text.cpp:135:19 89 | #25 0x7f93e9387557 in SkBitmapDevice::drawGlyphRunList(SkGlyphRunList const&) ./../../third_party/skia/src/core/SkBitmapDevice.cpp:557:5 90 | #26 0x7f93e9588d8a in SkGlyphRunBuilder::drawTextBlob(SkPaint const&, SkTextBlob const&, SkPoint, SkBaseDevice*) ./../../third_party/skia/src/core/SkGlyphRun.cpp:202:17 91 | #27 0x7f93e9426773 in SkCanvas::onDrawTextBlob(SkTextBlob const*, float, float, SkPaint const&) ./../../third_party/skia/src/core/SkCanvas.cpp:2629:34 92 | #28 0x7f93e94273df in SkCanvas::drawTextBlob(SkTextBlob const*, float, float, SkPaint const&) ./../../third_party/skia/src/core/SkCanvas.cpp:2663:11 93 | #29 0x7f93e60ff11f in cc::DrawTextBlobOp::RasterWithFlags(cc::DrawTextBlobOp const*, cc::PaintFlags const*, SkCanvas*, cc::PlaybackParams const&)::$_111::operator()(SkCanvas*, SkPaint const&) const ./../../cc/paint/paint_op_buffer.cc:1626:8 94 | 95 | Thread T8 (CompositorTileW) created by T0 (chrome) here: 96 | #0 0x56396e6ded6a in pthread_create /b/s/w/ir/cache/builder/src/third_party/llvm/compiler-rt/lib/asan/asan_interceptors.cpp:214:3 97 | #1 0x7f93ef4e4e6f in base::(anonymous namespace)::CreateThread(unsigned long, bool, base::PlatformThread::Delegate*, base::PlatformThreadHandle*, base::ThreadPriority) ./../../base/threading/platform_thread_posix.cc:126:13 98 | #2 0x7f93ef4e4960 in base::PlatformThread::CreateWithPriority(unsigned long, base::PlatformThread::Delegate*, base::PlatformThreadHandle*, base::ThreadPriority) ./../../base/threading/platform_thread_posix.cc:252:10 99 | #3 0x7f93ef377ed6 in base::SimpleThread::StartAsync() ./../../base/threading/simple_thread.cc:51:13 100 | #4 0x7f93db39cc13 in content::CategorizedWorkerPool::Start(int) ./../../content/renderer/categorized_worker_pool.cc:201:13 101 | #5 0x7f93db6ed493 in content::RenderThreadImpl::Init() ./../../content/renderer/render_thread_impl.cc:728:29 102 | #6 0x7f93db6f0c97 in content::RenderThreadImpl::RenderThreadImpl(base::RepeatingCallback, std::__Cr::unique_ptr >) ./../../content/renderer/render_thread_impl.cc:572:3 103 | #7 0x7f93db7957b3 in content::RendererMain(content::MainFunctionParams const&) ./../../content/renderer/renderer_main.cc:213:9 104 | #8 0x7f93dc1f222c in content::RunZygote(content::ContentMainDelegate*) ./../../content/app/content_main_runner_impl.cc:484:14 105 | #9 0x7f93dc1f2fb4 in content::RunOtherNamedProcessTypeMain(std::__Cr::basic_string, std::__Cr::allocator > const&, content::MainFunctionParams const&, content::ContentMainDelegate*) ./../../content/app/content_main_runner_impl.cc:536:12 106 | #10 0x7f93dc1f5927 in content::ContentMainRunnerImpl::Run(bool) ./../../content/app/content_main_runner_impl.cc:858:10 107 | #11 0x7f93dc1ebe8d in content::RunContentProcess(content::ContentMainParams const&, content::ContentMainRunner*) ./../../content/app/content_main.cc:372:36 108 | #12 0x7f93dc1ed7e2 in content::ContentMain(content::ContentMainParams const&) ./../../content/app/content_main.cc:398:10 109 | #13 0x56396e7272cf in ChromeMain ./../../chrome/app/chrome_main.cc:130:12 110 | #14 0x56396e726ec1 in main ./../../chrome/app/chrome_exe_main_aura.cc:17:10 111 | #15 0x7f934de490b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16 112 | 113 | SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/oz1ng/Desktop/chromium/src/out/asan/libfreetype_harfbuzz.so+0xc7988c) 114 | Shadow bytes around the buggy address: 115 | 0x0c3a80004130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 116 | 0x0c3a80004140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 117 | 0x0c3a80004150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 118 | 0x0c3a80004160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 119 | 0x0c3a80004170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 120 | =>0x0c3a80004180: 00 00 00 00 04 fa fa fa fa fa fa fa[fa]fa fa fa 121 | 0x0c3a80004190: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 122 | 0x0c3a800041a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 123 | 0x0c3a800041b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 124 | 0x0c3a800041c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 125 | 0x0c3a800041d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 126 | Shadow byte legend (one shadow byte represents 8 application bytes): 127 | Addressable: 00 128 | Partially addressable: 01 02 03 04 05 06 07 129 | Heap left redzone: fa 130 | Freed heap region: fd 131 | Stack left redzone: f1 132 | Stack mid redzone: f2 133 | Stack right redzone: f3 134 | Stack after return: f5 135 | Stack use after scope: f8 136 | Global redzone: f9 137 | Global init order: f6 138 | Poisoned by user: f7 139 | Container overflow: fc 140 | Array cookie: ac 141 | Intra object redzone: bb 142 | ASan internal: fe 143 | Left alloca redzone: ca 144 | Right alloca redzone: cb 145 | Shadow gap: cc 146 | ==169196==ABORTING 147 | [169132:169154:1001/031824.818756:WARNING:discardable_shared_memory_manager.cc(432)] Some MojoDiscardableSharedMemoryManagerImpls are still alive. They will be leaked. 148 | [169132:169132:1001/031824.840424:WARNING:pref_notifier_impl.cc(40)] Pref observer for media_router.cast_allow_all_ips found at shutdown. 149 | [1001/031825.318798:ERROR:nacl_helper_linux.cc(307)] NaCl helper process running without a sandbox! 150 | Most likely you need to configure your SUID sandbox correctly 151 | -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-15999/README.md: -------------------------------------------------------------------------------- 1 | # CVE-2020-15999 2 | 3 | > FreeType 4 | - Original Report : [Link](https://bugs.chromium.org/p/chromium/issues/detail?id=1139963&q=cve-2020-15999&can=1) 5 | 6 | - asan 로그 7 | 8 | [asanlog.txt](./CVE-2020-15999_data/asanlog.txt) 9 | 10 | ## POC 11 | 12 | ```html 13 | 14 | 54 | 55 | ``` 56 | 57 | ## 요약 58 | 59 | ![Untitled](./CVE-2020-15999_data/Untitled.png) 60 | 61 | - freetype에서 sbix 태그를 통해 png 이미지를 사용하게 되면 libpng를 통해 읽어옵니다. 62 | 63 | 이때 freetype에서 미리 생성해두는 bitmap 데이터를 저장해둘 heap chunk의 크기를 계산한 후 해당 크기로 heap chunk를 할당, 해당 chunk에 비트맵 데이터를 저장하게 됩니다. 64 | 65 | 실제 PNG 파일의 width 값(4바이트)과 height 값(4바이트)을 Uns Short 형(2바이트)으로 형변환 하고 이 형변환된 값으로 heap chunk의 크기를 계산하게 되면서 실제 비트맵 데이터의 크기는 생성된 heap chunk의 크기보다 더 클 수 있게 됩니다. 66 | 67 | 결과적으로 생성된 chunk의 크기보다 더 큰 값을 읽어오게 되면서 취약점이 발생하게 됩니다. 68 | 69 | 70 | ## 소스코드 분석 71 | 72 | - 적절하지 못한 자료형으로 캐스팅 된 값 저장 73 | 74 | ```c 75 | FT_LOCAL_DEF( FT_Error ) 76 | Load_SBit_Png( FT_GlyphSlot slot, 77 | FT_Int x_offset, 78 | FT_Int y_offset, 79 | FT_Int pix_bits, 80 | TT_SBit_Metrics metrics, 81 | FT_Memory memory, 82 | FT_Byte* data, 83 | FT_UInt png_len, 84 | FT_Bool populate_map_and_metrics, 85 | FT_Bool metrics_only ) 86 | { 87 | ... 88 | png_read_info( png, info ); // [A] : png 헤더 정보 저장 89 | png_get_IHDR( png, info, 90 | &imgWidth, &imgHeight, 91 | &bitdepth, &color_type, &interlace, 92 | NULL, NULL ); 93 | 94 | if ( error || 95 | ( !populate_map_and_metrics && 96 | ( (FT_Int)imgWidth != metrics->width || 97 | (FT_Int)imgHeight != metrics->height ) ) ) 98 | goto DestroyExit; 99 | 100 | if ( populate_map_and_metrics ) 101 | { 102 | **metrics->width = (FT_UShort)imgWidth; // [B] // 32 bit -> 16 bit 103 | metrics->height = (FT_UShort)imgHeight; // 32 bit -> 16 bit** 104 | 105 | **map->width = metrics->width;** 106 | **map->rows = metrics->height;** 107 | map->pixel_mode = FT_PIXEL_MODE_BGRA; 108 | **map->pitch = (int)( map->width * 4 );** 109 | map->num_grays = 256; 110 | 111 | /* reject too large bitmaps similarly to the rasterizer */ 112 | if ( map->rows > 0x7FFF || map->width > 0x7FFF ) 113 | { 114 | error = FT_THROW( Array_Too_Large ); 115 | goto DestroyExit; 116 | } 117 | } 118 | ... 119 | } 120 | ``` 121 | 122 | - [A] 에서 png 헤더 정보를 저장한 후 [B]에서 가져온 정보들을 저장합니다. 123 | 124 | - [png_ptr, info_ptr 값 저장과정](png_ptr,info_ptr값_저장_과정.md) 125 | 126 | **[루트 커즈] 이때, `imgWidth`와 `imgHeight`는 unsigned short(16비트)로 형 변환 되어 저장됩니다.** 127 | 128 | - `png_structp` 구조체 129 | 130 | ```c 131 | // third_party/libpng/pngstruct.h 132 | struct png_struct_def 133 | { 134 | ... 135 | png_uint_32 width; /* width of image in pixels */ 136 | png_uint_32 height; /* height of image in pixels */ 137 | png_uint_32 num_rows; /* number of rows in current pass */ 138 | png_uint_32 usr_width; /* width of row at start of write */ 139 | size_t rowbytes; /* size of row in bytes */ 140 | png_uint_32 iwidth; /* width of current interlaced row in pixels */ 141 | png_uint_32 row_number; /* current row in interlace pass */ 142 | png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ 143 | png_bytep prev_row; /* buffer to save previous (unfiltered) row. 144 | * While reading this is a pointer into 145 | * big_prev_row; while writing it is separately 146 | * allocated if needed. 147 | */ 148 | png_bytep row_buf; /* buffer to save current (unfiltered) row. 149 | * While reading, this is a pointer into 150 | * big_row_buf; while writing it is separately 151 | * allocated. 152 | */ 153 | ... 154 | } 155 | ``` 156 | 157 | - png bitmap data chunk 할당 158 | 159 | ```c 160 | FT_LOCAL_DEF( FT_Error ) 161 | Load_SBit_Png( FT_GlyphSlot slot, 162 | FT_Int x_offset, 163 | FT_Int y_offset, 164 | FT_Int pix_bits, 165 | TT_SBit_Metrics metrics, 166 | FT_Memory memory, 167 | FT_Byte* data, 168 | FT_UInt png_len, 169 | FT_Bool populate_map_and_metrics, 170 | FT_Bool metrics_only ) 171 | { 172 | ... 173 | if ( populate_map_and_metrics ) 174 | { 175 | /* this doesn't overflow: 0x7FFF * 0x7FFF * 4 < 2^32 */ 176 | FT_ULong size = **map->rows** * (FT_ULong)map->pitch; // [A] 177 | 178 | error = ft_glyphslot_alloc_bitmap( slot, size ); 179 | if ( error ) 180 | goto DestroyExit; 181 | } 182 | ... 183 | } 184 | ``` 185 | 186 | - [A]에서 `size` 값을 정하고 `ft_glyphslot_alloc_bitmap` 으로 `slot->bitmap.buffer`에 `bitmap chunk`를 할당합니다. 187 | - [bitmap chunk를 할당하는 과정](./bitmap_chunk에_할당하는_과정.md) 188 | - **[루트 커즈]** 189 | 190 | **여기서 size를 정할 때 사용되는 `map->rows`의 값은 16비트로 저장된 `imgheight`값입니다. 191 | 실제 `imgheight` 값은 4바이트이고 실제로 한 줄(row) 단위로 bitmap 데이터를 읽어 올 때 사용하는 height 값 또한 4바이트를 사용하게 됩니다. 192 | 때문에 만약 `imgheight`의 값이 2바이트 이상이라면 해당 부분에서 할당되는 `png의 bitmap data를 담을 chunk`의 크기는 정상적인 경우보다 더 작게 할당이 되게 됩니다. 193 | 이는 결과적으로 실제로 bitmap data를 읽어올 때 Heap Overflow를 발생시키게 됩니다.** 194 | 195 | - Heap BOF! 코드 흐름 196 | 197 | ```c 198 | FT_LOCAL_DEF( FT_Error ) 199 | Load_SBit_Png( FT_GlyphSlot slot, 200 | FT_Int x_offset, 201 | FT_Int y_offset, 202 | FT_Int pix_bits, 203 | TT_SBit_Metrics metrics, 204 | FT_Memory memory, 205 | FT_Byte* data, 206 | FT_UInt png_len, 207 | FT_Bool populate_map_and_metrics, 208 | FT_Bool metrics_only ) 209 | { 210 | FT_Bitmap *map = &slot->bitmap; 211 | ... 212 | png_byte* *rows = NULL; /* pacify compiler */ 213 | // png_byte는 unsigned char입니다. pngconf.h에 정의되어 있습니다. 214 | ... 215 | for ( i = 0; i < **(FT_Int)imgHeight**; i++ ) // [A] **여기서 사용하는 imgHeight 값은 32비트입니다. 즉, 위에서 생성된 size의 값을 생성할때 사용한 map->rows와 자료형이 다릅니다.** 216 | rows[i] = map->buffer + ( y_offset + i ) * map->pitch + x_offset * 4; 217 | png_read_image( png, rows ); // [B] 218 | ... 219 | } 220 | ``` 221 | 222 | - **[A]** : 해당 부분에서 사용하는 `imgHeight`의 값은 32비트의 원본 height값입니다. 즉, `imgheight`의 값이 2바이트 이상의 값이라면 `bitmap chunk`를 생성할 때 사용한 size의 값을 정할 때보다 더 큰 값이 있게 되므로 더 많이 루프를 돌며 값을 읽어오게 되고 이 과정에서 Heap Overflow가 발생하게 됩니다. 223 | - [B] : `png_read_image`를 호출합니다. 224 | - [libpng 내에서 Heap Overflow가 발생하는 위치](./libpng_내에서_Heap_Overflow가_발생하는_위치.md) 225 | 226 | ## 패치 된 코드 227 | 228 | - 수전 전 229 | 230 | ```c 231 | // pngshim.c 232 | FT_LOCAL_DEF( FT_Error ) 233 | Load_SBit_Png( FT_GlyphSlot slot, 234 | FT_Int x_offset, 235 | FT_Int y_offset, 236 | FT_Int pix_bits, 237 | TT_SBit_Metrics metrics, 238 | FT_Memory memory, 239 | FT_Byte* data, 240 | FT_UInt png_len, 241 | FT_Bool populate_map_and_metrics, 242 | FT_Bool metrics_only ) 243 | { 244 | ... 245 | if ( populate_map_and_metrics ) 246 | { 247 | metrics->width = (FT_UShort)imgWidth; 248 | metrics->height = (FT_UShort)imgHeight; 249 | 250 | map->pixel_mode = FT_PIXEL_MODE_BGRA; 251 | map->pitch = (int)( map->width * 4 ); 252 | map->num_grays = 256; 253 | 254 | **/* reject too large bitmaps similarly to the rasterizer */ 255 | if ( map->rows > 0x7FFF || map->width > 0x7FFF ) 256 | { 257 | error = FT_THROW( Array_Too_Large ); 258 | goto DestroyExit; 259 | }** 260 | } 261 | ... 262 | } 263 | ``` 264 | 265 | - 수정 후 266 | 267 | ```c 268 | // pngshim.c 269 | FT_LOCAL_DEF( FT_Error ) 270 | Load_SBit_Png( FT_GlyphSlot slot, 271 | FT_Int x_offset, 272 | FT_Int y_offset, 273 | FT_Int pix_bits, 274 | TT_SBit_Metrics metrics, 275 | FT_Memory memory, 276 | FT_Byte* data, 277 | FT_UInt png_len, 278 | FT_Bool populate_map_and_metrics, 279 | FT_Bool metrics_only ) 280 | { 281 | ... 282 | if ( populate_map_and_metrics ) 283 | { 284 | **/* reject too large bitmaps similarly to the rasterizer */ 285 | if ( imgWidth > 0x7FFF || imgHeight > 0x7FFF ) 286 | { 287 | error = FT_THROW( Array_Too_Large ); 288 | goto DestroyExit; 289 | }** 290 | 291 | metrics->width = (FT_UShort)imgWidth; 292 | metrics->height = (FT_UShort)imgHeight; 293 | ... 294 | } 295 | ... 296 | } 297 | ``` 298 | 299 | - `imgWidth`와 `imgHeight`의 값을 metrics에 저장하기 전에 값을 검증하도록 수정되었습니다. -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-15999/bitmap_chunk에_할당하는_과정.md: -------------------------------------------------------------------------------- 1 | - ### `bitmap chunk`를 할당하는 과정 2 | 3 | ```c 4 | FT_BASE_DEF( FT_Error ) 5 | ft_glyphslot_alloc_bitmap( FT_GlyphSlot slot, 6 | FT_ULong size ) 7 | { 8 | FT_Memory memory = FT_FACE_MEMORY( slot->face ); 9 | FT_Error error; 10 | 11 | if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) 12 | FT_FREE( slot->bitmap.buffer ); 13 | else 14 | slot->internal->flags |= FT_GLYPH_OWN_BITMAP; 15 | 16 | FT_MEM_ALLOC( slot->bitmap.buffer, size ); // [A] 17 | return error; 18 | } 19 | ``` 20 | 21 | - 아래 코드를 요약하면 `FT_MEM_ALLOC` 전처리기 함수에서 `ft_mem_alloc` 함수를 통해 메모리를 할당받고 `ptr`에 저장합니다. 22 | 23 | ```c 24 | #define FT_MEM_ALLOC( ptr, size ) \ 25 | FT_ASSIGNP_INNER( ptr, ft_mem_alloc( memory, (FT_Long)(size), &error ) ) 26 | ``` 27 | 28 | ```c 29 | // ftutil.c 30 | FT_BASE_DEF( FT_Pointer ) 31 | ft_mem_alloc( FT_Memory memory, 32 | FT_Long size, 33 | FT_Error *p_error ) 34 | { 35 | FT_Error error; 36 | FT_Pointer block = ft_mem_qalloc( memory, size, &error ); 37 | 38 | if ( !error && block && size > 0 ) 39 | FT_MEM_ZERO( block, size ); 40 | 41 | *p_error = error; 42 | return block; 43 | } 44 | ``` 45 | 46 | ```c 47 | // ftutil.c 48 | FT_BASE_DEF( FT_Pointer ) 49 | ft_mem_qalloc( FT_Memory memory, 50 | FT_Long size, 51 | FT_Error *p_error ) 52 | { 53 | FT_Error error = FT_Err_Ok; 54 | FT_Pointer block = NULL; 55 | 56 | if ( size > 0 ) 57 | { 58 | block = memory->alloc( memory, size ); // [A] 59 | if ( !block ) 60 | error = FT_THROW( Out_Of_Memory ); 61 | } 62 | else if ( size < 0 ) 63 | { 64 | /* may help catch/prevent security issues */ 65 | error = FT_THROW( Invalid_Argument ); 66 | } 67 | 68 | *p_error = error; 69 | return block; 70 | } 71 | ``` 72 | 73 | ```c 74 | struct FT_MemoryRec_ 75 | { 76 | void* user; 77 | FT_Alloc_Func alloc; 78 | FT_Free_Func free; 79 | FT_Realloc_Func realloc; 80 | }; 81 | ``` -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-15999/libpng_내에서_Heap_Overflow가_발생하는_위치.md: -------------------------------------------------------------------------------- 1 | ### libpng 내에서 Heap Overflow가 발생하는 위치 2 | 3 | ```c 4 | // pngread.c 5 | void PNGAPI 6 | png_read_image(png_structrp png_ptr, png_bytepp image) 7 | { 8 | png_uint_32 i, image_height; 9 | int pass, j; 10 | png_bytepp rp; 11 | ... 12 | 13 | /* 14 | // pnglibconf.h 15 | #define PNG_READ_INTERLACING_SUPPORTED 16 | */ 17 | 18 | #ifdef PNG_READ_INTERLACING_SUPPORTED 19 | if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) 20 | { 21 | pass = png_set_interlace_handling(png_ptr); 22 | /* And make sure transforms are initialized. */ 23 | png_start_read_image(png_ptr); 24 | } 25 | else 26 | { 27 | ... 28 | pass = png_set_interlace_handling(png_ptr); // png_ptr->interaced가 참이면 7 리턴 아니라면 1 리턴 29 | } 30 | #else 31 | ... 32 | #endif 33 | 34 | image_height=png_ptr->height; // [A] 35 | 36 | for (j = 0; j < pass; j++) 37 | { 38 | rp = image; 39 | for (i = 0; i < image_height; i++) 40 | { 41 | png_read_row(png_ptr, *rp, NULL); // [B] 42 | rp++; 43 | } 44 | } 45 | } 46 | ``` 47 | 48 | - `png_set_interlace_handling` 함수 49 | 50 | ```c 51 | // pngtrans.c 52 | int PNGAPI 53 | png_set_interlace_handling(png_structrp png_ptr) 54 | { 55 | png_debug(1, "in png_set_interlace handling"); 56 | 57 | if (png_ptr != 0 && png_ptr->interlaced != 0) 58 | { 59 | png_ptr->transformations |= PNG_INTERLACE; // #define PNG_INTERLACE 0x0002U 60 | return (7); 61 | } 62 | 63 | return (1); 64 | } 65 | ``` 66 | 67 | - [A] : png 헤더에 저장된 32bit의 `height` 값을 `image_height`에 저장합니다. 68 | 69 | - [B] : `png_read_row` 함수를 호출하여 한 열씩 읽어옵니다. 70 | 71 | ```c 72 | // pngread.c 73 | void PNGAPI 74 | png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) 75 | { 76 | ****png_row_info row_info; 77 | 78 | ... 79 | 80 | /* png_read_start_row sets the information (in particular iwidth) for this 81 | * interlace pass. 82 | */ 83 | if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) 84 | png_read_start_row(png_ptr); 85 | 86 | /* 1.5.6: row_info moved out of png_struct to a local here. */ 87 | **row_info.width** = png_ptr->iwidth; /* NOTE: width of current interlaced row */ // [A] : png_read_start_row에서 설정 88 | row_info.color_type = png_ptr->color_type; 89 | row_info.bit_depth = png_ptr->bit_depth; 90 | row_info.channels = png_ptr->channels; 91 | row_info.pixel_depth = png_ptr->pixel_depth; 92 | row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); 93 | 94 | ... 95 | 96 | /* Fill the row with IDAT data: */ 97 | png_ptr->row_buf[0]=255; /* to force error if no data was found */ 98 | png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1); 99 | 100 | if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) // png.h // #define PNG_FILTER_VALUE_NONE 0 101 | { 102 | if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) 103 | png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, 104 | png_ptr->prev_row + 1, png_ptr->row_buf[0]); // png.h // #define PNG_FILTER_VALUE_LAST 5 105 | else 106 | png_error(png_ptr, "bad adaptive filter value"); 107 | } 108 | 109 | /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before 110 | * 1.5.6, while the buffer really is this big in current versions of libpng 111 | * it may not be in the future, so this was changed just to copy the 112 | * interlaced count: 113 | */ 114 | memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); 115 | 116 | ... 117 | 118 | /* 119 | // pnglibconf.h 120 | #define PNG_READ_INTERLACING_SUPPORTED 121 | */ 122 | #ifdef PNG_READ_INTERLACING_SUPPORTED 123 | /* Expand interlaced rows to full size */ 124 | if (png_ptr->interlaced != 0 && 125 | (png_ptr->transformations & PNG_INTERLACE) != 0) 126 | { 127 | ... 128 | if (row != NULL) 129 | png_combine_row(png_ptr, row, 0/*row*/); // [B] 130 | } 131 | 132 | else 133 | #endif 134 | ... 135 | } 136 | ``` 137 | 138 | - [A] : **`png_ptr->iwidth`** 저장되는 위치 139 | 140 | ```c 141 | void /* PRIVATE */ 142 | png_read_start_row(png_structrp png_ptr) 143 | { 144 | ... 145 | if (png_ptr->interlaced != 0) 146 | { 147 | ... 148 | **png_ptr->iwidth** = (png_ptr->width + 149 | png_pass_inc[png_ptr->pass] - 1 - 150 | png_pass_start[png_ptr->pass]) / 151 | png_pass_inc[png_ptr->pass]; 152 | } 153 | 154 | else 155 | { 156 | png_ptr->num_rows = png_ptr->height; 157 | **png_ptr->iwidth** = png_ptr->width; 158 | } 159 | ... 160 | } 161 | ``` 162 | 163 | - [B] : `png_combine_row` 함수를 호출합니다. 이 함수에서 Heap Overflow가 발생합니다. 164 | 165 | ```c 166 | // pngrutil.c 167 | void /* PRIVATE */ 168 | png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) 169 | { 170 | unsigned int pixel_depth = png_ptr->transformed_pixel_depth; 171 | png_const_bytep sp = png_ptr->row_buf + 1; 172 | png_alloc_size_t row_width = png_ptr->width; 173 | unsigned int pass = png_ptr->pass; 174 | png_bytep end_ptr = 0; 175 | png_byte end_byte = 0; 176 | unsigned int end_mask; 177 | 178 | ... 179 | switch (bytes_to_copy) 180 | { 181 | case 1: 182 | ... 183 | 184 | case 2: 185 | ... 186 | 187 | case 3: 188 | ... 189 | 190 | default: 191 | #if PNG_ALIGN_TYPE != PNG_ALIGN_NONE 192 | /* Check for double byte alignment and, if possible, use a 193 | * 16-bit copy. Don't attempt this for narrow images - ones that 194 | * are less than an interlace panel wide. Don't attempt it for 195 | * wide bytes_to_copy either - use the memcpy there. 196 | */ 197 | if (bytes_to_copy < 16 /*else use memcpy*/ && 198 | png_isaligned(dp, png_uint_16) && 199 | png_isaligned(sp, png_uint_16) && 200 | bytes_to_copy % (sizeof (png_uint_16)) == 0 && 201 | bytes_to_jump % (sizeof (png_uint_16)) == 0) 202 | { 203 | /* Everything is aligned for png_uint_16 copies, but try for 204 | * png_uint_32 first. 205 | */ 206 | if (png_isaligned(dp, png_uint_32) && 207 | png_isaligned(sp, png_uint_32) && 208 | bytes_to_copy % (sizeof (png_uint_32)) == 0 && 209 | bytes_to_jump % (sizeof (png_uint_32)) == 0) 210 | { 211 | /* 212 | // pngpriv.h 213 | # define png_aligncast(type, value) ((void*)(value)) // void로 형변환 214 | # define png_aligncastconst(type, value) ((const void*)(value)) // const void로 형변환 215 | */ 216 | png_uint_32p dp32 = png_aligncast(png_uint_32p,dp); 217 | png_const_uint_32p sp32 = png_aligncastconst( 218 | png_const_uint_32p, sp); 219 | size_t skip = (bytes_to_jump-bytes_to_copy) / 220 | (sizeof (png_uint_32)); 221 | 222 | do 223 | { 224 | size_t c = bytes_to_copy; 225 | do 226 | { 227 | ***dp32**++ = ***sp32**++; // [D] : Heap BOF!!! 228 | **c** -= (sizeof (png_uint_32)); 229 | } 230 | while (**c** > 0); 231 | 232 | if (**row_width** <= **bytes_to_jump**) 233 | return; 234 | 235 | dp32 += **skip**; 236 | sp32 += **skip**; 237 | row_width -= bytes_to_jump; 238 | } 239 | while (**bytes_to_copy** <= **row_width**); 240 | 241 | /* Get to here when the row_width truncates the final copy. 242 | * There will be 1-3 bytes left to copy, so don't try the 243 | * 16-bit loop below. 244 | */ 245 | dp = (png_bytep)dp32; 246 | sp = (png_const_bytep)sp32; 247 | do 248 | *dp++ = *sp++; 249 | while (--row_width > 0); 250 | return; 251 | } 252 | 253 | ... 254 | } 255 | ``` 256 | 257 | - [D] : 해당 코드를 실행할 때 chunk의 범위를 벗어난 곳에 값을 입력하여 Heap Overflow가 발생합니다. -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-15999/png_ptr,info_ptr값_저장_과정.md: -------------------------------------------------------------------------------- 1 | ### `png_ptr`, `info_ptr` 값 저장 과정 2 | 3 | - `png_set_IHDR`함수에서 검증되지 않은 `width`와 `height` 값을 `info_ptr`에 값을 저장합니다. 4 | - `png_set_IHDR` 함수의 호출 순서는 다음과 같습니다. 5 | - `png_read_info` → `png_handle_IHDR` → `png_set_IHDR` 6 | 7 | ```c 8 | // pngshim.c 9 | FT_LOCAL_DEF( FT_Error ) 10 | Load_SBit_Png( FT_GlyphSlot slot, 11 | FT_Int x_offset, 12 | FT_Int y_offset, 13 | FT_Int pix_bits, 14 | TT_SBit_Metrics metrics, 15 | FT_Memory memory, 16 | FT_Byte* data, 17 | FT_UInt png_len, 18 | FT_Bool populate_map_and_metrics, 19 | FT_Bool metrics_only ) 20 | { 21 | ... 22 | png_read_info( png, info ); // [A] 23 | png_get_IHDR( png, info, // [F] 24 | &imgWidth, &imgHeight, 25 | &bitdepth, &color_type, &interlace, 26 | NULL, NULL ); 27 | ... 28 | } 29 | ``` 30 | 31 | - [A] : `png_read_info` 를 호출합니다. 32 | 33 | ```c 34 | // pngread.c 35 | void PNGAPI 36 | png_read_info(png_structrp png_ptr, png_inforp info_ptr) 37 | { 38 | ... 39 | if (png_ptr == NULL || info_ptr == NULL) 40 | return; 41 | ... 42 | for (;;) 43 | { 44 | png_uint_32 length = png_read_chunk_header(png_ptr); 45 | ... 46 | /* This should be a binary subdivision search or a hash for 47 | * matching the chunk name rather than a linear search. 48 | */ 49 | if (chunk_name == png_IHDR) 50 | png_handle_IHDR(png_ptr, info_ptr, length); // [B] 51 | ... 52 | } 53 | } 54 | ``` 55 | 56 | - [B] : `png_handle_IHDR` 를 호출합니다. 57 | 58 | ```c 59 | //third_party/libpng/pngrutil.c 60 | void /* PRIVATE */ 61 | png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) 62 | { 63 | png_byte buf[13]; 64 | png_uint_32 width, height; 65 | int bit_depth, color_type, compression_type, filter_type; 66 | int interlace_type; 67 | ... 68 | png_crc_read(png_ptr, buf, 13); // [C] 69 | ... 70 | // [C-1] 71 | width = png_get_uint_31(png_ptr, buf); 72 | height = png_get_uint_31(png_ptr, buf + 4); 73 | bit_depth = buf[8]; 74 | color_type = buf[9]; 75 | compression_type = buf[10]; 76 | filter_type = buf[11]; 77 | interlace_type = buf[12]; 78 | 79 | /* Set internal variables */ 80 | png_ptr->width = width; 81 | png_ptr->height = height; 82 | png_ptr->bit_depth = (png_byte)bit_depth; 83 | png_ptr->interlaced = (png_byte)interlace_type; 84 | png_ptr->color_type = (png_byte)color_type; 85 | #ifdef PNG_MNG_FEATURES_SUPPORTED 86 | png_ptr->filter_type = (png_byte)filter_type; 87 | #endif 88 | png_ptr->compression_type = (png_byte)compression_type; 89 | 90 | /* Find number of channels */ 91 | switch (png_ptr->color_type) 92 | { 93 | ... 94 | case PNG_COLOR_TYPE_RGB_ALPHA: 95 | png_ptr->channels = 4; 96 | break; 97 | } 98 | 99 | /* Set up other useful info */ 100 | png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * png_ptr->channels); 101 | png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); 102 | ... 103 | png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, 104 | color_type, interlace_type, compression_type, filter_type); // [D] 105 | } 106 | ``` 107 | 108 | - [C], [C-1] : `png_handle_IHDR`함수에서는 `png_ptr`에 `png 헤더 구조체의 값`을 읽어와 저장합니다. 109 | - [D] : `png_set_IHDR`을 호출합니다. 110 | 111 | ```c 112 | // third_party/libpng/pngset.c 113 | void PNGAPI 114 | png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr, 115 | png_uint_32 width, png_uint_32 height, int bit_depth, 116 | int color_type, int interlace_type, int compression_type, 117 | int filter_type) 118 | { 119 | ... 120 | // [E] 121 | info_ptr->width = width; 122 | info_ptr->height = height; 123 | info_ptr->bit_depth = (png_byte)bit_depth; 124 | info_ptr->color_type = (png_byte)color_type; 125 | info_ptr->compression_type = (png_byte)compression_type; 126 | info_ptr->filter_type = (png_byte)filter_type; 127 | info_ptr->interlace_type = (png_byte)interlace_type; 128 | ... 129 | info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); 130 | info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); 131 | } 132 | ``` 133 | 134 | - [E] : `png_set_IHDR`함수에서는 `png_handle_IHDR`에서 읽어온 `png 헤더 구조체의 값`을 `info_ptr`에 저장합니다. 135 | 136 | ```c 137 | png_uint_32 PNGAPI 138 | png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr, 139 | png_uint_32 *width, png_uint_32 *height, int *bit_depth, 140 | int *color_type, int *interlace_type, int *compression_type, 141 | int *filter_type) 142 | { 143 | ... 144 | if (png_ptr == NULL || info_ptr == NULL) 145 | return (0); 146 | 147 | if (width != NULL) 148 | *width = info_ptr->width; 149 | 150 | if (height != NULL) 151 | *height = info_ptr->height; 152 | 153 | if (bit_depth != NULL) 154 | *bit_depth = info_ptr->bit_depth; 155 | 156 | if (color_type != NULL) 157 | *color_type = info_ptr->color_type; 158 | 159 | if (compression_type != NULL) 160 | *compression_type = info_ptr->compression_type; 161 | 162 | if (filter_type != NULL) 163 | *filter_type = info_ptr->filter_type; 164 | 165 | if (interlace_type != NULL) 166 | *interlace_type = info_ptr->interlace_type; 167 | ... 168 | return (1); 169 | } 170 | ``` 171 | 172 | - [F] : png 파일 헤더에서 읽어온 값을 `png_get_IHDR` 함수에서 각 변수에 저장합니다. -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-16002/img/Untitled 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-16002/img/Untitled 1.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-16002/img/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-16002/img/Untitled.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-16002/img/poc.drawio_(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-16002/img/poc.drawio_(1).png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-16002/img/uaf0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-16002/img/uaf0.pdf -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-6449/README.md: -------------------------------------------------------------------------------- 1 | # CVE-2020-6449 2 | 3 | 4 | 💡 이 문서는 Man Yue Mo님의 [GHSL-2020-040: Use After Free in Chrome WebAudio](https://securitylab.github.com/advisories/GHSL-2020-040-chrome/) 문서를 번역한 내용입니다. 5 | 6 | 💡 [+]은 제가 이해하는데 필요했던 추가적인 내용을 작성한 것입니다. 7 | 8 | ## **Summary** 9 | 10 | UaF in `DeferredTaskHandler::BreakConnections(2)` 11 | 12 | ## **Product** 13 | 14 | 15 | Chrome 16 | 17 | ## **CVE** 18 | 19 | 20 | CVE-2020-6449 21 | 22 | ## **Tested Version** 23 | 24 | 25 | Chrome version: master branch build 79956ba, asan build 80.3987.132 Operating System: Ubuntu 18.04 26 | 27 | ## **Details** 28 | 29 | 30 | 이 issue는 issue 1057593과 동일한 crash가 나지만 root cause와 fix가 다르다. 31 | 32 | 1057593과 유사하게 `BaseAudioContext`의 중지와 `AudioScheduleSourceNode`의 중지가 동일한 퀀텀에서 발생하면 `BaseAudioContext`가 일시 중단되는 동안 `AudioScheduleSourceNode`가 소멸될 수 있다. 이 시점에서 `DeferredTaskHandler`의 `active_source_handlers_`[[1]](#1-chromium-code-search)는 `DeferredTaskHandler::BreakConnections`[[2]](#2-chromium-code-search)에서 사용되기 전에 해당 `AudioScheduleSourceHandler`를 활성 상태로 유지하는 역할을 한다. 33 | 34 | `DeferredTaskHandler::BreakConnections`의 코드는 묵시적으로 `done_source_handlers_`가 `active_source_handlers_`의 하위 집합이므로 `active_source_handlers_`가 `finished_source_handlers_`의 `raw pointer`를 활성 상태로 유지한다고 가정한다. (`active_source_handlers_.erase`에서 볼 수 있듯이 `finished`가 `active_source_handlers_`에 포함되어 있다고 가정함) 35 | 36 | 그러나 문제는 `active_source_handlers_`가 `DeferredTaskHandler::ClearHandlersToBeDeleted` 메서드[[3]](#3-chromium-code-search)에 의해 지워질 수 있는 반면 `finished_source_handlers_` 는 동시에 지워지지 않아 `finished_source_handlers_`에 댕글링 포인터가 남게 되어 `BreakConnections`에서 `UaF`가 발생한다. 37 | 38 | ```cpp 39 | **// void DeferredTaskHandler::BreakConnections()** 40 | for (auto* finished : finished_source_handlers_) { 41 | // Break connection first and then remove from the list because that can 42 | // cause the handler to be deleted. 43 | finished->BreakConnectionWithLock(); //<-- `active_source_handlers_` may have been cleared, and finished is already freed 44 | active_source_handlers_.erase(finished); //<-- assumes `active_source_handlers_` contains finished 45 | } 46 | 47 | ``` 48 | 49 | `ClearHandlersToBeDeleted`를 호출하는 `BaseAudioContext::Uninitialize`를 트리거하는 컨텍스트를 파괴하면 `active_source_handlers_`를 지운 다음 DeferredTaskHandler::BreakConnections를 트리거하여 UaF를 발생시킬 수 있다. 50 | 51 | ### **[1]** [Chromium Code Search](https://source.chromium.org/chromium/chromium/src/+/bf433ad6dcfcaac460512bb45a53d5a2ea5356f9:third_party/blink/renderer/modules/webaudio/deferred_task_handler.h;l=255;drc=67e598a2ae32101acac19318c0c56830c12a303f?originalUrl=https:%2F%2Fcs.chromium.org%2F) 52 | 53 | ![third_party/blink/renderer/modules/webaudio/deferred_task_handler.h](img/Untitled.png) 54 | 55 | third_party/blink/renderer/modules/webaudio/deferred_task_handler.h 56 | 57 | - **[+]** 58 | 59 | ### WTF::HashSet 60 | 61 | [Chromium Code Search](https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/wtf/hash_set.h) 62 | 63 | base나 std 라이브러리에 우선하여 WTF에 정의된 타입을 사용해야 한다. 그 중 가장 인기 있는 것은 vectors, **hashsets**, hashmaps, strings 이다. 블링크에서는 WTF::Vector, **WTF::HashSet**, WTF::HashMAp, WTF::String, WTF::AtomicString을 std::vector, std::*set, std::*map, std::string을 대신하여 사용하여야 한다. 64 | 65 | 66 | [WTF (Web Template Framework)](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/third_party/blink/renderer/platform/wtf/README.md) 67 | 68 | 69 | ### **[2]** [Chromium Code Search](https://source.chromium.org/chromium/chromium/src/+/bf433ad6dcfcaac460512bb45a53d5a2ea5356f9:third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc;l=83;drc=67e598a2ae32101acac19318c0c56830c12a303f?originalUrl=https:%2F%2Fcs.chromium.org%2F) 70 | 71 | ![third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc](img/Untitled%201.png) 72 | 73 | third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc 74 | 75 | - **[+]** 76 | 77 | ![third_party/blink/renderer/modules/webaudio/audio_node.cc](img/Untitled%202.png) 78 | 79 | third_party/blink/renderer/modules/webaudio/audio_node.cc 80 | 81 | finished_source_handlers_를 참조하고 있는 포인터의 수를 줄이는 것으로 추측됨 82 | 83 | 84 | ### **[3]** [Chromium Code Search](https://source.chromium.org/chromium/chromium/src/+/bf433ad6dcfcaac460512bb45a53d5a2ea5356f9:third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc;l=361;drc=67e598a2ae32101acac19318c0c56830c12a303f;bpv=1;bpt=1?originalUrl=https:%2F%2Fcs.chromium.org%2F) 85 | 86 | ![third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc](img/Untitled%203.png) 87 | 88 | third_party/blink/renderer/modules/webaudio/deferred_task_handler.cc 89 | 90 | ### [+] **summary** 91 | 92 | `DeferredTaskHandler::BreakConnections`에서 raw pinter(finished)의 reference count하나씩 지운 다음, raw pointer(finished)를 erase해준다. 만약 이 함수 전에, `DeferredTaskHandler::ClearHandlersToBeDeleted` 에서 raw pointer들을 저장하고 있는 hashset인 active_source_handlers_가 clear 된다면, raw pointer(finished)들은 전부 댕글링 포인터가 되어서 UaF가 발생할 수도 있다. 93 | 94 | 95 | ### **Impact** 96 | 97 | Use-after-free in renderer. 98 | 99 | ## **Coordinated Disclosure Timeline** 100 | 101 | 102 | - 09/03/2020 Reported as [Chromium Issue 1059686](https://bugs.chromium.org/p/chromium/issues/detail?id=1059686) 103 | - 18/03/2020 [Fixed in version 80.0.3987.149](https://chromereleases.googleblog.com/2020/03/stable-channel-update-for-desktop_18.html) 104 | - [+] Patch[4c57222340cfb78edadf08532f4468c676df5395 - chromium/src.git - Git at Google](https://chromium.googlesource.com/chromium/src.git/+/4c57222340cfb78edadf08532f4468c676df5395) 105 | 106 | 107 | 108 | 109 | `finished_source_handlers_` 를 raw pointer가 아니라 smart pointer인 base::scoped_refptr<>로 관리한다. 110 | 111 | 또한 실직적으로 UaF가 발생하는 것을 막기 위해 `DeferredTaskHandler::ClearHandlersToBeDeleted` 에서 active_source_handlers_를 clear하기 전에 finished_source_handlers_부터 clear하는 코드를 추가하였다. 112 | 113 | ### reference count를 관리해주는 이유 114 | 115 | 가비지 컬렉터 알고리즘 중에 reference counting을 이용한 알고리즘이 있다. 해당 객체의 reference count가 0이 되면 메모리에서 해제시킨다. 이를 위해 포인터를 해제시킬때 reference count를 관리한다. 116 | 117 | ### base::scoped_refptr 118 | 119 | [Smart Pointer Guidelines - The Chromium Projects](https://www.chromium.org/developers/smart-pointer-guidelines) 120 | 121 | - Chromium에서 사용하는 포인터는 주로 `std::unique_ptr<>`과 `base::scoped_refptr<>`이다. 122 | - base::scoped_refptr<> 은 `std::shared_ptr<>`과 비슷한 개념이다. 123 | - **std::unique_ptr<>** 124 | - 객체에 대한 유일한 "소유권"이 있음을 보장하는 포인터 125 | - 복사 불가능, 소유권 없이 객체 삭제 불가 126 | - **std::shared_ptr<>** 127 | - 객체를 참조하고 있는 포인터의 개수를 보유한 포인터 128 | - 포인터의 개수가 0일 경우, delete로 메모리 해제 ***(reference count 방식)*** 129 | 130 | 131 | 132 | ## **Credit** 133 | 134 | 135 | This issue was discovered and reported by GHSL team member [@m-y-mo (Man Yue Mo)](https://github.com/m-y-mo). 136 | 137 | ## **Contact** 138 | 139 | 140 | You can contact the GHSL team at `securitylab@github.com`, please include the `GHSL-2020-040` in any communication regarding this issue. 141 | -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 1.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 2.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 3.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 4.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled 5.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2020-6449/img/Untitled.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2021-30519/CVE-2021-30519 b0d2dd219c0842beab126cdbab162ac2/CVE2.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2021-30519/CVE-2021-30519 b0d2dd219c0842beab126cdbab162ac2/CVE2.drawio.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2021-30519/CVE-2021-30519 b0d2dd219c0842beab126cdbab162ac2/InkedCVE1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2021-30519/CVE-2021-30519 b0d2dd219c0842beab126cdbab162ac2/InkedCVE1.jpg -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2021-30519/CVE-2021-30519 b0d2dd219c0842beab126cdbab162ac2/codeaudit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2021-30519/CVE-2021-30519 b0d2dd219c0842beab126cdbab162ac2/codeaudit.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2021-30519/CVE-2021-30519 b0d2dd219c0842beab126cdbab162ac2/cve3.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/1_vulnerability_analysis_whitepapers/cve-2021-30519/CVE-2021-30519 b0d2dd219c0842beab126cdbab162ac2/cve3.drawio.png -------------------------------------------------------------------------------- /1_vulnerability_analysis_whitepapers/cve-2021-30519/README.md: -------------------------------------------------------------------------------- 1 | # CVE-2021-30519 2 | 1. [초록](#1-초록) 3 | 2. [Description: UAF from Payments in Google Chrome](#2-description-uaf-from-payments-in-google-chrome) 4 | 2.1. [Payment Request API](#21-payment-request-api) 5 | 2.2 [정리](#22-정리) 6 | 3. [취약점 재현법](#3-취약점-재현법) 7 | 4. [Root Cause 분석](#4-root-cause-분석) 8 | 4.1. [코멘트와 패치 커밋 메시지 분석](#41-코멘트와-패치-커밋-메시지-분석) 9 | 4.1.1. [코멘트-Root cause 분석 코멘트](#411-코멘트-root-cause-분석-코멘트) 10 | 4.1.2. [코멘트-Variant cases에 관한 문제 제기](#412-코멘트-variant-cases에-관한-문제-제기) 11 | 4.1.3. [패치의 커밋 메시지](#413-패치의-커밋-메시지) 12 | 4.2. [패치 내역 분석](#42-패치-내역-분석) 13 | 4.2.1. [주요 객체 확인](#421-주요-객체-확인) 14 | 4.2.2. [패치 내역 분석](#422-패치-내역-분석) 15 | 5. [결론](#5-결론) 16 | 17 | --- 18 | 19 | # 1. 초록 20 | 21 | 크롬 90.0.4430.212 버전 이전 버전에 모두 해당되는 크롬의 Payments 프로세스에서 발생하는 **UAF** 보안 약점이다. 22 | 23 | OS: Windows 10, Linux 24 | 25 | 1. 사전에 유저의 크롬에 malicious payments app을 설치해 둔 상태에서, 26 | 2. 특수한 HTML 파일을 통해 heap corruption을 일으키고, 27 | 3. 이어서 sandbox escape를 야기할 수 있다는 점에서 취약점이 될 수 있다. 28 | 29 | 30 | # 2. Description: UAF from Payments in Google Chrome 31 | 32 | - Question: What is a "Payments" in Google Chrome? 33 | 34 | ## 2.1. Payment Request API 35 | 36 | 참고자료: [https://developers.google.com/web/updates/2018/06/payment-handler-api](https://developers.google.com/web/updates/2018/06/payment-handler-api) 37 | 38 | 주요 기능: 브라우저 상에서의 결제를 가능케 한다. 39 | 40 | 세부 기능: 41 | 42 | - 결제 인증 정보를 수집한다. 43 | - 소비자로부터 배송&연락처 정보를 수집한다. 44 | - 빠르고 쉬운 유저 인터페이스를 제공한다. 45 | 46 | ## 2.2. 정리 47 | 48 | `Payments` 란, 크롬 웹 브라우저 상에서 결제를 할 수 있도록 해주는 application API이다. 49 | 50 | 51 | # 3. 취약점 재현법 52 | 53 | 취약점 재현 방식에는 쉬운 방식 하나, 조금 어려운 방식 하나, 두 가지가 있다. 54 | 55 | 1. 쉬운 방식: poc2.gif 56 | - Open [https://liquangumax.github.io/lindapay/](https://liquangumax.github.io/lindapay/) , install 57 | - Open [https://liquangumax.github.io/lindapay2/](https://liquangumax.github.io/lindapay2/) , install 58 | - Open [https://maxlgu.github.io/pr/lindapay-github/](https://maxlgu.github.io/pr/lindapay-github/) , buy 59 | - Click "continue" 60 | - Click the link "Directory Picker" 61 | - Click "Open the file dir", click "select" 62 | - Click the back button, boom 63 | 2. 어려운 방식: poc1.gif(이 방법은 이제 안됨) 64 | - open [https://skilful-reserve-239412.appspot.com/static/apps/navigation-tester/](https://skilful-reserve-239412.appspot.com/static/apps/navigation-tester/) and install 65 | - open [https://skilful-reserve-239412.appspot.com/static/apps/max-nonbasiccard/](https://skilful-reserve-239412.appspot.com/static/apps/max-nonbasiccard/) and install 66 | - open [https://maxlgu.github.io/pr/max-nonbasiccard/](https://maxlgu.github.io/pr/max-nonbasiccard/) and click Buy 67 | - Navigate to a Dialog pop up Page and then click the Back button ,Boom 68 | 69 | 어려운 방식은 서버가 닫혀 실행이 불가능했으며, 쉬운 방식으로만 재현할 수 있었다. 70 | 71 | 72 | # 4. Root Cause 분석 73 | 74 | ## 4.1. 코멘트와 패치 커밋 메시지 분석 75 | 76 | ### 4.1.1. 코멘트-Root cause 분석 코멘트 77 | 78 | > From comment 21(번역): 79 | > 80 | > 81 | > `payment_handler_web_flow_view_controller` 의 `dialog_manager_delegate_` 가 `web_contents_modal_dialog_manager` 에 사용되기 전의 어딘가에서 released되는 것 같다. 어떻게 이런 일이 일어날 수 있는지 아직 나로서는 확실히 모르겠다. 82 | > 83 | 84 | > From comment 23(번역): 85 | > 86 | > 87 | > ```powershell 88 | > The browser process crash log: 89 | > (2cc0.2ff4): Access violation - code c0000005 (first chance) 90 | > First chance exceptions are reported before any exception handling. 91 | > This exception may be expected and handled. 92 | > chrome_7ffd672c0000!ovly_debug_event+0x1ea219b: 93 | > 00007ffd`6d510ffb 488b00 mov rax,qword ptr [rax] ds:efefefef`efefefef=???????????????? 94 | > 0:000> kv 95 | > ``` 96 | > 97 | 98 | > From comment 46(번역): 99 | > 100 | > 101 | > `web_contents_modal_dialog_manager.cc` 과 연관된 클래스가 맞다. 102 | > 103 | 104 | > From comment 49(번역): 105 | > 106 | > 107 | > comment 21이 맞는 것 같다. 이런 현상을 제대로 고치려면 `PaymentHandlerWebFlowViewController` 가 `WebContentsModalDialogManager` 에 걸린 `dialog_manager_delegate_` 를, `delegate`[1] 가 삭제되기 전에 먼저 없애도록(unset) 해야 할 것 같다. 108 | > 109 | > ([1]: delegate는 메소드를 다른 메소드로, 파라미터로서 전달할 때 해당 메소드를 정의하는 타입이다.) 110 | > 111 | > 다른 `WebContentsModalDialogManager` 객체에 대해 앞서 설명한 기능이 구현된 사례는 [여기](https://source.chromium.org/chromium/chromium/src/+/master:chrome/browser/ash/login/ui/webui_login_view.cc;drc=3eb027037d29138f58f98356af0295fc17030f0e;l=142) 에서 확인 요망! 112 | > 113 | 114 | ### 4.1.2. 코멘트-Variant cases에 관한 문제 제기 115 | 116 | Comment 49에서 제시한 방법대로 패치하니 괜찮아졌다는 댓글이 달렸고, 뒤이어 Variant cases에 대한 고찰이 제기되었다. 117 | 118 | 119 | > From comment 51(번역): 120 | > 121 | > 122 | > 동일 패치를 적용해야 하는, 즉 `dialog_manager_delegate` 를 없애도록(unset) 고쳐야 하는 다른 `dialog` 객체들이 있나? 우리가 최근에 만든 `SPC` dialog 같은 것들이 좀 불안하다. 123 | > 124 | 125 | > From comment 53(번역): 126 | > 127 | > 128 | > Comment 51번에게. 지금 우리가 고친 이게 `WebContentsModalDialogManager::SetDelegate()` 를 사용하는 유일한 payment 코드이다. 저번에 만든 `SPC` 의 경우 `WebContentsModalDialogManager` 를 쓰지 않기 때문에 괜찮다. 129 | > 130 | 131 | 132 | ### 4.1.3. 패치의 커밋 메시지 133 | 134 | ```cpp 135 | 지금부터 서술될 패치 내용은 아래의 버그에 대한 것입니다: 136 | 137 | [https://chromium.googlesource.com/chromium/src/+/8f6ad3de87d3d7b375f75004b1389604887d31fb](https://chromium.googlesource.com/chromium/src/+/8f6ad3de87d3d7b375f75004b1389604887d31fb) 138 | 139 | commit 140 | 141 | [8f6ad3de87d3d7b375f75004b1389604887d31fb](https://crrev.com/8f6ad3de87d3d7b375f75004b1389604887d31fb) 142 | 143 | (...) 144 | 145 | <데스크탑의 `Payment` UI에서 `WebContentsModalDialogManagerDelegate` 를 삭제하는 패치> 146 | 147 | `payment_request_dialog_view` 의 `controller_map_` 은 148 | 149 | `PaymentHandlerWebFlowViewController` 와 그에 대응되는 `web-view` 를 key-value 쌍으로 저장하는 객체입니다. `payment_request_dialog_view`의 view stack에서 `web-view` 하나가 pop될 때, 이것의 키 쌍(`PaymentHandlerWebFlowViewController`)은 삭제되어야 합니다. 만일 `PaymentHandlerWebFlowViewController`가 `controller_map_` 의 소멸자(destructor) 안에 있는 150 | 151 | `WebContentsModalDialogManager` 의 `delegate` 를 없애지 못할 경우, `web-view` 의 소멸자가 이후 `WebContentsModalDialogManager` 의 `delegate` 에 접근 시도를 하게 됨으로써 Use-After-Free 크래시가 발생하게 됩니다. 152 | 153 | 패치를 위한 커밋은 154 | 155 | `PaymentHandlerWebFlowViewController`의 소멸자가 156 | 157 | `WebContentsModalDialogManager` 의 `delegate` 를 삭제(reset) 하게 함으로써 문제를 해결한 상황입니다. 158 | 159 | ~~[Bug: 1194058](https://bugs.chromium.org/p/chromium/issues/detail?id=1194058)~~ 160 | 161 | Change-Id: Iead54db4b3e682bbccccddf335780c5ceff2e990 162 | 163 | Reviewed-on: 164 | 165 | [https://chromium-review.googlesource.com/c/chromium/src/+/2844509](https://chromium-review.googlesource.com/c/chromium/src/+/2844509) 166 | 167 | (...) 168 | 169 | Cr-Commit-Position: refs/heads/master@{#875868} 170 | 171 | [modify] 172 | 173 | [https://crrev.com/8f6ad3de87d3d7b375f75004b1389604887d31fb/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc](https://crrev.com/8f6ad3de87d3d7b375f75004b1389604887d31fb/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc) 174 | ``` 175 | 176 | ## 4.2. 패치 내역 분석 177 | 178 | ### 4.2.1. 주요 객체 확인 179 | 180 | 먼저 4.1.3절에 명시된 커밋 메시지와 전체적인 코드 오디팅 결과를 토대로 간략하게 관계도를 그려보았다. 181 | 182 | ![<그림 1>-문제 상황이 발생한 코드에 개입된 오브젝트들의 전체적인 관계](CVE-2021-30519%20b0d2dd219c0842beab126cdbab162ac2/InkedCVE1.jpg) 183 | 184 | <그림 1>-문제 상황이 발생한 코드에 개입된 오브젝트들의 전체적인 관계 185 | 186 | 위 구조에 표시된 오브젝트들 중, `controller_map`에 대해서 코드 오디팅을 수행했다. 187 | 188 | - `controller_map_` 189 | 190 | `payment_request_dialog_view.h` 에 선언된 해당 모듈 내부에서 사용되는 `controller_map_` 의 형태를 정의한 코드이다. 191 | 192 | ```cpp 193 | using ControllerMap = 194 | std::map>; //(1) 195 | (...) 196 | }; 197 | ``` 198 | 199 | (1)에서 확인된 바와 같이, `controller_map_` 은 위의 <그림 1> 에 표시된 대로 <`View*` : `PaymentRequestSheetController`> 가 쌍으로 짝지어져 있는 c++의 map 객체로 선언되어 있음을 알 수 있다. `PaymentRequestSheetController` 는 `PaymentHandlerWebFlowViewController` 로 사용되며, 앞으로는 `controller`로 칭한다. 정리하자면, `controller` 객체의 key는 `views::View*` 타입의 객체이며, 이에 대해 `controller` 를 이용해 접근할 수 있다. 200 | 201 | - `controller_map_` 을 이용해 대응되는 `View*` 타입 객체에 접근하는 과정 202 | - `CreateViewAndInstallController` 에 대응 key를 찾고 싶은 `controller` 객체를 인자로 넘긴다. 203 | 204 | ```cpp 205 | std::unique_ptr CreateViewAndInstallController( 206 | std::unique_ptr controller, 207 | payments::ControllerMap* map) { 208 | std::unique_ptr view = controller->CreateView(); 209 | (*map)[view.get()] = std::move(controller); 210 | return view; // (1) 211 | } 212 | ``` 213 | 214 | - `PaymentHandlerWebFlowViewController` 와 `WebContentsModalDialogManager` 의 관계: 215 | 216 | 아래 `PaymentHandlerWebFlowViewController` 를 보면, 처음 웹 페이지를 콜하여 페이지에 보여줄 때 `WebContentsModalDialogManager` 를 웹페이지 상에서 결제하는 `Payment API` 의 `Handler` 격으로 사용한다는 것을 알 수 있다. 즉, 이 둘은 생애 주기를 같이 하게 될 수밖에 없다. 217 | 218 | ```cpp 219 | void PaymentHandlerWebFlowViewController::FillContentView( 220 | views::View* content_view) { 221 | // The first time this is called, also create and add the header/content 222 | // separator container children. This must be done before calling 223 | // LoadInitialURL() below so these will be set up before that calls back to 224 | // LoadProgressChanged(), and it can't be done in the constructor since the 225 | // container doesn't exist yet. 226 | (...) 227 | PaymentHandlerNavigationThrottle::MarkPaymentHandlerWebContents( 228 | web_contents()); 229 | web_contents()->SetDelegate(this); 230 | (...) 231 | // Enable modal dialogs for web-based payment handlers. 232 | dialog_manager_delegate_.SetWebContents(web_contents()); 233 | web_modal::WebContentsModalDialogManager::CreateForWebContents( 234 | web_contents()); 235 | web_modal::WebContentsModalDialogManager::FromWebContents(web_contents()) 236 | ->SetDelegate(&dialog_manager_delegate_); 237 | (...) 238 | } 239 | ``` 240 | 241 | 242 | ### 4.2.2. 패치 내역 분석 243 | 244 | 이 절에서는 패치가 어떻게 이루어졌는지, 그것이 끼치는 영향은 어떠한지 분석한다. 245 | 246 | 분석에 앞서, 이해의 용이함을 위해 이 절의 결과로 산출된 정상적으로 처리되는 상황의 도식과 문제가 발생할 때의 도식을 명시하겠다. 247 | 248 | ![<그림 2>-정상적으로 처리될 때의 도식](CVE-2021-30519%20b0d2dd219c0842beab126cdbab162ac2/CVE2.drawio.png) 249 | 250 | <그림 2>-정상적으로 처리될 때의 도식 251 | 252 | ![<그림 3>-이미 삭제된 web-view 객체에 대한 후처리가 이루어지지 않았을 때의 도식(UAF 발생)](CVE-2021-30519%20b0d2dd219c0842beab126cdbab162ac2/cve3.drawio.png) 253 | 254 | <그림 3>-이미 삭제된 web-view 객체에 대한 후처리가 이루어지지 않았을 때의 도식(UAF 발생) 255 | 256 | - chrome/browser/ui/views/payments/view_stack.cc 257 | 258 | `Payment_request_dialog_view` 에서 view_stack_ 을 pop 할 때, 어떤 일이 일어나는지 정확히 알아보기 위해 `view_stack_→Pop()` 의 코드를 오디팅했다. 259 | 260 | ```cpp 261 | void ViewStack::Pop(bool animate) { 262 | DCHECK_LT(1u, 263 | GetSize()); // There must be at least one view left after popping. 264 | 265 | // Set the second-to-last view as visible, since it is about to be revealed 266 | // when the last view animates out. 267 | stack_[GetSize() - 2]->SetVisible(true); 268 | 269 | if (animate) { 270 | gfx::Rect destination = bounds(); 271 | destination.set_origin(gfx::Point(width(), 0)); 272 | slide_out_animator_->AnimateViewTo(stack_.back(), destination); 273 | } else { 274 | RemoveChildViewT(stack_.back()); //(1) 275 | stack_.pop_back(); 276 | } 277 | } 278 | ``` 279 | 280 | (1): `RemoveChildViewT` 는 결론적으로 `RemoveChildeView` 를 수행하는데, 이는 통상적인 `View::~View()` 내부에서 수행하는 `RemoveChildView` 와 동일한 기능을 한다. 즉, `ViewStack::Pop` 에서 `RemoveChildViewT` 가 call된다는 것은 `View` 타입 객체의 소멸자가 수행되는 것과 비슷하다고 볼 수 있다. 또한, `stack_` 는 `Views::view*` 타입에 대한 벡터이므로, `View` 타입 객체가 삭제됨과 동시에 `Views::view*`, 즉 해당 `View` 와 매핑된 `controller` 도 소멸된다. 281 | 282 | 283 | 현재까지의 코드 오디팅 결과를 정리하면, <그림 2> 에 명시된 대로 `view_stack_` 에 저장되어 있던 `View` 타입 객체가 `Pop` 되었을 때 해당 객체에 대한 소멸이 진행되며, 해당 `View` 와 매핑된 `controller` 도 소멸된다는 결론을 낼 수 있다. 284 | 285 | - chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc 286 | 287 | `View` 객체에 대한 Pop이 진행될 때 어떤 결과가 나오는지 확인했으므로, 이제는 패치된 코드가 각각 무엇을 의미하는지 확인한다. 288 | 289 | ```cpp 290 | // 변경 전 코드 291 | PaymentHandlerWebFlowViewController::~PaymentHandlerWebFlowViewController() { 292 | state()->OnPaymentAppWindowClosed(); 293 | } 294 | 295 | // 변경 후 코드 296 | PaymentHandlerWebFlowViewController::~PaymentHandlerWebFlowViewController() { 297 | if (web_contents()) { 298 | auto* manager = web_modal::WebContentsModalDialogManager::FromWebContents( 299 | web_contents()); 300 | if (manager) 301 | manager->SetDelegate(nullptr); 302 | } 303 | state()->OnPaymentAppWindowClosed(); 304 | } 305 | ``` 306 | 307 | `PaymentHandlerWebFlowViewController` 의 소멸자가 패치되었음을 알 수 있다. 308 | 309 | 앞서 서술했던 `ViewStack::Pop` 의 소스 코드 중 `RemoveChildViewT(stack_.back());` 의 실행 흐름을 감안하여 패치 전의 코드 흐름은 310 | 311 | 1. `stack_.back()` 콜백 함수가 먼저 실행됨 312 | 2. 따라서 `Controller` 의 소멸자인 `~PaymentHandlerWebFlowViewController` 가 실행됨, `Controller`를 관장하는 핸들러 격의 객체인 `WebContentsModalDialogManagerDelegate` 는 관리되지 않고 그대로 남음. 313 | 3. 이후 `RemoveChildViewT` 의 코드부가 실행되며 `View*` 타입 객체가 소멸됨 314 | 4. `View*` 타입 객체가 소멸되는 와중에 다시 `WebContentsModalDialogManagerDelegate` 에 access 하려 시도함 315 | 5. Use-After-Free 발생 316 | 317 | 이었음을 알 수 있다. 318 | 319 | 패치 내역과 그것의 영향을 더 자세히 알아보기 위해서 코드의 콜 관계를 분석해 아래 <그림 4>를 그렸다. 320 | 321 | ![<그림 4>-패치 후 코드의 콜 관계](CVE-2021-30519%20b0d2dd219c0842beab126cdbab162ac2/codeaudit.png) 322 | 323 | <그림 4>-패치 후 코드의 콜 관계 324 | 325 | 즉, 현재 페이지의 웹 컨텐츠가 존재할 경우 그 웹 컨텐츠와 연관된 key를 관리하는 포인터를 주게 되는데, 그 포인터를 패치 상의 `auto* manager` 가 저장하며, 현재 페이지의 웹 컨텐츠는 존재할 수밖에 없기 때문에 `if(manager)` 의 branch 안으로 코드 흐름이 들어가게 된다. 326 | 327 | 현재 `manager` 가 저장한 웹 컨텐츠의 key를 관리하는 포인터는 `WebContentsModalDialogManager` 를 가리키는 포인터가 된다.[2] `manager->SetDelegate(nullptr)` 을 통해 `WebContentsModalDialogManagerDelegate` 가 `nullptr` 로 설정되므로[3] 올바르게 reset 됨을 알 수 있다. 328 | 329 | 따라서, 패치 후의 코드 흐름은 이렇게 된다: 330 | 331 | 1. `stack_.back()` 콜백 함수가 먼저 실행됨 332 | 2. 따라서 `Controller` 의 소멸자인 `~PaymentHandlerWebFlowViewController` 가 실행됨.(`PaymentHandlerWebFlowViewController` 소멸 완료, 즉 이를 관장하는 `WebContentsModalDialogManagerDelegate` 또한 reset) 333 | 3. 이후 `RemoveChildViewT` 의 코드부가 실행되며 `View*` 타입 객체가 소멸됨 334 | 4. `View*` 타입 객체가 소멸되는 와중에 다시 `WebContentsModalDialogManagerDelegate` 에 access 하려 시도함 335 | 5. 그러나 이미 제대로 reset 된 객체에 접근을 시도하는 것이므로, 이미 상정된 오류 범위 내에 들어 오류 없이 프로그램 실행. 336 | 337 | ([2]: `components/web_modal/web_contents_modal_dialog_manager.cc` 소스 코드를 오디팅하면 `WebContentsModalDialogManager`는 `content::WebContents` 타입을 `using` 커맨드로 사용하고 있음을 알 수 있다. ) 338 | 339 | ([3]: `WebContentsModalDialogManager::SetDelegate` 코드는 아래와 같다. 340 | 341 | ```cpp 342 | void WebContentsModalDialogManager::SetDelegate( 343 | WebContentsModalDialogManagerDelegate* d) { 344 | delegate_ = d;//raw_ptr delegate_ 345 | 346 | for (const auto& dialog : child_dialogs_) { 347 | // Delegate can be null on Views/Win32 during tab drag. 348 | dialog.manager->HostChanged(d ? d->GetWebContentsModalDialogHost() 349 | : nullptr); 350 | } 351 | } 352 | ``` 353 | 354 | ) 355 | 356 | 357 | # 5. 결론 358 | 359 | 이 취약점은 특정 개체와 생애주기가 긴밀히 연관된 다른 객체의 생애주기를 신경쓰지 않아 발생한 취약점이다. 크롬에서 발생하는 주요 취약점 중 Object Lifetime으로 인해 발생한 문제가 상당히 높은 비율을 차지하고 있다는 사실을 감안했을 때 그닥 놀라운 사실은 아니다. 360 | 361 | 차후 이와 비슷한 종류의 취약점(Object Lifetime)을, 동일한 어택 벡터(Payments)에서 발생시킬 수 있을까 고민해 보았으나, 4-1-2에 언급된 대로 동일 객체(`WebContentsModalDialogManager` 혹은 그것의 delegate)로 인해 발생하는 variant cases는 없는 것에 가깝다는 점으로 인해 퍼저를 위한 유력 어택 벡터 선정 과정에서 탈락하게 되었다. 개인적으로 전자결제 과정에서 웹 브라우저 프로세스의 허점으로 인해 발생하는 취약점에 관심이 많았는지라 아쉬운 결말이었다. 362 | 363 | 글쓴이: 이윤희(toubva@gmail.com) 364 | -------------------------------------------------------------------------------- /2_fuzzer_development/Domino/Domino_img/Untitled 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Domino/Domino_img/Untitled 1.png -------------------------------------------------------------------------------- /2_fuzzer_development/Domino/Domino_img/Untitled 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Domino/Domino_img/Untitled 2.png -------------------------------------------------------------------------------- /2_fuzzer_development/Domino/Domino_img/Untitled 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Domino/Domino_img/Untitled 3.png -------------------------------------------------------------------------------- /2_fuzzer_development/Domino/Domino_img/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Domino/Domino_img/Untitled.png -------------------------------------------------------------------------------- /2_fuzzer_development/Domino/README.md: -------------------------------------------------------------------------------- 1 | # Domino.md 2 | 3 | # 목차 4 | 5 | 1. [퍼징 아이디어](#1-퍼징-아이디어) 6 | 1.1. [퍼징 아이디어](#11-퍼징-아이디어) 7 | 2. [Domino 퍼저](#2-Domino-퍼저) 8 | 2.1.[퍼징 방식](#21-퍼징-방식) 9 | 2.2.[퍼징 테스트](#22-퍼징-테스트) 10 | 3. [뮤테이션 전략](#3-뮤테이션-전략) 11 | 3.1. [Domato](#31-Domato) 12 | 3.2. [Template-Based Fuzzing](#32-Template-Based-Fuzzing) 13 | 4. [작동 화면](#4-작동-화면) 14 | 5. [결론](#5-결론) 15 | 5.1. [결과](#51-결과) 16 | 5.2. [개선 점](#52-개선-점) 17 | --- 18 | # 1. 퍼징 아이디어 19 | 20 | ## 1.1. 퍼징 아이디어 21 | 22 | Domino는 [Domato Fuzzer](https://github.com/googleprojectzero/domato)(링크)에 Template-Based Fuzzing 방식을 추가로 적용하여 만든 DOM Fuzzer이다. 우리가 만들었던 Fuzzer들 중 가장 많은 크래시 로그를 찾아냈던 Fuzzer이기도 하다. 23 | 24 | 방식을 요약하면 [source.chromium.org](http://source.chromium.org/)에서 carsh poc들을 찾아 template으로 만들고 해당 template들을 랜덤으로 뽑아 mutation 후 test를 하는 방식이다. 25 | 26 | Fuzzing 환경은 우분투와 윈도우 모두 지원한다. 27 | 28 | - [GIthub - Domino_Fuzzer](https://github.com/BOB-Jour/Domino_Fuzzer) 29 | 30 | # 2. Domino 퍼저 31 | 32 | ## 2.1. 퍼징 방식 33 | 34 | Domino의 퍼징 방식은 normal 모드와 iframe 모드 두 가지로 구분되고 사용자가 선택할 수 있다. 35 | 36 | ### 2.1.1. normal 모드 37 | 38 | normal 모드는 사용자가 템플릿들을 미리 만들어 `~\Domino\Test_Templates` 디렉토리에 넣어두면 해당 템플릿들 중 하나를 랜덤으로 골라 Domato 태그들이 있는 위치에 코드를 생성해 치환하여 test.html를 생성하고 테스트를 바로 진행하는 모드이다. 39 | 40 | ![normal 모드 Fuzzing Test 화면](./Domino_img/Untitled.png) 41 | 42 | normal 모드 Fuzzing Test 화면 43 | 44 | 위는 normal 모드로 생성한 test.html을 실행시킨 모습이다. 45 | 46 | test.html의 코드는 `~\Domino\templates\test.html` 에서 확인할 수 있다. 47 | 48 | ### 2.1.2 iframe 모드 49 | 50 | iframe 모드는 iframe 내에 Domato로 Generate된 템플릿 코드를 실행한 후 실행 도중 강제로 iframe을 `iframe.remove()`로 제거하면서 발생하는 사이트 이펙트 취약점을 발견해 내는 것을 목표로 만든 모드이다. 실제로 iframe을 실행 도중 중간에 제거하는 것으로 19년, 20년도에 크래시들이 나왔었다. 51 | 52 | 기본적으로 다음의 템플릿을 사용한다. 53 | 54 | ```python 55 | 56 | 71 | 72 | 73 | 74 | ``` 75 | 76 | - iframe 창 크기 또한 크래시에 영향을 줄 수 있기 때문에 위의 템플릿을 사용해서 랜덤으로 iframe의 `width`와 `height`를 계속해서 설정하여 띄워주도록 했다. 77 | - 또한 `setTimeout`을 사용하여 랜덤한 시간 동안 iframe을 실행하고 도중에 `iframe.remove()`를 호출하여 iframe을 제거하도록 하였다. 78 | 79 | 아래는 iframe모드로 생성된 iframe_test.html을 실행한 모습이다. 80 | 81 | ![Untitled](./Domino_img/Untitled%201.png) 82 | 83 | ### 2.2. 퍼징 테스트 84 | 85 | Domino의 퍼징 테스트는 아래와 같은 방식으로 진행된다. 86 | 87 | ![퍼징 테스트](./Domino_img/Untitled%202.png) 88 | 89 | ### 2.2.1. 구성 90 | 91 | - Domino_server.py (웹 서버) 92 | - Domino_server는 클라이언트(크롬)로부터 접속 요청이 들어오면 testcase를 Domino를 이용하여 생성하고 생성된 testcase.html을 보내주는 역할을 한다. 93 | - run_fuzz.py (모니터) 94 | - 모니터는 실행한 크롬을 계속 모니터링하면서 크래시가 발생한 경우 해당 testcase와 로그를 수집한다. 95 | - 또한 모니터는 크롬이 장시간 실행되는 경우 내부적으로 메모리 구조가 일반적인 경우와 조금 달라질 수 있기 때문에 5분 단위로 강제 종료 후 다시 실행시킨다. 96 | 97 | ### 2.2.2. 흐름 98 | 99 | 1. 웹 서버 실행 100 | 2. 모니터 실행 101 | - 크롬 실행 102 | 3. 크롬이 웹 서버에게 testcase 요청 103 | 4. 요청을 받은 웹 서버는 testcase 생성 후 해당 testcase 전송 104 | 5. 크롬은 testcase 실행 105 | 6. 만약 4 중 크래시가 발생한 경우 모니터가 testcase, log 수집 및 크롬 종료 후 재실행 106 | - 크래시가 발생하지 않았다면 3 ~ 5 반복 107 | 7. 크래시 없이 실행 중 5분이 경과하면 크롬 종료 후 재실행 108 | - 재실행 후 3 ~ 5 반복 109 | 110 | ### 2.2.3. 공통 템플릿 111 | 112 | 흐름의 3번 과정을 위해서는 크롬이 스스로 테스팅을 끝낸 후 웹 서버에게 요청을 해야 할 필요가 있다. 이 과정을 위해 normal 모드와 iframe 모드의 템플릿에 추가하는 공통 방식으로 아래와 같은 방법을 사용하였다. 113 | 114 | - normal 모드 115 | 116 | ```html 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 126 | RELOAD_HTML 127 | 130 | 131 | 132 | 133 | 134 | ``` 135 | 136 | normal 모드에서는 위와 같이 testcase html 아래에 서버에 요청을 위한 html 코드가 추가로 붙게 된다. 브라우저는 html을 파싱, 실행할 때 html 태그가 두개가 존재하면 상단의 html을 먼저 완료 한 후 그 다음이 html을 실행하는 특성이 있는데 이점을 이용하여 상단의 html이 파싱, 실행되며 테스팅이 끝나면 이어서 아래의 새로고침 html의 내용이 실행되면서 서버에 새로고침을 자동으로 요청하게 되는 방식이다. 137 | 138 | 이 방식을 사용하면 동기식으로 Fuzzing이 가능하다는 장점이 있다. 139 | 140 | - iframe 모드 141 | 142 | ```html 143 | 144 | 145 | Refresh_HTML 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 168 | 169 | 170 | 171 | ``` 172 | 173 | iframe 모드에서는 위처럼 `iframe.remove()` 다음에 바로 이어서 새로고침을 하도록 되어 있다. 174 | 175 | 특이한건 normal 모드와는 다르게 상단에 refresh를 하는 html 코드가 있다는 것인데 이는 가끔씩 실행 후 서버에 재요청을 하지 않는 경우가 있어 이런 경우를 대비해 3초 후 강제로 새로고침을 하도록 하였다. 176 | 177 | 178 | ### 2.2.4. testcase 생성 제한 179 | 180 | 위 공통 템플릿을 보면 서버에 접속을 하는 url이 `http://127.0.0.1:8080/flag?test` 인 것을 볼 수 있다. 보면 `flag`에 요청하고 GET 파라미터로 `test` 또는 `iframe_test`를 붙여주는 것을 볼 수 있는데 이는 testcase가 크롬에서 실행되고 있는 도중 `url()` 과 같은 것을 사용하여 웹 서버에 접근 요청 → testcase 생성(덮어씌워짐)이 되는 경우를 방지하기 위해 고안한 방식이다. 이런 일이 발생할 경우 크래시가 발생하였을 때 제대로된 testcase가 저장이 되지 않는 문제가 발생할 수 있기 때문에 고안하고 적용하게 되었다. 181 | 182 | flag를 거치지 않고 testcase 생성 요청이 오는 경우 웹 서버는 잘못된 요청이라고 판단하고 testcase를 생성하지 않는다. 183 | 184 | # 3. 뮤테이션 전략 185 | 186 | ### 3.1. Domato 187 | 188 | Google Project Zero팀에서 만든 DOM Fuzzer이다. DOM Fuzzer는 이름대로 브라우저의 DOM(Document Object Model)을 대상으로 Mutation 또는 Generate을 진행하는 것을 말한다. Domato는 크게 generator.py와 grammer.py, *.txt 파일들로 구분되는데 각각 하는 일은 다음과 같다. 189 | 190 | - generator.py : testcase를 생성합니다. (main 코드) 191 | - grammer.py : 문법적으로 유효한 코드를 생성한다. 192 | - *.txt 파일 : txt파일에는 html, js, css 등 각각 해당하는 언어의 문법이 모든 경우의 수에 맞춰 만들어져 있다. 193 | 194 | ```bash 195 | #XPathEvaluator 196 | = .createExpression(""); 197 | = .createExpression("",); 198 | = .createNSResolver(); 199 | = .evaluate("",); 200 | = .evaluate("",,); 201 | = .evaluate("",,,); 202 | = .evaluate("",,,,); 203 | = .evaluate("",document); 204 | = .evaluate("",document,null,XPathResult.ANY_TYPE,); 205 | ``` 206 | 207 | 위 예시처럼 되어있고 위 예시에 보이는 <> 태그들은 실제로 생성될 때 올바른 값들로 치환되며 제대로된 코드가 생성되게 된다. 208 | 209 | 210 | 기존의 Domato generator.py는 템플릿을 받지 않고 바로 랜덤한 코드를 생성해주기 때문에 우리는 Domato를 Template Based Fuzzing에 사용하기 위해 가장 먼저 generator.py를 클래스화 하였다. 211 | 212 | - 클래스화 된 Domato - generator.py 사용법 213 | 214 | ```python 215 | // ~/domato/generator.py 216 | if __name__ == '__main__': 217 | # test 218 | domato = Domato() 219 | with open('./template.html', 'r') as fp: 220 | temp = fp.read() 221 | testcase = domato.generator(temp) # testcase 생성 222 | ``` 223 | 224 | 225 | ### 3.2. Template-Based Fuzzing 226 | 227 | Template-Based Fuzzing은 취약점이 발생할 가능성이 높은 몇 개의 템플릿을 미리 만들어두고 해당 템플릿의 값을 추가 혹은 변경해가며 Fuzzing을 진행하는 방식을 말한다. 228 | 229 | 구글은 크래시를 발생시켰던 html코드들을 [source.chromium.org](http://source.chromium.org/)에 저장해놓고 있는데 그 것들을 ‘Domato 제너레이트가 가능한 형태로 템플릿화 하여 실행시켜본다면 비슷한 곳에서 발생하는 취약점을 찾을 수 있지 않을까?’라는 생각으로 적용해본 방식이다. 230 | 231 | - [source.chromium.org](http://source.chromium.org/)에 접속하여 `file:html$ third_party/blink/web_tests crash` 로 검색하면 크래시를 발생시켰던 html 코드들이 나온다. 232 | 233 | ### 3.2.1. 템플릿 예시 234 | 235 | - 우리가 만든 템플릿 중 하나는 아래와 같다. 236 | 237 | ```html 238 | 239 | 240 | 241 | 242 | 250 | 279 | 280 | 281 | 282 | 283 | ``` 284 | 285 | - 위 템플릿을 보면 중간중간에 , , 와 같은 태그들이 보일텐데 해당 태그들은 Domato에서 각 부분에 대한 랜덤한 코드를 생성한 후 치환하는 위치는 표시해두는 일종의 키워드이다. 따라서 사용자가 템플릿을 만든 후 원하는 위치에 위의 태그들을 심어두면 그 위치에 해당하는 코드들이 생성된다. 286 | 287 | # 4. 작동 화면 288 | 289 | ![작동화면](./Domino_img/Untitled%203.png) 290 | 291 | - 왼쪽은 chromium, 오른쪽 상단은 대시보드, 오른쪽 하단은 Domino_server의 로그이다. 292 | - 대시보드 표시 목록 293 | - StartTime : 퍼징 시작 시간 294 | - RunTime : 실행 시간 295 | - Crash : 수집된 Crash 로그 개수 296 | - Lastest Crash : 마지막으로 수집된 Crash 로그 개수 297 | - Dcheck Failed : 수집된 Dcheck 로그 개수 298 | - TestCase : 생성된 모든 TestCase 개수 299 | - Chrome Count : 재시작된 chromium 개수 300 | - Chrome PID : 현재 실행중인 choromium의 PID 301 | 302 | # 5. 결론 303 | 304 | ## 5.1. 결과 305 | 306 | Domino는 굉장히 많은 DCHECK 로그들을 찾아내 주었고 몇 개의 ASAN 로그도 찾아내 주었지만 307 | 매우 아쉽게도 대부분 악용 가능성이 없는 것들이었다. 퍼징 도중 중간에 한번 heap-of-poison 로그도 떴었지만, 재현이 불가능한 크래시 로그여서 제보를 할 수는 없었다... 308 | 309 | ## 5.2. 개선 점 310 | 311 | 이유는 잘 모르겠지만 현재 크래시 로그를 수집할 때 최대 5분의 시간이 걸린다. 때문에 크래시 발생 후 크롬이 재실행되며 Fuzzing이 다시 시작하기까지 불필요한 딜레이가 발생한다. 이 문제는 Domino에서 사용한 Fuzzing Test 방식을 적용한 우리가 만든 모든 Fuzzer들(Glitch, Pleaser)에서 발생하고 있다. 이 점에 대한 해결 방법은 후에 우리의 Fuzzing Test방식을 사용하는 사람들에게 맡기겠다. 312 | -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch-TTF와 멀티미디어 파일에 관하여.md: -------------------------------------------------------------------------------- 1 | # Glitch-TTF와 멀티미디어 파일에 관하여 2 | 1. [TTF](#1-TTF) 3 | 1.1. [TTF 파일의 구조](#11-TTF-파일의-구조) 4 | 1.2. [TTF 태그에 관한 세부 내용](#12-TTF-태그에-관한-세부-내용) 5 | 1.2.1. [\](#121-sbix) 6 | --- 7 | 8 | # 1. TTF 9 | 10 | ## 1.1. TTF 파일의 구조 11 | 12 | - 여러 테이블들이 연이어 나열된 구조. 이 테이블을 ‘태그’ 라고 부른다. 13 | - 첫 번째 테이블, 즉 TTF 파일의 시작 부분은 언제나 `Font directory` 라는 태그이다. 해당 태그의 구조는 아래 사진과 같다. 14 | - `offset_table`은 현재 TTF 파일의 메타 데이터를 저장하며, `directory_table`은 `Font directory` 태그 이후로 이어질 태그들의 오프셋과 길이 등의 태그 관련 메타 데이터를 저장한다. 이 구조는 <그림 1> 에 명시적으로 표현되어 있으며, 실제 TTF 파일 바이너리 내에서 표현되는 형태는 <그림 2>와 같다. 15 | 16 | ![<그림 1>](Glitch_img/Glitch-TTF-Image/Untitled.png) 17 | 18 | <그림 1> 19 | 20 | ![<그림 2>-빨간 박스가 `offset_table`, 파란 박스가 `directory_table` 이다.](Glitch_img/Glitch-TTF-Image/Untitled%201.png) 21 | 22 | <그림 2>-빨간 박스가 `offset_table`, 파란 박스가 `directory_table` 이다. 23 | 24 | - 아래 <그림 3>에서 볼 수 있듯, `directory_table` 은 현 TTF 파일에 포함된 태그들에 대한 정보를 나열하고 있다. 25 | 26 | ![<그림 3>- `directory_table`의 구성](Glitch_img/Glitch-TTF-Image/Untitled%202.png) 27 | 28 | <그림 3>- `directory_table`의 구성 29 | 30 | - `Font directory` 태그 이후로는, `directory_table` 에 명시된 태그들의 필드가 쭉 나열된다. TTF 파일에 필수적으로 포함되어야 하는 태그의 종류는 아래 <표 1>과 같다. 31 | 32 | ![<표 1>-TTF 파일에 필수로 포함되는 태그들](Glitch_img/Glitch-TTF-Image/Untitled%203.png) 33 | 34 | <표 1>-TTF 파일에 필수로 포함되는 태그들 35 | 36 | 37 | ## 1.2. TTF 태그에 관한 세부 내용 38 | 39 | Glitch 퍼저를 개발하기 위해 모인 팀인 우리(홍택균, 이윤희, 이주협)는 TTF에 포함될 수 있는 모든 태그에 대해 각자 조사를 진행했고, 조사한 내용을 문서 형태로 정리하여 함께 공유했다. 그러나, 이미 존재하는 TTF 태그에 대한 개발자 문서를 단순히 공부하여 정리한 것이니만큼 이 절에 팀원이 작성했던 문서를 모두 표현하는 것은 비효율적이라 판단했다. 40 | 41 | 따라서 TTF 폰트 파일에서 멀티미디어 파일을 사용할 때 관여하는 `sbix` 태그에 관한 내용만을 게시하고, 다른 태그들의 내용은 참고했던 문서를 명시함으로써 이 절은 마무리하려 한다. 42 | 43 | TTF 파일에 포함될 수 있는 모든 태그들에 대한 개발자 문서는 [여기](https://developer.apple.com/fonts/TrueType-Reference-Manual/)로 가면 확인할 수 있다. 44 | 45 | ### 1.2.1. \ 46 | 47 | - png, jpec, tiff 등의 포맷을 가진 이미지 파일들의 bitmap data에 TTF가 접근할 수 있게 해주는 테이블이다. 48 | - 태그의 헤더는 다음의 <그림 4>와 같다. 49 | 50 | ![<그림 4>](Glitch_img/Glitch-TTF-Image/Untitled%204.png) 51 | 52 | <그림 4> 53 | 54 | 위 <그림 4>에서 등장하는 strike란, 각 glyph[1]의 정보를 모두 가지고 있는 구조체이다. 즉 `strikeOffset[numStrikes]` 는 Bitmap strike의 오프셋을 알려주고 있다. `StrikeOffset[numStrikes]` 필드에 명시된 오프셋을 따라가 확인한 Bitmap strike의 구조는 아래 <그림 5>와 같다. 55 | 56 | ([1]: glyph는 폰트에서 표현 가능한 하나의 글자를 의미한다) 57 | 58 | ![<그림 5>](Glitch_img/Glitch-TTF-Image/Untitled%205.png) 59 | 60 | <그림 5> 61 | 62 | ![<그림 6>-Bitmap strike의 구성과 설명](Glitch_img/Glitch-TTF-Image/Untitled%206.png) 63 | 64 | <그림 6>-Bitmap strike의 구성과 설명 65 | 66 | <그림 5>의 첫 번째 초록색이 `ppem`, 두 번째 초록색이 `resolution`, 이하 노란색, 주황색, 파란색, 회색 영역이 각 glyph 구조체가 Bitmap strike를 기준으로 갖는 오프셋을 의미한다. 67 | 68 | 특정 이미지를 한 개의 glyph로 사용하고 싶다면, 해당 이미지에 대한 비트맵 데이터는 대응되는 glyph의 데이터로 변형되어 TTF 파일 안에 저장되어야 한다. 따라서, glyph의 데이터는 `glyph data record` 구조체로 정돈되어 TTF 파일 내에서 표현되게 된다. 69 | 70 | ![<그림 7>-`glyph data record` 구조체의 구성과 설명](Glitch_img/Glitch-TTF-Image/Untitled%207.png) 71 | 72 | <그림 7>-`glyph data record` 구조체의 구성과 설명 73 | 74 | 위 구조체의 `graphicType` 를 보면, 현재는 오직 jpg, pdf, png, tiff 만이 정의되어 있다고 명시하고 있다. 그러나, 코드 오디팅 결과, jpg, pdf, tiff과 관련한 처리 기제는 FreeType 라이브러리에서 미구현된 상태였다. 실제로 사용 중인 이미지 포맷은 png 뿐인 것이다. png 파일에 관한 자세한 설명은 이 [문서](https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=gnsehfvlr&logNo=220733132744)에서 찾아볼 수 있다. 75 | -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 1.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 2.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 3.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 4.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 5.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 6.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled 7.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Glitch-TTF-Image/Untitled.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Structure.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/Untitled.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/glitch_structure.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/glitch_structure.drawio.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/Glitch_img/unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Glitch/Glitch_img/unknown.png -------------------------------------------------------------------------------- /2_fuzzer_development/Glitch/README.md: -------------------------------------------------------------------------------- 1 | # Glitch.md 2 | # 목차 3 | 1. [퍼징 아이디어](#1-퍼징-아이디어) 4 | 1.1. [배경이 된 취약점:CVE-2020-15999](#11-배경이-된-취약점-cve-2020-15999) 5 | 1.1.1. [취약점 유형](#111-취약점-유형) 6 | 1.1.2. [FreeType이란?](#112-freeType이란) 7 | 1.1.3. [TTF 헤더 구조](#113-ttf-헤더-구조) 8 | 1.1.4. [PNG 헤더 구조](#114-png-헤더-구조) 9 | 1.1.5. [근본 원인(Root Cause)](#115-근본-원인root-cause) 10 | 1.1.6. [어택 벡터 선정](#116-어택-벡터-선정) 11 | 1.2. [퍼징 아이디어 선정](#12-퍼징-아이디어-선정) 12 | 2. [Glitch 퍼저](#2-glitch-퍼저) 13 | 2.1.[퍼저 구조](#21-퍼저-구조) 14 | 2.2.[Glitch 퍼저의 실행 단계](#22-glitch-퍼저의-실행-단계) 15 | 3. [뮤테이션 전략](#3-뮤테이션-전략) 16 | 3.1. [뮤테이터 구조](#31-뮤테이터-구조) 17 | 3.2. [뮤테이터 실행 과정](#32-뮤테이터-실행-과정) 18 | 4. [실행 결과](#4-실행-결과) 19 | 5. [결론](#5-결론) 20 | 5.1. [결과](#51-결과) 21 | 5.2. [보완책](#52-보완책) 22 | 23 | --- 24 | 25 | # 1. 퍼징 아이디어 26 | 27 | ## 1.1. 배경이 된 취약점: CVE-2020-15999 28 | 29 | 이 절에서는 `CVE-2020-15999`에 관해 간략히 설명하고, 이를 이해하기 위한 정보 또한 간략히 서술한다. 자세한 원데이 분석 보고서는 [여기](../../1_vulnerability_analysis_whitepapers/cve-2020-15999/README.md) 30 | 31 | ### 1.1.1. **취약점 유형** 32 | 33 | > `==169196==ERROR: AddressSanitizer: heap-buffer-overflow` 34 | > 35 | 36 | **FreeType** 에서 발생한 **Heap Overflow** 취약점이다. 37 | 38 | ### 1.1.2. **FreeType이란?** 39 | 40 | **텍스트를 비트맵으로 렌더링** 하는 데 사용되는 **소프트웨어 개발 라이브러리** . 기타 **글꼴 관련 작업** 을 지원한다. 자세한 내용은 [여기](https://freetype.org/freetype2/docs/index.html) 41 | 42 | ### 1.1.3. **TTF 헤더 구조** 43 | 44 | TTF (True Type Font) 파일은 일련의 태그[^1]들이 나열되어 있는 구조를 취하고 있다. 특정 태그가 TTF 파일에 포함되어 있을 경우, TTF 파일의 헤더에 그 이름과 간략한 정보를 명시한 후 TTF 파일의 바디에 태그의 자세한 데이터를 기록해 두는 것을 원칙으로 한다. 45 | 46 | [^1]: 태그: 특정 기준으로 분류되는 데이터를 테이블 형식으로 가지고 있는 구조체. 47 | 48 | 아래 표는 TTF에 필수적으로 포함되어야 하는 9개 태그들이다. 49 | 50 | | 태그 이름 | 테이블 | 51 | | --- | --- | 52 | | cmap | 문자 코드를 glyph 인덱스에 매핑 | 53 | | glyf | glyph 데이터 | 54 | | head | font 헤더 | 55 | | hhea | 가로에 대한 헤더 | 56 | | hmtx | 가로에 대한 행렬 | 57 | | loca | glyph 데이터 테이블의 시작 부분 부터 glyph까지의 오프셋을 저장 | 58 | | maxp | font에 대한 메모리 요구 사항을 설정 | 59 | | name | 다국어 문자열을 OpenType font와 연결 | 60 | | post | PostScript 프린터에서 TrueType 또는 OpenType font를 사용하는 데 필요한 추가 정보 | 61 | 62 | <표 1-TTF 태그 종류와 기능> 63 | 64 | **추가. `sbix` 태그가 `CVE-2020-15999`의 첫 번째 핵심인데, 그것은 이 태그에 PNG 등의 멀티미디어 파일의 정보를 저장하기 때문이다.** 65 | 66 | TTF 파일 구조에 관한 자세한 내용은 [여기](https://docs.fileformat.com/font/ttf/). 67 | 68 | ### 1.1.4. **PNG 헤더 구조** 69 | 70 | PNG란 그림 파일 형식 중 한 종류이며 그 구조는 크게 4개의 청크(chunk)로 이루어져 있다. 이 절에서는 PNG 파일의 헤더 구조와 청크에 관해 설명한다. 71 | 72 | | 청크 이름 | 설명 | 73 | | --- | --- | 74 | | IHDR | 이미지 헤더 정보 영역 | 75 | | PLTE | 색깔 정보 영역 | 76 | | IDAT | 실제 이미지 데이터 영역 | 77 | | IEND | PNG 파일의 끝 | 78 | 79 | <표 2-PNG 파일을 구성하는 4가지 청크> 80 | 81 | 위 표에 나온 것과 같이, PNG 파일은 총 4개의 청크로 구성된다. 그 중 헤더 역할을 하는 것이 바로 첫 번째에 표시된 IHDR 청크인데, 아래 코드는 IHDR 청크의 구조를 보여주는 코드이다. 82 | 83 | ```java 84 | Length : 00 00 00 0D (13 byte), 85 | Chunk Type : 49 48 44 52 "IHDR" (4 byte), 86 | Chunk Data (13 byte), 87 | { 88 | Width (4 byte), 89 | Height (4 byte), 90 | Bit depth (1 byte), 91 | Color Type (1 byte), 92 | Compression method (1 byte), 93 | Filter method (1 byte), 94 | Interlace method (1 byte), 95 | } 96 | ``` 97 | 98 | 위에 표시된 IHDR 청크의 구조를 보면, `Width` 와 `Height` 라는 항목이 있다. 이 두 항목은 PNG 이미지의 가로, 세로 길이를 지정하는 항목인데, 바로 이들이 `CVE-2020-1599`의 두 번째 핵심 요소로서 기능한다. 99 | 100 | 여기 설명된 이외의 자세한 PNG 파일 구조를 알고 싶다면 [여기](https://ryanking13.github.io/2018/03/24/png-structure.html) 101 | 102 | ### 1.1.5. **근본 원인(Root Cause)** 103 | 104 | 위 정보를 토대로 우리는 `CVE-2020-1599`에 대한 Root Cause 분석을 진행했고, 그 결과로 나온 것을 이 절에서 짤막히 설명하려 한다. 언급된 취약점이 발발한 과정은 아래와 같다: 105 | 106 | 1. FreeType에서 비트맵 청크 크기를 정할 때 **Height 값이 저장되는 변수** 는 **16비트로 고정된다.** 107 | 2. 그러나, 실제 PNG 헤더에 명시된 Height 값은 **32비트이다.** 108 | 3. 따라서, PNG 파일 데이터를 읽어와 FreeType에서 사용할 때 **정수 잘림** 이 발생한다. 109 | 4. 결과적으로 Bitmap Data를 메모리에 할당할 시, PNG의 실제 Height 값이 16비트 크기를 넘어서는 불일치 현상이 발생하게 된다. 110 | 5. 따라서, Heap Overflow가 발생한다. 111 | 112 | 정리하자면, `CVE-2020-15999`의 근본 원인은 **'FreeType 폰트 중 한 종류인 TTF 상에서 PNG 파일의 정보를 처리할 때 발생한 Heap overflow 취약점'** 으로 압축될 수 있다. 113 | 114 | ### 1.1.6. 어택 벡터 선정 115 | 116 | 앞선 `1.1.1`~ `1.1.5` 절에서, FreeType을 사용하는 폰트 `TTF` 에서 발생한 취약점인 `CVE-2020-15999` 에 대해 설명했다. 당시 이 취약점 분석을 통해 **1) FreeType을 사용하는 폰트에서 멀티미디어 데이터를 가져와 사용할 때** , **2) 비슷한 종류의 취약점이 발생** 할 수 있으리라는 가능성이 제기되었다. 특히나 **FreeType에서는 멀티미디어 파일의 종류를 단순한 이미지 파일 하나로 한정하지 않았으므로** 충분한 variant cases가 발생할 수 있다고 판단했다. 따라서 우리는 FreeType을 사용하는 폰트들이 어택 벡터로서 충분한 가치가 있다는 결론을 내리게 되었다. 117 | 118 | 결론적으로 우리는 FreeType을 사용하는 폰트들을 어택 백터로 선정하게 되었다. 119 | 120 | ## 1.2. 퍼징 아이디어 선정 121 | 122 | 어택 벡터를 선정했으니, 이제는 어택 벡터의 어떤 갈래가 더 가능성이 있고, 퍼징으로 공략하기에 유리한지를 판단해야 했다. 이러한 어택 벡터 검토를 위한 조건을 브레인스토밍하였더니, 아래와 같은 결과가 나왔다. 123 | 124 | | 검토 사항 | 125 | | --- | 126 | | 1. FreeType을 사용하는 폰트 중, TTF 폰트 외 다른 형식의 폰트에서도 멀티미디어를 사용하는가? | 127 | | ┗1-1. (다른 형식의 폰트에서도 사용한다면) 어떤 멀티미디어를 사용하는가? | 128 | | 2. TTF 파일 내부에서 PNG 외에도 다른 멀티미디어를 사용하는가? | 129 | | ┗2-1. (다른 멀티미디어를 사용한다면) 어느 부분에서 취약점이 터질 수 있겠는가? | 130 | 131 | <표 3-어택 벡터에 대한 검토 사항> 132 | 133 | 위 조건들을 검토해 보니, 아래 <표 4>와 같은 결과가 나왔다. 134 | 135 | | 검토 사항 | 결과 | 136 | | --- | --- | 137 | | 1. FreeType에서 TTF 폰트 외 다른 형식의 폰트에서도 멀티미디어를 사용하는가? | X | 138 | | ┗1-1. (다른 형식의 폰트에서도 사용한다면) 어떤 멀티미디어를 사용하는가? | 검토 불가 | 139 | | 2. TTF 파일 내부에서 PNG 외에도 다른 멀티미디어를 사용하는가? | X | 140 | | ┗2-1. (다른 멀티미디어를 사용한다면) 어느 부분에서 취약점이 터질 수 있겠는가? | 검토 불가 | 141 | 142 | 특히, TTF 파일 내부에서 PNG 외에도 다른 멀티미디어를 사용하는지 여부는 코드 오디팅을 통해 확인해 보니 타 멀티미디어 포맷 사용 계획은 있으나 구현은 하지 않은 상태인 것으로 확인되었다. 143 | 144 | 즉, 기존에 취약점이 발생했던 경로 이외로의 확장이 불가능한 상황이었으므로 우리는 `CVE-2020-15999` 와 동일한 어택 벡터를 공략하기로 결정했다. 145 | 146 | # 2. Glitch 퍼저 147 | 148 | Glitch 퍼저의 깃허브 주소는 [여기](https://github.com/BOB-Jour/Glitch) 149 | 150 | 이 절에서는 우리가 만든 Glitch 퍼저의 대략적인 구조에 대해 설명한다. 본 퍼저의 핵심이라 할 수 있는 뮤테이터는 3절에서 더욱 자세히 설명한다. 151 | 152 | ## 2.1. 퍼저 구조 153 | 154 | 아래는 Glitch 퍼저의 구조를 도식화한 그림이다. 2.1절에서 Glitch 퍼저의 각 모듈에 대한 대략적인 설명을 진행한 후, 2.2절에서 실행 흐름은 어떻게 되는지 설명하겠다. 155 | 156 | Glitch는 크게 **세 가지의 모듈** 로 구성되어 있다. **첫째, Glitch Server** , **둘째, Glitch Mutator** , **셋째, Giltch Monitor** 가 바로 그것이다. Glitch Server에 대한 클라이언트 역할은 Chrome browser가 수행하며, 이러한 **Server-Client 구조** 를 차용함으로써 **`TTF` 파일을 html 형식으로 실행할 때의 번거로움을 없앨 수 있었다.** 157 | 158 | **Glitch 서버** 는 클라이언트의 요청을 감지하여 testcase.html을 클라이언트에게 보내줌과 동시에 뮤테이터에게 `TTF` 파일의 생성을 요청하고, 이후 클라이언트가 TTF 파일을 요청할 경우 만들어진 TTF 파일을 보내주는 역할을 한다. 159 | 160 | 또한, **뮤테이터** 의 경우 확률적인 값 삽입과 태그의 필드 변형을 통해 testcase를 생성하도록 했으며, **모니터** 는 브라우저 프로세스를 일정 주기로 강제 종료 후 새롭게 실행시키고, 테스트 케이스 실행 결과를 체크해 크래시인지 여부를 판단하며, 만일 크래시인 경우 기록을 남기도록 했다. 이렇게 구성된 Glitch를 실행시켰을 때의 흐름은 아래와 같다. 161 | 162 | ![<그림 1-Glitch 퍼저의 실행 구조>](./Glitch_img/glitch_structure.drawio.png) 163 | 164 | <그림 1-Glitch 퍼저의 실행 구조> 165 | 166 | ## 2.2 Glitch 퍼저의 실행 단계 167 | 168 | Glitch 퍼저의 실행 단계는 아래와 같다. 169 | 170 | > 1. 모니터가 실행된다. 171 | > 172 | > 1. 모니터는 크롬 프로세스를 실행한다. 173 | > 2. 실행된 크롬 프로세스가 url([http://localhost/flag?test](http://localhost/flag?test))을 통해 서버에 접근한다. 174 | > - 이 때, 이 요청은 아래 사진처럼 url을 통해 이루어진다. 175 | > 176 | > ![Untitled](./Glitch_img/Untitled.png) 177 | > 178 | > 3. 서버는 url을 통한 접근을 인식하고, 뮤테이터에게 `TTF` 파일 생성을 요청한다. 179 | > 4. 생성된 `TTF` 파일을 뮤테이터가 폴더에 저장한다. 180 | > 5. 4~5 작업을 끝낸 서버가 testcase.html을 클라이언트에게 보낸다. 181 | > 6. 크롬 프로세스가 testcase.html을 받아서 실행하는 중, `url(glitch_testcase.ttf)`로 서버에게 `TTF`파일을 요청한다. 182 | > 7. 요청을 받은 서버는 폴더에 저장된 `TTF`를 크롬 프로세스에게 전달해 준다. 183 | > 8. 3~8 반복 184 | > 9. 그 동안 모니터는 크래시가 발생하는지 여부를 계속 체크하며, 5분 주기로 브라우저 프로세스를 종료한 후 1번부터 다시 실행한다. 185 | 186 | 본 퍼저의 핵심은 뮤테이터라고 할 수 있다. Glitch 뮤테이터는 기존에 존재하는 TTF 파일 형식에서 멀티미디어 파일을 불러올 때 영향을 주는 태그를 선정해 그에 대한 뮤테이션을 수행하며, 해당 기능의 구현을 위해 TTF 파일의 태그들에 대한 심층적인 조사를 수행했다. 이어질 3절에서는 수행했던 조사와, 뮤테이터에 들어간 아이디어에 관해 상세히 설명한다. 187 | 188 | # 3. 뮤테이션 전략 189 | 190 | 이 절에서는 우리의 뮤테이션 전략, 즉 Glitch 뮤테이터와 그 아이디어에 관해 자세히 설명한다. 191 | 192 | Glitch 뮤테이터는 `TTF` 파일에서 멀티미디어를 사용할 때 관여하는 `` 태그에 대한 필드값의 뮤테이션을 수행한다. 이를 구현하기 위해 `TTF` 파일의 필드에 대한 자료조사를 수행했으며 관련 내용은 [여기](https://developer.apple.com/fonts/TrueType-Reference-Manual/)에서 확인할 수 있다. 193 | 194 | 뮤테이션 방식은 확률적으로 결정되는데, 자세한 사항은 이어지는 내용에서 설명한다. 195 | 196 | ## 3.1. 뮤테이터 구조 197 | 198 | ![<그림 2>-Glitch 뮤테이터의 구조](./Glitch_img/Structure.png) 199 | 200 | <그림 2>-Glitch 뮤테이터의 구조 201 | 202 | 뮤테이터는 크게 5가지의 메소드로 구성되어 있다. 203 | 204 | 1. `make_png()`: PNG 바이너리를 생성하는 메소드. 205 | 2. `mutate_IDHR()`: IDHR Chunk 값을 50% 확률로 뮤테이션하는 메소드. 206 | 3. `init_sbix()`: sbix 태그를 생성하는 메소드. 207 | 4. `mutate_sbix()`: sbix 태그 속성 값을 70% 확률로 뮤테이션하는 메소드. 208 | 5. `append_sbix()`: sbix 태그를 TTF 파일에 적용해 주는 메소드. 209 | 6. `make_ttf()`: 1~5까지의 메소드를 내부에서 순서에 맞게 호출하며, 호출된 메소드들의 결과물을 모아 ttf 파일을 만드는 메소드. 210 | 211 | 확률은 차후 유지보수 과정에서 변할 수 있다. 212 | 213 | ## 3.2. 뮤테이터 실행 과정 214 | 215 | 뮤테이터를 실행했을 때, 코드의 흐름은 아래와 같다. 216 | 217 | 1. PNG 바이너리를 생성한다. 218 | 2. 50%의 확률로 IDHR Chunk 값을 뮤테이트한다. 219 | 3. sbix 태그를 생성한다. 220 | 4. 70%의 확률로 sbix 태그 속성 값들을 뮤테이트 시킨다. 221 | 5. 생성된 sbix 태그를 `TTF` 파일에 적용한다. 222 | 6. 만들어진 `TTF` 파일을 디렉토리에 저장한다. 223 | 224 | # 4. 실행 결과 225 | 226 | ![<그림 3>-Glitch 퍼저의 대시보드. ](./Glitch_img/unknown.png) 227 | 228 | <그림 3>-Glitch 퍼저의 대시보드. 229 | 230 | 실제로 Glitch 퍼저를 작동시켰을 때의 대시보드는 위의 <그림 3>과 같다. Domato 퍼저의 대시보드를 그대로 사용하였으며, 상당히 직관적인 형태이므로 자세한 설명은 생략하는 것으로 한다. 231 | 232 | Glitch 퍼저를 약 **일주일** 동안 당시 기준 **최신 버전의 크롬(스테이블)** 에 대해 실행한 결과, 아래의 표와 같은 결과가 나왔다. 퍼저의 근거로 삼았던 `CVE-2020-15999` 를 제외하면 아무 크래시도 나오지 않았기 때문에, 문제가 있다고 판단하여 퍼저 작동을 중지하게 되었다. 233 | 234 | | 크래시 | 0개 | 235 | | --- | --- | 236 | | 유니크 크래시 | 0개 | 237 | | 분석한 크래시 | 0개 | 238 | 239 | # 5. 결론 240 | 241 | ## 5.1. 결과 242 | 243 | 1. 어택 벡터 선정 시, 해당 어택 벡터에서 최근 어느 정도의 빈도로 어느 수준의 취약점이 발생했는지 확인하지 않았다. 244 | 2. 이미 패치가 되어 더는 동일한 취약점이 발생하지 않을 것이 확실한 어택 벡터를 공략했다. 245 | 3. 선정된 어택 벡터에 대해 다수의 CVE를 재현할 수 있는지 여부를 확인하지 않았다. 즉, 배경으로 삼은 CVE 하나를 재현하는 데에만 그쳤다. 246 | 247 | 정리하자면, Glitch 퍼저가 실패한 이유는 근거가 빈약했기 때문이었다. 따라서, 이어질 5.2절에서 서술할 보완책은 어택 벡터 선정과, 퍼저 아이디어 결정의 근거를 탄탄하게 굳히는 것을 목표로 하고 있다. 248 | 249 | ## 5.2. 보완책 250 | 251 | 1. 최근 일정 기간 동안 발생한 브라우저 취약점들의 어택 벡터 종류와, 그 빈도와 심각도를 측정해 가장 빈도가 높은 어택 벡터를 공략한다. 252 | 253 | → 이 보완책을 실행하기 위해 크롬 취약점을 리스팅한 웹 페이지를 크롤링하여 파싱하는 [파서](https://github.com/BOB-Jour/CRP)를 개발했고, 크롤링 결과를 토대로 어택 벡터를 선정해 작성한 것이 바로 WebIDL 퍼저인 [Pleaser](https://github.com/BOB-Jour/Pleaser)이다. 254 | 255 | 2. 1에서 선정한 어택 벡터에서 발생했던 취약점들 중 최근 일정 기간 동안 발생했던 취약점들을 모두 분석해서 공통 패턴을 찾고, 해당 패턴을 퍼징 아이디어로 활용한다. 256 | 257 | → 그러나 시간이 부족하여 이 보완책은 아이디어 단계에서 멈췄다. 258 | -------------------------------------------------------------------------------- /2_fuzzer_development/Pleaser/Pleaser_img/Untitled 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Pleaser/Pleaser_img/Untitled 1.png -------------------------------------------------------------------------------- /2_fuzzer_development/Pleaser/Pleaser_img/Untitled 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Pleaser/Pleaser_img/Untitled 2.png -------------------------------------------------------------------------------- /2_fuzzer_development/Pleaser/Pleaser_img/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BOB-Jour/Chromium-Bug-Hunting-Project/36044e9b0fcd86cdff6288520cd3a8ab372db5f7/2_fuzzer_development/Pleaser/Pleaser_img/Untitled.png -------------------------------------------------------------------------------- /2_fuzzer_development/Pleaser/WebIDL로_파이어폭스_퍼징하기.md: -------------------------------------------------------------------------------- 1 | # WebIDL로_파이어폭스_퍼징하기.md 2 | - [TL;DR, 들어가는 글](#tldr-들어가는-글) 3 | - [퍼징 기초](#퍼징-기초) 4 | - [Fuzzer의 유형](#fuzzer의-유형) 5 | - [블랙박스 퍼징](#블랙박스-퍼징) 6 | - [화이트박스 퍼징](#화이트박스-퍼징) 7 | - [그레이박스 퍼징](#그레이박스-퍼징) 8 | - [문법 기반 퍼징(Grammar-Based Fuzzing)](#문법-기반-퍼징grammar-based-fuzzing) 9 | - [기존 문법들의 문제점](#기존-문법들의-문제점) 10 | - [퍼징 문법으로서의 WebIDL](#퍼징-문법으로서의-webidl) 11 | - [표준화된 문법](#표준화된-문법) 12 | - [단순화된 문법 개발](#단순화된-문법-개발) 13 | - [ECMAScript Extended Attributes](#ecmascript-extended-attributes) 14 | - [자동으로 API 변경 여부 감지하기](#자동으로-api-변경-여부-감지하기) 15 | - [IDL을 JavaScript로 변환하기](#idl을-javascript로-변환하기) 16 | - [제네릭 타입](#제네릭-타입) 17 | - [객체 참조](#객체-참조) 18 | - [콜백 핸들러](#콜백-핸들러) 19 | - [무설정(無設定) 퍼징](#무설정無設定-퍼징) 20 | - [GrIDL을 사용한 룰 패치(Rule Patching)](#gridl을-사용한-룰-패치rule-patching) 21 | - [실례](#실례) 22 | - [Contributing](#contributing) 23 | - [Jason Kratzer에 대해:](#jason-kratzer에-대해) 24 | --- 25 | 원본 주소: [https://hacks.mozilla.org/2020/04/fuzzing-with-webidl/](https://hacks.mozilla.org/2020/04/fuzzing-with-webidl/) 26 | 27 | 위 주소의 글을 번역한 게시글입니다. 28 | 29 | --- 30 | 31 | ## **TL;DR, 들어가는 글** 32 | 33 | 퍼징(Fuzz Testing이라고도 함)이란 소프트웨어의 안전성과 안정성을 자동으로 테스트하는 방식 전반을 일컫습니다. 소프트웨어 내부의 예상치 못한 동작, 더 나아가서는 위험한 동작을 알아내기 위해 특수하게 조작해 둔 입력값을 넣어주는 것이 일반적인 퍼징 방식이지요. 34 | 35 | 만일 퍼징에 익숙지 않으신 분이 있다면 [Firefox Fuzzing Docs](https://firefox-source-docs.mozilla.org/tools/fuzzing/index.html) 및 [Fuzzing Book](https://www.fuzzingbook.org/) 을 참조해 주세요. 36 | 37 | 지난 3년 간 Firefox 퍼징 팀은 Firefox에서 WebAPI를 사용할 때 발생하는 보안 취약점을 찾아내기 위해서 새로운 퍼저 개발에 매달려 왔습니다. 현재는 개발이 완료되어 Domino라고 명명했는데요, 이 퍼저에서는 Fuzzing Grammar로 WebAPI에 대하여 WebIDL로써 정의해 둔 것을 사용한다는 특징이 있습니다.  38 | 39 | 저희가 만든 Domino, 즉 저희만의 고유한 접근 방식이 들어간 이 퍼저를 사용해 [850](https://bugzilla.mozilla.org/show_bug.cgi?id=1340565) 개 [이상의 버그를](https://bugzilla.mozilla.org/show_bug.cgi?id=1340565) 찾아낼 수 있었습니다. 이러한 버그 중 116개는 Security Rating을 받았지요. 40 | 41 | 이 글을 빌어 1) Domino의 몇몇 주요 기능들과 2)어째서 Domino가 이전까지의 WebAPI 퍼징 시도들과 다른지에 대해 설명하고자 합니다. 42 | 43 | ## **퍼징 기초** 44 | 45 | Domino에 대해 자세히 파고 들어가기 전에, 먼저 현존하는 퍼징 기술의 유형에 대해 서술하겠습니다. 46 | 47 | ### **Fuzzer의 유형** 48 | 49 | Fuzzer는 일반적으로 블랙박스, 그레이박스 또는 화이트박스 퍼저로 분류됩니다. 이 분류의 기준은 퍼저가 대상 애플리케이션에 대해 얼마나 잘 알고, 얼마나 맞춤화된 정보를 넣어주느냐인데요*, 가장 널리 쓰이는 두 가지 유형은 블랙박스와 그레이박스 퍼저입니다. 50 | 51 | (* 역주: 원문은 ‘These designations are based upon the level of communication between the fuzzer and the target application.’이지만 의역을 거침.) 52 | 53 | ### **블랙박스 퍼징** 54 | 55 | 블랙박스 퍼징은 인풋으로 주고자 하는 데이터가 대상 애플리케이션에 어떤 영향을 미치는지에 대해 아무것도 모르는 채로 데이터를 넣어주는 것을 의미합니다. 이런 전제 때문에 블랙박스 퍼저의 효율성은 생성된 데이터가 얼마나 운좋게 애플리케이션의 구조에 걸맞는지에 전적으로 의지하게 됩니다. 56 | 57 | 하지만 이런 점이 오히려 장점이 될 수 있죠. 블랙박스 퍼징은 대규모의 비결정적 애플리케이션*이나, 고도로 구조화된 데이터를 처리하는 애플리케이션을 퍼징할 때 유용합니다. 58 | 59 | (* 역주: 동일한 인풋인데도 다른 아웃풋이 나올 수 있는 프로그램) 60 | 61 | ### **화이트박스 퍼징** 62 | 63 | 화이트박스 퍼징은 퍼저와 대상 응용 프로그램이 서로의 코드에 대해 확실하게 알고 있는 상황에서, 퍼저가 응용 프로그램의 요구 사항을 정확히 충족하는 데이터를 생성하여 인풋으로 주는 방식을 일컫습니다. 64 | 65 | 이 퍼징 방식에서는 응용 프로그램 코드 실행 시의 분기 조건을 체크해서, 데이터가 들어갔을 때 코드의 모든 분기를 실행하도록 모종의 이론적인 도구를 사용하는데요, 이를 통해 블랙박스 혹은 그레이박스 퍼저였다면 돌려 보지 못했을 분기를 테스트할 수 있다는 장점이 있습니다. 66 | 67 | 하지만 단점 또한 존재합니다. 바로 계산 비용이 많이 든다는 것인데요, 굉장히 복잡한 코드 분기점들을 가진 아주 무거운 프로그램을 테스트할 경우 정말 많은 시간이 걸릴 수 있기 때문입니다. 이렇게 되면 정해진 시간 안에 테스트할 수 있는 인풋 데이터의 수 또한 확연히 줄어들죠. 학문적으로 연구하는 경우를 제외하고, 화이트박스 퍼징은 실생활에서 사용하기엔 그리 적합하지 않습니다. 68 | 69 | ### **그레이박스 퍼징** 70 | 71 | 그레이박스 퍼징은 최근 들어서 가장 인기 있고 효과적인 퍼징 기법으로 인정받고 있죠. 현재 퍼징 상황을 확인해 퍼저에게 알려 주는 피드백 메커니즘을 이용해 이 다음에는 어떤 데이터를 만들 것인지 결정하는 식인데요, 더 많은 코드 분기를 이용하는 듯한* 인풋 데이터들은 이후 테스트에서 기반 코드로 재사용된다는 특징이 있습니다. 또한, 저조한 코드 분기 이용률을 보이는 데이터들은 바로 버려 버리지요. 72 | 73 | (* 역주: 원어로는 ‘cover more code’.) 74 | 75 | 이 퍼징 방식은 정형화된 방식으로 찾아내긴 힘든 코드 경로에 도달하는 데 있어 높은 효율성과 속도를 보여주는데요, 이 점이 큰 인기를 끌고 있습니다. 하지만 모든 애플리케이션들이 그레이박스 퍼징의 대상으로 적합하진 않아요. 그레이박스 퍼징은 보통 [많은 수의 입력을 빠르게 처리할 수 있는, 작고 입력값에 따른 출력값이 정해져 있는 프로그램](https://llvm.org/docs/LibFuzzer.html#id22)을 퍼징하는 데 효율적이기 때문입니다. 76 | 77 | > *저희는 주로 파이어폭스의 구성 요소들, 그러니까 미디어 파서 같은 것들을 시험하기 위해 그레이박스 퍼저를 사용하고 있습니다. 만일 독자 분이 본인의 코드를 시험하는 데 어떻게 이런 류의 퍼저들을 사용하는지 알고 싶다면, [이쪽](https://firefox-source-docs.mozilla.org/tools/fuzzing/fuzzing_interface.html)으로 가셔서 Fuzzing Interface Documentation을 한 번 읽어보세요!* 78 | > 79 | 80 | 그런데 WebAPI를 퍼징할 때 그레이박스 퍼징을 적용하는 데에는 한계가 있습니다. 브라우저는 어쨌든 입력값 한 개에 따른 출력값이 어떻게 달라질지 모르고(Non-deterministic), 그 입력값도 고도로 구조화되어있는 애플리케이션이기 때문입니다. 그뿐일까요, 브라우저를 시작하고—테스트를 실행하고—문제점이 나오는지, 나온 문제점은 어떤지 모니터링하는 이 일련의 과정이 느릴 수밖에 없습니다. 보통 한 번 테스트하는데 몇 초에서 몇 분까지 걸리니까요. 81 | 82 | 따라서 WebAPI 퍼징에 쓰기에 그나마 나은 것은 블랙박스 퍼징입니다. 83 | 84 | 하지만, 이런 부류의 API에 들어가야 하는 입력은 고도로 구조화된 입력이어야 하기 때문에, 과연 현재 사용하는 퍼저가 **'적어도 구조라도 유효한 입력 데이터를 생성하는지'가 중요해집니다.*** 85 | 86 | (* 역주: 원문 'However, since the inputs expected by these APIs are highly structured, we need to ensure that our fuzzer generates data that is considered valid.' 문장 선후 맥락으로 미루어, valid하게 considered되는 데이터는 구조라도 유효한 입력 데이터일 것으로 생각하고 의역했다.) 87 | 88 | ### **문법 기반 퍼징(Grammar-Based Fuzzing)** 89 | 90 | 문법 기반 퍼징은 일반적인 프로그래밍 언어의 문법을 사용하여 생성할 데이터의 구조(형식)를 정의하는 퍼징 기술입니다.  91 | 92 | 이런 문법은 보통 평문으로 쓰여 있고, 여러 기호와 상수의 조합을 이용해 데이터를 표현하게끔 합니다. 이렇게 정의된 문법을 퍼저는 파싱해서 퍼징된 결과물을 생성하는 데 사용합니다. 93 | 94 | ![https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/Untitled-drawing.svg](https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/Untitled-drawing.svg) 95 | 96 | 여기 보이는 그림은 [Domato](https://github.com/googleprojectzero/domato) 와 [Dharma](https://github.com/MozillaSecurity/dharma) [fuzzers](https://github.com/googleprojectzero/domato) 에서 발췌해 단순화시킨 두 가지 문법을 표현한 것인데요, 이 문법들은 `[HTMLCanvasElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement)` 을 생성하고 이것의 속성과 작동을 조정하기 위해 사용되었어요. 97 | 98 | ### 기존 문법들의 문제점 99 | 100 | 하지만 문제가 있죠. 문법을 개발하는 데 필요한 노력의 크기는 이 문법으로 표현하려는 데이터의 복잡도와 크기에 정비례합니다. 그리고 이게 바로 문법 기반 퍼징의 가장 큰 문제점이죠. 파이어폭스에서 쓰는 WebAPI들이 730개가 넘는 인터페이스를 이용하고, 이 안에 6300개쯤 되는 멤버들이 있다는 걸 고려하면... 더더욱 큰 문제점입니다.* 101 | 102 | (* 역주: Java에서의 인터페이스와 그 내부의 선언을 일컫는 멤버가 맞다.) 103 | 104 | 콜백이라든가, [enum](https://en.wikipedia.org/wiki/Enumerated_type)이라든가, 딕셔너리같은 요소들처럼 함수 등등에서 요구하는 데이터 구조까지 생각하게 되면 저 숫자들의 크기는 훨씬 커지겠죠. 이렇게나 다양하고 많은 API들을 정확하게 표현할 수 있는 문법을 개발하는 건 정말 정말로 대형 프로젝트가 되겠죠. 그런데 개발만이 문제가 아니죠. 이런 류의 것들은 에러가 엄청 많이 발생하고, 유지보수가 정말로 힘드니까요. 말 그대로 사람이 갈려 들어가는 것입니다. 105 | 106 | 저희 팀은, 이런 API들을 더 효과적으로 퍼징하려면 최대한 수작업으로 문법을 개발하는 방식은 피하는 게 좋겠다고 생각했습니다. 107 | 108 | ## **퍼징 문법으로서의 WebIDL** 109 | 110 | ``` 111 | typedef (BufferSource or Blob or USVString) BlobPart; 112 | 113 | [Exposed=(Window,Worker)] 114 | interface Blob { 115 | [Throws] 116 | constructor(optional sequence blobParts, 117 | optional BlobPropertyBag options = {}); 118 | 119 | [GetterThrows] 120 | readonly attribute unsigned long long size; 121 | readonly attribute DOMString type; 122 | 123 | [Throws] 124 | Blob slice(optional [Clamp] long long start, 125 | optional [Clamp] long long end, 126 | optional DOMString contentType); 127 | [NewObject, Throws] ReadableStream stream(); 128 | [NewObject] Promise text(); 129 | [NewObject] Promise arrayBuffer(); 130 | 131 | }; 132 | 133 | enum EndingType { "transparent", "native" }; 134 | 135 | dictionary BlobPropertyBag { 136 | DOMString type = ""; 137 | EndingType endings = "transparent"; 138 | }; 139 | ``` 140 | 141 | [WebIDL](https://heycam.github.io/webidl/) 은 브라우저가 구현해 낸 API들을 설명하기 위해 만들어진 IDL([Interface Description Language](https://en.wikipedia.org/wiki/Interface_description_language))입니다. IDL이 API를 설명할 때는 당 API의 인터페이스, 멤버, 값, 그리고 구문을 나열하는 방식을 사용하지요. 142 | 143 | WebIDL이 무엇인지는 브라우저 퍼징 커뮤니티에서 굉장히 유명한데요, WebIDL이 다양한 정보들을 그 안에 가지고 있다는 점 때문입니다. 예전에는 웹 브라우저 퍼징을 할 때, WebIDL이 가지고 있는 데이터를 추출해 퍼징 문법으로 사용하려는 시도가 있었어요. [Sensepost에서 나온 WADI fuzzer](https://sensepost.com/blog/2015/wadi-fuzzer/)가 꼭 이런 시도로 나온 결과물입니다. 하지만, 저희가 저 퍼저를 이용한 몇몇 사례를 조사해 보니, 이렇게 정의된 퍼징 문법 정보들은 퍼저 고유의 문법 구문으로 설정된 형식을 이용해 다시 한 번 추출과 재구현 과정을 거치더군요. 이런 식으로 퍼징하는 건 여전히 상당한 양의 수작업을 요합니다. 추가로, 어떤 인스턴스 내부에서 특정 WebAPI의 작동을 다른 WebAPI에게 정확히 전달해 주려 한다고 생각해 봅시다. 이 행위의 난이도는 퍼저 고유의 문법 구문으로 인해 훨씬 올라가게 되어 있어요. 144 | 145 | 이런 문제점들에 기반해서, 저희 팀은 위 퍼저의 방식이 아닌*, WebIDL의 정의를 곧장 퍼징 문법으로 가져다 쓰기로 결론을 내렸습니다. 이런 접근 방식 덕에 꽤 많은 이득을 볼 수 있었죠. 146 | 147 | (* 역주: 한 마디로, WebIDL의 정의를 현재 사용중인 퍼저의 문법 구문 형식으로 바꿔버리는 방식.) 148 | 149 | ### **표준화된 문법** 150 | 151 | 첫째로, WebIDL Specification이 표준화된 문법을 알맞게 정의해 둔다는 점입니다. 이렇게 해둠으로써 raw WebIDL 정의들을 파싱해서 AST([Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree))형태로 만들 때 [WebIDL2.js](https://github.com/w3c/webidl2.js/)같은 도구들을 사용할 수 있게 되는 것이죠. 이렇게 만들어진 AST는 퍼저가 테스트 케이스들을 생성할 때 퍼저가 AST를 해석해서 이용하는 식으로 사용됩니다. 152 | 153 | ### **단순화된 문법 개발** 154 | 155 | 둘째로, WebIDL이 타겟으로 찍어 둔 API의 구조와 동작을 정의해 둔다는 점입니다. 이것 덕에 퍼징에 필요한 규칙 개발의 양이 크게 줄죠. 만약 이 방법을 사용하지 않고, 즉 앞에서 말했던 다른 퍼저의 방식대로, 같은 API를 서술하려면 이 API에 대한 맞춤형 규칙(API에서 정의된 인터페이스, 멤버, 값 등등에 대해서)을 만들어야 한다는 점에서 저희가 사용하는 방식이 훨씬 나은 걸 알 수 있습니다. 156 | 157 | ### **ECMAScript Extended Attributes** 158 | 159 | 셋째로, 데이터 구조만 정의해 주는 기존의 문법과는 다르게, WebIDL Specification은 ECMAScript Extended Attributes를 통해 인터페이스의 동작과 관련된 추가적인 정보를 알게 해 준다는 점입니다. Extended Attributes는 아래와 같은 다양한 동작 정보를 알려 주는데요: 160 | 161 | - 특정 인터페이스가 사용될 수 있는 전후 맥락 162 | - 리턴된 객체가 새로 생성된 건지/기존 인스턴스의 복제인지 여부 163 | - 특정 멤버 인스턴스가 대체될 수 있는지의 여부 164 | 165 | 이와 같은 유형의 동작 정보들은 기존의 문법들로는 표현하기 힘들기 때문에 WebIDL 문법을 고스란히 사용하는 게 표현에 유리합니다. 166 | 167 | ### 자동으로 **API 변경 여부 감지하기** 168 | 169 | 마지막으로, WebIDL 파일이 브라우저에 의해 구현된 인터페이스들과 연결되어 있다는 점을 이득의 한 예로 들 수 있겠군요. 이 ‘연결되어 있다’는 특징으로 인해 웹 브라우저 인터페이스들의 업데이트 사항이 WebIDL에도 고스란히 반영되었으리라는 것이 기본 전제가 되니까요. 170 | 171 | ## **IDL을 JavaScript로 변환하기** 172 | 173 | ![https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/WebIDL-Inference2.svg](https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/WebIDL-Inference2.svg) 174 | 175 | 퍼징에 WebIDL을 활용하려면 먼저 파싱을 거쳐야 합니다. 다행히도, [WebIDL2.js](https://github.com/w3c/webidl2.js/) 라이브러리라는 게 존재해서, raw IDL 파일을 AST([Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree))로 변환할 수 있습니다. 이렇게 WebIDL2.js를 이용해 만들어진 AST는 데이터를 트리 형태로 나열된 노드의 집합으로 표현해 주는데요, 여기에 존재하는 각각의 노드들은 WebIDL 문법상에서의 요소(construct)들을 나타냅니다.* 176 | 177 | (* 역주: 자바스크립트에서의 constructor와는 다른 개념입니다. constructor(): 생성자 함수. construct: 특정 형태를 취하며, 어떠한 기능을 하는 임의의 코드의 집합들. JS에만 한정된 개념이 아님.) 178 | 179 | > WebIDL2 AST 구조에 대해 더 알고 싶으신 분은 [이쪽을](https://github.com/w3c/webidl2.js/#ast-abstract-syntax-tree) 참고해 주세요. 180 | > 181 | 182 | AST가 만들어지기만 하면 그 이후는 간단합니다. AST 상에서의 각각의 노드들, 즉 각각의 요소(construct)들이 어떻게 번역될지만 정의하면 되기 때문입니다. 저희는 이번에 개발한 Domino 퍼저에 AST 탐색과 AST 상의 노드를 자바스크립트로 변환하기 위한 여러 도구를 만들어 넣어두었답니다. 위에 보이는 사진이 저희 도구가 어떤 식으로 번역하는지를 예시 몇 개로 설명하고 있으니 한 번 확인해 보세요. 183 | 184 | AST상에 존재하는 노드의 대부분은 '정적 변환', 그러니까, AST 상에 있는 특정 노드는 언제나 JS상에서 똑같은 의미를 지난다는 가정 하에 이루어지는 변환을 사용하면 목표 언어로 번역할 수 있습니다. 예를 들어 봅시다. 생성자(constructor) 키워드는 JS상의 'new' 연산자 인터페이스 이름이 합쳐진 형식과 언제나 동치입니다. 185 | 186 | 하지만 언제나 이런 것만은 아닙니다. WebIDL의 요소(construct)가 굉장히 많은 의미를 가지고 있고, 맥락에 따라 그 의미가 이리저리 달라진다면 반드시 앞뒤 관계를 고려해 동적으로 생성해야 하기 때문이에요. 187 | 188 | ### 제네릭 타입* 189 | 190 | (* 역주: Generic Type는 매개변수화된 클래스나 인터페이스를 의미합니다. JS의 경우 클래스 이름 뒤에 를 붙여 제네릭 타입의 사용을 알립니다. 여기에서 T는 Generic Type의 형식을 의미합니다.) 191 | 192 | [WebIDL Specification](https://heycam.github.io/webidl/#idl-types)에 들어가 보면, generic value들을 표현하는 데 사용되는 자료형의 종류들을 볼 수 있습니다. 여기에 나온 각각의 자료형에 대해, 저희 Domino는 특정 함수를 이용해 요청받은 자료형 형태의 랜덤한 value를 리턴해 주거나, 예전에 이 자료형을 가지고 작업한 적이 있다면 이 자료형의 객체(object)를 기억해 뒀다가 이 객체형과 일치하는 랜덤한 value를 리턴해 줍니다. 예시와 함께 설명해 보겠습니다. AST상의 노드들을 번역할 때, 즉 AST를 순회할 때, 숫자 형식의 자료형, 즉, octec이나, short나, long같은 자료형들을 요구하는 요소(construct)가 존재하겠죠? 그렇다면 그 때, 이 자료형들의 범위 내에 존재하는 값을 우리 Domino가 리턴해 준다는 것입니다. 193 | 194 | ### 객체 참조 195 | 196 | AST 상에서의 특정 노드, 그러니까 특정 요소(construct)의 자료형이 1) 특정한 다른 IDL 요소의 정의를 따와 쓰면서, 2) 함수에게 전달되는 인수로 쓰인다고 해 봅시다. 그렇다면 이 요소는 현재 이 요소가 따와서 쓰려는 IDL 요소에 대해, 그 요소가 표방하는 객체의 복사본이 필요하겠지요? 저희 Domino가 바로 그 일을 해 줍니다. 197 | 198 | Domino는 앞서 말한 방식대로 일해 주기도 하지만, 현재 번역하려는 요소가 요구하는 객체 형식을 리턴해 주는 다른 멤버 argument를 찾아내 엑세스함으로써 필요한 객체를 받아다 리턴해 주기도 합니다. 199 | 200 | ### 콜백 핸들러 201 | 202 | 앞서 이야기했던 WebIDL Specification에서는 함수를 표현하는 여러 자료형에 대해서도 찾아볼 수 있는데요(예: [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), [callbacks](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function), and [event listeners](https://developer.mozilla.org/en-US/docs/Web/API/EventListener)), 이 각각의 자료형에 대해 Domino는 독특한 함수를 생성해서 리턴해 줍니다. 이렇게 생성된 함수는 인수가 없어도 기능하는 것이 있지만, 만일 인수를 받는 함수라면 그 인수에 대해 랜덤한 연산을 수행해 줍니다. 203 | 204 | 모두 아시겠지만, 지금까지 이야기한 과정들이라든가, 동적으로 번역해야 하는 IDL요소들이라든가.... 이런 것들은 IDL을 JS로 완전히 번역하는 과정의 새발의 피에 불과합니다. Domino의 제너레이터는 WebIDL Specification 전체에 대해 번역을 지원하니까요, 전체 과정은 얼마나 방대하겠어요? 205 | 206 | 그럼 이제, 실제 적용 예를 한 번 봅시다. Blob WebIDL을 퍼징 문법으로 사용했을 때 어떻게 되는지 말이에요. 207 | 208 | ## 무설정(**無設定)** **퍼징** 209 | 210 | ``` 211 | > const { Domino } = require('~/domino/dist/src/index.js') 212 | > const { Random } = require('~/domino/dist/src/strategies/index.js') 213 | > const domino = new Domino(blob, { strategy: Random, output: '~/test/' }) 214 | > domino.generateTestcase() 215 | … 216 | 217 | const o = [] 218 | o[2] = new ArrayBuffer(8484) 219 | o[1] = new Float64Array(o[2]) 220 | o[0] = new Blob([o[1]]) 221 | o[0].text().then(function (arg0) { 222 | o[0].text().then(function (arg1) { 223 | o[3] = o[0].slice() 224 | o[3].stream() 225 | o[3].slice(65535, 1, ‘foobar’) 226 | }) 227 | }) 228 | o[0].arrayBuffer().then(function (arg2) { 229 | o[3].text().then(function (arg3) { 230 | O[4] = arg3 231 | o[0].slice() 232 | }) 233 | }) 234 | ``` 235 | 236 | 위 코드에서 보이는 바와 같이, IDL로 알아낼 수 있는 정보는 그것만으로도 유효한 테스트 케이스를 생성하기에 충분한 수준입니다. 이런 식으로 생성된 테스트 케이스들은 Blob이 관여하는 코드들을 꽤 많이 실행해 주고요. 결과적으로 사람의 개입 없이, 처음 작업해 보는 API에 대한 fuzzer 기본틀을 빠르게 개발할 수 있겠죠. 237 | 238 | 하지만 이 매커니즘에게 입맛에 맞을 수준의 정확도를 바라기엔 부족한 감이 있습니다. 예를 들어 봅시다. 슬라이스 작업*에 필요한 값이 있다고 쳐요. [Blob Specification](https://w3c.github.io/FileAPI/#dfn-slice)을 확인해 보면, 시작과 끝으로 주어지는 인수(arguments)가 Blob의 크기를 감안한 상태에서 바이트 오더 순서로 놓여 있어야 한다는 걸 알 수 있는데요, 아무래도 퍼저를 사용하는 상황이다 보니 시작값과 끝값을 랜덤으로 생성할 수밖에 없어요. 이렇게 되면 Blob의 길이(크기) 범위에 맞지 않는 랜덤값을 반환하는 경우도 생기겠죠. 239 | 240 | 이런 류의 문제가 생길 수 있다는 겁니다. 241 | 242 | (* 역주: Slice operation. 문자열을 원하는 만큼 잘라서 리턴하는 작업이다.) 243 | 244 | 또한, 슬라이스 연산에서의 `contentType` 인수와 `BlobPropertyBag` 딕셔너리의 타입 속성은 모두 `[DOMString](https://developer.mozilla.org/en-US/docs/Web/API/DOMString)`의 한 종류라는 특징이 있어요. 그렇다면 퍼저를 사용할 때 숫자 값들처럼 스트링 값들도 랜덤으로 생성해 집어넣는다는 점에 집중해 봅시다. 스펙 문서를 잘 분석해 보면 지금 랜덤으로 생성되어 들어가고 있는 스트링 값들이 Blob 데이터의 미디어 유형을 나타내는 데 사용된다는 걸 알아낼 수 있는데요, 여기에서 정말 곤혹스러운 딜레마를 만나게 되죠. 과연 이 스트링 값은 Blob을 사용하는 API에 어떤 영향을 끼칠 것인가? 과연 아무 영향도 끼치지 않을까? 245 | 246 | 이런 문제를 해결하기 위해서 앞서 설명했던 제네릭 타입들을 구분하는 방식을 만들어야 했습니다. 247 | 248 | ## **GRIDL을 사용한 룰 패치(Rule Patching)** 249 | 250 | ![https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/GrIDL-Domino-Relationship2.png](https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/GrIDL-Domino-Relationship2.png) 251 | 252 | 따라서 저희 팀은 GrIDL이라는 또 다른 도구를 하나 개발해 냈습니다. GrIDL은 IDL 정의를 AST로 변환할 때 WebIDL2.js 라이브러리를 이용하는데요, 거기에 추가로 AST를 최적화해 줌으로써 퍼징 문법으로 AST를 사용할 때 최대한의 효율을 낼 수 있게 한다는 특징이 있습니다. 253 | 254 | 그런데, GrIDL의 가장 재미있는 점은 다른 곳에서 찾아볼 수 있습니다. 바로 여러 맥락을 고려한 정확한 값을 설정하고 싶을 때 IDL 정의를 동적으로 패치할 수 있다는 것이죠. GrIDL은 규칙 기반 매칭 시스템(Rule-Based matching system)을 이용해 타겟 값을 설정하고 고유 식별자(Unique Identifier)라는 것을 삽입하는데, 이게 Domino에서 구현해 놨던 매칭 제너레이터(Matching generator)랑 서로 통신하거든요*. AST를 순회하는 도중에 이런 식별자와 마주치게 되면 Domino는 매칭 제너레이터를 콜해다가 알맞은 값을 만들어 리턴해 줍니다. 255 | 256 | (* 역주: 원문 ‘Those identifiers correspond with a matching generator implemented by Domino.‘ correspond with는 일치한다는 뜻도 있으나 편지를 주고받는다는 의미도 있어 후자의 의미를 차용했다.) 257 | 258 | ![https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/GrIDL-Markup-and-Generators1.svg](https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/GrIDL-Markup-and-Generators1.svg) 259 | 260 | 위 모식도는 GrIDL 식별자와 Domino의 제너레이터 간 상관 관계를 표현한 그림입니다. 제너레이터 두 개가 정의된 게 보이시죠? 하나는 바이트 오프셋을, 다른 하나는 유효한 MIME 타입을 반환해 주는 제너레이터입니다. 261 | 262 | 그리고 중요한 점. 각 제너레이터가 현재 퍼징중인 객체의 실황을 받아볼 수 있다는 것입니다. 이를 통해 객체의 현 상태를 반영한 값을 생성해 낼 수 있죠. 263 | 264 | > 앞에서 확인한 예시에 따르면, 위 객체를 활용해 slice() 연산을 퍼징할 때 대상 스트링의 길이를 감안한 바이트 오프셋을 만들 수 있다는 사실을 알 수 있습니다. 그럼 더 복잡한 경우에 대해서는 어떨까요? WebGLRenderingContextBase 인터페이스와 연관된 애트리뷰트나 연산이 있다고 합시다. 이 인터페이스는 WebGL로도 WebGL2로도 구현할 수 있는데요, 이 경우 무엇을 선택해 구현하느냐에 따라 들어가는 인수가 정말 많이 달라지게 됩니다. 하지만, 현재 퍼징되고 있는 객체의 실 상황을 이용함으로써 여기 아래에 있는 코드처럼 컨텍스트 타입과 리턴 값을 알맞게 결정할 수 있어요. 265 | > 266 | 267 | ``` 268 | > domino.generateTestcase() 269 | … 270 | const o = [] 271 | o[1] = new Uint8Array(14471) 272 | o[0] = new Blob([null, null, o[1]], { 273 | 'type': 'image/*', 274 | 'endings': 'transparent' 275 | }) 276 | o[2] = o[0].slice((1642420336 % o[0].size), (3884321603 % o[0].size), 'application/xhtml+xml') 277 | o[0].arrayBuffer().then(function (arg0) { 278 | setTimeout(function () { o[0].text().then(function (arg1) { o[0].stream() }) }, 180) 279 | o[2].arrayBuffer().then(function (arg2) { 280 | o[0].slice((3412050218 % o[0].size), (646665894 % o[0].size), 'text/plain') 281 | o[0].stream() 282 | }) 283 | o[2].text().then(function (arg3) { 284 | o[2].slice((2025414481 % o[2].size), (2615146387 % o[2].size), 'text/html') 285 | o[3] = o[0].slice((753872984 % o[0].size), (883984089 % o[0].size), 'text/xml') 286 | o[3].stream() 287 | }) 288 | }) 289 | 290 | ``` 291 | 292 | 이렇게 만든 규칙을 사용하면, Specification 문서의 설명과 더 부합하는 값을 생성해낼 수 있습니다. 293 | 294 | ## **실례** 295 | 296 | 이번 글에서 소개한 예시들은 정말로 많이 단순화되었기 때문에, 실제 사용되는 복잡한 API에 대해서는 어떤 원리가 어떻게 적용되는지 확인이 어려울 수 있겠다는 생각이 들었습니다. 그러니 실제 상황에서 Domino로 발견한 복잡한 취약점 중 하나를 골라 소개해 드리려 해요. 297 | 298 | ![https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/Bug-15585221.svg](https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2020/04/Bug-15585221.svg) 299 | 300 | bug [1558522](https://bugzilla.mozilla.org/show_bug.cgi?id=1558522) 입니다. 링크 들어가 보시면 저희 팀이 [IndexedDBAPI](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)에 영향을 주는 심각한 [Use-After-Free](https://en.wikipedia.org/wiki/Dangling_pointer) 취약점을 찾아냈다고 쓰여 있는데요, 이 취약점은 유발하는 과정이 복잡하기 때문에 이걸 퍼징으로 찾아냈다는 건 퍼징하는 사람 입장에서 굉장히 흥미로운 일이 아닐 수 없습니다. 301 | 302 | Domino가 이걸 어떻게 찾아냈냐고요? Domino는 global context 상에서 파일을 만들어서, 그 파일(객체가 되겠죠)을 IndexedDB 데이터베이스 연결을 수립해 주는 worker context에게로 넘겨줌으로써 취약점을 유발해 찾아냈습니다. 303 | 304 | 이런 수준의 컨텍스트 간 유기성은 기존의 문법으로는 어떻게 설명할 길이 없죠. 하지만 WebIDL이 알려 주는 API의 세세한 정보 덕에 Domino는 이런 취약점도 척척 찾아낼 수 있답니다. 305 | 306 | ## Contributing 307 | 308 | 마지막 참고 사항: Domino는 저희 브라우저에서 심각한 보안 취약점을 계속 찾고 있습니다. 그리고 안타깝게도 이건 우리가 아직 그것을 공개적으로 사용할 수 없다는 뜻이기도 해요. 그래도 최대한 빨리 일반 공개 버전을 출시할 계획은 있으니, 조금만 더 지켜봐 주세요. [Firefox 개발에 코드 기여를](https://codetribute.mozilla.org/) 시작하고 싶다면 저희 문은 언제든지 열려 있으니 편히 생각하시고요, 또 Mozilla 직원이거나 NDA 코드 기고자인데 Domino에서 작업하고 싶으신 분은 Riot(Matrix)의 Fuzzing 룸에 있는 팀에 언제든지 연락하십시오! 309 | 310 | ## Jason Kratzer에 대해: 311 | 312 | - [@pyor_](http://twitter.com/pyoor_) 313 | 314 | [Jason Kratzer의 더 많은 기사…](https://hacks.mozilla.org/author/jkratzermozilla-com/) 315 | -------------------------------------------------------------------------------- /2_fuzzer_development/README.md: -------------------------------------------------------------------------------- 1 | # 2_fuzzer_development 2 | 3 | ## Intro 4 | 5 | 6 | 크롬의 구현 코드와 메모리 구조는 상대적으로 복잡하기 때문에 이제 갓 프로젝트를 시작하려는 우리들은 코드 오디팅만을 하기에는 힘들다는 판단이 있었다. 따라서 자동화 기술 중 우리가 모두 접해본 퍼징을 하기로 하였고, 퍼징을 위한 퍼저 개발을 수행했다. 프로젝트의 전반적인 기간을 퍼저 개발을 하는 데에 쏟았다. 7 | 8 | 결과적으로 우리는 원데이 분석을 통해 4개의 어택 벡터를 선정한 후 각각의 어택 벡터를 퍼징하기 위해 총 4개의 퍼저를 개발하였다. Contents의 문서는 어택 벡터를 퍼징하기 위한 아이디어 및 퍼저의 전체적인 구조, 뮤테이션 전략을 소개하고 퍼저 작동 화면을 보여준 후 마지막 결론에서 퍼저의 작동 결과 및 필요하다면 보완책까지 설명한다. 9 | 10 | ## Contents 11 | 12 | 13 | | 퍼저 이름 | 어택 벡터 | 14 | | --- | --- | 15 | | [Domino](./Domino/) | Dom | 16 | | [WatTF](./watTF/) | WebAssembly | 17 | | [Glitch](./Glitch/) | FreeType | 18 | | [Pleaser](./Pleaser/) | WebRTC | 19 | -------------------------------------------------------------------------------- /2_fuzzer_development/watTF/README.md: -------------------------------------------------------------------------------- 1 | # watTF.md 2 | 3 | # 목차 4 | 1. [퍼징 아이디어](#1-퍼징-아이디어)
5 | 1.1. [배경이 된 취약점: CVE-2018-6036](#11-배경이-된-취약점)
6 | 2. [퍼저 구조](#2-퍼저-구조)
7 | 2.1. [LLVM Compiler](#21-LLVM-Compiler)
8 | 2.2. [wabt](#22-wabt)
9 | 2.3. [dharma](#23-dharma)
10 | 2.4. [fuzzer (python code)](#24-fuzzer)
11 | 3. [뮤테이션 전략](#3-뮤테이션-전략)
12 | 3.1. [구현 방법](#31-구현-방법)
13 | 3.2. [뮤테이션 예시](#32-뮤테이션-예시)
14 | 3.3. [뮤테이션 도식](#33-뮤테이션-도식)
15 | 4. [작동 화면](#4-작동-화면)
16 | 4.1. [사용법](#41-사용법)
17 | 4.2. [작동 화면](#42-작동-화면)
18 | 5. [결론](#5-결론)
19 | 20 | # 1. 퍼징 아이디어 21 | 22 | ## 1.1. 배경이 된 취약점: CVE-2018-6092 23 | 24 | ### 1.1.1. 취약점 유형 25 | 26 | > `==69118==ERROR: AddressSanitizer: heap-buffer-overflow` 27 | > 28 | 29 | Google V8 JavaScript 엔진의 WebAssembly(WASM) 기능에서 Integer underflow 취약점으로 인한 heap-buffer-overflow이다. 30 | 31 | ### 1.1.2. 루트 커즈 32 | 33 | ![Untitled](img/Untitled.png) 34 | 35 | v8은 로컬 함수를 디코딩할 때 검사를 수행하는데, 32bit 플랫폼에서 integer overflow로 인해 이 검사를 건너뛸 수 있다. 이렇게 되면 로컬 함수의 수가 많아지고 로컬이 할당될 때 memory corruption이 발생할 수 있다. 36 | 37 | ```cpp 38 | // src/wasm/function-body-decoder-impl.h 39 | if ((count + type_list->size()) > kV8MaxWasmFunctionLocals) { 40 | decoder->error(decoder->pc() - 1, "local count too large"); 41 | return false; 42 | } 43 | ``` 44 | 45 | count에는 실제로 0xffffffff가 들어가게 되는데, 이때 count의 자료형이 uint32_t이기 때문에 최대값인 4294967295가 들어가게 된다. 46 | 47 | 하지만 계산할때는 integer overflow가 일어나 이 값이 -1로 들어가게 되어 저 if문 검증을 건너뛸 수 있게 된다. 48 | 49 | 결국, 유효하지(검증되지) 않은 count 값을 인자로, insert함수를 실행하게 되고, 여기서 memory corruption이 발생할 수 있다. 50 | 51 | ```cpp 52 | type_list->insert(type_list->end(), count, type); 53 | ``` 54 | 55 | ## 1.2. 퍼징 아이디어 56 | 57 | Count를 늘렸더니 로컬 함수의 개수가 많아지고 이 공간을 할당할 때 메모리 커럽션 발생한 것을 보고 wasm에서 변수 혹은 함수의 Index(순서)를 바꾸고 js에서 바뀐 index에 해당하는 변수나 함수를 호출하게 되면 어떻게 될까?라는 idea를 떠올리게 되었다. 58 | 59 | # 2. 퍼저 구조 60 | 61 | - 설계된 퍼저 구조는 아래와 같다. 62 | 63 | ![Untitled](img/Untitled%201.png) 64 | 65 | - fuzzer의 주요 component는 다음과 같다. 66 | 1. LLVM 컴파일러 67 | 2. wabt 68 | 3. dharma 69 | 4. fuzzer (python code) 70 | 71 | 72 | ## 2.1. LLVM Compiler 73 | 74 | ### 2.1.1. 사용 이유 75 | 76 | wasm seed파일을 만들 수 있는 방법에는 크게 두 가지가 있다. 77 | 78 | **Emscripten을 사용하여 컴파일** 79 | 80 | ![Untitled](img/Untitled%202.png) 81 | 82 | **LLVM을 사용하여 컴파일** 83 | 84 | ![Untitled](img/Untitled%203.png) 85 | 86 | 두 경우의 큰 차이점은 Emscripten으로 컴파일 하였을 경우 JavaScript glue code가 생성이 되지만 LLVM으로 컴파일 할 경우 glue code가 생성되지 않는다는 것이다. 이에 따라 fuzzer의 input 자체의 크기 또한 차이가 커지며 fuzzer의 overhead를 결정짓는 매우 중요한 요인으로 작용한다. 87 | 88 | 따라서 watTF에서는 더 overhead가 적은 LLVM을 통한 컴파일 방법을 사용하기로 하였다. 비록 LLVM으로 컴파일을 하게 되면 JavaScript glue code를 사용할 수 없기에 c코드에 libc함수를 사용하지 못하지만, watTF의 퍼징 벡터는 WebAssembly JavaScript API이며, 이를 fuzzing하는데 libc의 사용은 필수적인 요소가 아니기 때문에 배제하였다. 89 | 90 | ### 2.1.2. 사용법 91 | 92 | - LLVM 컴파일러를 사용하기 위해서 우선적으로 llvm을 설치하여야 한다. 93 | 94 | `sudo apt-get install llvm` 95 | 96 | - 컴파일할 c코드를 생성한다. 97 | 98 | ```c 99 | // Filename: add.c 100 | int add(int a, int b) { 101 | return a*a + b; 102 | } 103 | ``` 104 | 105 | - 다음과 같이 wasm파일을 생성할 수 있다. 아래의 과정은 c를 IR로 변환, IR을 object file로 변환, linking까지의 일련의 과정이 포함되어 있으며 최적화 기능까지 추가되어있는 명령어이다. 106 | 107 | `clang --target=wasm32 -O3 -flto -nostdlib -Wl,--no-entry -Wl,--export-all -Wl,--lto-O3 -o add.wasm add.c` 108 | 109 | ## 2.2. wabt 110 | 111 | ### 2.2.1. 사용 이유 112 | 113 | watTF 퍼저의 가장 핵심 요소는 wasm mutator이다. 이 wasm 파일을 mutation 하기 위해서는 format을 깨지 않는 선에서 변경해야 하기 때문에 필수적으로 wat파일 및 wasm파일의 구조 분석이 필요하며 wat파일을 parsing하는 기능이 필요하다. 때문에 parser를 제작하거나, 또는 기존에 존재하는 parser를 이용하여야 한다. 우리는 이미 존재하는 wabt라는 tool에서 wat파일을 wasm파일로 변경할 때 사용되는 parsing 로직을 그대로 이용하여 mutator를 제작하고자 하였다. 114 | 115 | ### 2.2.2. 제작 과정 116 | 117 | wabt의 코드를 수정하기에 앞서 wasm 파일 구조를 분석할 필요가 있었다. [[공식 문서]](https://webassembly.github.io/spec/core/binary/index.html)를 참고하여 wasm 파일 구조 분석을 진행했다. 118 | 119 | 분석한 wasm 파일 구조를 토대로 wabt를 이용하여 wat파일을 wasm파일로 변환할 때 특정 section을 치환하면서 mutation하는 방법으로 구현하였다. wabt 코드를 어떻게 수정하였는지는 [github](https://github.com/BOB-Jour/WatTF_Fuzzer)에서 확인할 수 있다. 120 | 121 | ### 2.2.3. 사용법 122 | 123 | - 기존의 wabt 사용법에서 -m option을 통해서 생성할 wasm파일의 개수를 추가로 지정해 줄 수 있다. 124 | 125 | `./wat2wasm -m ` 126 | 127 | ## 2.3. dharma 128 | 129 | ### 2.3.1. 사용 이유 130 | 131 | 본격적으로 JavaScript code를 생성하기 위해서 code generator가 필요하였다. 대표적인 grammar based generator로는 domato와 dharma가 있는데, 의도한 token들을 효율적으로 관리하기 위해서 dharma가 더 편리하다는 판단 하에 dharma를 이용하기로 하였다. 132 | 133 | ### 2.3.2. 템플릿 제작 134 | 135 | 위의 wabt로 생성된 wasm파일을 template에 삽입하는 과정이 필요하다. 또 앞서 설명한 것과 같이 wasm파일과 JavaScript 파일은 서로 상호작용이 필요하기 때문에 wasm에 존재하는 함수를 export하는 정보인 exports.txt에 대한 정보를 template에 삽입하여야 한다. 136 | 137 | ### 2.3.3. 사용법 138 | 139 | - 기존 dharma 사용법과 동일하다 140 | 141 | `dharma -grammars