⚠️ This article is only for educational and OSINT investigation purposes. Author not responsible for any misuse of it!

TBOT_OSINT Python Script Execution

None
None

Subdomain takeover ❌ Telegram Bot takeover ✅

None
None

🪛 Python OSINT Script for Telegram BOT Hunting

import asyncio
import aiohttp
import json
import webbrowser
import sys
from aiohttp import web

# --- RUNTIME INPUTS ---
print("TELEGRAM BOT Script by Legion Hunter")
BOT_TOKEN = input("Enter BOT_TOKEN: ").strip() or None
CHAT_ID = input("Enter TARGET CHAT_ID (Enter for null): ").strip() or None
USER_ID = input("Enter TARGET USER_ID (Enter for null): ").strip() or None
PORT = 8080

if not BOT_TOKEN:
    print("FATAL ERROR: BOT_TOKEN is required.")
    sys.exit(1)

# --- WEB INTERFACE (MODERN DARK THEME) ---
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Bot OSINT Investigator</title>
    <style>
        :root {
            --bg: #0d1117;
            --card-bg: #161b22;
            --border: #30363d;
            --text-main: #c9d1d9;
            --text-dim: #8b949e;
            --accent: #58a6ff;
            --success: #238636;
            --row-hover: #1c2128;
        }
        body {
            background-color: var(--bg);
            color: var(--text-main);
            font-family: -apple-system, sans-serif;
            margin: 0;
            padding: 40px;
        }
        .header { margin-bottom: 30px; border-bottom: 1px solid var(--border); padding-bottom: 20px; }
        .grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(480px, 1fr));
            gap: 20px;
        }
        .card {
            background: var(--card-bg);
            border: 1px solid var(--border);
            border-radius: 8px;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }
        .card-header {
            padding: 12px 20px;
            background: rgba(255, 255, 255, 0.02);
            border-bottom: 1px solid var(--border);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .method-title { font-size: 11px; font-weight: 800; color: var(--accent); letter-spacing: 1px; }
        
        table { width: 100%; border-collapse: collapse; font-size: 13px; }
        th { text-align: left; padding: 10px 20px; color: var(--text-dim); border-bottom: 1px solid var(--border); font-weight: 500; }
        td { padding: 8px 20px; border-bottom: 1px solid var(--border); }
        tr:hover { background: var(--row-hover); }

        .list-container { padding: 15px; }
        .list-entry { 
            background: #0d1117; 
            border: 1px solid var(--border); 
            border-radius: 4px; 
            padding: 8px; 
            margin-bottom: 5px; 
            font-size: 12px;
        }
        .status-badge { font-size: 10px; font-weight: bold; color: var(--success); }
    </style>
</head>
<body>
    <div class="header">
        <h1>Telegram Bot OSINT Investigator by Legion Hunter</h1>
        <p style="color: var(--text-dim)">Intelligence Analysis Dashboard</p>
    </div>
    <div class="grid" id="main-grid"></div>

    <script>
        function formatData(val) {
            if (val === true) return '<span style="color:var(--success)">TRUE</span>';
            if (val === false) return '<span style="color:var(--text-dim)">FALSE</span>';
            if (val === null || val === "" || (Array.isArray(val) && val.length === 0)) return '<i>null</i>';
            if (typeof val === 'object') return '<span style="font-family:monospace; font-size:10px;">OBJECT DATA</span>';
            return val;
        }

        function renderArray(arr) {
            return `<div class="list-container">` + arr.map(item => {
                const label = item.emoji || item.user?.username || item.status || "Record";
                return `<div class="list-entry"><strong>${label}</strong>: ${JSON.stringify(item).substring(0,60)}...</div>`;
            }).join('') + `</div>`;
        }

        async function loadResults() {
            const r = await fetch('/api/data');
            const results = await r.json();
            const grid = document.getElementById('main-grid');

            Object.entries(results).forEach(([method, data]) => {
                const card = document.createElement('div');
                card.className = 'card';
                const body = data.result;

                let htmlContent = '';
                if (Array.isArray(body)) {
                    htmlContent = renderArray(body);
                } else if (typeof body === 'object' && body !== null) {
                    let rows = Object.entries(body).map(([k, v]) => `
                        <tr>
                            <td style="color:var(--text-dim); width:40%; font-family:monospace;">${k}</td>
                            <td>${formatData(v)}</td>
                        </tr>
                    `).join('');
                    htmlContent = `<table><thead><tr><th>Property</th><th>Value</th></tr></thead><tbody>${rows}</tbody></table>`;
                } else {
                    htmlContent = `<div style="padding:20px; color:var(--text-dim)">No data found or target unavailable.</div>`;
                }

                card.innerHTML = `
                    <div class="card-header">
                        <span class="method-title">${method.toUpperCase()}</span>
                        <span class="status-badge">COMPLETED</span>
                    </div>
                    ${htmlContent}
                `;
                grid.appendChild(card);
            });
        }
        loadResults();
    </script>
</body>
</html>
"""

# --- BACKEND LOGIC ---
api_cache = {}

async def fetch_api(session, method, params=None):
    url = f"https://api.telegram.org/bot{BOT_TOKEN}/{method}"
    try:
        async with session.get(url, params=params, timeout=10) as resp:
            data = await resp.json()
            if data.get("ok"):
                api_cache[method] = data
    except Exception:
        pass

async def run_investigation():
    connector = aiohttp.TCPConnector(ssl=False)
    async with aiohttp.ClientSession(connector=connector) as session:
        # Standard Bot OSINT
        tasks = [
            fetch_api(session, "getMe"),
            fetch_api(session, "getWebhookInfo"),
            fetch_api(session, "getMyCommands"),
            fetch_api(session, "getMyName"),
            fetch_api(session, "getMyDescription"),
            fetch_api(session, "getMyShortDescription"),
            fetch_api(session, "getMyDefaultAdministratorRights"),
            fetch_api(session, "getChatMenuButton"),
            fetch_api(session, "getForumTopicIconStickers"),
            fetch_api(session, "getUpdates")
        ]
        
        # Target Specific OSINT
        if CHAT_ID:
            tasks.append(fetch_api(session, "getChat", {"chat_id": CHAT_ID}))
            tasks.append(fetch_api(session, "getChatAdministrators", {"chat_id": CHAT_ID}))
            tasks.append(fetch_api(session, "getChatMemberCount", {"chat_id": CHAT_ID}))
        
        if USER_ID:
            tasks.append(fetch_api(session, "getUserProfilePhotos", {"user_id": USER_ID}))
            if CHAT_ID:
                tasks.append(fetch_api(session, "getChatMember", {"chat_id": CHAT_ID, "user_id": USER_ID}))

        await asyncio.gather(*tasks)

async def start_server():
    await run_investigation()
    app = web.Application()
    app.router.add_get('/', lambda r: web.Response(text=HTML_TEMPLATE, content_type='text/html'))
    app.router.add_get('/api/data', lambda r: web.json_response(api_cache))
    
    runner = web.AppRunner(app)
    await runner.setup()
    await web.TCPSite(runner, 'localhost', PORT).start()
    
    print(f"OSINT Dashboard live at http://localhost:{PORT}")
    webbrowser.open(f"http://localhost:{PORT}")
    while True: await asyncio.sleep(3600)

if __name__ == "__main__":
    try:
        asyncio.run(start_server())
    except KeyboardInterrupt:
        print("Investigation halted.")

This was only demo, you can customize yourself & add more endpoints to retrieve information.

Refer API docs at : core.telegram.org/bots/api

Read the documentation carefully for more juicy stuff and what can be done if XYZ permission present associated with the bot.

🔍 FOFA Dorking

Site: en.fofa.info

New to FOFA Dorking ? Check complete series from beginning

Time to get some juicy data for weekend hacks :)

Just this single fofa dork, that's it!

body="api.telegram.org" && body="BOT_TOKEN" && body="CHAT_ID"
None
en.fofa.info
None

You can test ethically by creating your own BOT

None
None

This is the token that shouldn't be hardcoded / exposed in the client side.

Nuclei Template 😈

id: telegram-bot-token-leak

info:
  name: Telegram Bot Token Leak
  author: Legion Hunter
  severity: high
  description: |
    Detects exposed Telegram Bot API tokens, Chat IDs, and endpoint 
    references to api.telegram.org. This information can be used 
    to gain unauthorized access to bot functions and message history.
  tags: osint, leak, telegram, token, exposure

http:
  - method: GET
    path:
      - "{{BaseURL}}"
      - "{{BaseURL}}/js/app.js"
      - "{{BaseURL}}/main.js"
      - "{{BaseURL}}/.env"
      - "{{BaseURL}}/config.json"
      - "{{BaseURL}}/.git/config"

    extractors:
      - type: regex
        name: bot_token
        part: body
        regex:
          - "[0-9]{8,10}:[a-zA-Z0-9_-]{35}"

      - type: regex
        name: chat_id
        part: body
        regex:
          - 'chat_id["'']?\s*[:=]\s*["'']?(-?\d+)'

    matchers-condition: or
    matchers:
      - type: word
        words:
          - "api.telegram.org"
        part: body

      - type: regex
        name: token_found
        regex:
          - "[0-9]{8,10}:[a-zA-Z0-9_-]{35}"
        part: body

💻 Run on all IPs

  • Real attackers have setup bots that run 24/7 to continously scan VPS provider IP subnet/range.
  • It can be from simple SSH brute force to finding leak stuff like this.
None

🤘 Good luck, will meet again

None
GIF from TENOR