php特性
php特性
类型转换
强类型&弱类型
=== (强类型)在进行比较的时候,会先判断两种的类型是否相等,再比较
== (弱类型)在进行比较的时候,会将字符串类型转换成相同,再比较
弱比较:
字符串与int类型比较
PHP规定当进行字符串与数字的弱比较时
,会进行如下步骤:
字符串与字符串比较
因为这个是字符串之间进行比较,想要绕过这个弱比较只能用0e
的方式。
在PHP中"0e"判断为科学计数法,0e123就是0的10123次方
不难推出:0e123456789==0e1 // 因为0的任意次方都为0
不过要注意:
0e后面不能含有字母!!!
0e后面不能含有字母!!!
0e后面不能含有字母!!!
否则判断为False
弱类型绕过
整数和字符串:
整数与字符串的比较应该是最多的,会将字符串转为整形再比较,字符串会被从左第个字符进行转换知道遇到非数字。
哈希比较:
遇到 数字+e+数字这种类型时,PHP会把它看做科学记数法,那么0e开头的就会被看作0比如在用md5函数对传入的字符串进行处理且进行松散比较时,就可以传入两个实际不相等但是md5
加密后以0e开头的字符串。
以下字符经MD5加密均以0e开头
进制:
字符串以0x开头会被当做16进制,0开头会被当做8进制。php7版本以下,会将字符串转化为十进制再比较。
php松散比较表
强类型绕过
传入数组经过md5处理以后强类型比较也是强类型比较下相同的
下面是一个十分经典的对于强类型的考察
加string?不加加string?
MD5碰撞
我们可以使用快速MD5碰撞生成器来构建两个MD5一样,但内容完全不一样的字符串。
md5碰撞器:http://www.win.tue.nl/hashclash/fastcoll_v1.0.0.5.exe.zip
变量名格式
php会自动把传参进去的 空格 . [ +
转换为_
但是当一个参数中已经有一个[
被替换为_时,后面的就不会再被替换了
优先级
优先级排序:&& || = and or(从高到低)
其实关注重点就是优先级较低的运算符
精度
php踩过的那些坑(1)浮点数计算_在路上_技术分享博客 (haodaquan.com)
当小鼠小于1016 后,PHP对于小数就大小写不分了
_+.
在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_
,所以按理来说我们构造不出CTF_SHOW.COM这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换
php函数特性
intval
作用:函数用于获取变量的整数值
特性1:非空的arry返回1
特性2:进制转换 第二个参数是否是0
特性3:小数点 intval(4476.0,0)===4476
- 如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,
- 如果字符串以 "0" 开始,使用 8 进制(octal);否则,
- 将使用 10 进制 (decimal)。
特性4:科学计数法 intval(4476e1,0)=44760
MD5&&sha1
一般结合弱&&强类型进行考察,上面已经讲述过一部分,比如字符经MD5加密后为0e绕过判断,这里还有一个重要的绕过姿势就是传入数组。
MD5()函数需要一个string类型的参数。当传入一个array时,MD5()不会报错,无法求出array的MD5值,结果为NULL,这就会导致任意2个array的MD5值都会相等
sha1()函数无法处理数组类型,将报错并返回false
几个sha1()弱比较相等的字符串
CTF的MD5弱比较
md5($a)==md5($b)&$a!=$b
这个时候就要利用0e
的形式来解题,找到两个不同字符,md5加密后却都是0e324234的形式。
MMHUWUV | 0e701732711630150438129209816536 |
---|---|
MAUXXQC | 0e478478466848439040434801845361 |
IHKFRNS | 0e256160682445802696926137988570 |
GZECLQZ | 0e537612333747236407713628225676 |
GGHMVOE | 0e362766013028313274586933780773 |
GEGHBXL | 0e248776895502908863709684713578 |
EEIZDOI | 0e782601363539291779881938479162 |
DYAXWCA | 0e424759758842488633464374063001 |
$a==md5($a)
这一类题型要求满足$a是0e开头,且加密后也是0e开头
0e215962017 | 0e291242476940776845150308577824 |
---|---|
0e1284838308 | 0e708279691820928818722257405159 |
0e1137126905 | 0e291659922323405260514745084877 |
0e807097110 | 0e318093639164485566453180786895 |
0e730083352 | 0e870635875304277170259950255928 |
CTF的MD5强比较
md5($a)===md5($b)&$a!=$b
法一:数组绕过
md5_1[]=1&md5_2[]=2
因为PHP对无法md5加密的东西不加密,结果为NULL,虽然会报错,但是null=null,逻辑关系为True。所以可以输出flag
法二:两串不一样的字符,加密结果却相同
原理是将hex字符串转化为ascii字符串,并写入到bin文件
这里了解到MD5和sql注入的绕过有关,有机会仔细研究
preg_match
特性1:修饰符 s匹配多行 i不区分大小写
特性2:preg_match()只能处理字符串,当传入的subject是数组时会返回false
特性3:正则回溯 如果超出了上线 正则返回的不是0而是bool(false)
PHP利用PCRE回溯次数限制绕过某些安全限制 | 离别歌 (leavesongs.com)
特性4:e模式下代码执行
preg_match()
返回
pattern
的匹配次数。 它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后 将会停止搜索.preg_match_all()不同于此,它会一直搜索
subject
直到到达结尾。 如果发生错误preg_match() 返回FALSE
。
绕过方法:
- 数组绕过
- %0a绕过
strpos
查找字符串首次出现的位置
strpos()如果传入数组,便会返回NULL
与stpos()不同,stripos()不区分大小写
In_array
如果不设置第三个参数会使用宽松比较,也就是弱比较
即使把传进的字符串转换成int型再与数组中的数字进行比较(弱类型的性质)
In_arry(‘jpg’,arry(‘jpg’,’png)) ——>判断后缀名的白名单
Is_numeric
用来变量判断是否为数字。支持普通数字型字符串、科学计数法型字符串、部分支持十六进制0x型字符串
在数字的前面加上%09 %0a %0b %0c %0d任意一个都可以使其为真不影响判断。
作用:检测变量是否为数字或数字字符串
绕过方法:
-
16进制会被判断为数字
-
%00即空格会被判断为非数字–>即直接跳过检测了
ereg
-
ereg()传入数组会返回NULL
-
ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。 ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配
strcmp
比较两个字符串(区分大小写),定义中时比较字符串类型的,但如果输入其他类型这个函数将发生错误,在php 5.2版本之前,利用strcmp函数将数组与字符串进行比较会返回-1,但是从5.3开始,会返回0.
如果str1小于str2返回<0;如果str1大于str2返回>0;如果两者相等,返回0.当传入参数为数组时,会返回NULL
变量覆盖
变量覆盖漏洞的基础概念:
变量覆盖漏洞是指自定义的参数替换原有变量值的情况,如$$使用不当,extract函数使用不当,parse_str() 函数使用不当,import_request_variables() 使用不当,开启了全局变量注册等。
1、全局变量导致的变量覆盖
当register_globals全局变量设置开启时,传递过来的值会被直接注册为全局变量而使用,这会造成全局变量覆盖 。
register_globals全局变量设置, 在PHP5.3之前默认开启, PHP5.3默认关闭 ,PHP5.6以后已经被移除 。
2、$$动态变量覆盖
PHP动态变量是指一个变量名的变量名可以动态的设置和使用,一个变量获取另一个变量的值作为这个变量的变量名。
3、extract()函数变量覆盖
定义:extract() 函数从数组中将变量导入到当前的符号表。该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量,返回成功设置的变量数目。
语法:extract(array,extract_rules,prefix)
//array:必须,规定用于准换的数组
//extract_rules:可选,函数将检查每个键名是否为合法的变量名,同时也检查和符号表中已存在的变量名是否冲突。对不合法和冲突的键名的处理将根据此参数决定。默认设置EXTR_OVERWRITE,有冲突则覆盖。
//prefix:该参数规定了前缀。前缀和数组键名之间会自动加上一个下划线,可选。
从数组中将变量导入到当前的符号表
4、parse_str函数 变量覆盖
语法:parse_str(string,array)
定义:parse_str() 函数把查询字符串解析到变量中。如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。
注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。
将字符串解析成多个变量,parse_str(string $string,array&$result):void
,如果string是URL传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域((如果提供了 result 则会设置到该数组里)
5、import_request_variables()变量覆盖
使用范围:PHP 4 >= 4.1.0, PHP 5 < 5.4.0
函数语法:import_request_variables ( string $types [, string $prefix ] )
作用:将 GET/POST/Cookie 变量导入到全局作用域中。如果你禁止了 register_globals,但又想用到一些全局变量,那么此函数就很有用。
参数说明:
$types
:指定需要导入的变量,可以用字母 G、P 和 C 分别表示 GET、POST 和 Cookie,这些字母不区分大小写
$prefix
:变量名的前缀,置于所有被导入到全局作用域的变量之前。所以如果你有个名为 userid 的 GET 变量,同时提供了 pref_ 作为前缀,那么你将获得一个名为 $pref_userid 的全局变量。虽然 prefix 参数是可选的,但如果不指定前缀,或者指定一个空字符串作为前缀,你将获得一个 E_NOTICE 级别的错误。
'import_request_variables' 已在 PHP 5.4 版本中被移除
补充:
两种情况下会导致变量覆盖
- 当
$$x=$y
提前存在$other
,啧我们可以传$other=xxx
,将$other的值重新赋值为xxx
- 当
$$x=$$y
如提前存在$flag
和$other
,那我们只要传$other
=$flag
就可以把flag的值重新赋值给other
做题总结
反射类
反射,通俗来讲就是可以通过一个对象来获取所属类的具体内容,php中内置了强大的反射API:
ReflectionClass:一个反射类,功能十分强大,内置了各种获取类信息的方法,创建方式为new ReflectionClass(str 类名),可以用echo new ReflectionClass(‘className’)打印类的信息。
ReflectionObject:另一个反射类,创建方式为new ReflectionObject(对象名)。
web101playload:
?v1=1&v2=echo new Reflectionclass&v3=;
妙数+php伪协议
115044383959474e6864434171594473
这一串数字有些巧妙,这是16进制与字符串之间的互转,转换为16进制后的字符串,他其中又带有e也会被当做科学计数法,在这个题目中,结合hex2bin函数,从第3的数字读取转换为字符串正好就是<?=cat *
; 实在是太妙了
hex2bin函数:返回转换字符串的 ASCII 字符,如果失败则返回 FALSE。
web102payload:
GET:
POST:
Exception 类
他会把传入的值输出出来比如v2给了一个phpinfo,输出出来正好与后面的()对应上,成为了phpinfo函数,加上前面有eval接着就把命令给执行了,如果传入的是system(“ls”),他输出出来成了system(“ls”)()后面这个括号就没有用到也无所谓,eval也会将它执行
web109padload:
?v1=exception&v2=system('tac fl36dg.txt')
FilesystemIterator 类
php内置类 利用 FilesystemIterator 获取指定目录下的所有文件,getcwd()函数 获取当前工作目录 返回当前工作目录
web110payload:
?v1=FilesystemIterator&v2=getcwd
全局变量GLOBALS引用
web111
主要的一个问题是getflag是一个内部函数,flag.php是一个外部的文件,内部的无法得到外部文件
要利用的GLOBALS把所有全局变量全输出来,结合var_dump即可得到全部的数组
?v1=ctfshow&v2=GLOBALS
Is_file
is_file可以使用包装器 伪协议来绕过
不影响使用file_get_contents highlight_file
is_file判断,要求传入的file不是文件,但还能highlight_file,这就要说明is_file和highlight_file对于文件的判断:is_file认为伪协议不是文件,highlight_file认为伪协议是文件
目录溢出
在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容
多次重复后绕过is_file
一个目录溢出,让is_file文件认为flag.php不是文件,但highlight_file函数认他为是文件,可得到flag
_()和get_defined_vars
_()是一个函数
_()==gettext() 是gettext()的拓展函数,开启text扩展。需要php扩展目录下有php_gettext.dll
get_defined_vars()函数
get_defined_vars — 返回由所有已定义变量所组成的数组 这样可以获得 $flag
web128payload:
?f1=_&f2=get_defined_vars
目录穿越
利用目录穿越漏洞绕过 stripos 检测字符
web129payload:
?f=/ctfshow/../../../../../../../../../var/www/html/flag.php
curl -F
利用curl去带出flag.php
curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies)
使用方法见我的ctfshwo web133
flag.php cp/> 1.txt/tee
把flag.php到1.txt
从文件 “flag.php” 中读取内容,并将去除重复行的结果输出到文件 “4.txt” 中
tee命令主要被用来向standout(标准输出流,通常是命令执行窗口)输出的同时也将内容输出到文件
用法:
tee file1 file2 //复制文件
ls|tee 1.txt //命令输出
hint:
payload: ls /|tee 1 访问1下载发现根目录下有flag
payload: cat /f149_15_h3r3|tee 2 访问下载就OK
strripos()
strripos()函数,查找xxx在字符串中最后一次出现的位置,如果大于-1就退出函数,意思是不让用冒号了呗
可以用数组
盲注
这里需要提前构造好盲注的payload,刚好我们也试下命令盲注
awk
是一种文本处理工具,用于按行和列处理文本文件。NR
是awk
内置变量,表示当前处理的行号。在awk
中,行号从1开始递增。
web139
脚本爆目录
可能会因为网络的问题,导致爆错,最好多爆两次(校园网值得你多跑几次😅)
得到f149&15_h3r3(我还能说什么呢),假装我得到f149_15_h3r3,爆flag
无字母RCE
首先是那个正则表达式,\W是反义,匹配任意不是字母,数字,下划线,汉字的字符。这里可以无数字字母rce(想不到,根本想不到[附+人哪有不疯的别硬撑了表情包.png])
既然v3可以无数字字母rce,正常是直接构造就可以的,但是这题有个return。return会中止当前字符串的执行。什么意思呢,看这两个例子:
第一句会执行phpinfo(),但是第二句不行,因为return后就中止了。
但是这样可以:eval("return phpinfo();")
因此v3肯定是命令执行,但是v1v2又怎么弄呢?PHP里面数字是可以和一些命令进行运算,例如1-phpinfo()-1,这样仍然可以执行phpinfo(),因此构造就很明显了。
payload:
+system ls+
得到flag.php
+system tac flag.php+
这题我用的是取或
脚本如下
奥,payload前面要加上?v1=1&v2=1&v3=
另一个大佬的脚本(这个感觉好用点,异或)
create_function()的代码注入
函数定义:
- 获取参数,函数体
- 拼凑一个“function_lambda_func(参数){函数体}”
- eval
原理就是}闭合原来的函数,然后执行命令,然后再把多余的}给注释掉就可以了。
大佬的例子:
这个匿名函数相当于这样的创建函数过程:
现在构造了一个这样的payload:
payload访问后相当于如下:
;}闭合了函数,phpinfo();后的/*会注释掉之后的代码,在前面的定义中提到create_function函数是调用了eval的,所以phpinfo()函数得以执行
在php中,可以自己定义命名空间,但是最根本,最大的命名空间就是\,正常我们调用create_function函数,其实是调用的\这个命名空间下的函数,即\create_function,带上\相当于linux中的绝对路径的写法。
web 147payload:
__EOF__

本文链接:https://www.cnblogs.com/solitude0-c/p/17584388.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!