Skip to content

Instantly share code, notes, and snippets.

@framp
Created June 30, 2025 19:00
Show Gist options
  • Save framp/9747e1517017b1f3d23b6ffb8d4f8433 to your computer and use it in GitHub Desktop.
Save framp/9747e1517017b1f3d23b6ffb8d4f8433 to your computer and use it in GitHub Desktop.
const jwt = '...'
// Split JWT by dots
const sections = jwt.split('.');
// Function to check if a string is valid base64
function isValidBase64(str) {
try {
// Add padding if needed
while (str.length % 4 !== 0) {
str += '=';
}
// Try to decode
const decoded = Buffer.from(str, 'base64');
return true;
} catch (error) {
return false;
}
}
// Function to decode base64 and show result
function decodeBase64(str) {
try {
// Add padding if needed
while (str.length % 4 !== 0) {
str += '=';
}
const decoded = Buffer.from(str, 'base64');
return decoded.toString('utf8');
} catch (error) {
return null;
}
}
// New approach: Use regex to find and replace I/l characters systematically
function fixJwtSection(str) {
// First, try the original string
if (isValidBase64(str)) {
return { combination: str, decoded: decodeBase64(str) };
}
// Create a regex pattern to match I and l characters
const pattern = /[Il]/g;
const matches = [...str.matchAll(pattern)];
if (matches.length === 0) {
return null;
}
console.log(`Found ${matches.length} I/l characters to test`);
// Use a more efficient approach: try common patterns first
const commonReplacements = [
// Try replacing all with 'I'
str.replace(pattern, 'I'),
// Try replacing all with 'l'
str.replace(pattern, 'l'),
// Try alternating pattern
str.replace(pattern, (match, index) => index % 2 === 0 ? 'I' : 'l'),
// Try reverse alternating pattern
str.replace(pattern, (match, index) => index % 2 === 0 ? 'l' : 'I'),
];
// Test common patterns first
for (const replacement of commonReplacements) {
if (isValidBase64(replacement)) {
const decoded = decodeBase64(replacement);
if (decoded) {
return { combination: replacement, decoded };
}
}
}
// If common patterns don't work, use a more targeted approach
// Try replacing only the first occurrence
let testStr = str;
for (let i = 0; i < matches.length; i++) {
const match = matches[i];
const char = match[0];
const replacement = char === 'I' ? 'l' : 'I';
// Replace this specific occurrence
testStr = testStr.substring(0, match.index) + replacement + testStr.substring(match.index + 1);
if (isValidBase64(testStr)) {
const decoded = decodeBase64(testStr);
if (decoded) {
return { combination: testStr, decoded };
}
}
}
// If still no success, try a limited brute force (max 8 characters)
if (matches.length <= 8) {
const totalCombinations = Math.pow(2, matches.length);
for (let i = 0; i < totalCombinations; i++) {
let combination = str.split('');
for (let j = 0; j < matches.length; j++) {
const match = matches[j];
const useI = (i >> j) & 1;
combination[match.index] = useI ? 'I' : 'l';
}
const testStr = combination.join('');
if (isValidBase64(testStr)) {
const decoded = decodeBase64(testStr);
if (decoded) {
return { combination: testStr, decoded };
}
}
}
}
return null;
}
// Process each section
sections.forEach((section, index) => {
console.log(`\n=== Section ${index + 1} ===`);
console.log(`Original: ${section}`);
const result = fixJwtSection(section);
if (result) {
console.log(`\nValid combination found: ${result.combination}`);
console.log(`Decoded: ${result.decoded}`);
} else {
console.log('No valid base64 combination found for this section');
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment