From e2f6f7d9ee2a5eb46030b90c3939985a56f3a5a6 Mon Sep 17 00:00:00 2001
From: Malfurious <m@lfurio.us>
Date: Sat, 7 May 2022 20:52:36 -0400
Subject: Writeup angstromCTF 2022 / Auth Skip

Signed-off-by: Malfurious <m@lfurio.us>
---
 docs/writeups/angstromCTF_2022/Auth_Skip.txt | 61 ++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 docs/writeups/angstromCTF_2022/Auth_Skip.txt

(limited to 'docs')

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}.`);
+});
-- 
cgit v1.2.3


From 69002ed19f3c239d9ed86da4e52ec5363279ddf0 Mon Sep 17 00:00:00 2001
From: Malfurious <m@lfurio.us>
Date: Sat, 7 May 2022 20:56:22 -0400
Subject: Writeup angstromCTF 2022 / baby3

Signed-off-by: Malfurious <m@lfurio.us>
---
 docs/writeups/angstromCTF_2022/baby3.txt | 43 ++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 docs/writeups/angstromCTF_2022/baby3.txt

(limited to 'docs')

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}
-- 
cgit v1.2.3


From aeb7ef3dba2dc1524a795b5faac72fa1f0472897 Mon Sep 17 00:00:00 2001
From: Malfurious <m@lfurio.us>
Date: Sat, 7 May 2022 22:28:30 -0400
Subject: Writeup angstromCTF 2022 / uninspired

Signed-off-by: Malfurious <m@lfurio.us>
---
 docs/writeups/angstromCTF_2022/uninspired.txt | 150 ++++++++++++++++++++++++++
 1 file changed, 150 insertions(+)
 create mode 100644 docs/writeups/angstromCTF_2022/uninspired.txt

(limited to 'docs')

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;
+}
-- 
cgit v1.2.3


From 8456a85a083c7cbc957e6a9176c0c7a608b63283 Mon Sep 17 00:00:00 2001
From: Malfurious <m@lfurio.us>
Date: Sat, 7 May 2022 22:52:52 -0400
Subject: Writeup angstromCTF 2022 / whatsmyname

Signed-off-by: Malfurious <m@lfurio.us>
---
 docs/writeups/angstromCTF_2022/whatsmyname.txt | 115 +++++++++++++++++++++++++
 1 file changed, 115 insertions(+)
 create mode 100644 docs/writeups/angstromCTF_2022/whatsmyname.txt

(limited to 'docs')

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;
+}
-- 
cgit v1.2.3