Kibana 5.6.15未満および6.6.1未満のバージョンには、Timelionビジュアライザーにおける任意のコード実行の脆弱性が存在します。Timelionアプリケーションにアクセスできる攻撃者は、JavaScriptコードを実行しようとするリクエストを送信する可能性があります。これにより、攻撃者はホストシステム上のKibanaプロセスの権限で任意のコマンドを実行する可能性があります。
CVSS:

ブログ: https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/
ペイロード: https://github.com/mpgn/CVE-2019-7609
セットアップ:
使用:
シートの解析後:

シートの事前処理後、queriesオブジェクトが得られます:

次に、queries配列(valuesメソッドを使用して配列に変換されます)をループし、invokeにquery.functionとquery.argumentsを渡して関数を呼び出します。
invoke関数では、まず関数名(query.function)に基づいて関数定義を取得します:

この関数定義には、fn、originalFn、timelionFnの3種類の関数が含まれています。次に、args(query.arguments)をループして引数を解決します:

この場合、args内の引数は'literal'型のオブジェクトであるため、リテラル値をそのまま返します:

実際には、値はPromiseでラップされています。

インデックス付き引数は、内部にq配列を持つbyNameオブジェクトを持つ引数です:

その後、invoke関数は、すべてのインデックス付き引数を使用してfunctionDef.fn(Timelionクラスの関数)を呼び出します:

fn関数はoriginalFnを呼び出します。これはconfig.fn(configはtlConfig)から来ており、その関数はDatasource extends Timelionクラスのfnです:

ご覧の通り、esFnという別のoriginalFnを呼び出しており、これはseriesListオブジェクトを返します:

この関数は、「Elasticsearchインスタンスからデータをプルする」ために使用され、callWithRequest関数を呼び出します。
seriesListはDatasource.fnでは次のようになります:

Timelion.fnでは次のようになります:

シートの事前処理後、チェーンリストは次のようになります:

そして、コードはresolveChainListを呼び出してチェーンリストを解決します:

各チェーンは、invokeに'first'をfnNameとして、チェーン自体をargsとして渡して返されたPromiseにマッピングされます。
関数定義は再び名前で取得されます:

今回は、resolveArgumentsはargsを配列として受け取り、各引数を解決するために再帰的にresolveArgumentsを呼び出します。ただし、インデックス0の引数は1つだけです。その引数はtype == chainのオブジェクトです。
そのため、switchケースでは、invokeChain関数を呼び出し、チェーンオブジェクトを渡します:

invoke chainの中では、chain配列をshiftします。shift関数は削除された要素を返すため、linkはesオブジェクトになります:

esのタイプは'chain'ではなく('function'です)、!resultはtruthyであるため、コードはinvoke関数を'first'をfnNameとして、esオブジェクトをargsとして呼び出します:
今回は上記と同じ手順なので、linkがpropsオブジェクトのinvokeChain呼び出しにスキップします:

再び、コードはinvoke関数を'first'をfnNameとして、propsオブジェクトをargsとして呼び出します:

propsのoriginalFnは、esのようにfnではなくfirstFnであり、1つの引数のみを受け入れます:

そのオリジナル関数は、最終的に脆弱なunflatten関数を呼び出します。実際には、最初にTimelionのfnを呼び出し、次にfnがpropsのoriginalFnを呼び出します:

propsのfirstFnは、argsのbyNameオブジェクトからinputSeriesとglobalプロパティを省略します:

そのため、unflattenに渡されるdataは次のようになります:
{
label.__proto__.env.AAA: "a"
}
証明:

props(label.__proto__.env.AAA="a")を使用します。dataは次のようになります:

最初の反復では、prop = ''は存在しないため、cur[''] = {}を代入します。つまり、resultにも''という名前のプロパティが作成され、その値は空のオブジェクトになります。その後、curには前の代入の戻り値である{}が割り当てられます(curとcur[prop]は同じオブジェクトを指します):

次に、propは'label'になります:

前と同様に、resultに'label'プロパティが追加され、propは__proto__になります:

3回目の反復では、__proto__はすべてのオブジェクトに常に存在するため、curはそのプロトタイプになります:

その後、propはenvになります。
そして、curはObject.prototypeを指しているため、Object.prototypeにenvが空のオブジェクトとして追加され、curもObject.prototype.env内のenvを参照します。

その後、ループが終了し、cur[prop] = data[p]はenv[AAA] = 'a'と等価になります。

Object.prototypeがenvで汚染されました:
