-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 26.7 KB
/
content.json
1
{"meta":{"title":"yyp0's blog","subtitle":"","description":"业余web选手","author":"yyp0","url":"http://yoursite.com","root":"/"},"pages":[],"posts":[{"title":"漏扫开发","slug":"漏扫开发","date":"2020-05-29T13:34:59.000Z","updated":"2020-05-29T13:35:49.534Z","comments":true,"path":"2020/05/29/漏扫开发/","link":"","permalink":"http://yoursite.com/2020/05/29/%E6%BC%8F%E6%89%AB%E5%BC%80%E5%8F%91/","excerpt":"","text":"立个 flag,6.15 之前搞完漏扫。。","categories":[],"tags":[]},{"title":"一些Java反序列化漏洞的复现","slug":"一些Java反序列化漏洞的复现","date":"2020-05-28T07:31:54.000Z","updated":"2020-05-29T02:51:57.541Z","comments":true,"path":"2020/05/28/一些Java反序列化漏洞的复现/","link":"","permalink":"http://yoursite.com/2020/05/28/%E4%B8%80%E4%BA%9BJava%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E7%9A%84%E5%A4%8D%E7%8E%B0/","excerpt":"Apache Common CollectionsApache Common Collections 反序列化造成的 rce 出现在 InvokerTransformer 这个类里,漏洞点是通过 Java 反射机制调用方法:","text":"Apache Common CollectionsApache Common Collections 反序列化造成的 rce 出现在 InvokerTransformer 这个类里,漏洞点是通过 Java 反射机制调用方法: 在 InvokerTransformer.class 中: 12345678910111213141516public Object transform(Object input) { if (input == null) { return null; } try { Class cls = input.getClass(); Method method = cls.getMethod(iMethodName, iParamTypes); return method.invoke(input, iArgs); } catch (NoSuchMethodException ex) { throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist"); } catch (IllegalAccessException ex) { throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed"); } catch (InvocationTargetException ex) { throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex); }} 可以看到,在漏洞函数内存在反射调用,回溯 iMethodName 和 iParamType 以及 iArgs,可以看到 InvokerTransformer 类的构造函数: 123456public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { super(); iMethodName = methodName; iParamTypes = paramTypes; iArgs = args; } 变量可控。 接着看 TransformedMap.class,在 TransformedMap 类的 checkSetValue 方法中,可以看到: 123protected Object checkSetValue(Object value) { return valueTransformer.transform(value); } 这里存在一个 transform 的方法调用,回溯一下 valueTransformer,构造函数: 123456789public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) { // 对外创建类对象 return new TransformedMap(map, keyTransformer, valueTransformer);}protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer;} valueTransformer 类型为 Transformer 且可控。 另外,在 AbstractInputCheckedMapDecorator.class 中,重载了 Map 的内部借口 Map.Entry 的 setValue 方法,并且调用了 checkSetValue 方法: 1234public Object setValue(Object value) { value = parent.checkSetValue(value); return entry.setValue(value);} 也就是说,如果再某个类中自定义了反序列化方法,并且对 map 对象操作了键值,那么就可以触发以上反序列化链。","categories":[],"tags":[{"name":"漏洞复现","slug":"漏洞复现","permalink":"http://yoursite.com/tags/%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/"}]},{"title":"Java反序列化","slug":"Java反序列化","date":"2020-05-26T06:12:22.000Z","updated":"2020-05-26T13:13:23.998Z","comments":true,"path":"2020/05/26/Java反序列化/","link":"","permalink":"http://yoursite.com/2020/05/26/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/","excerpt":"Java序列化原理Java 的序列化与反序列化分别是由 ObjectOutputStream 类的 writeObject 方法和 ObejctInputStream 的 readObject 方法实现的","text":"Java序列化原理Java 的序列化与反序列化分别是由 ObjectOutputStream 类的 writeObject 方法和 ObejctInputStream 的 readObject 方法实现的 最简单的(反)序列化Java 中并不是所有类都可以进行序列化的,如果要对类进行序列化的话,需要让类实现 java.io.serialize 这个接口 序列化 一个简单的例子: 123456789101112// Person.javapublic class Person implements serialze{ private String name; public Person(String name){ this.name = name; } public String toString(){ return "Person("+this.name+")"; }} 1234567891011121314151617// demo1.javapublic class demo1{ public static void main(String[] args){ Person p = new Person("yyp"); try{ FileOutputStream f = new FileOutputStream("1.txt"); ObjectOutputStream ser = new ObejctOutputStream(f); ser.writeObject(p); ser.close(); f.close(); }catch(IOException i){ i.printStackTrace(); }catch(ClassNotFoundException c){ c.printStackTrace(); } }} 可以看到在 writeObject() 方法上实现了类的序列化 反序列化 直接看例子: 12345678910111213141516// demo2.javapublic class demo2{ public static void main(String[] args){ Person p = new Person("yyp"); try{ FIleInputStream f = new FileInputStream("1.txt"); ObejctInputStream unser = new ObejectInputStream(f); Person pp = (Person)unser.readObject(); System.out.println(pp); }catch(IOException i){ i.printStackTrace(); }catch(ClassNotFoundException c){ c.printStackTrace(); } }} 在 readObject 处实现了反序列化 自定义(反)序列化 transient 序列化时会自动忽略带有 transient 属性 对于上面的 Person 类,如果给 name 加上 transient 关键字 private transient String name; ,反序列化输出得到的结果为 Person(null),而没有加这个关键字时,得到的鸡国为 Person(yyp)。可以看到对于带有 transient 字段的属性会在序列化的时候赋予默认值。 writeReplace 和 readResolve 方法 writeReplace 和 readResolve 类似于 php 的 __sleep__ 和 __wakeup__ 方法,这两个方法使用时在需要被序列化的类中进行定义,如: 12345public Object writeReplace() throws ObjectStreamException { ArrayList arr = new ArrayList<>2; arr.add(this.name); return arr;} 加上这个函数时,我们得到的反序列化结果为: [yyp],可以看到,writeObject 序列化时的对象为 writeReplace 函数返回的值 再来看一下 readResolve 方法: 1234public Object readResolve() throws ObjectStreamException { Person p = new Person("asdasd"); return p;} 重新序列化,查看反序列化结果,得到 Person(asdasd) 综上,可以得到函数之间调用的顺序:writeReplace -> writeObject,readObject -> readResolve Externalizable 自定义序列化 需要在类中实现 writeExternal 和 readExternal 方法: 123456789101112131415public class Person implements Externalizable { private String name; private int age; public void writeExternal(ObjectOutput out) throws IOException{ // 可以对属性进行其他的操作 out.writeObject(name); out.writeInt(age); } public void readExternam(ObejctInput in) throws IOExeption,classNotFoundException{ name = (String) in.readObject(); age = in.readInt(); // ... }} resolveClass 方法 resolveClass 方法是 ObjectInputStream 类中的一个方法,在 ObjectInputStream 中的 resolveClass 方法默认做的工作是检查这个类可否被 load(类的加载),但是可以通过重载 resolveClass,使其具有白名单功能。","categories":[],"tags":[{"name":"Java基础","slug":"Java基础","permalink":"http://yoursite.com/tags/Java%E5%9F%BA%E7%A1%80/"}]},{"title":"ClassLoader与反射","slug":"ClassLoader与反射","date":"2020-05-24T08:06:03.000Z","updated":"2020-05-24T15:35:13.281Z","comments":true,"path":"2020/05/24/ClassLoader与反射/","link":"","permalink":"http://yoursite.com/2020/05/24/ClassLoader%E4%B8%8E%E5%8F%8D%E5%B0%84/","excerpt":"ClassLoader什么是类加载器类加载器完成的是类加载机制中的类的加载的部分,完成从静态的 .class 文件生成 java.lang.class 对象(可以参照上一篇对 Java 类加载机制的学习","text":"ClassLoader什么是类加载器类加载器完成的是类加载机制中的类的加载的部分,完成从静态的 .class 文件生成 java.lang.class 对象(可以参照上一篇对 Java 类加载机制的学习 类加载器的种类在 Java 中内置的 ClassLoader 有三种:Bootstrap ClassLoader, Extension ClassLoader, Application ClassLoader,另外还可以通过自定义 ClassLoader 来决定类的加载方式 Bootstrap ClassLoader 这是 Java 最基础的类加载器,负责加载 JVM 核心类,通常是我们常常看到的 java. 开头的内置库。这些类通常位于 $JAVA_HOME/lib/rt.jar 中, Bootstrap ClassLoader 为根加载器 Extension ClassLoader Extension ClassLoader 加载的对象是 $JAVA_HOME/lib/ext/ 目录下的 jar 包中的类,加载得到的类通常是我们看到的 javax. 开头的扩展类,如 javax.swing.* 等,Extension ClassLoader 继承 Bootstrap ClassLoader,是 Bootstrap ClassLoader 的子类 Application ClassLoader Application ClassLoader 是直接面向用户的加载器,Application ClassLoader 加载的对象是环境变量里面的路径的 jar 包或者路径中的 jar 包,还有我们自己编写的 java 文件以及第三方 jar 包。Application ClassLoader 继承 Extension ClassLoader,是Extension ClassLoader 的子类 。Application ClassLoader 也被成为系统加载器,可以通过 getSystemClassLoader 获取。 以上三个加载器都是在本地文件系统加载类,在 Java 中还有一种类加载器 Url ClassLoader,使用Url ClassLoader 可以加载网络上的 jar 包中的类。 自定义加载器 定义一个加载器时,需要注意定义这几个方法:loadClass, findClass, defineClass。其中 loadClass 的作用是在当前类加载器中寻找这个类看这个类是否已被加载,如果未被加载的话,就调用父加载器进行加载,如果双亲都加载不了的话,就调用 findClass 由自定义加载器自己进行加载。这里需要注意 findClass 常常由自定义类加载器的子类自己定义。如果调用了 findClass 还是加载不了这个类的话,就会抛出异常。defineClass 的作用就是当类加载器完成 loadClass 和 findClass 的步骤时,将字节码对象转换成 class 对象,完成类的加载过程。 类加载器的运作双亲委派双亲委派是 Java 类加载器运作的重要原理。上面已经提到 Bootstrap ClassLoader 是根加载器,Extension ClassLoader 和 Application ClassLoader 都是 Bootstrap ClassLoader 子类,同时 Application ClassLoader 是 Extension ClassLoader 的子类,三者之间的父子关系为: Bootstrap ClassLoader -> Extension ClassLoader -> Application ClassLoader。 先以 Application ClassLoader 的运作过程作为例子:当 Application ClassLoader 去加载一个类的时候(由于含有 main() 的类会首先被 Application ClassLoader 初始化,所以很多时候 Java 文件遇到陌生的类时,会先用 Application ClassLoader 去加载,这是类加载器的传递性),会首先将加载类的任务交给 Extension ClassLoader,让 Extension ClassLoader去加载类,这时 Extension ClassLoader 同样的将加载的任务交给它的父亲 Bootstrap ClassLoader,Bootstrap ClassLoader 拿到任务就会去加载这个类,如果没找到这个类的话, Extension ClassLoader 就会自己去执行加载任务,同样的,如果 Extension ClassLoader 没找到这个类的话,Application ClassLoader 就会自己去执行类的加载任务。可以看到,类的加载的过程就像一个递归的过程。 反射类对象的获取 class.forName(“xxx.xxx”) 通过这种方式,我们可以通过类的全限定名直接获取类的 class 对象,利用起来比较容易 className.class 通过这种方式来获取类对象的话,需要当前文件引用了 className 这个类的库,依赖性较强 new className().getClass() 同样的这种方式只能在引用这个类的前提条件下使用,依赖性也比较强 以上方法返回值均为类对象 反射的相关方法和使用通过反射,我们可以获取到类对象中的属性对象和方法 获取属性对象 1234getField(String name) // 获取类对象的 name 属性(可以是父类的)getFields() // 获取类对象的所有属性(包括父类的)getDeclaredField(String name) // 获取当前类的 name 属性(不可以是父类的)getDeclaredFields() // 获取当前类的所有属性(不包括父类的) 获取方法 1234getMethod(String name, parameters) // 获取类对象中的 name 方法(可以是父类的)getMethods() // 获取类对象的所有方法(包括父类的)getDeclaredMethod(String name, parameters) // 获取类对象的 name 方法(不可以是父类的)getDeclaredMethods() // 获取类对象的所有方法(不包括父类的) 需要注意的是使用 getMethod 和 getDeclaredMethod 来获取单个方法时,我们需要在第二个参数传入需要获取的方法的参数的 class 对象,比如:getMethod(add, int.class, int.class) 或是 getMethod(add, new Class[]{int.class, int.class}),到这里,我们还只可以获取方法对象(getMethod 返回值为方法对象),接下来我们可以通过 method 对象的 invoke 方法来执行方法。 先来看看 invoke 函数的原型: 12invoke(Object obj,Object args[])两个参数均为对象,第一个参数为目标方法所在的类的对象(常常用newInstance来初始化),第二个参数为对象数组,为当前方法接受到的参数 一个例子: 1method.invoke(className.class.newInstance(), new Object[]{new Interger(11), new Interger(22)}) 综合以上,得到一个完整的例子: 1class.forName("xxx.className").getMethod("add", new Class[]{int.class, int.class}).invoke(class.forName("xxx.className").newInstance(), new Object[]{new Interger(11), new Interger(22)})","categories":[],"tags":[{"name":"Java基础","slug":"Java基础","permalink":"http://yoursite.com/tags/Java%E5%9F%BA%E7%A1%80/"}]},{"title":"Java类加载机制","slug":"Java类加载机制","date":"2020-05-23T14:40:23.000Z","updated":"2020-05-24T08:00:27.359Z","comments":true,"path":"2020/05/23/Java类加载机制/","link":"","permalink":"http://yoursite.com/2020/05/23/Java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/","excerpt":"Java从源代码到运行经历了编译和运行的过程,编译就是将源代码编译成字节码的过程,.java 文件被编译成了 .class 文件。运行过程中,.class 文件被 Java 虚拟机解释,经历了类的加载和类的执行两个过程。以下主要阐述 Java 类的加载。","text":"Java从源代码到运行经历了编译和运行的过程,编译就是将源代码编译成字节码的过程,.java 文件被编译成了 .class 文件。运行过程中,.class 文件被 Java 虚拟机解释,经历了类的加载和类的执行两个过程。以下主要阐述 Java 类的加载。 类加载机制什么是类加载机制在编译阶段 .java 文件被编译成了 .class 文件,.class 文件中主要是描述类的数据。类的加载就是 Java 虚拟机把描述类的数据从 .class 文件加载到内存,并且队数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型。 Java 类加载机制的过程Java 类加载机制主要分为类的加载、类的连接(验证、准备、解析)、类的初始化三大过程。 类的加载 类的加载主要有三大阶段:定位 .class 文件,转换数据存储结构,生成 java.lang.Class 对象 a. 定位 .class 文件 通过类的全限定名(包名与类名)来获取相应的 .class 文件,可获取 .class 文件的方式有:本地加载、通过网络下载 .class 文件,从 jar 包或者 war包获取、JSP 文件生成等方式 b. 转换数据存储结构 这一步完成的是将 .class 文件中的类的静态存储结构转换成运行时在内存中方法区的数据结构 c. 生成 java.lang.Class对象 在内存中生成一个 java.lang.Class 对象,这个对象作为方法区中这个类的数据的入口。 类的初始化 在类的连接的准备过程中,(仅仅)类的静态常量被初始化赋值(非默认值),在类的初始化阶段,类的静态变量会被初始化赋值(非默认值,在类的连接过程中,静态变量被赋值为默认值) 以下几种情况需要对类进行初始化: a. 使用 new 创建对象 b. 通过反射调用类的方法和对象时,如果类没有被初始化过的话,就必须对类进行初始化 c. 初始化子类时,如果这个类的父类没有被初始化的话那么需要先初始化父类 d. 虚拟机启动时,定义了 main() 方法的类先被初始化 e. 访问类的静态方法和静态变量(非静态常量) 常见的被动引用例子: a. 通过子类调用父类的静态变量和方法时,只会对父类进行初始化,子类不会被初始化 1234567891011121314151617181920212223242526// father.javapublic class father { static { System.out.println("This is father!"); } public static int Fvalue = 1;}// son.javapublic class son extends father { static { System.out.println("This is son"); } public static int Svalue = 0;}// test.javapublic class test { public static void main(String[] args) { System.out.println(son.Fvalue); }}// result:This is father!1 可以看到通过子类访问父类的静态变量时,父类被初始化了,但是子类没有被初始化 b. 使用 new 创建类数组 稍微修改以上代码: 123456// test.javapublic class test { public static void main(string[] args) { father[] farr = new father[5]; }} 从运行的结果可以看出来此时没有进行类的初始化 c. 访问类的常量时,不会初始化类(常量在类的连接的准备过程中就被初始化了) 给 father 类添加一个静态常量: 12345678// father.javapublic class father { static { System.out.println("This is father!"); } public static int Fvalue = 1; public static final int FSvalue = 11;} 得到的结果直接输出了 11,可以看到静态常量在类初始化之前就已经被赋值了","categories":[],"tags":[{"name":"Java基础","slug":"Java基础","permalink":"http://yoursite.com/tags/Java%E5%9F%BA%E7%A1%80/"}]},{"title":"文件上传小总结","slug":"文件上传小总结","date":"2020-05-15T08:11:47.000Z","updated":"2020-05-15T11:47:54.295Z","comments":true,"path":"2020/05/15/文件上传小总结/","link":"","permalink":"http://yoursite.com/2020/05/15/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E5%B0%8F%E6%80%BB%E7%BB%93/","excerpt":"前端校验前端校验即客户端校验,一般在前端对文件名检验一次 123456789101112131415<script> var file_arr = ['jpg', 'png', 'txt']; function checkfile(filename){ var pos = filename.indexof('.'); var file_ext = filename.substr(pos+1); for(int i=0; i<arr.length; i++){ if(file_arr == file_ext) return true; } retun false }</script><form action="login.php" enctype="multipart/form-data"> <input type="file" name="upload" onchange=checkfile(this.value)/></form>","text":"前端校验前端校验即客户端校验,一般在前端对文件名检验一次 123456789101112131415<script> var file_arr = ['jpg', 'png', 'txt']; function checkfile(filename){ var pos = filename.indexof('.'); var file_ext = filename.substr(pos+1); for(int i=0; i<arr.length; i++){ if(file_arr == file_ext) return true; } retun false }</script><form action="login.php" enctype="multipart/form-data"> <input type="file" name="upload" onchange=checkfile(this.value)/></form> 这种情况下,我们可以直接在本地删除掉 onchange 事件,或者先上传一个符合条件的文件,然后再用 burp 抓包改包 后端校验后端校验,一般会校验文件的后缀,文件的 MIME,文件头,文件内容等,校验方式一般有黑名单和白名单 黑名单一般校验后缀使用黑名单的方式: 假设漏洞代码如下: 12345678910111213<?php $filename = $_FILE['file']['filename'] $path = '/xxxxxx/'.$filname; $arr = ['.php', '.html', '.php5', 'php4', 'php2', 'phtml','phtm'...] $file_ext = strstr($filename, '.'); if(in_array($filename, $arr)){ die("Not allow...") }else{ if(move_uploaded_file($_FILE['file']['tmp_name'], $path)){ echo "Upload success..." } }?> 绕过方法: 大小写绕过 上面的代码没有对大小写进行检验,当我们上传 1.PHP 时可以上传成功 修补: 1$file_ext = strtolower(strstr($filename, '.')); 将文件后置转换成小写 空格绕过 可以看到上面的代码,没有去除原文件后缀的空格,所以我们可以上传文件名为”1.php “进行绕过,这是因为php 解析文件名的时候会自动去掉后面的空格 修补: 1$file_ext = trim($file_ext); ‘.’ 绕过 漏洞代码没有对文件后缀的 . 进行处理,在 windows 系统中,会自动去除文件后缀中的点,所以我们可以上传文件名为 “1.php.”,后端取出的文件名就为 “.php.”,即可绕过 修补: 12345678910$len = strlen($file_ext);$ext = '';for($i=1; $i<$len; $i++){ if($file_ext[i] != '.'){ $ext += $file_ext[i]; }else{ break; }}$file_ext = $ext; 删除文件后缀的所有点 ::$DATA 绕过 同样在 windows 下,创建文件时如果后缀中有 ::$DATA,就会直接删掉这个字段 所以我们可以上传文件绕过黑名单检测,上传文件:1.php::$DATA 修补: 123if(preg_match(/::DATA/i, $filename)){ $file_ext = substr($file_ext,0,str_len($file_ext)-6);} 寻找漏网之鱼 fuzz 没被黑名单过滤的文件,比如上传 .htaccess 文件 1<FilesMatch "sec.jpg"> SetHandler application/x-httpd-php </FilesMatch> 双写绕过 有些代码过滤逻辑是这样的: 12$pattern = "/php|htm|htaccess.../i"$file_ext = preg_replace($pattern, '', $file_ext); 这时候上传文件 1.pphphp 即可绕过 白名单 对 MIME 的检测一般用的是白名单的方式,比如 12345if($_FILE['file']['type']=='image/jpeg' || $_FILE['file']['type']=='image/png'){ ....}else{ die("Type not allowed....");} 绕过这种只要 burp 抓包改掉 content-type 即可,由于用户发过来的包中 content-type 并不真实,所以,尽量不要依靠这种方式做检验 自定义文件上传的路径 12345678910$upfodler = $_GET['fodler'];$file_name = $_FILE['file']['name'];$file_ext = explode($file_name, '.')[1];$arr = array('png', 'jpg', 'gif');if(in_array($file_ext, $arr)){ $path = $upfodler.$file_name; if(move_uploaded_file($_FILE['file']['tmp_name'], $path)){ echo "upload success..." }} 由于这里得到文件最终路劲的方法是直接拼接,给了我们进行 00 截断的可能性。 利用手段: 123folder = /var/www/html/1.php%00上传的文件名为:1.jpg 所以,1.jpg可以绕过后缀的检测,而 move_uploaded_file 时,可以将临时文件的内容写到 1.php,达到了绕过的功能 文件幻数和文件信息检测 一般来说用图片马即可绕过常见的 getimagesize 和 phpexif 函数 制作图片马: 1copy normal.jpg /b + shell.php /a webshell.jpg 图片渲染一次渲染:只要在图片的空白处注入代码即可 二次渲染:对比 GD库处理后的图片,在处理前后没有发生变化的地方注入代码 条件竞争比如如下代码: 1234567891011121314151617181920if(isset($_POST['submit'])){ $ext_arr = array('jpg','png','gif'); $file_name = $_FILES['upload_file']['name']; $temp_file = $_FILES['upload_file']['tmp_name']; $file_ext = substr($file_name,strrpos($file_name,".")+1); $upload_file = UPLOAD_PATH . '/' . $file_name; if(move_uploaded_file($temp_file, $upload_file)){ if(in_array($file_ext,$ext_arr)){ $img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext; rename($upload_file, $img_path); $is_upload = true; }else{ $msg = "只允许上传.jpg|.png|.gif类型文件!"; unlink($upload_file); } }else{ $msg = '上传出错!'; }} 先 move_uploaded_file 且move_uploaded_file时还没有对文件重命名,再 unlink,给了我们时间去访问 webshell 文件上传的两要素:1. 上传点 2. 文件存储路径,如果文件存储路径不存在的话,那么就找这个文件在网页中的输出点","categories":[],"tags":[{"name":"文件上传","slug":"文件上传","permalink":"http://yoursite.com/tags/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0/"}]}],"categories":[],"tags":[{"name":"漏洞复现","slug":"漏洞复现","permalink":"http://yoursite.com/tags/%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/"},{"name":"Java基础","slug":"Java基础","permalink":"http://yoursite.com/tags/Java%E5%9F%BA%E7%A1%80/"},{"name":"文件上传","slug":"文件上传","permalink":"http://yoursite.com/tags/%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0/"}]}