实例讲解二阶SQL注入
2019-08-02 11:08:30 Author: www.4hou.com(查看原文) 阅读量:141 收藏

二阶SQL注入简述

所谓二阶注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。

二阶注入也是SQL注入的一种,与我们平时接触最多的一阶SQL注入相比利用门槛更高。普通的一阶SQL注入数据直接就进入到SQL查询中,而二阶SQL注入则是输入数据经处理后存储,然后取出数据,最后才进入到SQL查询。

事实上你在网上搜索到的SQL注入文章,大部分都可以归结为一阶SQL注入,因为这些例子涉及的事件均发生在单个HTTP请求和响应中,如下所示:

· 攻击者在HTTP请求中提交某种经过构思的输入。

· 应用处理输入,导致攻击者注入的SQL查询被执行。

· 如果可行的话,会直接向攻击者返回查询结果。

二阶注入的流程如下:

· 攻击者在HTTP请求中提交某种经过构思的输入。

· 应用存储该输入(通常保存在数据库中)以便后面使用并响应请求。

· 攻击者提交第二个(不同的)请求。

· 为处理第二个请求,应用会检索已经存储的输入并处理它,从而导致攻击者注入的SQL查询被执行。

· 如果可行的话,会在应用对第二个请求的响应中向攻击者返回查询结果。

61260346-5eea3200-a7b0-11e9-9b83-281bb7225d9a.png

通过对比可以看到,二阶注入相较一阶注入更为隐蔽,难以被黑盒测试检测到,这也是目前各大漏洞平台二阶注入案例少的一个因素吧!

这几天刚好看到一个案例,群马启介第一时间就想着与诸君分享。

寻找不需身份验证的XSS

ZoneMinder是一款用于管理监控摄像头的软件系统,它有一个Web界面,也就是我们今天文章的主jio了。

那么我们就先在把测试环境搭建起来吧。需要注意,默认情况下ZoneMinder是没有开启身份验证的,我们是不需要进行用户名、密码验证的。在信息配置栏中有一个设置HOME URL的字段,并且设置的HOME URL是可以通过网页左上角的LOGO进行访问的。这可是存储型XSS必备的条件之一啊,我们试试在这个地方插入http://zoneminder.com" onmouseover="alert(1) ,能弹出JavaScript提示框:

61264486-5c430900-a7bf-11e9-8e74-b863ac384041.png

如果XSS能打到高权限用户就极妙了,然鹅在本例中不适用,咱们这个不需要登录鸭。因此,我们需要把默认配置修改为开启身份验证。通过应用登录日志发现,不论登录是否成功,都会记录相关信息。那么我们在用户名字段插入<h1 onmouseover="alert(1)">XSS</h1>,密码字段任意输入,得到的结果是,页面提示用户密码错误,但是用户名的值已经被记录下来,静待管理员查看日志吧!

61270662-e9448d00-a7d4-11e9-897b-25dcac3bdd8b.png

61270683-f5c8e580-a7d4-11e9-85a9-0102ade7d8ca.png

寻找SQL注入

我们登录了两次,一次是HTML渲染,一次是HTML剥离。我们找到身份验证函数,看看其运作逻辑。于是找到web/includes/auth.php的userLogin函数:

function userLogin($username, $password='', $passwordHashed=false) {
  global $user;
  $sql = 'SELECT * FROM Users WHERE Enabled=1';
  …
  $_SESSION['username'] = $username;
  …
  if ( $dbUser = dbFetchOne($sql, NULL, $sql_values) ) {
    Info("Login successful for user \"$username\"");
    $_SESSION['user'] = $user = $dbUser;
    unset($_SESSION['loginFailed']);
    if ( ZM_AUTH_TYPE == 'builtin' ) {
      $_SESSION['passwordHash'] = $user['Password'];
    }
    session_regenerate_id();
  } else {
    Warning("Login denied for user \"$username\"");
    $_SESSION['loginFailed'] = true;
    unset($user);
  }
  if ( $close_session )
    session_write_close();
  return isset($user) ? $user: null;
} # end function userLogin

Warning对象中没有对username字段进行编码,此外有趣的是$_SESSION['username']竟然比用户名密码验证还早。这也意味着我们不需要身份验证就可以在登录表单会话中设置username字段,仔细找找是否可以在其他地方利用这个值。

function getAuthUser($auth) {
  …
  if ( isset($_SESSION['username']) ) {
    # Most of the time we will be logged in already and the session will have our username, so we can significantly speed up our hash testing by only looking at our user.
    # Only really important if you have a lot of users.
    $sql = "SELECT * FROM Users WHERE Enabled = 1 AND Username='".$_SESSION['username']."'";
  } else {
  …
} // end getAuthUser($auth)

getAuthUser函数使用的username变量没有进行转义就进行了SQL查询,这下该漏洞就符合我们对二阶SQL注入的定义了。接下来在登录表单的username字段构造一个SQL注入表达式,之后该表达式会被getAuthUser函数执行,那么在哪里调用getAuthUser函数呢?AppController.php

public function beforeFilter() {
  …
  $mAuth = $this->request->query('auth') ? $this->request->query('auth') : $this->request->data('auth');
  …
    } else if ( $mAuth ) {
      $user = getAuthUser($mAuth);
  …
} # end function beforeFilter()

每次API调用都会调用beforeFilter函数,如果调用任意带auth参数的API,就会顺带调用getAuthUser,之后便执行我们的SQL查询

所以我们只需在用户名输入a' or SLEEP(3)='a,然后就会被存储到$_SESSION['username'],接着调用http://zoneminder.local/zm/api/index.php/logs.json?auth=a执行SQL注入。

总结

就其本身来说就是个SQL注入,只不过触发方式相对隐晦了一些。一般情况下很难用自动化工具扫出来,最好可以白盒审计源代码,当然黑盒情况下也是可能找到二阶注入的,这就需要结合经验构造合适的payload了。

参考资料

渗透攻防Web篇-SQL注入攻击高级

Second order SQL injection in ZoneMinder

Second Order SQL Injection Explained with Example

How do I demonstrate a Second Order SQL Injection


文章来源: https://www.4hou.com/info/news/19267.html
如有侵权请联系:admin#unsafe.sh