Skip to content

develop

Aur0ra edited this page Mar 10, 2023 · 1 revision

APIKiller 二次开发文档

为保证项目的结构化性与完整性,建议二次开发时严格遵守开发规范。非规范开发将会被拒绝merge!

项目开发部分规范

类型 开发规范
文件&package命名 所有文件&package默认遵从小驼峰式命名方式,针对特殊专有名词可以忽略,例如CSRFDetector
日志打印 所有核心模块加载时,实例化方法&处理方法必须按格式打印(可参考已有代码),且标识必须保证首字母大写,其余描述信息小写,例如:[Load Filter] load http filter
注释 1. 代码通用注释,全部小写 2. 方法、函数,全部采用格式化

环境搭建

先clone项目源码,根据食用wiki中将环境搭建好,能跑通即为成功

全局配置加载

对项目进行二次开发时,如果需要从config.yaml中读取配置,请查看此部分

  1. 首先在config.yaml中根据结构app.功能.模块.xxx进行结构化配置【重要】
  2. 在需要使用的地方,直接调用viper.GETxxx("app.功能.模块.xxx")进行配置读取

拓展数据Origin加载模块

通过对Origin接口的实现,可以完成不同方式、不同来源的Origin数据加载

以加载burpsuite文件来作为加载源进行演示

  1. 先在首先在APIKiller/core/origin/ 创建对应的包目录,例如fileInputOrigin,再创建对应的处理文件
    img.png

  2. 创建FileInputOrigin类,实现APIKiller/core/Origin中的接口

    type FileInputOrigin struct {
        path string
    }
    
    func (o *FileInputOrigin) LoadOriginRequest(ctx context.Context, httpItemQueue chan *Origin.TransferItem) {
        logger.Infoln("[Load Request] load request from file input Origin") //统一标准必须打印日志
        
        // 1. 解析静态文本成 *http.Request 以及 &http.Response 对象
    
        // ... code
        
        // 2. 构建httpItem结构体,然后扔进httpItemQueue channel即可
        httpItemQueue <- &Origin.TransferItem{
            Req:  req,
            Resp: resp,
        }
    
    }

    注意:由于body的特殊性,不管以什么形式成功加载了http.Request或者http.Response对象后,必须调用aio.TransformReadCloser(),将request.body和response.body进行类型转换

  3. 完成类构造后,创建实例化函数,并在main中进行Origin来源逻辑的更改
    img_1.png

  4. 完成上述部分,即可进行调试判断是否成功切换到新的Origin加载方式

拓展filter过滤器模块

通过Filter接口的实现,可以完成对origin加载的流量进行过滤

  1. 首先在APIKiller/core/filter目录下创建对应的filter 文件(以staticResourceFilter为例)
    img_2.png
  2. 创建StaticResourceFilter类,并实现APIKiller/core/filter/Filter接口
    type StaticResourceFilter struct {
        forbidenExts []string
    }
    
    func (f *StaticResourceFilter) Filter(ctx context.Context, req *http.Request) bool {
        logger.Debugln("[Filter] static file filter") // 此处必须统一打印加载debug级别日志
    
        // get request path extension
        lastIndex := strings.LastIndex(req.URL.Path, ".")
        if lastIndex == -1 {
            return FilterPass
        }
        ext := req.URL.Path[lastIndex+1:]
    
        // filter
        for _, forbidenExt := range f.forbidenExts {
            if forbidenExt == ext {
                return FilterBlocked
            }
        }
    
        return FilterPass
    }
    通过对*http.Request对象中属性的判断,来返回FilterBlocked\FilterPass,分别代表过滤当前流量\流量放行,对于过滤掉的流量,将不会进入后续的处理引擎
  3. 创建对应的构造器
    func NewStaticFileFilter(ctx context.Context) Filter {
        logger.Infoln("[Load Filter] static file filter") //构造器类必须完成日志打印
    
        return &StaticResourceFilter{
            forbidenExts: viper.GetStringSlice("app.filter.staticFileFilter.ext"),
        }
    }
  4. 通过修改main.go将过滤器模块添加到context中,启动项目正常运行,则代表成功添加Filter img_3.png

拓展核心检测模块

通过对Detect接口的实现,可以拓展核心检测引擎集群,扩大检测范围

  1. 先在APIKiller/core/module/ 创建对应检测模块包目录,例如openRedirectDetector/
    img_6.png

  2. 构建对应的类,并继承Detect接口

    type OpenRedirectDetector struct {
        rawQueryParams []string
        failFlag       []string
        evilLink       string
    }
    
    func (d *OpenRedirectDetector) Detect(ctx context.Context, item *data.DataItem) {
        logger.Debugln("[Detect] Open-Redirect detect") // 规范格式需求
    
        srcResp := item.SourceResponse
        srcReq := item.SourceRequest
    
        // get raw query parameter list
        for key, _ := range srcReq.URL.Query() {
            if slices.Contains(d.rawQueryParams, key) {
                // clone a new newReq
                newReq := ahttp.RequestClone(srcReq)
    
                // replace value of params in new newReq
                newReq.URL.Query()[key] = []string{d.evilLink}
    
                // do newReq
                newResp := ahttp.DoRequest(newReq)
    
                // judge
                if d.judge(srcResp, newResp) {
                    item.VulnType = append(item.VulnType, "open-redirect")
                    item.VulnRequest = append(item.VulnRequest, newReq)
                    item.VulnResponse = append(item.VulnResponse, newResp)
                }
    
                return
            }
        }
    }
  3. 在Detect引擎中写检测规则

    1. 如果需要对srcRequest进行修改,必须先进行 newReq := ahttp.RequestClone(srcReq) ,复制出一个*http.Request对象
    2. 如果需要进行http/https发包测试,必须 使用内部提供的 ahttp.DoRequest(newReq) 进行发包探测
    3. 如果需要修改http 请求报文中的参数,建议使用内部 ModifyURLQueryParameter 、ModifyPostJsonParameter、ModifyPostFormParameter进行参数修改,暂不支持multipart类型body进行参数修改
  4. 判定引擎逻辑添加

    1. 这个比较随意,主要看你这边怎么判定是否存在漏洞的
  5. 检测结果保存。如果检测结果是存在xx漏洞,需要进行数据保存

    // judge
    if d.judge(srcResp, newResp) {
       item.VulnType = append(item.VulnType, "open-redirect")
       item.VulnRequest = append(item.VulnRequest, newReq)
       item.VulnResponse = append(item.VulnResponse, newResp)
    }
  6. 构建构造器,并通过修改将检测模块添加到context中

    func NewOpenRedirectDetector(ctx context.Context) module.Detecter {
        if viper.GetInt("app.module.openRedirectDetector.option") == 0 { //规范化,通过在配置中添加option,来调度核心检测模块
            return nil
        }
    
        logger.Infoln("[Load Module] Open-Redirect detect module")
    
        d := &OpenRedirectDetector{
            rawQueryParams: viper.GetStringSlice("app.module.openRedirectDetector.rawQueryParams"),
            evilLink:       "https://www.baidu.com",
            failFlag:       viper.GetStringSlice("app.module.openRedirectDetector.failFlag"),
        }
    
        return d
    }

    img_7.png

  7. 调试新增检测引擎的检测能力

通知模块

通过Notify接口的实现,可以实现以不同的方式对漏洞探测结果进行及时通知

  1. 先在APIKiller/core/notify/ 创建对应notify文件,例如Dingding
    img_4.png

  2. 构建通知类,且其中必须要有 notifyQueue chan *data.DataItem 成员变量

    type Dingding struct {
        webhookUrl  string
        notifyQueue chan *data.DataItem
    }
  3. 实现Notify接口,完成发送逻辑的书写。其中NotifyQueue和SetNotifyQueue均采用默认的getter、setter方式即可

    func (d *Dingding) NotifyQueue() chan *data.DataItem {
        return d.notifyQueue
    }
    
    func (d *Dingding) SetNotifyQueue(NotifyQueue chan *data.DataItem) {
        d.notifyQueue = NotifyQueue
    }
    
    func (d *Dingding) Notify(item *data.DataItem) {
        logger.Infoln("notify dingding robot") //规范化日志打印
    
        // ... processing logic code 
    }
  4. 书写对应的构造类,并通过修改main.go将通知模块添加到context中

    func NewDingdingNotifer(ctx context.Context) *Dingding {
        // get config
        webhookUrl := viper.GetString("app.notifier.Dingding.webhookUrl")
        // create
        notifer := &Dingding{webhookUrl: webhookUrl}
    
        return notifer
    }

    img_5.png

  5. 调试成功即代表成功拓展通知方式

HTTP Hook机制

为避免扫描时造成过无效流量,可以通过提供的HTTP HOOK机制,对请求流量自定义修改,例如添加header,来区分测试流量和实际流量

【注意】当前由于golang plugin机制特性,暂不支持windows下的流量修改

  1. HTTP HOOK 样例
    package main
    
    import (
        "fmt"
        "net/http"
    )
    
    type RequestHook interface {
        HookBefore(*http.Request) // hook before initiating http request
        HookAfter(*http.Request)  // hook after finishing http request
    }
    
    type AddHeaderHook struct {
    }
    
    func (a AddHeaderHook) HookBefore(request *http.Request) {
        fmt.Println("HOOK Before: hhhhhhh")
        // ....
    }
    
    func (a AddHeaderHook) HookAfter(request *http.Request) {
    
    }
    
    // Hook this is exported, and this name must be set Hook
    var Hook AddHeaderHook

【严格按照上面的代码规范,其中最后一行代码,命名必须设置为Hook】

  1. 生成对应的so链接库

    go build -buildmode=plugin APIKillerHookSample.go
    $ ls
    APIKillerHookSample.go  APIKillerHookSample.so  go.mod
  2. 将生成的so放置到项目的hooks目录下

    $ ls ./hooks
    APIKillerHookSample.so
  3. 启动项目即可完成流量更改

Clone this wiki locally