bhp-pro 1.3.0__py3-none-any.whl → 1.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bhp_pro-1.3.0.dist-info → bhp_pro-1.3.2.dist-info}/METADATA +1 -1
- bhp_pro-1.3.2.dist-info/RECORD +6 -0
- {bhp_pro-1.3.0.dist-info → bhp_pro-1.3.2.dist-info}/WHEEL +1 -1
- bhp_pro.py +680 -73
- bhp_pro-1.3.0.dist-info/RECORD +0 -6
- {bhp_pro-1.3.0.dist-info → bhp_pro-1.3.2.dist-info}/entry_points.txt +0 -0
- {bhp_pro-1.3.0.dist-info → bhp_pro-1.3.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
bhp_pro.py,sha256=i5J-aBNaC14n76DYyym6cs2BBkHF7kT_cLImMl1zb38,777565
|
|
2
|
+
bhp_pro-1.3.2.dist-info/METADATA,sha256=p5i7atVTfveV5iPPzgiUMtczuAsgD6xN-sDAzPCGT_E,600
|
|
3
|
+
bhp_pro-1.3.2.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
4
|
+
bhp_pro-1.3.2.dist-info/entry_points.txt,sha256=Yn3HpraGX3lXX4FPq3Gm-lHh3SwQA-5rtgPWNWMFXkw,41
|
|
5
|
+
bhp_pro-1.3.2.dist-info/top_level.txt,sha256=1xjbIaVM77UJz9Tsi1JjILgE0YDG7iLhY6KSMNEi9zM,8
|
|
6
|
+
bhp_pro-1.3.2.dist-info/RECORD,,
|
bhp_pro.py
CHANGED
|
@@ -16875,89 +16875,696 @@ def menu3():
|
|
|
16875
16875
|
#===SUBDOmain TAKEOVER===#
|
|
16876
16876
|
def access_control():
|
|
16877
16877
|
|
|
16878
|
-
|
|
16879
|
-
|
|
16880
|
-
|
|
16881
|
-
|
|
16882
|
-
|
|
16883
|
-
|
|
16884
|
-
|
|
16885
|
-
|
|
16886
|
-
'Origin': 'https://yahoo.com/',
|
|
16887
|
-
'Access-Control-Request-Method': 'GET',
|
|
16888
|
-
'Access-Control-Request-Headers': 'X-Requested-With, X-Online-Host, X-Forwarded-For',
|
|
16889
|
-
'User-Agent': 'Mozilla/5.0'
|
|
16890
|
-
}
|
|
16891
|
-
|
|
16892
|
-
for protocol in ['http://', 'https://']:
|
|
16893
|
-
url = protocol + domain
|
|
16894
|
-
try:
|
|
16895
|
-
response = requests.options(url, headers=preflight_headers, timeout=5)
|
|
16896
|
-
status = response.status_code
|
|
16897
|
-
allowed_headers = response.headers.get('Access-Control-Allow-Headers', '').lower()
|
|
16898
|
-
|
|
16899
|
-
allowed = []
|
|
16900
|
-
if 'x-requested-with' in allowed_headers:
|
|
16901
|
-
allowed.append('X-Requested-With')
|
|
16902
|
-
if 'x-online-host' in allowed_headers:
|
|
16903
|
-
allowed.append('X-Online-Host')
|
|
16904
|
-
if 'x-forwarded-for' in allowed_headers:
|
|
16905
|
-
allowed.append('X-Forwarded-For')
|
|
16906
|
-
|
|
16907
|
-
if allowed:
|
|
16908
|
-
server = response.headers.get('Server', 'Unknown')
|
|
16909
|
-
print(f"✅ {url} - ALLOWS: {', '.join(allowed)} | Status: {status}")
|
|
16910
|
-
with LOCK:
|
|
16911
|
-
with open(OUTPUT_FILE, "a") as f:
|
|
16912
|
-
f.write(f"{url} | Status: {status} | Server: {server} | Allowed Headers: {', '.join(allowed)}\n")
|
|
16913
|
-
else:
|
|
16914
|
-
print(f"⚠️ {url} - None of the desired X-* headers allowed.")
|
|
16915
|
-
|
|
16916
|
-
except requests.exceptions.RequestException as e:
|
|
16917
|
-
print(f"❌ {url} - Request failed: {e}")
|
|
16918
|
-
finally:
|
|
16919
|
-
with LOCK:
|
|
16920
|
-
progress.update(1)
|
|
16921
|
-
|
|
16922
|
-
|
|
16923
|
-
def worker(domain_queue, progress):
|
|
16924
|
-
while not domain_queue.empty():
|
|
16925
|
-
domain = domain_queue.get()
|
|
16926
|
-
check_domain(domain, progress)
|
|
16927
|
-
domain_queue.task_done()
|
|
16878
|
+
import os
|
|
16879
|
+
import re
|
|
16880
|
+
import socket
|
|
16881
|
+
import time
|
|
16882
|
+
import threading
|
|
16883
|
+
import ipaddress
|
|
16884
|
+
from queue import Queue
|
|
16885
|
+
from tqdm import tqdm
|
|
16928
16886
|
|
|
16929
|
-
|
|
16887
|
+
FREE_SERVERS = {
|
|
16888
|
+
'google': 'www.google.com',
|
|
16889
|
+
'cloudflare': '1.1.1.1',
|
|
16890
|
+
'open_dns': '208.67.222.222',
|
|
16891
|
+
'quad9': '9.9.9.9',
|
|
16892
|
+
'example': 'example.com',
|
|
16893
|
+
'test_server': 'test.server.com'
|
|
16894
|
+
}
|
|
16930
16895
|
|
|
16896
|
+
XHTTP_PAYLOADS = {
|
|
16897
|
+
"1": {
|
|
16898
|
+
"name": "Standard HTTP/1.1",
|
|
16899
|
+
"payload": "GET / HTTP/1.1\r\nHost: [HOST]\r\nUser-Agent: [UA]\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n",
|
|
16900
|
+
"type": "XHTTP"
|
|
16901
|
+
},
|
|
16902
|
+
"2": {
|
|
16903
|
+
"name": "HTTP/1.1 Keep-Alive",
|
|
16904
|
+
"payload": "GET / HTTP/1.1\r\nHost: [HOST]\r\nUser-Agent: [UA]\r\nConnection: Keep-Alive\r\nKeep-Alive: timeout=30\r\n\r\n",
|
|
16905
|
+
"type": "XHTTP"
|
|
16906
|
+
},
|
|
16907
|
+
"3": {
|
|
16908
|
+
"name": "Chrome User-Agent",
|
|
16909
|
+
"payload": "GET / HTTP/1.1\r\nHost: [HOST]\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.9\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n",
|
|
16910
|
+
"type": "XHTTP"
|
|
16911
|
+
},
|
|
16912
|
+
"4": {
|
|
16913
|
+
"name": "CONNECT Method",
|
|
16914
|
+
"payload": "CONNECT [HOST]:443 HTTP/1.1\r\nHost: [HOST]:443\r\nUser-Agent: [UA]\r\nProxy-Connection: Keep-Alive\r\n\r\n",
|
|
16915
|
+
"type": "XHTTP"
|
|
16916
|
+
},
|
|
16917
|
+
"5": {
|
|
16918
|
+
"name": "X-Online-Host Injection",
|
|
16919
|
+
"payload": f"GET / HTTP/1.1\r\nHost: [HOST]\r\nX-Online-Host: {FREE_SERVERS['google']}\r\nUser-Agent: [UA]\r\n\r\n",
|
|
16920
|
+
"type": "XHTTP"
|
|
16921
|
+
},
|
|
16922
|
+
"6": {
|
|
16923
|
+
"name": "X-Forwarded-Host Injection",
|
|
16924
|
+
"payload": f"GET / HTTP/1.1\r\nHost: [HOST]\r\nX-Forwarded-Host: {FREE_SERVERS['example']}\r\nUser-Agent: [UA]\r\n\r\n",
|
|
16925
|
+
"type": "XHTTP"
|
|
16926
|
+
},
|
|
16927
|
+
"7": {
|
|
16928
|
+
"name": "Simple POST Request",
|
|
16929
|
+
"payload": "POST / HTTP/1.1\r\nHost: [HOST]\r\nUser-Agent: [UA]\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 0\r\n\r\n",
|
|
16930
|
+
"type": "XHTTP"
|
|
16931
|
+
},
|
|
16932
|
+
"8": {
|
|
16933
|
+
"name": "HTTP/2 Preface",
|
|
16934
|
+
"payload": "GET / HTTP/1.1\r\nHost: [HOST]\r\nUser-Agent: [UA]\r\nUpgrade: h2c\r\nHTTP2-Settings: AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA\r\nConnection: Upgrade, HTTP2-Settings\r\n\r\n",
|
|
16935
|
+
"type": "XHTTP"
|
|
16936
|
+
},
|
|
16937
|
+
"9": {
|
|
16938
|
+
"name": "Mobile Safari UA",
|
|
16939
|
+
"payload": "GET / HTTP/1.1\r\nHost: [HOST]\r\nUser-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.9\r\n\r\n",
|
|
16940
|
+
"type": "XHTTP"
|
|
16941
|
+
},
|
|
16942
|
+
"10": {
|
|
16943
|
+
"name": "No Cache Headers",
|
|
16944
|
+
"payload": "GET / HTTP/1.1\r\nHost: [HOST]\r\nUser-Agent: [UA]\r\nCache-Control: no-cache, no-store, must-revalidate\r\nPragma: no-cache\r\nExpires: 0\r\n\r\n",
|
|
16945
|
+
"type": "XHTTP"
|
|
16946
|
+
},
|
|
16947
|
+
"11": {
|
|
16948
|
+
"name": "Double Host Header",
|
|
16949
|
+
"payload": f"GET / HTTP/1.1\r\nHost: [HOST]\r\nHost: {FREE_SERVERS['example']}\r\nUser-Agent: [UA]\r\n\r\n",
|
|
16950
|
+
"type": "XHTTP"
|
|
16951
|
+
},
|
|
16952
|
+
"12": {
|
|
16953
|
+
"name": "WebSocket Upgrade",
|
|
16954
|
+
"payload": "GET / HTTP/1.1\r\nHost: [HOST]\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: [KEY]\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n",
|
|
16955
|
+
"type": "WS"
|
|
16956
|
+
},
|
|
16957
|
+
"13": {
|
|
16958
|
+
"name": "Google Bot UA",
|
|
16959
|
+
"payload": "GET / HTTP/1.1\r\nHost: [HOST]\r\nUser-Agent: Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Googlebot/2.1; +http://www.google.com/bot.html) Chrome/120.0.0.0 Safari/537.36\r\n\r\n",
|
|
16960
|
+
"type": "XHTTP"
|
|
16961
|
+
},
|
|
16962
|
+
"14": {
|
|
16963
|
+
"name": "Forwarded Headers",
|
|
16964
|
+
"payload": f"GET / HTTP/1.1\r\nHost: [HOST]\r\nUser-Agent: [UA]\r\nForwarded: for=192.168.1.1;proto=http;host={FREE_SERVERS['test_server']}\r\nX-Forwarded-For: 192.168.1.1\r\nX-Real-IP: 192.168.1.1\r\n\r\n",
|
|
16965
|
+
"type": "XHTTP"
|
|
16966
|
+
},
|
|
16967
|
+
"15": {
|
|
16968
|
+
"name": "Pipelined Request",
|
|
16969
|
+
"payload": "GET / HTTP/1.1\r\nHost: [HOST]\r\n\r\nGET /favicon.ico HTTP/1.1\r\nHost: [HOST]\r\n\r\n",
|
|
16970
|
+
"type": "XHTTP"
|
|
16971
|
+
}
|
|
16972
|
+
}
|
|
16931
16973
|
|
|
16932
|
-
|
|
16933
|
-
|
|
16934
|
-
|
|
16935
|
-
|
|
16936
|
-
|
|
16974
|
+
class Config:
|
|
16975
|
+
def __init__(self):
|
|
16976
|
+
self.payloads = []
|
|
16977
|
+
self.targets = []
|
|
16978
|
+
self.proxies = []
|
|
16979
|
+
self.output_file = "working_configs.txt"
|
|
16980
|
+
self.timeout = 3
|
|
16981
|
+
self.threads = 50
|
|
16982
|
+
|
|
16983
|
+
class PayloadManager:
|
|
16984
|
+
@staticmethod
|
|
16985
|
+
def generate_websocket_key():
|
|
16986
|
+
import base64, os
|
|
16987
|
+
return base64.b64encode(os.urandom(16)).decode()
|
|
16988
|
+
|
|
16989
|
+
@staticmethod
|
|
16990
|
+
def prepare_payload(payload_template, target_domain):
|
|
16991
|
+
replacements = {
|
|
16992
|
+
'[HOST]': target_domain,
|
|
16993
|
+
'[UA]': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
16994
|
+
'[KEY]': PayloadManager.generate_websocket_key()
|
|
16995
|
+
}
|
|
16996
|
+
payload = payload_template
|
|
16997
|
+
for placeholder, value in replacements.items():
|
|
16998
|
+
payload = payload.replace(placeholder, value)
|
|
16999
|
+
return payload
|
|
17000
|
+
|
|
17001
|
+
@staticmethod
|
|
17002
|
+
def format_config(result):
|
|
17003
|
+
escaped_payload = result['sent_payload'].replace('\r\n', '[crlf]')
|
|
17004
|
+
return f"""# ====================================================
|
|
17005
|
+
# CONFIG #{result['config_id']}
|
|
17006
|
+
# ====================================================
|
|
17007
|
+
[CONFIG]
|
|
17008
|
+
Name={result['payload_name']} | {result['status_code']} | {result['proxy_host']}:{result['proxy_port']}
|
|
17009
|
+
Server={result['proxy_host']}
|
|
17010
|
+
Port={result['proxy_port']}
|
|
17011
|
+
Protocol=XHTTP
|
|
17012
|
+
SNI={result['target']}
|
|
17013
|
+
ProxyType=HTTP
|
|
17014
|
+
|
|
17015
|
+
[PAYLOAD]
|
|
17016
|
+
{escaped_payload}
|
|
17017
|
+
|
|
17018
|
+
[HEADERS]
|
|
17019
|
+
User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
|
|
17020
|
+
Accept=*/*
|
|
17021
|
+
Accept-Language=en-US,en;q=0.9
|
|
17022
|
+
Accept-Encoding=gzip, deflate
|
|
17023
|
+
Connection=keep-alive
|
|
17024
|
+
|
|
17025
|
+
[SETTINGS]
|
|
17026
|
+
ForwardHost=true
|
|
17027
|
+
UseSNI=true
|
|
17028
|
+
|
|
17029
|
+
[TEST_INFO]
|
|
17030
|
+
Date={time.strftime("%Y-%m-%d %H:%M:%S")}
|
|
17031
|
+
Target={result['target']}
|
|
17032
|
+
Status=HTTP {result['status_code']}
|
|
17033
|
+
Response_Time={result['response_time']:.2f}s
|
|
17034
|
+
Server={result['server']}
|
|
17035
|
+
Bypass_Indicators={','.join(result['bypass_indicators'])}
|
|
17036
|
+
# ====================================================
|
|
16937
17037
|
|
|
16938
|
-
|
|
16939
|
-
|
|
17038
|
+
"""
|
|
17039
|
+
|
|
17040
|
+
@staticmethod
|
|
17041
|
+
def save_results(results, filename, status_filter='All'):
|
|
17042
|
+
if not results:
|
|
17043
|
+
return 0
|
|
17044
|
+
|
|
17045
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
|
17046
|
+
f.write(f"""# HTTP Injector Configurations
|
|
17047
|
+
# Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}
|
|
17048
|
+
# Total Configs: {len(results)}
|
|
17049
|
+
# Status Filter: {status_filter}
|
|
17050
|
+
#
|
|
17051
|
+
# HOW TO USE:
|
|
17052
|
+
# 1. Copy each [CONFIG] section
|
|
17053
|
+
# 2. Create new config in HTTP Injector
|
|
17054
|
+
# 3. Paste the settings
|
|
17055
|
+
# ====================================================
|
|
16940
17056
|
|
|
16941
|
-
|
|
16942
|
-
|
|
16943
|
-
|
|
17057
|
+
""")
|
|
17058
|
+
f.write("[SUMMARY]\n")
|
|
17059
|
+
f.write(f"Total_Configs={len(results)}\n")
|
|
17060
|
+
f.write(f"Status_Filter={status_filter}\n")
|
|
17061
|
+
|
|
17062
|
+
status_stats = {}
|
|
17063
|
+
for result in results:
|
|
17064
|
+
status = result['status_code']
|
|
17065
|
+
status_category = f"{status//100}xx"
|
|
17066
|
+
status_stats[status_category] = status_stats.get(status_category, 0) + 1
|
|
17067
|
+
|
|
17068
|
+
f.write("\n[STATUS_DISTRIBUTION]\n")
|
|
17069
|
+
for status, count in sorted(status_stats.items()):
|
|
17070
|
+
f.write(f"{status}={count}\n")
|
|
17071
|
+
|
|
17072
|
+
f.write(f"\n#{'='*50}\n# WORKING CONFIGURATIONS\n#{'='*50}\n\n")
|
|
17073
|
+
|
|
17074
|
+
for i, result in enumerate(results, 1):
|
|
17075
|
+
result['config_id'] = i
|
|
17076
|
+
f.write(PayloadManager.format_config(result))
|
|
17077
|
+
|
|
17078
|
+
return len(results)
|
|
16944
17079
|
|
|
16945
|
-
|
|
17080
|
+
class ProxyManager:
|
|
17081
|
+
@staticmethod
|
|
17082
|
+
def parse_proxy_input(proxy_input, ports):
|
|
17083
|
+
proxies = []
|
|
17084
|
+
|
|
17085
|
+
if ':' in proxy_input:
|
|
17086
|
+
ip_part, port = proxy_input.split(':', 1)
|
|
17087
|
+
port = int(port)
|
|
17088
|
+
|
|
17089
|
+
if '/' in ip_part:
|
|
17090
|
+
try:
|
|
17091
|
+
network = ipaddress.ip_network(ip_part, strict=False)
|
|
17092
|
+
for ip in network.hosts():
|
|
17093
|
+
proxies.append((str(ip), port))
|
|
17094
|
+
except:
|
|
17095
|
+
pass
|
|
17096
|
+
else:
|
|
17097
|
+
proxies.append((ip_part, port))
|
|
17098
|
+
return proxies
|
|
17099
|
+
|
|
17100
|
+
if '/' in proxy_input:
|
|
17101
|
+
try:
|
|
17102
|
+
network = ipaddress.ip_network(proxy_input, strict=False)
|
|
17103
|
+
for ip in network.hosts():
|
|
17104
|
+
for port in ports:
|
|
17105
|
+
proxies.append((str(ip), port))
|
|
17106
|
+
except:
|
|
17107
|
+
pass
|
|
17108
|
+
return proxies
|
|
17109
|
+
|
|
17110
|
+
if '-' in proxy_input:
|
|
17111
|
+
parts = proxy_input.split('-')
|
|
17112
|
+
if len(parts) == 2:
|
|
17113
|
+
base_ip = parts[0].rsplit('.', 1)[0]
|
|
17114
|
+
start = int(parts[0].rsplit('.', 1)[1])
|
|
17115
|
+
end = int(parts[1])
|
|
17116
|
+
|
|
17117
|
+
for i in range(start, end + 1):
|
|
17118
|
+
ip = f"{base_ip}.{i}"
|
|
17119
|
+
for port in ports:
|
|
17120
|
+
proxies.append((ip, port))
|
|
17121
|
+
return proxies
|
|
17122
|
+
|
|
17123
|
+
for port in ports:
|
|
17124
|
+
proxies.append((proxy_input.strip(), port))
|
|
17125
|
+
|
|
17126
|
+
return proxies
|
|
16946
17127
|
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
|
|
16950
|
-
|
|
16951
|
-
|
|
17128
|
+
class NetworkTester:
|
|
17129
|
+
def __init__(self, config):
|
|
17130
|
+
self.config = config
|
|
17131
|
+
self.working_configs = []
|
|
17132
|
+
self.lock = threading.Lock()
|
|
17133
|
+
self.queue = Queue()
|
|
17134
|
+
self.tested = 0
|
|
17135
|
+
self.successful = 0
|
|
17136
|
+
self.failed = 0
|
|
17137
|
+
|
|
17138
|
+
def detect_provider(self, response_text, target):
|
|
17139
|
+
provider_indicators = {
|
|
17140
|
+
'Google': ['Google', 'gws', 'GSE', 'YouTube'],
|
|
17141
|
+
'Microsoft': ['Microsoft', 'IIS', 'Azure', 'Office'],
|
|
17142
|
+
'Facebook': ['Facebook', 'fb', 'Instagram'],
|
|
17143
|
+
'Cloudflare': ['Cloudflare', 'CF-RAY', 'cf-cache-status'],
|
|
17144
|
+
'Akamai': ['Akamai', 'Akamai-Ghost'],
|
|
17145
|
+
'Amazon': ['Amazon', 'AWS', 'CloudFront', 'S3'],
|
|
17146
|
+
'Alibaba': ['Alibaba', 'Aliyun', 'EMAS', 'AlibabaCloud'],
|
|
17147
|
+
'Tencent': ['Tencent', 'QCloud', 'TCB'],
|
|
17148
|
+
'Baidu': ['Baidu', 'baidu.com'],
|
|
17149
|
+
'Fastly': ['Fastly', 'Fastly error'],
|
|
17150
|
+
'CDN77': ['CDN77', 'NetDNA'],
|
|
17151
|
+
'Cloudflare': ['Cloudflare', 'CF-RAY'],
|
|
17152
|
+
'Nginx': ['nginx'],
|
|
17153
|
+
'Apache': ['Apache', 'httpd'],
|
|
17154
|
+
'LiteSpeed': ['LiteSpeed', 'LSWS'],
|
|
17155
|
+
'Varnish': ['Varnish'],
|
|
17156
|
+
'Sucuri': ['Sucuri'],
|
|
17157
|
+
'Imperva': ['Imperva', 'Imperva Incapsula']
|
|
17158
|
+
}
|
|
17159
|
+
|
|
17160
|
+
for provider, indicators in provider_indicators.items():
|
|
17161
|
+
for indicator in indicators:
|
|
17162
|
+
if indicator.lower() in response_text.lower():
|
|
17163
|
+
return provider
|
|
17164
|
+
|
|
17165
|
+
return 'Unknown'
|
|
17166
|
+
|
|
17167
|
+
def test_connection(self, payload_info, target, proxy_host, proxy_port):
|
|
17168
|
+
try:
|
|
17169
|
+
sent_payload = PayloadManager.prepare_payload(payload_info['payload'], target)
|
|
17170
|
+
|
|
17171
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
17172
|
+
sock.settimeout(self.config.timeout)
|
|
17173
|
+
|
|
17174
|
+
start_time = time.time()
|
|
17175
|
+
sock.connect((proxy_host, proxy_port))
|
|
17176
|
+
sock.send(sent_payload.encode())
|
|
17177
|
+
|
|
17178
|
+
response = b""
|
|
17179
|
+
try:
|
|
17180
|
+
sock.settimeout(self.config.timeout)
|
|
17181
|
+
while True:
|
|
17182
|
+
chunk = sock.recv(1024)
|
|
17183
|
+
if not chunk:
|
|
17184
|
+
break
|
|
17185
|
+
response += chunk
|
|
17186
|
+
if len(response) > 8192:
|
|
17187
|
+
break
|
|
17188
|
+
except socket.timeout:
|
|
17189
|
+
pass
|
|
17190
|
+
|
|
17191
|
+
sock.close()
|
|
17192
|
+
response_time = time.time() - start_time
|
|
17193
|
+
|
|
17194
|
+
response_text = response.decode('utf-8', errors='ignore')
|
|
17195
|
+
|
|
17196
|
+
status_match = re.search(r'HTTP/\d\.\d\s+(\d{3})', response_text)
|
|
17197
|
+
status_code = int(status_match.group(1)) if status_match else 0
|
|
17198
|
+
|
|
17199
|
+
if status_code == 0:
|
|
17200
|
+
return {'success': False, 'error': 'No HTTP response'}
|
|
17201
|
+
|
|
17202
|
+
server_match = re.search(r'Server:\s*([^\r\n]+)', response_text, re.IGNORECASE)
|
|
17203
|
+
server_info = server_match.group(1) if server_match else "Unknown"
|
|
17204
|
+
|
|
17205
|
+
bypass_indicators = self._check_bypass_indicators(response_text)
|
|
17206
|
+
provider = self.detect_provider(response_text, target)
|
|
17207
|
+
|
|
17208
|
+
return {
|
|
17209
|
+
'payload_name': payload_info['name'],
|
|
17210
|
+
'payload_template': payload_info['payload'],
|
|
17211
|
+
'sent_payload': sent_payload,
|
|
17212
|
+
'target': target,
|
|
17213
|
+
'proxy_host': proxy_host,
|
|
17214
|
+
'proxy_port': proxy_port,
|
|
17215
|
+
'status_code': status_code,
|
|
17216
|
+
'response_time': response_time,
|
|
17217
|
+
'server': server_info,
|
|
17218
|
+
'bypass_indicators': bypass_indicators,
|
|
17219
|
+
'provider': provider,
|
|
17220
|
+
'success': True
|
|
17221
|
+
}
|
|
17222
|
+
|
|
17223
|
+
except socket.timeout:
|
|
17224
|
+
return {'success': False, 'error': 'Connection timeout'}
|
|
17225
|
+
except ConnectionRefusedError:
|
|
17226
|
+
return {'success': False, 'error': 'Connection refused'}
|
|
17227
|
+
except Exception as e:
|
|
17228
|
+
return {'success': False, 'error': str(e)}
|
|
17229
|
+
|
|
17230
|
+
def _check_bypass_indicators(self, response_text):
|
|
17231
|
+
indicators = []
|
|
17232
|
+
checks = [
|
|
17233
|
+
(r'CF-RAY', 'Cloudflare Proxy'),
|
|
17234
|
+
(r'X-Cache', 'Caching Proxy'),
|
|
17235
|
+
(r'Connection established', 'CONNECT Successful'),
|
|
17236
|
+
(r'101 Switching Protocols', 'WebSocket Upgrade'),
|
|
17237
|
+
(r'HTTP/1.1 200.*HTTP/1.1', 'Request Smuggling'),
|
|
17238
|
+
(r'Upgrade:', 'Protocol Upgrade'),
|
|
17239
|
+
(r'Keep-Alive', 'Connection Persistence'),
|
|
17240
|
+
(r'X-Forwarded-For', 'Forwarded Request'),
|
|
17241
|
+
(r'X-Real-IP', 'Real IP Header'),
|
|
17242
|
+
(r'X-Forwarded-Host', 'Host Bypass'),
|
|
17243
|
+
(r'X-Original-Host', 'Original Host'),
|
|
17244
|
+
(r'Via:', 'Proxy Chain'),
|
|
17245
|
+
(r'Proxy-Connection', 'Proxy Support'),
|
|
17246
|
+
(r'Transfer-Encoding: chunked', 'Chunked Encoding'),
|
|
17247
|
+
(r'Content-Encoding: gzip', 'Compression Support'),
|
|
17248
|
+
(r'Server:.*nginx', 'Nginx Server'),
|
|
17249
|
+
(r'Server:.*Apache', 'Apache Server'),
|
|
17250
|
+
(r'Set-Cookie', 'Session Cookie'),
|
|
17251
|
+
(r'Vary:', 'Cache Variation'),
|
|
17252
|
+
(r'Age:', 'Cache Age')
|
|
17253
|
+
]
|
|
17254
|
+
|
|
17255
|
+
for pattern, meaning in checks:
|
|
17256
|
+
if re.search(pattern, response_text, re.IGNORECASE | re.DOTALL):
|
|
17257
|
+
indicators.append(meaning)
|
|
17258
|
+
|
|
17259
|
+
return indicators
|
|
17260
|
+
|
|
17261
|
+
def worker(self, progress_bar):
|
|
17262
|
+
while True:
|
|
17263
|
+
try:
|
|
17264
|
+
task = self.queue.get_nowait()
|
|
17265
|
+
except:
|
|
17266
|
+
break
|
|
17267
|
+
|
|
17268
|
+
try:
|
|
17269
|
+
payload_info, target, proxy_host, proxy_port = task
|
|
17270
|
+
result = self.test_connection(payload_info, target, proxy_host, proxy_port)
|
|
17271
|
+
|
|
17272
|
+
with self.lock:
|
|
17273
|
+
self.tested += 1
|
|
17274
|
+
|
|
17275
|
+
if result['success']:
|
|
17276
|
+
self.successful += 1
|
|
17277
|
+
self.working_configs.append(result)
|
|
17278
|
+
|
|
17279
|
+
icon = "✅" if 200 <= result['status_code'] < 300 else \
|
|
17280
|
+
"↪️" if 300 <= result['status_code'] < 400 else \
|
|
17281
|
+
"⚠️" if 400 <= result['status_code'] < 500 else "❌"
|
|
17282
|
+
|
|
17283
|
+
if result['bypass_indicators']:
|
|
17284
|
+
icon += "🚀"
|
|
17285
|
+
|
|
17286
|
+
provider_icon = {
|
|
17287
|
+
'Google': '🔍',
|
|
17288
|
+
'Microsoft': '🪟',
|
|
17289
|
+
'Facebook': '👥',
|
|
17290
|
+
'YouTube': '📺',
|
|
17291
|
+
'Amazon': '📦',
|
|
17292
|
+
'Cloudflare': '🛡️'
|
|
17293
|
+
}.get(result['provider'], '🌐')
|
|
17294
|
+
|
|
17295
|
+
print(f"\r{icon}{provider_icon} {result['payload_name'][:20]:20} → "
|
|
17296
|
+
f"{target[:15]:15} HTTP {result['status_code']:3} via {proxy_host}:{proxy_port}")
|
|
17297
|
+
else:
|
|
17298
|
+
self.failed += 1
|
|
17299
|
+
|
|
17300
|
+
progress_bar.update(1)
|
|
17301
|
+
self.queue.task_done()
|
|
17302
|
+
|
|
17303
|
+
except Exception as e:
|
|
17304
|
+
with self.lock:
|
|
17305
|
+
self.tested += 1
|
|
17306
|
+
self.failed += 1
|
|
17307
|
+
progress_bar.update(1)
|
|
17308
|
+
self.queue.task_done()
|
|
17309
|
+
|
|
17310
|
+
def run_tests(self):
|
|
17311
|
+
tasks = []
|
|
17312
|
+
for payload_info in self.config.payloads:
|
|
17313
|
+
for target in self.config.targets:
|
|
17314
|
+
if self.config.proxies:
|
|
17315
|
+
for proxy_host, proxy_port in self.config.proxies:
|
|
17316
|
+
tasks.append((payload_info, target, proxy_host, proxy_port))
|
|
17317
|
+
else:
|
|
17318
|
+
tasks.append((payload_info, target, target, 80))
|
|
17319
|
+
|
|
17320
|
+
for task in tasks:
|
|
17321
|
+
self.queue.put(task)
|
|
17322
|
+
|
|
17323
|
+
total_tests = len(tasks)
|
|
17324
|
+
|
|
17325
|
+
print(f"\n{'='*60}")
|
|
17326
|
+
print(f"🧪 TESTING CONFIGURATION")
|
|
17327
|
+
print(f"{'='*60}")
|
|
17328
|
+
print(f"Payloads: {len(self.config.payloads)}")
|
|
17329
|
+
print(f"Targets: {len(self.config.targets)}")
|
|
17330
|
+
print(f"Proxies: {len(self.config.proxies) if self.config.proxies else 'Direct'}")
|
|
17331
|
+
print(f"Total Tests: {total_tests}")
|
|
17332
|
+
print(f"Threads: {min(self.config.threads, total_tests)}")
|
|
17333
|
+
print(f"{'='*60}")
|
|
17334
|
+
|
|
17335
|
+
print("\n📡 TESTING IN PROGRESS...\n")
|
|
17336
|
+
|
|
17337
|
+
thread_count = min(self.config.threads, total_tests, 100)
|
|
17338
|
+
|
|
17339
|
+
self.working_configs = []
|
|
17340
|
+
self.tested = 0
|
|
17341
|
+
self.successful = 0
|
|
17342
|
+
self.failed = 0
|
|
17343
|
+
|
|
17344
|
+
with tqdm(total=total_tests, desc="Testing", unit="test", ncols=80) as progress_bar:
|
|
17345
|
+
threads = []
|
|
17346
|
+
for _ in range(thread_count):
|
|
17347
|
+
t = threading.Thread(target=self.worker, args=(progress_bar,))
|
|
17348
|
+
t.daemon = True
|
|
17349
|
+
t.start()
|
|
17350
|
+
threads.append(t)
|
|
17351
|
+
|
|
17352
|
+
for t in threads:
|
|
17353
|
+
t.join()
|
|
17354
|
+
|
|
17355
|
+
print("\r" + " " * 80 + "\r", end="")
|
|
17356
|
+
|
|
17357
|
+
print(f"\n{'='*60}")
|
|
17358
|
+
print(f"✅ TESTING COMPLETE")
|
|
17359
|
+
print(f"{'='*60}")
|
|
17360
|
+
print(f"Tests Completed: {self.tested}")
|
|
17361
|
+
print(f"Successful: {self.successful}")
|
|
17362
|
+
print(f"Failed: {self.failed}")
|
|
17363
|
+
print(f"Configs Found: {len(self.working_configs)}")
|
|
17364
|
+
|
|
17365
|
+
return self.working_configs
|
|
16952
17366
|
|
|
16953
|
-
|
|
16954
|
-
|
|
17367
|
+
def get_user_input():
|
|
17368
|
+
config = Config()
|
|
17369
|
+
|
|
17370
|
+
print("\n📋 AVAILABLE PAYLOADS:")
|
|
17371
|
+
for key, info in XHTTP_PAYLOADS.items():
|
|
17372
|
+
print(f"{key:>2}. {info['name']:25} [{info['type']:6}]")
|
|
17373
|
+
|
|
17374
|
+
selection = input("\nSelect payloads (comma-separated, or 'all'): ").strip()
|
|
17375
|
+
|
|
17376
|
+
if selection.lower() == 'all':
|
|
17377
|
+
selected_keys = list(XHTTP_PAYLOADS.keys())
|
|
17378
|
+
else:
|
|
17379
|
+
selected_keys = [s.strip() for s in selection.split(',') if s.strip() in XHTTP_PAYLOADS]
|
|
17380
|
+
|
|
17381
|
+
if not selected_keys:
|
|
17382
|
+
print("❌ No payloads selected!")
|
|
17383
|
+
return None
|
|
17384
|
+
|
|
17385
|
+
for key in selected_keys:
|
|
17386
|
+
config.payloads.append(XHTTP_PAYLOADS[key])
|
|
17387
|
+
|
|
17388
|
+
print(f"\n✅ Selected {len(config.payloads)} payloads")
|
|
17389
|
+
|
|
17390
|
+
targets_input = input("\nEnter target domain(s) (comma-separated or file): ").strip()
|
|
17391
|
+
|
|
17392
|
+
if os.path.isfile(targets_input):
|
|
17393
|
+
with open(targets_input, 'r') as f:
|
|
17394
|
+
config.targets = [line.strip() for line in f if line.strip()]
|
|
17395
|
+
else:
|
|
17396
|
+
config.targets = [t.strip() for t in targets_input.split(',') if t.strip()]
|
|
17397
|
+
|
|
17398
|
+
if not config.targets:
|
|
17399
|
+
print("❌ No targets specified!")
|
|
17400
|
+
return None
|
|
17401
|
+
|
|
17402
|
+
print("\n🔄 PROXY SETTINGS")
|
|
17403
|
+
print("1. Use proxies")
|
|
17404
|
+
print("2. Direct connection")
|
|
17405
|
+
|
|
17406
|
+
proxy_choice = input("\nSelect option (1-2): ").strip()
|
|
17407
|
+
|
|
17408
|
+
if proxy_choice == '1':
|
|
17409
|
+
proxy_input = input("Enter proxy (IP, IP:PORT, CIDR, or IP range): ").strip()
|
|
17410
|
+
|
|
17411
|
+
ports_input = input("Ports (default=80,443,8080,8443): ").strip()
|
|
17412
|
+
ports = [int(p.strip()) for p in ports_input.split(',') if p.strip().isdigit()] if ports_input else [80, 443, 8080, 8443]
|
|
17413
|
+
|
|
17414
|
+
config.proxies = ProxyManager.parse_proxy_input(proxy_input, ports)
|
|
17415
|
+
|
|
17416
|
+
if not config.proxies:
|
|
17417
|
+
print("❌ Invalid proxy input!")
|
|
17418
|
+
return None
|
|
17419
|
+
|
|
17420
|
+
print(f"✅ Loaded {len(config.proxies)} proxies")
|
|
17421
|
+
|
|
17422
|
+
return config
|
|
16955
17423
|
|
|
16956
|
-
|
|
16957
|
-
|
|
17424
|
+
def show_status_distribution(working_configs):
|
|
17425
|
+
status_details = {}
|
|
17426
|
+
for config in working_configs:
|
|
17427
|
+
status = config['status_code']
|
|
17428
|
+
status_details[status] = status_details.get(status, 0) + 1
|
|
17429
|
+
|
|
17430
|
+
print(f"\n{'='*60}")
|
|
17431
|
+
print("📊 STATUS CODE DISTRIBUTION")
|
|
17432
|
+
print(f"{'='*60}")
|
|
17433
|
+
|
|
17434
|
+
for status, count in sorted(status_details.items()):
|
|
17435
|
+
print(f" HTTP {status}: {count} configs")
|
|
17436
|
+
|
|
17437
|
+
print(f"\n{'='*60}")
|
|
17438
|
+
print("💾 SAVE RESULTS")
|
|
17439
|
+
print(f"{'='*60}")
|
|
17440
|
+
print("Which status codes to save?")
|
|
17441
|
+
print("1. All status codes")
|
|
17442
|
+
print("2. Only 2xx (Success)")
|
|
17443
|
+
print("3. Only 3xx (Redirection)")
|
|
17444
|
+
print("4. Only 4xx (Client Error)")
|
|
17445
|
+
print("5. Only 5xx (Server Error)")
|
|
17446
|
+
print("6. Success (2xx and 3xx)")
|
|
17447
|
+
|
|
17448
|
+
while True:
|
|
17449
|
+
choice = input("\nSelect option (1-6): ").strip()
|
|
17450
|
+
|
|
17451
|
+
if choice in ['1', '2', '3', '4', '5', '6']:
|
|
17452
|
+
status_filter_map = {
|
|
17453
|
+
'1': 'all',
|
|
17454
|
+
'2': '2xx',
|
|
17455
|
+
'3': '3xx',
|
|
17456
|
+
'4': '4xx',
|
|
17457
|
+
'5': '5xx',
|
|
17458
|
+
'6': 'success'
|
|
17459
|
+
}
|
|
17460
|
+
status_filter = status_filter_map[choice]
|
|
17461
|
+
|
|
17462
|
+
if choice == '2' and not any(200 <= s < 300 for s in status_details.keys()):
|
|
17463
|
+
print("⚠️ No 2xx responses found!")
|
|
17464
|
+
continue
|
|
17465
|
+
elif choice == '3' and not any(300 <= s < 400 for s in status_details.keys()):
|
|
17466
|
+
print("⚠️ No 3xx responses found!")
|
|
17467
|
+
continue
|
|
17468
|
+
elif choice == '4' and not any(400 <= s < 500 for s in status_details.keys()):
|
|
17469
|
+
print("⚠️ No 4xx responses found!")
|
|
17470
|
+
continue
|
|
17471
|
+
elif choice == '5' and not any(500 <= s < 600 for s in status_details.keys()):
|
|
17472
|
+
print("⚠️ No 5xx responses found!")
|
|
17473
|
+
continue
|
|
17474
|
+
elif choice == '6' and not any(200 <= s < 400 for s in status_details.keys()):
|
|
17475
|
+
print("⚠️ No success responses found!")
|
|
17476
|
+
continue
|
|
17477
|
+
elif choice == '':
|
|
17478
|
+
print("⚠️ Back to main menu.")
|
|
17479
|
+
continue
|
|
17480
|
+
|
|
17481
|
+
return status_filter
|
|
17482
|
+
else:
|
|
17483
|
+
print("❌ Invalid option!")
|
|
17484
|
+
|
|
17485
|
+
def save_with_filter(results, filename, status_filter):
|
|
17486
|
+
if not results:
|
|
17487
|
+
return 0
|
|
17488
|
+
|
|
17489
|
+
if status_filter == 'all':
|
|
17490
|
+
filtered_results = results
|
|
17491
|
+
elif status_filter == '2xx':
|
|
17492
|
+
filtered_results = [r for r in results if 200 <= r['status_code'] < 300]
|
|
17493
|
+
elif status_filter == '3xx':
|
|
17494
|
+
filtered_results = [r for r in results if 300 <= r['status_code'] < 400]
|
|
17495
|
+
elif status_filter == '4xx':
|
|
17496
|
+
filtered_results = [r for r in results if 400 <= r['status_code'] < 500]
|
|
17497
|
+
elif status_filter == '5xx':
|
|
17498
|
+
filtered_results = [r for r in results if 500 <= r['status_code'] < 600]
|
|
17499
|
+
elif status_filter == 'success':
|
|
17500
|
+
filtered_results = [r for r in results if 200 <= r['status_code'] < 400]
|
|
17501
|
+
else:
|
|
17502
|
+
filtered_results = results
|
|
17503
|
+
|
|
17504
|
+
if not filtered_results:
|
|
17505
|
+
print(f"❌ No results matching filter: {status_filter}")
|
|
17506
|
+
return 0
|
|
17507
|
+
|
|
17508
|
+
return PayloadManager.save_results(filtered_results, filename, status_filter)
|
|
16958
17509
|
|
|
17510
|
+
def xhttp_payload_validator():
|
|
17511
|
+
os.system('clear' if os.name == 'posix' else 'cls')
|
|
17512
|
+
|
|
17513
|
+
banner = """
|
|
17514
|
+
╔══════════════════════════════════════════════════════════╗
|
|
17515
|
+
║ XHTTP TUNNEL PAYLOAD TESTER ║
|
|
17516
|
+
║ Generate HTTP Injector Configs from Live Tests ║
|
|
17517
|
+
╚══════════════════════════════════════════════════════════╝
|
|
17518
|
+
"""
|
|
17519
|
+
print(banner)
|
|
17520
|
+
|
|
17521
|
+
config = get_user_input()
|
|
17522
|
+
if not config:
|
|
17523
|
+
return
|
|
17524
|
+
|
|
17525
|
+
estimated_tests = len(config.payloads) * len(config.targets) * max(len(config.proxies), 1)
|
|
17526
|
+
|
|
17527
|
+
print(f"\n{'='*60}")
|
|
17528
|
+
print("READY TO TEST")
|
|
17529
|
+
print(f"{'='*60}")
|
|
17530
|
+
print(f"Payloads: {len(config.payloads)}")
|
|
17531
|
+
print(f"Targets: {len(config.targets)}")
|
|
17532
|
+
print(f"Proxies: {len(config.proxies) if config.proxies else 'Direct'}")
|
|
17533
|
+
print(f"Estimated Tests: {estimated_tests}")
|
|
17534
|
+
print(f"{'='*60}")
|
|
17535
|
+
|
|
17536
|
+
if input("\nStart testing? (y/n): ").strip().lower() != 'y':
|
|
17537
|
+
print("❌ Cancelled")
|
|
17538
|
+
return
|
|
17539
|
+
|
|
17540
|
+
start_time = time.time()
|
|
17541
|
+
tester = NetworkTester(config)
|
|
17542
|
+
working_configs = tester.run_tests()
|
|
17543
|
+
|
|
17544
|
+
if working_configs:
|
|
17545
|
+
status_filter = show_status_distribution(working_configs)
|
|
17546
|
+
|
|
17547
|
+
output_file = input("\n💾 Output file (default=working_configs.txt): ").strip()
|
|
17548
|
+
if output_file:
|
|
17549
|
+
if not output_file.endswith('.txt'):
|
|
17550
|
+
output_file += '.txt'
|
|
17551
|
+
config.output_file = output_file
|
|
17552
|
+
|
|
17553
|
+
saved_count = save_with_filter(working_configs, config.output_file, status_filter)
|
|
17554
|
+
elapsed = time.time() - start_time
|
|
17555
|
+
|
|
17556
|
+
if saved_count > 0:
|
|
17557
|
+
print(f"\n{'='*60}")
|
|
17558
|
+
print("📦 RESULTS SAVED")
|
|
17559
|
+
print(f"{'='*60}")
|
|
17560
|
+
print(f"✅ Generated {saved_count} configs")
|
|
17561
|
+
print(f"📁 Output: {config.output_file}")
|
|
17562
|
+
print(f"⏱️ Time: {elapsed:.1f}s")
|
|
17563
|
+
print(f"{'='*60}")
|
|
17564
|
+
else:
|
|
17565
|
+
print("\n❌ No working configurations found!")
|
|
16959
17566
|
|
|
16960
|
-
|
|
17567
|
+
xhttp_payload_validator()
|
|
16961
17568
|
|
|
16962
17569
|
|
|
16963
17570
|
def x_menu():
|
|
@@ -17058,7 +17665,7 @@ def banner():
|
|
|
17058
17665
|
MAGENTA + "██╔═══╝ ██╔══██╗██║ ██║" + LIME + "user should understand that useage of this script may be" + ENDC,
|
|
17059
17666
|
MAGENTA + "██║ ██║ ██║╚██████╔╝" + LIME + "concidered an attack on a data network, and may violate terms" + ENDC,
|
|
17060
17667
|
MAGENTA + "╚═╝ ╚═╝ ╚═╝ ╚═════╝" + LIME + "of service, use on your own network or get permission first" + ENDC,
|
|
17061
|
-
PURPLE + "script_version@ 1.3.
|
|
17668
|
+
PURPLE + "script_version@ 1.3.2 ®" + ENDC,
|
|
17062
17669
|
ORANGE + "All rights reserved 2022-2026 ♛: ®" + ENDC,
|
|
17063
17670
|
MAGENTA + "In Collaboration whit Ayan Rajpoot ® " + ENDC,
|
|
17064
17671
|
BLUE + "Support: https://t.me/BugScanX 💬" + ENDC,
|
bhp_pro-1.3.0.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
bhp_pro.py,sha256=WJ2J94cH1-qKf3AynRr8Q4t0GhzNxs2VunaQip0T7zA,748620
|
|
2
|
-
bhp_pro-1.3.0.dist-info/METADATA,sha256=t7JildGhotdLKQG8WAcZWV-rFl9R6FhukOP2UhCNEMs,600
|
|
3
|
-
bhp_pro-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
-
bhp_pro-1.3.0.dist-info/entry_points.txt,sha256=Yn3HpraGX3lXX4FPq3Gm-lHh3SwQA-5rtgPWNWMFXkw,41
|
|
5
|
-
bhp_pro-1.3.0.dist-info/top_level.txt,sha256=1xjbIaVM77UJz9Tsi1JjILgE0YDG7iLhY6KSMNEi9zM,8
|
|
6
|
-
bhp_pro-1.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|