『PHP内核』PHP 7 escapeshellarg底层探究
2021/10/23 7:12:58
本文主要是介绍『PHP内核』PHP 7 escapeshellarg底层探究,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- escapeshellarg描述
- 歧义
- 静态调试
- PHP_FUNCTION(escapeshellarg)
- PHPAPI zend_string *php_escape_shell_arg(char *str)
- 添加前面的引号
- 跳过多字节字符
- PHP历史漏洞:GBK宽字节注入
- 转义
- 添加后面的引号
- 检查结果有效性返回
- 动态调试
- 完
escapeshellarg描述
escapeshellarg把字符串转义为安全的shell参数
escapeshellarg(string $arg): string
- Linux:对传入的字符串用一对单引号包围,将内容的
'
先用反斜杠转义,再添加一对单引号包围,即单引号会被转义为'\''
- Windows:对传入的字符串用一对双引号包围,将内容的
"%!
以空格替换
歧义
PHP Manual中文版翻译为:给字符串增加一个单引号
静态调试
PHP_FUNCTION(escapeshellarg)
函数大概和之前分析过的它的兄弟escapeshellcmd类似
『PHP内核』PHP 7 escapeshellcmd底层探究 CSDN@Ho1aAs
第510行判断传入参数的有效性不同于escapeshellcmd:后者是以传入字符串的长度验证的,而这里是直接以字符串本身来判断,因此传入任何变量都能够进入这个if,即使是NULL;另外:escapeshellcmd在这里还有个else返回空字符串的操作
第二步检验是否传入了空字符,没有则进入功能实现函数,最后通过RETVAL
返回转义后的字符串
PHPAPI zend_string *php_escape_shell_arg(char *str)
进入到PHPAPI类的函数实现
estimate是预估转义后的最大字符串长度,最大长度是Linux下传入单引号的情况:一个单引号被转义成'\''
,然后整个字符串用一堆单引号包裹,再加上最后的结束符,因此是4l+3;然后就是判断传入字符串是否超过了最大单行命令的长度,条件见注释
uint64_t estimate = (4 * (uint64_t)l) + 3; /* max command line length - two single quotes - \0 byte length */ if (l > cmd_max_len - 2 - 1) { php_error_docref(NULL, E_ERROR, "Argument exceeds the allowed length of %zu bytes", cmd_max_len); return ZSTR_EMPTY_ALLOC(); }
给返回值cmd分配内存,大小为4l+2
cmd = zend_string_safe_alloc(4, l, 2, 0); /* worst case */
添加前面的引号
然后就是第一步给返回值的第一个字符赋值为引号:Windows是双引号、Linux是单引号
#ifdef PHP_WIN32 ZSTR_VAL(cmd)[y++] = '"'; #else ZSTR_VAL(cmd)[y++] = '\''; #endif
跳过多字节字符
处理方式同escapeshellcmd
PHP历史漏洞:GBK宽字节注入
同理这一部分也是在PHP 5.2.6 之后加入以修复漏洞
转义
遍历字符转义,分平台
- Windows将
"%!
直接以空格替换 - Linux将
'
首先以反斜杠转义再套上一对单引号,实际操作是自左向右的,借用了default
default是无需转义,直接向cmd赋值
switch (str[x]) { #ifdef PHP_WIN32 case '"': case '%': case '!': ZSTR_VAL(cmd)[y++] = ' '; break; #else case '\'': ZSTR_VAL(cmd)[y++] = '\''; ZSTR_VAL(cmd)[y++] = '\\'; ZSTR_VAL(cmd)[y++] = '\''; #endif /* fall-through */ default: ZSTR_VAL(cmd)[y++] = str[x]; } }
添加后面的引号
接下来是添加后面的引号,这里对于Windows在这之前多处理了一步:如果字符串末尾是个反斜杠需要再用反斜杠转义,为了防止逃出双引号执行命令
#ifdef PHP_WIN32 if (y > 0 && '\\' == ZSTR_VAL(cmd)[y - 1]) { int k = 0, n = y - 1; for (; n >= 0 && '\\' == ZSTR_VAL(cmd)[n]; n--, k++); if (k % 2) { ZSTR_VAL(cmd)[y++] = '\\'; } } ZSTR_VAL(cmd)[y++] = '"'; #else ZSTR_VAL(cmd)[y++] = '\''; #endif ZSTR_VAL(cmd)[y] = '\0';
然后就是添加结束符
检查结果有效性返回
判断转义结果的长度是否有效、判断分配空间是否溢出
if (y > cmd_max_len + 1) { php_error_docref(NULL, E_ERROR, "Escaped argument exceeds the allowed length of %zu bytes", cmd_max_len); zend_string_release(cmd); return ZSTR_EMPTY_ALLOC(); } if ((estimate - y) > 4096) { /* realloc if the estimate was way overill * Arbitrary cutoff point of 4096 */ cmd = zend_string_truncate(cmd, y, 0); } ZSTR_LEN(cmd) = y; return cmd; }
登记cmd的长度,返回转义结果
动态调试
测试代码:
// test.php <?php echo escapeshellarg('"\\');
断点:
先把前面的双引号添加了
然后遍历字符串:第一个字符是双引号,需要转义,直接被空格替换,存入cmd,然后break跳出当前switch,读入下一个字符
第二个字符是反斜杠,无需转义,存入cmd
转义结束,之后检测末尾是否是反斜杠,是就再将其转义一次
添加后面的引号,加上结束符截断
检查结果有效,完成操作,返回
完
欢迎在评论区留言,欢迎关注我的CSDN @Ho1aAs
这篇关于『PHP内核』PHP 7 escapeshellarg底层探究的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-19php8的协程和hyperf的协程有什么区别?-icode9专业技术文章分享
- 2024-12-19php8 的fiber是什么?-icode9专业技术文章分享
- 2024-12-05怎么在php8,1 里面开启 debug?-icode9专业技术文章分享
- 2024-12-05怎么在php8,1 里面开启 debug?-icode9专业技术文章分享
- 2024-11-29使用PHP 将ETH账户的资产汇集到一个账户
- 2024-11-23怎么实现安卓+php 热更新方案?-icode9专业技术文章分享
- 2024-11-22PHP 中怎么实现判断多个值是否为空、null 或者为 false?-icode9专业技术文章分享
- 2024-11-11开源 PHP 商城项目 CRMEB 二次开发和部署教程
- 2024-11-09怎么使用php在kaufland平台刊登商品?-icode9专业技术文章分享
- 2024-11-05PHP的抽象类和接口是什么,有什么区别-icode9专业技术文章分享