PasteFS logoPasteFS
guest on 13 Jun, 2019
Raw Data
  1. from bs4 import BeautifulSoup
  2. from time import sleep
  3. import requests
  4. import configparser
  5. import webbrowser
  6. import random
  7. import base64
  8. import uuid
  9. import glob
  10. import sys
  11. import re
  12. import os
  13. import requests.sessions
  14. from typing import Dict, List, Pattern, TextIO, Union
  15. posts = 0  # type: int
  16. config = configparser.ConfigParser()  # type: configparser.ConfigParser
  17. if not os.path.exists("config.ini"):
  18.     config["Posting defaults"] = {
  19.         "Name": "",
  20.         "Email": "",
  21.         "Subject": "",
  22.         "Comment": "This is an example. Multiline comments are supported, but each newline after the first that contains text must be preceded by a single space. If a line will not contain text, use a single newline, then on your next newline, follow the single-space rule. You may use variables in this comment which will choose from a list of substitutions that you define. Variables take the form of #var1& and #var2& and should be defined in the [Substitutions] section of config.ini",
  23.         "Password": "",
  24.         "Delay": "0",
  25.     }
  26.     config["Identification"] = {
  27.         "User-Agent": "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0",
  28.         "Proxy": "True",
  29.     }
  30.     config["File upload"] = {
  31.         "Directory": os.path.dirname(os.path.realpath(__file__)),
  32.         "Extensions": "jpg,jpeg,png,gif,webm,mp4",
  33.         "UUID": "True",
  34.         "Evade": "True",
  35.     }
  36.     config["Substitutions"] = {
  37.         "var1": "comment,separated,substitutions",
  38.         "var2": "will,be,used",
  39.     }
  40.     with open("config.ini", "w") as configfile:
  41.         config.write(configfile)
  42.     sys.exit(
  43.         "config.ini was not found, it has been created for you. Please customize and re-run this program."
  44.     )
  45. else:
  46.     print("config.ini found! Using provided configuration.")
  47.     config.read("config.ini")
  48. s = requests.Session()  # type: requests.sessions.Session
  49. s.headers["User-Agent"] = config["Identification"]["User-Agent"]
  50. s.headers["Referer"] = "https://8ch.net/"
  51. if not os.path.exists("boards.txt"):
  52.     print("Did not find boards.txt! Creating...")
  53.     j = s.get("https://8ch.net/boards.json").json()  # type: List[Dict[str, str]]
  54.     with open("boards.txt", "w") as f:
  55.         for board in j:
  56.             f.write("%s\n" % board["uri"])
  57.     print("boards.txt created!")
  58. else:
  59.     print("boards.txt found! Using existing data.")
  60.     BOARDS = [line.strip() for line in open("boards.txt", "r")]  # type: List[str]
  61. USE_PROXY = config["Identification"]["Proxy"] == "True"  # type: bool
  62. USE_UUID = config["File upload"]["UUID"] == "True"  # type: bool
  63. TRY_MD5_EVADE = config["File upload"]["Evade"] == "True"  # type: bool
  64. subbed = {}  # type: Dict[str, str]
  65. SUBS = {}  # type: Dict[str, List[str]]
  66. for substitution in config["Substitutions"]:
  67.     SUBS[substitution] = config["Substitutions"][substitution].split(",")
  68. NAME = config["Posting defaults"]["Name"]  # type: str
  69. EMAIL = config["Posting defaults"]["Email"]  # type: str
  70. SUBJECT = config["Posting defaults"]["Subject"]  # type: str
  71. BODY = config["Posting defaults"]["Comment"]  # type: str
  72. PASSWORD = config["Posting defaults"]["Password"]  # type: str
  73. DIRECTORY = config["File upload"]["Directory"]  # type: str
  74. EXTENSIONS = [
  75.     ".%s" % ext for ext in config["File upload"]["Extensions"].split(",")
  76. ]  # type: List[str]
  77. files = []  # type: List[str]
  78. for ext in EXTENSIONS:
  79.     files += glob.glob("%s/*%s" % (DIRECTORY, ext))
  80. if USE_PROXY:
  81.     if not os.path.exists("proxies.txt"):
  82.         with open("proxies.txt", "w") as f:
  83.             f.write("protocol://addresss:port\n")
  84.             f.write("socks5://127.0.0.1:9050\n")
  85.         sys.exit(
  86.             "Did not find proxies.txt! An example file will be created for you. Please customize and re-run this program."
  87.         )
  88.     else:
  89.         print("Using information from proxies.txt")
  90.         with open("proxies.txt", "r") as f:
  91.             PATTERN = r"(https?|socks5)://.+:\d+"  # type: str
  92.             r = re.compile(PATTERN)  # type: Pattern
  93.             PROXIES = [line.strip() for line in f if r.match(line)]  # type: List[str]
  94. else:
  95.     print("No proxy will be used.")
  96. def new_proxy() -> None:
  97.     p = random.choice(PROXIES)  # type: Union[str, Dict[str, str]]
  98.     p = {"http": p, "https": p}
  99.     s.proxies = p
  100. def get_captcha() -> None:
  101.     try:
  102.         soup = BeautifulSoup(
  103.             s.get("https://8ch.net/dnsbls_bypass.php").content, "html.parser"
  104.         )  # type: BeautifulSoup
  105.     except requests.exceptions.ProxyError as e:
  106.         print("Encountered an exception trying GET /dnsbls_bypass.php")
  107.         print("Exception: %s" % e)
  108.     image = soup.select("image")[0].get("src")[22:]  # type: str
  109.     with open("captcha_image.png", "wb") as f:
  110.         f.write(base64.b64decode(image))
  111.     # print("This line is directly after where the captcha image gets saved. If you see this, the file should exist.")
  112.     webbrowser.open("file://%s" % os.path.realpath("captcha_image.png"))
  113.     answer = input("Enter captcha text: ")  # type: str
  114.     captcha_cookie = soup.select("input.captcha_cookie")[0].get("value")  # type: str
  115.     try:
  116.         r = s.post(
  117.             "https://8ch.net/dnsbls_bypass.php",
  118.             data={"captcha_text": answer, "captcha_cookie": captcha_cookie},
  119.         )  # type: requests.models.Response
  120.     except requests.exceptions.ProxyError as e:
  121.         print(
  122.             "Encountered an exception trying POST challenge data to /dnsbls_bypass.php"
  123.         )
  124.         print("Exception: %s" % e)
  125.     if "You may now go back and make your post." not in r.text:
  126.         get_captcha()
  127.     else:
  128.         print("Captcha successfully solved for this IP address!")
  129.         global posts
  130.         posts = 50
  131.     # os.remove("captcha_image.png")
  132. def try_post() -> None:
  133.     NUM_NULLS = b"\0" * random.randint(1, 1000)  # type: bytes
  134.     if TRY_MD5_EVADE and USE_UUID:
  135.         print(
  136.             s.post(
  137.                 "https://8ch.net/post.php",
  138.                 data=data,
  139.                 files={
  140.                     "file": (
  141.                         "%s%s" % (uuid.uuid4(), e),
  142.                         open(f, "rb").read() + NUM_NULLS,
  143.                     )
  144.                 },
  145.             ).text
  146.         )
  147.     elif TRY_MD5_EVADE and not USE_UUID:
  148.         print(
  149.             s.post(
  150.                 "https://8ch.net/post.php",
  151.                 data=data,
  152.                 files={"file": open(f, "rb").read() + NUM_NULLS},
  153.             ).text
  154.         )
  155.     elif not TRY_MD5_EVADE and USE_UUID:
  156.         print(
  157.             s.post(
  158.                 "https://8ch.net/post.php",
  159.                 data=data,
  160.                 files={"file": ("%s%s" % (uuid.uuid4(), e), open(f, "rb"))},
  161.             ).text
  162.         )
  163.     elif not TRY_MD5_EVADE and not USE_UUID:
  164.         print(
  165.             s.post(
  166.                 "https://8ch.net/post.php", data=data, files={"file": open(f, "rb")}
  167.             ).text
  168.         )
  169.     sleep(float(config["Posting defaults"]["Delay"]))
  170. orig_data = {
  171.     "page": "1",
  172.     "name": NAME,
  173.     "email": EMAIL,
  174.     "subject": SUBJECT,
  175.     "body": BODY,
  176.     "password": PASSWORD,
  177.     "json_response": "1",
  178.     "post": "New Thread",
  179. }  # type: Dict[str, str]
  180. data = {}  # type: Dict[str, str]
  181. def do_substitutions() -> None:
  182.     for key in orig_data:
  183.         data[key] = orig_data[key]
  184.         subbed[key] = orig_data[key]
  185.         for substitution in config["Substitutions"]:
  186.             subbed[key] = subbed[key].replace(
  187.                 "#%s&" % substitution, random.choice(SUBS[substitution])
  188.             )
  189.     data["body"] = subbed["body"]
  190.     data["subject"] = subbed["subject"]
  191.     data["name"] = subbed["name"]
  192.     data["email"] = subbed["email"]
  193.     data["password"] = subbed["password"]
  194. if USE_PROXY:
  195.     new_proxy()
  196. try:
  197.     get_captcha()
  198. except ConnectionRefusedError:
  199.     # this should probably not happen since get_captcha() is handling ProxyError exceptions, but just to be safe
  200.     print("Connection was refused by proxy server. Trying new one.")
  201.     print("Bad proxy used by session object: %s" % s.proxies)
  202.     new_proxy()
  203. for board in BOARDS:
  204.     data["board"] = board
  205.     do_substitutions()
  206.     f = random.choice(files)  # type: Union[str, TextIO]
  207.     e = os.path.splitext(f)[1]  # type: str
  208.     if posts > 0:
  209.         try_post()
  210.     else:
  211.         if USE_PROXY:
  212.             new_proxy()
  213.         get_captcha()
  214.     posts -= 1