Apache HTTP/2 Double Free 취약점 대응과 무중단 패치 운영

CVE-2026-23918 Apache HTTP Server mod_http2 취약점의 공격 조건, 패치 적용, HTTP/2 임시 완화와 롤백 운영 방법을 정리한다.

2026-08-14 · 최초 발행 2026-05-16

Apache HTTP Server 2.4.66의 mod_http2에는 인증 없이 원격 코드 실행(RCE)을 유발할 수 있는 CVSS 8.8 취약점 CVE-2026-23918이 있다. HEADERS 프레임 뒤에 RST_STREAM 프레임을 연속으로 보내는 방식으로 힙 손상이 일어나며, APR mmap 기반 환경인 Debian 계열과 공식 Docker 이미지에서는 실질적인 RCE 위험이 높다. 대응은 취약점의 메모리 정리 경로를 이해하는 일과, 서비스 중단 없이 2.4.67로 전환하는 운영 설계를 함께 요구한다.

영향을 받는 Apache 환경과 공격 조건

영향 범위는 Apache HTTP Server 2.4.66으로 한정된다. 2.4.65 이하는 물론 2.4.67 이상도 영향권에 포함되지 않는다. CWE-415(Double Free)로 분류되며, HTTP/2가 기본 활성화된 2.4.66에서 멀티스레드 MPM인 worker 또는 event를 사용하는 서버가 대상이다.

항목 내용
CVE ID CVE-2026-23918
CVSS 점수 8.8 (High)
CWE 분류 CWE-415 Double Free
영향 버전 Apache HTTP Server 2.4.66
패치 버전 Apache HTTP Server 2.4.67
취약 모듈 mod_http2 (h2_mplx.c)
공격 요구사항 인증 불필요, TCP 접근 가능한 모든 클라이언트
영향 DoS(확실), RCE(APR mmap 환경에서 가능)

문제의 프레임 조합은 HTTP/2 RFC 명세를 벗어나지 않는다. 따라서 네트워크 계층에서 비정상 요청으로 분류하기 어렵고, 기존 WAF 및 IDS 시그니처 다수는 이를 놓칠 수 있다.

HEADERS와 RST_STREAM 사이에서 생기는 이중 해제

문제는 h2_mplx.c의 스트림 정리 경로에 있다. 공격자는 새 스트림 ID의 HEADERS 프레임을 보낸 뒤, 같은 스트림에 비영(non-zero) 에러 코드를 포함한 RST_STREAM 프레임을 즉시 전송한다.

이때 멀티플렉서가 스트림을 내부 자료구조에 등록하기 전 리셋이 도착하는 경쟁 조건이 만들어진다. nghttp2는 RST 처리를 위한 on_frame_recv_cb와 스트림 종료를 위한 on_stream_close_cb를 차례로 호출한다. 두 콜백은 모두 h2_mplx_c1_client_rst → m_stream_cleanup을 거쳐 같은 스트림 객체를 정리하려 한다. 이후 c1_purge_streams가 purge 큐를 순회하며 h2_stream_destroy를 호출하면, 이미 해제된 메모리를 다시 해제하는 double free가 발생한다.

"힙 메모리""h2_mplx.c 멀티플렉서""nghttp2 라이브러리""공격자 클라이언트""힙 메모리""h2_mplx.c 멀티플렉서""nghttp2 라이브러리""공격자 클라이언트""스트림 등록 전 리셋 수신""HEADERS frame (stream_id=N)""RST_STREAM (stream_id=N, error!=0)""on_frame_recv_cb (RST 처리)""(1) h2_stream_destroy → free(stream)""on_stream_close_cb (종료 처리)""(2) m_stream_cleanup → free(stream) ← DOUBLE FREE!""힙 손상 (Heap Corruption)""DoS 또는 RCE 가능"

손상된 힙 영역을 제어할 수 있게 되면 임의 코드 실행 시도가 가능해진다. APR(Apache Portable Runtime)이 mmap 기반 메모리 할당을 사용하는 환경에서는 그 제어 가능성이 더 높아진다.

스트림 수명과 정리 책임을 분리하는 방식

mod_http2의 스트림 상태 머신은 IDLE → OPEN → HALF_CLOSED → CLOSED 생명주기를 따라야 한다. CVE-2026-23918에서는 스트림이 OPEN으로 완전히 전환되기 전에 CLOSED 전환이 요청되는 경계 조건을 처리하지 못했다.

스트림 객체에 원자적 참조 카운터를 두면 카운터가 0일 때만 실제 메모리 해제가 실행된다. 두 콜백이 하나의 객체를 참조하더라도 마지막 참조가 해제될 때만 free()가 호출된다.

2.4.67의 h2_mplx.c 패치는 스트림 정리 경로를 하나로 묶어, 실제 메모리 해제를 담당하는 경로가 하나만 남도록 소유권을 재설계했다. nghttp2 콜백이 진입할 때 스트림 등록 상태를 검사하는 가드 조건도 추가되어 미등록 스트림 정리를 막는다.

mod_http2 강화를 위해 다음 설정을 적용할 수 있다.

# Apache 2.4.67+ 권고 설정
<IfModule http2_module>
    H2MaxSessionStreams 100
    H2MaxWorkerIdleSeconds 600
    H2MinWorkers 1
    H2MaxWorkers 25
    H2StreamMaxMemSize 65536
    H2Push off
</IfModule>

H2MaxSessionStreams를 제한하면 많은 스트림을 한꺼번에 열어 자원을 고갈시키는 DoS 공격 표면도 줄일 수 있다.

로드밸런서 뒤에서 패치를 전개하는 흐름

CVSS 8.8 취약점은 즉각적인 대응 대상이지만, 프로덕션 서비스에서는 가용성을 유지하며 업그레이드해야 한다. 로드밸런서에서 노드를 순차적으로 빼고, 패치와 검증을 마친 뒤 다시 편입하는 롤링 방식이 이 요구에 맞는다.

(1) 트래픽 제거YesNo(2) 트래픽 제거(3) 트래픽 제거로드밸런서Apache 2.4.66 - Node AApache 2.4.66 - Node BApache 2.4.66 - Node CApache 2.4.67 - Node A(패치됨)Apache 2.4.67 - Node B(패치됨)Apache 2.4.67 - Node C(패치됨)헬스체크통과?롤백 실행업그레이드 완료

먼저 대상 노드를 로드밸런서에서 제외한다. HAProxy에서는 disable server backend/node-a를 사용하고, Nginx upstream에서는 해당 서버를 down으로 표시한다.

기존 연결은 자연스럽게 drain되도록 둔다. Apache를 graceful 방식으로 정지하면 처리 중인 요청이 끝난 뒤 종료된다.

# Apache graceful stop (현재 요청 처리 후 종료)
apachectl graceful-stop

# 패키지 업그레이드 (Debian/Ubuntu)
apt-get install --only-upgrade apache2=2.4.67-*

# 설정 검증 후 재시작
apachectl configtest && apachectl start

헬스체크에서 정상 동작을 확인한 노드만 로드밸런서에 다시 등록한다.

카나리 검증과 되돌릴 경로

보안 패치도 설정 호환성 문제를 일으킬 수 있다. 전체 플리트에 한 번에 적용하기보다 카나리 노드에서 먼저 관찰하고 범위를 넓혀야 한다.

5%95%YesNo로드밸런서(트래픽 분배)카나리 노드2.4.67 (5% 트래픽)프로덕션 노드2.4.66 (95% 트래픽)모니터링(에러율, 레이턴시)임계값초과?롤백:2.4.66 복원점진적 확대25% 50% 100%

카나리에서는 HTTP 5xx 에러율이 기준값보다 0.1% 이상 오르는지, 응답 레이턴시 P99가 기준값보다 20% 이상 증가하는지를 확인한다. Worker 프로세스 crash 빈도 증가와 h2_mplx, mod_http2 관련 HTTP/2 연결 오류 로그도 즉시 검토 대상이다.

패키지 다운그레이드가 어려운 환경이라면 이전 바이너리를 /opt/apache/versions/에 보관하고 심볼릭 링크 전환으로 롤백할 수 있어야 한다.

# 이전 버전 보관 구조
/opt/apache/versions/2.4.66/
/opt/apache/versions/2.4.67/
/opt/apache/current -> /opt/apache/versions/2.4.67/

# 롤백 실행
ln -sfn /opt/apache/versions/2.4.66 /opt/apache/current
apachectl graceful-stop && apachectl start

패치 전 HTTP/2 노출을 줄이는 방법

즉시 패치할 수 없는 환경에서는 WAF를 임시 방어선으로 사용할 수 있다. 다만 공격 시퀀스 자체가 RFC를 준수하는 프레임 조합이므로, HTTP/2 프레임 계층을 검사할 수 있는 WAF여야 탐지가 가능하다.

가장 확실한 임시 조치는 HTTP/2를 끄는 것이다. 클라이언트 성능은 저하되지만 취약 코드 경로가 사라진다.

# httpd.conf 또는 VirtualHost 설정
# HTTP/2 비활성화 (HTTP/1.1만 허용)
Protocols http/1.1

# 또는 모듈 언로드
# /etc/apache2/mods-enabled/http2.load 제거 후 재시작

HTTP/2 프레임 분석이 가능한 경우에는 RST_STREAM 연속 패턴을 대상으로 ModSecurity 규칙을 배포할 수 있다.

# ModSecurity 규칙 예시 (OWASP CRS 확장)
SecRule REQUEST_PROTOCOL "@streq HTTP/2.0" \
    "id:9000001,\
    phase:1,\
    block,\
    msg:'CVE-2026-23918: Suspicious HTTP/2 early reset pattern',\
    logdata:'%{REQUEST_HEADERS}',\
    tag:'CVE-2026-23918'"

Nginx나 HAProxy를 Apache 앞에 두고, 프록시에서 HTTP/2를 종단한 뒤 Apache 백엔드에는 HTTP/1.1로 전달하는 구성도 임시 완화책이 된다.

# Nginx 역방향 프록시 설정 (HTTP/2 종단)
upstream apache_backend {
    server 127.0.0.1:8080;  # Apache는 HTTP/1.1만 수신
}

server {
    listen 443 ssl http2;
    location / {
        proxy_pass http://apache_backend;
        proxy_http_version 1.1;  # 백엔드 연결은 HTTP/1.1
    }
}

이 구조에서는 Apache가 HTTP/2 트래픽을 직접 받지 않으므로 mod_http2 취약점이 트리거되지 않는다.

로그와 네트워크에서 징후를 찾는 기준

네트워크 계층만으로는 탐지에 제약이 있지만, 로그와 프로세스 지표를 함께 보면 공격 시도를 어느 정도 식별할 수 있다.

# 비정상적으로 높은 RST 관련 에러 탐지
grep "AH02429\|h2_mplx\|stream_cleanup" /var/log/apache2/error.log | \
  awk '{print $1, $2}' | sort | uniq -c | sort -rn | head -20

# HTTP/2 스트림 에러 집계
grep "h2" /var/log/apache2/error.log | \
  grep -E "rst|reset|double|free" | wc -l

Suricata 또는 Snort에서는 다음 시그니처를 사용할 수 있다.

alert tcp any any -> $HTTP_SERVERS $HTTP_PORTS (
    msg:"CVE-2026-23918 Apache HTTP2 Early Reset DoS Attempt";
    flow:established,to_server;
    content:"|00 00 00 04|";  # HTTP/2 RST_STREAM frame type
    threshold:type both, track by_src, count 50, seconds 10;
    classtype:attempted-dos;
    sid:2026239180;
    rev:1;
)

단일 IP에서 초당 50회 이상의 짧은 수명 HTTP/2 스트림이 생기는 패턴, Worker 프로세스 비정상 종료 빈도, httpd 프로세스 메모리 사용량 급증을 모니터링 대시보드에 포함한다. 마지막 항목은 힙 손상 징후가 될 수 있다.

HTTP/1.1 다운그레이드인터넷 트래픽CDN / DDoS 방어WAF(HTTP/2 프레임 검사)역방향 프록시(HTTP/2 종단점)IDS/IPS(이상 탐지)Apache 2.4.66(HTTP/1.1만 수신)SIEM / 로그 집계

업그레이드 전후에 확인할 항목

업그레이드 전에 현재 Apache 버전과 컴파일 옵션(apache2 -V), 활성 모듈(apache2ctl -M), 설정 파일 전체(/etc/apache2/ 또는 /etc/httpd/)를 기록하거나 백업한다. apache2ctl -M | grep http2로 HTTP/2 사용 여부를, apache2ctl -M | grep mpm으로 MPM 유형을 확인한다. 현재 성능 기준값, 롤백용 이전 바이너리 보관 경로, 모니터링 알림 임계값도 준비한다.

업그레이드 뒤에는 apache2 -v | grep 2.4.67로 버전을 확인하고, apachectl configtestapachectl -M으로 설정 및 모듈 로딩 상태를 검증한다. curl -I --http2 https://your-domain.com으로 HTTP/2 동작을 점검하며, tail -f /var/log/apache2/error.log에서 이상 로그가 없는지 본다. 헬스체크 엔드포인트 응답과 성능 메트릭을 기준값과 비교한 뒤, WAF 및 IDS의 CVE-2026-23918 임시 규칙을 업데이트하거나 제거한다.

2.4.67에서는 mod_http2 관련 기본값 일부가 변경되었을 수 있다. H2 디렉티브 전체를 검토하고 CHANGELOG 또는 공식 보안 권고문을 참조한다.

CVE-2026-23918은 정상 HTTP/2 프레임 조합이 메모리 관리 버그와 결합한 사례다. Apache 2.4.66 운영 환경에서는 2.4.67 업그레이드가 우선이며, 전환 전에는 HTTP/2 비활성화나 역방향 프록시를 통한 HTTP/1.1 다운그레이드로 노출을 줄일 수 있다. 롤링 업그레이드와 카나리 배포, 롤백 경로를 묶어 운영하면 보안 대응과 가용성 요구를 함께 다룰 수 있다.

Sources

Apache HTTP ServerCVE-2026-23918HTTP/2취약점 대응패치 관리