Writeup: intrainspection

  • This week we have a challenge!
    • The challenge is to unzip a zip file!
      • unzip Safe.zip
      • Archive: Safe.zip
      • [Safe.zip] jXeC.zip password:
        • The challenge is to guess the password!
  • The challenge is to guess 30 passwords!
    • That sounds hard!
      • So we’ll do it automatically!
    • You may notice that this doesn’t parse the output of john!
      • That seems like it might be hard!
        • We’ll do it… semiautomatically!

    What follows is real, uncensored shell history – viewer discretion is advised

    $ ./solve.sh jXeC.zip
    $ john --show jXeC.zip
    $ john --show example.hash
    $ unzip jXeC.zip
    $ ./solve.sh dHag.zip
    $ unzip dHag.zip
    $ ./solve.sh bSmC.zip
    $ unzip bSmC.zip
    $ ./solve.sh pbtJ.zip
    $ unzip pbtJ.zip
    $ ./solve.sh VGQc.zip
    $ unzip VGQc.zip
    $ ./solve.sh jxLD.zip
    $ unzip jxLD.zip
    $ ./solve.sh iXZA.zip
    $ unzip iXZA.zip
    $ ./solve.sh KxrU.zip
    $ unzip KxrU.zip
    $ ./solve.sh DlTL.zip
    $ unzip DlTL.zip
    $ ./solve.sh PEOa.zip
    $ unzip PEOa.zip
    $ ./solve.sh Dggp.zip
    $ unzip Dggp.zip
    $ ./solve.sh tXFO.zip
    $ unzip tXFO.zip
    $ ./solve.sh IrHd.zip
    $ unzip IrHd.zip
    $ ./solve.sh wedJ.zip
    $ unzip wedJ.zip
    $ ./solve.sh wbTt.zip
    $ unzip wbTt.zip
    $ ./solve.sh TUuF.zip
    $ unzip TUuF.zip
    $ ./solve.sh tiTW.zip
    $ unzip tiTW.zip
    $ ./solve.sh dFhG.zip
    $ unzip dFhG.zip
    $ unzip dFhG.zip -P DSLA
    $ unzip dFhG.zip
    $ ./solve.sh fjIZ.zip
    $ unzip fjIZ.zip
    $ ./solve.sh CMMw.zip
    $ unzip CMMw.zip
    $ ./solve.sh MzNR.zip
    $ unzip MzNR.zip -p YQND
    $ man unzip
    $ unzip -P YQND MzNR.zip
    $ ./solve.sh jLUX.zip
    $ unzip -P YQND jLUX.zip
    $ unzip -P WBOZ jLUX.zip
    $ ./solve.sh XDDN.zip
    $ unzip -P PEEC XDDN.zip
    $ ./solve.sh vfyN.zip
    $ unzip -P qoxe vfyN.zip
    $ ./solve.sh uwPY.zip
    $ unzip -P NgHV uwPY.zip
    $ ./solve.sh xfQR.zip
    $ unzip -P fhkC xfQR.zip
    $ ./solve.sh DtQE.zip
    $ unzip -P teEk DtQE.zip
    $ ./solve.sh KWHz.zip
    $ unzip -P peRR KWHz.zip
    $ ./solve.sh EVqP.zip
    $ unzip -P wgcw EVqP.zip
    $ ./solve.sh DZen.zip
    $ unzip -P Alon DZen.zip
    $ ./solve.sh Qymr.zip
    $ unzip -P rTPR Qymr.zip

    • That was convenient and definitely faster than figuring out how to parse out the stdout of john! (UNIX timestamps benchmark me at around 650 seconds and honestly that might be faster than how long it would take for me to script that)
      • We have a word doc!
        • grep -rn osu{ returns nothing 🙁
          • Let’s look at the .docx file after unzipping it!
            • feh media/image1.png
    • Epic! Let’s scan it with our phone!
      • Helb helbbb heellp it not working!
        • Oh it’s not supposed to be a link. ok. I guess i never really put it together that these things can hold more than just a link
          • Let’s scan it with our computer!
        7f 45 4c 46 01 01 01 00 79 5f 66 69 67 30 30 33 02 00 03 00 01 00 00 00 50 ef bf bd 04 08 2c 00 00 00 00 00 00 00 00 00 00 00 34 00 20 00 01 00 00 00 00 00 00 00 00 ef bf bd 04 08 00 ef bf bd 04 08 ef bf bd 00 00 00 ef bf bd 00 00 00 05 00 00 00 00 10 00 00 31 31 69 6e ef bf bd 04 00 00 00 31 ef bf bd 43 ef bf bd 04 00 00 00 ef bf bd 7d ef bf bd 04 08 cd 80 ef bf bd ef bf bd 71 cd 80 29 ef bf bd cd 80 ef bf bd ef bf bd 44 cd 80 ef bf bd ef bf bd 35 4a 4a cd 80 48 4b cd 80 6f 73 75 7b 7d 0a 0a 
        
        • This is an ELF!
          • Converting it to a binary in cyberchef gives us
        $ ./download.dat
        zsh: exec format error: ./download.dat
        
        • But that’s ok! Because we can run strings!
        $ strings download.dat
        y_fig003
        11in
        osu{}
        
        • $submit osu{fingy_30011}
          • $submit osu{y_fig00311in}
            • Helb helbbb heellp it not working!
          Is this legible

          Let the record show that I was getting pretty close with my guesses and if hypothetically the flag ended in a g I definitely 100% would have guessed it in one second. Please hypothetically award me a gold star.

          Writeup: pop quiz

          POP QUIZ TIME! Can you solve these challenging questions?

          What number am I thinking of?

          If I were a human, it would be difficult to know what the answer to this question might be1. Thankfully, I am a computer, and will bare my essential nature to anyone with a keyboard. How about you decompile me and figure out what I’m thinking? In fact, I’ll do it for you.

          bool question_1(void)
          
          {
            long in_FS_OFFSET;
            int local_18;
            int local_14;
            long local_10;
            
            local_10 = *(long *)(in_FS_OFFSET + 0x28);
            local_14 = 0x2325;
            printf("What number am I thinking of? ");
            __isoc99_scanf(&DAT_00100e27,&local_18);
            if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                              /* WARNING: Subroutine does not return */
              __stack_chk_fail();
            }
            return local_14 == local_18;
          }

          First, I check if local_14 (my secret number) is equal to local_18 (your input). local_14 is assigned a value of 0x2325 (in my language). Converting to meat numbers gives us 8997, which is hopefully the number you were thinking of already. Regardless,

          gimme some characters

          This isn’t even a question! It’s a command! And it’s so vague! Does it mean fictional characters? historical figures? This quiz sucks!

          These are all things you might be saying if this had been written by a human. Thankfully, you’re asking a computer, who is more than willing to bare its essential nature to anyone with a keyboard. Isn’t this easy?

          bool question_2(void)
          
          {
            long in_FS_OFFSET;
            undefined4 local_14;
            long local_10;
            
            local_10 = *(long *)(in_FS_OFFSET + 0x28);
            printf("Gimme some characters: ");
            local_14 = 0;
            __isoc99_scanf("\n%c%c%c%c",&local_14,(long)&local_14 + 1,(long)&local_14 + 2,(long)&local_14 + 3)
            ;
            if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                              /* WARNING: Subroutine does not return */
              __stack_chk_fail();
            }
            return (int)local_14._3_1_ + (int)(char)local_14 + (int)local_14._1_1_ + (int)local_14._2_1_ ==
                   0x1a0;
          }

          If you’re paying attention, you’ll notice that I don’t particularly care what the characters are. I just want them to add up to 0x1a0. Which is 416 in meat numbers. It’s time for you to google “ASCII table” once again and click the top result which is really the worst result because it’s not ctrl-F’able.5

          I also am only scanfing 4 characters, so please be concise. “hhhh” works for me.2

          ???

          Screw you, meatbag! You don’t even get a vague command anymore!

          void question_3(void)
          
          {
            long in_FS_OFFSET;
            undefined4 local_14; // int/uint
            long local_10;
            
            local_10 = *(long *)(in_FS_OFFSET + 0x28);
            puts("???");
            local_14 = 0;
            __isoc99_scanf(&DAT_00100e27,&local_14);
            check(local_14); // Check runs on the integer
            if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                              /* WARNING: Subroutine does not return */
              __stack_chk_fail();
            }
            return;
          }

          Haha! This function doesn’t even return anything! That’ll throw you for a loop! What’s that, you didn’t even notice and have begun to reverse check(local_14)??3

          undefined8 check(uint guess)
          
          {
            int iVar1;
            int WHAT;
            size_t sVar2;
            undefined8 WIN;
            long lVar3;
            undefined8 *inputPointer;
            long in_FS_OFFSET;
            int local_128;
            int local_124;
            undefined8 input;
            long local_10;
            
            local_10 = *(long *)(in_FS_OFFSET + 0x28);
            inputPointer = &input;
            for (lVar3 = 0x1f; lVar3 != 0; lVar3 = lVar3 + -1) {
              *inputPointer = 0;
              inputPointer = inputPointer + 1;
            }
            *(undefined4 *)inputPointer = 0;
            *(undefined2 *)((long)inputPointer + 4) = 0;
            *(undefined *)((long)inputPointer + 6) = 0;
            sprintf((char *)&input,"%d",(ulong)guess);
            sVar2 = strnlen((char *)&input,0xff);
            WHAT = (int)sVar2;
            if ((sVar2 & 1) == 0) {
              if (WHAT < 4) {
                WIN = 0;
              }
              else {
                for (local_128 = 1; iVar1 = WHAT / 2, local_128 < WHAT / 2; local_128 = local_128 + 1) {
                  if (*(char *)((long)&input + (long)local_128) <=
                      *(char *)((long)&input + (long)(local_128 + -1))) {
                    WIN = 0;
                    goto function1;
                  }
                }
                do {
                  local_124 = iVar1 + 1;
                  if (WHAT <= local_124) {
                    WIN = 1;
                    goto function1;
                  }
                  lVar3 = (long)iVar1;
                  iVar1 = local_124;
                } while (*(char *)((long)&input + (long)local_124) < *(char *)((long)&input + lVar3));
                WIN = 0;
              }
            }
            else {
              WIN = 0;
            }
          function1:
            if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
              return WIN;
            }
                              /* WARNING: Subroutine does not return */
            __stack_chk_fail();
          }

          Try reversing this, bucko! You don’t stand a chance! Wait, what’s that? You still had 8997 in your clipboard and entered that and got the flag? Wait, you even added some gibberish text to the end and it passed my function?4 That’s awesome 🙂

          Hacking is all about playing by the rules however, so let’s build some character and actually look at what this does (pardon the partial variable renames):

          undefined8 check(uint guess)
          
          {
            int iVar1;
            int WHAT;
            size_t sVar2;
            undefined8 WIN;
            long lVar3;
            undefined8 *inputPointer;
            long in_FS_OFFSET;
            int local_128;
            int local_124;
            undefined8 input;
            long local_10;
            
          
            local_10 = *(long *)(in_FS_OFFSET + 0x28);
            // Honestly the next lines are so ugly. They look like they're zeroing out
            // input via inputPointer, so we'll go with that
            inputPointer = &input;
            for (lVar3 = 0x1f; lVar3 != 0; lVar3 = lVar3 + -1) {
              *inputPointer = 0; // null bytes
              inputPointer = inputPointer + 1;
            }
            *(undefined4 *)inputPointer = 0;
            *(undefined2 *)((long)inputPointer + 4) = 0;
            *(undefined *)((long)inputPointer + 6) = 0;
            sprintf((char *)&input,"%d",(ulong)guess); // Scan guess into input
            sVar2 = strnlen((char *)&input,0xff); // These lines put the length
            WHAT = (int)sVar2;                    // of input into WHAT
            if ((sVar2 & 1) == 0) { // strlen must be even
              if (WHAT < 4) {      // Input has to be at least 4 char
                WIN = 0;           // As soon as I got to this function I just tossed
              }                    // in 8997 and won. lol. onwards!
              else {
                // Looks like we're looping through the first half of the string
                for (local_128 = 1; iVar1 = WHAT / 2, local_128 < WHAT / 2; local_128 = local_128 + 1) {
                  // If the character at local_128 is <= the previous one, fail
                  if (*(char *)((long)&input + (long)local_128) <=
                      *(char *)((long)&input + (long)(local_128 + -1))) {
                    WIN = 0;
                    goto function1; // Considered harmful
                  }
                }
                // Let's not pretend that either of us are fully familiar with
                // do while syntax.
                // But it's gotta be somewhat intuitive, right?
                // Looks like this increments iVar1 and local_124
                // starting just after where we stopped
                do {
                  local_124 = iVar1 + 1;
                  // If we've iterated past the end of the string, win!
                  if (WHAT <= local_124) {
                    WIN = 1;
                    goto function1;
                  }
                  lVar3 = (long)iVar1;
                  iVar1 = local_124; // iVar1 gets set to where the first loop ended
                } while (*(char *)((long)&input + (long)local_124) < *(char *)((long)&input + lVar3)); // Same as the last loop, but descend
                WIN = 0; // Otherwise, lose
              }
            }
            else {
              WIN = 0;
            }
          function1:
            if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) {
              return WIN;
            }
                              /* WARNING: Subroutine does not return */
            __stack_chk_fail();
          }

          This function walks up the first half of the number, expecting each digit to be greater than the last. It then walks back down, expecting each digit to be lesser than the last. Any number that fits this criteria will work, but 8997 happens to be a convenient test case 🙂

          footnotes

          1: The most common PIN numbers are 1234, 1111, and 0000, in case you ever find a debit card on the street and want to take your chances at the ATM.

          2: There also might be junk after the bytes you send in. Testing shows that 0xD0 + 0xD0 + 0x00 + 0x00 (ÐÐ), which should sum correctly does not succeed. It doesn’t I don’t seem to like extended ASCII codes anyways, but bonus points if you can figure out a way to do it with fewer characters. I think I’ll drop the computer roleplay for the rest of the footnotes

          3: This is, presumably, a Ghidra bug. Anyways, I didn’t notice until afterward. And I started by googling various permutations of “check c” because I had forgotten that it was a handmade function.

          4:

          Pin em wojak
          Nooo you can’t just paste gibberish you’re supposed to read through my incomprehensible function!!
          8997abagaga go brrrrr

          5: Seriously, I need to permanently remove that site from search results. Sorry this footnote is out of order, I’m putting them in by hand. Next project a writeup bloginator with footnote capabilities that would bring David Foster Wallace to jealous tears.

          Writeup: bounce_the_flag

          OSUSEC CTF League is back, baby! And it’s on Mondays! Piping hot writeups are back on the regular menu and we’re getting right into things with a classic SQL injection.

          The challenge: “Let’s kick things off with one of my favorite classic games: Bounce the Flag! Bounce the flag is an immersive hyper-realistic gaming experience blah blah blah. One of Bounce the Flag’s most celebrated competitors, Mr. Flag, blahdie blahdie blah, forgot the password to his account, blah blah blah”

          Forget about all that stuff – It’s gaming time!

          Sweet! High score!

          Awesome! I just got a high score! Time to record this epic win on the Bounce the Flag HOF!

          What?! I definitely typed my password in right, but I must not have an account. I’m mad! This piece of gaming history deserves to be on the leaderboard! I’m gonna get this score up on the leaderboard, mark my words.

          Luckily, we have access to the source code of the server and have been told ahead of time that the server is vulnerable to SQL injection 😀

          username = request.form['username_input']
          password = request.form['password_input']
          
          res = sql_fetchall(
                  connection,
                  f"""
                  SELECT score, game_time
                  FROM users
                  INNER JOIN games
                  ON users.id = games.user_id
                  WHERE username = '{username}' AND password = '{password}'
                  ORDER BY game_time
                  """
              )

          Our opportunity lies in the unsanitized username and password field. Submitting

          Mr. Flag' -- 

          gives us the message “Pfffffft you call that a high score?!! Try again when you score at least 1337 points!”

          As the score is held client-side, opening up the dev console and entering score=1338 is enough to log in and save our score.

          If we can modify our username or password to break the SQL request for the stats page, we’re golden.

          I had trouble crafting the username statement, so I switched to putting the exploit in the password, with the final exploit being:

          Username: Mr. Flag
          Password ' or 1=1 union select password,username from users -- 

          Writeup: Target Practice

          This was my first time getting first blood with my team, so I’m excited to write it up. OSINT challenges have a lot of collaboration opportunities, and our first blood was due to some good teamwork.

          Challenge Description: Can you help me find the full name of the person behind the alias “anonhunter26”? This link might be helpful: https://osintframework.com/ Submit flag as osu{firstname_lastname}

          Looks like we’ve got to track someone down given their username. Before doing anything, I checked for a twitter account. The bio states that our target is a “Senior software developer at a small startup.” Looks like finding the company might be our secondary goal. Scrolling through the rest of the account, we see reference to a “coworker and good friend” whose handle is @hatebav2ropc. Neither of these accounts follow or are followed by anyone, and their likes are relevant to infosec but not helpful in identification.

          Looks like “hatebav2ropc” might give us a clue, though, so let’s gather some information. https://whatsmyname.app/ shows that hatebav2ropc exists on GitHub. There is only one repository, with two commits. Clicking on a commit and adding “.patch” to the URL gives patch information.

          From b8e908c74c182c73d840192cd89d3f27b5177641 Mon Sep 17 00:00:00 2001
          From: Anonymous <anonymousfreak32@gmail.com>
          Date: Fri, 24 Sep 2021 23:57:15 -0700
          Subject: [PATCH] Fixed line formatting
          

          We’ve got an email! Looking at it with https://tools.epieos.com/email.php shows that the account belongs to someone named Gabriel Cortney.

          Using Twitter search for “Gabriel Cortney” reveals a more professional Twitter account, whose bio states that Cortney is a “Lead security analyst for @opticalsocial.” The company’s Twitter account is followed only by Cortney and a man claiming to be the CEO. We’re looking for a senior software developer, so we might take our new information back to Github.

          Searching for “opticalsocial” on Github gives us one user in the results: Oswald Denman, a senior software developer.

          Submitting the flag osu{Oswald_Denman} is a success!

          Writeup: Ultrasecure

          In this challenge, we are given a binary and the instructions to “Use pwntools and ghidra to reverse engineer and break into the ultrasecure(tm) vault!”

          Let’s check it out!

          Running the binary gives us the output:

          $ ./ultrasecure
          Prove that you are not human, repeat this to me in less than .05s: 1626412526
          1626412526
          Whoops, too slow

          Looks like my reflexes aren’t quite fast enough, so we’ll have to automate the nonce. This is a good time to use pwntools! First we’ll connect to the binary, and then we’ll read in and repeat the nonce back. The code for the nonce looks like this:

          
          nonce = conn.recvline()
          nonce = nonce.split()
          nonce = nonce[-1]
          # Sends line
          conn.sendline(nonce)
          

          Running this script gives us

          You passed the nonce check! Now, Unlock the UltraSecure(tm) Vault:
          $ password
          Whoops, wrong password :(

          Firing up Ghidra gives us a password_check function, which contains the lines below.

            
            local_c = -0x21524cc1;  
          __isoc99_scanf(&DAT_00400b9f,&local_3c);
            if (local_c == local_3c) {
              print_flag();
                              /* WARNING: Subroutine does not return */
              exit(0);
            }

          -0x21524cc1 = -559041729, so we’ve got our password! Entering it after running our script gives us access to the flag.

          Writeup: Hash Browns

          In our first OSUSEC challenge of 2021, we were given the instructions to “open the linked website in your browser. Good luck!”

          Opening the link in Firefox, we reach this site prompting for a password.

          My spidey-senses tingling, I opened inspect element. This revealed some javascript that activated when the submit button was pressed:

          function check_password() {
            let password = document.getElementById("password").value;
            let hash = get_sha256(password);
            if (hash == "b0fef621727ff82a7d334d9f1f047dc662ed0e27e05aa8fd1aefd19b0fff312c") {
              document.getElementById("login").submit();
            }
          }  

          We see that there is a sha256 hash in the script. Using Hash Toolkit on the hash reveals that the password is “pineapple.” Hit submit, and getting the flag should be trivial from here!

          Looks like we’re in for a bit more! We’ve got to click the link, which is twisting and turning all about the webpage. We can either click it (the fun way) or pretend to click it (the 1337 way).

          Looks like it calls a function called print_flag(), which can be entered using the web console. After this function is called, the text “Get the flag!” is replaced with the flag. Hitting Ctrl-A selects the text, which can then be copied and submitted!

          This was a fun, goofy introduction to OSUSEC, and I enjoyed my first-ever CTF challenge 🙂