记录一次某协同系统SQL注入漏洞挖掘过程
分析项目结构和依赖,发现没有如mybatis等第三方数据持久化框架,存在Dao目录
在Java Web开发中,DAO(Data Access Object)是一个设计模式,用于将数据访问逻辑从业务逻辑中分离出来,提高代码的可维护性和可重用性。DAO层是一个抽象接口,封装了数据的CRUD操作(增加、删除、修改、查询),并且提供了面向对象的方式访问数据库。
所以一般来讲其与数据库交互的代码都是在该目录中实现的。
我们首先找到dao接口(主要是定义了对数据库进行CRUD操作的方法)
其中SysDao
接口定义了查询数据库等操作的方法
他有5个实现,其中BasicDaoImpl
,ModeDaoImpl
和SysTableDao
为Dao实现类
我们看到在SysDao
定义方法中getStringData
这个方法看命名就像是通过传入string来获取数据,于是我们根据到getStringData(String var1)
的实现
其在SysTableDao
类中,调用重载方法String getStringData(String sql, Object[] params)
最终调用getStringData(final String sql, final Object[] params, boolean debug)
,该方法也是getStringData
的具体实现
其主要逻辑是
Connection
实例;Connection
实例创建PreparedStatement
实例;executeQuery
执行SQL语句,如果是查询,则通过ResultSet
读取结果集当调用getStringData(String sql)
是传入的参数params
为null,不会进入if逻辑中处理,其实就相当于直接执行了sql
参数。
于是我们寻找哪里调用了getStringData(String)
方法。
发现有6个jsp中调用了getStringData(String)
方法
一个个跟进发现除了index.jsp外其他jsp均可控
例如share_select.jsp
中获取请求中type
和fid
参数,当type
的值为2
时会直接拼接fid
到sql语句中,调用getStringData
方法。这里很明显存在SQL注入。
但是尝试直接访问该JSP会跳转至登录页,该系统存在jsp的鉴权操作。我们顺势找找有关的filter,看有没有可以绕过的方法。
我们找到web.xml
,寻找有没有相关filter
果然有一个拦截.jsp
的filter,跟进到其中找到doFilter
方法实现
首先调用ServletRequest.getRequestURI()
获取请求接口地址,随后判断请求接口地址是否包括或者以某特定字符串结尾做响应处理。从中看到当我们请求jsp时会调用validata()
方法
其主要是获取请求参数,检测sql注入黑名单字符:
private static boolean sqlValidate(String str) {
str = str.toLowerCase();
String[] badStrs = new String[]{"exec%20", "execute%20", "drop%20", "truncate%20", "net%20user%20", "xp_cmdshell%20", "select%20", "insert%20", "update%20"};
String[] arr$ = badStrs;
int len$ = badStrs.length;
for(int i$ = 0; i$ < len$; ++i$) {
String badStr = arr$[i$];
if (str.contains(badStr)) {
return true;
}
}
return false;
}
当存在以上黑名单字符串时返回true,最后进入if逻辑跳转至登录页。
扎眼一看好像该filter只会对参数存在sql注入黑名单字符串的请求进行跳转,直接访问应该是直接放行,但是我们实际访问还是跳到了登录页,有点迷惑。
根据关键字找到该filter中有一个方法verificationURL()
:
当是未授权请求时(即UserSession为null)会进行设置返回包跳转,在doFilter方法中有三处调用
当我们访问xx.jsp
时会进入到这个if判断中
if ((!uri.endsWith(".jsp") || !this.isNotValidatePage(uri)) && !uri.endsWith(".xml") && !uri.endsWith(".xf") || this.verificationURL(request, uri, response))
其逻辑是请求xxx.jsp
,!uri.endsWith(".jsp")
默认为false,会依次调用isNotValidatePage()
(isNotValidatePage
主要是判断请求的jsp是不是登录等可以未授权方法的白名单jsp文件,默认也是返回false)和verificationURL()
。
此时因为我们是未授权访问所以该if语句最终结果为if(false || false || false)
不会进入到下面逻辑中,所以被进行了拦截返回到了登录页。
不过此处因为调用request.getRequestURI()
方法,可以利用;
进行绕过(即构造xxx.jsp;.jpg),同时在doFilter
逻辑中当访问路径以图片后缀结尾时直接放行
于是我们可以构造访问地址为/xxxx/share_select.jsp;jpg
可成功测试SQL注入
我们回到SysDao
接口,其还有一个继承接口Dao
看到其定义了getFieldSetBySql()
的方法,这个方法也像是通过sql语法获取数据。
我们跟进来点其实现类BasicDaoImpl
中查看其具体实现
他有两个getFieldSetBySql
的重载方法,主要是实现是在getFieldSetBySql(String sql, Object[] params, String tableName)
方法中
其首先是调用SQLTranslatorManager.translator()解析处理sql语句
这里translator
默认是false
,所以直接返回原始sql语句
回到getFieldSetBySql
方法,处理完sql语句,调用this.getDataTable
方法执行sql语句获取结果
因为传入tableName
为null,所以不在if逻辑中,当我们调用getFieldSetBySql(String sql, String tableName)
方法时,传入params
参数为null,也会跳过ToolFormatSql.format(sql, params);
,最后调用execute
方法执行sql语句。
分析完getFieldSetBySql
方法处理逻辑,我们寻找调用该方法且参数可控地方,利用IDEA找到了多处调用
但是基本都是拼接user.getUserID
作为SQL语句,没有找到可控点,一时陷入不知所措
不过根据javaweb项目 有Dao
层就会有Service
层
Service层叫服务层,被称为服务,粗略的理解就是对一个或多个Dao进行再次封装,封装成一个服务,所以这一层不会是一个原子操作了,需要事务控制。
我们在其Service
层寻找有没有调用getFieldSetBySql
的方法,经过筛查最终找到一处在FormService
类中调用getFieldSetBySql
的方法,其拼接参数到sql语句且处理逻辑比较简单
右键方法名找到调用该方法的地方,最后定位在validate.jsp
中
可以看到这里解析请求中的json数据,根据type选择处理方式,当type等于0是进入formService.getFormTableInfo
方法,所以relId
这个参数存在SQL注入风险,同时根据json特性可通过unicode编码绕过前面filter中对sql注入的检测,具体利用就不赘述了
79 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!