🧠 Path Traversal
What is it?
- Concept: The server used does not sanitize user input or uses a flawed filtering/sanitizing logic that allow malicious actor to traverse through the local file system.
- Impact: It is a way to achieve arbitrary file access (either read or write or execute - aka. LFI).
Exploitations
Common Obfuscation
- URL Encodings:
.becomes%2e(or%2E) and%252E(double encoding)/becomes%2f(or%2F) and%252F(double encoding)\becomes%5c(or%5C) - Useful for Windows backends
URL Encode:
# ../
%2e%2e%2f
# example
GET /image?file=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswdDouble URL Encode:
# ../
%252E%252E%252F
#example
GET /image?file=%252E%252E%252F%252E%252E%252F%252E%252E%252Fetc%252Fpasswd- The
....//bypass
# example
GET /image?file=....//....//....//etc/passwdPHP Object Hex Obfuscation
Sometimes the path is inside a PHP object and the server filter it using something likestripos($input, '..'), you can obfuscate your injected path inside the object with HEX-encoding using the S: flag inside the object.
S: flag indicates that the field is a string but unlike its lowercase version, S: accepts input as HEX-encoding when the object is being deserialized (like being passed to unserialized()).
Common HEX-encoding of charaters related to path traversal:
.becomes\2e/becomes\2f\becomes\5c\0null bytes becomes\00
Javascript string.includes() Bypass
Javascript has a rather annoying function called includes() that gives you a boolean whether the input string contains a certain substring or not, a famous example of such a usage is the HTB - Next Path challenge where input.includes("/") || input.includes("..") was implemented in order to stop Path Traversal.
However, in Javascript array also has its own includes() methods that check whether a certain ELEMENTS is being contained inside the array or not.
const array1 = ["123", "/"];
console.log(array1.includes("/"));
// output: true
const array2 = ["123", "../../etc/passwd"];
console.log(array2.includes("/"));
// output: false If a POST endpoints allows you to input a array inside a json body you can do something like this to inject and array:
{
"Message":["item1", "item2"]
}As for GET endpoints you can sometimes parse an array by using duplicate parameters (i.e. ?id=&id=...). This is called Parameter Pollution if the back-end does not handle this correctly. Here are some Framework/Library that supports this:
Takes duplicates parameters as Array:
- Express.js (Node.js)
- Koa.js (Node.js)
- Ruby on Rails
Only takes the First parameters as value:
- Python (Flask / Werkzeug): To get the array, the developer specifically has to know to call
request.args.getlist('id') - Modern Javascript (
URLSearchParams): UseURLSearchParams.getAll('id')to get array input.
Only takes the Last parameters as value:
- PHP: use the brackets syntax
?id[]=to get an array input.
Mitigation
- Normalization before Validation: Always normalize the input path into its real form, collapses all path traversal, then check it with a white-list before actually validate the path.
- Indirect Object Reference: If possible, never allow user input a path in the first place, instead, map the filenames to a database id, user can only input the id, the database looks it up, and serves the file at the corresponding location.
- Strict Type Checking: To prevent parameter poisoning, we must strictly verify that the input is a string
- Use Schema Validation: Route the incoming request data through a schema validator like Zod or Joi, etc. If the schema defines the input is a string, inputting an array using duplicate parameters will be rejected.
- Stop Allow Passing An Unserialized Object: Bro, just use Json, please. Passing a serialized object is dangerous, and using
unserialize()is also dangerous. If you are using PHP version 7.0.0, please update to version 8, so that things like (incomplete) PHP Archive Deserialization can never exist and do path traversal.
Related Usage
TABLE creation_date AS "Created"
FROM "05 - Content"
WHERE contains(techniques, this.file.link) AND contains(tags, "🚩")
SORT file.name ASC