RCE
靶场
- eval执行
1 | <?php |
最简单的漏洞题 知识点直接在wp里面找 但几乎都知道
$_REQUEST 是一个超全局变量,它包含了通过 GET、POST 或 COOKIE 方法传递的参数。
eval() 函数会将传递进来的 cmd 参数的值作为 PHP 代码来执行。使用system(),让eval直接执行我们对系统的操作 就是普通的/?cmd=system("ls /") 然后查看即可
exec(),执行系统命令,默认仅返回命令输出的最后一行
语法:exec(string $command, array $output = null, int $return_var = null): string|false
$command:要执行的系统命令;
$output:可选,数组引用,存储命令的所有输出行;
$return_var:可选,整数引用,存储命令的退出状态码(0 表示执行成功,非 0 为失败)。
特点:无直接输出(需手动打印$output),适合需要获取结构化输出的场景,不输出二进制数据,我在靶场里面没有用到过。
system(),执行系统命令,直接将命令输出打印到页面.
passthru()
作用:执行系统命令,直接将原始输出(包括二进制数据)打印到页面,无返回值。
shell_exec() 无直接输出,需手动打印,也就是自己先存然后echo出来 基本用不太到
有一个特殊的,反引号等价性:PHP 中,将命令用反引号包裹,效果与shell_exec()完全一致(如ls -l等价于shell_exec(‘ls -l’))
2. php里面 error_reporting(0);:将错误报告级别设置为 0,也就是关闭所有错误报告,就是运行的时候即使出现问题也不会在页面上报错,为了隐藏在代码运行时潜在的问题
include():语句的主要作用是将指定文件的内容插入到当前 PHP 文件中该语句所在的位置,并当作当前文件的一部分来执行。这里在upload靶场就见过 可以说是风险很大的一个函数 用上本身的文件然后加上rce即可
文件包含需要 (1)采用 include 等文件包含函数,并且需要包含的文件路径是通过用户传输参数的方式引入;
(2)用户能够控制包含文件的参数,被包含的文件可被当前页面访问
3. php://input php://input 用于 直接读取 HTTP 请求体(request body)中的原始数据。这里用文章例子来说明
1 | <?php |
也就是一种直接读取原始 POST 数据的方式 前提是若要启用 allow_url_include,首先需要确保 allow_url_fopen 也是启用的,因为 allow_url_include 依赖于 allow_url_fopen 发送post的<?php system('ls /');?>即可
4. php://filter 用于在读取 / 写入数据流(比如读取文件内容、处理输入输出)时,对数据进行过滤 / 转换处理 数据在经过这个通道时,会按照你指定的规则被处理(比如编码、解码、大小写转换等)。基本语法是这样的php://filter/[过滤规则]/resource=[目标资源] 过滤规则一般常用的就是读取文件并 Base64 编码输出 多个过滤器用|分隔,按顺序处理 比如
1 | $content =file_get_contents("php://filter/convert.base64encode|string.toupper/resource=test.txt") |
php://filter/read = convert.base64 - encode/resource=/flag
文件包含获取flag的条件:
(1)攻击者需要知道文件存放的物理路径;
(2)对上传文件所在目录拥有可执行权限;
(3)存在文件包含漏洞;
- 命令过滤 这里靶场主要都是过滤 下面详细记录绕过过滤
- 关于管道操作符
windows:
- “|”:直接执行后面的语句。
- “||”:如果前面的语句执行失败,则执行后面的语句,前面的语句只能为假才行。
- “&”:两条命令都执行,如果前面的语句为假则直接执行后面的语句,前面的语句可真可假。
- “&&”:如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句为真则两条命令都执行,前面的语句只能为真。
linux:
- “;”:执行完前面的语句再执行后面的语句。
- “|”:显示后面语句的执行结果。
- “||”:当前面的语句执行出错时,执行后面的语句。
- “&”:两条命令都执行,如果前面的语句为假则执行执行后面的语句,前面的语句可真可假。
- “&&”:如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句为真则两条命令都执行,前面的语句只能为真。
查询cat的替代
(1)more:一页一页的显示的显示档案内容
(2)less:与more类似,但是比more更好的是,他可以[pg dn][pg up]翻页
(3)head:查看头几行
(4)tac:从最后一行开始显示,可以看出tac是cat的反向显示
(5)tail:查看尾几行
(6)nl:显示的时候,顺便输出行号
(7)od:以二进制的方式读取档案内容
(8)vi:一种编辑器,这个也可以查看
(9)vim:一种编辑器,这个也可以查看
(10)sort:可以查看
(11)uniq:可以查看
(12)file -f:报错出具体的内容
此处引用:https://blog.csdn.net/m_de_g/article/details/118929528关键字绕过
$@是位置参数变量,如果没有给命令传参(比如这里),$@会被解析为空字符串
\绕过
用 ${PATH:0:1} 来代替目录分隔符 /空格绕过
; 的绕过方式
;
%0a
%0d
&
终极过滤
1 | 127.0.0.1%0acd${IFS}fl$*a$*g_is_here%0amore${IFS}fl$*a$*g_295443149417453.php |
用到的原文链接:https://blog.csdn.net/2401_86190146/article/details/145136049
无字母数字 RCE
这里的思路就是利用各种非数字字母的字符,经过各种变换(异或、取反、自增),构造出单个的字母字符
- 异或 在php里,两个值进行异或,会先转变成ASCII在转变成2进制异或^:数字相同为1不同为0(1^1=1,1^0=0),利用这个特性来构造字符串0&0=0;1&1=0;0&1=1;1&0=1
例如:’a’^’$’=E 需要两个字符才能构造出一个字符 - 取反 原理大概 32位表示二进制的12:
0000 0000 0000 0000 0000 0000 0000 1100
取反结果:
1111 1111 1111 1111 1111 1111 1111 0011
观察取反后的结果:从左向右看,第一位0正1符
-111 1111 1111 1111 1111 1111 1111 0011
负数是用补码表示的,补码是原码取反+1,也就是说
111 1111 1111 1111 1111 1111 1111 0011是某个数取反+1得到的。反过来,也就是
111 1111 1111 1111 1111 1111 1111 0011先-1,结果是
111 1111 1111 1111 1111 1111 1111 0010,再取反是
000 0000 0000 0000 0000 0000 0000 1101
1101就是:12的三次方+12的二次方+02的一次方+12的零次方
即1101就是:8+4+1=13,别忘了前边的负号,最终结果-13
还有一种
利用的是UTF-8编码的某个汉字,并将其中某个字符取出来,然后再进行一次取反操作,就能得到一个我们想要的字符
原文链接:https://blog.csdn.net/WilliamsWayne/article/details/78259501 - 自增 在 PHP 中,可以递增非数字字符串
1 | 下划线这个可以当作变量名例如:$_ $__这样 |
payload都很长 网上有已经写好的脚本可以在本地转化 也有很多现成payload
无参数RCE
就是说在无法传入参数的情况下,仅仅依靠传入没有参数的函数套娃就可以达到命令执行的效果 只能使用没有参数的php函数 看了一篇很好很详细的文章 链接如下https://www.cnblogs.com/pursue-security/p/15406272.html
在本地搭建了类似的过滤
1 | <?php |
- getallheaders() 这个函数的作用是获取
http所有的头部信息,也就是headers,然后我们可以用var_dump把它打印出来,但这个有个限制条件就是必须在apache的环境下可以使用,其它环境都是用不了的 直接end()函数取出最后一位 它只会以字符串的形式取出值而不会取出键GET /1.php?code=eval(end(getallheaders())); HTTP/1.1...flag: system('id');这里对php版本有限制的 5可以 7就不行了 高版本没这个函数了(居然还是有类似的 7以上的 payload类似于
GET /1.php?exp=eval(end(apache_request_headers()));HTTP/1.1...flag: system('id');) - get_defined_vars() 没有服务器的限制条件 获取的四个全局变量
$_GET $_POST $_FILES $_COOKIEcurrent()函数,这个函数的作用是返回数组中的当前单元,而它的默认是第一个单元?code=eval(end(current(get_defined_vars())));&flag=system('ls') 而如果网站对$_GET,$_POST,$_COOKIE都做的过滤, 那我们只能从$_FILES`入手了,file数组在最后一个,需要end定位,然后pos两次定位获得文件名
exp:
1 | import requests |
- session_id() 适用于:php7以下的版本 把恶意代码写到COOKIE的PHPSESSID中,然后利用session_id()这个函数去读取它 PHPSESSIID中只能有A-Z a-z 0-9,-,所以说我们要先将恶意代码16进制编码以后再插入进去(好像也不是 主要看服务端吧 因为我在别人payload里面看到了里面还能输入/flag)再补(看版本 旧版本 PHP Session ID 校验非常宽松 /、.、甚至 : 都可能被接受)
大概是
1 | GET /1.php?code=show_source(session_id(session_start())); HTTP/1.1 |
命令执行就这样GET /1.php?code=eval(hex2bin(session_id(session_start()))); HTTP/1.1 Cookie: PHPSESSID=706870696e666f28293b 4.  5. localeconv() 函数返回一个包含本地数字及货币格式信息的数组。它返回的是一个二维数组,而它的第一位居然是一个点. 利用current()`函数将这个点取出来的 pos是current的别名,如果都被过滤还可以使用reset(),该函数返回数组第一个单元的值,如果数组为空则返回 FALSE
文件读取
● show_source(session_id(session_start()));
● var_dump(file_get_contents(session_id(session_start())))
● highlight_file(session_id(session_start()));
● readfile(session_id(session_start()));
抓包传入Cookie: PHPSESSID=(想读的文件)即可
3. scandir 列出目录中的文件和目录 var_dump(scandir(current(localeconv())));
或者print_r(scandir(current(localeconv()))) 直接读取flag在哪
1 | ### 读取当前目录文件 |
- chdir()
这个函数是用来跳目录的,有时想读的文件不在当前目录下就用这个来切换,因为scandir()会将这个目录下的文件和目录都列出来,那么利用操作数组的函数将内部指针移到我们想要的目录上然后直接用chdir切就好了,如果要向上跳就要构造chdir('..') - array_reverse()
将整个数组倒过来,有的时候当我们想读的文件比较靠后时,就可以用这个函数把它倒过来,就可以少用几个next() - highlight_file()
打印输出或者返回 filename 文件中语法高亮版本的代码,相当于就是用来读取文件的 - getenv()
getenv() :获取环境变量的值(在PHP7.1之后可以不给予参数)
适用于:php7以上的版本 - 还有些新颖的函数 过滤一般到不了这里
pos current pop都被过滤了的话,还有个array_shift()函数可以用
array_shift() - 删除数组中第一个元素,并返回被删除元素的值。
输出函数echo、print_r、var_dump都被过滤了话,exit()函数的别名die()函数
die() 函数输出一条消息,并退出当前脚本。
这样子也可以 是个很好的payloadsystem(array_shift(apache_request_headers()));当然前面这个说过了 因为有apache_request_headers()这个函数 php7以上才有
最后把全部相关函数记一下
1 |
|
原文章:https://xz.aliyun.com/news/10228
然后用上面搜到的payload打了两个题(文章上面写的 有源码 部署到本地打的)




这里对php版本有限制的 5可以 7就不行了 高版本没这个函数了(居然还是有类似的 7以上的 payload类似于

