panda-re / panda

Platform for Architecture-Neutral Dynamic Analysis
https://panda.re
Other
2.48k stars 479 forks source link

PyPANDA `proc_start_linux` repeated calls occasionally #954

Closed fengjian closed 3 years ago

fengjian commented 3 years ago

from pandare import Panda, blocking
panda = Panda(generic="x86_64")

@panda.ppp("proc_start_linux","on_rec_auxv")
def rec_auxv(cpu, tb, av):
    procname = panda.get_process_name(cpu)
    if procname == "uaf":
        asid = panda.current_asid(cpu)
        print(f"[hook] started proc {procname} {av.phdr:x} {av.entry:x}")

@blocking
def run_cmd():
    # First revert to root snapshot, then type a command via serial

    panda.revert_sync("root")
    panda.copy_to_guest("/root/.panda/target")
    print(panda.run_serial_cmd("uname -a"))
    print(panda.run_serial_cmd("cp /root/target/uaf /tmp/uaf && chmod +x /tmp/uaf"))
    print(panda.run_serial_cmd("/tmp/uaf"))
    print("Finding cat in cat's memory map:")
    maps = panda.run_serial_cmd("cat /proc/self/maps")
    for line in maps.split("\n"):
        if 'cat' in line:
            print(line)
    panda.end_analysis()

panda.queue_async(run_cmd)
panda.run()
PANDA[osi_linux]:W> kernelinfo bytes [20-23] not read
PANDA[core]:loading required plugin syscalls2
PANDA[core]:/panda/build//x86_64-softmmu/panda/plugins//panda_syscalls2.so already loaded
[PYPANDA] mount: /root/target: WARNING: device write-protected, mounted read-only.
Linux ubuntu 4.15.0-72-generic #81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

[hook] started proc uaf 555555554040 555555554660
[hook] started proc uaf 555555554040 555555554660
lacraig2 commented 3 years ago

I've tried your script a dozen times and can't reproduce. Could you share a recording where this happens?

fengjian commented 3 years ago

I can share a record. but I don't have any NetDisk storage.

test2.py

from pandare import Panda, blocking
import os.path
import capstone
from colorama import Fore, Back, Style

md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64)

panda = Panda(generic="x86_64")
panda.enable_memcb()
panda.load_plugin("syscalls2")
panda.load_plugin("hooks")

libc = {
        "__libc_malloc": 0x97070,
         "malloc": 0x97070,
        "__libc_free": 0x97950,
        "cfree": 0x97950,
        "free": 0x97950
        }

shared_lib_addr_space = 0x7ffff0000000
libc_baseaddr = 0x7ffff79e4000

malloc_count = 0
free_count = 0

target_proc = "uaf"

pc_cache_set = set()
free_cache_set = set()

def print_line():
    print(f"{Fore.GREEN}"+"-"*80+Style.RESET_ALL)

def uaf_exec_hook(cpu, tb, h):
    procname = panda.get_process_name(cpu)
    if procname == target_proc:
        print(f"{Fore.RED}[hook] UAF exec")
        print_line()
        code = panda.virtual_memory_read(cpu, tb.pc, tb.size)
        for i in md.disasm(code, tb.pc):
            print(f"0x{i.address:x}:{Fore.YELLOW}\t{i.mnemonic}\t{i.op_str}"+Style.RESET_ALL)
        print_line()
    pass

@panda.cb_virt_mem_after_write(procname="uaf")
def virt_mem_after_write(cpu, pc, addr, size, buf):
    if pc < shared_lib_addr_space:
        for cache in free_cache_set:
            if addr == cache + 24:
                evil_addr = panda.virtual_memory_read(cpu, addr, size, 'int')
                asid = panda.current_asid(cpu)
                panda.hook(evil_addr, enabled=True, kernel=False, asid=asid)(uaf_exec_hook)
                print(f"[hook] UAF mem write addr: {Fore.GREEN}0x{addr:x}{Style.RESET_ALL}, value: {Fore.RED}0x{evil_addr:x}"+Style.RESET_ALL)
        return

def call_return_hook(cpu, tb, h):
    procname = panda.get_process_name(cpu)
    if procname == target_proc:
        ret = panda.arch.get_return_value(cpu)
        print(f"[hook] proc:{procname} malloc ret: {Fore.GREEN}0x{ret:x}"+Style.RESET_ALL)
        if ret in free_cache_set:
            print(f"[hook] proc:{procname} UAF in {Fore.RED}0x{ret:x}"+Style.RESET_ALL)

def libc_malloc_hook(cpu, tb, h):
    global malloc_count
    procname = panda.get_process_name(cpu)
    if procname == target_proc:
        sp = panda.current_sp(cpu)
        retaddr = panda.arch.get_return_address(cpu)
        size = panda.arch.get_arg(cpu, 0)

        print(f"[hook] proc:{procname} malloc: call_addr:0x{retaddr:x} arg0:{size}")
        if retaddr < shared_lib_addr_space:
            if retaddr not in pc_cache_set:
                pc_cache_set.add(retaddr)
                asid = panda.current_asid(cpu)
                panda.hook(retaddr, enabled=True, kernel=False, asid=asid)(call_return_hook)
            malloc_count += 1

def libc_free_hook(cpu, tb, h):
    global free_count
    procname = panda.get_process_name(cpu)
    if procname == target_proc:
        sp = panda.current_sp(cpu)
        retaddr = panda.arch.get_return_address(cpu)
        addr = panda.arch.get_arg(cpu, 0)

        free_cache_set.add(addr)
        print(f"[hook] proc:{procname} free: call_addr:0x{retaddr:x} arg0:{Fore.GREEN}0x{addr:x}"+Style.RESET_ALL)
        if retaddr < shared_lib_addr_space:
            free_count += 1

@panda.ppp("proc_start_linux","on_rec_auxv")
def rec_auxv(cpu, tb, av):
    procname = panda.get_process_name(cpu)
    if procname == "uaf":
        asid = panda.current_asid(cpu)
        panda.hook(libc["malloc"]+libc_baseaddr, enabled=True, kernel=False, asid=asid)(libc_malloc_hook)
        panda.hook(libc["free"]+libc_baseaddr, enabled=True, kernel=False, asid=asid)(libc_free_hook)
        print(f"[hook] started proc {procname} {av.phdr:x} {av.entry:x}")

@blocking
def run_cmd():
    # First revert to root snapshot, then type a command via serial

    panda.revert_sync("root")
    panda.copy_to_guest("/root/.panda/target")
    print(panda.run_serial_cmd("uname -a"))
    print(panda.run_serial_cmd("cp /root/target/uaf /tmp/uaf && chmod +x /tmp/uaf"))
    print(panda.run_serial_cmd("/tmp/uaf"))
    print("Finding cat in cat's memory map:")
    maps = panda.run_serial_cmd("cat /proc/self/maps")
    for line in maps.split("\n"):
        if 'cat' in line:
            print(line)
    panda.end_analysis()

panda.queue_async(run_cmd)
panda.run()

print(malloc_count)
print(free_count)

not work occasionally

[PYPANDA] mount: /root/target: WARNING: device write-protected, mounted read-only.
Linux ubuntu 4.15.0-72-generic #81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

p1 malloc addr: 0x555555756260
hello world
free p1: hello world again
p2 malloc addr: 0x555555756260
p1 origin addr: 0x555555756260
call p1: whoami
root
Finding cat in cat's memory map:
555555554000-55555555c000 r-xp 00000000 08:01 19                         /bin/cat
55555575b000-55555575c000 r--p 00007000 08:01 19                         /bin/cat
55555575c000-55555575d000 rw-p 00008000 08:01 19                         /bin/cat
0
0

repeated calls

[hook] started proc uaf 555555554040 555555554660
[hook] started proc uaf 555555554040 555555554660
[hook] proc:uaf malloc: call_addr:0x5555555547b2 arg0:16
[hook] proc:uaf malloc: call_addr:0x5555555547b2 arg0:16
[hook] proc:uaf malloc ret: 0x555555756260
[hook] proc:uaf malloc: call_addr:0x7ffff7a6218c arg0:4096
[hook] proc:uaf malloc: call_addr:0x7ffff7a6218c arg0:4096
[hook] proc:uaf free: call_addr:0x555555554800 arg0:0x555555756260
[hook] proc:uaf free: call_addr:0x555555554800 arg0:0x555555756260
[hook] proc:uaf malloc: call_addr:0x55555555481e arg0:16
[hook] proc:uaf malloc: call_addr:0x55555555481e arg0:16
[hook] proc:uaf malloc ret: 0x555555756260
[hook] proc:uaf UAF in 0x555555756260
[hook] UAF mem write addr: 0x555555756278, value: 0x55555555476a
[hook] UAF exec

repeated calls and Segmentation fault

[hook] started proc uaf 555555554040 555555554660
[hook] started proc uaf 555555554040 555555554660
[hook] proc:uaf malloc: call_addr:0x5555555547b2 arg0:16
[hook] proc:uaf malloc: call_addr:0x5555555547b2 arg0:16
[hook] proc:uaf malloc ret: 0x555555756260
[hook] proc:uaf malloc: call_addr:0x7ffff7a6218c arg0:4096
[hook] proc:uaf malloc: call_addr:0x7ffff7a6218c arg0:4096
[hook] proc:uaf free: call_addr:0x555555554800 arg0:0x555555756260
[hook] proc:uaf free: call_addr:0x555555554800 arg0:0x555555756260
[hook] proc:uaf malloc: call_addr:0x55555555481e arg0:16
[hook] proc:uaf malloc: call_addr:0x55555555481e arg0:16
[hook] proc:uaf malloc ret: 0x555555756260
[hook] proc:uaf UAF in 0x555555756260
[hook] UAF mem write addr: 0x555555756278, value: 0x55555555476a
[hook] UAF exec
--------------------------------------------------------------------------------
0x55555555476a: push    rbp
0x55555555476b: mov     rbp, rsp
0x55555555476e: sub     rsp, 0x10
0x555555554772: mov     qword ptr [rbp - 8], rdi
0x555555554776: mov     rax, qword ptr [rbp - 8]
0x55555555477a: mov     rdi, rax
0x55555555477d: call    0x555555554620
--------------------------------------------------------------------------------
p1 malloc addr: 0x555555756260
hello world
free p1: hello world again
p2 malloc addr: 0x555555756260
p1 origin addr: 0x555555756260
call p1: whoami
root
Finding cat in cat's memory map:
555555554000-55555555c000 r-xp 00000000 08:01 19                         /bin/cat
55555575b000-55555575c000 r--p 00007000 08:01 19                         /bin/cat
55555575c000-55555575d000 rw-p 00008000 08:01 19                         /bin/cat
Segmentation fault (core dumped)
[2507790.571593] python[28518]: segfault at 2a70b38 ip 0000000002a70b38 sp 00007fff7d0eb9e8 error 15
[2518189.800371] python[29315]: segfault at 1eb2ba8 ip 0000000001eb2ba8 sp 00007ffdc3582998 error 15
[2518215.395536] python[29324]: segfault at 12d5ba8 ip 00000000012d5ba8 sp 00007fff7e7ef758 error 15
[2598383.266256] python[24997]: segfault at e9bba8 ip 0000000000e9bba8 sp 00007fff7e38f9a8 error 15
lacraig2 commented 3 years ago

Hi @fengjian,

I wrote up a PR #958 that should address some of the issues here. Specifically it allows for specifying offsets into libraries directly.

e.g.

@panda.hook_symbol("libc-", 0x1234)
def hook_random_lib_offset(cpu,tb,h):
    pass

I'm really unsure why you would be seeing a segfault. Could you consider providing a stack trace from GDB?

Re: recording we can set up some other way of transferring it. Feel free to shoot me an email at the email on my profile.

fengjian commented 3 years ago

Hi, @lacraig2 I've tried the PR #958 and lastest image

The bug still exists, let me share a recording.

REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
pandare/pandadev             latest              d6731925401d        34 hours ago        4.99GB

repeated calls: I've tried a dozen times and can't reproduce if use panda.run_replay. but the bug still exists if use panda.run.

The hook not working occasionally, I have a recording about this.

fengjian commented 3 years ago

I think disable PANDA_CB_BEFORE_TCG_CODEGEN is incorrect if use panda.run.

@panda.ppp("proc_start_linux","on_rec_auxv")
def rec_auxv(cpu, tb, av):
    procname = panda.get_process_name(cpu)
    print(f"started proc {procname} pc:0x{tb.pc:x}")
    if procname == "uaf":
        asid = panda.current_asid(cpu)
        panda.hook(libc["malloc"]+libc_baseaddr, enabled=True, kernel=False, asid=asid)(libc_malloc_hook)
        panda.hook(libc["free"]+libc_baseaddr, enabled=True, kernel=False, asid=asid)(libc_free_hook)
        print(f"[hook] started proc {procname} pc:0x{tb.pc:x} {av.phdr:x} {av.entry:x}")
started proc uaf
[hook] started proc uaf pc: 0x7ffff7dd6090 555555554040 555555554660
started proc uaf
[hook] started proc uaf pc: 0x7ffff7dd60ca 555555554040 555555554660
[hook] proc:uaf malloc: call_addr:0x5555555547b2 arg0:16
[hook] proc:uaf malloc: call_addr:0x5555555547b2 arg0:16
[hook] proc:uaf malloc ret: 0x555555756260
[hook] proc:uaf malloc: call_addr:0x7ffff7a6218c arg0:4096
[hook] proc:uaf malloc: call_addr:0x7ffff7a6218c arg0:4096
[hook] proc:uaf free: call_addr:0x555555554800 arg0:0x555555756260
[hook] proc:uaf free: call_addr:0x555555554800 arg0:0x555555756260
[hook] proc:uaf malloc: call_addr:0x55555555481e arg0:16
[hook] proc:uaf malloc: call_addr:0x55555555481e arg0:16
[hook] proc:uaf malloc ret: 0x555555756260
[hook] proc:uaf UAF in 0x555555756260
[hook] UAF mem write addr: 0x555555756278, value: 0x55555555476a
[hook] UAF exec
started proc mkdir pc: 0x7ffff7dd6090
started proc mount pc: 0x7ffff7dd6090
started proc mount pc: 0x7ffff7dd60ca
started proc modprobe pc: 0x7ffff7dd6090
started proc cp pc: 0x7ffff7dd6090
started proc sh pc: 0x7ffff7dd6090
[PYPANDA] mount: /root/target.ro: WARNING: device write-protected, mounted read-only.
started proc ln pc: 0x7ffff7dd6090
started proc uname pc: 0x7ffff7dd6090
Linux ubuntu 4.15.0-72-generic #81-Ubuntu SMP Tue Nov 26 12:20:02 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
started proc readlink pc: 0x7ffff7dd6090
started proc cp pc: 0x7ffff7dd6090
started proc cdrom_id pc: 0x7ffff7dd6090
started proc cdrom_id pc: 0x7ffff7dd6098