PagedOut 005
PagedOut 005
Lord of the Apples: One Page To Rule Them All Karol Mazurek 22
macOS Notifications Forensics Csaba Fitzl 25
Make Your Own Linux with Buildroot and QEMU Karol Przybylski 27
Games retro and love if Forth code then Rodolfo García Flores & Lauren S. Ferro 46
1: 2: 3: 4: ∏[𝑒𝑖,𝑗,𝑙 , ⏞
𝛼 ] = 𝑐 for all edges (𝑖, 𝑗)
𝑙=1
If we assign the value of each vertex 𝑣𝑖 to has a solution for 𝑒𝑖,𝑗,𝑙 iff 𝛼 ∉ 𝑍(𝐺), i.e.,
a color corresponding to which set it be- iff 𝑣 and 𝑣 are in different cosets of 𝑍(𝐺).
𝑖 𝑗
longs (e.g., 𝑣𝑖 = means vertex 𝑣𝑖 is the Assign each coset a color. Since [𝐺 : 𝑍(𝐺)] >
first color ), the equation [𝑒𝑖,𝑗 , 𝛼] = only 2 for non-abelian groups, we can always find
has a solution for 𝑒𝑖,𝑗 iff 𝑣𝑖 and 𝑣𝑗 correspond a 𝑘 > 2 such that solving the above equations
yields a solution to the 𝑘-coloring problem
to different colors !
(which is NP-complete).
Hence, solving the system of equations in 𝐷4 :
Hence, we can’t expect a polynomial-time way
[𝑒 , 𝛼] = // for all edges (𝑖, 𝑗) to solve systems in non-abelian simple groups.
𝑖,𝑗
Jules Poon
Blog: https://juliapoo.github.io
4 SAA-POOL 0.0.7
Simple (and works!)
Meet the Balloon for PAKEs. This is processed after the password
and salt.
Key Derivation 6. No variants: Balloon and Balloon-M have been
Function (BKDF) merged into one algorithm, and there is only a data-
independent version. This avoids confusion about
which variant to use (Argon2id, Argon2i, Argon2d,
Balloon, also known as Balloon Hashing1 , is a or Argon2ds), resists cache-timing attacks in all
memory-hard password hashing algorithm that was pub- cases, and simplifies implementation.
lished shortly after the Password Hashing Competition 7. No delta parameter: The delta security param-
(PHC). Compared to the winner, Argon2, it supports eter is fixed at 3 instead of being tweakable. This
using any cryptographic hash function and is much eas- matches the user-specified parameters of other al-
ier to understand and implement. gorithms and helps with performance.
In summary, it fills a large buffer with pseudorandom
bytes by hashing the password and salt before repeat- 8. Improved domain separation: The space cost,
edly hashing the previous output. Next, the buffer gets time cost, parallelism, and parallelism loop iter-
mixed, with each hash-sized block becoming equal to the ation are used to compute the first block of the
hash of the previous block, the current block, and sev- buffer. The salt is no longer used for parallelism
eral other blocks pseudorandomly chosen from the salt. domain separation to avoid copying.
Finally, the last block of the buffer is output as the hash.
Note that a global counter is used for domain separation 9. No computing memory accesses from the
when hashing throughout. salt: Instead, they are computed from the space
Unfortunately, the paper does not properly specify the cost, time cost, parallelism, and parallelism loop
algorithm, there are multiple variants, and functionality iteration. This prevents a cache-timing attack leak-
like support for key derivation is missing. ing when a user logs in as well as data-dependent
That is where BKDF comes in. With the help of access if the salt depends on the password (relevant
cryptographers/cryptographic engineers, it is a redesign for PAKEs).
of Balloon to address these limitations, which is being 10. An encoding: Parameters/lengths are encoded in
published as an Internet-Draft. And in this article, I am little-endian as unsigned 32-bit integers, whereas
going to discuss the changes as of July 2024. the global counter is a 64-bit integer to avoid an
overflow.
1. Support for HMAC: Now a collision-resistant
PRF must be used. To turn a hash function into a 11. No canonicalization attacks: Variable-length
PRF, prefix MAC with the key padded to the block inputs now get their length encoded when hashing,
size, HMAC, or the key parameter for algorithms which avoids ambiguity.
like BLAKE2/BLAKE3 can be used. To derive a
key from the password and salt and to compute 12. No modulo bias: The space cost must be a power
the data-independent memory accesses, an all-zero of 2 to avoid modulo bias when computing the mem-
PRF key is specified, like HKDF-Extract. The rest ory accesses. This also helps simplify the space cost
of the algorithm uses the derived key. parameter selection.
2. Support for key derivation: A variant of the On top of these changes, the Internet-Draft pseu-
NIST SP 800-108 KDF in feedback mode has been docode is written for readability and lots of parame-
added to support larger outputs. The algorithm ter/security guidance is provided. The goal is to also
name and final block of the buffer are used as con- have test vectors for all the popular hash functions and
text. This is basically HKDF-Expand but with a XOFs, not just NIST approved algorithms like SHA-2.
larger counter and some parameters moved around. Of course, BKDF is not perfect. Although it improves
on the performance of Balloon, it is still slower than Ar-
3. Improved performance: The memory accesses gon2. The small block size, which is limited by the hash
are now precomputed and only depend on fixed- function output length, and delta also hinder the mem-
length inputs, meaning fewer hash function calls. ory hardness. However, it is a lot better than PBKDF2
4. Support for a pepper: In the HKDF-Extract whilst still having the ingredients for NIST approval.
style step, a pepper can be used as the key instead And on that bombshell, it’s time to end. If you are
of zeros. Steps have been taken to avoid equivalent interested in BKDF, please watch the Internet-Draft on
keys with HMAC and prefix MAC. GitHub2 as it is a work in progress. The more eyes on
it, the better. The best work is the result of collabora-
5. Support for associated data: There may be con- tion, and helpful feedback will be acknowledged in the
text information that you want to include when Acknowledgments section. Implementations will also be
computing the output, such as a user and server ID linked in the GitHub repo.
1 https://crypto.stanford.edu/balloon/ 2 https://github.com/samuel-lucas6/draft-lucas-bkdf
Samuel Lucas
Blog: https://samuellucas.com
CC BY 4.0 GitHub: https://github.com/samuel-lucas6 7
File Formats A Playable PDF
Rob Hogan
https://iridisalpha.com
8 SAA-TIP 0.0.7
(untitled) (I) Art
Maung Thuta
Twitter: @CypressDahlia
SAA-ALL 0.0.7 9
File Formats The art of Java class golfing
If you ever needed to create the smallest Java class that can do something – this is the right paper for you.
The challenge of Binary Golf Grand Prix 5 (BGGP5) was to create the smallest Java class that has a public static void main(String[])
method that’d show the downloaded contents of https://binary.golf/5/5 to the console.
My approach was running “curl -L 7f.uk” with Runtime.exec(String) (the 7f.uk is a purposely registered short URL).
cafe babe 0000 0037 0011 0a00 0800 0908 000a 0a00 0800 0b07 0005 0100 0443 6f64 6501 0004
6d61 696e 0100 1628 5b4c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b29 5607 000c 0c00 0d00
0e01 0027 6261 7368 202d 6320 6375 726c 247b 4946 537d 2d4c 247b 4946 537d 3766 2e75 6b3e
2f64 2a2f 7474 790c 000f 0010 0100 116a 6176 612f 6c61 6e67 2f52 756e 7469 6d65 0100 0a67
6574 5275 6e74 696d 6501 0015 2829 4c6a 6176 612f 6c61 6e67 2f52 756e 7469 6d65 3b01 0004
6578 6563 0100 2728 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 294c 6a61 7661 2f6c 616e
672f 5072 6f63 6573 733b 0421 0004 0008 0000 0000 0001 0009 0006 0007 0001 0005 0000 0015
0002 0001 0000 0009 b800 0112 02b6 0003 b100 0000 0000 00
Maung Thuta
Twitter: @CypressDahlia
SAA-ALL 0.0.7 11
https://www.pixiepointsecurity.com
https://www.pixiepointsecurity.com
Slowcoding my childhood GameDev
To get some visual output, the least documented part of the Lots of respect to The Defence Force for reverse engineering and
Oric must be implemented, the ULA: a gate array that documenting the Oric as well as creating great demos and games!
Anders Piniesjö
GitHub: https://github.com/pugo/Pugo-Oric
SAA-TIP 0.0.7 Me: https://www.pugo.org/ 13
Hardware Making a simple Macintosh LC PDS card
In 1990, Apple released the Macintosh LC, which was To handle read cycles, we respond with the existing
powered by the Motorola MC68020 processor. Its LED state whenever our card is selected, /AS
only expansion card slot was the Processor Direct and /DS are asserted, and RW is high.
Slot (PDS), a 96-pin Eurocard connector that
provided direct access to most of the CPU's signals. PDS_SELECT = A31 & !A24 & AS;
[D0..2] = [LED1..3];
This slot survived all the way to the mid '90s when [D0..2].oe = PDS_SELECT & DS & RW;
the Mac had moved to PowerPC. On newer
machines, it was no longer directly connected to the Write cycles are slightly more complex but still
CPU but instead an ASIC emulated 68030 bus cycles straightforward. We latch the new state of the LEDs
for backward compatibility. Many popular machines from the data bus on the rising clock edge at the
including the Color Classic and LC 475 have a PDS. end of S3, assuming our card is selected. On all
other rising clock edges, we just latch the existing
Let's make a simple PDS card that enables a Mac state, resulting in no change to the LEDs.
program to control a few LEDs. First, we need to
understand 68020 and 68030 read and write cycles. LED_WRITING = PDS_SELECT & DS & !RW;
[LED1..3].d = ([D0..2] & LED_WRITING) #
([LED1..3] & !LED_WRITING);
[LED1..3].ar = 'h'0; [LED1..3].sp = 'h'0;
[DSACK0..1] = PDS_SELECT;
Doug Brown
Blog: https://www.downtowndougbrown.com/
14 X/Twitter: @dt_db SAA-TIP 0.0.7
Remote work automation using an old AVR programmer Hardware
Eradication
Moving the mouse cursor
What can be easier than removing code? The slightly
Easy-peasy, mumbles Steve, and provides random values
bored man opens Makefile and removes isp.o, clock.o
for the 2nd (relative X coordinate) and 3rd (Y) byte
and tpi.o from built objects. He removes correspond-
of reportBuffer each time before it is sent. Such a
ing .c and .h Ąles as well. He deletes any code refer-
solution does not resemble human interaction. Could
encing them from main.c hard-heartedly. What is left
you do better? If not, take a look at https://github.com
are stubs of usbFunctionSetup, usbFunctionRead and
/szymor/usbasp-jiggler for inspiration.
usbFunctionWrite.
Szymon Morawski
https://szymor.github.io/
CC0 15
History AI Won’t Take Your Job
It's 2024, and we’ve hit quite the wall with technology—or should I say, war (lol).
The wall in question? Jobs. Yeah, everyone’s freaking out about how AI is
supposedly coming for all the real hackers' and developers' gigs. But hey, instead
of jumping on the clickbait train with every video, maybe it's time to look at things
from a more positive angle.
If we sit and ask ourselves the question - “will AI take our job” and think it through
logically, while also analyzing our current implementation of AI, will you go
through the ringer because of AI, or, is it since an individual might not just have a
good enough skill set?
Many jobs that people worry about being taken, especially intricate ones such as
reverse engineering and binary exploit development, will likely not be replaced
due to how much it requires to train a model to do such tasks. This often involves
a lot of money and resources. However, you may find jobs where simple tasks
are being given to AI, such as minor development tasks rather than full-fledged
entire code completion. That being said, while AI is taking some jobs, saying that
we will be out of jobs or AI is going to replace us is entirely wrong.
A company I worked at as a ghostwriter for some time replaced my job with GPT.
I have been writing articles for a few months, but they wanted to meet impossible
mass production quantities for cheap, so they moved to using GPT and Jasper to
generate articles. But, after that, I did not stop and I kept going forward and found
ways to tune my work, and make it more unique. By then, I figured out that the
reason jobs are taken from people is split up into multiple reasons, but the
primary one is a list of needs, such as the following: mass production, funding,
production time, and fast ideas.
Ever since I dedicated some time researching the capabilities of AI, and
understanding what it is, also viewing people’s perspectives, especially in other
fields such as art, I realized that you will only have your job taken if you are easy
to replace, predictable, fixed in style, and just doing a task. After all, there is a
reason why some artists are losing to AI when others are still racking stacks of
cash off their art. Not only is it unique, but it's unique because it uses elements
that AI cannot achieve, such as true randomness (true randomness is
unpredictable because it comes from natural, uncontrollable processes, unlike
AI's algorithms). I truly feel that one can survive the wave of AI if one has not only
a versatile skillset, but understands how to get a job without just relying on a
resume, and one who also understands how to adapt to modern-day changes.
Adaption and collaboration are how you survive and soar into the skies.
bah
https://b.horn.uk/
20 https://github.com/bahorn/ CC0
Art diary of Ninja Jo (III) Art
Introduction
Before malicious software can run on macOS, it must overcome multiple layers of security mechanisms that Apple has designed to
protect users. From the moment a file is downloaded to the point of its execution, macOS implements a series of thorough checks that
rigorously block threats. Even when the malware successfully runs, it is additionally isolated with sandboxing mechanisms.
1. Quarantine and Gatekeeper - Prevents automatic execution of files, notifying users before they open the file.
When a file is downloaded from the internet, macOS applies a quarantine flag. It indicates that the file originated from an
untrusted source. Technically, this flag is a file extended attribute of com.apple.quarantine (we can check it using ls -l@ or
xattr -l). Files without it bypass all further security mechanisms related to Gatekeeper.
2. Gatekeeper and Notarization - Ensures the application meets the Apple Security policy and it is free from malware.
The Gatekeeper verifies the file to ensure it meets Apple's security requirements. This verification is done using the ticket that
Apple attaches to the application during notarization. All applications must first be uploaded to Apple and notarized.
3. Gatekeeper and XProtect - Blocks malicious files by checking for valid signatures and known threats.
However, it is still possible for the user to agree to run a file that has not been notarized in the window displayed by
Gatekeeper. In this case, XProtect (a built-in malware scanner) verifies whether the file is a virus based on its signatures. If the
signature is known as a virus, the execution is blocked.
4. Code Signing and AMFI (Apple Mobile File Integrity) - Prevents execution of unsigned or tampered applications.
All applications must be code-signed to run by a Developer using its Developer Certificate or by being signed directly by Apple
or ad hoc. Code signing confirms the integrity and authenticity of applications by verifying their digital signatures, ensuring
that they have not been tampered with. During runtime, AMFI is the component that enforces the validity of these signatures.
5. Sandboxing and TCC - Prevents untrusted applications from accessing critical system resources and sensitive data.
TCC manages access to user data (through user consent), while the Sandbox controls app behavior (via system-imposed
restrictions). Both isolate applications, restricting their access to system resources (such as camera or microphone) and
manage permissions for access to sensitive data. So even when the malware successfully bypasses all the layers before now,
its damage is mitigated thanks to this protection because it cannot access all files and use all hardware resources.
6. System Integrity Protection (SIP) - Prevents unauthorized modification of system files, even by the root user.
Even if malware exploits a zero-day vulnerability to gain root access, macOS has System Integrity Protection (SIP), which
restricts root-level modifications to system files and processes. SIP ensures that critical system components are protected
from even privileged users. You will not damage your Mac when SIP is on even from root (at least you shouldn't :D).
References
1. https://github.com/Karmaz95/Snake_Apple
2. https://support.apple.com/en-gb/guide/security/welcome/web
3. https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution
4. https://developer.apple.com/documentation/security/code-signing-services
5. https://developer.apple.com/documentation/security/app-sandbox
Csaba Fitzl
Blog: https://theevilbit.github.io/
SAA-NA-TIP 0.0.7 X/Twitter: @theevilbit 25
REVERSE ENGINEERING CONFERENCE
ORLANDO, FL
2025.02.28 - 2025.03.01
TICKETS AVAILABLE NOW
https://re-verse.io
Become an
exploit-dev Learn:
---------------------
➔ Reverse Engineering
➔ Memory Corruption
without leaving ➔ Shellcoding
➔ Stack Canaries
➔ DEP + ROP
➔ ASLR + Leaks
➔ Heap + Use-After-Free
your ➔ Race Conditions
-------------------------
browser
.--
.-- .-
.- .-.
.-. --.
--. .-
.- --
-- .. ...
... .-.-.-
.-.-.- .-.
.-. .. -- ..---
..--- .-.-.-
.-.-.- ...
... -.--
-.-- ...
... -- .. --
-- ...
...
https://wargames.ret2.systems
Make Your Own Linux with Buildroot and QEMU Operating Systems
Make Your Own write our default configuration into the working .con-
fig file (this is what is used when building a particular
Linux with image), we type:
Buildroot and make q e m u x 8 6 6 4 d e f c o n f i g
Karol Przybylski
https://linuxdev.pl/
SAA-ALL 0.0.7 27
Programming Analyzing and Improving Performance Issues with Go applications
Analyzing and Improving Performance The next tip is to carefully decide between the builder
Issues with Go Applications / buffer structures, depending on the API you require.
I generally recommend using strings.Builder, its
The goal of every software developer should be to faster than bytes.Buffer
design and implement a fun-to-use product and slow
software is never fun-to-use. On the contrary, making BenchmarkBuffer-4 0.0000603 ns/op
your own software faster is always a joy - so let me BenchmarkString-4 0.0000466 ns/op
tell you about some things I found fun and practical BenchmarkBufferLarge-4 0.004109 ns/op
when reworking Go applications around performance BenchmarkStringLarge-4 0.003431 ns/op
improvements. If these options are too slow for you, con-
Let’s start off with some tales of mine and why you sider buffering in your own []byte and
should even read this: I wrote an article about a leet- use *(*string)(unsafe.Pointer(&buf)) or
code solution and how I made it significantly faster1 . unsafe.String(unsafe.SliceData(buf), len(buf)),
I created a programming language and made it 8 this reuses the memory already stored at []byte.
times faster afterwards2 . I wrote a paper about the The latter option can half the time spent in string
garbage collection implementations of programming concatenation - however, both approaches use the
languages with a friend and Go was featured in it3 . unsafe package which makes no guarantees for
Furthermore, I implemented a just-in-time compiler portability and compatibility 6 .
and made a Go runtime for a programming language,
making it 14 times faster4 . I also am currently work- BenchmarkArray-4 0.0000222 ns/op
ing on a JSON parser for Go that’s already beating BenchmarkArrayLarge-4 0.002167 ns/op
the encoding/json package 5 . If you consider a function to be hot, e.g. it being
called often and in loops, you should switch from a
Analyzing Applications generic function to a specific function - if applicable.
To find areas for improvement, we can use the pack- func generic[T any](data any) (T, bool) {
age Go provides for this exact purpose. This isn’t the v, ok := data.(T);
place to discuss specifics, but I recommend tinkering if !ok { var e T; return e, false }
with the package. return v, ok
}
package main;import p"runtime/pprof" func specific(data any) (bool, bool) {
func main() { switch data.(type) {
f, _ := os.Create("cpu.pprof") case bool:
p.StartCPUProfile(f) return true, true
defer p.StopCPUProfile() default:
// logic here return false, false
} }
}
Another way for conducting an analysis is to use
The results are very situation dependent and require
hyperfine for comparing the performance of two bi-
lots of benchmarking.
naries.
BenchmarkGeneric-4 0.0003623 ns/op
BenchmarkSpecific-4 0.0003494 ns/op
Performance Tips for Go
To minimize the usage of expensive syscalls and
Let’s look at some specific Go tips and tricks for low batch input/output actions, the bufio package should
hanging, fast universal changes one can make to get always be used for files and other file-like structures.
better performance out of your existing code. The The final tip is to use (*bytes.Reader).ReadByte in-
first specific tip is to start preallocating slices and stead of (*bytes.Reader).ReadRune.
maps with values determined with benchmarks:
BenchmarkReadByte-4 0.0004150 ns/op
// don't BenchmarkReadRune-4 0.0008462 ns/op
a := []byte{}
Tu sum up: always benchmark all changes and note
m := map[string]int{}
their improvements. If you make a lot of long liv-
// do
ing copies, as is often the case with interpreters and
a := make([]byte, 0, 16)
parsers, either use an arena or pointers - copying
m := make(map[string]int, 16)
can be expensive if there are a lot of those. Always
1 https://xnacly.me/posts/2023/leetcode-optimization/
search for fast paths, the goal is to always do less,
2 https://xnacly.me/posts/2023/language-performance/
3 https://xnacly.me/papers/modern_algorithms_for_gc.pdf
look for early returns, such as edge cases for zero
4 https://xnacly.me/papers/tree-walk-vs-go-jit.pdf values and such.
5 https://github.com/xNaCly/libjson 6 https://pkg.go.dev/unsafe
xnacly
Blog: https://xnacly.me
28 Github: https://github.com/xnacly SAA-TIP 0.0.7
King Skull Art
parigraf/pix
Instagram : @parigrafpix
SAA-TIP 0.0.7 Artstation : https://www.artstation.com/parigraf 29
Programming Base64 Unused Bits Steganography
Base64 encoding is well known and used everywhere. There are, however, some less-known quirks related to it,
which are known only to... well, everyone who ever implemented Base64 encoding or decoding manually,
especially if it was tested on a large diverse test set.
Regardless, not everyone has done that, so let's share this one specific trick with everyone else :)
The trick is rather simple, but we do have to start with how Base64 actually works – and this is explained best
with a diagram (TL;DW Base64 basically maps every 3 raw bytes of input to 4 alphabet characters of output):
bit bit
7 byte 0 0 byte 1 byte 2
byte 0
unused
bits
byte 0 byte 1
unus
ed
bits
If you are into steganography, this should be enough for you. The unused bits are almost never checked by a
decoder (at least I don't believe I've seen one that would complain about it), meaning you can hide 2 or 4 bits of
data there. That's not a lot, but then again no one said you need to use only one Base64-encoded string.
P.S. Yes, this sometimes shows up on CTFs – be on a lookout for tasks with A LOT of Base64 strings.
These illustrations were originally published as part of this blogpost: https://hexarcana.ch/b/20240816-base64-beyond-encoding/
Gynvael Coldwind
HexArcana Cybersecurity GmbH
30 https://hexarcana.ch/ SAA-ALL 0.0.7
C++ Pitfalls Programming
int main() {
Operator precedence A a(5);
a = 3; // ???
Some time ago, I wrote code that had an "if" condition expression return 0;
like in the example below: }
int x = 2; But why does a = 3; compile? a is of class type and 3 is an int!
if (x & 1 == 0) Such functionality was "kindly" provided by the A's one-argument
std::cout << "true";
else constructor. It allows int values to be implicitly converted to this
std::cout << "false"; type. But why would we not want it to compile? Aren't implicit
Output: false conversions convenient? Maybe, at times, they are, but it can
When I ran my program, I noticed something wrong. After some easily introduce a bug. That is why it is good to always add the
time debugging it, the last thing to check was that "if" statement. explicit specifier to a declaration of a one-argument
When I removed the "== 0" part and negated the expression, it constructor which will prevent implicit conversions unless we are
finally worked! It was at this moment I realized that I completely sure that we need such implicit conversions in our code.
forgot about operator precedence rules. And so, if we look at the
reference list [1], the "==" operator is just above the "&" operator, Order of evaluation
making it being evaluated first. The lesson is to be more of a A word about the order of evaluation of expressions in C++ [4]:
defensive programmer e.g. by using parentheses in such cases.
Order of evaluation of any part of any expression, including order
Arithmetic conversion rules of evaluation of function arguments is unspecified (...). The
I noticed this thing when reading about arithmetic conversion compiler (...) may choose another order when the same
rules [2]. I have never caught a bug related to it, but it looks like expression is evaluated again.
an easy-to-introduce one, so I wanted to cover it here. This means we cannot expect any specific order of function calls
Suppose we have a code like below: in any expression. It's not limited only to function arguments
unsigned int x = 3u; evaluation. Below is an example of this. Please note that letters
int y = -5; could be printed in any possible order during the z() function call
if (y < x)
std::cout << "true"; and the return value calculation.
else int a() { return std::puts("a"); }
std::cout << "false"; int b() { return std::puts("b"); }
Output: false int c() { return std::puts("c"); }
For a human, it is logical that -5 is less than 3, but in C++ there are void z(int, int, int) {}
various integer types and in most operations data types need to
match. In this example, both operands of the "<" operator are int main()
converted to the unsigned int which makes the y variable {
z(a(), b(), c());
really big (the bytes holding the value are just reinterpreted as return a() + b() + c();
unsigned type) and greater than x. I encourage you to read about }
the conversion rules at least once as they are not trivial at times. Output: unspecified
Artur Nowicki
https://github.com/arturn-dev
SAA-ALL 0.0.7 [email protected] 31
Programming EasyMSXbas2wav
MSXBAS2wav
https://github.com/4nimanegra/EasyMSXBAS2wav
I=✩((✩I+1));
done;
}
wavheader(){
printf "RIFF";
printf "\xFF\xFF\xFF\xFF";
On this page, we will create a simple Bash script to printf "WAVEfmt ";
printf "\x10\x00\x00\x00\x01\x00\x01\x00";
encode MSX Basic programs into WAV files. printf "\x44\xac\x00\x00";
printf "\x44\xac\x00\x00";
MSX computers have different methods for encoding printf "\x01\x00\x08\x00";
AUX=❵printf "%08X" ✩((✩1))❵;
data on audio tapes. The way the bits are stored can AUX=❵awk ✬{print "\\\\x"substr(✩1,7,2)"\\\\x"substr(✩1,5,2)"\\\\x" \
substr(✩1,3,2)"\\\\x"substr(✩1,1,2)}✬ < <(printf "✩AUX")❵;
involve encoding zeros as square waves using either 1200 printf "data✩AUX";
}
Hz or 2400 Hz. Ones are encoded using a square wave encodefile(){
if [ -e ✩1 ]; then
that is twice as fast as the signal for zeros. In this work, BYTELEN=❵cat ✩1 | tr "\n" "\r" | xxd -i | tr "\n" " " | sed s/" "/""/g | \
awk -F "," ✬{print NF}✬❵;
we have encoded the data by using 1200 Hz for zeros CONT=0;
while read A; do
and 2400 Hz for ones. if [ ✩CONT == ✩((256*11)) ]; then
silence;
header;
CONT=0;
fi;
encode ✩A;
CONT=✩((CONT+1));
done < <(cat ✩1 | xxd -b | sed s/".*:"/""/ | sed s/"^ "/""/ | sed s/"[ ][ ].*"/""/ | \
sed s/" "/"\n"/g | awk ✬{print "0";I=8;while(I>0){print substr(✩1,I,1);I=I-1;}print \
"1";print "1";}✬);
Each byte should be encoded by preceding it with a if [ "" == "✩2" ]; then
ZEROS=✩((256-✩BYTELEN));
zero and using two ones as a trailer. Thus, each byte is II=0;
while [ ✩II -lt ✩ZEROS ]; do
encoded using 11 bits. for III in 0 0 0 0 0 0 0 0 0 1 1; do
encode ✩III;
done;
II=✩((✩II+1));
done;
fi;
fi;
}
The stored data begins with a header consisting of a long msxheaderfile(){
echo -n "" > tmp/headerfile.tmp;
beep: a sequence of ones over 16,000 pulses, followed II=0;
while [ ✩II -lt 10 ]; do
by the byte 0xEA repeated 10 times and the program printf "\xEA" >> tmp/headerfile.tmp;
II=✩((✩II+1));
name in 6 bytes. After this, blocks of 256 bytes are done;
echo "✩1" | awk -F "/" ✬{printf substr(✩NF,1,6);}✬ >> tmp/headerfile.tmp;
encoded, each preceded by a short beep (ones over 4,000 }
lastblockfile(){
pulses). At the end, an additional block with the byte echo -n "" > tmp/lastblockfile.tmp;
II=0;
0x1A repeated 256 times is added. while [ ✩II -lt 256 ]; do
printf "\x1A" >> tmp/lastblockfile.tmp;
II=✩((✩II+1));
done;
}
helpme(){
echo "✩0 is used to convert bas MSX basic programs into wav files.";
printf "\t✩0 command must me used with 2 arguments:\n";
printf "\t\t✩0 file.bas output.wav\n"
echo "";
printf "\tfile.bas: The name of the file whith the basic program in ascii.";
echo "";
echo "";
printf "\tfile.wav: The name of the output file where the wav is created.";
echo "";
echo "";
}
if [ "" != "✩1" ]; then
The code shows as follow: if [ "" != "✩2" ]; then
if [ -e "✩1" ]; then
#! /bin/bash if [ -e "✩2" ]; then
TEMPDATAFILE="tmp/tempdata.tmp"; echo "✩2 exists, please remove it before run the program.";
encode(){ else
BIT=✩((✩1)); touch ✩2;
if [ ✩BIT == 0 ]; then if [ "✩?" == "0" ]; then
PARAM=18; mkdir tmp 2> /dev/null;
TOTAL=1; echo -n "" > ✩TEMPDATAFILE
else header "long";
PARAM=9; msxheaderfile ✩1;
TOTAL=2; encodefile "tmp/headerfile.tmp" "NOZEROS";
fi; silence;
while [ ✩TOTAL -gt 0 ]; do header;
I=0; encodefile ✩1;
while [ ✩I -lt ✩PARAM ]; do silence;
printf "\xC0" >> ✩TEMPDATAFILE; header;
I=✩((✩I+1)); lastblockfile;
done; encodefile "tmp/lastblockfile.tmp" "NOZEROS";
I=0; TOTALLONG=❵ls -al ✩TEMPDATAFILE | awk ✬{print ✩5}✬❵;
while [ ✩I -lt ✩PARAM ]; do wavheader ✩TOTALLONG > ✩2;
printf "\x40" >> ✩TEMPDATAFILE; cat ✩TEMPDATAFILE >> ✩2;
I=✩((✩I+1)); rm tmp/headerfile.tmp tmp/lastblockfile.tmp tmp/tempdata.tmp
done; else
TOTAL=✩((✩TOTAL-1)); echo "Can not write on ✩2.";
done; fi;
} fi;
header(){ else
if [ "long" == "✩1" ]; then echo "✩1 does not exists.";
PULSES=8000; fi;
else else
PULSES=2000; helpme;
fi fi;
II=0; else
while [ ✩II -lt ✩PULSES ]; do helpme;
encode 1; fi;
II=✩((✩II+1));
done;
} The script should be executed as follows:
silence(){
I=0; ./convert.sh Easytr0n.bas ./out/Easytr0n.wav
Garcia-Jimenez, Santiago
https://github.com/4nimanegra
32 CC BY 4.0
www.trailofbits.com
References
Pure-Rust
Appsec Testing implementation
Handbook of SLH-DSA
Curated list of ML
ZKDocs
security resources
Sándor Dargó
Blog: https://www.sandordargo.com/
34 X/Twitter: @SandorDargo SAA-ALL 0.0.7
Mobile Coding Journey Programming
When I was a child, I loved building things with On other phones, I saw this game where you start
Lego bricks. You have some basic building blocks, as a small fish and grow by eating other smaller
you combine them in all sorts of fascinating ways, fish. Let’s implement that for starters.
you create structures. You can build a car or a
truck, an airplane or a ship. It felt awesome. Why Need to draw sprites, let’s use mobile PaintCAD:
buy pre-built toys at all? Give me more Lego
bricks!
Artem Zakirullin
https://twitter.com/zakirullin
SAA-ALL 0.0.7 https://github.com/zakirullin 35
Art New Inhabitants
Dmitry Petyakin
https://www.instagram.com/dmitrypetyakin/
36 https://www.artstation.com/el-metallico SAA-NA 0.0.7
My journey in KDE and FOSS Programming
I was a lucky, of course, but I also have skills that I would And by critique, I mean respectful discussion. That is
have never gotten if I had never started contributing. something to remember when you're the one giving the
Sure I could write code, but with open source, social critique as well.
skills are also really important, since you work with
people all day, in the open. I keep working on all those # YOU MAY ENCOUNTER NASTY PEOPLE
skills every single day. I have to put this here because it really is a thing. It's a sad,
sad thing. But it's something you may have to face. So be
For anyone else interested in working at open source, I prepared for it, but do not let it get you down.
do not have any surefire ways to get there but it is
possible. For one naysayer, there's usually 9 others who like the
changes you made.
IF YOU HAVE THE DRIVE FOR IT AND KEEP
HONING YOUR SKILLS FOR IT, YOU # REMEMBER TO REST
MIGHT BE CLOSER THAN YOU THINK. Working on open source causes people to burn out very
often, especially if one has to deal with rude people. It's good
to just completely distance yourself from the project from
time to time, and let your body and mind rest.
The project and the other contributors will wait for you to
return. I do wish I took this advice more often myself!
Akseli Lahtinen
Blog: https://www.akselmo.dev
CC BY-SA 4.0 Mastodon: https://scalie.zone/@aks 37
Programming On Hash maps and their shortest implementation possible
const size_t BASE = 0x811c9dc5; Pointer Extraction Extracting a pointer works the
const size_t PRIME = 0x01000193; same way as the insertion: computing the hash and
size_t hash(Map *m, char *str) { returning the value at the index:
size_t initial = BASE;
while(*str) initial ^= *str++ * PRIME; void *get(Map *m, char *str) {
return initial & (m->cap - 1); return m->buckets[hash(m, str)];
} }
The first things to notice is the two constants required Usage Example The callee of the map functions
by fnva-1a, the parameter of the hash function of the can even insert pointers to stack variables, even if
Map type and the bitwise and in the return statement. they do not outlive the scope. They also have to free
The m parameter is used specifically in combination the allocated bucket array.
with the bitwise & to restrict the resulting hash to
the size of the underlying array, thus eliminating out int main(void) {
of bounds errors - this way of computing modulo is Map m = init(1024);
faster than initial % (m->cap-1), but only works for double d1 = 25.0;
the cap being a power of two. We control the size of put(&m, "key", (void *)&d1);
the map, thus we can keep this in mind. printf("key=%f\n", *(double *)get(&m, "key"));
free(m.buckets);
1 https://en.wikipedia.org/wiki/Hash_collision return EXIT_SUCCESS;
2 https://docs.oracle.com/javase/8/docs/api/java/lang/String.html
}
3 https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function
xnacly
Blog: https://xnacly.me
38 Github: https://github.com/xnacly SAA-TIP 0.0.7
The Hitchhiker's Guide to Building a Distributed Filesystem in Rust. The beginning...
Programming
Radu Marias
https://xorio.rs
Public Domain 39
The Hitchhiker’s Guide to Building an Encrypted Filesystem in Rust Programming
The Hitchhiker’s Guide to inodes (which ends up in catastrophic failure), we keep se-
Building an Encrypted quences in keyring too and use max(keyring, data_dir).
Limits: if the instance_id is u8, the max inode (u64)
Filesystem in Rust is reduced to 256 - 3. It’s -3 and not -1 because inode
0 is not used, and 1 is reserved for root dir, so we’re left
with value 72,057,594,037,927,933. And max data to en-
BEGINNING: It all started after I began learning Rust crypt (3.09485009821345068724781055 * 1026 - 1) *
and wanted an interesting learning project to keep me mo- 256 KB, which is 7.92281625142643375935439 * 1013
tivated. Initially, I had some ideas, then consulted Chat- petabytes.
GPT, which suggested apps like a Todo list :) I pushed it Using ring for encryption will extend to RustCrypto
to more interesting and challenging realms, leading to sugges- too, which is pure Rust. First time, we generate a random
tions like a distributed filesystem, password manager, encryption key and encrypt that with another key derived
proxy, network traffic monitor... Now, these all sound from user’s password using argon2. We use only AEAD
interesting, but maybe some are a bit too complicated for a ciphers, ChaCha20Poly1305 and Aes256Gcm. Creden-
learning project, like the distributed filesystem. tials are kept in mem with secrecy, mlocked when used,
IDEA: My project idea originated from having a work- mprotected when not read and zeroized on drop. Hash-
ing directory with project information, including some pri- ing is made with blake3 and rand_chacha for random
vate data (not credentials, which I keep in Proton Pass. numbers.
I synced this directory with Resilio across multiple devices DATA-PRIVACY: We aim to offer true privacy and
but considered using Google Drive or Dropbox, but hey, for that we need to make sure we hide all metadata, con-
there is private info in there, so it is not ideal for them to tent, file name, file size, *time fields, files count, direc-
have access to it. So a solution like encrypted directo- tory structure and that all of these are encrypted. File-
ries, keeping the privacy, was appealing. So I decided to name and content are easier to hide; we just encrypt them
build one. I thought to myself this would be a great learning and pad filenames to fix the size, and we’re fine. But file
experience after all. And it was indeed. size, file count, *time fields, and directory structure are not
From a learning project, it evolved into something more, trivial. For that, we split the file in chunks, and each is like
which will soon be released as a stable version with many an item in a LinkedList on disk with next pointer kept
interesting features. You can view the project https:// encrypted inside chunk file content. This hides the actual
github.com/radumarias/rencfs. file count, but we add dummy nodes at the beginning with
FUSE: I used it before, and I could use it to expose the random data to hide it even more. Also, we add dummy
filesystem to the OS to access it from File Manager and random data to each chunk at the beginning (as it’s easier to
terminal. I looked for FUSE implementations in Rust and skip), so we hide the file size even more. All these hide file
found fuser, and later migrated to fuse3, which is async. I sizes, file count, and *times fields. This creates a problem:
began with its examples. how do we get to the root chunk files (nodes) without an at-
IN-MEMORY-FS: I started wth a simple in-memory tacker being able to do the same, given our code is publicly
FS using FUSE, where I learned more about smart point- available on GitHub? For that, we keep an index file with
ers like Box, Rc, RefCell, Arc and lifetimes. Aargh... all root chunk files (inodes, actually). What’s remaining
lifetimes, would say many, one of the most complicated con- is directory structure in the sense of the directories in-
cept in Rust, after the Borrow-Checker. They are quite side another directory. For this, we do similarly, we create
complicated at first, but after you fight them for a while, you dummy folders with random names so we hide how many ac-
bury the hatchet, and then they are easier to live with. After tual directories are there, and we keep all these in the index
you understand how and why the compiler lets you do things, file.
you understand that’s the correct way to do it, and it saves FILE-INTEGRITY: "There’s The Great Wall, and then
you from many problems, and you appreciate it. After all, there’s this: an okay WAL.". WAL(Write-ahead logging)
these are the promises of Rust, memory safety, no data is a very common technique used in DBs world for writing
race, and reduces race conditions. And indeed, it lives transactions to ensure file integrity. I’m using okaywal.
up to its promise. You need to come from other languages SEEK: To support fast seeks, we encrypt file in blocks
where you had all sorts of problems to really appreciate what of 256KB. When we need to seek on read, we translate
Rust is offering you. from plaintext offset to ciphertext block_index, and de-
crypt that block. We actually impl Seek on the same Read
STRUCTURE: I started with a simple one that keeps
struct. For seek on write it’s a bit more complicated, we
the files in inode structure, each metadata is stored in inodes
need to act as reader too. First, we need to decrypt the block,
dir in a file with inode’s name and in contents directory we
then write to it, and when at the end of the block encrypt
have files with inode’s name with the actual content of the
the block and write it to disk. Because Rust doesn’t have
file.
method overwriting, the code is not as clean as for the
MULTI-NODE: We must run in multi-node, as the
reader, where we only extend.
folder will be synced over several devices. The app could
WRITES-IN-PARALLEL: Using RwLock we allow
run in parallel or even offline. We must generate unique
reading and writing in parallel and we resolve conflicts with
inodes for new files. Solution is to assign an instance_id
WAL. It is particularly useful for torrent apps that write
as a random id to each device (or to set it by command arg,
different chunks in parallel, but also for DBs.
which is safer) and generate as instance_id | inode_seq,
STACK: See more https://github.com/radumarias/
where inode_seq is a sequence/counter for each device.
rencfs?tab=readme-ov-file#stack.
SECURITY: We do the same for nonce, instance_id
| nonce_seq. The sequences we keep in data_dir in a
per instance folder. To resolve the problem where user re-
stores a backup and hence would reuse nonces and reuses
Radu Marias
https://xorio.rs
Public Domain 41
Art Problematic communication
aliquid
X/Twitter: @_aaliquid
42 ArtStation: https://artstation.com/aliquid SAA-TIP 0.0.7
Understanding State Space with a Simple 8-bit Computer Programming
Understanding may think that the number of programs that can be im-
plemented is extremely small. You may be surprised by
State Space with a the answer.
Let’s start by computing the size of the computer’s
Simple 8-bit state space. To simplify the discussion, let’s only con-
sider the computer’s RAM. There are 256 bytes of mem-
Computer ory and each byte has 256 bit permutations, so the state
space of this computer is 256256 or 3.23*10616 states.
This is an amazingly large number of states. For com-
parison, it is estimated that there are only 1080 atoms
State space is an important concept in computer sci-
in the universe [3].
ence as it allows us to determine key fundamental limits
While state space gives us the upper bound of the
of a computational model. In binary computers, each
number of bit permutations RAM could be in, the com-
component of the computer can be represented by one
puter’s ISA severely restricts the number of valid pro-
or more binary digits or bits. The set of these compo-
grams that can be executed. I define valid programs
nents at any moment, represented by these bits, is the
as those constructed from implemented opcodes. Any
computer’s state. This state can change billions of times
opcode values not implemented are considered invalid.
per second as the computer executes code. State space
In order to calculate the upper bound of the number of
is the collection of all states the computer could ever be
valid programs that can be created with this computer,
in [1]. You may think that a computer could represent
we need to understand a bit more about this computer’s
an infinite number of states, but it is actually finite for
ISA. As mentioned, there are only 10 implemented op-
any computer we could build, though the state space is
codes. 8 of the opcodes require an operand byte that
very large as we will see.
could have 256 bit permutations. So, 8*28 + 2 opcodes
I have created a simple 8-bit computer, built within
that don’t need an operand = 2,050 valid instructions
Logisim (http://www.cburch.com/logisim/) to illus-
in the ISA. (We will ignore the fact that the two re-
trate. This simple 8-bit computer allows us to better
maining opcodes do actually require an explicit, padded
understand how computers function at the lowest level.
operand in this fixed-length ISA.) So, the upper bound
I have included the Logisim file and a Python emulator
of the number of valid programs is 2050128 , since we can
of this simple 8-bit computer at: https://github.com/
fit 128 two-byte instructions in the 256 bytes of RAM.
meuer26/Simple-8-bit-Computer .
This is 8.0*10423 valid programs.
Again, an amazingly large number of valid programs
for this simple 8-bit computer with 256 bytes of RAM.
Very large but finite. The state space of this com-
puter is 3.23*10616 and the number of valid programs
is 8.0*10423 . So, we can think of the ISA as a lower-
dimensional structure in the higher-dimensional state
space of the computer. It is also now clear that the
memory of the computer is what determines the state
space, while the ISA dictates what a valid program could
be. The number of valid programs necessarily must be
equal or smaller than the state space of the computer.
If this computer had a hard drive, the state space would
need to be computed based on the size of the hard drive
(since RAM could be swapped to the hard drive in that
scenario). I’ll leave it to the reader to compute the state
Figure 1: A Simple 8-Bit Computer space of their modern computer and the number of pos-
sibly valid programs based on their ISA.
This computer is a Von Neumann architecture and a
RISC machine. This computer’s Instruction Set Archi-
tecture (ISA) only has 10 implemented opcodes and yet References
it possesses the primary characteristics for Turing Com-
pleteness: (1) the ability to read and modify memory, [1] C. Moore and S. Mertens, The nature of computa-
(2) the ability to branch for program control (including tion. OUP Oxford, 2011.
conditional branching), and (3) the ability to do arith- [2] P. A. Laplante, “A novel single instruction computer
metic operations [2]. It is, therefore, capable of universal architecture,” ACM SIGARCH Computer Architec-
computation, or has the ability to implement any com- ture News, vol. 18, no. 4, pp. 22–26, 1990.
putable function (assuming enough memory). This as-
sumption of enough memory is a major distinction and [3] E. Babb, “Calculating the amount of dark energy
key to our understanding of state space. This simple in the universe using a novel space energy theory of
8-bit computer only has 256 bytes of memory, so you gravity,” Academia,(Just use Google).
Daniel O'Malley
X/Twitter: @binarywonder
SAA-TIP 0.0.7 43
Programming Using QR codes to share files directly between devices
Suppose you have a file / some data you’d like to share that cannot or should not
live on any machines other than the ones it is to be shared between. Perhaps:
● the data is sensitive
● the host has no network access
● there’s no computer at all! It’s just raw digital data IRL
There are plenty of options for transferring data, but in 2024, few are more
practical than QR codes. They are trivial to generate and display (hand draw one in the
dirt if you want!) and it is reported that a majority of Earth’s human inhabitants now
own a smartphone. I can’t confirm that all of those have QR scanners baked in, but
hopefully you’ll allow me to assume that most of them do. Point being: QR codes are cheap
and ubiquitous. They’re also content-agnostic, which is great! You can encode any chunk
of binary data as long as it fits within the 2,953 byte limit.
However, we hit a snag when we consider the “no internet” constraints defined above:
The default QR scanner apps on both iOS and Android desperately want to hand you off to
your web browser and pretty much force you to send your data to a third party before
letting you access it. In the best case, you’ve scanned an HTTP/S URL and website loads
or you’re deep-linked into a pre-installed app. In most other cases, you are prompted to
perform a web search with the contents of the QR code. iOS won’t even let you do that in
some cases - you cannot view/interact with a scanned data URL, for example. To work
around this, and to prevent potential file recipients from having to manually install a
custom scanner application, I’ve created a simple web app that parses the URL it was
accessed from as a base64-encoded file, and then hands the decoded version of that file
back to the accessor as a standard browser download:
<!DOCTYPE html>
<html>
<body>
<script>
function downloadBase64File(base64Data, filename) {
const ascii = atob(base64Data);
const bytes = new Array(ascii.length);
for (let i = 0; i < ascii.length; i++) {
bytes[i] = ascii.charCodeAt(i);
}
const byteArray = new Uint8Array(bytes);
const blob = new Blob(
[byteArray],
{ type: "application/octet-stream"}
);
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = filename;
// :P simulate a click to trigger download
document.body.appendChild(link); The QR code above contains a
link.click(); miniature PNG version of the Paged
document.body.removeChild(link); Out! logo. The data backing this
} version of the image only exists as
const params = new URLSearchParams(window.location.search); the black and white squares
// use fragment so data not sent to server rendered in this PDF - it is not
// idea: @[email protected] hosted on any other server (until
const data = window.location.hash.substring(1); you scan and download it, if you’re
// the filename used for the download should be feeling brave). Note that scanning
// passed in as a query param: 'f' will direct your browser to the web
const filename = params.get("f"); app over there <- (currently hosted
downloadBase64File(decodeURIComponent(data), filename); via Github Pages) but the image
</script> data itself should never leave your
</body> phone.
</html>
Once you have this URL, use your favorite QR code generator to QRify it.
2. The key here is that the file data is located in the URL fragment (the bit following
the ‘#’). URL fragments are (theoretically) only used by browsers and should not be
sent out with a network request. I encourage you to verify this yourself!
3. Yes, the device scanning your code will need internet access, but only to retrieve
the HTML file above. Again, we’re operating under the assumption that most people
are out in the world, scanning with their smartphones.
4. It was inspired by ‘Itty Bitty’: https://itty.bitty.site
Guy Dupont
Portfolio: https://www.guycombinator.net
44 Project Source: https://github.com/dupontgu/qr-file-share SAA-POOL 0.0.7
WebDev... in SQL ? Programming
WebDev… in SQL ?
$ sqlite3
sqlite> select introduction from article;
The tool I'd like to present here is a single executable le that lets you build full-stack applications
with nothing but… **SQL queries** !
sqlite> select answer from faq where question = 'That sounds like a terrible idea';
# Why it works
Yes, making a web page entirely in SQL sounds like heresy. But *SQLPage* makes it work by
providing **ready-to-use components** that take data in from your SQL queries, and produce nicely
styled HTML. It also exposes URL parameters and form elds as SQL prepared statement parameters.
For some applications, the traditional separation of frontend, backend, and database brings more
overhead than bene ts. By collapsing these layers into just SQL, SQLPage makes building web apps
accessible to people who don't have the time to learn the Javascript framework *du jour* every day.
Write a .sql le, connect your Postgres, MySQL, SQL Server, or SQLite db, and you have a website.
| code | result |
+------------------------------------+--------------------------------------------+
| select 'list' as component; | |
| select | |
| word as title, | |
| 'plus' as icon; | |
| from greetings; | |
| | |
+------------------------------------+--------------------------------------------+
| select 'form' as component; | |
| select Pet as name; | |
| | |
| insert into pets (name) | |
| select :Pet | |
| where :Pet is not null; | |
| | |
| select 'table' as component; | |
| select * from pets; | |
| | |
+------------------------------------+--------------------------------------------+
Ophir Lojkine
https://x.com/ophir_dev
Public Domain https://ophir.dev 45
Retro Games retro and love if Forth code then
In this article, I would like to talk about how most decom- To successfully inject an impossible index for the stack vari-
pilers manage to infer about the allocation of local variables within ables:
the function. As soon as the disassembly phase, which involves
transforming bytes into understandable instructions, is completed, 1. create a dead execution branch that will never be executed at
the decompiler begins its work by applying a series of fixed-point runtime (e.g., via conditions that we know are a priori, always
analyses, such as dataflow analysis (constant propagation, liveness), true or always false via opaque predicates).
and the time comes when it must try to reconstruct the local 2. mention access to a local variable placed in a very high or very
variables of a function. That is, figuring out which variables the low value of the stack base pointer in the branch never executed.
low-level code uses are allocated on the stack, destroyed as soon as
the subprocedure call returns the value. The stack is in fact used Note that this also works for values that go above the base of the
for three main purposes: to pass arguments from function callee stack pointer, i.e., the arguments (much also depends on how the
to function called, to allocate temporary variables that are valid stack is constructed, whether upward or downward). In addition to
only for the scope of the function, and to store the return address destroying local variables recognition, it is possible to cause the de-
that is retrieved when a return statement is encountered within the compiler to make very different assumptions about how arguments
function. are passed at runtime, making it almost impossible to recognize the
usual call conventions.
1 # define h u g e _ s p _ p r e d i c a t e _ f o r _ l o c a l _ v a r i a b l e s \
2 The semi-naive algorithm 2 __asm__ ( " push rax \ n " \ // mem [ sp ] = rax
3 " xor eax , eax \ n " \ // eax = eax ^ eax
4 " jz live_branch \ n " \ // is eax == 0?
The most common decompilers – including Binary Ninja, IDA, 5 " and ecx , [ rbp - 123456] \ n " // huge value
and Ghidra – may use a very naive version of the variable retrieval 6 " live_branch : \ n " \ // always true branch
algorithm that works based on index within the base pointer 7 " pop rax \ n " ) ; // rax = mem [ sp ]
8
register, also called BP-frame heuristic. It is much easier to write 9 # define h u g e _ s p _ p r e d i c a t e _ f o r _ a r g u m e n t s \
an example than to explain it: within the disassembly code, we 10 __asm__ ( " push rbx \ n " \ // mem [ sp ] = rax
have several mentions of the base stack pointer. Instructions of 11 " xor ebx , ebx \ n " \ // eax = eax ^ eax
12 " jz live_branch_2 \ n " \ // is eax == 0?
the type mov [ebp-0x14], eax are actually converted to simple 13 " and ecx , [ rbp + 123456] \ n " // huge value
MEM[ebp-0x14] = eax by an operation called lifting. This allows 14 " live_branch_2 : \ n " \ // always true branch
15 " pop rbx \ n " ) ; // rax = mem [ sp ]
the decompiler to be able to immediately say that what is pointed 16
to the address of ebp minus 0x14 must take the contents of the 17 // where needed
general register eax. When we find ebp - 0x14 or esp - 0x14, we 18 huge_sp_predicate_for_local_variables ;
19 huge_sp_predicate_for_arguments ;
are most likely referring to a local variable at address -0x14 named
in most cases var 14.
If you recognized the dead branch, fixing it is very simple for an
The decompiler recognizes that address in memory and assumes analyst in IDA: you can click the portion of the assembly code that
that a new variable has been declared within the ”hypothetical” you think is dead and with right-click use the Undefine option. An-
high-level source code. The decompiler runs through the entire other alternative is to manually edit the stack via the Edit function
instruction set of the basic blocks on which a function is built option, or change the heuristic for finding the local variables.
on. The algorithm keeps track of all the accesses on the stack:
for each new index it encounters on the stack, it allocates a new
variable whose type it does not yet know but knows that there
is a write/read at that address. The stack analysis algorithm is
thus completed by going to check where parameters are saved and
further checks are needed to ensure the analysis is sound (such
as stack balancement, and checking if there are any overlapping
variables).
Seekbytes
https://nicolo.dev
48 https://twitter.com/nicolodev SAA-ALL 0.0.7
Examining USB Copy Protection Reverse Engineering
Examining USB So far it holds the line. I reviewed all the info I had
and listed a few options to try. I can reverse engineer
Copy Protection the application and figure out how it works. This is defi-
nitely going to work, but it can be very time-consuming.
I can also hook the kernel32!ReadFile API and dump the
A while ago, a friend of mine asked me whether it is content as they are read. But if the PDF reader does
possible to prevent people from copying the files on a not read all of the file at once, my dump would be in-
USB thumb drive. Specifically, the files are PDFs and complete.
are his intellectual work. He wishes that people could The core problem is that the PDF reader is reading
read them, but could not copy them to another location, the file just fine, but I cannot read it using another tool.
e.g., the hard drive of a computer. What could be making the difference? I made some edu-
In other words, he wants a DRM solution. Intuitively, cated guesses and figured it is likely that the application
his goal is hard to achieve, since being able to view the is enforcing some access controls on it. Maybe it checks
files means the PDF reader can access the content of the whether the process that tries to read the file is a sub-
file, and there is no easy way to prevent it from writing process of itself, or it validates the path of the requesting
it elsewhere. Encrypting the files is not enough, since process, etc. There are plenty of ways to do it.
the files still have to be decrypted before the PDF reader Soon, I had an Eureka moment – regardless of the
can process them. actual access control policy, we know the PDF reader is
allowed to read the file. If we inject our code into it,
then it is very likely it just works. I quickly wrote some
1 How does it work? simple code to read the file and write it to a different
location.
I purchased one of the USB copy protection solutions I compiled it into a DLL and injected it into the PDF
and the product looks like a regular thumb-drive (and it reader process with Cheat Engine. And it works – the
is!). I inserted it into my computer and found an appli- file is successfully copied to the hard drive!
cation on it. I launched it and it asked me to configure
an admin password and a guest password. Then it pre-
sented an explorer-like GUI that allows me to add files 3 Remarks
into it. The idea is that I use the admin password to
add files into it, ship the drive to the user, who uses the Now that we have broken the myth of the USB copy
guest password to view the files. protection – let us think from the other side and see
I added a “test.pdf” into the root directory of the whether the protection can be improved. Though I do
drive. Files added into the drive are invisible in the not know how it works exactly, let us just assume it uses
Windows explorer, and can only be accessed using the the above mentioned access control policy and validate
application that comes with the drive. When the file is the file access requests. First of all, it can harden the
double-clicked, a PDF reader is launched and it opens PDF reader to make DLL injection harder, or, when a
the file. It is not the PDF reader on my computer – DLL injection is detected, reject the access.
the PDF reader comes with the drive. And it is rigged, Going deeper, the core issue is the file gets decrypted
so that the “Save As” option (among others) is missing too early. There is a clear boundary of encrypted and
from the menu. There is also no way to open the PDF decrypted file at the process level. In other words,
using an external reader that I can control. the manager application decrypts the file, and the PDF
I played around for a while and I did not see a triv- reader reads the original unencrypted file. This bound-
ial way to defeat it. I then checked the command line ary is so vulnerable and easily exploited. If it can move
arguments of the PDF reader, and I see it reads a file the decryption logic into the PDF reader, and decrypt
“Z:\test.pdf”. I suspect the “Z:\” drive is emulated by the file on the fly right before it gets parsed, then my
the application, and whenever someone tries to access a method will fail (it can only read the encrypted file).
file in it, the application kicks in and provides the appro- That said, these would not be easy to implement. Not
priate content. Something like this can be implemented only it starts getting closer to an anti-cheat/DRM so-
via minifilter 1 drive, though I do not know if it is the lution, it also means doing extensive modifications to
case for this particular product. the PDF reader, which makes the entire solution signif-
icantly more complex.
In the end, I presented my research to my friend and
2 Let us break it! explained that while the copy protection can be circum-
vented, it is still an option because the attack is be-
The first thing I tried is – can I access the file using its yond an average user. I also suggested a different solu-
path directly? I tried to copy it in PowerShell, or open tion based on watermarking, i.e., adding invisible water-
it with a normal PDF reader on my computer. Both marks on the PDFs (e.g., on the images), and each copy
failed. I got access denied on it. has a different watermark which can be used to identify
1 https://learn.microsoft.com/en-us/windows-hardware/ the leaker in the case of a leak. Still, it would not be
drivers/ifs/about-file-system-filter-drivers perfect – as is always the case with DRM.
Xusheng Li
https://xusheng.dev/
SAA-ALL 0.0.7 49
Reverse Engineering Lying with Sections
Revitalize!
The Revitalize approach offers most of the
advantages of full recompilation but for a fraction of
the effort. The big idea: keep most of the code the
same, but replace just a few definitions with
decompiled versions.
Project Ironfist, a game mod for Heroes of Might and
Magic II, created with Revitalize. www.ironfi.st The first step is to “unlink” a program by turning it into
a disassembly where all function and global variable
Revitalizing Binaries addresses are replaced by names. IDA can basically do
this, except that it uses its own dialect of assembly
So you have an old program and you want it to have that needs patching to be reassembled. But the magic
some new features, but you don’t have the source comes from making the assembly look like this:
code. What can you do?
IFDEF IMPORT_?SomeFunc@@namemangling
This article is a crash course on binary modification, ?SomeFunc@@namemangling PROTO SYSCALL
ELSE
the techniques used by hackers, game modders, and
?SomeFunc@@namemangling proc near SYSCALL
retro software enthusiasts to change software they <function definition>
don’t control. I’ll explain the main ways people do it. END
But also I’ve worked on a rare approach to binary
modification that I call Revitalize. I don’t think it’s that
That’s Microsoft Macro Assembly for “if a flag is set,
hard, but I’ve found almost no instances of anyone
declare SomeFunc as defined elsewhere, else define it
doing similar. Yet I think it has the best ROI for a lot of
here.” What does this let you do? Well, in a related file,
use-cases. I explain it here for the first time.
you write IMPORT_?SomeFunc@@namemangling=1.
How to modify binaries? Then you write a new SomeFunc in C/C++. And
suddenly all the old code uses your new SomeFunc
instead of the existing one. You can also have it
There are three main ways to modify a program
generate a copy of the original SomeFunc, so that your
without its source. The simplest is binary patching,
new SomeFunc can just wrap the old behavior.
where you just open the program in a hex editor. For
example, changing 0x0F84 to 0x0F85 swaps two
The first thing this lets you do is modify existing
if-branches. Done in the right place, it can let you run
functions in the same way as DLL injection, except
a program that checks for a physical CD on a laptop
that, after generating the specially-formatted
without a CD drive. Or if you find the table that says
disassembly, it’s mostly like normal programming in a
how many hit points each unit has, you can just
normal IDE. But the really cool thing is this also works
change it. But you can’t make the program bigger, so
for static data structures. Say your game has an array
you can’t really add new features – unless you find
defining the stats and assets of all the 70 unit types in
some unused bytes in the binary (a code cave).
the game, and you want to add a new unit. If you had
the original code, you would just edit the array and
The second is to DLL Injection: loading code alongside
add a new entry. Binary patching and DLL injection
the existing process that tweaks it somehow.
can’t do this. But with Revitalize, one simply types
Commonly, the new code will hot-patch some
IMPORT_?globalUnitsArray@@namemangling=1,
function by overwriting it in memory to jump to some
and then you can copy the decompiled array into a
new code, placed in freshly allocated memory. This is
C++ file, and modify it as easily as if you had the
enough to add major new features, and is done by
original code.
everything from Cheat Engine to the Magisk and Cydia
engines used to “tweak” jailbroken mobile devices.
And that’s basically it! You pretty much just need a
copy of IDA and a script to output a disassembly in
The third is to decompile and recompile the entire
this special format, which you can find at
program. But this is really hard. Decompilers today
https://tinyurl.com/binaryrevitalize. There are a few
are imperfect, and doing this requires manually
extra steps that won’t fit here, but I’ll happily coach
changing the entire program to get it to re-compile.
anyone interested in trying this.
Lots of room to add bugs.
Guy Sviry
github: https://github.com/guysv
SAA-TIP 0.0.7 53
May
Join Us for the 7th
26-
Edition of TyphoonCon! 30
Want to have your Don’t miss your chance [Location]
[training] considered to headline our 2-day Le Meridien Seoul
for TyphoonCon 2025? [conference] and enjoy Myeongdong
𝟶 𝟷 𝟸 𝟹 𝟺 𝟻 𝟼 𝟽 𝟾 𝟿
the past – Unicode
(thankfully) is widely
𝟘 𝟙 𝟚 𝟛 𝟜 𝟝 𝟞 𝟟 𝟠 𝟡
supported, and its support
has also reached the
aforementioned str-to-int
𝟢 𝟣 𝟤 𝟥 𝟦 𝟧 𝟨 𝟩 𝟪 𝟫
𝟬 𝟭 𝟮 𝟯 𝟰 𝟱 𝟲 𝟳 𝟴 𝟵
conversion.
For example:
Homoglyph attack example
(i.e. what happens if you display what you received
before doing the conversion).
● Python's int() supports
68 different digit groups. { "offer_value": "\u0b68\u0b68\u0b68" }
● Java's
Integer.parseInt()
supports 38 different digit
groups.
● On the flip side e.g.
JavaScript supports only Buyer's offer: ୨୨୨ USD
the standard "ASCII"
group. Might be different
for e.g. Node.js though.
Accept Reject
In general, support varies
between both languages (i.e.
their standard libraries),
frameworks, other libraries, int("୨୨୨") » 222
and so on.
Gynvael Coldwind
https://hexarcana.ch/
SAA-ALL 0.0.7 https://gynvael.coldwind.pl/ 55
Security/Hacking EasyHoneypot
EasyHoneypot
Santiago Garcia-Jimenez
if(read(clientSocket,&data,99) < 1){continue;};
data[99]=’\0’;sscanf(data,"%s",user);
write(clientSocket,"password: ",10);
https://github.com/4nimanegra/EasyHoneyPot memset(data,0,100*sizeof(char));
if(read(clientSocket,&data,99) < 1){continue;};data[99]=’\0’;
sscanf(data,"%s",pass);close(clientSocket);
The code implements a simple user and password- printf("%d:%s:TELNET:%s:%s\n",mytime.tv_sec,
logging honeypot, designed to detect lateral movements inet_ntoa(ipclient.sin_addr),user,pass);
fflush(stdout);clientSocket=0;}}
in cyberattacks. It handles authentication for FTP, int smtpHoney(){
SSH, Telnet, and SMTP services, displaying credentials, int smtpSocket, clientLen=0, clientSocket;struct sockaddr_in ip;
struct sockaddr_in ipclient;char data[100];
timestamps, and IP addresses on the screen. memset(data,0,100*sizeof(char));char user[100];
#include <libssh/libssh.h> memset(user,0,100*sizeof(char));char pass[100];
#include <libssh/server.h> memset(pass,0,100*sizeof(char));char *b64user,*b64pass;
#include <stdlib.h> struct timeval mytime;bzero((char *) &ip, sizeof(ip));
#include <string.h> ip.sin_family = AF_INET;ip.sin_addr.s_addr = htonl(INADDR_ANY);
#include <stdio.h> ip.sin_port = htons(2500);clientLen=sizeof(ipclient);
#include <sys/time.h> smtpSocket = socket(AF_INET,SOCK_STREAM,0);
#include <unistd.h> if(bind(smtpSocket, &ip , sizeof(ip))<0){return -1;}
#include <sys/types.h> listen(smtpSocket , 20);clientSocket=0;
#include <sys/socket.h> while(1 == 1){if(clientSocket != 0){close(clientSocket);}
#include <netinet/in.h> clientSocket = accept(smtpSocket,&ipclient,&clientLen);
#include <arpa/inet.h> gettimeofday(&mytime, NULL);write(clientSocket,
#include <pthread.h> "220 smtp.ezequiel.ca ESMTP server\r\n",35);
#include <openssl/pem.h> memset(data,0,100*sizeof(char));
#include <signal.h> if(read(clientSocket,&data,99)<1){continue;}; data[99]=’\0’;
struct sockaddr_in myip; if(strlen(data)>7){sscanf(&data[5],"%s",user);}else{
char *base64decode (const void *b64, int b64lon){ continue;};write(clientSocket,"250-smtp.ezequiel.ca Hello ",
BIO *b64_bio, *mem_bio;int index = 0; 27); write(clientSocket,user,strlen(user));
char *clean = calloc(b64lon,sizeof(char)); write(clientSocket,"\r\n",2);write(clientSocket,
b64_bio = BIO_new(BIO_f_base64()); "250 AUTH LOGIN\r\n",16); memset(data,0,100*sizeof(char));
mem_bio = BIO_new(BIO_s_mem());BIO_write(mem_bio, b64, b64lon); if(read(clientSocket,&data,99)<1){continue;};
BIO_push(b64_bio, mem_bio); sprintf(user,"AUTH"); while(strcmp(user,"AUTH")==0){
BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); write(clientSocket,"334 VXNlcm5hbWU6\r\n",18);
while ( 0 < BIO_read(b64_bio, clean+index, 1) ){index=index+1;} memset(data,0,100*sizeof(char));
BIO_free_all(b64_bio); return clean;} if(read(clientSocket,&data,99)<1){data[0]=’\0’;break;};
static int auth_password(const char *user, const char *password){ data[99]=’\0’;sscanf(data,"%s",user);}
return 0;} if(strlen(data)<1){continue;};
char *getClientIp(ssh_session session) { data[99]=’\0’;sscanf(data,"%s",user);
struct sockaddr_storage tmp; struct sockaddr_in *sock; write(clientSocket,"334 UGFzc3dvcmQ6\r\n",18);
unsigned int len = 100; memset(data,0,100*sizeof(char));
char *ip = (char *)malloc(100*sizeof(char));ip[0]=’\0’; if(read(clientSocket,&data,99)<1){continue;};
getpeername(ssh_get_fd(session), (struct sockaddr*)&tmp, &len); data[99]=’\0’;sscanf(data,"%s",pass);
sock = (struct sockaddr_in *)&tmp; write(clientSocket,"535 Bad password.\r\n",19);
inet_ntop(AF_INET, &sock->sin_addr, ip, len);return ip;} close(clientSocket);
int sshHoney(){ b64user=base64decode(user,strlen(user));
ssh_session session;ssh_bind sshbind;ssh_message message; b64pass=base64decode(pass,strlen(pass));
ssh_channel chan=0; char buf[2048]; int auth=0, sftp=0, i,r; strtok(b64user,"@ezequiel.ca");
struct timeval mytime; printf("%d:%s:SMTP:%s:%s\n",mytime.tv_sec,
while(1==1){ sshbind=ssh_bind_new(); session=ssh_new(); inet_ntoa(ipclient.sin_addr),b64user,b64pass);
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, free(b64user);free(b64pass);fflush(stdout);clientSocket=0;}}
"2200"); int ftpHoney(){
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, int ftpSocket, clientLen=0, clientSocket; struct sockaddr_in ip;
"./ssh_host_rsa_key"); struct sockaddr_in ipclient; char data[100]; char user[100];
gettimeofday(&mytime, NULL); memset(data,0,100*sizeof(char));char pass[100];
if(ssh_bind_listen(sshbind)<0){return -1; memset(user,0,100*sizeof(char));struct timeval mytime;
}else{r=ssh_bind_accept(sshbind,session); memset(pass,0,100*sizeof(char));bzero((char *) &ip, sizeof(ip));
if(r!=SSH_ERROR){if(!ssh_handle_key_exchange(session)) { ip.sin_family = AF_INET; ip.sin_addr.s_addr = htonl(INADDR_ANY);
auth=0; ip.sin_port = htons(2100); clientLen=sizeof(ipclient);
while(!auth){message=ssh_message_get(session); ftpSocket = socket(AF_INET,SOCK_STREAM,0);
if(!message)break; if(bind(ftpSocket, &ip , sizeof(ip))<0){return -1;}
if(ssh_message_type(message)==SSH_REQUEST_AUTH){ listen(ftpSocket , 20);
if(ssh_message_subtype(message)==SSH_AUTH_METHOD_PASSWORD){ while(1 == 1){
printf("%d:%s:SSH:%s:%s\n",mytime.tv_sec, clientSocket = accept(ftpSocket,&ipclient,&clientLen);
getClientIp(session),ssh_message_auth_user(message), gettimeofday(&mytime, NULL);write(clientSocket,"220 \r\n",6);
ssh_message_auth_password(message));fflush(stdout); memset(data,0,100*sizeof(char));
ssh_message_auth_set_methods(message, if(read(clientSocket,&data,99) < 6){continue;};
SSH_AUTH_METHOD_PASSWORD);}} data[99]=’\0’;user[0]=’\0’;sscanf(data,"USER %s",user);
ssh_message_reply_default(message);ssh_message_free(message); write(clientSocket,"331 \r\n",6);
}}}}ssh_disconnect(session);ssh_bind_free(sshbind); memset(data,0,100*sizeof(char));
ssh_finalize();}return 0;} if(read(clientSocket,&data,99) < 6){continue;};
int telnetHoney(){ data[99]=’\0’;pass[0]=’\0’;sscanf(data,"PASS %s",pass);
int telnetSocket,clientLen=0, clientSocket; write(clientSocket,"530 User cannot log in.\r\n",25);
struct sockaddr_in ip;struct sockaddr_in ipclient; close(clientSocket); printf("%d:%s:FTP:%s:%s\n",
char data[100];memset(data,0,100*sizeof(char));char user[100]; mytime.tv_sec,inet_ntoa(ipclient.sin_addr),user,pass);
memset(user,0,100*sizeof(char));char pass[100]; fflush(stdout);clientSocket=0;}}
memset(pass,0,100*sizeof(char));struct timeval mytime; int main(int argc, char **argv){
bzero((char *) &ip, sizeof(ip)); signal(SIGPIPE,SIG_IGN);
ip.sin_family = AF_INET;ip.sin_addr.s_addr = htonl(INADDR_ANY); pthread_t sshThread,ftpThread,telnetThread,smtpThread;
ip.sin_port = htons(2300);clientLen=sizeof(ipclient); pthread_create(&sshThread, NULL,&sshHoney, NULL);
telnetSocket = socket(AF_INET,SOCK_STREAM,0); pthread_create(&ftpThread, NULL,&ftpHoney, NULL);
if(bind(telnetSocket, &ip , sizeof(ip))<0){return -1;} pthread_create(&telnetThread, NULL,&telnetHoney, NULL);
listen(telnetSocket , 20);clientSocket=0; pthread_create(&smtpThread, NULL,&smtpHoney, NULL);
while(1 == 1){if(clientSocket != 0){close(clientSocket);} while(1==1){sleep(60);}}
clientSocket = accept(telnetSocket,&ipclient,&clientLen);
gettimeofday(&mytime, NULL);write(clientSocket,"user: ",6); This work was originally created for PagedOut and translated by the author for UnderD0cs Magazine 12.
memset(data,0,100*sizeof(char)); https://underc0de.org/foro/e-zines/underdocs-julio-2020-numero-12/ (It requires free registration).
Garcia-Jimenez, Santiago
https://github.com/4nimanegra
56 CC BY 4.0
Execve(2)-less dropper to annoy security engineers Security/Hacking
Hugo Blanc
Blog: https://syscall.cafe/
CC BY-SA 4.0 X/Twitter: @_angry_penguin 57
Security/Hacking Hackers' Favorite SSH Usernames: A Top 320 Ranking
Szymon Morawski
https://szymor.github.io/
58 CC0
Wizard's Inventory Art
There are 2+1 ways to create a Linux binary that work almost everywhere:
A self-contained binary with all the dependencies built in; usually SaaS clients or enterprise tools do that, as does
a bash script with binary stuff
A binary that will look in your machine for the dependencies, like usually the packages provided with Linux
distributions
Package the binary using the dependencies from the machine is running and redistributing it, like PyInstaller
does for python projects
It is clear that the binary generated will work only on the same architecture, so amd64 on amd64, arm64 on amr64 and so
on.
Compared to appimage/flatpack/snap, this solution doesn’t use a container with all the benefits and issues they have.
The story
I discovered this topic when I was contributing to github.com/sonic2kk/steamtinkerlaunch/ open source project that
needed a github.com/pvonmoradi/yad/ binary updated (GTK tool to create UIs for CLI scripts). At the end of my
experimentation, the project decided to update it but still keep the AppImage package instead of my pure Linux version
(which I prefer as an approach, honestly).
What I got was github.com/Mte90/yad-static-build/releases/ with a CI that automatically compiles Yad and generates this
static build so it doesn’t need any human interaction (like the project I was contributing to).
They don't trust this kind of build to be a tool that can run from a Steam Deck to a complete distribution but I tested the
outcome on Archlinux, Ubuntu and Debian with the same binary with no issues (also in a KDE environment).
How works
LD_LIBRARY_PATH is a predefined environment variable on Unix/Linux, it is very helpful and used a lot for Linux hacks.
The purpose of this variable is to change on runtime the dynamic/shared libraries (separated by a comma) loaded from
the linker with specific ones instead of the system avalaible. This is very powerful because in a Open Source example, we
can download a library, patch and use it for a specific program, in our case, we will use it for something different instead.
An example to run in your shell $ LD_LIBRARY_PATH="/opt/my_program/lib.so" /opt/my_program/start, as
you can see you can do easily a Bash script with this content.
With ldd, it is possible to list all the libraries needed by the program we want and investigate them, there is usually a lot
of them, and it can get boring to manually gather them all using UI tools. An example:
$ ldd /usr/bin/echo
linux-vdso.so.1 (0x00007f57cf2f8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f57cf0c6000)
/lib64/ld-linux-x86-64.so.2 (0x00007f57cf2fa000)
The tool
So, we have right now a way to gather a list of libraries, a way to inject them in the process but it can be handy to have a
binary that autoextract itself with all this stuff.
With this askubuntu.com/questions/537479/is-there-any-open-source-way-to-make-a-static-from-a-
dynamic-executable-with-no discussion, I discovered a handy tool (that I expanded but the original developer
implemented my changes after my Pull Request in a different way) that is github.com/oufm/packelf.
The fact that this tool is in Bash and generates a Bash auto extracting script allows also for modifying it easily with no
bundling tools for any needs including understanding how it works.
This tool generated a targizzed bash script with all the libraries (and the binary) from the machine you are running, the
libraries are extracted on runtime on /tmp/ in a folder and deleted when the process is closed (this behavior can be
disabled).
intigriti.com
Art lightstation
Grzegorz Tworek
https://x.com/0gtweet
SAA-TIP 0.0.7 https://github.com/gtworek/PSBits 63
Security/Hacking Using PNG as a way to share files
Sharing
Will sharing somehow destroy our file? I tested 5 different 4. Discord
ways to share and checked it.
Same as protonmail, when we’re sending images via
1. Messenger Discord we get the exact image with all metadata.
First issue with Messenger is that Meta has a policy of getting 5. SMS
rid of EXIF data, so our length inside metadata is gone. But
when we share the message length of a fi le - we can set it With SMS, we have 2 issues but one is major. Firstly,
manually and it works! We shared the file via image! EXIF has been cut off. Secondly, the image has been
converted to JPG, so it’s impossible to decode our file
2. Signal even if we enter the length manually.
Signal has the same policy - they cut off all EXIF data. But this 6. Instagram
time when we tried to set length by ourselves, we got a False
return. When we read pixels from an image, we can see that Instagram has the same issue as SMS - they convert
they’re displayed in (R, G, B, A) so Signal converts our picture to images to JPG, so we can’t read bytes from pixels.
RGBA.
3. Protonmail
Note: I haven’t tested it on large files.
With sharing files as attachment or embedded image,
both cases were True.
I am sure many people reading this are already familiar with the security and
vulnerability research world. It is often that many of the vulnerabilities found are
quite simple. For example, you attack an IoT device to find the most simplistic or
maybe even a simple, but new form of XSS within that device. Going through this
myself, I have spent a lot of time raging over the simple stuff I was finding, as
given my experience in development and my love for it, it's quite sad to see the
commonality of simple flaws. So I tried to look for a new angle and see if there
was anything that not only was a bit more difficult to go through, but also taught
me something new, and felt nice to handle.
The “new angle” involves taking the root of a system, and searching there. What
do I mean by this? Well, this is a way of saying that many flaws in existing
software come from unmaintained software designs. For example, let's say we
are ripping open a new IoT device, this device uses a custom network protocol
that nobody has seen in the wild yet. The first idea, for any security researcher,
would be if not found or discovered yet, to find it yourself, which means to
reverse engineer the protocol yourself. When you go down that path, you should
be looking for the systems that the network protocol is built on, such as the
network layer the protocol is on, if the protocol is part of an existing standard, or
what the protocol is doing. All questions alike can be used to break down the
design of the protocol more, and thus, bring you to the root of where every flaw
may sit.
Going to the bare bones of a system is not only just necessary but helpful
because of the ability to get a full image of a system, and all systems that rely on
that design. For example, instead of finding XSS in a regular web application, you
may want to find XSS pertaining to a specific technological design in which that
technology and others use would be much more valuable. Think CWE versus
CVE. I came up with this angle in my workflow by being able to assess the
current state of the world and technologies alongside vulnerabilities. Well, the
honest and hard truth about this viewpoint is that when looking at it from a design
perspective, people built so many technologies, computers, protocols, standards,
new forms of GSM, etc all on old standards that were never designed with
security in mind. This is an extreme problem we are facing now, and I much see
that the impact that will hit a system harder resides in the root of every system,
rather than at the surface-level design. While not being the only method used for
finding bugs, it does not hurt to add it to your routines.
Zed Attack – test your web app allowing users to discover and fix vulnerabilities quickly
and efficiently, thanks in part to its open-source
Zed Attack Proxy (zaproxy.org) is an open-source philosophy. Open-source software has the advantage of
cybersecurity tool designed for developers and security being free, with no license fees that increase the cost of
experts to identify and fix vulnerabilities in web testing. Another advantage is the ability to customize and
applications. This tool is maintained by the Open Web improve the software due to the availability of the source
Application Security Project (OWASP), an organization code. ZAP also has a free plugin marketplace, which
committed to improving web application security by allows users to expand its functionality.
sharing knowledge and resources. Similar tools and comparison
Needless to say, conducting regular and thorough testing Other similar open-source tools include Nuclei
for web applications is critical to ensuring the quality and (vulnerability scanner), Sn1perSecurity (attack surface
security of the application and providing users with an management platform), Nikto (web server vulnerability
optimal experience. Such testing commonly includes scanner), and Arachni (web application security scanner
functional testing, penetration testing, and fuzz testing framework), all available on GitHub.
(fuzzing). Functional testing involves verifying that all
Another popular free tool is the Community Edition of Burp
application features are implemented correctly and meet
Suite, which also offers a paid Enterprise Edition.
user expectations. Penetration testing is a fundamental
Compared to the other tools mentioned, both OWASP ZAP
security test that aims to identify vulnerabilities and
and Burp Suite are considered eavesdropping proxies that
security risks in the application. Fuzz testing is an
interpose themselves between the browser and the web
automated technique that involves generating random or
server to intercept and manipulate request exchanges. A
semi-random inputs to a program to identify vulnerabilities
brief comparison of ZAP and Burp Suite CE is provided at
or bugs.
the bottom of the page.
This last method is particularly useful in the context of
While I've previously mentioned the positives of
computer security, as it can identify potential flaws in
open-source projects, it must be noted that in all
software that attackers could exploit. Fuzzing can be
open-source projects, both development (such as fixes
performed in various ways, such as mutation-based
and new features) and support heavily depend on the
fuzzing, where input data is randomly altered, or
volunteers behind them. Paid tools like Burp Suite have
generation-based fuzzing, where valid but unexpected
the advantage of a dedicated company continually
input is created. This technique is widely used by
working to improve the software, unlike many open-source
computer security experts to test software robustness
projects where volunteers may only contribute a small
and identify vulnerabilities that could be exploited by
portion of their time each week.
malicious attackers. Fuzzing can enhance computer
system security and protect sensitive data from However, ZAP is a key tool for technical cybersecurity
cyberattacks. analysts involved in managing web applications with
open-source solutions. With its powerful suite of features
As one might expect, ZAP offers special support for
and ease of use, ZAP helps users secure their web
fuzzing web applications. Additionally, ZAP provides
applications and protect them from external threats.
several features for testing and analyzing web application
Furthermore, ZAP, with its free and open-source
security, including detecting vulnerabilities such as SQL
philosophy, supports many open standards and known
injection, cross-site scripting (XSS), clickjacking, and
protocols, making it easy to develop and use add-ons or
SSL/TLS issues. That its scanning feature supports
plugins. Additionally, the ZAP community is available for
various modes, from "Attack" to "Safe," allowing both
support through the ZAP user group on Google Groups [1]
aggressive and cautious approaches to targets is also
and the IRC channel [2].
worth adding.
Because of its intuitive user interface and flexibility, ZAP
has become a popular tool among developers and security [1] https://groups.google.com/g/zaproxy-users
experts. It supports both automated and manual testing, [2] https://web.libera.chat/#zaproxy
Would you like to see your article published in the next issue of Paged
Out!?
Here’s how to make that happen:
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].
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!