Share
## https://sploitus.com/exploit?id=563E7E32-66C1-5004-A157-37FB51446E5B
# picoCTF 2025 โ€” Unssafe Eval (Web Exploitation)

**Challenge:** Bank-Loan Calculator  
**Category:** Web Exploitation  
**Author:** Theoneste Byagutangaza  
**Solved by:** [6876h9](https://github.com/6876h9)  
**Flag:** `picoCTF{D0nt_Use_Unsecure_f@nctionsf847a9bc}`

---

## Description

> ABC Bank's website has a loan calculator to help its clients calculate the amount they pay if they take a loan from the bank. Unfortunately, they are using an `eval` function to calculate the loan. Bypassing this will give you Remote Code Execution (RCE). Can you exploit the bank's calculator and read the flag?

**Hints provided:**
- Bypass regex
- The flag file is `/flag.txt`
- You might need encoding or dynamic construction to bypass restrictions

---

## Reconnaissance

Navigating to the challenge URL presents a simple loan calculator interface that accepts a formula string, evaluates it server-side, and returns the result.

![Calculator Interface](screenshots/calculator.png)

The placeholder text in the input field hints at the expected format:

```
PRT*RATE*TIME(10000*23*12)
```

This is a standard arithmetic-style expression โ€” but the backend is using Python's `eval()` to process it, which means anything Python can evaluate will be executed.

---

## Vulnerability Analysis

The backend is running a Python web application (Flask or similar) that calls `eval()` on user-supplied input. A simplified version of the vulnerable code pattern is:

```python
# Vulnerable server-side pattern
expression = request.form['formula']
result = eval(expression)  # Arbitrary code execution
```

This is a textbook case of **unsafe `eval()` usage**. Python's `eval()` does not sandbox the execution environment โ€” it has access to builtins including `open()`, `__import__()`, and `os`.

To prevent obvious exploitation, the server applies a **regex-based blacklist** that blocks certain keywords and characters. Based on the hints and the bypass technique required, the filter blocks patterns such as:

- The literal string `open`
- Forward slash `/`
- The literal string `flag`
- The literal dot `.` in string context

---

## Bypass Strategy

Since the filter operates on the literal string content of the input, the bypass is to **dynamically construct the blocked strings at runtime** using Python's `chr()` function, which converts an integer to its ASCII character equivalent.

| Character | ASCII Decimal | `chr()` Equivalent |
|-----------|--------------|---------------------|
| `/`       | 47           | `chr(47)`           |
| `.`       | 46           | `chr(46)`           |

The target operation is:

```python
open('/flag.txt').read()
```

Which, after encoding the filtered characters, becomes:

```python
open(chr(47)+'flag'+chr(46)+'txt').read()
```

This constructs the path `/flag.txt` at runtime, entirely bypassing the static string filter.

---

## Exploitation

Submit the following payload into the formula input field:

```
open(chr(47)+'flag'+chr(46)+'txt').read()
```

The server evaluates this expression, opens `/flag.txt`, and returns its contents as the result.

![Flag Result](screenshots/result.png)

**Flag retrieved:**

```
picoCTF{D0nt_Use_Unsecure_f@nctionsf847a9bc}
```

---

## Root Cause

Python's `eval()` function evaluates **any** valid Python expression within the interpreter's current scope. It is not a sandboxed calculator โ€” it is a full code execution primitive. Regex-based blacklisting is an inherently flawed defence against this because:

1. Python provides multiple ways to construct the same string at runtime (`chr()`, string concatenation, `bytes.fromhex()`, list comprehensions, etc.).
2. A blacklist can never be exhaustively comprehensive against a Turing-complete language.
3. The filter operates on the static input string, not on the semantics of the expression being evaluated.

---

## Remediation

The only correct fix is to **never use `eval()` on user input**. For a loan calculator, the correct approach is to use a dedicated, safe expression parser:

```python
import ast
import operator

# Safe arithmetic evaluation using AST
SAFE_OPS = {
    ast.Add: operator.add,
    ast.Sub: operator.sub,
    ast.Mult: operator.mul,
    ast.Div: operator.truediv,
}

def safe_eval(expr):
    tree = ast.parse(expr, mode='eval')
    return eval_node(tree.body)

def eval_node(node):
    if isinstance(node, ast.Constant):
        return node.value
    elif isinstance(node, ast.BinOp):
        op = SAFE_OPS.get(type(node.op))
        if op is None:
            raise ValueError("Unsupported operator")
        return op(eval_node(node.left), eval_node(node.right))
    else:
        raise ValueError("Unsupported expression type")
```

This approach only permits arithmetic operations and raises an exception on anything else, including file I/O, imports, and attribute access.

---

## References

- [OWASP โ€” Code Injection](https://owasp.org/www-community/attacks/Code_Injection)
- [Python `eval()` Security Considerations](https://docs.python.org/3/library/functions.html#eval)
- [picoCTF 2025](https://picoctf.org)

---

*Writeup authored by [6876h9](https://github.com/6876h9). Challenge authored by Theoneste Byagutangaza.*