Closed AlexandraBaker closed 1 year ago
Fingerprints should match closely of what current browser will output. You can check my forked version for reference. (how I randomized canvasFp is inspired by how Tor Browser block canvas fingerprinting).
Apart from that, support for game type 4 ~works great~ has some issues when token has more than 1 wave. Correct answer only sometimes got recognized. Not sure what happened, will look at it later on.
I assume this fixes the JSON.parse() error when calling getChallenge
Fingerprints should match closely of what current browser will output. You can check my forked version for reference. (how I randomized canvasFp is inspired by how Tor Browser block canvas fingerprinting).
Apart from that, support for game type 4 ~works great~ has some issues when token has more than 1 wave. Correct answer only sometimes got recognized. Not sure what happened, will look at it later on.
The answer body needs render_type: "canvas", to be added. However, even though solved is true, there is still something being detected after that I have not quite figured out yet. Let me know if you make any progress.
Fingerprints should match closely of what current browser will output. You can check my forked version for reference. (how I randomized canvasFp is inspired by how Tor Browser block canvas fingerprinting). Apart from that, support for game type 4 ~works great~ has some issues when token has more than 1 wave. Correct answer only sometimes got recognized. Not sure what happened, will look at it later on.
The answer body needs render_type: "canvas", to be added. However, even though solved is true, there is still something being detected after that I have not quite figured out yet. Let me know if you make any progress.
This might do the trick. Not tested yet.
@BadAimWeeb I have fixed the issues, could you please try again?
Got this error. Any way to fix this?
SyntaxError: Unexpected end of JSON input
at JSON.parse (
Got this error. Any way to fix this?
SyntaxError: Unexpected end of JSON input at JSON.parse () at Session. (C:\Users\Carll\Desktop\PROJECT BOTBASED MGUI\node_modules\funcaptcha\lib\session.js:56:29) at Generator.next () at fulfilled (C:\Users\Carll\Desktop\PROJECT BOTBASED MGUI\node_modules\funcaptcha\lib\session.js:5:58) at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
You are using stable (but broken) version, and should try out either this currrent PR:
npm install git+https://github.com/AlexandraBaker/funcaptcha.git#patch-8
or my fork (which is also based on this PR but patching even more things):
npm install git+https://github.com/BadAimWeeb/funcaptcha.git
Patches from this PR will (and should) be applied to stable soon (after everything is done).
@Oli2004 Are you using the fork to solve the captcha? Does your code work if you generate and solve a captcha using a different method? (ex. using the SDK on the Roblox website) Are you using a proxy with the captcha? (regions must match for Roblox to accept the token) Is the User-Agent header the same? Could you send a snippet of your code? What region are you in? Does manually solving the captcha work? What type of challenge are you getting? (type 3/4, what are the instructions?)
I have a mistake, im using challenge type 4 and when using getEmbedUrl return the embedurl but when I put it in an iframe it does not load anything, I think it must be loaded with javascript, any solution?
@Oli2004 Are you using the fork to solve the captcha? Does your code work if you generate and solve a captcha using a different method? (ex. using the SDK on the Roblox website) Are you using a proxy with the captcha? (regions must match for Roblox to accept the token) Is the User-Agent header the same? Could you send a snippet of your code? What region are you in? Does manually solving the captcha work? What type of challenge are you getting? (type 3/4, what are the instructions?)
Roblox is updating something that disallow us to just request to /v2/login
. If IP region didn't match, it would return "Token Validation Failed" instead. I guess token is fine but there is something else (tracking or something), will try using a browser to login though.
(using Tor because it's more reliable to generate token somehow, maybe it treats Tor as anonymous and just return challenge, not trying to assert IP risk)
@BadAimWeeb Token Validation Failed has to do with the csrf token, not the captcha token. If you do not use the same region for solving the captcha as in your requests to Roblox or change the IP between requests to Roblox, it will not work.
I'm getting type 4 captchas to work but Roblox isn't accepting them
@zachariapopcorn Make sure to update your user agent
@AlexandraBaker I have this for my UA
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Should I use the default one here?
@zachariapopcorn Try
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36
Still failed
// General captcha solving logic
let session = new funcaptcha.Session(captchaData, {
userAgent: UA,
});
let challenge = await session.getChallenge();
let res: SolvedCaptchaResult;
if (challenge instanceof Challenge1) {
BetterConsole.log(`Captcha type given: 1`);
res = await solveChallenge1(interaction, client, challenge);
} else if (challenge instanceof Challenge3) {
BetterConsole.log(`Captcha type given: 3`);
res = await solveChallenge3(interaction, client, challenge);
} else if (challenge instanceof Challenge4) {
BetterConsole.log(`Captcha type given: 4`);
res = await solveChallenge4(interaction, client, challenge);
}
if (res.success) {
captchaToken = captchaData.token;
} else {
if (res.error !== "CE" && res.error !== "CF") {
throw new Error(res.error);
}
return;
}
solveChallenge4
import Discord from 'discord.js';
import { Challenge4 } from 'funcaptcha/lib/challenge';
import fs from "fs";
import BotClient from '../../utils/classes/BotClient';
import BetterConsole from '../classes/BetterConsole';
import SolvedCaptchaResult from '../interfaces/SolvedCaptchaResult';
const map = {
"0️⃣": 0,
"1️⃣": 1,
"2️⃣": 2,
"3️⃣": 3,
"4️⃣": 4,
"5️⃣": 5
}
const keys = Object.keys(map);
export default async function solveChallenge4(interaction: Discord.CommandInteraction, client: BotClient, challenge: Challenge4): Promise<SolvedCaptchaResult> {
try {
let amountOfWaves = challenge.data.game_data.waves;
await fs.promises.writeFile(`${process.cwd()}/Image.gif`, await challenge.getImage());
let embed = client.embedMaker({title: "Captcha Required", description: `Logins require a captcha to be completed, please complete the captcha below\n\nObjective: ${challenge.instruction}\n\nGuide: https://i.imgur.com/05OYegq.png\n\nAmount of Waves: ${amountOfWaves}`, type: "info", author: interaction.user});
await interaction.editReply({embeds: [embed]});
for(let i = 0; i < amountOfWaves; i++) {
await fs.promises.writeFile(`${process.cwd()}/Image.gif`, await challenge.getImage());
let msg = await (interaction.channel as Discord.TextChannel).send({files: [`${process.cwd()}/Image.gif`]});
for(let i = 0; i < keys.length; i++) {
await msg.react(keys[i]);
}
let collected = await msg.awaitReactions({
filter: (reaction: Discord.MessageReaction, user: Discord.User) => {
if(interaction.user.id !== user.id) return false;
if(keys.findIndex(key => key === reaction.emoji.name) === -1) return false;
return true;
},
time: client.config.collectorTime,
max: 1
});
await msg.delete();
if(collected.size === 0) {
let embed = client.embedMaker({title: "Captcha Expired", description: "You didn't answer the captcha in time, please rerun the command", type: "error", author: interaction.user});
await interaction.editReply({embeds: [embed]});
return {success: false, error: "CE"};
}
let answer = map[collected.at(0).emoji.name];
let answerResponse = await challenge.answer(answer);
if(answerResponse.response === "answered" && answerResponse.solved === false) {
let embed = client.embedMaker({title: "Captcha Failed", description: "You've failed the captcha, please rerun the command", type: "error", author: interaction.user});
await interaction.editReply({embeds: [embed]});
return {success: false, error: "CF"};
}
}
return {success: true}; // answerResponse.response = "answered" and answerResponse.solved = true
} catch(e) {
BetterConsole.log(e);
return {success: false, error: e};
}
}
// Login logic
let embed = client.embedMaker({
title: "Captcha Completed",
description:
"You've successfully completed the captcha, I am now attempting to login to the Roblox account",
type: "info",
author: interaction.user,
});
await interaction.editReply({ embeds: [embed] });
await fs.promises.unlink(`${process.cwd()}/Image.gif`);
res = await login(
client,
client.config.ROBLOX_USERNAME,
client.config.ROBLOX_PASSWORD,
csrfToken,
rblxChallengeId,
rblxChallengeMetadata.unifiedCaptchaId,
captchaToken,
rblxChallengeType
);
let rawCookie = res.headers.get("set-cookie");
if (!rawCookie) {
let embed = client.embedMaker({
title: "Error",
description: `There was an error while trying to login to the Roblox account: ${
(await res.json()).errors[0].message
}`,
type: "error",
author: interaction.user,
});
return await interaction.editReply({ embeds: [embed] });
}
login
async function login(client: BotClient, username: string, password: string, csrfToken?: string, challengeId?: string, unifiedCaptchaId?: string, captchaToken?: string, challengeType?: string) {
let headers = {
"Content-Type": "application/json",
"User-Agent": UA,
}
if(csrfToken) {
headers["X-CSRF-TOKEN"] = csrfToken;
}
if(challengeId) {
headers["rblx-challenge-id"] = challengeId;
let metaData = JSON.stringify({
"unifiedCaptchaId": unifiedCaptchaId,
"captchaToken": captchaToken,
"actionType": "Login"
});
headers["rblx-challenge-metadata"] = Buffer.from(metaData, "utf-8").toString("base64");
headers["rblx-challenge-type"] = challengeType;
}
return await client.request({
url: "https://auth.roblox.com/v2/login",
method: "POST",
headers: headers,
body: {
"ctype": "Username",
"cvalue": username,
"password": password
},
robloxRequest: false
});
}
Also I just realized that they updated their login logic to include this shit https://i.imgur.com/mDnD3Vr.png
Edit: These things don't seem to be required
@AlexandraBaker ^
@zachariapopcorn I do not have the time to debug your code/specific issue. Please refer to this working example where the answers are between 0 and 5:
const username = "testing12345"
const password = "testpassword"
const fs = require("fs")
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
const undici = require("undici")
const funcaptcha = require("../lib")
const readline = require("readline")
let rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
function ask(question) {
return new Promise((resolve, reject) => {
rl.question(question, (answer) => {
resolve(answer)
})
})
}
let csrf = "";
async function sendRequest(challengeId, fieldData, token) {
const headers = {
"x-csrf-token": csrf,
"content-type": "application/json",
"user-agent": USER_AGENT,
'rblx-challenge-type': 'captcha',
origin: "https://www.roblox.com",
referer: "https://www.roblox.com/"
}
if (fieldData) {
headers['rblx-challenge-metadata'] = Buffer.from(JSON.stringify({
unifiedCaptchaId: fieldData.unifiedCaptchaId,
captchaToken: token.token,
actionType: fieldData.actionType,
})).toString('base64')
}
if (challengeId) {
headers['rblx-challenge-id'] = challengeId
}
const res3 = await undici.request("https://auth.roblox.com/v2/login", {
method: "POST",
headers,
body: JSON.stringify({
"ctype": "Username",
"cvalue": username,
"password": password,
})
})
const newCsrf = res3.headers['x-csrf-token']
if (newCsrf) {
csrf = newCsrf
return sendRequest(challengeId, fieldData, token)
}
const body = await res3.body.json()
console.log('BODY', body)
return { body, headers: res3.headers }
}
sendRequest().then(async ({ headers }) => {
setTimeout(async () => {
const fieldData = JSON.parse(Buffer.from(headers["rblx-challenge-metadata"], "base64"))
const token = await funcaptcha.getToken({
pkey: "476068BF-9607-4799-B53D-966BE98E2B81",
surl: "https://client-api.arkoselabs.com",
data: {
"blob": fieldData.dataExchangeBlob,
},
headers: {
"User-Agent": USER_AGENT,
},
site: "https://www.roblox.com",
location: "https://www.roblox.com/login",
})
if (!token.token.includes('sup=1')) {
const session = new funcaptcha.Session(token, {
userAgent: USER_AGENT,
})
const challenge = await session.getChallenge().catch((err) => console.log('login fail', err))
console.log(challenge.data.game_data.customGUI.api_breaker)
console.log(challenge.instruction)
for(let x = 0; x < challenge.data.game_data.waves; x++) {
fs.writeFileSync(`image.gif`, await challenge.getImage())
console.log(await challenge.answer(parseInt(await ask("Answer: "))))
}
} else {
console.log('Suppressed captcha!')
}
sendRequest(headers['rblx-challenge-id'], fieldData, token).then(console.log)
}, 2500)
})
@AlexandraBaker
Huh, seems like I was missing the origin
and referer
headers and had the wrong surl
Thanks, though in testing, I sometimes had the following error TypeError: Cannot read properties of undefined (reading 'gameType')
which came from the session file, but this is probably some random error that fixes itself
@zachariapopcorn That probably means you got a suppressed captcha
Thanks
I have a mistake, im using challenge type 4 and when using getEmbedUrl return the embedurl but when I put it in an iframe it does not load anything, I think it must be loaded with javascript, any solution?
up +1
@thurdev @Rupulsttiky27 This library does not support embedding the URL in an iframe, there are additional assets loaded to make the captcha work in an iframe. If you wish to solve captchas without detection, you will need to use the built in challenge class as shown in the README example.
@Oli2004 This should be fixed now.
@Oli2004 Are you sure that your answers are correct? What are the API breakers when they are failing?
Edit: Can you try logging in a bunch manually using the official captcha? I'm doing this and randomly I will go into a streak of failing even though the answers are correct.
Game type 4 can now have from 5 to 16 (!) images to select. You might want to remove the 0-5 input limit.
Edit: Can you try logging in a bunch manually using the official captcha? I'm doing this and randomly I will go into a streak of failing even though the answers are correct.
Also FunCaptcha might have hidden challenges for bad fingerprint (that's why you're failing). In my observation, I can only pass captcha after second tries (official client also does this).
@BadAimWeeb Seeing as I am getting suppressed and 1 wave easy captchas I highly doubt it's due to a bad fingerprint. Since it's happening on the official client, I am inclined to believe this is an unavoidable mechanism that might be some sort of random failure. Arkose does have something called "punishable_actioned" which is described as "an attack mitigation tactic, which randomly fails verification attempts, even if the response was correct. This field indicates if punishable was activated."
@Oli2004 Check if sup=1 is present in the token. This means the captcha was suppressed, aka an "invisible captcha," and the token can be used without generating a challenge.
@Oli2004 It's probably because tbio and kbio are not included in the request. If you can send me the website, I can get some data when I have the time and add it in.
@Oli2004 I think this is because Challenge3 was not updated the same way as Challenge4. I will work on a more standardized way to update all challenges.
ROBLOX Login API seems to only accept suppressed captchas and doesn't seem to accept non-suppressed ones.
Error: Challenge failed to authorize
I've already added origin
and referer
to my headers and it still doesn't work. Any help?
I'm getting reports of people getting Error: Invalid API breaker
once in a while. Do you know what this means, and if it's one of those errors that just fix itself if you try again?
@AlexandraBaker This error has now become very consistent, making captcha attempts fail. Can you please fix when you can? Thanks
@zachariapopcorn It has already been fixed, you need to update the package.
I did, same error
@zachariapopcorn The latest version does not have this error in the code.
Hi all,
Support for gametype 4 and API Breakers V2 is currently being developed on a separate branch.
I will be keeping this pull request open for any bugs found or feedback given.
https://github.com/noahcoolboy/funcaptcha/tree/gt4
You can install it using the following command: npm i https://github.com/noahcoolboy/funcaptcha/tree/gt4
I will not provide help if you are using the library incorrectly. Thank you.
there seems to be an issue with getting the embed url (on both forks and the official branch). the gt4 branch just shows a white screen when embedded in a iframe, while @BadAimWeeb's fork creates this. it IS a valid session, as i can use getChallenge() and other functions fine.
there seems to be an issue with getting the embed url (on both forks and the official branch). the gt4 branch just shows a white screen when embedded in a iframe, while @BadAimWeeb's fork creates this. it IS a valid session, as i can use getChallenge() and other functions fine.
You can't just only using embed URL in and expect that it will work. You also need to write some scripts to communicate to the iframe (which sadly, I don't have any PoC since code from funcaptcha is very obfuscated and I cannot figure out how yet).
there seems to be an issue with getting the embed url (on both forks and the official branch). the gt4 branch just shows a white screen when embedded in a iframe, while @BadAimWeeb's fork creates this. it IS a valid session, as i can use getChallenge() and other functions fine.
getEmbedUrl seems to work just fine on the gt4 branch. See image below:
Please make sure all parameters are configured correctly (site, blob, etc...). It seemed to have worked just fine (for me) when modifying test/test.js or logging the embedUrl and placing it in an iframe.
there seems to be an issue with getting the embed url (on both forks and the official branch). the gt4 branch just shows a white screen when embedded in a iframe, while @BadAimWeeb's fork creates this. it IS a valid session, as i can use getChallenge() and other functions fine.
getEmbedUrl seems to work just fine on the gt4 branch. See image below:
Please make sure all parameters are configured correctly (site, blob, etc...). It seemed to have worked just fine (for me) when modifying test/test.js or logging the embedUrl and placing it in an iframe.
ah, thanks. i guess i’ll give the gt4 branch another try!
Merged in #39
there seems to be an issue with getting the embed url (on both forks and the official branch). the gt4 branch just shows a white screen when embedded in a iframe, while @BadAimWeeb's fork creates this. it IS a valid session, as i can use getChallenge() and other functions fine.
getEmbedUrl seems to work just fine on the gt4 branch. See image below: Please make sure all parameters are configured correctly (site, blob, etc...). It seemed to have worked just fine (for me) when modifying test/test.js or logging the embedUrl and placing it in an iframe.
ah, thanks. i guess i’ll give the gt4 branch another try!
did u find a solution for it? I have been trying to make embed work but nothing ...
Everything should work based on my limited testing and websites were accepting the tokens that were generated. Anyone else who wants to test is welcome to!