본문 바로가기

1-day 취약점 분석

[ 1-Day ] HTTP/2 PUSH_PROMISE DoS

본 포스팅은 학습 목적으로 작성되었으며, hackerone report를 기반으로 작성되었습니다. 

 

분석 레포트

https://hackerone.com/reports/2402853

 

curl disclosed on HackerOne: HTTP/2 PUSH_PROMISE DoS

 

hackerone.com

 

discard_newhandle()에서 if 문의 조건은 부정(!)으로 인해 http 전송에 대해 항상 거짓이기에, http2_data_done은 절대로 호출되지 않는다. 

 

static void discard_newhandle(struct Curl_cfilter *cf,
                              struct Curl_easy *newhandle)
{
  if(!newhandle->req.p.http) {
    http2_data_done(cf, newhandle, TRUE);
    newhandle->req.p.http = NULL;
  }
  (void)Curl_close(&newhandle);
}

 

discard_newhandle은 push_promise에서 오류가 발생하면 스트림을 닫고 http2_data_setup에 할당된 리소스를 해제할 뿐만 아니라 Curl_easy 핸들을 닫아야 한다. 예를 들어 PUSH_PROMISE 프레임에 invailid :scheme 의사 헤더 set_transfer_url이 있는 경우 push_promise에서 오류를 반환한다.

 

    rv = set_transfer_url(newhandle, &heads);
    if(rv) {
      discard_newhandle(cf, newhandle);
      rv = CURL_PUSH_DENY;
      goto fail;
    }

 

공격자는 특수하게 조작된 PUSH_PROMISE 프레임을 전송하여, 오류를 유발할 수 있다. 이렇게 하면 수신된 모든 잘못된 프레임에 대해 메모리 누수가 발생하여 결과적으로 사용가능한 모든 메모리를 사용하게 된다. 

 

1. nghttp_v1.59.patch(F3099706 )를 적용한 nghttp2를 컴파일한다.
2. http2_push_promise.c(F3099707)를 컴파일한다.
3. nghttpd -p/=/foo.bar --no-tls 8181을 실행한다.
4. 실행 valgrind --leak-check=full ./http2_push_headers

각 -p 옵션에 대해 nghttpd는 유효하지 않은 :scheme 헤더를 가진 200개의 PUSH_PROMISE 프레임을 보낸다.

 

 

valgrind --leak-check=full ./http2_push_headers 의 결과(output)

 

==5247== 
==5247== HEAP SUMMARY:
==5247==     in use at exit: 162,946 bytes in 873 blocks
==5247==   total heap usage: 7,170 allocs, 6,297 frees, 1,696,049 bytes allocated
==5247== 
==5247== 70,400 bytes in 200 blocks are definitely lost in loss record 6 of 7
==5247==    at 0x48485EF: calloc (vg_replace_malloc.c:1340)
==5247==    by 0x48ADC29: http2_data_setup (http2.c:249)
==5247==    by 0x48AF154: h2_duphandle (http2.c:789)
==5247==    by 0x48AF420: push_promise (http2.c:877)
==5247==    by 0x48AFCF6: on_stream_frame (http2.c:1065)
==5247==    by 0x48B08C7: on_frame_recv (http2.c:1265)
==5247==    by 0x4C36AE3: nghttp2_session_mem_recv (in /usr/lib64/libnghttp2.so.14.26.0)
==5247==    by 0x48AE851: h2_process_pending_input (http2.c:551)
==5247==    by 0x48B294F: h2_progress_ingress (http2.c:1930)
==5247==    by 0x48B2B54: cf_h2_recv (http2.c:1969)
==5247==    by 0x4877F03: Curl_conn_recv (cfilters.c:183)
==5247==    by 0x48DB1B3: Curl_read (sendf.c:813)
==5247== 
==5247== LEAK SUMMARY:
==5247==    definitely lost: 70,400 bytes in 200 blocks
==5247==    indirectly lost: 0 bytes in 0 blocks
==5247==      possibly lost: 0 bytes in 0 blocks
==5247==    still reachable: 92,546 bytes in 673 blocks
==5247==         suppressed: 0 bytes in 0 blocks
==5247== Reachable blocks (those to which a pointer was found) are not shown.
==5247== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==5247== 
==5247== For lists of detected and suppressed errors, rerun with: -s
==5247== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

 

 

영향

 

DOS

 

조치

 

코드 수정을 실시하였다. 

 

static void discard_newhandle(struct Curl_cfilter *cf,
                              struct Curl_easy *newhandle)
{
  //if(!newhandle->req.p.http) {
  if(newhandle->req.p.http) {
    http2_data_done(cf, newhandle, TRUE);
    //newhandle->req.p.http = NULL;
  }
  (void)Curl_close(&newhandle);
}

 

 

 

참고