# KUNPENG_NOTE **Repository Path**: zmf96/KUNPENG_NOTE ## Basic Information - **Project Name**: KUNPENG_NOTE - **Description**: kunpeng开源poc框架学习记录。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2019-12-19 - **Last Updated**: 2021-11-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 开源POC框架学习1-Kunpeng篇 - [开源POC框架学习1-Kunpeng篇](#%e5%bc%80%e6%ba%90poc%e6%a1%86%e6%9e%b6%e5%ad%a6%e4%b9%a01-kunpeng%e7%af%87) - [1. 概述](#1-%e6%a6%82%e8%bf%b0) - [2. 编译](#2-%e7%bc%96%e8%af%91) - [3. 使用方法](#3-%e4%bd%bf%e7%94%a8%e6%96%b9%e6%b3%95) - [4. 使用例子](#4-%e4%bd%bf%e7%94%a8%e4%be%8b%e5%ad%90) - [5. 项目的目录结构](#5-%e9%a1%b9%e7%9b%ae%e7%9a%84%e7%9b%ae%e5%bd%95%e7%bb%93%e6%9e%84) - [6. web/api.go](#6-webapigo) - [7. plugin部分代码](#7-plugin%e9%83%a8%e5%88%86%e4%bb%a3%e7%a0%81) - [7.1. 如何实现一个插件](#71-%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%e4%b8%80%e4%b8%aa%e6%8f%92%e4%bb%b6) - [7.2. 插件的调用部分如何实现的](#72-%e6%8f%92%e4%bb%b6%e7%9a%84%e8%b0%83%e7%94%a8%e9%83%a8%e5%88%86%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%e7%9a%84) - [7.2.1. StartWebServer](#721-startwebserver) - [7.2.2. GetLog](#722-getlog) - [7.2.3. GetVersion](#723-getversion) - [7.2.4. SetConfig](#724-setconfig) - [7.2.5. GetPlugins](#725-getplugins) - [7.2.6. Check](#726-check) - [7.2.7. StartBuffer](#727-startbuffer) - [7.2.8. ShowLog](#728-showlog) - [8. example/poc-scanner的实现](#8-examplepoc-scanner%e7%9a%84%e5%ae%9e%e7%8e%b0) - [9. 后记](#9-%e5%90%8e%e8%ae%b0) - [10. QA](#10-qa) - [10.1. example例子(call_webapi_test.py)报错](#101-example%e4%be%8b%e5%ad%90callwebapitestpy%e6%8a%a5%e9%94%99) ## 1. 概述 为啥从Kunpeng开始,一方面是学习下golang,另外一方面是因为它的代码量少(项目的核心代码只有几百行的样子),项目文档及注释比较完善。 ```shll $ cloc . 140 text files. 140 unique files. 11 files ignored. github.com/AlDanial/cloc v 1.84 T=0.09 s (1501.2 files/s, 95778.4 lines/s) ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- Go 63 378 137 4613 Markdown 5 197 0 886 JSON 36 0 0 828 Python 12 131 44 426 HTML 9 47 2 411 JavaScript 3 12 12 109 C 1 12 4 39 Lua 1 9 9 35 Java 1 16 26 23 Bourne Shell 1 4 0 12 ------------------------------------------------------------------------------- SUM: 132 806 234 7382 ------------------------------------------------------------------------------- ``` ## 2. 编译 ```bash git clone https://github.com/opensec-cn/kunpeng.git cd kunpeng # 静态资源打包进工程的小程序 git clone https://github.com/mjibson/esc cd esc go build # 打包JSON插件到项目代码中 ./esc/main -include='\.json$' -o plugin/json/JSONPlugin.go -pkg jsonplugin plugin/json/ # 编译c版本(所有语言均可使用) go build -buildmode=c-shared --ldflags="-w -s -X main.VERSION=20191218" -o kunpeng_c.so # 编译Go专用版本(不支持win) go build -buildmode=plugin --ldflags="-w -s -X main.VERSION=20191218" -o kunpeng_go.so # 样例测试 python example/call_so_test.py go run example/callsoTest.go ``` ## 3. 使用方法 ```go 接口调用说明 /* 传入需检测的目标JSON,格式为: { "type": "web", //目标类型web或者service "netloc": "http://xxx.com", //目标地址,web为URL,service格式为123.123.123.123:22 "target": "wordpress", //目标名称,GO插件注册时使用的字符串(模糊匹配)、JSON插件的target属性(模糊匹配)、CVE编号(例:CVE-xx-xxx)、KPID(例:KP-0013)编号,决定使用哪些POC进行检测,具体查看 /doc/plguin.md "meta":{ "system": "windows", //操作系统,部分漏洞检测方法不同系统存在差异,提供给插件进行判断 "pathlist":[], //目录路径URL列表,部分插件需要此类信息,例如列目录漏洞插件 "filelist":[], //文件路径URL列表,部分插件需要此类信息,例如struts2漏洞相关插件 "passlist":[] //自定义密码字典 } // 非必填 } 返回是否存在漏洞和漏洞检测结果 */ Check(taskJSON string) string // 获取插件列表信息 GetPlugins() string /* 配置设置,传入配置JSON,格式为: { "timeout": 15, // 插件连接超时 "aider": "http://123.123.123.123:8088", // 漏洞辅助验证接口,部分漏洞无法通过回显判断是否存在漏洞,可通过辅助验证接口进行判断。python -c'import socket,base64;exec(base64.b64decode("aGlzdG9yeSA9IFtdCndlYiA9IHNvY2tldC5zb2NrZXQoc29ja2V0LkFGX0lORVQsc29ja2V0LlNPQ0tfU1RSRUFNKQp3ZWIuYmluZCgoJzAuMC4wLjAnLDgwODgpKQp3ZWIubGlzdGVuKDEwKQp3aGlsZSBUcnVlOgogICAgdHJ5OgogICAgICAgIGNvbm4sYWRkciA9IHdlYi5hY2NlcHQoKQogICAgICAgIGRhdGEgPSBjb25uLnJlY3YoNDA5NikKICAgICAgICByZXFfbGluZSA9IGRhdGEuc3BsaXQoIlxyXG4iKVswXQogICAgICAgIGFjdGlvbiA9IHJlcV9saW5lLnNwbGl0KClbMV0uc3BsaXQoJy8nKVsxXQogICAgICAgIHJhbmtfc3RyID0gcmVxX2xpbmUuc3BsaXQoKVsxXS5zcGxpdCgnLycpWzJdCiAgICAgICAgaHRtbCA9ICJORVcwMCIKICAgICAgICBpZiBhY3Rpb24gPT0gImFkZCI6CiAgICAgICAgICAgIGhpc3RvcnkuYXBwZW5kKHJhbmtfc3RyKQogICAgICAgICAgICBwcmludCAiYWRkIityYW5rX3N0cgogICAgICAgIGVsaWYgYWN0aW9uID09ICJjaGVjayI6CiAgICAgICAgICAgIHByaW50ICJjaGVjayIrcmFua19zdHIKICAgICAgICAgICAgaWYgcmFua19zdHIgaW4gaGlzdG9yeToKICAgICAgICAgICAgICAgIGh0bWw9IlZVTDAwIgogICAgICAgICAgICAgICAgaGlzdG9yeS5yZW1vdmUocmFua19zdHIpCiAgICAgICAgcmF3ID0gIkhUVFAvMS4wIDIwMCBPS1xyXG5Db250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9dXRmLThcclxuQ29udGVudC1MZW5ndGg6ICVkXHJcbkNvbm5lY3Rpb246IGNsb3NlXHJcblxyXG4lcyIgJShsZW4oaHRtbCksaHRtbCkKICAgICAgICBjb25uLnNlbmQocmF3KQogICAgICAgIGNvbm4uY2xvc2UoKQogICAgZXhjZXB0OnBhc3M="))' 在辅助验证机器上运行以上代码,填入http://IP:8088,不开启则留空。 "http_proxy": "http://123.123.123.123:1080", // HTTP代理,所有插件http请求流量将通过代理发送(需使用内置的http请求函数util.RequestDo) "pass_list": ["passtest"], // 默认密码字典,不定义则使用硬编码在代码里的小字典 "extra_plugin_path": "/tmp/plugin/" // 除已编译好的插件(Go、JSON)外,可指定额外插件目录(仅支持JSON插件),指定后程序会周期读取加载插件 } */ SetConfig(configJSON string) // 开启web接口,开启后可通过web接口进行调用,webapi调用格式请查看例子:/example/call_webapi_test.py StartWebServer(bindAddr string) // 获取当前版本 例如:20190227 GetVersion() string ``` ## 4. 使用例子 下面kuepng_c.so对应的头文件 ```c // 查看.so导出函数: // objdump -tT kunpeng_c.so // nm -D kunpeng_C.so /* Code generated by cmd/cgo; DO NOT EDIT. */ /* package kunpeng */ #line 1 "cgo-builtin-export-prolog" #include /* for ptrdiff_t below */ #ifndef GO_CGO_EXPORT_PROLOGUE_H #define GO_CGO_EXPORT_PROLOGUE_H #ifndef GO_CGO_GOSTRING_TYPEDEF typedef struct { const char *p; ptrdiff_t n; } _GoString_; #endif #endif /* Start of preamble from import "C" comments. */ /* End of preamble from import "C" comments. */ /* Start of boilerplate cgo prologue. */ #line 1 "cgo-gcc-export-header-prolog" #ifndef GO_CGO_PROLOGUE_H #define GO_CGO_PROLOGUE_H typedef signed char GoInt8; typedef unsigned char GoUint8; typedef short GoInt16; typedef unsigned short GoUint16; typedef int GoInt32; typedef unsigned int GoUint32; typedef long long GoInt64; typedef unsigned long long GoUint64; typedef GoInt64 GoInt; typedef GoUint64 GoUint; typedef __SIZE_TYPE__ GoUintptr; typedef float GoFloat32; typedef double GoFloat64; typedef float _Complex GoComplex64; typedef double _Complex GoComplex128; /* static assertion to make sure the file is being used on architecture at least with matching size of GoInt. */ typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; #ifndef GO_CGO_GOSTRING_TYPEDEF typedef _GoString_ GoString; #endif typedef void *GoMap; typedef void *GoChan; typedef struct { void *t; void *v; } GoInterface; typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; #endif /* End of boilerplate cgo prologue. */ #ifdef __cplusplus extern "C" { #endif extern void StartWebServer(char* p0); extern char* Check(char* p0); extern char* GetPlugins(); extern void SetConfig(char* p0); extern void ShowLog(); extern char* GetVersion(); extern void StartBuffer(); extern char* GetLog(char* p0); #ifdef __cplusplus } #endif ``` ```python #coding:utf-8 import time import json from ctypes import * # 加载动态连接库 kunpeng = cdll.LoadLibrary('./kunpeng_c.so') # 定义出入参变量类型 kunpeng.GetPlugins.restype = c_char_p kunpeng.Check.argtypes = [c_char_p] kunpeng.Check.restype = c_char_p kunpeng.SetConfig.argtypes = [c_char_p] kunpeng.GetVersion.restype = c_char_p # 获取插件信息 out = kunpeng.GetPlugins() print(out) # 修改配置 config = { 'timeout': 10, # 'aider': 'http://xxxx:8080', # 'http_proxy': 'http://xxxxx:1080', # 'pass_list':['xtest'] # 'extra_plugin_path': '/home/test/plugin/', } kunpeng.SetConfig(json.dumps(config)) # 开启日志打印 kunpeng.ShowLog() # 扫描目标 task = { 'type': 'web', 'netloc': 'http://www.google.cn', 'target': 'web' } task2 = { 'type': 'service', 'netloc': '192.168.0.105:3306', 'target': 'mysql' } out = kunpeng.Check(json.dumps(task)) print(json.loads(out)) out = kunpeng.Check(json.dumps(task2)) print(json.loads(out)) ``` ## 5. 项目的目录结构 ``` . ├── config │   └── config.go  # 配置文件 ├── doc │   ├── img.png │   └── plugin.md # 漏洞poc列表 ├── example # 使用示例,包括各种语言的调用示例 C、go、java、js、lua、python │   ├── call_so_test.c │   ├── callsoTest.go │   ├── call_so_test.java │   ├── call_so_test.js │   ├── call_so_test.lua │   ├── call_so_test.py │   ├── call_webapi_test.py │   ├── nmap_kunpeng # 先使用nmap扫描开放端口,然后在调用kunpeng进行检查 │   │   ├── nmap_kunpeng.py │   │   ├── README.md │   │   └── requirements.txt │   └── poc-scanner # 使用kunpeng做的一个扫描器,后面会有详细的学习记录 ..... ├── go.mod # go mod文件 ├── go.sum ├── kunpeng_c.h # 生成的C语言头文件 ├── kunpeng_c.so # go build生成的so文件 ├── kunpeng_go.so ├── LICENSE ├── main.go # 程序入口文件 主要就上面实现的那几个导出函数 ├── note.md ├── plugin │   ├── go │   ├── go.go │   ├── json ..... │   │   ├── init.go │   │   ├── JSONPlugin.go │   │   ├── phpmyadmin_deserialization.json ..... │   ├── json.go │   └── plugin.go ├── README.md ├── util │   ├── aider.go │   ├── fun.go │   ├── log.go │   └── net.go └── web # 使用gin框架写的http接口,一个get,一个post请求,比较简单。 └── api.go 25 directories, 185 files ``` ## 6. web/api.go 该部分比较简单,只有三个简单的请求,主要就是调用该框架实现的核心函数 GetPlugins()、Scan()、config.Set() ```go // StartServer 启动web服务接口 func StartServer(bindAddr string) { router := gin.Default() // 创建路由对象 router.GET("/api/pluginList", func(c *gin.Context) { // 添加一个get请求 c.JSON(200, plugin.GetPlugins()) // 该请求以json格式返回插件相关的信息 }) router.POST("/api/check", func(c *gin.Context) { // 添加一个post请求,调用插件开始扫描 var json plugin.Task if err := c.ShouldBindJSON(&json); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } result := plugin.Scan(json) // 开始扫描 c.JSON(200, result) /// 返回扫描结果 }) router.POST("/api/config", func(c *gin.Context) { // 添加一个pos请求,设置config buf := make([]byte, 2048) n, _ := c.Request.Body.Read(buf) config.Set(string(buf[0:n])) c.JSON(200, map[string]bool{"success": true}) }) router.Run(bindAddr) // h监听并在 bindAddr (例如: 0.0.0.0:38080 )上启动服务 } ``` 对应的python测试代码 ```python import time from ctypes import * import json import requests kunpeng = cdll.LoadLibrary('../kunpeng_c.so') # 加载so文件 kunpeng.StartWebServer.argtypes = [c_char_p] # 说明StartWebServer函数的参数类型 kunpeng.StartWebServer("0.0.0.0:38080".encode("utf-8")) # 启动kunpneg的web服务 time.sleep(5) # 等待5s api = 'http://127.0.0.1:38080' # 请求接口,获取插件信息 plugin_list = requests.get(api + '/api/pluginList').json() print(plugin_list) # 请求接口,设置相应配置 config = { 'timeout': 10, # 'aider': 'http://xxxx:8080', # 'http_proxy': 'http://xxxxx:1080', 'pass_list':['xtest'], # 'extra_plugin_path': '/home/test/plugin/', } requests.post(api + '/api/config',json=config) task = { 'type': 'web', 'netloc': 'http://www.google.cn', 'target': 'web', 'meta':{ 'system': '', 'pathlist':[], 'filelist':[], 'passlist':[] } } task2 = { 'type': 'service', 'netloc': '192.168.0.105:3306', 'target': 'mysql', 'meta':{ 'system': '', 'pathlist':[], 'filelist':[], 'passlist':[] } } result = requests.post(api + '/api/check',json=task).json() # 开始第一个扫描任务 print(result) result = requests.post(api + '/api/check',json=task2).json() # 开始第二个扫描任务 print(result) ``` ## 7. plugin部分代码 ### 7.1. 如何实现一个插件 kunpeng支持json插件与go插件,参见作者写的README及example目录中的现有插件。 - golang插件例子1 ```go // 包名需定义goplugin package goplugin // 引入plugin import ( "fmt" "kunpeng/plugin" "github.com/go-redis/redis" ) // 定义插件结构,info,result需固定存在 type redisWeakPass struct { info plugin.Plugin // 插件信息 result []plugin.Plugin // 漏洞结果集,可返回多个 } func init() { // 注册插件,定义插件目标名称 plugin.Regist("redis", &redisWeakPass{}) } func (d *redisWeakPass) Init() plugin.Plugin{ d.info = plugin.Plugin{ Name: "Redis 未授权访问/弱口令", // 插件名称 Remarks: "导致敏感信息泄露,严重可导致服务器直接被入侵控制。", // 漏洞描述 Level: 0, // 漏洞等级 {0:"严重",1:"高危",2:"中危",3:"低危",4:"提示"} Type: "WEAKPASS", // 漏洞类型,自由定义 Author: "wolf", // 插件编写作者 References: plugin.References{ URL: "https://www.freebuf.com/vuls/162035.html", // 漏洞相关文章 CVE: "", // CVE编号,没有留空或不申明 KPID: "KP-0008", // kunpeng的POC编号,累加数字 }, } return d.info } func (d *redisWeakPass) GetResult() []plugin.Plugin { var result = d.result d.result = []plugin.Plugin{} return result } func (d *redisWeakPass) Check(netloc string, meta plugin.TaskMeta) bool { for _, pass := range meta.PassList { client := redis.NewClient(&redis.Options{ Addr: netloc, Password: pass, DB: 0, }) _, err := client.Ping().Result() if err == nil { client.Close() result := d.info result.Request = fmt.Sprintf("redis://%s@%s", pass, netloc) if pass == "" { result.Remarks = fmt.Sprintf("未授权访问,%s", result.Remarks) } else { result.Remarks = fmt.Sprintf("弱口令:%s,%s", pass, result.Remarks) } d.result = append(d.result, result) return true } } return false } ``` - JSON插件例子 ```javascript { "//": "用 Google 的方式进行注释", "//": "插件所属应用名,自由定义", "target": "wordpress", "meta":{ "//": "插件名称", "name": "WordPress example.html jQuery DomXSS", "//": "漏洞描述", "remarks": "WordPress example.html jQuery 1.7.2 存在DomXSS漏洞", "//": "漏洞等级 {0:严重,1:高危,2:中危,3:低危,4:提示}", "level": 3, "//": "漏洞类型,自由定义", "type": "XSS", "//": "插件编写作者", "author": "wolf", "references": { "//": "漏洞相关文章", "url":"https://www.seebug.org/vuldb/ssvid-89179", "//": "CVE编号,没有留空", "cve":"", "//": "kunpeng的POC编号,累加数字", "kpid":"KP-0003" } }, "request":{ "//": "漏洞请求URL", "path": "/wp-content/themes/twentyfifteen/genericons/example.html", "//": "请求POST内容,留空即为GET", "postData": "" }, "verify":{ "//": "漏洞验证类型 {string:字符串判断,regex:正则匹配,md5:文件md5}", "type": "string", "//": "漏洞验证值,与type相关联", "match": "jquery/1.7.2/jquery.min.js" } } ``` ### 7.2. 插件的调用部分如何实现的 核心代码就以下6个文件,看起来比较简单的样子。 ```bash plugin ├── go ├── go.go ├── json │ ├── docker_api.json │ ├── init.go │ ├── JSONPlugin.go ├── json.go └── plugin.go main.go ``` 主要的8个导出函数 ```c extern void StartWebServer(char* p0);  extern char* Check(char* p0); extern char* GetPlugins(); extern void SetConfig(char* p0); extern void ShowLog(); extern char* GetVersion(); extern void StartBuffer(); extern char* GetLog(char* p0); ``` #### 7.2.1. StartWebServer 启动web api ```go //export StartWebServer func StartWebServer(bindAddr *C.char) { go web.StartServer(C.GoString(bindAddr)) } ``` #### 7.2.2. GetLog 获取log,待补充 #### 7.2.3. GetVersion 获取Version,没啥好说的 #### 7.2.4. SetConfig 待补充 #### 7.2.5. GetPlugins 循环GoPlugins与JSONPlugins两个map,将插件的信息放到plugins,方便其他部分用。 ```go // GetPlugins 获取插件信息 func GetPlugins() (plugins []map[string]interface{}) { for name, pluginList := range GoPlugins { for _, plugin := range pluginList { info := plugin.Init() pluginMap := util.Struct2Map(info) delete(pluginMap, "request") delete(pluginMap, "response") pluginMap["target"] = name plugins = append(plugins, pluginMap) } } for name, pluginList := range JSONPlugins { for _, plugin := range pluginList { pluginMap := util.Struct2Map(plugin.Meta) delete(pluginMap, "request") delete(pluginMap, "response") pluginMap["target"] = name plugins = append(plugins, pluginMap) } } sort.Stable(pluginsSlice(plugins)) return plugins } ``` #### 7.2.6. Check 待补充 大概调用流程: Check() -> plugin.Scan() -> plugin.Run()/jsonCheck() ```go //export Check func Check(task *C.char) *C.char { util.Logger.Info(C.GoString(task)) var m plugin.Task // 插件任务m err := json.Unmarshal([]byte(C.GoString(task)), &m) if err != nil { util.Logger.Error(err.Error()) return C.CString("[]") } util.Logger.Info(m) // 打印log result := plugin.Scan(m) // 开始扫描 if len(result) == 0 { return C.CString("[]") } b, err := json.Marshal(result) if err != nil { util.Logger.Error(err.Error()) return C.CString("[]") } return C.CString(string(b)) // 返回扫描结果 } ``` #### 7.2.7. StartBuffer 待补充 #### 7.2.8. ShowLog 待补充 ## 8. example/poc-scanner的实现 目录结构如下: ```bash ├── app.py # 程序入口 ├── config.py ├── extra │ └── ugj_59eeb735187efa73c0c4e94dcada4570.json ├── poc_server.conf ├── README.md ├── requirements.txt ├── static # 静态资源文件 │ └── js │ ├── formating.js │ └── md5.min.js ├── templates # 静态模板文件 │ ├── edit.html │ ├── login.html │ ├── message.html │ ├── navbar.html │ ├── panel_header.html │ ├── pluginList.html │ ├── register.html │ ├── result.html │ └── scan.html ├── tools │ ├── basetornado.py │ ├── command.py # 程序启动代码 │ ├── handlers │ │ ├── Index.py # 插件列表、自行添加的json插件的修改删除、 用户登录退出 │ │ ├── __init__.py │ │ ├── Message.py │ │ ├── Register.py # 添加新json插件 │ │ └── Scan.py # 执行扫描任务的部分 │ ├── __init__.py │ └── so_proxy.py # 对kunpeng_c.co的封装 └── upso.sh ``` 核心就是对Kunpeng_c.so的封装,其余部分是一个tornado写的网站。 ## 9. 后记 年前就开始写了,然后... 有些关键的地方没写清楚,有时间在补充 水平有限,大佬多多指教 感谢opensec-cn的开源 ## 10. QA ### 10.1. example例子(call_webapi_test.py)报错 ```bash Traceback (most recent call last): File "call_webapi_test.py", line 8, in kunpeng.StartWebServer("0.0.0.0:38080") ctypes.ArgumentError: argument 1: : wrong type ``` 修改第8行为`kunpeng.StartWebServer("0.0.0.0:38080".encode("utf-8"))`,问题解决