<?xml version="1.0" encoding="utf8"?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title>tech</title> <description><![CDATA[This is a technical phlog. The main topic is exploration and fun with computers, networks, and unix operating systems. ]]></description> <link>gopher://example.conf:70/1/tech/</link> <item> <title>openSUSE on Acer Aspire One D270</title> <link>gopher://example.conf:70/0/tech/2024-07-11.txt</link> <description><![CDATA[A few days ago I've inherited a really cute laptop. As by title, it is an Acer Aspire One D270. It turns out there's a wikipedia page about Acer Aspire One, and the D270 model somehow deserved a dedicated section: https://en.wikipedia.org/wiki/Acer_Aspire_One#Acer_Aspire_One_D270 The display of this unit was unfortunately damaged, but the laptop was otherwise in working conditions, and I could see that the operating system was booting regularly! I wasn't sure whether it was worth the effort, but I decided to tempt the fates and fix the display. The operation succeded, although it was a little expensive to buy a replacement. Once the new display was mounted I could see Windows 7 Starter reaching the login screen, where I was prompted to enter the password for the user. I guessed it correctly as the name of the previous owner. Naive. By my code of honor respected their privacy by not looking at any of the stored data. It should come with no surprise that there wasn't much to do for the discontinued operating system. Screw that, I was planning to install Linux anyway, so I figured I shold probably try Puppy Linux, or some other distro targeting old hardware. With my surprise, even if the operating system managed to boot, the computer ended up frozen as soon as the graphical server was started. I tried all of the available boot options, but nothing seemed to work. Then I decided to try with another distro, but I got the same result. It took a while to figure out that the `quiet` option passed by many distros as boot parameter was responsible for this odd behaviour. Even so, running a lightweight desktop environment such as XFCE4 turned out to be too much for this laptop. The installation procedure just froze before any installation could be done. Eventually I decided to give Debian a shot, since it comes with a textual installer. It took ages, but Debian + XFCE4 eventually popped up, unbearably slow, but working. After a few days of torturing myself with a very slow system (as I didn't have a much faster workstation at my disposal) I challenged myself to attempt the installation of openSUSE. Why openSUSE? - you may ask. Well, obviously I could have fixed the perfectly working Debian by dropping XFCE4 in favour of any of the many window managers available. But I grew fond of openSUSE lately, so why not? I just decided to try, just for fun, and because I can. The challenge comes from the fact that openSUSE is not known to be the most lightweight out there. I'm quite sure there is some minimal net install available for download, but I decided to just use the live installed on my USB drive. I edited the kernel command line at the boot loader, removing the problematic `quiet` option, and adding the `systemd.unit=multi-user.target` parameter. The latter corresponds to "ye olde" runlevel 3 (multi-user mode with networking, and text-only login on TTYs). In openSUSE, the installation is started by launching a shell script that can be found under `/sbin`. Now I don't recall by heart the name of it, but there are just a few commands whose name ends with the `.sh` suffix, and that's one of them. The installation took some time, but it ended smoothly. By chosing the "Generic Desktop" system role, I ended up with a very snappy system, even on such a old piece of hardware! The system boots in a reasonable time to a text prompt. I can optionally start a graphical session based on IceWM by typing `startx`, exactly as it was on Slackware, when I was a teenager. The result? A cute little laptop, running a cute little distro. It is unfortunately not so good at running the huge and bloated browser that you need to surf our huge and bloated web, but it is good enough for many other things. :) ]]></description> </item> <item> <title>Shellcode on a setuid program (2/?)</title> <link>gopher://example.conf:70/0/tech/2024-05-02.txt</link> <description><![CDATA[Resuming learning process of previous episode. A fellow hacker suggested that dash also supports 'privmode', a feature that resets euid if different from uid, disabled with `-p`. https://sources.debian.org/patches/dash/0.5.12-2/9002-Add-privmode-Part-2.diff/ Good to know... Still this is not the problem, running id(1) via execve("/bin/id") does not list the user who owns the setuid binary. And there's no shell interpreter invoked here. ]]></description> </item> <item> <title>Shellcode on a setuid program (1/?)</title> <link>gopher://example.conf:70/0/tech/2024-04-29.txt</link> <description><![CDATA[Despite the severe lack of free time, I'm currently sharpening my security skills by doing CTFs. My focus is on details and on building solid understanding on how things work at low level, which is an elegant way of justifying the fact that it takes forever for me to advance a single level. While doing the current exercise, I've battled against a number of things [and I regret taking notes in such a poor way]. We are talking about setuid binaries that should be convinced to steal the flag for us. Injecting a shellcode in this one is trivial, as it is one of the first exercises. My focus on the exploit, which is where I'm less competent. Over (way too much) time, I managed to put together a dumb shellcode, which is just running execve("/bin/sh", ["/bin/sh", "/bin/sh", NULL]). I can see the shell process is running, since the prompt is printed, but as it turns out the shell does not have the permissions that I would expect to be granted by setuid. Why so? - As I learned, bash will drop privileges in case of unequal euid and reuid[1]. This is a clever idea from the multi-layer security perspective, and overall good to know. But the target system runs dash, not bash! - I encoded a execve("/bin/id", ["/bin/id", NULL]) instead, learning that not even id(1) will show the correct permissions. This is somewhat surprising: I wrote a quick program that does the same, gave it setuid and a different user, and *on my system* it behave as I would have expected! - Next up, I'll try capturing the flag directly, without relying on an exec. I think `ragg2` will be useful for this to happen without spending all this time. [1] https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html#Invoked-with-unequal-effective-and-real-UID_002fGIDs ]]></description> </item> <item> <title>More radare 2 (6/?): functions call graph</title> <link>gopher://example.conf:70/0/tech/2024-02-14.txt</link> <description><![CDATA[I'm investigating on a CVE concerning a well known library that is part of our dependency tree at $dayjob. Anything calling the vulnerable function is to be considered vulnerable, so I wanted to obtain a call graph of the function. An IDE would probably be able to figure that out for me, but I don't use one. On the other hand I'm learning Radare2 these days, so why not using it? Visualize global call graph: agC ag supports a number of graph visualization options. graphviz dot visualized from within r2: [...]> agCd | xdot - same but from shell: $ r2 -Aqc agCd ./package/usr/lib/lib....so | xdot - Visualize xref graph: agx @ addr The displayed xrefs are addresses because r2 will not list the name of the calling function, but the address of the calling site within functions. This is not very useful for our needs, so the global call graph mentioned above is more practical. While figuring out the call graph from the source code might be more natural, the binary analysis approach has the advantage of taking in account the compile-time configuration. This is effective In many software packages such as Busybox, U-Boot, Linux (kernel) and mbedTLS many features can be disabled in the build system. ]]></description> </item> <item> <title>More radare 2 (5/?): environment variables etc.</title> <link>gopher://example.conf:70/0/tech/2024-02-13.txt</link> <description><![CDATA[I'm still focusing on the narnia (https://overthewire.org/wargames/narnia/) capture the flag, which is trivial in itself, but I'm trying to beat it completely within R2, with the goal of learning the tool. Radare2 is rich of features, but also baroque and inconsistently documented. How do I set environment variables? Rarun is the answer, but the documentation glosses over how that is integrated. Search on the web. Find out about radare2-explorations (a complementary book) https://monosource.gitbooks.io/radare2-explorations/content/intro/debugging.html In a nutshell: 1. Run `rarun2 -t` in another tty and yank the path of the printed character device (e.g. /dev/pts/8) 2. Execute r2 like this: $ r2 -r rarun.rr2 -d program Example with narnia1: $ cat rarun.rr2 stdio=/dev/pts/8 setenv=EGG=exploit_here $ r2 -r rarun.rr2 -d ./narnia1 Well, the software is powerful and intriguing, but I'm a little frustrated by the (well known) steep learning curve, IMO mostly due to old/inconsistent docs and opcode-like commands. For a person like myself, who has so little free time, this is a big obstacle. I'm grateful nonetheless. -- mov esi, str._bin_sh mov edi, str._bin_sh mov eax, 0 call sym.imp.execl ]]></description> </item> <item> <title>More radare 2 (4/?): filter and search</title> <link>gopher://example.conf:70/0/tech/2024-02-07.txt</link> <description><![CDATA[## Output filtering: see ~? ~ follows a command, to massage the output of it. let `ia` be our command) Notable variants: ~.. pager ~{path} json Examples: izz~.. izj~{[0].vaddr} ## Searching in binary: see /? Notable variants: /c search for crypto material Should investigate on what is available Would it be possible to find traces of a statically compiled library (possibly even multiple copies of it) within an executable? ]]></description> </item> <item> <title>More radare2 (3/?): binary editing / persisting / debugging</title> <link>gopher://example.conf:70/0/tech/2024-02-04.txt</link> <description><![CDATA[Invoke r2 with -w flag -> Write mode. In visual mode ('V') the visual assembler 'A' is available, and allows to enter assembly directly in place of the current seek. => patch binary. Visual mode supports a number of shorcuts (listed in the book, but also as inline help). The `;` shortcut, allows to add comments to the assembly. Exercise: how to do so in normal mode? ## Flags It is useful to place flags around, e.g when an interesting offset is found. f myflag = $$ # Where $$ is the 'current seek' variable. ## Persist I was wondering how to persist metadata. As it turns out, radare2 can save projects and version them via git. Try `P?` and read up :) ## Debug Visual debugging: `Vpp` (or `V` followed by hitting `p` twice) Top: stack Middle: registers Bottom: instructions db List breakpoints db main Add breakpoint to main. dc Continue to breakpoint. dsr Step till end of frame (that is, till return) F2 | B toggle breakpoint (Same as `db $$`) F4 Run to cursor F7 | s Step F8 | S Step over F9 Continue What is visual diff? ]]></description> </item> <item> <title>Signed overflow / unsigned wrap-around</title> <link>gopher://example.conf:70/0/tech/2024-02-03.txt</link> <description><![CDATA[I've recently read this: https://lwn.net/ml/linux-kernel/20240122235208.work.748-kees@kernel.org/ Trying to experiment a bit with what I learned: $ cat wrapme.c #include <stdint.h> #include <err.h> #include <stdlib.h> #include <stdio.h> #include <limits.h> int main(int argc, char **argv) { int val, ival; if (argc < 2) errx(1, "Usage: %s VALUE", argv[0]); val = INT_MAX; ival = atoi(argv[1]); // feeling lazy today. printf("will it overflow?\n"); if (val + ival < val) printf("It will overflow\n"); else printf("It won't overflow\n"); val += ival; printf("%d\n", val); return 0; } Compiled with different options... $ make gcc -o ftrapv -ftrapv wrapme.c gcc -o fwrapv -fwrapv wrapme.c gcc -o default wrapme.c How will it behave upon signed integer overflow? $ ./fwrapv 0 will it overflow? It won't overflow 2147483647 $ ./fwrapv 1 will it overflow? It will overflow -2147483648 $ ./ftrapv 0 will it overflow? It won't overflow 2147483647 $ ./ftrapv 1 will it overflow? Aborted (core dumped) $ ./default 0 will it overflow? It won't overflow 2147483647 $ ./default 1 will it overflow? It won't overflow -2147483648 ]]></description> </item> <item> <title>More radare2 (2/?)</title> <link>gopher://example.conf:70/0/tech/2024-01-27.txt</link> <description><![CDATA[== xrefs == The dayjob monolith is using all RAM in our embedded platform. A colleague is trying to mitigate the problem, and he asked me if I knew a way to check the amount of space used by strings. Using strings(1) is of course the first thing to do, but while doing so I've also found build paths leaked in the executable. Classic [ab]use of __FILE__ and similar macros. Where to find the culprit code? radare2 has a way to find references to simbols (in jargon we speak about "xrefs"). The command is `axt`, described as "find data/code references to this address". In order to obtain results from `axt` enhanced binary analysis commands such as `aa` must be executed first. Example on narnia1 (https://overthewire.org/wargames/narnia/): [0x0040106e]> cat ./narnia1.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdio.h> int main(){ int (*ret)(); if(getenv("EGG")==NULL){ printf("Give me something to execute at the env-variable EGG\n"); exit(1); } printf("Trying to execute EGG!\n"); ret = getenv("EGG"); ret(); return 0; } [0x0040106e]> fs strings [0x0040106e]> f 0x00402010 53 str.Give_me_something_to_execute_at_the_env_variable_EGG 0x00402045 23 str.Trying_to_execute_EGG_ [0x0040106e]> aa INFO: Analyze all flags starting with sym. and entry0 (aa) INFO: Analyze all functions arguments/locals (afva@@@F) [0x0040106e]> axt str.Give_me_something_to_execute_at_the_env_variable_EGG main 0x401181 [DATA:r--] mov edi, str.Give_me_something_to_execute_at_the_env_variable_EGG == Configuration is useful Added ~/.radare2rc in my rcm(1). I use light terminals at work, and dark terminals on my own machines (this follows my life patterns: working by day, hacking at night). # The ogray theme works on both light and dark terminals. eco ogray == Debugging session Let's keep working on narnia1, however trivial, there's probably something to learn from it about radare2. First off, useful debugging commands: dc - continue execution of all children ood - reopen in debug mode The exercise requires me to pass around an environment variable named EGG. How do I in radare2? That's how I found out about rarun2. My free time is so limited :( Session is over. Little learned is better than nothing. ]]></description> </item> <item> <title>More radare2 (1/?)</title> <link>gopher://example.conf:70/0/tech/2024-01-01.txt</link> <description><![CDATA[Last day of vacation, but being home with small kids completely flips the perspective, which means that tomorrow, first day of work, will be the actual first day of vacation... Once everyone is in bed, I can finally indulge in learning some secret art. Lately this means Radare2: a baroque binary analysis tool. Let's list some commands I've been playing with: iI - Binary info af - Analyze functions px - Print hexadecimal > Flags:_ Basically they are pointers: the value is usually an address in memory, and the size property defines the size of the pointed object. The `af` command populates the flag space named "functions" with flags having names such as `sym.main`. fs - list available flag spaces fs SPACENAME - enable filter on listed flags, only list from the flag space named SPACENAME f FLAGNAME 0x10 0x23040 - Assign a flag having name FLAGNAME, size 0x10 and offset 0x23040. f-- - Erase all flags. > Example:_ af - analyze functions fs functions - filter flag space of functions f | grep main - print all flags, but grep for main f-- - reset the flags, losing analyzed info. bf FLAGNAME - set the block size to the size value of the provided flag. b* - display block size. > Example:_ bf sym.main b* > Corollary:_ bf sym.x - set block size the size of function x pd sym.x - disassemble one block starting from the beginning of x, therefore the whole x. pdf sym.x - Same as for previous two commands, but without changing the global block size. > Dollar-prefix_ There's a bunch of variables prefixed with '$', such as... $l - length of the opcode at the current seek. $FB - function beginning $FS - function size $$ - current seek. ?v - Print value. > Example:_ s sym.main - Seek to main() ?v $l - Print length of the first opcode. ?v $l @ sym.x - (not tested), temporary seek to x() and print first opcode length. > Json querying?_ I've noticed that a few commands, usually suffixed with `j`, emit output in json format. I suppose this is useful for front-ends. Since I learned that the pipe symbol works as in shells, I could do the following: oj | jq . where `oj` lists open files in json format, and jq(1) is used to pretty print the resulting json. Nice but pointless: as it turns ut, the `~` suffix allows to modify the output, and `{}` stands for json indentation. (hint: try `~?`). oj ~{} oj ~{[0].writable} > Conclusions_ I spent some time fooling around the idea of loading a file in the addressing space of a binary, so that a program (written on purpose) that dumps data from an arbitrary address in memory will effectively find something there, instead of crashing and burning. I failed, but I've learned some valuable shit. The notes above have been jotted on paper with my Parker Jotter, and copied in this text file. What a pleasant night! Time to sleep now :) ]]></description> </item> <item> <title>X86 / X86_64 calling conventions</title> <link>gopher://example.conf:70/0/tech/2023-12-28.txt</link> <description><![CDATA[https://en.wikipedia.org/wiki/X86_calling_conventions Who cleans the stack from function arguments? - Caller cleanup: caller pushes args, jumps to callee, then restores the stack when over. Notable: cdecl, de facto standard on linux (gcc) - Callee cleanup: the callee resets the argument adding to the stack pointer. Conventions may differ on: - Order of parameters to push to stack (Left-to-Right or RtL) - 'thiscall': how should pass the `this` pointer (common in C++, for non-static methods) - First few parameters might be passed over registers (e.g. microsoft fastcall), or SIMD registers (e.g. vectorcall). X86_64: more registers, larger. - Both Microsoft and Unixes: use more registers for arguments. - Small structs might fit registers. ]]></description> </item> <item> <title>Learning Radare2</title> <link>gopher://example.conf:70/0/tech/2023-12-27.txt</link> <description><![CDATA[The last few days have been hectic because family. Even so, I managed to spend some time modifying Crossbow, my own RSS/ATOM reader, so that it uses Mini-xml instead of libmrss/libnxml. I think I wrote on this topic before, but the long story short is that I'm planning since long time to ditch libmrss/libnxml in favour of Mini-xml. I wrote a library named libeccio on the top of Mini-xml, and I'm trying to integrate it into Crossbow. The thing is not technically difficult, but it takes forever because family. I eventually managed to make it work! While running casual quality checks on the result, I figured the compiled binary embeds the filesystem paths where it was built. Classic issue, usually breaking reproducible builds, and often due to the use of the __FILE__ macro. I noticed the problem persists even after changing the code so that __FILE__ is not used, but I couldn't trivially figure out what kept adding it. The right tool for the job is a disassembler with xref support. I heard of Ghidra before, and I managed to get my answer in a couple of clicks on a very 90's Java GUI. In case you're curious, the problem was caused by a UTHash macro using assert(3). This whole story woke up my interest in binary analysis, and I even if the answer came from Ghidra, I started to be intrigued by Radare2. I suppose it qualifies as the most baroque software I've ever seen, but I find it fascinating. I found this article very good: https://www.megabeets.net/a-journey-into-radare-2-part-1 ]]></description> </item> <item> <title>Reads on Security (x509 certificates)</title> <link>gopher://example.conf:70/0/tech/2023-12-15.txt</link> <description><![CDATA[RFC5280 Entities: Users Certificate authority Registration authority Repositories Repositories meant as collections of Certificates and Certificate Revocation Lists. Certificate VS Public Key The certificate binds the pub key to a subject, for a specific time interval, and gets signed by the certificate authority. Multiple certificates constitute the "identification path" finalized to the validation of a public key. Certificate Authority Certificates: Cross-certificates: define the trust relation between two certificate authorities. Self-issued cert: Issuer and signer is the same entity Self-signed cert: Issuer and signer is the same entity (Self-issued) + The contained pubkey can verify the signature. ROOT CERTS are Self-signed. Certificate Revocation List: On a public repo, signed by CA, contains serial numbers of the keys revoked by the CA. A recent CRL needs to be verified upon certificate validation. The design is somewhat flawed: in the time window between CRL updates, a revoked certificate is considered valid. Alternatives: Online Certificate Status Protocol (OCSR) Online Certificate Status Protocol (not part of RFC): Not implemented everywhere (e.g. certain browsers do not) Possible privacy issues (online check on need implies disclosure). Check wikipedia page for details. X509 format: In ASN-1 (a general purpose Type Length Value format) [ { certificate, -> { sig_algorithm, version, sig_value serial_no, }, signature, ... validity, ] subject_name, subject_pubkey } ]]></description> </item> <item> <title>Using DMA under FreeBSD 14</title> <link>gopher://example.conf:70/0/tech/2023-12-05.txt</link> <description><![CDATA[FreeBSD 14 ships DMA (DragonFly Mail Agent) and uses it as a default replacement for the venerable Sendmail. The integration is seamless thanks to mailer.conf(5). The FreeBSD Handbook provides a few hints on how to set it up with GMail and FastMail. The latter happens to be my email provider of choice. I could easily set up a smart host on my Raspberry. ]]></description> </item> <item> <title>Notes on electronics (learning some basics)</title> <link>gopher://example.conf:70/0/tech/2023-06-04.txt</link> <description><![CDATA[Sparkfun.com provides some useful info for an electronic newbie such as myself. I've seen some of this while I was a high school student, but electronics wasn't the main path, so I barely remember about it. These notions are just interesting for hardware hacking, but they might also be helpful for work! As usual, these notes are not meant to be a summary of the pages I'm referencing: please read the full pages! :) https://learn.sparkfun.com/tutorials/logic-levels Digital ommunication translates voltages into binary values. The voltage on a communication line is interpreted according to ranges: V_oh = Output High, min voltage expressing high V_ol = Output Low, max voltage expressing low V_ih = Input High, min voltage read as high V_il = Input Low, max voltage read as low Undefined behaviour "floating" in the "noise margin" The ranges could be around 5V or 3.3V. The latter takes less energy. 3.3V is somewhat 'backward compatible' to 5V: components are able to generate a signal that is recognized by a 5V component. The opposite holds only if the 3.3V component is 5V tolerant. https://learn.sparkfun.com/tutorials/serial-communication Saying "serial protocol" usually implies using asyncrhonous serial communication. To cope with the lack of shared clock: - Baud rate> Unlikely to have it beyond 115200 bps - Framing> Data chunks have variable size, e.g. 8 bits. Can be 5 to 9 bits, little or big endian. Start bit: signals begin of tx, moving line from 1 to 0 Stop bit: end of tx, moving line form 0 to 1. Parity bit: basic error checking, optional Common standards: - TTL (Transistor Transistor Logic), usualy 3.3V or 5V where HIGH=1, LOW=0. - RS-232, old style, voltage range from -13V to +13V, or even +/-3V or +/-25V, and flipped logic (HIGH=0, LOW=1). Better suited for longer range communication. https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi SPI (Serial Peripheral Interface) is a synchronous serial. The receiver end can be as simple as a shift register. Sampling happens on the clock edge (either 0 to 1 or opposite). SPI is full duplex. The controller side defines the clock, which also determines the response from the peripheral. Chip select: held high disconnect the peripheral from the SPI bus. Next Up: https://learn.sparkfun.com/tutorials/i2c/ ]]></description> </item> <item> <title>Software supply chain security with podman.</title> <link>gopher://example.conf:70/0/tech/2023-05-31.txt</link> <description><![CDATA[SYNOPSIS sudo podman image trust set -t reject default sudo podman image trust set --type accept docker.io sudo podman image trust set --type signedBy -f "$pubkey" docker.io DESCRIPTION Since my Arch Linux workstation is currently unavailable, I'm trying to build Archiso my work laptop, where I'm running Gentoo. In order to get the proper build environment, the plan is to run Arch Linux in a container. I decided to start with a little Dockerfile, "FROM archlinux:latest". The invocation to docker-build(1) throws me a warning about some deprecation, so I'm probably a bit rusty about Docker. Instead of learning what's wrong on Docker, why not trying to switch to Podman instead? Podman should be a drop in replacement, but I immediately noticed some differences along the way. One of them is the need of a /etc/containers/policy.json file which is specifying what policy to adopt for image repositories. This is good news! I've recently participated to a security conference, where one of the presentation is about the security of the software supply chain, and even if nothing I heard there enlightened me that much, the whole experience put me in a certain mood for additional security. The commands in the SYNOPSIS are a short path for the configuration of /etc/containers/policy.json. I wish I could find the public keys for docker.io, since there would be a way to specify the public keys in use for the account. This is a topic I find interesting. Do you have opionions about it? Feel free to send me an email. dacav at fastmail.com. ]]></description> </item> <item> <title>Good use for pushd / popd / dirs</title> <link>gopher://example.conf:70/0/tech/2023-04-05.txt</link> <description><![CDATA[These days I'm often working with Yocto. A peculiar thing about Yocto is the ridiculous amount of directories it spawns, and high number of places in the file-system where to modify scripts or look at generated data. Using 'cd' to jump from one directory to the other can be quite slow. A possible solution could be using environment variables, e.g. path1=/path/to/place/1 path2=/where/to/find/another/place ... cd $path1 ... cd $path2 A more 'casual' approach could consist in making good use of the pushd/popd/dirs built-ins (bash). Example shell session: $ cd "$(mktemp -d)" $ mkdir -p ./foo/bar/baz ./lol/ ./path/to/this ./path/to/that $ dirs 0 /tmp/tmp.NMiIJ8GVmY $ pushd ./foo/bar/baz/ /tmp/tmp.NMiIJ8GVmY/foo/bar/baz /tmp/tmp.NMiIJ8GVmY $ dirs 0 /tmp/tmp.NMiIJ8GVmY/foo/bar/baz 1 /tmp/tmp.NMiIJ8GVmY $ pushd +1 /tmp/tmp.NMiIJ8GVmY /tmp/tmp.NMiIJ8GVmY/foo/bar/baz $ pushd ./path/to/this/ /tmp/tmp.NMiIJ8GVmY/path/to/this /tmp/tmp.NMiIJ8GVmY /tmp/tmp.NMiIJ8GVmY/foo/bar/baz $ dirs 0 /tmp/tmp.NMiIJ8GVmY/path/to/this 1 /tmp/tmp.NMiIJ8GVmY 2 /tmp/tmp.NMiIJ8GVmY/foo/bar/baz $ pushd +1 /tmp/tmp.NMiIJ8GVmY /tmp/tmp.NMiIJ8GVmY/foo/bar/baz /tmp/tmp.NMiIJ8GVmY/path/to/this $ pushd ./ foo/ lol/ path/ $ pushd ./path/to/that/ /tmp/tmp.NMiIJ8GVmY/path/to/that /tmp/tmp.NMiIJ8GVmY /tmp/tmp.NMiIJ8GVmY/foo/bar/baz /tmp/tmp.NMiIJ8GVmY/path/to/this $ dirs 0 /tmp/tmp.NMiIJ8GVmY/path/to/that 1 /tmp/tmp.NMiIJ8GVmY 2 /tmp/tmp.NMiIJ8GVmY/foo/bar/baz 3 /tmp/tmp.NMiIJ8GVmY/path/to/this ]]></description> </item> <item> <title>Lazy loading, .plt / .got / .got.plt</title> <link>gopher://example.conf:70/0/tech/2023-03-25.txt</link> <description><![CDATA[Quick notes on the topic of lazy dynamic loading, learned from "Practical Binary Analisys" by Denniss Andriesse (Section 2.3.4). The dynamic linker makes process execution faster by delaying the effective loading of library functions on their first usage. This feature can be turned off if real-time predictability is needed. Invoking a function such as 'puts' from the .text section implies a jump to the corresponding stub in the .plt (Procedure Linking Table) section. The .plt section contains read-only executable code. The first instruction of the stub is an intdirect jump to an address specified in a dedicated pointer that belongs to the .got.plt (Global Offset Table / Procedure Linking Table) section. In C pseudocode: void (*ptr)() = got_plt[x]; ptr(); Initially got_plt[x] is assigned to the instruction that follows the indirect jump, so the jump effectively results in a no-op on the first execution. The following instructions will load the parameters for the dynamic loader, and jump to a lookup procedure internal to the dynamic loader. The dynamic loader will assign got_plt[x] to the correct (now resolved) address that the code intended to jump on, before effectively jumpign to it. Subsequent calls to the same library functions will directly jump to the resolved library procedure, without invoking the dynamic linker. Security implications when an exploit can write got_plt[x]. .plt -> Executable, read-only, shared among all processes using the library, references with indirect jump the .got.plt table of the process. Corollary: all processes should map the .got.plt in the same address. .got.plt -> Data, read-write, modified by the dynamic linker. Contains function pointers to be updated with the resolved ones. .got -> Data, read-write, similar to .got.plt but involved in *data* rather than function pointers. ]]></description> </item> <item> <title>Reads on Security (topic: double free)</title> <link>gopher://example.conf:70/0/tech/2023-03-18.txt</link> <description><![CDATA[I spent a fairly long period without writing on this phlog. Besides being quite busy with work, I didn't feel very inspired to do so. It is time to resume this practice, as much as my limited spare time allows me to. A few weeks ago I wanted to update these pages, since I've been reading up on certain security topics, of which I'd like to keep some bookmarks. I will keep things minimal. As a matter of fact, the least I type, the more time I'm left with to learn. 1. https://github.com/stong/how-to-exploit-a-double-free Very interesting even if I didn't try the CTF. To understand certain things I will need more info on the system calls. Status: Partially read. 2. https://people.eecs.berkeley.edu/~kubitron/courses/cs194-24-S14/hand-outs/bonwick_slab.pdf Suggested by a colleague, a very good paper. Status: Complete. https://hammertux.github.io/slab-allocator Out of scope beyond a certain point. Status: Partially read. https://sourceware.org/glibc/wiki/MallocInternals About malloc pools, hard to follow as it is often the case for GNU products >:-/ Status: Partially read. ]]></description> </item> <item> <title>Using CMake on BSD</title> <link>gopher://example.conf:70/0/tech/2022-12-13.txt</link> <description><![CDATA[As mentioned in my previous post, I have decided to migrate Crossbow to CMake. Crossbow is supposed to support GNU/Linux, FreeBSD and OpenBSD. Bonus points for more UNIX-likes. No, I don't think I'll try to make it work on Windows, that's not my thing. Compiling it under GNU/Linux was trivial. Ensuring that it works under BSD systems is the next step, and it simply requires the modification of a few existing scripts. They automate the installation and the execution a collection of black-box tests, and they must re-adapted to install a cmake project. I started by testing on OpenBSD. I stumbled into a problem that seems to match some issue I've seen before, while trying to build a different cmake project on FreeBSD. I'm using pkg_check_modules to find my dependencies, and while CMake claims that the dependencies were found, it later fails at supplying the system path extensions needed to actually compile against them. I'm not entirely sure if this problem matches the similar one that I mention, that is what happened on FreeBSD. In that case the fix wasn't really a fix: I just added the extra paths manually to the CMake target which needed it. In this case I'm aiming at a better quality, so I'm not interested in a it-just-works kind of hack. On the other hand I'm clueless. I guess I will need to delve into this problem. But not tonight. Tonight I'm only good for sleeping. It doesn't look like an insurmountable problem: I'm sure that plenty of projects using CMake have been ported to BSD, and that's exactly where I'm going to look as first step). Yet, if anyone reading this has a clue, feel free to reach me (dacav at fastmail dot com). ]]></description> </item> </channel> </rss>