Now we have working paste sending to 4 places?. PrivateBin client is functional, we can use any PrivateBin with it!

This commit is contained in:
kalzu rekku 2024-07-28 11:12:44 +03:00
parent 9977b834e2
commit fa369d636f
4 changed files with 477 additions and 39 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__/
*.txt

423
cpaste.py Normal file
View File

@ -0,0 +1,423 @@
#!/usr/bin/env python3
#
#######################################################################
#
# A script to paste to https://cpaste.org/
#
# Copyright (c) 2013-2019 Andreas Schneider <asn@samba.org>
# Copyright (c) 2013 Alexander Bokovoy <ab@samba.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#######################################################################
#
# Requires: python3-requests
# Requires: python3-cryptography
#
# Optionally requires: python-Pygments
#
import os
import sys
import json
import base64
import zlib
import requests
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
from optparse import OptionParser
from mimetypes import guess_type
try:
from pygments.lexers import guess_lexer, guess_lexer_for_filename
from pygments.util import ClassNotFound
guess_lang = True
except ImportError:
guess_lang = False
def base58_encode(v: bytes):
# 58 char alphabet
alphabet = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
alphabet_len = len(alphabet)
nPad = len(v)
v = v.lstrip(b'\0')
nPad -= len(v)
x = 0
for (i, c) in enumerate(v[::-1]):
if isinstance(c, str):
c = ord(c)
x += c << (8 * i)
string = b''
while x:
x, idx = divmod(x, alphabet_len)
string = alphabet[idx:idx+1] + string
return (alphabet[0:1] * nPad + string)
def json_encode(d):
return json.dumps(d, separators=(',', ':')).encode('utf-8')
#
# The encryption format is described here:
# https://github.com/PrivateBin/PrivateBin/wiki/Encryption-format
#
def privatebin_encrypt(paste_passphrase,
paste_password,
paste_plaintext,
paste_formatter,
paste_attachment_name,
paste_attachment,
paste_compress,
paste_burn,
paste_opendicussion):
if paste_password:
paste_passphrase += bytes(paste_password, 'utf-8')
# PBKDF
kdf_salt = bytes(os.urandom(8))
kdf_iterations = 100000
kdf_keysize = 256 # size of resulting kdf_key
backend = default_backend()
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),
length=int(kdf_keysize / 8), # 256bit
salt=kdf_salt,
iterations=kdf_iterations,
backend=backend)
kdf_key = kdf.derive(paste_passphrase)
# AES-GCM
adata_size = 128
cipher_iv = bytes(os.urandom(int(adata_size / 8)))
cipher_algo = "aes"
cipher_mode = "gcm"
compression_type = "none"
if paste_compress:
compression_type = "zlib"
# compress plaintext
paste_data = {'paste': paste_plaintext}
if paste_attachment_name and paste_attachment:
paste_data['attachment'] = paste_attachment
paste_data['attachment_name'] = paste_attachment_name
print(paste_attachment_name)
print(paste_attachment)
if paste_compress:
zobj = zlib.compressobj(wbits=-zlib.MAX_WBITS)
paste_blob = zobj.compress(json_encode(paste_data)) + zobj.flush()
else:
paste_blob = json_encode(paste_data)
# Associated data to authenticate
paste_adata = [
[
base64.b64encode(cipher_iv).decode("utf-8"),
base64.b64encode(kdf_salt).decode("utf-8"),
kdf_iterations,
kdf_keysize,
adata_size,
cipher_algo,
cipher_mode,
compression_type,
],
paste_formatter,
int(paste_opendicussion),
int(paste_burn),
]
paste_adata_json = json_encode(paste_adata)
aesgcm = AESGCM(kdf_key)
ciphertext = aesgcm.encrypt(cipher_iv, paste_blob, paste_adata_json)
# Validate
# aesgcm.decrypt(cipher_iv, ciphertext, paste_adata_json)
paste_ciphertext = base64.b64encode(ciphertext).decode("utf-8")
return paste_adata, paste_ciphertext
def privatebin_send(paste_url,
paste_password,
paste_plaintext,
paste_formatter,
paste_attachment_name,
paste_attachment,
paste_compress,
paste_burn,
paste_opendicussion,
paste_expire):
paste_passphrase = bytes(os.urandom(32))
paste_adata, paste_ciphertext = privatebin_encrypt(paste_passphrase,
paste_password,
paste_plaintext,
paste_formatter,
paste_attachment_name,
paste_attachment,
paste_compress,
paste_burn,
paste_opendicussion)
# json payload for the post API
# https://github.com/PrivateBin/PrivateBin/wiki/API
payload = {
"v": 2,
"adata": paste_adata,
"ct": paste_ciphertext,
"meta": {
"expire": paste_expire,
}
}
# http content type
headers = {'X-Requested-With': 'JSONHttpRequest'}
r = requests.post(paste_url,
data=json_encode(payload),
headers=headers)
r.raise_for_status()
try:
result = r.json()
except:
print('Oops, error: %s' % (r.text))
sys.exit(1)
paste_status = result['status']
if paste_status:
paste_message = result['message']
print("Oops, error: %s" % paste_message)
sys.exit(1)
paste_id = result['id']
paste_url_id = result['url']
paste_deletetoken = result['deletetoken']
print('Delete paste: %s/?pasteid=%s&deletetoken=%s' %
(paste_url, paste_id, paste_deletetoken))
# print('')
# print('### Paste (%s): %s%s#%s' %
# (paste_formatter,
# paste_url,
# paste_url_id,
# base58_encode(paste_passphrase).decode('utf-8')))
return paste_url, paste_url_id, base58_encode(paste_passphrase).decode('utf-8'), paste_deletetoken
def guess_lang_formatter(paste_plaintext, paste_filename=None):
paste_formatter = 'plaintext'
# Map numpy to python because the numpy lexer gives false positives
# when guessing.
lexer_lang_map = {'numpy': 'python'}
# If we have a filename, try guessing using the more reliable
# guess_lexer_for_filename function.
# If that fails, try the guess_lexer function on the code.
lang = None
if paste_filename:
try:
lang = guess_lexer_for_filename(paste_filename,
paste_plaintext).name.lower()
except ClassNotFound:
print("No guess by filename")
pass
else:
try:
lang = guess_lexer(paste_plaintext).name.lower()
except ClassNotFound:
pass
if lang:
if lang == 'markdown':
paste_formatter = 'markdown'
if lang != 'text only':
paste_formatter = 'syntaxhighlighting'
return paste_formatter
def main():
parser = OptionParser()
parser.add_option("-f", "--file", dest="filename",
help="Read from a file instead of stdin",
metavar="FILE")
parser.add_option("-p", "--password", dest="password",
help="Create a password protected paste",
metavar="PASSWORD")
parser.add_option("-e", "--expire",
action="store", dest="expire", default="1day",
choices=["5min",
"10min",
"1hour",
"1day",
"1week",
"1month",
"1year",
"never"],
help="Expiration time of the paste (default: 1day)")
parser.add_option("-s", "--sourcecode",
action="store_true", dest="source", default=False,
help="Use source code highlighting")
parser.add_option("-m", "--markdown",
action="store_true", dest="markdown", default=False,
help="Parse paste as markdown")
parser.add_option("-b", "--burn",
action="store_true", dest="burn", default=False,
help="Burn paste after reading")
parser.add_option("-o", "--opendiscussion",
action="store_true", dest="opendiscussion",
default=False,
help="Allow discussion for the paste")
parser.add_option("-a", "--attachment", dest="attachment",
help="Specify path to a file to attachment to the paste",
metavar="FILE")
(options, args) = parser.parse_args()
paste_url = 'https://cpaste.org'
paste_formatter = 'plaintext'
paste_compress = True
paste_expire = '1day'
paste_opendiscussion = 0
paste_burn = 0
paste_password = None
paste_attachment_name = None
paste_attachment = None
if options.filename:
f = open(options.filename)
if not f:
print("Oops, could not open file!")
paste_plaintext = f.read()
f.close()
else:
paste_plaintext = sys.stdin.read()
if not paste_plaintext:
print("Oops, we have no data")
sys.exit(1)
if options.burn:
paste_burn = 1
if options.opendiscussion:
paste_opendiscussion = 1
if options.source:
paste_formatter = 'syntaxhighlighting'
elif options.markdown:
paste_formatter = 'markdown'
elif guess_lang:
paste_formatter = guess_lang_formatter(paste_plaintext,
options.filename)
if options.expire:
paste_expire = options.expire
if options.password:
paste_password = options.password
if options.attachment:
paste_attachment_name = os.path.basename(options.attachment)
mime = guess_type(options.attachment, strict=False)[0]
if not mime:
mime = 'application/octet-stream'
f = open(options.attachment, mode='rb')
if not f:
print("Oops, could not open file for attachment!")
data = f.read()
f.close()
paste_attachment = 'data:%s;base64,' % (mime)
paste_attachment += base64.b64encode(data).decode('utf-8')
print(paste_url)
print(paste_password )
print(paste_plaintext )
print(paste_formatter )
print(paste_attachment_name )
print(paste_attachment )
print(paste_compress )
print(paste_burn )
print(paste_opendiscussion )
print(paste_expire )
privatebin_send(paste_url,
paste_password,
paste_plaintext,
paste_formatter,
paste_attachment_name,
paste_attachment,
paste_compress,
paste_burn,
paste_opendiscussion,
paste_expire)
sys.exit(0)
def publish_to_cpaste(file):
#paste_url = 'https://cpaste.org'
paste_url = 'https://paste.devsite.pl'
paste_formatter = 'plaintext'
paste_compress = True
paste_expire = '1day'
paste_opendiscussion = 0
paste_burn = 0
paste_password = None
paste_attachment_name = None
paste_attachment = None
f = open(file)
if not f:
print("Oops, could not open file!")
paste_plaintext = f.read()
f.close()
paste_url, paste_id, paste_decrypt, paste_deletetoken = privatebin_send(paste_url,
paste_password,
paste_plaintext,
paste_formatter,
paste_attachment_name,
paste_attachment,
paste_compress,
paste_burn,
paste_opendiscussion,
paste_expire)
output = '%s%s#%s DELETE TOKEN: %s' % (paste_url, paste_id, paste_decrypt, paste_deletetoken)
return output
if __name__ == "__main__":
main()

View File

@ -1,5 +1,11 @@
import requests import requests
import secrets
import base64
import tempfile
import socket import socket
import os
from cpaste import publish_to_cpaste
def publish_to_termbin(content): def publish_to_termbin(content):
host = 'termbin.com' host = 'termbin.com'
@ -25,18 +31,8 @@ def publish_to_mozilla(content):
} }
response = requests.post(url, data=data) response = requests.post(url, data=data)
if response.status_code == 200: if response.status_code == 200:
return f"https://pastebin.mozilla.org/{response.text}" # Mozilla returns full link to the paste
return None return f"{response.text}"
def publish_to_paste2(content):
url = 'https://paste2.org/api/create'
data = {
'content': content,
'lang': 'text'
}
response = requests.post(url, data=data)
if response.status_code == 200:
return f"https://paste2.org/{response.text}"
return None return None
def publish_to_dpaste(content): def publish_to_dpaste(content):
@ -50,21 +46,46 @@ def publish_to_dpaste(content):
return response.text.strip() return response.text.strip()
return None return None
def publish_to_pasteee(content, api_key):
url = 'https://paste.ee/api/v1/pastes' def publish_to_pastesh(content):
HOST = 'https://paste.sh'
VERSION = 'v2'
# Generate ID
id = f"p{secrets.token_urlsafe(6)}"
# Create a temporary file
with tempfile.NamedTemporaryFile(mode='w+', delete=False) as temp_file:
temp_file_path = temp_file.name
# Write content to the temporary file
temp_file.write(content)
temp_file.flush()
try:
# Prepare headers
headers = { headers = {
'X-Auth-Token': api_key 'X-Server-Key': '',
'Content-type': f'text/vnd.paste.sh-{VERSION}'
} }
data = {
'description': 'File content', # Send the request
'sections': [{'contents': content}] with open(temp_file_path, 'rb') as file:
} response = requests.post(f"{HOST}/{id}", headers=headers, data=file)
response = requests.post(url, json=data, headers=headers)
if response.status_code == 201: if response.status_code == 200:
return response.json()['link'] return f"{HOST}/{id}"
else:
print(f"Error: Status code {response.status_code}")
print(f"Response: {response.text}")
return None return None
def publish_to_multiple_pastebins(file_path, pasteee_api_key=None): finally:
# Clean up the temporary file
os.unlink(temp_file_path)
def publish_to_multiple_pastebins(file_path):
# Read the file content # Read the file content
with open(file_path, 'r') as file: with open(file_path, 'r') as file:
file_content = file.read() file_content = file.read()
@ -81,29 +102,22 @@ def publish_to_multiple_pastebins(file_path, pasteee_api_key=None):
if mozilla_url: if mozilla_url:
results['Mozilla Pastebin'] = mozilla_url results['Mozilla Pastebin'] = mozilla_url
# Publish to Paste2
paste2_url = publish_to_paste2(file_content)
if paste2_url:
results['Paste2'] = paste2_url
# Publish to Dpaste # Publish to Dpaste
dpaste_url = publish_to_dpaste(file_content) dpaste_url = publish_to_dpaste(file_content)
if dpaste_url: if dpaste_url:
results['Dpaste'] = dpaste_url results['Dpaste'] = dpaste_url
# Publish to Paste.ee (if API key is provided) # Publish to CPaste
if pasteee_api_key: cpaste_url = publish_to_cpaste(file_path)
pasteee_url = publish_to_pasteee(file_content, pasteee_api_key) if cpaste_url:
if pasteee_url: results['CPaste'] = cpaste_url
results['Paste.ee'] = pasteee_url
return results return results
# Example usage # Example usage
file_path = 'test.txt' file_path = 'test.txt'
#pasteee_api_key = 'YOUR_PASTE_EE_API_KEY' # Optional, remove if not using
results = publish_to_multiple_pastebins(file_path) results = publish_to_multiple_pastebins(file_path)
for service, url in results.items(): for service, url in results.items():

View File

@ -1 +0,0 @@
This is a test file. Please be gentle with me. Gentlemens.