FrontPage > Armadillo-9 > exploit xpdf_arm

xpdf(changed)(on ARM)

環境

パッチが当たると、以下のように境界チェックが入るので、境界チェックを排除する。

//Gfx.cc:2660 行目付近
if (maskObj.isArray()) {
  for (i = 0;
       i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;
       ++i) {
    maskObj.arrayGet(i, &obj1);
    maskColors[i] = obj1.getInt();
-----------------------------------------------------------------
// get the mask
haveMask = gFalse;
dict->lookup("Mask", &maskObj);
if (maskObj.isArray()) {
  for (i = 0; i < maskObj.arrayGetLength(); ++i) {
    maskObj.arrayGet(i, &obj1);
    maskColors[i] = obj1.getInt();
    obj1.free();
  }
  haveMask = gTrue;
}

変数の宣言順序について

デフォルトだと、以下のような配置になっている。上にある方が 0x00000000 側のアドレスである。

[size]  [declaration]
4       int i;
12      Object obj2;
12      Object obj1;
256     int[64] maskColors; //[!]
4       GBool haveMask;
12      Object maskObj;
4       GfxImageColorMap *colorMap;
4       GfxColorSpace *colorSpace;
4       GBool invert;
4       GBool mask;
4       int bits;
4       int height;
4       int width;
4       Dict *dict;
----------------------------------
4       (arg2)GBool inlineImg;
4       (arg1)Stream *str;
4       (arg0)Object *ref;
4       this pointer;
----------------------------------
84      (backup area for registers)
----------------------------------
4       ret_addr

変更後は、maskColors が後ろに来るので、以下のようになる。

[size]  [declaration]
(...snip...)
4       int height;
4       int width;
4       Dict *dict;
256     int[64] maskColors; //[!]
----------------------------------
4       (arg2)GBool inlineImg;
4       (arg1)Stream *str;
4       (arg0)Object *ref;
4       this pointer;
----------------------------------
84      (backup area for registers)
----------------------------------
4       ret_addr

具体的には以下のような変更を行う。

//original
Dict *dict;
(...snip...)
Object maskObj;
GBool haveMask;
int maskColors[2*gfxColorMaxComps]; //[!]
Object obj1, obj2;
int i;
---------------------------------------
//changed
int maskColors[2*gfxColorMaxComps]; //[!]
Dict *dict;
(...snip...)
Object maskObj;
GBool haveMask;
Object obj1, obj2;
int i;

攻撃対象

リターンアドレス書き換えが使えない理由

ARM では引数とリターンアドレスの位置関係によって(libpngと同様の理由)リターンアドレスだけ書き換える、という手法が使えない。

ARMでの攻撃手法

バッファオーバーフローを起こした後、out->drawImage という仮想関数が呼び出される。この部分を利用する。

// get the mask
haveMask = gFalse;
dict->lookup("Mask", &maskObj);
if (maskObj.isArray()) {
  for (i = 0; i < maskObj.arrayGetLength(); ++i) {
    maskObj.arrayGet(i, &obj1);
    maskColors[i] = obj1.getInt();
    obj1.free();
  }
  haveMask = gTrue;
}
// draw it
out->drawImage(state, ref, str, width, height, colorMap,
               haveMask ? maskColors : (int *)NULL, inlineImg); //[!]
delete colorMap;
maskObj.free();

攻撃の詳細

計算すべきアドレス

攻撃作成の際の注意点

攻撃データの詳細

今回の攻撃には、仮想関数の関数ポインタを書き換えるために必要なエリアが 3つ、攻撃コード記述用のエリアが 1つ必要である。

スタックのレイアウト(再掲)

先述した変数の宣言位置の変更を行った後のスタックのレイアウトは以下のとおりである。

[size]  [declaration]
(...snip...)
4       int height;
4       int width;
4       Dict *dict;
256     int[64] maskColors; //[!]
----------------------------------
4       (arg2)GBool inlineImg;
4       (arg1)Stream *str;
4       (arg0)Object *ref;
4       this pointer;
----------------------------------
84      (backup area for registers)
----------------------------------
4       ret_addr

攻撃データのレイアウト

スタック開始位置のずれ

環境によって、ポインタで指す位置がずれる。

|----| スタックが予想していたよりも低い
|    | ときに指される領域
|    |
|----| <-- 予想とぴったりのときに指す位置
|    |
|    | スタックが予想していたよりも高い
|----| ときに指される領域

今回使ったオフセット値

今回使ったデータでは、以下のような位置関係にした。

maskColors の予想開始位置をベースとしたオフセットで表す。手で計算してもいいけどcalc_address という補助プログラムを作ったので、それを使うほうが簡単。

[data]    [offset] [why]
this:     +96      (to Area A(n))
Area A:   +368     (Area A(268) + this(4) + to Area B(n))
Area B:   +752     (A(268) + this(4) + B(384) + to C(n))
Area C:   +1136    (A(268) + this(4) + B(384) + C(384) + to D(n))

to Area X(n) は、今回の場合 n=96 である。ただしこの数値は「低め用の領域」をどのくらい取るかによって変わるので注意である。

変換

pdf に記述するときは「符号付の 10進数」に直さなければいけないので、converter を書いた。


トップ   編集 凍結 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-09-13 (土) 08:26:38