虚渺的PWN学习笔记(一)

前言

此篇文章用于自省,教学意义不大,且参考性不高,适合萌新。(但是我觉得有这些没必要看我的文章),主要包括的就是ret2text到ret2csu的题型和一些自己的感悟。

以下内容均为个人主观看法,不隐喻任何现实中的事、人、地点和时间。

正文

学习基础

地址的唯一性及其承载功能

作为初学者,我们对于地址的功能可以用数学中的坐标来辅助理解。在一个二维平面直角坐标中,每一个点都是有唯一对应的(x,y)坐标,这不仅突出了坐标的唯一性,同时也方便了观赏。

比如(0,0),这是原点,独一无二。同时(1,2)、(5,1237)和(17513,1491)这些坐标都是独一无二的。我们要做的就是理解地址的唯一性。一个32位地址,比如0xFFABCDEE,这是一个真实地址,他和坐标(1,2)一样唯一。

那么地址有什么用呢?答:和坐标一样。

比如设一点(5,9)为a,它在直线y=x+4上,那么,坐标(5,4)的其中两个意义为“a”和“在直线y=x+4上”。

那么,“等量代换”一下,设“地址0xFFABCDEE”为“ret”,它在“text段”上,那么“地址0xFFABCDEE”的其中两个意义为“ret”和“在text段上”。

其中“a”是坐标(5,4)所承载的变量。而“ret”是“地址0xFFABCDEE”所承载的“汇编指令”。

ret指令

首先我们要知道的是它的作用:将程序指向的地址改变。注意!他改变的是地址!

其次,它的实质是pop指令,可以简单理解将地址放到eip中,eip是一个寄存器,其功能简单来说就是存放“程序执行指向的地址”,说人话就是,程序执行某一指令,该指令的下一个指令的地址就存放在eip中。

ret2text(幻听:return to text )

text段

我们要执行的程序都是一般都是在这个地方,这里存放着我们的源码。

先说eip吧,在程序执行的过程中,eip的作用举足轻重,eip作为一个寄存器,其中保存的是下一条指令执行的地址,换句话说,eip掌控着,程序的走向。

那么,如果我们能控制eip中保存的值是不是就代表着我们已经控制了这个程序?答案是:是的。

接下来我们用ctfshow中的一道题(点我跳转题目)来巩固知识点。

笔者这里用的是ubuntu20.04。

首先讲题目复制到ubuntu20.04中,在该目录下打开命令窗。

首先用checksec指令检查程序保护情况和运行环境

然后用touch + 名字的方式,创建一个文档,用来写我们的exp,即攻击脚本。

不急着写脚本,我们先看看源码长什么样,用ida(32位)打开这个文件,只需要把文件拖到ida(32位)的图标上就行,或者右键文件,选择其他打开方式,选择ida(32位),然后点ok就行。

但是进去之后是一堆汇编指令,作为初学者的我们看不懂怎么办?

没关系,ida有反汇编的功能,只需要按一下键盘上的F5就可以把汇编代码变成c语言。

进来第一步是看函数,目的是找到奇奇怪怪的函数,比如上面的pwnme,意思很明显了,它就是我们要找的危险函数,所谓危险函数,就是这个函数可以帮助我们pwn掉程序或者说夺取程序的控制权,再大些,就是可以控制电脑。

接下来就是看代码了,作为初学者,有些代码看不懂怎么办,我的回答是自行百度指令是干什么的,既然师父们点进来看我的文章了,那么我也需要给出我的答案了,接下来我会一步步解读代码。

首先是main函数,这是面向用户的程序的入口,换句话说,我们写的代码首先执行的是main函数中的代码,一般我们自己写的c语言中main函数是没有参数的,这里却有参数,那么,是ida出错了吗?还是说我们一直都在写错误的代码?答案是:main函数是有参数的,但是C语言规定可以不写参数,因此我们写的也不算错误的代码。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  puts("stack happy!");
  puts("32bits\n");
  pwnme();
  puts("\nExiting");
  return 0;
}

setvbuf函数暂时把它理解为初始化函数,而它初始化的是输入缓冲区和输出缓冲区。

puts函数是输出函数,效果是把双引号中的数据打印在屏幕上并加上‘\n',效果就是打印完双引号里的数据就换行了。

pwnme是一个函数,那么让我们看看里面有什么吧!

int pwnme()
{
  char s[9]; // [esp+Fh] [ebp-9h] BYREF

  fgets(s, 50, stdin);
  return 0;
}

fget函数是格式化输入函数,s是接收数据的变量,50是接收多少字节的数据,stdin是指在输入缓冲区中接收数据。

s数组只有9个字节的大小,而我们却可以输入50个字节的数据,不难看出这里存在栈溢出漏洞。

既然我们找到了漏洞,那么接下来,我们就要去寻找可以利用的危险代码了,危险代码和危险函数一样,可以帮助我们获取程序的控制权,一般来说危险代码是指system("/bin/sh"),而危险函数一般是含有这个代码,或者可以帮助我们执行这个代码。

但我们点开stack函数的时候……我们要做的便是让eip里存放这个代码的地址,依靠的便是我们前面讲的ret指令。

接下来便是调试,获取偏移,然后覆盖栈道ebp+4的地方为危险代码的地址。 

gdb ./stack

先gdb +./程序名开启调试,笔者这里用的是pwndbg

输入b main,意思是在main函数的入口处下一个断点,程序到断点就会暂停,不再继续执行。

然后输入r表示执行程序,按回车后便能看到和笔者下面一样的界面。

下面表明ebp指向的地址为0xffffd198,+4便是下一行,也就是ret将要执行的位置,而我们要做的便是将该处变成system("/bin/sh")的地址。

输入cyclic 50,效果是输出50个随机字符,它的作用马上揭晓

 输入n,然后敲回车,便能进行下一步指令。绿色箭头和地址便是下一条指令及其地址。

直到指向pwnme函数是停下,输入s,然后回车一敲,就能进入pwnme函数内部,如果输入n则会执行完这个函数,然后直接指向sub汇编指令。

直到然后输入n直到执行fgets函数

那么,这时候,轮到我们之前复制的随机数登场了!

再走一步,下方的<pwndbg>会跳到一行,这时候粘贴我们的随机数上去,然后回车一敲!

然后输入n,走到ret汇编指令的位置,然后你就会发现……

红框里的0x65616161便是ret指令将要放到eip寄存器的值。

这时候我们再输入

cyclic -l 0x65616161

回车一敲!

这里绿色中的数字便是要填充的字节数,注意这里的13是包括了覆盖ebp的4个字节数!

输入q,然后按回车退出pwndbg。

最后,我们就可以写我们的脚本了。

from pwn import*
context(arch='i386',os='linux',log_level='debug')
sh=process('./stack')

binsh=0x8048518

payload=b'a'*13+p32(binsh)

sh.sendline(payload)

sh.interactive()

如果出现/bin/sh:,一般就代表成功了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值