Skip to main content

より安全な未来を共に:SnykとCISPAが社会全体の利益のために協力

著者
Headshot of Aviad Hahami

Aviad Hahami

feature snyk cispa collab

2022年6月6日

0 分で読めます

学術界とソフトウェア業界が協力すれば、大きな成果が生まれます。今回は、ドイツを代表する科学研究機関であるCISPA Helmholtz Center for Information Securityとの最近の共同研究をご紹介します。

1月、Cris Staicu Ph.D.(CISPAのテニュアトラック教員)から、NodeJSとJavaScriptに関する研究について連絡がありました。Staicu氏とチームが行った研究では、プロトタイプ汚染の脆弱性、JSサンドボックスの脱出、NodeJSのネイティブ拡張機能における低レベルのC/C++の脆弱性など、JavaScriptおよびNodeJS環境における興味深いセキュリティ上の問題に焦点を当てていました。

CISPAと協力して、発見が難しい脆弱性をいくつか確認し、修正に向けてメンテナーに報告するとともに、CVEを割り当て、勧告を公開しました。

この記事では、CISPAと共に発見した脆弱性をいくつか解説するとともに、オープンソースの世界を誰もがより安全に利用できるよう、他の学術機関にも協力を呼びかけます。

NodeJSサンドボックスを脱出するガジェット

最初に取り組んだ脆弱性は、サンドボックス脱出の脆弱性でした。

「サンドボックス」という言葉に馴染みがない方のために説明すると、セキュリティへの影響を心配せずに処理を実行できる環境、プロセス、またはマシンを指します。たとえば、サニタイズされていないクライアント提供のコードやファイルを扱う場合に利用します。

このケースでは、Cris氏のチームが、回避可能なサンドボックスを備えたJSライブラリを発見しました。報告されたライブラリnotevilと、その派生版argencoders-notevilでは、プロトタイプ汚染の脆弱性を悪用してサンドボックスを回避できました。

この問題のPoCは次のとおりです。

var notevil = require('notevil')

notevil(`  
Object.defineProperty(({})[["__proto__"]][["__proto__"]], 'polluted', {
  value: 'success'
});`);

console.log(polluted); // prints "success"

このPoCは一見すると有害ではありませんが、同じライブラリにある類似の脆弱性が以前に報告されており、NodeJS環境ではRCE、ブラウザー環境ではXSSにつながる可能性があります。

このライブラリはすでに非推奨となっているものの、今なお多く利用されているため、Cris氏のチームはCVEの発行とメンテナーへの通知にあたり、私たちの支援を必要としていました。ご存じない方もいるかもしれませんが、Snykは公式CNAであり、必要に応じてCVEを発行できます。そこで、この脆弱性を今後の開発者に知らせるため、CVE-2021-23771を発行しました。

NodeJSネイティブ拡張機能における型の混同

CISPAは、NodeJSアプリケーションとNodeJSエンジン向けのC拡張機能間で起きる型の混同に焦点を当てていたため、このテーマで協力しました。CISPAが行った型の混同に関する実験の多くでV8エンジンがクラッシュしたことから、両者でさらに調査し、問題を詳しく理解することにしました。

例として、libxmljsライブラリを見てみましょう。ご存じない方のために説明すると、libxmljsはXMLの解析に使われ、内部ではネイティブのlibxmlバイナリを利用しています。

ライブラリのREADMEから引用した、基本的な使用例のコードを紹介します。

// As copied from libxmljs readme
var libxmljs = require("libxmljs");
var xml =  '<?xml version="1.0" encoding="UTF-8"?>' +
           '<root>' +
               '<child foo="bar">' +
                   '<grandchild baz="fizbuzz">grandchild content</grandchild>' +
               '</child>' +
               '<sibling>with content!</sibling>' +
           '</root>';

var xmlDoc = libxmljs.parseXml(xml); // you can now query the document

しかし、次のPoCを使うと、アプリケーション全体をクラッシュさせることができました。

let libxmljs = require("libxmljs");
let xml = {toString: 23};
try { 
    libxmljs.parseXml(xml);
} catch(e) {
    // never executed because of the hard crash
}

try/catchブロックが設定されているにもかかわらず、ユーザー入力によってアプリケーションをクラッシュさせられるため、DoS攻撃の侵入口となります。

前のブロックのコードを実行すると、次のスタックトレースが出力されます。

FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal.
 1: 0xb23a90 node::Abort() [node]
 2: 0xa3823c node::FatalError(char const*, char const*) [node]
 3: 0xd13ffa v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: 0x7fa0bc5eec9b libxmljs::XmlDocument::FromXml(Nan::FunctionCallbackInfo<v8::Value> const&) [/opt/node_modules/libxmljs/build/Release/xmljs.node]
 5: 0x7fa0bc5ea208  [/opt/node_modules/libxmljs/build/Release/xmljs.node]
 6: 0xd7019e  [node]
 7: 0xd715bf v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [node]
 8: 0x16138f9  [node]
Aborted

GDBを使ってさらにデバッグしたところ、基盤となるエンジンはString型の引数を想定しているものの、オブジェクトが渡されると、それを文字列に変換しようとすることがわかりました。

上記の例では、toStringメソッドを呼び出せません(値が単なる数値のため)。V8はこれを処理できず、例外を発生させます。しかし、この例外はV8内で発生するため、try/catchブロックでは捕捉できず、アプリケーション全体がクラッシュします。

CISPAと共にメンテナーに連絡し、脆弱性を責任ある形で開示しました。この脆弱性はCVE-2022-21144として追跡されています。

共同研究のご案内

CISPAのような学術機関との共同研究を、さらに広げていきたいと考えています。Snykと連携することで、いくつものメリットを得られます。脆弱性を発見することと、特にオープンソースライブラリの脆弱性を開示することは別の課題です。専任のアナリストと研究者のチームが、脆弱性の検証と影響の把握を支援します。また、脆弱性を責任を持って、慎重かつ専門的に開示できるプロセスを整えています。さらに、SnykはCNAであるため、専用の追跡システムを使って報告者とメンテナーと連携し、双方に常に最新情報を共有できます。

新たな脆弱性を発見したら、ぜひご連絡ください。確認と開示のプロセスを喜んで支援します。

安全な開発を!