paint-brush
nix libX11 の 35 年前の脆弱性を明らかにする: パート 1@yairjfrog
319 測定値
319 測定値

nix libX11 の 35 年前の脆弱性を明らかにする: パート 1

Yair Mizrahi18m2024/03/08
Read on Terminal Reader

長すぎる; 読むには

このブログ シリーズでは、X.Org libX11 で見つかったセキュリティの脆弱性、つまり CVE-2023-43786 および CVE-2023-43787 を詳しく調べ、XPM ファイル形式の複雑さを調査し、これらの脆弱性の悪用を実証します。提供される包括的な洞察と修正によりシステムを保護する方法を学びましょう。
featured image - nix libX11 の 35 年前の脆弱性を明らかにする: パート 1
Yair Mizrahi HackerNoon profile picture
0-item


私のチームが最近発見したX.Org libX11 の 2 つのセキュリティ脆弱性、広く普及しているグラフィックス ライブラリ – CVE-2023-43786 および CVE-2023-43787 (NVD 重大度が高いCVSS 7.8 )。これらの脆弱性により、サービス妨害やリモートでのコード実行が引き起こされます。 X11 の最新バージョンには、これらの脆弱性に対する修正が含まれています。


チームはオープンソース プロジェクトを常に監視して新しい脆弱性や悪意のあるパッケージを発見し、それらをより広範なコミュニティと共有して全体的なセキュリティ体制の向上に役立てています。


この 2 部構成のブログ シリーズでは、脆弱な Xpm ファイル形式の内部動作の詳細を提供し、これらの脆弱性の悪用について詳しく説明します。


libX11とは何ですか?

Xorg X11 は、X Window System とも呼ばれ、Unix 系オペレーティング システムでのグラフィカル ユーザー インターフェイスの作成と管理を可能にするオープンソースグラフィカル サーバー プロトコルです。ネットワーク環境でグラフィカル アプリケーションを実行し、Windows を管理し、ユーザー入力を処理するためのフレームワークを提供します。


libx11 パッケージは、クライアント アプリケーションがデスクトップ上でデータをレンダリングして表示するために必要な必須の共有ライブラリを提供します。


クライアント_サーバー X アーキテクチャ


Xterm ウィンドウ - X11 クライアント アプリケーション


libXpmとは何ですか?

libXpm は、 X Pixmap (XPM) 形式でイメージを読み取り、書き込み、表示する関数を提供します。

XPM は主に、透明ピクセルをサポートするアイコン ピクスマップを生成することを目的としています。これは XBM 構文に基づいており、XPM2 形式のプレーン テキスト ファイルにすることも、C プログラミング言語構文を使用することもできるため、C プログラム ファイル内に含めるのに適しています。


XPM イメージ形式 – バージョン

前任者 – XBM


XPM (X PixMap) が 1989 年に登場する前には、XBM 形式 (X BitMap) がありました。


プレーンテキストのバイナリ イメージ形式。X GUI で使用されるアイコンとカーソルのビットマップを保存するために使用されます。


XBM ファイルは C ソース ファイルとして構造化されています。これが、今日のほとんどの画像形式との主な違いです。そうすることで、アプリケーションに直接組み込むことができます。ただし、これと圧縮を使用できない(1 文字から 1 バイトのマッピング)ため、生のピクセル データよりもはるかに大きくなります。


X BitMap (XBM) データは、生のモノクロ ピクセル情報を格納する一連の静的な unsigned char 配列で構成されます。


たとえば、次の C コードは、イメージ内のhelloの XBM ファイルです。

XnView に表示される hello Xbm 画像

 #define hello_width 35 #define hello_height 25 static char hello_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x88, 0x00, 0x00, 0x10, 0x00, 0x88, 0x00, 0x00, 0x10, 0x00, 0x88, 0x00, 0x00, 0xD0, 0xE1, 0x88, 0x78, 0x00, 0x30, 0x13, 0x89, 0xC4, 0x00, 0x10, 0x12, 0x89, 0x84, 0x00, 0x10, 0xF2, 0x89, 0x84, 0x00, 0x10, 0x12, 0x88, 0x84, 0x00, 0x10, 0x12, 0x88, 0x44, 0x00, 0x10, 0xE2, 0x89, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

通常の画像ファイル形式ヘッダーの代わりに、XBM ファイルには#defineステートメントがあります。最初の値のペアは、ビットマップのピクセル寸法を定義し、高さと幅を示します。


X BitMap (XBM) 画像データは、静的配列内に格納されるピクセル値の連続シーケンスとして構造化されています。各ピクセルが 1 つのビット (0 は白を示し、1 は黒を表す) でシンボル化されているとすると、配列内の各バイトには 8 つの個別のピクセルのデータが含まれます。特に、最初のバイトの最下位ビットは、ビットマップ内の左上のピクセルのアンカーとして機能します。


XPM1

XPMバージョンに対応


1989 年に初めてリリースされたこの形式は、上で概説した XBM 形式と多くの類似点を共有しています。 XBM はモノクロでしたが、XPM1 では画像にカラーが導入されました。


色に追加のマクロと変数を使用し、ピクセル データのバイトを文字に置き換えます。


たとえば、次は XPM1 形式の同じhello画像です。


 /* XPM */ static char *_hello[] = { /* columns rows colors chars-per-pixel */ "35 25 2 1 ", " c white", "bc black", /* pixels */ " ", " ", " ", " ", " ", " ", " ", " bbb ", " bbb ", " bbb ", " b bbb bbb bb bbbb ", " bb bb bbbbb bb ", " bbbbbbbb ", " bb bbbbb bbbb ", " bbbbbbb ", " bbbbbbb ", " bb bbbb bb bbb ", " ", " ", " ", " ", " ", " ", " ", " " };

ここで、 XFACE_ncolors画像内の色の数を示し、 XFACE_chars_per_pixelピクセルあたりの文字数を示します。


a白の色「#ffffff」に置き換えられ、各b黒の色「#000000」に置き換えられます。


XPM2

1 年後の 1990 年に、状況を改善するためにフォーマットの 2 番目のバージョンが設定されました。これにより作業が簡素化され、イメージ形式からすべての C コードが削除されました。


簡略化した構造:

 ! XPM2 <Header> <Colors> <Pixels>


ヘッダー行は、XPM1 の #define ステートメントと同様にイメージの寸法を示します。

色のセクションでは文字の値を定義します。


新しい型の概念が導入されました。

c – はカラーピクセルを表します

m – はモノクロを表します

g – グレースケール用の氷

s – はシンボリックです


シンボリック機能は、コンテキストに基づいて色を割り当て、読みやすいようにそれらの変数名を作成するために使用されます。


前の例で示したものと同じhelloを含む XPM2 イメージの例:

 ! XPM2 35 25 2 1 ac #000000 bc #ffffff aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaabaaaaaaaaaaaaaabaaabaaaaaaaaaaa aaaabaaaaaaaaaaaaaabaaabaaaaaaaaaaa aaaabaaaaaaaaaaaaaabaaabaaaaaaaaaaa aaaababbbaaaabbbaaabaaabaaabbbbaaaa aaaabbaabbaabaaabaabaaabaabaaabbaaa aaaabaaaabaabaaabaabaaabaabaaaabaaa aaaabaaaabaabbbbbaabaaabaabaaaabaaa aaaabaaaabaabaaaaaabaaabaabaaaabaaa aaaabaaaabaabaaaaaabaaabaabaaabaaaa aaaabaaaabaaabbbbaabaaabaaabbbaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

16 進数のカラー コードとは別に、X11 のカラー名 (例: red ) を使用して色を指定することもできますNone透明度を示します。


XPM3

これは XPM 形式の現在のバージョンです。


1991 年にリリースされたバージョンでは C コードが復活しましたが、純粋な C コード スタイルを使用する代わりに、内部の値は基本的に XPM2 形式と同じです。


例:

 /* XPM */ static char *_hello[] = { /* columns rows colors chars-per-pixel */ "35 25 2 1 ", " c white", "bc black", /* pixels */ " ", " ", " ", " ", " ", " ", " ", " bbb ", " bbb ", " bbb ", " b bbb bbb bb bbbb ", " bb bb bbbbb bb ", " bbbbbbbb ", " bb bbbbb bbbb ", " bbbbbbb ", " bbbbbbb ", " bb bbbb bb bbb ", " ", " ", " ", " ", " ", " ", " ", " " };


説明したように、XPM 形式は、JFrog ロゴなど、より洗練された画像を表すこともできます。

XPM3 を使用して描かれた JFrog ロゴ


DoS 脆弱性 – CVE-2023-43786

CVE-2023-43786 の脆弱性は、本質的には、誤った再帰停止条件の計算によって生じる無限ループです。


修正されたコミット:

https://gitlab.freedesktop.org/xorg/lib/libx11/-/commit/204c3393c4c90a29ed6bef64e43849536e863a86


XPutImage 、X Drawable (通常は X Window) に画像を配置できるようにする libX11 の関数です。この関数を使用すると、ピクセル情報を XImage 構造体からウィンドウやピックスマップなどの指定されたドローアブルに転送し、必要に応じて配置できます。


xpmCreatePixmapFromImage

xpmCreatePixmapFromImage libXpm 関数は、次のXPutImage関数を呼び出します。

 void xpmCreatePixmapFromImage( Display *display, Drawable d, XImage *ximage, Pixmap *pixmap_return) { GC gc; XGCValues values; *pixmap_return = XCreatePixmap(display, d, ximage->width, ximage->height, ximage->depth); /* set fg and bg in case we have an XYBitmap */ values.foreground = 1; values.background = 0; gc = XCreateGC(display, *pixmap_return, GCForeground | GCBackground, &values); XPutImage(display, *pixmap_return, gc, ximage, 0, 0, 0, 0, ximage->width, ximage->height); XFreeGC(display, gc); }


この関数では、 ximage表示されるソース イメージのピクセル データであり、 X Drawableオブジェクト (この場合はpixmap_return ) にコピーされます。


XPutImage

XPutImage libX11 関数は次のとおりです。

 int XPutImage ( register Display *dpy, Drawable d, GC gc, register XImage *image, int req_xoffset, int req_yoffset, int x, int y, unsigned int req_width, unsigned int req_height){ ..... PutSubImage(dpy, d, gc, &img, 0, 0, x, y, (unsigned int) width, (unsigned int) height, dest_bits_per_pixel, dest_scanline_pad); UnlockDisplay(dpy); SyncHandle(); Xfree(img.data); return 0; } }
 LockDisplay(dpy); FlushGC(dpy, gc); PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, (unsigned int) width, (unsigned int) height, dest_bits_per_pixel, dest_scanline_pad);......... }


PutSubImage関数を呼び出します。

 static void PutSubImage ( register Display *dpy, Drawable d, GC gc, register XImage *image, int req_xoffset, int req_yoffset, int x, int y, unsigned int req_width, unsigned int req_height, int dest_bits_per_pixel, int dest_scanline_pad) { int left_pad, BytesPerRow, Available; if ((req_width == 0) || (req_height == 0)) return; Available = ((65536 < dpy->max_request_size) ? (65536 << 2) : (dpy->max_request_size << 2)) - SIZEOF(xPutImageReq); if ((image->bits_per_pixel == 1) || (image->format != ZPixmap)) { [1] left_pad = (image->xoffset + req_xoffset) & (dpy->bitmap_unit - 1); BytesPerRow = (ROUNDUP((long)req_width + left_pad, dpy->bitmap_pad) >> 3) * image->depth; } else { [2] left_pad = 0; BytesPerRow = ROUNDUP((long)req_width * dest_bits_per_pixel, [3] dest_scanline_pad) >> 3; } if ((BytesPerRow * req_height) <= Available) { [4] PutImageRequest(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, req_width, req_height, dest_bits_per_pixel, dest_scanline_pad); } else if (req_height > 1) { int SubImageHeight = Available / BytesPerRow; if (SubImageHeight == 0) SubImageHeight = 1; PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, req_width, (unsigned int) SubImageHeight, dest_bits_per_pixel, dest_scanline_pad); PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset + SubImageHeight, x, y + SubImageHeight, req_width, req_height - SubImageHeight, dest_bits_per_pixel, dest_scanline_pad); } else { [5] int SubImageWidth = (((Available << 3) / dest_scanline_pad) [6] * dest_scanline_pad) - left_pad; PutSubImage(dpy, d, gc, image, req_xoffset, req_yoffset, x, y, (unsigned int) SubImageWidth, 1, dest_bits_per_pixel, dest_scanline_pad); PutSubImage(dpy, d, gc, image, req_xoffset + SubImageWidth, req_yoffset, x + SubImageWidth, y, req_width - SubImageWidth, 1, dest_bits_per_pixel, dest_scanline_pad); } }


技術的な脆弱性の詳細

次のサンプル画像を見てみましょう。


 Available [the requested size] = (65,536 * 4) - 28 = 262,116 bits_per_pixel = 32 width = 90,000 pixels height = 1 pixel


画像のbits_per_pixelは 32 であるため、[1] の条件ステートメントは通過せず、[2] で定義された代替コード ブロックに入る必要があります。


次に、[3] のBytesPerRowを計算し、8 で割ります。この例では、BytesPerRow = 90000 * 32 / 8 = 360,0


この例では、[4] のチェックはパスしません。360000 は要求されたサイズ 262116 より小さくなく、要求された幅の 1 行を 1 つの要求に収めることができないためです。これにより、[5] のelseが開始されます。 ]。


これにより、1 つのリクエストに含めることができるピクセル数が決まります。次に、 PutSubImage関数への再帰呼び出しを開始してそのサブセットのみを渡し、その後に続く再帰呼び出しで行の残りの部分を管理します。必要に応じて、この残りの部分を追加の再帰呼び出しによってさらに分割することもできます。


ただし、[6] の計算ではピクセルあたりのビット数が考慮されておらず、再帰呼び出しにより 2096928 ビットではなく 2096928 ピクセルを送信するリクエストが作成されます。これは 1 回のリクエストに収まりきらないサイズです。


これにより、ピクセルのラインを分割しようとする無限ループが発生し、その結果、数値が大きすぎて適合しなくなり、同じ値でプロセスを再試行することになります。この再帰は、呼び出しスタックが使い果たされるまで継続します。


バグ修正により[6] の計算が変更され、 bits_per_pixelが考慮されました。この例では、65529 ピクセルだけの送信を要求する再帰呼び出しが行われ、BytesPerRow が 262116 となり、利用可能なスペース内に完全に収まるため、再帰は前方に進み、わずか 2 回の呼び出しで終了することができます。


バグを引き起こす概念実証イメージの例: https://github.com/jfrog/jfrog-CVE-2023-43786-libX11_DoS/blob/main/cve-2023-43786.xpm


バグが引き起こされる仕組み

脆弱な libXpm ライブラリ関数を呼び出すアプリの例としては、Xpm イメージを画面に表示するために使用される CLI ユーティリティsxpmがあります。


これは、脆弱なxpmCreatePixmapFromImage Xpm 関数を呼び出し、次に脆弱な libX11 関数XPutImageを呼び出し、次にPutSubImage

xpmCreatePixmapFromImage_XPutImage_PutSubImage