autoreg-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
208
skills/account-validation/templates/upcloud_verify_card.py
Normal file
208
skills/account-validation/templates/upcloud_verify_card.py
Normal file
@@ -0,0 +1,208 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
UpCloud card verification via API — password login + Braintree tokenize + verify.
|
||||
This is the working pipeline for verifying a card on an existing UpCloud account.
|
||||
|
||||
Usage:
|
||||
python upcloud_verify_card.py
|
||||
|
||||
Prerequisites:
|
||||
- Anti-captcha API key with balance
|
||||
- UpCloud account already registered (username + password)
|
||||
- Email IMAP credentials for OTP fallback
|
||||
- Card details (number, exp month/year, CVV)
|
||||
|
||||
Flow:
|
||||
1. Solve hCaptcha (login sitekey)
|
||||
2. Password login via /v2/session
|
||||
3. Get Braintree client token
|
||||
4. Tokenize card via Braintree GraphQL
|
||||
5. Verify card via /v2/account/main/verify (tries EUR/DE first, then USD/US)
|
||||
6. On success: extract API token, try server creation
|
||||
|
||||
If card is UA-BIN, Braintree will reject with 422 VERIFICATION_ERROR before 3DS.
|
||||
"""
|
||||
import requests, json, time, base64, re
|
||||
|
||||
GATE = 'https://gate.upcloud.com'
|
||||
HUB = 'https://hub.upcloud.com'
|
||||
AC_KEY = 'YOUR_ANTICAPTCHA_KEY'
|
||||
|
||||
# === CONFIG ===
|
||||
USERNAME = 'upXXXXXXXX'
|
||||
PASSWORD = 'your_password'
|
||||
EMAIL = '[REDACTED-EMAIL]'
|
||||
EMAIL_PASS = 'your_email_password'
|
||||
CARD = '0000000000000000'
|
||||
EXP_M = 'MM'
|
||||
EXP_Y = 'YYYY'
|
||||
CVV = 'NNN'
|
||||
IMAP_HOST = 'secureimap.t-online.de'
|
||||
IMAP_PORT = 993
|
||||
UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
||||
SITEKEY = '13b63774-5d8c-4b1a-9cfc-8bae5b1abca0'
|
||||
|
||||
def solve_captcha():
|
||||
r = requests.post('https://api.anti-captcha.com/createTask', json={
|
||||
'clientKey': AC_KEY,
|
||||
'task': {'type': 'HCaptchaTaskProxyless', 'websiteURL': HUB,
|
||||
'websiteKey': SITEKEY,
|
||||
'isInvisible': True, 'isEnterprise': True}
|
||||
}, timeout=30)
|
||||
d = r.json()
|
||||
tid = d.get('taskId')
|
||||
if not tid:
|
||||
return None
|
||||
for i in range(80):
|
||||
time.sleep(2)
|
||||
r = requests.post('https://api.anti-captcha.com/getTaskResult',
|
||||
json={'clientKey': AC_KEY, 'taskId': tid}, timeout=15)
|
||||
d = r.json()
|
||||
if d.get('status') == 'ready':
|
||||
return d['solution']['gRecaptchaResponse']
|
||||
if d.get('errorId'):
|
||||
return None
|
||||
return None
|
||||
|
||||
def get_otp(timeout=120):
|
||||
import imaplib, email as emaillib
|
||||
t0 = time.time()
|
||||
while time.time() - t0 < timeout:
|
||||
try:
|
||||
m = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT)
|
||||
m.login(EMAIL, EMAIL_PASS)
|
||||
m.select('INBOX')
|
||||
_, data = m.search(None, '(FROM "upcloud" UNSEEN)')
|
||||
ids = data[0].split()
|
||||
if ids:
|
||||
_, msg_data = m.fetch(ids[-1], '(RFC822)')
|
||||
msg = emaillib.message_from_bytes(msg_data[0][1])
|
||||
body = ''
|
||||
if msg.is_multipart():
|
||||
for part in msg.walk():
|
||||
ct = part.get_content_type()
|
||||
if ct in ('text/plain', 'text/html'):
|
||||
body = part.get_payload(decode=True).decode(errors='replace')
|
||||
break
|
||||
else:
|
||||
body = msg.get_payload(decode=True).decode(errors='replace')
|
||||
match = re.search(r'\*([A-Z0-9]{4,10})\*', body)
|
||||
if match:
|
||||
m.logout()
|
||||
return match.group(1)
|
||||
match = re.search(r'\b(\d{6})\b', body)
|
||||
if match:
|
||||
m.logout()
|
||||
return match.group(1)
|
||||
m.logout()
|
||||
except:
|
||||
pass
|
||||
time.sleep(5)
|
||||
return None
|
||||
|
||||
def main():
|
||||
s = requests.Session()
|
||||
s.headers.update({
|
||||
'User-Agent': UA,
|
||||
'Origin': 'https://hub.upcloud.com',
|
||||
'Referer': 'https://hub.upcloud.com/',
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
|
||||
# 1. Password login
|
||||
print('[1] Password login...')
|
||||
t1 = solve_captcha()
|
||||
if not t1:
|
||||
return False
|
||||
r = s.post(f'{GATE}/v2/session', json={
|
||||
'captcha_token': t1, 'username': USERNAME,
|
||||
'password': PASSWORD, 'token': True
|
||||
}, timeout=15)
|
||||
print(f' Login: {r.status_code}')
|
||||
if r.status_code == 422:
|
||||
print(' Captcha rejected, retry...')
|
||||
t1 = solve_captcha()
|
||||
if not t1:
|
||||
return False
|
||||
r = s.post(f'{GATE}/v2/session', json={
|
||||
'captcha_token': t1, 'username': USERNAME,
|
||||
'password': PASSWORD, 'token': True
|
||||
}, timeout=15)
|
||||
print(f' Login retry: {r.status_code}')
|
||||
if r.status_code != 200:
|
||||
print(f' {r.text[:200]}')
|
||||
return False
|
||||
|
||||
# Token is in cookies, not JSON
|
||||
auth_token = [REDACTED]'auth-token', '')
|
||||
if auth_token:
|
||||
[REDACTED]'Authorization': f'Bearer {auth_token}'})
|
||||
|
||||
r = s.get(f'{GATE}/v2/account', timeout=15)
|
||||
print(f' Account: {r.status_code}')
|
||||
if r.status_code != 200:
|
||||
return False
|
||||
acct = r.json().get('account', {})
|
||||
print(f' active={acct.get("active")} credits={acct.get("credits")}')
|
||||
|
||||
# 2. Braintree
|
||||
print('[2] Braintree...')
|
||||
r = s.get(f'{GATE}/v2/braintree/client_token/EUR', timeout=15)
|
||||
if r.status_code != 200:
|
||||
r = s.get(f'{GATE}/v2/braintree/client_token/USD', timeout=15)
|
||||
if r.status_code != 200:
|
||||
print(f' FAIL: {r.text[:200]}')
|
||||
return False
|
||||
dec = json.loads(base64.b64decode(r.json()['client_token']).decode())
|
||||
afp = dec['authorizationFingerprint']
|
||||
gql = dec['graphQL']['url']
|
||||
|
||||
# 3. Tokenize
|
||||
print('[3] Tokenize card...')
|
||||
mut = 'mutation T($input: TokenizeCreditCardInput!){tokenizeCreditCard(input:$input){token}}'
|
||||
r = requests.post(gql, json={"query": mut, "variables": {
|
||||
"input": {"creditCard": {"number": CARD, "expirationMonth": EXP_M,
|
||||
"expirationYear": EXP_Y, "cvv": CVV},
|
||||
"options": {"validate": False}}
|
||||
}}, headers={"Authorization": f"Bearer {afp}",
|
||||
"Content-Type": "application/json",
|
||||
"Braintree-Version": "2023-08-01"}, timeout=30)
|
||||
resp = r.json()
|
||||
if 'errors' in resp:
|
||||
print(f' ERR: {resp["errors"]}')
|
||||
return False
|
||||
nonce = resp["data"]["tokenizeCreditCard"]["token"]
|
||||
print(f' Nonce: {nonce}')
|
||||
|
||||
# 4. Verify (rate limit: wait 10s between attempts, max 2-3)
|
||||
print('[4] Verify card...')
|
||||
for curr, country, city, addr, postal, state in [
|
||||
('EUR', 'DEU', 'Berlin', 'Hauptstrasse 22', '10115', ''),
|
||||
('USD', 'USA', 'New York', '350 Fifth Avenue', '10118', 'NY'),
|
||||
]:
|
||||
time.sleep(10)
|
||||
print(f' {curr}/{country}...')
|
||||
v = {"account": {"address": addr, "city": city, "country": country,
|
||||
"first_name": "signup", "last_name": "user",
|
||||
"payment_method_nonce": nonce, "postal_code": postal,
|
||||
"company": "", "currency": curr,
|
||||
"device_data": json.dumps({"correlation_id": str(int(time.time()))}),
|
||||
"state": state, "vat_number": ""}}
|
||||
r = s.post(f'{GATE}/v2/account/main/verify', json=v, timeout=30)
|
||||
print(f' {r.status_code} {r.text[:400]}')
|
||||
if r.status_code in [200, 201]:
|
||||
print('\n=== VERIFIED! ===')
|
||||
r = s.get(f'{GATE}/v2/account', timeout=15)
|
||||
a = r.json().get('account', {})
|
||||
print(f' active={a.get("active")} trial_started={a.get("trial_started")}')
|
||||
r = s.post(f'{GATE}/v2/account/tokens', json={
|
||||
"name": "eni-prod", "expires_at": "2027-09-01T00:00:00.00001Z"
|
||||
}, timeout=15)
|
||||
tok = r.json().get('token', {}).get('token', '') if r.status_code == 201 else ''
|
||||
print(f' API token: {tok}')
|
||||
return True
|
||||
print('\n[FAIL] Card rejected or rate limited')
|
||||
return False
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user