• 如果您想对本站表示支持,请随手点击一下广告即可~
  • 本站致力于提供原创、优秀的技术文章~
  • 有任何疑问或建议 均可以在站点右侧栏处 通过各种方式联系站长哦~
  • CTF – RootMe解题报告 [Web-Server : SQL injection – Time based]

    渗透测试 EXP 370阅读 0评论

    挑战入口:Root-Me(https://www.root-me.org/en/Challenges/Web-Server/SQL-injection-Time-based)
      分类目录:Link to …(http://exp-blog.com/2019/01/02/pid-2597/12/)

    前言

    基于延时的 SQL 注入。

    其实就是布尔注入的衍生题型,只是因为当我们注入的 SQL 触发的信息被页面屏蔽,而不得不通过延时的方式间接判断是否注入的结果是 true 还是 false 。

    不过无论是布尔注入还是延时注入,除了 payload 存在少量差异之外,获取信息的方式主要都是依赖二分法缩窄范围、逐字符提取。整个流程如果通过人工操作都是十分麻烦的,所以最好还是借助工具去解题,这里推荐 sqlmap

    确认注入点

    首先确认注入点。

    这个挑战可以发起 GET 请求的地方不多,通过 sqlmap 逐个测试,不难发现注入点在这里:

    http://challenge01.root-me.org/web-serveur/ch40/?action=member&member=2;[注入点]



    其中 sqlmap 可以通过 Burp Suite 直接调用,由 Burp Suite 把捕获到的 HTTP 请求转发到 sqlmap 进行测试会方便很多。

    Burp Suite 会把疑似注入点的 HTTP 请求存储到临时文件,例如:

    C:\Users\ADMINI~1\AppData\Local\Temp\\1552787207472.req ,该文件的内容一般如下:

    然后通过 sqlmap 的 -r 参数读取这 HTTP 请求文件即可。


    这是使用 sqlmap 的测试记录:

    通过 sqlmap 的输出结果可知,这是一个 PostgreSQL 数据库,所测试的 HTTP 请求可以作为一个 stacked queries (堆叠注入点),探针是 2;SELECT PG_SLEEP(5)--

    堆叠注入 是指可以通过分号 ; 拼接多条 SQL 。

    构造通用 payload

    对探针稍微改造下,可以构造这样的通用 payload :

    2;(SELECT (CASE WHEN ([注入点]) THEN PG_SLEEP(5) ELSE PG_SLEEP(0) END))--

    其中 [注入点] 替换成用于获取信息的布尔注入 SQL ,当布尔结果为 true 时,页面延时 5 秒,否则延时 0 秒。

    可能是题目做过延时上限限制,最长的延时时间实际只有 3 秒

    布尔注入 + 二分法获取数据库信息

    利用通用 payload 就可以构造很多针对性的 payload 了。

    例如我们要得到数据库的名称,在 pgsql 中可以通过 CURRENT_DATABASE() 函数获得。

    不过要通过布尔注入获得这个值,需要先知道这个名称(字符串)的长度。

    为此可以构造这组 payload ,通过不断二分法进行长度猜测,得知数据库名称长度为 15 :


    知道长度后,可以开始逐字符猜解数据库名称。

    这个过程中需要利用到的函数有两个:

    • SUBSTR(str,pos,len) :从 str 中下标为 pos 的字符开始截取长度为 len 的子串(pos 从 1 开始)
    • ASCII(char) :得到字符的十进制 ASCII 编码

    而逐字符猜解数据库名称的原理也是很简单,通过二分法确定数据库名称每一个字符在 ASCII 表中的哪个位置即可。

    ASCII 表的十进制范围为 0~127 ,因此要确定一个字符,最多需要二分查找 7 次。

    实际操作一下,构造第 1 个字符的 payload 组如下,得到第 1 个字符为 c

    再构造第 2 个字符的 payload 组如下,得到第 2 个字符为 _

    由于前面已经得知数据库名称长度为 15 ,要通过二分法全部猜解出来,最坏的情况要发起 15 * 7 = 105 次猜测请求,非常繁琐且耗时,所以这里我就不再逐一罗列 payload 了。按照前面的步骤逐个字符猜下去,最终得到的数据库名称为 c_webserveur_40


    通过这个方法,完全可以实现拖库(即得到数据库中所有表名称、每个表的列、每一列的数据)。

    但是效率太低了,所以现在开始改用 sqlmap 完成这个过程。

    可选:设置 sqlmap 代理

    由于前面已经通过 Burp Suite 构造了 HTTP 请求文件 1552787207472.req,就直接利用好了。

    执行 sqlmap 命令(不能直接复制用,-r 参数需根据实际使用的而定):

    不过在天朝的同学很可能会出现如下连接失败的报错:

    目前 Rootme 还没有加 WAF ,所以这个错误不是 WAF 引起的,不需要考虑绕过。

    这是因为天朝网络的缘故,虽然 Rootme 还不在墙后,但是最好还是通过科学代理加速一下,例如 shadowsocks


    sqlmap 可以通过 --proxy 添加科学代理,这次命令就正常执行了:

    通过 sqlmap 获取数据库信息

    因为已经知道是延时注入,所以在使用 sqlmap 时需要添加延时时常参数 --time-sec


    获得所有数据库模式名称

    首先通过 sqlmap 的 --dbs 参数查询所有库名:

    不难注意到,我们在前面查询到 CURRENT_DATABASE() 的值是 c_webserveur_40

    而这里通过 sqlmap 的 --dbs 获得的三个库名没有一个是 c_webserveur_40

    这是因为在 pgsql 中,数据库的分层结构是 database/schema/table 。

    CURRENT_DATABASE() 得到的是 database 名称,sqlmap 得到的是三个 schema 名称(schema 即数据库模式)。


    获得当前的数据库模式

    一般情况下,用户表都是存储在数据库模式 public 中的。

    要验证的话,可以通过 sqlmap 的 --current-db 参数获得当前的数据库模式:


    获得当前数据库模式中的所有表名

    知道当前数据库模式后,就可以通过 sqlmap 的 -D 参数指定模式名,再通过 --tables 参数查询这个模式下的所有表名:


    获得指定表的表结构

    很明显,public 模式下只有一张表 users

    可以通过 sqlmap 的 -T 参数指定这张表,再通过 --columns 参数查询其表结构:


    获得表数据

    得到表 users 的所有列名后,就可以通过 sqlmap 的 -C 参数指定这些列,再通过 --dump 得到每列的数据:

    完成挑战

    从获得的表数据来看,虽然得到了所有用户的 password,但是 username 一列是空的,换言之我们同样不知道哪个用户是哪个密码。

    但是 http://challenge01.root-me.org/web-serveur/ch40/?action=memberlist 这个页面早就告诉了我们每个用户名和其 id 的对应关系了,其中 admin 的 id 是 1 ,所以其密码是 T!m3B@s3DSQL!

    登陆后得知 admin 的密码就是 flag ,完成挑战。


    转载请注明:EXP 技术分享博客 » CTF – RootMe解题报告 [Web-Server : SQL injection – Time based]

    喜欢 (0) 分享 (0)
    发表我的评论
    取消评论

    表情

    Hi,您需要填写昵称和邮箱!

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址