如何解析并白嫖xray yml V2 poc

VSole2022-03-31 07:35:09

从去年开始 xray的yml poc升级到了v2版本和v1版本相比,执行流程上有了较大变化,以较为简单的thinkphp5的poc来看

v1版本

name: poc-yaml-thinkphp5-controller-rce
rules:
  - method: GET
    path: /index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=printf&vars[1][]=a29hbHIgaXMg%25%25d2F0Y2hpbmcgeW91
    expression: |
      response.body.bcontains(b"a29hbHIgaXMg%d2F0Y2hpbmcgeW9129")

detail:
  links:
    - https://github.com/vulhub/vulhub/tree/master/thinkphp/5-rce

v2版本

name: poc-yaml-thinkphp5-controller-rce
manual: true
transport: http
rules:
    r0:
        request:
            cache: true
            method: GET
            path: /index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=printf&vars[1][]=a29hbHIgaXMg%25%25d2F0Y2hpbmcgeW91
        expression: response.body.bcontains(b"a29hbHIgaXMg%d2F0Y2hpbmcgeW9129")
expression: r0()
detail:
    links:
        - https://github.com/vulhub/vulhub/tree/master/thinkphp/5-rce

最主要的区别是是新增了transport、expression两个字段。

transport的取值范围为tcp、udp、http,给xray赋予了探测tcp协议的漏洞。

expression字段改变了v1 poc的执行流程,可利用短路的逻辑来设计执行的流程。

为了彻底搞明白cel执行yml poc的流程,今天就写一个最简单的yml 执行引擎demo,来学习执行的整体流程以及思路。

xray是使用cel-go来做执行引擎的,所以需要cel-go和golang的基础

关于cel语法的demo,可以查看

https://github.com/google/cel-go/blob/master/examples/README.md
https://codelabs.developers.google.com/codelabs/cel-go#0

1.反序列化yml文件

执行yml文件第一步是要把yml反序列化到golang的结构体,根据poc文件可以提取出如下结构体

package main

import (
    "gopkg.in/yaml.v2"
    "io/ioutil"
)

type Poc struct {
    Name       string            `yaml:"name"`
    Transport  string            `yaml:"transport"`
    Set        map[string]string `yaml:"set"`
    Rules      map[string]Rule            `yaml:"rules"`
    Expression string            `yaml:"expression"`
    Detail     Detail            `yaml:"detail"`
}

type Rule struct {
    Request    RuleRequest `yaml:"request"`
    Expression string      `yaml:"expression"`
}

type RuleRequest struct {
    Cache  bool   `yaml:"cache"`
    method string `yaml:"method"`
    path   string `yaml:"path"`
    Expression string      `yaml:"expression"`
}

type Detail struct {
    Links []string `yaml:"links"`
}

func main() {
    poc := Poc{}
    pocFile, _ := ioutil.ReadFile("poc.yml")
    err := yaml.Unmarshal(pocFile,&poc)
    if err != nil{
        println(err.Error())
    }
    println(pocFile)
}

符合预期

2.处理set 全局变量

尽管这个poc中没有使用到set这个结构,但是其他poc中大量使用set结构来保存全局变量

所以需要一个定义一个map来保存变量,而变量的值就是来源于cel-go执行语句,并获取out,可以定义如下函数

func execSetExpression(Expression string) (interface{}, error) {
    //定义set 内部函数接口
    setFuncsInterface := cel.Declarations(
        decls.NewFunction("randomInt",
            decls.NewOverload("randomInt_int_int",
                []*exprpb.Type{decls.Int, decls.Int},
                decls.String)),
        decls.NewFunction("randomLowercase",
            decls.NewOverload("randomLowercase_string",
                []*exprpb.Type{decls.Int},
                decls.String)),
    )

    //实现set 内部函数接口
    setFuncsImpl := cel.Functions(
        &functions.Overload{
            Operator: "randomInt_int_int",
            Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
                randSource := rand.New(rand.NewSource(time.Now().UnixNano()))
                min := int(lhs.Value().(int64))
                max := int(rhs.Value().(int64))
                return types.String(strconv.Itoa(min + randSource.Intn(max-min)))
            }},
        &functions.Overload{
            Operator: "randomLowercase_string",
            Unary: func(lhs ref.Val) ref.Val {
                n := lhs.Value().(int64)
                letterBytes := "abcdefghijklmnopqrstuvwxyz"
                randSource := rand.New(rand.NewSource(time.Now().UnixNano()))
                const (
                    letterIdxBits = 6                    // 6 bits to represent a letter index
                    letterIdxMask = 1<
                    letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
                )
                randBytes := make([]byte, n)
                for i, cache, remain := n-1, randSource.Int63(), letterIdxMax; i >= 0; {
                    if remain == 0 {
                        cache, remain = randSource.Int63(), letterIdxMax
                    }
                    if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
                        randBytes[i] = letterBytes[idx]
                        i--
                    }
                    cache >>= letterIdxBits
                    remain--
                }
                return types.String(randBytes)
            }},
    )

    //创建set 执行环境
    env, err := cel.NewEnv(setFuncsInterface)
    if err != nil {
        log.Fatalf("environment creation error: %v", err)
    }
    ast, iss := env.Compile(Expression)
    if iss.Err() != nil {
        log.Fatalln(iss.Err())
        return nil, iss.Err()
    }
    prg, err := env.Program(ast, setFuncsImpl)
    if err != nil {
        return nil, errors.New(fmt.Sprintf("Program creation error: %v", err))
    }
    out, _, err := prg.Eval(map[string]interface{}{})
    if err != nil {
        log.Fatalf("Evaluation error: %v", err)
        return nil, errors.New(fmt.Sprintf("Evaluation error: %v", err))
    }
    return out, nil
}

进行测试,符合预期


3.生成request和response

部分request中会{{rand}}这种格式来使用上一步中生成的全局变量,

可以定义如下渲染函数


// 渲染函数 渲染变量到request中
func render(v string, setMap map[string]interface{}) string {
    for k1, v1 := range setMap {
        _, isMap := v1.(map[string]string)
        if isMap {
            continue
        }
        v1Value := fmt.Sprintf("%v", v1)
        t := "{{" + k1 + "}}"
        if !strings.Contains(v, t) {
            continue
        }
        v = strings.ReplaceAll(v, t, v1Value)
    }
    return v
}

再看expression字段中response.body.bcontains(b"a29hbHIgaXMg%d2F0Y2hpbmcgeW9129")

有一个response结构体,抽象成golang代码,大概如下


type Response struct {
    Body []byte
}

但是在cel中是不能直接使用golang的struct的,需要用proto来做一个转换

定义如下proto文件


syntax = "proto3";
option go_package = "./;structs";
package structs;

message Response {
  //数据类型 字段名称 字段id
  bytes body = 1;
}

通过protoc -I . --go_out=. requests.proto生成go文件

然后定义如下函数来执行单条rule的表达式,返回值为如bool,来判断单条rule是否成立

func execRuleExpression(Expression string, variableMap map[string]interface{}) bool {
   env, _ := cel.NewEnv(
      cel.Container("structs"),
      cel.Types(&structs.Response{}),
      cel.Declarations(
         decls.NewVar("response", decls.NewObjectType("structs.Response")),
         decls.NewFunction("bcontains",
            decls.NewInstanceOverload("bytes_bcontains_bytes",
               []*exprpb.Type{decls.Bytes, decls.Bytes},
               decls.Bool)),
      ),
   )
   funcImpl := []cel.ProgramOption{
      cel.Functions(
         &functions.Overload{
            Operator: "bytes_bcontains_bytes",
            Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
               v1, ok := lhs.(types.Bytes)
               if !ok {
                  return types.ValOrErr(lhs, "unexpected type '%v' passed to bcontains", lhs.Type())
               }
               v2, ok := rhs.(types.Bytes)
               if !ok {
                  return types.ValOrErr(rhs, "unexpected type '%v' passed to bcontains", rhs.Type())
               }
               return types.Bool(bytes.Contains(v1, v2))
            },
         },
      )}
   ast, iss := env.Compile(Expression)
   if iss.Err() != nil {
      log.Fatalln(iss.Err())
   }
   prg, err := env.Program(ast, funcImpl...)
   if err != nil {
      log.Fatalf("Program creation error: %v", err)
   }
   out, _, err := prg.Eval(variableMap)
   if err != nil {
      log.Fatalf("Evaluation error: %v", err)
   }
   return out.Value().(bool)
}

然后根据request流程,可以抽象为如下匿名函数,方便最后执行poc中的Expression

var RequestsInvoke = func(target string, setMap map[string]interface{}, rule Rule) bool {
    var req *http.Request
    var err error
    if rule.Request.Body == "" {
        req, err = http.NewRequest(rule.Request.Method, target+render(rule.Request.Path, setMap), nil)
    } else {
        req, err = http.NewRequest(rule.Request.Method, target+render(rule.Request.Path, setMap), bytes.NewBufferString(render(rule.Request.Body, setMap)))
    }
    if err != nil {
        log.Println(fmt.Sprintf("http request error: %s", err.Error()))
        return false
    }
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        println(err.Error())
        return false
    }
    response := &structs.Response{}
    response.Body, _ = ioutil.ReadAll(resp.Body)
    return execRuleExpression(rule.Expression, map[string]interface{}{"response": response})
}

4.执行poc Expression

将前面生成的request匿名函数,按照rules中的key定义成函数。注入到cel执行环境中,即可实现短路的逻辑,避免无效请求。

func execPocExpression(target string, setMap map[string]interface{}, Expression string, rules map[string]Rule) bool {   var funcsInterface []*exprpb.Decl   var funcsImpl []*functions.Overload   for key, rule := range rules {      funcName := key      funcRule := rule      funcsInterface = append(funcsInterface, decls.NewFunction(key, decls.NewOverload(key, []*exprpb.Type{}, decls.Bool)))      funcsImpl = append(funcsImpl,         &functions.Overload{            Operator: funcName,            Function: func(values ...ref.Val) ref.Val {               return types.Bool(RequestsInvoke(target, setMap, funcRule))            },         })   }   env, err := cel.NewEnv(cel.Declarations(funcsInterface...))   if err != nil {      log.Fatalf("environment creation error: %v", err)   }   ast, iss := env.Compile(Expression)   if iss.Err() != nil {      log.Fatalln(iss.Err())   }   prg, err := env.Program(ast, cel.Functions(funcsImpl...))   if err != nil {      log.Fatalln(fmt.Sprintf("Program creation error: %v", err))   }   out, _, err := prg.Eval(map[string]interface{}{})   return out.Value().(bool)}

代码仅为学习使用,为了精简代码体量,绝大多数函数和结构体未实现。

5.测试

项目代码全部开源在:

https://github.com/lanyi1998/yml-poc-demo

参考项目:

https://github.com/google/cel-go
https://github.com/jjf012/gopoc
https://github.com/WAY29/pocV
https://docs.xray.cool/#/guide/poc/v2
stringresponse
本作品采用《CC 协议》,转载必须注明作者和本文链接
APP协议分析心得
2023-07-18 09:23:41
对脱壳流程有不明白的可参考我之前写的文章:[原创]ART环境下dex加载流程分析及frida dump dex方案。var magic_Hex = [0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00];var dex_path = "/data/data/" + apk_Name + "/" + dex_size + ".dex";
从fofa中搜索RDP,会看到它会解析出RDP的信息。本文探索如何自己实现一个。1. Nmap指纹在http
从去年开始 xray的yml poc升级到了v2版本和v1版本相比,执行流程上有了较大变化,以较为简单的thinkphp5的poc来看 v1版本 name: poc-yaml-thinkphp5-controller-rce rules: - method: GET path: /index.php?s=/Index/\think\app/invokefunction&functi
命令行版 HTTP 工具集,自动化检查网站状态。
前段时间Confluence发布了CVE-2021-26085补丁,刚好之前分析过Confluence的漏洞,免去了搭建漏洞分析环境的麻烦,因此分析下这个漏洞。 分析过程 漏洞点定位 这个漏洞爆出来已经有一段时间了,所以已经有公开的POC了
Spring MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的
引言 在JavaWeb应用中,任意文件上传一直是关注的重点,攻击者通过上传恶意jsp文件,可以获取服务器权限。但是在Springboot框架对JSP解析存在一定的限制。 Spring官方原文如下,大概意思是jsp对内嵌的容器的支持不太...
一款盲目WAF识别工具
2023-05-19 08:58:46
一种识别工具,可以基于盲目推理识别Web保护类型。盲推理是通过检查由一组预定义的攻击性有效载荷引起的响应来完成的,其中这些有效载荷仅用于触发介于两者之间的Web保护系统(例如),目前,它支持80多种不同的保护产品。____ ___ ___ ____ ______ | T T __ __ ____ _____. l j| \ / _]| \ | T| | || T__T T / T| __|. | T | \ / [_ | _ Yl_j l_j| ~ || | | |Y o || l_. | | | D YY _]| | | | | |___ || | | || || _|. j l | || [_ | | | | | | !-h, --help Show this help message and exit. --proxy-file=PRO.. Load HTTP proxy list from a file
在前天的沙龙上,师傅们积极探讨,期间提出了一些关于app抓包的相关问题。在此小小的总结一波有关的分析以及解决办法。
VSole
网络安全专家