Qakbotの文字列暗号化(2022/6,2022/9)

はじめに

この記事の内容は2022年6月、9月時点での解析で確認したQbotについての記事です。

アンパック後Qakbotの文字列

アンパックした後の検体を見てみると、図のようにほとんど解析に有用な文字列は少ない。「%u.%u.%u.%u」のようなIPアドレスと思われるフォーマットはあるが、解析に有用そうな文字列は見当たらない。

Qakbotに限らず、マルウェアの文字列とかはアンパック後も暗号化されていることがある。ネタバレをするが、Qakbotの文字列はxorで暗号化されている。

Qakbotの復号関数

以下はQakbotの文字列復号関数の一部だが意味のない処理が大量に含まれている。私が確認した検体ではGhidraのデコンパイル画面で389行もあった。

しかし、389行のほとんどは意味のない処理のため、しっかり読み込んで整理すると、実際のところは次のような復号関数になる。

Qakbotの文字列復号

文字列復号の関数は以下の図のように呼び出されている(なお、確認した検体では同様の処理が4つあった。)

検体に含まれている2種類のデータ(下図のDAT_1001e5b0DAT_1001f5d8)をxorすることで文字列を復号している。

復号するスクリプトは次の通り、実際の検体は呼び出す際に復号したい文字列だけを得られるように数値を渡している。(スクリプト中のhex_numは呼び出す際に渡す数値をいれる)

def decrypt_string(hex_num, dat_addr1, dat_addr2):
    decrypted_str = ""
    # calc size of decrypted string
    cp_hex = hex_num
    while True:
        if (getBytes(dat_addr2.add(cp_hex % 0x5a),1)[0] & 0xff) == (getBytes(dat_addr1.add(cp_hex),1)[0] & 0xff):
            break
        cp_hex += 1
    size = cp_hex - hex_num

    # decrypted string
    for i in range(size):
        decrypted_str += chr((getBytes(dat_addr2.add((hex_num + i) % 0x5a),1)[0] & 0xff) ^ (getBytes(dat_addr1.add(hex_num + i),1)[0] & 0xff) )
    return decrypted_str 

この復号するスクリプトを使うことで次のようになり、復号した文字列が得られる。

単純にxorだけしてみた結果

Ghidra上のPythonで検体内のデータを単純にxorすると次のような結果となり、復号できていることがわかる。どんな文字列があるか知りたいときはさくっとやってしまうとよい。

検体のハッシュ値

今回のブログを書く上で確認した検体のハッシュ値は次の通り

  • 2022/6の検体:117a60cb0cde4199a78f99d4f6eb5f50
  • 2022/9の検体:3fd6ff929bb62358cee961d45ff1471d