从去年开始 xray的yml poc升级到了v2版本和v1版本相比,执行流程上有了较大变化,以较为简单的thinkphp5的poc来看
v1版本
name: poc-yaml-thinkphp5-controller-rce
v2版本
name: poc-yaml-thinkphp5-controller-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
执行yml文件第一步是要把yml反序列化到golang的结构体,根据poc文件可以提取出如下结构体
package main
符合预期
尽管这个poc中没有使用到set这个结构,但是其他poc中大量使用set结构来保存全局变量
如
所以需要一个定义一个map来保存变量,而变量的值就是来源于cel-go执行语句,并获取out,可以定义如下函数
func execSetExpression(Expression string) (interface{}, error) {
进行测试,符合预期
部分request中会{{rand}}
这种格式来使用上一步中生成的全局变量,
可以定义如下渲染函数
// 渲染函数 渲染变量到request中
再看expression
字段中response.body.bcontains(b"a29hbHIgaXMg%d2F0Y2hpbmcgeW9129")
有一个response结构体,抽象成golang代码,大概如下
type Response struct {
但是在cel中是不能直接使用golang的struct的,需要用proto来做一个转换
定义如下proto文件
syntax = "proto3";
通过protoc -I . --go_out=. requests.proto
生成go文件
然后定义如下函数来执行单条rule的表达式,返回值为如bool,来判断单条rule是否成立
func execRuleExpression(Expression string, variableMap map[string]interface{}) bool {
然后根据request流程,可以抽象为如下匿名函数,方便最后执行poc中的Expression
var RequestsInvoke = func(target string, setMap map[string]interface{}, rule Rule) bool {
将前面生成的request匿名函数,按照rules中的key定义成函数。注入到cel执行环境中,即可实现短路的逻辑,避免无效请求。
func execPocExpression(target string, setMap map[string]interface{}, Expression string, rules map[string]Rule) bool {
项目代码全部开源在:
参考项目: