CTF | PWN练习一

预备知识:

1、了解PWN(溢出

       PWN是一个黑客语法的俚语词,自"own"这个字引申出来的,这个词的含意在于,玩家在整个游戏对战中处在胜利的优势,或是说明竞争对手处在完全惨败的 情形下,这个词习惯上在网络游戏文化主要用于嘲笑竞争对手在整个游戏对战中已经完全被击败(例如:"You just got pwned!")。有一个非常著名的国际赛事叫做Pwn2Own,相信你现在已经能够理解这个名字的含义了,即通过打败对手来达到拥有的目的。

      CTF中PWN题型通常会直接给定一个已经编译好的二进制程序(Windows下的EXE或者Linux下的ELF文件等),然后参赛选手通过对二进制程 序进行逆向分析和调试来找到利用漏洞,并编写利用代码,通过远程代码执行来达到溢出攻击的效果,最终拿到目标机器的shell夺取flag。

2、Linux管道

      Linux管道可以将一个进程的标准输出作为另一个进程的标准输入,管道的操作符号为“|”,比如ls命令可用于查看当前目录下的文件列表,而grep命 令可用于匹配特定的字符,因此ls | grep test命令可用于列出当前目录下文件名包含test的文件。

3、Python基础

      在Linux shell中执行python -c "print 'Hello'"可以执行双引号中的Python语句,即通过print打印出Hello字符串。Python中单引号和双引号没有区别,因为这里使用双 引号修饰Python语句,因此使用单引号修饰字符串。

4、gdb调试器

      gdb是Linux下常用的一款命令行调试器,拥有十分强大的调试功能。本实验中需要用到的gdb命令如下:

      

5、汇编基础

      读懂常见的汇编指令是CTF竞赛中PWN解题的基本要求,本实验中需要理解的汇编指令如下:

      

      注:图为AT&T风格的汇编指令。

      汇编语言中,esp寄存器用于指示当前函数栈帧的栈顶的位置,函数中局部变量都存储在栈空间中,栈的生长方向是向下的(即从高地址往低地址方向生长)。

     ebp存储着当前函数栈底的地址,栈底通常作为基址,可以通过栈底地址和偏移相加减来获取变量地址。

      缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量,使得溢出的数据覆盖在合法数据上,理想的情况是程序检查数据长度并不允许输入超 过缓冲区长度的字符,但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配,这就为缓冲区溢出埋下隐患。

实验描述:

        主机/home/test/1目录下有一个pwn1程序,执行这个程序的时候可以输入数据进行测试,pwn1程序会输出Please try again.的提示信息,请对pwn1程序进行逆向分析和调试,找到程序内部的漏洞,并构造特殊的输入数据,使之输出Congratulations, you pwned it.信息。

实验步骤:

1、源码审计

      (在没有源代码的情况下,我们通常使用IDA Pro对二进制程序进行逆向分析,使用IDA的Hex-Rays插件可以将反汇编代码还原为C语言伪代码,可以达到类似源代码的可读效果,在后期的实验中会专门对IDA的使用进行讲解)

      使用cd /home/test/1切换到程序所在目录,执行cat pwn1.c即可看到源代码:

     使用gets函数读取输入数据时,并不会对buffer缓冲区的长度进行检查,输入超长的输入数据时会引发缓冲区溢出。

2、使用gdb调试程序

     执行gdb pwn1即可开始通过gdb对pwn1进行调试,现在需要阅读main函数的汇编代码,在gdb中执行disas main命令即可:

 下面是对main函数中的汇编代码的解释:

0x080482a0 <+0>:   push   %ebp

0x080482a1 <+1>:   mov    %esp,%ebp

0x080482a3 <+3>:   and    $0xfffffff0,%esp

; esp = esp - 0x60,即在栈上分配0x60)字节的空间

0x080482a6 <+6>:   sub    $0x60,%esp

; modified变量位于esp + 0x5C处,将其初始化为0

0x080482a9 <+9>:   movl   $0x0,0x5c(%esp)

; buffer位于esp + 0x1C处

0x080482b1 <+17>:  lea    0x1c(%esp),%eax

0x080482b5 <+21>:  mov    %eax,(%esp)

; 调用gets(buffer)读取输入数据

0x080482b8 <+24>:  call   0x8049360 <gets>

; 判断modified变量的值是否是0

0x080482bd <+29>:  cmpl   $0x0,0x5c(%esp)

; 如果modified的值等于0,就跳转到 0x080482d2

0x080482c2 <+34>:  je     0x80482d2 <main+50>

; modified不为0,打印成功提示

0x080482c4 <+36>:  movl   $0x80b3eec,(%esp)

0x080482cb <+43>:  call   0x8049500 <puts>

0x080482d0 <+48>:  jmp    0x80482de <main+62>

; modified为0,打印失败提示

0x080482d2 <+50>:  movl   $0x80b3f0b,(%esp)

0x080482d9 <+57>:  call   0x8049500 <puts>

0x080482de <+62>:  mov    $0x0,%eax

0x080482e3 <+67>:  leave

0x080482e4 <+68>:  ret

      通过对上面的汇编代码进行分析,知道buffer位于esp+0x1C处,而modified位于esp+0x5C处,两个地址的距离为0x5C - 0x1C = 0x40,即64,刚好为buffer数组的大小。因此当输入的数据超过64字节时,modified变量就可以被覆盖。

      下面在gdb中进行验证,在gdb中执行b *0x080482bd命令对gets的下一条指令下一个断点:

     在gdb中执行r命令,让被调试的pwn1程序跑起来,就可以输入数据进行测试了,这里我们输入64个A以及1个B,按下 Enter键程序就在断点处断下了,然后在gdb中输入x $esp+0x5C,查看modified变量的值已经被修改成了0x00000042,而0x42就是字符’B’的ASCII值,表明我们成功用输入数据的第65个字节覆盖了modified变量:

      在gdb中连续两次执行ni命令(用来断点、定位),可以看到je指令没有跳转,说明modified的值不为0,程序进入输出通过信息的if语句分支,然后在gdb中输入c命令就可以让程序继续执行,看到输出了通过提示信息:

3、体验溢出攻击效果

      通过上面的步骤已经知道了如果控制输入数据来进行攻击,以达到进入if语句分支的目的。下面我们就可以通过构造输入数据进行攻击了。

     还没有退出gdb,输入q命令就可以退出gdb。下面通过python语句构造输入数据,然后通过管道传给pwn1程序,执行命令python -c "print 'A'*64+'B'" | ./pwn1

实验总结:

1、linux安装gdb:sudo apt-get install gdb

2、gdb调试:gdb 文件名

3、查看main函数汇编代码,在gdb执行命令:disas main

4、gdb设置断点:

源文件的某一行设置断点:break/b 行号

一个特定的函数设置断点:break/b 函数名

设置条件断点:break/b 行号 if 条件

5、gdb删除断点:delete 行号

6、查看断点信息并保存:info b

7、运行调试的程序:run/r

8、跳转到断点命令(countine 简写c):c

9、退出gdb命令:quit/q

10、当使用gdb调试一个正在运行的进程时参考:https://blog.csdn.net/zxh2075/article/details/76850092

更多GDB命令参考:gbd命令

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值