问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
某系统模板注入分析
漏洞分析
某系统前阵子公开了模板注入的漏洞,漏洞点在于程序对传入的sql语句处理时使用了freemarker模板渲染导致的问题。代码对该接口传参的处理先经过sql关键字的过滤,且分析中发现了一处可用的sql注入的payload,故先从sql注入开始介绍。
某系统模板注入漏洞分析 =========== 某系统前阵子公开了模板注入的漏洞,漏洞点在于程序对传入的sql语句处理时使用了freemarker模板渲染导致的问题。代码对该接口传参的处理先经过sql关键字的过滤,且分析中发现了一处可用的sql注入的payload,故先从sql注入开始介绍。 1.sql注入 ======= 下载源码后导入idea运行(使用mysql数据库) 先来看一眼公开的模板注入的payload,传参的字段和接口名称让人不免怀疑此处是否还有sql注入,于是看看接口逻辑 ```php POST /jeecg-boot/jmreport/queryFieldBySql HTTP/1.1 Host: localhost:8080 Content-Type: application/json Content-Length: 100 {"sql":"select '<#assign value=\"freemarker.template.utility.Execute\"?new()>${value(\"whoami\")}'"} ``` 反编译漏洞依赖包后导入idea,全局搜索queryFieldBySql定位漏洞代码如下 ![image-20230915114712114](https://shs3.b.qianxin.com/butian_public/f432164f203a2c45000037621b2da9cc76cd7843a4e63.jpg) 打上断点,bp发送payload({"sql":"select 1 from demo"}),开始调试 进入i.a()方法 ![image-20230914212223422](https://shs3.b.qianxin.com/butian_public/f546478f5376fb51b01417fff531c27b9cc0782d9a001.jpg) 定义了黑名单的sql关键字 String\[\] var1 = " exec |peformance\_schema|information\_schema|extractvalue|updatexml|geohash|gtid\_subset|gtid\_subtract| insert | alter | delete | grant | update | drop | chr | mid | master | truncate | char | declare |user()|".split("\\|"); 先调用b()方法匹配是否存在内联注释,在把传入的值全部转化为小写,在调用c()方法判断是否有sleep()函数的关键字,接着挨个和黑名单数组中的关键字逐一匹配,匹配到了则拦截并抛出异常 接着经过一大段基础配置的调用来到此方法,步入 ![image-20230914212313851](https://shs3.b.qianxin.com/butian_public/f611752b3d7d6b85f3867786761321dc0d6bcbf7ef0d6.jpg) 又初始化了一些基本配置,由于此处paramArray传入为空,来到如下函数 ![image-20230914212622177](https://shs3.b.qianxin.com/butian_public/f21649599b182b8a7b304212590b3774169ce491afdd5.jpg) 跟进 ![image-20230914212809441](https://shs3.b.qianxin.com/butian_public/f532744177d8123aaee9ff9ac5d7c82d405c06b296026.jpg) 步入发现函数去除了传入参数末尾的分号(如果有的话),又进行了一遍i.a()方法的拦截sql关键字。后由于传入var1为空,直接进入 var0 = a(var2,var0) ![image-20230914213135679](https://shs3.b.qianxin.com/butian_public/f2321022f30c70e7f6b9f30af6932a53d948236950b5e.jpg) 正则匹配 ”${“ 和 “}“ 之间的字符串存入hashmap中,我们的注入的payload此次明显是匹配不到的,接着进到如下方法 ![image-20230914213321847](https://shs3.b.qianxin.com/butian_public/f1098717eb60fc11d03cbeadaab536422a9c664f3328c.jpg) freemarker模板的方法,此处分析sql注入,暂时跳出 ![image-20230914213711589](https://shs3.b.qianxin.com/butian_public/f7882094a5110fa0efc5c027ac862f530f53f9c2f1fab.jpg) 跟进 ![image-20230914213818166](https://shs3.b.qianxin.com/butian_public/f736455b6c2e09339ca5b641665ef7f61d0c6e8c26816.jpg) 又是一些处理sql拦截的代码,匹配诸如 where/and/or/=等字符进行相应的过滤,接着回到此处向下执行 ![image-20230914214101489](https://shs3.b.qianxin.com/butian_public/f69666092989af17c474452fa1312b96257eb7f52752c.jpg) 最终调用jeeecg的minidao层服务执行sql语句 ![image-20230914214224634](https://shs3.b.qianxin.com/butian_public/f78251327ec6e89c3aeac25e73b536ec28dfa619e587c.jpg) ![image-20230914214911282](https://shs3.b.qianxin.com/butian_public/f2679387f2fd715ed2d8ca4d4fa85c962db7fd701ceb9.jpg) 分析下来发现程序对sql的传参基本只做了黑名单过滤,但是其中漏掉了exp()函数关键字的匹配,使用exp()函数即可实现报错注入 ![image-20230914220705912](https://shs3.b.qianxin.com/butian_public/f212310a434f6e28734a57bb3532d8e9985375d5cd833.jpg) ![image-20230914220652150](https://shs3.b.qianxin.com/butian_public/f57126095ee41d8d5edcabf533e181a0c4b6374c22942.jpg) 2.模板注入 ====== 接着分析上面提到的调用freemarker函数的地方,我们的payload “sql”参数的值作为var1 传入 ![image-20230928181823581](https://shs3.b.qianxin.com/butian_public/f5631260f4a0608b7c5f67c394f280d8c378a285f5fee.jpg) 跟进函数调用,默认var0为空,进行一些初始化后来到如下函数 ![image-20230914222845505](https://shs3.b.qianxin.com/butian_public/f493311db3827c67ecc77a1cb11329b528a781c020701.jpg) 创建一个名为"template"的模板对象,使用模板对象对var1和var3进行处理,故跟进process()方法 ![image-20230914223723161](https://shs3.b.qianxin.com/butian_public/f8188251e52865e6dcce37592025fb9dfc4efa382fd6c.jpg) 继续跟进process() ![image-20230914225648572](https://shs3.b.qianxin.com/butian_public/f772872cccc6d059fea498ef8a0884af4b3e8335e4213.jpg) 跟进this.visit(this.getTemplate().getRootTreeNode()) ![image-20230914230650615](https://shs3.b.qianxin.com/butian_public/f1154705305d53ddf7fde43b848711286e76745c90152.jpg) accept()方法匹配到ftl表达式,将插值和插值的传参赋值给templateElementsToVisit ![image-20230915000827033](https://shs3.b.qianxin.com/butian_public/f131156d84a28fc12f23b59bbd834d9709afb90060b0f.jpg) 而后第一次调用this.visit(el);(el此时为freemaker表达式中的插值,即“<#assign value=\\"freemarker.template.utility.Execute\\"?new()>”),顺带提一下,在freemarker模板中,<#assign value=\\"freemarker.template.utility.Execute\\"?new()>语句可以创建一个继承自 freemarker.template.TemplateModel 类的变量 ![image-20230915112210219](https://shs3.b.qianxin.com/butian_public/f80289180fa5ab484d7c5e05114462bb1b7ecaafa01da.jpg) 继续跟进一系列方法到此处 ![image-20230915104316804](https://shs3.b.qianxin.com/butian_public/f2930090eccecbf9777593e8febf10db365898322ddd5.jpg) 经过freemaker处理ftl语句后 targetMethod就是构造的ftl语句中传入的需要使用的命令执行的类freemarker.template.utility.Execute 即freemarker.template.utility.Execute.exec(argumentStrings),当然此处argumentStrings的值为空,还没有真正命令执行 接着进入visit()的第二次调用,传入ftl语句中插值的赋值,即“${value(\\"calc\\")}” ![image-20230915105032222](https://shs3.b.qianxin.com/butian_public/f795265f401f788496b7de31745c9a816b389f8d9d081.jpg) 接着进到关键调用exec(),将获取到的ftl语句中value的值传入 ![image-20230915105459937](https://shs3.b.qianxin.com/butian_public/f60634743ef960fad56dc9ce96c5c2905761f1ed3fc53.jpg) 跟进exec()方法使用系统命令执行我们ftl语句中对value的赋值 ![image-20230915105722214](https://shs3.b.qianxin.com/butian_public/f808196ae394d278d2e48d636f0fb278303bf9915672a.jpg) 3.修复 ==== 目前官方在最新版本使用了 setNewBuiltinClassResolver(TemplateClassResolver.SAFER\_RESOLVER) ![image-20230915113747086](https://shs3.b.qianxin.com/butian_public/f392511b48f270bdfa646a2947417a4e3813fd38430e7.jpg) 禁用了freemarker模板注入中常用三个类的解析 ![image-20230915114146962](https://shs3.b.qianxin.com/butian_public/f501436896904f4d45d41bc2beee14634058d5e2e5806.jpg)
发表于 2023-10-31 09:00:01
阅读 ( 16666 )
分类:
漏洞分析
0 推荐
收藏
0 条评论
请先
登录
后评论
中铁13层打工人
72 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!