【HUST】ネットワーク攻撃・防御演習|5_バイナリファイルパッチ技術|実験2 getshell

実験要件

printf関数を新しい関数に変更しnewprintfgetshell次の命令を実行します。

whoami
echo "学号" > num.txt
cat num.txt
exit

/bin/sh文字列はのパラメータであるgetshellため、printfこれを直接使用できます。

実験手順

私が採用した実験原理: パッチ プログラムを.textターゲット プログラムにコピーし.eh_frame、ターゲット プログラムの関数ジャンプ アドレスを変更して、脆弱なプログラムがジャンプしてパッチ コード.eh_frameを。

1. 新しい printf 関数を書く

インライン アセンブリを使用します。インライン アセンブリで使用される構文はAT&T. 通常書かれている編纂とは違いますIntelインライン アセンブリの記述については、GCC Inline Assembly Basics - Short Book (jianshu.com)という短い本を参照します。
ブロガーさんの詳しい解説ありがとうございます!

書かれたコードはhook.c次のとおりです。

void myprintf(char *a, int b) {
    
    
  int tmp;
  asm(
    "mov %1,%%rdi\n"
    :"=r"(tmp)
    :"r"(a)
    :"%rdi"
  );
  asm(
    "mov $0,%rsi\n"
    "mov $0,%rdx\n"
    "mov $59,%rax\n"
    "syscall\n"
  );
}

主な意味は、元のパラメーターexecve(a,0,0)であるコンストラクターです64 ビット システムのシステム コール番号に対応しますaprintf/bin/shexecve59

上記のコードを静的リンク ライブラリにコンパイルします。

gcc -c hook.c -o hook

2. LIEF を使用する Python スクリプトを作成する

最初に依存関係をインストールします。

pip install lief -y
pip install pwn -y

Note: -yThe parameter is the default when the "whether to install" pop up yes. 下位バージョンの pip はこのオプションをサポートしていないため、追加しないでください。個人的には、パラメータを追加することに慣れています-y

python2 のリーフをダウンロードすると、常にエラーが報告されます。公式liefサイトで確認したところ、最新バージョンのliefはpython2に対応していません。そのため、python3 環境が使用されます。

python2 を使用している場合は、説明書のスクリプトを直接コピーするだけです。

スクリプトの考え方は説明書と同じです。ファイルを読み取って解析し、セクションをセクションコピーgetshellステートメントを に変更します最後に、変更した実行可能ファイルを に書き込みます。hookhook.textgetshell.eh_framecall _printfcall .eh_frame的起始地址getshell.patched

バージョンの問題のみが原因でpython、変更が必要な場所が 3 つあります。

  1. python3 の出力にprintは括弧が必要です。
  2. 2 つのデータ型は、python3 では区別されbytesstringpython2 では混在しているため、bp32 の変換結果とつなぎ合わせるには、python3 ではバイト文字列の前に追加する必要があります。
  3. ord()この関数が受け取る型は長さ 1 の文字列です. 型をfor...in...トラバースすると取得したデータは既にその型です. python2 では呼び出す必要がありますが, python3 では呼び出す必要はありません.bytesintord

具体的なスクリプトはlief_test.py次のとおりです。

import lief
from pwn import *

def patch_call(file,srcaddr, dstaddr , arch = "amd64"):
  print(hex(dstaddr))
  length = p32((dstaddr - (srcaddr + 5 ))& 0xffffffff)
  order = b'\xe8' +length
  print(disasm(order , arch=arch))
  file.patch_address(srcaddr, [i for i in order])

binary = lief.parse("./getshell")
hook = lief.parse('./hook')

# write hook 's .text content to binary's .eh_frame content
sec_ehrame = binary.get_section( '.eh_frame')
print(sec_ehrame.content)
sec_text = hook.get_section('.text')
print(sec_text.content)
sec_ehrame.content = sec_text.content
print(binary.get_section('.eh_frame').content)

# hook target call
dstaddr = sec_ehrame.virtual_address
srcaddr = 0x401149

patch_call(binary,srcaddr,dstaddr)

binary.write('getshell.patched')

3.getshellセクション.eh_frameを実行可能に設定

.eh_frameセクションを直接変更してジャンプして実行すると、次のセグメント エラーが報告されます。

パーミッションの変更:セグメントフォルト|ELF 非実行セグメントパーミッションの変更 (010 エディター) .

それはいいです。

4. パッチを適用し、パッチを適用したプログラムを実行します

パッチをコンパイルし、パッチを適用して、パッチを適用したプログラムを実行します。

gcc -c hook.c -o hook
python3 lief_test.py
./getshell.patched

最後に、フェッチが成功したことを確認しますshell

入力:

whoami
echo "学号" > num.txt
cat num.txt
exit

シェルが正常に取得できていることがわかります。

5. 修正前後でプログラムのサイズが変わらないことを確認する

同級生と話し合ったところ、主にLIEFこのライブラリの制限により、プログラムのサイズが大きく変わっていることがわかりました。このパッチは非常に単純なので、プログラムのサイズが変更の前後で同じであることを確認するために、パッチ適用に直接IDA Pro使用することに。うまくいくかどうかはわかりませんが、それ以外の方法は本当にわかりません

原理は前回課題と同じoverflow. 詳しくはこちらのHUSTネットワーク攻防演習|5_バイナリファイルパッチ技術|実験1 オーバーフロー.

ずさんな書き方をした先輩もいて、検索したら出てきた
【ネットワークセキュリティ実践Ⅲ】実験5.パッチ

ジャンプする開始位置であるcall _printf変更します。次にパッチ コードを記述します。パッチ コードは最終的に元の実行フローに戻ります。jmp 0x402058.eh_frame.eh_frame

mov rsi,0
mov rdx,0
mov rax,59
syscall
jmp 0x40114E

次に、元のプログラムが配置されているセクション.eh_frameを実行可能に変更します。
操作結果は以下の通りで、getshellファイルサイズを変更せずに成功したことがわかります。

おすすめ

転載: blog.csdn.net/qq_46106285/article/details/125098087