AUCTF 2020 Writeup

この大会は2020/4/3 22:00(JST)~2020/4/6 13:00(JST)に開催されました。
今回もチームで参戦。結果は 39406点で1184チーム中4位でした。
自分で解けた問題をWriteupとして書いておきます。

Start Here (Welcome)

問題にフラグが書いてあった。

auctf{on_the_house}

Join our Discord (Welcome)

Discordに入ると、#ruleチャネルのトピックにフラグが書いてあった。

auctf{welcome_to_the_danger_zone}

Mr. Game and Watch (Reversing)

classファイルをByteode Viewerでデコンパイルする。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Scanner;

public class mr_game_and_watch {
   public static String secret_1 = "d5c67e2fc5f5f155dff8da4bdc914f41";
   public static int[] secret_2 = new int[]{114, 118, 116, 114, 113, 114, 36, 37, 38, 38, 120, 121, 33, 36, 37, 113, 117, 118, 118, 113, 33, 117, 121, 37, 119, 34, 118, 115, 114, 120, 119, 114, 36, 120, 117, 120, 38, 114, 35, 118};
   public static int[] secret_3 = new int[]{268, 348, 347, 347, 269, 256, 348, 269, 256, 256, 344, 271, 271, 264, 266, 348, 257, 266, 267, 348, 269, 266, 266, 344, 267, 270, 267, 267, 348, 349, 349, 265, 349, 267, 256, 269, 270, 349, 268, 271, 351, 349, 347, 269, 349, 271, 257, 269, 344, 351, 265, 351, 265, 271, 346, 271, 266, 264, 351, 349, 351, 271, 266, 266};
   public static int key_2 = 64;
   public static int key_3 = 313;

   public static void main(String[] var0) {
      System.out.println("Welcome to the Land of Interpreted Languages!");
      System.out.println("If you are used to doing compiled languages this might be a shock... but if you hate assembly this is the place to be!");
      System.out.println("\nUnfortunately, if you hate Java, this may suck...");
      System.out.println("Good luck!\n");
      if (crackme()) {
         print_flag();
      }

   }

   private static boolean crackme() {
      Scanner var0 = new Scanner(System.in);
      if (crack_1(var0) && crack_2(var0) && crack_3(var0)) {
         System.out.println("That's correct!");
         var0.close();
         return true;
      } else {
         System.out.println("Nope that's not right!");
         var0.close();
         return false;
      }
   }

   private static boolean crack_1(Scanner var0) {
      System.out.println("Let's try some hash cracking!! I'll go easy on you the first time. The first hash we are checking is this");
      String var10001 = secret_1;
      System.out.println("\t" + var10001);
      System.out.print("Think you can crack it? If so give me the value that hashes to that!\n\t");
      String var1 = var0.nextLine();
      String var2 = hash(var1, "MD5");
      return var2.compareTo(secret_1) == 0;
   }

   private static boolean crack_2(Scanner var0) {
      System.out.println("Nice work! One down, two to go ...");
      System.out.print("This next one you don't get to see, if you aren't already digging into the class file you may wanna try that out!\n\t");
      String var1 = var0.nextLine();
      return hash(var1, "SHA1").compareTo(decrypt(secret_2, key_2)) == 0;
   }

   private static boolean crack_3(Scanner var0) {
      System.out.print("Nice work! Here's the last one...\n\t");
      String var1 = var0.nextLine();
      String var2 = hash(var1, "SHA-256");
      int[] var3 = encrypt(var2, key_3);
      return Arrays.equals(var3, secret_3);
   }

   private static int[] encrypt(String var0, int var1) {
      int[] var2 = new int[var0.length()];

      for(int var3 = 0; var3 < var0.length(); ++var3) {
         var2[var3] = var0.charAt(var3) ^ var1;
      }

      return var2;
   }

   private static String decrypt(int[] var0, int var1) {
      String var2 = "";

      for(int var3 = 0; var3 < var0.length; ++var3) {
         var2 = var2 + (char)(var0[var3] ^ var1);
      }

      return var2;
   }

   private static void print_flag() {
      String var0 = "flag.txt";

      try {
         BufferedReader var1 = new BufferedReader(new FileReader(var0));

         String var2;
         try {
            while((var2 = var1.readLine()) != null) {
               System.out.println(var2);
            }
         } catch (Throwable var5) {
            try {
               var1.close();
            } catch (Throwable var4) {
               var5.addSuppressed(var4);
            }

            throw var5;
         }

         var1.close();
      } catch (IOException var6) {
         System.out.println("Could not find file please notify admin");
      }

   }

   public static String hash(String var0, String var1) {
      String var2 = null;

      try {
         MessageDigest var3 = MessageDigest.getInstance(var1);
         byte[] var4 = var3.digest(var0.getBytes("UTF-8"));
         StringBuilder var5 = new StringBuilder(2 * var4.length);
         byte[] var6 = var4;
         int var7 = var4.length;

         for(int var8 = 0; var8 < var7; ++var8) {
            byte var9 = var6[var8];
            var5.append(String.format("%02x", var9 & 255));
         }

         var2 = var5.toString();
      } catch (Exception var10) {
         System.out.println("broke");
      }

      return var2;
   }
}

crack1~3の関数、それぞれでtrueを返す条件を満たせばよい。
まずcrack_1がtrueになる条件は、md5がd5c67e2fc5f5f155dff8da4bdc914f41になる文字列を指定することである。CrackStationで逆変換すると、masterchief であることがわかる。
次にcrack_2がtrueになる条件は、sha1がdecrypt(secret_2, key_2)となる文字列を指定することである。decryptはXORを取ればよい。こう考えると、以下の条件を満たすものであることになる。

decrypt(secret_2, key_2) = 264212deff89ade15661a59e7b632872d858f2c6

CrackStationで逆変換すると、princesspeach であることがわかる。
最後にcrack_3がtrueになる条件は、sha256したものをvar2としたときに、encrypt(var2, key_3)がsecret_3 と同じになる文字列を指定することである。encryptもXORを取っているので、逆算できる。この結果、sha256が5ebb49e499a6613e832e433a2722edd0d2947d56fdb4d684af0f06c631fdf633になる文字列がわかればよい。CrackStationで逆変換すると、solidsnake であることがわかる。
サーバにつなげてこのクラックした結果を答えていく。

$ nc challenges.auctf.com 30001
Welcome to the Land of Interpreted Languages!
If you are used to doing compiled languages this might be a shock... but if you hate assembly this is the place to be!

Unfortunately, if you hate Java, this may suck...
Good luck!

Let's try some hash cracking!! I'll go easy on you the first time. The first hash we are checking is this
	d5c67e2fc5f5f155dff8da4bdc914f41
Think you can crack it? If so give me the value that hashes to that!
	masterchief
Nice work! One down, two to go ...
This next one you don't get to see, if you aren't already digging into the class file you may wanna try that out!
	princesspeach
Nice work! Here's the last one...
	solidsnake
That's correct!
auctf{If_u_h8_JAVA_and_@SM_try_c_sharp_2922}
auctf{If_u_h8_JAVA_and_@SM_try_c_sharp_2922}

Land Locked (Cryptography)

base64文字列をデコードする。

$ echo YXVjdGZ7NExMX3kwVXJfQjQ1M19SX2IzbDBOZ18yX3VTXzEyNGRmMnNkYXN2fQ== | base64 -d
auctf{4LL_y0Ur_B453_R_b3l0Ng_2_uS_124df2sdasv}
auctf{4LL_y0Ur_B453_R_b3l0Ng_2_uS_124df2sdasv}

I'll Have The Salad (Cryptography)

シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。

Rotation 21:
auctf{jU5t_4_W4rM_uP_1_4421_9952}
auctf{jU5t_4_W4rM_uP_1_4421_9952}

Shifty Shwoozy (Cryptography)

Vigenere暗号。https://www.dcode.fr/vigenere-cipherで復号してみる。
部分的に試すが、完全に鍵を割り出すことができない。一番近そうなのが、ELPYOUSOLVETHISPROBLEMであるがやはり不完全。プログラムで調整しながら、鍵を割り出し、復号する。最終的なコードは以下のようになった。

import string

def decrypt(s, key):
    i = 0
    dec = ''
    for c in s:
        if c in string.lowercase:
            index_ct = string.lowercase.index(c)
            index_key = string.lowercase.index(key[i%len(key)])
            index_pt = (index_ct - index_key) % 26
            dec += string.lowercase[index_pt]
            i += 1
        elif c in string.uppercase:
            index_ct = string.uppercase.index(c)
            index_key = string.lowercase.index(key[i%len(key)])
            index_pt = (index_ct - index_key) % 26
            dec += string.uppercase[index_pt]
            i += 1
        else:
            dec += c
    return dec


with open('ciphertext.txt', 'r') as f:
    enc = f.read()

key = 'coincidencesmayhelpyousolvethisproblem'

dec = decrypt(enc, key)
print dec

鍵はcoincidencesmayhelpyousolvethisproblem。復号結果は以下の通り。

In my younger and more vulnerable years my father gave me some advice that I've been turning over in my mind ever since.

"Whenever you feel like criticizing any one," he told me, "just remember that all the people in this world haven't had the advantages that you've had."

He didn't say any more but we've always been unusually communicative in a reserved way, and I understood that he meant a great deal more than that. In consequence I'm inclined to reserve all judgments, a habit that has opened up many curious natures to me and also made me the victim of not a few veteran bores. The abnormal mind is quick to detect and attach itself to this quality when it appears in a normal person, and so it came about that in college I was unjustly accused of being a politician, because I was privy to the secret griefs of wild, unknown men. Most of the confidences were unsought--frequently I have feigned sleep, preoccupation, or a hostile levity when I realized by some unmistakable sign that an intimate revelation was quivering on the horizon--for the intimate revelations of young men or at least the terms in which they express them are usually plagiaristic and marred by obvious suppressions. Reserving judgments is a matter of infinite hope. I am still a little afraid of missing something if I forget that, as my father snobbishly suggested, and I snobbishly repeat, a sense of the fundamental decencies is parcelled out unequally at birth.

The answer is  the following: sometimes you have to do things yourself. Replaces the spaces with underscores and wrap it in the usual format.

And, after boasting this way of my tolerance, I come to the admission that it has a limit. Conduct may be founded on the hard rock or the wet marshes but after a certain point I don't care what it's founded on. When I came back from the East last autumn I felt that I wanted the world to be in uniform and at a sort of moral attention forever; I wanted no more riotous excursions with privileged glimpses into the human heart. Only Gatsby, the man who gives his name to this book, was exempt from my reaction--Gatsby who represented everything for which I have an unaffected scorn. If personality is an unbroken series of successful gestures, then there was something gorgeous about him, some heightened sensitivity to the promises of life, as if he were related to one of those intricate machines that register earthquakes ten thousand miles away. This responsiveness had nothing to do with that flabby impressionability which is dignified under the name of the "creative temperament"--it was an extraordinary gift for hope, a romantic readiness such as I have never found in any other person and which it is not likely I shall ever find again. No--Gatsby turned out all right at the end; it is what preyed on Gatsby, what foul dust floated in the wake of his dreams that temporarily closed out my interest in the abortive sorrows and short-winded elations of men.
                :
It was lonely for a day or so until one morning some man, more recently arrived than I, stopped me on the road.

The answer is  the following: sometimes you have to do things yourself. Replaces the spaces with underscores and wrap it in the usual format.

"How do you get to West Egg village?" he asked helplessly.
                :
"But we heard it," insisted Daisy, surprising me by opening up again in a flower-like way. "We heard it from three people so it must be true."

Of course I knew what they were referring to, but I wasn't even vaguely engaged. The fact that gossip had published the banns was one of the reasons I had come east. You can't stop going with an old friend on account of rumors and on the other hand I had no intention of being rumored into marriage.

Their interest rather touched me and made them less remotely rich--nevertheless, I was confused and a little disgusted as I drove away. It seemed to me that the thing for Daisy to do was to rush out of the house, child in arms--but apparently there were no such intentions in her head. As for Tom, the fact that he "had some woman in New York" was really less surprising than that he had been depressed by a book. Something was making him nibble at the edge of stale ideas as if his sturdy physical egotism no longer nourished his peremptory heart.

Already it was deep summer on roadhouse roofs and in front of wayside garages, where new red gas-pumps sat out in pools of light, and when I reached my estate at West Egg I ran the car under its shed and sat for a while on an abandoned grass roller in the yard. The wind had blown off, leaving a loud bright night with wings beating in the trees and a persistent organ sound as the full bellows of the earth blew the frogs full of life. The silhouette of a moving cat wavered across the moonlight and turning my head to watch it I saw that I was not alone--fifty feet away a figure had emerged from the shadow of my neighbor's mansion and was standing with his hands in his pockets regarding the silver pepper of the stars. Something in his leisurely movements and the secure position of his feet upon the lawn suggested that it was Mr. Gatsby himself, come out to determine what share was his of our local heavens.

I decided to call to him. Miss Baker had mentioned him at dinner, and that would do for an introduction. But I didn't call to him for he gave a sudden intimation that he was content to be alone--he stretched out his arms toward the dark water in a curious way, and far as I was from him I could have sworn he was trembling. Involuntarily I glanced seaward--and distinguished nothing except a single green light, minute and far away, that might have been the end of a dock. When I looked once more for Gatsby he had vanished, and I was alone again in the unquiet darkness.

フラグに関係ある文章は以下の部分。

The answer is  the following: sometimes you have to do things yourself. Replaces the spaces with underscores and wrap it in the usual format.
auctf{sometimes_you_have_to_do_things_yourself}

Twice As Secret, Right? (Cryptography)

Audacityで読み込み、スペクトログラムを拡大してみると、モールス信号になっている。モールス信号をデコードすると、"A","B"の組み合わせしかないので、ベーコン暗号として復号する必要がある。

bacon = {'AAAAA': 'a', 'AAAAB': 'b', 'AAABA': 'c', 'AAABB': 'd', 'AABAA': 'e',
    'AABAB': 'f', 'AABBA': 'g', 'AABBB': 'h', 'ABAAA': 'i',
    'ABAAB': 'k', 'ABABA': 'l', 'ABABB': 'm', 'ABBAA': 'n', 'ABBAB': 'o',
    'ABBBA': 'p', 'ABBBB': 'q', 'BAAAA': 'r', 'BAAAB': 's', 'BAABA': 't',
    'BAABB': 'u', 'BABAA': 'w', 'BABAB': 'x', 'BABBA': 'y', 'BABBB': 'z'}

enc = 'BABAAAABAAABABAABABAAABBBAAAAABAABBAABAABAABAABBABABABBAABAAAABAABAABA' \
 + 'AAAAAABBAAABBABBAABAAABBBAABAABAAAAAAABBAAAAABABBABABAAAABAABAAAAAABAAAAB' \
 + 'AABABABABBBAAABAAAAABABAABAABAAAABBAAAABBABAABAAABBBAABAAABABBBAABAABBABA' \
 + 'AABAABBABABABBAABAAAABBBAABAABAAAAAABAABAAABABBABABBABABBAABAABAAABBBAABA' \
 + 'AABABBAABAABAAABBAAABAAAAAAABBAAABAAAABBBAAAAABAAABBAABAABBABAAAABAABAAAA' \
 + 'ABBAABAAABABAABAAABAABBAABAABAAAAAABAAAAABBBAAABABBABABBABABBAAABBABBAABA' \
 + 'AABBBAABAABAAAABABAAABAAABAAABAABAAAAAAAABABAABABAABAAABAAABAABABABBABBAA' \
 + 'AAABBAAAAAAABAABBAABBAAABBBBAABABAABAAABBBAABAAAABABABABAAAAAAAABBAABAAAB' \
 + 'AAABAAAAABAABBAAABABAABAAABABBAAABABBABABABBAABAAABBABABBAAAABAAABBAAAABA' \
 + 'AAABAAAAABBBAAABBAABAABBABAAABAAAAAAABABAABABABAABAAABBBAABAAAABABAAABAAA' \
 + 'ABAABBABABBAABAABAAABBBAABAABAAABAABAAAAABABAAAAAAAAABABBBBABBAAABABAAAAA' \
 + 'BAAAAABABBAABAABAAAABAAAB'

msg = ''
for i in range(0, len(enc), 5):
    msg += bacon[enc[i:i+5]]

print msg

復号結果は以下の通り。

wellhauetomeetanotherdaywereexpectingthemtocomeheresoonthemessagehastobedeliueredsoonotherwiseallisfornaughttheflagisauctfsomeoneneedstocallthefcconthesecrazyfarmers

i/j, u/vの区別はつかないことを考慮して意味が通じるよう文にしてみる。

well have to meet another day were expecting them to come here soon the message has to be delivered soon otherwise all is for naught the flag is auctfsomeoneneedstocallthefcconthesecrazyfarmers
auctfsomeoneneedstocallthefcconthesecrazyfarmers

Extraordinary (Cryptography)

$ nc challenges.auctf.com 30030
> 1
b'P'
> 2
b'S'
> 1
b'P'
> aa
b'\x14'
> 11
b'PD'
> 11111111111
b'PDREWJ_\x02G\x02c'
> 13213asdajsdadgia
b'PFQEU\x1a\x1dW\x17Y!;\x13W2\x1aR'
> 13213adasdakdadasd
b'PFQEU\x1a\nR\x05W34\x16R1\x12@;'
> 43296
b'UFQMP'
> dj
b'\x05\x1f'
> 121
b'PGR'
> 111111
b'PDREWJ'

位置により同じ文字でも異なる文字に変換される。ブルートフォースして復号し、さらに復号したメッセージと暗号のXORを取る。

import socket

def recvuntil(s, tail):
    data = ''
    while True:
        if tail in data:
            return data
        data += s.recv(1)

enc = '6\x1d\x0cT*\x12\x18V\x05\x13c1R\x07u#\x021Jq\x05\x02n\x03t%1\\\x04@V7P\\\x17aN'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('challenges.auctf.com', 30030))

msg = ''
for i in range(len(enc)):
    for code in range(32, 127):
        data = recvuntil(s, '> ')
        print data + msg + chr(code)
        s.sendall(flag + chr(code) + '\n')
        data = recvuntil(s, '\n').rstrip()
        print data
        try_enc = eval(data)
        if try_enc[:i+1] == enc[:i+1]:
            msg += chr(code)
            break

print msg

flag = ''
for i in range(len(msg)):
    code = ord(msg[i]) ^ ord(enc[i])
    flag += chr(code)

print flag

実行結果は以下の通り。

    :
> Who Lives 1n 4 P1n3ApP13 Und3r The S1
b'6\x1d\x0cT*\x12\x18V\x05\x13c1R\x07u#\x021Jq\x05\x02n\x03t%1\\\x04@V7P\\\x17aL'
> Who Lives 1n 4 P1n3ApP13 Und3r The S2
b'6\x1d\x0cT*\x12\x18V\x05\x13c1R\x07u#\x021Jq\x05\x02n\x03t%1\\\x04@V7P\\\x17aO'
> Who Lives 1n 4 P1n3ApP13 Und3r The S3
b'6\x1d\x0cT*\x12\x18V\x05\x13c1R\x07u#\x021Jq\x05\x02n\x03t%1\\\x04@V7P\\\x17aN'
Who Lives 1n 4 P1n3ApP13 Und3r The S3
auctf{n3v3R_r3Us3_y0uR_0Tp_872vc8972}
auctf{n3v3R_r3Us3_y0uR_0Tp_872vc8972}

Pretty Ridiculous (Cryptography)

nを素因数分解する。

$ python -m primefac 627585038806247
627585038806247: 46631887 13458281

あとは各値についてそのまま復号する。

from Crypto.Util.number import inverse

with open('message.txt', 'r') as f:
    cs = eval(f.read())

n = 627585038806247
e = 65537
p = 46631887
q = 13458281

phi = (p - 1) * (q - 1)
d = inverse(e, phi)

flag = ''
for c in cs:
    m = pow(c, d, n)
    flag += chr(m)

print flag
auctf{R34lLy_Pr1M3s_w1L1_n3vEr_b3_thI5_Sm411_BuT_h3y}

Sign Me Up (Cryptography)

nの素因数分解は難しい。1文字ずつ暗号化しているようなので、ブルートフォースで復号する。

with open('message.txt', 'r') as f:
    cs = eval(f.read())

n = 20312493432722984634615913227523125265781662152013094377607630781356105942700273581600613724248110835803158659086732527322062709047441686884292861771528866639670389435647460159612029672461252955594829829663172687201461554413049025271464412190235617740846789840419025423396967519520427432799227162339126087426790939948330768088600429869826069490486741417370162186831426441346576810446894902659826134877586519596679449287778809427232767231366708775004671368581690484301650399106765403344734339945464967775820750215294237822308697430395972800155973323880641007064174229976873404987801414040860359400339131120435868680687
e = 65537

flag = ''
for c in cs:
    for m in range(32, 127):
        if pow(m, e, n) == c:
            flag += chr(m)
            break

print flag
auctf{D0nT_5igN_r4nd0m_Cr4P_w1tH_y0uR_pr1vAT3_k3y_d00d}