## 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.

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 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.*