Skip to content

Instantly share code, notes, and snippets.

@BitTheByte
Last active August 22, 2023 17:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save BitTheByte/88437dbb3e751aa1dc9b66401d45ccba to your computer and use it in GitHub Desktop.
Save BitTheByte/88437dbb3e751aa1dc9b66401d45ccba to your computer and use it in GitHub Desktop.
Script to tweet like the Official Twitter Application with 2fa support
# -*- coding: utf-8 -*-
from base64 import b64decode,b64encode
from urllib.parse import quote,parse_qs,urlsplit,urlparse
from random import randint
from bs4 import BeautifulSoup
import calendar
import requests
import hashlib
import base64
import time
import hmac
import types
class Twitter(object):
def __init__(self):
self.twitter_android_secret = "Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys"
self.twitter_android_key = "3nVuSoBZnx6U4vzUxf5w"
self.session = requests.Session()
self.access_token = None
self.guest_token = None
self.challenged = False
self.challenge_type = None
self.challenge = None
self.on_challenge = None
self.xauth = None
def oauth_signature(self,request,oauth_secret):
key = bytes(self.twitter_android_secret +"&"+ oauth_secret, 'UTF-8')
message = bytes(request, 'UTF-8')
digester = hmac.new(key, message, hashlib.sha1)
signature1 = digester.digest()
signature2 = base64.urlsafe_b64encode(signature1)
signature2 = str(signature2, 'UTF-8').replace("-","+").replace("_","/")
return quote(signature2,safe='')
def signature_message(self,url,params,oauth_secret,method="POST"):
header = method + "&" + quote(url,safe='') + "&"
body = bytes()
for key,value in params.items():
body += bytes(key,"UTF-8") + b"=" + bytes(value,"UTF-8") + b"&"
body = body[:-1]
return self.oauth_signature(header + quote(body,safe=''),oauth_secret)
def xauth_login(self,username,password):
self.access_token = self.session.post("https://api.twitter.com/oauth2/token",params = {'grant_type':'client_credentials'},
headers = {
"Accept": "application/json",
"Authorization": "Basic " + b64encode((self.twitter_android_key + ":" + self.twitter_android_secret).encode("utf8")).decode("utf8")
}
).json()["access_token"]
self.guest_token = self.session.post("https://api.twitter.com/1.1/guest/activate.json",headers={"Authorization": "Bearer " + self.access_token}).json()["guest_token"]
auth = self.session.post("https://api.twitter.com/auth/1/xauth_password.json",headers={
"Authorization": "Bearer " + self.access_token,
"X-Guest-Token": self.guest_token
},
params = {
"x_auth_identifier":username,
"x_auth_password":password,
"send_error_codes":"true",
"x_auth_login_challenge":"1",
"x_auth_login_verification":"1",
"x_auth_country_code":"US",
"ui_metrics":""
}).json()
self.xauth = auth
if "login_verification_request_url" in auth.keys():
self.challenged = True
self.challenge = auth
self.challenge_type = parse_qs(urlparse(auth["login_verification_request_url"]).query)['challenge_type'][0]
return auth
def challenge_access_token(self,user_id,request_id):
oauth_nonce = ''.join([str(randint(0,9)) for n in range(31)])
oauth_timestamp = str(calendar.timegm(time.gmtime()))
return self.session.post("https://api.twitter.com/oauth/access_token",data={
"x_auth_mode":"client_auth",
"x_auth_login_verification":"1",
"x_auth_login_challenge": "1",
"send_error_codes":"true",
"login_verification_user_id": user_id,
"login_verification_request_id": request_id
},
headers = {
"Authorization":'OAuth realm="http://api.twitter.com/", oauth_version="1.0", oauth_nonce="{oauth_nonce}", oauth_timestamp="{oauth_timestamp}", oauth_signature="{oauth_signature}", oauth_consumer_key="{oauth_consumer_key}", oauth_signature_method="HMAC-SHA1"'.format(
oauth_nonce = oauth_nonce,
oauth_timestamp = oauth_timestamp,
oauth_consumer_key = self.twitter_android_key,
oauth_signature = self.signature_message(url="https://api.twitter.com/oauth/access_token",params={
"login_verification_request_id": request_id,
"login_verification_user_id": user_id,
"oauth_consumer_key":self.twitter_android_key,
"oauth_nonce": oauth_nonce,
"oauth_signature_method":"HMAC-SHA1",
"oauth_timestamp": oauth_timestamp,
"oauth_version":"1.0",
"send_error_codes":"true",
"x_auth_login_challenge":"1",
"x_auth_login_verification":"1",
"x_auth_mode":"client_auth",
},oauth_secret="")
),
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
}).json()
def solve_challenge(self,challenge,answer):
self.has_challenge = True
challenge_url = challenge["login_verification_request_url"]
request_id = challenge["login_verification_request_id"]
response = self.session.get(challenge_url)
soup = BeautifulSoup(response.content, "lxml")
authenticity_token = soup.select_one("input[name=authenticity_token]")["value"]
challenge_id = soup.select_one("input[name=challenge_id]")["value"]
user_id = soup.select_one("input[name=user_id]")["value"]
self.challenge_type = soup.select_one("input[name=challenge_type]")["value"]
data = {
'authenticity_token':authenticity_token,
'challenge_id':challenge_id,
'user_id':user_id,
'challenge_type':self.challenge_type,
'platform':'mobile',
'redirect_after_login':'',
'remember_me':'true',
'challenge_response':answer,
}
self.session.post("https://twitter.com/account/login_challenge",data=data)
twitter_session = self.challenge_access_token(user_id, request_id)
self.xauth = twitter_session
return twitter_session
def auth_headers(self,oauth_nonce,oauth_timestamp,oauth_signature,skip_token=False):
return {
"Authorization": 'OAuth realm="http://api.twitter.com/", oauth_version="1.0", oauth_token="{oauth_token}", oauth_nonce="{oauth_nonce}", oauth_timestamp="{oauth_timestamp}", oauth_signature="{oauth_signature}", oauth_consumer_key="{oauth_consumer_key}", oauth_signature_method="{oauth_signature_method}"'.format(
oauth_token = self.xauth["oauth_token"] if not skip_token else "",
oauth_nonce = oauth_nonce,
oauth_timestamp = oauth_timestamp,
oauth_signature = oauth_signature,
oauth_consumer_key = self.twitter_android_key,
oauth_signature_method = "HMAC-SHA1"
),
"User-Agent": "TwitterAndroid/8.40.0-release.02 (18400002-r-2) Nexus 7/7.0 (asus;Nexus 7;google;nakasi;0;;0;2012)",
"X-Twitter-Active-User": "yes",
"X-Twitter-API-Version": "5",
"X-Twitter-Client": "TwitterAndroid",
"X-Twitter-Client-Language": "en-US",
"Accept": "application/json",
}
def tweet(self, text):
api_statuses = "https://api.twitter.com/1.1/statuses/update.json"
oauth_nonce = ''.join([str(randint(0,9)) for n in range(31)])
oauth_timestamp = str(calendar.timegm(time.gmtime()))
params = {
"batch_mode":"off",
"cards_platform":"Android-12",
"earned_read":"true",
"enable_dm_commands":"false",
"ext":"mediaRestrictions%2CaltText%2CmediaStats%2CmediaColor%2Cinfo360%2CcameraMoment%2ChighlightedLabel%2Cmaster_playlist_only",
"include_blocked_by":"true",
"include_blocking":"true",
"include_cards":"true",
"include_entities":"true",
"include_media_features":"true",
"include_reply_count":"true",
"oauth_consumer_key":self.twitter_android_key,
"oauth_nonce": oauth_nonce,
"oauth_signature_method":"HMAC-SHA1",
"oauth_timestamp":oauth_timestamp,
"oauth_token": self.xauth["oauth_token"],
"oauth_version":"1.0",
"send_error_codes":"true",
"status": quote(text,safe=''),
"tweet_mode":"extended"
}
headers = self.auth_headers(
oauth_nonce= oauth_nonce,
oauth_timestamp= oauth_timestamp,
oauth_signature= self.signature_message(
url= api_statuses,
params= params,
oauth_secret= self.xauth['oauth_token_secret']
)
)
headers["Content-Type"] = "application/x-www-form-urlencoded"
post_params = '&'.join([k + u"=" + v for k,v in params.items()])
return self.session.post(api_statuses,headers=headers,data=post_params.encode('utf-8'),verify=False).json()
api = Twitter()
result = api.xauth_login("username","password")
if api.challenged:
answer = input("code:").strip()
api.solve_challenge(result,answer)
api.tweet("XXXXXXXXXXXXXXXXXXXXX")
@tangxiangpi
Copy link

xauth_password dead

@dfelici87
Copy link

xauth_password doesn't work... any solution?

@BitTheByte
Copy link
Author

Hi Everyone, this implementation was based on the mobile version of Twitter, if anyone would like to continue supporting it please reverse engineer the app again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment