-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
119 lines (116 loc) · 55.1 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<!doctype html>
<html lang="zh"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><meta><title>Blog</title><link rel="manifest" href="/manifest.json"><meta name="application-name" content="Naman"><meta name="msapplication-TileImage" content="/img/site-config/logo.svg"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-title" content="Naman"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta property="og:type" content="blog"><meta property="og:title" content="Naman"><meta property="og:url" content="https://naman.live/"><meta property="og:site_name" content="Blog"><meta property="og:locale" content="zh_CN"><meta property="og:image" content="https://naman.live/img/og_image.png"><meta property="article:author" content="Naman"><meta property="twitter:card" content="summary"><meta property="twitter:image:src" content="https://naman.live/img/og_image.png"><script type="application/ld+json">{"@context":"https://schema.org","@type":"BlogPosting","mainEntityOfPage":{"@type":"WebPage","@id":"https://naman.live"},"headline":"Blog","image":["https://naman.live/img/og_image.png"],"author":{"@type":"Person","name":"Naman"},"publisher":{"@type":"Organization","name":"Blog","logo":{"@type":"ImageObject","url":"https://naman.live/img/site-config/logo.svg"}},"description":""}</script><link rel="icon" href="/img/site-config/logo.svg"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.0.0/css/all.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/styles/xt256.css"><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Oxanium:wght@300;400;600&family=Roboto+Mono"><link rel="stylesheet" href="/css/cyberpunk.css"><style>body>.footer,body>.navbar,body>.section{opacity:0}</style><!--!--><!--!--><!--!--><!--!--><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/build/cookieconsent.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/lightgallery.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/justifiedGallery.min.css"><!--!--><!--!--><style>.pace{-webkit-pointer-events:none;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.pace-inactive{display:none}.pace .pace-progress{background:#3273dc;position:fixed;z-index:2000;top:0;right:100%;width:100%;height:2px}</style><script src="https://cdn.jsdelivr.net/npm/[email protected]/pace.min.js"></script><!--!--><!--!--><!-- hexo injector head_end start --><script>
(function () {
function switchTab() {
if (!location.hash) {
return;
}
const id = '#' + CSS.escape(location.hash.substring(1));
const $tabMenu = document.querySelector(`.tabs a[href="${id}"]`);
if (!$tabMenu) {
return;
}
const $tabMenuContainer = $tabMenu.parentElement.parentElement;
Array.from($tabMenuContainer.children).forEach($menu => $menu.classList.remove('is-active'));
Array.from($tabMenuContainer.querySelectorAll('a'))
.map($menu => document.getElementById($menu.getAttribute("href").substring(1)))
.forEach($content => $content.classList.add('is-hidden'));
if ($tabMenu) {
$tabMenu.parentElement.classList.add('is-active');
}
const $activeTab = document.querySelector(id);
if ($activeTab) {
$activeTab.classList.remove('is-hidden');
}
}
switchTab();
window.addEventListener('hashchange', switchTab, false);
})();
</script><!-- hexo injector head_end end --><meta name="generator" content="Hexo 6.3.0"><link rel="alternate" href="/atom.xml" title="Blog" type="application/atom+xml">
</head><body class="is-2-column"><nav class="navbar navbar-main"><div class="container navbar-container"><div class="navbar-brand justify-content-center"><a class="navbar-item navbar-logo" href="/"><img src="/img/site-config/logo.svg" alt="Blog" height="28"></a></div><div class="navbar-menu"><div class="navbar-start"><a class="navbar-item is-active" href="/">Home</a><a class="navbar-item" href="/archives">Archives</a><a class="navbar-item" href="/categories">Categories</a><a class="navbar-item" href="/tags">Tags</a><a class="navbar-item" href="/about">About</a></div><div class="navbar-end"><a class="navbar-item" target="_blank" rel="noopener" title="Download on GitHub" href="https://github.com/nama769/"><i class="fab fa-github"></i></a><a class="navbar-item" target="_blank" rel="noopener" title="RSS" href="/atom.xml"><i class="fas fa-rss"></i></a><a class="navbar-item search" title="搜索" href="javascript:;"><i class="fas fa-search"></i></a></div></div></div></nav><section class="section"><div class="container"><div class="columns"><div class="column order-2 column-main"><div class="card"><div class="card-image"><a class="image is-7by3" href="/2023/01/30/shiro-CVE-2023-22602/"><img class="fill" src="/img/shiro-CVE-2023-22602/1675179394921.png" alt="shiro_CVE-2023-22602"></a></div><article class="card-content article" role="article"><div class="article-meta is-size-7 is-uppercase level is-mobile"><div class="level-left"><span class="level-item"><time dateTime="2023-01-30T09:32:52.000Z" title="2023/1/30 17:32:52">2023-01-30</time>发表</span><span class="level-item"><time dateTime="2076-11-30T12:00:00.000Z" title="2076/11/30 20:00:00">2076-11-30</time>更新</span><span class="level-item"><a class="link-muted" href="/categories/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/">漏洞分析</a></span><span class="level-item">21 分钟读完 (大约3127个字)</span></div></div><h1 class="title is-3 is-size-4-mobile"><a class="link-muted" href="/2023/01/30/shiro-CVE-2023-22602/">shiro_CVE-2023-22602</a></h1><div class="content"><h1 id="Shiro-lt-1-11-0-amp-Spring-Boot-2-6-鉴权绕过(CVE-2023-22602)"><a href="#Shiro-lt-1-11-0-amp-Spring-Boot-2-6-鉴权绕过(CVE-2023-22602)" class="headerlink" title="Shiro < 1.11.0 & Spring Boot 2.6+ 鉴权绕过(CVE-2023-22602)"></a>Shiro < 1.11.0 & Spring Boot 2.6+ 鉴权绕过(CVE-2023-22602)</h1><h2 id="漏洞描述"><a href="#漏洞描述" class="headerlink" title="漏洞描述"></a>漏洞描述</h2><p><a target="_blank" rel="noopener" href="https://shiro.apache.org/blog/2023/01/13/apache-shiro-1110-released.html">官方通告</a><br>shiro 在 1.11.0版本之前,当与spring boot 2.6以上版本组合使用的时候,在默认配置下,配合特定的路由规则,攻击者可以通过发送特殊的请求造成shiro中的鉴权绕过。</p>
<h2 id="Spring-shiro-请求处理流程"><a href="#Spring-shiro-请求处理流程" class="headerlink" title="Spring + shiro 请求处理流程"></a>Spring + shiro 请求处理流程</h2><h3 id="搭环境"><a href="#搭环境" class="headerlink" title="搭环境"></a>搭环境</h3><p>先要找一个简单标准的spring+shiro的项目。<a target="_blank" rel="noopener" href="https://github.com/apache/shiro/tree/main/samples/spring-boot-web">shiro的simpals</a>里正好给了。<br>下载到本地,<code>mvn spring-boot:run</code> 就可以跑起来。<br>因为spring用的log4j做日志记录,shiro用了slf4j,为了方便分析流程,可以把日志的记录级别设成ALL。<br>原本项目的pom.xml需要改一下,添加下面版本的依赖。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.slf4j<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>slf4j-api<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.7.20<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.slf4j<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>slf4j-log4j12<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.5.6<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure>
<p>为了打印出spring的日志,在<code>application.properties</code>中添加<code>debug=ture</code>和<code>trace=ture</code>。<br>为了打印出shiro中各种级别的日志,在资源目录下新建<code>logback.xml</code>内容如下:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">appender</span> <span class="attr">name</span>=<span class="string">"STDOUT"</span> <span class="attr">class</span>=<span class="string">"ch.qos.logback.core.ConsoleAppender"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">encoder</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">pattern</span>></span>%-4relative [%thread] %-5level %logger{35} - %msg %n<span class="tag"></<span class="name">pattern</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">encoder</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">appender</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">logger</span> <span class="attr">name</span>=<span class="string">"org.apache.shiro"</span> <span class="attr">level</span>=<span class="string">"ALL"</span> <span class="attr">additivity</span>=<span class="string">"false"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">"STDOUT"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">logger</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">root</span> <span class="attr">level</span>=<span class="string">"ALL"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">appender-ref</span> <span class="attr">ref</span>=<span class="string">"STDOUT"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">root</span>></span></span><br><span class="line"><span class="tag"></<span class="name">configuration</span>></span></span><br></pre></td></tr></table></figure>
<p>这样就可以看到全部的调试信息,方便分析整个请求处理流程。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675162564528.png" alt="1675162564528"></p>
<h3 id="Spring初始化"><a href="#Spring初始化" class="headerlink" title="Spring初始化"></a>Spring初始化</h3><h3 id="请求处理"><a href="#请求处理" class="headerlink" title="请求处理"></a>请求处理</h3><h4 id="doFilter"><a href="#doFilter" class="headerlink" title="doFilter"></a>doFilter</h4><p>初始化完成后,当有请求进来时,一开始是对数据的处理,以及一些初始化,我们从<code>ApplicationFilterChain.doFilter</code>开始跟。<br>ApplicationFilterChain中保存spring中的所有Filter,其中和我们分析有关的是3号索引下的shiroFilter。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675164193493.png" alt="FilterChain"><br>通过shiroFilter,可以把执行流从spring转移到shiro,shiro的所有功能都是在这里实现的。<br>在进入shiroFilter之前,按照下面的顺序依次执行Filter。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">CharacterEncodingFilter(org.springframework.web.filter.OncePerRequestFilter).doFilter</span><br><span class="line">CharacterEncodingFilter.doFilterInternal</span><br><span class="line"></span><br><span class="line">ApplicationFilterChain.doFilter</span><br><span class="line"></span><br><span class="line">FormContentFilter(org.springframework.web.filter.OncePerRequestFilter).doFilter</span><br><span class="line">FormContentFilter.doFilterInternal</span><br><span class="line"></span><br><span class="line">ApplicationFilterChain.doFilter</span><br><span class="line"></span><br><span class="line">RequestContextFilter(org.springframework.web.filter.OncePerRequestFilter).doFilter</span><br><span class="line">RequestContextFilter.doFilterInternal</span><br><span class="line">OrderedRequestContextFilter.initContextHolders</span><br></pre></td></tr></table></figure>
<p>在进入shiroFilter后,开始执行shiro中的filter。<br>关键的调用过程如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">ShiroFilter(org.apache.shiro.web.servlet.OncePerRequestFilter).doFilter</span><br><span class="line">ShiroFilter(AbstractShiroFilter).doFilterInternal</span><br><span class="line">ShiroFilter(AbstractShiroFilter).executeChain</span><br><span class="line">ShiroFilter(AbstractShiroFilter).getExecutionChain</span><br><span class="line">PathMatchingFilterChainResolver.getChain</span><br><span class="line">…</span><br><span class="line">AntPathMatcher.doMatch</span><br><span class="line">…</span><br><span class="line">DefaultFilterChainManager.proxy</span><br><span class="line">SimpleNamedFilterList.proxy</span><br><span class="line">ProxiedFilterChain.doFilter</span><br></pre></td></tr></table></figure>
<p>但shiro会根据具体请求去寻找对应的filters,封装成ProxiedFilterChain,然后调用ProxiedFilterChain.doFilter。例如在shiro.ini中配置了<code>/admin/** roles[admin]</code>,那么在shiro就会为/admin/xxx的请求分配InvalidRequestFilter和RolesAuthorizationFilter两个filter,当分配的filter执行完成后,shiro继续调用原本的spring的filter链。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675168403801.png" alt="ProxiedFilterChain.doFilter"></p>
<h4 id="doService"><a href="#doService" class="headerlink" title="doService"></a>doService</h4><p>当filter链执行完成后,表示shiro的鉴权已经完成。ApplicationFilterChain.doFilter基本执行了一半。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675169367815.png" alt="ApplicationFilterChain.doFilter"><br>通过<code>servlet.service(request, response);</code>调用对应的doService去处理请求。关键调用大概像这样</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">…</span><br><span class="line">HttpServlet.service</span><br><span class="line">FrameworkServlet.service</span><br><span class="line">HttpServlet.service</span><br><span class="line">FrameworkServlet.doGet</span><br><span class="line">FrameworkServlet.processRequest</span><br><span class="line">DispatcherServlet.doService</span><br><span class="line">DispatcherServlet.getHandler</span><br><span class="line">AbstractHandlerMethodMapping.getHandlerInternal</span><br><span class="line"></span><br><span class="line">AbstractHandlerMapping.initLookupPath</span><br><span class="line">AbstractHandlerMapping.lookupHandlerMethod</span><br></pre></td></tr></table></figure>
<p>spring在doService阶段,才会在lookupHandlerMethod里做路由分发。最后通过反射进入Controller里的函数。</p>
<h3 id="shiro中的路由匹配"><a href="#shiro中的路由匹配" class="headerlink" title="shiro中的路由匹配"></a>shiro中的路由匹配</h3><p>因为shiro只存在于doFilter的流程,所以路由匹配肯定是在ShiroFilter中进行的。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">2484 [main] TRACE o.a.s.w.f.a.FormAuthenticationFilter - Adding login url to applied paths. </span><br><span class="line">2484 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Creating chain [/login.html] with global filters [invalidRequest] and from String definition [authc] </span><br><span class="line">2484 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/login.html] to filter [invalidRequest] with config [null] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/login.html] to filter [authc] with config [null] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Creating chain [/logout] with global filters [invalidRequest] and from String definition [logout] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/logout] to filter [invalidRequest] with config [null] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/logout] to filter [logout] with config [null] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Creating chain [/admin/**] with global filters [invalidRequest] and from String definition [roles[admin]] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/admin/**] to filter [invalidRequest] with config [null] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/admin/**] to filter [roles] with config [admin] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Creating chain [/guest/**] with global filters [invalidRequest] and from String definition [roles[guest]] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/guest/**] to filter [invalidRequest] with config [null] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/guest/**] to filter [roles] with config [guest] </span><br><span class="line">2499 [main] DEBUG o.a.s.w.f.m.DefaultFilterChainManager - Attempting to apply path [/**] to filter [invalidRequest] with config [null] </span><br></pre></td></tr></table></figure>
<p>从上面的日志里可以看到,在初始化的时候,shiro会通过各种给定的配置将不同路由和不同filter链匹配到一起。<br>以下面的请求为例,最终会匹配到/admin/**的chain。</p>
<figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GET</span> <span class="string">/123/../admin</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>127.0.0.1:8081</span><br><span class="line"><span class="attribute">User-Agent</span><span class="punctuation">: </span>Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0</span><br><span class="line"><span class="attribute">Accept</span><span class="punctuation">: </span>text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8</span><br><span class="line"><span class="attribute">Accept-Language</span><span class="punctuation">: </span>zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2</span><br><span class="line"><span class="attribute">Accept-Encoding</span><span class="punctuation">: </span>gzip, deflate</span><br><span class="line"><span class="attribute">DNT</span><span class="punctuation">: </span>1</span><br><span class="line"><span class="attribute">Connection</span><span class="punctuation">: </span>close</span><br><span class="line"><span class="attribute">Cookie</span><span class="punctuation">: </span>JSESSIONID=0a597a79-d2bf-4595-a3b9-ac70b5bda507</span><br><span class="line"><span class="attribute">Upgrade-Insecure-Requests</span><span class="punctuation">: </span>1</span><br><span class="line"><span class="attribute">Sec-Fetch-Dest</span><span class="punctuation">: </span>document</span><br><span class="line"><span class="attribute">Sec-Fetch-Mode</span><span class="punctuation">: </span>navigate</span><br><span class="line"><span class="attribute">Sec-Fetch-Site</span><span class="punctuation">: </span>none</span><br><span class="line"><span class="attribute">Sec-Fetch-User</span><span class="punctuation">: </span>?1</span><br></pre></td></tr></table></figure>
<p>日志记录如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">TRACE o.a.s.w.f.m.PathMatchingFilterChainResolver - Matched path pattern [/admin/**] for requestURI [/admin]. Utilizing corresponding filter </span><br><span class="line">TRACE o.a.s.w.servlet.AbstractShiroFilter - Resolved a configured FilterChain for the current request. </span><br><span class="line">TRACE o.a.s.w.servlet.ProxiedFilterChain - Invoking wrapped filter at index [0] </span><br><span class="line">TRACE o.a.s.w.s.OncePerRequestFilter - Filter 'invalidRequest' not yet executed. Executing now. </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/login.html' with current requestURI '/admin'... </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/login.html] matches path [/admin] => [false] </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/login.html' with current requestURI '/admin'... </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/login.html] matches path [/admin] => [false] </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/logout' with current requestURI '/admin'... </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/logout] matches path [/admin] => [false] </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/logout' with current requestURI '/admin'... </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/logout] matches path [/admin] => [false] </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/admin/**' with current requestURI '/admin'... </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/admin/**] matches path [/admin] => [true] </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Current requestURI matches pattern '/admin/**'. Determining filter chain execution... </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Filter 'invalidRequest' is enabled for the current request under path '/admin/**' with config </span><br><span class="line"> implementation for 'onPreHandle' check. </span><br><span class="line">TRACE o.a.shiro.web.servlet.AdviceFilter - Invoked preHandle method. Continuing chain?: [true] </span><br><span class="line">TRACE o.a.s.w.servlet.ProxiedFilterChain - Invoking wrapped filter at index [1] </span><br><span class="line">TRACE o.a.s.w.s.OncePerRequestFilter - Filter 'roles' not yet executed. Executing now. </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Attempting to match pattern '/admin/**' with current requestURI '/admin'... </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Pattern [/admin/**] matches path [/admin] => [true] </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Current requestURI matches pattern '/admin/**'. Determining filter chain execution... </span><br><span class="line">TRACE o.a.s.web.filter.PathMatchingFilter - Filter 'roles' is enabled for the current request under path '/admin/**' with config [[admin]]. </span><br><span class="line">tation for 'onPreHandle' check. </span><br><span class="line">TRACE org.apache.shiro.util.ThreadContext - get() - in thread [http-nio-8081-exec-1] </span><br><span class="line">TRACE org.apache.shiro.util.ThreadContext - Retrieved value of type [org.apache.shiro.web.subject.support.WebDelegatingSubject] for key [org.</span><br><span class="line">_SUBJECT_KEY] bound to thread [http-nio-8081-exec-1] </span><br><span class="line">TRACE o.a.s.s.support.DelegatingSubject - attempting to get session; create = false; session is null = true; session has id = false </span><br><span class="line">TRACE org.apache.shiro.util.ThreadContext - get() - in thread [http-nio-8081-exec-1] </span><br><span class="line">TRACE org.apache.shiro.util.ThreadContext - Retrieved value of type [org.apache.shiro.web.subject.support.WebDelegatingSubject] for key [org.</span><br><span class="line">_SUBJECT_KEY] bound to thread [http-nio-8081-exec-1] </span><br><span class="line">TRACE o.a.s.s.support.DelegatingSubject - attempting to get session; create = false; session is null = true; session has id = false </span><br><span class="line">TRACE org.apache.shiro.util.ThreadContext - get() - in thread [http-nio-8081-exec-1] </span><br><span class="line">TRACE org.apache.shiro.util.ThreadContext - Retrieved value of type [org.apache.shiro.web.subject.support.WebDelegatingSubject] for key [org.</span><br><span class="line">_SUBJECT_KEY] bound to thread [http-nio-8081-exec-1] </span><br><span class="line">TRACE o.a.s.s.support.DelegatingSubject - attempting to get session; create = true; session is null = true; session has id = false </span><br><span class="line">TRACE o.a.s.s.support.DelegatingSubject - Starting session for host 127.0.0.1 </span><br><span class="line">TRACE o.a.s.s.mgt.DefaultSessionManager - Creating session for host 127.0.0.1 </span><br><span class="line">DEBUG o.a.s.s.mgt.DefaultSessionManager - Creating new EIS record for new session instance [org.apache.shiro.session.mgt.SimpleSession,id=null] </span><br><span class="line">TRACE o.a.shiro.web.servlet.SimpleCookie - calculated path: / </span><br><span class="line">DEBUG o.a.shiro.web.servlet.SimpleCookie - Added HttpServletResponse Cookie [JSESSIONID=972d286f-8770-46ec-ad0a-22ad95736da0; Path=/; HttpOnly; </span><br><span class="line">TRACE o.a.s.w.s.m.DefaultWebSessionManager - Set session ID cookie for session with id 972d286f-8770-46ec-ad0a-22ad95736da0 </span><br><span class="line">TRACE o.a.s.s.m.AbstractValidatingSessionManager - Attempting to retrieve session with key org.apache.shiro.web.session.mgt.</span><br><span class="line">TRACE o.a.shiro.web.servlet.AdviceFilter - Invoked preHandle method. Continuing chain?: [false] </span><br><span class="line">TRACE o.a.shiro.web.servlet.AdviceFilter - Successfully invoked postHandle method </span><br><span class="line">TRACE o.a.shiro.web.servlet.AdviceFilter - Successfully invoked afterCompletion method. </span><br><span class="line">TRACE o.a.shiro.web.servlet.AdviceFilter - Successfully invoked postHandle method </span><br><span class="line">TRACE o.a.shiro.web.servlet.AdviceFilter - Successfully invoked afterCompletion method.</span><br></pre></td></tr></table></figure>
<h4 id="获取uri"><a href="#获取uri" class="headerlink" title="获取uri"></a>获取uri</h4><p>第一行就可以看到,shiro解析到的uri是<code>/admin</code>而不是<code>/123/../admin</code>。<br>这个uri的来源是org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver的getChain中的<br><code>final String requestURI = getPathWithinApplication(request);</code><br><img src="/2023/01/30/shiro-CVE-2023-22602/1675170757408.png" alt="getChain"><br><img src="/2023/01/30/shiro-CVE-2023-22602/1675170827553.png" alt="getPathWithinApplication"><br>WebUtils.getPathWithinApplication里的normalize会解析<code>/./</code>和<code>/../</code><br><img src="/2023/01/30/shiro-CVE-2023-22602/1675171024428.png" alt="WebUtils"><br><img src="/2023/01/30/shiro-CVE-2023-22602/1675171053907.png" alt="normalize"><br>此外,getServletPath(request) 和 getPathInfo(request)获得的是经过urldecode和<code>..</code>解析的uri。</p>
<h4 id="匹配"><a href="#匹配" class="headerlink" title="匹配"></a>匹配</h4><p>shiro中在pathMatches里进行匹配。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675171323810.png" alt="pathMatches"><br>而pathMatcher便是AntPathMatcher。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675171356917.png" alt="AntPathMatcher"></p>
<h3 id="spring中的路由匹配"><a href="#spring中的路由匹配" class="headerlink" title="spring中的路由匹配"></a>spring中的路由匹配</h3><p>在spring中,路由匹配发生在doService中。<br>在<code>AbstractHandlerMethodMapping.getHandlerInternal</code>中,在lookupHandlerMethod中获取uri以及匹配。<br>调用如下。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">AbstractHandlerMapping.lookupHandlerMethod </span><br><span class="line">AbstractHandlerMapping.addMatchingMappings</span><br><span class="line">AbstractHandlerMapping.getMatchingMapping</span><br><span class="line">RequestMappingInfo.getMatchingCondition</span><br></pre></td></tr></table></figure>
<p><img src="/2023/01/30/shiro-CVE-2023-22602/1675172439595.png" alt="getMatchingCondition"><br>在getMatchingCondition里可以看到pathPatternsCondition和patternsCondition两种方式,对应了spring配置中<code>spring.mvc.pathmatch.matching-strategy</code>的ant_path_matcher和path_pattern_parser的路由匹配模式。<br>ant_path_matcher对应patternsCondition。path_pattern_parser对应pathPatternsCondition。<br>在spring2.6+后,path_pattern_parser为默认配置。<br>ant_path_matcher中<code>?</code>匹配单个字符,<code>*</code>匹配单级目录,<code>**</code>匹配多级目录。<br>path_pattern_parser支持<code>{*name}</code>获取多级变量,并且<code>**</code>后面不能再有其他东西。<br>通过跟源码可以发下,这部分的处理中spring基本都是并列写两个if,去分别处理ant_path_matcher和path_pattern_parser的情况。</p>
<h4 id="ant-path-matcher"><a href="#ant-path-matcher" class="headerlink" title="ant_path_matcher"></a>ant_path_matcher</h4><p>ant_path_matcher对应的是PatternsRequestCondition.getMatchingCondition<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675175821121.png" alt="PatternsRequestCondition.getMatchingCondition"></p>
<h5 id="获取uri-1"><a href="#获取uri-1" class="headerlink" title="获取uri"></a>获取uri</h5><p><code>String lookupPath = UrlPathHelper.getResolvedLookupPath(request);</code>获取uri。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675174888102.png" alt="UrlPathHelper.getResolvedLookupPath"><br>PATH_ATTRIBUTE的设置是在resolveAndCacheLookupPath函数中,调用点为AbstractHandlerMapping.initLookupPath<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675175163003.png" alt="AbstractHandlerMapping.initLookupPath"><br>getUrlPathHelper()拿到的其实是shiro里继承的ShiroUrlPathHelper。<br>类中重写了getPathWithinApplication方法。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675175361234.png" alt="ShiroUrlPathHelper"><br>spring获取uri是调用getLookupPathForRequest,又调用ShiroUrlPathHelper.getPathWithinApplication,拿到的uri和shiro中的是一致的。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675175443952.png" alt="1675175443952"></p>
<h5 id="匹配-1"><a href="#匹配-1" class="headerlink" title="匹配"></a>匹配</h5><p>匹配便是ant风格的匹配。</p>
<h4 id="path-pattern-parser"><a href="#path-pattern-parser" class="headerlink" title="path_pattern_parser"></a>path_pattern_parser</h4><p>在path_pattern_parser模式下对应的是PathPatternsRequestCondition.getMatchingCondition<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675175885851.png" alt="PathPatternsRequestCondition.getMatchingCondition"></p>
<h5 id="获取uri-2"><a href="#获取uri-2" class="headerlink" title="获取uri"></a>获取uri</h5><p><code>ServletRequestPathUtils.getParsedRequestPath(request).pathWithinApplication();</code>获取uri。<br>也是通过获取PATH_ATTRIBUTE属性,在parseAndCache中设置。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675175993901.png" alt="parseAndCache"><br>parseAndCache在DispatcherServlet.doService中调用。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675176121436.png" alt="doService"><br>调用ServletRequestPath.parse(request)<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675176190865.png" alt="parse"><br>然后通过getRequestURI获得。从注释可以看到,返回的应该是没有decode以及解析<code>..</code>的原始值。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675176230645.png" alt="getRequestURI"></p>
<h2 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h2><p>所以,就如漏洞描述中写的,当spring中使用path_pattern_parser配置的时候,就会造成在shiro和spring中路由分发时候获取uri时的结果差异。<br>也就可能造成shiro中鉴权绕过。<br>例如shiro中配置路由规则如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">DefaultShiroFilterChainDefinition</span> <span class="variable">chainDefinition</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultShiroFilterChainDefinition</span>();</span><br><span class="line">chainDefinition.addPathDefinition(<span class="string">"/login.html"</span>, <span class="string">"authc"</span>); <span class="comment">// need to accept POSTs from the login form</span></span><br><span class="line">chainDefinition.addPathDefinition(<span class="string">"/logout"</span>, <span class="string">"logout"</span>);</span><br><span class="line">chainDefinition.addPathDefinition(<span class="string">"/admin/**"</span>, <span class="string">"roles[admin]"</span>);</span><br><span class="line">chainDefinition.addPathDefinition(<span class="string">"/guest/**"</span>, <span class="string">"roles[guest]"</span>);</span><br></pre></td></tr></table></figure>
<p>在controller中如下设置:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AccountInfoController</span> {</span><br><span class="line"> <span class="meta">@RequestMapping("/admin/**")</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">home</span><span class="params">(Model model)</span> {</span><br><span class="line"> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">"World"</span>;</span><br><span class="line"> <span class="type">Subject</span> <span class="variable">subject</span> <span class="operator">=</span> SecurityUtils.getSubject();</span><br><span class="line"> <span class="type">PrincipalCollection</span> <span class="variable">principalCollection</span> <span class="operator">=</span> subject.getPrincipals();</span><br><span class="line"> <span class="keyword">if</span> (principalCollection != <span class="literal">null</span> && !principalCollection.isEmpty()) {</span><br><span class="line"> name = principalCollection.getPrimaryPrincipal().toString();</span><br><span class="line"> }</span><br><span class="line"> model.addAttribute(<span class="string">"name"</span>, name);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"account-info"</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>正常来说,应该之后admin角色才能访问AccountInfoController的home。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675176916392.png" alt="admin-200"><br><img src="/2023/01/30/shiro-CVE-2023-22602/1675176946663.png" alt="guest-401"><br>但如果利用shiro会解析<code>..</code>而spirng不会的差异,可以发送如下请求,使guest的cookie可以访问这个接口。</p>
<figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GET</span> <span class="string">/admin/..</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>127.0.0.1:8081</span><br><span class="line"><span class="attribute">User-Agent</span><span class="punctuation">: </span>Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0</span><br><span class="line"><span class="attribute">Accept</span><span class="punctuation">: </span>text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8</span><br><span class="line"><span class="attribute">Accept-Language</span><span class="punctuation">: </span>zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2</span><br><span class="line"><span class="attribute">Accept-Encoding</span><span class="punctuation">: </span>gzip, deflate</span><br><span class="line"><span class="attribute">DNT</span><span class="punctuation">: </span>1</span><br><span class="line"><span class="attribute">Connection</span><span class="punctuation">: </span>close</span><br><span class="line"><span class="attribute">Cookie</span><span class="punctuation">: </span>JSESSIONID=f75894a9-955f-4778-b78e-bf6ae7c6f423</span><br><span class="line"><span class="attribute">Upgrade-Insecure-Requests</span><span class="punctuation">: </span>1</span><br><span class="line"><span class="attribute">Sec-Fetch-Dest</span><span class="punctuation">: </span>document</span><br><span class="line"><span class="attribute">Sec-Fetch-Mode</span><span class="punctuation">: </span>navigate</span><br><span class="line"><span class="attribute">Sec-Fetch-Site</span><span class="punctuation">: </span>none</span><br><span class="line"><span class="attribute">Sec-Fetch-User</span><span class="punctuation">: </span>?1</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p><img src="/2023/01/30/shiro-CVE-2023-22602/1675177069267.png" alt="guest-200"><br>因为shiro认为这是一个<code>/</code>的请求,但spring最后会分发到<code>/admin/**</code>上。</p>
<h2 id="漏洞修复"><a href="#漏洞修复" class="headerlink" title="漏洞修复"></a>漏洞修复</h2><p><img src="/2023/01/30/shiro-CVE-2023-22602/1675177292639.png" alt="https://github.com/apache/shiro/compare/shiro-root-1.10.1...shiro-root-1.11.0#diff-654a62c0dc61e9e9f936bca9bfdbb7ccc11ec5462b12a1056f096fe3b2807870R32"><br>shiro在1.11.0中强制使用ant_path_matcher…</p>
<h2 id="else"><a href="#else" class="headerlink" title="else"></a>else</h2><h3 id="利用"><a href="#利用" class="headerlink" title="利用"></a>利用</h3><p>感觉这个漏洞的危害不高。<br>因为这个要依靠特定的配置才能打通。简单说就是spring中的RequestMapping要能匹配多级路径或者至少二级。<br>下面的写法都可能造成绕过。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping("/admin/**")</span></span><br><span class="line"><span class="meta">@RequestMapping("/admin/*")</span></span><br><span class="line"><span class="meta">@RequestMapping("/admin/{*path}")</span></span><br></pre></td></tr></table></figure>
<p>但如果只写成<code>@RequestMapping("/admin")</code>应该是无法利用的。<br>另外,如果利用注释去为函数设置权限,将会在最终spring匹配成功后真正进行invoke的时候调用鉴权,导致无法利用。<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675178704512.png" alt="@RequiresRoles("admin")"></p>
<h3 id="杂谈"><a href="#杂谈" class="headerlink" title="杂谈"></a>杂谈</h3><p>记录一下在看跟源码的时候看到的一种我没见过的if else写法<br><img src="/2023/01/30/shiro-CVE-2023-22602/1675179049506.png" alt="if-else"></p>
</div></article></div><div class="card"><div class="card-image"><a class="image is-7by3" href="/2023/01/21/hello-world/"><img class="fill" src="/img/hello-world/CP77-KV-en.jpg" alt="hello icarus!"></a></div><article class="card-content article" role="article"><div class="article-meta is-size-7 is-uppercase level is-mobile"><div class="level-left"><span class="level-item"><time dateTime="2023-01-21T10:44:47.679Z" title="2023/1/21 18:44:47">2023-01-21</time>发表</span><span class="level-item"><time dateTime="2023-01-22T04:00:00.000Z" title="2023/1/22 12:00:00">2023-01-22</time>更新</span><span class="level-item">5 分钟读完 (大约690个字)</span></div></div><h1 class="title is-3 is-size-4-mobile"><a class="link-muted" href="/2023/01/21/hello-world/">hello icarus!</a></h1><div class="content"><h2 id="初见hexo"><a href="#初见hexo" class="headerlink" title="初见hexo"></a>初见hexo</h2><p>2020年第一次接触用hexo搭博客,用的Next主题,现在已经无法访问了。虽然Next十分的简洁,但看起来总觉得缺少一些东西,无法勾起我写博客的欲望。<br>之后,见到了像wordpress这种带后端的blog框架,觉得比hexo牛多了。就弃用了hexo+Next,开始使用<a target="_blank" rel="noopener" href="https://github.com/solstice23/argon-theme">wordpress+argon</a>。<br>因为想便于迁移,所以想搞成docker的,最后写了一个wordpress的dockerfile。不过在后来的使用中,发现许多的问题。</p></div><a class="article-more button is-small is-size-7" href="/2023/01/21/hello-world/#more">阅读更多</a></article></div></div><div class="column column-left is-3-tablet is-3-desktop is-3-widescreen order-1 is-sticky"><!--!--><div class="card widget" data-type="recent-posts"><div class="card-content"><h3 class="menu-label">最新文章</h3><article class="media"><figure class="media-left"><a class="image" href="/2023/01/30/shiro-CVE-2023-22602/"><img src="/img/shiro-CVE-2023-22602/1675179394921.png" alt="shiro_CVE-2023-22602"></a></figure><div class="media-content"><p class="date"><time dateTime="2023-01-30T09:32:52.000Z">2023-01-30</time></p><p class="title"><a href="/2023/01/30/shiro-CVE-2023-22602/">shiro_CVE-2023-22602</a></p><p class="categories"><a href="/categories/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/">漏洞分析</a></p></div></article><article class="media"><figure class="media-left"><a class="image" href="/2023/01/21/hello-world/"><img src="/img/hello-world/CP77-KV-en.jpg" alt="hello icarus!"></a></figure><div class="media-content"><p class="date"><time dateTime="2023-01-21T10:44:47.679Z">2023-01-21</time></p><p class="title"><a href="/2023/01/21/hello-world/">hello icarus!</a></p></div></article></div></div><div class="card widget" data-type="categories"><div class="card-content"><div class="menu"><h3 class="menu-label">分类</h3><ul class="menu-list"><li><a class="level is-mobile" href="/categories/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/"><span class="level-start"><span class="level-item">漏洞分析</span></span><span class="level-end"><span class="level-item tag">1</span></span></a></li></ul></div></div></div><div class="card widget" data-type="archives"><div class="card-content"><div class="menu"><h3 class="menu-label">归档</h3><ul class="menu-list"><li><a class="level is-mobile" href="/archives/2023/01/"><span class="level-start"><span class="level-item">一月 2023</span></span><span class="level-end"><span class="level-item tag">2</span></span></a></li></ul></div></div></div><div class="card widget" data-type="tags"><div class="card-content"><div class="menu"><h3 class="menu-label">标签</h3><div class="field is-grouped is-grouped-multiline"><div class="control"><a class="tags has-addons" href="/tags/%E6%9D%82%E8%B0%88/"><span class="tag">杂谈</span><span class="tag">1</span></a></div></div></div></div></div><div class="card widget" data-type="links"><div class="card-content"><div class="menu"><h3 class="menu-label">链接</h3><ul class="menu-list"><li><a class="level is-mobile" href="https://hexo.io" target="_blank" rel="noopener"><span class="level-left"><span class="level-item">Hexo</span></span><span class="level-right"><span class="level-item tag">hexo.io</span></span></a></li><li><a class="level is-mobile" href="https://bulma.io" target="_blank" rel="noopener"><span class="level-left"><span class="level-item">Bulma</span></span><span class="level-right"><span class="level-item tag">bulma.io</span></span></a></li></ul></div></div></div></div><!--!--></div></div></section><footer class="footer"><div class="container"><div class="level"><div class="level-start"><a class="footer-logo is-block mb-2" href="/"><img src="/img/site-config/logo.svg" alt="Blog" height="28"></a><p class="is-size-7"><span>© 2023 Naman</span> Powered by <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a> & <a href="https://github.com/ppoffice/hexo-theme-icarus" target="_blank" rel="noopener">Icarus</a></p></div><div class="level-end"><div class="field has-addons"><p class="control"><a class="button is-transparent is-large" target="_blank" rel="noopener" title="Creative Commons" href="https://creativecommons.org/"><i class="fab fa-creative-commons"></i></a></p><p class="control"><a class="button is-transparent is-large" target="_blank" rel="noopener" title="Attribution 4.0 International" href="https://creativecommons.org/licenses/by/4.0/"><i class="fab fa-creative-commons-by"></i></a></p><p class="control"><a class="button is-transparent is-large" target="_blank" rel="noopener" title="Download on GitHub" href="https://github.com/nama769/"><i class="fab fa-github"></i></a></p></div></div></div></div></footer><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script><script src="https://cdn.jsdelivr.net/npm/[email protected]/min/moment-with-locales.min.js"></script><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/clipboard.min.js" defer></script><script>moment.locale("zh-cn");</script><script>var IcarusThemeSettings = {
article: {
highlight: {
clipboard: true,
fold: 'unfolded'
}
}
};</script><script src="/js/column.js"></script><script src="/js/animation.js"></script><a id="back-to-top" title="回到顶端" href="javascript:;"><i class="fas fa-chevron-up"></i></a><script src="/js/back_to_top.js" defer></script><!--!--><!--!--><!--!--><script src="https://cdn.jsdelivr.net/npm/[email protected]/build/cookieconsent.min.js" defer></script><script>window.addEventListener("load", () => {
window.cookieconsent.initialise({
type: "info",
theme: "edgeless",
static: false,
position: "bottom-left",
content: {
message: "此网站使用Cookie来改善您的体验。",
dismiss: "知道了!",
allow: "允许使用Cookie",
deny: "拒绝",
link: "了解更多",
policy: "Cookie政策",
href: "https://www.cookiesandyou.com/",
},
palette: {
popup: {
background: "#edeff5",
text: "#838391"
},
button: {
background: "#4b81e8"
},
},
});
});</script><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/lightgallery.min.js" defer></script><script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/jquery.justifiedGallery.min.js" defer></script><script>window.addEventListener("load", () => {
if (typeof $.fn.lightGallery === 'function') {
$('.article').lightGallery({ selector: '.gallery-item' });
}
if (typeof $.fn.justifiedGallery === 'function') {
if ($('.justified-gallery > p > .gallery-item').length) {
$('.justified-gallery > p > .gallery-item').unwrap();
}
$('.justified-gallery').justifiedGallery();
}
});</script><!--!--><!--!--><!--!--><!--!--><!--!--><script src="/js/main.js" defer></script><div class="searchbox"><div class="searchbox-container"><div class="searchbox-header"><div class="searchbox-input-container"><input class="searchbox-input" type="text" placeholder="想要查找什么..."></div><a class="searchbox-close" href="javascript:;">×</a></div><div class="searchbox-body"></div></div></div><script src="/js/insight.js" defer></script><script>document.addEventListener('DOMContentLoaded', function () {
loadInsight({"contentUrl":"/content.json"}, {"hint":"想要查找什么...","untitled":"(无标题)","posts":"文章","pages":"页面","categories":"分类","tags":"标签"});
});</script></body></html>