原文地址:https://forum.butian.net/share/2448
原文作者:Duck
一次偶然机会,找人要了套源码,是Stuts2和spring框架混用的系统,比较简单适合入门学习
0x01 SQL注入
该项目是基于mvc框架开发的,与数据库交互的代码在DAO
目录下,在core/base/dao中存在IBaseDao类属于该项目的数据库操作的基类
根据其定义的函数名,都能看出是传入SQL语句进行查询,我们找的他的实现类
可以看到其方法实现也是传入SQL语句字符串直接它通过hibernateTemplate API直接进行查询。其实现SQL查询的方法也有很多。
同时我们发现该baseDao类有七百多个继承者
跟进去发现基本都继承了BaseDao的方法,并没有进行重载,于是我们全局搜索相关危险方法,这里以sqlFind方法为例
发现在构造SQL语句是用了?占位符,这样的语法是无法进行注入的,于是我们寻找直接拼接参数到SQL语句字符串中的方法
根据sqlFind方法的重装情况,当参数param
为空则不会进行占位符参数绑定,于是我们可以缩小搜索范围
向下翻找就找到在BaseDataService
类中listBaseDataByFlowId
方法,存在直接拼接参数构造SQL语句的情况,
这里直接将flowId
等参数拼接到语句中调用sqlFind
方法进行数据库查询,我们向上找是哪个controller调用该危险方法
最终定位在BaseDataController
中的listBaseDataByFlowId
方法中
这里直接将请求中type和flowId传入service层进行查询,我们构造该接口的请求
提示我们没有登录,应该是spring进行了拦截处理
0x02 权限绕过
我们查看spring配置文件,找的拦截器的实现类
可以看到该项目有个全局拦截器PermissionInterceptor
,我们跟进到里面,根据spring拦截器实现原理直接来到prehandle
方法,主要处理逻辑如下
该拦截器定义了两个成员数组UN_LOGIN_EXPOSE_CONTROLLERS
和UN_LOGIN_EXPOSE_SERVLET_PATH
,当我们请求的控制器或者接口url在这两个数组中是会放行我们的请求,也就是定义了白名单请求,很遗憾我们的listBaseDataByFlowId
方法的请求不在白名单中,会进入if判断。
首先判断session中是否有登录凭证(显然没有),如果没有会获取请求中jwt参数的值进行JSON Web Token的认证,检查token的有效性。
看到jwt 认证,很快就想到会不会硬编码jwt 密钥,因为在初期翻配置文件中没看到有jwt 密钥的定义
于是我们跟进到jwtCheck
方法,果然验证了我们的猜想
于是我们根据密钥生成token
带着jwt token访问目标接口,发现不在提示未登录
根据配置文件该系统是属于了sql server数据库,于是构造语句
flowId = 1'+(case when (1=1) then '' else char(1/0) end)+'
当为正是,正常返回
为假是进行char(1/0)构造报错会返回500
证明存在SQL注入
0x03 总结
1、mvc框架中service层一般是负责sql语句的封装,dao层一般负责与数据库交互,所以我们可以在dao层中找直接执行sql语句的方法,在service层调用可疑查询方法处查看sql语句构造情况,找的可能存在sql注入的点,向上找的controller层的具体调用接口
2、主要还是看鉴权,通过配置文件找的鉴权类,如使用了request.getRequestUri等危险方法来获取请求url做判断时很容易就被bypass,或是看jwt等第三方鉴权组件硬编码的问题
0x04 另外发现
1、未授权文件上传
在鉴权文件中存在白名单controller类和接口数组UN_LOGIN_EXPOSE_CONTROLLERS
和UN_LOGIN_EXPOSE_SERVLET_PATH
,我们关注一下
翻找未授权类时在appCallController中,有文件操作的方法
"uploadFile"}) ({
(
readOnly = false
)
public String uploadFile( (value = "file",required = false) MultipartFile file) {
try {
String id = UUIDUtils.getuid();
String extension = StringUtils.getFilenameExtension(file.getOriginalFilename());
if (StringUtils.hasText(extension)) {
extension = "." + extension;
}
String fileUrl = "../xxx_Ueditor_File/app/files/" + id + extension;
String savePath = getCurRequest().getSession().getServletContext().getRealPath(fileUrl);
File saveFile = new File(savePath);
if (!saveFile.exists()) {
saveFile.mkdirs();
}
file.transferTo(saveFile);
APPFiles af = new APPFiles();
af.setFileLength(file.getSize());
af.setId(id);
af.setFilePath(saveFile.getAbsolutePath());
af.setFileName(file.getOriginalFilename());
af.setTime(new Date());
af.setFileType(file.getContentType());
af.setMsgId("");
af.setType(2);
this.dao.save(af);
return id;
} catch (Exception var8) {
var8.printStackTrace();
return "";
}
}
该方法是获取随机数id,然后取到表单上传中的文件名后缀名,然后将其保存在/xxx_Ueditor_File/app/files/
目录中,最后讲随机数id返回给前端,因为未对文件后缀名做限制导致可以上传jsp文件
成功上传文件
2、未授权SQL注入
该项目除了用到spring框架外还用到了st2框架,在struts.xml
查看配置信息,这里也定义了拦截器
具体实现逻辑在doIntercept()方法中
可以看到这里也是定义白名单方法名和类名
private Set<String> UN_LOGIN_EXPOSE_METHODS = new HashSet<String>() {
{
this.add("saveStaffInfo");
this.add("updateStaffInfo");
this.add("listRegionTree");
this.add("CostCloudProject");
...
this.add("handle");
this.add("listProjAuditQGC");
this.add("getlistProjAuditDataQGC");
}
};
private static List<Class<?>> TEMP_USER_EXPOSE_ACTIONS = new ArrayList<Class<?>>() {
{
this.add(IndexAction.class);
...
}
};
private static List<String> TEMP_USER_EXPOSE_METHODS = new ArrayList<String>() {
{
this.add("showPhoto");
this.add("download");
}
};
看到白名单方法中有些get操作的函数,如getlistProjAuditDataQGC
看名称是获取list数据,跟进其中
可以看到调用findByBusinessByQueryQGC
方法进行查询,来到findByBusinessByQueryQGC
方法实现,可以看到其是获取请求中的特定参数,并将其直接拼接在sql变量中
最后调用sqlFindPage
拼接sql变量执行
此处也是存在SQL注入
原文始发于微信公众号(WebSec):某OA系统的审计(新手学习)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论